Partition.swift 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // This source file is part of the Swift Algorithms open source project
  4. //
  5. // Copyright (c) 2020 Apple Inc. and the Swift project authors
  6. // Licensed under Apache License v2.0 with Runtime Library Exception
  7. //
  8. // See https://swift.org/LICENSE.txt for license information
  9. //
  10. //===----------------------------------------------------------------------===//
  11. //===----------------------------------------------------------------------===//
  12. // stablePartition(by:)
  13. //===----------------------------------------------------------------------===//
  14. extension MutableCollection {
  15. /// Moves all elements satisfying `belongsInSecondPartition` into a suffix of
  16. /// the collection, preserving their relative order, and returns the start of
  17. /// the resulting suffix.
  18. ///
  19. /// - Complexity: O(*n* log *n*), where *n* is the number of elements.
  20. /// - Precondition:
  21. /// `n == distance(from: range.lowerBound, to: range.upperBound)`
  22. @inlinable
  23. internal mutating func stablePartition(
  24. count n: Int,
  25. subrange: Range<Index>,
  26. by belongsInSecondPartition: (Element) throws -> Bool
  27. ) rethrows -> Index {
  28. if n == 0 { return subrange.lowerBound }
  29. if n == 1 {
  30. return try belongsInSecondPartition(self[subrange.lowerBound])
  31. ? subrange.lowerBound
  32. : subrange.upperBound
  33. }
  34. let h = n / 2, i = index(subrange.lowerBound, offsetBy: h)
  35. let j = try stablePartition(
  36. count: h,
  37. subrange: subrange.lowerBound..<i,
  38. by: belongsInSecondPartition)
  39. let k = try stablePartition(
  40. count: n - h,
  41. subrange: i..<subrange.upperBound,
  42. by: belongsInSecondPartition)
  43. return rotate(subrange: j..<k, toStartAt: i)
  44. }
  45. /// Moves all elements satisfying the given predicate into a suffix of the
  46. /// given range, preserving the relative order of the elements in both
  47. /// partitions, and returns the start of the resulting suffix.
  48. ///
  49. /// - Parameters:
  50. /// - subrange: The range of elements within this collection to partition.
  51. /// - belongsInSecondPartition: A predicate used to partition the
  52. /// collection. All elements satisfying this predicate are ordered after
  53. /// all elements not satisfying it.
  54. ///
  55. /// - Complexity: O(*n* log *n*), where *n* is the length of this collection.
  56. @inlinable
  57. public mutating func stablePartition(
  58. subrange: Range<Index>,
  59. by belongsInSecondPartition: (Element) throws-> Bool
  60. ) rethrows -> Index {
  61. try stablePartition(
  62. count: distance(from: subrange.lowerBound, to: subrange.upperBound),
  63. subrange: subrange,
  64. by: belongsInSecondPartition)
  65. }
  66. /// Moves all elements satisfying the given predicate into a suffix of this
  67. /// collection, preserving the relative order of the elements in both
  68. /// partitions, and returns the start of the resulting suffix.
  69. ///
  70. /// - Parameter belongsInSecondPartition: A predicate used to partition the
  71. /// collection. All elements satisfying this predicate are ordered after
  72. /// all elements not satisfying it.
  73. ///
  74. /// - Complexity: O(*n* log *n*), where *n* is the length of this collection.
  75. @inlinable
  76. public mutating func stablePartition(
  77. by belongsInSecondPartition: (Element) throws-> Bool
  78. ) rethrows -> Index {
  79. try stablePartition(
  80. subrange: startIndex..<endIndex,
  81. by: belongsInSecondPartition)
  82. }
  83. }
  84. //===----------------------------------------------------------------------===//
  85. // partition(by:)
  86. //===----------------------------------------------------------------------===//
  87. extension MutableCollection {
  88. /// Moves all elements satisfying `isSuffixElement` into a suffix of the
  89. /// collection, returning the start position of the resulting suffix.
  90. ///
  91. /// - Complexity: O(*n*) where n is the length of the collection.
  92. @inlinable
  93. public mutating func partition(
  94. subrange: Range<Index>,
  95. by belongsInSecondPartition: (Element) throws -> Bool
  96. ) rethrows -> Index {
  97. // This version of `partition(subrange:)` is half stable; the elements in
  98. // the first partition retain their original relative order.
  99. guard var i = try self[subrange].firstIndex(where: belongsInSecondPartition)
  100. else { return subrange.upperBound }
  101. var j = index(after: i)
  102. while j != subrange.upperBound {
  103. if try !belongsInSecondPartition(self[j]) {
  104. swapAt(i, j)
  105. formIndex(after: &i)
  106. }
  107. formIndex(after: &j)
  108. }
  109. return i
  110. }
  111. }
  112. extension MutableCollection where Self: BidirectionalCollection {
  113. /// Moves all elements satisfying `isSuffixElement` into a suffix of the
  114. /// collection, returning the start position of the resulting suffix.
  115. ///
  116. /// - Complexity: O(*n*) where n is the length of the collection.
  117. @inlinable
  118. public mutating func partition(
  119. subrange: Range<Index>,
  120. by belongsInSecondPartition: (Element) throws -> Bool
  121. ) rethrows -> Index {
  122. var lo = subrange.lowerBound
  123. var hi = subrange.upperBound
  124. // 'Loop' invariants (at start of Loop, all are true):
  125. // * lo < hi
  126. // * predicate(self[i]) == false, for i in startIndex ..< lo
  127. // * predicate(self[i]) == true, for i in hi ..< endIndex
  128. Loop: while true {
  129. FindLo: do {
  130. while lo < hi {
  131. if try belongsInSecondPartition(self[lo]) { break FindLo }
  132. formIndex(after: &lo)
  133. }
  134. break Loop
  135. }
  136. FindHi: do {
  137. formIndex(before: &hi)
  138. while lo < hi {
  139. if try !belongsInSecondPartition(self[hi]) { break FindHi }
  140. formIndex(before: &hi)
  141. }
  142. break Loop
  143. }
  144. swapAt(lo, hi)
  145. formIndex(after: &lo)
  146. }
  147. return lo
  148. }
  149. }
  150. //===----------------------------------------------------------------------===//
  151. // partitioningIndex(where:)
  152. //===----------------------------------------------------------------------===//
  153. extension Collection {
  154. /// Returns the index of the first element in the collection that matches
  155. /// the predicate.
  156. ///
  157. /// The collection must already be partitioned according to the predicate.
  158. /// That is, there should be an index `i` where for every element in
  159. /// `collection[..<i]` the predicate is `false`, and for every element in
  160. /// `collection[i...]` the predicate is `true`.
  161. ///
  162. /// - Parameter belongsInSecondPartition: A predicate that partitions the
  163. /// collection.
  164. /// - Returns: The index of the first element in the collection for which
  165. /// `predicate` returns `true`.
  166. ///
  167. /// - Complexity: O(log *n*), where *n* is the length of this collection if
  168. /// the collection conforms to `RandomAccessCollection`, otherwise O(*n*).
  169. @inlinable
  170. public func partitioningIndex(
  171. where belongsInSecondPartition: (Element) throws -> Bool
  172. ) rethrows -> Index {
  173. var n = count
  174. var l = startIndex
  175. while n > 0 {
  176. let half = n / 2
  177. let mid = index(l, offsetBy: half)
  178. if try belongsInSecondPartition(self[mid]) {
  179. n = half
  180. } else {
  181. l = index(after: mid)
  182. n -= half + 1
  183. }
  184. }
  185. return l
  186. }
  187. }