ErrorsManager/Python/ErrorsManager.py
2026-07-15 07:20:23 +02:00

501 lines
16 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from math import log2, ceil
import string
from typing import Any, Optional, Sequence, Self
from re import compile as re_compile, Pattern as REPattern, IGNORECASE as RE_IGNORECASE
class ErrorsManager:
ALPHABET:tuple[str] = tuple("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=")
ERRORS_MESSAGES:tuple[str] = (
"invalid_alphabet",
"invalid_base",
"invalid_alphabet_type",
"too_short_alphabet",
"repeated_characters_in_alphabet",
"too_long_alphabet",
"base_lower_than_2",
"base_greater_than_128",
"base_greater_than_alphabet"
)
RE_KEY:REPattern = re_compile(r"^[a-z_][a-z0-9_]*$", RE_IGNORECASE)
def __init__(self:Self, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
self.__error:int = 0
self.__alphabet:tuple[str] = tuple()
self.__dictionary:dict[str, int] = {}
self.__base:int = 0
self.__power:int = 0
self.set_alphabet(
self.get("alphabet", inputs, self.ALPHABET),
self.get("base", inputs, 64)
)
def set_alphabet(self:Self, alphabet:Optional[str|Sequence[str]] = None, base:int = 64) -> int:
original_length:int
i:int
character:str
self.__error = 0
if alphabet is None:
self.__alphabet = self.ALPHABET
elif self.is_string(alphabet) or self.is_array(alphabet):
self.__alphabet = tuple(alphabet)
else:
self.__error |= 1 << 2
self.__alphabet = self.ALPHABET
original_length = len(self.__alphabet)
self.__alphabet = tuple(self.unique(self.__alphabet))
if len(self.__alphabet) < 2:
self.__error |= 1 << 3
self.__alphabet = self.ALPHABET
if len(self.__alphabet) != original_length:
self.__error |= 1 << 4
if len(self.__alphabet) > 128:
self.__error |= 1 << 5
self.__error |= (
1 << 0 if base < 2 else
1 << 1 if base > 128 else
1 << 2 if base >= len(self.__alphabet) else
0) << 6
if not self.__error >> 6:
self.__alphabet = self.__alphabet[0:base]
self.__power = int(log2(len(self.__alphabet[0:128])))
self.__base = 2 ** self.__power
self.__alphabet = self.__alphabet[0:self.__base]
for i, character in enumerate(self.__alphabet):
self.__dictionary[character] = i
return self.__error
def get_alphabet(self:Self) -> str:
return "".join(self.__alphabet)
def to_array(self:Self, code:str|Sequence[int]|int) -> list[int]:
if self.is_string(code):
return [self.__dictionary[character] for character in code]
if self.is_array(code):
return list(code)
if self.is_integer(code):
hexas:list[int] = []
while code:
hexas.append(code % self.__base)
code //= self.__base
return hexas or [0]
return []
def to_string(self:Self, code:list[int]|str|Sequence[int]|int) -> str:
if self.is_string(code):
return code
if self.is_array(code):
return "".join(self.__alphabet[i] for i in code)
if self.is_integer(code):
hexas:str = ""
while code:
hexas += self.__alphabet[code % self.__base]
code //= self.__base
return hexas or self.__alphabet[0]
return ""
def to_integer(self:Self, code:str|Sequence[int]|int) -> int:
if self.is_string(code):
return sum(self.__dictionary[character] * (self.__base ** i) for i, character in enumerate(code))
if self.is_array(code):
return sum(i * (self.__base ** j) for j, i in enumerate(code))
if self.is_integer(code):
return code
return 0
def to_string_binary(self:Self, code:str|Sequence[int]|int) -> str:
if self.is_string(code):
return "".join(("0000000" + "{0:b}".format(self.__dictionary[character]))[-self.__power:] for character in code[::-1])
if self.is_array(code):
return "".join(("0000000" + "{0:b}".format(hexa))[-self.__power:] for hexa in code[::-1])
if self.is_integer(code):
binary:str = "{0:b}".format(code)
module:int = len(binary) % self.__power
return ("0000000"[:self.__power - module] if module else "") + binary
return ""
def get_bits(self:Self, code:str|Sequence[int]|int) -> int:
if self.is_string(code):
return (len(code) - 1) * self.__power + ceil(log2(self.__dictionary[code[-1]] + 1))
if self.is_array(code):
return (len(code) - 1) * self.__power + ceil(log2(code[-1] + 1))
if self.is_integer(code):
return code.bit_length()
return 0
def get_from_bits(self:Self, code:str|Sequence[int]|int, _from:int, bits:int) -> tuple[int, int]:
if _from < 0:
_from = self.get_bits(code) + _from
if _from < 0:
_from = 0
if bits < 0:
_from += bits
bits = -bits
if _from < 0:
bits += _from
_from = 0
return _from, bits
def clean(self:Self, code:str|Sequence[int]|int) -> str|Sequence[int]|int|None:
if self.is_string(code):
l:int = len(code)
while l > 0 and code[l - 1] != self.__alphabet[0]:
l -= 1
return code[:l] or self.__alphabet[0]
if self.is_array(code):
l:int = len(code)
while l > 0 and code[l - 1] == 0:
l -= 1
return code[:l] or [0]
if self.is_integer(code):
return code
return None
def get_range(self:Self, code:str|Sequence[int]|int, _from:int, bits:int) -> str|Sequence[int]|int|None:
if self.is_string(code):
return self.to_string(self.get_range(self.to_array(code), _from, bits))
if self.is_array(code):
hexas:list[int]
_from, bits = self.get_from_bits(code, _from, bits)
if not bits:
bits = self.get_bits(code) - _from
if bits <= 0:
return [0]
hexas = list(code)
if _from > 0:
shift:int = _from % self.__power
mask:int = (self.__base) - 1
hexas = hexas[_from // self.__power:]
if shift and len(hexas):
hexa:int
i:int
for i, hexa in enumerate(hexas[:-1]):
hexas[i] = ((hexa >> shift) | (hexas[i + 1] << self.__power - shift)) & mask
hexas[-1] >>= shift
if bits > 0:
shift:int = bits % self.__power
hexas = hexas[0:int(ceil(bits / self.__power))]
if shift and len(hexas):
hexas[len(hexas) - 1] &= (1 << shift) - 1
return self.clean(hexas)
if self.is_integer(code):
_from, bits = self.get_from_bits(code, _from, bits)
if not bits:
bits = self.get_bits(code) - _from
if bits <= 0 or _from >= 31:
return 0
if _from + bits > 31:
bits = 31 - _from
return (code >> _from) & ((1 << bits) - 1)
return None
def process(self:Self, code:str|Sequence[int]|int, messages:Sequence[str], blocks:dict[int, int]) -> list[tuple[int, str]]:
if self.is_string(code) or self.is_integer(code):
return self.process(self.to_array(code), messages, blocks)
if self.is_array(code):
response:list[tuple[int, str]] = []
k:int = 0
i:int
l:int = len(messages)
i:int = 0
m:int = len(code) * self.__power
if blocks is None:
blocks = {}
while i < m:
if i in blocks and blocks[i]:
logarithm:int = int(ceil(log2(blocks[i])))
_k:int = self.to_integer(self.get_range(code, i, logarithm if logarithm else (logarithm := 0)))
if _k:
_k += k
response.append((_k, messages[k] if k < l else "error_message_" + str(_k)))
k += blocks[i]
i += logarithm
else:
if code[i // self.__power] & (1 << (i % self.__power)):
response.append((k, messages[k] if k < l else "error_message_" + str(k)))
k += 1
i += 1
return response
return []
def bitwise(self:Self, code:str|Sequence[int]|int, bits:int) -> str|Sequence[int]|int|None:
if self.is_string(code):
return self.to_string(self.bitwise(self.to_array(code), bits))
if self.is_array(code):
if not len(code) or not bits:
return self.clean(code)
shift:int = abs(bits) % self.__power
mask:int = self.__base - 1
hexas:list[int] = list(code)
if bits < 0:
hexas = hexas[-bits // self.__power:]
if shift and len(hexas):
hexa:int
i:int
for i, hexa in enumerate(hexas[:-1]):
hexas[i] = ((hexa >> shift) | (hexas[i + 1] << self.__power - shift)) & mask
hexas[-1] >>= shift
else:
if shift:
last_hexa:int = hexas[-1] << shift
for i, hexa in reversed(list(enumerate(hexas))[1:]):
hexas[i] = ((hexas[i] << shift) | (hexas[i - 1] >> self.__power - shift)) & mask
hexas[0] = (hexas[0] << shift) & mask
if last_hexa >= self.__base:
hexas.append(last_hexa >> self.__power)
hexas = [0] * (bits // self.__power) + hexas
return self.clean(hexas)
if self.is_integer(code):
return (
code << bits if bits > 0 else
code >> -bits if bits < 0 else
code)
return None
def reset(self:Self, code:str|Sequence[int]|int, _from:int, bits:int = 0, reversed:bool = False) -> str|Sequence[int]|int|None:
if self.is_string(code):
return self.to_string(self.reset(self.to_array(code), _from, bits, reversed))
if self.is_array(code):
hexas:list[int] = list(code)
hexa_from:int
hexa_to:int
l:int
from_mask:int
to_mask:int
_from, bits = self.get_from_bits(code, _from, bits)
hexa_from = _from // self.__power
hexa_to = (_from + bits) // self.__power
if reversed:
i:int
l = _from % self.__power
from_mask = ~((1 << l) - 1)
to_mask = (1 << ((_from + bits) % self.__power)) - 1
for i in range(len(hexas)):
if i < hexa_from or i > hexa_to:
hexas[i] = 0
if hexa_from == hexa_to:
hexas[hexa_from] &= from_mask & to_mask
else:
hexas[hexa_from] &= from_mask
if hexa_to < len(hexas):
hexas[hexa_to] &= to_mask
elif hexa_from < len(hexas):
l = (_from + bits) % self.__power
from_mask = ((1 << (_from % self.__power)) - 1)
to_mask = ((1 << (self.__power - l)) - 1) << l
if hexa_from == hexa_to:
hexas[hexa_from] &= from_mask | to_mask
else:
i:int
m:int = len(hexas)
hexas[hexa_from] &= from_mask
for i in range(hexa_from + 1, hexa_to):
if i >= m:
break
hexas[i] = 0
if hexa_to < m:
hexas[hexa_to] &= to_mask
return self.clean(hexas)
if self.is_integer(code):
_from, bits = self.get_from_bits(code, _from, bits)
if _from + bits > 31:
bits = 31 - _from
return code & (
((1 << bits) - 1) << _from if reversed else
((1 << self.get_bits(code)) - 1 << _from + bits) | ((1 << _from) - 1))
return None
def has(self:Self, code:str|Sequence[int]|int, _from:int = 0, bits:int = 0) -> bool:
if self.is_string(code):
for hexa in self.get_range(code, _from, bits):
if self.__dictionary[hexa]:
return True
if self.is_array(code):
for hexa in self.get_range(code, _from, bits):
if hexa:
return True
if self.is_integer(code) and self.get_range(code, _from, bits):
return True
return False
def set(self:Self, code:str|Sequence[int]|int, error:str|Sequence[int]|int, _from:int, bits:int = 0) -> str|Sequence[int]|int|None:
if self.is_string(code):
return self.to_string(self.set(self.to_array(code), error, _from, bits))
if self.is_array(code):
l:int
n:int = len(error := self.to_array(error))
m:int = len(code)
results:list[int] = []
if bits:
m = len(code := self.reset(code, _from, bits))
if _from:
n = len(error := self.bitwise(error, _from))
l = m if m > n else n
for i in range(l):
results.append(
(error[i] if i < n else 0) |
(code[i] if i < m else 0)
)
return self.clean(results)
if self.is_integer(code):
error = self.to_integer(error)
if bits:
code = self.reset(code, _from, bits)
if _from:
error = self.bitwise(error, _from)
return error | code
return None
@classmethod
def get_keys(cls:type[Self], *items:list[Any|None]) -> list[str]:
keys:list[str] = []
for item in items:
if isinstance(item, str):
cls.RE_KEY.match(item) and item not in keys and keys.append(item)
elif isinstance(item, (list, tuple)):
key:str
for key in cls.get_keys(*item):
key not in keys and keys.append(key)
return keys
@classmethod
def get_dictionaries(cls:type[Self], *items:list[Any|None]) -> list[dict[str, Any|None]]:
dictionaries:list[dict[str, Any|None]] = []
for item in items:
if isinstance(item, dict):
dictionaries.append(item)
elif isinstance(item, (list, tuple)):
dictionaries.extend(cls.get_dictionaries(*item))
return dictionaries
@classmethod
def get(cls:type[Self], keys:str|list[str], inputs:dict[str, Any|None]|Sequence[Any|None], default:Optional[Any] = None) -> Any|None:
if len(keys := cls.get_keys(keys)):
subinputs:dict[str, Any|None]
for subinputs in cls.get_dictionaries(inputs):
key:str
for key in keys:
if key in subinputs:
return subinputs[key]
return default
@staticmethod
def unique(items:str|bytes|Sequence[Any|None]) -> list[Any|None]:
return [item for i, item in enumerate(items) if list(items).index(item) == i]
@staticmethod
def is_string(item:Any|None) -> bool:
return isinstance(item, str)
@staticmethod
def is_array(item:Any|None) -> bool:
return isinstance(item, (list, tuple))
@staticmethod
def is_integer(item:Any|None) -> bool:
return isinstance(item, int)