Jump to content

Extended Straddling Checkerboard

From Rosetta Code
Extended Straddling Checkerboard is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

An extended Straddling Checkerboard, is like the regular Straddling checkerboard, but allows word dictionaries and arbitrary functional codes such as FIGURE, where you can specify a number literally.

Task

Implement encoding and decoding of a message using the extended straddling checkerboard, CT-37w, as described in the reference below.

You may switch the codes for F/L (99) and SUPP (98) to help differentiate the code for '9' from that of '999' and, if you do that, then digits only needed to be doubled rather than tripled. So we would then have the following checkerboard:

  A   E   I   N   O   T  CODE
  0   1   2   3   4   5   6

  B   C   D   F   G   H   J   K   L   M
 70  71  72  73  74  75  76  77  78  79

  P   Q   R   S   U   V   W   X   Y   Z
 80  81  82  83  84  85  86  87  88  89

SPC (.) ACK REQ MSG  RV GRD SND F/L SUP
 90  91  92  93  94  95  96  97  98  99

  0   1   2   3   4   5   6   7   8   9
 00  11  22  33  44  55  66  77  88  99

There is no need to create a word dictionary for CODE (6). It suffices to just include CODE followed by some 3 digit number in the message to be encoded.

Test your solution by encoding and decoding the message:

'Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March'

Related task
Reference

FreeBASIC

Translation of: Wren
Const As String efigs = "0123456789"
Const As String spc_ = "90"
Const As String dot = "91"
Const As String fsl = "98"

Const As String row1 = "AEINOT"
Const As String row2 = "BCDFGHJKLM"
Const As String row3 = "PQRSUVWXYZ"
Const As String row4 = " ."

Dim Shared As String emapKeys(Len(row1) + Len(row2) + Len(row3) + Len(row4))
Dim Shared As String emapValues(Len(row1) + Len(row2) + Len(row3) + Len(row4))
Dim As Integer i, index = 0

For i = 1 To Len(row1)
    emapKeys(index) = Mid(row1, i, 1)
    emapValues(index) = Str(i - 1)
    index += 1
Next
For i = 1 To Len(row2)
    emapKeys(index) = Mid(row2, i, 1)
    emapValues(index) = Str(70 + i - 1)
    index += 1
Next
For i = 1 To Len(row3)
    emapKeys(index) = Mid(row3, i, 1)
    emapValues(index) = Str(80 + i - 1)
    index += 1
Next
For i = 1 To Len(row4)
    emapKeys(index) = Mid(row4, i, 1)
    emapValues(index) = Str(90 + i - 1)
    index += 1
Next

Function getEmapValue(Byval key As String) As String
    For i As Integer = 0 To Ubound(emapKeys)
        If emapKeys(i) = key Then Return emapValues(i)
    Next
    Return ""
End Function

Type Map
    Dim As String keys(100)
    Dim As String values(100)
End Type

Dim Shared As Map ewords
ewords.keys(0) = "ACK"  : ewords.values(0) = "92"
ewords.keys(1) = "REQ"  : ewords.values(1) = "93"
ewords.keys(2) = "MSG"  : ewords.values(2) = "94"
ewords.keys(3) = "RV"   : ewords.values(3) = "95"
ewords.keys(4) = "GRID" : ewords.values(4) = "96"
ewords.keys(5) = "SEND" : ewords.values(5) = "97"
ewords.keys(6) = "SUPP" : ewords.values(6) = "99"

Function getValue(Byval key As String, Byval map As Map) As String
    For i As Integer = 0 To Ubound(map.keys)
        If map.keys(i) = key Then Return map.values(i)
    Next
    Return ""
End Function

Dim Shared As String*6 drow1 = "012345"

Dim Shared As String dmapKeys(Len(drow1))
Dim Shared As String dmapValues(Len(drow1))

For i = 1 To Len(drow1)
    dmapKeys(i - 1) = Mid(drow1, i, 1)
    dmapValues(i - 1) = emapKeys(i - 1)
Next

