Skip to content

Instantly share code, notes, and snippets.

@natecook1000
Last active July 12, 2016 07:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save natecook1000/70f36608ecd6236552ce0e9f79b98cff to your computer and use it in GitHub Desktop.
Save natecook1000/70f36608ecd6236552ce0e9f79b98cff to your computer and use it in GitHub Desktop.
extension MutableCollection {
/// Reorders the elements of the collection such that all the elements
/// that match the given predicate are after all the elements that do
/// not match the predicate.
///
/// After partitioning a collection, there is a pivot index `p` where
/// no element before `p` satisfies the `belongsInSecondPartition`
/// predicate and every element at or after `p` satisfies
/// `belongsInSecondPartition`.
///
/// In the following example, an array of numbers is partitioned by a
/// predicate that matches elements greater than 30.
///
/// var numbers = [30, 40, 20, 30, 30, 60, 10]
/// let p = numbers.partition(by: { $0 > 30 })
/// // p == 5
/// // numbers == [30, 10, 20, 30, 30, 60, 40]
///
/// The `numbers` array is now arranged in two partitions. The first
/// partition, `numbers.prefix(upTo: p)`, is made up of the elements that
/// are not greater than 30. The second partition, `numbers.suffix(from: p)`,
/// is made up of the elements that *are* greater than 30.
///
/// let first = numbers.prefix(upTo: p)
/// // first == [30, 10, 20, 30, 30]
/// let second = numbers.suffix(from: p)
/// // second == [60, 40]
///
/// - Parameter belongsInSecondPartition: A predicate used to partition
/// the collection. All elements satisfying this predicate are ordered
/// after all elements not satisfying it.
/// - Returns: The index of the first element in the reordered collection
/// that matches `belongsInSecondPartition`. If no elements in the
/// collection match `belongsInSecondPartition`, the returned index is
/// equal to the collection's `endIndex`.
///
/// - Complexity: O(n)
public mutating func partition(
by belongsInSecondPartition: @noescape (Iterator.Element) throws -> Bool
) rethrows -> Index {
var pivot = startIndex
while true {
if pivot == endIndex {
return pivot
}
if try belongsInSecondPartition(self[pivot]) {
break
}
formIndex(after: &pivot)
}
var i = index(after: pivot)
while i < endIndex {
if try !belongsInSecondPartition(self[i]) {
swap(&self[i], &self[pivot])
formIndex(after: &pivot)
}
formIndex(after: &i)
}
return pivot
}
}
extension MutableCollection where Self: BidirectionalCollection {
/// Reorders the elements of the collection such that all the elements
/// that match the given predicate are after all the elements that do
/// not match the predicate.
///
/// After partitioning a collection, there is a pivot index `p` where
/// no element before `p` satisfies the `belongsInSecondPartition`
/// predicate and every element at or after `p` satisfies
/// `belongsInSecondPartition`.
///
/// In the following example, an array of numbers is partitioned by a
/// predicate that matches elements greater than 30.
///
/// var numbers = [30, 40, 20, 30, 30, 60, 10]
/// let p = numbers.partition(by: { $0 > 30 })
/// // p == 5
/// // numbers == [30, 10, 20, 30, 30, 60, 40]
///
/// The `numbers` array is now arranged in two partitions. The first
/// partition, `numbers.prefix(upTo: p)`, is made up of the elements that
/// are not greater than 30. The second partition, `numbers.suffix(from: p)`,
/// is made up of the elements that *are* greater than 30.
///
/// let first = numbers.prefix(upTo: p)
/// // first == [30, 10, 20, 30, 30]
/// let second = numbers.suffix(from: p)
/// // second == [60, 40]
///
/// - Parameter belongsInSecondPartition: A predicate used to partition
/// the collection. All elements satisfying this predicate are ordered
/// after all elements not satisfying it.
/// - Returns: The index of the first element in the reordered collection
/// that matches `belongsInSecondPartition`. If no elements in the
/// collection match `belongsInSecondPartition`, the returned index is
/// equal to the collection's `endIndex`.
///
/// - Complexity: O(n)
public mutating func partition(
by belongsInSecondPartition: @noescape (Iterator.Element) throws -> Bool
) rethrows -> Index {
var lo = startIndex
var hi = endIndex
// 'Loop' invariants (at start of Loop, all are true):
// * lo < hi
// * predicate(self[i]) == false, for i in startIndex ..< lo
// * predicate(self[i]) == true, for i in hi ..< endIndex
Loop: while true {
FindLo: repeat {
while lo < hi {
if try belongsInSecondPartition(self[lo]) { break FindLo }
formIndex(after: &lo)
}
break Loop
} while false
FindHi: repeat {
formIndex(before: &hi)
while lo < hi {
if try !belongsInSecondPartition(self[hi]) { break FindHi }
formIndex(before: &hi)
}
break Loop
} while false
swap(&self[lo], &self[hi])
formIndex(after: &lo)
}
return lo
}
}
var n = [30, 40, 20, 30, 30, 60, 10]
let p = n.partition(by: { $0 > 30 })
// n == [30, 10, 20, 30, 30, 60, 40]
// p == 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment