You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Reword the "uninitialized garbage" / "touches no memory" claims in SparseSet's
XML docs, docs/api/collections.md, README, ship card, and benchmark comment:
.NET arrays are zero-initialized, so the real property is tolerating *stale*
sparse entries across Clear/mutations, and Clear() resets count/version rather
than touching no memory (it just leaves the backing arrays untouched).
- Condense the CHANGELOG [Unreleased] SparseSet entries to the repo's brevity
standard (a user-facing lead bullet + one parity-rollout bullet), so the section
stays within the release-body size the release workflow extracts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+2-7Lines changed: 2 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,13 +6,8 @@ All notable changes to Celerity are documented here. This project follows [Keep
6
6
7
7
### Added
8
8
9
-
-**`SparseSet`** in `Celerity.Collections` — a set of non-negative integers over a bounded universe `[0, Universe)`, backed by the Briggs–Torczon sparse-set representation (a dense value array + a sparse index array), filling a BCL gap (.NET ships no sparse set). Its two wins over `HashSet<int>`: `Clear()` is `O(1)` and touches no memory (`HashSet` zeroes its whole table), and iteration is a dense, cache-friendly scan over exactly the present elements — the winning shape for clear-and-rebuild "visited" sets in graph traversal, ECS, and sweep-line code. `Add` / `Contains` / `Remove` are `O(1)` with no hashing. Costs `O(Universe)` memory and stores only values in `[0, Universe)` (out-of-range throws on `Add`, reads as absent on `Contains` / `Remove`). Implements `ISet<int>`. An opt-in specialized type, not a `HashSet<int>` replacement. Closes [#287](https://github.com/marius-bughiu/Celerity/issues/287).
10
-
-`SparseSetTests`, `SparseSetEnumerationTests`, and `SparseSetDifferentialTests` (`Celerity.Tests/Collections`) — dedicated coverage mirroring the `IntSet*` / `SmallSet*` files: the core surface (add / try-add / duplicate / out-of-range / contains / remove-swap / dense-array growth / the O(1) `Clear`-then-reuse path that must reject stale sparse entries), the enumeration surface (every-element-once, growth, post-remove/-clear, mutation detection, `Reset`, non-generic path), and a 6,000-step randomized differential reconciling every mutating / query / clear operation against a `HashSet<int>` oracle over a bounded non-negative universe.
11
-
- Cross-collection set-algebra rows for `SparseSet` in `SetAlgebraTests` — `ISet<int>` conformance, the basic algebra within the universe, and the bounded-universe caveat that a mutating op which must add an out-of-universe value throws `ArgumentOutOfRangeException`. (`SparseSet` carries its own `SparseSetDifferentialTests` rather than joining the shared `SetAlgebraDifferentialTests`, whose universe spans negatives it cannot store — exactly as `EnumSet` does.)
12
-
-`SparseSetBenchmark` in `Celerity.Benchmarks`, registered in `Program.cs`'s `CoreBenchmarks` array (so it joins the per-PR core run and the gh-pages dashboard, mirroring `IntSetBenchmark`) — `SparseSet` vs `HashSet<int>` across `Add` / `Contains` / **`ClearRefill`** (the O(1)-clear headline win) / `Remove` at `[Params(1000, 100_000)]` over a universe 4× the item count.
13
-
- Dashboard wiring for `SparseSet`: the "What ships in the box" ship card in [`web/index.html`](web/index.html) and the `COLLECTIONS` arrays in [`web/dev/bench/index.html`](web/dev/bench/index.html) (key / title / vs / ops incl. `ClearRefill`) and [`web/dev/bench/detail.html`](web/dev/bench/detail.html) (key / title / vs).
14
-
- A `SparseSet` differential target in the `Celerity.Fuzz` harness (`SparseSetCase`) — reconciling `TryAdd` / `Remove` / `Clear` churn and enumeration against a `HashSet<int>` oracle over the type's bounded non-negative universe (it draws from `[0, 32)` rather than the shared `[-8, 24]` key domain it cannot store).
15
-
- Documentation for `SparseSet`: a full API section in [`docs/api/collections.md`](docs/api/collections.md#sparseset) (the Briggs–Torczon mechanism, the O(1)-clear and dense-iteration wins, the `O(Universe)`-memory / non-negative-only tradeoffs, constructors, the method table, the set-algebra caveat, and a runnable BFS-visited-set example), plus README entries — the Sets list, the `ISet<T>` note, a `SparseSet` paragraph with a runnable example, and a new "cleared-and-rebuilt bounded-int set" row in the "Choosing a collection" decision table.
9
+
-**`SparseSet`** in `Celerity.Collections` — a bounded-universe `[0, Universe)` integer set (the Briggs–Torczon sparse set), filling a BCL gap. It wins over `HashSet<int>` where the set is cleared and rebuilt often: `Clear()` is `O(1)` (it leaves the backing arrays untouched, versus `HashSet` zeroing its table) and iteration is a dense scan over just the present elements — the "visited"-set shape in graph traversal, ECS, and sweep-line code. Costs `O(Universe)` memory and stores only values in `[0, Universe)`. Implements `ISet<int>`; an opt-in specialized type, not a `HashSet<int>` replacement. Closes [#287](https://github.com/marius-bughiu/Celerity/issues/287).
10
+
- Full parity rollout for `SparseSet`: dedicated tests (`SparseSetTests` / `SparseSetEnumerationTests` / `SparseSetDifferentialTests`), a `SetAlgebraTests` conformance row, a `Celerity.Fuzz``SparseSetCase`, `SparseSetBenchmark` (registered in `Program.cs`, with a `ClearRefill` category), dashboard wiring (`web/index.html`, `web/dev/bench/{index,detail}.html`), and docs (`docs/api/collections.md` + README).
Copy file name to clipboardExpand all lines: README.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -62,7 +62,7 @@ Standalone libraries built **on top of** Celerity — each solves a real problem
62
62
-`IntSet` / `LongSet` — `int` / `long`-keyed set specializations.
63
63
-`SmallSet<T>` — flat-array, linear-scan set for the very-small (`n <= ~16`) case. No hasher; the default element is stored inline. The set counterpart of `SmallDictionary`.
64
64
-`EnumSet<TEnum>` — bit-vector set for enum keys (the .NET `EnumSet`): membership is a single bit test and set algebra is word-wise bitwise ops, with no hashing or boxing. Enumerates in ascending underlying-value order.
65
-
-`SparseSet` — bounded-universe integer set (Briggs–Torczon sparse set): `O(1)``Clear` that touches no memory, plus dense, cache-friendly iteration — for clear-and-rebuild "visited" sets over ids in `[0, N)` (graph traversal, ECS, sweep-line). Costs `O(Universe)` memory.
65
+
-`SparseSet` — bounded-universe integer set (Briggs–Torczon sparse set): `O(1)``Clear` that leaves the backing arrays untouched, plus dense, cache-friendly iteration — for clear-and-rebuild "visited" sets over ids in `[0, N)` (graph traversal, ECS, sweep-line). Costs `O(Universe)` memory.
66
66
67
67
The mutable sets (`CeleritySet`, `SwissSet`, `RobinHoodSet`, `HashCachingSet`, `IntSet`, `LongSet`, `SmallSet`, `EnumSet`, `SparseSet`) all implement **`ISet<T>`** — the full `HashSet<T>` set-algebra surface (`UnionWith` / `IntersectWith` / `ExceptWith` / `SymmetricExceptWith` and the `IsSubsetOf` / `IsSupersetOf` / `Overlaps` / `SetEquals` query family, plus `CopyTo`) with BCL semantics — so they drop in wherever a `HashSet<T>` is used.
`SparseSet` is the bounded-universe integer set — the classic Briggs–Torczon sparse set (a dense value array + a sparse index array). Over a fixed universe `[0, Universe)` chosen at construction, `Add` / `Contains` / `Remove` are `O(1)` with no hashing, but the point of the type is what `HashSet<int>` can't match: `Clear()` is `O(1)` (it resets a count and touches no memory, versus zeroing the whole table) and iteration is a dense, contiguous scan over exactly the present elements. That is the winning shape for clear-and-rebuild "visited" sets — graph BFS/DFS, ECS entity membership, sweep-line — where the set is emptied every iteration. The cost is `O(Universe)` memory and non-negative-values-only: a value outside `[0, Universe)` throws on `Add` and reads as absent on `Contains` / `Remove`. It is an opt-in specialized type, not a `HashSet<int>` replacement — for an unbounded or huge-and-sparse key space, reach for `IntSet`.
290
+
`SparseSet` is the bounded-universe integer set — the classic Briggs–Torczon sparse set (a dense value array + a sparse index array). Over a fixed universe `[0, Universe)` chosen at construction, `Add` / `Contains` / `Remove` are `O(1)` with no hashing, but the point of the type is what `HashSet<int>` can't match: `Clear()` is `O(1)` (it resets the count and version without scanning or clearing the backing arrays, versus zeroing the whole table) and iteration is a dense, contiguous scan over exactly the present elements. That is the winning shape for clear-and-rebuild "visited" sets — graph BFS/DFS, ECS entity membership, sweep-line — where the set is emptied every iteration. The cost is `O(Universe)` memory and non-negative-values-only: a value outside `[0, Universe)` throws on `Add` and reads as absent on `Contains` / `Remove`. It is an opt-in specialized type, not a `HashSet<int>` replacement — for an unbounded or huge-and-sparse key space, reach for `IntSet`.
291
291
292
292
```csharp
293
293
varvisited=newSparseSet(nodeCount); // universe = ids in [0, nodeCount)
@@ -453,7 +453,7 @@ Each type buys a different tradeoff. Find your workload below; if it isn't here,
453
453
| Dictionary keyed by a small **enum** — config-by-enum, per-state data, enum→handler tables |`EnumMap<TEnum, TValue>`| Dense array indexed on the enum's underlying value (the .NET `EnumMap`): `this[key]` / `TryGetValue` / `Add` / `Remove` are a single direct array index — no hashing, no probing, no collisions — and a full sweep is a linear array walk. The dictionary counterpart of `EnumSet`; enumerates ascending by value. For enums whose members are small non-negative integers (the default); negative or sparse `[Flags]` enums are unsupported — use `CelerityDictionary<TEnum, TValue, THasher>` there. |
454
454
| Tiny set (`n <= ~16`) that stays small — per-scope "seen" sets, small membership guards, deduping a handful of items |`SmallSet<T>`| The set counterpart of `SmallDictionary`: flat-array linear scan beats hashing at small `n`, no hasher to pick, the default element is stored inline. Implements `ISet<T>`. Degrades to `O(n)` for large sets, so only when instances stay small. |
455
455
| Set of **enum** values — flag sets, permission sets, state sets over a small enum |`EnumSet<TEnum>`| Bit-vector set indexed on the enum's underlying value (the .NET `EnumSet`): `Add` / `Contains` / `Remove` are a single bit op — no hashing, no boxing — and set algebra between two `EnumSet`s is a word-wise bitwise `OR` / `AND` / `XOR`. Enumerates ascending by value; `All()` builds the full universe. For enums whose members are small non-negative integers (the default); negative or sparse `[Flags]` enums are unsupported — use `CeleritySet<TEnum, THasher>` there. |
456
-
| Set of small **non-negative ints** over a bounded range that is **cleared & rebuilt often** — "visited" sets in graph BFS/DFS, ECS entity membership, sweep-line |`SparseSet`| Briggs–Torczon sparse set (dense value array + sparse index array): `O(1)``Clear` that touches no memory (vs `HashSet<int>` zeroing its table) and dense, cache-friendly iteration over just the present elements. `Add` / `Contains` / `Remove` are `O(1)`, no hashing. Costs `O(Universe)` memory and stores only values in `[0, Universe)`; for an unbounded or huge-and-sparse key space use `IntSet` / `HashSet<int>`. |
456
+
| Set of small **non-negative ints** over a bounded range that is **cleared & rebuilt often** — "visited" sets in graph BFS/DFS, ECS entity membership, sweep-line |`SparseSet`| Briggs–Torczon sparse set (dense value array + sparse index array): `O(1)``Clear` that leaves the backing arrays untouched (vs `HashSet<int>` zeroing its table) and dense, cache-friendly iteration over just the present elements. `Add` / `Contains` / `Remove` are `O(1)`, no hashing. Costs `O(Universe)` memory and stores only values in `[0, Universe)`; for an unbounded or huge-and-sparse key space use `IntSet` / `HashSet<int>`. |
457
457
| Set of `int` values |`IntSet`| Same fast path as `IntDictionary`, membership only. |
458
458
| Set of `long` values |`LongSet`| 64-bit equivalent of `IntSet`; defaults to `Int64WangNaiveHasher`. |
459
459
| Set of any other type |`CeleritySet<T, THasher>`| Same hasher choice as `CelerityDictionary`. |
0 commit comments