Dim Shared As Map dwords
dwords.keys(0) = "92" : dwords.values(0) = "ACK"
dwords.keys(1) = "93" : dwords.values(1) = "REQ"
dwords.keys(2) = "94" : dwords.values(2) = "MSG"
dwords.keys(3) = "95" : dwords.values(3) = "RV"
dwords.keys(4) = "96" : dwords.values(4) = "GRID"
dwords.keys(5) = "97" : dwords.values(5) = "SEND"
dwords.keys(6) = "99" : dwords.values(6) = "SUPP"

Function getDmapValue(Byval key As String) As String
    For i As Integer = 0 To Ubound(dmapKeys)
        If dmapKeys(i) = key Then Return dmapValues(i)
    Next
    Return ""
End Function

Function keyExists(Byval key As String, keys() As String) As Boolean
    For i As Integer = 0 To Ubound(keys)
        If keys(i) = key Then Return True
    Next
    Return False
End Function

Function encode(Byval s As String) As String
    s = Ucase(s)
    Dim As String res = ""
    Dim As String word = ""
    Dim As Integer wc = Len(s)
    
    For i As Integer = 1 To wc
        Dim As String c = Mid(s, i, 1)
        If c <> " " And i <> wc Then
            word &= c
        Else
            If i = wc Then word &= c
            Dim As String add_ = ""
            If keyExists(word, ewords.keys()) Then
                add_ = getValue(word, ewords)
            Elseif keyExists(Left(word, Len(word) - 1), ewords.keys()) And Right(word, 1) = "." Then
                add_ = getValue(Left(word, Len(word) - 1), ewords) & dot
            Elseif Left(word, 4) = "CODE" Then
                add_ = "6" & Mid(word, 5)
            Else
                Dim As Boolean figs = False
                For j As Integer = 1 To Len(word)
                    Dim As String c = Mid(word, j, 1)
                    If Instr(efigs, c) > 0 Then
                        If figs Then
                            add_ &= c & c
                        Else
                            figs = True
                            add_ &= fsl & c & c
                        End If
                    Else
                        Dim As String ec = getEmapValue(c)
                        If ec = "" Then
                            Print "Message contains unrecognized character '" + c + "'."
                            End
                        End If
                        If figs Then
                            add_ &= fsl & ec
                            figs = False
                        Else
                            add_ &= ec
                        End If
                    End If
                Next
                If figs And i < wc Then add_ &= fsl
            End If
            res &= add_
            If i < wc Then res &= spc_
            word = ""
        End If
    Next
    Return res
End Function

Function decode(Byval s As String) As String
    Dim As String res = ""
    Dim As Integer sc = Len(s)
    Dim As Integer figs = 0
    Dim As Integer i = 1
    
    While i <= sc
        Dim As String c = Mid(s, i, 1)
        Dim As String twoChars = Mid(s, i, 2)
        
        If figs Then
            If twoChars = fsl Then
                figs = 0
            Else
                res &= c
            End If
            i += 2
        Elseif Instr(drow1, c) > 0 Then
            res &= emapKeys(Val(c))
            i += 1
        Elseif c = "6" Then
            res &= "CODE" & Mid(s, i + 1, 3)
            i += 4
        Elseif c = "7" Or c = "8" Then
            Dim As String mapKey = twoChars
            For j As Integer = 0 To Ubound(emapKeys)
                If emapValues(j) = mapKey Then
                    res &= emapKeys(j)
                    Exit For
                End If
            Next
            i += 2
        Elseif c = "9" Then
            Dim As String d = Mid(s, i + 1, 1)
            If d = "0" Then
                res &= " "
            Elseif d = "1" Then
                res &= "."
            Elseif d = "8" Then
                figs = Not figs
            Else
                res &= getValue(c & d, dwords)
            End If
            i += 2
        End If
    Wend
    
    Return res
End Function

Dim As String msg = "Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March"
Print !"Message:\n"; msg
Dim As String enc = encode(msg)
Print !"\nEncoded:\n"; enc
Dim As String dec = decode(enc)
Print !"\nDecoded:\n"; dec

Sleep
Output:
Same as Wren entry.

jq

Adapted from Wren

Works with jq, the C implementation of jq

