diff --git a/Sources/Algorithms/Product.swift b/Sources/Algorithms/Product.swift index 5ab05091..3f6d40ca 100644 --- a/Sources/Algorithms/Product.swift +++ b/Sources/Algorithms/Product.swift @@ -29,6 +29,15 @@ public struct Product2Sequence { extension Product2Sequence: Sequence { public typealias Element = (Base1.Element, Base2.Element) + public var underestimatedCount: Int { + // Watch out if at least one source doesn't actually implement their + // `underestimatedCount` in constant time + // (which `Collection` can permit, unlike `Sequence`). + let (product, overflow) = base1.underestimatedCount + .multipliedReportingOverflow(by: base2.underestimatedCount) + return overflow ? .max : product + } + /// The iterator for a `Product2Sequence` sequence. public struct Iterator: IteratorProtocol { @usableFromInline diff --git a/Tests/SwiftAlgorithmsTests/ProductTests.swift b/Tests/SwiftAlgorithmsTests/ProductTests.swift index 6dd487bb..1ca7a68f 100644 --- a/Tests/SwiftAlgorithmsTests/ProductTests.swift +++ b/Tests/SwiftAlgorithmsTests/ProductTests.swift @@ -37,6 +37,13 @@ final class ProductTests: XCTestCase { func testProductDistanceFromTo() { let p = product([1, 2], "abc") XCTAssertEqual(p.distance(from: p.startIndex, to: p.endIndex), 6) + XCTAssertLessThanOrEqual(p.underestimatedCount, 6) + } + + func testOverflowingUnderestimatedCount() { + let p = product(repeatElement(true, count: .max / 2), + repeatElement(false, count: .max / 2)) + XCTAssertLessThanOrEqual(p.underestimatedCount, .max) } func testProductIndexTraversals() {