# 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 } # pohl encode 6-bit word function pohlencode(digits, retval, triplet1, triplet2) { if (length(digits) == 3) { retval = s2p[digits] } else { triplet1 = substr(digits, 1, 3) triplet2 = substr(digits, 4, 6) retval = f2p[triplet1] "-" s2p[triplet2] } 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 } BEGIN { main() exit 0 }