Works with gojq, the Go implementation of jq

Works with jaq, the Rust implementation of jq

### Generic utility
# Emit a stream of the constituent codepoints:
def chars: explode[] | [.] | implode;

### The checkerboard
def efigs: "0123456789";

def drow1: "012345";

def checkerboard:
  def row1: "AEINOT";
  def row2: "BCDFGHJKLM";
  def row3: "PQRSUVWXYZ";
  def row4: " .";
  { ewords: {
     "SPC":  "90",  "DOT": "91",
     "ACK":  "92",  "REQ": "93", "MSG": "94", "RV": "95",
     "GRID": "96", "SEND": "97", "FSL": "98", "SUPP": "99"
    },
    emap: {},
    dmap: {},
    dwords:{}
  }
  | reduce range(0; row1|length) as $i (.; .emap[row1[$i:$i+1]] = ($i|tostring) )
  | reduce range(0; row2|length) as $i (.; .emap[row2[$i:$i+1]] = ((70 + $i)|tostring))
  | reduce range(0; row3|length) as $i (.; .emap[row3[$i:$i+1]] = ((80 + $i)|tostring))
  | reduce range(0; row4|length) as $i (.; .emap[row4[$i:$i+1]] = ((90 + $i)|tostring))

  | reduce (.emap|keys[])   as $k (.; .dmap[.emap[$k]] = $k)
  | reduce (.ewords|keys[]) as $k (.; .dwords[.ewords[$k]] = $k) ;

def encode:
  (ascii_upcase|split(" ")) as $words
  | ($words|length) as $wc
  | checkerboard
  | .res = ""
  | reduce range(0; $wc) as $i (.;
      $words[$i] as $word
      | .add = ""
      |  if .ewords[$word]
         then .add = .ewords[$word]
         elif .ewords[$word[0:-1]] and ($word|endswith("."))
         then .add = .ewords[$word[0:-1]] + .ewords["DOT"]
         elif $word|startswith("CODE")
         then .add = "6" + $word[4:]
         else .figs = false
         | reduce ($word|chars) as $c (.;
             if (efigs|contains($c))
             then if .figs
                  then .add += 2 * $c
                  else .figs = true
                  | .add += .ewords["FSL"] + 2 * $c
                  end
             else .emap[$c] as $ec
             | if ($ec|not) 
               then  "Message contains unrecognized character '\($c)'" | error
               else if .figs
                    then .add += .ewords["FSL"] + $ec
                    | .figs = false
                    else .add += $ec
                    end
                end
             end )
         | if .figs and ($i < $wc - 1)
           then .add += .ewords["FSL"]
           else .
           end
         end 
         | .res += .add
         | if ($i < $wc - 1) then .res += .ewords["SPC"] else . end
  )
  | .res ;

def decode:
  {s: .} + checkerboard
  | .ewords["FSL"] as $fsl
  | .res = ""
  | .figs = false
  | until (.s == "";
      .s[0:1] as $c
      | .ix = -1
      | if .figs
        then if (.s | startswith($fsl) | not)
             then .res += $c
             else .figs = false
             end
             | .s |= .[2:]
        else .ix = (drow1|index($c))
        | if .ix and .ix >= 0
          then .res += .dmap[drow1[.ix:.ix+1]]
          | .s |= .[1:]
          elif $c == "6"
          then .res += "CODE" + .s[1:4]
          | .s |= .[4:]
          elif $c == "7" or $c == "8"
          then .s[1:2] as $d
          | .res += .dmap[$c + $d]
          | .s |= .[2:]
          elif $c == "9"
          then .s[1:2] as $d
          | if $d == "0"
            then .res += " "
            elif $d == "1"
            then .res +=  "."
            elif $d == "8"
            then .figs |= not
            else .res += .dwords[$c + $d]
            end
            | .s |= .[2:]
          end
         end )
  | .res ;

### Demonstration
def demo:
  "Message:\n\(.)",
   (encode
    | "\nEncoded:\n\(.)",
      "\nDecoded:\n\(decode)" );

"Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March"
| demo
Output:
Message:
Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March

