-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathSort.swift.gyb
434 lines (397 loc) · 12.9 KB
/
Sort.swift.gyb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
%{
def cmp(a, b, p):
if p:
return "(try areInIncreasingOrder(" + a + ", " + b + "))"
else:
return "(" + a + " < " + b + ")"
}%
// Generate two versions of sorting functions: one with an explicitly passed
// predicate 'areInIncreasingOrder' and the other for Comparable types that don't
// need such a predicate.
% preds = [True, False]
% for p in preds:
%{
if p:
rethrows_ = "rethrows"
try_ = "try"
else:
rethrows_ = ""
try_ = ""
}%
@_inlineable
@_versioned
internal func _insertionSort<C>(
_ elements: inout C,
subRange range: Range<C.Index>
${", by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool" if p else ""}
) ${rethrows_}
where
C : MutableCollection & BidirectionalCollection
${"" if p else ", C.Element : Comparable"} {
if !range.isEmpty {
let start = range.lowerBound
// Keep track of the end of the initial sequence of sorted
// elements.
var sortedEnd = start
// One element is trivially already-sorted, thus pre-increment
// Continue until the sorted elements cover the whole sequence
elements.formIndex(after: &sortedEnd)
while sortedEnd != range.upperBound {
// get the first unsorted element
let x: C.Element = elements[sortedEnd]
// Look backwards for x's position in the sorted sequence,
// moving elements forward to make room.
var i = sortedEnd
repeat {
let predecessor: C.Element = elements[elements.index(before: i)]
% if p:
// If clouser throws the error, We catch the error put the element at right
// place and rethrow the error.
do {
// if x doesn't belong before y, we've found its position
if !${cmp("x", "predecessor", p)} {
break
}
} catch {
elements[i] = x
throw error
}
% else:
if !${cmp("x", "predecessor", p)} {
break
}
% end
// Move y forward
elements[i] = predecessor
elements.formIndex(before: &i)
} while i != start
if i != sortedEnd {
// Plop x into position
elements[i] = x
}
elements.formIndex(after: &sortedEnd)
}
}
}
/// Sorts the elements at `elements[a]`, `elements[b]`, and `elements[c]`.
/// Stable.
///
/// The indices passed as `a`, `b`, and `c` do not need to be consecutive, but
/// must be in strict increasing order.
///
/// - Precondition: `a < b && b < c`
/// - Postcondition: `elements[a] <= elements[b] && elements[b] <= elements[c]`
@_inlineable
public // @testable
func _sort3<C>(
_ elements: inout C,
_ a: C.Index, _ b: C.Index, _ c: C.Index
${", by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool" if p else ""}
) ${rethrows_}
where
C : MutableCollection & RandomAccessCollection
${"" if p else ", C.Element : Comparable"}
{
// There are thirteen possible permutations for the original ordering of
// the elements at indices `a`, `b`, and `c`. The comments in the code below
// show the relative ordering of the three elements using a three-digit
// number as shorthand for the position and comparative relationship of
// each element. For example, "312" indicates that the element at `a` is the
// largest of the three, the element at `b` is the smallest, and the element
// at `c` is the median. This hypothetical input array has a 312 ordering for
// `a`, `b`, and `c`:
//
// [ 7, 4, 3, 9, 2, 0, 3, 7, 6, 5 ]
// ^ ^ ^
// a b c
//
// - If each of the three elements is distinct, they could be ordered as any
// of the permutations of 1, 2, and 3: 123, 132, 213, 231, 312, or 321.
// - If two elements are equivalent and one is distinct, they could be
// ordered as any permutation of 1, 1, and 2 or 1, 2, and 2: 112, 121, 211,
// 122, 212, or 221.
// - If all three elements are equivalent, they are already in order: 111.
switch (${cmp("elements[b]", "elements[a]", p)},
${cmp("elements[c]", "elements[b]", p)}) {
case (false, false):
// 0 swaps: 123, 112, 122, 111
break
case (true, true):
// 1 swap: 321
// swap(a, c): 312->123
elements.swapAt(a, c)
case (true, false):
// 1 swap: 213, 212 --- 2 swaps: 312, 211
// swap(a, b): 213->123, 212->122, 312->132, 211->121
elements.swapAt(a, b)
if ${cmp("elements[c]", "elements[b]", p)} {
// 132 (started as 312), 121 (started as 211)
// swap(b, c): 132->123, 121->112
elements.swapAt(b, c)
}
case (false, true):
// 1 swap: 132, 121 --- 2 swaps: 231, 221
// swap(b, c): 132->123, 121->112, 231->213, 221->212
elements.swapAt(b, c)
if ${cmp("elements[b]", "elements[a]", p)} {
// 213 (started as 231), 212 (started as 221)
// swap(a, b): 213->123, 212->122
elements.swapAt(a, b)
}
}
}
/// Reorders `elements` and returns an index `p` such that every element in
/// `elements[range.lowerBound..<p]` is less than every element in
/// `elements[p..<range.upperBound]`.
///
/// - Precondition: The count of `range` must be >= 3:
/// `elements.distance(from: range.lowerBound, to: range.upperBound) >= 3`
@_inlineable
@_versioned
internal func _partition<C>(
_ elements: inout C,
subRange range: Range<C.Index>
${", by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool" if p else ""}
) ${rethrows_} -> C.Index
where
C : MutableCollection & RandomAccessCollection
${"" if p else ", C.Element : Comparable"}
{
var lo = range.lowerBound
var hi = elements.index(before: range.upperBound)
// Sort the first, middle, and last elements, then use the middle value
// as the pivot for the partition.
let half = numericCast(elements.distance(from: lo, to: hi)) as UInt / 2
let mid = elements.index(lo, offsetBy: numericCast(half))
${try_} _sort3(&elements, lo, mid, hi
${", by: areInIncreasingOrder" if p else ""})
let pivot = elements[mid]
// Loop invariants:
// * lo < hi
// * elements[i] < pivot, for i in range.lowerBound..<lo
// * pivot <= elements[i] for i in hi..<range.upperBound
Loop: while true {
FindLo: do {
elements.formIndex(after: &lo)
while lo != hi {
if !${cmp("elements[lo]", "pivot", p)} { break FindLo }
elements.formIndex(after: &lo)
}
break Loop
}
FindHi: do {
elements.formIndex(before: &hi)
while hi != lo {
if ${cmp("elements[hi]", "pivot", p)} { break FindHi }
elements.formIndex(before: &hi)
}
break Loop
}
elements.swapAt(lo, hi)
}
return lo
}
@_inlineable
public // @testable
func _introSort<C>(
_ elements: inout C,
subRange range: Range<C.Index>
${", by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool" if p else ""}
) ${rethrows_}
where
C : MutableCollection & RandomAccessCollection
${"" if p else ", C.Element : Comparable"} {
let count =
elements.distance(from: range.lowerBound, to: range.upperBound)
if count < 2 {
return
}
// Set max recursion depth to 2*floor(log(N)), as suggested in the introsort
// paper: http://www.cs.rpi.edu/~musser/gp/introsort.ps
let depthLimit = 2 * count._binaryLogarithm()
${try_} _introSortImpl(
&elements,
subRange: range,
${"by: areInIncreasingOrder," if p else ""}
depthLimit: depthLimit)
}
@_inlineable
@_versioned
internal func _introSortImpl<C>(
_ elements: inout C,
subRange range: Range<C.Index>
${", by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool" if p else ""},
depthLimit: Int
) ${rethrows_}
where
C : MutableCollection & RandomAccessCollection
${"" if p else ", C.Element : Comparable"} {
// Insertion sort is better at handling smaller regions.
if elements.distance(from: range.lowerBound, to: range.upperBound) < 20 {
${try_} _insertionSort(
&elements,
subRange: range
${", by: areInIncreasingOrder" if p else ""})
return
}
if depthLimit == 0 {
${try_} _heapSort(
&elements,
subRange: range
${", by: areInIncreasingOrder" if p else ""})
return
}
// Partition and sort.
// We don't check the depthLimit variable for underflow because this variable
// is always greater than zero (see check above).
let partIdx: C.Index = ${try_} _partition(
&elements,
subRange: range
${", by: areInIncreasingOrder" if p else ""})
${try_} _introSortImpl(
&elements,
subRange: range.lowerBound..<partIdx,
${"by: areInIncreasingOrder, " if p else ""}
depthLimit: depthLimit &- 1)
${try_} _introSortImpl(
&elements,
subRange: partIdx..<range.upperBound,
${"by: areInIncreasingOrder, " if p else ""}
depthLimit: depthLimit &- 1)
}
@_inlineable
@_versioned
internal func _siftDown<C>(
_ elements: inout C,
index: C.Index,
subRange range: Range<C.Index>
${", by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool" if p else ""}
) ${rethrows_}
where
C : MutableCollection & RandomAccessCollection
${"" if p else ", C.Element : Comparable"} {
let countToIndex = elements.distance(from: range.lowerBound, to: index)
let countFromIndex = elements.distance(from: index, to: range.upperBound)
// Check if left child is within bounds. If not, return, because there are
// no children of the given node in the heap.
if countToIndex + 1 >= countFromIndex {
return
}
let left = elements.index(index, offsetBy: countToIndex + 1)
var largest = index
if ${cmp("elements[largest]", "elements[left]", p)} {
largest = left
}
// Check if right child is also within bounds before trying to examine it.
if countToIndex + 2 < countFromIndex {
let right = elements.index(after: left)
if ${cmp("elements[largest]", "elements[right]", p)} {
largest = right
}
}
// If a child is bigger than the current node, swap them and continue sifting
// down.
if largest != index {
elements.swapAt(index, largest)
${try_} _siftDown(
&elements,
index: largest,
subRange: range
${", by: areInIncreasingOrder" if p else ""})
}
}
@_inlineable
@_versioned
internal func _heapify<C>(
_ elements: inout C,
subRange range: Range<C.Index>
${", by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool" if p else ""}
) ${rethrows_}
where
C : MutableCollection & RandomAccessCollection
${"" if p else ", C.Element : Comparable"} {
// Here we build a heap starting from the lowest nodes and moving to the root.
// On every step we sift down the current node to obey the max-heap property:
// parent >= max(leftChild, rightChild)
//
// We skip the rightmost half of the array, because these nodes don't have
// any children.
let root = range.lowerBound
var node = elements.index(
root,
offsetBy: elements.distance(
from: range.lowerBound, to: range.upperBound) / 2)
while node != root {
elements.formIndex(before: &node)
${try_} _siftDown(
&elements,
index: node,
subRange: range
${", by: areInIncreasingOrder" if p else ""})
}
}
@_inlineable
@_versioned
internal func _heapSort<C>(
_ elements: inout C,
subRange range: Range<C.Index>
${", by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool" if p else ""}
) ${rethrows_}
where
C : MutableCollection & RandomAccessCollection
${"" if p else ", C.Element : Comparable"} {
var hi = range.upperBound
let lo = range.lowerBound
${try_} _heapify(
&elements,
subRange: range
${", by: areInIncreasingOrder" if p else ""})
elements.formIndex(before: &hi)
while hi != lo {
elements.swapAt(lo, hi)
${try_} _siftDown(
&elements,
index: lo,
subRange: lo..<hi
${", by: areInIncreasingOrder" if p else ""})
elements.formIndex(before: &hi)
}
}
% end
// for p in preds
/// Exchanges the values of the two arguments.
///
/// The two arguments must not alias each other. To swap two elements of a
/// mutable collection, use the `swapAt(_:_:)` method of that collection
/// instead of this function.
///
/// - Parameters:
/// - a: The first value to swap.
/// - b: The second value to swap.
@_inlineable
public func swap<T>(_ a: inout T, _ b: inout T) {
// Semantically equivalent to (a, b) = (b, a).
// Microoptimized to avoid retain/release traffic.
let p1 = Builtin.addressof(&a)
let p2 = Builtin.addressof(&b)
_debugPrecondition(
p1 != p2,
"swapping a location with itself is not supported")
// Take from P1.
let tmp: T = Builtin.take(p1)
// Transfer P2 into P1.
Builtin.initialize(Builtin.take(p2) as T, p1)
// Initialize P2.
Builtin.initialize(tmp, p2)
}