Repository navigation
fix: make IsAdjacentTo symmetric, and normalization with it - #17
Merged
Merged
Conversation
IsAdjacentTo switched on the receiver's shape and handled only IFiniteRange<T>;
every other shape fell through to false. Its inner switch did handle unbounded
operands, so the relation was asymmetric: [1,3].IsAdjacentTo((,0]) was true
while (,0].IsAdjacentTo([1,3]) was false. PostgreSQL's -|- is symmetric and
answers true for both — queried directly to confirm. The XML doc asserted the
broken behaviour as if intended, which is why reading the code confirmed it.
The damage was in normalization rather than the predicate. RangeSet.From and
RangeSet.Union merge neighbours with current.IsAdjacentTo(next) after sorting by
lower bound, so an unbounded-start element is always the receiver and always took
the broken direction:
RangeSet.From([(,0], [1,)]) was {(,0],[1,)} now {(,)}
blocks.Union(blocks.Complement()) was {(,0],[1,)} now the Infinite set
Sets violated the pairwise-non-adjacent invariant they document, two sets that
should be equal compared unequal depending on construction path, and a set
covering the whole domain did not equal RangeSet.Infinite.
Rewritten to switch on the pair (range, other), deciding each unordered shape
pair once so both receiver orders route to the same test — symmetry is now
structural rather than something each arm has to remember. Empty, Infinity and
two ranges open at the same end still answer false.
Verified by reverting: the symmetry sweep, the four normalization tests and the
new live-PostgreSQL parity test all fail without the fix, the last one naming
the disagreeing pair.
Co-Authored-By: Claude Opus 5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The defect
RangeExtensions.IsAdjacentToswitched on the receiver's shape and handled onlyIFiniteRange<T>; every other shape fell through tofalse. Its inner switch did handle unbounded operands, so the relation was asymmetric:PostgreSQL's
-|-is symmetric and answerstruefor all five — queried directly against the server to confirm, not inferred. The XML doc asserted the broken behaviour as if it were intended, which is why reading the code confirmed the comment rather than catching the bug.Why it mattered beyond the predicate
RangeSet.FromandRangeSet.Unionmerge neighbours withcurrent.IsAdjacentTo(next)after sorting by lower bound, so an unbounded-start element is always the receiver and always took the broken direction. Sets were built violating the pairwise-non-adjacent invariant the type documents:Two sets that should be equal compared unequal depending on how they were built, and a set covering the whole domain did not equal
RangeSet.Infinite.The fix
Rewritten to switch on the pair
(range, other), deciding each unordered shape pair once so both receiver orders route to the same test — symmetry is structural rather than something each arm has to remember.Empty,Infinity, and two ranges open at the same end still answerfalse.RangeSet.IsAdjacentTowas never affected: it has its own bound-based implementation that tracks infinity flags explicitly.Verification
Tests were written first and confirmed red before the fix, then green, then red again on reverting only the source:
UnionwithComplementRangeAdjacency_UnboundedShapes_MatchPostgrescompares model against server; it fails naming the disagreeing pair'(,0]' -|- '[1,3]'when the fix is revertedNo pre-existing test broke — the fix only adds
truewhere PostgreSQL saystrue. The existing unbounded tests kept passing because they only ever called the working direction, with comments explaining the receiver "must be finite".Compatibility
Docs: XML doc corrected, README adjacency section gains a symmetric-and-unbounded example with a "Changed in 6.2.1" callout, changelogs for all four packages.
🤖 Generated with Claude Code