#!/usr/bin/env python # -*- coding: utf-8 -*- from typing import Self, Any, Optional, Sequence from math import log2 from re import compile as re_compile, Pattern as REPattern, IGNORECASE as RE_IGNORE_CASE class ErrorsManager: RE_KEY:REPattern = re_compile(r'^[a-z_][a-z0-9_]*$', RE_IGNORE_CASE) def __init__(self:Self, inputs:Optional[str|dict[str, Any|None]|Sequence[Any|None]] = None) -> None: alphabet:str|Sequence[str] = self.unique(self.get_value("alphabet", ( inputs := {"alphabet" : inputs} if isinstance(inputs, str) else inputs), "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=")) maximum_base:int = int(log2(len(alphabet))) self.__alphabet:tuple[str] = ( tuple(alphabet) if isinstance(alphabet, (list, str)) else alphabet) self.__alphabet_dictionary:dict[str, int] = {character : i for i, character in enumerate(self.__alphabet)} self.__base:int = self.get_value("base", inputs, maximum_base) self.__mask:int if self.__base > maximum_base: self.__base = maximum_base self.__mask = ~-(1 << self.__base) print([self.__mask, self.__base, self.__alphabet]) def to_string(self:Self, code:str|int|Sequence[int]) -> str: if isinstance(code, str): return code if isinstance(code, (list, tuple)): return "".join([self.__alphabet[hexa] for hexa in code]) if isinstance(code, int): string:str = "" while code: string = self.__alphabet[code & self.__mask] + string code >>= self.__base return string return "" def to_integer(self:Self, code:str|int|Sequence[int]) -> int: if isinstance(code, str): integer:int = 0 hexa:str for i, hexa in enumerate(reversed(code)): integer |= self.__alphabet_dictionary[hexa] << self.__base * i return integer if isinstance(code, (list, tuple)): integer:int = 0 hexa:int for i, hexa in enumerate(reversed(code)): integer |= hexa << self.__base * i return integer if isinstance(code, int): return code return 0 def to_array(self:Self, code:str|int|Sequence[int]) -> list[int]: if isinstance(code, str): return [self.__alphabet_dictionary[hexa] for hexa in code] if isinstance(code, list): return list(code) if isinstance(code, tuple): return code if isinstance(code, int): array:list[int] = [] while code: array.append(code & self.__mask) code >>= self.__base return list(reversed(array)) return [] def reset(self:Self, code:int, start:int, length:int) -> int: return code and (code & ~(~-(1 << length) << start)) def set(self:Self, code:int, map:Sequence[int], codes:Sequence[int], start:int = 0, clean_bits:int = 0) -> int: new_code:int = 0 shift:int = 0 block:int i:int code = self.reset(code, start, clean_bits) for i, block in enumerate(map): new_code |= (codes[i] or (code >> start + shift & ~-(1 << block))) << shift shift += block return ( (code & ~-(1 << start)) | (new_code << start) | # ((code >> start + shift) << start + shift) (code & ~(~-(1 << start + shift))) ) def validate(self:Self, code:int, map:Sequence[int], messages:Sequence[Sequence[str]] = [] ) -> tuple[tuple[int, str]]: errors:list[tuple[int, str]] = [] if code: shift:int = 0 l:int = len(messages) i:int block:int for i, block in enumerate(map): subcode:int = (code >> shift) & ~-(1 << block) shift += block subcode and errors.append((i, ( messages[i][subcode - 1] if i < l and subcode - 1 < len(messages[i]) else "error_" + str(i) + "_" + str(subcode)))) return len(errors) != 0, tuple(errors) @classmethod def get_keys(cls:type[Self], *items:Sequence[Any|None]) -> list[str]: keys:list[str] = [] item:Any|None for item in items: if isinstance(item, str): cls.RE_KEY.match(item) and keys.append(item) elif isinstance(item, (list, tuple)): keys.extend(cls.get_keys(*item)) return keys @classmethod def get_dictionaries(cls:type[Self], *items:Sequence[Any|None]) -> list[dict[str, Any|None]]: dictionaries:list[str] = [] item: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_value(cls:type[Self], keys:str|Sequence[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|Sequence[Any|None]) -> str|Sequence[Any|None]: if isinstance(items, str): return "".join(character for i, character in enumerate(items) if items.index(character) == i) if isinstance(items, tuple): return tuple(item for i, item in enumerate(items) if items.index(item) == i) if isinstance(items, list): return [item for i, item in enumerate(items) if items.index(item) == i] return items