├── .gitignore ├── Package.swift ├── BinarySearch.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── WorkspaceSettings.xcsettings ├── xcshareddata │ ├── xcbaselines │ │ └── 007563E91BFA1AE1000ED5EB.xcbaseline │ │ │ ├── 81AD72D7-C41B-4703-9EDF-E6AA3DE2BDC8.plist │ │ │ └── Info.plist │ └── xcschemes │ │ └── BinarySearch.xcscheme ├── xcuserdata │ └── giannif.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj ├── .travis.yml ├── Sources ├── BinarySearch.h ├── Info.plist └── BinarySearch.swift ├── BinarySearchTests ├── Info.plist ├── PerformanceTests.swift ├── BinarySearchRangeTests.swift ├── BinarySearchExtractTests.swift ├── BinarySearchLastTests.swift ├── BinarySearchInsertionIndexForTests.swift ├── BinarySearchTests.swift ├── BinarySearchFirstTests.swift └── DocumentationTests.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/xcuserdata/** 2 | .build 3 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | let package = Package( 3 | name: "BinarySearch" 4 | ) 5 | -------------------------------------------------------------------------------- /BinarySearch.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BinarySearch.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode8.2 2 | language: objective-c 3 | 4 | before_install: 5 | - brew update || brew update 6 | - brew outdated xctool || brew upgrade xctool 7 | 8 | script: 9 | - xcodebuild -scheme BinarySearch -destination 'id=22FA2149-1241-469C-BF6D-462D3837DB72' 10 | - xcodebuild test -scheme BinarySearch -destination 'id=22FA2149-1241-469C-BF6D-462D3837DB72' 11 | -------------------------------------------------------------------------------- /Sources/BinarySearch.h: -------------------------------------------------------------------------------- 1 | // 2 | // BinarySearch.h 3 | // BinarySearch 4 | // 5 | // Created by Gianni Ferullo on 11/16/15. 6 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for BinarySearch. 12 | FOUNDATION_EXPORT double BinarySearchVersionNumber; 13 | 14 | //! Project version string for BinarySearch. 15 | FOUNDATION_EXPORT const unsigned char BinarySearchVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /BinarySearch.xcodeproj/xcshareddata/xcbaselines/007563E91BFA1AE1000ED5EB.xcbaseline/81AD72D7-C41B-4703-9EDF-E6AA3DE2BDC8.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | BinarySearchTests 8 | 9 | testExample() 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 0.00067972 15 | baselineIntegrationDisplayName 16 | Local Baseline 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BinarySearch.xcodeproj/xcuserdata/giannif.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | BinarySearch.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 007563DF1BFA1AE1000ED5EB 16 | 17 | primary 18 | 19 | 20 | 007563E91BFA1AE1000ED5EB 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /BinarySearchTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.3 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /BinarySearchTests/PerformanceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PerformanceTests.swift 3 | // BinarySearch 4 | // 5 | // Created by Gianni Ferullo on 11/22/15. 6 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import BinarySearch 11 | 12 | class PerformanceTests: XCTestCase { 13 | 14 | func testFindingClosestValue() { 15 | let test = (0...100000).map {$0} 16 | var numberOfSteps = 0 17 | func counter(_ val1:Int, val2:Int) -> Bool { 18 | numberOfSteps += 1 19 | return val1 == val2 20 | } 21 | guard let result = test.binarySearch(2500, predicate:counter) else { 22 | XCTFail("Didn't find value.") 23 | return 24 | } 25 | XCTAssertEqual(test[result], 2500) 26 | XCTAssertEqual(numberOfSteps, 13) 27 | } 28 | 29 | func testLogN() { 30 | let test = (0...200000).map {$0} 31 | var numberOfSteps = 0 32 | func counter(_ val1:Int, val2:Int) -> Bool { 33 | numberOfSteps += 1 34 | return val1 == val2 35 | } 36 | guard let result = test.binarySearch(2500, predicate:counter) else { 37 | XCTFail("Didn't find value.") 38 | return 39 | } 40 | XCTAssertEqual(test[result], 2500) 41 | XCTAssertEqual(numberOfSteps, 14) // Doubling the input increases the number of steps by 1. 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /BinarySearch.xcodeproj/xcshareddata/xcbaselines/007563E91BFA1AE1000ED5EB.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | 81AD72D7-C41B-4703-9EDF-E6AA3DE2BDC8 8 | 9 | localComputer 10 | 11 | busSpeedInMHz 12 | 100 13 | cpuCount 14 | 1 15 | cpuKind 16 | Intel Core i7 17 | cpuSpeedInMHz 18 | 1700 19 | logicalCPUCoresPerPackage 20 | 4 21 | modelCode 22 | MacBookAir6,2 23 | physicalCPUCoresPerPackage 24 | 2 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | x86_64 30 | targetDevice 31 | 32 | modelCode 33 | iPhone8,2 34 | platformIdentifier 35 | com.apple.platform.iphonesimulator 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /BinarySearchTests/BinarySearchRangeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BinarySearchRangeTests.swift 3 | // BinarySearch 4 | // 5 | // Created by Gianni Ferullo on 11/20/15. 6 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import BinarySearch 11 | 12 | class BinarySearchRangeTests: XCTestCase { 13 | 14 | func testFindingClosestValue() { 15 | var test = [0,3,9,10,11,12,13,18] 16 | for input in test { 17 | let result = test.binarySearchRange(input, predicate: {$0 >= $1}) 18 | XCTAssertEqual(result?.lowerBound, test.index(of: input)) 19 | XCTAssertEqual(result?.upperBound, test.count - 1) 20 | } 21 | for input in test { 22 | let result = test.binarySearchRange(input, predicate: {$0 <= $1}) 23 | XCTAssertEqual(result?.lowerBound, 0) 24 | XCTAssertEqual(result?.upperBound, test.index(of: input)) 25 | } 26 | let intermediateVals = [1,2,4,5,6,7,8,14,15,16,17] 27 | let expecteStart = [1,1,2,2,2,2,2,7,7,7,7] 28 | for (index, input) in intermediateVals.enumerated() { 29 | let result = test.binarySearchRange(input, predicate: {$0 >= $1}) 30 | XCTAssertEqual(result?.lowerBound, expecteStart[index]) 31 | XCTAssertEqual(result?.upperBound, test.count - 1) 32 | } 33 | let expectedEnd = [0,0,1,1,1,1,1,6,6,6,6] 34 | for (index, input) in intermediateVals.enumerated() { 35 | let result = test.binarySearchRange(input, predicate: {$0 <= $1}) 36 | XCTAssertEqual(result?.lowerBound, 0) 37 | XCTAssertEqual(result?.upperBound, expectedEnd[index]) 38 | } 39 | 40 | test = [0,3,9,10,11,12,13] 41 | for input in test { 42 | let result = test.binarySearchRange(input, predicate: {$0 >= $1}) 43 | XCTAssertEqual(result?.lowerBound, test.index(of: input)) 44 | XCTAssertEqual(result?.upperBound, test.count - 1) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /BinarySearchTests/BinarySearchExtractTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BinarySearchExtractTest.swift 3 | // BinarySearch 4 | // 5 | // Created by Gianni Ferullo on 11/20/15. 6 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 7 | // 8 | 9 | 10 | 11 | import XCTest 12 | @testable import BinarySearch 13 | 14 | class BinarySearchExtractTests: XCTestCase { 15 | 16 | func testExtract() { 17 | func convertToDict(_ val:Int) -> Dictionary { 18 | return ["testValue": val] 19 | } 20 | let dictArray = (0...10).map(convertToDict) 21 | 22 | var result = dictArray.binarySearch(6, extract:{$0["testValue"]!}) 23 | XCTAssertEqual(result, 6) 24 | result = dictArray.binarySearchFirst(6, extract:{$0["testValue"]!}) 25 | XCTAssertEqual(result, 6) 26 | result = dictArray.binarySearchLast(6, extract:{$0["testValue"]!}) 27 | XCTAssertEqual(result, 6) 28 | 29 | // with predicate >= 30 | result = dictArray.binarySearchLast(6, extract:{$0["testValue"]!}, predicate:{$0>=$1}) 31 | XCTAssertEqual(result, 10) 32 | result = dictArray.binarySearchFirst(6, extract:{$0["testValue"]!}, predicate:{$0>=$1}) 33 | XCTAssertEqual(result, 6) 34 | var rangeResult = dictArray.binarySearchRange(6, extract:{$0["testValue"]!}, predicate:{$0>=$1}) 35 | XCTAssertEqual(rangeResult?.lowerBound, 6) 36 | XCTAssertEqual(rangeResult?.upperBound, 10) 37 | 38 | // with predicate <= 39 | result = dictArray.binarySearchFirst(6, extract:{$0["testValue"]!}, predicate:{$0<=$1}) 40 | XCTAssertEqual(result, 0) 41 | result = dictArray.binarySearchLast(6, extract:{$0["testValue"]!}, predicate:{$0<=$1}) 42 | XCTAssertEqual(result, 6) 43 | rangeResult = dictArray.binarySearchRange(6, extract:{$0["testValue"]!}, predicate:{$0<=$1}) 44 | XCTAssertEqual(rangeResult?.lowerBound, 0) 45 | XCTAssertEqual(rangeResult?.upperBound, 6) 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /BinarySearchTests/BinarySearchLastTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BinarySearchLast.swift 3 | // BinarySearch 4 | // 5 | // Created by Gianni Ferullo on 11/19/15. 6 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import BinarySearch 11 | 12 | class BinarySearchLastTests: XCTestCase { 13 | 14 | let test = (0...100000).map {$0} 15 | 16 | func testFindingClosestValue() { 17 | let test = [0,3,9,10,11,12,13,18] 18 | var result = test.binarySearchLast(0, predicate: {$0 >= $1}) 19 | XCTAssertEqual(test[result!], 18) 20 | result = test.binarySearchLast(5, predicate: {$0 <= $1}) 21 | XCTAssertEqual(test[result!], 3) 22 | result = test.binarySearchLast(18, predicate: {$0 <= $1}) 23 | XCTAssertEqual(test[result!], 18) 24 | result = test.binarySearchLast(17, predicate: {$0 <= $1}) 25 | XCTAssertEqual(test[result!], 13) 26 | for input in 4...9 { 27 | result = test.binarySearchLast(input, predicate: {$0 >= $1}) 28 | XCTAssertEqual(test[result!], 18) 29 | } 30 | for input in 4...8 { 31 | result = test.binarySearchLast(input, predicate: {$0 <= $1}) 32 | XCTAssertEqual(test[result!], 3) 33 | } 34 | for input in 9...13 { 35 | result = test.binarySearchLast(input, predicate: {$0 <= $1}) 36 | XCTAssertEqual(test[result!], input) 37 | } 38 | for input in 13...17 { 39 | result = test.binarySearchLast(input, predicate: {$0 <= $1}) 40 | XCTAssertEqual(test[result!], 13) 41 | } 42 | result = test.binarySearchLast(18, predicate: {$0 <= $1}) 43 | XCTAssertEqual(test[result!], 18) 44 | 45 | } 46 | 47 | func testPredicateGreaterOrEqualFailSafe() { 48 | let dictArray = [1.0,2.0,Double.nan] 49 | let result = dictArray.binarySearchLast(3.0, predicate:{$0 >= $1}) 50 | XCTAssertNil(result) 51 | } 52 | 53 | func testPredicateLessOrEqualFailSafe() { 54 | let dictArray = [1.0,2.0,Double.nan] 55 | let result = dictArray.binarySearchLast(0.0, predicate:{$0 <= $1}) 56 | XCTAssertNil(result) 57 | } 58 | 59 | func testPredicateEqualFailSafe() { 60 | let dictArray = [1.0,2.0,Double.nan] 61 | let result = dictArray.binarySearchLast(3.0) 62 | XCTAssertNil(result) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /BinarySearchTests/BinarySearchInsertionIndexForTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BinarySearchInsertionIndexForTests.swift 3 | // BinarySearch 4 | // 5 | // Created by Gianni Ferullo on 12/17/15. 6 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // 12 | // BinarySearchInsertionIndexFoTests 13 | // BinarySearch 14 | // 15 | // Created by Gianni Ferullo on 12/17/15. 16 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 17 | // 18 | 19 | import XCTest 20 | @testable import BinarySearch 21 | 22 | class BinarySearchInsertionIndexFoTests: XCTestCase { 23 | let testArray = [0,1,3,4] 24 | 25 | func testExists() { 26 | guard let result = testArray.binarySearchInsertionIndexFor(0) else { 27 | XCTAssert(true, "result is nil") 28 | return 29 | } 30 | XCTFail("result should be nil but it was \(result)") 31 | } 32 | 33 | func testFoundMiddleIndex() { 34 | guard let result = testArray.binarySearchInsertionIndexFor(2) else { 35 | XCTFail("result should be nil") 36 | return 37 | } 38 | XCTAssertEqual(result, 2, "Insertable at 2") 39 | } 40 | 41 | func testFoundBefore() { 42 | guard let result = testArray.binarySearchInsertionIndexFor(-1) else { 43 | XCTFail("result should be nil") 44 | return 45 | } 46 | XCTAssertEqual(result, 0, "Insertable at 0") 47 | } 48 | 49 | func testFoundAfter() { 50 | guard let result = testArray.binarySearchInsertionIndexFor(10) else { 51 | XCTFail("result should be nil") 52 | return 53 | } 54 | XCTAssertEqual(result, testArray.count, "Insertable at \(testArray.count)") 55 | } 56 | 57 | func testEmpty() { 58 | let testArray:[Int] = [] 59 | guard let result = testArray.binarySearchInsertionIndexFor(10) else { 60 | XCTFail("result should be nil") 61 | return 62 | } 63 | XCTAssertEqual(result, 0, "Insertable at 0") 64 | } 65 | 66 | func testExtract() { 67 | struct Fruit { 68 | let name:String 69 | } 70 | // An Array of Fruits 71 | let dict = [ 72 | Fruit(name:"apple"), 73 | Fruit(name:"banana"), 74 | Fruit(name:"orange") 75 | ] 76 | guard let result = dict.binarySearchInsertionIndexFor("cantaloupe", extract:{$0.name}) else { 77 | XCTFail("Failed to find a dictionary with a testValue of 'bananas'") 78 | return 79 | } 80 | XCTAssertEqual(result, 2, "Insertable at 2") 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /BinarySearchTests/BinarySearchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BinarySearchTests.swift 3 | // BinarySearchTests 4 | // 5 | // Created by Gianni Ferullo on 11/16/15. 6 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import BinarySearch 11 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 12 | // Consider refactoring the code to use the non-optional operators. 13 | fileprivate func < (lhs: T?, rhs: T?) -> Bool { 14 | switch (lhs, rhs) { 15 | case let (l?, r?): 16 | return l < r 17 | case (nil, _?): 18 | return true 19 | default: 20 | return false 21 | } 22 | } 23 | 24 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 25 | // Consider refactoring the code to use the non-optional operators. 26 | fileprivate func <= (lhs: T?, rhs: T?) -> Bool { 27 | switch (lhs, rhs) { 28 | case let (l?, r?): 29 | return l <= r 30 | default: 31 | return !(rhs < lhs) 32 | } 33 | } 34 | 35 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 36 | // Consider refactoring the code to use the non-optional operators. 37 | fileprivate func > (lhs: T?, rhs: T?) -> Bool { 38 | switch (lhs, rhs) { 39 | case let (l?, r?): 40 | return l > r 41 | default: 42 | return rhs < lhs 43 | } 44 | } 45 | 46 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 47 | // Consider refactoring the code to use the non-optional operators. 48 | fileprivate func >= (lhs: T?, rhs: T?) -> Bool { 49 | switch (lhs, rhs) { 50 | case let (l?, r?): 51 | return l >= r 52 | default: 53 | return !(lhs < rhs) 54 | } 55 | } 56 | 57 | 58 | class BinarySearchTests: XCTestCase { 59 | 60 | let test = (0...1000).map {$0} 61 | 62 | func testPredicateEqual() { 63 | for find in 0...1000 { 64 | // Find any value less than "find" 0 to 49 65 | let result = test.binarySearch(find, predicate: {$0 == $1}) 66 | XCTAssertNotNil(result) 67 | XCTAssertEqual(find, result) 68 | } 69 | } 70 | 71 | func testPredicateLess() { 72 | for find in 1...1000 { 73 | // Find any value less than "find" 74 | let result = test.binarySearch(find, predicate: {$0 < $1}) 75 | XCTAssertNotNil(result) 76 | XCTAssert(result < find) 77 | } 78 | } 79 | 80 | func testPredicateLessOrEqual() { 81 | for find in 0...1000 { 82 | // Find any value less or equal than "find" 83 | let result = test.binarySearch(find, predicate: {$0 <= $1}) 84 | XCTAssertNotNil(result) 85 | XCTAssert(result <= find) 86 | } 87 | } 88 | 89 | func testPredicateGreater() { 90 | for find in 0...999 { 91 | // Find any value greater than "find" 92 | let result = test.binarySearch(find, predicate: {$0 > $1}) 93 | XCTAssertNotNil(result) 94 | XCTAssert(result > find) 95 | } 96 | } 97 | 98 | func testPredicateGreaterOrEqual() { 99 | for find in 0...1000 { 100 | // Find any value greater than "find" 101 | let result = test.binarySearch(find, predicate: {$0 >= $1}) 102 | XCTAssertNotNil(result) 103 | XCTAssert(result >= find) 104 | } 105 | } 106 | 107 | func testPredicateGreaterOrEqualFailSafe() { 108 | let dictArray = [1.0,2.0,Double.nan] 109 | let result = dictArray.binarySearch(3.0, predicate:{$0 >= $1}) 110 | XCTAssertNil(result) 111 | } 112 | 113 | func testPredicateLessOrEqualFailSafe() { 114 | let dictArray = [1.0,2.0,Double.nan] 115 | let result = dictArray.binarySearch(0.0, predicate:{$0 <= $1}) 116 | XCTAssertNil(result) 117 | } 118 | 119 | func testPredicateEqualFailSafe() { 120 | let dictArray = [1.0,2.0,Double.nan] 121 | let result = dictArray.binarySearch(3.0) 122 | XCTAssertNil(result) 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /BinarySearch.xcodeproj/xcshareddata/xcschemes/BinarySearch.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /BinarySearchTests/BinarySearchFirstTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BinarySearchTests.swift 3 | // BinarySearchTests 4 | // 5 | // Created by Gianni Ferullo on 11/16/15. 6 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import BinarySearch 11 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 12 | // Consider refactoring the code to use the non-optional operators. 13 | fileprivate func < (lhs: T?, rhs: T?) -> Bool { 14 | switch (lhs, rhs) { 15 | case let (l?, r?): 16 | return l < r 17 | case (nil, _?): 18 | return true 19 | default: 20 | return false 21 | } 22 | } 23 | 24 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 25 | // Consider refactoring the code to use the non-optional operators. 26 | fileprivate func <= (lhs: T?, rhs: T?) -> Bool { 27 | switch (lhs, rhs) { 28 | case let (l?, r?): 29 | return l <= r 30 | default: 31 | return !(rhs < lhs) 32 | } 33 | } 34 | 35 | // FIXME: comparison operators with optionals were removed from the Swift Standard Libary. 36 | // Consider refactoring the code to use the non-optional operators. 37 | fileprivate func > (lhs: T?, rhs: T?) -> Bool { 38 | switch (lhs, rhs) { 39 | case let (l?, r?): 40 | return l > r 41 | default: 42 | return rhs < lhs 43 | } 44 | } 45 | 46 | 47 | class BinarySearchFirstTests: XCTestCase { 48 | 49 | let test = (0...100000).map {$0} 50 | 51 | func testPredicateEqual() { 52 | for find in 0...1000 { 53 | let result = test.binarySearchFirst(find, predicate: {$0 == $1}) 54 | XCTAssertNotNil(result) 55 | XCTAssertEqual(find, result) 56 | } 57 | } 58 | 59 | func testPredicateLess() { 60 | for find in 1...1000 { 61 | // Find any value less than "find" 62 | let result = test.binarySearchFirst(find, predicate: {$0 < $1}) 63 | XCTAssertNotNil(result) 64 | XCTAssert(result < find) 65 | } 66 | } 67 | 68 | func testPredicateLessOrEqual() { 69 | for find in 0...1000 { 70 | // Find any value less or equal than "find" 71 | let result = test.binarySearchFirst(find, predicate: {$0 <= $1}) 72 | XCTAssertNotNil(result) 73 | XCTAssert(result <= find) 74 | } 75 | } 76 | 77 | func testPredicateGreater() { 78 | for find in 0...999 { 79 | // Find any value greater than "find" 80 | let result = test.binarySearchFirst(find, predicate: {$0 > $1}) 81 | XCTAssertNotNil(result) 82 | XCTAssert(result > find) 83 | } 84 | } 85 | 86 | func testFindingClosestValue() { 87 | // Find any value greater than "find" 88 | let test = [0,3,9,10,11,12,13,18] 89 | var result = test.binarySearchFirst(0, predicate: {$0 >= $1}) 90 | XCTAssertEqual(test[result!], 0) 91 | 92 | result = test.binarySearchFirst(1, predicate: {$0 >= $1}) 93 | XCTAssertEqual(test[result!], 3) 94 | result = test.binarySearchFirst(2, predicate: {$0 >= $1}) 95 | XCTAssertEqual(test[result!], 3) 96 | result = test.binarySearchFirst(3, predicate: {$0 >= $1}) 97 | XCTAssertEqual(test[result!], 3) 98 | for input in 4...9 { 99 | result = test.binarySearchFirst(input, predicate: {$0 >= $1}) 100 | XCTAssertEqual(test[result!], 9) 101 | } 102 | } 103 | 104 | func testPredicateGreaterOrEqual() { 105 | for find in 0...1000 { 106 | // Find any value greater than "find" 107 | let result = test.binarySearchFirst(find, predicate: {$0 >= $1}) 108 | XCTAssertNotNil(result) 109 | XCTAssert(result == find) 110 | } 111 | } 112 | 113 | func testPredicateGreaterOrEqualFailSafe() { 114 | let dictArray = [1.0,2.0,Double.nan] 115 | let result = dictArray.binarySearchFirst(3.0, predicate:{$0 >= $1}) 116 | XCTAssertNil(result) 117 | } 118 | 119 | func testPredicateLessOrEqualFailSafe() { 120 | let dictArray = [1.0,2.0,Double.nan] 121 | let result = dictArray.binarySearchFirst(0.0, predicate:{$0 <= $1}) 122 | XCTAssertNil(result) 123 | } 124 | 125 | func testPredicateEqualFailSafe() { 126 | let dictArray = [1.0,2.0,Double.nan] 127 | let result = dictArray.binarySearchFirst(3.0) 128 | XCTAssertNil(result) 129 | } 130 | 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /Sources/BinarySearch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BinarySearch.swift 3 | // BinarySearch 4 | // 5 | // Created by Gianni Ferullo on 11/16/15. 6 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Collection where Index: Strideable { 12 | 13 | func binarySearch(_ find:T, 14 | extract: (Iterator.Element) -> T = {(el) -> T in return el as! T}, 15 | predicate: (T,T) -> Bool = {(val1, val2) -> Bool in return val1 == val2} 16 | )-> Index? { 17 | var left = startIndex 18 | var right = endIndex - 1 19 | while (left <= right) { 20 | let mid = index(left, offsetBy: distance(from: left, to: right) / 2) 21 | let currentValue:T = extract(self[mid]) 22 | if left == right { 23 | return predicate(currentValue, find) ? left : nil 24 | } 25 | if predicate(currentValue, find) { 26 | return mid 27 | }else if currentValue == find { 28 | return checkLeft(find, mid, extract, predicate) ?? checkRight(find, mid, extract, predicate) 29 | }else if currentValue < find { 30 | left = index(mid, offsetBy: 1) 31 | }else if currentValue > find { 32 | right = index(mid, offsetBy: -1) 33 | }else { 34 | return nil 35 | } 36 | } 37 | return nil 38 | } 39 | 40 | func binarySearchFirst(_ find:T, 41 | extract: (Iterator.Element) -> T = {(el) -> T in return el as! T}, 42 | predicate: (T,T) -> Bool = {(val1, val2) -> Bool in return val1 == val2} 43 | )-> Index? { 44 | var left = startIndex 45 | var right = endIndex - 1 46 | while (left <= right) { 47 | let mid = index(left, offsetBy: distance(from: left, to: right) / 2) 48 | let currentValue:T = extract(self[mid]) 49 | if left == right { 50 | return predicate(currentValue, find) ? left : nil 51 | } 52 | if predicate(currentValue, find) { 53 | if checkLeft(find, mid, extract, predicate) == nil { 54 | return mid 55 | } 56 | right = index(mid, offsetBy: -1) 57 | }else if currentValue == find { 58 | return checkLeft(find, mid, extract, predicate) ?? checkRight(find, mid, extract, predicate) 59 | }else if currentValue < find { 60 | left = index(mid, offsetBy: 1) 61 | }else if currentValue > find { 62 | right = index(mid, offsetBy: -1) 63 | }else { 64 | return nil 65 | } 66 | } 67 | return nil 68 | } 69 | 70 | func binarySearchLast(_ find:T, 71 | extract: (Iterator.Element) -> T = {(el) -> T in return el as! T}, 72 | predicate: (T,T) -> Bool = {(val1, val2) -> Bool in return val1 == val2} 73 | )-> Index? { 74 | var left = startIndex 75 | var right = endIndex - 1 76 | while (left <= right) { 77 | let mid = index(left, offsetBy: distance(from: left, to: right) / 2) 78 | let currentValue:T = extract(self[mid]) 79 | if left == right { 80 | return predicate(currentValue, find) ? left : nil 81 | } 82 | if predicate(currentValue, find) { 83 | if checkRight(find, mid, extract, predicate) == nil { 84 | return mid 85 | } 86 | left = index(mid, offsetBy: 1) 87 | }else if currentValue == find { 88 | return checkLeft(find, mid, extract, predicate) ?? checkRight(find, mid, extract, predicate) 89 | }else if currentValue < find { 90 | left = index(mid, offsetBy: 1) 91 | }else if currentValue > find { 92 | right = index(mid, offsetBy: -1) 93 | }else { 94 | return nil 95 | } 96 | } 97 | return nil 98 | } 99 | 100 | func binarySearchRange(_ find:T, 101 | extract: (Iterator.Element) -> T = {(el) -> T in return el as! T}, 102 | predicate: (T,T) -> Bool = {(val1, val2) -> Bool in return val1 == val2} 103 | )-> Range? { 104 | if let first = binarySearchFirst(find, extract: extract, predicate: predicate), 105 | let last = binarySearchLast(find, extract: extract, predicate: predicate){ 106 | return Range(uncheckedBounds: (lower: first, upper: last)) 107 | } 108 | return nil 109 | } 110 | 111 | func binarySearchInsertionIndexFor(_ find:T, 112 | extract: (Iterator.Element) -> T = {(el) -> T in return el as! T} 113 | ) -> Index? { 114 | if let foundIndex = binarySearchLast(find, extract:extract, predicate:{$0 <= $1}) { 115 | let val = extract(self[foundIndex]) 116 | if val != find { 117 | return index(foundIndex, offsetBy: 1) 118 | }else{ 119 | return nil 120 | } 121 | }else if let first = self.first { 122 | return find < extract(first) ? startIndex : endIndex 123 | }else { 124 | return startIndex 125 | } 126 | } 127 | 128 | private func checkRight(_ find:T, _ mid:Index, _ extract: (Iterator.Element) -> T, _ predicate: (T,T) -> Bool) -> Index?{ 129 | if distance(from: mid, to: endIndex) > 0 { 130 | let i = index(mid, offsetBy: 1) 131 | if predicate(extract(self[i]), find) { 132 | return i 133 | } 134 | } 135 | return nil 136 | } 137 | 138 | private func checkLeft(_ find:T, _ mid:Index, _ extract: (Iterator.Element) -> T, _ predicate: (T,T) -> Bool) -> Index?{ 139 | if distance(from: startIndex, to: mid) > 0 { 140 | let i = index(mid, offsetBy: -1) 141 | if predicate(extract(self[i]), find) { 142 | return i 143 | } 144 | } 145 | return nil 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /BinarySearchTests/DocumentationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BinarySearchExtractTest.swift 3 | // BinarySearch 4 | // 5 | // Created by Gianni Ferullo on 11/20/15. 6 | // Copyright © 2015 Gianni Ferullo. All rights reserved. 7 | // 8 | 9 | 10 | 11 | import XCTest 12 | @testable import BinarySearch 13 | 14 | class DocumentationTests: XCTestCase { 15 | 16 | func test() { 17 | let sortedInts = [0,1,2,3,4,5,6,7,8,9,10] 18 | guard let intIndex = sortedInts.binarySearch(6) else { 19 | XCTFail("Failed to find Int 6") 20 | return 21 | } 22 | XCTAssertEqual(intIndex, 6) 23 | XCTAssertEqual(sortedInts[intIndex], 6) // Our values are the same as the indices in this example. 24 | 25 | let fruitsArray = ["apples", "bananas", "oranges"] 26 | guard let fruitIndex = fruitsArray.binarySearch("bananas") else { 27 | XCTFail("Failed to find 'bananas'") 28 | return 29 | } 30 | XCTAssertEqual(fruitIndex, 1) 31 | XCTAssertEqual(fruitsArray[fruitIndex], "bananas") 32 | 33 | let kiwiIndex = fruitsArray.binarySearch("kiwi") 34 | kiwiIndex // nil 35 | XCTAssertNil(kiwiIndex) 36 | } 37 | 38 | func testPredicate() { 39 | let fruitsArray = ["apples", "bananas", "oranges"] 40 | guard let index = fruitsArray.binarySearch("bana", predicate:{$0.hasPrefix($1)}) else { 41 | XCTFail("Failed to find element that begins with 'bana'") 42 | return 43 | } 44 | XCTAssertEqual(index, 1) 45 | XCTAssertEqual(fruitsArray[index], "bananas") 46 | } 47 | 48 | func testExtract() { 49 | struct Fruit { 50 | let name:String 51 | } 52 | // An Array of Fruits 53 | let dict = [ 54 | Fruit(name:"apple"), 55 | Fruit(name:"banana"), 56 | Fruit(name:"orange") 57 | ] 58 | guard let index = dict.binarySearch("banana", extract:{$0.name}) else { 59 | XCTFail("Failed to find a dictionary with a testValue of 'bananas'") 60 | return 61 | } 62 | XCTAssertEqual(index, 1) 63 | XCTAssertEqual(dict[index].name, "banana") 64 | } 65 | 66 | func testFullExample() { 67 | 68 | struct Album { 69 | let name:String 70 | let date:TimeInterval 71 | init(_ name:String, _ date:String){ 72 | self.name = name 73 | self.date = Album.getDate(date) 74 | } 75 | static func getDate(_ date: String) -> TimeInterval{ 76 | let formatter = DateFormatter() 77 | formatter.dateFormat = "yyyy-MM-dd" 78 | return formatter.date(from: date)!.timeIntervalSince1970 79 | } 80 | } 81 | 82 | // An Array of albums, sorted by date 83 | let stonesAlbums = [ 84 | Album("Beggars Banquet", "1968-12-06"), 85 | Album("Let It Bleed", "1969-12-05"), 86 | Album("Sticky Fingers", "1971-04-23"), 87 | Album("Exile on Main St.", "1972-05-12"), 88 | Album("Some Girls", "1978-06-9"), 89 | Album("Emotional Rescue", "1980-06-20"), 90 | Album("Tattoo You", "1981-08-24"), 91 | Album("Undercover", "1983-11-07") 92 | ] 93 | 94 | guard let firstStonesAlbumSeventies = stonesAlbums.binarySearchFirst(Album.getDate("1970-01-01"), 95 | // Extract the date value to test with the predicate above 96 | extract: {$0.date}, 97 | // We want to find the first value greater than Jan 1, 1970 98 | predicate: {$0 >= $1}) else { 99 | XCTFail("Failed to find a Rolling Stones album after 1970") 100 | return 101 | } 102 | XCTAssertEqual(firstStonesAlbumSeventies, 2) 103 | XCTAssertEqual(stonesAlbums[firstStonesAlbumSeventies].name, "Sticky Fingers") 104 | guard let lastStonesAlbumSixties = stonesAlbums.binarySearchLast(Album.getDate("1970-01-01"), 105 | // Extract the date value to test with the predicate above 106 | extract: {$0.date}, 107 | // We want to find the first value greater than Jan 1, 1970 108 | predicate: {$0 <= $1}) else { 109 | XCTFail("Failed to find a Rolling Stones album after 1970") 110 | return 111 | } 112 | XCTAssertEqual(lastStonesAlbumSixties, 1) 113 | XCTAssertEqual(stonesAlbums[lastStonesAlbumSixties].name, "Let It Bleed") 114 | guard let albumSixties = stonesAlbums.binarySearchRange(Album.getDate("1970-01-01"), 115 | // Extract the date value to test with the predicate above 116 | extract: {$0.date}, 117 | // We want to find the first value greater than Jan 1, 1970 118 | predicate: {$0 >= $1 && $0 < Album.getDate("1978-01-01")}) else { 119 | XCTFail("Failed to find a Rolling Stones album after 1970") 120 | return 121 | } 122 | XCTAssertEqual(albumSixties.lowerBound, 2) 123 | XCTAssertEqual(albumSixties.upperBound, 3) 124 | } 125 | 126 | func testFirst() { 127 | let sortedArray = [0,5,15,75,100] 128 | func compare(_ val:Int, val2:Int) -> Bool { 129 | return val > 10 130 | } 131 | guard let indexGreaterThanOrEqual = sortedArray.binarySearchFirst(10, predicate: compare) else { 132 | XCTFail("Failed to find an index greater than or equal to 10") 133 | return 134 | } 135 | // Find the first value that's greater than or equal to 10 136 | XCTAssertEqual(indexGreaterThanOrEqual, 2) 137 | XCTAssertEqual(sortedArray[indexGreaterThanOrEqual], 15) 138 | 139 | guard let indexLessThanOrEqual = sortedArray.binarySearchFirst(10, predicate: {$0 <= $1}) else { 140 | XCTFail("Failed to find an index less than or equal to 10") 141 | return 142 | } 143 | XCTAssertEqual(indexLessThanOrEqual, 0) 144 | XCTAssertEqual(sortedArray[indexLessThanOrEqual], 0) 145 | } 146 | 147 | func testLast() { 148 | let sortedArray = [0,5,15,75,100] 149 | guard let indexGreaterThanOrEqual = sortedArray.binarySearchLast(10, predicate: {$0 >= $1}) else { 150 | XCTFail("Failed to find an index greater than or equal to 10") 151 | return 152 | } 153 | // Find the first value that's greater than or equal to 10 154 | XCTAssertEqual(indexGreaterThanOrEqual, 4) 155 | XCTAssertEqual(sortedArray[indexGreaterThanOrEqual], 100) 156 | 157 | guard let indexLessThanOrEqual = sortedArray.binarySearchLast(10, predicate: {$0 <= $1}) else { 158 | XCTFail("Failed to find an index less than or equal to 10") 159 | return 160 | } 161 | XCTAssertEqual(indexLessThanOrEqual, 1) 162 | XCTAssertEqual(sortedArray[indexLessThanOrEqual], 5) 163 | } 164 | 165 | func testRange() { 166 | let sortedArray = [0,5,15,75,100] 167 | guard let range = sortedArray.binarySearchRange(10, predicate:{$0 >= $1}) else { 168 | XCTFail("Failed to find a range of Ints greater than or equal to 10") 169 | return 170 | } 171 | // Find the range of values that's greater than or equal to 10 172 | XCTAssertEqual(range.lowerBound, 2) 173 | XCTAssertEqual(sortedArray[range.lowerBound], 15) 174 | XCTAssertEqual(range.upperBound, 4) 175 | XCTAssertEqual(sortedArray[range.upperBound], 100) 176 | 177 | } 178 | 179 | func testManualRange() { 180 | let sortedArray = [0,5,15,75,100,150] 181 | guard let startIndex = sortedArray.binarySearchFirst(10, predicate:{$0 >= $1}), 182 | let endIndex = sortedArray.binarySearchLast(100, predicate:{$0 <= $1}) 183 | else { 184 | XCTFail("Failed to find a range of Ints greater than or equal to 10") 185 | return 186 | } 187 | // Find the range of values that's greater than or equal to 10 188 | XCTAssertEqual(startIndex, 2) 189 | XCTAssertEqual(sortedArray[startIndex], 15) 190 | XCTAssertEqual(endIndex, 4) 191 | XCTAssertEqual(sortedArray[endIndex], 100) 192 | } 193 | 194 | func testInsertionIndexFor() { 195 | guard let foundIndex = [0,5,15,75,100].binarySearchInsertionIndexFor(10) else { 196 | XCTFail("The find should have succeeded") 197 | return 198 | } 199 | // The value 10 would go at the 2nd index 200 | XCTAssertEqual(foundIndex, 2) 201 | guard let foundIndex2 = [0,5,15,75,100].binarySearchInsertionIndexFor(-5) else { 202 | XCTFail("The find should have succeeded") 203 | return 204 | } 205 | // The value -5 would go at the index 0 206 | XCTAssertEqual(foundIndex2, 0) 207 | guard let foundIndex3 = [0,5,15,75,100].binarySearchInsertionIndexFor(500) else { 208 | XCTFail("The find should have succeeded") 209 | return 210 | } 211 | // The value 500 would go at the end index 212 | XCTAssertEqual(foundIndex3, 5) 213 | guard let _ = [0,5,15,75,100].binarySearchInsertionIndexFor(75) else { 214 | XCTAssertTrue(true, "The return value should be nil") 215 | return 216 | } 217 | XCTFail("The foundIndex should have be nil") 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Binary Search Swift Extension 2 | [![Build Status](https://travis-ci.org/giannif/BinarySearch.svg)](https://travis-ci.org/giannif/BinarySearch) 3 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 4 | 5 | Perform a [binary search][wiki] on [Collections][] (e.g. [Array][], [Dictionary][], [Set][]), with a few powerful options such as finding the first or last index in the collection, matching an optional [predicate](#predicate), and the ability to [extract](#extract) values from a collection to avoid having to prepare that collection for a binary search. 6 | 7 | ### Methods 8 | - [binarySearch](#binarySearch) - Find the index of the first element that passes the truth test (predicate). The default predicate is `==`. 9 | - [binarySearchFirst](#binarySearchFirst) - Find the index of the first element in the collection that matches the predicate. 10 | - [binarySearchLast](#binarySearchLast) - Find the index of the last element in the collection that matches the predicate. 11 | - [binarySearchRange](#binarySearchRange) - Find the startIndex and endIndex of elements that match the predicate. 12 | - [binarySearchInsertionIndexFor](#binarySearchInsertionIndexFor) - Find the index that an element would be inserted at. 13 | 14 | 15 | ### Arguments 16 | - `find` _required_ - The value you're looking for 17 | - `predicate` _optional_ - A truth test, e.g. `$0 <= $1`, `$0.hasPrefix($1)`, where `$0` is the value of `find` and `$1` is the current element value. See [more](#predicate). 18 | - `extract` _optional_ - The value to compare, e.g. `$0.someVal`, where `someVal` is the same type as the argument for 19 | `find`. See [more](#extract). 20 | 21 | **Important** 22 | > Running binary search requires sorted data. 23 | 24 | **Important** 25 | > Duplicate values are unsupported. 26 | 27 | ### Performance Benefit 28 | Binary Search runs in O(log n), as opposed to linear search which runs in O(n). 29 | Meaning, if you use linear search, in the worst case scenario, the number of steps to find your value will equal the number of elements. 30 | For binary search, this number is [dramatically lower][wikiPerformance]. 31 | 32 | All binary search methods in this extension return an `Index`, or a `Range` for `binarySearchRange`. 33 | 34 | 35 | #### binarySearch 36 | 37 | _In all examples below, assume the tests pass._ The documation code has tests. 38 | 39 | Find an item that is equal to the first argument. 40 | 41 | ```Swift 42 | let sortedInts = [0,1,2,3,4,5,6,7,8,9,10] 43 | guard let intIndex = sortedInts.binarySearch(6) else { 44 | XCTFail("Failed to find Int 6") 45 | return 46 | } 47 | XCTAssertEqual(intIndex, 6) 48 | // In this contrived example, our values are the same as the indices. 49 | XCTAssertEqual(sortedInts[intIndex], 6) 50 | ``` 51 | 52 | ```Swift 53 | let fruitsArray = ["apples", "bananas", "oranges"] 54 | guard let fruitIndex = fruitsArray.binarySearch("bananas") else { 55 | XCTFail("Failed to find 'bananas'") 56 | return 57 | } 58 | XCTAssertEqual(fruitIndex, 1) 59 | XCTAssertEqual(fruitsArray[fruitIndex], "bananas") 60 | 61 | let kiwiIndex = fruitsArray.binarySearch("kiwi") 62 | XCTAssertNil(kiwiIndex) // If the search fails the result is nil 63 | ``` 64 | 65 | #### Predicate 66 | An optional argument to evaluate truthfulness. The default is `$0 == $1`, where `$0` is the value of `find` and `$1` is the current element value. 67 | 68 | 69 | ```Swift 70 | let fruitsArray = ["apples", "bananas", "oranges"] 71 | guard let index = fruitsArray.binarySearch("bana", predicate:{$0.hasPrefix($1)}) else { 72 | XCTFail("Failed to find element that begins with 'bana'") 73 | return 74 | } 75 | XCTAssertEqual(index, 1) 76 | XCTAssertEqual(fruitsArray[index], "bananas") 77 | ``` 78 | 79 | 80 | #### Extract 81 | An optional argument to extract a value. The value must match the type you're searching for, 82 | and the array must be sorted on the property that you're extracting. 83 | 84 | The default is `Generator.Element -> T = {(el) -> T in return el as! T}` 85 | 86 | ```Swift 87 | struct Fruit { 88 | let name:String 89 | } 90 | // An Array of Fruits 91 | let fruits = [ 92 | Fruit(name:"apple"), 93 | Fruit(name:"banana"), 94 | Fruit(name:"orange") 95 | ] 96 | guard let index = fruits.binarySearch("banana", extract:{$0.name}) else { 97 | XCTFail("Failed to find a dictionary with a name 'bananas'") 98 | return 99 | } 100 | XCTAssertEqual(index, 1) 101 | XCTAssertEqual(fruits[index].name, "banana") 102 | ``` 103 | 104 | 105 | #### binarySearchFirst 106 | 107 | Find the value with the lowest index that meets the predicate. 108 | 109 | ```Swift 110 | let sortedArray = [0,5,15,75,100] 111 | guard let indexGreaterThanOrEqual = sortedArray.binarySearchFirst(10, predicate: {$0 >= $1}) else { 112 | XCTFail("Failed to find an index greater than or equal to 10") 113 | return 114 | } 115 | XCTAssertEqual(indexGreaterThanOrEqual, 2) 116 | XCTAssertEqual(sortedArray[indexGreaterThanOrEqual], 15) 117 | 118 | guard let indexLessThanOrEqual = sortedArray.binarySearchFirst(10, predicate: {$0 <= $1}) else { 119 | XCTFail("Failed to find an index less than or equal to 10") 120 | return 121 | } 122 | XCTAssertEqual(indexLessThanOrEqual, 0) 123 | XCTAssertEqual(sortedArray[indexLessThanOrEqual], 0) 124 | ``` 125 | 126 | #### binarySearchLast 127 | 128 | Find the value with the highest index that meets the predicate. 129 | ```Swift 130 | let sortedArray = [0,5,15,75,100] 131 | guard let indexGreaterThanOrEqual = sortedArray.binarySearchLast(10, predicate: {$0 >= $1}) else { 132 | XCTFail("Failed to find an index greater than or equal to 10") 133 | return 134 | } 135 | XCTAssertEqual(indexGreaterThanOrEqual, 4) 136 | XCTAssertEqual(sortedArray[indexGreaterThanOrEqual], 100) 137 | 138 | guard let indexLessThanOrEqual = sortedArray.binarySearchLast(10, predicate: {$0 <= $1}) else { 139 | XCTFail("Failed to find an index less than or equal to 10") 140 | return 141 | } 142 | XCTAssertEqual(indexLessThanOrEqual, 1) 143 | XCTAssertEqual(sortedArray[indexLessThanOrEqual], 5) 144 | ``` 145 | 146 | 147 | #### binarySearchRange 148 | 149 | A convenience method that wraps [binarySearchFirst](#binarySearchFirst) and [binarySearchLast](#binarySearchLast) 150 | 151 | ```Swift 152 | let sortedArray = [0,5,15,75,100] 153 | guard let range = sortedArray.binarySearchRange(10, predicate:{$0 >= $1}) else { 154 | XCTFail("Failed to find a range of Ints greater than or equal to 10") 155 | return 156 | } 157 | XCTAssertEqual(range.startIndex, 2) 158 | XCTAssertEqual(sortedArray[range.startIndex], 15) 159 | XCTAssertEqual(range.endIndex, 4) 160 | XCTAssertEqual(sortedArray[range.endIndex], 100) 161 | ``` 162 | 163 | If you need to have a different predicate for the start and end, just use the `binarySearchFirst` and `binarySearchLast` methods: 164 | 165 | ```Swift 166 | let sortedArray = [0,5,15,75,100,150] 167 | guard let startIndex = sortedArray.binarySearchFirst(10, predicate:{$0 >= $1}), 168 | endIndex = sortedArray.binarySearchLast(100, predicate:{$0 <= $1}) 169 | else { 170 | XCTFail("Failed to find a range of Ints greater than or equal to 10 and less than or equal to 100") 171 | return 172 | } 173 | // Find the range of values that's greater than or equal to 10 174 | XCTAssertEqual(startIndex, 2) 175 | XCTAssertEqual(sortedArray[startIndex], 15) 176 | XCTAssertEqual(endIndex, 4) 177 | XCTAssertEqual(sortedArray[endIndex], 100) 178 | ``` 179 | 180 | 181 | #### binarySearchInsertionIndexFor 182 | 183 | Returns the `Index` that an element could be inserted at while maintaing the sort. If the return value is nil, the element already exists in the collection. 184 | 185 | ```Swift 186 | guard let foundIndex = [0,5,15,75,100].binarySearchInsertionIndexFor(10) else { 187 | XCTFail("The find should have succeeded") 188 | return 189 | } 190 | // The value 10 would go at the 2nd index 191 | XCTAssertEqual(foundIndex, 2) 192 | ``` 193 | ```Swift 194 | guard let foundIndex = [0,5,15,75,100].binarySearchInsertionIndexFor(-5) else { 195 | XCTFail("The find should have succeeded") 196 | return 197 | } 198 | // The value -5 would go at the start index 199 | XCTAssertEqual(foundIndex, 0) 200 | ``` 201 | ```Swift 202 | guard let foundIndex = [0,5,15,75,100].binarySearchInsertionIndexFor(500) else { 203 | XCTFail("The find should have succeeded") 204 | return 205 | } 206 | // The value 500 would go at the end index 207 | XCTAssertEqual(foundIndex, 5) 208 | ``` 209 | ```Swift 210 | guard let _ = [0,5,15,75,100].binarySearchInsertionIndexFor(75) else { 211 | XCTAssertTrue(true, "The element already exists") 212 | return 213 | } 214 | XCTFail("The foundIndex should be nil") 215 | ``` 216 | 217 | #### Installation 218 | 219 | Copy the BinarySearch.swift file into your project, or use [Carthage][]. 220 | 221 | # To Do # 222 | * [ ] What can the predicate contain? 223 | * [ ] Support for duplicate values, or throw an error when they're encountered 224 | * [ ] Clean up tests. Write more tests. 225 | * [x] Carthage support 226 | * [x] Installation instructions 227 | * [x] Run tests on Travis CI 228 | 229 | [wiki]: https://en.wikipedia.org/wiki/Binary_search_algorithm 230 | [wikiPerformance]: https://en.wikipedia.org/wiki/Binary_search_algorithm#Performance "Binary Search Performance" 231 | [Collections]: https://developer.apple.com/library/watchos/documentation/Swift/Reference/Swift_CollectionType_Protocol/index.html 232 | [Array]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/ 233 | [Set]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Set_Structure/ 234 | [Dictionary]: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Dictionary_Structure/ 235 | [Carthage]: https://github.com/Carthage/Carthage 236 | 237 | -------------------------------------------------------------------------------- /BinarySearch.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 002BFDC61BFE44BB00709DC4 /* BinarySearchLastTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002BFDC51BFE44BB00709DC4 /* BinarySearchLastTests.swift */; }; 11 | 002BFDC81BFF696300709DC4 /* BinarySearchRangeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002BFDC71BFF696300709DC4 /* BinarySearchRangeTests.swift */; }; 12 | 002BFDCA1BFF70C600709DC4 /* BinarySearchExtractTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002BFDC91BFF70C600709DC4 /* BinarySearchExtractTests.swift */; }; 13 | 002BFDCC1C00BC1200709DC4 /* DocumentationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002BFDCB1C00BC1200709DC4 /* DocumentationTests.swift */; }; 14 | 007563E41BFA1AE1000ED5EB /* BinarySearch.h in Headers */ = {isa = PBXBuildFile; fileRef = 007563E31BFA1AE1000ED5EB /* BinarySearch.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 007563EB1BFA1AE1000ED5EB /* BinarySearch.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 007563E01BFA1AE1000ED5EB /* BinarySearch.framework */; }; 16 | 007563F01BFA1AE1000ED5EB /* BinarySearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007563EF1BFA1AE1000ED5EB /* BinarySearchTests.swift */; }; 17 | 007563FB1BFA1AF3000ED5EB /* BinarySearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007563FA1BFA1AF3000ED5EB /* BinarySearch.swift */; }; 18 | 00A0B1D61C235F2000FDF27A /* BinarySearchInsertionIndexForTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A0B1D51C235F2000FDF27A /* BinarySearchInsertionIndexForTests.swift */; }; 19 | 00AB63511BFBEF98001514CC /* BinarySearchFirstTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AB63501BFBEF98001514CC /* BinarySearchFirstTests.swift */; }; 20 | 00BAE0A61C0277730090B52D /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BAE0A51C0277730090B52D /* PerformanceTests.swift */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | 007563EC1BFA1AE1000ED5EB /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = 007563D71BFA1AE1000ED5EB /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = 007563DF1BFA1AE1000ED5EB; 29 | remoteInfo = BinarySearch; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 002BFDC51BFE44BB00709DC4 /* BinarySearchLastTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinarySearchLastTests.swift; sourceTree = ""; }; 35 | 002BFDC71BFF696300709DC4 /* BinarySearchRangeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinarySearchRangeTests.swift; sourceTree = ""; }; 36 | 002BFDC91BFF70C600709DC4 /* BinarySearchExtractTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinarySearchExtractTests.swift; sourceTree = ""; }; 37 | 002BFDCB1C00BC1200709DC4 /* DocumentationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentationTests.swift; sourceTree = ""; }; 38 | 007563E01BFA1AE1000ED5EB /* BinarySearch.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BinarySearch.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 007563E31BFA1AE1000ED5EB /* BinarySearch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BinarySearch.h; sourceTree = ""; }; 40 | 007563E51BFA1AE1000ED5EB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 41 | 007563EA1BFA1AE1000ED5EB /* BinarySearchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BinarySearchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 007563EF1BFA1AE1000ED5EB /* BinarySearchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinarySearchTests.swift; sourceTree = ""; }; 43 | 007563F11BFA1AE1000ED5EB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 007563FA1BFA1AF3000ED5EB /* BinarySearch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinarySearch.swift; sourceTree = ""; }; 45 | 00A0B1D51C235F2000FDF27A /* BinarySearchInsertionIndexForTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinarySearchInsertionIndexForTests.swift; sourceTree = ""; }; 46 | 00AB63501BFBEF98001514CC /* BinarySearchFirstTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinarySearchFirstTests.swift; sourceTree = ""; }; 47 | 00BAE0A51C0277730090B52D /* PerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = ""; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | 007563DC1BFA1AE1000ED5EB /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | 007563E71BFA1AE1000ED5EB /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | 007563EB1BFA1AE1000ED5EB /* BinarySearch.framework in Frameworks */, 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | 007563D61BFA1AE1000ED5EB = { 70 | isa = PBXGroup; 71 | children = ( 72 | 007563E21BFA1AE1000ED5EB /* Sources */, 73 | 007563EE1BFA1AE1000ED5EB /* BinarySearchTests */, 74 | 007563E11BFA1AE1000ED5EB /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 007563E11BFA1AE1000ED5EB /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 007563E01BFA1AE1000ED5EB /* BinarySearch.framework */, 82 | 007563EA1BFA1AE1000ED5EB /* BinarySearchTests.xctest */, 83 | ); 84 | name = Products; 85 | sourceTree = ""; 86 | }; 87 | 007563E21BFA1AE1000ED5EB /* Sources */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 007563E31BFA1AE1000ED5EB /* BinarySearch.h */, 91 | 007563E51BFA1AE1000ED5EB /* Info.plist */, 92 | 007563FA1BFA1AF3000ED5EB /* BinarySearch.swift */, 93 | ); 94 | path = Sources; 95 | sourceTree = ""; 96 | }; 97 | 007563EE1BFA1AE1000ED5EB /* BinarySearchTests */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 007563EF1BFA1AE1000ED5EB /* BinarySearchTests.swift */, 101 | 00AB63501BFBEF98001514CC /* BinarySearchFirstTests.swift */, 102 | 007563F11BFA1AE1000ED5EB /* Info.plist */, 103 | 002BFDC51BFE44BB00709DC4 /* BinarySearchLastTests.swift */, 104 | 002BFDC71BFF696300709DC4 /* BinarySearchRangeTests.swift */, 105 | 002BFDC91BFF70C600709DC4 /* BinarySearchExtractTests.swift */, 106 | 002BFDCB1C00BC1200709DC4 /* DocumentationTests.swift */, 107 | 00BAE0A51C0277730090B52D /* PerformanceTests.swift */, 108 | 00A0B1D51C235F2000FDF27A /* BinarySearchInsertionIndexForTests.swift */, 109 | ); 110 | path = BinarySearchTests; 111 | sourceTree = ""; 112 | }; 113 | /* End PBXGroup section */ 114 | 115 | /* Begin PBXHeadersBuildPhase section */ 116 | 007563DD1BFA1AE1000ED5EB /* Headers */ = { 117 | isa = PBXHeadersBuildPhase; 118 | buildActionMask = 2147483647; 119 | files = ( 120 | 007563E41BFA1AE1000ED5EB /* BinarySearch.h in Headers */, 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | /* End PBXHeadersBuildPhase section */ 125 | 126 | /* Begin PBXNativeTarget section */ 127 | 007563DF1BFA1AE1000ED5EB /* BinarySearch */ = { 128 | isa = PBXNativeTarget; 129 | buildConfigurationList = 007563F41BFA1AE1000ED5EB /* Build configuration list for PBXNativeTarget "BinarySearch" */; 130 | buildPhases = ( 131 | 007563DB1BFA1AE1000ED5EB /* Sources */, 132 | 007563DC1BFA1AE1000ED5EB /* Frameworks */, 133 | 007563DD1BFA1AE1000ED5EB /* Headers */, 134 | 007563DE1BFA1AE1000ED5EB /* Resources */, 135 | ); 136 | buildRules = ( 137 | ); 138 | dependencies = ( 139 | ); 140 | name = BinarySearch; 141 | productName = BinarySearch; 142 | productReference = 007563E01BFA1AE1000ED5EB /* BinarySearch.framework */; 143 | productType = "com.apple.product-type.framework"; 144 | }; 145 | 007563E91BFA1AE1000ED5EB /* BinarySearchTests */ = { 146 | isa = PBXNativeTarget; 147 | buildConfigurationList = 007563F71BFA1AE1000ED5EB /* Build configuration list for PBXNativeTarget "BinarySearchTests" */; 148 | buildPhases = ( 149 | 007563E61BFA1AE1000ED5EB /* Sources */, 150 | 007563E71BFA1AE1000ED5EB /* Frameworks */, 151 | 007563E81BFA1AE1000ED5EB /* Resources */, 152 | ); 153 | buildRules = ( 154 | ); 155 | dependencies = ( 156 | 007563ED1BFA1AE1000ED5EB /* PBXTargetDependency */, 157 | ); 158 | name = BinarySearchTests; 159 | productName = BinarySearchTests; 160 | productReference = 007563EA1BFA1AE1000ED5EB /* BinarySearchTests.xctest */; 161 | productType = "com.apple.product-type.bundle.unit-test"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 007563D71BFA1AE1000ED5EB /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastSwiftUpdateCheck = 0710; 170 | LastUpgradeCheck = 0710; 171 | ORGANIZATIONNAME = "Gianni Ferullo"; 172 | TargetAttributes = { 173 | 007563DF1BFA1AE1000ED5EB = { 174 | CreatedOnToolsVersion = 7.1; 175 | LastSwiftMigration = 0820; 176 | }; 177 | 007563E91BFA1AE1000ED5EB = { 178 | CreatedOnToolsVersion = 7.1; 179 | LastSwiftMigration = 0820; 180 | }; 181 | }; 182 | }; 183 | buildConfigurationList = 007563DA1BFA1AE1000ED5EB /* Build configuration list for PBXProject "BinarySearch" */; 184 | compatibilityVersion = "Xcode 3.2"; 185 | developmentRegion = English; 186 | hasScannedForEncodings = 0; 187 | knownRegions = ( 188 | en, 189 | ); 190 | mainGroup = 007563D61BFA1AE1000ED5EB; 191 | productRefGroup = 007563E11BFA1AE1000ED5EB /* Products */; 192 | projectDirPath = ""; 193 | projectRoot = ""; 194 | targets = ( 195 | 007563DF1BFA1AE1000ED5EB /* BinarySearch */, 196 | 007563E91BFA1AE1000ED5EB /* BinarySearchTests */, 197 | ); 198 | }; 199 | /* End PBXProject section */ 200 | 201 | /* Begin PBXResourcesBuildPhase section */ 202 | 007563DE1BFA1AE1000ED5EB /* Resources */ = { 203 | isa = PBXResourcesBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | 007563E81BFA1AE1000ED5EB /* Resources */ = { 210 | isa = PBXResourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | }; 216 | /* End PBXResourcesBuildPhase section */ 217 | 218 | /* Begin PBXSourcesBuildPhase section */ 219 | 007563DB1BFA1AE1000ED5EB /* Sources */ = { 220 | isa = PBXSourcesBuildPhase; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | 007563FB1BFA1AF3000ED5EB /* BinarySearch.swift in Sources */, 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | }; 227 | 007563E61BFA1AE1000ED5EB /* Sources */ = { 228 | isa = PBXSourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | 00BAE0A61C0277730090B52D /* PerformanceTests.swift in Sources */, 232 | 002BFDCC1C00BC1200709DC4 /* DocumentationTests.swift in Sources */, 233 | 007563F01BFA1AE1000ED5EB /* BinarySearchTests.swift in Sources */, 234 | 00A0B1D61C235F2000FDF27A /* BinarySearchInsertionIndexForTests.swift in Sources */, 235 | 00AB63511BFBEF98001514CC /* BinarySearchFirstTests.swift in Sources */, 236 | 002BFDC81BFF696300709DC4 /* BinarySearchRangeTests.swift in Sources */, 237 | 002BFDC61BFE44BB00709DC4 /* BinarySearchLastTests.swift in Sources */, 238 | 002BFDCA1BFF70C600709DC4 /* BinarySearchExtractTests.swift in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | /* End PBXSourcesBuildPhase section */ 243 | 244 | /* Begin PBXTargetDependency section */ 245 | 007563ED1BFA1AE1000ED5EB /* PBXTargetDependency */ = { 246 | isa = PBXTargetDependency; 247 | target = 007563DF1BFA1AE1000ED5EB /* BinarySearch */; 248 | targetProxy = 007563EC1BFA1AE1000ED5EB /* PBXContainerItemProxy */; 249 | }; 250 | /* End PBXTargetDependency section */ 251 | 252 | /* Begin XCBuildConfiguration section */ 253 | 007563F21BFA1AE1000ED5EB /* Debug */ = { 254 | isa = XCBuildConfiguration; 255 | buildSettings = { 256 | ALWAYS_SEARCH_USER_PATHS = NO; 257 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 258 | CLANG_CXX_LIBRARY = "libc++"; 259 | CLANG_ENABLE_MODULES = YES; 260 | CLANG_ENABLE_OBJC_ARC = YES; 261 | CLANG_WARN_BOOL_CONVERSION = YES; 262 | CLANG_WARN_CONSTANT_CONVERSION = YES; 263 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 264 | CLANG_WARN_EMPTY_BODY = YES; 265 | CLANG_WARN_ENUM_CONVERSION = YES; 266 | CLANG_WARN_INT_CONVERSION = YES; 267 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 268 | CLANG_WARN_UNREACHABLE_CODE = YES; 269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 270 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 271 | COPY_PHASE_STRIP = NO; 272 | CURRENT_PROJECT_VERSION = 1; 273 | DEBUG_INFORMATION_FORMAT = dwarf; 274 | ENABLE_STRICT_OBJC_MSGSEND = YES; 275 | ENABLE_TESTABILITY = YES; 276 | GCC_C_LANGUAGE_STANDARD = gnu99; 277 | GCC_DYNAMIC_NO_PIC = NO; 278 | GCC_NO_COMMON_BLOCKS = YES; 279 | GCC_OPTIMIZATION_LEVEL = 0; 280 | GCC_PREPROCESSOR_DEFINITIONS = ( 281 | "DEBUG=1", 282 | "$(inherited)", 283 | ); 284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 285 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 286 | GCC_WARN_UNDECLARED_SELECTOR = YES; 287 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 288 | GCC_WARN_UNUSED_FUNCTION = YES; 289 | GCC_WARN_UNUSED_VARIABLE = YES; 290 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 291 | MTL_ENABLE_DEBUG_INFO = YES; 292 | ONLY_ACTIVE_ARCH = YES; 293 | SDKROOT = iphoneos; 294 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 295 | TARGETED_DEVICE_FAMILY = "1,2"; 296 | VERSIONING_SYSTEM = "apple-generic"; 297 | VERSION_INFO_PREFIX = ""; 298 | }; 299 | name = Debug; 300 | }; 301 | 007563F31BFA1AE1000ED5EB /* Release */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ALWAYS_SEARCH_USER_PATHS = NO; 305 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 306 | CLANG_CXX_LIBRARY = "libc++"; 307 | CLANG_ENABLE_MODULES = YES; 308 | CLANG_ENABLE_OBJC_ARC = YES; 309 | CLANG_WARN_BOOL_CONVERSION = YES; 310 | CLANG_WARN_CONSTANT_CONVERSION = YES; 311 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 312 | CLANG_WARN_EMPTY_BODY = YES; 313 | CLANG_WARN_ENUM_CONVERSION = YES; 314 | CLANG_WARN_INT_CONVERSION = YES; 315 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 316 | CLANG_WARN_UNREACHABLE_CODE = YES; 317 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 318 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 319 | COPY_PHASE_STRIP = NO; 320 | CURRENT_PROJECT_VERSION = 1; 321 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 322 | ENABLE_NS_ASSERTIONS = NO; 323 | ENABLE_STRICT_OBJC_MSGSEND = YES; 324 | GCC_C_LANGUAGE_STANDARD = gnu99; 325 | GCC_NO_COMMON_BLOCKS = YES; 326 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 327 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 328 | GCC_WARN_UNDECLARED_SELECTOR = YES; 329 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 330 | GCC_WARN_UNUSED_FUNCTION = YES; 331 | GCC_WARN_UNUSED_VARIABLE = YES; 332 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 333 | MTL_ENABLE_DEBUG_INFO = NO; 334 | SDKROOT = iphoneos; 335 | TARGETED_DEVICE_FAMILY = "1,2"; 336 | VALIDATE_PRODUCT = YES; 337 | VERSIONING_SYSTEM = "apple-generic"; 338 | VERSION_INFO_PREFIX = ""; 339 | }; 340 | name = Release; 341 | }; 342 | 007563F51BFA1AE1000ED5EB /* Debug */ = { 343 | isa = XCBuildConfiguration; 344 | buildSettings = { 345 | APPLICATION_EXTENSION_API_ONLY = YES; 346 | CLANG_ENABLE_MODULES = YES; 347 | DEFINES_MODULE = YES; 348 | DYLIB_COMPATIBILITY_VERSION = 1; 349 | DYLIB_CURRENT_VERSION = 1; 350 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 351 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; 352 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 353 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 354 | PRODUCT_BUNDLE_IDENTIFIER = hotsoup.BinarySearch; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | SKIP_INSTALL = YES; 357 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 358 | SWIFT_VERSION = 3.0; 359 | }; 360 | name = Debug; 361 | }; 362 | 007563F61BFA1AE1000ED5EB /* Release */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | APPLICATION_EXTENSION_API_ONLY = YES; 366 | CLANG_ENABLE_MODULES = YES; 367 | DEFINES_MODULE = YES; 368 | DYLIB_COMPATIBILITY_VERSION = 1; 369 | DYLIB_CURRENT_VERSION = 1; 370 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 371 | INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; 372 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 373 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 374 | PRODUCT_BUNDLE_IDENTIFIER = hotsoup.BinarySearch; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | SKIP_INSTALL = YES; 377 | SWIFT_VERSION = 3.0; 378 | }; 379 | name = Release; 380 | }; 381 | 007563F81BFA1AE1000ED5EB /* Debug */ = { 382 | isa = XCBuildConfiguration; 383 | buildSettings = { 384 | INFOPLIST_FILE = BinarySearchTests/Info.plist; 385 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 386 | PRODUCT_BUNDLE_IDENTIFIER = hotsoup.BinarySearchTests; 387 | PRODUCT_NAME = "$(TARGET_NAME)"; 388 | SWIFT_VERSION = 3.0; 389 | }; 390 | name = Debug; 391 | }; 392 | 007563F91BFA1AE1000ED5EB /* Release */ = { 393 | isa = XCBuildConfiguration; 394 | buildSettings = { 395 | INFOPLIST_FILE = BinarySearchTests/Info.plist; 396 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 397 | PRODUCT_BUNDLE_IDENTIFIER = hotsoup.BinarySearchTests; 398 | PRODUCT_NAME = "$(TARGET_NAME)"; 399 | SWIFT_VERSION = 3.0; 400 | }; 401 | name = Release; 402 | }; 403 | /* End XCBuildConfiguration section */ 404 | 405 | /* Begin XCConfigurationList section */ 406 | 007563DA1BFA1AE1000ED5EB /* Build configuration list for PBXProject "BinarySearch" */ = { 407 | isa = XCConfigurationList; 408 | buildConfigurations = ( 409 | 007563F21BFA1AE1000ED5EB /* Debug */, 410 | 007563F31BFA1AE1000ED5EB /* Release */, 411 | ); 412 | defaultConfigurationIsVisible = 0; 413 | defaultConfigurationName = Release; 414 | }; 415 | 007563F41BFA1AE1000ED5EB /* Build configuration list for PBXNativeTarget "BinarySearch" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | 007563F51BFA1AE1000ED5EB /* Debug */, 419 | 007563F61BFA1AE1000ED5EB /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | 007563F71BFA1AE1000ED5EB /* Build configuration list for PBXNativeTarget "BinarySearchTests" */ = { 425 | isa = XCConfigurationList; 426 | buildConfigurations = ( 427 | 007563F81BFA1AE1000ED5EB /* Debug */, 428 | 007563F91BFA1AE1000ED5EB /* Release */, 429 | ); 430 | defaultConfigurationIsVisible = 0; 431 | defaultConfigurationName = Release; 432 | }; 433 | /* End XCConfigurationList section */ 434 | }; 435 | rootObject = 007563D71BFA1AE1000ED5EB /* Project object */; 436 | } 437 | --------------------------------------------------------------------------------