|
| 1 | +// |
| 2 | +// InsertionSort.swift |
| 3 | +// SortStateUniversity |
| 4 | +// |
| 5 | +// Created by Kyle Hughes on 6/22/21. |
| 6 | +// |
| 7 | + |
| 8 | +import Foundation |
| 9 | + |
| 10 | +/// A simple sorting algorithm that sorts its elements one at a time. |
| 11 | +/// |
| 12 | +/// - SeeAlso: https://en.wikipedia.org/wiki/Insertion_sort |
| 13 | +public struct InsertionSort<Element>: Identifiable { |
| 14 | + /// A type that represents the collection of the elements that the algorithm is sorting. |
| 15 | + public typealias Elements = Array<Element> |
| 16 | + |
| 17 | + /// The stable identity of the algorithm. |
| 18 | + public let id: UUID |
| 19 | + |
| 20 | + /// The given elements that the algorithm is sorting. |
| 21 | + /// |
| 22 | + /// This value is constant and will not change after instantiation. |
| 23 | + public let input: Elements |
| 24 | + |
| 25 | + /// The current result of applying the sorting algorithm to `input`. |
| 26 | + /// |
| 27 | + /// This value is "live" and will change as the algorithm is executed. When the algorithm is finished this value |
| 28 | + /// will contain the sorted result of `input`. It is primarily exposed to allow the internals of the algorithm to |
| 29 | + /// be observed. |
| 30 | + /// |
| 31 | + /// This value should not be used as the final output of the algorithm unless it is known that the algorithm has |
| 32 | + /// finished. It may be easier to perform `callAsFunction()` and respond to the step that is returned – the output |
| 33 | + /// will be reported through that function if the algorithm is finished. |
| 34 | + /// |
| 35 | + /// - SeeAlso: `outputAfterTransactions` |
| 36 | + public private(set) var output: Elements |
| 37 | + |
| 38 | + /// The position (in `output`) of the element that is currently being sorted. |
| 39 | + public private(set) var sortingElementIndex: Elements.Index |
| 40 | + |
| 41 | + /// The position (in `output`) that is one greater than the last sorted element in `output`. |
| 42 | + /// |
| 43 | + /// `output` is sorted when this value is equal to `output.endIndex`. |
| 44 | + public private(set) var sortedEndIndex: Elements.Index |
| 45 | + |
| 46 | + // MARK: Public Initialization |
| 47 | + |
| 48 | + /// Creates an algorithm to sort the given input using insertion sort. |
| 49 | + /// |
| 50 | + /// - Parameter input: The elements to sort. |
| 51 | + public init(input: Elements) { |
| 52 | + self.input = input |
| 53 | + |
| 54 | + id = UUID() |
| 55 | + output = input |
| 56 | + sortingElementIndex = output.index(after: output.startIndex) |
| 57 | + sortedEndIndex = sortingElementIndex |
| 58 | + } |
| 59 | + |
| 60 | + // MARK: Private Instance Interface |
| 61 | + |
| 62 | + private var adjacentSortedElementIndex: Elements.Index { |
| 63 | + output.index(before: sortingElementIndex) |
| 64 | + } |
| 65 | + |
| 66 | + private var isNotFinished: Bool { |
| 67 | + sortedEndIndex < output.endIndex |
| 68 | + } |
| 69 | + |
| 70 | + private mutating func advanceToNextIndexToSort() { |
| 71 | + output.formIndex(after: &sortedEndIndex) |
| 72 | + sortingElementIndex = sortedEndIndex |
| 73 | + } |
| 74 | + |
| 75 | + @discardableResult |
| 76 | + private mutating func swapSortingElementWithAdjacentSortedElement() -> Elements.Index { |
| 77 | + let sortedElementIndex = adjacentSortedElementIndex |
| 78 | + output.swapAt(sortingElementIndex, sortedElementIndex) |
| 79 | + |
| 80 | + return sortedElementIndex |
| 81 | + } |
| 82 | + |
| 83 | + private mutating func swapSortingElementWithAdjacentSortedElementAndAdvanceToNextComparison() { |
| 84 | + let newSortingElementIndex = swapSortingElementWithAdjacentSortedElement() |
| 85 | + |
| 86 | + if output.startIndex < newSortingElementIndex { |
| 87 | + sortingElementIndex = newSortingElementIndex |
| 88 | + } else { |
| 89 | + advanceToNextIndexToSort() |
| 90 | + } |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +// MARK: - SortingAlgorithm Extension |
| 95 | + |
| 96 | +extension InsertionSort: SortingAlgorithm { |
| 97 | + // MARK: Public Static Interface |
| 98 | + |
| 99 | + /// The runtime complexity of the algorithm. |
| 100 | + public static var complexity: Complexity { |
| 101 | + .quadratic |
| 102 | + } |
| 103 | + |
| 104 | + /// The unique name of the sorting algorithm. |
| 105 | + public static var label: SortingAlgorithmLabel { |
| 106 | + .insertion |
| 107 | + } |
| 108 | + |
| 109 | + /// Returns the number of comparisons that the algorithm will perform, in the worst case, given an input |
| 110 | + /// with `n` elements. |
| 111 | + /// |
| 112 | + /// The algorithm may require fewer total comparisons than returned depending on the state of the input and the |
| 113 | + /// answers to the comparisons. The algorithm is guaranteed to not require more comparisons than returned. |
| 114 | + /// |
| 115 | + /// This value is provably correct and precise. It is not an estimate. |
| 116 | + /// |
| 117 | + /// - SeeAlso: http://watson.latech.edu/book/algorithms/algorithmsSorting2.html |
| 118 | + /// - Parameter n: The number of elements. |
| 119 | + /// - Returns: The number of comparisons that the algorithm will perform in the worst case. |
| 120 | + public static func calculateMaximumNumberOfComparisonsInWorstCase(for n: Int) -> Int { |
| 121 | + guard 1 < n else { |
| 122 | + return 0 |
| 123 | + } |
| 124 | + |
| 125 | + var sum = 0 |
| 126 | + |
| 127 | + for i in 1 ... n-1 { |
| 128 | + sum += i |
| 129 | + } |
| 130 | + |
| 131 | + return sum |
| 132 | + } |
| 133 | + |
| 134 | + // MARK: Public Instance Interface |
| 135 | + |
| 136 | + /// Answers the current comparison with the given side. |
| 137 | + /// |
| 138 | + /// The algorithm is advanced to the state that follows the answer. |
| 139 | + /// |
| 140 | + /// If the algorithm is not at a point of comparison then this function will have no effect. |
| 141 | + /// |
| 142 | + /// - Parameter answer: The answer to the current comparison. |
| 143 | + public mutating func answer(_ answer: Comparison<Self>.Side) { |
| 144 | + guard isNotFinished else { |
| 145 | + return |
| 146 | + } |
| 147 | + |
| 148 | + switch answer { |
| 149 | + case .left: |
| 150 | + advanceToNextIndexToSort() |
| 151 | + case .right: |
| 152 | + swapSortingElementWithAdjacentSortedElementAndAdvanceToNextComparison() |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + /// Executes the algorithm in its current state and, if possible, advances it to the next state and returns the |
| 157 | + /// step to the caller. |
| 158 | + /// |
| 159 | + /// When the algorithm is not finished this function will return the next comparison that needs to |
| 160 | + /// be answered to continue the algorithm. When the algorithm is finished this function will return the sorted |
| 161 | + /// output. |
| 162 | + /// |
| 163 | + /// This function is idempotent: for a given state of the algorithm, calling this function will always produce |
| 164 | + /// the same result and will always leave the algorithm in the same – possibly new – state. Performing this |
| 165 | + /// function consecutively on the same algorithm will have no additional affect. |
| 166 | + /// |
| 167 | + /// - Returns: The next step in the algorithm: either the next comparison to answer, or the sorted output. |
| 168 | + public func callAsFunction() -> SortingAlgorithmStep<Self> { |
| 169 | + guard isNotFinished else { |
| 170 | + return .finished(output) |
| 171 | + } |
| 172 | + |
| 173 | + return .comparison(Comparison(source: self)) |
| 174 | + } |
| 175 | + |
| 176 | + /// Returns the element that represents the given side of the current comparison. |
| 177 | + /// |
| 178 | + /// If the algorithm is not at a point of comparison then `nil` will be returned. For example, if the |
| 179 | + /// algorithm has not started or is finished then `nil` will be returned. |
| 180 | + /// |
| 181 | + /// - Parameter answer: The answer that represents the side of the comparison to peek at. |
| 182 | + /// - Returns: The element that represents the given side of the current comparison, or `nil` if the algorithm |
| 183 | + /// is not at a point of comparison. |
| 184 | + public func peekAtElement(for answer: Comparison<InsertionSort<Element>>.Side) -> Element? { |
| 185 | + guard isNotFinished else { |
| 186 | + return nil |
| 187 | + } |
| 188 | + |
| 189 | + switch answer { |
| 190 | + case .left: |
| 191 | + return output[adjacentSortedElementIndex] |
| 192 | + case .right: |
| 193 | + return output[sortingElementIndex] |
| 194 | + } |
| 195 | + } |
| 196 | +} |
| 197 | + |
| 198 | +// MARK: - Codable Extension |
| 199 | + |
| 200 | +extension InsertionSort: Codable where Element: Codable { |
| 201 | + // NO-OP |
| 202 | +} |
| 203 | + |
| 204 | +// MARK: - Equatable Extension |
| 205 | + |
| 206 | +extension InsertionSort: Equatable where Element: Equatable { |
| 207 | + // NO-OP |
| 208 | +} |
| 209 | + |
| 210 | +// MARK: - Hashable Extension |
| 211 | + |
| 212 | +extension InsertionSort: Hashable where Element: Hashable { |
| 213 | + // NO-OP |
| 214 | +} |
0 commit comments