Skip to content

Commit 43211b6

Browse files
[stdlib] De-gyb sorting (swiftlang#9135)
* [stdlib] De-gyb sort algorithms * [stdlib] Rename Sort.swift.gyb * Update tests for de-gybbed sort
1 parent 8790997 commit 43211b6

File tree

5 files changed

+66
-137
lines changed

5 files changed

+66
-137
lines changed

stdlib/public/core/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ set(SWIFTLIB_ESSENTIAL
111111
ShadowProtocols.swift
112112
Shims.swift
113113
Slice.swift.gyb
114-
Sort.swift.gyb
114+
Sort.swift
115115
StaticString.swift
116116
Stride.swift.gyb
117117
StringCharacterView.swift # ORDER DEPENDENCY: Must precede String.swift

stdlib/public/core/CollectionAlgorithms.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ extension MutableCollection
389389
return ()
390390
}
391391
if didSortUnsafeBuffer == nil {
392-
_introSort(&self, subRange: startIndex..<endIndex)
392+
_introSort(&self, subRange: startIndex..<endIndex, by: <)
393393
}
394394
}
395395
}

stdlib/public/core/Sort.swift.gyb renamed to stdlib/public/core/Sort.swift

Lines changed: 62 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,15 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
%{
14-
def cmp(a, b, p):
15-
if p:
16-
return "(try areInIncreasingOrder(" + a + ", " + b + "))"
17-
else:
18-
return "(" + a + " < " + b + ")"
19-
20-
}%
21-
22-
// Generate two versions of sorting functions: one with an explicitly passed
23-
// predicate 'areInIncreasingOrder' and the other for Comparable types that don't
24-
// need such a predicate.
25-
% preds = [True, False]
26-
% for p in preds:
27-
%{
28-
if p:
29-
rethrows_ = "rethrows"
30-
try_ = "try"
31-
else:
32-
rethrows_ = ""
33-
try_ = ""
34-
}%
35-
3613
@_inlineable
3714
@_versioned
3815
func _insertionSort<C>(
3916
_ elements: inout C,
40-
subRange range: Range<C.Index>
41-
${", by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool" if p else ""}
42-
) ${rethrows_}
43-
where
44-
C : MutableCollection & BidirectionalCollection
45-
${"" if p else ", C.Iterator.Element : Comparable"} {
46-
17+
subRange range: Range<C.Index>,
18+
by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool
19+
) rethrows where
20+
C : MutableCollection & BidirectionalCollection
21+
{
4722
if !range.isEmpty {
4823
let start = range.lowerBound
4924

@@ -62,25 +37,20 @@ func _insertionSort<C>(
6237
// moving elements forward to make room.
6338
var i = sortedEnd
6439
repeat {
65-
let predecessor: C.Iterator.Element = elements[elements.index(before: i)]
40+
let predecessor: C.Iterator.Element =
41+
elements[elements.index(before: i)]
6642

67-
% if p:
68-
// If clouser throws the error, We catch the error put the element at right
69-
// place and rethrow the error.
43+
// If closure throws the error, We catch the error put the element at
44+
// right place and rethrow the error.
7045
do {
71-
// if x doesn't belong before y, we've found its position
72-
if !${cmp("x", "predecessor", p)} {
46+
// if x doesn't belong before y, we've found its position
47+
if try !areInIncreasingOrder(x, predecessor) {
7348
break
7449
}
7550
} catch {
7651
elements[i] = x
7752
throw error
7853
}
79-
% else:
80-
if !${cmp("x", "predecessor", p)} {
81-
break
82-
}
83-
% end
8454

8555
// Move y forward
8656
elements[i] = predecessor
@@ -108,12 +78,11 @@ func _insertionSort<C>(
10878
public // @testable
10979
func _sort3<C>(
11080
_ elements: inout C,
111-
_ a: C.Index, _ b: C.Index, _ c: C.Index
112-
${", by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool" if p else ""}
113-
) ${rethrows_}
81+
_ a: C.Index, _ b: C.Index, _ c: C.Index,
82+
by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool
83+
) rethrows
11484
where
11585
C : MutableCollection & RandomAccessCollection
116-
${"" if p else ", C.Iterator.Element : Comparable"}
11786
{
11887
// There are thirteen possible permutations for the original ordering of
11988
// the elements at indices `a`, `b`, and `c`. The comments in the code below
@@ -135,8 +104,8 @@ func _sort3<C>(
135104
// 122, 212, or 221.
136105
// - If all three elements are equivalent, they are already in order: 111.
137106

138-
switch (${cmp("elements[b]", "elements[a]", p)},
139-
${cmp("elements[c]", "elements[b]", p)}) {
107+
switch (try areInIncreasingOrder(elements[b], elements[a]),
108+
try areInIncreasingOrder(elements[c], elements[b])) {
140109
case (false, false):
141110
// 0 swaps: 123, 112, 122, 111
142111
break
@@ -151,7 +120,7 @@ func _sort3<C>(
151120
// swap(a, b): 213->123, 212->122, 312->132, 211->121
152121
elements.swapAt(a, b)
153122

154-
if ${cmp("elements[c]", "elements[b]", p)} {
123+
if try areInIncreasingOrder(elements[c], elements[b]) {
155124
// 132 (started as 312), 121 (started as 211)
156125
// swap(b, c): 132->123, 121->112
157126
elements.swapAt(b, c)
@@ -162,7 +131,7 @@ func _sort3<C>(
162131
// swap(b, c): 132->123, 121->112, 231->213, 221->212
163132
elements.swapAt(b, c)
164133

165-
if ${cmp("elements[b]", "elements[a]", p)} {
134+
if try areInIncreasingOrder(elements[b], elements[a]) {
166135
// 213 (started as 231), 212 (started as 221)
167136
// swap(a, b): 213->123, 212->122
168137
elements.swapAt(a, b)
@@ -180,12 +149,11 @@ func _sort3<C>(
180149
@_versioned
181150
func _partition<C>(
182151
_ elements: inout C,
183-
subRange range: Range<C.Index>
184-
${", by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool" if p else ""}
185-
) ${rethrows_} -> C.Index
152+
subRange range: Range<C.Index>,
153+
by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool
154+
) rethrows -> C.Index
186155
where
187156
C : MutableCollection & RandomAccessCollection
188-
${"" if p else ", C.Iterator.Element : Comparable"}
189157
{
190158
var lo = range.lowerBound
191159
var hi = elements.index(before: range.upperBound)
@@ -194,8 +162,7 @@ func _partition<C>(
194162
// as the pivot for the partition.
195163
let half = numericCast(elements.distance(from: lo, to: hi)) as UInt / 2
196164
let mid = elements.index(lo, offsetBy: numericCast(half))
197-
${try_} _sort3(&elements, lo, mid, hi
198-
${", by: areInIncreasingOrder" if p else ""})
165+
try _sort3(&elements, lo, mid, hi, by: areInIncreasingOrder)
199166
let pivot = elements[mid]
200167

201168
// Loop invariants:
@@ -206,7 +173,7 @@ func _partition<C>(
206173
FindLo: do {
207174
elements.formIndex(after: &lo)
208175
while lo != hi {
209-
if !${cmp("elements[lo]", "pivot", p)} { break FindLo }
176+
if try !areInIncreasingOrder(elements[lo], pivot) { break FindLo }
210177
elements.formIndex(after: &lo)
211178
}
212179
break Loop
@@ -215,7 +182,7 @@ func _partition<C>(
215182
FindHi: do {
216183
elements.formIndex(before: &hi)
217184
while hi != lo {
218-
if ${cmp("elements[hi]", "pivot", p)} { break FindHi }
185+
if try areInIncreasingOrder(elements[hi], pivot) { break FindHi }
219186
elements.formIndex(before: &hi)
220187
}
221188
break Loop
@@ -231,87 +198,66 @@ func _partition<C>(
231198
public // @testable
232199
func _introSort<C>(
233200
_ elements: inout C,
234-
subRange range: Range<C.Index>
235-
${", by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool" if p else ""}
236-
) ${rethrows_}
201+
subRange range: Range<C.Index>,
202+
by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool
203+
) rethrows
237204
where
238205
C : MutableCollection & RandomAccessCollection
239-
${"" if p else ", C.Iterator.Element : Comparable"} {
240-
241-
let count =
242-
elements.distance(from: range.lowerBound, to: range.upperBound)
206+
{
207+
let count = elements.distance(from: range.lowerBound, to: range.upperBound)
243208
if count < 2 {
244209
return
245210
}
246211
// Set max recursion depth to 2*floor(log(N)), as suggested in the introsort
247212
// paper: http://www.cs.rpi.edu/~musser/gp/introsort.ps
248213
let depthLimit = 2 * _floorLog2(Int64(count))
249-
${try_} _introSortImpl(
250-
&elements,
251-
subRange: range,
252-
${"by: areInIncreasingOrder," if p else ""}
214+
try _introSortImpl(&elements, subRange: range, by: areInIncreasingOrder,
253215
depthLimit: depthLimit)
254216
}
255217

256218
@_inlineable
257219
@_versioned
258220
func _introSortImpl<C>(
259221
_ elements: inout C,
260-
subRange range: Range<C.Index>
261-
${", by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool" if p else ""},
222+
subRange range: Range<C.Index>,
223+
by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool,
262224
depthLimit: Int
263-
) ${rethrows_}
225+
) rethrows
264226
where
265227
C : MutableCollection & RandomAccessCollection
266-
${"" if p else ", C.Iterator.Element : Comparable"} {
267-
228+
{
268229
// Insertion sort is better at handling smaller regions.
269230
if elements.distance(from: range.lowerBound, to: range.upperBound) < 20 {
270-
${try_} _insertionSort(
271-
&elements,
272-
subRange: range
273-
${", by: areInIncreasingOrder" if p else ""})
231+
try _insertionSort(&elements, subRange: range, by: areInIncreasingOrder)
274232
return
275233
}
276234
if depthLimit == 0 {
277-
${try_} _heapSort(
278-
&elements,
279-
subRange: range
280-
${", by: areInIncreasingOrder" if p else ""})
235+
try _heapSort(&elements, subRange: range, by: areInIncreasingOrder)
281236
return
282237
}
283238

284239
// Partition and sort.
285240
// We don't check the depthLimit variable for underflow because this variable
286241
// is always greater than zero (see check above).
287-
let partIdx: C.Index = ${try_} _partition(
288-
&elements,
289-
subRange: range
290-
${", by: areInIncreasingOrder" if p else ""})
291-
${try_} _introSortImpl(
292-
&elements,
293-
subRange: range.lowerBound..<partIdx,
294-
${"by: areInIncreasingOrder, " if p else ""}
295-
depthLimit: depthLimit &- 1)
296-
${try_} _introSortImpl(
297-
&elements,
298-
subRange: partIdx..<range.upperBound,
299-
${"by: areInIncreasingOrder, " if p else ""}
300-
depthLimit: depthLimit &- 1)
242+
let partIdx: C.Index = try _partition(&elements, subRange: range,
243+
by: areInIncreasingOrder)
244+
try _introSortImpl(&elements, subRange: range.lowerBound..<partIdx,
245+
by: areInIncreasingOrder, depthLimit: depthLimit &- 1)
246+
try _introSortImpl(&elements, subRange: partIdx..<range.upperBound,
247+
by: areInIncreasingOrder, depthLimit: depthLimit &- 1)
301248
}
302249

303250
@_inlineable
304251
@_versioned
305252
func _siftDown<C>(
306253
_ elements: inout C,
307254
index: C.Index,
308-
subRange range: Range<C.Index>
309-
${", by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool" if p else ""}
310-
) ${rethrows_}
255+
subRange range: Range<C.Index>,
256+
by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool
257+
) rethrows
311258
where
312259
C : MutableCollection & RandomAccessCollection
313-
${"" if p else ", C.Iterator.Element : Comparable"} {
314-
260+
{
315261
let countToIndex = elements.distance(from: range.lowerBound, to: index)
316262
let countFromIndex = elements.distance(from: index, to: range.upperBound)
317263
// Check if left child is within bounds. If not, return, because there are
@@ -321,38 +267,35 @@ func _siftDown<C>(
321267
}
322268
let left = elements.index(index, offsetBy: countToIndex + 1)
323269
var largest = index
324-
if ${cmp("elements[largest]", "elements[left]", p)} {
270+
if try areInIncreasingOrder(elements[largest], elements[left]) {
325271
largest = left
326272
}
327273
// Check if right child is also within bounds before trying to examine it.
328274
if countToIndex + 2 < countFromIndex {
329275
let right = elements.index(after: left)
330-
if ${cmp("elements[largest]", "elements[right]", p)} {
276+
if try areInIncreasingOrder(elements[largest], elements[right]) {
331277
largest = right
332278
}
333279
}
334280
// If a child is bigger than the current node, swap them and continue sifting
335281
// down.
336282
if largest != index {
337283
elements.swapAt(index, largest)
338-
${try_} _siftDown(
339-
&elements,
340-
index: largest,
341-
subRange: range
342-
${", by: areInIncreasingOrder" if p else ""})
284+
try _siftDown(&elements, index: largest, subRange: range,
285+
by: areInIncreasingOrder)
343286
}
344287
}
345288

346289
@_inlineable
347290
@_versioned
348291
func _heapify<C>(
349292
_ elements: inout C,
350-
subRange range: Range<C.Index>
351-
${", by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool" if p else ""}
352-
) ${rethrows_}
293+
subRange range: Range<C.Index>,
294+
by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool
295+
) rethrows
353296
where
354297
C : MutableCollection & RandomAccessCollection
355-
${"" if p else ", C.Iterator.Element : Comparable"} {
298+
{
356299
// Here we build a heap starting from the lowest nodes and moving to the root.
357300
// On every step we sift down the current node to obey the max-heap property:
358301
// parent >= max(leftChild, rightChild)
@@ -367,45 +310,32 @@ func _heapify<C>(
367310

368311
while node != root {
369312
elements.formIndex(before: &node)
370-
${try_} _siftDown(
371-
&elements,
372-
index: node,
373-
subRange: range
374-
${", by: areInIncreasingOrder" if p else ""})
313+
try _siftDown(&elements, index: node, subRange: range,
314+
by: areInIncreasingOrder)
375315
}
376316
}
377317

378318
@_inlineable
379319
@_versioned
380320
func _heapSort<C>(
381321
_ elements: inout C,
382-
subRange range: Range<C.Index>
383-
${", by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool" if p else ""}
384-
) ${rethrows_}
322+
subRange range: Range<C.Index>,
323+
by areInIncreasingOrder: (C.Iterator.Element, C.Iterator.Element) throws -> Bool
324+
) rethrows
385325
where
386326
C : MutableCollection & RandomAccessCollection
387-
${"" if p else ", C.Iterator.Element : Comparable"} {
327+
{
388328
var hi = range.upperBound
389329
let lo = range.lowerBound
390-
${try_} _heapify(
391-
&elements,
392-
subRange: range
393-
${", by: areInIncreasingOrder" if p else ""})
330+
try _heapify(&elements, subRange: range, by: areInIncreasingOrder)
394331
elements.formIndex(before: &hi)
395332
while hi != lo {
396333
elements.swapAt(lo, hi)
397-
${try_} _siftDown(
398-
&elements,
399-
index: lo,
400-
subRange: lo..<hi
401-
${", by: areInIncreasingOrder" if p else ""})
334+
try _siftDown(&elements, index: lo, subRange: lo..<hi, by: areInIncreasingOrder)
402335
elements.formIndex(before: &hi)
403336
}
404337
}
405338

406-
% end
407-
// for p in preds
408-
409339
/// Exchange the values of `a` and `b`.
410340
///
411341
/// - Precondition: `a` and `b` do not alias each other.

0 commit comments

Comments
 (0)