Self-normalizing interval data structure that automatically maintains valid form with useful utility methods.
Project description
canonical-interval
Self-normalizing interval data structure that automatically maintains valid form with useful utility methods.
The CanonicalInterval class represents left-closed, right-open intervals ([start, stop)) that, given a comparator (default: operator.lt), automatically enforce the following standardized valid form:
- Guarantees
start <= stopaccording to the comparator- Empty intervals become
[start, start)
- Empty intervals become
- All operations maintain canonical form
Key Features:
- Automatic Canonicalization
- Custom Comparison: Supports any comparator function
- Useful Utility Methods:
__bool__: Determines if the current interval is empty.relative_position_of(item): Determines ifitemis before/within/after the current interval.three_way_split(target): Split a target interval into three parts:- Parts of
targetbefore current interval - Overlapping parts between
targetand current interval - Parts of
targetafter current interval
- Parts of
- Supports Python 2+
Installation
pip install canonical-interval
Examples
from canonical_interval import CanonicalInterval
# Automatically becomes [20, 20) (empty interval)
assert CanonicalInterval(20, 10).start == 20
assert CanonicalInterval(20, 10).stop == 20
assert not CanonicalInterval(20, 10)
assert 15 not in CanonicalInterval(20, 10)
# Automatically becomes ['z', 'z') (empty interval)
assert CanonicalInterval('z', 'a').start == 'z'
assert CanonicalInterval('z', 'a').stop == 'z'
assert not CanonicalInterval('z', 'a')
assert 'm' not in CanonicalInterval('z', 'a')
# Respects custom comparator
# [20, 10) (non-empty interval)
assert CanonicalInterval(20, 10, comparator=lambda a, b: a > b).start == 20
assert CanonicalInterval(20, 10, comparator=lambda a, b: a > b).stop == 10
assert CanonicalInterval(20, 10, comparator=lambda a, b: a > b)
assert 15 in CanonicalInterval(20, 10, comparator=lambda a, b: a > b)
# Respects custom comparator
# ['z', 'a') (non-empty interval)
assert CanonicalInterval('z', 'a', comparator=lambda a, b: a > b).start == 'z'
assert CanonicalInterval('z', 'a', comparator=lambda a, b: a > b).stop == 'a'
assert CanonicalInterval('z', 'a', comparator=lambda a, b: a > b)
assert 'm' in CanonicalInterval('z', 'a', comparator=lambda a, b: a > b)
# Various three-way splits
## Non-empty current
non_empty_current = CanonicalInterval(0, 2)
### Non-empty targets
assert non_empty_current.three_way_split(CanonicalInterval(-3, -1)) == (
CanonicalInterval(-3, -1), CanonicalInterval(0, 0), CanonicalInterval(2, 2)
)
assert non_empty_current.three_way_split(CanonicalInterval(-2, 0)) == (
CanonicalInterval(-2, 0), CanonicalInterval(0, 0), CanonicalInterval(2, 2)
)
assert non_empty_current.three_way_split(CanonicalInterval(-1, 1)) == (
CanonicalInterval(-1, 0), CanonicalInterval(0, 1), CanonicalInterval(2, 2)
)
assert non_empty_current.three_way_split(CanonicalInterval(0, 2)) == (
CanonicalInterval(0, 0), CanonicalInterval(0, 2), CanonicalInterval(2, 2)
)
assert non_empty_current.three_way_split(CanonicalInterval(0.5, 1.5)) == (
CanonicalInterval(0, 0), CanonicalInterval(0.5, 1.5), CanonicalInterval(2, 2)
)
assert non_empty_current.three_way_split(CanonicalInterval(1, 3)) == (
CanonicalInterval(0, 0), CanonicalInterval(1, 2), CanonicalInterval(2, 3)
)
assert non_empty_current.three_way_split(CanonicalInterval(2, 4)) == (
CanonicalInterval(0, 0), CanonicalInterval(2, 2), CanonicalInterval(2, 4)
)
assert non_empty_current.three_way_split(CanonicalInterval(3, 5)) == (
CanonicalInterval(0, 0), CanonicalInterval(2, 2), CanonicalInterval(3, 5)
)
### Empty targets
assert non_empty_current.three_way_split(CanonicalInterval(-1, -1)) == (
CanonicalInterval(-1, -1), CanonicalInterval(0, 0), CanonicalInterval(2, 2)
)
assert non_empty_current.three_way_split(CanonicalInterval(0, 0)) == (
CanonicalInterval(0, 0), CanonicalInterval(0, 0), CanonicalInterval(2, 2)
)
assert non_empty_current.three_way_split(CanonicalInterval(1, 1)) == (
CanonicalInterval(0, 0), CanonicalInterval(1, 1), CanonicalInterval(2, 2)
)
assert non_empty_current.three_way_split(CanonicalInterval(2, 2)) == (
CanonicalInterval(0, 0), CanonicalInterval(2, 2), CanonicalInterval(2, 2)
)
assert non_empty_current.three_way_split(CanonicalInterval(3, 3)) == (
CanonicalInterval(0, 0), CanonicalInterval(2, 2), CanonicalInterval(3, 3)
)
## Empty current
empty_current = CanonicalInterval(0, 0)
### Non-empty targets
assert empty_current.three_way_split(CanonicalInterval(-3, -1)) == (
CanonicalInterval(-3, -1), CanonicalInterval(0, 0), CanonicalInterval(0, 0)
)
assert empty_current.three_way_split(CanonicalInterval(-2, 0)) == (
CanonicalInterval(-2, 0), CanonicalInterval(0, 0), CanonicalInterval(0, 0)
)
assert empty_current.three_way_split(CanonicalInterval(-1, 1)) == (
CanonicalInterval(-1, 0), CanonicalInterval(0, 0), CanonicalInterval(0, 1)
)
assert empty_current.three_way_split(CanonicalInterval(0, 2)) == (
CanonicalInterval(0, 0), CanonicalInterval(0, 0), CanonicalInterval(0, 2)
)
assert empty_current.three_way_split(CanonicalInterval(1, 3)) == (
CanonicalInterval(0, 0), CanonicalInterval(0, 0), CanonicalInterval(1, 3)
)
### Empty targets
assert empty_current.three_way_split(CanonicalInterval(-1, -1)) == (
CanonicalInterval(-1, -1), CanonicalInterval(0, 0), CanonicalInterval(0, 0)
)
assert empty_current.three_way_split(CanonicalInterval(0, 0)) == (
CanonicalInterval(0, 0), CanonicalInterval(0, 0), CanonicalInterval(0, 0)
)
assert empty_current.three_way_split(CanonicalInterval(1, 1)) == (
CanonicalInterval(0, 0), CanonicalInterval(0, 0), CanonicalInterval(1, 1)
)
Contributing
Contributions are welcome! Please submit pull requests or open issues on the GitHub repository.
License
This project is licensed under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file canonical_interval-0.1.0a2.tar.gz.
File metadata
- Download URL: canonical_interval-0.1.0a2.tar.gz
- Upload date:
- Size: 4.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f59c044c276fc6d076f175d9e32b4b731d44925bf95c5a0b8ed49685e0d1b11
|
|
| MD5 |
528af3f873a00c2a047b454cd720999b
|
|
| BLAKE2b-256 |
c63e7efb4a10863eb11abf59cf056c9520a023ee916ed35766ebff76a7dbd16e
|
File details
Details for the file canonical_interval-0.1.0a2-py2.py3-none-any.whl.
File metadata
- Download URL: canonical_interval-0.1.0a2-py2.py3-none-any.whl
- Upload date:
- Size: 5.0 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ca8eee4ac9b5b1f352a7c03e91ebd273df01f30741dd4f9162e05f2f66766e4
|
|
| MD5 |
7a8429a3938d57274dd7de7088acfe1c
|
|
| BLAKE2b-256 |
1c8b8bb40d329aa08bc98befa1d0a11e0bd6096da77fcc0dfbc92bfb42dfe56c
|