├── .github └── workflows │ ├── linux.yml │ └── mac.yml ├── .gitignore ├── Configs ├── SortedArray.plist └── SortedArrayTests.plist ├── LICENSE.txt ├── Package.swift ├── README.md ├── SortedArray.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ ├── xcbaselines │ └── DD7502791C68FCFC006590AF.xcbaseline │ │ ├── 28BBA58F-EC4C-4E7A-943A-5B8FD5CC1F33.plist │ │ └── Info.plist │ └── xcschemes │ ├── Performance-Tests-macOS.xcscheme │ ├── SortedArray-iOS.xcscheme │ ├── SortedArray-macOS.xcscheme │ ├── SortedArray-tvOS.xcscheme │ └── SortedArray-watchOS.xcscheme ├── Sources └── SortedArray.swift └── Tests ├── LinuxMain.swift ├── PerformanceTests └── PerformanceTests.swift └── UnitTests ├── SortedArrayTests.swift └── TestHelpers.swift /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | # GitHub actions documentation: 2 | # https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions 3 | # 4 | # List of installed software for GitHub actions: 5 | # https://docs.github.com/en/actions/reference/software-installed-on-github-hosted-runners 6 | 7 | name: SwiftPM Linux 8 | on: 9 | push: 10 | workflow_dispatch: 11 | 12 | jobs: 13 | linux-swiftpm: 14 | name: Build and run tests 15 | runs-on: ubuntu-20.04 16 | # Ubuntu 20.04: Focal 17 | # Ubuntu 18.04: Xenial 18 | container: swift:focal 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Print Swift version 22 | run: swift --version 23 | - name: Run tests 24 | run: swift test 25 | -------------------------------------------------------------------------------- /.github/workflows/mac.yml: -------------------------------------------------------------------------------- 1 | # GitHub actions documentation: 2 | # https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions 3 | # 4 | # List of installed software for GitHub actions: 5 | # https://docs.github.com/en/actions/reference/software-installed-on-github-hosted-runners 6 | 7 | name: SwiftPM Mac 8 | on: 9 | push: 10 | workflow_dispatch: 11 | 12 | jobs: 13 | macos-swiftpm: 14 | name: Build and run tests 15 | runs-on: macOS-latest 16 | # Set DEVELOPER_DIR to customize Xcode version 17 | # env: 18 | # DEVELOPER_DIR: /Applications/Xcode_12.app 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Print Swift version 22 | run: swift --version 23 | - name: Run SwiftPM tests 24 | run: swift test 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | -------------------------------------------------------------------------------- /Configs/SortedArray.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 | 0.6.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2017 Ole Begemann. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Configs/SortedArrayTests.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 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Ole Begemann 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | import PackageDescription 3 | 4 | /// Provides the `SortedArray` type, an array that keeps its elements 5 | /// sorted according to a given sort predicate. 6 | /// 7 | /// - Author: Ole Begemann 8 | /// - Seealso: https://github.com/ole/SortedArray 9 | /// - Seealso: https://blog/2017/02/sorted-array/ 10 | /// 11 | let package = Package( 12 | name: "SortedArray", 13 | products: [ 14 | .library( 15 | name: "SortedArray", 16 | targets: ["SortedArray"]), 17 | ], 18 | targets: [ 19 | .target( 20 | name: "SortedArray", 21 | dependencies: [], 22 | path: "Sources"), 23 | .testTarget( 24 | name: "UnitTests", 25 | dependencies: ["SortedArray"]), 26 | .testTarget( 27 | name: "PerformanceTests", 28 | dependencies: ["SortedArray"]), 29 | ], 30 | swiftLanguageVersions: [4] 31 | ) 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SortedArray 2 | 3 | A sorted array type for Swift 4.0+. 4 | 5 | Provides the `SortedArray` type, an array that keeps its elements sorted according to a given sort predicate. 6 | 7 | Written by Ole Begemann, February 2017. 8 | 9 | For more info, see the [GitHub repo](https://github.com/ole/SortedArray) and my accompanying [blog article](https://oleb.net/blog/2017/02/sorted-array/). 10 | 11 | ## Supported Platforms 12 | 13 | The current release supports Swift 4.0 and up. 14 | 15 | If you need support for older Swift version, here's a list of the latest releases that support specific Swift versions: 16 | 17 | | Swift version | Latest SortedArray release | 18 | | ------------- | -------------------------- | 19 | | 4.x | master | 20 | | 3.x | [0.6.0](https://github.com/ole/SortedArray/releases/tag/0.6.0) | 21 | | 3.0 | [0.4](https://github.com/ole/SortedArray/releases/tag/0.4.0) | 22 | 23 | Since the code has no dependencies other than the Swift standard library (it doesn't even use Foundation), it should work on all platforms where Swift is available. 24 | 25 | I tested it on macOS, iOS, tvOS, and Linux. 26 | 27 | ## Usage 28 | 29 | ### Swift Package Manager 30 | 31 | Add this to your `Package.swift` file: 32 | 33 | ```swift 34 | // Package.swift 35 | import PackageDescription 36 | 37 | let package = Package( 38 | name: "", 39 | dependencies: [ 40 | .Package(url: "https://github.com/ole/SortedArray.git", majorVersion: 0) 41 | ] 42 | ) 43 | ``` 44 | 45 | ### Carthage 46 | 47 | Add this to your `Cartfile`: 48 | 49 | ``` 50 | github "ole/SortedArray" ~> 0.7 51 | ``` 52 | 53 | Integration via Carthage should work for macOS, iOS, tvOS, and watchOS targets. 54 | 55 | ### Manually 56 | 57 | Clone the repository and add or copy `SortedArray.swift` to your project. It has no dependencies. 58 | 59 | ## Dependencies 60 | 61 | None. 62 | 63 | ## License 64 | 65 | [MIT license](https://github.com/ole/SortedArray/blob/master/LICENSE.txt). 66 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 47; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 52D6D9871BEFF229002C0205 /* SortedArray.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* SortedArray.framework */; }; 11 | 52D6D9981BEFF375002C0205 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D6D9961BEFF375002C0205 /* SortedArray.swift */; }; 12 | 52D6D9EA1BEFFFA4002C0205 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D6D9961BEFF375002C0205 /* SortedArray.swift */; }; 13 | 52D6DA091BF00081002C0205 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D6D9961BEFF375002C0205 /* SortedArray.swift */; }; 14 | 52D6DA261BF00118002C0205 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D6D9961BEFF375002C0205 /* SortedArray.swift */; }; 15 | 5DB8B87920D7FF5E00D463D6 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB8B87520D7FF4600D463D6 /* PerformanceTests.swift */; }; 16 | 5DB8B87A20D7FF5F00D463D6 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB8B87520D7FF4600D463D6 /* PerformanceTests.swift */; }; 17 | 5DB8B87B20D7FF5F00D463D6 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB8B87520D7FF4600D463D6 /* PerformanceTests.swift */; }; 18 | 5DB8B87C20D7FF6200D463D6 /* SortedArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB8B87720D7FF4600D463D6 /* SortedArrayTests.swift */; }; 19 | 5DB8B87D20D7FF6200D463D6 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB8B87820D7FF4600D463D6 /* TestHelpers.swift */; }; 20 | 5DB8B87E20D7FF6300D463D6 /* SortedArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB8B87720D7FF4600D463D6 /* SortedArrayTests.swift */; }; 21 | 5DB8B87F20D7FF6300D463D6 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB8B87820D7FF4600D463D6 /* TestHelpers.swift */; }; 22 | 5DB8B88020D7FF6300D463D6 /* SortedArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB8B87720D7FF4600D463D6 /* SortedArrayTests.swift */; }; 23 | 5DB8B88120D7FF6300D463D6 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DB8B87820D7FF4600D463D6 /* TestHelpers.swift */; }; 24 | DD7502881C68FEDE006590AF /* SortedArray.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6DA0F1BF000BD002C0205 /* SortedArray.framework */; }; 25 | DD7502921C690C7A006590AF /* SortedArray.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D9F01BEFFFBE002C0205 /* SortedArray.framework */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 52D6D97B1BEFF229002C0205; 34 | remoteInfo = SortedArray; 35 | }; 36 | DD7502801C68FCFC006590AF /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 52D6DA0E1BF000BD002C0205; 41 | remoteInfo = "SortedArray-macOS"; 42 | }; 43 | DD7502931C690C7A006590AF /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 52D6D9731BEFF229002C0205 /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = 52D6D9EF1BEFFFBE002C0205; 48 | remoteInfo = "SortedArray-tvOS"; 49 | }; 50 | /* End PBXContainerItemProxy section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | 52D6D97C1BEFF229002C0205 /* SortedArray.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SortedArray.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 52D6D9861BEFF229002C0205 /* SortedArray-iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SortedArray-iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 52D6D9961BEFF375002C0205 /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SortedArray.swift; path = Sources/SortedArray.swift; sourceTree = ""; }; 56 | 52D6D9E21BEFFF6E002C0205 /* SortedArray.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SortedArray.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 52D6D9F01BEFFFBE002C0205 /* SortedArray.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SortedArray.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 52D6DA0F1BF000BD002C0205 /* SortedArray.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SortedArray.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 5D7AAFCC20D71F4200080885 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; 60 | 5D7AAFCD20D71F4200080885 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 61 | 5D7AAFCE20D71F4200080885 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 62 | 5D7AAFD020D722B100080885 /* travis-build-script.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "travis-build-script.sh"; sourceTree = ""; }; 63 | 5D7AAFD120D7231E00080885 /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = ""; }; 64 | 5DB8B87320D7FF4600D463D6 /* LinuxMain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinuxMain.swift; sourceTree = ""; }; 65 | 5DB8B87520D7FF4600D463D6 /* PerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = ""; }; 66 | 5DB8B87720D7FF4600D463D6 /* SortedArrayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortedArrayTests.swift; sourceTree = ""; }; 67 | 5DB8B87820D7FF4600D463D6 /* TestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestHelpers.swift; sourceTree = ""; }; 68 | AD2FAA261CD0B6D800659CF4 /* SortedArray.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SortedArray.plist; sourceTree = ""; }; 69 | AD2FAA281CD0B6E100659CF4 /* SortedArrayTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = SortedArrayTests.plist; sourceTree = ""; }; 70 | DD75027A1C68FCFC006590AF /* SortedArray-macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SortedArray-macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | DD75028D1C690C7A006590AF /* SortedArray-tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SortedArray-tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 72 | /* End PBXFileReference section */ 73 | 74 | /* Begin PBXFrameworksBuildPhase section */ 75 | 52D6D9781BEFF229002C0205 /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | 52D6D9831BEFF229002C0205 /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | 52D6D9871BEFF229002C0205 /* SortedArray.framework in Frameworks */, 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | 52D6D9DE1BEFFF6E002C0205 /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | 52D6D9EC1BEFFFBE002C0205 /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | ); 102 | runOnlyForDeploymentPostprocessing = 0; 103 | }; 104 | 52D6DA0B1BF000BD002C0205 /* Frameworks */ = { 105 | isa = PBXFrameworksBuildPhase; 106 | buildActionMask = 2147483647; 107 | files = ( 108 | ); 109 | runOnlyForDeploymentPostprocessing = 0; 110 | }; 111 | DD7502771C68FCFC006590AF /* Frameworks */ = { 112 | isa = PBXFrameworksBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | DD7502881C68FEDE006590AF /* SortedArray.framework in Frameworks */, 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | DD75028A1C690C7A006590AF /* Frameworks */ = { 120 | isa = PBXFrameworksBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | DD7502921C690C7A006590AF /* SortedArray.framework in Frameworks */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXFrameworksBuildPhase section */ 128 | 129 | /* Begin PBXGroup section */ 130 | 52D6D9721BEFF229002C0205 = { 131 | isa = PBXGroup; 132 | children = ( 133 | 52D6D9961BEFF375002C0205 /* SortedArray.swift */, 134 | 5DB8B87220D7FF4600D463D6 /* Tests */, 135 | 5D7AAFCB20D71F0800080885 /* Other files (not in any target) */, 136 | 5D7AAFCF20D722B100080885 /* scripts */, 137 | 52D6D99C1BEFF38C002C0205 /* Configs */, 138 | 52D6D97D1BEFF229002C0205 /* Products */, 139 | ); 140 | sourceTree = ""; 141 | }; 142 | 52D6D97D1BEFF229002C0205 /* Products */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 52D6D97C1BEFF229002C0205 /* SortedArray.framework */, 146 | 52D6D9861BEFF229002C0205 /* SortedArray-iOS Tests.xctest */, 147 | 52D6D9E21BEFFF6E002C0205 /* SortedArray.framework */, 148 | 52D6D9F01BEFFFBE002C0205 /* SortedArray.framework */, 149 | 52D6DA0F1BF000BD002C0205 /* SortedArray.framework */, 150 | DD75027A1C68FCFC006590AF /* SortedArray-macOS Tests.xctest */, 151 | DD75028D1C690C7A006590AF /* SortedArray-tvOS Tests.xctest */, 152 | ); 153 | name = Products; 154 | sourceTree = ""; 155 | }; 156 | 52D6D99C1BEFF38C002C0205 /* Configs */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | DD7502721C68FC1B006590AF /* Frameworks */, 160 | DD7502731C68FC20006590AF /* Tests */, 161 | ); 162 | path = Configs; 163 | sourceTree = ""; 164 | }; 165 | 5D7AAFCB20D71F0800080885 /* Other files (not in any target) */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 5D7AAFCE20D71F4200080885 /* README.md */, 169 | 5D7AAFCC20D71F4200080885 /* LICENSE.txt */, 170 | 5D7AAFCD20D71F4200080885 /* Package.swift */, 171 | 5D7AAFD120D7231E00080885 /* .travis.yml */, 172 | ); 173 | name = "Other files (not in any target)"; 174 | sourceTree = ""; 175 | }; 176 | 5D7AAFCF20D722B100080885 /* scripts */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | 5D7AAFD020D722B100080885 /* travis-build-script.sh */, 180 | ); 181 | path = scripts; 182 | sourceTree = ""; 183 | }; 184 | 5DB8B87220D7FF4600D463D6 /* Tests */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | 5DB8B87320D7FF4600D463D6 /* LinuxMain.swift */, 188 | 5DB8B87420D7FF4600D463D6 /* PerformanceTests */, 189 | 5DB8B87620D7FF4600D463D6 /* UnitTests */, 190 | ); 191 | path = Tests; 192 | sourceTree = ""; 193 | }; 194 | 5DB8B87420D7FF4600D463D6 /* PerformanceTests */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | 5DB8B87520D7FF4600D463D6 /* PerformanceTests.swift */, 198 | ); 199 | path = PerformanceTests; 200 | sourceTree = ""; 201 | }; 202 | 5DB8B87620D7FF4600D463D6 /* UnitTests */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 5DB8B87720D7FF4600D463D6 /* SortedArrayTests.swift */, 206 | 5DB8B87820D7FF4600D463D6 /* TestHelpers.swift */, 207 | ); 208 | path = UnitTests; 209 | sourceTree = ""; 210 | }; 211 | DD7502721C68FC1B006590AF /* Frameworks */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | AD2FAA261CD0B6D800659CF4 /* SortedArray.plist */, 215 | ); 216 | name = Frameworks; 217 | sourceTree = ""; 218 | }; 219 | DD7502731C68FC20006590AF /* Tests */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | AD2FAA281CD0B6E100659CF4 /* SortedArrayTests.plist */, 223 | ); 224 | name = Tests; 225 | sourceTree = ""; 226 | }; 227 | /* End PBXGroup section */ 228 | 229 | /* Begin PBXHeadersBuildPhase section */ 230 | 52D6D9791BEFF229002C0205 /* Headers */ = { 231 | isa = PBXHeadersBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | 52D6D9DF1BEFFF6E002C0205 /* Headers */ = { 238 | isa = PBXHeadersBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | 52D6D9ED1BEFFFBE002C0205 /* Headers */ = { 245 | isa = PBXHeadersBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | 52D6DA0C1BF000BD002C0205 /* Headers */ = { 252 | isa = PBXHeadersBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | /* End PBXHeadersBuildPhase section */ 259 | 260 | /* Begin PBXNativeTarget section */ 261 | 52D6D97B1BEFF229002C0205 /* SortedArray-iOS */ = { 262 | isa = PBXNativeTarget; 263 | buildConfigurationList = 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "SortedArray-iOS" */; 264 | buildPhases = ( 265 | 52D6D9771BEFF229002C0205 /* Sources */, 266 | 52D6D9781BEFF229002C0205 /* Frameworks */, 267 | 52D6D9791BEFF229002C0205 /* Headers */, 268 | 52D6D97A1BEFF229002C0205 /* Resources */, 269 | ); 270 | buildRules = ( 271 | ); 272 | dependencies = ( 273 | ); 274 | name = "SortedArray-iOS"; 275 | productName = SortedArray; 276 | productReference = 52D6D97C1BEFF229002C0205 /* SortedArray.framework */; 277 | productType = "com.apple.product-type.framework"; 278 | }; 279 | 52D6D9851BEFF229002C0205 /* SortedArray-iOS Tests */ = { 280 | isa = PBXNativeTarget; 281 | buildConfigurationList = 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "SortedArray-iOS Tests" */; 282 | buildPhases = ( 283 | 52D6D9821BEFF229002C0205 /* Sources */, 284 | 52D6D9831BEFF229002C0205 /* Frameworks */, 285 | 52D6D9841BEFF229002C0205 /* Resources */, 286 | ); 287 | buildRules = ( 288 | ); 289 | dependencies = ( 290 | 52D6D9891BEFF229002C0205 /* PBXTargetDependency */, 291 | ); 292 | name = "SortedArray-iOS Tests"; 293 | productName = SortedArrayTests; 294 | productReference = 52D6D9861BEFF229002C0205 /* SortedArray-iOS Tests.xctest */; 295 | productType = "com.apple.product-type.bundle.unit-test"; 296 | }; 297 | 52D6D9E11BEFFF6E002C0205 /* SortedArray-watchOS */ = { 298 | isa = PBXNativeTarget; 299 | buildConfigurationList = 52D6D9E71BEFFF6E002C0205 /* Build configuration list for PBXNativeTarget "SortedArray-watchOS" */; 300 | buildPhases = ( 301 | 52D6D9DD1BEFFF6E002C0205 /* Sources */, 302 | 52D6D9DE1BEFFF6E002C0205 /* Frameworks */, 303 | 52D6D9DF1BEFFF6E002C0205 /* Headers */, 304 | 52D6D9E01BEFFF6E002C0205 /* Resources */, 305 | ); 306 | buildRules = ( 307 | ); 308 | dependencies = ( 309 | ); 310 | name = "SortedArray-watchOS"; 311 | productName = "SortedArray-watchOS"; 312 | productReference = 52D6D9E21BEFFF6E002C0205 /* SortedArray.framework */; 313 | productType = "com.apple.product-type.framework"; 314 | }; 315 | 52D6D9EF1BEFFFBE002C0205 /* SortedArray-tvOS */ = { 316 | isa = PBXNativeTarget; 317 | buildConfigurationList = 52D6DA011BEFFFBE002C0205 /* Build configuration list for PBXNativeTarget "SortedArray-tvOS" */; 318 | buildPhases = ( 319 | 52D6D9EB1BEFFFBE002C0205 /* Sources */, 320 | 52D6D9EC1BEFFFBE002C0205 /* Frameworks */, 321 | 52D6D9ED1BEFFFBE002C0205 /* Headers */, 322 | 52D6D9EE1BEFFFBE002C0205 /* Resources */, 323 | ); 324 | buildRules = ( 325 | ); 326 | dependencies = ( 327 | ); 328 | name = "SortedArray-tvOS"; 329 | productName = "SortedArray-tvOS"; 330 | productReference = 52D6D9F01BEFFFBE002C0205 /* SortedArray.framework */; 331 | productType = "com.apple.product-type.framework"; 332 | }; 333 | 52D6DA0E1BF000BD002C0205 /* SortedArray-macOS */ = { 334 | isa = PBXNativeTarget; 335 | buildConfigurationList = 52D6DA201BF000BD002C0205 /* Build configuration list for PBXNativeTarget "SortedArray-macOS" */; 336 | buildPhases = ( 337 | 52D6DA0A1BF000BD002C0205 /* Sources */, 338 | 52D6DA0B1BF000BD002C0205 /* Frameworks */, 339 | 52D6DA0C1BF000BD002C0205 /* Headers */, 340 | 52D6DA0D1BF000BD002C0205 /* Resources */, 341 | ); 342 | buildRules = ( 343 | ); 344 | dependencies = ( 345 | ); 346 | name = "SortedArray-macOS"; 347 | productName = "SortedArray-macOS"; 348 | productReference = 52D6DA0F1BF000BD002C0205 /* SortedArray.framework */; 349 | productType = "com.apple.product-type.framework"; 350 | }; 351 | DD7502791C68FCFC006590AF /* SortedArray-macOS Tests */ = { 352 | isa = PBXNativeTarget; 353 | buildConfigurationList = DD7502821C68FCFC006590AF /* Build configuration list for PBXNativeTarget "SortedArray-macOS Tests" */; 354 | buildPhases = ( 355 | DD7502761C68FCFC006590AF /* Sources */, 356 | DD7502771C68FCFC006590AF /* Frameworks */, 357 | DD7502781C68FCFC006590AF /* Resources */, 358 | ); 359 | buildRules = ( 360 | ); 361 | dependencies = ( 362 | DD7502811C68FCFC006590AF /* PBXTargetDependency */, 363 | ); 364 | name = "SortedArray-macOS Tests"; 365 | productName = "SortedArray-OS Tests"; 366 | productReference = DD75027A1C68FCFC006590AF /* SortedArray-macOS Tests.xctest */; 367 | productType = "com.apple.product-type.bundle.unit-test"; 368 | }; 369 | DD75028C1C690C7A006590AF /* SortedArray-tvOS Tests */ = { 370 | isa = PBXNativeTarget; 371 | buildConfigurationList = DD7502951C690C7A006590AF /* Build configuration list for PBXNativeTarget "SortedArray-tvOS Tests" */; 372 | buildPhases = ( 373 | DD7502891C690C7A006590AF /* Sources */, 374 | DD75028A1C690C7A006590AF /* Frameworks */, 375 | DD75028B1C690C7A006590AF /* Resources */, 376 | ); 377 | buildRules = ( 378 | ); 379 | dependencies = ( 380 | DD7502941C690C7A006590AF /* PBXTargetDependency */, 381 | ); 382 | name = "SortedArray-tvOS Tests"; 383 | productName = "SortedArray-tvOS Tests"; 384 | productReference = DD75028D1C690C7A006590AF /* SortedArray-tvOS Tests.xctest */; 385 | productType = "com.apple.product-type.bundle.unit-test"; 386 | }; 387 | /* End PBXNativeTarget section */ 388 | 389 | /* Begin PBXProject section */ 390 | 52D6D9731BEFF229002C0205 /* Project object */ = { 391 | isa = PBXProject; 392 | attributes = { 393 | LastSwiftUpdateCheck = 0720; 394 | LastUpgradeCheck = 1000; 395 | ORGANIZATIONNAME = SortedArray; 396 | TargetAttributes = { 397 | 52D6D97B1BEFF229002C0205 = { 398 | CreatedOnToolsVersion = 7.1; 399 | LastSwiftMigration = 0900; 400 | }; 401 | 52D6D9851BEFF229002C0205 = { 402 | CreatedOnToolsVersion = 7.1; 403 | LastSwiftMigration = 0900; 404 | }; 405 | 52D6D9E11BEFFF6E002C0205 = { 406 | CreatedOnToolsVersion = 7.1; 407 | LastSwiftMigration = 0900; 408 | }; 409 | 52D6D9EF1BEFFFBE002C0205 = { 410 | CreatedOnToolsVersion = 7.1; 411 | LastSwiftMigration = 0900; 412 | }; 413 | 52D6DA0E1BF000BD002C0205 = { 414 | CreatedOnToolsVersion = 7.1; 415 | LastSwiftMigration = 0900; 416 | }; 417 | DD7502791C68FCFC006590AF = { 418 | CreatedOnToolsVersion = 7.2.1; 419 | LastSwiftMigration = 0900; 420 | }; 421 | DD75028C1C690C7A006590AF = { 422 | CreatedOnToolsVersion = 7.2.1; 423 | LastSwiftMigration = 0900; 424 | }; 425 | }; 426 | }; 427 | buildConfigurationList = 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SortedArray" */; 428 | compatibilityVersion = "Xcode 6.3"; 429 | developmentRegion = English; 430 | hasScannedForEncodings = 0; 431 | knownRegions = ( 432 | en, 433 | ); 434 | mainGroup = 52D6D9721BEFF229002C0205; 435 | productRefGroup = 52D6D97D1BEFF229002C0205 /* Products */; 436 | projectDirPath = ""; 437 | projectRoot = ""; 438 | targets = ( 439 | 52D6D97B1BEFF229002C0205 /* SortedArray-iOS */, 440 | 52D6DA0E1BF000BD002C0205 /* SortedArray-macOS */, 441 | 52D6D9E11BEFFF6E002C0205 /* SortedArray-watchOS */, 442 | 52D6D9EF1BEFFFBE002C0205 /* SortedArray-tvOS */, 443 | 52D6D9851BEFF229002C0205 /* SortedArray-iOS Tests */, 444 | DD7502791C68FCFC006590AF /* SortedArray-macOS Tests */, 445 | DD75028C1C690C7A006590AF /* SortedArray-tvOS Tests */, 446 | ); 447 | }; 448 | /* End PBXProject section */ 449 | 450 | /* Begin PBXResourcesBuildPhase section */ 451 | 52D6D97A1BEFF229002C0205 /* Resources */ = { 452 | isa = PBXResourcesBuildPhase; 453 | buildActionMask = 2147483647; 454 | files = ( 455 | ); 456 | runOnlyForDeploymentPostprocessing = 0; 457 | }; 458 | 52D6D9841BEFF229002C0205 /* Resources */ = { 459 | isa = PBXResourcesBuildPhase; 460 | buildActionMask = 2147483647; 461 | files = ( 462 | ); 463 | runOnlyForDeploymentPostprocessing = 0; 464 | }; 465 | 52D6D9E01BEFFF6E002C0205 /* Resources */ = { 466 | isa = PBXResourcesBuildPhase; 467 | buildActionMask = 2147483647; 468 | files = ( 469 | ); 470 | runOnlyForDeploymentPostprocessing = 0; 471 | }; 472 | 52D6D9EE1BEFFFBE002C0205 /* Resources */ = { 473 | isa = PBXResourcesBuildPhase; 474 | buildActionMask = 2147483647; 475 | files = ( 476 | ); 477 | runOnlyForDeploymentPostprocessing = 0; 478 | }; 479 | 52D6DA0D1BF000BD002C0205 /* Resources */ = { 480 | isa = PBXResourcesBuildPhase; 481 | buildActionMask = 2147483647; 482 | files = ( 483 | ); 484 | runOnlyForDeploymentPostprocessing = 0; 485 | }; 486 | DD7502781C68FCFC006590AF /* Resources */ = { 487 | isa = PBXResourcesBuildPhase; 488 | buildActionMask = 2147483647; 489 | files = ( 490 | ); 491 | runOnlyForDeploymentPostprocessing = 0; 492 | }; 493 | DD75028B1C690C7A006590AF /* Resources */ = { 494 | isa = PBXResourcesBuildPhase; 495 | buildActionMask = 2147483647; 496 | files = ( 497 | ); 498 | runOnlyForDeploymentPostprocessing = 0; 499 | }; 500 | /* End PBXResourcesBuildPhase section */ 501 | 502 | /* Begin PBXSourcesBuildPhase section */ 503 | 52D6D9771BEFF229002C0205 /* Sources */ = { 504 | isa = PBXSourcesBuildPhase; 505 | buildActionMask = 2147483647; 506 | files = ( 507 | 52D6D9981BEFF375002C0205 /* SortedArray.swift in Sources */, 508 | ); 509 | runOnlyForDeploymentPostprocessing = 0; 510 | }; 511 | 52D6D9821BEFF229002C0205 /* Sources */ = { 512 | isa = PBXSourcesBuildPhase; 513 | buildActionMask = 2147483647; 514 | files = ( 515 | 5DB8B87C20D7FF6200D463D6 /* SortedArrayTests.swift in Sources */, 516 | 5DB8B87920D7FF5E00D463D6 /* PerformanceTests.swift in Sources */, 517 | 5DB8B87D20D7FF6200D463D6 /* TestHelpers.swift in Sources */, 518 | ); 519 | runOnlyForDeploymentPostprocessing = 0; 520 | }; 521 | 52D6D9DD1BEFFF6E002C0205 /* Sources */ = { 522 | isa = PBXSourcesBuildPhase; 523 | buildActionMask = 2147483647; 524 | files = ( 525 | 52D6D9EA1BEFFFA4002C0205 /* SortedArray.swift in Sources */, 526 | ); 527 | runOnlyForDeploymentPostprocessing = 0; 528 | }; 529 | 52D6D9EB1BEFFFBE002C0205 /* Sources */ = { 530 | isa = PBXSourcesBuildPhase; 531 | buildActionMask = 2147483647; 532 | files = ( 533 | 52D6DA091BF00081002C0205 /* SortedArray.swift in Sources */, 534 | ); 535 | runOnlyForDeploymentPostprocessing = 0; 536 | }; 537 | 52D6DA0A1BF000BD002C0205 /* Sources */ = { 538 | isa = PBXSourcesBuildPhase; 539 | buildActionMask = 2147483647; 540 | files = ( 541 | 52D6DA261BF00118002C0205 /* SortedArray.swift in Sources */, 542 | ); 543 | runOnlyForDeploymentPostprocessing = 0; 544 | }; 545 | DD7502761C68FCFC006590AF /* Sources */ = { 546 | isa = PBXSourcesBuildPhase; 547 | buildActionMask = 2147483647; 548 | files = ( 549 | 5DB8B87E20D7FF6300D463D6 /* SortedArrayTests.swift in Sources */, 550 | 5DB8B87A20D7FF5F00D463D6 /* PerformanceTests.swift in Sources */, 551 | 5DB8B87F20D7FF6300D463D6 /* TestHelpers.swift in Sources */, 552 | ); 553 | runOnlyForDeploymentPostprocessing = 0; 554 | }; 555 | DD7502891C690C7A006590AF /* Sources */ = { 556 | isa = PBXSourcesBuildPhase; 557 | buildActionMask = 2147483647; 558 | files = ( 559 | 5DB8B88020D7FF6300D463D6 /* SortedArrayTests.swift in Sources */, 560 | 5DB8B87B20D7FF5F00D463D6 /* PerformanceTests.swift in Sources */, 561 | 5DB8B88120D7FF6300D463D6 /* TestHelpers.swift in Sources */, 562 | ); 563 | runOnlyForDeploymentPostprocessing = 0; 564 | }; 565 | /* End PBXSourcesBuildPhase section */ 566 | 567 | /* Begin PBXTargetDependency section */ 568 | 52D6D9891BEFF229002C0205 /* PBXTargetDependency */ = { 569 | isa = PBXTargetDependency; 570 | target = 52D6D97B1BEFF229002C0205 /* SortedArray-iOS */; 571 | targetProxy = 52D6D9881BEFF229002C0205 /* PBXContainerItemProxy */; 572 | }; 573 | DD7502811C68FCFC006590AF /* PBXTargetDependency */ = { 574 | isa = PBXTargetDependency; 575 | target = 52D6DA0E1BF000BD002C0205 /* SortedArray-macOS */; 576 | targetProxy = DD7502801C68FCFC006590AF /* PBXContainerItemProxy */; 577 | }; 578 | DD7502941C690C7A006590AF /* PBXTargetDependency */ = { 579 | isa = PBXTargetDependency; 580 | target = 52D6D9EF1BEFFFBE002C0205 /* SortedArray-tvOS */; 581 | targetProxy = DD7502931C690C7A006590AF /* PBXContainerItemProxy */; 582 | }; 583 | /* End PBXTargetDependency section */ 584 | 585 | /* Begin XCBuildConfiguration section */ 586 | 52D6D98E1BEFF229002C0205 /* Debug */ = { 587 | isa = XCBuildConfiguration; 588 | buildSettings = { 589 | ALWAYS_SEARCH_USER_PATHS = NO; 590 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 591 | CLANG_CXX_LIBRARY = "libc++"; 592 | CLANG_ENABLE_MODULES = YES; 593 | CLANG_ENABLE_OBJC_ARC = YES; 594 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 595 | CLANG_WARN_BOOL_CONVERSION = YES; 596 | CLANG_WARN_COMMA = YES; 597 | CLANG_WARN_CONSTANT_CONVERSION = YES; 598 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 599 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 600 | CLANG_WARN_EMPTY_BODY = YES; 601 | CLANG_WARN_ENUM_CONVERSION = YES; 602 | CLANG_WARN_INFINITE_RECURSION = YES; 603 | CLANG_WARN_INT_CONVERSION = YES; 604 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 605 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 606 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 607 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 608 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 609 | CLANG_WARN_STRICT_PROTOTYPES = YES; 610 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 611 | CLANG_WARN_UNREACHABLE_CODE = YES; 612 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 613 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 614 | COPY_PHASE_STRIP = NO; 615 | CURRENT_PROJECT_VERSION = 1; 616 | DEBUG_INFORMATION_FORMAT = dwarf; 617 | ENABLE_STRICT_OBJC_MSGSEND = YES; 618 | ENABLE_TESTABILITY = YES; 619 | GCC_C_LANGUAGE_STANDARD = gnu99; 620 | GCC_DYNAMIC_NO_PIC = NO; 621 | GCC_NO_COMMON_BLOCKS = YES; 622 | GCC_OPTIMIZATION_LEVEL = 0; 623 | GCC_PREPROCESSOR_DEFINITIONS = ( 624 | "DEBUG=1", 625 | "$(inherited)", 626 | ); 627 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 628 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 629 | GCC_WARN_UNDECLARED_SELECTOR = YES; 630 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 631 | GCC_WARN_UNUSED_FUNCTION = YES; 632 | GCC_WARN_UNUSED_VARIABLE = YES; 633 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 634 | MTL_ENABLE_DEBUG_INFO = YES; 635 | ONLY_ACTIVE_ARCH = YES; 636 | SDKROOT = iphoneos; 637 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 638 | SWIFT_VERSION = 4.0; 639 | TARGETED_DEVICE_FAMILY = "1,2"; 640 | VERSIONING_SYSTEM = "apple-generic"; 641 | VERSION_INFO_PREFIX = ""; 642 | }; 643 | name = Debug; 644 | }; 645 | 52D6D98F1BEFF229002C0205 /* Release */ = { 646 | isa = XCBuildConfiguration; 647 | buildSettings = { 648 | ALWAYS_SEARCH_USER_PATHS = NO; 649 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 650 | CLANG_CXX_LIBRARY = "libc++"; 651 | CLANG_ENABLE_MODULES = YES; 652 | CLANG_ENABLE_OBJC_ARC = YES; 653 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 654 | CLANG_WARN_BOOL_CONVERSION = YES; 655 | CLANG_WARN_COMMA = YES; 656 | CLANG_WARN_CONSTANT_CONVERSION = YES; 657 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 658 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 659 | CLANG_WARN_EMPTY_BODY = YES; 660 | CLANG_WARN_ENUM_CONVERSION = YES; 661 | CLANG_WARN_INFINITE_RECURSION = YES; 662 | CLANG_WARN_INT_CONVERSION = YES; 663 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 664 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 665 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 666 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 667 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 668 | CLANG_WARN_STRICT_PROTOTYPES = YES; 669 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 670 | CLANG_WARN_UNREACHABLE_CODE = YES; 671 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 672 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 673 | COPY_PHASE_STRIP = NO; 674 | CURRENT_PROJECT_VERSION = 1; 675 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 676 | ENABLE_NS_ASSERTIONS = NO; 677 | ENABLE_STRICT_OBJC_MSGSEND = YES; 678 | GCC_C_LANGUAGE_STANDARD = gnu99; 679 | GCC_NO_COMMON_BLOCKS = YES; 680 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 681 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 682 | GCC_WARN_UNDECLARED_SELECTOR = YES; 683 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 684 | GCC_WARN_UNUSED_FUNCTION = YES; 685 | GCC_WARN_UNUSED_VARIABLE = YES; 686 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 687 | MTL_ENABLE_DEBUG_INFO = NO; 688 | SDKROOT = iphoneos; 689 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 690 | SWIFT_VERSION = 4.0; 691 | TARGETED_DEVICE_FAMILY = "1,2"; 692 | VALIDATE_PRODUCT = YES; 693 | VERSIONING_SYSTEM = "apple-generic"; 694 | VERSION_INFO_PREFIX = ""; 695 | }; 696 | name = Release; 697 | }; 698 | 52D6D9911BEFF229002C0205 /* Debug */ = { 699 | isa = XCBuildConfiguration; 700 | buildSettings = { 701 | APPLICATION_EXTENSION_API_ONLY = YES; 702 | CLANG_ENABLE_MODULES = YES; 703 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 704 | DEFINES_MODULE = YES; 705 | DYLIB_COMPATIBILITY_VERSION = 1; 706 | DYLIB_CURRENT_VERSION = 1; 707 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 708 | INFOPLIST_FILE = Configs/SortedArray.plist; 709 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 710 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 711 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 712 | ONLY_ACTIVE_ARCH = NO; 713 | PRODUCT_BUNDLE_IDENTIFIER = "com.olebegemann.SortedArray-iOS"; 714 | PRODUCT_NAME = SortedArray; 715 | SKIP_INSTALL = YES; 716 | }; 717 | name = Debug; 718 | }; 719 | 52D6D9921BEFF229002C0205 /* Release */ = { 720 | isa = XCBuildConfiguration; 721 | buildSettings = { 722 | APPLICATION_EXTENSION_API_ONLY = YES; 723 | CLANG_ENABLE_MODULES = YES; 724 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 725 | DEFINES_MODULE = YES; 726 | DYLIB_COMPATIBILITY_VERSION = 1; 727 | DYLIB_CURRENT_VERSION = 1; 728 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 729 | INFOPLIST_FILE = Configs/SortedArray.plist; 730 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 731 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 732 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 733 | PRODUCT_BUNDLE_IDENTIFIER = "com.olebegemann.SortedArray-iOS"; 734 | PRODUCT_NAME = SortedArray; 735 | SKIP_INSTALL = YES; 736 | }; 737 | name = Release; 738 | }; 739 | 52D6D9941BEFF229002C0205 /* Debug */ = { 740 | isa = XCBuildConfiguration; 741 | buildSettings = { 742 | CLANG_ENABLE_MODULES = YES; 743 | INFOPLIST_FILE = Configs/SortedArrayTests.plist; 744 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 745 | PRODUCT_BUNDLE_IDENTIFIER = "com.SortedArray.SortedArray-iOS-Tests"; 746 | PRODUCT_NAME = "$(TARGET_NAME)"; 747 | }; 748 | name = Debug; 749 | }; 750 | 52D6D9951BEFF229002C0205 /* Release */ = { 751 | isa = XCBuildConfiguration; 752 | buildSettings = { 753 | CLANG_ENABLE_MODULES = YES; 754 | INFOPLIST_FILE = Configs/SortedArrayTests.plist; 755 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 756 | PRODUCT_BUNDLE_IDENTIFIER = "com.SortedArray.SortedArray-iOS-Tests"; 757 | PRODUCT_NAME = "$(TARGET_NAME)"; 758 | }; 759 | name = Release; 760 | }; 761 | 52D6D9E81BEFFF6E002C0205 /* Debug */ = { 762 | isa = XCBuildConfiguration; 763 | buildSettings = { 764 | APPLICATION_EXTENSION_API_ONLY = YES; 765 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 766 | DEFINES_MODULE = YES; 767 | DYLIB_COMPATIBILITY_VERSION = 1; 768 | DYLIB_CURRENT_VERSION = 1; 769 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 770 | INFOPLIST_FILE = Configs/SortedArray.plist; 771 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 772 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 773 | PRODUCT_BUNDLE_IDENTIFIER = "com.olebegemann.SortedArray-watchOS"; 774 | PRODUCT_NAME = SortedArray; 775 | SDKROOT = watchos; 776 | SKIP_INSTALL = YES; 777 | TARGETED_DEVICE_FAMILY = 4; 778 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 779 | }; 780 | name = Debug; 781 | }; 782 | 52D6D9E91BEFFF6E002C0205 /* Release */ = { 783 | isa = XCBuildConfiguration; 784 | buildSettings = { 785 | APPLICATION_EXTENSION_API_ONLY = YES; 786 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 787 | DEFINES_MODULE = YES; 788 | DYLIB_COMPATIBILITY_VERSION = 1; 789 | DYLIB_CURRENT_VERSION = 1; 790 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 791 | INFOPLIST_FILE = Configs/SortedArray.plist; 792 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 793 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 794 | PRODUCT_BUNDLE_IDENTIFIER = "com.olebegemann.SortedArray-watchOS"; 795 | PRODUCT_NAME = SortedArray; 796 | SDKROOT = watchos; 797 | SKIP_INSTALL = YES; 798 | TARGETED_DEVICE_FAMILY = 4; 799 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 800 | }; 801 | name = Release; 802 | }; 803 | 52D6DA021BEFFFBE002C0205 /* Debug */ = { 804 | isa = XCBuildConfiguration; 805 | buildSettings = { 806 | APPLICATION_EXTENSION_API_ONLY = YES; 807 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 808 | DEFINES_MODULE = YES; 809 | DYLIB_COMPATIBILITY_VERSION = 1; 810 | DYLIB_CURRENT_VERSION = 1; 811 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 812 | INFOPLIST_FILE = Configs/SortedArray.plist; 813 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 814 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 815 | PRODUCT_BUNDLE_IDENTIFIER = "com.olebegemann.SortedArray-tvOS"; 816 | PRODUCT_NAME = SortedArray; 817 | SDKROOT = appletvos; 818 | SKIP_INSTALL = YES; 819 | TARGETED_DEVICE_FAMILY = 3; 820 | TVOS_DEPLOYMENT_TARGET = 9.0; 821 | }; 822 | name = Debug; 823 | }; 824 | 52D6DA031BEFFFBE002C0205 /* Release */ = { 825 | isa = XCBuildConfiguration; 826 | buildSettings = { 827 | APPLICATION_EXTENSION_API_ONLY = YES; 828 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 829 | DEFINES_MODULE = YES; 830 | DYLIB_COMPATIBILITY_VERSION = 1; 831 | DYLIB_CURRENT_VERSION = 1; 832 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 833 | INFOPLIST_FILE = Configs/SortedArray.plist; 834 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 835 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 836 | PRODUCT_BUNDLE_IDENTIFIER = "com.olebegemann.SortedArray-tvOS"; 837 | PRODUCT_NAME = SortedArray; 838 | SDKROOT = appletvos; 839 | SKIP_INSTALL = YES; 840 | TARGETED_DEVICE_FAMILY = 3; 841 | TVOS_DEPLOYMENT_TARGET = 9.0; 842 | }; 843 | name = Release; 844 | }; 845 | 52D6DA211BF000BD002C0205 /* Debug */ = { 846 | isa = XCBuildConfiguration; 847 | buildSettings = { 848 | APPLICATION_EXTENSION_API_ONLY = YES; 849 | CODE_SIGN_IDENTITY = "-"; 850 | COMBINE_HIDPI_IMAGES = YES; 851 | DEFINES_MODULE = YES; 852 | DYLIB_COMPATIBILITY_VERSION = 1; 853 | DYLIB_CURRENT_VERSION = 1; 854 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 855 | FRAMEWORK_VERSION = A; 856 | INFOPLIST_FILE = Configs/SortedArray.plist; 857 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 858 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 859 | MACOSX_DEPLOYMENT_TARGET = 10.10; 860 | PRODUCT_BUNDLE_IDENTIFIER = "com.olebegemann.SortedArray-macOS"; 861 | PRODUCT_NAME = SortedArray; 862 | SDKROOT = macosx; 863 | SKIP_INSTALL = YES; 864 | }; 865 | name = Debug; 866 | }; 867 | 52D6DA221BF000BD002C0205 /* Release */ = { 868 | isa = XCBuildConfiguration; 869 | buildSettings = { 870 | APPLICATION_EXTENSION_API_ONLY = YES; 871 | CODE_SIGN_IDENTITY = "-"; 872 | COMBINE_HIDPI_IMAGES = YES; 873 | DEFINES_MODULE = YES; 874 | DYLIB_COMPATIBILITY_VERSION = 1; 875 | DYLIB_CURRENT_VERSION = 1; 876 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 877 | FRAMEWORK_VERSION = A; 878 | INFOPLIST_FILE = Configs/SortedArray.plist; 879 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 880 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 881 | MACOSX_DEPLOYMENT_TARGET = 10.10; 882 | PRODUCT_BUNDLE_IDENTIFIER = "com.olebegemann.SortedArray-macOS"; 883 | PRODUCT_NAME = SortedArray; 884 | SDKROOT = macosx; 885 | SKIP_INSTALL = YES; 886 | }; 887 | name = Release; 888 | }; 889 | DD7502831C68FCFC006590AF /* Debug */ = { 890 | isa = XCBuildConfiguration; 891 | buildSettings = { 892 | CODE_SIGN_IDENTITY = "-"; 893 | COMBINE_HIDPI_IMAGES = YES; 894 | INFOPLIST_FILE = Configs/SortedArrayTests.plist; 895 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 896 | MACOSX_DEPLOYMENT_TARGET = 10.11; 897 | PRODUCT_BUNDLE_IDENTIFIER = "com.SortedArray.SortedArray-macOS-Tests"; 898 | PRODUCT_NAME = "$(TARGET_NAME)"; 899 | SDKROOT = macosx; 900 | }; 901 | name = Debug; 902 | }; 903 | DD7502841C68FCFC006590AF /* Release */ = { 904 | isa = XCBuildConfiguration; 905 | buildSettings = { 906 | CODE_SIGN_IDENTITY = "-"; 907 | COMBINE_HIDPI_IMAGES = YES; 908 | INFOPLIST_FILE = Configs/SortedArrayTests.plist; 909 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 910 | MACOSX_DEPLOYMENT_TARGET = 10.11; 911 | PRODUCT_BUNDLE_IDENTIFIER = "com.SortedArray.SortedArray-macOS-Tests"; 912 | PRODUCT_NAME = "$(TARGET_NAME)"; 913 | SDKROOT = macosx; 914 | }; 915 | name = Release; 916 | }; 917 | DD7502961C690C7A006590AF /* Debug */ = { 918 | isa = XCBuildConfiguration; 919 | buildSettings = { 920 | INFOPLIST_FILE = Configs/SortedArrayTests.plist; 921 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 922 | PRODUCT_BUNDLE_IDENTIFIER = "com.SortedArray.SortedArray-tvOS-Tests"; 923 | PRODUCT_NAME = "$(TARGET_NAME)"; 924 | SDKROOT = appletvos; 925 | TVOS_DEPLOYMENT_TARGET = 9.1; 926 | }; 927 | name = Debug; 928 | }; 929 | DD7502971C690C7A006590AF /* Release */ = { 930 | isa = XCBuildConfiguration; 931 | buildSettings = { 932 | INFOPLIST_FILE = Configs/SortedArrayTests.plist; 933 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 934 | PRODUCT_BUNDLE_IDENTIFIER = "com.SortedArray.SortedArray-tvOS-Tests"; 935 | PRODUCT_NAME = "$(TARGET_NAME)"; 936 | SDKROOT = appletvos; 937 | TVOS_DEPLOYMENT_TARGET = 9.1; 938 | }; 939 | name = Release; 940 | }; 941 | /* End XCBuildConfiguration section */ 942 | 943 | /* Begin XCConfigurationList section */ 944 | 52D6D9761BEFF229002C0205 /* Build configuration list for PBXProject "SortedArray" */ = { 945 | isa = XCConfigurationList; 946 | buildConfigurations = ( 947 | 52D6D98E1BEFF229002C0205 /* Debug */, 948 | 52D6D98F1BEFF229002C0205 /* Release */, 949 | ); 950 | defaultConfigurationIsVisible = 0; 951 | defaultConfigurationName = Release; 952 | }; 953 | 52D6D9901BEFF229002C0205 /* Build configuration list for PBXNativeTarget "SortedArray-iOS" */ = { 954 | isa = XCConfigurationList; 955 | buildConfigurations = ( 956 | 52D6D9911BEFF229002C0205 /* Debug */, 957 | 52D6D9921BEFF229002C0205 /* Release */, 958 | ); 959 | defaultConfigurationIsVisible = 0; 960 | defaultConfigurationName = Release; 961 | }; 962 | 52D6D9931BEFF229002C0205 /* Build configuration list for PBXNativeTarget "SortedArray-iOS Tests" */ = { 963 | isa = XCConfigurationList; 964 | buildConfigurations = ( 965 | 52D6D9941BEFF229002C0205 /* Debug */, 966 | 52D6D9951BEFF229002C0205 /* Release */, 967 | ); 968 | defaultConfigurationIsVisible = 0; 969 | defaultConfigurationName = Release; 970 | }; 971 | 52D6D9E71BEFFF6E002C0205 /* Build configuration list for PBXNativeTarget "SortedArray-watchOS" */ = { 972 | isa = XCConfigurationList; 973 | buildConfigurations = ( 974 | 52D6D9E81BEFFF6E002C0205 /* Debug */, 975 | 52D6D9E91BEFFF6E002C0205 /* Release */, 976 | ); 977 | defaultConfigurationIsVisible = 0; 978 | defaultConfigurationName = Release; 979 | }; 980 | 52D6DA011BEFFFBE002C0205 /* Build configuration list for PBXNativeTarget "SortedArray-tvOS" */ = { 981 | isa = XCConfigurationList; 982 | buildConfigurations = ( 983 | 52D6DA021BEFFFBE002C0205 /* Debug */, 984 | 52D6DA031BEFFFBE002C0205 /* Release */, 985 | ); 986 | defaultConfigurationIsVisible = 0; 987 | defaultConfigurationName = Release; 988 | }; 989 | 52D6DA201BF000BD002C0205 /* Build configuration list for PBXNativeTarget "SortedArray-macOS" */ = { 990 | isa = XCConfigurationList; 991 | buildConfigurations = ( 992 | 52D6DA211BF000BD002C0205 /* Debug */, 993 | 52D6DA221BF000BD002C0205 /* Release */, 994 | ); 995 | defaultConfigurationIsVisible = 0; 996 | defaultConfigurationName = Release; 997 | }; 998 | DD7502821C68FCFC006590AF /* Build configuration list for PBXNativeTarget "SortedArray-macOS Tests" */ = { 999 | isa = XCConfigurationList; 1000 | buildConfigurations = ( 1001 | DD7502831C68FCFC006590AF /* Debug */, 1002 | DD7502841C68FCFC006590AF /* Release */, 1003 | ); 1004 | defaultConfigurationIsVisible = 0; 1005 | defaultConfigurationName = Release; 1006 | }; 1007 | DD7502951C690C7A006590AF /* Build configuration list for PBXNativeTarget "SortedArray-tvOS Tests" */ = { 1008 | isa = XCConfigurationList; 1009 | buildConfigurations = ( 1010 | DD7502961C690C7A006590AF /* Debug */, 1011 | DD7502971C690C7A006590AF /* Release */, 1012 | ); 1013 | defaultConfigurationIsVisible = 0; 1014 | defaultConfigurationName = Release; 1015 | }; 1016 | /* End XCConfigurationList section */ 1017 | }; 1018 | rootObject = 52D6D9731BEFF229002C0205 /* Project object */; 1019 | } 1020 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/xcshareddata/xcbaselines/DD7502791C68FCFC006590AF.xcbaseline/28BBA58F-EC4C-4E7A-943A-5B8FD5CC1F33.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | PerformanceTests 8 | 9 | testPerformanceOfIndexOf() 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 7.1619e-05 15 | baselineIntegrationDisplayName 16 | 18. Jun 2018 at 17:12:56 17 | 18 | 19 | testPerformanceOfIndexOfObject() 20 | 21 | com.apple.XCTPerformanceMetric_WallClockTime 22 | 23 | baselineAverage 24 | 9.9912e-05 25 | baselineIntegrationDisplayName 26 | 18. Jun 2018 at 17:12:56 27 | 28 | 29 | testPerformanceOfIndexOfObjectInRange() 30 | 31 | com.apple.XCTPerformanceMetric_WallClockTime 32 | 33 | baselineAverage 34 | 2.2075e-05 35 | baselineIntegrationDisplayName 36 | 18. Jun 2018 at 17:12:56 37 | 38 | 39 | testPerformanceOfLastIndexOf() 40 | 41 | com.apple.XCTPerformanceMetric_WallClockTime 42 | 43 | baselineAverage 44 | 7.4486e-05 45 | baselineIntegrationDisplayName 46 | 18. Jun 2018 at 17:12:56 47 | 48 | 49 | testPerformanceOfLastIndexOfObject() 50 | 51 | com.apple.XCTPerformanceMetric_WallClockTime 52 | 53 | baselineAverage 54 | 5.5835e-05 55 | baselineIntegrationDisplayName 56 | 18. Jun 2018 at 17:12:56 57 | 58 | 59 | testPerformanceOfLastIndexOfObjectInRange() 60 | 61 | com.apple.XCTPerformanceMetric_WallClockTime 62 | 63 | baselineAverage 64 | 1.1141e-05 65 | baselineIntegrationDisplayName 66 | 18. Jun 2018 at 17:12:56 67 | 68 | 69 | 70 | SortedArrayTests 71 | 72 | testIndexOfPerformance() 73 | 74 | com.apple.XCTPerformanceMetric_WallClockTime 75 | 76 | baselineAverage 77 | 0.32278 78 | baselineIntegrationDisplayName 79 | Local Baseline 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/xcshareddata/xcbaselines/DD7502791C68FCFC006590AF.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | 28BBA58F-EC4C-4E7A-943A-5B8FD5CC1F33 8 | 9 | localComputer 10 | 11 | busSpeedInMHz 12 | 100 13 | cpuCount 14 | 1 15 | cpuKind 16 | Intel Core i7 17 | cpuSpeedInMHz 18 | 2600 19 | logicalCPUCoresPerPackage 20 | 8 21 | modelCode 22 | MacBookPro11,2 23 | physicalCPUCoresPerPackage 24 | 4 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | x86_64 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/xcshareddata/xcschemes/Performance-Tests-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 35 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 56 | 57 | 58 | 59 | 60 | 61 | 71 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 89 | 90 | 96 | 97 | 98 | 99 | 101 | 102 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/xcshareddata/xcschemes/SortedArray-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 36 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 57 | 58 | 59 | 60 | 61 | 62 | 72 | 73 | 79 | 80 | 81 | 82 | 83 | 84 | 90 | 91 | 97 | 98 | 99 | 100 | 102 | 103 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/xcshareddata/xcschemes/SortedArray-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 36 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 57 | 58 | 59 | 60 | 61 | 62 | 72 | 73 | 79 | 80 | 81 | 82 | 83 | 84 | 90 | 91 | 97 | 98 | 99 | 100 | 102 | 103 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/xcshareddata/xcschemes/SortedArray-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 36 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 57 | 58 | 59 | 60 | 61 | 62 | 72 | 73 | 79 | 80 | 81 | 82 | 83 | 84 | 90 | 91 | 97 | 98 | 99 | 100 | 102 | 103 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /SortedArray.xcodeproj/xcshareddata/xcschemes/SortedArray-watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Sources/SortedArray.swift: -------------------------------------------------------------------------------- 1 | import Foundation // Needed for ComparisonResult (used privately) 2 | 3 | /// An array that keeps its elements sorted at all times. 4 | public struct SortedArray { 5 | /// The backing store 6 | fileprivate var _elements: [Element] 7 | 8 | public typealias Comparator = (A, A) -> Bool 9 | 10 | /// The predicate that determines the array's sort order. 11 | fileprivate let areInIncreasingOrder: Comparator 12 | 13 | /// Initializes an empty array. 14 | /// 15 | /// - Parameter areInIncreasingOrder: The comparison predicate the array should use to sort its elements. 16 | public init(areInIncreasingOrder: @escaping Comparator) { 17 | self._elements = [] 18 | self.areInIncreasingOrder = areInIncreasingOrder 19 | } 20 | 21 | /// Initializes the array with a sequence of unsorted elements and a comparison predicate. 22 | public init(unsorted: S, areInIncreasingOrder: @escaping Comparator) where S.Element == Element { 23 | let sorted = unsorted.sorted(by: areInIncreasingOrder) 24 | self._elements = sorted 25 | self.areInIncreasingOrder = areInIncreasingOrder 26 | } 27 | 28 | /// Initializes the array with a sequence that is already sorted according to the given comparison predicate. 29 | /// 30 | /// This is faster than `init(unsorted:areInIncreasingOrder:)` because the elements don't have to sorted again. 31 | /// 32 | /// - Precondition: `sorted` is sorted according to the given comparison predicate. If you violate this condition, the behavior is undefined. 33 | public init(sorted: S, areInIncreasingOrder: @escaping Comparator) where S.Element == Element { 34 | self._elements = Array(sorted) 35 | self.areInIncreasingOrder = areInIncreasingOrder 36 | } 37 | 38 | /// Inserts a new element into the array, preserving the sort order. 39 | /// 40 | /// - Returns: the index where the new element was inserted. 41 | /// - Complexity: O(_n_) where _n_ is the size of the array. O(_log n_) if the new 42 | /// element can be appended, i.e. if it is ordered last in the resulting array. 43 | @discardableResult 44 | public mutating func insert(_ newElement: Element) -> Index { 45 | let index = insertionIndex(for: newElement) 46 | // This should be O(1) if the element is to be inserted at the end, 47 | // O(_n) in the worst case (inserted at the front). 48 | _elements.insert(newElement, at: index) 49 | return index 50 | } 51 | 52 | /// Inserts all elements from `elements` into `self`, preserving the sort order. 53 | /// 54 | /// This can be faster than inserting the individual elements one after another because 55 | /// we only need to re-sort once. 56 | /// 57 | /// - Complexity: O(_n * log(n)_) where _n_ is the size of the resulting array. 58 | public mutating func insert(contentsOf newElements: S) where S.Element == Element { 59 | _elements.append(contentsOf: newElements) 60 | _elements.sort(by: areInIncreasingOrder) 61 | } 62 | } 63 | 64 | extension SortedArray where Element: Comparable { 65 | /// Initializes an empty sorted array. Uses `<` as the comparison predicate. 66 | public init() { 67 | self.init(areInIncreasingOrder: <) 68 | } 69 | 70 | /// Initializes the array with a sequence of unsorted elements. Uses `<` as the comparison predicate. 71 | public init(unsorted: S) where S.Element == Element { 72 | self.init(unsorted: unsorted, areInIncreasingOrder: <) 73 | } 74 | 75 | /// Initializes the array with a sequence that is already sorted according to the `<` comparison predicate. Uses `<` as the comparison predicate. 76 | /// 77 | /// This is faster than `init(unsorted:)` because the elements don't have to sorted again. 78 | /// 79 | /// - Precondition: `sorted` is sorted according to the `<` predicate. If you violate this condition, the behavior is undefined. 80 | public init(sorted: S) where S.Element == Element { 81 | self.init(sorted: sorted, areInIncreasingOrder: <) 82 | } 83 | } 84 | 85 | extension SortedArray: RandomAccessCollection { 86 | public typealias Index = Int 87 | 88 | public var startIndex: Index { return _elements.startIndex } 89 | public var endIndex: Index { return _elements.endIndex } 90 | 91 | public func index(after i: Index) -> Index { 92 | return _elements.index(after: i) 93 | } 94 | 95 | public func index(before i: Index) -> Index { 96 | return _elements.index(before: i) 97 | } 98 | 99 | public subscript(position: Index) -> Element { 100 | return _elements[position] 101 | } 102 | } 103 | 104 | extension SortedArray { 105 | /// Like `Sequence.filter(_:)`, but returns a `SortedArray` instead of an `Array`. 106 | /// We can do this efficiently because filtering doesn't change the sort order. 107 | public func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> SortedArray { 108 | let newElements = try _elements.filter(isIncluded) 109 | return SortedArray(sorted: newElements, areInIncreasingOrder: areInIncreasingOrder) 110 | } 111 | } 112 | 113 | extension SortedArray: CustomStringConvertible, CustomDebugStringConvertible { 114 | public var description: String { 115 | return "\(String(describing: _elements)) (sorted)" 116 | } 117 | 118 | public var debugDescription: String { 119 | return " \(String(reflecting: _elements))" 120 | } 121 | } 122 | 123 | // MARK: - Removing elements. This is mostly a reimplementation of part `RangeReplaceableCollection`'s interface. `SortedArray` can't conform to `RangeReplaceableCollection` because some of that protocol's semantics (e.g. `append(_:)` don't fit `SortedArray`'s semantics. 124 | extension SortedArray { 125 | /// Removes and returns the element at the specified position. 126 | /// 127 | /// - Parameter index: The position of the element to remove. `index` must be a valid index of the array. 128 | /// - Returns: The element at the specified index. 129 | /// - Complexity: O(_n_), where _n_ is the length of the array. 130 | @discardableResult 131 | public mutating func remove(at index: Int) -> Element { 132 | return _elements.remove(at: index) 133 | } 134 | 135 | /// Removes the elements in the specified subrange from the array. 136 | /// 137 | /// - Parameter bounds: The range of the array to be removed. The 138 | /// bounds of the range must be valid indices of the array. 139 | /// 140 | /// - Complexity: O(_n_), where _n_ is the length of the array. 141 | public mutating func removeSubrange(_ bounds: Range) { 142 | _elements.removeSubrange(bounds) 143 | } 144 | 145 | /// Removes the elements in the specified subrange from the array. 146 | /// 147 | /// - Parameter bounds: The range of the array to be removed. The 148 | /// bounds of the range must be valid indices of the array. 149 | /// 150 | /// - Complexity: O(_n_), where _n_ is the length of the array. 151 | public mutating func removeSubrange(_ bounds: ClosedRange) { 152 | _elements.removeSubrange(bounds) 153 | } 154 | 155 | // Starting with Swift 4.2, CountableRange and CountableClosedRange are typealiases for 156 | // Range and ClosedRange, so these methods trigger "Invalid redeclaration" errors. 157 | // Compile them only for older compiler versions. 158 | // swift(3.1): Latest version of Swift 3 under the Swift 3 compiler. 159 | // swift(3.2): Swift 4 compiler under Swift 3 mode. 160 | // swift(3.3): Swift 4.1 compiler under Swift 3 mode. 161 | // swift(3.4): Swift 4.2 compiler under Swift 3 mode. 162 | // swift(4.0): Swift 4 compiler 163 | // swift(4.1): Swift 4.1 compiler 164 | // swift(4.1.50): Swift 4.2 compiler in Swift 4 mode 165 | // swift(4.2): Swift 4.2 compiler 166 | #if !swift(>=4.1.50) 167 | /// Removes the elements in the specified subrange from the array. 168 | /// 169 | /// - Parameter bounds: The range of the array to be removed. The 170 | /// bounds of the range must be valid indices of the array. 171 | /// 172 | /// - Complexity: O(_n_), where _n_ is the length of the array. 173 | public mutating func removeSubrange(_ bounds: CountableRange) { 174 | _elements.removeSubrange(bounds) 175 | } 176 | 177 | /// Removes the elements in the specified subrange from the array. 178 | /// 179 | /// - Parameter bounds: The range of the array to be removed. The 180 | /// bounds of the range must be valid indices of the array. 181 | /// 182 | /// - Complexity: O(_n_), where _n_ is the length of the array. 183 | public mutating func removeSubrange(_ bounds: CountableClosedRange) { 184 | _elements.removeSubrange(bounds) 185 | } 186 | #endif 187 | 188 | /// Removes the specified number of elements from the beginning of the 189 | /// array. 190 | /// 191 | /// - Parameter n: The number of elements to remove from the array. 192 | /// `n` must be greater than or equal to zero and must not exceed the 193 | /// number of elements in the array. 194 | /// 195 | /// - Complexity: O(_n_), where _n_ is the length of the array. 196 | public mutating func removeFirst(_ n: Int) { 197 | _elements.removeFirst(n) 198 | } 199 | 200 | /// Removes and returns the first element of the array. 201 | /// 202 | /// - Precondition: The array must not be empty. 203 | /// - Returns: The removed element. 204 | /// - Complexity: O(_n_), where _n_ is the length of the collection. 205 | @discardableResult 206 | public mutating func removeFirst() -> Element { 207 | return _elements.removeFirst() 208 | } 209 | 210 | /// Removes and returns the last element of the array. 211 | /// 212 | /// - Precondition: The collection must not be empty. 213 | /// - Returns: The last element of the collection. 214 | /// - Complexity: O(1) 215 | @discardableResult 216 | public mutating func removeLast() -> Element { 217 | return _elements.removeLast() 218 | } 219 | 220 | /// Removes the given number of elements from the end of the array. 221 | /// 222 | /// - Parameter n: The number of elements to remove. `n` must be greater 223 | /// than or equal to zero, and must be less than or equal to the number of 224 | /// elements in the array. 225 | /// - Complexity: O(1). 226 | public mutating func removeLast(_ n: Int) { 227 | _elements.removeLast(n) 228 | } 229 | 230 | /// Removes all elements from the array. 231 | /// 232 | /// - Parameter keepCapacity: Pass `true` to keep the existing capacity of 233 | /// the array after removing its elements. The default value is `false`. 234 | /// 235 | /// - Complexity: O(_n_), where _n_ is the length of the array. 236 | public mutating func removeAll(keepingCapacity keepCapacity: Bool = true) { 237 | _elements.removeAll(keepingCapacity: keepCapacity) 238 | } 239 | 240 | /// Removes an element from the array. If the array contains multiple 241 | /// instances of `element`, this method only removes the first one. 242 | /// 243 | /// - Complexity: O(_n_), where _n_ is the size of the array. 244 | public mutating func remove(_ element: Element) { 245 | guard let index = index(of: element) else { return } 246 | _elements.remove(at: index) 247 | } 248 | } 249 | 250 | // MARK: - More efficient variants of default implementations or implementations that need fewer constraints than the default implementations. 251 | extension SortedArray { 252 | /// Returns the first index where the specified value appears in the collection. 253 | /// 254 | /// - Complexity: O(_log(n)_), where _n_ is the size of the array. 255 | public func firstIndex(of element: Element) -> Index? { 256 | var range: Range = startIndex ..< endIndex 257 | var match: Index? = nil 258 | while case let .found(m) = search(for: element, in: range) { 259 | // We found a matching element 260 | // Check if its predecessor also matches 261 | if let predecessor = index(m, offsetBy: -1, limitedBy: range.lowerBound), 262 | compare(self[predecessor], element) == .orderedSame 263 | { 264 | // Predecessor matches => continue searching using binary search 265 | match = predecessor 266 | range = range.lowerBound ..< predecessor 267 | } 268 | else { 269 | // We're done 270 | match = m 271 | break 272 | } 273 | } 274 | return match 275 | } 276 | 277 | /// Returns the first index in which an element of the collection satisfies the given predicate. 278 | /// 279 | /// - Requires: The `predicate` must return `false` for elements of the array up to a given point, and `true` for 280 | /// all elements after that point _(the opposite of `lastIndex(where:)`)_. 281 | /// The given point may be before the first element or after the last element; i.e. it is valid to return `true` 282 | /// for all elements or `false` for all elements. 283 | /// For most use-cases, the `predicate` closure will use the form `{ $0 > … }` or `{ $0 >= … }` _(or equivalent, 284 | /// if the SortedArray was initialized with a custom Comparator)_. 285 | /// 286 | /// - Complexity: O(_log(n)_), where _n_ is the size of the array. 287 | public func firstIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? { 288 | var match: Index? = nil 289 | if case let .found(m) = try searchFirst(where: predicate) { 290 | match = m 291 | } 292 | return match 293 | } 294 | 295 | /// Returns the first element of the sequence that satisfies the given predicate. 296 | /// 297 | /// - Requires: The `predicate` must return `false` for elements of the array up to a given point, and `true` for 298 | /// all elements after that point _(the opposite of `last(where:)`)_. 299 | /// The given point may be before the first element or after the last element; i.e. it is valid to return `true` 300 | /// for all elements or `false` for all elements. 301 | /// For most use-cases, the `predicate` closure will use the form `{ $0 > … }` or `{ $0 >= … }` _(or equivalent, 302 | /// if the SortedArray was initialized with a custom Comparator)_. 303 | /// 304 | /// - Complexity: O(_log(n)_), where _n_ is the size of the array. 305 | public func first(where predicate: (Element) throws -> Bool) rethrows -> Element? { 306 | guard let index = try firstIndex(where: predicate) else { return nil } 307 | return self[index] 308 | } 309 | 310 | /// Returns the first index where the specified value appears in the collection. 311 | /// Old name for `firstIndex(of:)`. 312 | /// - Seealso: `firstIndex(of:)` 313 | public func index(of element: Element) -> Index? { 314 | return firstIndex(of: element) 315 | } 316 | 317 | /// Returns a Boolean value indicating whether the sequence contains the given element. 318 | /// 319 | /// - Complexity: O(_log(n)_), where _n_ is the size of the array. 320 | public func contains(_ element: Element) -> Bool { 321 | return anyIndex(of: element) != nil 322 | } 323 | 324 | /// Returns the minimum element in the sequence. 325 | /// 326 | /// - Complexity: O(1). 327 | @warn_unqualified_access 328 | public func min() -> Element? { 329 | return first 330 | } 331 | 332 | /// Returns the maximum element in the sequence. 333 | /// 334 | /// - Complexity: O(1). 335 | @warn_unqualified_access 336 | public func max() -> Element? { 337 | return last 338 | } 339 | } 340 | 341 | // MARK: - APIs that go beyond what's in the stdlib 342 | extension SortedArray { 343 | /// Returns an arbitrary index where the specified value appears in the collection. 344 | /// Like `index(of:)`, but without the guarantee to return the *first* index 345 | /// if the array contains duplicates of the searched element. 346 | /// 347 | /// Can be slightly faster than `index(of:)`. 348 | public func anyIndex(of element: Element) -> Index? { 349 | switch search(for: element) { 350 | case let .found(at: index): return index 351 | case .notFound(insertAt: _): return nil 352 | } 353 | } 354 | 355 | /// Returns the last index where the specified value appears in the collection. 356 | /// 357 | /// - Complexity: O(_log(n)_), where _n_ is the size of the array. 358 | public func lastIndex(of element: Element) -> Index? { 359 | var range: Range = startIndex ..< endIndex 360 | var match: Index? = nil 361 | while case let .found(m) = search(for: element, in: range) { 362 | // We found a matching element 363 | // Check if its successor also matches 364 | let lastValidIndex = index(before: range.upperBound) 365 | if let successor = index(m, offsetBy: 1, limitedBy: lastValidIndex), 366 | compare(self[successor], element) == .orderedSame 367 | { 368 | // Successor matches => continue searching using binary search 369 | match = successor 370 | guard let afterSuccessor = index(successor, offsetBy: 1, limitedBy: lastValidIndex) else { 371 | break 372 | } 373 | range = afterSuccessor ..< range.upperBound 374 | } 375 | else { 376 | // We're done 377 | match = m 378 | break 379 | } 380 | } 381 | return match 382 | } 383 | 384 | /// Returns the index of the last element in the collection that matches the given predicate. 385 | /// 386 | /// - Requires: The `predicate` must return `true` for elements of the array up to a given point, and `false` for 387 | /// all elements after that point _(the opposite of `firstIndex(where:)`)_. 388 | /// The given point may be before the first element or after the last element; i.e. it is valid to return `true` 389 | /// for all elements or `false` for all elements. 390 | /// For most use-cases, the `predicate` closure will use the form `{ $0 < … }` or `{ $0 <= … }` _(or equivalent, 391 | /// if the SortedArray was initialized with a custom Comparator)_. 392 | /// 393 | /// - Complexity: O(_log(n)_), where _n_ is the size of the array. 394 | public func lastIndex(where predicate: (Element) throws -> Bool) rethrows -> Index? { 395 | var match: Index? = nil 396 | if case let .found(m) = try searchLast(where: predicate) { 397 | match = m 398 | } 399 | return match 400 | } 401 | 402 | /// Returns the last element of the sequence that satisfies the given predicate. 403 | /// 404 | /// - Requires: The `predicate` must return `true` for elements of the array up to a given point, and `false` for 405 | /// all elements after that point _(the opposite of `first(where:)`)_. 406 | /// The given point may be before the first element or after the last element; i.e. it is valid to return `true` 407 | /// for all elements or `false` for all elements. 408 | /// For most use-cases, the `predicate` closure will use the form `{ $0 < … }` or `{ $0 <= … }` _(or equivalent, 409 | /// if the SortedArray was initialized with a custom Comparator)_. 410 | /// 411 | /// - Complexity: O(_log(n)_), where _n_ is the size of the array. 412 | public func last(where predicate: (Element) throws -> Bool) rethrows -> Element? { 413 | guard let index = try lastIndex(where: predicate) else { return nil } 414 | return self[index] 415 | } 416 | } 417 | 418 | // MARK: - Converting between a stdlib comparator function and Foundation.ComparisonResult 419 | extension SortedArray { 420 | fileprivate func compare(_ lhs: Element, _ rhs: Element) -> Foundation.ComparisonResult { 421 | if areInIncreasingOrder(lhs, rhs) { 422 | return .orderedAscending 423 | } else if areInIncreasingOrder(rhs, lhs) { 424 | return .orderedDescending 425 | } else { 426 | // If neither element comes before the other, they _must_ be 427 | // equal, per the strict ordering requirement of `areInIncreasingOrder`. 428 | return .orderedSame 429 | } 430 | } 431 | } 432 | 433 | // MARK: - Binary search 434 | extension SortedArray { 435 | /// The index where `newElement` should be inserted to preserve the array's sort order. 436 | fileprivate func insertionIndex(for newElement: Element) -> Index { 437 | switch search(for: newElement) { 438 | case let .found(at: index): return index 439 | case let .notFound(insertAt: index): return index 440 | } 441 | } 442 | } 443 | 444 | fileprivate enum Match { 445 | case found(at: Index) 446 | case notFound(insertAt: Index) 447 | } 448 | 449 | extension Range where Bound == Int { 450 | var middle: Int? { 451 | guard !isEmpty else { return nil } 452 | return lowerBound + count / 2 453 | } 454 | } 455 | 456 | extension SortedArray { 457 | /// Searches the array for `element` using binary search. 458 | /// 459 | /// - Returns: If `element` is in the array, returns `.found(at: index)` 460 | /// where `index` is the index of the element in the array. 461 | /// If `element` is not in the array, returns `.notFound(insertAt: index)` 462 | /// where `index` is the index where the element should be inserted to 463 | /// preserve the sort order. 464 | /// If the array contains multiple elements that are equal to `element`, 465 | /// there is no guarantee which of these is found. 466 | /// 467 | /// - Complexity: O(_log(n)_), where _n_ is the size of the array. 468 | fileprivate func search(for element: Element) -> Match { 469 | return search(for: element, in: startIndex ..< endIndex) 470 | } 471 | 472 | fileprivate func search(for element: Element, in range: Range) -> Match { 473 | guard let middle = range.middle else { return .notFound(insertAt: range.upperBound) } 474 | switch compare(element, self[middle]) { 475 | case .orderedDescending: 476 | return search(for: element, in: index(after: middle).. … }` or `{ $0 >= … }` _(or equivalent, 491 | /// if the SortedArray was initialized with a custom Comparator)_. 492 | /// 493 | /// - Parameter predicate: A closure that returns `false` for elements up to a point; and `true` for all after. 494 | /// - Returns: If `element` is in the array, returns `.found(at: index)` 495 | /// where `index` is the index of the element in the array. 496 | /// If `element` is not in the array, returns `.notFound(insertAt: index)` 497 | /// where `index` is the index where the element should be inserted to 498 | /// preserve the sort order. 499 | /// If the array contains multiple elements that are equal to `element`, 500 | /// there is no guarantee which of these is found. 501 | /// 502 | /// - Complexity: O(_log(n)_), where _n_ is the size of the array. 503 | /// - SeeAlso: http://ruby-doc.org/core-2.6.3/Array.html#method-i-bsearch_index 504 | fileprivate func searchFirst(where predicate: (Element) throws -> Bool) rethrows -> Match { 505 | return try searchFirst(where: predicate, in: startIndex ..< endIndex) 506 | } 507 | 508 | fileprivate func searchFirst(where predicate: (Element) throws -> Bool, in range: Range) rethrows -> Match { 509 | guard let middle = range.middle else { return .notFound(insertAt: range.upperBound) } 510 | if try predicate(self[middle]) { 511 | if middle == 0 { 512 | return .found(at: middle) 513 | } else if !(try predicate(self[index(before: middle)])) { 514 | return .found(at: middle) 515 | } else { 516 | return try searchFirst(where: predicate, in: range.lowerBound ..< middle) 517 | } 518 | } else { 519 | return try searchFirst(where: predicate, in: index(after: middle) ..< range.upperBound) 520 | } 521 | } 522 | 523 | /// Searches the array for the last element matching the `predicate` using binary search. 524 | /// 525 | /// - Requires: The `predicate` must return `true` for elements of the array up to a given point, and `false` for 526 | /// all elements after that point _(the opposite of `searchFirst(where:)`)_. 527 | /// The given point may be before the first element or after the last element; i.e. it is valid to return `true` 528 | /// for all elements or `false` for all elements. 529 | /// For most use-cases, the `predicate` closure will use the form `{ $0 < … }` or `{ $0 <= … }` _(or equivalent, 530 | /// if the SortedArray was initialized with a custom Comparator)_. 531 | /// 532 | /// - Parameter predicate: A closure that returns `false` for elements up to a point; and `true` for all after. 533 | /// - Returns: If `element` is in the array, returns `.found(at: index)` 534 | /// where `index` is the index of the element in the array. 535 | /// If `element` is not in the array, returns `.notFound(insertAt: index)` 536 | /// where `index` is the index where the element should be inserted to 537 | /// preserve the sort order. 538 | /// If the array contains multiple elements that are equal to `element`, 539 | /// there is no guarantee which of these is found. 540 | /// 541 | /// - Complexity: O(_log(n)_), where _n_ is the size of the array. 542 | /// - SeeAlso: http://ruby-doc.org/core-2.6.3/Array.html#method-i-bsearch_index 543 | fileprivate func searchLast(where predicate: (Element) throws -> Bool) rethrows -> Match { 544 | return try searchLast(where: predicate, in: startIndex ..< endIndex) 545 | } 546 | 547 | fileprivate func searchLast(where predicate: (Element) throws -> Bool, in range: Range) rethrows -> Match { 548 | guard let middle = range.middle else { return .notFound(insertAt: range.upperBound) } 549 | if try predicate(self[middle]) { 550 | if middle == range.upperBound - 1 { 551 | return .found(at: middle) 552 | } else if !(try predicate(self[index(after: middle)])) { 553 | return .found(at: middle) 554 | } else { 555 | return try searchLast(where: predicate, in: index(after: middle) ..< range.upperBound) 556 | } 557 | } else { 558 | return try searchLast(where: predicate, in: range.lowerBound ..< middle) 559 | } 560 | } 561 | } 562 | 563 | #if swift(>=4.1) 564 | extension SortedArray: Equatable where Element: Equatable { 565 | public static func == (lhs: SortedArray, rhs: SortedArray) -> Bool { 566 | // Ignore the comparator function for Equatable 567 | return lhs._elements == rhs._elements 568 | } 569 | } 570 | #else 571 | public func == (lhs: SortedArray, rhs: SortedArray) -> Bool { 572 | return lhs._elements == rhs._elements 573 | } 574 | 575 | public func != (lhs: SortedArray, rhs: SortedArray) -> Bool { 576 | return lhs._elements != rhs._elements 577 | } 578 | #endif 579 | 580 | #if swift(>=4.1.50) 581 | extension SortedArray: Hashable where Element: Hashable { 582 | public func hash(into hasher: inout Hasher) { 583 | hasher.combine(_elements) 584 | } 585 | } 586 | #endif 587 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import UnitTests 3 | @testable import PerformanceTests 4 | 5 | XCTMain([ 6 | testCase(SortedArrayTests.allTests), 7 | testCase(PerformanceTests.allTests), 8 | ]) 9 | -------------------------------------------------------------------------------- /Tests/PerformanceTests/PerformanceTests.swift: -------------------------------------------------------------------------------- 1 | import SortedArray 2 | import XCTest 3 | 4 | class PerformanceTests: XCTestCase { 5 | func testLinuxTestSuiteIncludesAllTests() { 6 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 7 | let darwinTestCount = PerformanceTests.defaultTestSuite.testCaseCount 8 | let linuxTestCount = PerformanceTests.allTests.count 9 | XCTAssertEqual(linuxTestCount, darwinTestCount, "allTests (used for testing on Linux) is missing \(darwinTestCount - linuxTestCount) tests") 10 | #endif 11 | } 12 | 13 | func testPerformanceOfIndexOf() { 14 | let sourceArray = Array(repeating: 500, count: 1_000_000) + Array(repeating: 40, count: 1_000_000) + Array(repeating: 3, count: 1_000_000) 15 | let sut = SortedArray(unsorted: sourceArray) 16 | measure { 17 | XCTAssertEqual(sut.index(of: 500), 2_000_000) 18 | } 19 | } 20 | 21 | func testPerformanceOfLastIndexOf() { 22 | let sourceArray = Array(repeating: 500, count: 1_000_000) + Array(repeating: 40, count: 1_000_000) + Array(repeating: 3, count: 1_000_000) 23 | let sut = SortedArray(unsorted: sourceArray) 24 | measure { 25 | XCTAssertEqual(sut.lastIndex(of: 3), 999_999) 26 | } 27 | } 28 | 29 | func testPerformanceOfFirstIndexWhere() { 30 | let sourceArray = Array(repeating: 500, count: 1_000_000) + Array(repeating: 40, count: 1_000_000) + Array(repeating: 3, count: 1_000_000) 31 | let sut = SortedArray(unsorted: sourceArray) 32 | measure { 33 | XCTAssertEqual(sut.firstIndex(where: { $0 >= 500 }), 2_000_000) 34 | } 35 | } 36 | 37 | func testPerformanceOfLastIndexWhere() { 38 | let sourceArray = Array(repeating: 500, count: 1_000_000) + Array(repeating: 40, count: 1_000_000) + Array(repeating: 3, count: 1_000_000) 39 | let sut = SortedArray(unsorted: sourceArray) 40 | measure { 41 | XCTAssertEqual(sut.lastIndex(where: { $0 <= 3 }), 999_999) 42 | } 43 | } 44 | 45 | func testPerformanceOfIndexOfObject() { 46 | let sourceArray = Array(repeating: Box(500), count: 1_000_000) + Array(repeating: Box(40), count: 1_000_000) + Array(repeating: Box(3), count: 1_000_000) 47 | let sut = SortedArray(unsorted: sourceArray) 48 | measure { 49 | XCTAssertEqual(sut.index(of: Box(500)), 2_000_000) 50 | } 51 | } 52 | 53 | func testPerformanceOfLastIndexOfObject() { 54 | let sourceArray = Array(repeating: Box(500), count: 1_000_000) + Array(repeating: Box(40), count: 1_000_000) + Array(repeating: Box(3), count: 1_000_000) 55 | let sut = SortedArray(unsorted: sourceArray) 56 | measure { 57 | XCTAssertEqual(sut.lastIndex(of: Box(3)), 999_999) 58 | } 59 | } 60 | 61 | func testPerformanceOfFirstIndexWhereObject() { 62 | let sourceArray = Array(repeating: Box(500), count: 1_000_000) + Array(repeating: Box(40), count: 1_000_000) + Array(repeating: Box(3), count: 1_000_000) 63 | let sut = SortedArray(unsorted: sourceArray) 64 | measure { 65 | XCTAssertEqual(sut.firstIndex(where: { $0 >= Box(500) }), 2_000_000) 66 | } 67 | } 68 | 69 | func testPerformanceOfLastIndexWhereObject() { 70 | let sourceArray = Array(repeating: Box(500), count: 1_000_000) + Array(repeating: Box(40), count: 1_000_000) + Array(repeating: Box(3), count: 1_000_000) 71 | let sut = SortedArray(unsorted: sourceArray) 72 | measure { 73 | XCTAssertEqual(sut.lastIndex(where: { $0 <= Box(3) }), 999_999) 74 | } 75 | } 76 | 77 | func testPerformanceOfIndexOfObjectInRange() { 78 | let sut = SortedArray(sorted: (0..<3_000_000).lazy.map(Box.init)) 79 | measure { 80 | XCTAssertEqual(sut.index(of: Box(500)), 500) 81 | } 82 | } 83 | 84 | func testPerformanceOfLastIndexOfObjectInRange() { 85 | let sut = SortedArray(sorted: (0..<3_000_000).lazy.map(Box.init)) 86 | XCTAssertEqual(sut.lastIndex(of: Box(1_999_999)), 1_999_999) 87 | measure { 88 | XCTAssertEqual(sut.lastIndex(of: Box(1_999_999)), 1_999_999) 89 | } 90 | } 91 | 92 | func testPerformanceOfFirstIndexWhereObjectInRange() { 93 | let sut = SortedArray(sorted: (0..<3_000_000).lazy.map(Box.init)) 94 | measure { 95 | XCTAssertEqual(sut.firstIndex(where: { $0 >= Box(500) }), 500) 96 | } 97 | } 98 | 99 | func testPerformanceOfLastIndexWhereObjectInRange() { 100 | let sut = SortedArray(sorted: (0..<3_000_000).lazy.map(Box.init)) 101 | measure { 102 | XCTAssertEqual(sut.lastIndex(where: { $0 <= Box(1_999_999) }), 1_999_999) 103 | } 104 | } 105 | } 106 | 107 | extension PerformanceTests { 108 | static var allTests : [(String, (PerformanceTests) -> () throws -> Void)] { 109 | return [ 110 | ("testLinuxTestSuiteIncludesAllTests", testLinuxTestSuiteIncludesAllTests), 111 | ("testPerformanceOfIndexOf", testPerformanceOfIndexOf), 112 | ("testPerformanceOfLastIndexOf", testPerformanceOfLastIndexOf), 113 | ("testPerformanceOfFirstIndexWhere", testPerformanceOfFirstIndexWhere), 114 | ("testPerformanceOfLastIndexWhere", testPerformanceOfLastIndexWhere), 115 | ("testPerformanceOfIndexOfObject", testPerformanceOfIndexOfObject), 116 | ("testPerformanceOfLastIndexOfObject", testPerformanceOfLastIndexOfObject), 117 | ("testPerformanceOfFirstIndexWhereObject", testPerformanceOfFirstIndexWhereObject), 118 | ("testPerformanceOfLastIndexWhereObject", testPerformanceOfLastIndexWhereObject), 119 | ("testPerformanceOfIndexOfObjectInRange", testPerformanceOfIndexOfObjectInRange), 120 | ("testPerformanceOfLastIndexOfObjectInRange", testPerformanceOfLastIndexOfObjectInRange), 121 | ("testPerformanceOfFirstIndexWhereObjectInRange", testPerformanceOfFirstIndexWhereObjectInRange), 122 | ("testPerformanceOfLastIndexWhereObjectInRange", testPerformanceOfLastIndexWhereObjectInRange), 123 | ] 124 | } 125 | } 126 | 127 | /// Helper class 128 | class Box: Comparable { 129 | static func ==(lhs: Box, rhs: Box) -> Bool { 130 | return lhs.value == rhs.value 131 | } 132 | 133 | static func <(lhs: Box, rhs: Box) -> Bool { 134 | return lhs.value < rhs.value 135 | } 136 | 137 | let value: T 138 | init(_ value: T) { 139 | self.value = value 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Tests/UnitTests/SortedArrayTests.swift: -------------------------------------------------------------------------------- 1 | import SortedArray 2 | import XCTest 3 | 4 | class SortedArrayTests: XCTestCase { 5 | override func setUp() { 6 | super.setUp() 7 | } 8 | 9 | override func tearDown() { 10 | super.tearDown() 11 | } 12 | 13 | func testLinuxTestSuiteIncludesAllTests() { 14 | #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) 15 | let darwinTestCount = SortedArrayTests.defaultTestSuite.testCaseCount 16 | let linuxTestCount = SortedArrayTests.allTests.count 17 | XCTAssertEqual(linuxTestCount, darwinTestCount, "allTests (used for testing on Linux) is missing \(darwinTestCount - linuxTestCount) tests") 18 | #endif 19 | } 20 | 21 | func testInitUnsortedSorts() { 22 | let sut = SortedArray(unsorted: [3,4,2,1], areInIncreasingOrder: <) 23 | assertElementsEqual(sut, [1,2,3,4]) 24 | } 25 | 26 | func testInitSortedDoesntResort() { 27 | // Warning: this is not a valid way to create a SortedArray 28 | let sut = SortedArray(sorted: [3,2,1]) 29 | assertElementsEqual(Array(sut), [3,2,1]) 30 | } 31 | 32 | func testSortedArrayCanUseArbitraryComparisonPredicate() { 33 | struct Person { 34 | var firstName: String 35 | var lastName: String 36 | } 37 | let a = Person(firstName: "A", lastName: "Smith") 38 | let b = Person(firstName: "B", lastName: "Jones") 39 | let c = Person(firstName: "C", lastName: "Lewis") 40 | 41 | var sut = SortedArray { $0.firstName > $1.firstName } 42 | sut.insert(contentsOf: [b,a,c]) 43 | assertElementsEqual(sut.map { $0.firstName }, ["C","B","A"]) 44 | } 45 | 46 | func testConvenienceInitsUseLessThan() { 47 | let sut = SortedArray(unsorted: ["a","c","b"]) 48 | assertElementsEqual(sut, ["a","b","c"]) 49 | } 50 | 51 | func testInsertAtBeginningPreservesSortOrder() { 52 | var sut = SortedArray(unsorted: 1...3) 53 | sut.insert(0) 54 | assertElementsEqual(sut, [0,1,2,3]) 55 | } 56 | 57 | func testInsertInMiddlePreservesSortOrder() { 58 | var sut = SortedArray(unsorted: 1...5) 59 | sut.insert(4) 60 | assertElementsEqual(sut, [1,2,3,4,4,5]) 61 | } 62 | 63 | func testInsertAtEndPreservesSortOrder() { 64 | var sut = SortedArray(unsorted: 1...3) 65 | sut.insert(5) 66 | assertElementsEqual(sut, [1,2,3,5]) 67 | } 68 | 69 | func testInsertAtBeginningReturnsInsertionIndex() { 70 | var sut = SortedArray(unsorted: [1,2,3]) 71 | let index = sut.insert(0) 72 | XCTAssertEqual(index, 0) 73 | } 74 | 75 | func testInsertInMiddleReturnsInsertionIndex() { 76 | var sut = SortedArray(unsorted: [1,2,3,5]) 77 | let index = sut.insert(4) 78 | XCTAssertEqual(index, 3) 79 | } 80 | 81 | func testInsertAtEndReturnsInsertionIndex() { 82 | var sut = SortedArray(unsorted: [1,2,3]) 83 | let index = sut.insert(100) 84 | XCTAssertEqual(index, 3) 85 | } 86 | 87 | func testInsertInEmptyArrayReturnsInsertionIndex() { 88 | var sut = SortedArray() 89 | let index = sut.insert(10) 90 | XCTAssertEqual(index, 0) 91 | } 92 | 93 | func testInsertEqualElementReturnsCorrectInsertionIndex() { 94 | var sut = SortedArray(unsorted: [3,1,0,2,1]) 95 | let index = sut.insert(1) 96 | XCTAssert(index == 1 || index == 2 || index == 3) 97 | } 98 | 99 | func testInsertContentsOfPreservesSortOrder() { 100 | var sut = SortedArray(unsorted: [10,9,8]) 101 | sut.insert(contentsOf: (7...11).reversed()) 102 | assertElementsEqual(sut, [7,8,8,9,9,10,10,11]) 103 | } 104 | 105 | func testIndexOfFindsElementInMiddle() { 106 | let sut = SortedArray(unsorted: ["a","z","r","k"]) 107 | let index = sut.firstIndex(of: "k") 108 | XCTAssertEqual(index, 1) 109 | } 110 | 111 | func testIndexOfFindsFirstElement() { 112 | let sut = SortedArray(sorted: 1..<10) 113 | let index = sut.firstIndex(of: 1) 114 | XCTAssertEqual(index, 0) 115 | } 116 | 117 | func testIndexOfFindsLastElement() { 118 | let sut = SortedArray(sorted: 1..<10) 119 | let index = sut.firstIndex(of: 9) 120 | XCTAssertEqual(index, 8) 121 | } 122 | 123 | func testIndexOfReturnsNilWhenNotFound() { 124 | let sut = SortedArray(unsorted: "Hello World") 125 | let index = sut.firstIndex(of: "h") 126 | XCTAssertNil(index) 127 | } 128 | 129 | func testIndexOfReturnsNilForEmptyArray() { 130 | let sut = SortedArray() 131 | let index = sut.firstIndex(of: 1) 132 | XCTAssertNil(index) 133 | } 134 | 135 | func testIndexOfCanDealWithSingleElementArray() { 136 | let sut = SortedArray(unsorted: [5]) 137 | let index = sut.firstIndex(of: 5) 138 | XCTAssertEqual(index, 0) 139 | } 140 | 141 | func testIndexOfFindsFirstIndexOfDuplicateElements1() { 142 | let sut = SortedArray(unsorted: [1,2,3,3,3,3,3,3,3,3,4,5]) 143 | let index = sut.firstIndex(of: 3) 144 | XCTAssertEqual(index, 2) 145 | } 146 | 147 | func testIndexOfFindsFirstIndexOfDuplicateElements2() { 148 | let sut = SortedArray(unsorted: [1,4,4,4,4,4,4,4,4,3,2]) 149 | let index = sut.firstIndex(of: 4) 150 | XCTAssertEqual(index, 3) 151 | } 152 | 153 | func testIndexOfFindsFirstIndexOfDuplicateElements3() { 154 | let sut = SortedArray(unsorted: String(repeating: "A", count: 10)) 155 | let index = sut.firstIndex(of: "A") 156 | XCTAssertEqual(index, 0) 157 | } 158 | 159 | func testIndexOfFindsFirstIndexOfDuplicateElements4() { 160 | let sut = SortedArray(unsorted: Array(repeating: "a", count: 100_000)) 161 | let index = sut.firstIndex(of: "a") 162 | XCTAssertEqual(index, 0) 163 | } 164 | 165 | func testIndexOfFindsFirstIndexOfDuplicateElements5() { 166 | let sourceArray = Array(repeating: 5, count: 100_000) + [1,2,6,7,8,9] 167 | let sut = SortedArray(unsorted: sourceArray) 168 | let index = sut.firstIndex(of: 5) 169 | XCTAssertEqual(index, 2) 170 | } 171 | 172 | func testIndexOfExistsAndIsAnAliasForFirstIndexOf() { 173 | let sut = SortedArray(unsorted: [1,2,3,3,3,3,3,3,3,3,4,5]) 174 | let index = sut.index(of: 3) 175 | XCTAssertEqual(index, 2) 176 | } 177 | 178 | func testFirstIndexWhereFindsElementInMiddle() { 179 | let sut = SortedArray(unsorted: ["a","z","r","k"]) 180 | let index = sut.firstIndex(where: { $0 >= "k" }) 181 | XCTAssertEqual(index, 1) 182 | } 183 | 184 | func testFirstIndexWhereFindsFirstElement() { 185 | let sut = SortedArray(sorted: 1..<10) 186 | let index = sut.firstIndex(where: { $0 >= 1 }) 187 | XCTAssertEqual(index, 0) 188 | } 189 | 190 | func testFirstIndexWhereFindsLastElement() { 191 | let sut = SortedArray(sorted: 1..<10) 192 | let index = sut.firstIndex(where: { $0 >= 9 }) 193 | XCTAssertEqual(index, 8) 194 | } 195 | 196 | func testFirstIndexWhereReturnsNilWhenNotFound() { 197 | let sut = SortedArray(unsorted: "Hello World") 198 | let index = sut.firstIndex(where: { $0 > "z"}) 199 | XCTAssertNil(index) 200 | } 201 | 202 | func testFirstIndexWhereReturnsNilForEmptyArray() { 203 | let sut = SortedArray() 204 | let index = sut.firstIndex(where: { $0 >= 1 }) 205 | XCTAssertNil(index) 206 | } 207 | 208 | func testFirstIndexWhereCanDealWithSingleElementArray() { 209 | let sut = SortedArray(unsorted: [5]) 210 | let index = sut.firstIndex(where: { $0 >= 5 }) 211 | XCTAssertEqual(index, 0) 212 | } 213 | 214 | func testFirstIndexWhereFindsFirstIndexOfDuplicateElements1() { 215 | let sut = SortedArray(unsorted: [1,2,3,3,3,3,3,3,3,3,4,5]) 216 | let index = sut.firstIndex(where: { $0 >= 3 }) 217 | XCTAssertEqual(index, 2) 218 | } 219 | 220 | func testFirstIndexWhereFindsFirstIndexOfDuplicateElements2() { 221 | let sut = SortedArray(unsorted: [1,4,4,4,4,4,4,4,4,3,2]) 222 | let index = sut.firstIndex(where: { $0 >= 4 }) 223 | XCTAssertEqual(index, 3) 224 | } 225 | 226 | func testFirstIndexWhereFindsFirstIndexOfDuplicateElements3() { 227 | let sut = SortedArray(unsorted: String(repeating: "A", count: 10)) 228 | let index = sut.firstIndex(where: { $0 >= "A" }) 229 | XCTAssertEqual(index, 0) 230 | } 231 | 232 | func testFirstIndexWhereFindsFirstIndexOfDuplicateElements4() { 233 | let sut = SortedArray(unsorted: Array(repeating: "a", count: 100_000)) 234 | let index = sut.firstIndex(where: { $0 >= "a" }) 235 | XCTAssertEqual(index, 0) 236 | } 237 | 238 | func testFirstIndexWhereFindsFirstIndexOfDuplicateElements5() { 239 | let sourceArray = Array(repeating: 5, count: 100_000) + [1,2,6,7,8,9] 240 | let sut = SortedArray(unsorted: sourceArray) 241 | let index = sut.firstIndex(where: { $0 >= 5 }) 242 | XCTAssertEqual(index, 2) 243 | } 244 | 245 | func testFirstWhereFindsFirstMatchingElement() { 246 | let sut = SortedArray(unsorted: [4, 3, 2, 1]) 247 | let last = sut.first(where: { $0 >= 2 }) 248 | XCTAssertEqual(last, 2) 249 | } 250 | 251 | func testLastIndexOfFindsElementInMiddle() { 252 | let sut = SortedArray(unsorted: ["a","z","r","k"]) 253 | let index = sut.lastIndex(of: "k") 254 | XCTAssertEqual(index, 1) 255 | } 256 | 257 | func testLastIndexOfFindsFirstElement() { 258 | let sut = SortedArray(sorted: 1..<10) 259 | let index = sut.lastIndex(of: 1) 260 | XCTAssertEqual(index, 0) 261 | } 262 | 263 | func testLastIndexOfFindsLastElement() { 264 | let sut = SortedArray(sorted: 1..<10) 265 | let index = sut.lastIndex(of: 9) 266 | XCTAssertEqual(index, 8) 267 | } 268 | 269 | func testLastIndexOfReturnsNilWhenNotFound() { 270 | let sut = SortedArray(unsorted: "Hello World") 271 | let index = sut.lastIndex(of: "h") 272 | XCTAssertNil(index) 273 | } 274 | 275 | func testLastIndexOfReturnsNilForEmptyArray() { 276 | let sut = SortedArray() 277 | let index = sut.lastIndex(of: 1) 278 | XCTAssertNil(index) 279 | } 280 | 281 | func testLastIndexOfCanDealWithSingleElementArray() { 282 | let sut = SortedArray(unsorted: [5]) 283 | let index = sut.lastIndex(of: 5) 284 | XCTAssertEqual(index, 0) 285 | } 286 | 287 | func testLastIndexOfFindsLastIndexOfDuplicateElements1() { 288 | let sut = SortedArray(unsorted: [1,2,3,3,3,3,3,3,3,3,4,5]) 289 | let index = sut.lastIndex(of: 3) 290 | XCTAssertEqual(index, 9) 291 | } 292 | 293 | func testLastIndexOfFindsLastIndexOfDuplicateElements2() { 294 | let sut = SortedArray(unsorted: [1,4,4,4,4,4,4,4,4,3,2]) 295 | let index = sut.lastIndex(of: 4) 296 | XCTAssertEqual(index, 10) 297 | } 298 | 299 | func testLastIndexOfFindsLastIndexOfDuplicateElements3() { 300 | let sut = SortedArray(unsorted: String(repeating: "A", count: 10)) 301 | let index = sut.lastIndex(of: "A") 302 | XCTAssertEqual(index, 9) 303 | } 304 | 305 | func testLastIndexWhereFindsElementInMiddle() { 306 | let sut = SortedArray(unsorted: ["a","z","r","k"]) 307 | let index = sut.lastIndex(where: { $0 <= "k" }) 308 | XCTAssertEqual(index, 1) 309 | } 310 | 311 | func testLastIndexWhereFindsFirstElement() { 312 | let sut = SortedArray(sorted: 1..<10) 313 | let index = sut.lastIndex(where: { $0 <= 1 }) 314 | XCTAssertEqual(index, 0) 315 | } 316 | 317 | func testLastIndexWhereFindsLastElement() { 318 | let sut = SortedArray(sorted: 1..<10) 319 | let index = sut.lastIndex(where: { $0 <= 9 }) 320 | XCTAssertEqual(index, 8) 321 | } 322 | 323 | func testLastIndexWhereReturnsNilWhenNotFound() { 324 | let sut = SortedArray(unsorted: "Hello World") 325 | let index = sut.lastIndex(where: { $0 < " " }) 326 | XCTAssertNil(index) 327 | } 328 | 329 | func testLastIndexWhereReturnsNilForEmptyArray() { 330 | let sut = SortedArray() 331 | let index = sut.lastIndex(where: { $0 <= 1 }) 332 | XCTAssertNil(index) 333 | } 334 | 335 | func testLastIndexWhereCanDealWithSingleElementArray() { 336 | let sut = SortedArray(unsorted: [5]) 337 | let index = sut.lastIndex(where: { $0 <= 5 }) 338 | XCTAssertEqual(index, 0) 339 | } 340 | 341 | func testLastIndexWhereFindsLastIndexOfDuplicateElements1() { 342 | let sut = SortedArray(unsorted: [1,2,3,3,3,3,3,3,3,3,4,5]) 343 | let index = sut.lastIndex(where: { $0 <= 3 }) 344 | XCTAssertEqual(index, 9) 345 | } 346 | 347 | func testLastIndexWhereFindsLastIndexOfDuplicateElements2() { 348 | let sut = SortedArray(unsorted: [1,4,4,4,4,4,4,4,4,3,2]) 349 | let index = sut.lastIndex(where: { $0 <= 4 }) 350 | XCTAssertEqual(index, 10) 351 | } 352 | 353 | func testLastIndexWhereFindsLastIndexOfDuplicateElements3() { 354 | let sut = SortedArray(unsorted: String(repeating: "A", count: 10)) 355 | let index = sut.lastIndex(where: { $0 <= "A" }) 356 | XCTAssertEqual(index, 9) 357 | } 358 | 359 | func testLastWhereFindsLastMatchingElement() { 360 | let sut = SortedArray(unsorted: [4, 3, 2, 1]) 361 | let last = sut.last(where: { $0 <= 3 }) 362 | XCTAssertEqual(last, 3) 363 | } 364 | 365 | func testsContains() { 366 | let sut = SortedArray(unsorted: "Lorem ipsum") 367 | XCTAssertTrue(sut.contains(" ")) 368 | XCTAssertFalse(sut.contains("a")) 369 | } 370 | 371 | func testMin() { 372 | let sut = SortedArray(unsorted: -10...10) 373 | XCTAssertEqual(sut.min(), -10) 374 | } 375 | 376 | func testMax() { 377 | let sut = SortedArray(unsorted: -10...(-1)) 378 | XCTAssertEqual(sut.max(), -1) 379 | } 380 | 381 | func testCustomStringConvertible() { 382 | let sut = SortedArray(unsorted: ["a", "c", "b"]) 383 | let description = String(describing: sut) 384 | XCTAssertEqual(description, "[\"a\", \"b\", \"c\"] (sorted)") 385 | } 386 | 387 | func testCustomDebugStringConvertible() { 388 | let sut = SortedArray(unsorted: ["a", "c", "b"]) 389 | let description = String(reflecting: sut) 390 | XCTAssertEqual(description, " [\"a\", \"b\", \"c\"]") 391 | } 392 | 393 | func testFilter() { 394 | let sut = SortedArray(unsorted: ["a", "b", "c"]) 395 | assertElementsEqual(sut.filter { $0 != "a" }, ["b", "c"]) 396 | } 397 | 398 | func testRemoveAtIndex() { 399 | var sut = SortedArray(unsorted: [3,4,2,1]) 400 | let removedElement = sut.remove(at: 1) 401 | assertElementsEqual(sut, [1,3,4]) 402 | XCTAssertEqual(removedElement, 2) 403 | } 404 | 405 | func testRemoveSubrange() { 406 | var sut = SortedArray(unsorted: ["a","d","c","b"]) 407 | sut.removeSubrange(2..<4) 408 | assertElementsEqual(sut, ["a","b"]) 409 | } 410 | 411 | func testRemoveCountableSubrange() { 412 | var sut = SortedArray(unsorted: ["a","d","c","b"]) 413 | let countableRange: CountableRange = 2..<4 414 | sut.removeSubrange(countableRange) 415 | assertElementsEqual(sut, ["a","b"]) 416 | } 417 | 418 | func testRemoveFirst() { 419 | var sut = SortedArray(unsorted: [3,4,2,1]) 420 | let removedElement = sut.removeFirst() 421 | assertElementsEqual(sut, [2,3,4]) 422 | XCTAssertEqual(removedElement, 1) 423 | } 424 | 425 | func testRemoveFirstN() { 426 | var sut = SortedArray(unsorted: [3,4,2,1]) 427 | sut.removeFirst(2) 428 | assertElementsEqual(sut, [3,4]) 429 | } 430 | 431 | func testRemoveLast() { 432 | var sut = SortedArray(unsorted: [3,4,2,1]) 433 | let removedElement = sut.removeLast() 434 | assertElementsEqual(sut, [1,2,3]) 435 | XCTAssertEqual(removedElement, 4) 436 | } 437 | 438 | func testRemoveLastN() { 439 | var sut = SortedArray(unsorted: [3,4,2,1]) 440 | sut.removeLast(2) 441 | assertElementsEqual(sut, [1,2]) 442 | } 443 | 444 | func testRemoveAll() { 445 | var sut = SortedArray(unsorted: ["a","d","c","b"]) 446 | sut.removeAll() 447 | assertElementsEqual(sut, []) 448 | } 449 | 450 | func testRemoveElementAtBeginningPreservesSortOrder() { 451 | var sut = SortedArray(unsorted: 1...3) 452 | sut.remove(1) 453 | assertElementsEqual(sut, [2,3]) 454 | } 455 | 456 | func testRemoveElementInMiddlePreservesSortOrder() { 457 | var sut = SortedArray(unsorted: 1...5) 458 | sut.remove(4) 459 | assertElementsEqual(sut, [1,2,3,5]) 460 | } 461 | 462 | func testRemoveElementAtEndPreservesSortOrder() { 463 | var sut = SortedArray(unsorted: 1...3) 464 | sut.remove(3) 465 | assertElementsEqual(sut, [1,2]) 466 | } 467 | 468 | func testIsEquatableInSwift4_1AndHigher() { 469 | #if swift(>=4.1) 470 | let array1 = SortedArray(unsorted: [3,2,1]) 471 | let array2 = SortedArray(unsorted: 1...3) 472 | XCTAssertEqual(array1, array2) 473 | #endif 474 | } 475 | 476 | func testComparatorFunctionIsNotRelevantForEquatable() { 477 | #if swift(>=4.1) 478 | let array1 = SortedArray(unsorted: [1,1,1], areInIncreasingOrder: <) 479 | let array2 = SortedArray(unsorted: [1,1,1], areInIncreasingOrder: >) 480 | let array3 = SortedArray(unsorted: [3,2,1,4]) 481 | XCTAssertEqual(array1, array2) 482 | XCTAssertNotEqual(array1, array3) 483 | #endif 484 | } 485 | 486 | func testImplementsEqual() { 487 | let sut = SortedArray(unsorted: [3,2,1]) 488 | XCTAssertTrue(sut == SortedArray(unsorted: 1...3)) 489 | } 490 | 491 | func testImplementsNotEqual() { 492 | let sut = SortedArray(unsorted: 1...3) 493 | XCTAssertTrue(sut != SortedArray(unsorted: 1...4)) 494 | } 495 | 496 | func testIsHashableInSwift4_2AndHigher() { 497 | #if swift(>=4.1.50) 498 | let array1 = SortedArray(unsorted: [3,2,1]) 499 | let array2 = SortedArray(unsorted: 1...3) 500 | let array3 = SortedArray(unsorted: [3,2,1,4]) 501 | XCTAssertEqual(array1.hashValue, array2.hashValue) 502 | XCTAssertNotEqual(array1.hashValue, array3.hashValue) 503 | #endif 504 | } 505 | } 506 | 507 | extension SortedArrayTests { 508 | static var allTests : [(String, (SortedArrayTests) -> () throws -> Void)] { 509 | return [ 510 | ("testLinuxTestSuiteIncludesAllTests", testLinuxTestSuiteIncludesAllTests), 511 | ("testInitUnsortedSorts", testInitUnsortedSorts), 512 | ("testInitSortedDoesntResort", testInitSortedDoesntResort), 513 | ("testSortedArrayCanUseArbitraryComparisonPredicate", testSortedArrayCanUseArbitraryComparisonPredicate), 514 | ("testConvenienceInitsUseLessThan", testConvenienceInitsUseLessThan), 515 | ("testInsertAtBeginningPreservesSortOrder", testInsertAtBeginningPreservesSortOrder), 516 | ("testInsertInMiddlePreservesSortOrder", testInsertInMiddlePreservesSortOrder), 517 | ("testInsertAtEndPreservesSortOrder", testInsertAtEndPreservesSortOrder), 518 | ("testInsertAtBeginningReturnsInsertionIndex", testInsertAtBeginningReturnsInsertionIndex), 519 | ("testInsertInMiddleReturnsInsertionIndex", testInsertInMiddleReturnsInsertionIndex), 520 | ("testInsertAtEndReturnsInsertionIndex", testInsertAtEndReturnsInsertionIndex), 521 | ("testInsertInEmptyArrayReturnsInsertionIndex", testInsertInEmptyArrayReturnsInsertionIndex), 522 | ("testInsertEqualElementReturnsCorrectInsertionIndex", testInsertEqualElementReturnsCorrectInsertionIndex), 523 | ("testInsertContentsOfPreservesSortOrder", testInsertContentsOfPreservesSortOrder), 524 | ("testIndexOfFindsElementInMiddle", testIndexOfFindsElementInMiddle), 525 | ("testIndexOfFindsFirstElement", testIndexOfFindsFirstElement), 526 | ("testIndexOfFindsLastElement", testIndexOfFindsLastElement), 527 | ("testIndexOfReturnsNilWhenNotFound", testIndexOfReturnsNilWhenNotFound), 528 | ("testIndexOfReturnsNilForEmptyArray", testIndexOfReturnsNilForEmptyArray), 529 | ("testIndexOfCanDealWithSingleElementArray", testIndexOfCanDealWithSingleElementArray), 530 | ("testIndexOfFindsFirstIndexOfDuplicateElements1", testIndexOfFindsFirstIndexOfDuplicateElements1), 531 | ("testIndexOfFindsFirstIndexOfDuplicateElements2", testIndexOfFindsFirstIndexOfDuplicateElements2), 532 | ("testIndexOfFindsFirstIndexOfDuplicateElements3", testIndexOfFindsFirstIndexOfDuplicateElements3), 533 | ("testIndexOfFindsFirstIndexOfDuplicateElements4", testIndexOfFindsFirstIndexOfDuplicateElements4), 534 | ("testIndexOfFindsFirstIndexOfDuplicateElements5", testIndexOfFindsFirstIndexOfDuplicateElements4), 535 | ("testIndexOfExistsAndIsAnAliasForFirstIndexOf", testIndexOfExistsAndIsAnAliasForFirstIndexOf), 536 | ("testFirstIndexWhereFindsElementInMiddle", testFirstIndexWhereFindsElementInMiddle), 537 | ("testFirstIndexWhereFindsFirstElement", testFirstIndexWhereFindsFirstElement), 538 | ("testFirstIndexWhereFindsLastElement", testFirstIndexWhereFindsLastElement), 539 | ("testFirstIndexWhereReturnsNilWhenNotFound", testFirstIndexWhereReturnsNilWhenNotFound), 540 | ("testFirstIndexWhereReturnsNilForEmptyArray", testFirstIndexWhereReturnsNilForEmptyArray), 541 | ("testFirstIndexWhereCanDealWithSingleElementArray", testFirstIndexWhereCanDealWithSingleElementArray), 542 | ("testFirstIndexWhereFindsFirstIndexOfDuplicateElements1", testFirstIndexWhereFindsFirstIndexOfDuplicateElements1), 543 | ("testFirstIndexWhereFindsFirstIndexOfDuplicateElements2", testFirstIndexWhereFindsFirstIndexOfDuplicateElements2), 544 | ("testFirstIndexWhereFindsFirstIndexOfDuplicateElements3", testFirstIndexWhereFindsFirstIndexOfDuplicateElements3), 545 | ("testFirstIndexWhereFindsFirstIndexOfDuplicateElements4", testFirstIndexWhereFindsFirstIndexOfDuplicateElements4), 546 | ("testFirstIndexWhereFindsFirstIndexOfDuplicateElements5", testFirstIndexWhereFindsFirstIndexOfDuplicateElements5), 547 | ("testFirstWhereFindsFirstMatchingElement", testFirstWhereFindsFirstMatchingElement), 548 | ("testLastIndexOfFindsElementInMiddle", testLastIndexOfFindsElementInMiddle), 549 | ("testLastIndexOfFindsFirstElement", testLastIndexOfFindsFirstElement), 550 | ("testLastIndexOfFindsLastElement", testLastIndexOfFindsLastElement), 551 | ("testLastIndexOfReturnsNilWhenNotFound", testLastIndexOfReturnsNilWhenNotFound), 552 | ("testLastIndexOfReturnsNilForEmptyArray", testLastIndexOfReturnsNilForEmptyArray), 553 | ("testLastIndexOfCanDealWithSingleElementArray", testLastIndexOfCanDealWithSingleElementArray), 554 | ("testLastIndexOfFindsLastIndexOfDuplicateElements1", testLastIndexOfFindsLastIndexOfDuplicateElements1), 555 | ("testLastIndexOfFindsLastIndexOfDuplicateElements2", testLastIndexOfFindsLastIndexOfDuplicateElements2), 556 | ("testLastIndexOfFindsLastIndexOfDuplicateElements3", testLastIndexOfFindsLastIndexOfDuplicateElements3), 557 | ("testLastIndexWhereFindsElementInMiddle", testLastIndexWhereFindsElementInMiddle), 558 | ("testLastIndexWhereFindsFirstElement", testLastIndexWhereFindsFirstElement), 559 | ("testLastIndexWhereFindsLastElement", testLastIndexWhereFindsLastElement), 560 | ("testLastIndexWhereReturnsNilWhenNotFound", testLastIndexWhereReturnsNilWhenNotFound), 561 | ("testLastIndexWhereReturnsNilForEmptyArray", testLastIndexWhereReturnsNilForEmptyArray), 562 | ("testLastIndexWhereCanDealWithSingleElementArray", testLastIndexWhereCanDealWithSingleElementArray), 563 | ("testLastIndexWhereFindsLastIndexOfDuplicateElements1", testLastIndexWhereFindsLastIndexOfDuplicateElements1), 564 | ("testLastIndexWhereFindsLastIndexOfDuplicateElements2", testLastIndexWhereFindsLastIndexOfDuplicateElements2), 565 | ("testLastIndexWhereFindsLastIndexOfDuplicateElements3", testLastIndexWhereFindsLastIndexOfDuplicateElements3), 566 | ("testLastWhereFindsLastMatchingElement", testLastWhereFindsLastMatchingElement), 567 | ("testsContains", testsContains), 568 | ("testMin", testMin), 569 | ("testMax", testMax), 570 | ("testCustomStringConvertible", testCustomStringConvertible), 571 | ("testCustomDebugStringConvertible", testCustomDebugStringConvertible), 572 | ("testFilter", testFilter), 573 | ("testRemoveAtIndex", testRemoveAtIndex), 574 | ("testRemoveSubrange", testRemoveSubrange), 575 | ("testRemoveCountableSubrange", testRemoveCountableSubrange), 576 | ("testRemoveFirst", testRemoveFirst), 577 | ("testRemoveFirstN", testRemoveFirstN), 578 | ("testRemoveLast", testRemoveLast), 579 | ("testRemoveLastN", testRemoveLastN), 580 | ("testRemoveAll", testRemoveAll), 581 | ("testRemoveElementAtBeginningPreservesSortOrder", testRemoveElementAtBeginningPreservesSortOrder), 582 | ("testRemoveElementInMiddlePreservesSortOrder", testRemoveElementInMiddlePreservesSortOrder), 583 | ("testRemoveElementAtEndPreservesSortOrder", testRemoveElementAtEndPreservesSortOrder), 584 | ("testIsEquatableInSwift4_1AndHigher", testIsEquatableInSwift4_1AndHigher), 585 | ("testComparatorFunctionIsNotRelevantForEquatable", testComparatorFunctionIsNotRelevantForEquatable), 586 | ("testImplementsEqual", testImplementsEqual), 587 | ("testImplementsNotEqual", testImplementsNotEqual), 588 | ("testIsHashableInSwift4_2AndHigher", testIsHashableInSwift4_2AndHigher), 589 | ] 590 | } 591 | } 592 | 593 | struct Pair { 594 | let first: A 595 | let second: B 596 | 597 | init(_ first: A, _ second: B) { 598 | self.first = first 599 | self.second = second 600 | } 601 | } 602 | 603 | extension Pair: Equatable where A: Equatable, B: Equatable {} 604 | -------------------------------------------------------------------------------- /Tests/UnitTests/TestHelpers.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | // FIXME: Can be replaced with XCTAssertEqual when we drop Swift 4.0 compatibility 4 | func assertElementsEqual(_ expression1: @autoclosure () throws -> S1, _ expression2: @autoclosure () throws -> S2, _ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) 5 | where S1: Sequence, S2: Sequence, S1.Element == S2.Element, S1.Element: Equatable 6 | { 7 | // This should give a better error message than using XCTAssert(try expression1().elementsEqual(expression2()), ...) 8 | try XCTAssertEqual(Array(expression1()), Array(expression2()), message, file: file, line: line) 9 | } 10 | --------------------------------------------------------------------------------