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

Add Stride for Collection #24

Merged
merged 11 commits into from
Dec 1, 2020
Prev Previous commit
Next Next commit
Refactor to index(_:offsetBy:limitedBy:) and utilise the new test uti…
…lities for validateIndexTraversals
  • Loading branch information
ollieatkinson committed Nov 1, 2020
commit 6df6133ab7400b2b5a8c6fe40a94a4f74178cc43
60 changes: 35 additions & 25 deletions Sources/Algorithms/Stride.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ extension Stride: Collection {
self.base = base
}

init?(_ base: Base.Index?) {
guard let base = base else { return nil }
self.base = base
}
ollieatkinson marked this conversation as resolved.
Show resolved Hide resolved

public static func < (lhs: Index, rhs: Index) -> Bool {
lhs.base < rhs.base
}
Expand All @@ -79,17 +84,34 @@ extension Stride: Collection {

public func index(after i: Index) -> Index {
precondition(i.base < base.endIndex, "Advancing past end index")
return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)
.map(Index.init) ?? endIndex
return index(i, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}

public func index(
_ i: Index,
offsetBy distance: Int,
offsetBy n: Int,
limitedBy limit: Index
) -> Index? {
base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)
.map(Index.init)
guard n != 0 else { return i }
guard limit != i else { return nil }
switch (i, n) {
case (endIndex, ..<0):
let baseEnd = base.index(base.endIndex, offsetBy: -((base.count - 1) % stride + 1))
return Index(base.index(baseEnd, offsetBy: (n - n.signum()) * stride, limitedBy: limit.base))
case (_, 1...):
let max = limit < i ? endIndex.base : limit.base
let idx = base.index(i.base, offsetBy: n * stride, limitedBy: max)
if let idx = idx {
return idx > max ? endIndex : Index(idx)
}
guard i >= limit || limit == endIndex else {
return nil
}
let isToEnd = distance(from: i, to: endIndex) == n
return isToEnd ? endIndex : nil
case _:
return Index(base.index(i.base, offsetBy: n * stride, limitedBy: limit.base))
}
}

public var count: Int {
Expand All @@ -99,37 +121,25 @@ extension Stride: Collection {

public func distance(from start: Index, to end: Index) -> Int {
let distance = base.distance(from: start.base, to: end.base)
return distance / stride + (distance % stride > 0 ? 1 : 0)
return distance / stride + (abs(distance % stride) > 0 ? distance.signum() : 0)
ollieatkinson marked this conversation as resolved.
Show resolved Hide resolved
}

public func index(_ i: Index, offsetBy distance: Int) -> Index {
precondition(distance <= 0 || i.base < base.endIndex, "Advancing past end index")
precondition(distance >= 0 || i.base > base.startIndex, "Incrementing past start index")
return Index(base.index(i.base, offsetBy: distance * stride))
let limit = distance > 0 ? endIndex : startIndex
let idx = index(i, offsetBy: distance, limitedBy: limit)
precondition(idx != nil, "The distance \(distance) is not valid for this collection")
return idx!
}
}

extension Stride: BidirectionalCollection
where Base: RandomAccessCollection {

public func index(before i: Index) -> Index {
precondition(i.base > base.startIndex, "Incrementing past start index")
if i == endIndex {
let count = base.count
precondition(count > 0, "Can't move before the starting index")
return Index(
base.index(base.endIndex, offsetBy: -((count - 1) % stride + 1))
)
} else {
guard let step = base.index(
i.base,
offsetBy: -stride,
limitedBy: startIndex.base
) else {
fatalError("Incrementing past start index")
}
return Index(step)
}
return index(i, offsetBy: -1)
}
}

Expand Down
65 changes: 16 additions & 49 deletions Tests/SwiftAlgorithmsTests/StrideTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,59 +89,26 @@ final class StridingTests: XCTestCase {
}

func testIndexTraversals() {
let zero_to_one_hundered_range = 0...100
validateIndexTraversals(
(0...100).striding(by: 10)
// (0...100).striding(by: 11)
// (0...100).striding(by: 101)
zero_to_one_hundered_range.striding(by: 10),
zero_to_one_hundered_range.striding(by: 11),
zero_to_one_hundered_range.striding(by: 101)
)
let zero_to_one_hundered_array = zero_to_one_hundered_range.map{ $0 }
ollieatkinson marked this conversation as resolved.
Show resolved Hide resolved
validateIndexTraversals(
zero_to_one_hundered_array.striding(by: 10),
zero_to_one_hundered_array.striding(by: 11),
zero_to_one_hundered_array.striding(by: 101)
)
let string = "swift rocks".map(String.init)
validateIndexTraversals(
string.striding(by: 1),
string.striding(by: 2),
string.striding(by: 10)
)
}
ollieatkinson marked this conversation as resolved.
Show resolved Hide resolved

func testDistance() {

do {
let a = (0...100).striding(by: 11)
XCTAssertEqual(a.distance(from: a.startIndex, to: a.endIndex), a.count)
for (i, index) in a.indices.enumerated() {
XCTAssertEqual(a.distance(from: a.startIndex, to: index), i)
}

var i = a.startIndex
a.formIndex(&i, offsetBy: 3)
XCTAssertEqual(a.distance(from: a.startIndex, to: i), 3)
XCTAssertEqual(a[i], 33)
}

do {

let a = (0...100).striding(by: 10)
XCTAssertEqual(a.distance(from: a.startIndex, to: a.endIndex), a.count)

for (i, index) in a.indices.enumerated() {
XCTAssertEqual(a.distance(from: a.startIndex, to: index), i)
}

var i = a.startIndex
a.formIndex(&i, offsetBy: 3)
XCTAssertEqual(a.distance(from: a.startIndex, to: i), 3)
XCTAssertEqual(a[i], 30)
}

do {

let a = (0...100).striding(by: 101)
XCTAssertEqual(a.distance(from: a.startIndex, to: a.endIndex), a.count)

for (i, index) in a.indices.enumerated() {
XCTAssertEqual(a.distance(from: a.startIndex, to: index), i)
}

var i = a.startIndex
a.formIndex(&i, offsetBy: 1)
XCTAssertEqual(a.distance(from: a.startIndex, to: i), a.count)
XCTAssertEqual(i, a.endIndex)
// a[i] // == Fatal error: Index out of range
}
}

func testOffsetBy() {
let a = (0...100).striding(by: 22)
Expand Down