Skip to content

Align Heap._UnsafeHandle min/maxValue tie-breaking with Swift.min/max#455

Merged
lorentey merged 4 commits intoapple:mainfrom
DakshinD:heap-max-func
May 16, 2025
Merged

Align Heap._UnsafeHandle min/maxValue tie-breaking with Swift.min/max#455
lorentey merged 4 commits intoapple:mainfrom
DakshinD:heap-max-func

Conversation

@DakshinD
Copy link
Copy Markdown
Contributor

@DakshinD DakshinD commented Mar 8, 2025

Heap._UnsafeHandle has minValue and maxValue. Original functionality was minValue, if a == b, return b. For maxValue, if a == b, return a. In both cases, this is the opposite tiebreaker choice from Swift.min and Swift.max.

Heap.replaceMax used Heap._UnsafeHandle.maxValue while Heap.max used Swift.max, and due to different tiebreaker choices, led to inconsistency as seen in #439 .

Therefore I go ahead and update Heap._UnsafeHandle.maxValue/minValue to be consistent with Swift.max/min.

Fixes #439

Checklist

  • I've read the Contribution Guidelines
  • My contributions are licensed under the Swift license.
  • I've followed the coding style of the rest of the project.
  • I've added tests covering all new code paths my change adds to the project (if appropriate).
  • I've added benchmarks covering new functionality (if appropriate).
  • I've verified that my change does not break any existing tests or introduce unexplained benchmark regressions.
  • I've updated the documentation if necessary.

@DakshinD DakshinD requested a review from lorentey as a code owner March 8, 2025 16:16
@lorentey lorentey added this to the 1.2.0 milestone Apr 17, 2025
Copy link
Copy Markdown
Member

@lorentey lorentey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks -- this is definitely a good idea.

The goal of this is to guarantee that Heap behaves in a specific way for equal-but-distinguishable elements. This new guarantee must come with tests that verify that the implementation behaves as expected; otherwise the bug we want to fix could be accidentally revived in future package releases.

@inlinable @inline(__always)
internal func minValue(_ a: _HeapNode, _ b: _HeapNode) -> _HeapNode {
self[a] < self[b] ? a : b
self[a] <= self[b] ? a : b
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would make sense to add a comment explaining that the result emulates the stdlib. The implementation should also match precisely what the stdlib does, to emphasize this even further.

Suggested change
self[a] <= self[b] ? a : b
// The expression used here matches the implementation of the
// standard `Swift.min(_:_:)` function. This attempts to
// preserve any pre-existing order in case `T` has identity.
// `(min(x, y), max(x, y))` should return `(x, y)` in case `x == y`.
self[b] < self[a] ? b : a

@inlinable @inline(__always)
internal func maxValue(_ a: _HeapNode, _ b: _HeapNode) -> _HeapNode {
self[a] < self[b] ? b : a
self[a] > self[b] ? a : b
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self[a] > self[b] ? a : b
// In case `a` and `b` match, we need to pick `b`. See `minValue(_:_:)`.
self[b] >= self[a] ? b : a

@DakshinD
Copy link
Copy Markdown
Contributor Author

DakshinD commented May 2, 2025

Sounds good, thanks for the revision. Will go ahead and also add those tests.

lorentey added 3 commits May 15, 2025 17:30
…xpectations

The existing expressions had the same effect, but they used different operators (`<=` instead of `<` and `>` instead of `>=`). The distinction is usually irrelevant, but there is no reason not to just follow what the stdlib is doing exactly.
@lorentey
Copy link
Copy Markdown
Member

@swift-ci test

@lorentey lorentey merged commit ace6afa into apple:main May 16, 2025
23 checks passed
@lorentey lorentey added the Heap Min-max heap module label May 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Heap Min-max heap module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

max value decisions are different between Heap.max and Heap.replaceMax

2 participants