├── .gitattributes ├── .github └── workflows │ └── swift.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── SwiftPriorityQueue │ └── SwiftPriorityQueue.swift ├── SwiftPriorityQueue.podspec ├── SwiftPriorityQueue.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcbaselines │ └── 5539482E1AC6495600666559.xcbaseline │ ├── 010A85AE-666B-4CDA-BF1D-B7258494F15A.plist │ └── Info.plist ├── SwiftPriorityQueue ├── AppDelegate.swift ├── Base.lproj │ └── MainMenu.xib ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist └── astar.swift └── Tests ├── Info.plist ├── LinuxMain.swift └── SwiftPriorityQueueTests ├── SwiftPriorityQueuePerformanceTests.swift └── SwiftPriorityQueueTests.swift /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/* linguist-documentation 2 | 3 | -------------------------------------------------------------------------------- /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | name: Swift 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Build 17 | run: swift build -v 18 | - name: Run tests 19 | run: swift test -v 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .Trashes 3 | *.swp 4 | # Xcode 5 | # 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.xcuserstate 22 | 23 | # CocoaPods 24 | # 25 | # We recommend against adding the Pods directory to your .gitignore. However 26 | # you should judge for yourself, the pros and cons are mentioned at: 27 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 28 | # 29 | # Pods/ 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 1.4.0 2 | - Added a push() with a size limit, removing the lowest priority item (thanks @reedes) 3 | - Added some asserts and tests (thanks @vale-cocoa) 4 | 5 | ### 1.3.1 6 | - Improvements to pop() performance (thanks @peteraisher) 7 | - Some basic performance tests (thanks @peteraisher) 8 | 9 | ### 1.3.0 10 | - Swift 5 support 11 | 12 | ### 1.2.1 13 | - Fixed a critical bug in remove() and added a test for it 14 | - Rearranged the project to be testable on Linux 15 | - Updated the format of Package.swift to be in-line with Swift 4 16 | 17 | ### 1.2.0 18 | > Note: 1.2.0 is the first version of SwiftPriorityQueue to break compatibility with previous versions of Swift since release 1.0.1. From this point forward users of Swift 2 and Swift 3 should use release 1.1.2 of SwiftPriorityQueue. 19 | 20 | - Swift 4 support 21 | - Removed preprocessor macros and code to support Swift 2 22 | 23 | ### 1.1.2 24 | - Initializer that takes custom order function added 25 | - watchOS added to podspec 26 | 27 | ### 1.1.1 28 | - Added remove(item: T) method to remove an item at an arbitrary location 29 | - Added removeAll(item: T) method to remove multiple of the same item 30 | 31 | ### 1.1.0 32 | - Swift 3 support 33 | 34 | ### 1.0.3 35 | - Last Swift 2 only release 36 | - Improved unit tests 37 | 38 | ### 1.0.2 39 | - Better Swift 2 support 40 | 41 | ### 1.0.1 42 | - Access control bug fix 43 | - Documentation Additions 44 | 45 | ### 1.0 46 | - Initial Stable Release 47 | - Last Release to support Swift 1.2 48 | 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2020 David Kopec 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "SwiftPriorityQueue", 7 | products: [ 8 | .library( 9 | name: "SwiftPriorityQueue", 10 | targets: ["SwiftPriorityQueue"]), 11 | ], 12 | dependencies: [], 13 | targets: [ 14 | .target( 15 | name: "SwiftPriorityQueue", 16 | dependencies: []), 17 | .testTarget( 18 | name: "SwiftPriorityQueueTests", 19 | dependencies: ["SwiftPriorityQueue"]), 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftPriorityQueue 2 | 3 | [![Swift Versions](https://img.shields.io/badge/Swift-1%2C2%2C3%2C4%2C5-green.svg)](https://swift.org) 4 | [![CocoaPods Version](https://img.shields.io/cocoapods/v/SwiftPriorityQueue.svg)](https://cocoapods.org/pods/SwiftPriorityQueue) 5 | [![SPM Compatible](https://img.shields.io/badge/SPM-compatible-4BC51D.svg?style=flat)](https://swift.org/package-manager/) 6 | [![CocoaPods Platforms](https://img.shields.io/cocoapods/p/SwiftPriorityQueue.svg)](https://cocoapods.org/pods/SwiftPriorityQueue) 7 | [![Linux Compatible](https://img.shields.io/badge/Linux-compatible-4BC51D.svg?style=flat)](https://swift.org) 8 | [![Twitter Contact](https://img.shields.io/badge/contact-@davekopec-blue.svg?style=flat)](https://twitter.com/davekopec) 9 | 10 | SwiftPriorityQueue is a pure Swift (no Cocoa) implementation of a generic priority queue data structure, appropriate for use on all platforms (macOS, iOS, Linux, etc.) where Swift is supported. It features a straightforward interface and can be used with any type that implements `Comparable`. It utilizes comparisons between elements rather than separate numeric priorities to determine order. 11 | 12 | Internally, SwiftPriorityQueue uses a classic binary heap, resulting in O(lg n) pushes and pops. It includes in-source documentation, an A* based example maze solving program (for macOS), and unit tests (*pull requests are welcome for additional unit tests in particular*). 13 | 14 | ## Features 15 | * Easy-to-use method interface 16 | * Small, self contained, pure Swift code base 17 | * Classic binary heap implementation with O(lg n) pushes and pops 18 | * Iterable through standard Swift for...in loops (implements `Sequence` and `IteratorProtocol`) 19 | * In-source documentation 20 | * A fun maze solving A* based example program 21 | 22 | ## Installation 23 | 24 | Release 1.3.0 and above supports Swift 5. Use release 1.2.1 for Swift 4. Use release 1.1.2 for Swift 3 and Swift 2 support. Use release 1.0 for Swift 1.2 support. 25 | 26 | ### CocoaPods 27 | 28 | Use the CocoaPod `SwiftPriorityQueue`. 29 | 30 | ### Swift Package Manager (SPM) 31 | 32 | Add this repository as a dependency. 33 | 34 | ### Manual 35 | 36 | Copy `SwiftPriorityQueue.swift` into your project. 37 | 38 | ## Documentation 39 | There is a large amount of documentation in the source code using the standard Swift documentation technique (compatible with Jazzy). Essentially though, SwiftPriorityQueue has the three critical methods you'd expect - `push()`, `pop()`, and `peek()`. 40 | 41 | ### Initialization 42 | When you create a new `PriorityQueue` you can optionally specify whether the priority queue is ascending or descending. What does this mean? If the priority queue is ascending, its smallest values (as determined by their implementation of `Comparable` aka `<`) will be popped first, and if it's descending, its largest values will be popped first. 43 | ``` 44 | var pq: PriorityQueue = PriorityQueue(ascending: true) 45 | ``` 46 | You can also provide an array of starting values to be pushed sequentially immediately into the priority queue. 47 | ``` 48 | var pq: PriorityQueue = PriorityQueue(startingValues: [6, 2, 3, 235, 4, 500]) 49 | ``` 50 | Or you can specify both. 51 | ``` 52 | var pq: PriorityQueue = PriorityQueue(ascending: false, startingValues: [6, 2, 3, 235, 4, 500]) 53 | ``` 54 | Or you can specify neither. By default a `PriorityQueue` is descending and empty. As you've probably noticed, a PriorityQueue takes a generic type. This type must be `Comparable`, as its comparison will be used for determining priority. This means that your custom types must implement `Comparable` and utilize the overridden `<` to determine priority. 55 | 56 | ### Methods 57 | `PriorityQueue` has all of the standard methods you'd expect a priority queue data structure to have. 58 | * `push(element: T)` - Puts an element into the priority queue. O(lg n) 59 | * `push(element: T, maxCount: Int) -> T?` - Adds an element while limiting the size of the priority queue to `maxCount`. If more than `maxCount` elements are in the priority queue after the addition, the lowest priority element will be discarded and returned. Note this is inefficient because this is a binary heap, so only the highet priority item is efficient to retrieve. O(n) 60 | * `pop() -> T?` - Returns and removes the element with the highest (or lowest if ascending) priority or `nil` if the priority queue is empty. O(lg n) 61 | * `peek() -> T?` - Returns the element with the highest (or lowest if ascending) priority or `nil` if the priority queue is empty. O(1) 62 | * `clear()` - Removes all elements from the priority queue. 63 | * `remove(item: T)` - Removes the first found instance of *item* in the priority queue. Silently returns if not found. O(n) 64 | * `removeAll(item: T)` - Removes all instances of *item* in the priority queue through repeated calls to `remove()`. Silently returns if not found. 65 | 66 | ### Properties 67 | * `count: Int` - The number of elements in the priority queue. 68 | * `isEmpty: Bool` - `true` if the priority queue has zero elements, and `false` otherwise. 69 | 70 | ### Standard Swift Protocols 71 | `PriorityQueue` implements `IteratorProtocol`, `Sequence` and `Collection` so you can treat `PriorityQueue` like any other Swift sequence/collection. This means you can use Swift standard library fucntions on a `PriorityQueue` and iterate through a `PriorityQueue` like this: 72 | ``` 73 | for item in pq { // pq is a PriorityQueue 74 | print(item) 75 | } 76 | ``` 77 | When you do this, every item from the `PriorityQueue` is popped in order. `PriorityQueue` also implements `CustomStringConvertible` and `CustomDebugStringConvertible`. 78 | ``` 79 | print(pq) 80 | ``` 81 | Note: `PriorityQueue` is *not* thread-safe (do not manipulate it from multiple threads at once). 82 | 83 | ### Limited Heap Size Example 84 | 85 | Suppose you want to only keep the `maxCount` highest priority items in the priority queue. 86 | 87 | For example, say you only want the priority queue to only ever have 4 elements: 88 | 89 | ``` 90 | var pq: PriorityQueue = PriorityQueue() 91 | let maxCount = 4 92 | 93 | pq.push(4, maxCount: maxCount) 94 | pq.push(5, maxCount: maxCount) 95 | pq.push(0, maxCount: maxCount) 96 | pq.push(3, maxCount: maxCount) 97 | pq.push(6, maxCount: maxCount) 98 | pq.push(1, maxCount: maxCount) 99 | ``` 100 | 101 | In this case, after 4 elements were pushed, only the smallest elements were kept (because the order was `ascending`). So, the final priority queue has the elements 0, 1, 3, 4 in it. 102 | 103 | ### Just for Fun - A* (`astar.swift`) 104 | A* is a pathfinding algorithm that uses a priority queue. The sample program that comes with SwiftPriorityQueue is a maze solver that uses A*. You can find some in-source documentation if you want to reuse this algorithm inside `astar.swift`. 105 | 106 | ## Authorship & License 107 | SwiftPriorityQueue is written by David Kopec (@davecom on GitHub) and released under the MIT License (see `LICENSE`). You can find my contact information on my GitHub profile page. I encourage you to submit pull requests and open issues here on GitHub. Thank you to all of the contributors over the years. 108 | -------------------------------------------------------------------------------- /Sources/SwiftPriorityQueue/SwiftPriorityQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftPriorityQueue.swift 3 | // SwiftPriorityQueue 4 | // 5 | // Copyright (c) 2015-2023 David Kopec 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | // This code was inspired by Section 2.4 of Algorithms by Sedgewick & Wayne, 4th Edition 26 | 27 | /// A PriorityQueue takes objects to be pushed of any type that implements Comparable. 28 | /// It will pop the objects in the order that they would be sorted. A pop() or a push() 29 | /// can be accomplished in O(lg n) time. It can be specified whether the objects should 30 | /// be popped in ascending or descending order (Max Priority Queue or Min Priority Queue) 31 | /// at the time of initialization. 32 | public struct PriorityQueue { 33 | 34 | fileprivate(set) var heap = [T]() 35 | private let ordered: (T, T) -> Bool 36 | 37 | /// Creates a new PriorityQueue using either the `>` operator or `<` operator to determine order. 38 | /// The default order is descending if `ascending` is not specified. 39 | /// 40 | /// - parameter ascending: Use the `>` operator (`true`) or `<` operator (`false`). 41 | /// - parameter startingValues: An array of elements to initialize the PriorityQueue with. 42 | public init(ascending: Bool = false, startingValues: [T] = []) { 43 | self.init(order: ascending ? { $0 > $1 } : { $0 < $1 }, startingValues: startingValues) 44 | } 45 | 46 | /// Creates a new PriorityQueue with the given custom ordering function. 47 | /// 48 | /// - parameter order: A function that specifies whether its first argument should 49 | /// come after the second argument in the PriorityQueue. 50 | /// - parameter startingValues: An array of elements to initialize the PriorityQueue with. 51 | public init(order: @escaping (T, T) -> Bool, startingValues: [T] = []) { 52 | ordered = order 53 | 54 | // Based on "Heap construction" from Sedgewick p 323 55 | heap = startingValues 56 | var i = heap.count/2 - 1 57 | while i >= 0 { 58 | sink(i) 59 | i -= 1 60 | } 61 | } 62 | 63 | /// How many elements the Priority Queue stores. O(1) 64 | public var count: Int { return heap.count } 65 | 66 | /// true if and only if the Priority Queue is empty. O(1) 67 | public var isEmpty: Bool { return heap.isEmpty } 68 | 69 | /// Add a new element onto the Priority Queue. O(lg n) 70 | /// 71 | /// - parameter element: The element to be inserted into the Priority Queue. 72 | public mutating func push(_ element: T) { 73 | heap.append(element) 74 | swim(heap.count - 1) 75 | } 76 | 77 | /// Add a new element onto a Priority Queue, limiting the size of the queue. O(n^2) 78 | /// If the size limit has been reached, the lowest priority element will be removed and returned. 79 | /// Note that because this is a binary heap, there is no easy way to find the lowest priority 80 | /// item, so this method can be inefficient. 81 | /// Also note, that only one item will be removed, even if count > maxCount by more than one. 82 | /// 83 | /// - parameter element: The element to be inserted into the Priority Queue. 84 | /// - parameter maxCount: The Priority Queue will not grow further if its count >= maxCount. 85 | /// - returns: the discarded lowest priority element, or `nil` if count < maxCount 86 | public mutating func push(_ element: T, maxCount: Int) -> T? { 87 | precondition(maxCount > 0) 88 | if count < maxCount { 89 | push(element) 90 | } else { // heap.count >= maxCount 91 | // find the min priority element (ironically using max here) 92 | if let discard = heap.max(by: ordered) { 93 | if ordered(discard, element) { return element } 94 | push(element) 95 | remove(discard) 96 | return discard 97 | } 98 | } 99 | return nil 100 | } 101 | 102 | /// Remove and return the element with the highest priority (or lowest if ascending). O(lg n) 103 | /// 104 | /// - returns: The element with the highest priority in the Priority Queue, or nil if the PriorityQueue is empty. 105 | public mutating func pop() -> T? { 106 | 107 | if heap.isEmpty { return nil } 108 | let count = heap.count 109 | if count == 1 { return heap.removeFirst() } // added for Swift 2 compatibility 110 | // so as not to call swap() with two instances of the same location 111 | fastPop(newCount: count - 1) 112 | 113 | return heap.removeLast() 114 | } 115 | 116 | 117 | /// Removes the first occurence of a particular item. Finds it by value comparison using ==. O(n) 118 | /// Silently exits if no occurrence found. 119 | /// 120 | /// - parameter item: The item to remove the first occurrence of. 121 | public mutating func remove(_ item: T) { 122 | if let index = heap.firstIndex(of: item) { 123 | heap.swapAt(index, heap.count - 1) 124 | heap.removeLast() 125 | if index < heap.count { // if we removed the last item, nothing to swim 126 | swim(index) 127 | sink(index) 128 | } 129 | } 130 | } 131 | 132 | /// Removes all occurences of a particular item. Finds it by value comparison using ==. O(n^2) 133 | /// Silently exits if no occurrence found. 134 | /// 135 | /// - parameter item: The item to remove. 136 | public mutating func removeAll(_ item: T) { 137 | var lastCount = heap.count 138 | remove(item) 139 | while (heap.count < lastCount) { 140 | lastCount = heap.count 141 | remove(item) 142 | } 143 | } 144 | 145 | /// Get a look at the current highest priority item, without removing it. O(1) 146 | /// 147 | /// - returns: The element with the highest priority in the PriorityQueue, or nil if the PriorityQueue is empty. 148 | public func peek() -> T? { 149 | return heap.first 150 | } 151 | 152 | /// Eliminate all of the elements from the Priority Queue, optionally replacing the order. 153 | public mutating func clear() { 154 | heap.removeAll(keepingCapacity: false) 155 | } 156 | 157 | // Based on example from Sedgewick p 316 158 | private mutating func sink(_ index: Int) { 159 | var index = index 160 | while 2 * index + 1 < heap.count { 161 | 162 | var j = 2 * index + 1 163 | 164 | if j < (heap.count - 1) && ordered(heap[j], heap[j + 1]) { j += 1 } 165 | if !ordered(heap[index], heap[j]) { break } 166 | 167 | heap.swapAt(index, j) 168 | index = j 169 | } 170 | } 171 | 172 | /// Helper function for pop. 173 | /// 174 | /// Swaps the first and last elements, then sinks the first element. 175 | /// 176 | /// After executing this function, calling `heap.removeLast()` returns the popped element. 177 | /// - Parameter newCount: The number of elements in heap after the `pop()` operation is complete. 178 | private mutating func fastPop(newCount: Int) { 179 | var index = 0 180 | heap.withUnsafeMutableBufferPointer { bufferPointer in 181 | let _heap = bufferPointer.baseAddress! // guaranteed non-nil because count > 0 182 | swap(&_heap[0], &_heap[newCount]) 183 | while 2 * index + 1 < newCount { 184 | var j = 2 * index + 1 185 | if j < (newCount - 1) && ordered(_heap[j], _heap[j+1]) { j += 1 } 186 | if !ordered(_heap[index], _heap[j]) { return } 187 | swap(&_heap[index], &_heap[j]) 188 | index = j 189 | } 190 | } 191 | } 192 | 193 | // Based on example from Sedgewick p 316 194 | private mutating func swim(_ index: Int) { 195 | var index = index 196 | while index > 0 && ordered(heap[(index - 1) / 2], heap[index]) { 197 | heap.swapAt((index - 1) / 2, index) 198 | index = (index - 1) / 2 199 | } 200 | } 201 | } 202 | 203 | // MARK: - GeneratorType 204 | extension PriorityQueue: IteratorProtocol { 205 | 206 | public typealias Element = T 207 | mutating public func next() -> Element? { return pop() } 208 | } 209 | 210 | // MARK: - SequenceType 211 | extension PriorityQueue: Sequence { 212 | 213 | public typealias Iterator = PriorityQueue 214 | public func makeIterator() -> Iterator { return self } 215 | } 216 | 217 | // MARK: - CollectionType 218 | extension PriorityQueue: Collection { 219 | 220 | public typealias Index = Int 221 | 222 | public var startIndex: Int { return heap.startIndex } 223 | 224 | public var endIndex: Int { return heap.endIndex } 225 | 226 | /// Return the element at specified position in the heap (not the order). O(1) 227 | /// 228 | /// - Parameter position: the index of the element to retireve. 229 | /// **Must not be negative** 230 | /// and **must be less greater than ** 231 | /// `endindex`. 232 | /// 233 | /// - Returns: the element at the specified position in the heap. 234 | public subscript(position: Int) -> T { 235 | precondition( 236 | startIndex.. PriorityQueue.Index { 243 | return heap.index(after: i) 244 | } 245 | 246 | 247 | } 248 | 249 | // MARK: - CustomStringConvertible, CustomDebugStringConvertible 250 | extension PriorityQueue: CustomStringConvertible, CustomDebugStringConvertible { 251 | 252 | public var description: String { return heap.description } 253 | public var debugDescription: String { return heap.debugDescription } 254 | } 255 | -------------------------------------------------------------------------------- /SwiftPriorityQueue.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'SwiftPriorityQueue' 3 | s.version = '1.4.0' 4 | s.license = 'MIT' 5 | s.summary = 'A Generic Priority Queue in Pure Swift' 6 | s.homepage = 'https://github.com/davecom/SwiftPriorityQueue' 7 | s.social_media_url = 'https://twitter.com/davekopec' 8 | s.authors = { 'David Kopec' => 'david@oaksnow.com' } 9 | s.source = { :git => 'https://github.com/davecom/SwiftPriorityQueue.git', :tag => s.version } 10 | s.ios.deployment_target = '11.0' 11 | s.osx.deployment_target = '10.13' 12 | s.tvos.deployment_target = '11.0' 13 | s.swift_versions = ['5.0', '5.1', '5.2', '5.3', '5.4', '5.5', '5.6', '5.7'] 14 | s.source_files = 'Sources/SwiftPriorityQueue/SwiftPriorityQueue.swift' 15 | s.requires_arc = true 16 | end 17 | -------------------------------------------------------------------------------- /SwiftPriorityQueue.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 551E58BB1F770580007073B0 /* SwiftPriorityQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 551E58BA1F770580007073B0 /* SwiftPriorityQueue.swift */; }; 11 | 551E58BC1F770583007073B0 /* SwiftPriorityQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 551E58BA1F770580007073B0 /* SwiftPriorityQueue.swift */; }; 12 | 55296F0B2340508A007F53CC /* SwiftPriorityQueuePerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55296F0A2340508A007F53CC /* SwiftPriorityQueuePerformanceTests.swift */; }; 13 | 553948251AC6495600666559 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553948241AC6495600666559 /* AppDelegate.swift */; }; 14 | 553948271AC6495600666559 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 553948261AC6495600666559 /* Images.xcassets */; }; 15 | 5539482A1AC6495600666559 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 553948281AC6495600666559 /* MainMenu.xib */; }; 16 | 5564AFFB1AD509E8008CF6EF /* astar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5564AFFA1AD509E8008CF6EF /* astar.swift */; }; 17 | 5565CDFB2249BE24002664E3 /* SwiftPriorityQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5565CDFA2249BE24002664E3 /* SwiftPriorityQueueTests.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 553948301AC6495600666559 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 553948171AC6495600666559 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 5539481E1AC6495600666559; 26 | remoteInfo = SwiftPriorityQueue; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 551E58BA1F770580007073B0 /* SwiftPriorityQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftPriorityQueue.swift; path = Sources/SwiftPriorityQueue/SwiftPriorityQueue.swift; sourceTree = SOURCE_ROOT; }; 32 | 55296F0A2340508A007F53CC /* SwiftPriorityQueuePerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SwiftPriorityQueuePerformanceTests.swift; path = Tests/SwiftPriorityQueueTests/SwiftPriorityQueuePerformanceTests.swift; sourceTree = SOURCE_ROOT; }; 33 | 5539481F1AC6495600666559 /* SwiftPriorityQueue.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftPriorityQueue.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 553948231AC6495600666559 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 553948241AC6495600666559 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 36 | 553948261AC6495600666559 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 37 | 553948291AC6495600666559 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 38 | 5539482F1AC6495600666559 /* SwiftPriorityQueueTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftPriorityQueueTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 553948341AC6495600666559 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Info.plist; path = Tests/Info.plist; sourceTree = ""; }; 40 | 5564AFFA1AD509E8008CF6EF /* astar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = astar.swift; sourceTree = ""; }; 41 | 5565CDFA2249BE24002664E3 /* SwiftPriorityQueueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftPriorityQueueTests.swift; path = Tests/SwiftPriorityQueueTests/SwiftPriorityQueueTests.swift; sourceTree = SOURCE_ROOT; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 5539481C1AC6495600666559 /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | 5539482C1AC6495600666559 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXFrameworksBuildPhase section */ 60 | 61 | /* Begin PBXGroup section */ 62 | 553948161AC6495600666559 = { 63 | isa = PBXGroup; 64 | children = ( 65 | 553948211AC6495600666559 /* SwiftPriorityQueue */, 66 | 553948321AC6495600666559 /* SwiftPriorityQueueTests */, 67 | 553948201AC6495600666559 /* Products */, 68 | ); 69 | sourceTree = ""; 70 | }; 71 | 553948201AC6495600666559 /* Products */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 5539481F1AC6495600666559 /* SwiftPriorityQueue.app */, 75 | 5539482F1AC6495600666559 /* SwiftPriorityQueueTests.xctest */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | 553948211AC6495600666559 /* SwiftPriorityQueue */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 551E58BA1F770580007073B0 /* SwiftPriorityQueue.swift */, 84 | 553948241AC6495600666559 /* AppDelegate.swift */, 85 | 5564AFFA1AD509E8008CF6EF /* astar.swift */, 86 | 553948261AC6495600666559 /* Images.xcassets */, 87 | 553948281AC6495600666559 /* MainMenu.xib */, 88 | 553948221AC6495600666559 /* Supporting Files */, 89 | ); 90 | path = SwiftPriorityQueue; 91 | sourceTree = ""; 92 | }; 93 | 553948221AC6495600666559 /* Supporting Files */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 553948231AC6495600666559 /* Info.plist */, 97 | ); 98 | name = "Supporting Files"; 99 | sourceTree = ""; 100 | }; 101 | 553948321AC6495600666559 /* SwiftPriorityQueueTests */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 5565CDFA2249BE24002664E3 /* SwiftPriorityQueueTests.swift */, 105 | 55296F0A2340508A007F53CC /* SwiftPriorityQueuePerformanceTests.swift */, 106 | 553948331AC6495600666559 /* Supporting Files */, 107 | ); 108 | path = SwiftPriorityQueueTests; 109 | sourceTree = ""; 110 | }; 111 | 553948331AC6495600666559 /* Supporting Files */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 553948341AC6495600666559 /* Info.plist */, 115 | ); 116 | name = "Supporting Files"; 117 | sourceTree = ""; 118 | }; 119 | /* End PBXGroup section */ 120 | 121 | /* Begin PBXNativeTarget section */ 122 | 5539481E1AC6495600666559 /* SwiftPriorityQueue */ = { 123 | isa = PBXNativeTarget; 124 | buildConfigurationList = 553948391AC6495600666559 /* Build configuration list for PBXNativeTarget "SwiftPriorityQueue" */; 125 | buildPhases = ( 126 | 5539481B1AC6495600666559 /* Sources */, 127 | 5539481C1AC6495600666559 /* Frameworks */, 128 | 5539481D1AC6495600666559 /* Resources */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = SwiftPriorityQueue; 135 | productName = SwiftPriorityQueue; 136 | productReference = 5539481F1AC6495600666559 /* SwiftPriorityQueue.app */; 137 | productType = "com.apple.product-type.application"; 138 | }; 139 | 5539482E1AC6495600666559 /* SwiftPriorityQueueTests */ = { 140 | isa = PBXNativeTarget; 141 | buildConfigurationList = 5539483C1AC6495600666559 /* Build configuration list for PBXNativeTarget "SwiftPriorityQueueTests" */; 142 | buildPhases = ( 143 | 5539482B1AC6495600666559 /* Sources */, 144 | 5539482C1AC6495600666559 /* Frameworks */, 145 | 5539482D1AC6495600666559 /* Resources */, 146 | ); 147 | buildRules = ( 148 | ); 149 | dependencies = ( 150 | 553948311AC6495600666559 /* PBXTargetDependency */, 151 | ); 152 | name = SwiftPriorityQueueTests; 153 | productName = SwiftPriorityQueueTests; 154 | productReference = 5539482F1AC6495600666559 /* SwiftPriorityQueueTests.xctest */; 155 | productType = "com.apple.product-type.bundle.unit-test"; 156 | }; 157 | /* End PBXNativeTarget section */ 158 | 159 | /* Begin PBXProject section */ 160 | 553948171AC6495600666559 /* Project object */ = { 161 | isa = PBXProject; 162 | attributes = { 163 | LastSwiftMigration = 0710; 164 | LastSwiftUpdateCheck = 0710; 165 | LastUpgradeCheck = 1420; 166 | ORGANIZATIONNAME = "Oak Snow Consulting"; 167 | TargetAttributes = { 168 | 5539481E1AC6495600666559 = { 169 | CreatedOnToolsVersion = 6.2; 170 | LastSwiftMigration = 1020; 171 | }; 172 | 5539482E1AC6495600666559 = { 173 | CreatedOnToolsVersion = 6.2; 174 | LastSwiftMigration = 1020; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 5539481A1AC6495600666559 /* Build configuration list for PBXProject "SwiftPriorityQueue" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 553948161AC6495600666559; 187 | productRefGroup = 553948201AC6495600666559 /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 5539481E1AC6495600666559 /* SwiftPriorityQueue */, 192 | 5539482E1AC6495600666559 /* SwiftPriorityQueueTests */, 193 | ); 194 | }; 195 | /* End PBXProject section */ 196 | 197 | /* Begin PBXResourcesBuildPhase section */ 198 | 5539481D1AC6495600666559 /* Resources */ = { 199 | isa = PBXResourcesBuildPhase; 200 | buildActionMask = 2147483647; 201 | files = ( 202 | 553948271AC6495600666559 /* Images.xcassets in Resources */, 203 | 5539482A1AC6495600666559 /* MainMenu.xib in Resources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | 5539482D1AC6495600666559 /* Resources */ = { 208 | isa = PBXResourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | }; 214 | /* End PBXResourcesBuildPhase section */ 215 | 216 | /* Begin PBXSourcesBuildPhase section */ 217 | 5539481B1AC6495600666559 /* Sources */ = { 218 | isa = PBXSourcesBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | 551E58BB1F770580007073B0 /* SwiftPriorityQueue.swift in Sources */, 222 | 553948251AC6495600666559 /* AppDelegate.swift in Sources */, 223 | 5564AFFB1AD509E8008CF6EF /* astar.swift in Sources */, 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | }; 227 | 5539482B1AC6495600666559 /* Sources */ = { 228 | isa = PBXSourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | 551E58BC1F770583007073B0 /* SwiftPriorityQueue.swift in Sources */, 232 | 5565CDFB2249BE24002664E3 /* SwiftPriorityQueueTests.swift in Sources */, 233 | 55296F0B2340508A007F53CC /* SwiftPriorityQueuePerformanceTests.swift in Sources */, 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXSourcesBuildPhase section */ 238 | 239 | /* Begin PBXTargetDependency section */ 240 | 553948311AC6495600666559 /* PBXTargetDependency */ = { 241 | isa = PBXTargetDependency; 242 | target = 5539481E1AC6495600666559 /* SwiftPriorityQueue */; 243 | targetProxy = 553948301AC6495600666559 /* PBXContainerItemProxy */; 244 | }; 245 | /* End PBXTargetDependency section */ 246 | 247 | /* Begin PBXVariantGroup section */ 248 | 553948281AC6495600666559 /* MainMenu.xib */ = { 249 | isa = PBXVariantGroup; 250 | children = ( 251 | 553948291AC6495600666559 /* Base */, 252 | ); 253 | name = MainMenu.xib; 254 | sourceTree = ""; 255 | }; 256 | /* End PBXVariantGroup section */ 257 | 258 | /* Begin XCBuildConfiguration section */ 259 | 553948371AC6495600666559 /* Debug */ = { 260 | isa = XCBuildConfiguration; 261 | buildSettings = { 262 | ALWAYS_SEARCH_USER_PATHS = NO; 263 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 264 | CLANG_CXX_LIBRARY = "libc++"; 265 | CLANG_ENABLE_MODULES = YES; 266 | CLANG_ENABLE_OBJC_ARC = YES; 267 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 268 | CLANG_WARN_BOOL_CONVERSION = YES; 269 | CLANG_WARN_COMMA = YES; 270 | CLANG_WARN_CONSTANT_CONVERSION = YES; 271 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 272 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 273 | CLANG_WARN_EMPTY_BODY = YES; 274 | CLANG_WARN_ENUM_CONVERSION = YES; 275 | CLANG_WARN_INFINITE_RECURSION = YES; 276 | CLANG_WARN_INT_CONVERSION = YES; 277 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 278 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 279 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 281 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 282 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 283 | CLANG_WARN_STRICT_PROTOTYPES = YES; 284 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 285 | CLANG_WARN_UNREACHABLE_CODE = YES; 286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 287 | CODE_SIGN_IDENTITY = "-"; 288 | COPY_PHASE_STRIP = NO; 289 | DEAD_CODE_STRIPPING = YES; 290 | ENABLE_STRICT_OBJC_MSGSEND = YES; 291 | ENABLE_TESTABILITY = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu99; 293 | GCC_DYNAMIC_NO_PIC = NO; 294 | GCC_NO_COMMON_BLOCKS = YES; 295 | GCC_OPTIMIZATION_LEVEL = 0; 296 | GCC_PREPROCESSOR_DEFINITIONS = ( 297 | "DEBUG=1", 298 | "$(inherited)", 299 | ); 300 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 301 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 302 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 303 | GCC_WARN_UNDECLARED_SELECTOR = YES; 304 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 305 | GCC_WARN_UNUSED_FUNCTION = YES; 306 | GCC_WARN_UNUSED_VARIABLE = YES; 307 | MACOSX_DEPLOYMENT_TARGET = 10.13; 308 | MTL_ENABLE_DEBUG_INFO = YES; 309 | ONLY_ACTIVE_ARCH = YES; 310 | SDKROOT = macosx; 311 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 312 | }; 313 | name = Debug; 314 | }; 315 | 553948381AC6495600666559 /* Release */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 320 | CLANG_CXX_LIBRARY = "libc++"; 321 | CLANG_ENABLE_MODULES = YES; 322 | CLANG_ENABLE_OBJC_ARC = YES; 323 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 324 | CLANG_WARN_BOOL_CONVERSION = YES; 325 | CLANG_WARN_COMMA = YES; 326 | CLANG_WARN_CONSTANT_CONVERSION = YES; 327 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 328 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 329 | CLANG_WARN_EMPTY_BODY = YES; 330 | CLANG_WARN_ENUM_CONVERSION = YES; 331 | CLANG_WARN_INFINITE_RECURSION = YES; 332 | CLANG_WARN_INT_CONVERSION = YES; 333 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 334 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 335 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 336 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 337 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 338 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 339 | CLANG_WARN_STRICT_PROTOTYPES = YES; 340 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 341 | CLANG_WARN_UNREACHABLE_CODE = YES; 342 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 343 | CODE_SIGN_IDENTITY = "-"; 344 | COPY_PHASE_STRIP = NO; 345 | DEAD_CODE_STRIPPING = YES; 346 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 347 | ENABLE_NS_ASSERTIONS = NO; 348 | ENABLE_STRICT_OBJC_MSGSEND = YES; 349 | GCC_C_LANGUAGE_STANDARD = gnu99; 350 | GCC_NO_COMMON_BLOCKS = YES; 351 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 352 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 353 | GCC_WARN_UNDECLARED_SELECTOR = YES; 354 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 355 | GCC_WARN_UNUSED_FUNCTION = YES; 356 | GCC_WARN_UNUSED_VARIABLE = YES; 357 | MACOSX_DEPLOYMENT_TARGET = 10.13; 358 | MTL_ENABLE_DEBUG_INFO = NO; 359 | SDKROOT = macosx; 360 | }; 361 | name = Release; 362 | }; 363 | 5539483A1AC6495600666559 /* Debug */ = { 364 | isa = XCBuildConfiguration; 365 | buildSettings = { 366 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 367 | CODE_SIGN_IDENTITY = "-"; 368 | COMBINE_HIDPI_IMAGES = YES; 369 | DEAD_CODE_STRIPPING = YES; 370 | INFOPLIST_FILE = SwiftPriorityQueue/Info.plist; 371 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 372 | MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; 373 | PRODUCT_BUNDLE_IDENTIFIER = "com.oaksnow.$(PRODUCT_NAME:rfc1034identifier)"; 374 | PRODUCT_NAME = "$(TARGET_NAME)"; 375 | SWIFT_VERSION = 5.0; 376 | }; 377 | name = Debug; 378 | }; 379 | 5539483B1AC6495600666559 /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 383 | CODE_SIGN_IDENTITY = "-"; 384 | COMBINE_HIDPI_IMAGES = YES; 385 | DEAD_CODE_STRIPPING = YES; 386 | INFOPLIST_FILE = SwiftPriorityQueue/Info.plist; 387 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 388 | MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; 389 | PRODUCT_BUNDLE_IDENTIFIER = "com.oaksnow.$(PRODUCT_NAME:rfc1034identifier)"; 390 | PRODUCT_NAME = "$(TARGET_NAME)"; 391 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 392 | SWIFT_VERSION = 5.0; 393 | }; 394 | name = Release; 395 | }; 396 | 5539483D1AC6495600666559 /* Debug */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | COMBINE_HIDPI_IMAGES = YES; 400 | DEAD_CODE_STRIPPING = YES; 401 | FRAMEWORK_SEARCH_PATHS = ( 402 | "$(DEVELOPER_FRAMEWORKS_DIR)", 403 | "$(inherited)", 404 | ); 405 | GCC_PREPROCESSOR_DEFINITIONS = ( 406 | "DEBUG=1", 407 | "$(inherited)", 408 | ); 409 | INFOPLIST_FILE = Tests/Info.plist; 410 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 411 | MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; 412 | PRODUCT_BUNDLE_IDENTIFIER = "com.oaksnow.$(PRODUCT_NAME:rfc1034identifier)"; 413 | PRODUCT_NAME = "$(TARGET_NAME)"; 414 | SWIFT_VERSION = 5.0; 415 | }; 416 | name = Debug; 417 | }; 418 | 5539483E1AC6495600666559 /* Release */ = { 419 | isa = XCBuildConfiguration; 420 | buildSettings = { 421 | COMBINE_HIDPI_IMAGES = YES; 422 | DEAD_CODE_STRIPPING = YES; 423 | FRAMEWORK_SEARCH_PATHS = ( 424 | "$(DEVELOPER_FRAMEWORKS_DIR)", 425 | "$(inherited)", 426 | ); 427 | INFOPLIST_FILE = Tests/Info.plist; 428 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 429 | MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; 430 | PRODUCT_BUNDLE_IDENTIFIER = "com.oaksnow.$(PRODUCT_NAME:rfc1034identifier)"; 431 | PRODUCT_NAME = "$(TARGET_NAME)"; 432 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 433 | SWIFT_VERSION = 5.0; 434 | }; 435 | name = Release; 436 | }; 437 | /* End XCBuildConfiguration section */ 438 | 439 | /* Begin XCConfigurationList section */ 440 | 5539481A1AC6495600666559 /* Build configuration list for PBXProject "SwiftPriorityQueue" */ = { 441 | isa = XCConfigurationList; 442 | buildConfigurations = ( 443 | 553948371AC6495600666559 /* Debug */, 444 | 553948381AC6495600666559 /* Release */, 445 | ); 446 | defaultConfigurationIsVisible = 0; 447 | defaultConfigurationName = Release; 448 | }; 449 | 553948391AC6495600666559 /* Build configuration list for PBXNativeTarget "SwiftPriorityQueue" */ = { 450 | isa = XCConfigurationList; 451 | buildConfigurations = ( 452 | 5539483A1AC6495600666559 /* Debug */, 453 | 5539483B1AC6495600666559 /* Release */, 454 | ); 455 | defaultConfigurationIsVisible = 0; 456 | defaultConfigurationName = Release; 457 | }; 458 | 5539483C1AC6495600666559 /* Build configuration list for PBXNativeTarget "SwiftPriorityQueueTests" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 5539483D1AC6495600666559 /* Debug */, 462 | 5539483E1AC6495600666559 /* Release */, 463 | ); 464 | defaultConfigurationIsVisible = 0; 465 | defaultConfigurationName = Release; 466 | }; 467 | /* End XCConfigurationList section */ 468 | }; 469 | rootObject = 553948171AC6495600666559 /* Project object */; 470 | } 471 | -------------------------------------------------------------------------------- /SwiftPriorityQueue.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftPriorityQueue.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftPriorityQueue.xcodeproj/xcshareddata/xcbaselines/5539482E1AC6495600666559.xcbaseline/010A85AE-666B-4CDA-BF1D-B7258494F15A.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | SwiftPriorityQueuePerformanceTests 8 | 9 | testRemovePerformance() 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 0.267 15 | baselineIntegrationDisplayName 16 | Local Baseline 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SwiftPriorityQueue.xcodeproj/xcshareddata/xcbaselines/5539482E1AC6495600666559.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | 010A85AE-666B-4CDA-BF1D-B7258494F15A 8 | 9 | localComputer 10 | 11 | busSpeedInMHz 12 | 100 13 | cpuCount 14 | 1 15 | cpuKind 16 | Intel Core i7 17 | cpuSpeedInMHz 18 | 1700 19 | logicalCPUCoresPerPackage 20 | 4 21 | modelCode 22 | MacBookAir6,2 23 | physicalCPUCoresPerPackage 24 | 2 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | x86_64 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /SwiftPriorityQueue/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftPriorityQueue 4 | // 5 | // Copyright (c) 2015-2019 David Kopec 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | // This is an example of a maze search that uses astar via SwiftPriorityQueue 26 | 27 | import Cocoa 28 | 29 | // A Cell represents a grid location in the maze 30 | enum Cell { 31 | case empty 32 | case blocked 33 | case start 34 | case goal 35 | case path 36 | func color() -> CGColor { 37 | switch (self) { 38 | case .empty: return NSColor.white.cgColor 39 | case .blocked: return NSColor.black.cgColor 40 | case .start: return NSColor.green.cgColor 41 | case .goal: return NSColor.red.cgColor 42 | case .path: return NSColor.yellow.cgColor 43 | } 44 | } 45 | } 46 | 47 | // A point is a way to refer to the row and column of a cell 48 | struct Point: Hashable { 49 | let x: Int 50 | let y: Int 51 | } 52 | 53 | func == (lhs: Point, rhs: Point) -> Bool { 54 | return lhs.x == rhs.x && lhs.y == rhs.y 55 | } 56 | 57 | 58 | class MazeView: NSView { 59 | let NUM_ROWS: Int = 20 60 | let NUM_COLS: Int = 20 61 | var hasStart: Bool = false 62 | var start: Point = Point(x: -1, y: -1) 63 | var goal: Point = Point(x: -1, y: -1) 64 | var path: [Point] = [Point]() 65 | var position: [[Cell]] = [[Cell]]() 66 | var cellLayers:[[CALayer]] = [[CALayer]]() 67 | 68 | // initialize the cells 69 | override func awakeFromNib() { 70 | wantsLayer = true 71 | let width: CGFloat = self.bounds.size.width 72 | let height: CGFloat = self.bounds.size.height 73 | for i in 0.. Bool { 142 | if x == goal { 143 | return true 144 | } 145 | return false 146 | } 147 | 148 | func successors(_ p: Point) -> [Point] { //can't go on diagonals 149 | var ar: [Point] = [Point]() 150 | if (p.x + 1 < NUM_ROWS) && (position[p.x + 1][p.y] != .blocked) { 151 | ar.append(Point(x: p.x + 1, y: p.y)) 152 | } 153 | if (p.x - 1 >= 0) && (position[p.x - 1][p.y] != .blocked) { 154 | ar.append(Point(x: p.x - 1, y: p.y)) 155 | } 156 | if (p.y + 1 < NUM_COLS) && (position[p.x][p.y + 1] != .blocked) { 157 | ar.append(Point(x: p.x, y: p.y + 1)) 158 | } 159 | if (p.y - 1 >= 0) && (position[p.x][p.y - 1] != .blocked) { 160 | ar.append(Point(x: p.x, y: p.y - 1)) 161 | } 162 | 163 | return ar 164 | } 165 | 166 | func heuristic(_ p: Point) -> Float { // Manhattan distance 167 | let xdist = abs(p.x - goal.x) 168 | let ydist = abs(p.y - goal.y) 169 | return Float(xdist + ydist) 170 | } 171 | 172 | if let pathresult:[Point] = astar(start, goalTestFn: goalTest, successorFn: successors, heuristicFn: heuristic) { 173 | path = pathresult 174 | for p in path { 175 | if p != start && p != goal { 176 | let row = p.x 177 | let col = p.y 178 | position[row][col] = .path 179 | CATransaction.begin() 180 | CATransaction.setValue(NSNumber(value: 0.5), forKey: kCATransactionAnimationDuration) 181 | cellLayers[row][col].backgroundColor = position[row][col].color() 182 | CATransaction.commit() 183 | } 184 | } 185 | } 186 | } 187 | } 188 | } 189 | 190 | @NSApplicationMain 191 | class AppDelegate: NSObject, NSApplicationDelegate { 192 | 193 | @IBOutlet weak var window: NSWindow! 194 | 195 | 196 | func applicationDidFinishLaunching(_ aNotification: Notification) { 197 | // Insert code here to initialize your application 198 | } 199 | 200 | func applicationWillTerminate(_ aNotification: Notification) { 201 | // Insert code here to tear down your application 202 | } 203 | 204 | 205 | } 206 | 207 | -------------------------------------------------------------------------------- /SwiftPriorityQueue/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 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 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | Default 539 | 540 | 541 | 542 | 543 | 544 | 545 | Left to Right 546 | 547 | 548 | 549 | 550 | 551 | 552 | Right to Left 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | Default 564 | 565 | 566 | 567 | 568 | 569 | 570 | Left to Right 571 | 572 | 573 | 574 | 575 | 576 | 577 | Right to Left 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | -------------------------------------------------------------------------------- /SwiftPriorityQueue/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /SwiftPriorityQueue/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2015 Oak Snow Consulting. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /SwiftPriorityQueue/astar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // astar.swift 3 | // SwiftPriorityQueue 4 | // 5 | // Copyright (c) 2015-2019 David Kopec 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | // This is an example of astar that uses SwiftPriorityQueue 26 | 27 | class Node: Comparable, Hashable { 28 | let state: T 29 | let parent: Node? 30 | let cost: Float 31 | let heuristic: Float 32 | init(state: T, parent: Node?, cost: Float, heuristic: Float) { 33 | self.state = state 34 | self.parent = parent 35 | self.cost = cost 36 | self.heuristic = heuristic 37 | } 38 | 39 | func hash(into hasher: inout Hasher) { 40 | hasher.combine(state) 41 | hasher.combine(parent) 42 | hasher.combine(cost) 43 | hasher.combine(heuristic) 44 | } 45 | } 46 | 47 | func < (lhs: Node, rhs: Node) -> Bool { 48 | return (lhs.cost + lhs.heuristic) < (rhs.cost + rhs.heuristic) 49 | } 50 | 51 | func == (lhs: Node, rhs: Node) -> Bool { 52 | return lhs === rhs 53 | } 54 | 55 | /// Formulate a result path as an array from a goal node found in an astar search 56 | /// 57 | /// - parameter startNode: The goal node found from an astar search. 58 | /// - returns: An array containing the states to get from the start to the goal. 59 | func backtrack(_ goalNode: Node) -> [T] { 60 | var sol: [T] = [] 61 | var node = goalNode 62 | 63 | while (node.parent != nil) { 64 | sol.append(node.state) 65 | node = node.parent! 66 | } 67 | 68 | sol.append(node.state) 69 | 70 | return sol 71 | } 72 | 73 | /// Find the shortest path from a start state to a goal state. 74 | /// 75 | /// - parameter initialState: The state that we are starting from. 76 | /// - parameter goalTestFn: A function that determines whether a state is the goal state. 77 | /// - parameter successorFn: A function that finds the next states from a state. 78 | /// - parameter heuristicFn: A function that makes an underestimate of distance from a state to the goal. 79 | /// - returns: A path from the start state to a goal state as an array. 80 | func astar(_ initialState: T, goalTestFn: (T) -> Bool, successorFn: (T) -> [T], heuristicFn: (T) -> Float) -> [T]? { 81 | var frontier = PriorityQueue(ascending: true, startingValues: [Node(state: initialState, parent: nil, cost: 0, heuristic: heuristicFn(initialState))]) 82 | var explored = Dictionary() 83 | explored[initialState] = 0 84 | var nodesSearched: Int = 0 85 | 86 | while let currentNode = frontier.pop() { 87 | nodesSearched += 1 88 | // we know if there are still items, we can pop one 89 | let currentState = currentNode.state 90 | 91 | if goalTestFn(currentState) { 92 | print("Searched \(nodesSearched) nodes.") 93 | return backtrack(currentNode) 94 | } 95 | 96 | for child in successorFn(currentState) { 97 | let newcost = currentNode.cost + 1 //1 assumes a grid, there should be a cost function 98 | if (explored[child] == nil) || (explored[child]! > newcost) { 99 | explored[child] = newcost 100 | frontier.push(Node(state: child, parent: currentNode, cost: newcost, heuristic: heuristicFn(child))) 101 | } 102 | } 103 | } 104 | 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import SwiftPriorityQueueTests 3 | 4 | XCTMain([ 5 | testCase(SwiftPriorityQueueTests.allTests), 6 | testCase(SwiftPriorityQueuePerformanceTests.allTests), 7 | ]) 8 | -------------------------------------------------------------------------------- /Tests/SwiftPriorityQueueTests/SwiftPriorityQueuePerformanceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftPriorityQueuePerformanceTests.swift 3 | // SwiftPriorityQueue 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import XCTest 24 | @testable import SwiftPriorityQueue 25 | 26 | class SwiftPriorityQueuePerformanceTests: XCTestCase { 27 | 28 | func testBuildPerformance() { 29 | let input: [Int] = Array((0 ..< 100000)) 30 | measure { 31 | let _: PriorityQueue = PriorityQueue(ascending: true, startingValues: input) 32 | } 33 | } 34 | 35 | func testPopPerformance() { 36 | let original = PriorityQueue(ascending: true, startingValues: Array(0 ..< 100000)) 37 | measure { 38 | var pq = original 39 | for _ in 0 ..< 100000 { 40 | let _ = pq.pop() 41 | } 42 | } 43 | } 44 | 45 | func testPushPerformance() { 46 | measure { 47 | for i in 0 ..< 100000 { 48 | var pq = PriorityQueue(ascending: true, startingValues: []) 49 | pq.push(i) 50 | } 51 | } 52 | } 53 | 54 | func testRemovePerformance() { 55 | let original = PriorityQueue(ascending: true, startingValues: Array(0 ..< 10000)) 56 | measure { 57 | var pq = original 58 | for x in 0 ..< 100 { 59 | pq.remove(x * x) 60 | } 61 | } 62 | } 63 | 64 | static var allTests = [ 65 | ("testBuildPerformance", testBuildPerformance), 66 | ("testPopPerformance", testPopPerformance), 67 | ("testPushPerformance", testPushPerformance), 68 | ("testRemovePerformance", testRemovePerformance) 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /Tests/SwiftPriorityQueueTests/SwiftPriorityQueueTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftPriorityQueueTests.swift 3 | // SwiftPriorityQueue 4 | // 5 | // Copyright (c) 2015-2023 David Kopec 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import XCTest 26 | import Foundation 27 | @testable import SwiftPriorityQueue 28 | 29 | // based on gist https://gist.github.com/rymcol/48a505c2a1c874daea52a296a2687f5f 30 | // so we have something that sorta kinda looks like arc4random_uniform() on Linux 31 | #if os(Linux) 32 | import SwiftGlibc 33 | 34 | public func arc4random_uniform(_ max: UInt32) -> Int32 { 35 | return (SwiftGlibc.rand() % Int32(max-1)) 36 | } 37 | #endif 38 | 39 | class SwiftPriorityQueueTests: XCTestCase { 40 | 41 | // Not sure who this test is relevant to. The Priority Queue only ensures orders of pops, other orders should not be 42 | // depended upon 43 | // func testFastIteratingReturnsValuesInSameOrderOfIndexIteration() { 44 | // var pq = PriorityQueue(order: <, startingValues: [1, 2, 3, 4, 5]) 45 | // 46 | // var fastIterationValues = [Int]() 47 | // var iter = pq.makeIterator() 48 | // while let element = iter.next() { 49 | // fastIterationValues.append(element) 50 | // } 51 | // 52 | // var indexIterationValues = [Int]() 53 | // for i in pq.startIndex..(order: >, startingValues: [5, 4, 3, 2, 1]) 63 | // 64 | // iter = pq.makeIterator() 65 | // while let element = iter.next() { 66 | // fastIterationValues.append(element) 67 | // } 68 | // 69 | // var indexIterationValue = [Int]() 70 | // for i in pq.startIndex..(order: <, startingValues: [1, 2, 3, 4, 5]) 79 | var expectedHeap = pq.heap 80 | for i in pq.indices { 81 | let _ = pq[i] 82 | } 83 | XCTAssertEqual(pq.heap, expectedHeap) 84 | 85 | // Let's also test with the other sort: 86 | pq = PriorityQueue(order: >, startingValues: [5, 4, 3, 2, 1]) 87 | expectedHeap = pq.heap 88 | for i in pq.indices { 89 | let _ = pq[i] 90 | } 91 | XCTAssertEqual(pq.heap, expectedHeap) 92 | } 93 | 94 | func testCustomOrder() { 95 | let priorities = [0: 5000, 1: 4000, 2: 3000, 3: 2000, 4: 1000, 5: 0] 96 | var pq: PriorityQueue = PriorityQueue(order: { priorities[$0]! > priorities[$1]! }) 97 | for i in 0...5 { 98 | pq.push(i); 99 | } 100 | 101 | let expected: [Int] = [5, 4, 3, 2, 1, 0] 102 | var actual: [Int] = [] 103 | for j in pq { 104 | actual.append(j) 105 | } 106 | 107 | XCTAssertEqual(expected, actual) 108 | } 109 | 110 | func testBasic() { 111 | var pq: PriorityQueue = PriorityQueue() 112 | for i in 0..<10 { 113 | pq.push(i); 114 | } 115 | 116 | let expected: [Int] = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 117 | var actual: [Int] = [] 118 | for j in pq { 119 | actual.append(j) 120 | } 121 | 122 | XCTAssertEqual(expected, actual, "Basic 10 Integer Array Test Pass") 123 | } 124 | 125 | func testString() { 126 | var pq: PriorityQueue = PriorityQueue() 127 | var s = "a" 128 | while (s < "aaaaaa") { 129 | pq.push(s) 130 | s += "a" 131 | } 132 | 133 | let expected: [String] = ["aaaaa", "aaaa", "aaa", "aa", "a"] 134 | var actual: [String] = [] 135 | for i in pq { 136 | actual.append(i) 137 | } 138 | 139 | XCTAssertEqual(expected, actual, "Basic 5 String Array Test Pass") 140 | } 141 | 142 | func testSetEquiv() { 143 | for _ in 0..<100 { 144 | var s = Set((0..<(arc4random_uniform(100))).map { _ in arc4random_uniform(1000000) }) 145 | var q = PriorityQueue.init(startingValues: Array(s)) 146 | XCTAssertEqual(s.count, q.count, "Incorrect count with elements: " + s.description) 147 | while let se = s.max() { 148 | XCTAssertEqual(se, q.pop(), "Incorrect max item with elements: " + s.description) 149 | s.remove(se) 150 | } 151 | XCTAssert(q.isEmpty, "Is not empty. Still contains: " + q.description) 152 | } 153 | } 154 | 155 | func testClear() { 156 | var pq: PriorityQueue = PriorityQueue() 157 | for i in 0..<10 { 158 | pq.push(i); 159 | } 160 | pq.clear() 161 | XCTAssert(pq.isEmpty, "Is not empty. Still contains: " + pq.description) 162 | } 163 | 164 | func testPeek() { 165 | var pq: PriorityQueue = PriorityQueue() 166 | pq.push(1) 167 | pq.push(5) 168 | pq.push(3) 169 | XCTAssertEqual(pq.peek(), 5, "Peek didn't return top element: " + pq.description) 170 | } 171 | 172 | func testRemove() { 173 | var pq: PriorityQueue = PriorityQueue() 174 | for i in 0..<10 { 175 | pq.push(i); 176 | } 177 | 178 | pq.remove(4) 179 | pq.remove(7) 180 | 181 | let expected: [Int] = [9, 8, 6, 5, 3, 2, 1, 0] 182 | var actual: [Int] = [] 183 | for j in pq { 184 | actual.append(j) 185 | } 186 | 187 | XCTAssertEqual(expected, actual, "Trouble Removing 4 or 7") 188 | } 189 | 190 | func testRemoveAll() { 191 | var pq: PriorityQueue = PriorityQueue() 192 | for i in 0..<10 { 193 | pq.push(i); 194 | } 195 | 196 | pq.push(4) 197 | pq.push(7) 198 | pq.push(7) 199 | 200 | pq.remove(4) 201 | pq.removeAll(7) 202 | 203 | let expected: [Int] = [9, 8, 6, 5, 4, 3, 2, 1, 0] 204 | var actual: [Int] = [] 205 | for j in pq { 206 | actual.append(j) 207 | } 208 | 209 | XCTAssertEqual(expected, actual, "Trouble Removing 4 or all 7s") 210 | } 211 | 212 | func testRemoveLastInHeap() { 213 | var pq: PriorityQueue = PriorityQueue() 214 | pq.push(1) 215 | pq.push(2) 216 | 217 | pq.remove(1) 218 | 219 | let expected: [Int] = [2] 220 | var actual: [Int] = [] 221 | for j in pq { 222 | actual.append(j) 223 | } 224 | 225 | XCTAssertEqual(expected, actual) 226 | } 227 | 228 | func testPushWithLimitAscending() { 229 | var pq: PriorityQueue = PriorityQueue(ascending: false) 230 | let maxCount = 4 231 | XCTAssertNil(pq.push(4, maxCount: maxCount)) 232 | XCTAssert(Set([4]) == Set(pq)) 233 | XCTAssertNil(pq.push(5, maxCount: maxCount)) 234 | XCTAssert(Set([4, 5]) == Set(pq)) 235 | XCTAssertNil(pq.push(0, maxCount: maxCount)) 236 | XCTAssert(Set([0, 4, 5]) == Set(pq)) 237 | XCTAssertNil(pq.push(3, maxCount: maxCount)) 238 | XCTAssert(Set([0, 3, 4, 5]) == Set(pq)) 239 | XCTAssertEqual(pq.push(6, maxCount: maxCount), 6) // check first real discard 240 | XCTAssert(Set([0, 3, 4, 5]) == Set(pq)) 241 | XCTAssertEqual(5, pq.push(1, maxCount: maxCount)) // check second discard 242 | print(pq) 243 | XCTAssert(Set([0, 1, 3, 4]) == Set(pq)) 244 | } 245 | 246 | func testPushWithLimitDescending() { 247 | var pq: PriorityQueue = PriorityQueue(ascending: true) 248 | let maxCount = 4 249 | 250 | XCTAssertNil(pq.push(4, maxCount: maxCount)) 251 | XCTAssert(Set([4]) == Set(pq)) 252 | 253 | XCTAssertNil(pq.push(5, maxCount: maxCount)) 254 | XCTAssert(Set([4, 5]) == Set(pq)) 255 | 256 | XCTAssertNil(pq.push(2, maxCount: maxCount)) 257 | XCTAssert(Set([2, 4, 5]) == Set(pq)) 258 | 259 | XCTAssertNil(pq.push(3, maxCount: maxCount)) 260 | XCTAssert(Set([2, 3, 4, 5]) == Set(pq)) 261 | 262 | XCTAssertEqual(1, pq.push(1, maxCount: maxCount)) 263 | XCTAssert(Set([2, 3, 4, 5]) == Set(pq)) 264 | 265 | XCTAssertEqual(2, pq.push(6, maxCount: maxCount)) 266 | XCTAssert(Set([3, 4, 5, 6]) == Set(pq)) 267 | 268 | XCTAssertEqual(3, pq.push(6, maxCount: maxCount)) 269 | XCTAssert(Set([4, 5, 6, 6]) == Set(pq)) 270 | 271 | } 272 | 273 | static var allTests = [ 274 | ("testCustomOrder", testCustomOrder), 275 | ("testBasic", testBasic), 276 | ("testString", testString), 277 | ("testSetEquiv", testSetEquiv), 278 | ("testClear", testClear), 279 | ("testPeek", testPeek), 280 | ("testRemove", testRemove), 281 | ("testRemoveAll", testRemoveAll), 282 | ("testRemoveLastInHeap", testRemoveLastInHeap), 283 | ("testPushWithLimitAscending", testPushWithLimitAscending), 284 | ("testPushWithLimitDescending", testPushWithLimitDescending), 285 | ] 286 | } 287 | --------------------------------------------------------------------------------