# IntSet

> An immutable set of non-negative integers backed by a Roaring bitmap.

Source: https://docs.pyrula.io/primitives/int-set/

`IntSet` is an immutable set of non-negative integers backed by a [Roaring bitmap](https://roaringbitmap.org/). Union, intersection and difference run in Rust over compressed bitmaps with no per-element Python work. Use it for large sets of integer IDs where an `ISet` of boxed ints is the bottleneck.

```python
from pyrula import IntSet

s = IntSet([1, 2, 3, 3, 2])   # duplicates dropped
len(s)                         # 3
2 in s                         # True
s.add(4).to_list()             # [1, 2, 3, 4]; s is unchanged
IntSet().is_empty()            # True
```

Values must fit in an unsigned 64-bit integer; negatives and out-of-range values raise `ValueError`, non-integers `TypeError`. Membership and comparison accept any number equal to a member, as `frozenset` does (`1.0`, a NumPy integer). `contains` raises `TypeError` for a value that equals no integer, where `in` answers `False`.

## Set algebra

```python
a = IntSet([1, 2, 3, 4])
b = IntSet([3, 4, 5])

a.union(b)                  # IntSet([1, 2, 3, 4, 5])
a.intersection(b)           # IntSet([3, 4])
a.difference(b)             # IntSet([1, 2])
a.symmetric_difference(b)   # IntSet([1, 2, 5])

IntSet([3, 4]).is_subset(a)     # True
IntSet([7, 8]).is_disjoint(a)   # True
a.cardinality()                 # 4
```

`to_list` returns a `list[int]` in ascending order.

## Serialization

`to_bytes` writes the Roaring portable format, readable by other Roaring implementations. This suits durable state such as a persisted "seen" set.

```python
blob = IntSet([1, 5, 9, 2**40]).to_bytes()
IntSet.from_bytes(blob).to_list()   # [1, 5, 9, 1099511627776]
```

## Performance

`IntSet` pays off for dense or clustered integers (entity IDs, row offsets), where set algebra is far faster than a Python `set`. For integers spread uniformly across the 64-bit range the bitmap can't compress, and a plain `set` or `ISet` is faster.
