diff --git a/Benchmarks/Benchmarks/HeapBenchmarks.swift b/Benchmarks/Benchmarks/HeapBenchmarks.swift index e0489ebdf..2bff32ab2 100644 --- a/Benchmarks/Benchmarks/HeapBenchmarks.swift +++ b/Benchmarks/Benchmarks/HeapBenchmarks.swift @@ -11,7 +11,6 @@ import CollectionsBenchmark import PriorityQueueModule -import CppBenchmarks extension Benchmark { public mutating func addHeapBenchmarks() { @@ -33,12 +32,12 @@ extension Benchmark { title: "Heap insert", input: [Int].self ) { input in - var queue = Heap() + var heap = Heap() for i in input { - queue.insert(i) + heap.insert(i) } - precondition(queue.count == input.count) - blackHole(queue) + precondition(heap.count == input.count) + blackHole(heap) } self.add( @@ -46,10 +45,10 @@ extension Benchmark { input: ([Int], [Int]).self ) { (existing, new) in return { timer in - var queue = Heap(existing) - queue.insert(contentsOf: new) - precondition(queue.count == existing.count + new.count) - blackHole(queue) + var heap = Heap(existing) + heap.insert(contentsOf: new) + precondition(heap.count == existing.count + new.count) + blackHole(heap) } } @@ -58,14 +57,14 @@ extension Benchmark { input: [Int].self ) { input in return { timer in - var queue = Heap(input) + var heap = Heap(input) timer.measure { - while let max = queue.popMax() { + while let max = heap.popMax() { blackHole(max) } } - precondition(queue.isEmpty) - blackHole(queue) + precondition(heap.isEmpty) + blackHole(heap) } } @@ -74,15 +73,80 @@ extension Benchmark { input: [Int].self ) { input in return { timer in - var queue = Heap(input) + var heap = Heap(input) timer.measure { - while let min = queue.popMin() { + while let min = heap.popMin() { blackHole(min) } } - precondition(queue.isEmpty) - blackHole(queue) + precondition(heap.isEmpty) + blackHole(heap) } } + + // MARK: Small Struct Benchmarks + + self.addSimple( + title: "Heap insert", + input: [Int].self + ) { input in + var heap = Heap() + for i in input { + heap.insert(HeapTask(priority: i)) + } + precondition(heap.count == input.count) + blackHole(heap) + } + self.add( + title: "Heap popMax", + input: [Int].self + ) { input in + return { timer in + var heap = Heap(input.map { HeapTask(priority: $0) }) + timer.measure { + while let max = heap.popMax() { + blackHole(max) + } + } + precondition(heap.isEmpty) + blackHole(heap) + } + } + + self.add( + title: "Heap popMin", + input: [Int].self + ) { input in + return { timer in + var heap = Heap(input.map { HeapTask(priority: $0) }) + timer.measure { + while let min = heap.popMin() { + blackHole(min) + } + } + precondition(heap.isEmpty) + blackHole(heap) + } + } + } + + struct HeapTask: Comparable { + let name: String + let priority: Int + let work: () -> Void + + init(name: String = "", priority: Int, work: @escaping () -> Void = {}) { + self.name = name + self.priority = priority + self.work = work + } + + static func < (lhs: Self, rhs: Self) -> Bool { + lhs.priority < rhs.priority + } + + static func == (lhs: Self, rhs: Self) -> Bool { + lhs.priority == rhs.priority + } } } diff --git a/Benchmarks/Benchmarks/Library.json b/Benchmarks/Benchmarks/Library.json index 5064107dd..7cc557ca2 100644 --- a/Benchmarks/Benchmarks/Library.json +++ b/Benchmarks/Benchmarks/Library.json @@ -802,7 +802,7 @@ ] } ] - }, + } ] }, { diff --git a/Benchmarks/Benchmarks/PriorityQueueBenchmarks.swift b/Benchmarks/Benchmarks/PriorityQueueBenchmarks.swift new file mode 100644 index 000000000..31c4d2c8a --- /dev/null +++ b/Benchmarks/Benchmarks/PriorityQueueBenchmarks.swift @@ -0,0 +1,121 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +import CollectionsBenchmark +import PriorityQueueModule + +extension Benchmark { + public mutating func addPriorityQueueBenchmarks() { + self.addSimple( + title: "PriorityQueue insert", + input: [Int].self + ) { input in + var queue = PriorityQueue() + for i in input { + queue.insert(i, priority: i) + } + precondition(queue.count == input.count) + blackHole(queue) + } + + self.add( + title: "PriorityQueue popMax", + input: [Int].self + ) { input in + return { timer in + var queue = PriorityQueue(input.map({ ($0, $0) })) + timer.measure { + while let max = queue.popMax() { + blackHole(max) + } + } + precondition(queue.isEmpty) + blackHole(queue) + } + } + + self.add( + title: "PriorityQueue popMin", + input: [Int].self + ) { input in + return { timer in + var queue = PriorityQueue(input.map({ ($0, $0) })) + timer.measure { + while let min = queue.popMin() { + blackHole(min) + } + } + precondition(queue.isEmpty) + blackHole(queue) + } + } + + // MARK: Small Struct Benchmarks + + struct Task { + let name: String + let work: () -> Void + + init(name: String = "", work: @escaping () -> Void = {}) { + self.name = name + self.work = work + } + } + + self.addSimple( + title: "PriorityQueue insert", + input: [Int].self + ) { input in + var queue = PriorityQueue() + for i in input { + queue.insert(Task(name: "Test", work: {}), priority: i) + } + precondition(queue.count == input.count) + blackHole(queue) + } + + self.add( + title: "PriorityQueue popMax", + input: [Int].self + ) { input in + return { timer in + var queue = PriorityQueue( + input.map({ (Task(name: $0.description), $0) }) + ) + timer.measure { + while let max = queue.popMax() { + blackHole(max) + } + } + precondition(queue.isEmpty) + blackHole(queue) + } + } + + self.add( + title: "PriorityQueue popMin", + input: [Int].self + ) { input in + return { timer in + var queue = PriorityQueue( + input.map({ (Task(name: $0.description), $0) }) + ) + timer.measure { + while let min = queue.popMin() { + blackHole(min) + } + } + precondition(queue.isEmpty) + blackHole(queue) + } + } + } +} diff --git a/Benchmarks/Libraries/PriorityQueue.json b/Benchmarks/Libraries/PriorityQueue.json new file mode 100644 index 000000000..2e8b00cb4 --- /dev/null +++ b/Benchmarks/Libraries/PriorityQueue.json @@ -0,0 +1,64 @@ +{ + "kind": "group", + "title": "Priority Queue Benchmarks", + "directory": "Results", + "contents": [ + { + "kind": "group", + "title": "PriorityQueue Operations", + "contents": [ + { + "kind": "chart", + "title": "operations", + "tasks": [ + "PriorityQueue insert", + "PriorityQueue popMax", + "PriorityQueue popMin" + ] + }, + { + "kind": "chart", + "title": "insert", + "tasks": [ + "PriorityQueue insert", + ] + }, + { + "kind": "chart", + "title": "remove", + "tasks": [ + "PriorityQueue popMax", + "PriorityQueue popMin" + ] + } + ] + }, + { + "kind": "group", + "title": "PriorityQueue vs Heap", + "directory": "PriorityQueue + Heap", + "contents": [ + { + "kind": "chart", + "title": "insert", + "tasks": [ + "PriorityQueue insert", + "PriorityQueue insert", + "Heap insert", + "Heap insert" + ] + }, + { + "kind": "chart", + "title": "pop", + "tasks": [ + "PriorityQueue popMin", + "PriorityQueue popMax", + "Heap popMin", + "Heap popMax" + ] + }, + ] + } + ] +} diff --git a/Benchmarks/benchmark-tool/main.swift b/Benchmarks/benchmark-tool/main.swift index d2096d384..0edbff46e 100644 --- a/Benchmarks/benchmark-tool/main.swift +++ b/Benchmarks/benchmark-tool/main.swift @@ -20,6 +20,7 @@ benchmark.addDequeBenchmarks() benchmark.addOrderedSetBenchmarks() benchmark.addOrderedDictionaryBenchmarks() benchmark.addHeapBenchmarks() +benchmark.addPriorityQueueBenchmarks() benchmark.addCppBenchmarks() #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) benchmark.addFoundationBenchmarks() diff --git a/Documentation/PriorityQueue.md b/Documentation/PriorityQueue.md new file mode 100644 index 000000000..8729e4cdb --- /dev/null +++ b/Documentation/PriorityQueue.md @@ -0,0 +1,74 @@ +# PriorityQueue + +A double-ended queue where elements are arranged by their priority. + +## Declaration + +```swift +import PriorityQueueModule + +public struct PriorityQueue +``` + +## Overview + +[Priority queues](https://en.wikipedia.org/wiki/Priority_queue) are useful data structures that can be leveraged across a variety of applications (sorting algorithms, graph algorithms, network clients, task managers, etc). + +This implementation is built on top of the [`Heap`](Documentation/Heap.md), which provides performant lookups (`O(1)`) of the lowest- and highest-priority elements as well as insertion and removal (`O(log n)`). The main difference between `Heap` and `PriorityQueue` is that the latter separates the value from the comparable priority of it. This is useful in cases where a type doesn't conform to `Comparable` directly but it may have a property that does — e.g. `Task.priority`. `PriorityQueue` also keeps track of insertion order, so dequeueing of elements with the same priority happens in FIFO order. + +## Implementation Details + +We define a small `_Element` struct to hold the `Value`, `Priority`, and insertion order of an element. This is the type that is stored in the underlying `Heap`. + +```swift +public struct PriorityQueue { + public struct _Element: Comparable { + let value: Value + let priority: Priority + let insertionCounter: UInt64 + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.priority == rhs.priority + } + + public static func < (lhs: Self, rhs: Self) -> Bool { + if lhs.priority < rhs.priority { + return true + } else if lhs.priority == rhs.priority { + return lhs.insertionCounter < rhs.insertionCounter + } else { + return false + } + } + } + + ... + internal var _base: Heap<_Element> + ... +``` + +All of the querying and removal functions wrap those on `Heap` and simply return the `Value` in the `_Element`. + +### Insertion + +Insertion is a little different, as the priority of the element to insert also needs to be passed in: + +```swift +public mutating func insert(_ value: Value, priority: Priority) +``` + +In cases where the priority already exists on the `Value` inserted (e.g. `Task.priority`) we incur a small space penalty (as the priority would be stored in both places). Storing the priority separately does help prevent mutations that would otherwise invalidate the heap property of the storage in cases where the `Value` being stored is a reference type: + +```swift +class Task { + var priority: Int + var work: () -> Void +} + +let task = Task(priority: 10, work: { ... }) +queue.insert(task, priority: task.priority) + +task.priority = 100 // This is fine, as changing it doesn't matter to the PriorityQueue +``` + +We keep track of the number of insertions that have happened in the `PriorityQueue` and increment it whenever `insert(_:priority:)` is called. This allows us to dequeue elements in FIFO order. diff --git a/README.md b/README.md index 712fb3ea1..fa162a666 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,14 @@ The package currently provides the following implementations: The following data structures are currently being worked on but they aren't ready for inclusion in a tagged release: -- [`Heap`][Heap] and [`PriorityQueue`](https://github.com/apple/swift-collections/pull/51), min-max heaps backed by an array. +- [`Heap`][Heap] and [`PriorityQueue`][PriorityQueue], min-max heaps backed by an array. - [`SortedSet` and `SortedDictionary`](https://github.com/apple/swift-collections/pull/65), sorted collections backed by in-memory persistent b-trees. - [`HashSet` and `HashMap`](https://github.com/apple/swift-collections/pull/31), persistent hashed collections implemented as Compressed Hash-Array Mapped Prefix-Trees (CHAMP). - [`BitArray` and `BitSet`](https://github.com/apple/swift-collections/pull/83), dynamic bit vectors. - [`SparseSet`](https://github.com/apple/swift-collections/pull/80), a constant time set construct, trading off memory for speed. [Heap]: Documentation/Heap.md +[PriorityQueue]: Documentation/PriorityQueue.md Swift Collections uses the same modularization approach as [**Swift Numerics**](https://github.com/apple/swift-numerics): it provides a standalone module for each thematic group of data structures it implements. For instance, if you only need a double-ended queue type, you can pull in only that by importing `DequeModule`. `OrderedSet` and `OrderedDictionary` share much of the same underlying implementation, so they are provided by a single module, called `OrderedCollections`. However, there is also a top-level `Collections` module that gives you every collection type with a single import statement: diff --git a/Sources/PriorityQueueModule/CMakeLists.txt b/Sources/PriorityQueueModule/CMakeLists.txt index 14319cf3a..2c01b2789 100644 --- a/Sources/PriorityQueueModule/CMakeLists.txt +++ b/Sources/PriorityQueueModule/CMakeLists.txt @@ -12,7 +12,10 @@ add_library(PriorityQueueModule Heap.swift Heap+ExpressibleByArrayLiteral.swift Heap+Invariants.swift - Heap+UnsafeHandle.swift) + Heap+UnsafeHandle.swift + PriorityQueue.swift + PriorityQueue+ExpressibleByArrayLiteral.swift + PriorityQueue+ExpressibleByDictionaryLiteral.swift) set_target_properties(PriorityQueueModule PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY}) diff --git a/Sources/PriorityQueueModule/PriorityQueue+ExpressibleByArrayLiteral.swift b/Sources/PriorityQueueModule/PriorityQueue+ExpressibleByArrayLiteral.swift new file mode 100644 index 000000000..c32056f97 --- /dev/null +++ b/Sources/PriorityQueueModule/PriorityQueue+ExpressibleByArrayLiteral.swift @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +extension PriorityQueue: ExpressibleByArrayLiteral { + /// Creates a new priority queue from the contents of an array literal. + /// + /// **Do not call this initializer directly.** It is used by the compiler when + /// you use an array literal. Instead, create a new priority queue using an + /// array literal as its value by enclosing a comma-separated list of values + /// in square brackets. You can use an array literal anywhere a priority queue + /// is expected by the type context. + /// + /// - Parameter elements: A variadic list of tuples containing the element + /// and its priority (in that order). + @inlinable + public init(arrayLiteral elements: Pair...) { + self.init(elements) + } +} diff --git a/Sources/PriorityQueueModule/PriorityQueue+ExpressibleByDictionaryLiteral.swift b/Sources/PriorityQueueModule/PriorityQueue+ExpressibleByDictionaryLiteral.swift new file mode 100644 index 000000000..9bac5c79c --- /dev/null +++ b/Sources/PriorityQueueModule/PriorityQueue+ExpressibleByDictionaryLiteral.swift @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +extension PriorityQueue: ExpressibleByDictionaryLiteral { + /// Creates a new priority queue from the contents of a dictionary literal. + /// + /// **Do not call this initializer directly.** It is used by the compiler when + /// you use a dictionary literal. Instead, create a new queue using a + /// dictionary literal as its value by enclosing a comma-separated list of + /// values in square brackets. You can use a dictionary literal anywhere a + /// priority queue is expected by the type context. + /// + /// - Parameter elements: A variadic list of element-priority pairs for the new + /// queue. + @inlinable + public init(dictionaryLiteral elements: (Value, Priority)...) { + self.init(elements) + } +} diff --git a/Sources/PriorityQueueModule/PriorityQueue.swift b/Sources/PriorityQueueModule/PriorityQueue.swift new file mode 100644 index 000000000..0a57d4ecb --- /dev/null +++ b/Sources/PriorityQueueModule/PriorityQueue.swift @@ -0,0 +1,168 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +/// A double-ended priority queue built on top of `Heap`. +public struct PriorityQueue { + public typealias Pair = (value: Value, priority: Priority) + + @usableFromInline + struct _Element: Comparable { + @usableFromInline let value: Value + let priority: Priority + let insertionCounter: UInt64 + + @usableFromInline + init(value: Value, priority: Priority, insertionCounter: UInt64) { + self.value = value + self.priority = priority + self.insertionCounter = insertionCounter + } + + @usableFromInline + static func == (lhs: Self, rhs: Self) -> Bool { + lhs.priority == rhs.priority + } + + @usableFromInline + static func < (lhs: Self, rhs: Self) -> Bool { + if lhs.priority < rhs.priority { + return true + } else if lhs.priority == rhs.priority { + return lhs.insertionCounter < rhs.insertionCounter + } else { + return false + } + } + } + + @usableFromInline + internal var _base: Heap<_Element> + + @usableFromInline + internal var _insertionCounter: UInt64 = 0 + + /// A Boolean value indicating whether or not the queue is empty. + /// + /// - Complexity: O(1) + @inlinable + public var isEmpty: Bool { + _base.isEmpty + } + + /// The number of items in the queue. + /// + /// - Complexity: O(1) + @inlinable + public var count: Int { + _base.count + } + + /// Creates an empty queue. + @inlinable + public init() { + _base = Heap() + } + + // MARK: - + + /// Inserts the given item into the queue. + /// + /// - Complexity: O(log `count`) + @inlinable + public mutating func insert(_ value: Value, priority: Priority) { + defer { _insertionCounter += 1 } + + let pair = _Element( + value: value, + priority: priority, + insertionCounter: _insertionCounter + ) + + _base.insert(pair) + } + + /// Returns the item with the lowest priority, if available. + /// + /// - Complexity: O(1) + @inlinable + public func min() -> Value? { + _base.min()?.value + } + + /// Returns the item with the highest priority, if available. + /// + /// - Complexity: O(1) + @inlinable + public func max() -> Value? { + _base.max()?.value + } + + /// Removes and returns the item with the lowest priority, if available. + /// + /// - Complexity: O(log `count`) + @inlinable + public mutating func popMin() -> Value? { + _base.popMin()?.value + } + + /// Removes and returns the item with the highest priority, if available. + /// + /// - Complexity: O(log `count`) + @inlinable + public mutating func popMax() -> Value? { + _base.popMax()?.value + } + + /// Removes and returns the element with the lowest priority. + /// + /// The queue *must not* be empty. + /// + /// - Complexity: O(log `count`) + @inlinable + public mutating func removeMin() -> Value { + _base.removeMin().value + } + + /// Removes and returns the element with the highest priority. + /// + /// The queue *must not* be empty. + /// + /// - Complexity: O(log `count`) + @inlinable + public mutating func removeMax() -> Value { + _base.removeMax().value + } +} + +// MARK: - + +extension PriorityQueue { + /// Initializes a queue from a sequence. + /// + /// - Complexity: O(n), where `n` is the length of `elements`. + @inlinable + public init(_ elements: S) where S.Element == Pair { + _base = Heap( + elements + .enumerated() + .map({ + _Element( + value: $0.element.value, + priority: $0.element.priority, + insertionCounter: UInt64($0.offset) + ) + }) + ) + _insertionCounter = UInt64(_base.count) + } +} diff --git a/Tests/PriorityQueueTests/PriorityQueueTests.swift b/Tests/PriorityQueueTests/PriorityQueueTests.swift new file mode 100644 index 000000000..68db3c44d --- /dev/null +++ b/Tests/PriorityQueueTests/PriorityQueueTests.swift @@ -0,0 +1,150 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Collections open source project +// +// Copyright (c) 2021 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 +// +//===----------------------------------------------------------------------===// + +import XCTest +@testable import PriorityQueueModule + +final class PriorityQueueTests: XCTestCase { + func test_isEmpty() { + var queue = PriorityQueue() + XCTAssertTrue(queue.isEmpty) + + queue.insert("Hello", priority: 0.1) + XCTAssertFalse(queue.isEmpty) + + let _ = queue.popMin() + XCTAssertTrue(queue.isEmpty) + } + + func test_count() { + var queue = PriorityQueue() + XCTAssertEqual(queue.count, 0) + + queue.insert("Hello", priority: 0.1) + XCTAssertEqual(queue.count, 1) + queue.insert("World", priority: 0.5) + XCTAssertEqual(queue.count, 2) + + let _ = queue.popMax() + XCTAssertEqual(queue.count, 1) + } + + func test_min() { + var queue = PriorityQueue() + XCTAssertNil(queue.min()) + + queue.insert("Medium", priority: 5) + XCTAssertEqual(queue.min(), "Medium") + + queue.insert("High", priority: 10) + XCTAssertEqual(queue.min(), "Medium") + + queue.insert("Low", priority: 1) + XCTAssertEqual(queue.min(), "Low") + } + + func test_max() { + var queue = PriorityQueue() + XCTAssertNil(queue.max()) + + queue.insert("Medium", priority: 5) + XCTAssertEqual(queue.max(), "Medium") + + queue.insert("Low", priority: 1) + XCTAssertEqual(queue.max(), "Medium") + + queue.insert("High", priority: 10) + XCTAssertEqual(queue.max(), "High") + } + + func test_popMin() { + var queue = PriorityQueue() + XCTAssertNil(queue.popMin()) + + queue.insert("Low", priority: 1) + queue.insert("High", priority: 10) + queue.insert("Medium", priority: 5) + + XCTAssertEqual(queue.popMin(), "Low") + XCTAssertEqual(queue.popMin(), "Medium") + XCTAssertEqual(queue.popMin(), "High") + XCTAssertNil(queue.popMin()) + } + + func test_popMax() { + var queue = PriorityQueue() + XCTAssertNil(queue.popMax()) + + queue.insert("Low", priority: 1) + queue.insert("High", priority: 10) + queue.insert("Medium", priority: 5) + + XCTAssertEqual(queue.popMax(), "High") + XCTAssertEqual(queue.popMax(), "Medium") + XCTAssertEqual(queue.popMax(), "Low") + XCTAssertNil(queue.popMax()) + } + + // MARK: - + + func test_elementsWithEqualPriorityDequeuedInFIFOOrder() { + var queue = PriorityQueue() + + queue.insert("Foo 0", priority: 0) + queue.insert("Foo 1", priority: 1) + queue.insert("Bar 0", priority: 0) + queue.insert("Bar 1", priority: 1) + queue.insert("Baz 0", priority: 0) + queue.insert("Baz 1", priority: 1) + + let ordered = Array(sequence(state: queue, next: { $0.popMin() })) + + XCTAssertEqual( + ordered, + ["Foo 0", "Bar 0", "Baz 0", "Foo 1", "Bar 1", "Baz 1"] + ) + } + + // MARK: - Initializers + + func test_initializer_fromSequence() { + let queue = PriorityQueue( + (1...).prefix(20).map({ ($0.description, $0) }) + ) + XCTAssertEqual(queue.count, 20) + } + + func test_initializer_fromArrayLiteral() { + var queue: PriorityQueue = [ + ("One", 1), ("Three", 3), ("Five", 5), ("Seven", 7), ("Nine", 9) + ] + XCTAssertEqual(queue.count, 5) + + XCTAssertEqual(queue.popMax(), "Nine") + XCTAssertEqual(queue.popMax(), "Seven") + XCTAssertEqual(queue.popMax(), "Five") + XCTAssertEqual(queue.popMax(), "Three") + XCTAssertEqual(queue.popMax(), "One") + } + + func test_initializer_fromDictionaryLiteral() { + var queue: PriorityQueue = [ + "Urgent": 100, + "Low": 1, + "Medium": 40, + "High": 60 + ] + XCTAssertEqual(queue.count, 4) + + XCTAssertEqual(queue.popMax(), "Urgent") + XCTAssertEqual(queue.popMin(), "Low") + } +}