# Converts to and from integer bases between 1 and 85 (as per rfc1924)

from math import log, fabs, modf
from sets import Set

base85 = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
              'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
              'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 
              'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 
              'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
              'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 
              'y', 'z', '!', '#', '$', '%', '&', '(', ')', '*', 
              '+', '-', ';', '<', '=', '>', '?', '@', '^', '_', 
              '`', '{', '|', '}', '~')


class BaseError(Exception):
    pass

class BaseConvError(BaseError):
    pass

class BaseConv(object):
    """This converts any signed integer or float between integer bases.
    Lowest possible base is 1, highest possible base depends on the
    amount of symbols for digits used. The default symbolset covers
    bases up to and including standard base64.
    """
    digits = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
              'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
              'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 
              'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 
              'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
              'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 
              'y', 'z', '!', '#' )

    base = 2 # most frequently used base
    radixpoint = '.'
    wrapper = None

    def __init__(self, base=2, radixpoint='.', symbols=()):
        assert type(base) == type(int())
        self.base = base or self.base
        self.radixpoint = radixpoint or self.radixpoint
        if self.base < 1:
            raise BaseError, 'Cannot convert base %i, too low' % str(self.base)
        elif self.base > len(self.digits) - 1:
            raise BaseError, 'Cannot convert base %i, too high' % str(self.base)
        self.digits = self.digits or symbols
        if not self.digits:
            raise BaseConvError, 'Empty alphabet'

    def wrap(self, num):
        return num

    def unwrap(self, num):
        return num

    def dec2base(self, num, base=None):
        "Convert any decimal number to any valid base" 
        b = base or self.base
        sign = ''
        if num / fabs(num) < 0:
            sign = '-'
            num = fabs(num)
        
        f, i = modf(num)
        
        i = int(i)
        print 'I: %s F: %s B: %s' % (i, f, b)
        if i:
            if i < b:
                i = self.digits[i]
            else:    
                rem = []
                d = i
                while True:
                    d, r = divmod(d, b)
                    rem.append(r)
                    if d < b:
                        break
                rem.append(d)
                rem.reverse()
                i = ''.join([self.digits[digit] for digit in rem])
        else:
            i = '0'
        counter = 0    
        print 'I: %s F: %s B: %s' % (i, f, b)
        if f:
            rem = []
            stack = Set()
            stacklen = len(stack)
            while f > 0.0:
                if counter >= 15:
                    break
                #print '%i: f=%s b=%i f*b=%s int(f*b)=%s rem=%s stack=%s' % (counter, f, b, f*b, int(str(f*b).split('.')[0]), rem, stack)
                counter += 1
                f *= b
                stack.add(int(str(f).split('.')[0]))
                rem.append(int(str(f).split('.')[0]))
                #print rem[-1], stack
                if len(stack) == stacklen: break
                stacklen = len(stack)    
                f -= int(str(f).split('.')[0])
                #print 'Next f:', f
            #print rem    
            f = ''.join([self.digits[digit] for digit in rem])
            return sign + self.wrap(i+self.radixpoint+f)
        return sign + self.wrap(i)

    def base2dec(self, num, base=None):
        "Convert from any valid base to decimal"
        b = base or self.base
        num = self.unwrap(num)
        sign = ''
        if num[0] == '-':
            sign = '-'
            num = num[1:]
        if b == 1:
            return int(sign+'1') * len(num)
        
        integer, fraction = num.split(self.radixpoint)
        num = integer+fraction
        
        power = len(integer) - 1
        ldigits = list(self.digits)
        rem = []
        for letter in num:
            rem.append(ldigits.index(letter)*b**power)
            power -= 1
        return int(sign+'1') * sum(rem)

    def base2base(self, num, fro, to):
        "Convert from any valid base to any other"
        between = base2dec(num, fro, use_wrapper)
        return dec2base(between, to)

    def logb(self, num, base=None):
        base = base or self.base
        return log(num, base)

class AdaBaseConv(BaseConv):
    """This is a version of BaseConv that converts between numbers
    formatted according to the Ada standard, e.g. the Ada-formatted
    number #16#21# is the hexadecimal number 21. 
    """

    wrapper = 'ada'

    def __init__(self, base=2, symbols=()):
        pass

    def wrap(self, num, base):
        pass

    def unwrap(self, num, base):
        pass
