 	/// SwifterSwift: Shuffle array. (Using Fisher-Yates Algorithm)
 	///
 	///		[1, 2, 3, 4, 5].shuffle() // shuffles array
-	///		// It also works for other types!
 	///
 	public mutating func shuffle() {
 		//http://stackoverflow.com/questions/37843647/shuffle-array-swift-3

	///
	///		[1, 2, 3, 4, 5].safeSwap(from: 3, to: 0) -> [4, 2, 3, 1, 5]
	///		["h", "e", "l", "l", "o"].safeSwap(from: 1, to: 0) -> ["e", "h", "l", "l", "o"]
	///		// It also works for other types!
	///
	/// - Parameters:
	///   - index: index of first element.
	///   - otherIndex: index of other element.
	public mutating func safeSwap(from index: Int, to otherIndex: Int)  {
		guard index != otherIndex,
              startIndex..<endIndex ~= index,
              startIndex..<endIndex ~= otherIndex else { return }
		Swift.swap(&self[index], &self[otherIndex])
	}
	

	public mutating func swap(from index: Int, to otherIndex: Int)  {
		Swift.swap(&self[index], &self[otherIndex])
	}
	
	/// SwifterSwift: Get first index where condition is met.
	///
	///		[1, 7, 1, 2, 4, 1, 6].firstIndex { $0 % 2 == 0 } -> 3
	///
	/// - Parameter condition: condition to evaluate each element against.
	/// - Returns: first index where the specified condition evaluates to true. (optional)
	public func firstIndex(where condition: (Element) throws -> Bool) rethrows -> Int? {
		for (index, value) in lazy.enumerated() {
			if try condition(value) { return index }
		}
		return nil
	}
	
	/// SwifterSwift: Get last index where condition is met.
	///
	///     [1, 7, 1, 2, 4, 1, 8].lastIndex { $0 % 2 == 0 } -> 6
	///
	/// - Parameter condition: condition to evaluate each element against.
	/// - Returns: last index where the specified condition evaluates to true. (optional)
	public func lastIndex(where condition: (Element) throws -> Bool) rethrows -> Int? {
		for (index, value) in lazy.enumerated().reversed() {
			if try condition(value) { return index }
		}
		return nil
	}
	
	/// SwifterSwift: Get all indices where condition is met.
	///
	///     [1, 7, 1, 2, 4, 1, 8].indices(where: { $0 == 1 }) -> [0, 2, 5]
	///
	/// - Parameter condition: condition to evaluate each element against.
	/// - Returns: all indices where the specified condition evaluates to true. (optional)
	public func indices(where condition: (Element) throws -> Bool) rethrows -> [Int]? {
		var indicies: [Int] = []
		for (index, value) in lazy.enumerated() {
			if try condition(value) { indicies.append(index) }
		}
		return indicies.isEmpty ? nil : indicies
	}
	
	/// SwifterSwift: Check if all elements in array match a conditon.
	///
	///		[2, 2, 4].all(matching: {$0 % 2 == 0}) -> true
	///		[1,2, 2, 4].all(matching: {$0 % 2 == 0}) -> false
	///
	/// - Parameter condition: condition to evaluate each element against.
	/// - Returns: true when all elements in the array match the specified condition.
	public func all(matching condition: (Element) throws -> Bool) rethrows -> Bool {
		return try !contains { try !condition($0) }
	}
	
	/// SwifterSwift: Check if no elements in array match a conditon.
	///
	///		[2, 2, 4].none(matching: {$0 % 2 == 0}) -> false
	///		[1, 3, 5, 7].none(matching: {$0 % 2 == 0}) -> true
	///
	/// - Parameter condition: condition to evaluate each element against.
	/// - Returns: true when no elements in the array match the specified condition.
	public func none(matching condition: (Element) throws -> Bool) rethrows -> Bool {
		return try !contains { try condition($0) }
	}
	
	/// SwifterSwift: Get last element that satisfies a conditon.
	///
	///		[2, 2, 4, 7].last(where: {$0 % 2 == 0}) -> 4
	///
	/// - Parameter condition: condition to evaluate each element against.
	/// - Returns: the last element in the array matching the specified condition. (optional)
	public func last(where condition: (Element) throws -> Bool) rethrows -> Element? {
		for element in reversed() {
			if try condition(element) { return element }
		}
		return nil
	}
	
	/// SwifterSwift: Filter elements based on a rejection condition.
	///
	///		[2, 2, 4, 7].reject(where: {$0 % 2 == 0}) -> [7]
	///
	/// - Parameter condition: to evaluate the exclusion of an element from the array.
	/// - Returns: the array with rejected values filtered from it.
	public func reject(where condition: (Element) throws -> Bool) rethrows -> [Element] {
		return try filter { return try !condition($0) }
	}
	
	/// SwifterSwift: Get element count based on condition.
	///
	///		[2, 2, 4, 7].count(where: {$0 % 2 == 0}) -> 3
	///
	/// - Parameter condition: condition to evaluate each element against.
	/// - Returns: number of times the condition evaluated to true.
	public func count(where condition: (Element) throws -> Bool) rethrows -> Int {
		var count = 0
		for element in self {
			if try condition(element) { count += 1 }
		}
		return count
	}
	
	/// SwifterSwift: Iterate over a collection in reverse order. (right to left)
	///
	///		[0, 2, 4, 7].forEachReversed({ print($0)}) -> //Order of print: 7,4,2,0
	///
	/// - Parameter body: a closure that takes an element of the array as a parameter.
	public func forEachReversed(_ body: (Element) throws -> Void) rethrows {
		try reversed().forEach { try body($0) }
	}
	
	/// SwifterSwift: Calls given closure with each element where condition is true.
	///
	///		[0, 2, 4, 7].forEach( where: {$0 % 2 == 0}, body: { print($0)}) -> //print: 0, 2, 4
	///
	/// - Parameters:
	///   - condition: condition to evaluate each element against.
	///   - body: a closure that takes an element of the array as a parameter.
	public func forEach(where condition: (Element) throws -> Bool, body: (Element) throws -> Void) rethrows {
		for element in self where try condition(element) {
			try body(element)
		}
	}
	
	/// SwifterSwift: Reduces an array while returning each interim combination.
	///
	///     [1, 2, 3].accumulate(initial: 0, next: +) -> [1, 3, 6]
	///
	/// - Parameters:
	///   - initial: initial value.
	///   - next: closure that combines the accumulating value and next element of the array.
	/// - Returns: an array of the final accumulated value and each interim combination.
	public func accumulate<U>(initial: U, next: (U, Element) throws -> U) rethrows -> [U] {
		var runningTotal = initial
		return try map { element in
			runningTotal = try next(runningTotal, element)
			return runningTotal
		}
	}
	
	/// SwifterSwift: Filtered and map in a single operation.
	///
	///     [1,2,3,4,5].filtered({ $0 % 2 == 0 }, map: { $0.string }) -> ["2", "4"]
	///
	/// - Parameters:
	///   - isIncluded: condition of inclusion to evaluate each element against.
	///   - transform: transform element function to evaluate every element.
	/// - Returns: Return an filtered and mapped array.
	public func filtered<T>(_ isIncluded: (Element) throws -> Bool, map transform: (Element) throws -> T) rethrows ->  [T] {
		return try flatMap({
			if try isIncluded($0) {
				return try transform($0)
			}
			return nil
		})
	}
	
	
	/// SwifterSwift: Keep elements of Array while condition is true.
	///
	///		[0, 2, 4, 7].keep( where: {$0 % 2 == 0}) -> [0, 2, 4]
	///
	/// - Parameter condition: condition to evaluate each element against.
	public mutating func keep(while condition: (Element) throws -> Bool) rethrows {
		for (index, element) in lazy.enumerated() {
			if try !condition(element) {
				self = Array(self[startIndex..<index])
				break
			}
		}
	}
	
	/// SwifterSwift: Take element of Array while condition is true.
	///
	///		[0, 2, 4, 7, 6, 8].take( where: {$0 % 2 == 0}) -> [0, 2, 4]
	///
	/// - Parameter condition: condition to evaluate each element against.
	/// - Returns: All elements up until condition evaluates to false.
	public func take(while condition: (Element) throws -> Bool) rethrows -> [Element] {
		for (index, element) in lazy.enumerated() {
			if try !condition(element) {
				return Array(self[startIndex..<index])
			}
		}
		return self
	}
	
	/// SwifterSwift: Skip elements of Array while condition is true.
	///
	///		[0, 2, 4, 7, 6, 8].skip( where: {$0 % 2 == 0}) -> [6, 8]
	///
	/// - Parameter condition: condition to eveluate each element against.
	/// - Returns: All elements after the condition evaluates to false.
	public func skip(while condition: (Element) throws-> Bool) rethrows -> [Element] {
		for (index, element) in lazy.enumerated() {
			if try !condition(element) {
				return Array(self[index..<endIndex])
			}
		}
		return [Element]()
	}
	
	/// SwifterSwift: Calls given closure with an array of size of the parameter slice where condition is true.
	///
	///     [0, 2, 4, 7].forEach(slice: 2) { print($0) } -> //print: [0, 2], [4, 7]
	///     [0, 2, 4, 7, 6].forEach(slice: 2) { print($0) } -> //print: [0, 2], [4, 7], [6]
	///
	/// - Parameters:
	///   - slice: size of array in each interation.
	///   - body: a closure that takes an array of slice size as a parameter.
	public func forEach(slice: Int,  body: ([Element]) throws -> Void) rethrows {
		guard slice > 0, !isEmpty else { return }
		
		var value : Int = 0
		while value < count {
			try body(Array(self[Swift.max(value,startIndex)..<Swift.min(value + slice, endIndex)]))
			value += slice
		}
	}
	
	
	/// SwifterSwift: Returns an array of slices of length "size" from the array.  If array can't be split evenly, the final slice will be the remaining elements.
	///
	///     [0, 2, 4, 7].group(by: 2) -> [[0, 2], [4, 7]]
	///     [0, 2, 4, 7, 6].group(by: 2) -> [[0, 2], [4, 7], [6]]
	///
	/// - Parameters:
	///   - size: The size of the slices to be returned.
	public func group(by size: Int) -> [[Element]]? {
		//Inspired by: https://lodash.com/docs/4.17.4#chunk
		guard size > 0, !isEmpty else { return nil }
		var value : Int = 0
		var slices : [[Element]] = []
		while value < count {
			slices.append(Array(self[Swift.max(value,startIndex)..<Swift.min(value + size, endIndex)]))
			value += size
		}
		return slices
	}
	
	/// SwifterSwift: Group the elements of the array in a dictionary.
	///
	///     [0, 2, 5, 4, 7].groupByKey { $0%2 ? "evens" : "odds" } -> [ "evens" : [0, 2, 4], "odds" : [5, 7] ]
	///
	/// - Parameter getKey: Clousure to define the key for each element.
	/// - Returns: A dictionary with values grouped with keys.
	public func groupByKey<K: Hashable>(keyForValue: (_ element: Element) throws -> K) rethrows -> [K: [Element]] {
		var group : [K: [Element]] = [:]
		for value in self {
			let key = try keyForValue(value)
			group[key] = (group[key] ?? []) + [value]
		}
		return group
	}
	
	/// SwifterSwift: Returns a new rotated array by the given places.
	///
	///     [1, 2, 3, 4].rotated(by: 1) -> [4,1,2,3]
	///     [1, 2, 3, 4].rotated(by: 3) -> [2,3,4,1]
	///     [1, 2, 3, 4].rotated(by: -1) -> [2,3,4,1]
	///
	/// - Parameter places: Number of places that the array be rotated. If the value is positive the end becomes the start, if it negative it's that start becom the end.
	/// - Returns: The new rotated array
	public func rotated(by places: Int) -> [Element] {
		//Inspired by: https://ruby-doc.org/core-2.2.0/Array.html#method-i-rotate
		guard places != 0 && places < count else {
			return self
		}
		var array : [Element] = self
		if places > 0 {
			let range = (array.count - places)..<array.endIndex
			let slice = array[range]
			array.removeSubrange(range)
			array.insert(contentsOf: slice, at: 0)
		} else {
			let range = array.startIndex..<(places * -1)
			let slice = array[range]
			array.removeSubrange(range)
			array.append(contentsOf: slice)
		}
		return array
	}
	
	/// SwifterSwift: Rotate the array by the given places.
	///
	///     [1, 2, 3, 4].rotate(by: 1) -> [4,1,2,3]
	///     [1, 2, 3, 4].rotate(by: 3) -> [2,3,4,1]
	///     [1, 2, 3, 4].rotated(by: -1) -> [2,3,4,1]
	///
	/// - Parameter places: Number of places that the array should be rotated. If the value is positive the end becomes the start, if it negative it's that start becom the end.
	public mutating func rotate(by places: Int) {
		self = rotated(by: places)
	}
}