Encoded:
0727923909290884848290949190629190979073848257518290982200000098909990549075819070889098119890790827175

Decoded:
ADMIN ACK YOUR MSG. CODE291 SEND FURTHER 2000 SUPP TO HQ BY 1 MARCH

Julia

Translation of: Wren
const row1, row2, row3, row4 = "AEINOT", "BCDFGHJKLM", "PQRSUVWXYZ", " ."
const emap = Dict{String,String}()
for (row, k) in [(row1, -1), (row2, 69), (row3, 79), (row4, 89)]
    for i in eachindex(row)
        emap[string(row[i])] = string(i + k)
    end
end
const dmap = Dict{String,String}(v => k for (k, v) in emap)

const ewords = Dict{String,String}(
    "ACK" => "92",
    "REQ" => "93",
    "MSG" => "94",
    "RV" => "95",
    "GRID" => "96",
    "SEND" => "97",
    "SUPP" => "99",
)
const dwords = Dict{String,String}(v => k for (k, v) in ewords)

const efigs, spc, dot, fsl, drow1 = "0123456789", "90", "91", "98", "012345"

function encode(s)
    s, res = uppercase(s), ""
    words = split(s, r"\s")
    wc = length(words)
    for i = 1:wc
        word, add = words[i], ""
        if haskey(ewords, word)
            add = ewords[word]
        elseif haskey(ewords, word[begin:end-1]) && word[end] == "."
            add = ewords[word[begin:end-1]] * dot
        elseif startswith(word, "CODE")
            add = "6" * word[begin+4:end]
        else
            figs = false
            for c in word
                if contains(efigs, c)
                    if figs
                        add *= c^2
                    else
                        figs = true
                        add *= fsl * c^2
                    end
                else
                    ec = get(emap, string(c), "")
                    isempty(ec) && error("Message contains unrecognized character $c.")
                    if figs
                        add *= fsl * ec
                        figs = false
                    else
                        add *= ec
                    end
                end
            end
            if figs && i <= wc - 1
                add *= fsl
            end
        end
        res *= add
        if i <= wc - 1
            res *= spc
        end
    end
    return res
end

function decode(s)
    res, sc, figs, i = "", length(s), false, 1
    while i <= sc
        ch = s[i]
        c = string(ch)
        if figs
            if s[i:i+1] != fsl
                res *= c
                i += 2
            else
                figs = false
                i += 2
            end
        elseif !((ix = findfirst(==(ch), drow1)) isa Nothing)
            res *= dmap[string(drow1[ix])]
            i += 1
        elseif c == "6"
            res *= "CODE" * s[i+1:i+3]
            i += 4
        elseif c == "7" || c == "8"
            d = string(s[i+1])
            res *= dmap[c*d]
            i += 2
        elseif c == "9"
            d = string(s[i+1])
            if d == "0"
                res *= " "
            elseif d == "1"
                res *= "."
            elseif d == "8"
                figs = !figs
            else
                res *= dwords[c*d]
            end
            i += 2
        end
    end
    return res
end

const msg = "Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March"
println("Message:\n$msg")
enc = encode(msg)
println("\nEncoded:\n$enc")
dec = decode(enc)
println("\nDecoded:\n$dec")
Output:
Message:
Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March

Encoded:
07279239092908848482907983749190629190979073848257518290982200000098909990549075819070889098119890790827175

Decoded:
ADMIN ACK YOUR MSG. CODE291 SEND FURTHER 2000 SUPP TO HQ BY 1 MARCH

Phix

Translation of: Julia

Note this includes the two tiny tweaks to encode digits in two characters, as per "Moot point" on the talk page.

with javascript_semantics
constant emap = new_dict(),
         dmap = new_dict(),
         ewds = new_dict(),
         dwds = new_dict(),
         spc = "90", dot = "91", fsl = "98"
for d in {{"AEINOT",-1},{"BCDFGHJKLM",69},{"PQRSUVWXYZ",79},{" .",89}} do
    integer k = d[2]
    for i,ch in d[1] do
        string ik = sprintf("%d",i+k)
        setd(ch,ik,emap)
        setd(ik,ch,dmap)
    end for
