Skip to main content

A library to handle interval sets

Project description

Interval Arithmetic Library

Tests codecov PyPI version Python Versions License: MIT Code style: black

A Python library for working with intervals and points on the real number line. Supports continuous intervals with open/closed boundaries, interval arithmetic, set operations, and collections of disjoint intervals.

Features

  • ๐ŸŽฏ Points: Immutable points with arithmetic and comparison operations
  • ๐Ÿ“ Continuous Intervals: Intervals with configurable open/closed boundaries
  • ๐Ÿ”ข Set Operations: Union, intersection, difference, and complement
  • ๐Ÿ“ฆ Disjoint Intervals: Collections of non-overlapping intervals with automatic merging
  • ๐Ÿ”’ Type Safe: Comprehensive type hints and runtime type checking
  • โœ… Well Tested: 95%+ test coverage with 70+ test cases
  • ๐Ÿ Pythonic: Full support for in, hash(), iteration, and comparison operators

Installation

pip install interval-arithmetic  # Coming soon to PyPI

Or install from source:

git clone https://github.com/yourusername/interval-arithmetic.git
cd interval-arithmetic
pip install -e .

Quick Start

Working with Points

from src.intervals import Point

# Create points
p1 = Point(5)
p2 = Point(3)

# Arithmetic operations
p3 = p1 + p2  # Point(8)
p4 = p1 - p2  # Point(2)

# Comparisons
p1 > p2  # True
p1 == Point(5)  # True

# Points are hashable
point_set = {p1, p2, p3}
point_dict = {p1: "first", p2: "second"}

Working with Continuous Intervals

from src.intervals import ContinuousInterval, Point

# Create intervals with different boundary types
closed = ContinuousInterval(0, 10)                    # [0, 10]
open = ContinuousInterval(0, 10, True, True)          # (0, 10)
half_open = ContinuousInterval(0, 10, False, True)    # [0, 10)

# Check membership
5 in closed  # True
0 in open    # False (open boundary)
Point(5) in closed  # True

# Get interval properties
closed.length()  # 10.0
closed.is_empty()  # False

# Set operations
i1 = ContinuousInterval(0, 10)
i2 = ContinuousInterval(5, 15)

intersection = i1.intersection(i2)  # [5, 10]
union = i1.union(i2)                # [0, 15]
difference = i1.difference(i2)      # [[0, 5]]

# Using operators
i1 + i2  # [0, 15] (if overlapping/adjacent)
i1 - i2  # [[0, 5]] (set difference)

# Containment checks
i1.contains(Point(5))  # True
i1.contains(ContinuousInterval(3, 7))  # True
i1.contains_value(5)  # True

Interval Notation

The library supports mathematical interval notation:

  • [a, b] - Closed interval: includes both endpoints
  • (a, b) - Open interval: excludes both endpoints
  • [a, b) - Half-open: includes a, excludes b
  • (a, b] - Half-open: excludes a, includes b
# Creating intervals with different notations
closed = ContinuousInterval(0, 10, False, False)  # [0, 10]
open = ContinuousInterval(0, 10, True, True)      # (0, 10)
half_open = ContinuousInterval(0, 10, False, True) # [0, 10)

# String representation shows notation
print(closed)     # [0, 10]
print(open)       # (0, 10)
print(half_open)  # [0, 10)

Working with Disjoint Intervals

from src.intervals import DisjointInterval, ContinuousInterval

# Create a collection of intervals (automatically merges overlaps)
di = DisjointInterval([
    ContinuousInterval(0, 5),
    ContinuousInterval(10, 15),
    ContinuousInterval(3, 12)  # Overlaps with first two, will be merged
])

# After merging: [[0, 15], [20, 25]] becomes [[0, 15], [20, 25]]
len(di)  # 1 (merged into single interval [0, 15])

# Check membership
5 in di   # True
12 in di  # True
20 in di  # False

# Get specific interval containing a point
interval = di.get_interval_containing_point(7)  # Returns [0, 15]

