diff --git a/.gitignore b/.gitignore index 8615121..38c76c1 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ DerivedData *.hmap *.ipa *.xcuserstate +!*.xcodeproj/xcshareddata/ # CocoaPods # diff --git a/VPTree/VPTree.xcodeproj/project.pbxproj b/VPTree/VPTree.xcodeproj/project.pbxproj index fcfc380..603263a 100644 --- a/VPTree/VPTree.xcodeproj/project.pbxproj +++ b/VPTree/VPTree.xcodeproj/project.pbxproj @@ -86,11 +86,11 @@ isa = PBXGroup; children = ( 6FA8D7F11B23417E00821B73 /* VPTree.h */, - 6FA8D7EF1B23417E00821B73 /* Supporting Files */, + 6FA8D8151B2499B000821B73 /* Array.swift */, + 6FA8D80E1B23449600821B73 /* PriorityQueue.swift */, 6FA8D8081B23419000821B73 /* VPNode.swift */, 6FA8D80A1B2341A200821B73 /* VPTree.swift */, - 6FA8D80E1B23449600821B73 /* PriorityQueue.swift */, - 6FA8D8151B2499B000821B73 /* Array.swift */, + 6FA8D7EF1B23417E00821B73 /* Supporting Files */, ); path = VPTree; sourceTree = ""; @@ -177,7 +177,7 @@ 6FA8D7E31B23417E00821B73 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0640; + LastUpgradeCheck = 1230; ORGANIZATIONNAME = Sunday; TargetAttributes = { 6FA8D7EB1B23417E00821B73 = { @@ -190,10 +190,11 @@ }; buildConfigurationList = 6FA8D7E61B23417E00821B73 /* Build configuration list for PBXProject "VPTree" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 6FA8D7E21B23417E00821B73; productRefGroup = 6FA8D7ED1B23417E00821B73 /* Products */; @@ -263,17 +264,29 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -281,6 +294,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -296,7 +310,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -311,17 +325,29 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -338,9 +364,10 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.4; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -359,9 +386,11 @@ INFOPLIST_FILE = VPTree/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.sunday.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -376,8 +405,10 @@ INFOPLIST_FILE = VPTree/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.sunday.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -394,6 +425,7 @@ ); INFOPLIST_FILE = VPTreeTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.sunday.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -407,6 +439,7 @@ ); INFOPLIST_FILE = VPTreeTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.sunday.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -430,6 +463,7 @@ 6FA8D8041B23417E00821B73 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; 6FA8D8051B23417E00821B73 /* Build configuration list for PBXNativeTarget "VPTreeTests" */ = { isa = XCConfigurationList; @@ -438,6 +472,7 @@ 6FA8D8071B23417E00821B73 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/VPTree/VPTree.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/VPTree/VPTree.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/VPTree/VPTree.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/VPTree/VPTree/Array.swift b/VPTree/VPTree/Array.swift index 1e14e1e..a38dab1 100644 --- a/VPTree/VPTree/Array.swift +++ b/VPTree/VPTree/Array.swift @@ -1,14 +1,8 @@ -// -// Array.swift -// VPTree -// -// Created by Laurent CHENAY on 07/06/2015. -// Copyright (c) 2015 Sunday. All rights reserved. -// - -import Foundation - -private func split(inout array: [T], nbItemLeft: Int, nbItemRight: Int) -> (Array, Array) { +private func split( + _ array: inout [T], + _ nbItemLeft: Int, + _ nbItemRight: Int +) -> (Array, Array) { var left: Array = [] var right: Array = [] var middle: Array = [] @@ -52,20 +46,26 @@ private func split(inout array: [T], nbItemLeft: Int, nbItemRight } if left.count > nbItemLeft { - let (subLeft: [T], subRight: [T]) = split(&left, nbItemLeft, nbItemRight - right.count) - return (subLeft, right + subRight) + let sub: (left: [T], right: [T]) + = split(&left, nbItemLeft, nbItemRight - right.count) + return (sub.left, right + sub.right) } else if right.count > nbItemRight { - let (subLeft: [T], subRight: [T]) = split(&right, nbItemLeft - left.count, nbItemRight) - return (left + subLeft, subRight) + let sub: (left: [T], right: [T]) + = split(&right, nbItemLeft - left.count, nbItemRight) + return (left + sub.left, sub.right) } else { return (left, right) } } internal extension Array { - internal func splitByMedian() -> ([T], [T]) { + + func splitByMedian() -> ([T], [T]) where T: Comparable { let mid = count / 2 - var array: Array = self.map {return $0 as! T} - return split(&array, count-mid, mid) + var array: Array = self.map { + return $0 as! T + } + return VPTree.split(&array, count-mid, mid) } + } diff --git a/VPTree/VPTree/Info.plist b/VPTree/VPTree/Info.plist index 5c6040f..d3de8ee 100644 --- a/VPTree/VPTree/Info.plist +++ b/VPTree/VPTree/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.sunday.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/VPTree/VPTree/PriorityQueue.swift b/VPTree/VPTree/PriorityQueue.swift index daaae7d..245d4a1 100644 --- a/VPTree/VPTree/PriorityQueue.swift +++ b/VPTree/VPTree/PriorityQueue.swift @@ -1,6 +1,4 @@ -import Foundation - -class PriorityQueue { +struct PriorityQueue { private var _items: [(weight: Double, item: T)] = [] var limit: Int? @@ -11,19 +9,19 @@ class PriorityQueue { init() { } - func push(weight: Double, item: T) { + mutating func push(weight: Double, item: T) { var index = 0 while (index < _items.count) { if (weight < _items[index].weight) { break } - index++ + index += 1 } - _items.insert((weight: weight, item: item), atIndex: index) + _items.insert((weight: weight, item: item), at: index) - if limit != nil && _items.count > limit { + if let limit = limit, _items.count > limit { _items.removeLast() } } @@ -38,4 +36,4 @@ class PriorityQueue { } return Double.infinity } -} \ No newline at end of file +} diff --git a/VPTree/VPTree/VPNode.swift b/VPTree/VPTree/VPNode.swift index af119be..3f09ef6 100644 --- a/VPTree/VPTree/VPNode.swift +++ b/VPTree/VPTree/VPNode.swift @@ -1,14 +1,12 @@ -import Foundation - -private func <(left: Point, right: Point) -> Bool { +private func <(left: VPoint, right: VPoint) -> Bool { return left.d < right.d } -private func ==(left: Point, right: Point) -> Bool { +private func ==(left: VPoint, right: VPoint) -> Bool { return left.d == right.d } -private class Point: Comparable { +private final class VPoint: Comparable { var d: Double let point: T @@ -18,7 +16,7 @@ private class Point: Comparable { } } -internal class VPNode { +internal final class VPNode { var vpPoint: T! var points: [T] = [] var mu: Double? @@ -29,26 +27,26 @@ internal class VPNode { self.init(elements: [T](elements)) } - private func convertion (item: Point) -> T { + private func convertion (item: VPoint) -> T { return item.point } convenience init?(elements: [T]) { - let array: [Point] = elements.map { - (item: T) -> Point in - return Point(d: 0, point: item) + let array: [VPoint] = elements.map { + (item: T) -> VPoint in + return VPoint(d: 0, point: item) } self.init(elements: array) } - private init?(elements: [Point]) { + private init?(elements: [VPoint]) { if elements.count == 0 { return nil } var elements = elements //Random get of the VP points - self.vpPoint = elements.removeAtIndex(0).point + self.vpPoint = elements.remove(at: 0).point if elements.count == 0 { return @@ -58,14 +56,15 @@ internal class VPNode { item.d = item.point ~~ self.vpPoint } - let (left: [Point], right: [Point]) = elements.splitByMedian() + let splitElements: (left: [VPoint], right: [VPoint]) + = elements.splitByMedian() - mu = left.last!.d - leftChild = VPNode(elements: left) - rightChild = VPNode(elements: right) + mu = splitElements.left.last!.d + leftChild = VPNode(elements: splitElements.left) + rightChild = VPNode(elements: splitElements.right) } var isLeaf: Bool { return leftChild == nil && rightChild == nil } -} \ No newline at end of file +} diff --git a/VPTree/VPTree/VPTree.swift b/VPTree/VPTree/VPTree.swift index fd64a78..c60dddc 100644 --- a/VPTree/VPTree/VPTree.swift +++ b/VPTree/VPTree/VPTree.swift @@ -1,30 +1,28 @@ -import Foundation +infix operator ~~ -infix operator ~~ { +protocol Distance { + static func ~~(lhs: Self, rhs: Self) -> Double } -public protocol Distance { - func ~~(lhs: Self, rhs: Self) -> Double -} - -public class VPTree { +final class VPTree { internal var firstNode: VPNode init(elements: [T]) { firstNode = VPNode(elements: elements)!; } - private func _neighbors(point: T, limit: Int?, maxDistance: Double? = nil) -> [T] { + private func _neighbors(point: T, limit: Int?, maxDistance: Double? = nil) + -> [T] { var tau: Double = maxDistance ?? Double.infinity var nodesToTest: [VPNode?] = [firstNode] var neighbors = PriorityQueue(limit: limit) while(nodesToTest.count > 0) { - if let node = nodesToTest.removeAtIndex(0) { + if let node = nodesToTest.remove(at: 0) { let d = point ~~ node.vpPoint if d <= tau { - neighbors.push(d, item: node.vpPoint) + neighbors.push(weight: d, item: node.vpPoint) if maxDistance == nil { tau = neighbors.biggestWeigth } @@ -56,10 +54,11 @@ public class VPTree { } func findNeighbors(point: T, limit: Int) -> [T] { - return _neighbors(point, limit: limit, maxDistance: nil) + return _neighbors(point: point, limit: limit, maxDistance: nil) } func findClosest(point: T, maxDistance: Double) -> [T] { - return _neighbors(point, limit: nil, maxDistance: maxDistance) + return _neighbors(point: point, limit: nil, maxDistance: maxDistance) } -} \ No newline at end of file + +} diff --git a/VPTree/VPTreeTests/ArrayTests.swift b/VPTree/VPTreeTests/ArrayTests.swift index a65dcbc..00e8321 100644 --- a/VPTree/VPTreeTests/ArrayTests.swift +++ b/VPTree/VPTreeTests/ArrayTests.swift @@ -1,37 +1,23 @@ -// -// ArrayTests.swift -// VPTree -// -// Created by Laurent CHENAY on 07/06/2015. -// Copyright (c) 2015 Sunday. All rights reserved. -// - -import Foundation import XCTest class ArrayTests: XCTestCase { - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testSplit() { - var array: Array = [2, 1, 3, 5, 4, 6, 7]; - let (left: [Int], right: [Int]) = array.splitByMedian() + func testSplitByMedian_GivenValidValues_ReturnsExpectedLeftAndRight () { + let lut: Array = [2, 1, 3, 5, 4, 6, 7] + let result: (left: [Int], right: [Int]) = lut.splitByMedian() + + XCTAssertTrue(result.left.contains(1)) + XCTAssertTrue(result.left.contains(2)) + XCTAssertTrue(result.left.contains(3)) + XCTAssertTrue(result.left.contains(4)) - XCTAssertTrue(find(left, 1) != nil) - XCTAssertTrue(find(left, 2) != nil) - XCTAssertTrue(find(left, 3) != nil) - XCTAssertTrue(find(left, 4) != nil) + XCTAssertEqual(result.left.count, 4) - XCTAssertTrue(find(right, 5) != nil) - XCTAssertTrue(find(right, 6) != nil) - XCTAssertTrue(find(right, 7) != nil) + XCTAssertTrue(result.right.contains(5)) + XCTAssertTrue(result.right.contains(6)) + XCTAssertTrue(result.right.contains(7)) + + XCTAssertEqual(result.right.count, 3) } + } diff --git a/VPTree/VPTreeTests/Info.plist b/VPTree/VPTreeTests/Info.plist index 03dea7b..ba72822 100644 --- a/VPTree/VPTreeTests/Info.plist +++ b/VPTree/VPTreeTests/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.sunday.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/VPTree/VPTreeTests/VPTreeTests.swift b/VPTree/VPTreeTests/VPTreeTests.swift index c3977d5..798ba17 100644 --- a/VPTree/VPTreeTests/VPTreeTests.swift +++ b/VPTree/VPTreeTests/VPTreeTests.swift @@ -1,91 +1,62 @@ -import UIKit import XCTest -public func ~~(left: CGPoint, right: CGPoint) -> Double { - return Double(pow(pow(left.x - right.x, 2) + pow(left.y - right.y, 2), 0.5)) -} - -extension CGPoint: Distance { - -} - -func hammingWeight(x: UInt64) -> Int { - var i = x - i = i - ((i >> 1) & 0x55555555); - i = (i & 0x33333333) + ((i >> 2) & 0x33333333); - return Int((((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) -} - -func random64() -> UInt64 { - var rnd : UInt64 = 0 - arc4random_buf(&rnd, Int(sizeofValue(rnd))) - return rnd -} - -public func ~~(left: Photo, right: Photo) -> Double { - return Double(hammingWeight(left.pHash^right.pHash)) -} - -public class Photo: Distance { - let id: Int - let pHash: UInt64 - init(id: Int, pHash: UInt64) { - self.id = id - self.pHash = pHash - } -} - class VPTreeTests: XCTestCase { - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - func testFindNeighbors() { - let p1 = CGPoint(x: 0, y: 0) - let p2 = CGPoint(x: 1, y: 1) - let p3 = CGPoint(x: 1, y: 0) - let p4 = CGPoint(x: 0, y: 1) + let p1 = VPTreePoint(x: 0, y: 0) + let p2 = VPTreePoint(x: 1, y: 1) + let p3 = VPTreePoint(x: 1, y: 0) + let p4 = VPTreePoint(x: 0, y: 1) let tree = VPTree(elements: [p1, p2, p3]) - let founds = tree.findNeighbors(p4, limit: 3) + let founds = tree.findNeighbors(point: p4, limit: 3) XCTAssertEqual(founds.count, 3) - } func testFindClosest() { - let p1 = CGPoint(x: 0, y: 0) - let p2 = CGPoint(x: 1, y: 1) - let p3 = CGPoint(x: 1, y: 0) - let p4 = CGPoint(x: 0, y: 1) + let p1 = VPTreePoint(x: 0, y: 0) + let p2 = VPTreePoint(x: 1, y: 1) + let p3 = VPTreePoint(x: 1, y: 0) + let p4 = VPTreePoint(x: 0, y: 1) let tree = VPTree(elements: [p1, p2, p3]) - let founds = tree.findClosest(p4, maxDistance: 1.0) + let founds = tree.findClosest(point: p4, maxDistance: 1.0) XCTAssertEqual(founds.count, 2) - } - func testPerformanceOfHamming() { - self.measureBlock() { - var photos = [Photo]() - - for (var i = 0 ; i < 25000 ; i++) { - photos.append(Photo(id: i, pHash: random64())) - } - - let tree = VPTree(elements: photos) - + #warning("TODO: Restore measurement test.") + + // MARK: - Internals + + private struct VPTreePoint: Distance { + let x: Double + let y: Double + + static func ~~ (lhs: VPTreePoint, rhs: VPTreePoint) -> Double { + return Double( + pow(pow(lhs.x - rhs.x, 2) + pow(lhs.y - rhs.y, 2), 0.5) + ) } } + private struct Photo: Distance { + let id: Int + let pHash: UInt64 + + static func ~~ (lhs: Photo, rhs: Photo) + -> Double { + return Double(Photo.hammingWeight(x: (lhs.pHash^rhs.pHash))) + } + + private static func hammingWeight(x: UInt64) -> Int { + var i = x + i = i - ((i >> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + return Int((((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24) + } + } }