Skip to content

[4.2] [stdlib] Update complexity docs for seq/collection algorithms #18247

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 43 additions & 33 deletions stdlib/public/core/Arrays.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -619,24 +619,25 @@ extension ${Self}: RandomAccessCollection, MutableCollection {
/// print(numbers[i])
/// // Prints "50"
///
/// The value passed as `n` must not offset `i` beyond the bounds of the
/// collection.
/// The value passed as `distance` must not offset `i` beyond the bounds of
/// the collection.
///
/// - Parameters:
/// - i: A valid index of the array.
/// - n: The distance to offset `i`.
/// - Returns: An index offset by `n` from the index `i`. If `n` is positive,
/// this is the same value as the result of `n` calls to `index(after:)`.
/// If `n` is negative, this is the same value as the result of `-n` calls
/// to `index(before:)`.
@inlinable
public func index(_ i: Int, offsetBy n: Int) -> Int {
/// - distance: The distance to offset `i`.
/// - Returns: An index offset by `distance` from the index `i`. If
/// `distance` is positive, this is the same value as the result of
/// `distance` calls to `index(after:)`. If `distance` is negative, this
/// is the same value as the result of `abs(distance)` calls to
/// `index(before:)`.
@inlinable
public func index(_ i: Int, offsetBy distance: Int) -> Int {
// NOTE: this is a manual specialization of index movement for a Strideable
// index that is required for Array performance. The optimizer is not
// capable of creating partial specializations yet.
// NOTE: Range checks are not performed here, because it is done later by
// the subscript function.
return i + n
return i + distance
}

/// Returns an index that is the specified distance from the given index,
Expand Down Expand Up @@ -665,33 +666,36 @@ extension ${Self}: RandomAccessCollection, MutableCollection {
/// print(j)
/// // Prints "nil"
///
/// The value passed as `n` must not offset `i` beyond the bounds of the
/// collection, unless the index passed as `limit` prevents offsetting
/// The value passed as `distance` must not offset `i` beyond the bounds of
/// the collection, unless the index passed as `limit` prevents offsetting
/// beyond those bounds.
///
/// - Parameters:
/// - i: A valid index of the array.
/// - n: The distance to offset `i`.
/// - limit: A valid index of the collection to use as a limit. If `n > 0`,
/// `limit` has no effect if it is less than `i`. Likewise, if `n < 0`,
/// `limit` has no effect if it is greater than `i`.
/// - Returns: An index offset by `n` from the index `i`, unless that index
/// would be beyond `limit` in the direction of movement. In that case,
/// the method returns `nil`.
/// - distance: The distance to offset `i`.
/// - limit: A valid index of the collection to use as a limit. If
/// `distance > 0`, `limit` has no effect if it is less than `i`.
/// Likewise, if `distance < 0`, `limit` has no effect if it is greater
/// than `i`.
/// - Returns: An index offset by `distance` from the index `i`, unless that
/// index would be beyond `limit` in the direction of movement. In that
/// case, the method returns `nil`.
///
/// - Complexity: O(1)
@inlinable
public func index(
_ i: Int, offsetBy n: Int, limitedBy limit: Int
_ i: Int, offsetBy distance: Int, limitedBy limit: Int
) -> Int? {
// NOTE: this is a manual specialization of index movement for a Strideable
// index that is required for Array performance. The optimizer is not
// capable of creating partial specializations yet.
// NOTE: Range checks are not performed here, because it is done later by
// the subscript function.
let l = limit - i
if n > 0 ? l >= 0 && l < n : l <= 0 && n < l {
if distance > 0 ? l >= 0 && l < distance : l <= 0 && distance < l {
return nil
}
return i + n
return i + distance
}

/// Returns the distance between two indices.
Expand Down Expand Up @@ -735,12 +739,15 @@ extension ${Self}: RandomAccessCollection, MutableCollection {
/// - Parameter index: The position of the element to access. `index` must be
/// greater than or equal to `startIndex` and less than `endIndex`.
///
%if Self == 'ContiguousArray':
/// - Complexity: Reading an element from an array is O(1). Writing is O(1)
/// unless the array's storage is shared with another array, in which case
/// writing is O(*n*), where *n* is the length of the array.
%if Self == 'Array':
/// If the array uses a bridged `NSArray` instance as its storage, the
/// efficiency is unspecified.
%else:
/// - Complexity: Reading an element from an array is O(1). Writing is O(1)
/// unless the array's storage is shared with another array or uses a
/// bridged `NSArray` instance as its storage, in which case writing is
/// O(*n*), where *n* is the length of the array.
%end
@inlinable
public subscript(index: Int) -> Element {
Expand Down Expand Up @@ -1416,9 +1423,8 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol {
///
/// - Parameter newElement: The element to append to the array.
///
/// - Complexity: Amortized O(1) over many additions. If the array uses a
/// bridged `NSArray` instance as its storage, the efficiency is
/// unspecified.
/// - Complexity: O(1) on average, over many calls to `append(_:)` on the
/// same array.
@inlinable
@_semantics("array.append_element")
public mutating func append(_ newElement: Element) {
Expand All @@ -1441,7 +1447,9 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol {
///
/// - Parameter newElements: The elements to append to the array.
///
/// - Complexity: O(*n*), where *n* is the length of the resulting array.
/// - Complexity: O(*m*) on average, where *m* is the length of
/// `newElements`, over many calls to `append(contentsOf:)` on the same
/// array.
@inlinable
@_semantics("array.append_contentsOf")
public mutating func append<S : Sequence>(contentsOf newElements: S)
Expand Down Expand Up @@ -1588,7 +1596,8 @@ extension ${Self} : RangeReplaceableCollection, ArrayProtocol {
/// `index` must be a valid index of the array or equal to its `endIndex`
/// property.
///
/// - Complexity: O(*n*), where *n* is the length of the array.
/// - Complexity: O(*n*), where *n* is the length of the array. If
/// `i == endIndex`, this method is equivalent to `append(_:)`.
@inlinable
public mutating func insert(_ newElement: Element, at i: Int) {
_checkIndex(i)
Expand Down Expand Up @@ -1943,9 +1952,10 @@ extension ${Self} {
/// a subrange must be valid indices of the array.
/// - newElements: The new elements to add to the array.
///
/// - Complexity: O(`subrange.count`) if you are replacing a suffix of the
/// array with an empty collection; otherwise, O(*n*), where *n* is the
/// length of the array.
/// - Complexity: O(*n* + *m*), where *n* is length of the array and
/// *m* is the length of `newElements`. If the call to this method simply
/// appends the contents of `newElements` to the array, this method is
/// equivalent to `append(contentsOf:)`.
@inlinable
@_semantics("array.mutate_unknown")
public mutating func replaceSubrange<C>(
Expand Down
56 changes: 31 additions & 25 deletions stdlib/public/core/BidirectionalCollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ where SubSequence: BidirectionalCollection, Indices: BidirectionalCollection {
///
/// - Parameter bounds: A range of the collection's indices. The bounds of
/// the range must be valid indices of the collection.
///
/// - Complexity: O(1)
subscript(bounds: Range<Index>) -> SubSequence { get }

// FIXME(ABI): Associated type inference requires this.
Expand All @@ -147,26 +149,26 @@ extension BidirectionalCollection {
}

@inlinable // FIXME(sil-serialize-all)
public func index(_ i: Index, offsetBy n: Int) -> Index {
if n >= 0 {
return _advanceForward(i, by: n)
public func index(_ i: Index, offsetBy distance: Int) -> Index {
if distance >= 0 {
return _advanceForward(i, by: distance)
}
var i = i
for _ in stride(from: 0, to: n, by: -1) {
for _ in stride(from: 0, to: distance, by: -1) {
formIndex(before: &i)
}
return i
}

@inlinable // FIXME(sil-serialize-all)
public func index(
_ i: Index, offsetBy n: Int, limitedBy limit: Index
_ i: Index, offsetBy distance: Int, limitedBy limit: Index
) -> Index? {
if n >= 0 {
return _advanceForward(i, by: n, limitedBy: limit)
if distance >= 0 {
return _advanceForward(i, by: distance, limitedBy: limit)
}
var i = i
for _ in stride(from: 0, to: n, by: -1) {
for _ in stride(from: 0, to: distance, by: -1) {
if i == limit {
return nil
}
Expand Down Expand Up @@ -207,7 +209,7 @@ extension BidirectionalCollection where SubSequence == Self {
/// - Returns: The last element of the collection if the collection has one
/// or more elements; otherwise, `nil`.
///
/// - Complexity: O(1).
/// - Complexity: O(1)
@inlinable // FIXME(sil-serialize-all)
public mutating func popLast() -> Element? {
guard !isEmpty else { return nil }
Expand All @@ -234,20 +236,20 @@ extension BidirectionalCollection where SubSequence == Self {

/// Removes the given number of elements from the end of the collection.
///
/// - Parameter n: The number of elements to remove. `n` must be greater
/// - Parameter k: The number of elements to remove. `k` must be greater
/// than or equal to zero, and must be less than or equal to the number of
/// elements in the collection.
///
/// - Complexity: O(1) if the collection conforms to
/// `RandomAccessCollection`; otherwise, O(*n*), where *n* is the length
/// of the collection.
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the number of
/// elements to remove.
@inlinable // FIXME(sil-serialize-all)
public mutating func removeLast(_ n: Int) {
if n == 0 { return }
_precondition(n >= 0, "Number of elements to remove should be non-negative")
_precondition(count >= n,
public mutating func removeLast(_ k: Int) {
if k == 0 { return }
_precondition(k >= 0, "Number of elements to remove should be non-negative")
_precondition(count >= k,
"Can't remove more items from a collection than it contains")
self = self[startIndex..<index(endIndex, offsetBy: -n)]
self = self[startIndex..<index(endIndex, offsetBy: -k)]
}
}

Expand All @@ -264,18 +266,20 @@ extension BidirectionalCollection {
/// print(numbers.dropLast(10))
/// // Prints "[]"
///
/// - Parameter n: The number of elements to drop off the end of the
/// collection. `n` must be greater than or equal to zero.
/// - Returns: A subsequence that leaves off `n` elements from the end.
/// - Parameter k: The number of elements to drop off the end of the
/// collection. `k` must be greater than or equal to zero.
/// - Returns: A subsequence that leaves off `k` elements from the end.
///
/// - Complexity: O(*n*), where *n* is the number of elements to drop.
/// - Complexity: O(1) if the collection conforms to
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is the number of
/// elements to drop.
@inlinable // FIXME(sil-serialize-all)
public func dropLast(_ n: Int) -> SubSequence {
public func dropLast(_ k: Int) -> SubSequence {
_precondition(
n >= 0, "Can't drop a negative number of elements from a collection")
k >= 0, "Can't drop a negative number of elements from a collection")
let end = index(
endIndex,
offsetBy: -n,
offsetBy: -k,
limitedBy: startIndex) ?? startIndex
return self[startIndex..<end]
}
Expand All @@ -297,7 +301,9 @@ extension BidirectionalCollection {
/// - Returns: A subsequence terminating at the end of the collection with at
/// most `maxLength` elements.
///
/// - Complexity: O(*n*), where *n* is equal to `maxLength`.
/// - Complexity: O(1) if the collection conforms to
/// `RandomAccessCollection`; otherwise, O(*k*), where *k* is equal to
/// `maxLength`.
@inlinable // FIXME(sil-serialize-all)
public func suffix(_ maxLength: Int) -> SubSequence {
_precondition(
Expand Down
14 changes: 7 additions & 7 deletions stdlib/public/core/ClosedRange.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,24 +232,24 @@ where Bound : Strideable, Bound.Stride : SignedInteger
}

@inlinable
public func index(_ i: Index, offsetBy n: Int) -> Index {
public func index(_ i: Index, offsetBy distance: Int) -> Index {
switch i {
case .inRange(let x):
let d = x.distance(to: upperBound)
if n <= d {
let newPosition = x.advanced(by: numericCast(n))
if distance <= d {
let newPosition = x.advanced(by: numericCast(distance))
_precondition(newPosition >= lowerBound,
"Advancing past start index")
return .inRange(newPosition)
}
if d - -1 == n { return .pastEnd }
if d - -1 == distance { return .pastEnd }
_preconditionFailure("Advancing past end index")
case .pastEnd:
if n == 0 {
if distance == 0 {
return i
}
if n < 0 {
return index(.inRange(upperBound), offsetBy: numericCast(n + 1))
if distance < 0 {
return index(.inRange(upperBound), offsetBy: numericCast(distance + 1))
}
_preconditionFailure("Advancing past end index")
}
Expand Down
Loading