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
|
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
/// A comparison algorithm for a given type.
@preconcurrency
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public protocol SortComparator<Compared>: Hashable, Sendable {
/// The type that the `SortComparator` provides a comparison for.
associatedtype Compared
/// The relative ordering of lhs, and rhs.
///
/// The result of comparisons should be flipped if the current `order`
/// is `reverse`.
///
/// If `compare(lhs, rhs)` is `.orderedAscending`, then `compare(rhs, lhs)`
/// must be `.orderedDescending`. If `compare(lhs, rhs)` is
/// `.orderedDescending`, then `compare(rhs, lhs)` must be
/// `.orderedAscending`.
///
/// - Parameters:
/// - lhs: A value to compare.
/// - rhs: A value to compare.
func compare(_ lhs: Compared, _ rhs: Compared) -> ComparisonResult
/// If the `SortComparator`s resulting order is forward or reverse.
var order: SortOrder { get set }
}
/// The orderings that sorts can be performed with.
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
@frozen
public enum SortOrder: Hashable, Codable, Sendable {
/// The ordering where if compare(a, b) == .orderedAscending,
/// a is placed before b.
case forward
/// The ordering where if compare(a, b) == .orderedAscending,
/// a is placed after b.
case reverse
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let isForward = try container.decode(Bool.self)
if isForward {
self = .forward
} else {
self = .reverse
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .forward: try container.encode(true)
case .reverse: try container.encode(false)
}
}
}
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
extension ComparisonResult {
package func withOrder(_ order: SortOrder) -> ComparisonResult {
if order == .reverse {
if self == .orderedAscending { return .orderedDescending }
if self == .orderedDescending { return .orderedAscending }
}
return self
}
}
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
package struct AnySortComparator: SortComparator, Sendable {
var _base: any Hashable & Sendable // internal for testing
/// Takes `base` and two values to be compared and compares the two values
/// using `base`.
private let _compare: @Sendable (Any, Any, Any) -> ComparisonResult
/// Takes `base` inout, and a new `SortOrder` and changes `base`s `order`
/// to match the new `SortOrder`.
private let setOrder: @Sendable (inout any Hashable & Sendable, SortOrder) -> any Hashable & Sendable
/// Gets the current `order` property of `base`.
private let getOrder: @Sendable (Any) -> SortOrder
package init<Comparator: SortComparator>(_ comparator: Comparator) where Comparator : Sendable {
self._base = comparator
self._compare = { (base: Any, lhs: Any, rhs: Any) -> ComparisonResult in
(base as! Comparator).compare(lhs as! Comparator.Compared, rhs as! Comparator.Compared)
}
self.setOrder = { (base: inout any Hashable & Sendable, newOrder: SortOrder) -> AnyHashable in
var typedBase = base as! Comparator
typedBase.order = newOrder
base = typedBase
return AnyHashable(typedBase)
}
self.getOrder = { (base: Any) -> SortOrder in
(base as! Comparator).order
}
}
package var order: SortOrder {
get {
return getOrder(_base)
}
set {
_base = setOrder(&_base, newValue)
}
}
package func compare(_ lhs: Any, _ rhs: Any) -> ComparisonResult {
return _compare(_base, lhs, rhs)
}
package func hash(into hasher: inout Hasher) {
hasher.combine(_base)
}
package static func == (lhs: Self, rhs: Self) -> Bool {
func compare<L : Equatable, R : Equatable>(_ l : L, _ r : R) -> Bool {
guard let rr = r as? L else {
return false
}
return l == rr
}
return compare(lhs._base, rhs._base)
}
}
/// Compares `Comparable` types using their comparable implementation.
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public struct ComparableComparator<Compared: Comparable>: SortComparator, Sendable {
public var order: SortOrder
#if FOUNDATION_FRAMEWORK
@available(FoundationPreview 0.1, *)
public init(order: SortOrder = .forward) {
self.order = order
}
#else
// No need for availability on this initializer in the package.
public init(order: SortOrder = .forward) {
self.order = order
}
#endif
private func unorderedCompare(_ lhs: Compared, _ rhs: Compared) -> ComparisonResult {
if lhs < rhs { return .orderedAscending }
if lhs > rhs { return .orderedDescending }
return .orderedSame
}
public func compare(_ lhs: Compared, _ rhs: Compared) -> ComparisonResult {
return unorderedCompare(lhs, rhs).withOrder(order)
}
}
/// A comparator which orders optional values using a comparator which
/// compares the optional's `Wrapped` type. `nil` values are ordered before
/// non-nil values when `order` is `forward`.
package struct OptionalComparator<Base: SortComparator>: SortComparator {
private var base: Base
package init(_ base: Base) {
self.base = base
}
package var order: SortOrder {
get { base.order }
set { base.order = newValue }
}
package func compare(_ lhs: Base.Compared?, _ rhs: Base.Compared?) -> ComparisonResult {
guard let lhs else {
if rhs == nil { return .orderedSame }
return .orderedAscending.withOrder(order)
}
guard let rhs else { return .orderedDescending.withOrder(order) }
return base.compare(lhs, rhs)
}
}
extension OptionalComparator : Sendable where Base : Sendable { }
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
extension Never: SortComparator {
public typealias Compared = Never
public func compare(_ lhs: Never, _ rhs: Never) -> ComparisonResult {}
public var order: SortOrder {
get {
switch self {}
}
set {
switch self {}
}
}
}
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
extension Sequence {
/// Returns the elements of the sequence, sorted using the given comparator
/// to compare elements.
///
/// - Parameters:
/// - comparator: the comparator to use in ordering elements
/// - Returns: an array of the elements sorted using `comparator`.
public func sorted<Comparator: SortComparator>(using comparator: Comparator) -> [Element] where Comparator.Compared == Element {
return self.sorted {
comparator.compare($0, $1) == .orderedAscending
}
}
/// Returns the elements of the sequence, sorted using the given array of
/// `SortComparator`s to compare elements.
///
/// - Parameters:
/// - comparators: an array of comparators used to compare elements. The
/// first comparator specifies the primary comparator to be used in
/// sorting the sequence's elements. Any subsequent comparators are used
/// to further refine the order of elements with equal values.
/// - Returns: an array of the elements sorted using `comparators`.
public func sorted<S: Sequence, Comparator: SortComparator>(using comparators: S) -> [Element] where
S.Element == Comparator,
Element == Comparator.Compared
{
return self.sorted {
comparators.compare($0, $1) == .orderedAscending
}
}
}
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
extension Sequence {
/// If `lhs` is ordered before `rhs` in the ordering described by the given
/// sequence of `SortComparator`s
///
/// The first element of the sequence of comparators specifies the primary
/// comparator to be used in sorting the sequence's elements. Any subsequent
/// comparators are used to further refine the order of elements with equal
/// values.
public func compare<Comparator: SortComparator>(_ lhs: Comparator.Compared, _ rhs: Comparator.Compared) -> ComparisonResult where Element == Comparator {
for comparator in self {
let result = comparator.compare(lhs, rhs)
if result != .orderedSame { return result }
}
return .orderedSame
}
}
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
extension MutableCollection where Self: RandomAccessCollection {
/// Sorts the collection using the given comparator to compare elements.
/// - Parameters:
/// - comparator: the sort comparator used to compare elements.
public mutating func sort<Comparator: SortComparator>(using comparator: Comparator) where Comparator.Compared == Element {
self.sort {
comparator.compare($0, $1) == .orderedAscending
}
}
/// Sorts the collection using the given array of `SortComparator`s to
/// compare elements.
///
/// - Parameters:
/// - comparators: an array of comparators used to compare elements. The
/// first comparator specifies the primary comparator to be used in
/// sorting the sequence's elements. Any subsequent comparators are used
/// to further refine the order of elements with equal values.
public mutating func sort<S: Sequence, Comparator: SortComparator>(using comparators: S) where S.Element == Comparator, Element == Comparator.Compared {
self.sort {
comparators.compare($0, $1) == .orderedAscending
}
}
}
|