From 2df97ce7bf61856449e6552e796c1c4598137104 Mon Sep 17 00:00:00 2001
From: Ben Thorner <ben.thorner@krakentechnologies.ltd>
Date: Thu, 7 Dec 2023 15:25:18 +0000
Subject: [PATCH] Add HalfFiniteRangeSet (copy of FiniteRangeSet)

---
 CHANGELOG.md    |  4 ++++
 pyproject.toml  |  2 +-
 xocto/ranges.py | 21 +++++++++++++++++++++
 3 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3fdfc0e..0d6ad80 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Changelog
 
+## v4.9.2 - 2023-12-07
+
+- Add HalfFiniteRangeSet class to better support working with sets of such ranges
+
 ## v4.9.1 - 2023-11-20
 
 - Include py.typed files in package data to restore correct type checking [#127](https://github.com/octoenergy/xocto/pull/127)
diff --git a/pyproject.toml b/pyproject.toml
index 16370c3..05fa97e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "xocto"
-version = "4.9.1"
+version = "4.9.2"
 requires-python = ">=3.9"
 description = "Kraken Technologies Python service utilities"
 readme = "README.md"
diff --git a/xocto/ranges.py b/xocto/ranges.py
index 3cc0c35..c150261 100644
--- a/xocto/ranges.py
+++ b/xocto/ranges.py
@@ -726,6 +726,27 @@ def __sub__(self, other: RangeSet[T]) -> RangeSet[T]:
         return self.difference(other)
 
 
+class HalfFiniteRangeSet(RangeSet[T], Generic[T]):
+    """
+    This subclass is useful when dealing with half-finite intervals as we can offer stronger guarantees
+    than we can get with normal RangeSets - mostly around intersections being half-finite etc.
+    """
+
+    _ranges: Sequence[HalfFiniteRange[T]]
+
+    def __iter__(self) -> Iterator[HalfFiniteRange[T]]:
+        yield from self._ranges
+
+    def intersection(self, other: Range[T] | RangeSet[T]) -> "HalfFiniteRangeSet[T]":
+        return cast("HalfFiniteRangeSet[T]", super().intersection(other))
+
+    def pop(self) -> HalfFiniteRange[T]:
+        return cast(HalfFiniteRange[T], super().pop())
+
+    def __and__(self, other: RangeSet[T]) -> "HalfFiniteRangeSet[T]":
+        return self.intersection(other)
+
+
 class FiniteRangeSet(RangeSet[T]):
     """
     This subclass is useful when dealing with finite intervals as we can offer stronger guarantees