danielutils 1.1.28__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. danielutils/__init__.py +50 -0
  2. danielutils/abstractions/__init__.py +3 -0
  3. danielutils/abstractions/db/__init__.py +10 -0
  4. danielutils/abstractions/db/database.py +206 -0
  5. danielutils/abstractions/db/database_definitions.py +219 -0
  6. danielutils/abstractions/db/database_exceptions.py +34 -0
  7. danielutils/abstractions/db/database_factory.py +70 -0
  8. danielutils/abstractions/db/database_initializer.py +261 -0
  9. danielutils/abstractions/db/database_models.py +213 -0
  10. danielutils/abstractions/db/dependencies.py +6 -0
  11. danielutils/abstractions/db/implementations/__init__.py +4 -0
  12. danielutils/abstractions/db/implementations/in_memory_database.py +365 -0
  13. danielutils/abstractions/db/implementations/persistent_in_memory_database.py +174 -0
  14. danielutils/abstractions/db/implementations/redis_database.py +403 -0
  15. danielutils/abstractions/db/implementations/sqlite_database.py +491 -0
  16. danielutils/abstractions/multiprogramming/__init__.py +3 -0
  17. danielutils/abstractions/multiprogramming/multi_id.py +26 -0
  18. danielutils/abstractions/multiprogramming/worker.py +104 -0
  19. danielutils/abstractions/multiprogramming/worker_pool.py +98 -0
  20. danielutils/abstractions/repl.py +125 -0
  21. danielutils/aliases.py +174 -0
  22. danielutils/async_/__init__.py +7 -0
  23. danielutils/async_/async_cmd.py +28 -0
  24. danielutils/async_/async_command.py +879 -0
  25. danielutils/async_/async_layered_command.py +125 -0
  26. danielutils/async_/async_retry_executor.py +86 -0
  27. danielutils/async_/async_worker_pool.py +110 -0
  28. danielutils/async_/time_strategy.py +73 -0
  29. danielutils/async_/utils.py +57 -0
  30. danielutils/better_builtins/__init__.py +3 -0
  31. danielutils/better_builtins/counter.py +48 -0
  32. danielutils/better_builtins/frange.py +266 -0
  33. danielutils/better_builtins/typed_builtins/__init__.py +4 -0
  34. danielutils/better_builtins/typed_builtins/factory.py +126 -0
  35. danielutils/better_builtins/typed_builtins/tdict.py +36 -0
  36. danielutils/better_builtins/typed_builtins/tlist.py +223 -0
  37. danielutils/better_builtins/typed_builtins/tset.py +67 -0
  38. danielutils/better_builtins/typed_builtins/ttuple.py +15 -0
  39. danielutils/colors.py +203 -0
  40. danielutils/context_managers/__init__.py +5 -0
  41. danielutils/context_managers/attr_context.py +40 -0
  42. danielutils/context_managers/multi_context.py +34 -0
  43. danielutils/context_managers/optional_context.py +33 -0
  44. danielutils/context_managers/state_context.py +25 -0
  45. danielutils/context_managers/temporary_file.py +108 -0
  46. danielutils/convenience.py +12 -0
  47. danielutils/conversions/__init__.py +2 -0
  48. danielutils/conversions/main_conversions.py +131 -0
  49. danielutils/conversions/specialized_conversions/__init__.py +2 -0
  50. danielutils/conversions/specialized_conversions/to_hex.py +25 -0
  51. danielutils/conversions/specialized_conversions/to_int.py +19 -0
  52. danielutils/custom_types.py +18 -0
  53. danielutils/data_structures/__init__.py +8 -0
  54. danielutils/data_structures/algorithms.py +52 -0
  55. danielutils/data_structures/comparer.py +47 -0
  56. danielutils/data_structures/default_dict.py +23 -0
  57. danielutils/data_structures/functions.py +35 -0
  58. danielutils/data_structures/graph/__init__.py +4 -0
  59. danielutils/data_structures/graph/binary_node.py +71 -0
  60. danielutils/data_structures/graph/graph.py +208 -0
  61. danielutils/data_structures/graph/multinode.py +101 -0
  62. danielutils/data_structures/graph/node.py +60 -0
  63. danielutils/data_structures/heap/__init__.py +3 -0
  64. danielutils/data_structures/heap/heap.py +119 -0
  65. danielutils/data_structures/heap/max_heap.py +18 -0
  66. danielutils/data_structures/heap/min_heap.py +18 -0
  67. danielutils/data_structures/queue/__init__.py +3 -0
  68. danielutils/data_structures/queue/atomic_queue.py +15 -0
  69. danielutils/data_structures/queue/priority_queue.py +110 -0
  70. danielutils/data_structures/queue/queue.py +92 -0
  71. danielutils/data_structures/stack.py +102 -0
  72. danielutils/data_structures/trees/__init__.py +2 -0
  73. danielutils/data_structures/trees/binary_syntax_tree.py +84 -0
  74. danielutils/data_structures/trees/binary_tree.py +63 -0
  75. danielutils/date.py +43 -0
  76. danielutils/date_time.py +23 -0
  77. danielutils/decorators/__init__.py +18 -0
  78. danielutils/decorators/atomic.py +45 -0
  79. danielutils/decorators/attach.py +61 -0
  80. danielutils/decorators/chain_decorators.py +39 -0
  81. danielutils/decorators/decorate_conditionally.py +32 -0
  82. danielutils/decorators/delay_call.py +47 -0
  83. danielutils/decorators/deprecate.py +46 -0
  84. danielutils/decorators/final.py +39 -0
  85. danielutils/decorators/limit_recursion.py +63 -0
  86. danielutils/decorators/memo.py +68 -0
  87. danielutils/decorators/normalize_decorator.py +55 -0
  88. danielutils/decorators/overload.py +204 -0
  89. danielutils/decorators/partially_implemented.py +32 -0
  90. danielutils/decorators/processify.py +74 -0
  91. danielutils/decorators/property.py +42 -0
  92. danielutils/decorators/singleton.py +59 -0
  93. danielutils/decorators/threadify.py +40 -0
  94. danielutils/decorators/timeout.py +82 -0
  95. danielutils/decorators/total_ordering.py +82 -0
  96. danielutils/decorators/validate.py +227 -0
  97. danielutils/exceptions.py +90 -0
  98. danielutils/functions/__init__.py +13 -0
  99. danielutils/functions/areoneof.py +47 -0
  100. danielutils/functions/check_foreach.py +39 -0
  101. danielutils/functions/factorial.py +24 -0
  102. danielutils/functions/flatten.py +33 -0
  103. danielutils/functions/foreach.py +20 -0
  104. danielutils/functions/isoftype.py +475 -0
  105. danielutils/functions/isoneof.py +71 -0
  106. danielutils/functions/multiloop.py +51 -0
  107. danielutils/functions/parallel_for.py +47 -0
  108. danielutils/functions/partition.py +45 -0
  109. danielutils/functions/powerset.py +31 -0
  110. danielutils/functions/subseteq.py +25 -0
  111. danielutils/functions/types_subseteq.py +51 -0
  112. danielutils/generators/__init__.py +3 -0
  113. danielutils/generators/conditional_generator.py +59 -0
  114. danielutils/generators/generator_from_stream.py +31 -0
  115. danielutils/generators/join_generators.py +109 -0
  116. danielutils/internet.py +92 -0
  117. danielutils/io_.py +451 -0
  118. danielutils/java/__init__.py +18 -0
  119. danielutils/java/interfaces/__init__.py +1 -0
  120. danielutils/java/interfaces/comparable.py +31 -0
  121. danielutils/java/java_interface.py +59 -0
  122. danielutils/logging_/__init__.py +2 -0
  123. danielutils/logging_/_impl/__init__.py +4 -0
  124. danielutils/logging_/_impl/builtin_impls/__init__.py +2 -0
  125. danielutils/logging_/_impl/builtin_impls/file_logger.py +35 -0
  126. danielutils/logging_/_impl/builtin_impls/print_logger.py +21 -0
  127. danielutils/logging_/_impl/log_level.py +13 -0
  128. danielutils/logging_/_impl/logger.py +109 -0
  129. danielutils/logging_/_impl/logger_strategy_impl_base.py +37 -0
  130. danielutils/logging_/utils.py +199 -0
  131. danielutils/lombok/__init__.py +1 -0
  132. danielutils/lombok/builder.py +68 -0
  133. danielutils/math_/__init__.py +2 -0
  134. danielutils/math_/constants.py +103 -0
  135. danielutils/math_/functions.py +34 -0
  136. danielutils/math_/math_print.py +52 -0
  137. danielutils/math_/math_symbols.py +125 -0
  138. danielutils/metaclasses/__init__.py +5 -0
  139. danielutils/metaclasses/atomic_class_meta.py +36 -0
  140. danielutils/metaclasses/implicit_data_deleter_meta.py +87 -0
  141. danielutils/metaclasses/instance_cache_meta.py +66 -0
  142. danielutils/metaclasses/interface.py +328 -0
  143. danielutils/metaclasses/overload_meta.py +48 -0
  144. danielutils/mock_/__init__.py +1 -0
  145. danielutils/mock_/mock_module.py +27 -0
  146. danielutils/path.py +55 -0
  147. danielutils/print_.py +123 -0
  148. danielutils/progress_bar/__init__.py +3 -0
  149. danielutils/progress_bar/ascii_progress_bar.py +161 -0
  150. danielutils/progress_bar/progress_bar.py +75 -0
  151. danielutils/progress_bar/progress_bar_pool.py +72 -0
  152. danielutils/protocols/__init__.py +3 -0
  153. danielutils/protocols/dictable.py +38 -0
  154. danielutils/protocols/evaluable.py +26 -0
  155. danielutils/protocols/serializable.py +51 -0
  156. danielutils/py.typed +0 -0
  157. danielutils/random_.py +47 -0
  158. danielutils/reflection/__init__.py +6 -0
  159. danielutils/reflection/declarations.py +288 -0
  160. danielutils/reflection/file/__init__.py +1 -0
  161. danielutils/reflection/file/file_reflection.py +85 -0
  162. danielutils/reflection/function/__init__.py +1 -0
  163. danielutils/reflection/function/function_reflections.py +152 -0
  164. danielutils/reflection/info_classes/__init__.py +6 -0
  165. danielutils/reflection/info_classes/argument_info.py +278 -0
  166. danielutils/reflection/info_classes/class_info.py +204 -0
  167. danielutils/reflection/info_classes/decorator_info.py +54 -0
  168. danielutils/reflection/info_classes/file_info.py +1180 -0
  169. danielutils/reflection/info_classes/folder_info.py +592 -0
  170. danielutils/reflection/info_classes/function_info.py +551 -0
  171. danielutils/reflection/info_classes/import_info.py +207 -0
  172. danielutils/reflection/interpreter/__init__.py +9 -0
  173. danielutils/reflection/interpreter/callstack.py +57 -0
  174. danielutils/reflection/interpreter/get_traceback.py +27 -0
  175. danielutils/reflection/interpreter/interpreter.py +41 -0
  176. danielutils/reflection/interpreter/is_debugging.py +13 -0
  177. danielutils/reflection/interpreter/os_.py +33 -0
  178. danielutils/reflection/interpreter/packages.py +33 -0
  179. danielutils/reflection/interpreter/python_version.py +33 -0
  180. danielutils/reflection/interpreter/signals.py +18 -0
  181. danielutils/reflection/interpreter/tracer.py +175 -0
  182. danielutils/reflection/module/__init__.py +3 -0
  183. danielutils/reflection/module/lazy_module.py +104 -0
  184. danielutils/reflection/module/module_reflections.py +34 -0
  185. danielutils/reflection/module/package_reflection.py +161 -0
  186. danielutils/retry_executor/__init__.py +3 -0
  187. danielutils/retry_executor/backoff_strategies/__init__.py +6 -0
  188. danielutils/retry_executor/backoff_strategies/constant_backoff.py +25 -0
  189. danielutils/retry_executor/backoff_strategies/exponential_backoff.py +26 -0
  190. danielutils/retry_executor/backoff_strategies/functional_backoff.py +29 -0
  191. danielutils/retry_executor/backoff_strategies/linear_backoff.py +23 -0
  192. danielutils/retry_executor/backoff_strategies/multiplicative_backoff.py +23 -0
  193. danielutils/retry_executor/backoff_strategies/no_backoff.py +17 -0
  194. danielutils/retry_executor/backoff_strategy.py +28 -0
  195. danielutils/retry_executor/retry_executor.py +55 -0
  196. danielutils/snippets/__init__.py +1 -0
  197. danielutils/snippets/try_get.py +28 -0
  198. danielutils/system/__init__.py +3 -0
  199. danielutils/system/independent.py +197 -0
  200. danielutils/system/layered_command.py +130 -0
  201. danielutils/system/windows/__init__.py +2 -0
  202. danielutils/system/windows/utils/__init__.py +1 -0
  203. danielutils/system/windows/utils/filetime.py +19 -0
  204. danielutils/system/windows/win32_ctime.py +183 -0
  205. danielutils/system/windows/windows.py +78 -0
  206. danielutils/testing/__init__.py +1 -0
  207. danielutils/testing/unittest_/__init__.py +2 -0
  208. danielutils/testing/unittest_/always_teardown_testcase.py +75 -0
  209. danielutils/testing/unittest_/auto_cwd_testcase.py +114 -0
  210. danielutils/text.py +169 -0
  211. danielutils/time.py +64 -0
  212. danielutils/tools/__init__.py +1 -0
  213. danielutils/tools/unittest_test_runner/__init__.py +9 -0
  214. danielutils/tools/unittest_test_runner/cli.py +70 -0
  215. danielutils/tools/unittest_test_runner/core/__init__.py +17 -0
  216. danielutils/tools/unittest_test_runner/core/discovery.py +322 -0
  217. danielutils/tools/unittest_test_runner/core/execution.py +244 -0
  218. danielutils/tools/unittest_test_runner/core/parser.py +163 -0
  219. danielutils/tools/unittest_test_runner/core/runner.py +561 -0
  220. danielutils/tools/unittest_test_runner/models.py +82 -0
  221. danielutils/university/__init__.py +7 -0
  222. danielutils/university/computability_and_complexity/__init__.py +2 -0
  223. danielutils/university/computability_and_complexity/discreate_finite_automaton.py +89 -0
  224. danielutils/university/computability_and_complexity/languages/__init__.py +0 -0
  225. danielutils/university/computability_and_complexity/languages/language.py +17 -0
  226. danielutils/university/computability_and_complexity/languages/sat.py +71 -0
  227. danielutils/university/computability_and_complexity/turing_machine.py +47 -0
  228. danielutils/university/databases/__init__.py +1 -0
  229. danielutils/university/databases/all.py +756 -0
  230. danielutils/university/image_proccesing/__init__.py +1 -0
  231. danielutils/university/image_proccesing/encoding/__init__.py +3 -0
  232. danielutils/university/image_proccesing/encoding/encoding.py +35 -0
  233. danielutils/university/image_proccesing/encoding/lossless/__init__.py +4 -0
  234. danielutils/university/image_proccesing/encoding/lossless/huffman.py +0 -0
  235. danielutils/university/image_proccesing/encoding/lossless/lossless_encoding.py +10 -0
  236. danielutils/university/image_proccesing/encoding/lossless/lzw.py +0 -0
  237. danielutils/university/image_proccesing/encoding/lossless/run_length.py +22 -0
  238. danielutils/university/image_proccesing/encoding/lossy/__init__.py +1 -0
  239. danielutils/university/image_proccesing/encoding/lossy/lossy_encoding.py +10 -0
  240. danielutils/university/image_proccesing/tansformations/__init__.py +0 -0
  241. danielutils/university/image_proccesing/tansformations/gaussian.py +0 -0
  242. danielutils/university/image_proccesing/tansformations/gradient.py +0 -0
  243. danielutils/university/image_proccesing/tansformations/hough.py +0 -0
  244. danielutils/university/image_proccesing/tansformations/laplacian.py +0 -0
  245. danielutils/university/image_proccesing/tansformations/transformation.py +11 -0
  246. danielutils/university/linear_algebra/__init__.py +1 -0
  247. danielutils/university/linear_algebra/matrix.py +404 -0
  248. danielutils/university/machine_learning/__init__.py +0 -0
  249. danielutils/university/machine_learning/activation_functions/__init__.py +2 -0
  250. danielutils/university/machine_learning/activation_functions/activation_function.py +11 -0
  251. danielutils/university/machine_learning/activation_functions/relu.py +11 -0
  252. danielutils/university/machine_learning/neuron.py +15 -0
  253. danielutils/university/oop/__init__.py +1 -0
  254. danielutils/university/oop/observer.py +31 -0
  255. danielutils/university/oop/strategy.py +9 -0
  256. danielutils/university/probability/__init__.py +7 -0
  257. danielutils/university/probability/conditional_variable/__init__.py +3 -0
  258. danielutils/university/probability/conditional_variable/conditional_variable.py +138 -0
  259. danielutils/university/probability/conditional_variable/continuous/__init__.py +0 -0
  260. danielutils/university/probability/conditional_variable/discrete/__init__.py +8 -0
  261. danielutils/university/probability/conditional_variable/discrete/bernoulli.py +38 -0
  262. danielutils/university/probability/conditional_variable/discrete/bernoulli_sum.py +31 -0
  263. danielutils/university/probability/conditional_variable/discrete/binomial.py +40 -0
  264. danielutils/university/probability/conditional_variable/discrete/conditional_from_discrete_probability_func.py +36 -0
  265. danielutils/university/probability/conditional_variable/discrete/discrete.py +42 -0
  266. danielutils/university/probability/conditional_variable/discrete/geometric.py +43 -0
  267. danielutils/university/probability/conditional_variable/discrete/poisson.py +32 -0
  268. danielutils/university/probability/conditional_variable/discrete/uniform.py +33 -0
  269. danielutils/university/probability/distributions.py +35 -0
  270. danielutils/university/probability/expressions/__init__.py +2 -0
  271. danielutils/university/probability/expressions/accumulation_expression.py +291 -0
  272. danielutils/university/probability/expressions/probability_expression.py +110 -0
  273. danielutils/university/probability/funcs/__init__.py +4 -0
  274. danielutils/university/probability/funcs/covariance.py +12 -0
  275. danielutils/university/probability/funcs/expected_value.py +41 -0
  276. danielutils/university/probability/funcs/probability_function.py +14 -0
  277. danielutils/university/probability/funcs/variance.py +17 -0
  278. danielutils/university/probability/operator.py +79 -0
  279. danielutils/university/probability/protocols.py +32 -0
  280. danielutils/university/probability/supp.py +101 -0
  281. danielutils/university/probability/transformation.py +44 -0
  282. danielutils/versioned_imports.py +31 -0
  283. danielutils-1.1.28.dist-info/METADATA +390 -0
  284. danielutils-1.1.28.dist-info/RECORD +287 -0
  285. danielutils-1.1.28.dist-info/WHEEL +5 -0
  286. danielutils-1.1.28.dist-info/licenses/LICENSE +21 -0
  287. danielutils-1.1.28.dist-info/top_level.txt +1 -0