# Calculate total length
di.total_length()  # 15.0

# Set operations
di1 = DisjointInterval([ContinuousInterval(0, 10), ContinuousInterval(20, 30)])
di2 = DisjointInterval([ContinuousInterval(5, 15), ContinuousInterval(25, 35)])

union = di1.union(di2)           # Merges all overlaps
intersection = di1.intersection(di2)  # [[5, 10], [25, 30]]
difference = di1.difference(di2)      # [[0, 5], [20, 25]]

# Complement
universe = ContinuousInterval(0, 40)
complement = di1.complement(universe)  # [[10, 20], [30, 40]]

# Iteration
for interval in di:
    print(interval)  # Iterate over all intervals

# Access by index
first = di[0]  # Get first interval

Common Use Cases

1. Schedule Management

from src.intervals import DisjointInterval, ContinuousInterval

# Busy times (in hours)
busy = DisjointInterval([
    ContinuousInterval(9, 10),    # Meeting 9-10am
    ContinuousInterval(11, 12),   # Meeting 11am-12pm
    ContinuousInterval(14, 16)    # Meeting 2-4pm
])

# Work day
work_day = ContinuousInterval(9, 17)  # 9am-5pm

# Available times
available = busy.complement(work_day)
# Result: [[10, 11], [12, 14], [16, 17]]

2. Range Validation

from src.intervals import ContinuousInterval

# Valid temperature range
valid_temp = ContinuousInterval(18, 25)  # 18-25ยฐC

# Check if temperature is in valid range
current_temp = 22
is_valid = current_temp in valid_temp  # True

3. Data Range Analysis

from src.intervals import DisjointInterval, ContinuousInterval

# Data ranges with gaps
data_ranges = DisjointInterval([
    ContinuousInterval(0, 100),
    ContinuousInterval(150, 250),
    ContinuousInterval(300, 400)
])

# Find gaps
universe = ContinuousInterval(0, 400)
gaps = data_ranges.complement(universe)
# Result: [[100, 150], [250, 300]]

# Total data coverage
coverage = data_ranges.total_length()  # 250
coverage_percent = (coverage / universe.length()) * 100  # 62.5%

4. Numerical Ranges

from src.intervals import ContinuousInterval

# Mathematical operations with ranges
x_range = ContinuousInterval(-10, 10)
y_range = ContinuousInterval(0, 20)

# Check if ranges overlap
if x_range.is_overlapping(y_range):
    overlap = x_range.intersection(y_range)
    print(f"Overlap: {overlap}")  # [0, 10]

API Reference

Point

Constructor

  • Point(value: float) - Create a point with the given value

Methods

  • Arithmetic: +, -
  • Comparison: ==, !=, <, <=, >, >=
  • hash() - Points are hashable

ContinuousInterval

Constructor

  • ContinuousInterval(start, end, is_start_open=False, is_end_open=False)

Class Methods

  • ContinuousInterval.empty() - Create an empty interval

Properties

  • start - Start value
  • end - End value
  • is_start_open - Whether start boundary is open
  • is_end_open - Whether end boundary is open

Methods

  • length() - Get interval length
  • is_empty() - Check if interval is empty
  • contains(item) - Check if contains a Point or ContinuousInterval
  • contains_value(value) - Check if contains a numeric value
  • intersection(interval) - Compute intersection
  • union(interval) - Compute union
  • difference(interval) - Compute set difference
  • overlaps(interval) - Check if intervals overlap
  • is_overlapping(interval) - Comprehensive overlap check

Operators

  • in - Membership testing: 5 in interval
  • + - Add/merge intervals (if adjacent or overlapping)
  • - - Set difference
  • <, <=, >, >= - Interval ordering
  • ==, != - Equality comparison
  • hash() - Intervals are hashable

DisjointInterval

Constructor

  • DisjointInterval(intervals: List[ContinuousInterval]) - Automatically merges overlaps

Properties

  • intervals - Get list of non-overlapping intervals (copy)

