diff --git a/Sources/FirebladeECS/Hashing.swift b/Sources/FirebladeECS/Hashing.swift index 07db01e..05a06f3 100644 --- a/Sources/FirebladeECS/Hashing.swift +++ b/Sources/FirebladeECS/Hashing.swift @@ -5,6 +5,56 @@ // Created by Christian Treffs on 16.10.17. // +// MARK: - hash combine +/// Calculates the combined hash of two values. This implementation is based on boost::hash_combine. +/// Will always produce the same result for the same combination of seed and value during the single run of a program. +/// +/// - Parameters: +/// - seed: seed hash. +/// - value: value to be combined with seed hash. +/// - Returns: combined hash value. +func hash(combine seed: Int, _ value: Int) -> Int { + /// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/combine.html + /// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/reference.html#boost.hash_combine + /// http://www.boost.org/doc/libs/1_65_1/boost/functional/hash/hash.hpp + /// http://book.huihoo.com/data-structures-and-algorithms-with-object-oriented-design-patterns-in-c++/html/page214.html + /// https://stackoverflow.com/a/35991300 + /// https://stackoverflow.com/a/4948967 + /* + let phi = (1.0 + sqrt(5.0)) / 2 // golden ratio + let a32 = pow(2.0,32.0) / phi + let a64 = pow(2.0,64.0) / phi + */ + #if arch(x86_64) || arch(arm64) // 64 bit + let fibA: UInt = 0x9e3779b97f4a7c15 // = 11400714819323198485 aka Fibonacci Hash a value for 2^64; calculate by: 2^64 / (golden ratio) + #elseif arch(i386) || arch(arm) // 32 bit + let fibA: UInt = 0x9e3779b9 // = 2654435769 aka Fibonacci Hash a value for 2^32; calculate by: 2^32 / (golden ratio) + #endif + var uSeed = UInt(bitPattern: seed) + let uValue = UInt(bitPattern: value) + uSeed ^= uValue &+ fibA &+ (uSeed << 6) &+ (uSeed >> 2) + return Int(bitPattern: uSeed) +} + +/// Calculates the combined hash value of the elements. This implementation is based on boost::hash_range. +/// Is sensitive to the order of the elements. +/// - Parameter hashValues: sequence of hash values to combine. +/// - Returns: combined hash value. +func hash(combine hashValues: S) -> Int where S.Element == Int { + /// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/reference.html#boost.hash_range_idp517643120 + return hashValues.reduce(0, { hash(combine: $0, $1) }) +} + +/// Calculates the combined hash value of the elements. This implementation is based on boost::hash_range. +/// Is sensitive to the order of the elements. +/// - Parameter hashValues: sequence of hash values to combine. +/// - Returns: combined hash value. +func hash(combine hashables: H) -> Int where H.Element == Hashable { + /// http://www.boost.org/doc/libs/1_65_1/doc/html/hash/reference.html#boost.hash_range_idp517643120 + return hashables.reduce(0, { hash(combine: $0, $1.hashValue) }) +} + +// MARK: - entity component hash extension EntityComponentHash { static func compose(entityId: EntityIdentifier, componentTypeHash: ComponentTypeHash) -> EntityComponentHash { diff --git a/Tests/FirebladeECSTests/HashingTests.swift b/Tests/FirebladeECSTests/HashingTests.swift index b802357..2ab0ff1 100644 --- a/Tests/FirebladeECSTests/HashingTests.swift +++ b/Tests/FirebladeECSTests/HashingTests.swift @@ -11,7 +11,7 @@ import XCTest class HashingTests: XCTestCase { - func test() { + func testCollisionsInCritialRange() { var hashSet: Set = Set()