Skip to content

Commit 6933a31

Browse files
authored
Avoid unnecessary computations in Product.distance(from:to:) (#40)
1 parent b28d248 commit 6933a31

File tree

1 file changed

+49
-8
lines changed

1 file changed

+49
-8
lines changed

Sources/Algorithms/Product.swift

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,15 +132,56 @@ extension Product2: Collection where Base1: Collection {
132132

133133
@inlinable
134134
public func distance(from start: Index, to end: Index) -> Int {
135-
if start > end {
136-
return -distance(from: end, to: start)
137-
}
138-
if start.i1 == end.i1 {
139-
return base2[start.i2..<end.i2].count
140-
}
135+
guard start.i1 <= end.i1
136+
else { return -distance(from: end, to: start) }
137+
guard start.i1 != end.i1
138+
else { return base2.distance(from: start.i2, to: end.i2) }
139+
140+
// The number of full cycles through `base2` between `start` and `end`,
141+
// excluding the cycles that `start` and `end` are on.
142+
let fullBase2Cycles = base1[start.i1..<end.i1].count - 1
141143

142-
return base2[start.i2...].count + base2[..<end.i2].count
143-
+ base2.count * (base1.distance(from: start.i1, to: end.i1) - 1)
144+
if start.i2 <= end.i2 {
145+
// start.i2
146+
// v
147+
// start.i1 > [l l l|c c c c c c r r r]
148+
// [l l l c c c c c c r r r] >
149+
// ... > `fullBase2Cycles` times
150+
// [l l l c c c c c c r r r] >
151+
// end.i1 > [l l l c c c c c c|r r r]
152+
// ^
153+
// end.i2
154+
155+
let left = base2[..<start.i2].count
156+
let center = base2[start.i2..<end.i2].count
157+
let right = base2[end.i2...].count
158+
159+
return center + right
160+
+ fullBase2Cycles * (left + center + right)
161+
+ left + center
162+
} else {
163+
// start.i2
164+
// v
165+
// start.i1 > [l l l c c c c c c|r r r]
166+
// [l l l c c c c c c r r r] >
167+
// ... > `fullBase2Cycles` times
168+
// [l l l c c c c c c r r r] >
169+
// end.i1 > [l l l|c c c c c c r r r]
170+
// ^
171+
// end.i2
172+
173+
let left = base2[..<end.i2].count
174+
let right = base2[start.i2...].count
175+
176+
// We can avoid traversing `base2[end.i2..<start.i2]` if `start` and `end`
177+
// are on consecutive cycles.
178+
guard fullBase2Cycles > 0 else { return right + left }
179+
180+
let center = base2[end.i2..<start.i2].count
181+
return right
182+
+ fullBase2Cycles * (left + center + right)
183+
+ left
184+
}
144185
}
145186

146187
@inlinable

0 commit comments

Comments
 (0)