Methods

  • add_interval(interval) - Add interval (returns new DisjointInterval)
  • get_interval_containing_point(point) - Find containing interval
  • total_length() - Sum of all interval lengths
  • union(other) - Union with another DisjointInterval
  • intersection(other) - Intersection with another DisjointInterval
  • difference(other) - Set difference
  • complement(universe) - Complement within a universe interval

Collection Interface

  • len(di) - Number of intervals
  • di[i] - Access interval by index
  • for interval in di: - Iterate over intervals
  • item in di - Check membership (Point, float, or ContinuousInterval)
  • bool(di) - Check if non-empty

EmptySet

Represents an empty set. Returned by operations that produce no intervals.

Methods

  • == - Compare with other EmptySet or empty ContinuousInterval

Error Handling

The library provides clear error messages and custom exceptions:

from src.errors import InvalidIntervalError, IntervalError
from src.intervals import ContinuousInterval, Point

# Invalid interval (start > end)
try:
    interval = ContinuousInterval(10, 5)
except InvalidIntervalError as e:
    print(e)  # "Invalid interval: start (10) must be less than or equal to end (5)"

# Type errors
try:
    p = Point(5)
    result = p + "string"
except TypeError as e:
    print(e)  # "Unsupported operand type(s) for +: 'Point' and 'str'"

Design Principles

  1. Immutability: All objects are immutable; operations return new instances
  2. Type Safety: Strong type checking with helpful error messages
  3. Pythonic: Follows Python conventions (operators, protocols, naming)
  4. Explicit: Clear distinction between open and closed boundaries
  5. Composable: Operations can be chained naturally

Requirements

  • Python 3.8+
  • No external dependencies for core functionality
  • pytest for running tests

Development

Running Tests

# Install development dependencies
pip install pytest pytest-cov

# Run all tests
pytest

# Run with coverage
pytest --cov=src --cov-report=html

# Run specific test file
pytest tests/test_continuous_interval.py

Project Structure

interval-arithmetic/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ __init__.py         # Package exports
โ”‚   โ”œโ”€โ”€ intervals.py        # Core classes
โ”‚   โ”œโ”€โ”€ errors.py          # Exception classes
โ”‚   โ””โ”€โ”€ utils.py           # Utility functions
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ conftest.py        # Test fixtures
โ”‚   โ”œโ”€โ”€ test_point.py
โ”‚   โ”œโ”€โ”€ test_continuous_interval.py
โ”‚   โ”œโ”€โ”€ test_disjoint_interval.py
โ”‚   โ””โ”€โ”€ test_utils.py
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ pyproject.toml

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

License

MIT License - see LICENSE file for details

Changelog

Version 0.1.0 (Current)

  • Initial release
  • Point arithmetic and comparison
  • ContinuousInterval with open/closed boundaries
  • Set operations (union, intersection, difference)
  • DisjointInterval with automatic merging
  • Comprehensive test suite (95%+ coverage)

Credits

Developed with focus on mathematical correctness and practical usability.

Support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

interval_sets-0.1.0.tar.gz (24.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

interval_sets-0.1.0-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

File details

Details for the file interval_sets-0.1.0.tar.gz.

File metadata

  • Download URL: interval_sets-0.1.0.tar.gz
  • Upload date:
  • Size: 24.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.2

File hashes

Hashes for interval_sets-0.1.0.tar.gz
Algorithm Hash digest
SHA256 903fee10baf9a5c33b67d8623b30408e79f2b0917d83e48aed1d1bd5664782e0
MD5 9efd12d237367020f384b07f7ed3c8a0
BLAKE2b-256 f1694aacf963b2cc07c269df1f5f1246be5aebf5dbf80162d785470739918c07

See more details on using hashes here.

File details

Details for the file interval_sets-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for interval_sets-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9569ed627907810b364a81b443b46477f805dc6a51399313f0f10686ab0e1dff
MD5 398cabd93f91cad515a023fd647b4d12
BLAKE2b-256 401517191304fd1ab13acd7988d5ca247947a14c7ee9723573b8ae205f85fde5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page