fschmnn-duodecimal/BaseConverter.py

264 lines
8.5 KiB
Python

class BaseConverter:
''' class to represent numbers in diferent bases
Input:
------
numbers : either string (baseX) or float/int (base10)
digits :string of digits used in the base (e.g. binary: '01')
Variables:
----------
value : holds the decimal value
string : holds the representation in BaseConverter.base
http://www.dozenal.org/drupal/sites_bck/default/files/DSA-ConversionRules_0.pdf
'''
bases = {
2 : '01',
8 : '01234567',
10 : '0123456789',
12 : '0123456789XE',
16 : '0123456789ABCDEF'
}
def __init__(self,number,digits):
''' determine type of input and convert to decimal/duodecimal '''
self.digits = digits
self.base = len(digits)
if type(number) == int or type(number) == float:
self.value = number
self.string = BaseConverter.from_dec(number,digits)
elif type(number) == str:
self.value = BaseConverter.to_dec(number,digits)
self.string = number
elif isinstance(number,BaseConverter):
self.value = number.value
self.string = BaseConverter.from_dec(number,digits)
else:
raise TypeError('input type must be str or float/int')
@staticmethod
def from_dec(number,digits=None,base=None):
''' convert decimal float/int to duodecimal string '''
if digits:
base = len(digits)
to_digits = {k: v for k,v in enumerate(digits)}
elif base:
to_digits = {k: v for k,v in enumerate(BaseConverter.bases[base])}
# handle negative numbers
if number <0:
number = abs(number)
sign = '-'
else:
sign = ''
integer, fractional = divmod(number,1)
out = []
quotient = integer
while quotient != 0:
quotient, remainder = divmod(quotient,base)
out.insert(0,remainder)
integer = ''.join([to_digits[dig] for dig in out])
if fractional:
out = []
remainder = fractional
for i in range(len(str(fractional).split('.')[1])+1):
quotient, remainder = divmod(remainder*base,1)
out.append(quotient)
if remainder > 0.5:
out.append(1)
decimal = '.' + ''.join([to_digits[dig] for dig in out])
else:
decimal = ''
return sign + integer + decimal.rstrip('0')
@staticmethod
def to_dec(string,digits=None,base=None):
''' convert string to decimal float/int '''
if digits:
base = len(digits)
from_digits = {v: k for k,v in enumerate(digits)}
elif base:
from_digits = {v: k for k,v in enumerate(BaseConverter.bases[base])}
# handle negative numbers
if string.startswith('-'):
string = string[1:]
sign = -1
else:
sign = 1
if set(string) > set(digits+'.'):
invalid = set(string) - set(digits+'.')
raise ValueError('invalid character'.format(invalid))
if '.' in string:
integer, fractional = string.split('.')
else:
integer, fractional = string, ''
out = 0
for power, digit in enumerate(integer[::-1]):
out += from_digits[digit] * base**power
for power, digit in enumerate(str(fractional),1):
out += from_digits[digit] * base**(-power)
return out
def __add__(self,other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return BaseConverter(self.value + other.value,digits=self.digits)
def __iadd__(self,other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
self.value = self.value + other.value
self.string = BaseConverter.from_dec(self.value,digits=self.digits)
return self
def __radd__(self,other):
return BaseConverter(other,self.digits) + self
def __sub__(self,other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return BaseConverter(self.value - other.value,digits=self.digits)
def __isub__(self,other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
self.value = self.value - other.value
self.string = BaseConverter.from_dec(self.value,digits=self.digits)
return self
def __rsub__(self,other):
return BaseConverter(other,self.digits) - self
def __mul__(self,other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return BaseConverter(self.value * other.value,digits=self.digits)
def __imul__(self,other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
self.value = self.value * other.value
self.string = BaseConverter.from_dec(self.value,digits=self.digits)
return self
def __rmul__(self,other):
return BaseConverter(other,self.digits) * self
def __truediv__(self,other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return BaseConverter(self.value / other.value,self.digits)
def __itruediv__(self,other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
self.value = self.value / other.value
self.string = BaseConverter.from_dec(self.value,digits=self.digits)
return self
def __rtruediv__(self,other):
return BaseConverter(other,self.digits) / self
def __pow__(self,other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return BaseConverter(self.value ** other.value,self.digits)
def __ipow__(self,other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
self.value = self.value ** other.value
self.string = BaseConverter.from_dec(self.value,digits=self.digits)
return self
def __rpow__(self,other):
return BaseConverter(other,self.digits) ** self
def __repr__(self):
if '.' in self.string:
return self.string[:7].rstrip('0') + f'(base{self.base})'
else:
return self.string + f'(base{self.base})'
def __lt__(self, other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return self.value < other.value
def __le__(self, other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return self.value <= other.value
def __eq__(self, other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return self.value == other.value
def __ne__(self, other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return self.value != other.value
def __ge__(self, other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return self.value >= other.value
def __gt__(self, other):
if not isinstance(other, BaseConverter):
other = BaseConverter(other,self.digits)
return self.value > other.value
def __str__(self):
return self.string
def __format__(self,format):
return self.string
def decimal(self):
''' return the decimal representation '''
return self.value
def to_base(self,base):
''' give out in different base '''
return BaseConverter.from_dec(self.value,base=base)
class duo(BaseConverter):
''' class to represent duodecimal numbers
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , X , E
Input:
------
either string (base12) or float/int (base10)
'''
def __init__(self,number):
BaseConverter.__init__(self,number,'0123456789XE')
class binary(BaseConverter):
def __init__(self,number):
BaseConverter.__init__(self,number,'01')
class hexa(BaseConverter):
def __init__(self,number):
BaseConverter.__init__(self,number,'0123456789ABCDEF')
if __name__ == '__main__':
a = duo(2)
a /= 4
print(a)