end for
for d in {{"ACK","92"},{"REQ","93"},{"MSG","94"},{"RV","95"},
          {"GRID","96"},{"SEND","97"},{"SUPP","99"}} do
    string {k,v} = d
    setd(k,v,ewds)          
    setd(v,k,dwds)          
end for

function encode(string s)
    string res = ""
    sequence words = split(upper(s))
    integer wc = length(words)
    for i=1 to wc do
        string wrd = words[i], a = ""
        if getd_index(wrd,ewds) then
            a = getd(wrd,ewds)
        elsif getd_index(wrd[1..-2]) and wrd[$] == '.' then
            a = getd(wrd[1..-2],ewds) & dot
        elsif begins("CODE",wrd) then
            a = "6" & wrd[5..$]
        else
            bool figs = false
            for c in wrd do
                if find(c,"0123456789") then -- (efigs)
                    if not figs then figs = true; a &= fsl end if
--                  a &= repeat(c,3)
                    a &= repeat(c,2)
                elsif not getd_index(c,emap) then
                    throw("Message contains unrecognized character %c.",{c})
                else
                    if figs then figs = false; a &= fsl end if
                    a &= getd(c,emap)
                end if
            end for
            if figs and i<=wc-1 then a &= fsl end if
        end if
        res &= a
        if i <= wc-1 then res &= spc end if
    end for
    return res
end function

function decode(string s)
    string res = ""
    integer sc = length(s), figs = false, i = 1
    while i <= sc do
        integer c = s[i]
        if figs then
            if s[i..i+1] != fsl then
                res &= c
--              i += 3
                i += 2
            else
                figs = false
                i += 2
            end if
        elsif find(c,"012345") then -- row 1
            res &= getd(c&"",dmap)
            i += 1
        elsif c == '6' then
            res &= "CODE" & s[i+1..i+3]
            i += 4
        elsif c == '7'
           or c == '8' then
            res &= getd(s[i..i+1],dmap)
            i += 2
        elsif c == '9' then
            integer d = s[i+1]
            if d == '0' then
                res &= " "
            elsif d == '1' then
                res &= "."
            elsif d == '8' then
                figs = not figs
            else
                res &= getd(s[i..i+1],dwds)
            end if
            i += 2
        end if
    end while
    return res
end function

constant msg = "Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March",
         enc = encode(msg),
         unc = decode(enc)
printf(1,"Message:\n%s\n\nEncoded:\n%s\n\nDecoded:\n%s\n",{msg,enc,unc})
Output:
Message:
Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March

Encoded:
07279239092908848482907983749190629190979073848257518290982200000098909990549075819070889098119890790827175

Decoded:
ADMIN ACK YOUR MSG. CODE291 SEND FURTHER 2000 SUPP TO HQ BY 1 MARCH

Python

See the discussion for the different checkerboard table handling.

""" rosettacode.org/wiki/Extended_Straddling_Checkerboard """

from functools import reduce

WDICT = {
    'CODE': 'κ',
    'ACK': 'α',
    'REQ': 'ρ',
    'MSG': 'μ',
    'RV': 'ν',
    'GRID': 'γ',
    'SEND': 'σ',
    'SUPP': 'π',
}
# reversed WDICT for reverse lookup on decode
SDICT = {v: k for (k, v) in WDICT.items()}