@@ -0,0 +1,25 @@
1
+ from ...metaclasses.overload_meta import OverloadMeta
2
+ from ..main_conversions import int_to_hex, char_to_hex
3
+
4
+
5
+ @OverloadMeta.overload
6
+ def to_hex(v: int) -> str:
7
+ # docstring at last implementation
8
+ return int_to_hex(v)
9
+
10
+
11
+ @to_hex.overload # type:ignore
12
+ def to_hex2(v: str) -> str:
13
+ """to_hex has several options:\n
14
+ 1. type(v) == int\n
15
+ 2. type(v) == str and len(v) == 1
16
+
17
+ Returns:
18
+ str: str of the hex value
19
+ """
20
+ return char_to_hex(v)
21
+
22
+
23
+ __all__ = [
24
+ "to_hex"
25
+ ]
@@ -0,0 +1,19 @@
1
+ """functions that convert values to int"""
2
+ from typing import Union, List as List
3
+ from ..main_conversions import char_to_int
4
+ from ...reflection import get_python_version
5
+ if get_python_version() >= (3, 9):
6
+ from builtins import list as List
7
+
8
+
9
+ def to_int(value: str) -> Union[int, List[int]]:
10
+ """converts a single character or a full string to an int or list of int respectively
11
+ """
12
+ if len(value) == 1:
13
+ return char_to_int(value)
14
+ return [char_to_int(ch) for ch in value]
15
+
16
+
17
+ __all__ = [
18
+ "to_int"
19
+ ]
@@ -0,0 +1,18 @@
1
+ from typing import Callable, TypeVar
2
+ from .versioned_imports import TypeAlias
3
+
4
+ T = TypeVar('T')
5
+ U = TypeVar('U')
6
+ Procedure: TypeAlias = Callable[[], None]
7
+ Supplier: TypeAlias = Callable[[], T]
8
+ Consumer: TypeAlias = Callable[[T], None]
9
+ Predicate: TypeAlias = Callable[[], bool]
10
+ Mapper: TypeAlias = Callable[[T], U]
11
+
12
+ __all__ = [
13
+ 'Procedure',
14
+ 'Supplier',
15
+ 'Consumer',
16
+ 'Predicate',
17
+ 'Mapper'
18
+ ]
@@ -0,0 +1,8 @@
1
+ from .queue import *
2
+ from .heap import *
3
+ from .comparer import *
4
+ from .graph import *
5
+ from .stack import *
6
+ from .default_dict import *
7
+ from .trees import *
8
+ from .algorithms import *
@@ -0,0 +1,52 @@
1
+ import logging
2
+ from collections import defaultdict
3
+ from typing import TypeVar, List, Callable, Dict
4
+ from itertools import product
5
+ from copy import deepcopy
6
+ from ..logging_.utils import get_logger
7
+ logger = get_logger(__name__)
8
+
9
+ NodeT = TypeVar("NodeT")
10
+
11
+ DistanceMatrix = Dict[NodeT, Dict[NodeT, float]]
12
+
13
+
14
+ def bellman_ford(nodes: List[NodeT], weight_func: Callable[[NodeT, NodeT], float],
15
+ iteration_callback: Callable[[DistanceMatrix], None],
16
+ poisoned_reverse: bool = False) -> DistanceMatrix:
17
+ logger.debug("Starting Bellman-Ford algorithm with %s nodes, poisoned_reverse=%s", len(nodes), poisoned_reverse)
18
+ dist: Dict[NodeT, Dict[NodeT, float]] = defaultdict(defaultdict)
19
+ prev: Dict[NodeT, Dict[NodeT, NodeT]] = defaultdict(defaultdict)
20
+
21
+ for u, v in product(nodes, nodes):
22
+ dist[u][v] = weight_func(u, v)
23
+
24
+ logger.debug("Initial distance matrix computed")
25
+ iteration_callback(dist)
26
+
27
+ for iteration in range(len(nodes) - 1):
28
+ logger.debug("Bellman-Ford iteration %s/%s", iteration + 1, len(nodes) - 1)
29
+ tmp = deepcopy(dist)
30
+ for u, v in product(nodes, nodes):
31
+ if u == v:
32
+ continue
33
+
34
+ for mid in nodes:
35
+ if mid == u or mid == v:
36
+ continue
37
+
38
+ if dist[u][v] > dist[u][mid] + dist[mid][v]:
39
+ tmp[u][v] = dist[u][mid] + dist[mid][v]
40
+ prev[u][v] = mid
41
+
42
+ dist = tmp
43
+ iteration_callback(dist)
44
+
45
+ logger.debug("Bellman-Ford algorithm completed")
46
+ iteration_callback(prev)
47
+ return dist
48
+
49
+
50
+ __all__ = [
51
+ "bellman_ford"
52
+ ]
@@ -0,0 +1,47 @@
1
+ """Comparer class"""
2
+ import logging
3
+ from typing import Callable, Union, Generic, TypeVar
4
+ from ..logging_.utils import get_logger
5
+ from .functions import default_weight_function
6
+ logger = get_logger(__name__)
7
+
8
+ U = TypeVar("U")
9
+ V = TypeVar("V")
10
+
11
+
12
+ class Comparer(Generic[U, V]):
13
+ """a Comparer class to be used when comparing two objects
14
+ """
15
+
16
+ def __init__(self, func: Callable[[U, V], Union[int, float]]):
17
+ self.func = func
18
+ logger.debug("Comparer initialized with function: %s", func.__name__)
19
+
20
+ def compare(self, v1: U, v2: V) -> Union[int, float]:
21
+ """compares two objects
22
+
23
+ Args:
24
+ v1 (Any): first object
25
+ v2 (Any): second object
26
+
27
+ Returns:
28
+ int: a number specifying the order of the objects
29
+ """
30
+ logger.debug("Comparing objects: %s vs %s", v1, v2)
31
+ result = self.func(v1, v2)
32
+ logger.debug("Comparison result: %s", result)
33
+ return result
34
+
35
+ def __call__(self, v1: U, v2: V) -> Union[int, float]:
36
+ return self.compare(v1, v2)
37
+
38
+
39
+ CompareGreater: Comparer[U, V] = Comparer(lambda a, b: default_weight_function(a) -
40
+ default_weight_function(b))
41
+ CompareSmaller: Comparer[U, V] = Comparer(lambda a, b: default_weight_function(b) -
42
+ default_weight_function(a))
43
+ __all__ = [
44
+ "Comparer",
45
+ "CompareGreater",
46
+ "CompareSmaller"
47
+ ]
@@ -0,0 +1,23 @@
1
+ import logging
2
+ from ..logging_.utils import get_logger
3
+ logger = get_logger(__name__)
4
+
5
+
6
+ class DefaultDict(dict):
7
+ """
8
+ My implementation to `collections.defaultdict`
9
+ """
10
+
11
+ def __init__(self, cls):
12
+ self._cls = cls
13
+ logger.debug("DefaultDict initialized with default factory: %s", cls)
14
+
15
+ def __getitem__(self, key):
16
+ if key not in self:
17
+ logger.debug("Key '%s' not found, creating default value using %s", key, self._cls)
18
+ return super().get(key, self._cls())
19
+
20
+
21
+ __all__ = [
22
+ "DefaultDict"
23
+ ]
@@ -0,0 +1,35 @@
1
+ import logging
2
+ from typing import Any, Union
3
+ from ..functions import isoftype
4
+ from ..logging_.utils import get_logger
5
+ logger = get_logger(__name__)
6
+
7
+
8
+ def default_weight_function(v: Any) -> Union[int, float]:
9
+ """will return the weight of an object
10
+
11
+ Args:
12
+ v (Any): object
13
+
14
+ Raises:
15
+ AttributeError: if the object is not a number or doesn't have __weight__ function defined
16
+
17
+ Returns:
18
+ Union[int, float]: the object's weight
19
+ """
20
+ logger.debug("Computing weight for object: %s", v)
21
+ if isoftype(v, Union[int, float]): # type:ignore
22
+ logger.debug("Object is numeric, returning value: %s", v)
23
+ return v
24
+ if hasattr(v, "__weight__"):
25
+ logger.debug("Object has __weight__ method, calling it")
26
+ result = v.__weight__()
27
+ logger.debug("Weight method returned: %s", result)
28
+ return result
29
+ logger.error("Object %s has no __weight__ function and is not numeric", v)
30
+ raise AttributeError(f"{v} has no __weight__ function")
31
+
32
+
33
+ __all__ = [
34
+ "default_weight_function"
35
+ ]
@@ -0,0 +1,4 @@
1
+ from .graph import *
2
+ from .node import *
3
+ from .multinode import *
4
+ from .binary_node import *
@@ -0,0 +1,71 @@
1
+ from __future__ import annotations
2
+ from typing import Optional, TypeVar
3
+ from .multinode import MultiNode
4
+ from ...metaclasses import ImplicitDataDeleterMeta
5
+
6
+ T = TypeVar("T")
7
+
8
+
9
+ class BinaryNode(MultiNode[T], metaclass=ImplicitDataDeleterMeta):
10
+ """A 'classic' node class with only one child
11
+ """
12
+
13
+ def __init__(self, data: T, l: Optional[BinaryNode[T]] = None,
14
+ r: Optional[BinaryNode[T]] = None): # pylint: disable=redefined-builtin
15
+ # intentionally can be None
16
+ super().__init__(data, [l, r]) # type:ignore
17
+
18
+ def __str__(self):
19
+ return MultiNode.__str__(self).replace(
20
+ self.__class__.__mro__[1].__name__,
21
+ self.__class__.__name__
22
+ ).replace("[", "").replace("]", "")
23
+
24
+ def __repr__(self):
25
+ return str(self)
26
+
27
+ def __reversed__(self) -> 'BinaryNode[T]':
28
+ return self.reverse()
29
+
30
+ def __eq__(self, other):
31
+ return MultiNode.__eq__(self, other)
32
+
33
+ def __iter__(self):
34
+ return MultiNode.__iter__(self)
35
+
36
+ @property
37
+ def left(self) -> "BinaryNode[T]":
38
+ """return the next node after self
39
+ """
40
+ return self._children[0] # type:ignore
41
+
42
+ @left.setter
43
+ def left(self, value: "BinaryNode[T]") -> None:
44
+ self._children[0] = value
45
+
46
+ @property
47
+ def right(self) -> "BinaryNode[T]":
48
+ """return the next node after self
49
+ """
50
+ return self._children[1] # type:ignore
51
+
52
+ @right.setter
53
+ def right(self, value: "BinaryNode[T]") -> None:
54
+ self._children[1] = value
55
+
56
+ @property
57
+ def is_leaf(self) -> bool:
58
+ return self.left is None and self.right is None
59
+
60
+ def reverse(self) -> 'BinaryNode[T]':
61
+ new_left = self.right.reverse() if self.right is not None else None
62
+ new_right = self.left.reverse() if self.left is not None else None
63
+ return BinaryNode(self.data, new_left, new_right)
64
+
65
+ def depth(self) -> int:
66
+ return MultiNode.depth(self)
67
+
68
+
69
+ __all__ = [
70
+ "BinaryNode"
71
+ ]
@@ -0,0 +1,208 @@
1
+ import logging
2
+ from typing import Optional, Generator, List as List, Set as Set, Dict as Dict, Generic, \
3
+ TypeVar, Iterable, Iterator
4
+ from ...logging_.utils import get_logger
5
+ from ..queue import Queue
6
+ from .multinode import MultiNode
7
+ from ...reflection import get_python_version
8
+
9
+ if get_python_version() >= (3, 9):
10
+ from builtins import list as List, set as Set, dict as Dict
11
+
12
+ T = TypeVar("T")
13
+
14
+ logger = get_logger(__name__)
15
+
16
+
17
+ class Graph(Generic[T]):
18
+ """A general-purpose Graph class.
19
+
20
+ This class represents a directed graph, where nodes can be connected through edges.
21
+
22
+ Attributes:
23
+ nodes (Optional[List[MultiNode]]): A list of MultiNode instances representing the nodes in the graph.
24
+ Default is an empty list.
25
+
26
+ Methods:
27
+ __init__(self, nodes: Optional[List[MultiNode]] = None): Initialize the Graph with given nodes.
28
+ add_node(self, node): Add a node to the graph.
29
+ _extended_dfs(self) -> Generator: Perform an extended depth-first search on the graph.
30
+ dfs(self) -> Generator: Perform a depth-first search on the graph.
31
+ topological_sort(self) -> list: Get a topological sort of the graph nodes.
32
+ bfs(self) -> Generator: Perform a breadth-first search on the graph.
33
+ __str__(self) -> str: Get a string representation of the graph.
34
+
35
+ """
36
+
37
+ def to_dict(self) -> Dict[T, Set[T]]:
38
+ """
39
+ converts the graph to a dictionary.
40
+ Returns:
41
+ dict: A dictionary representing the graph.
42
+ """
43
+ logger.debug("Converting graph with %s nodes to dictionary", len(self.nodes))
44
+ dct: Dict[T, Set[T]] = {}
45
+ for node in self:
46
+ v = dct.get(node.data, set())
47
+ for child in node:
48
+ v.add(child.data)
49
+ dct[node.data] = v
50
+ logger.debug("Graph converted to dictionary with %s entries", len(dct))
51
+ return dct
52
+
53
+ @staticmethod
54
+ def from_dict(dct: Dict[T, Iterable[T]]) -> "Graph[T]":
55
+ """
56
+ converts a dictionary to a graph.
57
+ Args:
58
+ dct: A dictionary representing the graph.
59
+
60
+ Returns:
61
+ Graph[T]: A graph representing the given dictionary.
62
+ """
63
+ logger.debug("Creating graph from dictionary with %s entries", len(dct))
64
+ g: Graph[T] = Graph()
65
+ seen: Dict[T, MultiNode[T]] = {}
66
+ for k, v in dct.items():
67
+ seen[k] = seen.get(k, MultiNode(k))
68
+
69
+ for o in v:
70
+ seen[o] = seen.get(o, MultiNode(o))
71
+ seen[k].add_child(seen[o])
72
+
73
+ g.add_node(seen[k])
74
+ logger.info("Graph created from dictionary with %s nodes", len(g.nodes))
75
+ return g
76
+
77
+ def __init__(self, nodes: Optional[List[MultiNode[T]]] = None):
78
+ self.nodes: List[MultiNode[T]] = nodes if nodes is not None else []
79
+ logger.debug("Graph initialized with %s nodes", len(self.nodes))
80
+
81
+ def add_node(self, node: MultiNode[T]) -> None:
82
+ """Add a node to the graph.
83
+
84
+ Args:
85
+ node: The MultiNode instance to add to the graph.
86
+ """
87
+ self.nodes.append(node)
88
+ logger.debug("Added node to graph, total nodes: %s", len(self.nodes))
89
+
90
+ def _extended_dfs(self) -> Generator[MultiNode[T], None, List[MultiNode[T]]]:
91
+ """Perform an extended depth-first search on the graph.
92
+
93
+ This private method performs an extended depth-first search (DFS) on the graph,
94
+ keeping track of enter and exit times for each node, and returns a generator that yields
95
+ nodes in the order of DFS traversal.
96
+
97
+ Yields:
98
+ Generator: The MultiNode instances in the order of depth-first traversal.
99
+ """
100
+ seen: set = set()
101
+ enter_times: dict = {}
102
+ exit_times: dict = {}
103
+ travel_index: int = 1
104
+ all_nodes: List[MultiNode] = []
105
+
106
+ def handle_node(node: MultiNode[T]) -> Generator[MultiNode[T], None, None]:
107
+ nonlocal travel_index
108
+ seen.add(node)
109
+ all_nodes.append(node)
110
+ yield node
111
+ for subnode in node._children: # pylint: disable=protected-access
112
+ if subnode not in seen:
113
+ travel_index += 1
114
+ enter_times[subnode] = travel_index
115
+ if subnode is not None:
116
+ yield from handle_node(subnode)
117
+ travel_index += 1
118
+ exit_times[subnode] = travel_index
119
+
120
+ for node in self.nodes:
121
+ if node not in seen:
122
+ enter_times[node] = travel_index
123
+ travel_index += 1
124
+ yield from handle_node(node)
125
+ travel_index += 1
126
+ exit_times[node] = travel_index
127
+ topological_order = sorted(
128
+ all_nodes, key=lambda v: exit_times[v], reverse=True)
129
+ return topological_order
130
+
131
+ # def topological_sort(graph: Graph):
132
+ # def dfs(node: MultiNode, visited: set, result: list):
133
+ # visited.add(node)
134
+ # for neighbor in node:
135
+ # if neighbor not in visited:
136
+ # dfs(neighbor, visited, result)
137
+ # result.append(node)
138
+ #
139
+ # visited: set = set()
140
+ # result: list = []
141
+ # for node in graph:
142
+ # if node not in visited:
143
+ # dfs(node, visited, result)
144
+ # return result[::-1] # Reverse the result list
145
+
146
+ def dfs(self) -> Generator[MultiNode[T], None, None]:
147
+ """Perform a depth-first search on the graph.
148
+
149
+ This method performs a depth-first search (DFS) on the graph using the private _extended_dfs method.
150
+
151
+ Yields:
152
+ Generator: The MultiNode instances in the order of depth-first traversal.
153
+ """
154
+ logger.debug("Starting DFS traversal on graph with %s nodes", len(self.nodes))
155
+ yield from self._extended_dfs()
156
+
157
+ def topological_sort(self) -> List[MultiNode[T]]:
158
+ """Get a topological sort of the graph nodes.
159
+
160
+ This method performs a topological sort on the graph using the private _extended_dfs method.
161
+
162
+ Returns:
163
+ list: A list containing the MultiNode instances in topological order.
164
+ """
165
+ logger.debug("Starting topological sort on graph with %s nodes", len(self.nodes))
166
+ g = self._extended_dfs()
167
+ try:
168
+ while True:
169
+ next(g)
170
+ except StopIteration as e:
171
+ result = e.value
172
+ logger.info("Topological sort completed, result has %s nodes", len(result))
173
+ return result
174
+
175
+ def bfs(self) -> Generator[MultiNode[T], None, None]:
176
+ """Perform a breadth-first search on the graph.
177
+
178
+ This method performs a breadth-first search (BFS) on the graph using a queue.
179
+
180
+ Yields:
181
+ Generator: The MultiNode instances in the order of breadth-first traversal.
182
+ """
183
+ logger.debug("Starting BFS traversal on graph with %s nodes", len(self.nodes))
184
+ q: Queue[MultiNode[T]] = Queue()
185
+ for node in self.nodes:
186
+ q.push(node)
187
+ seen: Set[MultiNode] = set()
188
+ for node in q:
189
+ if node not in seen:
190
+ seen.add(node)
191
+ yield node
192
+ for child in node._children: # pylint: disable=protected-access
193
+ q.push(child) # type:ignore
194
+ logger.debug("BFS traversal completed, visited %s nodes", len(seen))
195
+
196
+ def __str__(self) -> str:
197
+ tmp = []
198
+ for n in self.dfs():
199
+ tmp.append(f"\t{str(n)}")
200
+ return "Graph(\n" + ",\n".join(tmp) + "\n)"
201
+
202
+ def __iter__(self) -> Iterator[MultiNode[T]]:
203
+ return iter(self.nodes)
204
+
205
+
206
+ __all__ = [
207
+ "Graph"
208
+ ]
@@ -0,0 +1,101 @@
1
+ from typing import Optional, Generator, TypeVar, Generic, List as List, Set
2
+
3
+ from ...reflection import get_python_version
4
+
5
+ if get_python_version() >= (3, 9):
6
+ from builtins import list as List
7
+ T = TypeVar("T")
8
+
9
+
10
+ class MultiNode(Generic[T]):
11
+ """A node class with no limit to children amount
12
+ """
13
+
14
+ def __init__(self, data: T, children: Optional[List[Optional['MultiNode[T]']]] = None):
15
+ self.data: T = data
16
+ self._children: List[Optional[MultiNode[T]]] = children if children is not None else []
17
+
18
+ def __getitem__(self, index) -> T:
19
+ return self._children[index]
20
+
21
+ def __setitem__(self, value: T, index: int) -> None:
22
+ self._children[index] = value # type:ignore
23
+
24
+ def __len__(self) -> int:
25
+ return len(self._children)
26
+
27
+ def __iter__(self) -> Generator["MultiNode[T]", None, None]:
28
+ yield from self._children
29
+
30
+ def __str__(self) -> str:
31
+ res = ""
32
+ seen = set()
33
+
34
+ def handle_node(node: MultiNode):
35
+ nonlocal res
36
+ # res += f"MultiNode({node.data}, ["
37
+ seen.add(node)
38
+ tmp = []
39
+ for child in node._children: # pylint: disable=protected-access
40
+ if child in seen:
41
+ tmp.append("...")
42
+ else:
43
+ if child is not None:
44
+ tmp.append(handle_node(child))
45
+ child_str = ", [" + ", ".join(tmp) + "]" if len(tmp) > 0 else ""
46
+ return f"{node.__class__.__name__}({node.data}{child_str})"
47
+
48
+ return handle_node(self)
49
+
50
+ def __repr__(self):
51
+ return str(self)
52
+
53
+ def __eq__(self, other) -> bool:
54
+ if not isinstance(other, MultiNode):
55
+ return False
56
+
57
+ return self.data == other.data and len(self) == len(other) and all(
58
+ [a.data == b.data for a, b in zip(self, other)])
59
+
60
+ def __hash__(self) -> int:
61
+ return hash((self.__class__, self.data, len(self), (c.data for c in self)))
62
+
63
+ def __reversed__(self) -> 'MultiNode[T]':
64
+ return self.reverse()
65
+
66
+ @property
67
+ def is_leaf(self) -> bool:
68
+ return len(self._children) == 0
69
+
70
+ def reverse(self) -> 'MultiNode[T]':
71
+ return MultiNode(self.data, self._children[::-1])
72
+
73
+ def add_child(self, child: "MultiNode[T]") -> None:
74
+ """adds a child to current node
75
+ """
76
+ self._children.append(child)
77
+
78
+ def depth(self) -> int:
79
+ """
80
+ Returns the depth of the current node (including).
81
+ Returns:
82
+ int
83
+ """
84
+
85
+ def helper(cur: MultiNode, seen: Optional[Set[MultiNode]] = None) -> int:
86
+ if not isinstance(cur, MultiNode):
87
+ return 0
88
+ if seen is None:
89
+ seen = set()
90
+ elif cur in seen:
91
+ return 0
92
+ seen.add(cur)
93
+ seq = [helper(child, seen) for child in cur if ((child is not None) and (child not in seen))]
94
+ return 1 + (max(seq) if len(seq) > 0 else 0)
95
+
96
+ return helper(self)
97
+
98
+
99
+ __all__ = [
100
+ "MultiNode"
101
+ ]
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+ from typing import Optional, TypeVar
3
+ from ...metaclasses import ImplicitDataDeleterMeta
4
+ from .multinode import MultiNode
5
+
6
+ T = TypeVar("T")
7
+
8
+
9
+ class Node(MultiNode[T], metaclass=ImplicitDataDeleterMeta):
10
+ """A 'classic' node class with only one child
11
+ """
12
+
13
+ def __init__(self, data: T, next: Optional[Node[T]] = None): # pylint: disable=redefined-builtin
14
+ super().__init__(data, [next])
15
+
16
+ @property
17
+ def next(self) -> "Node[T]":
18
+ """return the next node after self
19
+ """
20
+ return self._children[0] # type:ignore
21
+
22
+ @next.setter
23
+ def next(self, value: Node[T]) -> None:
24
+ self._children[0] = value
25
+
26
+ def __str__(self):
27
+ # res = ""
28
+ # seen = set()
29
+
30
+ # def handle_node(node: Node):
31
+ # nonlocal res
32
+ # if node in seen:
33
+ # res += "..."
34
+ # else:
35
+ # seen.add(node)
36
+ # res += f"Node({node.data}, "
37
+ # if node.next is None:
38
+ # res += "None)"
39
+ # elif node.next in seen:
40
+ # res += "...)"
41
+
42
+ # curr = self
43
+ # while curr is not None:
44
+ # handle_node(curr)
45
+ # curr = curr.next
46
+ # if curr in seen:
47
+ # break
48
+ # return res+")"
49
+ return MultiNode.__str__(self).replace(
50
+ self.__class__.__mro__[1].__name__,
51
+ self.__class__.__name__
52
+ ).replace("[", "").replace("]", "")
53
+
54
+ def __repr__(self):
55
+ return str(self)
56
+
57
+
58
+ __all__ = [
59
+ "Node"
60
+ ]
@@ -0,0 +1,3 @@
1
+ from .heap import *
2
+ from .max_heap import *
3
+ from .min_heap import *