blob: 1468ab85ad578e80fa4159110544c85dd3865783 [file] [log] [blame]
//===--- ClosedRange.swift ------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// FIXME: swift-3-indexing-model: Generalize all tests to check both
// [Closed]Range.
/// An interval from a lower bound up to, and including, an upper bound.
///
/// You create a `ClosedRange` instance by using the closed range
/// operator (`...`).
///
/// let throughFive = 0...5
///
/// A `ClosedRange` instance contains both its lower bound and its
/// upper bound.
///
/// throughFive.contains(3)
/// // true
/// throughFive.contains(10)
/// // false
/// throughFive.contains(5)
/// // true
///
/// Because a closed range includes its upper bound, a closed range whose lower
/// bound is equal to the upper bound contains that value. Therefore, a
/// `ClosedRange` instance cannot represent an empty range.
///
/// let zeroInclusive = 0...0
/// zeroInclusive.contains(0)
/// // true
/// zeroInclusive.isEmpty
/// // false
///
/// Using a Closed Range as a Collection of Consecutive Values
/// ----------------------------------------------------------
///
/// When a closed range uses integers as its lower and upper bounds, or any
/// other type that conforms to the `Strideable` protocol with an integer
/// stride, you can use that range in a `for`-`in` loop or with any sequence or
/// collection method. The elements of the range are the consecutive values
/// from its lower bound up to, and including, its upper bound.
///
/// for n in 3...5 {
/// print(n)
/// }
/// // Prints "3"
/// // Prints "4"
/// // Prints "5"
///
/// Because floating-point types such as `Float` and `Double` are their own
/// `Stride` types, they cannot be used as the bounds of a countable range. If
/// you need to iterate over consecutive floating-point values, see the
/// `stride(from:through:by:)` function.
@_fixed_layout
public struct ClosedRange<Bound: Comparable> {
/// The range's lower bound.
public let lowerBound: Bound
/// The range's upper bound.
public let upperBound: Bound
/// Creates an instance with the given bounds.
///
/// Because this initializer does not perform any checks, it should be used
/// as an optimization only when you are absolutely certain that `lower` is
/// less than or equal to `upper`. Using the closed range operator (`...`)
/// to form `ClosedRange` instances is preferred.
///
/// - Parameter bounds: A tuple of the lower and upper bounds of the range.
@inlinable
public init(uncheckedBounds bounds: (lower: Bound, upper: Bound)) {
self.lowerBound = bounds.lower
self.upperBound = bounds.upper
}
}
// define isEmpty, which is available even on an uncountable ClosedRange
extension ClosedRange {
/// A Boolean value indicating whether the range contains no elements.
///
/// Because a closed range cannot represent an empty range, this property is
/// always `false`.
@inlinable
public var isEmpty: Bool {
return false
}
}
extension ClosedRange: RangeExpression {
@inlinable // trivial-implementation
public func relative<C: Collection>(to collection: C) -> Range<Bound>
where C.Index == Bound {
return Range(
uncheckedBounds: (
lower: lowerBound, upper: collection.index(after: self.upperBound)))
}
/// Returns a Boolean value indicating whether the given element is contained
/// within the range.
///
/// A `ClosedRange` instance contains both its lower and upper bound.
/// `element` is contained in the range if it is between the two bounds or
/// equal to either bound.
///
/// - Parameter element: The element to check for containment.
/// - Returns: `true` if `element` is contained in the range; otherwise,
/// `false`.
@inlinable
public func contains(_ element: Bound) -> Bool {
return element >= self.lowerBound && element <= self.upperBound
}
}
extension ClosedRange: Sequence
where Bound: Strideable, Bound.Stride: SignedInteger {
public typealias Element = Bound
public typealias Iterator = IndexingIterator<ClosedRange<Bound>>
}
extension ClosedRange where Bound : Strideable, Bound.Stride : SignedInteger {
@_frozen // FIXME(resilience)
public enum Index {
case pastEnd
case inRange(Bound)
}
}
extension ClosedRange.Index : Comparable {
@inlinable
public static func == (
lhs: ClosedRange<Bound>.Index,
rhs: ClosedRange<Bound>.Index
) -> Bool {
switch (lhs, rhs) {
case (.inRange(let l), .inRange(let r)):
return l == r
case (.pastEnd, .pastEnd):
return true
default:
return false
}
}
@inlinable
public static func < (
lhs: ClosedRange<Bound>.Index,
rhs: ClosedRange<Bound>.Index
) -> Bool {
switch (lhs, rhs) {
case (.inRange(let l), .inRange(let r)):
return l < r
case (.inRange, .pastEnd):
return true
default:
return false
}
}
}
extension ClosedRange.Index: Hashable
where Bound: Strideable, Bound.Stride: SignedInteger, Bound: Hashable {
/// Hashes the essential components of this value by feeding them into the
/// given hasher.
///
/// - Parameter hasher: The hasher to use when combining the components
/// of this instance.
@inlinable
public func hash(into hasher: inout Hasher) {
switch self {
case .inRange(let value):
hasher.combine(0 as Int8)
hasher.combine(value)
case .pastEnd:
hasher.combine(1 as Int8)
}
}
}
// FIXME: this should only be conformance to RandomAccessCollection but
// the compiler balks without all 3
extension ClosedRange: Collection, BidirectionalCollection, RandomAccessCollection
where Bound : Strideable, Bound.Stride : SignedInteger
{
// while a ClosedRange can't be empty, a _slice_ of a ClosedRange can,
// so ClosedRange can't be its own self-slice unlike Range
public typealias SubSequence = Slice<ClosedRange<Bound>>
/// The position of the first element in the range.
@inlinable
public var startIndex: Index {
return .inRange(lowerBound)
}
/// The range's "past the end" position---that is, the position one greater
/// than the last valid subscript argument.
@inlinable
public var endIndex: Index {
return .pastEnd
}
@inlinable
public func index(after i: Index) -> Index {
switch i {
case .inRange(let x):
return x == upperBound
? .pastEnd
: .inRange(x.advanced(by: 1))
case .pastEnd:
_preconditionFailure("Incrementing past end index")
}
}
@inlinable
public func index(before i: Index) -> Index {
switch i {
case .inRange(let x):
_precondition(x > lowerBound, "Incrementing past start index")
return .inRange(x.advanced(by: -1))
case .pastEnd:
_precondition(upperBound >= lowerBound, "Incrementing past start index")
return .inRange(upperBound)
}
}
@inlinable
public func index(_ i: Index, offsetBy distance: Int) -> Index {
switch i {
case .inRange(let x):
let d = x.distance(to: upperBound)
if distance <= d {
let newPosition = x.advanced(by: numericCast(distance))
_precondition(newPosition >= lowerBound,
"Advancing past start index")
return .inRange(newPosition)
}
if d - -1 == distance { return .pastEnd }
_preconditionFailure("Advancing past end index")
case .pastEnd:
if distance == 0 {
return i
}
if distance < 0 {
return index(.inRange(upperBound), offsetBy: numericCast(distance + 1))
}
_preconditionFailure("Advancing past end index")
}
}
@inlinable
public func distance(from start: Index, to end: Index) -> Int {
switch (start, end) {
case let (.inRange(left), .inRange(right)):
// in range <--> in range
return numericCast(left.distance(to: right))
case let (.inRange(left), .pastEnd):
// in range --> end
return numericCast(1 + left.distance(to: upperBound))
case let (.pastEnd, .inRange(right)):
// in range <-- end
return numericCast(upperBound.distance(to: right) - 1)
case (.pastEnd, .pastEnd):
// end <--> end
return 0
}
}
/// Accesses the element at specified position.
///
/// You can subscript a collection with any valid index other than the
/// collection's end index. The end index refers to the position one past
/// the last element of a collection, so it doesn't correspond with an
/// element.
///
/// - Parameter position: The position of the element to access. `position`
/// must be a valid index of the range, and must not equal the range's end
/// index.
@inlinable
public subscript(position: Index) -> Bound {
// FIXME: swift-3-indexing-model: range checks and tests.
switch position {
case .inRange(let x): return x
case .pastEnd: _preconditionFailure("Index out of range")
}
}
@inlinable
public subscript(bounds: Range<Index>)
-> Slice<ClosedRange<Bound>> {
return Slice(base: self, bounds: bounds)
}
@inlinable
public func _customContainsEquatableElement(_ element: Bound) -> Bool? {
return lowerBound <= element && element <= upperBound
}
@inlinable
public func _customIndexOfEquatableElement(_ element: Bound) -> Index?? {
return lowerBound <= element && element <= upperBound
? .inRange(element) : nil
}
@inlinable
public func _customLastIndexOfEquatableElement(_ element: Bound) -> Index?? {
// The first and last elements are the same because each element is unique.
return _customIndexOfEquatableElement(element)
}
}
extension Comparable {
/// Returns a closed range that contains both of its bounds.
///
/// Use the closed range operator (`...`) to create a closed range of any type
/// that conforms to the `Comparable` protocol. This example creates a
/// `ClosedRange<Character>` from "a" up to, and including, "z".
///
/// let lowercase = "a"..."z"
/// print(lowercase.contains("z"))
/// // Prints "true"
///
/// - Parameters:
/// - minimum: The lower bound for the range.
/// - maximum: The upper bound for the range.
@_transparent
public static func ... (minimum: Self, maximum: Self) -> ClosedRange<Self> {
_precondition(
minimum <= maximum, "Can't form Range with upperBound < lowerBound")
return ClosedRange(uncheckedBounds: (lower: minimum, upper: maximum))
}
}
extension ClosedRange: Equatable {
/// Returns a Boolean value indicating whether two ranges are equal.
///
/// Two ranges are equal when they have the same lower and upper bounds.
///
/// let x = 5...15
/// print(x == 5...15)
/// // Prints "true"
/// print(x == 10...20)
/// // Prints "false"
///
/// - Parameters:
/// - lhs: A range to compare.
/// - rhs: Another range to compare.
@inlinable
public static func == (
lhs: ClosedRange<Bound>, rhs: ClosedRange<Bound>
) -> Bool {
return lhs.lowerBound == rhs.lowerBound && lhs.upperBound == rhs.upperBound
}
}
extension ClosedRange: Hashable where Bound: Hashable {
@inlinable
public func hash(into hasher: inout Hasher) {
hasher.combine(lowerBound)
hasher.combine(upperBound)
}
}
extension ClosedRange : CustomStringConvertible {
/// A textual representation of the range.
@inlinable // trivial-implementation...
public var description: String {
return "\(lowerBound)...\(upperBound)"
}
}
extension ClosedRange : CustomDebugStringConvertible {
/// A textual representation of the range, suitable for debugging.
public var debugDescription: String {
return "ClosedRange(\(String(reflecting: lowerBound))"
+ "...\(String(reflecting: upperBound)))"
}
}
extension ClosedRange : CustomReflectable {
public var customMirror: Mirror {
return Mirror(
self, children: ["lowerBound": lowerBound, "upperBound": upperBound])
}
}
extension ClosedRange {
/// Returns a copy of this range clamped to the given limiting range.
///
/// The bounds of the result are always limited to the bounds of `limits`.
/// For example:
///
/// let x: ClosedRange = 0...20
/// print(x.clamped(to: 10...1000))
/// // Prints "10...20"
///
/// If the two ranges do not overlap, the result is a single-element range at
/// the upper or lower bound of `limits`.
///
/// let y: ClosedRange = 0...5
/// print(y.clamped(to: 10...1000))
/// // Prints "10...10"
///
/// - Parameter limits: The range to clamp the bounds of this range.
/// - Returns: A new range clamped to the bounds of `limits`.
@inlinable // trivial-implementation
@inline(__always)
public func clamped(to limits: ClosedRange) -> ClosedRange {
let lower =
limits.lowerBound > self.lowerBound ? limits.lowerBound
: limits.upperBound < self.lowerBound ? limits.upperBound
: self.lowerBound
let upper =
limits.upperBound < self.upperBound ? limits.upperBound
: limits.lowerBound > self.upperBound ? limits.lowerBound
: self.upperBound
return ClosedRange(uncheckedBounds: (lower: lower, upper: upper))
}
}
extension ClosedRange where Bound: Strideable, Bound.Stride : SignedInteger {
/// Creates an instance equivalent to the given `Range`.
///
/// - Parameter other: A `Range` to convert to a `ClosedRange` instance.
///
/// An equivalent range must be representable as a closed range.
/// For example, passing an empty range as `other` triggers a runtime error,
/// because an empty range cannot be represented by a closed range instance.
public init(_ other: Range<Bound>) {
_precondition(!other.isEmpty, "Can't form an empty closed range")
let upperBound = other.upperBound.advanced(by: -1)
self.init(uncheckedBounds: (lower: other.lowerBound, upper: upperBound))
}
}
extension ClosedRange {
@inlinable
public func overlaps(_ other: ClosedRange<Bound>) -> Bool {
return self.contains(other.lowerBound) || other.contains(lowerBound)
}
@inlinable
public func overlaps(_ other: Range<Bound>) -> Bool {
return other.overlaps(self)
}
}
// Note: this is not for compatibility only, it is considered a useful
// shorthand. TODO: Add documentation
public typealias CountableClosedRange<Bound: Strideable> = ClosedRange<Bound>
where Bound.Stride : SignedInteger
extension ClosedRange: Decodable where Bound: Decodable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let lowerBound = try container.decode(Bound.self)
let upperBound = try container.decode(Bound.self)
guard lowerBound <= upperBound else {
throw DecodingError.dataCorrupted(
DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Cannot initialize \(ClosedRange.self) with a lowerBound (\(lowerBound)) greater than upperBound (\(upperBound))"))
}
self.init(uncheckedBounds: (lower: lowerBound, upper: upperBound))
}
}
extension ClosedRange: Encodable where Bound: Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(self.lowerBound)
try container.encode(self.upperBound)
}
}