Files
appchk-app/main/Extensions/Array.swift
2020-06-05 16:08:17 +02:00

89 lines
3.1 KiB
Swift

import Foundation
//extension Collection {
// subscript(ifExist i: Index?) -> Iterator.Element? {
// guard let i = i else { return nil }
// return indices.contains(i) ? self[i] : nil
// }
//}
extension Range where Bound == Int {
@inline(__always) func arr() -> [Bound] { self.map { $0 } }
}
// MARK: - Sorted Array
extension Array {
typealias CompareFn = (Element, Element) -> Bool
/// Binary tree search operation.
/// - Warning: Array must be sorted already.
/// - Parameters:
/// - mustExist: Determine whether to return low index or `nil` if element is missing.
/// - first: If `true`, keep searching for first matching element.
/// - Returns: Index or `nil` (only if `mustExist = true` and element does not exist).
/// - Complexity: O(log *n*), where *n* is the length of the array.
func binTreeIndex(of element: Element, compare fn: CompareFn, mustExist: Bool = false, findFirst: Bool = false) -> Int? {
var found = false
var lo = 0, hi = self.count - 1
while lo <= hi {
let mid = (lo + hi)/2
if fn(self[mid], element) {
lo = mid + 1
} else if fn(element, self[mid]) {
hi = mid - 1
} else {
if !findFirst { return mid } // exit early if we dont care about first index
hi = mid - 1
found = true
}
}
return (mustExist && !found) ? nil : lo // not found, would be inserted at position lo
}
/// Binary tree lookup whether element exists. Performs `binTreeIndex(of:compare:mustExist:)` internally.
func binTreeExists(_ element: Element, compare fn: CompareFn) -> Bool {
binTreeIndex(of: element, compare: fn, mustExist: true) != nil
}
/// Binary tree insert operation
/// - Warning: Array must be sorted already.
/// - Returns: Index at which `elem` was inserted
/// - Complexity: O(log *n*), where *n* is the length of the array.
@discardableResult mutating func binTreeInsert(_ elem: Element, compare fn: CompareFn) -> Int {
let newIndex = binTreeIndex(of: elem, compare: fn)!
insert(elem, at: newIndex)
return newIndex
}
/// Binary tree remove operation
/// - Warning: Array must be sorted already.
/// - Returns: Index of removed `elem` or `nil` if it does not exist
/// - Complexity: O(log *n*), where *n* is the length of the array.
@discardableResult mutating func binTreeRemove(_ elem: Element, compare fn: CompareFn) -> Int? {
if let i = binTreeIndex(of: elem, compare: fn, mustExist: true) {
remove(at: i)
return i
}
return nil
}
/// Sorted synchronous comparison between elements
/// - Parameter sortedSubset: Must be a strict subset of the sorted array.
/// - Returns: List of elements that are **not** present in `sortedSubset`.
/// - Complexity: O(*m*+*n*), where *n* is the length of the array and *m* the length of the `sortedSubset`.
/// If indices are found earlier, *n* may be significantly less (on average: `n/2`)
func difference(toSubset sortedSubset: [Element], compare fn: CompareFn) -> [Element] {
var result: [Element] = []
var iter = makeIterator()
for rhs in sortedSubset {
while let lhs = iter.next(), fn(lhs, rhs) {
result.append(lhs)
}
}
result.append(contentsOf: iter)
return result
}
}