# CT37w at https://www.ciphermachinesandcryptology.com/en/table.htm
CT37w = [['',  'A', 'E', 'I', 'N', 'O', 'T', 'κ', '',  '',  '',],
         ['7', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',],
         ['8', 'P', 'Q', 'R', 'S', 'U', 'V', 'W', 'X', 'Y', 'Z',],
         ['9', ' ', '.', 'α', 'ρ', 'μ', 'ν', 'γ', 'σ', 'π', '/'],]

# Modified CT37w: web site CT37w, but exchange '/' (FIG) char and 'π'
# to help differentiate the '999' encoding for a '9' from a terminator code
CT37w_mod = [['',  'A', 'E', 'I', 'N', 'O', 'T', 'κ', '',  '',  '',],
             ['7', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',],
             ['8', 'P', 'Q', 'R', 'S', 'U', 'V', 'W', 'X', 'Y', 'Z',],
             ['9', ' ', '.', 'α', 'ρ', 'μ', 'ν', 'γ', 'σ', '/', 'π',],]


def xcb_encode(message, table=CT37w, code='κ', wdict=WDICT):
    """
        Encode with extended straddling checkerboard. Default checkerboard is
        CT37w at https://www.ciphermachinesandcryptology.com/en/table.htm
        The numeric mode has the numbers as repeated in triplicate
        The CODE mode expects a 3-digit numeric code
    """
    encoded = []
    numericmode, codemode = False, False
    codemodecount = 0
    if table[-1][-1] == '/':
        nchangemode = '99'
        digit_repeats = 3
    else:
        nchangemode = '98'
        digit_repeats = 2

    # replace terms found in dictionary with a single char symbol that is in the table
    s = reduce(lambda x, p: x.replace(
        p[0], p[1]), wdict.items(), message.upper())
    for c in s:
        if c.isnumeric():
            if codemode:  # codemode symbols are preceded by the CODE digit '6' then as-is
                encoded.append(c)
                codemodecount += 1
                if codemodecount >= 3:
                    codemode = False

            else:  
                if not numericmode:
                    numericmode = True
                    encoded.append(nchangemode)  # FIG

                encoded.append(c*digit_repeats)

        else:
            codemode = False
            if numericmode:
                # end numericmode with the FIG numeric code for '/' (98)
                encoded.append(nchangemode)
                numericmode = False

            if c == code:
                codemode = True
                codemodecount = 0

            for row in table:
                if c in row:
                    k = row.index(c)
                    encoded.append(str(row[0]) + str(k-1))
                    break

    return ''.join(encoded)


def xcb_decode(s, table=CT37w, code='κ', sdict=SDICT):
    """ Decode extended straddling checkerboard """
    prefixes = sorted([row[0] for row in table], reverse=True)
    pos, numericmode, codemode = 0, False, False
    decoded = []
    if table[-1][-1] == '/':
        nchangemode = '99'
        digit_repeats = 3
    else:
        nchangemode = '98'
        digit_repeats = 2
    numbers = {c*digit_repeats: c for c in list('0123456789')}
    while pos < len(s):
        if numericmode:
            if s[pos:pos+digit_repeats] in numbers:
                decoded.append(numbers[s[pos:pos+digit_repeats]])
                pos += digit_repeats - 1
            elif s[pos:pos+2] == nchangemode:
                numericmode = False
                pos += 1
            elif decoded[-1] == '9':  # error, so backtrack if last was 9
                decoded.pop()
                numericmode = False
                pos -= digit_repeats - 1

        elif codemode:
            if (s[pos:pos+3]).isnumeric():
                decoded.append(s[pos:pos+3])
                pos += 2

            codemode = False

        elif s[pos:pos+2] == nchangemode:
            numericmode = not numericmode
            pos += 1

        else:
            for p in prefixes:
                if s[pos:].startswith(p):
                    n = len(p)
                    row = next(i for i, r in enumerate(table) if p == r[0])
                    c = table[row][int(s[pos+n])+1]
                    decoded.append(c)
                    if c == code:
                        codemode = True

                    pos += n
                    break

        pos += 1

    return reduce(lambda x, p: x.replace(p[0], p[1]), sdict.items(), ''.join(decoded))


if __name__ == '__main__':

    MESSAGE = 'Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March'
    print(MESSAGE)
    print('Encoded: ', xcb_encode(MESSAGE))
    print('Decoded: ', xcb_decode(xcb_encode(MESSAGE)))
    print('Encoded: ', xcb_encode(MESSAGE, CT37w_mod))
    print('Decoded: ', xcb_decode(xcb_encode(MESSAGE, CT37w_mod), CT37w_mod))
Output:
Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March
Encoded:  072792390929088484829094919062919097907384825751829099222000000000999098905490758190708890991119990790827175
Decoded:  ADMIN ACK YOUR MSG. CODE291 SEND FURTHER 2000 SUPP TO HQ BY 1 MARCH
Encoded:  0727923909290884848290949190629190979073848257518290982200000098909990549075819070889098119890790827175
Decoded:  ADMIN ACK YOUR MSG. CODE291 SEND FURTHER 2000 SUPP TO HQ BY 1 MARCH

Wren

Library: Wren-str
import "./str" for Str

var row1 = "AEINOT"
var row2 = "BCDFGHJKLM"
var row3 = "PQRSUVWXYZ"
var row4 = " ."

var emap = {}
for (i in 0...row1.count) emap[row1[i]] = i.toString
for (i in 0...row2.count) emap[row2[i]] = (70 + i).toString
for (i in 0...row3.count) emap[row3[i]] = (80 + i).toString
for (i in 0...row4.count) emap[row4[i]] = (90 + i).toString
var ewords = {
    "ACK": "92", "REQ": "93", "MSG": "94", "RV": "95",
    "GRID": "96", "SEND": "97", "SUPP": "99"
}
var efigs = "0123456789"
var spc = "90"
var dot = "91"
var fsl = "98"

var dmap = {}
var dwords = {}
for (k in emap.keys) dmap[emap[k]] = k
for (k in ewords.keys) dwords[ewords[k]] = k
var drow1 = "012345"

var encode = Fn.new { |s|
    s = Str.upper(s)
    var res = ""
    var words = s.split(" ")
    var wc = words.count
    for (i in 0...wc) {
        var word = words[i]
        var add = ""
        if (ewords.containsKey(word)) {
            add = ewords[word]
        } else if (ewords.containsKey(word[0...-1]) && word[-1] == ".") {
            add = ewords[word[0...-1]] + dot
        } else if (word.startsWith("CODE")) {
            add = "6" + word[4..-1]
        } else {
            var figs = false
            for (c in word) {
                if (efigs.contains(c)) {
                    if (figs) {
                        add = add + c * 2
                    } else {
                        figs = true
                        add = add + fsl + c * 2
                    }
                } else {
                    var ec = emap[c]
                    if (!ec) {
                        Fiber.abort("Message contains unrecognized character '%(c)'.")
                    }
                    if (figs) {
                        add = add + fsl + ec
                        figs = false
                    } else {
                        add = add + ec
                    }
                }
            }
            if (figs && i < wc - 1) add = add + fsl
        }
        res = res + add
        if (i < wc - 1) res = res + spc
    }
    return res
}

var decode = Fn.new { |s|
    var res = ""
    var sc = s.count
    var figs = false
    var i = 0
    while (i < sc) {
        var c = s[i]
        var ix = -1
        if (figs) {
            if (s[i..i+1] != fsl) {
                res = res + c
            } else {
                figs = false
            }
            i = i + 2
        } else if ((ix = drow1.indexOf(c)) >= 0) {
            res = res + dmap[drow1[ix]]
            i = i + 1
        } else if (c == "6") {
            res = res + "CODE" + s[i+1..i+3]
            i = i + 4
        } else if (c == "7" || c == "8") {
            var d = s[i+1]
            res = res + dmap[c + d]
            i = i + 2
        } else if (c == "9") {
            var d = s[i+1]
            if (d == "0") {
                res = res + " "
            } else if (d == "1") {
                res = res + "."
            } else if (d == "8") {
                figs = !figs
            } else {
                res = res + dwords[c + d]
            }
            i = i + 2
        }
    }
    return res
}
               
var msg = "Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March"
System.print("Message:\n%(msg)")
var enc = encode.call(msg)
System.print("\nEncoded:\n%(enc)")
var dec = decode.call(enc)
System.print("\nDecoded:\n%(dec)")
Output:
Message:
Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March

Encoded:
0727923909290884848290949190629190979073848257518290982200000098909990549075819070889098119890790827175

Decoded:
ADMIN ACK YOUR MSG. CODE291 SEND FURTHER 2000 SUPP TO HQ BY 1 MARCH
Cookies help us deliver our services. By using our services, you agree to our use of cookies.