This repository has been 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
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"java.compile.nullAnalysis.mode": "automatic" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
from collections.abc import Set, Sequence, Iterator, Mapping | ||
from typing import TypeVar, List | ||
from functools import cmp_to_key | ||
|
||
T = TypeVar('T') | ||
K = TypeVar('K') | ||
V = TypeVar('V') | ||
|
||
class ListBackedSet(Set[T], Sequence[T]): | ||
def __getitem__(self, index: int) -> T: | ||
# This method should be implemented by the subclass. | ||
raise NotImplementedError | ||
|
||
def __len__(self) -> int: | ||
# This should also be implemented by the subclass to return the number of items. | ||
raise NotImplementedError | ||
|
||
def __iter__(self) -> Iterator[T]: | ||
return self.ListBackedSetIterator(self) | ||
|
||
class ListBackedSetIterator(Iterator[T]): | ||
def __init__(self, list_backed_set: 'ListBackedSet[T]'): | ||
self.list_backed_set = list_backed_set | ||
self.index = 0 | ||
|
||
def __next__(self) -> T: | ||
if self.index < len(self.list_backed_set): | ||
result = self.list_backed_set[self.index] | ||
self.index += 1 | ||
return result | ||
else: | ||
raise StopIteration | ||
|
||
def __contains__(self, item: object) -> bool: | ||
# Efficient implementation of __contains__ should be provided by subclass if needed. | ||
for i in self: | ||
if i == item: | ||
return True | ||
return False | ||
|
||
class ArraySet(ListBackedSet[K]): | ||
def __init__(self, data: list): | ||
self.data = data | ||
self.sort_data() | ||
|
||
def sort_data(self): | ||
if self.data and isinstance(self.data[0], str): | ||
self.data.sort(key=cmp_to_key(self.string_slash_first_comparator)) | ||
else: | ||
self.data.sort() | ||
|
||
def string_slash_first_comparator(self, a, b): | ||
# Define sorting where '/' is considered the lowest key | ||
if a == '/': | ||
return -1 | ||
elif b == '/': | ||
return 1 | ||
else: | ||
return (a > b) - (a < b) | ||
|
||
def __len__(self): | ||
return len(self.data) | ||
|
||
def __getitem__(self, index: int) -> K: | ||
return self.data[index] | ||
|
||
def __contains__(self, item: K) -> bool: | ||
# Implementing binary search for efficiency | ||
left, right = 0, len(self.data) - 1 | ||
while left <= right: | ||
mid = (left + right) // 2 | ||
if self.data[mid] == item: | ||
return True | ||
elif self.data[mid] < item: | ||
left = mid + 1 | ||
else: | ||
right = mid - 1 | ||
return False | ||
|
||
def plus_or_this(self, key: K) -> 'ArraySet[K]': | ||
# Binary search to find the appropriate index or confirm existence | ||
left, right = 0, len(self.data) - 1 | ||
while left <= right: | ||
mid = (left + right) // 2 | ||
if self.data[mid] == key: | ||
return self # Key already exists | ||
elif self.data[mid] < key: | ||
left = mid + 1 | ||
else: | ||
right = mid - 1 | ||
|
||
# Key does not exist, insert in the sorted position | ||
new_data = self.data[:left] + [key] + self.data[left:] | ||
return ArraySet(new_data) | ||
|
||
class ArrayMap(Mapping[K, V]): | ||
def __init__(self, data: list): | ||
self.data = data | ||
|
||
@classmethod | ||
def empty(cls): | ||
return cls([]) | ||
|
||
def __getitem__(self, key: K) -> V: | ||
index = self._binary_search_key(key) | ||
if index >= 0: | ||
return self.data[2 * index + 1] | ||
raise KeyError(key) | ||
|
||
def __iter__(self): | ||
return (self.data[i] for i in range(0, len(self.data), 2)) | ||
|
||
def __len__(self) -> int: | ||
return len(self.data) // 2 | ||
|
||
def _binary_search_key(self, key: K) -> int: | ||
low, high = 0, len(self.data) // 2 - 1 | ||
while low <= high: | ||
mid = (low + high) // 2 | ||
mid_key = self.data[2 * mid] | ||
if mid_key < key: | ||
low = mid + 1 | ||
elif mid_key > key: | ||
high = mid - 1 | ||
else: | ||
return mid | ||
return -(low + 1) | ||
|
||
def plus(self, key: K, value: V) -> 'ArrayMap[K, V]': | ||
index = self._binary_search_key(key) | ||
if index >= 0: | ||
raise ValueError("Key already exists") | ||
else: | ||
insert_at = -(index + 1) | ||
new_data = self.data[:] | ||
new_data[insert_at * 2:insert_at * 2] = [key, value] | ||
return ArrayMap(new_data) | ||
|
||
def minus_sorted_indices(self, indicesToRemove: list[int]) -> 'ArrayMap[K, V]': | ||
if not indicesToRemove: | ||
return self | ||
newData = [] | ||
for i in range(0, len(self.data), 2): | ||
if i // 2 not in indicesToRemove: | ||
newData.extend(self.data[i:i + 2]) | ||
return ArrayMap(newData) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import unittest | ||
from selfie_lib.ArrayMap import ArrayMap | ||
|
||
class ArrayMapTest(unittest.TestCase): | ||
def assertEmpty(self, map): | ||
self.assertEqual(len(map), 0) | ||
self.assertEqual(list(map.keys()), []) | ||
self.assertEqual(list(map.values()), []) | ||
self.assertEqual(list(map.items()), []) | ||
with self.assertRaises(KeyError): | ||
_ = map["key"] | ||
self.assertEqual(map, {}) | ||
self.assertEqual(map, ArrayMap.empty()) | ||
|
||
def assertSingle(self, map, key, value): | ||
self.assertEqual(len(map), 1) | ||
self.assertEqual(set(map.keys()), {key}) | ||
self.assertEqual(list(map.values()), [value]) | ||
self.assertEqual(set(map.items()), {(key, value)}) | ||
self.assertEqual(map[key], value) | ||
with self.assertRaises(KeyError): | ||
_ = map[key + "blah"] | ||
self.assertEqual(map, {key: value}) | ||
self.assertEqual(map, ArrayMap.empty().plus(key, value)) | ||
|
||
def assertDouble(self, map, key1, value1, key2, value2): | ||
self.assertEqual(len(map), 2) | ||
self.assertEqual(set(map.keys()), {key1, key2}) | ||
self.assertEqual(list(map.values()), [value1, value2]) | ||
self.assertEqual(set(map.items()), {(key1, value1), (key2, value2)}) | ||
self.assertEqual(map[key1], value1) | ||
self.assertEqual(map[key2], value2) | ||
with self.assertRaises(KeyError): | ||
_ = map[key1 + "blah"] | ||
self.assertEqual(map, {key1: value1, key2: value2}) | ||
self.assertEqual(map, {key2: value2, key1: value1}) | ||
self.assertEqual(map, ArrayMap.empty().plus(key1, value1).plus(key2, value2)) | ||
self.assertEqual(map, ArrayMap.empty().plus(key2, value2).plus(key1, value1)) | ||
|
||
def assertTriple(self, map, key1, value1, key2, value2, key3, value3): | ||
self.assertEqual(len(map), 3) | ||
self.assertEqual(set(map.keys()), {key1, key2, key3}) | ||
self.assertEqual(list(map.values()), [value1, value2, value3]) | ||
self.assertEqual(set(map.items()), {(key1, value1), (key2, value2), (key3, value3)}) | ||
self.assertEqual(map[key1], value1) | ||
self.assertEqual(map[key2], value2) | ||
self.assertEqual(map[key3], value3) | ||
with self.assertRaises(KeyError): | ||
_ = map[key1 + "blah"] | ||
self.assertEqual(map, {key1: value1, key2: value2, key3: value3}) | ||
self.assertEqual(map, ArrayMap.empty().plus(key1, value1).plus(key2, value2).plus(key3, value3)) | ||
|
||
def test_empty(self): | ||
self.assertEmpty(ArrayMap.empty()) | ||
|
||
def test_single(self): | ||
empty = ArrayMap.empty() | ||
single = empty.plus("one", "1") | ||
self.assertEmpty(empty) | ||
self.assertSingle(single, "one", "1") | ||
|
||
def test_double(self): | ||
empty = ArrayMap.empty() | ||
single = empty.plus("one", "1") | ||
double = single.plus("two", "2") | ||
self.assertEmpty(empty) | ||
self.assertSingle(single, "one", "1") | ||
self.assertDouble(double, "one", "1", "two", "2") | ||
self.assertDouble(single.plus("a", "sorted"), "a", "sorted", "one", "1") | ||
|
||
with self.assertRaises(ValueError) as context: | ||
single.plus("one", "2") | ||
self.assertEqual(str(context.exception), "Key already exists") | ||
|
||
def test_triple(self): | ||
triple = ArrayMap.empty().plus("1", "one").plus("2", "two").plus("3", "three") | ||
self.assertTriple(triple, "1", "one", "2", "two", "3", "three") | ||
|
||
def test_multi(self): | ||
self.test_triple() | ||
triple = ArrayMap.empty().plus("2", "two").plus("3", "three").plus("1", "one") | ||
self.assertTriple(triple, "1", "one", "2", "two", "3", "three") | ||
triple = ArrayMap.empty().plus("3", "three").plus("1", "one").plus("2", "two") | ||
self.assertTriple(triple, "1", "one", "2", "two", "3", "three") | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |