Extended Straddling Checkerboard: Difference between revisions

→‎{{header|jq}}: more idiomatic version of decode
(→‎{{header|jq}}: more idiomatic version of decode)
 
(6 intermediate revisions by 4 users not shown)
Line 5:
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, soif weyou woulddo that, then havedigits F/Lonly (98)needed andto SUPPbe doubled rather than tripled. So we would then have (99)the asfollowing followscheckerboard:
<pre>
A E I N O T CODE
Line 20:
 
0 1 2 3 4 5 6 7 8 9
00 11 22 33 44 55 66 77 88 99
000 111 222 333 444 555 666 777 888 999
</pre>
 
Line 34:
;Reference:
:* Cipher Machines web page on Checkerboards: [https://www.ciphermachinesandcryptology.com/en/table.htm Checkerboards]
 
=={{header|jq}}==
'''Adapted from [[#Wren|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'''
<syntaxhighlight lang="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
</syntaxhighlight>
{{output}}
<pre>
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
</pre>
 
=={{header|Julia}}==
Line 76 ⟶ 220:
if contains(efigs, c)
if figs
add *= c^32
else
figs = true
add *= fsl * c^32
end
else
Line 112 ⟶ 256:
if s[i:i+1] != fsl
res *= c
i += 32
else
figs = false
Line 156 ⟶ 300:
 
Encoded:
07279239092908848482907983749190629190979073848257518290982200000098909990549075819070889098119890790827175
0727923909290884848290798374919062919097907384825751829098222000000000989099905490758190708890981119890790827175
 
Decoded:
Line 162 ⟶ 306:
</pre>
 
=={{header|PythonPhix}}==
{{trans|Julia}}
Note this includes the two tiny tweaks to encode digits in two characters, as per "Moot point" on the talk page.
<syntaxhighlight lang="phix">
with javascript_semantics
Line 203 ⟶ 348:
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})
Line 227 ⟶ 373:
if s[i..i+1] != fsl then
res &= c
-- i += 3
i += 2
else
figs = false
Line 262 ⟶ 409:
enc = encode(msg),
unc = decode(enc)
printf(1,"Message:\n%s\n\nEncoded:\n%s\n\nDecoded:\n%s\n",{msg,enc,unc})
</syntaxhighlight>
{{out}}
Line 270 ⟶ 417:
 
Encoded:
07279239092908848482907983749190629190979073848257518290982200000098909990549075819070889098119890790827175
0727923909290884848290798374919062919097907384825751829098222000000000989099905490758190708890981119890790827175
 
Decoded:
ADMIN ACK YOUR MSG. CODE291 SEND FURTHER 2000 SUPP TO HQ BY 1 MARCH
</pre>
 
=={{header|Python}}==
See the discussion for the different checkerboard table handling.
<syntaxhighlight lang="python">""" rosettacode.org/wiki/Extended_Straddling_Checkerboard """
 
Line 291 ⟶ 439:
'SUPP': 'π',
}
SDICT = {v: k for (k, v) in WDICT.items()} # 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
# web site CT37w, but '/' (FIG) char is 98 not 99 to help differentiate from code for 9 of '999'
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, nchangemode='98', code='κ', table=CT37w, wdict=WDICT):
def xcb_encode(message, table=CT37w, code='κ', wdict=WDICT):
"""
Encode with extended straddling checkerboard. Default checkerboard is
Line 310 ⟶ 466:
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
Line 321 ⟶ 484:
codemode = False
 
else: # numeric numbers are triplicate encoded so '3' -> '333'
if not numericmode:
numericmode = True
encoded.append(nchangemode) # FIG
 
encoded.append(c*3digit_repeats)
 
else:
codemode = False
if numericmode:
encoded.append(nchangemode) # end numericmode with the FIG numeric code for '/' (98)
encoded.append(nchangemode)
numericmode = False
 
Line 347 ⟶ 511:
 
 
def xcb_decode(s, nchangemodetable='98'CT37w, code='κ', table=CT37w, sdict=SDICT):
""" Decode extended straddling checkerboard """
numbers = {c*3: c for c in list('0123456789')}
prefixes = sorted([row[0] for row in table], reverse=True)
pos, numericmode, codemode = 0, False, False
numericmode = False
codemode = 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+3digit_repeats] in numbers:
decoded.append(numbers[s[pos:pos+3digit_repeats]])
pos += 2digit_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:
Line 374 ⟶ 546:
numericmode = not numericmode
pos += 1
 
else:
for p in prefixes:
Line 383 ⟶ 556:
if c == code:
codemode = True
 
pos += n
break
Line 397 ⟶ 571:
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))
 
</syntaxhighlight>{{out}}
<pre>
Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March
Encoded: 072792390929088484829094919062919097907384825751829099222000000000999098905490758190708890991119990790827175
Encoded: 072792390929088484829094919062919097907384825751829098222000000000989099905490758190708890981119890790827175
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
</pre>
Line 407 ⟶ 585:
=={{header|Wren}}==
{{libheader|Wren-str}}
For consistency with the Python example, this uses the CT-37w checkerboard with F/L changed to 98 and SUPP to 99.
<syntaxhighlight lang="wren">import "./str" for Str
 
Line 454 ⟶ 631:
if (efigs.contains(c)) {
if (figs) {
add = add + c * 32
} else {
figs = true
add = add + fsl + c * 32
}
} else {
Line 491 ⟶ 668:
if (s[i..i+1] != fsl) {
res = res + c
i = i + 3
} else {
figs = false
i = i + 2
}
i = i + 2
} else if ((ix = drow1.indexOf(c)) >= 0) {
res = res + dmap[drow1[ix]]
Line 536 ⟶ 712:
 
Encoded:
0727923909290884848290949190629190979073848257518290982200000098909990549075819070889098119890790827175
072792390929088484829094919062919097907384825751829098222000000000989099905490758190708890981119890790827175
 
Decoded:
2,442

edits