Chaocipher

From Rosetta Code
Task
Chaocipher
You are encouraged to solve this task according to the task description, using any language you may know.
Description

The Chaocipher was invented by J.F.Byrne in 1918 and, although simple by modern cryptographic standards, does not appear to have been broken until the algorithm was finally disclosed by his family in 2010.

The algorithm is described in this paper by M.Rubin in 2010 and there is a C# implementation here.


Task

Code the algorithm in your language and test that it works with the plaintext 'WELLDONEISBETTERTHANWELLSAID' used in the paper itself.

11l

Translation of: Python
F correct_case(string)
   R string.filter(s -> s.is_alpha()).map(s -> s.uppercase()).join(‘’)

F permu(String alp; num)
   R alp[num..]‘’alp[0 .< num]

F rotate_wheels(lalph, ralph, key)
   V newin = ralph.index(key)
   R (permu(lalph, newin), permu(ralph, newin))

F scramble_wheels(String =lalph, String =ralph)
   lalph = lalph[0]‘’lalph[2.<14]‘’lalph[1]‘’lalph[14..]
   ralph = ralph[1.<3]‘’ralph[4.<15]‘’ralph[3]‘’ralph[15..]‘’ralph[0]
   R (lalph, ralph)

F do_chao(=msg, =lalpha, =ralpha, en = 1B, show = 0B)
   msg = correct_case(msg)
   V out = ‘’
   I show
      print(‘=’ * 54)
      print((10 * ‘ ’)‘left:’(21 * ‘ ’)‘right: ’)
      print(‘=’ * 54)
      print(lalpha‘ ’ralpha" \n")
   L(l) msg
      I en
         (lalpha, ralpha) = rotate_wheels(lalpha, ralpha, l)
         out ‘’= lalpha[0]
      E
         (ralpha, lalpha) = rotate_wheels(ralpha, lalpha, l)
         out ‘’= ralpha[0]
      (lalpha, ralpha) = scramble_wheels(lalpha, ralpha)
      I show
         print(lalpha‘ ’ralpha)
   R out

V lalpha = ‘HXUCZVAMDSLKPEFJRIGTWOBNYQ’
V ralpha = ‘PTLNBQDEOYSFAVZKGJRIHWXUMC’
V msg = ‘WELLDONEISBETTERTHANWELLSAID’

print(‘L: ’lalpha)
print(‘R: ’ralpha)
print(‘I: ’msg)
V o = do_chao(msg, lalpha, ralpha, 1B, 0B)
print(‘O: ’o)
print(‘D: ’do_chao(o, lalpha, ralpha, 0B, 0B))
print()

do_chao(msg, lalpha, ralpha, 1B, 1B)
Output:
L: HXUCZVAMDSLKPEFJRIGTWOBNYQ
R: PTLNBQDEOYSFAVZKGJRIHWXUMC
I: WELLDONEISBETTERTHANWELLSAID
O: OAHQHCNYNXTSZJRRHJBYHQKSOUJY
D: WELLDONEISBETTERTHANWELLSAID

======================================================
          left:                     right: 
======================================================
HXUCZVAMDSLKPEFJRIGTWOBNYQ PTLNBQDEOYSFAVZKGJRIHWXUMC 

ONYQHXUCZVAMDBSLKPEFJRIGTW XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF OBMESWKYZXUCPRTLNHFAGVQDJI
YFJBGMTKWNOQXCHIDVALZRSPUE JIBMESWKYZXUCOPRTLNHFAGVQD

Ada

This solution uses array slices to permute the left and right strings. Use of slices clarifies the looping logic. Ada strings are indexed with the predefined subtype Positive which begins at 1.

with Ada.Text_IO; use Ada.Text_IO;

