This repository was archived by the owner on Apr 4, 2024. It is now read-only.
forked from diffplug/selfie
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathArrayMap.py
146 lines (115 loc) · 4.53 KB
/
ArrayMap.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
from collections.abc import Set, Iterator, Mapping
from typing import List, TypeVar, Union, Any, Callable, Optional, Generator
from abc import abstractmethod, ABC
T = TypeVar("T")
V = TypeVar("V")
K = TypeVar("K")
class BinarySearchUtil:
@staticmethod
def binary_search(
data, item, compare_func: Optional[Callable[[Any, Any], int]] = None
) -> int:
low, high = 0, len(data) - 1
while low <= high:
mid = (low + high) // 2
mid_val = data[mid] if not isinstance(data, ListBackedSet) else data[mid]
comparison = (
compare_func(mid_val, item)
if compare_func
else (mid_val > item) - (mid_val < item)
)
if comparison < 0:
low = mid + 1
elif comparison > 0:
high = mid - 1
else:
return mid # item found
return -(low + 1) # item not found
@staticmethod
def default_compare(a: Any, b: Any) -> int:
"""Default comparison function for binary search, with special handling for strings."""
if isinstance(a, str) and isinstance(b, str):
a, b = a.replace("/", "\0"), b.replace("/", "\0")
return (a > b) - (a < b)
class ListBackedSet(Set[T], ABC):
@abstractmethod
def __len__(self) -> int: ...
@abstractmethod
def __getitem__(self, index: Union[int, slice]) -> Union[T, List[T]]: ...
@abstractmethod
def __iter__(self) -> Iterator[T]: ...
def __contains__(self, item: Any) -> bool:
return self._binary_search(item) >= 0
def _binary_search(self, item: Any) -> int:
return BinarySearchUtil.binary_search(self, item)
class ArraySet(ListBackedSet[K]):
__data: List[K]
def __init__(self):
raise NotImplementedError("Use ArraySet.empty() or other class methods instead")
@classmethod
def __create(cls, data: List[K]) -> "ArraySet[K]":
instance = super().__new__(cls)
instance.__data = data
return instance
def __iter__(self) -> Iterator[K]:
return iter(self.__data)
@classmethod
def empty(cls) -> "ArraySet[K]":
if not hasattr(cls, "__EMPTY"):
cls.__EMPTY = cls.__create([])
return cls.__EMPTY
def __len__(self) -> int:
return len(self.__data)
def __getitem__(self, index: Union[int, slice]) -> Union[K, List[K]]:
return self.__data[index]
def plusOrThis(self, element: K) -> "ArraySet[K]":
index = self._binary_search(element)
if index >= 0:
return self
else:
insert_at = -(index + 1)
new_data = self.__data[:]
new_data.insert(insert_at, element)
return ArraySet.__create(new_data)
class ArrayMap(Mapping[K, V]):
__data: List[Union[K, V]]
def __init__(self):
raise NotImplementedError("Use ArrayMap.empty() or other class methods instead")
@classmethod
def __create(cls, data: List[Union[K, V]]) -> "ArrayMap[K, V]":
instance = cls.__new__(cls)
instance.__data = data
return instance
@classmethod
def empty(cls) -> "ArrayMap[K, V]":
if not hasattr(cls, "__EMPTY"):
cls.__EMPTY = cls.__create([])
return cls.__EMPTY
def __getitem__(self, key: K) -> V:
index = self._binary_search_key(key)
if index >= 0:
return self.__data[2 * index + 1] # type: ignore
raise KeyError(key)
def __iter__(self) -> Iterator[K]:
return (self.__data[i] for i in range(0, len(self.__data), 2)) # type: ignore
def __len__(self) -> int:
return len(self.__data) // 2
def _binary_search_key(self, key: K) -> int:
keys = [self.__data[i] for i in range(0, len(self.__data), 2)]
return BinarySearchUtil.binary_search(keys, key)
def plus(self, key: K, value: V) -> "ArrayMap[K, V]":
index = self._binary_search_key(key)
if index >= 0:
raise ValueError("Key already exists")
insert_at = -(index + 1)
new_data = self.__data[:]
new_data.insert(insert_at * 2, key)
new_data.insert(insert_at * 2 + 1, value)
return ArrayMap.__create(new_data)
def minus_sorted_indices(self, indices: List[int]) -> "ArrayMap[K, V]":
new_data = self.__data[:]
adjusted_indices = [i * 2 for i in indices] + [i * 2 + 1 for i in indices]
adjusted_indices.sort(reverse=True)
for index in adjusted_indices:
del new_data[index]
return ArrayMap.__create(new_data)