"""bases.py
Change of base functions
"""

def decimal(i):
    "return a string of decimal digits reprsenting the nonnegative integer i."
    if i == 0:
        return "0"
    numeral = ""
    while i != 0:
        digit = i % 10
        numeral = str(digit) + numeral # add next digit on the LEFT
        i = i/10
    return numeral

digits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
def intToBase(i, base):
    """Return a string representing the nonnrgative integer i
    in the specified base, from 2 to 36."""
    i = int(i)  # if i is a string, convert to int
    if i == 0:
        return '0'
    numeral = ""
    while i != 0:
        rem = i % base
        numeral = digits[rem] + numeral  # add next digit on LEFT
        i = i/base
    return numeral

def zstrip(numeral):
    """Strip leading zero characters from the numeral string"""
    while numeral.startswith('0'):
        numeral = numeral[1:] # remove leading '0'
    if not numeral: # case if initially only 0's -- and all removed
        numeral = '0'
    return numeral

def hexToBin(hexString):
    """Converts a hexadecimal string to a binary string of 4 times the length"""
    bin4s = []
    for hexDigit in hexString:
        n = digits.find(hexDigit) # converts to an integer
        bin = intToBase(n, 2)
        bin4 = bin.rjust(4, '0')  # pad on left with '0' to have 4 characters
        bin4s.append(bin4)
    return "".join(bin4s)

def binToHex(binString):
    """Converts a binary string to a hexadecimal string"""
    hexDigits = []
    while len(binString) % 4  != 0: # pad 0 ON LEFT until multiple of 4 bits
        binString = '0' + binString
    for i in range(0, len(binString), 4):  # every 4th index
        bin4 = binString[i:i+4]  # get next 4 bits
        n = int(bin4, 2)
        hexDigit = digits[n]
        hexDigits.append(hexDigit) # add corresponding hex digit
    return "".join(hexDigits)


def testAll():
    """Convert a list of numbers to binary and hexadecimal and back.
    Only show conversions back if they are different."""
    for n in [0, 1, 2, 5, 12, 45, 97, 123, 255, 2**27 + 1, 2**27 - 1]:
        print "The decimal number %d is:" % n
        binString = intToBase(n, 2)
        print "   %s in binary" % binString
        hexString = binToHex(binString)
        print "   %s in hexadecimal" % hexString
        binBack = hexToBin(hexString)
        binStrip = zstrip(binBack)
        if binBack != binStrip:
            print "   %s back to padded binary" % binBack
        if binStrip != binString:
            print "ERROR:  got %s binary, not %s!" % (binStrip, binString)
        n2 = int(binBack, 2)
        if n != n2:
            print "ERROR:  got %d decimal, not %d!" % (n2, n)
        
testAll()
