Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XCTAssertUnorderedEqualSequences: Improve algorithmic complexity when elements are Hashable (Resolves #176) #177

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
72 changes: 63 additions & 9 deletions Tests/SwiftAlgorithmsTests/TestUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,22 @@ func XCTAssertEqualSequences<S1: Sequence, S2: Sequence>(
message(), file: file, line: line)
}

// Two sequences contains exactly the same element but not necessarily in the same order.
/// Asserts two sequences contain exactly the same elements but not necessarily
/// in the same order.
/// - Complexity: O(*n* * *m*) where *n* is the number of elements in the first
/// sequence and *m* is the number of elements in the second sequence
func XCTAssertUnorderedEqualSequences<S1: Sequence, S2: Sequence>(
_ expression1: @autoclosure () throws -> S1,
_ expression2: @autoclosure () throws -> S2,
_ message: @autoclosure () -> String = "",
file: StaticString = #file, line: UInt = #line
) rethrows where S1.Element: Equatable, S1.Element == S2.Element {
func fail(_ reason: String) {
let message = message()
XCTFail(message.isEmpty ? reason : "\(message) - \(reason)",
file: file, line: line)
}

var s1 = Array(try expression1())
var missing: [S1.Element] = []
for elt in try expression2() {
Expand All @@ -100,15 +110,59 @@ func XCTAssertUnorderedEqualSequences<S1: Sequence, S2: Sequence>(
s1.remove(at: idx)
mdznr marked this conversation as resolved.
Show resolved Hide resolved
}

XCTAssertTrue(
missing.isEmpty, "first sequence missing '\(missing)' elements from second sequence",
file: file, line: line
)
if !missing.isEmpty {
fail("first sequence missing '\(missing)' elements from second sequence")
}

if !s1.isEmpty {
fail("first sequence contains \(s1) missing from second sequence")
}
}

XCTAssertTrue(
s1.isEmpty, "first sequence contains \(s1) missing from second sequence",
file: file, line: line
)
/// Asserts two sequences contain exactly the same elements but not necessarily
/// in the same order.
/// - Complexity: O(*n* + *m*) where *n* is the number of elements in the first
/// sequence and *m* is the number of elements in the second sequence
func XCTAssertUnorderedEqualSequences<S1: Sequence, S2: Sequence>(
_ expression1: @autoclosure () throws -> S1,
_ expression2: @autoclosure () throws -> S2,
_ message: @autoclosure () -> String = "",
file: StaticString = #file, line: UInt = #line
) rethrows where S1.Element: Hashable, S1.Element == S2.Element {
func fail(_ reason: String) {
let message = message()
XCTFail(message.isEmpty ? reason : "\(message) - \(reason)",
file: file, line: line)
}

// Map the elements of the first sequence to the number of times it appeared
// in the sequence
var s1 = [S1.Element: Int](try expression1().lazy.map({
($0, 1)
}), uniquingKeysWith: {
$0 + $1
})

var missing: [S1.Element] = []
for elt in try expression2() {
if let count = s1[elt] {
if count > 1 {
s1[elt] = (count - 1)
} else {
s1[elt] = nil
}
} else {
missing.append(elt)
}
}

if !missing.isEmpty {
fail("first sequence missing '\(missing)' elements from second sequence")
}

if !s1.isEmpty {
fail("second sequence missing one or more occurrences of '\(s1.keys)' elements from first sequence")
}
}

func XCTAssertEqualSequences<S1: Sequence, S2: Sequence>(
Expand Down