Last active
November 25, 2019 01:46
-
-
Save Qata/072f12a13f5d658edc5ec211819913a0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public extension Collection { | |
/// Allows lensing properties before comparison by the std lib `firstIndex(where:)` function, using the `where` closure. | |
func firstIndex<T>(lensing path: KeyPath<Element, T>, where predicate: (T) -> Bool) -> Index? { | |
return firstIndex { | |
predicate($0[keyPath: path]) | |
} | |
} | |
} | |
public extension Collection { | |
/// Same functionality as `joined` but without the flattening. | |
func intersperse(element: Element) -> [Element] { | |
Array( | |
flatMap { [$0, element] }.dropLast() | |
) | |
} | |
func chunked<I: BinaryInteger>(stride length: I) -> [SubSequence] { | |
stride(from: 0, to: count, by: numericCast(length)) | |
.map { dropFirst($0).prefix(numericCast(length)) } | |
} | |
/// Create `strides.count` chunks of the size given by their associated `BinaryInteger`. | |
/// If a chunk's length cannot be satisfied then it'll return the maximum length available, but discards when the length is `0`. | |
func chunked<I: BinaryInteger>(strides: I...) -> [SubSequence] { | |
chunked(strides: strides) | |
} | |
/// Create `strides.count` chunks of the size given by their associated `BinaryInteger`. | |
/// If a chunk's length cannot be satisfied then it'll return the maximum length available, but discards when the length is `0`. | |
func chunked<S: Sequence>(strides: S) -> [SubSequence] where S.Element: BinaryInteger { | |
zip(strides, strides.scan(0, +).prefix { $0 < count }) | |
.map { dropFirst(numericCast($1)).prefix(numericCast($0)) } | |
} | |
func rotated<I: BinaryInteger>(by steps: I) -> [Element] { | |
let normalised = Int(steps) % count | |
switch normalised.signum() { | |
case 0: | |
return .init(self) | |
case 1: | |
return .init([suffix(normalised), dropLast(normalised)].joined()) | |
case -1: | |
return .init([dropFirst(-normalised), prefix(-normalised)].joined()) | |
default: | |
fatalError() | |
} | |
} | |
} | |
public extension Collection where Element: BinaryFloatingPoint { | |
/// Normalizes any floating point numbers to fit within the new range. | |
/// The range of the values provided is assumed to extend from the min element to the max element. | |
func normalize(into range: ClosedRange<Element>) -> [Element] { | |
guard let min = self.min(), let max = self.max() else { return [] } | |
return normalize(from: min...max, into: range) | |
} | |
/// Normalizes any floating point numbers to fit within the new range. | |
func normalize(from: ClosedRange<Element>, into: ClosedRange<Element>) -> [Element] { | |
let fromMaxMinusMin = from.upperBound - from.lowerBound | |
let intoMaxMinusMin = into.upperBound - into.lowerBound | |
return map { | |
Swift.max( | |
into.lowerBound, | |
Swift.min( | |
into.upperBound, | |
($0 - from.lowerBound) / fromMaxMinusMin * intoMaxMinusMin + into.lowerBound | |
) | |
) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftCheck | |
import XCTest | |
extension ClosedRange: Arbitrary where Bound: Arbitrary { | |
public static var arbitrary: Gen<ClosedRange<Bound>> { | |
return Gen | |
.zip(Bound.arbitrary, Bound.arbitrary) | |
.map { Swift.min($0, $1)...Swift.max($0, $1) } | |
} | |
} | |
class CollectionTests: XCTestCase { | |
func testChunked() { | |
property("All chunked arrays other than the last one will have n elements") <- forAll { (i: Positive<Int>, n: UInt) in | |
Array(repeating: (), count: Int(n)) | |
.chunked(stride: i.getPositive) | |
.dropLast() | |
.allSatisfy { $0.count == i.getPositive } | |
} | |
property("If the length is not divisible by the stride, the last array will have n % i elements") <- forAll { (i: Positive<Int>, n: Positive<Int>) in | |
n.getPositive % i.getPositive != 0 ==> { | |
Array(repeating: (), count: n.getPositive) | |
.chunked(stride: i.getPositive) | |
.last? | |
.count == (n.getPositive % i.getPositive) | |
} | |
} | |
} | |
func testNormalize() { | |
property("Normalize will produce values within the range") <- forAll { (values: [Double], range: ClosedRange<Double>) in | |
values.normalize(into: range).allSatisfy(range.contains) | |
} | |
property("Normalize will produce values within the to range when manually supplied a from") <- forAllNoShrink(Double.arbitrary.proliferateNonEmpty, ClosedRange<Double>.arbitrary, ClosedRange<Double>.arbitrary) { values, fromRange, toRange in | |
(fromRange.lowerBound <= values.min()! && fromRange.upperBound >= values.max()!) ==> { | |
values.normalize(from: fromRange, into: toRange).allSatisfy(toRange.contains) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment