Skip to content

Commit 4f3525e

Browse files
author
Tim Vermeulen
committed
Add commonPrefix(with:), commonSuffix(with:)
1 parent 50be2c8 commit 4f3525e

File tree

2 files changed

+413
-0
lines changed

2 files changed

+413
-0
lines changed

Sources/Algorithms/CommonPrefix.swift

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift Algorithms open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
public struct CommonPrefix<Base: Sequence, Other: Sequence> {
13+
@usableFromInline
14+
internal let base: Base
15+
16+
@usableFromInline
17+
internal let other: Other
18+
19+
@usableFromInline
20+
internal let areEquivalent: (Base.Element, Other.Element) -> Bool
21+
22+
@inlinable
23+
internal init(
24+
base: Base,
25+
other: Other,
26+
areEquivalent: @escaping (Base.Element, Other.Element) -> Bool
27+
) {
28+
self.base = base
29+
self.other = other
30+
self.areEquivalent = areEquivalent
31+
}
32+
}
33+
34+
extension CommonPrefix: Sequence {
35+
public struct Iterator: IteratorProtocol {
36+
@usableFromInline
37+
internal var base: Base.Iterator
38+
39+
@usableFromInline
40+
internal var other: Other.Iterator
41+
42+
@usableFromInline
43+
internal let areEquivalent: (Base.Element, Other.Element) -> Bool
44+
45+
@inlinable
46+
internal init(
47+
base: Base.Iterator,
48+
other: Other.Iterator,
49+
areEquivalent: @escaping (Base.Element, Other.Element) -> Bool
50+
) {
51+
self.base = base
52+
self.other = other
53+
self.areEquivalent = areEquivalent
54+
}
55+
56+
public mutating func next() -> Base.Element? {
57+
if let next = base.next(),
58+
let otherNext = other.next(),
59+
areEquivalent(next, otherNext) {
60+
return next
61+
} else {
62+
return nil
63+
}
64+
}
65+
}
66+
67+
@inlinable
68+
public func makeIterator() -> Iterator {
69+
Iterator(
70+
base: base.makeIterator(),
71+
other: other.makeIterator(),
72+
areEquivalent: areEquivalent)
73+
}
74+
}
75+
76+
extension CommonPrefix: Collection where Base: Collection, Other: Collection {
77+
public struct Index {
78+
@usableFromInline
79+
internal let base: Base.Index
80+
81+
@usableFromInline
82+
internal let other: Other.Index
83+
84+
@inlinable
85+
internal init(base: Base.Index, other: Other.Index) {
86+
self.base = base
87+
self.other = other
88+
}
89+
}
90+
91+
@inlinable
92+
internal func normalizeIndex(base: Base.Index, other: Other.Index) -> Index {
93+
if base != self.base.endIndex
94+
&& other != self.other.endIndex
95+
&& areEquivalent(self.base[base], self.other[other])
96+
{
97+
return Index(base: base, other: other)
98+
} else {
99+
return endIndex
100+
}
101+
}
102+
103+
@inlinable
104+
public var startIndex: Index {
105+
normalizeIndex(base: base.startIndex, other: other.startIndex)
106+
}
107+
108+
@inlinable
109+
public var endIndex: Index {
110+
Index(base: base.endIndex, other: other.endIndex)
111+
}
112+
113+
@inlinable
114+
public func index(after index: Index) -> Index {
115+
normalizeIndex(
116+
base: base.index(after: index.base),
117+
other: other.index(after: index.other))
118+
}
119+
120+
@inlinable
121+
public subscript(index: Index) -> Base.Element {
122+
base[index.base]
123+
}
124+
}
125+
126+
extension CommonPrefix.Index: Comparable {
127+
@inlinable
128+
public static func == (lhs: Self, rhs: Self) -> Bool {
129+
lhs.base == rhs.base
130+
}
131+
132+
@inlinable
133+
public static func < (lhs: Self, rhs: Self) -> Bool {
134+
lhs.base < rhs.base
135+
}
136+
}
137+
138+
extension CommonPrefix.Index: Hashable where Base.Index: Hashable {
139+
public func hash(into hasher: inout Hasher) {
140+
hasher.combine(base)
141+
}
142+
}
143+
144+
extension CommonPrefix: LazySequenceProtocol where Base: LazySequenceProtocol {}
145+
extension CommonPrefix: LazyCollectionProtocol
146+
where Base: LazyCollectionProtocol, Other: Collection {}
147+
148+
//===----------------------------------------------------------------------===//
149+
// Sequence.commonPrefix(with:)
150+
//===----------------------------------------------------------------------===//
151+
152+
extension Sequence {
153+
@inlinable
154+
public func commonPrefix<Other: Sequence>(
155+
with other: Other,
156+
by areEquivalent: (Element, Other.Element) throws -> Bool
157+
) rethrows -> [Element] {
158+
var iterator = makeIterator()
159+
var otherIterator = other.makeIterator()
160+
var result: [Element] = []
161+
162+
while let next = iterator.next(),
163+
let otherNext = otherIterator.next(),
164+
try areEquivalent(next, otherNext)
165+
{
166+
result.append(next)
167+
}
168+
169+
return result
170+
}
171+
}
172+
173+
extension Sequence where Element: Equatable {
174+
@inlinable
175+
public func commonPrefix<Other: Sequence>(
176+
with other: Other
177+
) -> CommonPrefix<Self, Other> where Other.Element == Element {
178+
CommonPrefix(base: self, other: other, areEquivalent: ==)
179+
}
180+
}
181+
182+
//===----------------------------------------------------------------------===//
183+
// LazySequenceProtocol.commonPrefix(with:)
184+
//===----------------------------------------------------------------------===//
185+
186+
extension LazySequenceProtocol {
187+
@inlinable
188+
public func commonPrefix<Other: Sequence>(
189+
with other: Other,
190+
by areEquivalent: @escaping (Element, Other.Element) -> Bool
191+
) -> CommonPrefix<Self, Other> {
192+
CommonPrefix(base: self, other: other, areEquivalent: areEquivalent)
193+
}
194+
}
195+
196+
//===----------------------------------------------------------------------===//
197+
// Collection.commonPrefix(with:)
198+
//===----------------------------------------------------------------------===//
199+
200+
extension Collection {
201+
@inlinable
202+
public func commonPrefix<Other: Sequence>(
203+
with other: Other,
204+
by areEquivalent: (Element, Other.Element) throws -> Bool
205+
) rethrows -> SubSequence {
206+
let endIndex = endIndex
207+
208+
var index = startIndex
209+
var iterator = other.makeIterator()
210+
211+
while index != endIndex,
212+
let next = iterator.next(),
213+
try areEquivalent(self[index], next)
214+
{
215+
formIndex(after: &index)
216+
}
217+
218+
return self[..<index]
219+
}
220+
}
221+
222+
extension Collection where Element: Equatable {
223+
@inlinable
224+
public func commonPrefix<Other: Sequence>(
225+
with other: Other
226+
) -> SubSequence where Other.Element == Element {
227+
commonPrefix(with: other, by: ==)
228+
}
229+
}
230+
231+
//===----------------------------------------------------------------------===//
232+
// LazyCollectionProtocol.commonPrefix(with:)
233+
//===----------------------------------------------------------------------===//
234+
235+
extension LazyCollectionProtocol {
236+
@inlinable
237+
public func commonPrefix<Other: Sequence>(
238+
with other: Other,
239+
by areEquivalent: @escaping (Element, Other.Element) -> Bool
240+
) -> CommonPrefix<Self, Other> {
241+
CommonPrefix(base: self, other: other, areEquivalent: areEquivalent)
242+
}
243+
}
244+
245+
extension LazyCollectionProtocol where Element: Equatable {
246+
@inlinable
247+
public func commonPrefix<Other: Sequence>(
248+
with other: Other
249+
) -> CommonPrefix<Self, Other> where Other.Element == Element {
250+
commonPrefix(with: other, by: ==)
251+
}
252+
}
253+
254+
//===----------------------------------------------------------------------===//
255+
// BidirectionalCollection.commonSuffix(with:)
256+
//===----------------------------------------------------------------------===//
257+
258+
extension BidirectionalCollection {
259+
@inlinable
260+
public func commonSuffix<Other: BidirectionalCollection>(
261+
with other: Other,
262+
by areEquivalent: (Element, Other.Element) throws -> Bool
263+
) rethrows -> SubSequence {
264+
let (index, _) = try startsOfCommonSuffix(with: other, by: areEquivalent)
265+
return self[index...]
266+
}
267+
}
268+
269+
extension BidirectionalCollection where Element: Equatable {
270+
@inlinable
271+
public func commonSuffix<Other: BidirectionalCollection>(
272+
with other: Other
273+
) -> SubSequence where Other.Element == Element {
274+
commonSuffix(with: other, by: ==)
275+
}
276+
}
277+
278+
//===----------------------------------------------------------------------===//
279+
// Collection.endsOfCommonPrefix(with:)
280+
//===----------------------------------------------------------------------===//
281+
282+
extension Collection {
283+
@inlinable
284+
public func endsOfCommonPrefix<Other: Collection>(
285+
with other: Other,
286+
by areEquivalent: (Element, Other.Element) throws -> Bool
287+
) rethrows -> (Index, Other.Index) {
288+
var index = startIndex
289+
var otherIndex = other.startIndex
290+
291+
while index != endIndex && otherIndex != other.endIndex,
292+
try areEquivalent(self[index], other[otherIndex])
293+
{
294+
formIndex(after: &index)
295+
other.formIndex(after: &otherIndex)
296+
}
297+
298+
return (index, otherIndex)
299+
}
300+
}
301+
302+
extension Collection where Element: Equatable {
303+
@inlinable
304+
public func endsOfCommonPrefix<Other: Collection>(
305+
with other: Other
306+
) -> (Index, Other.Index) where Other.Element == Element {
307+
endsOfCommonPrefix(with: other, by: ==)
308+
}
309+
}
310+
311+
//===----------------------------------------------------------------------===//
312+
// BidirectionalCollection.startsOfCommonPrefix(with:)
313+
//===----------------------------------------------------------------------===//
314+
315+
extension BidirectionalCollection {
316+
@inlinable
317+
public func startsOfCommonSuffix<Other: BidirectionalCollection>(
318+
with other: Other,
319+
by areEquivalent: (Element, Other.Element) throws -> Bool
320+
) rethrows -> (Index, Other.Index) {
321+
let startIndex = startIndex
322+
let otherStartIndex = other.startIndex
323+
324+
var index = endIndex
325+
var otherIndex = other.endIndex
326+
327+
while index != startIndex && otherIndex != otherStartIndex {
328+
let prev = self.index(before: index)
329+
let otherPrev = other.index(before: otherIndex)
330+
331+
if try !areEquivalent(self[prev], other[otherPrev]) {
332+
break
333+
}
334+
335+
index = prev
336+
otherIndex = otherPrev
337+
}
338+
339+
return (index, otherIndex)
340+
}
341+
}
342+
343+
extension BidirectionalCollection where Element: Equatable {
344+
@inlinable
345+
public func startsOfCommonSuffix<Other: BidirectionalCollection>(
346+
with other: Other
347+
) -> (Index, Other.Index) where Other.Element == Element {
348+
startsOfCommonSuffix(with: other, by: ==)
349+
}
350+
}

0 commit comments

Comments
 (0)