340 lines
11 KiB
Swift
340 lines
11 KiB
Swift
// swiftlint:disable:next blanket_disable_command
|
|
// swiftlint:disable all
|
|
// Copied from https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/Windows.swift
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift Algorithms open source project
|
|
//
|
|
// Copyright (c) 2020 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// windows(ofCount:)
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
extension Collection {
|
|
/// Returns a collection of all the overlapping slices of a given size.
|
|
///
|
|
/// Use this method to iterate over overlapping subsequences of this
|
|
/// collection. This example prints every five character substring of `str`:
|
|
///
|
|
/// let str = "Hello, world!"
|
|
/// for substring in str.windows(ofCount: 5) {
|
|
/// print(substring)
|
|
/// }
|
|
/// // "Hello"
|
|
/// // "ello,"
|
|
/// // "llo, "
|
|
/// // "lo, W"
|
|
/// // ...
|
|
/// // "orld!"
|
|
///
|
|
/// - Parameter count: The number of elements in each window subsequence.
|
|
/// - Returns: A collection of subsequences of this collection, each with
|
|
/// length `count`. If this collection is shorter than `count`, the
|
|
/// resulting collection is empty.
|
|
///
|
|
/// - Complexity: O(1) if the collection conforms to
|
|
/// `RandomAccessCollection`, otherwise O(*k*) where `k` is `count`.
|
|
/// Access to successive windows is O(1).
|
|
func windows(ofCount count: Int) -> WindowsOfCountCollection<Self> {
|
|
WindowsOfCountCollection(base: self, windowSize: count)
|
|
}
|
|
}
|
|
|
|
/// A collection wrapper that presents a sliding window over the elements of
|
|
/// a collection.
|
|
struct WindowsOfCountCollection<Base: Collection> {
|
|
internal let base: Base
|
|
internal let windowSize: Int
|
|
internal var endOfFirstWindow: Base.Index?
|
|
|
|
internal init(base: Base, windowSize: Int) {
|
|
precondition(windowSize > 0, "Windows size must be greater than zero")
|
|
self.base = base
|
|
self.windowSize = windowSize
|
|
self.endOfFirstWindow =
|
|
base.index(base.startIndex, offsetBy: windowSize, limitedBy: base.endIndex)
|
|
}
|
|
}
|
|
|
|
extension WindowsOfCountCollection: Collection {
|
|
/// A position in a `WindowsOfCountCollection` instance.
|
|
struct Index: Comparable {
|
|
internal var lowerBound: Base.Index
|
|
internal var upperBound: Base.Index
|
|
|
|
internal init(lowerBound: Base.Index, upperBound: Base.Index) {
|
|
self.lowerBound = lowerBound
|
|
self.upperBound = upperBound
|
|
}
|
|
|
|
static func == (lhs: Index, rhs: Index) -> Bool {
|
|
lhs.lowerBound == rhs.lowerBound
|
|
}
|
|
|
|
static func < (lhs: Index, rhs: Index) -> Bool {
|
|
lhs.lowerBound < rhs.lowerBound
|
|
}
|
|
}
|
|
|
|
var startIndex: Index {
|
|
if let upperBound = endOfFirstWindow {
|
|
return Index(lowerBound: base.startIndex, upperBound: upperBound)
|
|
} else {
|
|
return endIndex
|
|
}
|
|
}
|
|
|
|
var endIndex: Index {
|
|
Index(lowerBound: base.endIndex, upperBound: base.endIndex)
|
|
}
|
|
|
|
subscript(index: Index) -> Base.SubSequence {
|
|
precondition(
|
|
index.lowerBound != index.upperBound,
|
|
"Windows index is out of range")
|
|
return base[index.lowerBound..<index.upperBound]
|
|
}
|
|
|
|
func index(after index: Index) -> Index {
|
|
precondition(index != endIndex, "Advancing past end index")
|
|
guard index.upperBound < base.endIndex else { return endIndex }
|
|
|
|
let lowerBound = windowSize == 1
|
|
? index.upperBound
|
|
: base.index(after: index.lowerBound)
|
|
let upperBound = base.index(after: index.upperBound)
|
|
return Index(lowerBound: lowerBound, upperBound: upperBound)
|
|
}
|
|
|
|
func index(_ i: Index, offsetBy distance: Int) -> Index {
|
|
guard distance != 0 else { return i }
|
|
|
|
return distance > 0
|
|
? offsetForward(i, by: distance)
|
|
: offsetBackward(i, by: -distance)
|
|
}
|
|
|
|
func index(
|
|
_ i: Index,
|
|
offsetBy distance: Int,
|
|
limitedBy limit: Index
|
|
) -> Index? {
|
|
guard distance != 0 else { return i }
|
|
guard limit != i else { return nil }
|
|
|
|
if distance > 0 {
|
|
return limit > i
|
|
? offsetForward(i, by: distance, limitedBy: limit)
|
|
: offsetForward(i, by: distance)
|
|
} else {
|
|
return limit < i
|
|
? offsetBackward(i, by: -distance, limitedBy: limit)
|
|
: offsetBackward(i, by: -distance)
|
|
}
|
|
}
|
|
|
|
internal func offsetForward(_ i: Index, by distance: Int) -> Index {
|
|
guard let index = offsetForward(i, by: distance, limitedBy: endIndex)
|
|
else { fatalError("Index is out of bounds") }
|
|
return index
|
|
}
|
|
|
|
internal func offsetBackward(_ i: Index, by distance: Int) -> Index {
|
|
guard let index = offsetBackward(i, by: distance, limitedBy: startIndex)
|
|
else { fatalError("Index is out of bounds") }
|
|
return index
|
|
}
|
|
|
|
internal func offsetForward(
|
|
_ i: Index, by distance: Int, limitedBy limit: Index
|
|
) -> Index? {
|
|
assert(distance > 0)
|
|
assert(limit > i)
|
|
|
|
// `endIndex` and the index before it both have `base.endIndex` as their
|
|
// upper bound, so we first advance to the base index _before_ the upper
|
|
// bound of the output, in order to avoid advancing past the end of `base`
|
|
// when advancing to `endIndex`.
|
|
//
|
|
// Advancing by 4:
|
|
//
|
|
// input: [x|x x x x x|x x x x] [x x|x x x x x|x x x]
|
|
// |> > >|>| or |> > >|
|
|
// output: [x x x x x|x x x x x] [x x x x x x x x x x] (`endIndex`)
|
|
|
|
if distance >= windowSize {
|
|
// Avoid traversing `self[i.lowerBound..<i.upperBound]` when the lower
|
|
// bound of the output is greater than or equal to the upper bound of the
|
|
// input.
|
|
|
|
// input: [x|x x x x|x x x x x x x]
|
|
// |> >|> > >|>|
|
|
// output: [x x x x x x x|x x x x|x]
|
|
|
|
guard limit.lowerBound >= i.upperBound,
|
|
let lowerBound = base.index(
|
|
i.upperBound,
|
|
offsetBy: distance - windowSize,
|
|
limitedBy: limit.lowerBound),
|
|
let indexBeforeUpperBound = base.index(
|
|
lowerBound,
|
|
offsetBy: windowSize - 1,
|
|
limitedBy: limit.upperBound)
|
|
else { return nil }
|
|
|
|
// If `indexBeforeUpperBound` equals `base.endIndex`, we're advancing to
|
|
// `endIndex`.
|
|
guard indexBeforeUpperBound != base.endIndex else { return endIndex }
|
|
|
|
return Index(
|
|
lowerBound: lowerBound,
|
|
upperBound: base.index(after: indexBeforeUpperBound))
|
|
} else {
|
|
// input: [x|x x x x x x|x x x x x]
|
|
// |> > > >| |> > >|>|
|
|
// output: [x x x x x|x x x x x x|x]
|
|
|
|
guard let indexBeforeUpperBound = base.index(
|
|
i.upperBound,
|
|
offsetBy: distance - 1,
|
|
limitedBy: limit.upperBound)
|
|
else { return nil }
|
|
|
|
// If `indexBeforeUpperBound` equals the limit, the upper bound itself
|
|
// exceeds it.
|
|
guard indexBeforeUpperBound != limit.upperBound || limit == endIndex
|
|
else { return nil }
|
|
|
|
// If `indexBeforeUpperBound` equals `base.endIndex`, we're advancing to
|
|
// `endIndex`.
|
|
guard indexBeforeUpperBound != base.endIndex else { return endIndex }
|
|
|
|
return Index(
|
|
lowerBound: base.index(i.lowerBound, offsetBy: distance),
|
|
upperBound: base.index(after: indexBeforeUpperBound))
|
|
}
|
|
}
|
|
|
|
internal func offsetBackward(
|
|
_ i: Index, by distance: Int, limitedBy limit: Index
|
|
) -> Index? {
|
|
assert(distance > 0)
|
|
assert(limit < i)
|
|
|
|
if i == endIndex {
|
|
// Advance `base.endIndex` by `distance - 1`, because the index before
|
|
// `endIndex` also has `base.endIndex` as its upper bound.
|
|
//
|
|
// Advancing by 4:
|
|
//
|
|
// input: [x x x x x x x x x x] (`endIndex`)
|
|
// |< < < < <|< < <|
|
|
// output: [x x|x x x x x|x x x]
|
|
|
|
guard let upperBound = base.index(
|
|
base.endIndex,
|
|
offsetBy: -(distance - 1),
|
|
limitedBy: limit.upperBound)
|
|
else { return nil }
|
|
|
|
return Index(
|
|
lowerBound: base.index(upperBound, offsetBy: -windowSize),
|
|
upperBound: upperBound)
|
|
} else if distance >= windowSize {
|
|
// Avoid traversing `self[i.lowerBound..<i.upperBound]` when the upper
|
|
// bound of the output is less than or equal to the lower bound of the
|
|
// input.
|
|
//
|
|
// input: [x x x x x x x|x x x x|x]
|
|
// |< < < <|< <|
|
|
// output: [x|x x x x|x x x x x x x]
|
|
|
|
guard limit.upperBound <= i.lowerBound,
|
|
let upperBound = base.index(
|
|
i.lowerBound,
|
|
offsetBy: -(distance - windowSize),
|
|
limitedBy: limit.upperBound)
|
|
else { return nil }
|
|
|
|
return Index(
|
|
lowerBound: base.index(upperBound, offsetBy: -windowSize),
|
|
upperBound: upperBound)
|
|
} else {
|
|
// input: [x x x x x|x x x x x x|x]
|
|
// |< < < <| |< < < <|
|
|
// output: [x|x x x x x x|x x x x x]
|
|
|
|
guard let lowerBound = base.index(
|
|
i.lowerBound,
|
|
offsetBy: -distance,
|
|
limitedBy: limit.lowerBound)
|
|
else { return nil }
|
|
|
|
return Index(
|
|
lowerBound: lowerBound,
|
|
upperBound: base.index(i.lowerBound, offsetBy: -distance))
|
|
}
|
|
}
|
|
|
|
func distance(from start: Index, to end: Index) -> Int {
|
|
guard start <= end else { return -distance(from: end, to: start) }
|
|
guard start != end else { return 0 }
|
|
guard end != endIndex else {
|
|
// We add 1 here because the index before `endIndex` also has
|
|
// `base.endIndex` as its upper bound.
|
|
return base[start.upperBound...].count + 1
|
|
}
|
|
|
|
if start.upperBound <= end.lowerBound {
|
|
// The distance between `start.lowerBound` and `start.upperBound` is
|
|
// already known.
|
|
//
|
|
// start: [x|x x x x|x x x x x x x]
|
|
// |- - - -|> >|
|
|
// end: [x x x x x x x|x x x x|x]
|
|
|
|
return windowSize + base[start.upperBound..<end.lowerBound].count
|
|
} else {
|
|
// start: [x|x x x x x x|x x x x x]
|
|
// |> > > >|
|
|
// end: [x x x x x|x x x x x x|x]
|
|
|
|
return base[start.lowerBound..<end.lowerBound].count
|
|
}
|
|
}
|
|
}
|
|
|
|
extension WindowsOfCountCollection: BidirectionalCollection
|
|
where Base: BidirectionalCollection
|
|
{
|
|
func index(before index: Index) -> Index {
|
|
precondition(index != startIndex, "Incrementing past start index")
|
|
if index == endIndex {
|
|
return Index(
|
|
lowerBound: base.index(index.lowerBound, offsetBy: -windowSize),
|
|
upperBound: index.upperBound
|
|
)
|
|
} else {
|
|
return Index(
|
|
lowerBound: base.index(before: index.lowerBound),
|
|
upperBound: base.index(before: index.upperBound)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension WindowsOfCountCollection: RandomAccessCollection
|
|
where Base: RandomAccessCollection {}
|
|
|
|
extension WindowsOfCountCollection: LazySequenceProtocol, LazyCollectionProtocol
|
|
where Base: LazySequenceProtocol {}
|
|
|
|
extension WindowsOfCountCollection.Index: Hashable where Base.Index: Hashable {}
|