procedure chao_slices is
   type iMode is (Encrypt, Decrypt);

   L_Alphabet : String := "HXUCZVAMDSLKPEFJRIGTWOBNYQ";
   R_Alphabet : String := "PTLNBQDEOYSFAVZKGJRIHWXUMC";
   plaintext  : String := "WELLDONEISBETTERTHANWELLSAID";
   ciphertext : String (1 .. plaintext'length);
   plaintext2 : String (1 .. plaintext'length);
   offset     : Natural;

   function IndexOf (Source : String; Value : Character) return Positive is
      Result : Positive;

   begin
      for I in Source'Range loop
         if Source (I) = Value then
            Result := I;
            exit;
         end if;
      end loop;
      return Result;
   end IndexOf;

   function Exec
     (Text : String; mode : iMode; showsteps : Boolean := False) return String
   is
      etext : String (Text'First .. Text'Last);
      temp  : String (1 .. 26);
      index : Positive;
      store : Character;
      left  : String := L_Alphabet;
      right : String := R_Alphabet;
   begin
      for I in Text'Range loop
         if showsteps then
            Put_Line (left & "  " & right);
         end if;

         if mode = Encrypt then
            index     := IndexOf (Source => right, Value => Text (I));
            etext (I) := left (index);
         else
            index     := IndexOf (Source => left, Value => Text (I));
            etext (I) := right (index);
         end if;

         exit when I = Text'Last;

         -- permute left
         -- The array value permutations are performed using array slices
         -- rather than explicit loops

         if index > 1 then
            offset                 := 26 - index;
            temp (1 .. offset + 1) := left (index .. index + offset);

            temp (offset + 2 .. 26) := left (1 .. index - 1);
            store                   := temp (2);

            temp (2 .. 13) := temp (3 .. 14);
            temp (14)      := store;
            left           := temp;

            -- permute right
            -- The array value permutations are performed using array slices
            -- rather than explicit loops

            temp (1 .. offset + 1) := right (index .. index + offset);

            temp (offset + 2 .. 26) := right (1 .. index - 1);
            store                   := temp (1);

            temp (1 .. 25) := temp (2 .. 26);
            temp (26)      := store;
            store          := temp (3);

            temp (3 .. 13) := temp (4 .. 14);
            temp (14)      := store;
            right          := temp;
         end if;

      end loop;

      return etext;

   end Exec;
begin
   Put_Line ("The original text is : " & plaintext);
   New_Line;
   Put_Line
     ("The left and right alphabets after each permutation during encryption are:");
   New_Line;
   ciphertext := Exec (plaintext, Encrypt, True);
   New_Line;
   Put_Line ("The ciphertext is : " & ciphertext);
   plaintext2 := Exec (ciphertext, Decrypt);
   New_Line;
   Put_Line ("The recovered plaintext is : " & plaintext2);
end chao_slices;
Output:
The original text is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are:

HXUCZVAMDSLKPEFJRIGTWOBNYQ  PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW  XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV  OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ  NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY  NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE  JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU  YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO  BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP  RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO  MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ  AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM  IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB  RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ  LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF  LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ  RYFWZXUCPTLNIHMSAVKGQDJOBE
RBSKWPEYHUFJIDLGMTONQXCVAZ  RYFWZXUCPTLNIHMSAVKGQDJOBE
UJIDLGMTONQXCFVAZRBSKWPEYH  LNHMSAVKGQDJOIBERYFWZXUCPT
ILGMTONQXCFVADZRBSKWPEYHUJ  MSVKGQDJOIBERAYFWZXUCPTLNH
DRBSKWPEYHUJIZLGMTONQXCFVA  YFZXUCPTLNHMSWVKGQDJOIBERA
HJIZLGMTONQXCUFVADRBSKWPEY  HMWVKGQDJOIBESRAYFZXUCPTLN
ILGMTONQXCUFVZADRBSKWPEYHJ  VKQDJOIBESRAYGFZXUCPTLNHMW
XUFVZADRBSKWPCEYHJILGMTONQ  SRYGFZXUCPTLNAHMWVKQDJOIBE
WCEYHJILGMTONPQXUFVZADRBSK  NAMWVKQDJOIBEHSRYGFZXUCPTL
KCEYHJILGMTONWPQXUFVZADRBS  NAWVKQDJOIBEHMSRYGFZXUCPTL
PXUFVZADRBSKCQEYHJILGMTONW  RYFZXUCPTLNAWGVKQDJOIBEHMS
KQEYHJILGMTONCWPXUFVZADRBS  WGKQDJOIBEHMSVRYFZXUCPTLNA
LMTONCWPXUFVZGADRBSKQEYHJI  BEMSVRYFZXUCPHTLNAWGKQDJOI

The ciphertext is : OAHQHCNYNXTSZJRRUIDHIXWKPKLY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

AppleScript

-- Chaocipher algorithm by J.F.Byrne 1918.
on chaocipher(input, |key|, mode)
    -- input: text to be enciphered or deciphered.
    -- |key|: script object or record with leftAlpha and rightAlpha properties, each of whose values is a shuffled alphabet text.
    -- mode: the text "encipher" or "decipher".
    script o
        property inputChars : input's characters
        property leftAlpha : |key|'s leftAlpha's characters
        property rightAlpha : |key|'s rightAlpha's characters
        property inAlpha : leftAlpha
        property outAlpha : rightAlpha
        property output : {}
    end script
    
    set alphaLen to (count o's leftAlpha)
    if ((count o's rightAlpha)  alphaLen) then error
    if (mode is "encipher") then
        set {o's inAlpha, o's outAlpha} to {o's rightAlpha, o's leftAlpha}
    else if (mode is not "decipher") then
        error
    end if
    set zenith to 1
    set nadir to alphaLen div 2 + 1
    repeat with char in o's inputChars
        set char to char's contents
        set found to false
        repeat with i from 1 to alphaLen
            if (o's inAlpha's item i = char) then
                set end of o's output to o's outAlpha's item i
                set found to true
                exit repeat
            end if
        end repeat
        if (found) then
            rotate(o's leftAlpha, zenith, alphaLen, -(i - zenith))
            rotate(o's leftAlpha, zenith + 1, nadir, -1)
            rotate(o's rightAlpha, zenith, alphaLen, -i)
            rotate(o's rightAlpha, zenith + 2, nadir, -1)
        end if
    end repeat
    
    return join(o's output, "")
end chaocipher

on rotate(theList, l, r, amount)
    set listLength to (count theList)
    if (listLength < 2) then return
    if (l < 0) then set l to listLength + l + 1
    if (r < 0) then set r to listLength + r + 1
    if (l > r) then set {l, r} to {r, l}
    script o
        property lst : theList
        property storage : missing value
    end script
    
    set rangeLength to r - l + 1
    set amount to (rangeLength + rangeLength - amount) mod rangeLength
    if (amount is 0) then return
    set o's storage to o's lst's items l thru (l + amount - 1)
    repeat with i from (l + amount) to r
        set o's lst's item (i - amount) to o's lst's item i
    end repeat
    set j to r - amount
    repeat with i from 1 to amount
        set o's lst's item (j + i) to o's storage's item i
    end repeat
end rotate

on join(lst, delim)
    set astid to AppleScript's text item delimiters
    set AppleScript's text item delimiters to delim
    set txt to lst as text
    set AppleScript's text item delimiters to astid
    return txt
end join

-- Return a script object containing a couple of randomised alphabets to use as a choacipher key.
on makeKey()
    set lAlpha to "ABCDEFGHIJKLMNOPQRSTUVWXYZ"'s characters
    copy lAlpha to rAlpha
    script |key|
        property leftAlpha : join(shuffle(lAlpha, 1, -1), "")
        property rightAlpha : join(shuffle(rAlpha, 1, -1), "")
    end script
    
    return |key|
end makeKey

-- Fisher-Yates (aka Durstenfeld, aka Knuth) shuffle.
on shuffle(theList, l, r)
    set listLength to (count theList)
    if (listLength < 2) then return array
    if (l < 0) then set l to listLength + l + 1
    if (r < 0) then set r to listLength + r + 1
    if (l > r) then set {l, r} to {r, l}
    script o
        property lst : theList
    end script
    
    repeat with i from l to (r - 1)
        set j to (random number from i to r)
        set v to o's lst's item i
        set o's lst's item i to o's lst's item j
        set o's lst's item j to v
    end repeat
    
    return theList
end shuffle

-- Demo using the two-alphabet key from the Rubin paper and another generated at random.
-- Decription must be with the key that was used for the encription.
on demo(originalText)
    set key1 to {leftAlpha:"HXUCZVAMDSLKPEFJRIGTWOBNYQ", rightAlpha:"PTLNBQDEOYSFAVZKGJRIHWXUMC"}
    set key2 to makeKey()
    set enciphered to chaocipher(originalText, key1, "encipher")
    set doubleEnciphered to chaocipher(enciphered, key2, "encipher")
    set deDoubleEnciphered to chaocipher(doubleEnciphered, key2, "decipher")
    set deciphered to chaocipher(deDoubleEnciphered, key1, "decipher")
    return join({"Original text = " & originalText, ¬
        "Enciphered = " & enciphered, "Double enciphered = " & doubleEnciphered, ¬
        "De-double enciphered = " & deDoubleEnciphered, "Deciphered  = " & deciphered}, linefeed)
end demo
demo("WELLDONEISBETTERTHANWELLSAID")
Output:
"Original text = WELLDONEISBETTERTHANWELLSAID
Enciphered = OAHQHCNYNXTSZJRRHJBYHQKSOUJY
Double enciphered = ZJVDGIXNNDNRHAXQUUJZGAFTANHW
De-double enciphered = OAHQHCNYNXTSZJRRHJBYHQKSOUJY
Deciphered  = WELLDONEISBETTERTHANWELLSAID"

Arc

(= lshift '((0 1) (2 14) (1 2) (14 26)))
(= rshift '((1 3) (4 15) (3 4) (15 26) (0 1)))

(= rot (fn (alpha shift)
  (let shift (mod shift 26)
    (string (cut alpha shift) (cut alpha 0 shift)))))

(= scramble-wheel (fn (alpha moves)
  (= oput '())
  (up i 0 (- (len moves) 1)
      (push (cut alpha ((moves i) 0) ((moves i) 1)) oput))
  (= oput (string (rev oput)))))

(= chaocipher (fn (left right msg (o crypted) (o dec?))
  (unless crypted
    (prn "Encoding " msg " with chaocipher")
    (prn left " " right))
  (when dec? (swap left right))
    (= offset ((positions (msg 0) right) 0))
    (= left (rot left offset))
    (= right (rot right offset))
    (push (cut left 0 1) crypted)
  (when dec? (swap left right))
  (prn (scramble-wheel left lshift)
   " " (scramble-wheel right rshift))
  (if (> (len msg) 1)
      (chaocipher (scramble-wheel left lshift)
             (scramble-wheel right rshift)
                  (cut msg 1) crypted dec?)
      (string (rev crypted)))))

(chaocipher "HXUCZVAMDSLKPEFJRIGTWOBNYQ" "PTLNBQDEOYSFAVZKGJRIHWXUMC"
            "WELLDONEISBETTERTHANWELLSAID")
(chaocipher "HXUCZVAMDSLKPEFJRIGTWOBNYQ" "PTLNBQDEOYSFAVZKGJRIHWXUMC"
            "OAHQHCNYNXTSZJRRHJBYHQKSOUJY" nil 1)
Output:
arc> (chaocipher "HXUCZVAMDSLKPEFJRIGTWOBNYQ" "PTLNBQDEOYSFAVZKGJRIHWXUMC"
                 "WELLDONEISBETTERTHANWELLSAID")
Encoding WELLDONEISBETTERTHANWELLSAID with chaocipher
HXUCZVAMDSLKPEFJRIGTWOBNYQ PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF OBMESWKYZXUCPRTLNHFAGVQDJI
YFJBGMTKWNOQXCHIDVALZRSPUE JIBMESWKYZXUCOPRTLNHFAGVQD
"OAHQHCNYNXTSZJRRHJBYHQKSOUJY"

AutoHotkey

LeftW := "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
RghtW := "PTLNBQDEOYSFAVZKGJRIHWXUMC"

PlainText    := "WELLDONEISBETTERTHANWELLSAID"
CipherText    := Chao_Cipher(PlainText, LeftW, RghtW)     ; "OAHQHCNYNXTSZJRRHJBYHQKSOUJY"
DecipherText:= Chao_Decipher(CipherText, LeftW, RghtW)    ; "WELLDONEISBETTERTHANWELLSAID"

MsgBox % Result := "Original text:`t" PlainText "`nCipher text:`t" CipherText "`nDecipher text:`t" DecipherText
return
;-------------------------------------------
Chao_Cipher(PT, LeftW, RghtW){
    oRght:=StrSplit(RghtW), oLeft:=StrSplit(LeftW)
    for i, p in StrSplit(PT){
        result .= (c := Key2Val(oRght, oLeft, p))
        oLeft:=Permute(oLeft, c, 1)
        oRght:=Permute(oRght, p)
    }
    return result
}
;-------------------------------------------
Chao_Decipher(CT, LeftW, RghtW){
    oRght:=StrSplit(RghtW), oLeft:=StrSplit(LeftW)
    for i, c in StrSplit(CT){
        result .= (p := Key2Val(oLeft, oRght, c))
        oLeft:=Permute(oLeft, c, 1)
        oRght:=Permute(oRght, p)
    }    
    return result
}
;-------------------------------------------
Key2Val(Key, Val, char){
    for i, ch in Key
        if (ch = char)
            return Val[i]
}
;-------------------------------------------
Permute(Arr, ch, dt:=0){
    for i, c in Arr
        if (c=ch)
            break
    loop % i-dt
        Arr.Push(Arr.RemoveAt(1))               ; shift left
    ch := Arr[3-dt]                             ; save 2nd/3rd chr
    loop % 11+dt
        Arr[A_Index+2-dt]:=Arr[A_Index+3-dt]    ; shift pos 3/4-14 left
    Arr[14] := ch                               ; place 2nd/3rd chr in pos 14
    return Arr
}
Output:
Original text:	WELLDONEISBETTERTHANWELLSAID
Cipher text:	OAHQHCNYNXTSZJRRHJBYHQKSOUJY
Decipher text:	WELLDONEISBETTERTHANWELLSAID

BASIC

Works with: FreeBasic
Works with: PowerBasic
' Caocipher Example
' Rosetta Code
' This code was made in Power Basic 3.5 for DOS

CLS

' Left Alphabet
Function AlphaLeft(ct as String, pt as String, CharPos as Integer) as String

  Dim tStr as String: tStr=ct

  ' 1. Shift the entire left alphabet cyclically so the ciphertext letter
  ' just enciphered is positioned at the zenith (i.e., position 1).
  tStr=Right$(ct, Len(ct)-CharPos+1)+Left$(ct, CharPos-1)

  ' 2. Extract the letter found at position zenith+1 (i.e., the letter to
  ' the right of the zenith), taking it out of the alphabet, temporarily
  ' leaving an unfilled "hole"

  Dim Hole as String: Hole=Mid$(tStr, 2, 1): Mid$(tStr, 2, 1)=" "

  ' 3. Shift all letters in positions zenith+2 up to, and including, the
  ' nadir (zenith+13), moving them one position to the left

  tStr=Left$(tStr, 1)+Mid$(tStr, 3, 12)+" "+Right$(tStr, 12)

  ' 4. Insert the just-extracted letter into the nadir position
  ' (i.e., zenith+13)

  Mid$(tStr, 14, 1)=Hole

  AlphaLeft=tStr
End Function

' Right Alphabet
Function AlphaRight(ct as String, pt as String, CharPos as Integer) as String

  Dim tStr as String: tStr=pt

  ' 1. Shift the entire right alphabet cyclically so the plaintext letter
  ' just enciphered is positioned at the zenith.

  tStr=Right$(tStr, Len(tStr)-CharPos+1)+Left$(tStr, CharPos-1)

  ' 2. Now shift the entire alphabet one more position to the left (i.e.,
  ' the leftmost letter moves cyclically to the far right), moving a new
  ' letter into the zenith position.

  tStr=Right$(tStr, 25)+Left$(tStr, 1)

  ' 3. Extract the letter at position zenith+2, taking it out of the
  ' alphabet, temporarily leaving an unfilled "hole".

  Dim Hole as String: Hole=Mid$(tStr, 3, 1): Mid$(tStr, 3, 1)=" ":

  ' 4. Shift all letters beginning with zenith+3 up to, and including, the
  ' nadir (zenith+13), moving them one position to the left.

  tStr=Left$(tStr, 2)+Mid$(tStr, 4, 11)+" "+Right$(tStr, 12)

  ' 5. Insert the just-extracted letter into the nadir position (zenith+13)

  Mid$(tStr, 14, 1)=Hole

  AlphaRight=tStr
End Function

Function Encode(Text as String, ct as String, pt as String) as String
  Dim t as Integer
  Dim tStr as String: tStr=""

  For t=1 to Len(Text)
    Dim Char as String: Char=Mid$(Text, t, 1)
    Dim CharPos as Integer: CharPos=Instr(pt, Char)

    ct=AlphaLeft(ct, pt, CharPos)
    pt=AlphaRight(ct, pt, CharPos)

    tStr=tStr+Left$(ct, 1)
  Next

  Encode=tStr
End Function

' Deciphering a Chaocipher-encrypted message is identical to the steps used
' for enciphering. The sole difference is that the decipherer locates the
' known ciphertext letter in the left (ct) alphabet, with the plaintext
' letter being the corresponding letter in the right (pt) alphabet
'
' Alphabet permuting is identical in enciphering and deciphering

Function Decode(Text as String, ct as String, pt as String) as String
  Dim t as Integer
  Dim tStr as String: tStr=""

  For t=1 to Len(Text)
    Dim Char as String: Char=Mid$(Text, t, 1)
    Dim CharPos as Integer: CharPos=Instr(ct, Char)

    ct=AlphaLeft(ct, pt, CharPos)
    pt=AlphaRight(ct, pt, CharPos)

    tStr=tStr+Right$(pt, 1)
  Next

  Decode=tStr
End Function

' Start of Main Code

' LEFT (Cipher Text): HXUCZVAMDSLKPEFJRIGTWOBNYQ
Dim tLeft as String: tLeft="HXUCZVAMDSLKPEFJRIGTWOBNYQ"

' RIGHT (Plain Text): PTLNBQDEOYSFAVZKGJRIHWXUMC
Dim tRight as String: tRight="PTLNBQDEOYSFAVZKGJRIHWXUMC"

' Cipher Message (Used to verify a good encoding)
Dim cText as String: cText="OAHQHCNYNXTSZJRRHJBYHQKSOUJY"

' Plain Text Message
Dim pText as String: pText="WELLDONEISBETTERTHANWELLSAID"
Print " Plain  Text: "; pText: Print

Dim ctLeft as String: ctLeft=tLeft
Dim ptRight as String: ptRight=tRight

' Final Cipher Text
Dim eText as String: eText=Encode(pText, ctLeft, ptRight)
Print " Cipher Text: "; eText: Print

If eText=cText then Print "Successful" else Print "Failed"

ctLeft=tLeft: ptRight=tRight
Dim dText as String: dText=Decode(eText, ctLeft, ptRight)
Print: Print " Plain Text: "; dText: Print

If dText=pText then Print "Successful" else Print "Failed"
Plain Text:	WELLDONEISBETTERTHANWELLSAID
Cipher text:	OAHQHCNYNXTSZJRRHJBYHQKSOUJY
Successful
Plain Text:	WELLDONEISBETTERTHANWELLSAID
Successful

C

Translation of: Kotlin
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define TRUE 1
#define FALSE 0

typedef int bool;
typedef enum { ENCRYPT, DECRYPT } cmode;

const char *l_alphabet = "HXUCZVAMDSLKPEFJRIGTWOBNYQ";
const char *r_alphabet = "PTLNBQDEOYSFAVZKGJRIHWXUMC";

void chao(const char *in, char *out, cmode mode, bool show_steps) {
    int i, j, index;
    char store;
    size_t len = strlen(in);
    char left[27], right[27], temp[27];
    strcpy(left, l_alphabet);
    strcpy(right, r_alphabet);
    temp[26] = '\0';

    for (i = 0; i < len; ++i ) {
        if (show_steps) printf("%s  %s\n", left, right);
        if (mode == ENCRYPT) {
            index = strchr(right, in[i]) - right;
            out[i] = left[index];
        }
        else {
            index = strchr(left, in[i]) - left;
            out[i] = right[index];
        }
        if (i == len - 1) break;

        /* permute left */

        for (j = index; j < 26; ++j) temp[j - index] = left[j];
        for (j = 0; j < index; ++j) temp[26 - index + j] = left[j];
        store = temp[1];
        for (j = 2; j < 14; ++j) temp[j - 1] = temp[j];
        temp[13] = store;
        strcpy(left, temp);

        /* permute right */

        for (j = index; j < 26; ++j) temp[j - index] = right[j];
        for (j = 0; j < index; ++j) temp[26 - index + j] = right[j];
        store = temp[0];
        for (j = 1; j < 26; ++j) temp[j - 1] = temp[j];
        temp[25] = store;
        store = temp[2];
        for (j = 3; j < 14; ++j) temp[j - 1] = temp[j];
        temp[13] = store;
        strcpy(right, temp);
    }
}

int main() {
    const char *plain_text = "WELLDONEISBETTERTHANWELLSAID";
    char *cipher_text = malloc(strlen(plain_text) + 1);
    char *plain_text2 = malloc(strlen(plain_text) + 1);
    printf("The original plaintext is : %s\n", plain_text);
    printf("\nThe left and right alphabets after each permutation"
           " during encryption are :\n\n");
    chao(plain_text, cipher_text, ENCRYPT, TRUE);
    printf("\nThe ciphertext is : %s\n", cipher_text);
    chao(cipher_text, plain_text2, DECRYPT, FALSE);
    printf("\nThe recovered plaintext is : %s\n", plain_text2);
    free(cipher_text);
    free(plain_text2);
    return 0;
}
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are :

HXUCZVAMDSLKPEFJRIGTWOBNYQ  PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW  XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV  OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ  NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY  NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE  JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU  YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO  BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP  RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO  MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ  AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM  IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB  RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ  LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF  LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ  RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ  YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY  LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF  MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD  VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE  HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX  RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON  SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS  NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR  NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW  WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP  GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF  OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

C#

Translation of: D
using System;

namespace Chaocipher {
    enum Mode {
        ENCRYPT,
        DECRYPT,
    }

    class Program {
        const string L_ALPHABET = "HXUCZVAMDSLKPEFJRIGTWOBNYQ";
        const string R_ALPHABET = "PTLNBQDEOYSFAVZKGJRIHWXUMC";

        static string Exec(string text, Mode mode, bool showSteps = false) {
            char[] left = L_ALPHABET.ToCharArray();
            char[] right = R_ALPHABET.ToCharArray();
            char[] eText = new char[text.Length];
            char[] temp = new char[26];

            for (int i = 0; i < text.Length; ++i) {
                if (showSteps) Console.WriteLine("{0} {1}", string.Join("", left), string.Join("", right));
                int index = 0;
                if (mode == Mode.ENCRYPT) {
                    index = Array.IndexOf(right, text[i]);
                    eText[i] = left[index];
                } else {
                    index = Array.IndexOf(left, text[i]);
                    eText[i] = right[index];
                }
                if (i == text.Length - 1) break;

                // permute left

                for (int j = index; j < 26; ++j) temp[j - index] = left[j];
                for (int j = 0; j < index; ++j) temp[26 - index + j] = left[j];
                var store = temp[1];
                for (int j = 2; j < 14; ++j) temp[j - 1] = temp[j];
                temp[13] = store;
                temp.CopyTo(left, 0);

                // permute right

                for (int j = index; j < 26; ++j) temp[j - index] = right[j];
                for (int j = 0; j < index; ++j) temp[26 - index + j] = right[j];
                store = temp[0];
                for (int j = 1; j < 26; ++j) temp[j - 1] = temp[j];
                temp[25] = store;
                store = temp[2];
                for (int j = 3; j < 14; ++j) temp[j - 1] = temp[j];
                temp[13] = store;
                temp.CopyTo(right, 0);
            }

            return new string(eText);
        }

        static void Main(string[] args) {
            var plainText = "WELLDONEISBETTERTHANWELLSAID";
            Console.WriteLine("The original plaintext is : {0}", plainText);
            Console.WriteLine("\nThe left and right alphabets after each permutation during encryption are :\n");
            var cipherText = Exec(plainText, Mode.ENCRYPT, true);
            Console.WriteLine("\nThe ciphertext is : {0}", cipherText);
            var plainText2 = Exec(cipherText, Mode.DECRYPT);
            Console.WriteLine("\nThe recovered plaintext is : {0}", plainText2);
        }
    }
}
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are :

HXUCZVAMDSLKPEFJRIGTWOBNYQ PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

C++

Translation of: C#
#include <iostream>

enum class Mode {
    ENCRYPT,
    DECRYPT,
};

const std::string L_ALPHABET = "HXUCZVAMDSLKPEFJRIGTWOBNYQ";
const std::string R_ALPHABET = "PTLNBQDEOYSFAVZKGJRIHWXUMC";

std::string exec(std::string text, Mode mode, bool showSteps = false) {
    auto left = L_ALPHABET;
    auto right = R_ALPHABET;
    auto eText = new char[text.size() + 1];
    auto temp = new char[27];

    memset(eText, 0, text.size() + 1);
    memset(temp, 0, 27);

    for (size_t i = 0; i < text.size(); i++) {
        if (showSteps) std::cout << left << ' ' << right << '\n';
        size_t index;
        if (mode == Mode::ENCRYPT) {
            index = right.find(text[i]);
            eText[i] = left[index];
        } else {
            index = left.find(text[i]);
            eText[i] = right[index];
        }
        if (i == text.size() - 1) break;

        // permute left

        for (int j = index; j < 26; ++j) temp[j - index] = left[j];
        for (int j = 0; j < index; ++j) temp[26 - index + j] = left[j];
        auto store = temp[1];
        for (int j = 2; j < 14; ++j) temp[j - 1] = temp[j];
        temp[13] = store;
        left = temp;

        // permurte right

        for (int j = index; j < 26; ++j) temp[j - index] = right[j];
        for (int j = 0; j < index; ++j) temp[26 - index + j] = right[j];
        store = temp[0];
        for (int j = 1; j < 26; ++j) temp[j - 1] = temp[j];
        temp[25] = store;
        store = temp[2];
        for (int j = 3; j < 14; ++j) temp[j - 1] = temp[j];
        temp[13] = store;
        right = temp;
    }

    return eText;
}

int main() {
    auto plainText = "WELLDONEISBETTERTHANWELLSAID";
    std::cout << "The original plaintext is : " << plainText << "\n\n";
    std::cout << "The left and right alphabets after each permutation during encryption are :\n";
    auto cipherText = exec(plainText, Mode::ENCRYPT, true);
    std::cout << "\nThe ciphertext is : " << cipherText << '\n';
    auto plainText2 = exec(cipherText, Mode::DECRYPT);
    std::cout << "\nThe recovered plaintext is : " << plainText2 << '\n';

    return 0;
}
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are :
HXUCZVAMDSLKPEFJRIGTWOBNYQ PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

D

Translation of: Kotlin
import std.stdio;
import std.string;

immutable L_ALPHABET = "HXUCZVAMDSLKPEFJRIGTWOBNYQ";
immutable R_ALPHABET = "PTLNBQDEOYSFAVZKGJRIHWXUMC";

enum Mode {
    ENCRYPT,
    DECRYPT,
}

string exec(string text, Mode mode, bool showSteps = false) {
    char[] left = L_ALPHABET.dup;
    char[] right = R_ALPHABET.dup;
    char[] eText;
    eText.length = text.length;
    char[26] temp;

    foreach (i; 0..text.length) {
        if (showSteps) writeln(left, ' ', right);
        int index;
        if (mode == Mode.ENCRYPT) {
            index = right.indexOf(text[i]);
            eText[i] = left[index];
        } else {
            index = left.indexOf(text[i]);
            eText[i] = right[index];
        }
        if (i == text.length - 1) break;

        // permute left

        foreach (j; index..26) temp[j - index] = left[j];
        foreach (j; 0..index) temp[26 - index + j] = left[j];
        auto store = temp[1];
        foreach (j; 2..14) temp[j - 1] = temp[j];
        temp[13] = store;
        left = temp.dup;

        // permute right

        foreach (j; index..26) temp[j - index] = right[j];
        foreach (j; 0..index) temp[26 - index + j] = right[j];
        store = temp[0];
        foreach (j; 1..26) temp[j - 1] = temp[j];
        temp[25] = store;
        store = temp[2];
        foreach (j; 3..14) temp[j - 1] = temp[j];
        temp[13] = store;
        right = temp.dup;
    }

    return eText.idup;
}

void main() {
    auto plainText = "WELLDONEISBETTERTHANWELLSAID";
    writeln("The original plaintext is : ", plainText);
    writeln("\nThe left and right alphabets after each permutation during encryption are :\n");
    auto cipherText = exec(plainText, Mode.ENCRYPT, true);
    writeln("\nThe ciphertext is : ", cipherText);
    auto plainText2 = exec(cipherText, Mode.DECRYPT);
    writeln("\nThe recovered plaintext is : ", plainText2);
}
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are :

HXUCZVAMDSLKPEFJRIGTWOBNYQ PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

Delphi

Translation of: Kotlin
program Chaocipher;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

type
  TMode = (mcEncrypt, mcDecrypt);

const
  lAlphabet = 'HXUCZVAMDSLKPEFJRIGTWOBNYQ';
  rAlphabet = 'PTLNBQDEOYSFAVZKGJRIHWXUMC';

function Chao(text: AnsiString; Mode: TMode; showSteps: boolean): AnsiString;
begin
  var len := Length(text);

  var left: AnsiString := lAlphabet;
  var right: AnsiString := rAlphabet;

  var eText: AnsiString;
  SetLength(eText, len);
  var temp: AnsiString;
  SetLength(temp, 26);

  for var i := 0 to len - 1 do
  begin
    if showSteps then
      writeln(left, ' ', right);

    var index := 0;

    if Mode = mcEncrypt then
    begin
      index := pos(text[i + 1], right) - 1;
      eText[i + 1] := left[index + 1];
    end
    else
    begin
      index := pos(text[i + 1], left) - 1;
      eText[i + 1] := right[index + 1];
    end;

    if i = len - 1 then
      Break;

    // premute left
    for var j := index to 25 do
      temp[j - index + 1] := left[j + 1];

    for var j := 0 to index - 1 do
      temp[27 - index + j] := left[j + 1];
    var store := temp[2];

    for var j := 2 to 13 do
      temp[j] := temp[j + 1];

    temp[14] := store;

    left := temp;

    // permute right
    for var j := index to 25 do
      temp[j - index + 1] := right[j + 1];

    for var j := 0 to index - 1 do
      temp[27 - index + j] := right[j + 1];

    store := temp[0 + 1];

    for var j := 1 to 25 do
      temp[j] := temp[j + 1];

    temp[26] := store;
    store := temp[3];

    for var j := 3 to 13 do
      temp[j] := temp[j + 1];

    temp[14] := store;

    right := temp;
  end;
  Result := eText;
end;

begin
  var plainText := 'WELLDONEISBETTERTHANWELLSAID';
  writeln('The original plaintext is :', plainText);
  write(#10'The left and right alphabets after each permutation ');
  writeln('during encryption are :'#10);
  var cipherText := Chao(plainText, mcEncrypt, true);
  writeln(#10'The ciphertext is :', cipherText);
  var plainText2 := Chao(cipherText, mcDecrypt, false);
  writeln(#10'The recovered plaintext is : ', plainText2);
  readln;
end.

EasyLang

proc index c$ . a$[] ind .
   for ind = 1 to len a$[]
      if a$[ind] = c$
         return
      .
   .
   ind = 0
.
left$ = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
right$ = "PTLNBQDEOYSFAVZKGJRIHWXUMC"
# 
func$ chao txt$ mode .
   left$[] = strchars left$
   right$[] = strchars right$
   len tmp$[] 26
   for c$ in strchars txt$
      # print strjoin left$[] & " " & strjoin right$[]
      if mode = 1
         index c$ right$[] ind
         if ind = 0
            return ""
         .
         r$ &= left$[ind]
      else
         index c$ left$[] ind
         if ind = 0
            print c$
            return ""
         .
         r$ &= right$[ind]
      .
      # permute left
      for j = ind to 26
         tmp$[j - ind + 1] = left$[j]
      .
      for j = 1 to ind - 1
         tmp$[26 - ind + j + 1] = left$[j]
      .
      h$ = tmp$[2]
      for j = 3 to 14
         tmp$[j - 1] = tmp$[j]
      .
      tmp$[14] = h$
      swap tmp$[] left$[]
      # 
      # permute right
      for j = ind to 26
         tmp$[j - ind + 1] = right$[j]
      .
      for j = 1 to ind - 1
         tmp$[26 - ind + j + 1] = right$[j]
      .
      h$ = tmp$[1]
      for j = 2 to 26
         tmp$[j - 1] = tmp$[j]
      .
      tmp$[26] = h$
      h$ = tmp$[3]
      for j = 4 to 14
         tmp$[j - 1] = tmp$[j]
      .
      tmp$[14] = h$
      swap tmp$[] right$[]
   .
   return r$
.
h$ = chao "WELLDONEISBETTERTHANWELLSAID" 1
print h$
print chao h$ 2
Output:
OAHQHCNYNXTSZJRRHJBYHQKSOUJY
WELLDONEISBETTERTHANWELLSAID

EMal

Translation of: C#
type Chaocipher:Mode
enum
  int ENCRYPT, DECRYPT
end
type Chaocipher
text L_ALPHABET = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
text R_ALPHABET = "PTLNBQDEOYSFAVZKGJRIHWXUMC"
fun exec = text by text value, Chaocipher:Mode mode, logic showSteps 
  ^|since texts are mutable, we can operate directly on them without the need of Lists|^
  text left = *L_ALPHABET # by using the valueOf operator we are sure that the string is copied
  text right = *R_ALPHABET
  text eText = text(" ", value.length)
  text temp = text(" ", 26)
  for int i = 0; i < value.length; ++i
    if showSteps do writeLine(left + " " + right) end
    int index = 0
    if mode == Chaocipher:Mode.ENCRYPT
      index = right.find(value[i])
      eText[i] = left[index]
    else
      index = left.find(value[i])
      eText[i] = right[index]
    end
    if i == value.length - 1 do break end
    # permute left
    for int j = index; j < 26; ++j do temp[j - index] = left[j] end
    for int j = 0; j < index; ++j do temp[26 - index + j] = left[j] end
    var store = temp[1]
    for int j = 2; j < 14; ++j do temp[j - 1] = temp[j] end
    temp[13] = store
	left = *temp
    # permute right
    for int j = index; j < 26; ++j do temp[j - index] = right[j] end
    for int j = 0; j < index; ++j do temp[26 - index + j] = right[j] end
    store = temp[0]
    for int j = 1; j < 26; ++j do temp[j - 1] = temp[j] end
    temp[25] = store
    store = temp[2]
    for int j = 3; j < 14; ++j do temp[j - 1] = temp[j] end
    temp[13] = store
	right = *temp
  end
  return eText
end 
var plainText = "WELLDONEISBETTERTHANWELLSAID"
writeLine("The original plaintext is : " + plainText)
writeLine(EOL + "The left and right alphabets after each permutation during encryption are :" + EOL)
var cipherText = exec(plainText, Chaocipher:Mode.ENCRYPT, true)
writeLine(EOL + "The ciphertext is : " + cipherText)
var plainText2 = exec(cipherText, Chaocipher:Mode.DECRYPT, false)
writeLine(EOL + "The recovered plaintext is : " + plainText2)
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are :

HXUCZVAMDSLKPEFJRIGTWOBNYQ PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

F#

The Functions

// Implement Chaocipher. Nigel Galloway: July 13th., 2019
let pL n=function g when g=n->0 |g when g=(n+1)%26->13 |g->let x=(25+g-n)%26 in if x<13 then x else x+1
let pR n=function g when g=n->25 |g when g=(n+3)%26->13 |g when g=(n+1)%26->0 |g when g=(n+2)%26->1 |g->let x=(24+g-n)%26 in if x<13 then x else x+1
let encrypt lW rW txt=Array.scan(fun (lW,rW) t->let n=Array.findIndex(fun n->n=t) rW in ((Array.permute(pL n) lW,(Array.permute(pR n) rW))))(lW,rW) txt
                        |>Array.skip 1|>Array.map(fun(n,_)->n.[0])|>System.String
let decrypt lW rW txt=Array.scan(fun (_,lW,rW) t->let n=Array.findIndex(fun n->n=t) lW in ((Array.item n rW,Array.permute(pL n) lW,(Array.permute(pR n) rW))))('0',lW,rW) txt
                        |>Array.skip 1|>Array.map(fun(n,_,_)->n)|>System.String

The Task

printfn "%s" (encrypt ("HXUCZVAMDSLKPEFJRIGTWOBNYQ".ToCharArray()) ("PTLNBQDEOYSFAVZKGJRIHWXUMC".ToCharArray()) ("WELLDONEISBETTERTHANWELLSAID".ToCharArray()))
printfn "%s" (decrypt ("HXUCZVAMDSLKPEFJRIGTWOBNYQ".ToCharArray()) ("PTLNBQDEOYSFAVZKGJRIHWXUMC".ToCharArray()) ("OAHQHCNYNXTSZJRRHJBYHQKSOUJY".ToCharArray()))
Output:
OAHQHCNYNXTSZJRRHJBYHQKSOUJY
WELLDONEISBETTERTHANWELLSAID

Factor

USING: arrays combinators fry io kernel locals math namespaces
prettyprint sequences sequences.extras strings ;
IN: rosetta-code.chaocipher

CONSTANT: zenith 0
CONSTANT: nadir  13

SYMBOLS: l-alphabet r-alphabet last-index ;

: init-alphabets ( -- )
    "HXUCZVAMDSLKPEFJRIGTWOBNYQ" l-alphabet
    "PTLNBQDEOYSFAVZKGJRIHWXUMC" r-alphabet [ set ] 2bi@ ;
    
: zero-alphabet ( seq -- seq' )
    last-index get rotate ;
    
: 3append ( a b c d -- abcd )
    append append append ;
    
:: permute-l-alphabet ( -- )
    l-alphabet get zero-alphabet dup
    zenith 1 + swap nth :> extracted-char
    {
        [ 1 head ]
        [ nadir 1 + head 2 tail ]
        [ drop extracted-char 1string ]
        [ nadir 1 + tail ]
    } cleave
    3append l-alphabet set ;
      
:: permute-r-alphabet ( -- )
    r-alphabet get zero-alphabet
    1 rotate dup
    zenith 2 + swap nth :> extracted-char
    {
        [ 2 head ]
        [ nadir 1 + head 3 tail ]
        [ drop extracted-char 1string ]
        [ nadir 1 + tail ]
    } cleave
    3append r-alphabet set ;
      
: encipher-char ( char alpha1 alpha2 -- char' )
    '[ _ get index dup last-index set _ get nth ] call ;
    
: encipher ( str quot -- str' )
    [ permute-l-alphabet permute-r-alphabet ] compose map
    init-alphabets ; inline
    
: encrypt ( str -- str' )
    [ r-alphabet l-alphabet encipher-char ] encipher ;
    
: decrypt ( str -- str' )
    [ l-alphabet r-alphabet encipher-char ] encipher ;

: main ( -- )
    init-alphabets
    "WELLDONEISBETTERTHANWELLSAID" encrypt dup decrypt
    [ print ] bi@ ;
    
MAIN: main
Output:
OAHQHCNYNXTSZJRRHJBYHQKSOUJY
WELLDONEISBETTERTHANWELLSAID

Fōrmulæ

Fōrmulæ programs are not textual, visualization/edition of programs is done showing/manipulating structures but not text. Moreover, there can be multiple visual representations of the same program. Even though it is possible to have textual representation —i.e. XML, JSON— they are intended for storage and transfer purposes more than visualization and edition.

Programs in Fōrmulæ are created/edited online in its website.

In this page you can see and run the program(s) related to this task and their results. You can also change either the programs or the parameters they are called with, for experimentation, but remember that these programs were created with the main purpose of showing a clear solution of the task, and they generally lack any kind of validation.

Solution

Test 1. Encryption

Test 2. Decryption

FreeBASIC

The BASIC solution works without any changes.

FutureBasic

begin enum
  _encrypt
  _decrypt
end enum

local fn chaocipher(orig as str255, action as byte, show as bool) as str255
  str255 leftStr, rightStr, out
  short  i, index
  leftStr  = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
  rightStr = "PTLNBQDEOYSFAVZKGJRIHWXUMC"
  orig     = ucase$(orig)
  out[0]   = orig[0]
  
  if show then print:print,"The left and right alphabets during encryption are:":print
  
  for i = 1 to orig[0]
    if show then print ,leftStr,,rightStr
    if action == _encrypt
      index  = instr$(0, rightStr, mid$(orig, i, 1))
      out[i] = leftStr[index]
    else
      index  = instr$(0, leftStr,  mid$(orig, i, 1))
      out[i] = rightStr[index]
    end if
    
    //leftStr permutation
    leftStr  = mid$(leftStr, index) + left$(leftStr, index-1)
    leftStr  = left$(leftStr, 1) + mid$(leftStr, 3, 12) + mid$(leftStr, 2, 1) + mid$(leftStr, 15)
    
    //rightStr permutation
    rightStr = mid$(rightStr, index+1) + left$(rightStr, index-1) + mid$(rightStr, index, 1)
    rightStr = left$(rightStr, 2) + mid$(rightStr, 4, 11) + mid$(rightStr, 3, 1) + mid$(rightStr, 15)
  next
  
end fn = out


str255 original, encrypted, decrypted
original = "WellDoneIsBetterThanWellSaid"

window 1, @"Chaocipher", ( 0, 0, 475, 550 )
print : print ,"The original text is: """;  original;  """"
encrypted = fn chaocipher(original, _encrypt, yes)
print : print ,"The encrypted text is: """; encrypted; """"
decrypted = fn chaocipher(encrypted, _decrypt, no)
print : print ,"The decrypted text is: """; decrypted; """"
handleevents
Output:


Go

Translation of: Kotlin
package main

import(
    "fmt"
    "strings"
    "unicode/utf8"
)

type Mode int

const(
    Encrypt Mode = iota
    Decrypt
)

const(
    lAlphabet = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
    rAlphabet = "PTLNBQDEOYSFAVZKGJRIHWXUMC"
)

func Chao(text string, mode Mode, showSteps bool) string {
    len := len(text)
    if utf8.RuneCountInString(text) != len {
        fmt.Println("Text contains non-ASCII characters")
        return ""
    }
    left  := lAlphabet
    right := rAlphabet
    eText := make([]byte, len)
    temp  := make([]byte, 26)

    for i := 0; i < len; i++ {
        if showSteps {
            fmt.Println(left, " ", right)
        }
        var index int
        if mode == Encrypt {
            index = strings.IndexByte(right, text[i])
            eText[i] = left[index]
        } else {
            index = strings.IndexByte(left, text[i])
            eText[i] = right[index]
        }
        if i == len - 1 {
            break
        }

        // permute left
        for j := index; j < 26; j++ {
            temp[j - index] = left[j]
        }
        for j := 0; j < index; j++ {
            temp[26 - index + j] = left[j]
        }
        store := temp[1]
        for j := 2; j < 14; j++ {
            temp[j - 1] = temp[j]
        }
        temp[13] = store
        left = string(temp[:])

        // permute right

        for j := index; j < 26; j++ {
            temp[j - index] = right[j]
        }
        for j := 0; j < index; j++ {
            temp[26 - index + j] = right[j]
        }
        store = temp[0]
        for j := 1; j < 26; j++ {
            temp[j - 1] = temp[j]
        }
        temp[25] = store
        store = temp[2]
        for j := 3; j < 14; j++ {
            temp[j - 1] = temp[j]
        }
        temp[13] = store
        right = string(temp[:])
    }

    return string(eText[:])
}

func main() {
    plainText := "WELLDONEISBETTERTHANWELLSAID"
    fmt.Println("The original plaintext is :", plainText)
    fmt.Print("\nThe left and right alphabets after each permutation ")
    fmt.Println("during encryption are :\n")
    cipherText := Chao(plainText, Encrypt, true)
    fmt.Println("\nThe ciphertext is :",  cipherText)
    plainText2 := Chao(cipherText, Decrypt, false)
    fmt.Println("\nThe recovered plaintext is :", plainText2)
}
Output:
Same as Kotlin entry.

Groovy

Translation of: Java
class Chaocipher {
    private enum Mode {
        ENCRYPT,
        DECRYPT
    }

    private static final String L_ALPHABET = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
    private static final String R_ALPHABET = "PTLNBQDEOYSFAVZKGJRIHWXUMC"

    private static int indexOf(char[] a, char c) {
        for (int i = 0; i < a.length; ++i) {
            if (a[i] == c) {
                return i
            }
        }
        return -1
    }

    private static String exec(String text, Mode mode) {
        return exec(text, mode, false)
    }

    private static String exec(String text, Mode mode, Boolean showSteps) {
        char[] left = L_ALPHABET.toCharArray()
        char[] right = R_ALPHABET.toCharArray()
        char[] eText = new char[text.length()]
        char[] temp = new char[26]

        for (int i = 0; i < text.length(); ++i) {
            if (showSteps) {
                println("${new String(left)}  ${new String(right)}")
            }
            int index
            if (mode == Mode.ENCRYPT) {
                index = indexOf(right, text.charAt(i))
                eText[i] = left[index]
            } else {
                index = indexOf(left, text.charAt(i))
                eText[i] = right[index]
            }
            if (i == text.length() - 1) {
                break
            }

            // permute left

            if (26 - index >= 0) System.arraycopy(left, index, temp, 0, 26 - index)
            System.arraycopy(left, 0, temp, 26 - index, index)
            char store = temp[1]
            System.arraycopy(temp, 2, temp, 1, 12)
            temp[13] = store
            left = Arrays.copyOf(temp, temp.length)

            // permute right

            if (26 - index >= 0) System.arraycopy(right, index, temp, 0, 26 - index)
            System.arraycopy(right, 0, temp, 26 - index, index)
            store = temp[0]
            System.arraycopy(temp, 1, temp, 0, 25)
            temp[25] = store
            store = temp[2]
            System.arraycopy(temp, 3, temp, 2, 11)
            temp[13] = store
            right = Arrays.copyOf(temp, temp.length)
        }

        return new String(eText)
    }

    static void main(String[] args) {
        String plainText = "WELLDONEISBETTERTHANWELLSAID"
        println("The original plaintext is : $plainText")
        println("\nThe left and right alphabets after each permutation during encryption are:")
        String cipherText = exec(plainText, Mode.ENCRYPT, true)
        println("\nThe cipher text is : $cipherText")
        String plainText2 = exec(cipherText, Mode.DECRYPT)
        println("\nThe recovered plaintext is : $plainText2")
    }
}
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are:
HXUCZVAMDSLKPEFJRIGTWOBNYQ  PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW  XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV  OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ  NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY  NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE  JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU  YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO  BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP  RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO  MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ  AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM  IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB  RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ  LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF  LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ  RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ  YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY  LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF  MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD  VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE  HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX  RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON  SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS  NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR  NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW  WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP  GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF  OBMESWKYZXUCPRTLNHFAGVQDJI

The cipher text is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

Haskell

import Data.List (elemIndex)

chao :: Eq a => [a] -> [a] -> Bool -> [a] -> [a]
chao _ _ _ [] = []
chao l r plain (x : xs) = maybe [] go (elemIndex x src)
  where
    (src, dst)
      | plain = (l, r)
      | otherwise = (r, l)
    go n =
      dst !! n :
      chao
        (shifted 1 14 (rotated n l))
        ((shifted 2 14 . shifted 0 26) (rotated n r))
        plain
        xs

rotated :: Int -> [a] -> [a]
rotated n = take . length <*> drop n . cycle

shifted :: Int -> Int -> [a] -> [a]
shifted src dst s = concat [x, rotated 1 y, b]
  where
    (a, b) = splitAt dst s
    (x, y) = splitAt src a

encode, decode :: Bool
encode = False
decode = True

main :: IO ()
main = do
  let chaoWheels =
        chao
          "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
          "PTLNBQDEOYSFAVZKGJRIHWXUMC"
      plainText = "WELLDONEISBETTERTHANWELLSAID"
      cipherText = chaoWheels encode plainText
  mapM_
    print
    [ plainText,
      cipherText,
      chaoWheels decode cipherText
    ]
Output:
"WELLDONEISBETTERTHANWELLSAID"
"OAHQHCNYNXTSZJRRHJBYHQKSOUJY"
"WELLDONEISBETTERTHANWELLSAID"

J

Translation of: Raku
reset =:  verb define
  LEFT  =:  'HXUCZVAMDSLKPEFJRIGTWOBNYQ'
  RIGHT =:  'PTLNBQDEOYSFAVZKGJRIHWXUMC'
)

enc =:  verb define
  z =.  LEFT  {~ i =. RIGHT i. y
  permute {. i
  z
)

dec =:  verb define
  z =.  RIGHT {~ i =. LEFT i. y
  permute {. i
  z
)

permute =: verb define
  LEFT  =:  LEFT  |.~ - y
  LEFT  =:  (1 |. 13 {. LEFT) , 13 }. LEFT
 
  RIGHT =:  RIGHT |.~ - y + 1
  RIGHT =:  ({. RIGHT) , (1 |. RIGHT {~ 2+i.12) , 13 }. RIGHT
)

chao =:  enc :. dec

reset ''
smoutput E =.  chao 'WELLDONEISBETTERTHANWELLSAID'
reset ''
smoutput D =.  chao^:_1 E
Output:
OMUUADCMTLZMXXMGXWPCOMUULPTA
WELLDONEISBETTERTHANWELLSAID

Java

Translation of: Kotlin
import java.util.Arrays;

public class Chaocipher {
    private enum Mode {
        ENCRYPT,
        DECRYPT
    }

    private static final String L_ALPHABET = "HXUCZVAMDSLKPEFJRIGTWOBNYQ";
    private static final String R_ALPHABET = "PTLNBQDEOYSFAVZKGJRIHWXUMC";

    private static int indexOf(char[] a, char c) {
        for (int i = 0; i < a.length; ++i) {
            if (a[i] == c) {
                return i;
            }
        }
        return -1;
    }

    private static String exec(String text, Mode mode) {
        return exec(text, mode, false);
    }

    private static String exec(String text, Mode mode, Boolean showSteps) {
        char[] left = L_ALPHABET.toCharArray();
        char[] right = R_ALPHABET.toCharArray();
        char[] eText = new char[text.length()];
        char[] temp = new char[26];

        for (int i = 0; i < text.length(); ++i) {
            if (showSteps) {
                System.out.printf("%s  %s\n", new String(left), new String(right));
            }
            int index;
            if (mode == Mode.ENCRYPT) {
                index = indexOf(right, text.charAt(i));
                eText[i] = left[index];
            } else {
                index = indexOf(left, text.charAt(i));
                eText[i] = right[index];
            }
            if (i == text.length() - 1) {
                break;
            }

            // permute left

            if (26 - index >= 0) System.arraycopy(left, index, temp, 0, 26 - index);
            System.arraycopy(left, 0, temp, 26 - index, index);
            char store = temp[1];
            System.arraycopy(temp, 2, temp, 1, 12);
            temp[13] = store;
            left = Arrays.copyOf(temp, temp.length);

            // permute right

            if (26 - index >= 0) System.arraycopy(right, index, temp, 0, 26 - index);
            System.arraycopy(right, 0, temp, 26 - index, index);
            store = temp[0];
            System.arraycopy(temp, 1, temp, 0, 25);
            temp[25] = store;
            store = temp[2];
            System.arraycopy(temp, 3, temp, 2, 11);
            temp[13] = store;
            right = Arrays.copyOf(temp, temp.length);
        }

        return new String(eText);
    }

    public static void main(String[] args) {
        String plainText = "WELLDONEISBETTERTHANWELLSAID";
        System.out.printf("The original plaintext is : %s\n", plainText);
        System.out.println("\nThe left and right alphabets after each permutation during encryption are:");
        String cipherText = exec(plainText, Mode.ENCRYPT, true);
        System.out.printf("\nThe cipher text is : %s\n", cipherText);
        String plainText2 = exec(cipherText, Mode.DECRYPT);
        System.out.printf("\nThe recovered plaintext is : %s\n", plainText2);
    }
}
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are:
HXUCZVAMDSLKPEFJRIGTWOBNYQ  PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW  XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV  OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ  NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY  NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE  JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU  YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO  BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP  RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO  MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ  AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM  IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB  RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ  LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF  LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ  RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ  YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY  LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF  MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD  VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE  HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX  RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON  SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS  NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR  NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW  WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP  GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF  OBMESWKYZXUCPRTLNHFAGVQDJI

The cipher text is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

JavaScript

Translation of: C

Script source

const L_ALPHABET = "HXUCZVAMDSLKPEFJRIGTWOBNYQ";
const R_ALPHABET = "PTLNBQDEOYSFAVZKGJRIHWXUMC";

const ENCRYPT = 0;
const DECRYPT = 1;

function setCharAt(str, index, chr) {
    if (index > str.length - 1) return str;
    return str.substr(0, index) + chr + str.substr(index + 1);
}

function chao(text, mode, show_steps) {
    var left = L_ALPHABET;
    var right = R_ALPHABET;
    var out = text;
    var temp = "01234567890123456789012345";
    var i = 0;
    var index, j, store;

    if (show_steps) {
        console.log("The left and right alphabets after each permutation during encryption are :");
    }
    while (i < text.length) {
        if (show_steps) {
            console.log(left + "  " + right);
        }
        if (mode == ENCRYPT) {
            index = right.indexOf(text[i]);
            out = setCharAt(out, i, left[index]);
        } else {
            index = left.indexOf(text[i]);
            out = setCharAt(out, i, right[index]);
        }
        if (i == text.length - 1) {
            break;
        }

        //permute left
        j = index;
        while (j < 26) {
            temp = setCharAt(temp, j - index, left[j])
            j += 1;
        }
        j = 0;
        while (j < index) {
            temp = setCharAt(temp, 26 - index + j, left[j]);
            j += 1;
        }
        store = temp[1];
        j = 2;
        while (j < 14) {
            temp = setCharAt(temp, j - 1, temp[j]);
            j += 1;
        }
        temp = setCharAt(temp, 13, store);
        left = temp;

        //permute right
        j = index;
        while (j < 26) {
            temp = setCharAt(temp, j - index, right[j]);
            j += 1;
        }
        j = 0;
        while (j < index) {
            temp = setCharAt(temp, 26 - index + j, right[j]);
            j += 1;
        }
        store = temp[0];
        j = 1;
        while (j < 26) {
            temp = setCharAt(temp, j - 1, temp[j]);
            j += 1;
        }
        temp = setCharAt(temp, 25, store);
        store = temp[2];
        j = 3;
        while (j < 14) {
            temp = setCharAt(temp, j - 1, temp[j]);
            j += 1;
        }
        temp = setCharAt(temp, 13, store);
        right = temp;

        i += 1;
    }

    return out;
}

function main() {
    var out = document.getElementById("content");
    const plain_text = "WELLDONEISBETTERTHANWELLSAID";

    out.innerHTML = "<p>The original plaintext is : " + plain_text + "</p>";
    var cipher_text = chao(plain_text, ENCRYPT, true);
    out.innerHTML += "<p>The ciphertext is : " + cipher_text + "</p>";
    var decipher_text = chao(cipher_text, DECRYPT, false);
    out.innerHTML += "<p>The recovered plaintext is : " + decipher_text + "</p>";
}

Solution page

<!DOCTYPE html>
<html>
    <head>
        <title>Chaocipher</title>
        <script src="chaocipher.js"></script>
    </head>
    <body onload="main()">
        <div id="content"></div>
    </body>
</html>
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID
The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY
The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

Julia

Modified from the Kotlin and Raku entries.

const leftalphabet = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
const rightalphabet = "PTLNBQDEOYSFAVZKGJRIHWXUMC"

function chacocoding(text, encoding, verbose=false)
    left, right = Vector{Char}(leftalphabet), Vector{Char}(rightalphabet)
    len, coded = length(text), similar(Vector{Char}(text))
    for i in 1:len
        verbose && println(String(left), "   ", String(right))
        n = indexin(text[i], encoding ? right : left)[1]
        coded[i] = encoding ? left[n] : right[n]
        if i < len
            left .= circshift(left, -n + 1)
            left[2:14] .= circshift(left[2:14], -1)
            right .= circshift(right, -n)
            right[3:14] .= circshift(right[3:14], -1)
        end
    end
    String(coded)
end

function testchacocipher(txt)
    println("The original plaintext is: $txt")
    println("\nThe left and right alphabets for each character during encryption are:")
    encoded = chacocoding(txt, true, true)
    println("\nThe encoded ciphertext is: $encoded")
    decoded = chacocoding(encoded, false)
    println("\nDecoded, the recovered plaintext is: $decoded")
end

testchacocipher("WELLDONEISBETTERTHANWELLSAID")
Output:
The original plaintext is: WELLDONEISBETTERTHANWELLSAID

The left and right alphabets for each character during encryption are:
HXUCZVAMDSLKPEFJRIGTWOBNYQ   PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW   XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV   OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ   NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY   NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE   JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU   YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO   BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP   RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO   MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ   AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM   IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB   RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ   LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF   LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ   RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ   YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY   LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF   MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD   VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE   HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX   RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON   SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS   NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR   NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW   WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP   GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF   OBMESWKYZXUCPRTLNHFAGVQDJI

The encoded ciphertext is: OAHQHCNYNXTSZJRRHJBYHQKSOUJY

Decoded, the recovered plaintext is: WELLDONEISBETTERTHANWELLSAID

Kotlin

This is based on the C# implementation referred to in the task description, except that the encrypt and decrypt operations are combined into a single method.

// Version 1.2.40

enum class Mode { ENCRYPT, DECRYPT }

object Chao {
    private val lAlphabet = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
    private val rAlphabet = "PTLNBQDEOYSFAVZKGJRIHWXUMC"

    fun exec(text: String, mode: Mode, showSteps: Boolean = false): String {
        var left  = lAlphabet
        var right = rAlphabet
        val eText = CharArray(text.length)
        val temp  = CharArray(26)

        for (i in 0 until text.length) {
            if (showSteps) println("$left  $right")
            var index: Int
            if (mode == Mode.ENCRYPT) {
                index = right.indexOf(text[i])
                eText[i] = left[index]
            }
            else {
                index = left.indexOf(text[i])
                eText[i] = right[index]
            }
            if (i == text.length - 1) break

            // permute left

            for (j in index..25) temp[j - index] = left[j]
            for (j in 0 until index) temp[26 - index + j] = left[j]
            var store = temp[1]
            for (j in 2..13) temp[j - 1] = temp[j]
            temp[13] = store
            left = String(temp)

            // permute right

            for (j in index..25) temp[j - index] = right[j]
            for (j in 0 until index) temp[26 - index + j] = right[j]
            store = temp[0]
            for (j in 1..25) temp[j - 1] = temp[j]
            temp[25] = store
            store = temp[2]
            for (j in 3..13) temp[j - 1] = temp[j]
            temp[13] = store
            right = String(temp)
        }

        return String(eText)
    }
}

fun main(args: Array<String>) {
    val plainText = "WELLDONEISBETTERTHANWELLSAID"
    println("The original plaintext is : $plainText")
    println("\nThe left and right alphabets after each permutation" +
             " during encryption are :\n")
    val cipherText = Chao.exec(plainText, Mode.ENCRYPT, true)
    println("\nThe ciphertext is : $cipherText")
    val plainText2 = Chao.exec(cipherText, Mode.DECRYPT)
    println("\nThe recovered plaintext is : $plainText2")
}
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are :

HXUCZVAMDSLKPEFJRIGTWOBNYQ  PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW  XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV  OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ  NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY  NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE  JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU  YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO  BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP  RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO  MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ  AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM  IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB  RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ  LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF  LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ  RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ  YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY  LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF  MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD  VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE  HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX  RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON  SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS  NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR  NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW  WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP  GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF  OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

Lua

-- Chaocipher, in Lua, 6/19/2020 db
local Chaocipher = {
  ct = "HXUCZVAMDSLKPEFJRIGTWOBNYQ",
  pt = "PTLNBQDEOYSFAVZKGJRIHWXUMC",
  encrypt = function(self, text) return self:_encdec(text, true) end,
  decrypt = function(self, text) return self:_encdec(text, false) end,
  _encdec = function(self, text, encflag)
    local ct, pt, s = self.ct, self.pt, ""
    local cshl = function(s,i) return s:sub(i) .. s:sub(1,i-1) end
    local sshl = function(s,i) return s:sub(1,i-1) .. s:sub(i+1,14) .. s:sub(i,i) .. s:sub(15) end
    for ch in text:gmatch(".") do
      local i = (encflag and pt or ct):find(ch)
      s = s .. (encflag and ct or pt):sub(i,i)
      if encflag then print(ct, pt, ct:sub(i,i), pt:sub(i,i)) end
      ct, pt = sshl(cshl(ct, i), 2), sshl(cshl(pt, i+1), 3)
    end
    return s
  end,
}
local plainText = "WELLDONEISBETTERTHANWELLSAID"
local encryptText = Chaocipher:encrypt(plainText)
local decryptText = Chaocipher:decrypt(encryptText)
print()
print("The original text was:  " .. plainText)
print("The encrypted text is:  " .. encryptText)
print("The decrypted text is:  " .. decryptText)
Output:
HXUCZVAMDSLKPEFJRIGTWOBNYQ      PTLNBQDEOYSFAVZKGJRIHWXUMC      O       W
ONYQHXUCZVAMDBSLKPEFJRIGTW      XUCPTLNBQDEOYMSFAVZKGJRIHW      A       E
ADBSLKPEFJRIGMTWONYQHXUCZV      OYSFAVZKGJRIHMWXUCPTLNBQDE      H       L
HUCZVADBSLKPEXFJRIGMTWONYQ      NBDEOYSFAVZKGQJRIHMWXUCPTL      Q       L
QUCZVADBSLKPEHXFJRIGMTWONY      NBEOYSFAVZKGQDJRIHMWXUCPTL      H       D
HFJRIGMTWONYQXUCZVADBSLKPE      JRHMWXUCPTLNBIEOYSFAVZKGQD      C       O
CVADBSLKPEHFJZRIGMTWONYQXU      YSAVZKGQDJRHMFWXUCPTLNBIEO      N       N
NQXUCVADBSLKPYEHFJZRIGMTWO      BIOYSAVZKGQDJERHMFWXUCPTLN      Y       E
YHFJZRIGMTWONEQXUCVADBSLKP      RHFWXUCPTLNBIMOYSAVZKGQDJE      N       I
NQXUCVADBSLKPEYHFJZRIGMTWO      MOSAVZKGQDJERYHFWXUCPTLNBI      X       S
XCVADBSLKPEYHUFJZRIGMTWONQ      AVKGQDJERYHFWZXUCPTLNBIMOS      T       B
TONQXCVADBSLKWPEYHUFJZRIGM      IMSAVKGQDJERYOHFWZXUCPTLNB      S       E
SKWPEYHUFJZRILGMTONQXCVADB      RYHFWZXUCPTLNOBIMSAVKGQDJE      Z       T
ZILGMTONQXCVARDBSKWPEYHUFJ      LNBIMSAVKGQDJOERYHFWZXUCPT      J       T
JILGMTONQXCVAZRDBSKWPEYHUF      LNIMSAVKGQDJOBERYHFWZXUCPT      R       E
RBSKWPEYHUFJIDLGMTONQXCVAZ      RYFWZXUCPTLNIHMSAVKGQDJOBE      R       R
RSKWPEYHUFJIDBLGMTONQXCVAZ      YFZXUCPTLNIHMWSAVKGQDJOBER      H       T
HFJIDBLGMTONQUXCVAZRSKWPEY      LNHMWSAVKGQDJIOBERYFZXUCPT      J       H
JDBLGMTONQUXCIVAZRSKWPEYHF      MWAVKGQDJIOBESRYFZXUCPTLNH      B       A
BGMTONQUXCIVALZRSKWPEYHFJD      VKQDJIOBESRYFGZXUCPTLNHMWA      Y       N
YFJDBGMTONQUXHCIVALZRSKWPE      HMAVKQDJIOBESWRYFGZXUCPTLN      H       W
HIVALZRSKWPEYCFJDBGMTONQUX      RYGZXUCPTLNHMFAVKQDJIOBESW      Q       E
QXHIVALZRSKWPUEYCFJDBGMTON      SWYGZXUCPTLNHRMFAVKQDJIOBE      K       L
KPUEYCFJDBGMTWONQXHIVALZRS      NHMFAVKQDJIOBRESWYGZXUCPTL      S       L
SPUEYCFJDBGMTKWONQXHIVALZR      NHFAVKQDJIOBRMESWYGZXUCPTL      O       S
OQXHIVALZRSPUNEYCFJDBGMTKW      WYZXUCPTLNHFAGVKQDJIOBRMES      U       A
UEYCFJDBGMTKWNOQXHIVALZRSP      GVQDJIOBRMESWKYZXUCPTLNHFA      J       I
JBGMTKWNOQXHIDVALZRSPUEYCF      OBMESWKYZXUCPRTLNHFAGVQDJI      Y       D

The original text was:  WELLDONEISBETTERTHANWELLSAID
The encrypted text is:  OAHQHCNYNXTSZJRRHJBYHQKSOUJY
The decrypted text is:  WELLDONEISBETTERTHANWELLSAID

Mathematica/Wolfram Language

ClearAll[ichaoalphabet, iMoveToFront, ChaoCipher]
ichaoalphabet = CharacterRange["A", "Z"];
iMoveToFront[l_List, sel_] := Module[{p},
  p = FirstPosition[l, sel];
  RotateLeft[l, p - 1]
  ]
ChaoCipher::wrongcipheralpha = 
  "The cipher alphabet `1` is not a permutation of \
\"A\"\[LongDash]\"Z\".";
ChaoCipher::wrongplainalpha = 
  "The plain alphabet `1` is not a permutation of \"A\"\[LongDash]\"Z\
\".";
ChaoCipher[str_String, {plainalpha_List, cipheralpha_List}] := 
 Module[{pa, ca, plain, new, papermute, capermute, out},
  ca = ToUpperCase[cipheralpha];
  pa = ToUpperCase[plainalpha];
  If[Sort[ca] =!= Sort[ichaoalphabet],
   Message[ChaoCipher::wrongcipheralpha, ca];
   $Failed
   ,
   If[Sort[pa] =!= Sort[ichaoalphabet],
    Message[ChaoCipher::wrongplainalpha, pa];
    $Failed
    ,
    capermute = SubsetMap[RotateLeft, Range[26], Range[2, 14]];
    papermute = 
     SubsetMap[RotateLeft, RotateLeft[Range[26], 1], Range[3, 14]];
    plain = 
     Select[Characters[ToUpperCase[str]], MemberQ[ichaoalphabet, #] &];
    
    out = Table[
      new = Association[Thread[pa -> ca]][p];
      pa = iMoveToFront[pa, p];
      ca = iMoveToFront[ca, new];
      pa = pa[[papermute]];
      ca = ca[[capermute]];
      new
      ,
      {p, plain}
      ];
    StringJoin[out]
    ]
   ]
  ]
ChaoCipher["WELLDONEISBETTERTHANWELLSAID",{Characters@"PTLNBQDEOYSFAVZKGJRIHWXUMC",Characters@"HXUCZVAMDSLKPEFJRIGTWOBNYQ"}]
Output:
OAHQHCNYNXTSZJRRHJBYHQKSOUJY

Nim

Translation of: Kotlin
import strformat

type
  Mode = enum
    Encrypt
    Decrypt

const lAlphabet: string = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
const rAlphabet: string = "PTLNBQDEOYSFAVZKGJRIHWXUMC"

proc chao(text: string, mode: Mode, verbose: bool = false): string =
  var left = lAlphabet
  var right = rAlphabet
  var eText = newSeq[char](text.len)
  var temp: array[26, char]
  
  for i in 0..<text.len:
    if verbose:
      echo &"{left}  {right}"
    var index: int
    if mode == Encrypt:
      index = right.find(text[i])
      eText[i] = left[index]
    else:
      index = left.find(text[i])
      eText[i] = right[index]
    if (i == text.len - 1):
      break
    
    # permute left
    for j in index..25:
      temp[j - index] = left[j]
    for j in 0..<index:
      temp[26 - index + j] = left[j]
    var store = temp[1]
    for j in 2..13:
      temp[j - 1] = temp[j]
    temp[13] = store
    left = ""
    for i in temp:
      left &= $i
    
    # permute right
    for j in index..25:
      temp[j - index] = right[j]
    for j in 0..<index:
      temp[26 - index + j] = right[j]
    store = temp[0]
    for j in 1..25:
      temp[j - 1] = temp[j]
    temp[25] = store
    store = temp[2]
    for j in 3..13:
      temp[j - 1] = temp[j]
    temp[13] = store
    right = ""
    for i in temp:
      right &= $i
   
  for i in eText:
    result &= $i

var plainText = "WELLDONEISBETTERTHANWELLSAID"
echo &"The original plaintext is: {plainText}"
echo "\nThe left and right alphabets after each permutation during encryption are:\n"
var cipherText = chao(plainText, Encrypt, true)
echo &"\nThe ciphertext is: {cipherText}"
var plainText2 = chao(cipherText, Decrypt, false)
echo &"\nThe recovered plaintext is: {plainText2}"
Output:
The original plaintext is: WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are:

HXUCZVAMDSLKPEFJRIGTWOBNYQ  PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW  XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV  OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ  NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY  NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE  JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU  YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO  BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP  RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO  MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ  AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM  IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB  RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ  LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF  LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ  RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ  YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY  LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF  MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD  VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE  HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX  RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON  SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS  NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR  NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW  WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP  GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF  OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is: OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is: WELLDONEISBETTERTHANWELLSAID

Another implementation

Using functions from the stdlib instead of manual array manipulations:

import std/[algorithm, strutils]

type
  Mode = enum
    Encrypt
    Decrypt

const
  lAlphabet = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
  rAlphabet = "PTLNBQDEOYSFAVZKGJRIHWXUMC"

proc chao(text: string; mode: Mode; verbose = false): string =
  var
    left = lAlphabet
    right = rAlphabet
    eText = newSeq[char](text.len)

  for i in 0 ..< text.len:
    if verbose:
      echo left, "  ", right
    var index: int
    if mode == Encrypt:
      index = right.find(text[i])
      eText[i] = left[index]
    else:
      index = left.find(text[i])
      eText[i] = right[index]
    if i == text.len - 1:
      break

    # permute left
    left.rotateLeft(index)
    left.rotateLeft(1..13, 1)

    # permute right
    right.rotateLeft(index + 1)
    right.rotateLeft(2..13, 1)

  result = eText.join()

let plainText = "WELLDONEISBETTERTHANWELLSAID"
echo "The original plaintext is: ", plainText
echo "\nThe left and right alphabets after each permutation during encryption are:\n"
let cipherText = chao(plainText, Encrypt, true)
echo "\nThe ciphertext is: ", cipherText
let plainText2 = chao(cipherText, Decrypt, false)
echo "\nThe recovered plaintext is: ", plainText2

Same output as above.

Objeck

Translation of: Kotlin
class Chaocipher {
  L_ALPHABET : static : Char[];
  R_ALPHABET : static : Char[];

  function : Main(args : String[]) ~ Nil {
    L_ALPHABET := "HXUCZVAMDSLKPEFJRIGTWOBNYQ"->ToCharArray();
    R_ALPHABET := "PTLNBQDEOYSFAVZKGJRIHWXUMC"->ToCharArray();
    plainText := "WELLDONEISBETTERTHANWELLSAID"->ToCharArray();

    System.IO.Console->Print("The original plaintext is: ")->PrintLine(plainText);
    "\nThe left and right alphabets after each permutation during encryption are:\n"->PrintLine();
    cipherText := Chao(plainText, Mode->ENCRYPT, true);
    System.IO.Console->Print("\nThe ciphertext is: ")->PrintLine(cipherText);
    plainText2 := Chao(cipherText, Mode->DECRYPT, false);
    System.IO.Console->Print("The recovered plaintext is: ")->PrintLine(plainText2);
  }

  function : Chao(in : Char[], mode : Mode, show_steps : Bool) ~ Char[] {  
    i : Int; j : Int; index : Int;
    store : Char;
    len := in->Size();
    left := Char->New[26];   right := Char->New[26]; temp := Char->New[26];
    eText := Char->New[len];

    Runtime->Copy(left, 0, L_ALPHABET, 0, L_ALPHABET->Size());
    Runtime->Copy(right, 0, R_ALPHABET, 0, R_ALPHABET->Size());

    for(i := 0; i < len; i += 1;) {
      if (show_steps) {
        System.IO.Console->Print(left)->Print(' ')->PrintLine(right);
      };
      if (mode = Mode->ENCRYPT) {
        index := IndexOf(right, in[i]);
        eText[i] := left[index];
      }
      else {
        index := IndexOf(left, in[i]);
        eText[i] := right[index];
      };

      if (i = len - 1) {
        break;
      };

      # left
      for(j := index; j < 26; j += 1;) { temp[j - index] := left[j]; };
      for(j :=0; j < index; j += 1;) { temp[26 - index + j] := left[j]; };
      store := temp[1];
      for(j := 2; j < 14; j += 1;) { temp[j - 1] := temp[j]; };
      temp[13] := store;
      Runtime->Copy(left, 0, temp, 0, temp->Size());

      # right
      for(j := index; j < 26; j += 1;) { temp[j - index] := right[j]; };
      for(j :=0; j < index; j += 1;) { temp[26 - index + j] := right[j]; };
      store := temp[0];
      for(j :=1; j < 26; j += 1;) { temp[j - 1] := temp[j]; };
          temp[25] := store;
          store := temp[2];
          for(j := 3; j < 14; j += 1;) { temp[j - 1] := temp[j]; };
          temp[13] := store;
          Runtime->Copy(right, 0, temp, 0, temp->Size());
    };
        
    return eText;
  }

  function : IndexOf(str : Char[], c : Char) ~ Int {
    for(i := 0; i < str->Size(); i += 1;) {
      if(c = str[i]) {
        return i;
      };
    };

    return -1;
  }

  enum Mode { ENCRYPT, DECRYPT }  
}
Output:
The original plaintext is: WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are:

HXUCZVAMDSLKPEFJRIGTWOBNYQ PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is: OAHQHCNYNXTSZJRRHJBYHQKSOUJY
The recovered plaintext is: WELLDONEISBETTERTHANWELLSAID

Pascal

Works with: Extended Pascal
program chaocipher(input, output);

const
	{ This denotes a `set` literal: }
	alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
		'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
	{ The `card` function is an Extended Pascal (ISO 10206) extension. }
	alphabetCardinality = card(alphabet);
	{ 1st character denotes “zenith”. }
	zenith = 1;
	{ In a 26-character alphabet the 14th character denotes “nadir”. }
	nadir = alphabetCardinality div 2 + 1;
	{ For simplicity use compile-time-defined maximum lengths. }
	messageMaximumLength = 80;

type
	{ This “discriminates” the Extended Pascal schema data type `string` to be }
	{ capable of holding strings up to `alphabetCardinality` `char` values. }
	map = string(alphabetCardinality);
	{ Variables of this data type can only assume integer values within 1..26: }
	mapCharacterIndex = 1..alphabetCardinality;
	{ Later used as a buffer for the input/output. }
	message = string(messageMaximumLength);
	messageCharacterIndex = 1..messageMaximumLength;
	{ Stores a key for the Chaocipher algorithm. }
	key = record
			cipherText: map;
			plainText: map;
		end;

{ --- auxilliary routines ---------------------------------------------- }

{
	\brief verifies that a key is valid for the Chaocipher
	\param sample a potential `key` for a Chaocipher
	\return `true` iff \param sample is an acceptable `key`
}
{ `protected` (Extended Pascal extension) denotes an immutable parameter. }
function isValid(protected sample: key): Boolean;
	{ Determines whether a `map` contains all characters of `alphabet`. }
	{ Nesting this function allows for a neat expression below. }
	function isComplete(protected text: map): Boolean;
	var
		i: integer;
		{ `value []` will initialize this variable to an empty set value. }
		{ This is an Extended Pascal (ISO 10206) extension. }
		s: set of char value [];
	begin
		{ NB: In Pascal `for`-loop limits are inclusive. }
		for i := 1 to length(text) do
		begin
			{ This adds the set containing one character to the set `s`. }
			s := s + [text[i]]
		end;
		isComplete := card(s) = alphabetCardinality
	end;
begin
	{ This way `sample.cipherText` can be simply written as `cipherText`. }
	with sample do
	begin
		{ `and_then` is an EP extension indicating “lazy evaluation”. }
		isValid := (alphabetCardinality > 8) and_then
			isComplete(cipherText) and_then isComplete(plainText)
	end
end;

{
	\brief permutes a key for the next encryption/decryption step
	\param shift the index of the characters just substituted
}
{ `var` means the parameter value will be modified _at_ the call site. }
procedure permute(var state: key; protected shift: mapCharacterIndex);
begin
	with state do
	begin
		{ Indices in `cipherText[1..pred(shift)]` _must_ be non-descending: }
		if shift > 1 then
		begin
			cipherText := subStr(cipherText, shift) + cipherText[1..pred(shift)]
			{ `subStr(str, ini)` is equivalent to `str[ini..length(str)]`. }
		end;
		{ Likewise, `succ(shift)` must be a valid index in `plainText`: }
		if shift < alphabetCardinality then
		begin
			plainText := subStr(plainText, succ(shift)) + plainText[1..shift]
		end;
		
		{ If it does _not_ _alter_ the _entire_ string’s _length_, you can }
		{ modify parts of a string like this (Extended Pascal extension): }
		cipherText[zenith+1..nadir] := cipherText[zenith+2..nadir] + cipherText[zenith+1];
		plainText[zenith+2..nadir] := plainText[zenith+3..nadir] + plainText[zenith+2]
	end
end;

{ --- the core routine of the algorithm -------------------------------- }

{
	\brief performs Chaocipher common steps
	\param line the message to encrypt/decrypt
	\param state the initial key to start encrpytion/decryption with
	\param locate a function determining the 2-tuple index in the key
	\param substitute the procedure substituting the correct characters
}
procedure chaocipher(
		var line: message;
		var state: key;
		{ These are “routine parameters”. Essentially the address of a routine }
		{ matching the specified routine signature is passed to `chaocipher`. }
		function locate(protected i: messageCharacterIndex): mapCharacterIndex;
		procedure substitute(
				protected i: messageCharacterIndex;
				protected z: mapCharacterIndex
			)
	);
var
	{ For demonstration purposes: In this program }
	{ `line.capacity` refers to `messageMaximumLength`. }
	i: 1..line.capacity;
	substitutionPairIndex: mapCharacterIndex;
begin
	{ Don’t trust user input, even though this is just a RosettaCode example. }
	if not isValid(state) then
	begin
		writeLn('Error: Key is invalid. Got:');
		writeLn('Cipher text: ', state.cipherText);
		writeLn(' Plain text: ', state.plainText);
		halt
	end;
	
	for i := 1 to length(line) do
	begin
		{ We’ll better skip characters that aren’t in the `alphabet`. }
		if line[i] in alphabet then
		begin
			{ Here you see the beauty of using routine parameters. }
			{ Depending on whether we’re encrypting or decrypting, }
			{ you need to find a character in the `cipherText` or }
			{ `plainText` key value respectively, yet the basic order
			{ of the steps are still the same. }
			substitutionPairIndex := locate(i);
			substitute(i, substitutionPairIndex);
			permute(state, substitutionPairIndex)
		end
	end
end;

{ --- entry routines --------------------------------------------------- }

{
	\brief encrypts a message according to Chaocipher
	\param line a message to encrypt
	\param state the key to begin with
	\return the encrypted message \param line using the provided key
}
{ Note: without `var` or `protected` both `encrypt` and `decrypt`get }
{       and have their own independent copies of the parameter values. }
function encrypt(line: message; state: key): message;
	function encryptor(protected i: messageCharacterIndex): mapCharacterIndex;
	begin
		encryptor := index(state.plainText, line[i])
	end;
	procedure substitutor(
			protected i: messageCharacterIndex;
			protected z: mapCharacterIndex
		);
	begin
		line[i] := state.cipherText[z]
	end;
begin	
	chaocipher(line, state, encryptor, substitutor);
	encrypt := line
end;

{
	\brief decrypts a message according to Chaocipher
	\param line the encrypted message
	\param state the key to begin with
	\return the decrypted message \param line using the provided key
}
function decrypt(line: message; state: key): message;
	function decryptor(protected i: messageCharacterIndex): mapCharacterIndex;
	begin
		decryptor := index(state.cipherText, line[i])
	end;
	procedure substitutor(
			protected i: messageCharacterIndex;
			protected z: mapCharacterIndex
		);
	begin
		line[i] := state.plainText[z]
	end;
begin
	chaocipher(line, state, decryptor, substitutor);
	decrypt := line
end;

{ === MAIN ============================================================= }
var
	exampleKey: key;
	line: message;
begin
	{ Instead of writing `exampleKey.cipherText := '…', you can }
	{ write in Extended Pascal a `record` literal like this: }
	exampleKey := key[
			cipherText: 'HXUCZVAMDSLKPEFJRIGTWOBNYQ';
			plainText: 'PTLNBQDEOYSFAVZKGJRIHWXUMC';
		];
	
	{ `EOF` is shorthand for `EOF(input)`. }
	while not EOF do
	begin
		{ `readLn(line)` is shorthand for `readLn(input, line)`. }
		readLn(line);
		line := encrypt(line, exampleKey);
		writeLn(decrypt(line, exampleKey));
		{ Likewise, `writeLn(line)` is short for `writeLn(output, line)`. }
		writeLn(line)
	end
end.
Input:
WELLDONEISBETTERTHANWELLSAID
Output:
WELLDONEISBETTERTHANWELLSAID
OAHQHCNYNXTSZJRRHJBYHQKSOUJY

Perl

Translation of: Raku

Since rotate is not a built-in in Perl, using a custom one, not general-purpose but sufficient for this task.

use strict;
use warnings;
my(@left,@right,$e_msg,$d_msg);

sub init {
    @left  = split '', 'HXUCZVAMDSLKPEFJRIGTWOBNYQ';
    @right = split '', 'PTLNBQDEOYSFAVZKGJRIHWXUMC';
}

sub encode {
    my($letter) = @_;
    my $index = index join('', @right), $letter;
    my $enc   = $left[$index];
    left_permute($index);
    right_permute($index);
    $enc
}

sub decode {
    my($letter) = @_;
    my $index = index join('', @left), $letter;
    my $dec   = $right[$index];
    left_permute($index);
    right_permute($index);
    $dec
}

sub right_permute {
    my($index) = @_;
    rotate(\@right, $index + 1);
    rotate(\@right, 1, 2, 13);
}

sub left_permute {
    my($index) = @_;
    rotate(\@left, $index);
    rotate(\@left, 1, 1, 13);
}

sub rotate {
    my @list = @{ shift() };
    my($n,$s,$e) = @_;
    $s ? @list[0..$s-1, $s+$n..$e+$n-1, $s..$s+$n-1, $e+1..$#list]
       : @list[$n..$#list, 0..$n-1]
}

init; $e_msg .= encode $_ for split '', 'WELLDONEISBETTERTHANWELLSAID';
init; $d_msg .= decode $_ for split '', $e_msg;

print "$e_msg\n$d_msg\n";
Output:
OMUUADCMTLZMXXMGXWPCOMUULPTA
WELLDONEISBETTERTHANWELLSAID

Phix

Originally translated from C, but ended up more of a direct implementation of the algorithm in the pdf.

-- demo\rosetta\Chao_cipher.exw
with javascript_semantics
constant l_alphabet = "HXUCZVAMDSLKPEFJRIGTWOBNYQ",
         r_alphabet = "PTLNBQDEOYSFAVZKGJRIHWXUMC"

enum ENCRYPT, DECRYPT
 
function chao_cipher(string s, integer mode, bool show_steps)
    integer len = length(s)
    string out = repeat(' ',len),
           left = l_alphabet,
           right = r_alphabet
    for i=1 to len do
        if show_steps then printf(1,"%s  %s\n", {left, right}) end if
        integer index = find(s[i],iff(mode==ENCRYPT?right:left))
        out[i] = iff(mode==ENCRYPT?left:right)[index]

        if i==len then exit end if
 
        /* permute left */
        left = left[index..26]&left[1..index-1]
        left[2..14] = left[3..14]&left[2]

        /* permute right */
        right = right[index+1..26]&right[1..index]
        right[3..14] = right[4..14]&right[3]
    end for
    return out
end function
 
string plain_text = "WELLDONEISBETTERTHANWELLSAID"
printf(1,"The original plaintext is : %s\n", {plain_text})

--printf(1,"\nThe left and right alphabets after each permutation"&
--       " during encryption are :\n\n")
--string cipher_text = chao_cipher(plain_text, ENCRYPT, true)
string cipher_text = chao_cipher(plain_text, ENCRYPT, false)
printf(1,"\nThe ciphertext is : %s\n", {cipher_text})

string plain_text2 = chao_cipher(cipher_text, DECRYPT, false)
printf(1,"\nThe recovered plaintext is : %s\n", {plain_text2})
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

Python

Procedural

# Python3 implementation of Chaocipher 
# left wheel = ciphertext wheel
# right wheel = plaintext wheel

def main():
    # letters only! makealpha(key) helps generate lalpha/ralpha. 
    lalpha = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
    ralpha = "PTLNBQDEOYSFAVZKGJRIHWXUMC"
    msg = "WELLDONEISBETTERTHANWELLSAID"

    print("L:", lalpha)
    print("R:", ralpha)
    print("I:", msg)
    print("O:", do_chao(msg, lalpha, ralpha, 1, 0), "\n")
    
    do_chao(msg, lalpha, ralpha, 1, 1)

def do_chao(msg, lalpha, ralpha, en=1, show=0):
    msg = correct_case(msg)
    out = ""    
    if show:
        print("="*54)        
        print(10*" " + "left:" + 21*" " + "right: ")
        print("="*54)        
        print(lalpha, ralpha, "\n")
    for L in msg:
        if en:
            lalpha, ralpha = rotate_wheels(lalpha, ralpha, L)
            out += lalpha[0]
        else:
            ralpha, lalpha = rotate_wheels(ralpha, lalpha, L)
            out += ralpha[0]
        lalpha, ralpha = scramble_wheels(lalpha, ralpha)
        if show:
            print(lalpha, ralpha)            
    return out
    
def makealpha(key=""):
    alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    z = set()
    key = [x.upper() for x in (key + alpha[::-1])
           if not (x.upper() in z or z.add(x.upper()))]
    return "".join(key)

def correct_case(string):
    return "".join([s.upper() for s in string if s.isalpha()])

def permu(alp, num):
    alp = alp[:num], alp[num:]
    return "".join(alp[::-1])

def rotate_wheels(lalph, ralph, key):
    newin = ralph.index(key)
    return permu(lalph, newin), permu(ralph, newin)    

def scramble_wheels(lalph, ralph):
    # LEFT = cipher wheel 
    # Cycle second[1] through nadir[14] forward
    lalph = list(lalph)
    lalph = "".join([*lalph[0],
                    *lalph[2:14],
                    lalph[1],
                    *lalph[14:]])
    
    # RIGHT = plain wheel                    
    # Send the zenith[0] character to the end[25],
    # cycle third[2] through nadir[14] characters forward
    ralph = list(ralph)
    ralph = "".join([*ralph[1:3],
                     *ralph[4:15],
                     ralph[3],
                     *ralph[15:],
                     ralph[0]])
    return lalph, ralph

main()
L: HXUCZVAMDSLKPEFJRIGTWOBNYQ
R: PTLNBQDEOYSFAVZKGJRIHWXUMC
I: WELLDONEISBETTERTHANWELLSAID
O: OAHQHCNYNXTSZJRRHJBYHQKSOUJY 

======================================================
          left:                     right: 
======================================================
HXUCZVAMDSLKPEFJRIGTWOBNYQ PTLNBQDEOYSFAVZKGJRIHWXUMC 

ONYQHXUCZVAMDBSLKPEFJRIGTW XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF OBMESWKYZXUCPRTLNHFAGVQDJI
YFJBGMTKWNOQXCHIDVALZRSPUE JIBMESWKYZXUCOPRTLNHFAGVQD

OAHQHCNYNXTSZJRRHJBYHQKSOUJY
WELLDONEISBETTERTHANWELLSAID

Functional

Translation of: Haskell
Works with: Python version 3.7
'''Chaocipher'''

from itertools import chain, cycle, islice


# chao :: String -> String -> Bool -> String -> String
def chao(l):
    '''Chaocipher encoding or decoding for the given
       left and right 'wheels'.
       A ciphertext is returned if the boolean flag
       is True, and a plaintext if the flag is False.
    '''
    def go(l, r, plain, xxs):
        if xxs:
            (src, dst) = (l, r) if plain else (r, l)
            (x, xs) = (xxs[0], xxs[1:])

            def chaoProcess(n):
                return [dst[n]] + go(
                    shifted(1)(14)(rotated(n, l)),
                    compose(shifted(2)(14))(shifted(0)(26))(
                        rotated(n, r)
                    ),
                    plain,
                    xs
                )

            return maybe('')(chaoProcess)(
                elemIndex(x)(src)
            )
        else:
            return []
    return lambda r: lambda plain: lambda xxs: concat(go(
        l, r, plain, xxs
    ))


# rotated :: Int -> [a] -> [a]
def rotated(z, s):
    '''Rotation of string s by z characters.'''
    return take(len(s))(
        drop(z)(
            cycle(s)
        )
    )


# shifted :: Int -> Int -> [a] -> [a]
def shifted(src):
    '''The string s with a set of its characters cyclically
       shifted from a source index to a destination index.
    '''
    def go(dst, s):
        (a, b) = splitAt(dst)(s)
        (x, y) = splitAt(src)(a)
        return concat([x, rotated(1, y), b])
    return lambda dst: lambda s: go(dst, s)


# TEST ----------------------------------------------------
# main :: IO ()
def main():
    '''Print the plain text, followed by
       a corresponding cipher text,
       and a decode of that cipher text.
    '''
    chaoWheels = chao(
        "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
    )(
        "PTLNBQDEOYSFAVZKGJRIHWXUMC"
    )
    plainText = "WELLDONEISBETTERTHANWELLSAID"
    cipherText = chaoWheels(False)(plainText)

    print(plainText)
    print(cipherText)
    print(
        chaoWheels(True)(cipherText)
    )


# GENERIC -------------------------------------------------

# Just :: a -> Maybe a
def Just(x):
    '''Constructor for an inhabited Maybe (option type) value.
       Wrapper containing the result of a computation.
    '''
    return {'type': 'Maybe', 'Nothing': False, 'Just': x}


# Nothing :: Maybe a
def Nothing():
    '''Constructor for an empty Maybe (option type) value.
       Empty wrapper returned where a computation is not possible.
    '''
    return {'type': 'Maybe', 'Nothing': True}


# compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
def compose(g):
    '''Right to left function composition.'''
    return lambda f: lambda x: g(f(x))


# concat :: [[a]] -> [a]
# concat :: [String] -> String
def concat(xs):
    '''The concatenation of all the elements
       in a list or iterable.
    '''
    def f(ys):
        zs = list(chain(*ys))
        return ''.join(zs) if isinstance(ys[0], str) else zs

    return (
        f(xs) if isinstance(xs, list) else (
            chain.from_iterable(xs)
        )
    ) if xs else []


# drop :: Int -> [a] -> [a]
# drop :: Int -> String -> String
def drop(n):
    '''The sublist of xs beginning at
       (zero-based) index n.
    '''
    def go(xs):
        if isinstance(xs, (list, tuple, str)):
            return xs[n:]
        else:
            take(n)(xs)
            return xs
    return lambda xs: go(xs)


# elemIndex :: Eq a => a -> [a] -> Maybe Int
def elemIndex(x):
    '''Just the index of the first element in xs
       which is equal to x,
       or Nothing if there is no such element.
    '''
    def go(xs):
        try:
            return Just(xs.index(x))
        except ValueError:
            return Nothing()
    return lambda xs: go(xs)


# maybe :: b -> (a -> b) -> Maybe a -> b
def maybe(v):
    '''Either the default value v, if m is Nothing,
       or the application of f to x,
       where m is Just(x).
    '''
    return lambda f: lambda m: v if None is m or m.get('Nothing') else (
        f(m.get('Just'))
    )


# splitAt :: Int -> [a] -> ([a], [a])
def splitAt(n):
    '''A tuple pairing the prefix of length n
       with the rest of xs.
    '''
    return lambda xs: (xs[0:n], xs[n:])


# take :: Int -> [a] -> [a]
# take :: Int -> String -> String
def take(n):
    '''The prefix of xs of length n,
       or xs itself if n > length xs.
    '''
    return lambda xs: (
        xs[0:n]
        if isinstance(xs, (list, tuple))
        else list(islice(xs, n))
    )


# MAIN ---
if __name__ == '__main__':
    main()
Output:
WELLDONEISBETTERTHANWELLSAID
OAHQHCNYNXTSZJRRHJBYHQKSOUJY
WELLDONEISBETTERTHANWELLSAID

QBasic

Translation of: BASIC
DECLARE FUNCTION AlphaLeft$ (ct$, pt$, CharPos!)
DECLARE FUNCTION AlphaRight$ (ct$, pt$, CharPos!)
DECLARE FUNCTION Decode$ (Text$, ct$, pt$)
DECLARE FUNCTION Encode$ (Text$, ct$, pt$)

CLS

' Deciphering a Chaocipher-encrypted message is identical to the steps used
' for enciphering. The sole difference is that the decipherer locates the
' known ciphertext letter in the left (ct$) alphabet, with the plaintext
' letter being the corresponding letter in the right (pt$) alphabet
'
' Alphabet permuting is identical in enciphering and deciphering

' Start of Main Code

' LEFT (Cipher Text$): HXUCZVAMDSLKPEFJRIGTWOBNYQ
tLeft$ = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"

' RIGHT (Plain Text$): PTLNBQDEOYSFAVZKGJRIHWXUMC
tRight$ = "PTLNBQDEOYSFAVZKGJRIHWXUMC"

' Cipher Message (Used to verify a good encoding)
cText$ = "OAHQHCNYNXTSZJRRHJBYHQKSOUJY"

' Plain Text$ Message
pText$ = "WELLDONEISBETTERTHANWELLSAID"
PRINT " Plain  Text$: "; pText$
PRINT

ctLeft$ = tLeft$
ptRight$ = tRight$

' Final Cipher Text$
eText$ = Encode$(pText$, ctLeft$, ptRight$)
PRINT " Cipher Text$: "; eText$
PRINT

IF eText$ = cText$ THEN PRINT "Successful" ELSE PRINT "Failed"

ctLeft$ = tLeft$
ptRight$ = tRight$
dText$ = Decode$(eText$, ctLeft$, ptRight$)
PRINT
PRINT " Plain Text$: "; dText$
PRINT

IF dText$ = pText$ THEN PRINT "Successful" ELSE PRINT "Failed"
END

' Left Alphabet
FUNCTION AlphaLeft$ (ct$, pt$, CharPos)
    tStr$ = ct$
    
    ' 1. Shift the entire left alphabet cyclically so the ciphertext letter
    ' just enciphered is positioned at the zenith (i.e., position 1).
    tStr$ = RIGHT$(ct$, LEN(ct$) - CharPos + 1) + LEFT$(ct$, CharPos - 1)
    
    ' 2. Extract the letter found at position zenith+1 (i.e., the letter to
    ' the right of the zenith), taking it out of the alphabet, temporarily
    ' leaving an unfilled "Hole$"
    
    Hole$ = MID$(tStr$, 2, 1)
	MID$(tStr$, 2, 1) = " "
    
    ' 3. Shift all letters in positions zenith+2 up to, and including, the
    ' nadir (zenith+13), moving them one position to the left
    
    tStr$ = LEFT$(tStr$, 1) + MID$(tStr$, 3, 12) + " " + RIGHT$(tStr$, 12)
    
    ' 4. Insert the just-extracted letter into the nadir position
    ' (i.e., zenith+13)
    
    MID$(tStr$, 14, 1) = Hole$
    
    AlphaLeft$ = tStr$
END FUNCTION

' Right Alphabet
FUNCTION AlphaRight$ (ct$, pt$, CharPos)
    tStr$ = pt$
    
    ' 1. Shift the entire right alphabet cyclically so the plaintext letter
    ' just enciphered is positioned at the zenith.
    
    tStr$ = RIGHT$(tStr$, LEN(tStr$) - CharPos + 1) + LEFT$(tStr$, CharPos - 1)
    
    ' 2. Now shift the entire alphabet one more position to the left (i.e.,
    ' the leftmost letter moves cyclically to the far right), moving a new
    ' letter into the zenith position.
    
    tStr$ = RIGHT$(tStr$, 25) + LEFT$(tStr$, 1)
    
    ' 3. Extract the letter at position zenith+2, taking it out of the
    ' alphabet, temporarily leaving an unfilled "Hole$".
    
    Hole$ = MID$(tStr$, 3, 1)
	MID$(tStr$, 3, 1) = " ":
    
    ' 4. Shift all letters beginning with zenith+3 up to, and including, the
    ' nadir (zenith+13), moving them one position to the left.
    
    tStr$ = LEFT$(tStr$, 2) + MID$(tStr$, 4, 11) + " " + RIGHT$(tStr$, 12)
    
    ' 5. Insert the just-extracted letter into the nadir position (zenith+13)
    
    MID$(tStr$, 14, 1) = Hole$
    
    AlphaRight$ = tStr$
END FUNCTION

FUNCTION Decode$ (Text$, ct$, pt$)
    tStr$ = ""
    
    FOR t = 1 TO LEN(Text$)
	Char$ = MID$(Text$, t, 1)
	CharPos = INSTR(ct$, Char$)
	
	ct$ = AlphaLeft$(ct$, pt$, CharPos)
	pt$ = AlphaRight$(ct$, pt$, CharPos)
	
	tStr$ = tStr$ + RIGHT$(pt$, 1)
    NEXT
    
    Decode$ = tStr$
END FUNCTION

FUNCTION Encode$ (Text$, ct$, pt$)
    tStr$ = ""
    
    FOR t = 1 TO LEN(Text$)
	Char$ = MID$(Text$, t, 1)
	CharPos = INSTR(pt$, Char$)
	
	ct$ = AlphaLeft$(ct$, pt$, CharPos)
	pt$ = AlphaRight$(ct$, pt$, CharPos)
	
	tStr$ = tStr$ + LEFT$(ct$, 1)
    NEXT
    
    Encode$ = tStr$
END FUNCTION

Raku

(formerly Perl 6)

Works with: Rakudo version 2018.03
my @left;
my @right;

sub reset {
    @left  = <HXUCZVAMDSLKPEFJRIGTWOBNYQ>.comb;
    @right = <PTLNBQDEOYSFAVZKGJRIHWXUMC>.comb;
}

sub encode ($letter) {
    my $index = @right.first: $letter.uc, :k;
    my $enc   = @left[$index];
    $index.&permute;
    $enc
}

sub decode ($letter) {
    my $index = @left.first: $letter.uc, :k;
    my $dec   = @right[$index];
    $index.&permute;
    $dec
}

sub permute ($index) {
    @left.=rotate: $index;
    @left[1..13].=rotate;
    @right.=rotate: $index + 1;
    @right[2..13].=rotate;
}

reset;
say 'WELLDONEISBETTERTHANWELLSAID'.comb».&encode.join;
reset;
say 'OAHQHCNYNXTSZJRRHJBYHQKSOUJY'.comb».&decode.join;
Output:
OAHQHCNYNXTSZJRRHJBYHQKSOUJY
WELLDONEISBETTERTHANWELLSAID

Ruby

txt    = "WELLDONEISBETTERTHANWELLSAID"
@left  = "HXUCZVAMDSLKPEFJRIGTWOBNYQ".chars
@right = "PTLNBQDEOYSFAVZKGJRIHWXUMC".chars

def encrypt(char)
  coded_char = @left[@right.index(char)]

  @left.rotate!(@left.index(coded_char))
  part = @left.slice!(1,13).rotate
  @left.insert(1, *part)

  @right.rotate!(@right.index(char)+1)
  part = @right.slice!(2,12).rotate
  @right.insert(2, *part)
  
  @left[0]
end

puts txt.each_char.map{|c| encrypt(c) }.join
Output:
OAHQHCNYNXTSZJRRHJBYHQKSOUJY

Rust

const LEFT_ALPHABET_CT: &str = "HXUCZVAMDSLKPEFJRIGTWOBNYQ";
const RIGHT_ALPHABET_PT: &str = "PTLNBQDEOYSFAVZKGJRIHWXUMC";
const ZENITH: usize = 0;
const NADIR: usize = 12;
const SEQUENCE: &str = "WELLDONEISBETTERTHANWELLSAID";

fn cipher(letter: &char, left: &String, right: &String) -> (usize, char) {
    let pos = right.find(*letter).unwrap();
    let cipher = left.chars().nth(pos).unwrap();
    (pos, cipher)
}

fn main() {
    let mut left = LEFT_ALPHABET_CT.to_string();
    let mut right = RIGHT_ALPHABET_PT.to_string();

    let ciphertext = SEQUENCE.chars()
        .map(|letter| {
            let (pos, cipher_char) = cipher(&letter, &left, &right);
            left = format!("{}{}", &left[pos..], &left[..pos]);
            left = format!("{}{}{}{}", &left[ZENITH..1], &left[2..NADIR+2], &left[1..2], &left[NADIR+2..]);
            if pos != right.len() - 1 {
                right = format!("{}{}", &right[pos + 1..], &right[..pos + 1]);
            }
            right = format!("{}{}{}{}", &right[ZENITH..2], &right[3..NADIR+2], &right[2..3], &right[NADIR+2..]);
            cipher_char
        })
        .collect::<String>();

    println!("Plaintext: {}", SEQUENCE);
    println!("Ciphertext: {}", ciphertext);
}
Output:
Plaintext: WELLDONEISBETTERTHANWELLSAID
Ciphertext: OAHQHCNYNXTSZJRRHJBYHQKSOUJY

Tailspin

templates chaocipher&{left:,right:,decode:}
  templates permute
    def ctshift: [ $@chaocipher.ct($..last)..., $@chaocipher.ct(1..$-1)...];
    def p1: $ mod 26 + 1;
    def ptshift: [ $@chaocipher.pt($p1..last)..., $@chaocipher.pt(1..$p1-1)...];
    ..|@chaocipher: { ct: [ $ctshift(1), $ctshift(3..14)..., $ctshift(2), $ctshift(15..last)...],
      pt: [ $ptshift(1..2)..., $ptshift(4..14)..., $ptshift(3), $ptshift(15..last)...] };
  end permute

  @: {ct:[ $left... ], pt: [ $right... ], result:[]};
  $... -> #
  '$@.result...;' !

  when <?($decode <=0>)> do
    def plain: $;
    def index: $@.pt -> \[i](<=$plain> $i!\) -> $(1);
    ..|@.result: $@.ct($index);
    $index -> permute -> !VOID
  otherwise
    def cipher: $;
    def index: $@.ct -> \[i](<=$cipher> $i!\) -> $(1);
    ..|@.result: $@.pt($index);
    $index -> permute -> !VOID
end chaocipher

'WELLDONEISBETTERTHANWELLSAID' -> chaocipher&{left:'HXUCZVAMDSLKPEFJRIGTWOBNYQ', right:'PTLNBQDEOYSFAVZKGJRIHWXUMC',decode:0} -> '$;
' -> !OUT::write

'OAHQHCNYNXTSZJRRHJBYHQKSOUJY' -> chaocipher&{left:'HXUCZVAMDSLKPEFJRIGTWOBNYQ', right:'PTLNBQDEOYSFAVZKGJRIHWXUMC',decode:1} -> '$;
' -> !OUT::write
Output:
OAHQHCNYNXTSZJRRHJBYHQKSOUJY
WELLDONEISBETTERTHANWELLSAID

Visual Basic .NET

Translation of: C#
Module Module1

    ReadOnly L_ALPHABET As String = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
    ReadOnly R_ALPHABET As String = "PTLNBQDEOYSFAVZKGJRIHWXUMC"

    Enum Mode
        ENCRYPT
        DECRYPT
    End Enum

    Function Exec(text As String, mode As Mode, Optional showSteps As Boolean = False) As String
        Dim left = L_ALPHABET.ToCharArray()
        Dim right = R_ALPHABET.ToCharArray()
        Dim eText(text.Length - 1) As Char
        Dim temp(25) As Char

        For i = 0 To text.Length - 1
            If showSteps Then Console.WriteLine("{0} {1}", String.Join("", left), String.Join("", right))
            Dim index As Integer
            If mode = Mode.ENCRYPT Then
                index = Array.IndexOf(right, text(i))
                eText(i) = left(index)
            Else
                index = Array.IndexOf(left, text(i))
                eText(i) = right(index)
            End If
            If i = text.Length - 1 Then Exit For

            'permute left

            For j = index To 25
                temp(j - index) = left(j)
            Next
            For j = 0 To index - 1
                temp(26 - index + j) = left(j)
            Next
            Dim store = temp(1)
            For j = 2 To 13
                temp(j - 1) = temp(j)
            Next
            temp(13) = store
            temp.CopyTo(left, 0)

            'permute right

            For j = index To 25
                temp(j - index) = right(j)
            Next
            For j = 0 To index - 1
                temp(26 - index + j) = right(j)
            Next
            store = temp(0)
            For j = 1 To 25
                temp(j - 1) = temp(j)
            Next
            temp(25) = store
            store = temp(2)
            For j = 3 To 13
                temp(j - 1) = temp(j)
            Next
            temp(13) = store
            temp.CopyTo(right, 0)
        Next

        Return eText
    End Function

    Sub Main()
        Dim plainText = "WELLDONEISBETTERTHANWELLSAID"
        Console.WriteLine("The original plaintext is : {0}", plainText)
        Console.WriteLine(vbNewLine + "The left and right alphabets after each permutation during encryption are :" + vbNewLine)
        Dim cipherText = Exec(plainText, Mode.ENCRYPT, True)
        Console.WriteLine(vbNewLine + "The ciphertext is : {0}", cipherText)
        Dim plainText2 = Exec(cipherText, Mode.DECRYPT)
        Console.WriteLine(vbNewLine + "The recovered plaintext is : {0}", plainText2)
    End Sub

End Module
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are :

HXUCZVAMDSLKPEFJRIGTWOBNYQ PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

V (Vlang)

Translation of: Go
type Mode = int
const(
    encrypt = Mode(0)
    decrypt = Mode(1)
)
 
const(
    l_alphabet = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
    r_alphabet = "PTLNBQDEOYSFAVZKGJRIHWXUMC"
)
 
fn chao(text string, mode Mode, show_steps bool) string {
    len := text.len
    if text.bytes().len != len {
        println("Text contains non-ASCII characters")
        return ""
    }
    mut left  := l_alphabet
    mut right := r_alphabet
    mut e_text := []u8{len: len}
    mut temp  := []u8{len: 26}
 
    for i in 0..len {
        if show_steps {
            println('$left $right')
        }
        mut index := 0
        if mode == encrypt {
            index = right.index_u8(text[i])
            e_text[i] = left[index]
        } else {
            index = left.index_u8(text[i])
            e_text[i] = right[index]
        }
        if i == len - 1 {
            break
        }
 
        // permute left
        for j in index..26 {
            temp[j - index] = left[j]
        }
        for j in 0..index {
            temp[26 - index + j] = left[j]
        }
        mut store := temp[1]
        for j in 2..14 {
            temp[j - 1] = temp[j]
        }
        temp[13] = store
        left = temp.bytestr()
 
        // permute right
 
        for j in index..26 {
            temp[j - index] = right[j]
        }
        for j in 0..index {
            temp[26 - index + j] = right[j]
        }
        store = temp[0]
        for j in 1..26 {
            temp[j - 1] = temp[j]
        }
        temp[25] = store
        store = temp[2]
        for j in 3..14 {
            temp[j - 1] = temp[j]
        }
        temp[13] = store
        right = temp.bytestr()
    }
 
    return e_text.bytestr()
}
 
fn main() {
    plain_text := "WELLDONEISBETTERTHANWELLSAID"
    println("The original plaintext is : $plain_text")
    print("\nThe left and right alphabets after each permutation ")
    println("during encryption are :\n")
    cypher_text := chao(plain_text, encrypt, true)
    println("\nThe ciphertext is : $cypher_text")
    plain_text2 := chao(cypher_text, decrypt, false)
    println("\nThe recovered plaintext is : $plain_text2")
}
Output:
Same as Kotlin Entry

Wren

Translation of: Kotlin
class Chao {
    static encrypt { 0 }
    static decrypt { 1 }

    static exec(text, mode, showSteps) {
        var len = text.count
        if (len != text.bytes.count) Fiber.abort("Text contains non-ASCII characters.")
        var left  = "HXUCZVAMDSLKPEFJRIGTWOBNYQ"
        var right = "PTLNBQDEOYSFAVZKGJRIHWXUMC"
        var eText = List.filled(len, "")
        var temp  = List.filled(26, "")
        for (i in 0...len) {
            if (showSteps) System.print("%(left)  %(right)")
            var index
            if (mode == Chao.encrypt) {
                index = right.indexOf(text[i])
                eText[i] = left[index]
            } else {
                index = left.indexOf(text[i])
                eText[i] = right[index]
            }
            if (i == len - 1) break

            // permute left
            for (j in index..25) temp[j-index] = left[j]
            for (j in 0...index) temp[26-index+j] = left[j]
            var store = temp[1]
            for (j in 2..13) temp[j-1] = temp[j]
            temp[13] = store
            left = temp.join()

            // permute right
            for (j in index..25) temp[j-index] = right[j]
            for (j in 0...index) temp[26-index+j] = right[j]
            store = temp[0]
            for (j in 1..25) temp[j-1] = temp[j]
            temp[25] = store
            store = temp[2]
            for (j in 3..13) temp[j-1] = temp[j]
            temp[13] = store
            right = temp.join()
        }
        return eText.join()
    }
}

var plainText = "WELLDONEISBETTERTHANWELLSAID"
System.print("The original plaintext is : %(plainText)")
System.write("\nThe left and right alphabets after each permutation ")
System.print("during encryption are :\n")
var cipherText = Chao.exec(plainText, Chao.encrypt, true)
System.print("\nThe ciphertext is : %(cipherText)")
var plainText2 = Chao.exec(cipherText, Chao.decrypt, false)
System.print("\nThe recovered plaintext is : %(plainText2)")
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are :

HXUCZVAMDSLKPEFJRIGTWOBNYQ  PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW  XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV  OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ  NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY  NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE  JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU  YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO  BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP  RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO  MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ  AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM  IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB  RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ  LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF  LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ  RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ  YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY  LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF  MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD  VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE  HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX  RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON  SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS  NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR  NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW  WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP  GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF  OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

XPL0

Translation of: C
include xpllib; \For StrLen, StrCopy, Print

func StrChar(Str, C);
char Str, C;
[loop   [if Str(0) = 0 then return 0;
        if Str(0) = C then return Str;
        Str:= Str+1;
        ];
];

def \CMode\ ENCRYPT, DECRYPT;
char L_alphabet, R_alphabet;

proc Chao(In, Out, Mode, Show_steps);
char In, Out, Mode, Show_steps;
int Len, I, J, Index;
char Store, Left(27), Right(27), Temp(27);
[Len:= StrLen(In);
StrCopy(Left, L_alphabet);
StrCopy(Right, R_alphabet);
Temp(26):= 0;

for I:= 0 to Len-1 do
        [if Show_steps then Print("%s  %s\n", Left, Right);
        if Mode = ENCRYPT then
                [Index:= StrChar(Right, In(I)) - Right;
                Out(I):= Left(Index);
                ]
        else    [Index:= StrChar(Left, In(I)) - Left;
                Out(I):= Right(Index);
                ];
        if I = Len-1 then return;

        \Permute Left
        for J:= Index to 26-1 do Temp(J-Index):= Left(J);
        for J:= 0 to Index-1 do Temp(26-Index+J):= Left(J);
        Store:= Temp(1);
        for J:= 2 to 14-1 do Temp(J-1):= Temp(J);
        Temp(13):= Store;
        StrCopy(Left, Temp);

        \Permute Right
        for J:= Index to 26-1 do Temp(J-Index):= Right(J);
        for J:= 0 to Index-1 do Temp(26-Index+J):= Right(J);
        Store:= Temp(0);
        for J:= 1 to 26-1 do Temp(J-1):= Temp(J);
        Temp(25):= Store;
        Store:= Temp(2);
        for J:= 3 to 14-1 do Temp(J-1):= Temp(J);
        Temp(13):= Store;
        StrCopy(Right, Temp);
        ];
];

char Plain_text, Cipher_text, Plain_text2;
[L_alphabet:= "HXUCZVAMDSLKPEFJRIGTWOBNYQ";
 R_alphabet:= "PTLNBQDEOYSFAVZKGJRIHWXUMC";
 Plain_text:= "WELLDONEISBETTERTHANWELLSAID";
Cipher_text:= MAlloc(StrLen(Plain_text) + 1);
Plain_text2:= MAlloc(StrLen(Plain_text) + 1);
Print("The original plaintext is : %s\n", Plain_text);
Print("\nThe left and right alphabets after each permutation during encryption are :\n\n");
Chao(Plain_text, Cipher_text, ENCRYPT, true);
Print("\nThe ciphertext is : %s\n", Cipher_text);
Chao(Cipher_text, Plain_text2, DECRYPT, false);
Print("\nThe recovered plaintext is : %s\n", Plain_text2);
Release(Cipher_text);
Release(Plain_text2);
]
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are :

HXUCZVAMDSLKPEFJRIGTWOBNYQ  PTLNBQDEOYSFAVZKGJRIHWXUMC
ONYQHXUCZVAMDBSLKPEFJRIGTW  XUCPTLNBQDEOYMSFAVZKGJRIHW
ADBSLKPEFJRIGMTWONYQHXUCZV  OYSFAVZKGJRIHMWXUCPTLNBQDE
HUCZVADBSLKPEXFJRIGMTWONYQ  NBDEOYSFAVZKGQJRIHMWXUCPTL
QUCZVADBSLKPEHXFJRIGMTWONY  NBEOYSFAVZKGQDJRIHMWXUCPTL
HFJRIGMTWONYQXUCZVADBSLKPE  JRHMWXUCPTLNBIEOYSFAVZKGQD
CVADBSLKPEHFJZRIGMTWONYQXU  YSAVZKGQDJRHMFWXUCPTLNBIEO
NQXUCVADBSLKPYEHFJZRIGMTWO  BIOYSAVZKGQDJERHMFWXUCPTLN
YHFJZRIGMTWONEQXUCVADBSLKP  RHFWXUCPTLNBIMOYSAVZKGQDJE
NQXUCVADBSLKPEYHFJZRIGMTWO  MOSAVZKGQDJERYHFWXUCPTLNBI
XCVADBSLKPEYHUFJZRIGMTWONQ  AVKGQDJERYHFWZXUCPTLNBIMOS
TONQXCVADBSLKWPEYHUFJZRIGM  IMSAVKGQDJERYOHFWZXUCPTLNB
SKWPEYHUFJZRILGMTONQXCVADB  RYHFWZXUCPTLNOBIMSAVKGQDJE
ZILGMTONQXCVARDBSKWPEYHUFJ  LNBIMSAVKGQDJOERYHFWZXUCPT
JILGMTONQXCVAZRDBSKWPEYHUF  LNIMSAVKGQDJOBERYHFWZXUCPT
RBSKWPEYHUFJIDLGMTONQXCVAZ  RYFWZXUCPTLNIHMSAVKGQDJOBE
RSKWPEYHUFJIDBLGMTONQXCVAZ  YFZXUCPTLNIHMWSAVKGQDJOBER
HFJIDBLGMTONQUXCVAZRSKWPEY  LNHMWSAVKGQDJIOBERYFZXUCPT
JDBLGMTONQUXCIVAZRSKWPEYHF  MWAVKGQDJIOBESRYFZXUCPTLNH
BGMTONQUXCIVALZRSKWPEYHFJD  VKQDJIOBESRYFGZXUCPTLNHMWA
YFJDBGMTONQUXHCIVALZRSKWPE  HMAVKQDJIOBESWRYFGZXUCPTLN
HIVALZRSKWPEYCFJDBGMTONQUX  RYGZXUCPTLNHMFAVKQDJIOBESW
QXHIVALZRSKWPUEYCFJDBGMTON  SWYGZXUCPTLNHRMFAVKQDJIOBE
KPUEYCFJDBGMTWONQXHIVALZRS  NHMFAVKQDJIOBRESWYGZXUCPTL
SPUEYCFJDBGMTKWONQXHIVALZR  NHFAVKQDJIOBRMESWYGZXUCPTL
OQXHIVALZRSPUNEYCFJDBGMTKW  WYZXUCPTLNHFAGVKQDJIOBRMES
UEYCFJDBGMTKWNOQXHIVALZRSP  GVQDJIOBRMESWKYZXUCPTLNHFA
JBGMTKWNOQXHIDVALZRSPUEYCF  OBMESWKYZXUCPRTLNHFAGVQDJI

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID

zkl

Translation of: Raku
class Chao{
   var [const private] lAlphabet = "HXUCZVAMDSLKPEFJRIGTWOBNYQ",
		       rAlphabet = "PTLNBQDEOYSFAVZKGJRIHWXUMC";
   fcn encode(text){ code(text,encodeL); }
   fcn decode(text){ code(text,decodeL); }
   // reset alphabets each [en|de]code and maintain re-entrancy
   fcn code(text,f){ text.apply(f,Data(Void,lAlphabet),Data(Void,rAlphabet)) }
   fcn [private] encodeL(letter,left,right){  // encode a letter
      index:=right.index(letter);
      enc  :=left[index].toChar();
      permute(left,right,index);
      println(left.text," ",right.text,"  ",index);
      enc
   }
   fcn [private] decodeL(letter,left,right){  // decode a letter
      index:=left.index(letter);
      dec  :=right[index].toChar();
      permute(left,right,index);
      dec
   }
   fcn [private] permute(left,right,index){
      left.append(left.pop(0,index));		// rotate index times
      left.insert(13,left.pop(1));		// rotate [1..13] once

      right.append(right.pop(0,index+1)); # rotate index+1 times, idx==25==noop
      right.insert(13,right.pop(2));		// rotate [2..13] once
   }
}
plainText:="WELLDONEISBETTERTHANWELLSAID";
println("The original plaintext is : ",plainText);
println("\nThe left and right alphabets after each permutation"
         " during encryption are:");
cipherText:=Chao.encode(plainText);
println("\nThe ciphertext is : ",cipherText);

plainText2:=Chao.decode(cipherText);
println("\nThe recovered plaintext is : ",plainText2);
Output:
The original plaintext is : WELLDONEISBETTERTHANWELLSAID

The left and right alphabets after each permutation during encryption are:
ONYQHXUCZVAMDBSLKPEFJRIGTW XUCPTLNBQDEOYMSFAVZKGJRIHW  21
ADBSLKPEFJRIGMTWONYQHXUCZV OYSFAVZKGJRIHMWXUCPTLNBQDE  10
HUCZVADBSLKPEXFJRIGMTWONYQ NBDEOYSFAVZKGQJRIHMWXUCPTL  20
QUCZVADBSLKPEHXFJRIGMTWONY NBEOYSFAVZKGQDJRIHMWXUCPTL  25
HFJRIGMTWONYQXUCZVADBSLKPE JRHMWXUCPTLNBIEOYSFAVZKGQD  13
CVADBSLKPEHFJZRIGMTWONYQXU YSAVZKGQDJRHMFWXUCPTLNBIEO  15
NQXUCVADBSLKPYEHFJZRIGMTWO BIOYSAVZKGQDJERHMFWXUCPTLN  21
YHFJZRIGMTWONEQXUCVADBSLKP RHFWXUCPTLNBIMOYSAVZKGQDJE  13
NQXUCVADBSLKPEYHFJZRIGMTWO MOSAVZKGQDJERYHFWXUCPTLNBI  12
XCVADBSLKPEYHUFJZRIGMTWONQ AVKGQDJERYHFWZXUCPTLNBIMOS  2
TONQXCVADBSLKWPEYHUFJZRIGM IMSAVKGQDJERYOHFWZXUCPTLNB  21
SKWPEYHUFJZRILGMTONQXCVADB RYHFWZXUCPTLNOBIMSAVKGQDJE  10
ZILGMTONQXCVARDBSKWPEYHUFJ LNBIMSAVKGQDJOERYHFWZXUCPT  10
JILGMTONQXCVAZRDBSKWPEYHUF LNIMSAVKGQDJOBERYHFWZXUCPT  25
RBSKWPEYHUFJIDLGMTONQXCVAZ RYFWZXUCPTLNIHMSAVKGQDJOBE  14
RSKWPEYHUFJIDBLGMTONQXCVAZ YFZXUCPTLNIHMWSAVKGQDJOBER  0
HFJIDBLGMTONQUXCVAZRSKWPEY LNHMWSAVKGQDJIOBERYFZXUCPT  7
JDBLGMTONQUXCIVAZRSKWPEYHF MWAVKGQDJIOBESRYFZXUCPTLNH  2
BGMTONQUXCIVALZRSKWPEYHFJD VKQDJIOBESRYFGZXUCPTLNHMWA  2
YFJDBGMTONQUXHCIVALZRSKWPE HMAVKQDJIOBESWRYFGZXUCPTLN  21
HIVALZRSKWPEYCFJDBGMTONQUX RYGZXUCPTLNHMFAVKQDJIOBESW  13
QXHIVALZRSKWPUEYCFJDBGMTON SWYGZXUCPTLNHRMFAVKQDJIOBE  23
KPUEYCFJDBGMTWONQXHIVALZRS NHMFAVKQDJIOBRESWYGZXUCPTL  10
SPUEYCFJDBGMTKWONQXHIVALZR NHFAVKQDJIOBRMESWYGZXUCPTL  25
OQXHIVALZRSPUNEYCFJDBGMTKW WYZXUCPTLNHFAGVKQDJIOBRMES  15
UEYCFJDBGMTKWNOQXHIVALZRSP GVQDJIOBRMESWKYZXUCPTLNHFA  12
JBGMTKWNOQXHIDVALZRSPUEYCF OBMESWKYZXUCPRTLNHFAGVQDJI  5
YFJBGMTKWNOQXCHIDVALZRSPUE JIBMESWKYZXUCOPRTLNHFAGVQD  23

The ciphertext is : OAHQHCNYNXTSZJRRHJBYHQKSOUJY

The recovered plaintext is : WELLDONEISBETTERTHANWELLSAID