 import Foundation
 
 
-protocol HashTableElementType {
+protocol HashTableElementType : Hashable {
+    associatedtype Key : Hashable
     associatedtype Element
     init(elem: Element)
-    var key : Int { get }
-    var elem : Element { get }
+    var key : Key       { get }
+    var value : Element { get }
     var hashValue : Int { get }
 }
 extension HashTableElementType {
-    var hashValue : Int { return key }
+    var hashValue : Int { return key.hashValue }
+    var tuple : (Int, Element) { return (hashValue, value) }
 }
 
 struct HashTable <K: Hashable, V> : ArrayLiteralConvertible, CustomStringConvertible {

        let oldTable = self.table
        let oldResetBuckets = self.resetBuckets
        do {
            self.resetBuckets = false
            self.hashFunction = f
            self.table = [[(key: K, value: V)]](count: buckets, repeatedValue: [])

    var decreaseBuckets: Bool = true { didSet { if decreaseBuckets && (!oldValue || !resetBuckets) { resetBuckets = true } } }
    var increaseBuckets: Bool = true { didSet { if increaseBuckets && (!oldValue || !resetBuckets) { resetBuckets = true } } }
    
    init(_ array: [(key: K, value: V)], buckets: Int = 0x10) throws {
        self.init(buckets: buckets, resetBuckets: false)
        try insert(array)
    }
    
    init(buckets: Int = 0x10, resetBuckets: Bool = true, hashFunction: K -> Int = { $0.hashValue }) {
        table = [[(key: K, value: V)]](count: buckets.nextPowerOf2, repeatedValue: [])
        self.hashFunction = hashFunction
        self.resetBuckets = resetBuckets
    }
    
    //P.ERFORMANCE maybe in parallel or hashing all first in parallel, then appending all?
    mutating func insert(elements elements: (key: K, value: V)...) throws {
        try insert(elements)
    }
    
    //P.ERFORMANCE maybe in parallel or hashing all first in parallel, then appending all?
    mutating func insert(elements: [(key: K, value: V)]) throws {
        try correctBucketCount(count + elements.count)
        let oldResetBuckets = resetBuckets
        self.resetBuckets = false
        for v in elements {
            try insert(element: v)
        }
        print("tries to correct in \(#function)")
        self.resetBuckets = oldResetBuckets
        try correctBucketCount()
    }
    
