|
| 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