aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/fixedint/base.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/fixedint/base.py')
-rw-r--r--.venv/lib/python3.12/site-packages/fixedint/base.py468
1 files changed, 468 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/fixedint/base.py b/.venv/lib/python3.12/site-packages/fixedint/base.py
new file mode 100644
index 00000000..ff93d43b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/fixedint/base.py
@@ -0,0 +1,468 @@
+# -*- coding: utf-8 -*-
+
+import sys
+from fixedint.compat import *
+from weakref import WeakValueDictionary
+
+class FixedProperty(object):
+ def __init__(self, val, doc):
+ self.val = val
+ self.__doc__ = doc
+ def __get__(self, obj, type=None):
+ return self.val
+ def __set__(self, obj, value):
+ raise AttributeError("property is read-only")
+
+class FixedMetaProperty(object):
+ def __init__(self, name, doc):
+ self.name = name
+ self.__doc__ = doc
+ def __get__(self, obj, type=None):
+ if self.name not in obj.__dict__:
+ # this should only happen when trying to access FixedInt.prop, which help() does
+ raise AttributeError("Attribute %s not defined on base class" % self.name)
+ prop = obj.__dict__[self.name]
+ return prop.__get__(obj)
+ def __set__(self, obj, value):
+ raise AttributeError("property %s is read-only" % self.name)
+
+if not PY3K:
+ # While it'd be nice to put machine-sized integers into Python 2.x "int"s,
+ # it turns out that the int methods occasionally freak out when given longs.
+ # So, we use arbitrary-precision ints for all Pythons.
+ int = long
+
+_class_cache = WeakValueDictionary()
+
+_doc_width = "Bit width of this integer, including the sign bit."
+_doc_signed = "True if this integer is a twos-complement signed type."
+_doc_mutable = "True if this integer is mutable (modifiable in-place)."
+_doc_minval = "Minimum representable value of this integer type"
+_doc_maxval = "Maximum representable value of this integer type"
+
+_subclass_token = object()
+class _FixedIntBaseMeta(type):
+ def __new__(cls, name, bases, dict):
+ if dict.get('_subclass_enable', None) == _subclass_token:
+ del dict['_subclass_enable']
+ return type.__new__(cls, name, bases, dict)
+
+ for base in bases:
+ if issubclass(base, FixedInt):
+ basename = base.__name__
+ raise Exception("Cannot subclass %s; use the %s constructor to produce new subclasses." % (basename, basename))
+ raise Exception("Cannot subclass this class.")
+
+ def __call__(self, width, signed=True, mutable=None):
+ signed = bool(signed)
+ if mutable is None:
+ # Take mutable from constructor used (FixedInt or MutableFixedInt)
+ mutable = (self == MutableFixedInt)
+
+ cachekey = (width, signed, mutable)
+ try:
+ return _class_cache[cachekey]
+ except KeyError:
+ pass
+
+ if signed:
+ min = -1<<(width-1)
+ max = (1<<(width-1))-1
+ else:
+ min = 0
+ max = (1<<width)-1
+
+ if mutable:
+ bases = (MutableFixedInt,)
+ else:
+ bases = (FixedInt, int)
+
+ dict = {}
+ dict['width'] = FixedProperty(width, doc=_doc_width)
+ dict['signed'] = FixedProperty(signed, doc=_doc_signed)
+ dict['mutable'] = FixedProperty(mutable, doc=_doc_mutable)
+ dict['minval'] = FixedProperty(min, doc=_doc_minval)
+ dict['maxval'] = FixedProperty(max, doc=_doc_maxval)
+
+ if signed:
+ _mask1 = (1<<(width-1)) - 1
+ _mask2 = 1<<(width-1)
+ def _rectify(val):
+ return (val & _mask1) - (val & _mask2)
+ else:
+ _mask = (1<<width) - 1
+ def _rectify(val):
+ return val & _mask
+ dict['_rectify'] = staticmethod(_rectify)
+
+ if not mutable:
+ intbase = bases[1]
+ def _newfunc(cls, val=0):
+ ''' Convert an integer into a fixed-width integer. '''
+ return intbase.__new__(cls, _rectify(int(val)))
+ _newfunc.__name__ = '__new__'
+ dict['__new__'] = _newfunc
+
+ name = ''.join(['Mutable'*mutable, 'U'*(not signed), 'Int', str(width)])
+
+ cls = _FixedIntMeta(name, bases, dict)
+ _class_cache[cachekey] = cls
+ return cls
+
+ width = FixedMetaProperty('width', doc=_doc_width)
+ signed = FixedMetaProperty('signed', doc=_doc_signed)
+ mutable = FixedMetaProperty('mutable', doc=_doc_mutable)
+ minval = FixedMetaProperty('minval', doc=_doc_minval)
+ maxval = FixedMetaProperty('maxval', doc=_doc_maxval)
+
+class _FixedIntMeta(_FixedIntBaseMeta):
+ __new__ = type.__new__
+ __call__ = type.__call__
+
+
+def int_method(f):
+ if isinstance(f, str):
+ def wrapper(f2):
+ f2.__name__ = f
+ return int_method(f2)
+ return wrapper
+ else:
+ f.__doc__ = getattr(int, f.__name__).__doc__
+ return f
+
+class FixedInt:
+ __slots__ = ()
+ _subclass_enable = _subclass_token
+
+ # width, signed, mutable, minval, maxval defined in metaclass
+ # _rectify defined in metaclass
+ # __new__ defined in metaclass
+
+ if not PY3K:
+ @int_method
+ def __hex__(self):
+ return '%#x' % int(self)
+ def __oct__(self):
+ return '%#o' % int(self)
+
+ @int_method
+ def __pow__(self, other, modulo=None):
+ # Jython can't handle int.__pow__(x, y, None)
+ if modulo is None:
+ return type(self)(int.__pow__(int(self), int(other)))
+ return type(self)(int.__pow__(int(self), int(other), modulo))
+
+ @int_method
+ def __rpow__(self, other):
+ return type(other)(int.__rpow__(int(self), int(other)))
+
+ @int_method
+ def __repr__(self):
+ return '%s(%s)' % (type(self).__name__, self)
+
+ @int_method
+ def __str__(self):
+ return str(int(self))
+
+ @classmethod
+ def _canonicalize_index(cls, idx):
+ if idx < 0:
+ idx += cls.width
+ return idx
+
+ @classmethod
+ def _canonicalize_slice(cls, slice):
+ start = slice.start
+ stop = slice.stop
+
+ if slice.step is not None:
+ raise ValueError("slice step unsupported")
+
+ if start is None:
+ start = 0
+ else:
+ start = cls._canonicalize_index(start)
+
+ if stop is None:
+ stop = cls.width
+ elif isinstance(stop, complex):
+ if stop.real:
+ raise ValueError("invalid slice stop: must be integer or pure-imaginary complex number")
+ stop = int(stop.imag) + start
+ else:
+ stop = cls._canonicalize_index(stop)
+
+ if 0 <= start < stop <= cls.width:
+ return (start, stop)
+ else:
+ raise IndexError("invalid slice %d:%d" % (start, stop))
+
+ def __getitem__(self, item):
+ ''' Slice the bits of an integer.
+
+ x[a] gets a single bit at position a, returning a bool.
+ x[a:b] gets a range of bits as a FixedInt.
+
+ For slice notation, b may be of the form 'bj' (a complex number) to treat it
+ as a length rather than a stop index.
+ The result will be of the type UIntX, where X is the number of bits in the range.
+
+ Examples:
+ x[0]: equal to (x & 1)
+ x[1:5] or x[1:4j]: equal to (x & 31) >> 1
+ x[:5]: equal to (x & 31)
+ '''
+
+ if isinstance(item, slice):
+ start, stop = self._canonicalize_slice(item)
+ return FixedInt(stop - start, signed=False)(int(self) >> start)
+ else:
+ item = self._canonicalize_index(item)
+ if 0 <= item < self.width:
+ return bool(int(self) & (1 << item))
+ else:
+ raise IndexError("index %d out of range" % item)
+
+ def to_bytes(self, length=None, byteorder=sys.byteorder):
+ if length is None:
+ length = (self.width + 7) // 8
+ try:
+ return int(self).to_bytes(length, byteorder=byteorder, signed=self.signed)
+ except (OverflowError, AttributeError):
+ pass
+
+ val = int(self) & ((1 << (length * 8)) - 1)
+ out = []
+ while length > 0:
+ out.append(val & 0xff)
+ val >>= 8
+ length -= 1
+ if byteorder == 'big':
+ out = reversed(out)
+ if PY3K:
+ return bytes(out)
+ else:
+ return ''.join(map(chr, out))
+
+ @classmethod
+ def from_bytes(cls, bytes, byteorder=sys.byteorder, signed=None):
+ if cls in (FixedInt, MutableFixedInt):
+ if signed is None:
+ signed = False
+ elif signed is None:
+ signed = cls.signed
+ else:
+ raise ValueError("can't set signed with a concrete FixedInt")
+
+ blen = len(bytes)
+ try:
+ val = int.from_bytes(bytes, byteorder=byteorder, signed=signed)
+ except AttributeError:
+ val = 0
+ if byteorder == 'big':
+ bytes = reversed(bytes)
+ if PY3K:
+ for i,c in enumerate(bytes):
+ val |= c << (8 * i)
+ else:
+ for i,c in enumerate(bytes):
+ val |= ord(c) << (8 * i)
+
+ if cls in (FixedInt, MutableFixedInt):
+ return cls(blen*8, signed=signed)(val)
+ else:
+ return cls(val)
+
+ if PY3K:
+ @int_method
+ def __round__(self, n=0):
+ return int(self)
+
+ # Inherited methods which are fine as-is:
+ # complex, int, long, float, index
+ # truediv, rtruediv, divmod, rdivmod, rlshift, rrshift
+ # format
+
+FixedInt = add_metaclass(_FixedIntBaseMeta)(FixedInt)
+
+
+class MutableFixedInt(FixedInt):
+ _subclass_enable = _subclass_token
+
+ def __init__(self, val=0, base=None):
+ ''' Convert an integer into a fixed-width integer. '''
+ if base is None:
+ val = int(val)
+ else:
+ val = int(val, base)
+
+ self._val = self._rectify(val)
+
+ if PY3K:
+ @int_method
+ def __format__(self, format_spec):
+ return format(self._val, format_spec)
+
+ def __ipow__(self, other, modulo=None):
+ if modulo is None:
+ self._val = self._rectify(int.__pow__(int(self), int(other)))
+ else:
+ self._val = self._rectify(int.__pow__(int(self), int(other), modulo))
+ return self
+
+ def __setitem__(self, item, value):
+ ''' Modify a slice of an integer.
+
+ x[a]=y sets a single bit at position a.
+ x[a:b]=y sets a range of bits from an integer.
+
+ See __getitem__ for more details on the slice notation.
+ '''
+
+ value = int(value)
+ if isinstance(item, slice):
+ start, stop = self._canonicalize_slice(item)
+ mask = (1 << (stop - start)) - 1
+ self._val = (self._val & ~(mask << start)) | ((value & mask) << start)
+ else:
+ item = self._canonicalize_index(item)
+ if 0 <= item < self.width:
+ if value:
+ self._val |= (1 << item)
+ else:
+ self._val &= ~(1 << item)
+ return bool(int(self) & (1 << item))
+ else:
+ raise IndexError("index %d out of range" % item)
+
+## Arithmetic methods
+def _arith_unary_factory(name, mutable):
+ ''' Factory function producing methods for unary operations. '''
+ intfunc = getattr(int, name)
+ if mutable:
+ @int_method(name)
+ def _f(self):
+ return type(self)(intfunc(self._val))
+ else:
+ @int_method(name)
+ def _f(self):
+ return type(self)(intfunc(self))
+ return _f
+
+_arith_unary = 'neg pos abs invert'.split()
+for f in _arith_unary:
+ s = '__%s__' % f
+ setattr(FixedInt, s, _arith_unary_factory(s, mutable=False))
+ setattr(MutableFixedInt, s, _arith_unary_factory(s, mutable=True))
+
+
+def _arith_convert(t1, t2):
+ if not issubclass(t2, FixedInt):
+ return t1
+
+ # Follow C conversion rules (ISO/IEC 9899:TC3 ยง6.3.1.8)
+ if t1.signed == t2.signed:
+ # If both are signed or both are unsigned, return the larger type.
+ if t1.width >= t2.width:
+ return t1
+ return t2
+
+ if not t1.signed:
+ ut, st = t1, t2
+ else:
+ ut, st = t2, t1
+
+ if ut.width >= st.width:
+ # If the unsigned type has rank >= the signed type, convert the signed type to the unsigned type.
+ return ut
+ else:
+ return st
+
+def _arith_binfunc_factory(name):
+ ''' Factory function producing methods for arithmetic operators '''
+ intfunc = getattr(int, name)
+ @int_method(name)
+ def _f(self, other):
+ nt = _arith_convert(type(self), type(other))
+ return nt(intfunc(int(self), int(other)))
+ return _f
+
+# divmod, rdivmod, truediv, rtruediv are considered non-arithmetic since they don't return ints
+# pow, rpow, rlshift, and rrshift are special since the LHS and RHS are very different
+_arith_binfunc = 'add sub mul floordiv mod lshift rshift and xor or'.split()
+_arith_binfunc += 'radd rsub rmul rfloordiv rmod rand rxor ror'.split()
+if not PY3K:
+ _arith_binfunc += 'div rdiv'.split()
+
+for f in _arith_binfunc:
+ s = '__%s__' % f
+ setattr(FixedInt, s, _arith_binfunc_factory(s))
+
+
+## Non-arithmetic methods (Mutable only)
+def _nonarith_unary_factory_mutable(name):
+ ''' Factory function producing methods for unary operations. '''
+ intfunc = getattr(int, name)
+ @int_method(name)
+ def _f(self):
+ return intfunc(self._val)
+ return _f
+
+_mutable_unary = 'int float'.split()
+if sys.version_info[:2] >= (2,5):
+ _mutable_unary += ['index']
+if sys.version_info[:2] >= (2,6):
+ _mutable_unary += ['trunc']
+if PY3K:
+ _mutable_unary += 'bool'.split()
+else:
+ _mutable_unary += 'nonzero long'.split()
+
+for f in _mutable_unary:
+ s = '__%s__' % f
+ setattr(MutableFixedInt, s, _nonarith_unary_factory_mutable(s))
+
+
+def _nonarith_binfunc_factory_mutable(name):
+ ''' Factory function producing methods for non-arithmetic binary operators on Mutable instances. '''
+ intfunc = getattr(int, name)
+ @int_method(name)
+ def _f(self, other):
+ return intfunc(self._val, int(other))
+ return _f
+
+_mutable_binfunc = 'truediv rtruediv divmod rdivmod rlshift rrshift'.split()
+if hasattr(int, '__cmp__'):
+ _mutable_binfunc += ['cmp']
+else:
+ _mutable_binfunc += 'lt le eq ne gt ge'.split()
+for f in _mutable_binfunc:
+ s = '__%s__' % f
+ setattr(MutableFixedInt, s, _nonarith_binfunc_factory_mutable(s))
+
+
+## In-place operators
+def _inplace_factory_mutable(iname, name, op):
+ ''' Factory function producing methods for augmented assignments on Mutable instances. '''
+ # This uses compiled operators instead of an int.__X__ function call for speed.
+ # Measured improvement is about 15% speed increase.
+ exec("""
+def _f(self, other):
+ self._val = self._rectify(self._val %s int(other))
+ return self
+globals()['_f'] = _f""" % op)
+ _f.__name__ = iname
+ doc = list.__iadd__.__doc__
+ if doc:
+ _f.__doc__ = doc.replace('__iadd__', name).replace('+=', op+'=')
+ return _f
+
+# pow is special because it takes three arguments.
+_inplace_func = 'add,+ sub,- mul,* truediv,/ floordiv,// mod,% lshift,<< rshift,<< and,& or,| xor,^'.split()
+if not PY3K:
+ _inplace_func += ['div,/']
+for f in _inplace_func:
+ fn, op = f.split(',')
+ si = '__i%s__' % fn
+ so = '__%s__' % fn
+ setattr(MutableFixedInt, si, _inplace_factory_mutable(si, so, op))