Skip to content

[patch] Break natural-order ties ordinally so distinct strings never compare equal - #65

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/break-natural-ties-ordinally
Oct 7, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/break-natural-ties-ordinally

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #59

What changed

NaturalStringComparer.Compare returned 0 whenever every chunk matched. Strings that differ only in leading zeros or digit script therefore compared equal: file5/file005, v1/v01, file٥/file5. That broke the IComparer<T> contract in three ways:

  • SortedSet silently dropped entries
  • SortedDictionary.Add threw on distinct keys
  • Array.Sort output depended on input order

The natural order is still the primary key. When it ties, Compare now falls back to string.CompareOrdinal(x, y), so it returns 0 only for ordinally equal strings. Numerically equal strings stay next to each other: file5, file05 and file005 all still sort after file4 and before file6. The fallback allocates nothing, so Compare_AllocatesNothing still holds. The <returns> doc now describes the tie-break.

Tests

Six existing tests asserted 0 for numerically equal but distinct strings. They now use an AssertTiedOnlyByOrdinal helper, which asserts a nonzero result whose sign matches CompareOrdinal and flips when the arguments are swapped. Compare_NonAsciiDigits_EqualValuesAreEqual is renamed ..._EqualValuesAreTiedOrdinally.

New tests, matching the issue's acceptance criteria:

  • Compare_NumericallyEqualStrings_AreNotEqual: over a set of values, Compare == 0 exactly when the strings are ordinally equal
  • Compare_NumericallyEqualStrings_StayBetweenTheirNeighbours: the tie-break only reorders strings within a tie
  • SortedSet_KeepsStringsThatDifferOnlyInLeadingZeros: Count == 3
  • SortedDictionary_AcceptsKeysThatDifferOnlyInLeadingZeros
  • Sort_IsTheSameForEveryInputOrder: every permutation of {a, b5, b05} gives the same output

With the library change reverted, 9 tests fail: the updated ones and the new ones, except the neighbours guard. With the change, all 25 pass, and the library builds for all its target frameworks.

🤖 Generated with Claude Code

https://claude.ai/code/session_013JBDCsjuRez5zdzBcBJ7Y7


Generated by Claude Code

…compare equal

NaturalStringComparer.Compare returned 0 when every chunk matched, so
strings that differ only in leading zeros or digit script ("file5" and
"file005", "v1" and "v01", "file٥" and "file5") compared equal. Sorted
collections then dropped one of them or threw on Add, and Array.Sort's
result depended on the input order.

The natural order stays the primary key; when it ties, the strings are
now ordered by string.CompareOrdinal, so Compare returns 0 only for
ordinally equal strings. The tests that asserted 0 for such pairs now
assert a nonzero, antisymmetric result.

Fixes #59

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013JBDCsjuRez5zdzBcBJ7Y7

Copy link
Copy Markdown
Contributor Author

CI is blocked by runner availability, not by this change. In run 37363660356, ci / .NET / Discover Test Projects (ubuntu-latest) waited 15 minutes for a runner and was cancelled before any step ran. That happened on the first attempt and again on the re-run (attempt 2). The build, test and analyze jobs that depend on it were then skipped, so the code never got to build. ci / Classify repository did get a runner on attempt 2 and passed.

RoundTripStringJsonConverter#113 and #114 show the same 15-minute queue cancellation at the same time, so it looks like an org-wide Actions capacity or limit issue. Locally the full suite passes (25/25) on this branch. Nothing in the diff can change this. Once runners are available, CI needs to be re-run.


Generated by Claude Code

@sonarqubecloud

sonarqubecloud Bot commented Oct 6, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit e613b7c into main Oct 7, 2026
20 of 23 checks passed
@matt-edmondson
matt-edmondson deleted the fix/break-natural-ties-ordinally branch October 7, 2026 00:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NaturalStringComparer returns 0 for different strings like "file5" and "file005", so SortedSet silently drops them and SortedDictionary throws on Add

2 participants