# pohlcode.awk version 3 by Ben Collver
#
# Implement spoken representation of binary numbers described in
# On Binary Digits And Human Habits by Frederik Pohl, 1962.
#
# Dual license: WTFPL and Public Domain
#
# Pohl encode string from decimal number:
# awk -f pohlcode.awk 31337
#
# Decode decimal number from Pohl encoded string:
# awk -f pohlcode.awk tee two group totter-poot totter-poot
# initialize tables used to convert between bases and encodings
function init( hl, hu, i) {
split("pohl poot pahtah pod too tot dye tee", second, " ")
split("ohly ooty ahtah oddy too totter dye teeter", first, " ")
split("000 001 010 011 100 101 110 111", trip, " ")
split("zero one two three four five six seven eight nine", eng, " ")
split("0 1 2 3 4 5 6 7 8 9 A B C D E F", hex, " ")
split("0000 0001 0010 0011 0100 0101 0110 0111 " \
"1000 1001 1010 1011 1100 1101 1110 1111", bin, " ")
# bin/dec/hex conversion tables
for (i in hex) {
hu = hex[i]
hl = tolower(hu)
h2b[hl] = h2b[hu] = bin[i]
h2d[hl] = h2d[hu] = i - 1
b2h[bin[i]] = hu
}
# english digit conversion tables
for (i in eng) {
d2e[i - 1] = eng[i]
e2d[eng[i]] = i - 1
}
# Pohl code binary triplet conversion tables
for (i in trip) {
f2p[trip[i]] = first[i]
s2p[trip[i]] = second[i]
p2f[first[i]] = trip[i]
p2s[second[i]] = trip[i]
}
return
}
function bin2dec(str, dec, hex) {
hex = bin2hex(str)
dec = hex2dec(hex)
return dec
}
function bin2hex(str, chunk, n, i, hex, pad, rem) {
str = zpad4(str)
n = length(str)
for (i = 1; i <= n; i += 4) {
chunk = zpad4(substr(str, i, 4))
hex = hex b2h[chunk]
}
return hex
}
function bin2pohl(bin, bbuf, groups, p, parts, retval, val) {
groups = int(length(bin) / 6)
if (groups > 9) {
print "Error: groups out of bounds: " groups
exit 1
}
p = 0
bbuf = zpad6(bin)
if (length(bbuf) > length(bin)) {
val = substr(bbuf, 1, 6)
sub(/^000/, "", val)
bbuf = substr(bbuf, 7)
p++
parts[p] = pohlencode(val)
}
if (groups > 0) {
p++
parts[p] = d2e[groups] " group"
}
for (i = 1; i <= groups; i++) {
val = substr(bbuf, 1, 6)
bbuf = substr(bbuf, 7)
p++
parts[p] = pohlencode(val)
}
retval = ""
for (i = 1; i <= p; i++) {
if (length(retval) == 0) {
retval = parts[i]
} else {
retval = retval " " parts[i]
}
}
return retval
}
function dec2bin(dec, hex, bin) {
hex = sprintf("%x\n", dec)
bin = hex2bin(hex)
return bin
}
function hex2bin(hex, n, i, bin) {
n = length(hex)
for (i = 1; i <= n; i++) {
bin = bin h2b[substr(hex, i, 1)]
}
sub(/^0+/, "", bin)
if (length(bin) == 0) {
bin = "0"
}
return bin
}
function hex2dec(val, out, i, n) {
if (val ~ /^0x/) {
val = substr(val, 3)
}
n = length(val)
for (i = 1; i <= n; i++) {
out = (out * 16) + h2d[substr(val, i, 1)]
}
#return sprintf("%.0f", out)
return out
}
function pohl2bin(str, bbuf, eng, groups, i, prefix, result, wnum, \
words)
{
bbuf = ""
groups = 1
result = match(str, /.* group /)
if (result > 0) {
prefix = substr(str, 1, RLENGTH)
str = substr(str, RLENGTH + 1)
wnum = split(prefix, words, " ")
if (wnum == 2) {
eng = words[1]
} else if (wnum == 3) {
eng = words[2]
bbuf = pohldecode(words[1])
sub(/^0*/, "", bbuf)
} else {
print "Bad Pohl code prefix: " prefix
exit 1
}
if (eng in e2d) {
groups = e2d[eng]
} else {
print "Invalid number of groups: " eng
exit 1
}
}
wnum = split(str, words, " ")
if (wnum != groups) {
print "Expected " groups " group(s) but got: " wnum
exit 1
}
for (i = 1; i <= wnum ; i++) {
bbuf = bbuf pohldecode(words[i])
}
return bbuf
}
# decode pohl-encoded 6-bit word
function pohldecode(word, parts, pnum, retval) {
pnum = split(word, parts, "-")
if (pnum == 2 && parts[1] in p2f && parts[2] in p2s) {
retval = p2f[parts[1]] p2s[parts[2]]
} else if (pnum == 1 && parts[1] in p2s) {
retval = "000" p2s[parts[1]]
} else {
print "Invalid pohl code word: " word
exit 1
}
return retval
}
# zero pad number until length is a multiple of 4 digits
function zpad4(str, pad, rem) {
rem = length(str) % 4
if (rem > 0) {
pad = substr("0000", 1, 4 - rem)
str = pad str
}
return str
}
# zero pad number until length is a multiple of 6 digits
function zpad6(str, pad, rem) {
rem = length(str) % 6
if (rem > 0) {
pad = substr("000000", 1, 6 - rem)
str = pad str
}
return str
}
function main( i, result, val) {
for (i = 1; i < ARGC; i++) {
if (length(val) == 0) {
val = ARGV[i]
} else {
val = val " " ARGV[i]
}
}
if (length(val) == 0) {
print "Usage: pohlcode.awk [code|number|test]"
print ""
print "[code]: Converts Pohl code to decimal."
print "[number]: Converts decimal to Pohl code."
print "test: Print test output."
exit 1
}
sub(/\n$/, "", val)
init()
if (val == "test") {
test()
} else if (val ~ /^[0-9][0-9]*$/) {
result = bin2pohl(dec2bin(val))
} else {
result = bin2dec(pohl2bin(val))
}
print result
return
}
function test( code, i, j) {
for (i = 0; i < 1024; i++) {
code = bin2pohl(dec2bin(i))
j = bin2dec(pohl2bin(code))
printf "%d\t%s\t%d\n", i, code, j
}
return
}