diff --git a/Python/v2/ErrorsManager.py b/Python/v2/ErrorsManager.py new file mode 100644 index 0000000..2fd640b --- /dev/null +++ b/Python/v2/ErrorsManager.py @@ -0,0 +1,193 @@ +#!/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 \ No newline at end of file diff --git a/Python/v2/tests.py b/Python/v2/tests.py new file mode 100644 index 0000000..2e1579a --- /dev/null +++ b/Python/v2/tests.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from ErrorsManager import ErrorsManager + +class Tests: + + @staticmethod + def builder() -> None: + print(ErrorsManager()) + + @staticmethod + def conversions() -> None: + + option:str|int|list[int] + errors:ErrorsManager = ErrorsManager() + + for option in ( + "Hola", 923452, [45, 2, 0, 12] + ): + + string:str = errors.to_string(option) + integer:int = errors.to_integer(option) + array:list[int] = errors.to_array(option) + + print(option) + print(["T", { + "s" : string, + "i" : integer, + "a" : array + }]) + print(["S", { + "s" : errors.to_string(string), + "i" : errors.to_integer(string), + "a" : errors.to_array(string) + }]) + print(["I", { + "s" : errors.to_string(integer), + "i" : errors.to_integer(integer), + "a" : errors.to_array(integer) + }]) + print(["A", { + "s" : errors.to_string(array), + "i" : errors.to_integer(array), + "a" : errors.to_array(array) + }]) + +Tests.conversions() \ No newline at end of file diff --git a/version b/version index c8bdd68..340ff46 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.0.1.29 \ No newline at end of file +0.0.2.1 \ No newline at end of file