Skip to content

Commit 84be085

Browse files
committed
Add alternate rotated collection view.
This one is based on ConcatenatedCollection, which concatenates two collections with the same element type.
1 parent 103b6b8 commit 84be085

File tree

1 file changed

+337
-0
lines changed

1 file changed

+337
-0
lines changed

test/Prototypes/Rotated.swift.gyb

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
//===--- Rotated.swift.gyb ------------------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
// RUN: rm -rf %t && mkdir -p %t && %gyb -DWORD_BITS=%target-ptrsize %s -o %t/out.swift
13+
// RUN: %line-directive %t/out.swift -- %target-build-swift -parse-stdlib %t/out.swift -o %t/a.out -Onone
14+
// RUN: %line-directive %t/out.swift -- %target-run %t/a.out
15+
// --stdlib-unittest-filter DoubleWidth/
16+
17+
// REQUIRES: executable_test
18+
19+
import Swift
20+
import StdlibUnittest
21+
22+
%{
23+
24+
from gyb_stdlib_support import (
25+
TRAVERSALS,
26+
collectionForTraversal,
27+
defaultIndicesForTraversal,
28+
sliceTypeName
29+
)
30+
31+
}%
32+
33+
//===----------------------------------------------------------------------===//
34+
// ConcatenatedCollection
35+
//===----------------------------------------------------------------------===//
36+
37+
/// Represents a position in either the first or second collection of a
38+
/// `ConcatenatedCollection`.
39+
internal enum _ConcatenatedCollectionIndexRepresentation<
40+
I1 : Comparable, I2 : Comparable
41+
> {
42+
case first(I1)
43+
case second(I2)
44+
}
45+
46+
/// A position in a `ConcatenatedCollection` collection.
47+
public struct ConcatenatedCollectionIndex<
48+
C1 : Collection, C2 : Collection
49+
> : Comparable {
50+
/// Creates a new index into the first underlying collection.
51+
internal init(first i: C1.Index) {
52+
_position = .first(i)
53+
}
54+
55+
/// Creates a new index into the second underlying collection.
56+
internal init(second i: C2.Index) {
57+
_position = .second(i)
58+
}
59+
60+
internal let _position:
61+
_ConcatenatedCollectionIndexRepresentation<C1.Index, C2.Index>
62+
}
63+
64+
public func < <C1: Collection, C2: Collection>(
65+
lhs: ConcatenatedCollectionIndex<C1, C2>,
66+
rhs: ConcatenatedCollectionIndex<C1, C2>
67+
) -> Bool {
68+
switch (lhs._position, rhs._position) {
69+
case (.first, .second):
70+
return true
71+
case (.second, .first):
72+
return false
73+
case let (.first(l), .first(r)):
74+
return l < r
75+
case let (.second(l), .second(r)):
76+
return l < r
77+
}
78+
}
79+
80+
public func == <C1: Collection, C2: Collection>(
81+
lhs: ConcatenatedCollectionIndex<C1, C2>,
82+
rhs: ConcatenatedCollectionIndex<C1, C2>
83+
) -> Bool {
84+
switch (lhs._position, rhs._position) {
85+
case let (.first(l), .first(r)):
86+
return l == r
87+
case let (.second(l), .second(r)):
88+
return l == r
89+
default:
90+
return false
91+
}
92+
}
93+
94+
% for Traversal in TRAVERSALS:
95+
% Collection = collectionForTraversal(Traversal)
96+
% Self = "Concatenated" + Collection
97+
98+
/// A concatenation of two collections with the same element type.
99+
public struct ${Self}<C1 : ${Collection}, C2: ${Collection}
100+
where C1.Iterator.Element == C2.Iterator.Element>: ${Collection} {
101+
102+
init(_base1: C1, base2: C2) {
103+
self._base1 = _base1
104+
self._base2 = base2
105+
}
106+
107+
public typealias Index = ConcatenatedCollectionIndex<C1, C2>
108+
109+
public var startIndex: Index {
110+
return ConcatenatedCollectionIndex(first: _base1.startIndex)
111+
}
112+
113+
public var endIndex: Index {
114+
return ConcatenatedCollectionIndex(second: _base2.endIndex)
115+
}
116+
117+
public subscript(i: Index) -> C1.Iterator.Element {
118+
switch i._position {
119+
case let .first(i):
120+
return _base1[i]
121+
case let .second(i):
122+
return _base2[i]
123+
}
124+
}
125+
126+
public func index(after i: Index) -> Index {
127+
switch i._position {
128+
case let .first(i):
129+
let next = _base1.index(after: i)
130+
return next == _base1.endIndex
131+
? ConcatenatedCollectionIndex(second: _base2.startIndex)
132+
: ConcatenatedCollectionIndex(first: next)
133+
case let .second(i):
134+
return ConcatenatedCollectionIndex(second: _base2.index(after: i))
135+
}
136+
}
137+
138+
% if Traversal in ['Bidirectional', 'RandomAccess']:
139+
public func index(before i: Index) -> Index {
140+
assert(i != startIndex, "Can't advance before startIndex")
141+
switch i._position {
142+
case let .first(i):
143+
return ConcatenatedCollectionIndex(first: _base1.index(before: i))
144+
case let .second(i):
145+
return i == _base2.startIndex
146+
? ConcatenatedCollectionIndex(
147+
first: _base1.index(before: _base1.endIndex))
148+
: ConcatenatedCollectionIndex(second: _base2.index(before: i))
149+
}
150+
}
151+
% end
152+
153+
% if Traversal is 'RandomAccess':
154+
public func index(_ i: Index, offsetBy n: ${Self}.IndexDistance) -> Index {
155+
if n == 0 { return i }
156+
return n > 0 ? _offsetForward(i, by: n) : _offsetBackward(i, by: -n)
157+
}
158+
159+
internal func _offsetForward(_ i: Index, by n: ${Self}.IndexDistance) -> Index {
160+
switch i._position {
161+
case let .first(i):
162+
let d: ${Self}.IndexDistance = numericCast(
163+
_base1.distance(from: i, to: _base1.endIndex))
164+
if n < d {
165+
return ConcatenatedCollectionIndex(
166+
first: _base1.index(i, offsetBy: numericCast(n)))
167+
} else {
168+
return ConcatenatedCollectionIndex(
169+
second: _base2.index(_base2.startIndex, offsetBy: numericCast(n - d)))
170+
}
171+
case let .second(i):
172+
return ConcatenatedCollectionIndex(
173+
second: _base2.index(i, offsetBy: numericCast(n)))
174+
}
175+
}
176+
177+
internal func _offsetBackward(_ i: Index, by n: ${Self}.IndexDistance) -> Index {
178+
switch i._position {
179+
case let .first(i):
180+
return ConcatenatedCollectionIndex(
181+
first: _base1.index(i, offsetBy: -numericCast(n)))
182+
case let .second(i):
183+
let d: ${Self}.IndexDistance = numericCast(
184+
_base2.distance(from: _base2.startIndex, to: i))
185+
if n <= d {
186+
return ConcatenatedCollectionIndex(
187+
second: _base2.index(i, offsetBy: -numericCast(n)))
188+
} else {
189+
return ConcatenatedCollectionIndex(
190+
first: _base1.index(_base1.endIndex, offsetBy: -numericCast(n - d)))
191+
}
192+
}
193+
}
194+
% end
195+
196+
let _base1: C1
197+
let _base2: C2
198+
}
199+
200+
/// Returns a new collection that presents a view onto the elements of the
201+
/// first collection and then the elements of the second collection.
202+
func concatenate<
203+
C1 : ${Collection}, C2 : ${Collection}
204+
where C1.Iterator.Element == C2.Iterator.Element
205+
>(_ first: C1, _ second: C2) -> ${Self}<C1, C2> {
206+
return ${Self}(_base1: first, base2: second)
207+
}
208+
209+
% end
210+
211+
212+
//===----------------------------------------------------------------------===//
213+
// RotatedCollection
214+
//===----------------------------------------------------------------------===//
215+
216+
/// A position in a rotated collection.
217+
public struct RotatedCollectionIndex<
218+
Base : Collection where Base.SubSequence: Collection
219+
> : Comparable {
220+
internal let _index:
221+
ConcatenatedCollectionIndex<Base.SubSequence, Base.SubSequence>
222+
}
223+
224+
public func < <Base: Collection where Base.SubSequence: Collection>(
225+
lhs: RotatedCollectionIndex<Base>, rhs: RotatedCollectionIndex<Base>
226+
) -> Bool {
227+
return lhs._index < rhs._index
228+
}
229+
230+
public func == <Base: Collection where Base.SubSequence: Collection>(
231+
lhs: RotatedCollectionIndex<Base>, rhs: RotatedCollectionIndex<Base>
232+
) -> Bool {
233+
return lhs._index == rhs._index
234+
}
235+
236+
% for Traversal in TRAVERSALS:
237+
% Collection = collectionForTraversal(Traversal)
238+
% Self = "Rotated" + Collection
239+
240+
/// A rotated view onto a `${Collection}`.
241+
public struct ${Self}<
242+
Base : ${Collection} where Base.SubSequence: ${Collection}
243+
> : ${Collection} {
244+
let _concatenation: Concatenated${Collection}<
245+
Base.SubSequence, Base.SubSequence>
246+
247+
init(_base: Base, shiftingFirstFrom i: Base.Index) {
248+
_concatenation = concatenate(_base.suffix(from: i), _base.prefix(upTo: i))
249+
}
250+
251+
public typealias Index = RotatedCollectionIndex<Base>
252+
253+
public var startIndex: Index {
254+
return RotatedCollectionIndex(_index: _concatenation.startIndex)
255+
}
256+
257+
public var endIndex: Index {
258+
return RotatedCollectionIndex(_index: _concatenation.endIndex)
259+
}
260+
261+
public subscript(i: Index) -> Base.SubSequence.Iterator.Element {
262+
return _concatenation[i._index]
263+
}
264+
265+
public func index(after i: Index) -> Index {
266+
return RotatedCollectionIndex(_index: _concatenation.index(after: i._index))
267+
}
268+
269+
% if Traversal in ['Bidirectional', 'RandomAccess']:
270+
public func index(before i: Index) -> Index {
271+
return RotatedCollectionIndex(
272+
_index: _concatenation.index(before: i._index))
273+
}
274+
% end
275+
276+
public func index(_ i: Index, offsetBy n: ${Self}.IndexDistance) -> Index {
277+
return RotatedCollectionIndex(
278+
_index: _concatenation.index(i._index, offsetBy: n))
279+
}
280+
281+
/// The shifted position of the base collection's `startIndex`.
282+
public var shiftedStartIndex: Index {
283+
return RotatedCollectionIndex(
284+
_index: ConcatenatedCollectionIndex(
285+
second: _concatenation._base2.startIndex)
286+
)
287+
}
288+
}
289+
290+
extension ${Collection} where SubSequence: ${Collection} {
291+
/// Returns a view of this collection with the elements reordered such the
292+
/// element at the given position ends up first.
293+
///
294+
/// The subsequence of the collection up to `i` is shifted to after the
295+
/// subsequence starting at `i`. The order of the elements within each
296+
/// partition is otherwise unchanged.
297+
///
298+
/// let a = [10, 20, 30, 40, 50, 60, 70]
299+
/// let r = a.rotated(shiftingFirstFrom: 3)
300+
/// // r.elementsEqual([40, 50, 60, 70, 10, 20, 30])
301+
///
302+
/// - Parameter i: The position in the collection that should be first in the
303+
/// result. `i` must be a valid index of the collection.
304+
/// - Returns: A rotated view on the elements of this collection, such that
305+
/// the element at `i` is first.
306+
func rotated(shiftingFirstFrom i: Index) -> ${Self}<Self> {
307+
return ${Self}(_base: self, shiftingFirstFrom: i)
308+
}
309+
}
310+
311+
% end
312+
313+
//===----------------------------------------------------------------------===//
314+
// Tests
315+
//===----------------------------------------------------------------------===//
316+
317+
var suite = TestSuite("Rotated")
318+
319+
suite.test("concatenate") {
320+
let c = concatenate([1, 2, 3, 4, 5], 6...10)
321+
expectEqual(Array(1...10), Array(c))
322+
323+
let h = "Hello, "
324+
let w = "world!"
325+
let hw = concatenate(h.characters, w.characters)
326+
expectEqual("Hello, world!", String(hw))
327+
}
328+
329+
suite.test("rotated") {
330+
let range = 1...10
331+
let i = range.index(range.startIndex, offsetBy: 3)
332+
let rotated = range.rotated(shiftingFirstFrom: i)
333+
expectEqual([4, 5, 6, 7, 8, 9, 10, 1, 2, 3], Array(rotated))
334+
expectEqual(1, rotated[rotated.shiftedStartIndex])
335+
}
336+
337+
runAllTests()

0 commit comments

Comments
 (0)