Skip to content

Commit 7805763

Browse files
authored
Merge pull request #1 from eightlay/develop
Develop
2 parents 0c4119d + c3c480d commit 7805763

File tree

6 files changed

+132
-3
lines changed

6 files changed

+132
-3
lines changed

ijim/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .interval_map import IntervalMap

ijim/comparable.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from abc import ABCMeta, abstractmethod
2+
from typing import Any
3+
4+
5+
class Comparable(metaclass=ABCMeta):
6+
"""Supports `==`, `<`, `<= operators"""
7+
@abstractmethod
8+
def __eq__(self, other: Any) -> bool: ...
9+
@abstractmethod
10+
def __lt__(self, other: Any) -> bool: ...
11+
@abstractmethod
12+
def __le__(self, other: Any) -> bool: ...

ijim/exceptions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
IntervalMapUnequalLength = ValueError(
2+
"len of the `intervals_left_points` must be equal to len of `vals`")
3+
IntervalMapMustBeSorted = ValueError(
4+
"`intervals_left_points` must be sorted in ascending order")
5+
IntervalMapNoDuplicates = ValueError(
6+
"`intervals_left_points` must not contain duplicates")

ijim/interval_map.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import copy
2+
import bisect
3+
from typing import Iterable, Generic, TypeVar
4+
5+
from .comparable import Comparable
6+
from .utils import (
7+
is_sorted,
8+
has_duplicates,
9+
)
10+
from .exceptions import (
11+
IntervalMapUnequalLength,
12+
IntervalMapMustBeSorted,
13+
IntervalMapNoDuplicates,
14+
)
15+
16+
17+
ComparableKey = TypeVar("ComparableKey", bound=Comparable)
18+
AnyValueType = TypeVar("AnyValueType")
19+
20+
21+
class IntervalMap(Generic[ComparableKey, AnyValueType]):
22+
def __init__(
23+
self,
24+
default_val: AnyValueType,
25+
interval_left_points: Iterable[ComparableKey] = [],
26+
vals: Iterable[AnyValueType] = [],
27+
) -> None:
28+
if has_duplicates(interval_left_points):
29+
raise IntervalMapNoDuplicates
30+
if not is_sorted(interval_left_points):
31+
raise IntervalMapMustBeSorted
32+
if len(interval_left_points) != len(vals):
33+
raise IntervalMapUnequalLength
34+
35+
self._lpoints = list(copy.deepcopy(interval_left_points))
36+
self._vals = [copy.deepcopy(default_val)] + list(copy.deepcopy(vals))
37+
38+
def __getitem__(self, key: ComparableKey) -> AnyValueType:
39+
return self.get(key)
40+
41+
def get(self, key: ComparableKey) -> AnyValueType:
42+
return self._vals[bisect.bisect(self._lpoints, key)]
43+
44+
def __setitem__(self, key: ComparableKey, val: AnyValueType) -> None:
45+
self.set(key, val)
46+
47+
def set(self, key: ComparableKey, val: AnyValueType) -> None:
48+
ind = bisect.bisect(self._lpoints, key)
49+
50+
if ind == len(self._lpoints):
51+
if self._vals[ind] != val:
52+
self._lpoints.append(key)
53+
self._vals.append(val)
54+
return
55+
56+
if self._lpoints[ind] == key:
57+
self._vals[ind + 1] = val
58+
elif self._vals[ind] != val:
59+
self._lpoints.insert(ind, key)
60+
self._vals.insert(ind + 1, val)
61+
62+
def __delitem__(self, key: ComparableKey) -> None:
63+
self.unset(key)
64+
65+
def unset(self, key: ComparableKey) -> bool:
66+
ind = bisect.bisect_left(self._lpoints, key)
67+
68+
if ind >= len(self._lpoints):
69+
return False
70+
elif self._lpoints[ind] == key:
71+
del self._lpoints[ind]
72+
del self._vals[ind + 1]
73+
return True
74+
return False
75+
76+
def slice_add(
77+
self,
78+
start: ComparableKey,
79+
end: ComparableKey,
80+
summand: AnyValueType,
81+
) -> None:
82+
if start > end:
83+
return
84+
85+
end_ind = bisect.bisect_left(self._lpoints, end)
86+
val = self._vals[end_ind]
87+
88+
start_ind = bisect.bisect(self._lpoints, start)
89+
self.set(start, self._vals[start_ind] + summand)
90+
end_ind = bisect.bisect_left(self._lpoints, end)
91+
92+
for ind in range(start_ind + 2, end_ind + 1):
93+
self._vals[ind] += summand
94+
95+
self.set(end, val)

ijim/utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from typing import Iterable
2+
3+
from .comparable import Comparable
4+
5+
6+
def is_sorted(iterable: Iterable[Comparable]) -> bool:
7+
for i in range(len(iterable) - 1):
8+
if not iterable[i] <= iterable[i + 1]:
9+
return False
10+
return True
11+
12+
13+
def has_duplicates(iterable: Iterable) -> bool:
14+
return len(iterable) != len(set(iterable))

setup.cfg

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
[metadata]
2-
name = ij-im
3-
version = 0.0.0
2+
name = ijim
3+
version = 0.0.1
44
description = interval map
55
long_description = file: README.md
6-
url = https://github.com/eightlay/ij-im
6+
url = https://github.com/eightlay/IntervalMap
7+
author_email = [email protected]
78

89
[options]
910
packages = find:

0 commit comments

Comments
 (0)