Selfie
Loading...
Searching...
No Matches
ArrayMap.py
Go to the documentation of this file.
1from abc import ABC, abstractmethod
2from collections.abc import ItemsView, Iterator, Mapping, Set
3from typing import Any, TypeVar, Union
4
5T = TypeVar("T")
6V = TypeVar("V")
7K = TypeVar("K")
8
9
10def _compare_normal(a, b) -> int:
11 if a == b:
12 return 0
13 elif a < b:
14 return -1
15 else:
16 return 1
17
18
19def _compare_string_slash_first(a: str, b: str) -> int:
20 return _compare_normal(a.replace("/", "\0"), b.replace("/", "\0"))
21
22
23def _binary_search(data, item) -> int:
24 compare_func = (
25 _compare_string_slash_first if isinstance(item, str) else _compare_normal
26 )
27 low, high = 0, len(data) - 1
28 while low <= high:
29 mid = (low + high) // 2
30 mid_val = data[mid]
31 comparison = compare_func(mid_val, item)
32
33 if comparison < 0:
34 low = mid + 1
35 elif comparison > 0:
36 high = mid - 1
37 else:
38 return mid # item found
39 return -(low + 1) # item not found
40
41
42class ListBackedSet(Set[T], ABC):
43 @abstractmethod
44 def __len__(self) -> int: ...
45
46 @abstractmethod
47 def __getitem__(self, index: Union[int, slice]) -> Union[T, list[T]]: ...
48
49 @abstractmethod
50 def __iter__(self) -> Iterator[T]: ...
51
52 def __contains__(self, item: Any) -> bool:
53 return self._binary_search(item) >= 0
54
55 def _binary_search(self, item: Any) -> int:
56 return _binary_search(self, item)
57
58
60 __data: list[K]
61
62 def __init__(self):
63 raise NotImplementedError("Use ArraySet.empty() or other class methods instead")
64
65 @classmethod
66 def __create(cls, data: list[K]) -> "ArraySet[K]":
67 instance = super().__new__(cls)
68 instance.__data = data # noqa: SLF001
69 return instance
70
71 def __iter__(self) -> Iterator[K]:
72 return iter(self.__data__data)
73
74 @classmethod
75 def empty(cls) -> "ArraySet[K]":
76 if not hasattr(cls, "__EMPTY"):
77 cls.__EMPTY = cls.__create([])
78 return cls.__EMPTY
79
80 def __len__(self) -> int:
81 return len(self.__data__data)
82
83 def __getitem__(self, index: Union[int, slice]) -> Union[K, list[K]]:
84 return self.__data__data[index]
85
86 def plusOrThis(self, element: K) -> "ArraySet[K]":
87 index = self._binary_search(element)
88 if index >= 0:
89 return self
90 else:
91 insert_at = -(index + 1)
92 new_data = self.__data__data[:]
93 new_data.insert(insert_at, element)
94 return ArraySet.__create(new_data)
95
96
98 def __init__(self, data: list[Union[K, V]]):
99 self.__data = data
100
101 def __len__(self) -> int:
102 return len(self.__data) // 2
103
104 def __getitem__(self, index: Union[int, slice]): # type: ignore
105 if isinstance(index, slice):
106 return [
107 self.__data[i]
108 for i in range(
109 index.start * 2 if index.start else 0,
110 index.stop * 2 if index.stop else len(self.__data),
111 index.step * 2 if index.step else 2,
112 )
113 ]
114 else:
115 return self.__data[2 * index]
116
117 def __iter__(self) -> Iterator[K]:
118 return (self.__data[i] for i in range(0, len(self.__data), 2)) # type: ignore
119
120
121class _ArrayMapEntries(ListBackedSet[tuple[K, V]], ItemsView[K, V]):
122 def __init__(self, data: list[Union[K, V]]):
123 self.__data = data
124
125 def __len__(self) -> int:
126 return len(self.__data) // 2
127
128 def __getitem__(self, index: Union[int, slice]): # type: ignore
129 if isinstance(index, slice):
130 return [
131 (self.__data[i], self.__data[i + 1])
132 for i in range(
133 index.start * 2 if index.start else 0,
134 index.stop * 2 if index.stop else len(self.__data),
135 index.step * 2 if index.step else 2,
136 )
137 ]
138 else:
139 return (self.__data[2 * index], self.__data[2 * index + 1])
140
141 def __iter__(self) -> Iterator[tuple[K, V]]:
142 return (
143 (self.__data[i], self.__data[i + 1]) for i in range(0, len(self.__data), 2)
144 ) # type: ignore
145
146
147class ArrayMap(Mapping[K, V]):
148 __data: list[Union[K, V]]
149 __keys: ListBackedSet[K]
150
151 def __init__(self):
152 raise NotImplementedError("Use ArrayMap.empty() or other class methods instead")
153
154 @classmethod
155 def __create(cls, data: list[Union[K, V]]) -> "ArrayMap[K, V]":
156 instance = cls.__new__(cls)
157 instance.__data = data # noqa: SLF001
158 instance.__keys = _ArrayMapKeys(data) # noqa: SLF001
159 return instance
160
161 @classmethod
162 def empty(cls) -> "ArrayMap[K, V]":
163 if not hasattr(cls, "__EMPTY"):
164 cls.__EMPTY = cls.__create([])
165 return cls.__EMPTY
166
167 def keys(self) -> ListBackedSet[K]: # type: ignore
168 return self.__keys__keys
169
170 def items(self) -> _ArrayMapEntries[K, V]: # type: ignore
171 return _ArrayMapEntries(self.__data__data)
172
173 def __getitem__(self, key: K) -> V:
174 index = self._binary_search_key(key)
175 if index >= 0:
176 return self.__data__data[2 * index + 1] # type: ignore
177 raise KeyError(key)
178
179 def __iter__(self) -> Iterator[K]:
180 return (self.__data__data[i] for i in range(0, len(self.__data__data), 2)) # type: ignore
181
182 def __len__(self) -> int:
183 return len(self.__data__data) // 2
184
185 def _binary_search_key(self, key: K) -> int:
186 return _binary_search(self.__keys__keys, key)
187
188 def plus(self, key: K, value: V) -> "ArrayMap[K, V]":
189 index = self._binary_search_key(key)
190 if index >= 0:
191 raise KeyError
192 insert_at = -(index + 1)
193 new_data = self.__data__data[:]
194 new_data.insert(insert_at * 2, key)
195 new_data.insert(insert_at * 2 + 1, value)
196 return ArrayMap.__create(new_data)
197
198 def minus_sorted_indices(self, indices: list[int]) -> "ArrayMap[K, V]":
199 new_data = self.__data__data[:]
200 adjusted_indices = [i * 2 for i in indices] + [i * 2 + 1 for i in indices]
201 adjusted_indices.sort(reverse=True)
202 for index in adjusted_indices:
203 del new_data[index]
204 return ArrayMap.__create(new_data)
205
206 def plus_or_noop(self, key: K, value: V) -> "ArrayMap[K, V]":
207 index = self._binary_search_key(key)
208 if index >= 0:
209 return self
210 else:
211 # Insert new key-value pair
212 insert_at = -(index + 1)
213 new_data = self.__data__data[:]
214 new_data.insert(insert_at * 2, key)
215 new_data.insert(insert_at * 2 + 1, value)
216 return ArrayMap.__create(new_data)
217
218 def plus_or_noop_or_replace(self, key: K, value: V) -> "ArrayMap[K, V]":
219 index = self._binary_search_key(key)
220 if index >= 0:
221 if (self.__data__data[2 * index + 1]) == value:
222 return self
223 # Replace existing value
224 new_data = self.__data__data[:]
225 new_data[2 * index + 1] = value # Update the value at the correct position
226 else:
227 # Insert new key-value pair
228 insert_at = -(index + 1)
229 new_data = self.__data__data[:]
230 new_data.insert(insert_at * 2, key)
231 new_data.insert(insert_at * 2 + 1, value)
232 return ArrayMap.__create(new_data)
233
234 def __str__(self):
235 return "{" + ", ".join(f"{k}: {v}" for k, v in self.items()) + "}"
236
237 def __repr__(self):
238 return self.__str__()
"ArrayMap[K, V]" plus(self, K key, V value)
Definition ArrayMap.py:188
"ArrayMap[K, V]" plus_or_noop_or_replace(self, K key, V value)
Definition ArrayMap.py:218
Iterator[K] __iter__(self)
Definition ArrayMap.py:179
"ArrayMap[K, V]" __create(cls, list[Union[K, V]] data)
Definition ArrayMap.py:155
"ArrayMap[K, V]" minus_sorted_indices(self, list[int] indices)
Definition ArrayMap.py:198
int _binary_search_key(self, K key)
Definition ArrayMap.py:185
V __getitem__(self, K key)
Definition ArrayMap.py:173
ListBackedSet[K] keys(self)
Definition ArrayMap.py:167
"ArrayMap[K, V]" empty(cls)
Definition ArrayMap.py:162
_ArrayMapEntries[K, V] items(self)
Definition ArrayMap.py:170
"ArrayMap[K, V]" plus_or_noop(self, K key, V value)
Definition ArrayMap.py:206
Union[K, list[K]] __getitem__(self, Union[int, slice] index)
Definition ArrayMap.py:83
"ArraySet[K]" __create(cls, list[K] data)
Definition ArrayMap.py:66
"ArraySet[K]" plusOrThis(self, K element)
Definition ArrayMap.py:86
Iterator[K] __iter__(self)
Definition ArrayMap.py:71
"ArraySet[K]" empty(cls)
Definition ArrayMap.py:75
int _binary_search(self, Any item)
Definition ArrayMap.py:55
Union[T, list[T]] __getitem__(self, Union[int, slice] index)
Definition ArrayMap.py:47
bool __contains__(self, Any item)
Definition ArrayMap.py:52
__init__(self, list[Union[K, V]] data)
Definition ArrayMap.py:122
Iterator[tuple[K, V]] __iter__(self)
Definition ArrayMap.py:141
__getitem__(self, Union[int, slice] index)
Definition ArrayMap.py:128
__getitem__(self, Union[int, slice] index)
Definition ArrayMap.py:104
__init__(self, list[Union[K, V]] data)
Definition ArrayMap.py:98
int _binary_search(data, item)
Definition ArrayMap.py:23
int _compare_string_slash_first(str a, str b)
Definition ArrayMap.py:19
int _compare_normal(a, b)
Definition ArrayMap.py:10