# 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
}