├── .travis.yml ├── LICENCE.md ├── Package.swift ├── README.md ├── RedBlackTree.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ ├── xcbaselines │ └── BB9AE45C1C1FCEB3002C5D63.xcbaseline │ │ ├── 38496774-E074-454C-A291-C4990C30544C.plist │ │ └── Info.plist │ └── xcschemes │ ├── Mac.xcscheme │ ├── iOS.xcscheme │ ├── tvOS.xcscheme │ └── watchOS.xcscheme ├── Sources ├── Info.plist ├── RedBlackKey.swift ├── RedBlackTree descriptions.swift ├── RedBlackTree.swift └── Summary.swift ├── Tests ├── Helpers │ ├── PermutationTests.swift │ ├── Permutations.swift │ ├── Random.swift │ ├── String Manipulation.swift │ └── XCTest extensions.swift ├── Info.plist ├── RedBlackTreeTests.swift ├── TestUtils.swift ├── TreeChecker.swift └── TreeDumper.swift ├── generate-docs.sh └── version.xcconfig /.travis.yml: -------------------------------------------------------------------------------- 1 | # This file contains some arcane technical incantations that are needed to 2 | # enable code coverage tracking via continuous integration. 3 | # It is not an integral part of the project and I'd rather remove it if I could help it. 4 | 5 | language: objective-c 6 | osx_image: xcode7.2 7 | script: xcrun xcodebuild -project RedBlackTree.xcodeproj -scheme Mac test 8 | after_success: bash <(curl -s https://codecov.io/bash) 9 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015–2016 Károly Lőrentey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Package.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-15. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import PackageDescription 10 | 11 | let package = Package( 12 | name: "RedBlackTree", 13 | dependencies: [] 14 | ) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Red-Black Trees in Swift 2 | 3 | [![Swift 2.1](https://img.shields.io/badge/Swift-2.1-blue.svg)](https://developer.apple.com/swift/) 4 | [![License](https://img.shields.io/badge/licence-MIT-blue.svg)](https://github.com/lorentey/RedBlackTree/blob/master/LICENCE.md) 5 | 6 | [![Build Status](https://travis-ci.org/lorentey/RedBlackTree.svg?branch=master)](https://travis-ci.org/lorentey/RedBlackTree) 7 | [![Code Coverage](https://codecov.io/github/lorentey/RedBlackTree/coverage.svg?branch=master)](https://codecov.io/github/lorentey/RedBlackTree?branch=master) 8 | 9 | This project provides an implementation of [red-black trees][wiki] in pure Swift as structs with value semantics. 10 | The nodes are stored in bulk inside a single flat `Array`, with array indexes serving as pointers. 11 | 12 | [wiki]: https://en.wikipedia.org/wiki/Red–black_tree 13 | 14 | `RedBlackTree` supports value-based lookup, positional lookup, or a combination of both, depending on how 15 | you configure its key type, `RedBlackKey`. For example, you can create a single red-black tree that supports 16 | lookup based on either a key stored in each element, the position of the element, or a weighted position. 17 | 18 | I created this package to make a set of ordered collection types in Swift. 19 | However, benchmarking showed that the performance of red-black trees isn't great, 20 | and I chose to base my collection types on b-trees instead. 21 | If you're in need of a fast ordered collection type, be sure 22 | check out my [BTree](https://github.com/lorentey/BTree) project before settling on red-black trees. 23 | -------------------------------------------------------------------------------- /RedBlackTree.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | BB54D0BA1C1F0357008A462B /* RedBlackTree.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB54D0AF1C1F0357008A462B /* RedBlackTree.framework */; }; 11 | BB8FE9BA1C7C8E0F0051268F /* RedBlackTree.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB8FE9B01C7C8E0E0051268F /* RedBlackTree.framework */; }; 12 | BB8FE9E31C7C9B420051268F /* RedBlackKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9DE1C7C9B420051268F /* RedBlackKey.swift */; }; 13 | BB8FE9E41C7C9B420051268F /* RedBlackTree descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9DF1C7C9B420051268F /* RedBlackTree descriptions.swift */; }; 14 | BB8FE9E51C7C9B420051268F /* RedBlackTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9E01C7C9B420051268F /* RedBlackTree.swift */; }; 15 | BB8FE9E71C7C9B420051268F /* Summary.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9E21C7C9B420051268F /* Summary.swift */; }; 16 | BB8FE9E81C7C9B4C0051268F /* RedBlackKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9DE1C7C9B420051268F /* RedBlackKey.swift */; }; 17 | BB8FE9E91C7C9B4C0051268F /* RedBlackTree descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9DF1C7C9B420051268F /* RedBlackTree descriptions.swift */; }; 18 | BB8FE9EA1C7C9B4C0051268F /* RedBlackTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9E01C7C9B420051268F /* RedBlackTree.swift */; }; 19 | BB8FE9EC1C7C9B4C0051268F /* Summary.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9E21C7C9B420051268F /* Summary.swift */; }; 20 | BB8FE9ED1C7C9B4D0051268F /* RedBlackKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9DE1C7C9B420051268F /* RedBlackKey.swift */; }; 21 | BB8FE9EE1C7C9B4D0051268F /* RedBlackTree descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9DF1C7C9B420051268F /* RedBlackTree descriptions.swift */; }; 22 | BB8FE9EF1C7C9B4D0051268F /* RedBlackTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9E01C7C9B420051268F /* RedBlackTree.swift */; }; 23 | BB8FE9F11C7C9B4D0051268F /* Summary.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9E21C7C9B420051268F /* Summary.swift */; }; 24 | BB8FE9F21C7C9B4D0051268F /* RedBlackKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9DE1C7C9B420051268F /* RedBlackKey.swift */; }; 25 | BB8FE9F31C7C9B4D0051268F /* RedBlackTree descriptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9DF1C7C9B420051268F /* RedBlackTree descriptions.swift */; }; 26 | BB8FE9F41C7C9B4D0051268F /* RedBlackTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9E01C7C9B420051268F /* RedBlackTree.swift */; }; 27 | BB8FE9F61C7C9B4D0051268F /* Summary.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9E21C7C9B420051268F /* Summary.swift */; }; 28 | BB8FE9FC1C7C9C010051268F /* RedBlackTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9F71C7C9C010051268F /* RedBlackTreeTests.swift */; }; 29 | BB8FE9FD1C7C9C010051268F /* RedBlackTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9F71C7C9C010051268F /* RedBlackTreeTests.swift */; }; 30 | BB8FE9FE1C7C9C010051268F /* RedBlackTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9F71C7C9C010051268F /* RedBlackTreeTests.swift */; }; 31 | BB8FE9FF1C7C9C010051268F /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9F81C7C9C010051268F /* TestUtils.swift */; }; 32 | BB8FEA001C7C9C010051268F /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9F81C7C9C010051268F /* TestUtils.swift */; }; 33 | BB8FEA011C7C9C010051268F /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9F81C7C9C010051268F /* TestUtils.swift */; }; 34 | BB8FEA051C7C9C010051268F /* TreeChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9FA1C7C9C010051268F /* TreeChecker.swift */; }; 35 | BB8FEA061C7C9C010051268F /* TreeChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9FA1C7C9C010051268F /* TreeChecker.swift */; }; 36 | BB8FEA071C7C9C010051268F /* TreeChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9FA1C7C9C010051268F /* TreeChecker.swift */; }; 37 | BB8FEA081C7C9C010051268F /* TreeDumper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9FB1C7C9C010051268F /* TreeDumper.swift */; }; 38 | BB8FEA091C7C9C010051268F /* TreeDumper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9FB1C7C9C010051268F /* TreeDumper.swift */; }; 39 | BB8FEA0A1C7C9C010051268F /* TreeDumper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FE9FB1C7C9C010051268F /* TreeDumper.swift */; }; 40 | BB8FEA151C7C9C5E0051268F /* Permutations.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA101C7C9C5E0051268F /* Permutations.swift */; }; 41 | BB8FEA161C7C9C5E0051268F /* Permutations.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA101C7C9C5E0051268F /* Permutations.swift */; }; 42 | BB8FEA171C7C9C5E0051268F /* Permutations.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA101C7C9C5E0051268F /* Permutations.swift */; }; 43 | BB8FEA181C7C9C5E0051268F /* PermutationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA111C7C9C5E0051268F /* PermutationTests.swift */; }; 44 | BB8FEA191C7C9C5E0051268F /* PermutationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA111C7C9C5E0051268F /* PermutationTests.swift */; }; 45 | BB8FEA1A1C7C9C5E0051268F /* PermutationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA111C7C9C5E0051268F /* PermutationTests.swift */; }; 46 | BB8FEA1B1C7C9C5E0051268F /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA121C7C9C5E0051268F /* Random.swift */; }; 47 | BB8FEA1C1C7C9C5E0051268F /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA121C7C9C5E0051268F /* Random.swift */; }; 48 | BB8FEA1D1C7C9C5E0051268F /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA121C7C9C5E0051268F /* Random.swift */; }; 49 | BB8FEA1E1C7C9C5E0051268F /* String Manipulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA131C7C9C5E0051268F /* String Manipulation.swift */; }; 50 | BB8FEA1F1C7C9C5E0051268F /* String Manipulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA131C7C9C5E0051268F /* String Manipulation.swift */; }; 51 | BB8FEA201C7C9C5E0051268F /* String Manipulation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA131C7C9C5E0051268F /* String Manipulation.swift */; }; 52 | BB8FEA211C7C9C5E0051268F /* XCTest extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA141C7C9C5E0051268F /* XCTest extensions.swift */; }; 53 | BB8FEA221C7C9C5E0051268F /* XCTest extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA141C7C9C5E0051268F /* XCTest extensions.swift */; }; 54 | BB8FEA231C7C9C5E0051268F /* XCTest extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB8FEA141C7C9C5E0051268F /* XCTest extensions.swift */; }; 55 | BB9AE4511C1FCE0B002C5D63 /* RedBlackTree.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB9AE43B1C1FCC35002C5D63 /* RedBlackTree.framework */; }; 56 | /* End PBXBuildFile section */ 57 | 58 | /* Begin PBXContainerItemProxy section */ 59 | BB54D0BB1C1F0357008A462B /* PBXContainerItemProxy */ = { 60 | isa = PBXContainerItemProxy; 61 | containerPortal = BB54D0A61C1F0357008A462B /* Project object */; 62 | proxyType = 1; 63 | remoteGlobalIDString = BB54D0AE1C1F0357008A462B; 64 | remoteInfo = TreeCollections; 65 | }; 66 | BB8FE9BB1C7C8E0F0051268F /* PBXContainerItemProxy */ = { 67 | isa = PBXContainerItemProxy; 68 | containerPortal = BB54D0A61C1F0357008A462B /* Project object */; 69 | proxyType = 1; 70 | remoteGlobalIDString = BB8FE9AF1C7C8E0E0051268F; 71 | remoteInfo = "BTree-tvOS"; 72 | }; 73 | BB9AE4521C1FCE0B002C5D63 /* PBXContainerItemProxy */ = { 74 | isa = PBXContainerItemProxy; 75 | containerPortal = BB54D0A61C1F0357008A462B /* Project object */; 76 | proxyType = 1; 77 | remoteGlobalIDString = BB9AE43A1C1FCC35002C5D63; 78 | remoteInfo = "OS X"; 79 | }; 80 | /* End PBXContainerItemProxy section */ 81 | 82 | /* Begin PBXFileReference section */ 83 | BB0D75D91C1F045C00538080 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; lineEnding = 0; path = README.md; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.markdown; }; 84 | BB54D0AF1C1F0357008A462B /* RedBlackTree.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RedBlackTree.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 85 | BB54D0B41C1F0357008A462B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 86 | BB54D0B91C1F0357008A462B /* RedBlackTreeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RedBlackTreeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 87 | BB54D0C01C1F0357008A462B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 88 | BB8FE9931C7C8BB70051268F /* version.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = version.xcconfig; sourceTree = ""; }; 89 | BB8FE9991C7C8CDC0051268F /* RedBlackTree.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RedBlackTree.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 90 | BB8FE9B01C7C8E0E0051268F /* RedBlackTree.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RedBlackTree.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 91 | BB8FE9B91C7C8E0E0051268F /* RedBlackTreeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RedBlackTreeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 92 | BB8FE9DE1C7C9B420051268F /* RedBlackKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedBlackKey.swift; sourceTree = ""; }; 93 | BB8FE9DF1C7C9B420051268F /* RedBlackTree descriptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RedBlackTree descriptions.swift"; sourceTree = ""; }; 94 | BB8FE9E01C7C9B420051268F /* RedBlackTree.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedBlackTree.swift; sourceTree = ""; }; 95 | BB8FE9E21C7C9B420051268F /* Summary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Summary.swift; sourceTree = ""; }; 96 | BB8FE9F71C7C9C010051268F /* RedBlackTreeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedBlackTreeTests.swift; sourceTree = ""; }; 97 | BB8FE9F81C7C9C010051268F /* TestUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestUtils.swift; sourceTree = ""; }; 98 | BB8FE9FA1C7C9C010051268F /* TreeChecker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TreeChecker.swift; sourceTree = ""; }; 99 | BB8FE9FB1C7C9C010051268F /* TreeDumper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TreeDumper.swift; sourceTree = ""; }; 100 | BB8FEA101C7C9C5E0051268F /* Permutations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Permutations.swift; sourceTree = ""; }; 101 | BB8FEA111C7C9C5E0051268F /* PermutationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PermutationTests.swift; sourceTree = ""; }; 102 | BB8FEA121C7C9C5E0051268F /* Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = ""; }; 103 | BB8FEA131C7C9C5E0051268F /* String Manipulation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String Manipulation.swift"; sourceTree = ""; }; 104 | BB8FEA141C7C9C5E0051268F /* XCTest extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCTest extensions.swift"; sourceTree = ""; }; 105 | BB9AE43B1C1FCC35002C5D63 /* RedBlackTree.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RedBlackTree.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 106 | BB9AE44C1C1FCE0B002C5D63 /* RedBlackTreeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RedBlackTreeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 107 | BB9AE47D1C207303002C5D63 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 108 | BBB224EF1C7BA09B0049FEF6 /* LICENCE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENCE.md; sourceTree = ""; }; 109 | BBBA30481C4946660019830F /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = ""; }; 110 | /* End PBXFileReference section */ 111 | 112 | /* Begin PBXFrameworksBuildPhase section */ 113 | BB54D0AB1C1F0357008A462B /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | ); 118 | runOnlyForDeploymentPostprocessing = 0; 119 | }; 120 | BB54D0B61C1F0357008A462B /* Frameworks */ = { 121 | isa = PBXFrameworksBuildPhase; 122 | buildActionMask = 2147483647; 123 | files = ( 124 | BB54D0BA1C1F0357008A462B /* RedBlackTree.framework in Frameworks */, 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | BB8FE9951C7C8CDC0051268F /* Frameworks */ = { 129 | isa = PBXFrameworksBuildPhase; 130 | buildActionMask = 2147483647; 131 | files = ( 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | BB8FE9AC1C7C8E0E0051268F /* Frameworks */ = { 136 | isa = PBXFrameworksBuildPhase; 137 | buildActionMask = 2147483647; 138 | files = ( 139 | ); 140 | runOnlyForDeploymentPostprocessing = 0; 141 | }; 142 | BB8FE9B61C7C8E0E0051268F /* Frameworks */ = { 143 | isa = PBXFrameworksBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | BB8FE9BA1C7C8E0F0051268F /* RedBlackTree.framework in Frameworks */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | BB9AE4371C1FCC35002C5D63 /* Frameworks */ = { 151 | isa = PBXFrameworksBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | ); 155 | runOnlyForDeploymentPostprocessing = 0; 156 | }; 157 | BB9AE4491C1FCE0B002C5D63 /* Frameworks */ = { 158 | isa = PBXFrameworksBuildPhase; 159 | buildActionMask = 2147483647; 160 | files = ( 161 | BB9AE4511C1FCE0B002C5D63 /* RedBlackTree.framework in Frameworks */, 162 | ); 163 | runOnlyForDeploymentPostprocessing = 0; 164 | }; 165 | /* End PBXFrameworksBuildPhase section */ 166 | 167 | /* Begin PBXGroup section */ 168 | BB54D0A51C1F0357008A462B = { 169 | isa = PBXGroup; 170 | children = ( 171 | BB0D75D91C1F045C00538080 /* README.md */, 172 | BBB224EF1C7BA09B0049FEF6 /* LICENCE.md */, 173 | BBBA30481C4946660019830F /* .travis.yml */, 174 | BB9AE47D1C207303002C5D63 /* Package.swift */, 175 | BB8FE9931C7C8BB70051268F /* version.xcconfig */, 176 | BB54D0B11C1F0357008A462B /* Sources */, 177 | BB54D0BD1C1F0357008A462B /* Tests */, 178 | BB54D0B01C1F0357008A462B /* Products */, 179 | ); 180 | sourceTree = ""; 181 | }; 182 | BB54D0B01C1F0357008A462B /* Products */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | BB54D0AF1C1F0357008A462B /* RedBlackTree.framework */, 186 | BB54D0B91C1F0357008A462B /* RedBlackTreeTests.xctest */, 187 | BB9AE43B1C1FCC35002C5D63 /* RedBlackTree.framework */, 188 | BB9AE44C1C1FCE0B002C5D63 /* RedBlackTreeTests.xctest */, 189 | BB8FE9991C7C8CDC0051268F /* RedBlackTree.framework */, 190 | BB8FE9B01C7C8E0E0051268F /* RedBlackTree.framework */, 191 | BB8FE9B91C7C8E0E0051268F /* RedBlackTreeTests.xctest */, 192 | ); 193 | name = Products; 194 | sourceTree = ""; 195 | }; 196 | BB54D0B11C1F0357008A462B /* Sources */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | BB8FE9E21C7C9B420051268F /* Summary.swift */, 200 | BB8FE9DE1C7C9B420051268F /* RedBlackKey.swift */, 201 | BB8FE9E01C7C9B420051268F /* RedBlackTree.swift */, 202 | BB8FE9DF1C7C9B420051268F /* RedBlackTree descriptions.swift */, 203 | BB54D0B41C1F0357008A462B /* Info.plist */, 204 | ); 205 | path = Sources; 206 | sourceTree = ""; 207 | }; 208 | BB54D0BD1C1F0357008A462B /* Tests */ = { 209 | isa = PBXGroup; 210 | children = ( 211 | BB54D0C01C1F0357008A462B /* Info.plist */, 212 | BB8FE9F71C7C9C010051268F /* RedBlackTreeTests.swift */, 213 | BB8FE9F81C7C9C010051268F /* TestUtils.swift */, 214 | BB8FE9FA1C7C9C010051268F /* TreeChecker.swift */, 215 | BB8FE9FB1C7C9C010051268F /* TreeDumper.swift */, 216 | BB8FEA0F1C7C9C5E0051268F /* Helpers */, 217 | ); 218 | path = Tests; 219 | sourceTree = ""; 220 | }; 221 | BB8FEA0F1C7C9C5E0051268F /* Helpers */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | BB8FEA101C7C9C5E0051268F /* Permutations.swift */, 225 | BB8FEA111C7C9C5E0051268F /* PermutationTests.swift */, 226 | BB8FEA121C7C9C5E0051268F /* Random.swift */, 227 | BB8FEA131C7C9C5E0051268F /* String Manipulation.swift */, 228 | BB8FEA141C7C9C5E0051268F /* XCTest extensions.swift */, 229 | ); 230 | path = Helpers; 231 | sourceTree = ""; 232 | }; 233 | /* End PBXGroup section */ 234 | 235 | /* Begin PBXHeadersBuildPhase section */ 236 | BB54D0AC1C1F0357008A462B /* Headers */ = { 237 | isa = PBXHeadersBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | ); 241 | runOnlyForDeploymentPostprocessing = 0; 242 | }; 243 | BB8FE9961C7C8CDC0051268F /* Headers */ = { 244 | isa = PBXHeadersBuildPhase; 245 | buildActionMask = 2147483647; 246 | files = ( 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | }; 250 | BB8FE9AD1C7C8E0E0051268F /* Headers */ = { 251 | isa = PBXHeadersBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | BB9AE4381C1FCC35002C5D63 /* Headers */ = { 258 | isa = PBXHeadersBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | /* End PBXHeadersBuildPhase section */ 265 | 266 | /* Begin PBXNativeTarget section */ 267 | BB54D0AE1C1F0357008A462B /* iOS */ = { 268 | isa = PBXNativeTarget; 269 | buildConfigurationList = BB54D0C31C1F0357008A462B /* Build configuration list for PBXNativeTarget "iOS" */; 270 | buildPhases = ( 271 | BB54D0AA1C1F0357008A462B /* Sources */, 272 | BB54D0AB1C1F0357008A462B /* Frameworks */, 273 | BB54D0AC1C1F0357008A462B /* Headers */, 274 | BB54D0AD1C1F0357008A462B /* Resources */, 275 | ); 276 | buildRules = ( 277 | ); 278 | dependencies = ( 279 | ); 280 | name = iOS; 281 | productName = TreeCollections; 282 | productReference = BB54D0AF1C1F0357008A462B /* RedBlackTree.framework */; 283 | productType = "com.apple.product-type.framework"; 284 | }; 285 | BB54D0B81C1F0357008A462B /* Tests-iOS */ = { 286 | isa = PBXNativeTarget; 287 | buildConfigurationList = BB54D0C61C1F0357008A462B /* Build configuration list for PBXNativeTarget "Tests-iOS" */; 288 | buildPhases = ( 289 | BB54D0B51C1F0357008A462B /* Sources */, 290 | BB54D0B61C1F0357008A462B /* Frameworks */, 291 | BB54D0B71C1F0357008A462B /* Resources */, 292 | ); 293 | buildRules = ( 294 | ); 295 | dependencies = ( 296 | BB54D0BC1C1F0357008A462B /* PBXTargetDependency */, 297 | ); 298 | name = "Tests-iOS"; 299 | productName = TreeCollectionsTests; 300 | productReference = BB54D0B91C1F0357008A462B /* RedBlackTreeTests.xctest */; 301 | productType = "com.apple.product-type.bundle.unit-test"; 302 | }; 303 | BB8FE9981C7C8CDC0051268F /* watchOS */ = { 304 | isa = PBXNativeTarget; 305 | buildConfigurationList = BB8FE99E1C7C8CDC0051268F /* Build configuration list for PBXNativeTarget "watchOS" */; 306 | buildPhases = ( 307 | BB8FE9941C7C8CDC0051268F /* Sources */, 308 | BB8FE9951C7C8CDC0051268F /* Frameworks */, 309 | BB8FE9961C7C8CDC0051268F /* Headers */, 310 | BB8FE9971C7C8CDC0051268F /* Resources */, 311 | ); 312 | buildRules = ( 313 | ); 314 | dependencies = ( 315 | ); 316 | name = watchOS; 317 | productName = BTree; 318 | productReference = BB8FE9991C7C8CDC0051268F /* RedBlackTree.framework */; 319 | productType = "com.apple.product-type.framework"; 320 | }; 321 | BB8FE9AF1C7C8E0E0051268F /* tvOS */ = { 322 | isa = PBXNativeTarget; 323 | buildConfigurationList = BB8FE9C11C7C8E0F0051268F /* Build configuration list for PBXNativeTarget "tvOS" */; 324 | buildPhases = ( 325 | BB8FE9AB1C7C8E0E0051268F /* Sources */, 326 | BB8FE9AC1C7C8E0E0051268F /* Frameworks */, 327 | BB8FE9AD1C7C8E0E0051268F /* Headers */, 328 | BB8FE9AE1C7C8E0E0051268F /* Resources */, 329 | ); 330 | buildRules = ( 331 | ); 332 | dependencies = ( 333 | ); 334 | name = tvOS; 335 | productName = "BTree-tvOS"; 336 | productReference = BB8FE9B01C7C8E0E0051268F /* RedBlackTree.framework */; 337 | productType = "com.apple.product-type.framework"; 338 | }; 339 | BB8FE9B81C7C8E0E0051268F /* Tests-tvOS */ = { 340 | isa = PBXNativeTarget; 341 | buildConfigurationList = BB8FE9C61C7C8E0F0051268F /* Build configuration list for PBXNativeTarget "Tests-tvOS" */; 342 | buildPhases = ( 343 | BB8FE9B51C7C8E0E0051268F /* Sources */, 344 | BB8FE9B61C7C8E0E0051268F /* Frameworks */, 345 | BB8FE9B71C7C8E0E0051268F /* Resources */, 346 | ); 347 | buildRules = ( 348 | ); 349 | dependencies = ( 350 | BB8FE9BC1C7C8E0F0051268F /* PBXTargetDependency */, 351 | ); 352 | name = "Tests-tvOS"; 353 | productName = "BTree-tvOSTests"; 354 | productReference = BB8FE9B91C7C8E0E0051268F /* RedBlackTreeTests.xctest */; 355 | productType = "com.apple.product-type.bundle.unit-test"; 356 | }; 357 | BB9AE43A1C1FCC35002C5D63 /* Mac */ = { 358 | isa = PBXNativeTarget; 359 | buildConfigurationList = BB9AE4401C1FCC35002C5D63 /* Build configuration list for PBXNativeTarget "Mac" */; 360 | buildPhases = ( 361 | BB9AE4361C1FCC35002C5D63 /* Sources */, 362 | BB9AE4371C1FCC35002C5D63 /* Frameworks */, 363 | BB9AE4381C1FCC35002C5D63 /* Headers */, 364 | BB9AE4391C1FCC35002C5D63 /* Resources */, 365 | ); 366 | buildRules = ( 367 | ); 368 | dependencies = ( 369 | ); 370 | name = Mac; 371 | productName = "TreeCollections-OS X"; 372 | productReference = BB9AE43B1C1FCC35002C5D63 /* RedBlackTree.framework */; 373 | productType = "com.apple.product-type.framework"; 374 | }; 375 | BB9AE44B1C1FCE0B002C5D63 /* Tests-Mac */ = { 376 | isa = PBXNativeTarget; 377 | buildConfigurationList = BB9AE4541C1FCE0B002C5D63 /* Build configuration list for PBXNativeTarget "Tests-Mac" */; 378 | buildPhases = ( 379 | BB9AE4481C1FCE0B002C5D63 /* Sources */, 380 | BB9AE4491C1FCE0B002C5D63 /* Frameworks */, 381 | BB9AE44A1C1FCE0B002C5D63 /* Resources */, 382 | ); 383 | buildRules = ( 384 | ); 385 | dependencies = ( 386 | BB9AE4531C1FCE0B002C5D63 /* PBXTargetDependency */, 387 | ); 388 | name = "Tests-Mac"; 389 | productName = TreeCollectionTests; 390 | productReference = BB9AE44C1C1FCE0B002C5D63 /* RedBlackTreeTests.xctest */; 391 | productType = "com.apple.product-type.bundle.unit-test"; 392 | }; 393 | /* End PBXNativeTarget section */ 394 | 395 | /* Begin PBXProject section */ 396 | BB54D0A61C1F0357008A462B /* Project object */ = { 397 | isa = PBXProject; 398 | attributes = { 399 | LastSwiftUpdateCheck = 0720; 400 | LastUpgradeCheck = 0720; 401 | ORGANIZATIONNAME = "Károly Lőrentey"; 402 | TargetAttributes = { 403 | BB54D0AE1C1F0357008A462B = { 404 | CreatedOnToolsVersion = 7.2; 405 | }; 406 | BB54D0B81C1F0357008A462B = { 407 | CreatedOnToolsVersion = 7.2; 408 | }; 409 | BB8FE9981C7C8CDC0051268F = { 410 | CreatedOnToolsVersion = 7.2.1; 411 | }; 412 | BB8FE9AF1C7C8E0E0051268F = { 413 | CreatedOnToolsVersion = 7.2.1; 414 | }; 415 | BB8FE9B81C7C8E0E0051268F = { 416 | CreatedOnToolsVersion = 7.2.1; 417 | }; 418 | BB9AE43A1C1FCC35002C5D63 = { 419 | CreatedOnToolsVersion = 7.2; 420 | }; 421 | BB9AE44B1C1FCE0B002C5D63 = { 422 | CreatedOnToolsVersion = 7.2; 423 | }; 424 | }; 425 | }; 426 | buildConfigurationList = BB54D0A91C1F0357008A462B /* Build configuration list for PBXProject "RedBlackTree" */; 427 | compatibilityVersion = "Xcode 3.2"; 428 | developmentRegion = English; 429 | hasScannedForEncodings = 0; 430 | knownRegions = ( 431 | en, 432 | Base, 433 | ); 434 | mainGroup = BB54D0A51C1F0357008A462B; 435 | productRefGroup = BB54D0B01C1F0357008A462B /* Products */; 436 | projectDirPath = ""; 437 | projectRoot = ""; 438 | targets = ( 439 | BB54D0AE1C1F0357008A462B /* iOS */, 440 | BB9AE43A1C1FCC35002C5D63 /* Mac */, 441 | BB8FE9981C7C8CDC0051268F /* watchOS */, 442 | BB8FE9AF1C7C8E0E0051268F /* tvOS */, 443 | BB54D0B81C1F0357008A462B /* Tests-iOS */, 444 | BB9AE44B1C1FCE0B002C5D63 /* Tests-Mac */, 445 | BB8FE9B81C7C8E0E0051268F /* Tests-tvOS */, 446 | ); 447 | }; 448 | /* End PBXProject section */ 449 | 450 | /* Begin PBXResourcesBuildPhase section */ 451 | BB54D0AD1C1F0357008A462B /* Resources */ = { 452 | isa = PBXResourcesBuildPhase; 453 | buildActionMask = 2147483647; 454 | files = ( 455 | ); 456 | runOnlyForDeploymentPostprocessing = 0; 457 | }; 458 | BB54D0B71C1F0357008A462B /* Resources */ = { 459 | isa = PBXResourcesBuildPhase; 460 | buildActionMask = 2147483647; 461 | files = ( 462 | ); 463 | runOnlyForDeploymentPostprocessing = 0; 464 | }; 465 | BB8FE9971C7C8CDC0051268F /* Resources */ = { 466 | isa = PBXResourcesBuildPhase; 467 | buildActionMask = 2147483647; 468 | files = ( 469 | ); 470 | runOnlyForDeploymentPostprocessing = 0; 471 | }; 472 | BB8FE9AE1C7C8E0E0051268F /* Resources */ = { 473 | isa = PBXResourcesBuildPhase; 474 | buildActionMask = 2147483647; 475 | files = ( 476 | ); 477 | runOnlyForDeploymentPostprocessing = 0; 478 | }; 479 | BB8FE9B71C7C8E0E0051268F /* Resources */ = { 480 | isa = PBXResourcesBuildPhase; 481 | buildActionMask = 2147483647; 482 | files = ( 483 | ); 484 | runOnlyForDeploymentPostprocessing = 0; 485 | }; 486 | BB9AE4391C1FCC35002C5D63 /* Resources */ = { 487 | isa = PBXResourcesBuildPhase; 488 | buildActionMask = 2147483647; 489 | files = ( 490 | ); 491 | runOnlyForDeploymentPostprocessing = 0; 492 | }; 493 | BB9AE44A1C1FCE0B002C5D63 /* Resources */ = { 494 | isa = PBXResourcesBuildPhase; 495 | buildActionMask = 2147483647; 496 | files = ( 497 | ); 498 | runOnlyForDeploymentPostprocessing = 0; 499 | }; 500 | /* End PBXResourcesBuildPhase section */ 501 | 502 | /* Begin PBXSourcesBuildPhase section */ 503 | BB54D0AA1C1F0357008A462B /* Sources */ = { 504 | isa = PBXSourcesBuildPhase; 505 | buildActionMask = 2147483647; 506 | files = ( 507 | BB8FE9E71C7C9B420051268F /* Summary.swift in Sources */, 508 | BB8FE9E31C7C9B420051268F /* RedBlackKey.swift in Sources */, 509 | BB8FE9E41C7C9B420051268F /* RedBlackTree descriptions.swift in Sources */, 510 | BB8FE9E51C7C9B420051268F /* RedBlackTree.swift in Sources */, 511 | ); 512 | runOnlyForDeploymentPostprocessing = 0; 513 | }; 514 | BB54D0B51C1F0357008A462B /* Sources */ = { 515 | isa = PBXSourcesBuildPhase; 516 | buildActionMask = 2147483647; 517 | files = ( 518 | BB8FEA181C7C9C5E0051268F /* PermutationTests.swift in Sources */, 519 | BB8FEA211C7C9C5E0051268F /* XCTest extensions.swift in Sources */, 520 | BB8FEA051C7C9C010051268F /* TreeChecker.swift in Sources */, 521 | BB8FEA081C7C9C010051268F /* TreeDumper.swift in Sources */, 522 | BB8FE9FC1C7C9C010051268F /* RedBlackTreeTests.swift in Sources */, 523 | BB8FEA1E1C7C9C5E0051268F /* String Manipulation.swift in Sources */, 524 | BB8FE9FF1C7C9C010051268F /* TestUtils.swift in Sources */, 525 | BB8FEA151C7C9C5E0051268F /* Permutations.swift in Sources */, 526 | BB8FEA1B1C7C9C5E0051268F /* Random.swift in Sources */, 527 | ); 528 | runOnlyForDeploymentPostprocessing = 0; 529 | }; 530 | BB8FE9941C7C8CDC0051268F /* Sources */ = { 531 | isa = PBXSourcesBuildPhase; 532 | buildActionMask = 2147483647; 533 | files = ( 534 | BB8FE9F11C7C9B4D0051268F /* Summary.swift in Sources */, 535 | BB8FE9ED1C7C9B4D0051268F /* RedBlackKey.swift in Sources */, 536 | BB8FE9EE1C7C9B4D0051268F /* RedBlackTree descriptions.swift in Sources */, 537 | BB8FE9EF1C7C9B4D0051268F /* RedBlackTree.swift in Sources */, 538 | ); 539 | runOnlyForDeploymentPostprocessing = 0; 540 | }; 541 | BB8FE9AB1C7C8E0E0051268F /* Sources */ = { 542 | isa = PBXSourcesBuildPhase; 543 | buildActionMask = 2147483647; 544 | files = ( 545 | BB8FE9EC1C7C9B4C0051268F /* Summary.swift in Sources */, 546 | BB8FE9E81C7C9B4C0051268F /* RedBlackKey.swift in Sources */, 547 | BB8FE9E91C7C9B4C0051268F /* RedBlackTree descriptions.swift in Sources */, 548 | BB8FE9EA1C7C9B4C0051268F /* RedBlackTree.swift in Sources */, 549 | ); 550 | runOnlyForDeploymentPostprocessing = 0; 551 | }; 552 | BB8FE9B51C7C8E0E0051268F /* Sources */ = { 553 | isa = PBXSourcesBuildPhase; 554 | buildActionMask = 2147483647; 555 | files = ( 556 | BB8FEA1A1C7C9C5E0051268F /* PermutationTests.swift in Sources */, 557 | BB8FEA231C7C9C5E0051268F /* XCTest extensions.swift in Sources */, 558 | BB8FEA071C7C9C010051268F /* TreeChecker.swift in Sources */, 559 | BB8FEA0A1C7C9C010051268F /* TreeDumper.swift in Sources */, 560 | BB8FE9FE1C7C9C010051268F /* RedBlackTreeTests.swift in Sources */, 561 | BB8FEA201C7C9C5E0051268F /* String Manipulation.swift in Sources */, 562 | BB8FEA011C7C9C010051268F /* TestUtils.swift in Sources */, 563 | BB8FEA171C7C9C5E0051268F /* Permutations.swift in Sources */, 564 | BB8FEA1D1C7C9C5E0051268F /* Random.swift in Sources */, 565 | ); 566 | runOnlyForDeploymentPostprocessing = 0; 567 | }; 568 | BB9AE4361C1FCC35002C5D63 /* Sources */ = { 569 | isa = PBXSourcesBuildPhase; 570 | buildActionMask = 2147483647; 571 | files = ( 572 | BB8FE9F61C7C9B4D0051268F /* Summary.swift in Sources */, 573 | BB8FE9F21C7C9B4D0051268F /* RedBlackKey.swift in Sources */, 574 | BB8FE9F31C7C9B4D0051268F /* RedBlackTree descriptions.swift in Sources */, 575 | BB8FE9F41C7C9B4D0051268F /* RedBlackTree.swift in Sources */, 576 | ); 577 | runOnlyForDeploymentPostprocessing = 0; 578 | }; 579 | BB9AE4481C1FCE0B002C5D63 /* Sources */ = { 580 | isa = PBXSourcesBuildPhase; 581 | buildActionMask = 2147483647; 582 | files = ( 583 | BB8FEA191C7C9C5E0051268F /* PermutationTests.swift in Sources */, 584 | BB8FEA221C7C9C5E0051268F /* XCTest extensions.swift in Sources */, 585 | BB8FEA061C7C9C010051268F /* TreeChecker.swift in Sources */, 586 | BB8FEA091C7C9C010051268F /* TreeDumper.swift in Sources */, 587 | BB8FE9FD1C7C9C010051268F /* RedBlackTreeTests.swift in Sources */, 588 | BB8FEA1F1C7C9C5E0051268F /* String Manipulation.swift in Sources */, 589 | BB8FEA001C7C9C010051268F /* TestUtils.swift in Sources */, 590 | BB8FEA161C7C9C5E0051268F /* Permutations.swift in Sources */, 591 | BB8FEA1C1C7C9C5E0051268F /* Random.swift in Sources */, 592 | ); 593 | runOnlyForDeploymentPostprocessing = 0; 594 | }; 595 | /* End PBXSourcesBuildPhase section */ 596 | 597 | /* Begin PBXTargetDependency section */ 598 | BB54D0BC1C1F0357008A462B /* PBXTargetDependency */ = { 599 | isa = PBXTargetDependency; 600 | target = BB54D0AE1C1F0357008A462B /* iOS */; 601 | targetProxy = BB54D0BB1C1F0357008A462B /* PBXContainerItemProxy */; 602 | }; 603 | BB8FE9BC1C7C8E0F0051268F /* PBXTargetDependency */ = { 604 | isa = PBXTargetDependency; 605 | target = BB8FE9AF1C7C8E0E0051268F /* tvOS */; 606 | targetProxy = BB8FE9BB1C7C8E0F0051268F /* PBXContainerItemProxy */; 607 | }; 608 | BB9AE4531C1FCE0B002C5D63 /* PBXTargetDependency */ = { 609 | isa = PBXTargetDependency; 610 | target = BB9AE43A1C1FCC35002C5D63 /* Mac */; 611 | targetProxy = BB9AE4521C1FCE0B002C5D63 /* PBXContainerItemProxy */; 612 | }; 613 | /* End PBXTargetDependency section */ 614 | 615 | /* Begin XCBuildConfiguration section */ 616 | BB3EAADD1C2707E90028E5A7 /* Unchecked */ = { 617 | isa = XCBuildConfiguration; 618 | baseConfigurationReference = BB8FE9931C7C8BB70051268F /* version.xcconfig */; 619 | buildSettings = { 620 | ALWAYS_SEARCH_USER_PATHS = NO; 621 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 622 | CLANG_CXX_LIBRARY = "libc++"; 623 | CLANG_ENABLE_MODULES = YES; 624 | CLANG_ENABLE_OBJC_ARC = YES; 625 | CLANG_WARN_BOOL_CONVERSION = YES; 626 | CLANG_WARN_CONSTANT_CONVERSION = YES; 627 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 628 | CLANG_WARN_EMPTY_BODY = YES; 629 | CLANG_WARN_ENUM_CONVERSION = YES; 630 | CLANG_WARN_INT_CONVERSION = YES; 631 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 632 | CLANG_WARN_UNREACHABLE_CODE = YES; 633 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 634 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 635 | COPY_PHASE_STRIP = NO; 636 | CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)"; 637 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 638 | DYLIB_COMPATIBILITY_VERSION = "$(BUILD_NUMBER)"; 639 | DYLIB_CURRENT_VERSION = "$(BUILD_NUMBER)"; 640 | ENABLE_NS_ASSERTIONS = NO; 641 | ENABLE_STRICT_OBJC_MSGSEND = YES; 642 | ENABLE_TESTABILITY = YES; 643 | GCC_C_LANGUAGE_STANDARD = gnu99; 644 | GCC_NO_COMMON_BLOCKS = YES; 645 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 646 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 647 | GCC_WARN_UNDECLARED_SELECTOR = YES; 648 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 649 | GCC_WARN_UNUSED_FUNCTION = YES; 650 | GCC_WARN_UNUSED_VARIABLE = YES; 651 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 652 | MACOSX_DEPLOYMENT_TARGET = 10.9; 653 | MTL_ENABLE_DEBUG_INFO = NO; 654 | SWIFT_DISABLE_SAFETY_CHECKS = YES; 655 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 656 | TARGETED_DEVICE_FAMILY = "1,2"; 657 | TVOS_DEPLOYMENT_TARGET = 9.0; 658 | VALIDATE_PRODUCT = YES; 659 | VERSIONING_SYSTEM = ""; 660 | VERSION_INFO_PREFIX = ""; 661 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 662 | }; 663 | name = Unchecked; 664 | }; 665 | BB3EAADE1C2707E90028E5A7 /* Unchecked */ = { 666 | isa = XCBuildConfiguration; 667 | buildSettings = { 668 | APPLICATION_EXTENSION_API_ONLY = YES; 669 | CLANG_ENABLE_MODULES = YES; 670 | DEFINES_MODULE = YES; 671 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 672 | INFOPLIST_FILE = Sources/Info.plist; 673 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 674 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 675 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.iOS; 676 | PRODUCT_NAME = RedBlackTree; 677 | SDKROOT = iphoneos; 678 | SKIP_INSTALL = YES; 679 | }; 680 | name = Unchecked; 681 | }; 682 | BB3EAADF1C2707E90028E5A7 /* Unchecked */ = { 683 | isa = XCBuildConfiguration; 684 | buildSettings = { 685 | APPLICATION_EXTENSION_API_ONLY = YES; 686 | CODE_SIGN_IDENTITY = "-"; 687 | COMBINE_HIDPI_IMAGES = YES; 688 | DEFINES_MODULE = YES; 689 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 690 | FRAMEWORK_VERSION = A; 691 | INFOPLIST_FILE = Sources/Info.plist; 692 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 693 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 694 | MACOSX_DEPLOYMENT_TARGET = 10.11; 695 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Mac; 696 | PRODUCT_NAME = RedBlackTree; 697 | SDKROOT = macosx; 698 | SKIP_INSTALL = YES; 699 | }; 700 | name = Unchecked; 701 | }; 702 | BB3EAAE01C2707E90028E5A7 /* Unchecked */ = { 703 | isa = XCBuildConfiguration; 704 | buildSettings = { 705 | INFOPLIST_FILE = Tests/Info.plist; 706 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 707 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.iOS; 708 | PRODUCT_NAME = RedBlackTreeTests; 709 | SDKROOT = iphoneos; 710 | }; 711 | name = Unchecked; 712 | }; 713 | BB3EAAE11C2707E90028E5A7 /* Unchecked */ = { 714 | isa = XCBuildConfiguration; 715 | buildSettings = { 716 | CODE_SIGN_IDENTITY = "-"; 717 | COMBINE_HIDPI_IMAGES = YES; 718 | INFOPLIST_FILE = Tests/Info.plist; 719 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 720 | MACOSX_DEPLOYMENT_TARGET = 10.11; 721 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.Mac; 722 | PRODUCT_NAME = RedBlackTreeTests; 723 | SDKROOT = macosx; 724 | }; 725 | name = Unchecked; 726 | }; 727 | BB54D0C11C1F0357008A462B /* Debug */ = { 728 | isa = XCBuildConfiguration; 729 | baseConfigurationReference = BB8FE9931C7C8BB70051268F /* version.xcconfig */; 730 | buildSettings = { 731 | ALWAYS_SEARCH_USER_PATHS = NO; 732 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 733 | CLANG_CXX_LIBRARY = "libc++"; 734 | CLANG_ENABLE_MODULES = YES; 735 | CLANG_ENABLE_OBJC_ARC = YES; 736 | CLANG_WARN_BOOL_CONVERSION = YES; 737 | CLANG_WARN_CONSTANT_CONVERSION = YES; 738 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 739 | CLANG_WARN_EMPTY_BODY = YES; 740 | CLANG_WARN_ENUM_CONVERSION = YES; 741 | CLANG_WARN_INT_CONVERSION = YES; 742 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 743 | CLANG_WARN_UNREACHABLE_CODE = YES; 744 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 745 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 746 | COPY_PHASE_STRIP = NO; 747 | CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)"; 748 | DEBUG_INFORMATION_FORMAT = dwarf; 749 | DYLIB_COMPATIBILITY_VERSION = "$(BUILD_NUMBER)"; 750 | DYLIB_CURRENT_VERSION = "$(BUILD_NUMBER)"; 751 | ENABLE_STRICT_OBJC_MSGSEND = YES; 752 | ENABLE_TESTABILITY = YES; 753 | GCC_C_LANGUAGE_STANDARD = gnu99; 754 | GCC_DYNAMIC_NO_PIC = NO; 755 | GCC_NO_COMMON_BLOCKS = YES; 756 | GCC_OPTIMIZATION_LEVEL = 0; 757 | GCC_PREPROCESSOR_DEFINITIONS = ( 758 | "DEBUG=1", 759 | "$(inherited)", 760 | ); 761 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 762 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 763 | GCC_WARN_UNDECLARED_SELECTOR = YES; 764 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 765 | GCC_WARN_UNUSED_FUNCTION = YES; 766 | GCC_WARN_UNUSED_VARIABLE = YES; 767 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 768 | MACOSX_DEPLOYMENT_TARGET = 10.9; 769 | MTL_ENABLE_DEBUG_INFO = YES; 770 | ONLY_ACTIVE_ARCH = YES; 771 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 772 | TARGETED_DEVICE_FAMILY = "1,2"; 773 | TVOS_DEPLOYMENT_TARGET = 9.0; 774 | VERSIONING_SYSTEM = ""; 775 | VERSION_INFO_PREFIX = ""; 776 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 777 | }; 778 | name = Debug; 779 | }; 780 | BB54D0C21C1F0357008A462B /* Release */ = { 781 | isa = XCBuildConfiguration; 782 | baseConfigurationReference = BB8FE9931C7C8BB70051268F /* version.xcconfig */; 783 | buildSettings = { 784 | ALWAYS_SEARCH_USER_PATHS = NO; 785 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 786 | CLANG_CXX_LIBRARY = "libc++"; 787 | CLANG_ENABLE_MODULES = YES; 788 | CLANG_ENABLE_OBJC_ARC = YES; 789 | CLANG_WARN_BOOL_CONVERSION = YES; 790 | CLANG_WARN_CONSTANT_CONVERSION = YES; 791 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 792 | CLANG_WARN_EMPTY_BODY = YES; 793 | CLANG_WARN_ENUM_CONVERSION = YES; 794 | CLANG_WARN_INT_CONVERSION = YES; 795 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 796 | CLANG_WARN_UNREACHABLE_CODE = YES; 797 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 798 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 799 | COPY_PHASE_STRIP = NO; 800 | CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)"; 801 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 802 | DYLIB_COMPATIBILITY_VERSION = "$(BUILD_NUMBER)"; 803 | DYLIB_CURRENT_VERSION = "$(BUILD_NUMBER)"; 804 | ENABLE_NS_ASSERTIONS = NO; 805 | ENABLE_STRICT_OBJC_MSGSEND = YES; 806 | GCC_C_LANGUAGE_STANDARD = gnu99; 807 | GCC_NO_COMMON_BLOCKS = YES; 808 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 809 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 810 | GCC_WARN_UNDECLARED_SELECTOR = YES; 811 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 812 | GCC_WARN_UNUSED_FUNCTION = YES; 813 | GCC_WARN_UNUSED_VARIABLE = YES; 814 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 815 | MACOSX_DEPLOYMENT_TARGET = 10.9; 816 | MTL_ENABLE_DEBUG_INFO = NO; 817 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 818 | TARGETED_DEVICE_FAMILY = "1,2"; 819 | TVOS_DEPLOYMENT_TARGET = 9.0; 820 | VALIDATE_PRODUCT = YES; 821 | VERSIONING_SYSTEM = ""; 822 | VERSION_INFO_PREFIX = ""; 823 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 824 | }; 825 | name = Release; 826 | }; 827 | BB54D0C41C1F0357008A462B /* Debug */ = { 828 | isa = XCBuildConfiguration; 829 | buildSettings = { 830 | APPLICATION_EXTENSION_API_ONLY = YES; 831 | CLANG_ENABLE_MODULES = YES; 832 | DEFINES_MODULE = YES; 833 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 834 | INFOPLIST_FILE = Sources/Info.plist; 835 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 836 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 837 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.iOS; 838 | PRODUCT_NAME = RedBlackTree; 839 | SDKROOT = iphoneos; 840 | SKIP_INSTALL = YES; 841 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 842 | }; 843 | name = Debug; 844 | }; 845 | BB54D0C51C1F0357008A462B /* Release */ = { 846 | isa = XCBuildConfiguration; 847 | buildSettings = { 848 | APPLICATION_EXTENSION_API_ONLY = YES; 849 | CLANG_ENABLE_MODULES = YES; 850 | DEFINES_MODULE = YES; 851 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 852 | INFOPLIST_FILE = Sources/Info.plist; 853 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 854 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 855 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.iOS; 856 | PRODUCT_NAME = RedBlackTree; 857 | SDKROOT = iphoneos; 858 | SKIP_INSTALL = YES; 859 | }; 860 | name = Release; 861 | }; 862 | BB54D0C71C1F0357008A462B /* Debug */ = { 863 | isa = XCBuildConfiguration; 864 | buildSettings = { 865 | INFOPLIST_FILE = Tests/Info.plist; 866 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 867 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.iOS; 868 | PRODUCT_NAME = RedBlackTreeTests; 869 | SDKROOT = iphoneos; 870 | }; 871 | name = Debug; 872 | }; 873 | BB54D0C81C1F0357008A462B /* Release */ = { 874 | isa = XCBuildConfiguration; 875 | buildSettings = { 876 | INFOPLIST_FILE = Tests/Info.plist; 877 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 878 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.iOS; 879 | PRODUCT_NAME = RedBlackTreeTests; 880 | SDKROOT = iphoneos; 881 | }; 882 | name = Release; 883 | }; 884 | BB8FE99F1C7C8CDC0051268F /* Debug */ = { 885 | isa = XCBuildConfiguration; 886 | buildSettings = { 887 | APPLICATION_EXTENSION_API_ONLY = YES; 888 | DEFINES_MODULE = YES; 889 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 890 | INFOPLIST_FILE = Sources/Info.plist; 891 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 892 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 893 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.watchOS; 894 | PRODUCT_NAME = RedBlackTree; 895 | SDKROOT = watchos; 896 | SKIP_INSTALL = YES; 897 | TARGETED_DEVICE_FAMILY = 4; 898 | }; 899 | name = Debug; 900 | }; 901 | BB8FE9A01C7C8CDC0051268F /* Release */ = { 902 | isa = XCBuildConfiguration; 903 | buildSettings = { 904 | APPLICATION_EXTENSION_API_ONLY = YES; 905 | DEFINES_MODULE = YES; 906 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 907 | INFOPLIST_FILE = Sources/Info.plist; 908 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 909 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 910 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.watchOS; 911 | PRODUCT_NAME = RedBlackTree; 912 | SDKROOT = watchos; 913 | SKIP_INSTALL = YES; 914 | TARGETED_DEVICE_FAMILY = 4; 915 | }; 916 | name = Release; 917 | }; 918 | BB8FE9A11C7C8CDC0051268F /* Profile */ = { 919 | isa = XCBuildConfiguration; 920 | buildSettings = { 921 | APPLICATION_EXTENSION_API_ONLY = YES; 922 | DEFINES_MODULE = YES; 923 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 924 | INFOPLIST_FILE = Sources/Info.plist; 925 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 926 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 927 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.watchOS; 928 | PRODUCT_NAME = RedBlackTree; 929 | SDKROOT = watchos; 930 | SKIP_INSTALL = YES; 931 | TARGETED_DEVICE_FAMILY = 4; 932 | }; 933 | name = Profile; 934 | }; 935 | BB8FE9A21C7C8CDC0051268F /* Unchecked */ = { 936 | isa = XCBuildConfiguration; 937 | buildSettings = { 938 | APPLICATION_EXTENSION_API_ONLY = YES; 939 | DEFINES_MODULE = YES; 940 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 941 | INFOPLIST_FILE = Sources/Info.plist; 942 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 943 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 944 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.watchOS; 945 | PRODUCT_NAME = RedBlackTree; 946 | SDKROOT = watchos; 947 | SKIP_INSTALL = YES; 948 | TARGETED_DEVICE_FAMILY = 4; 949 | }; 950 | name = Unchecked; 951 | }; 952 | BB8FE9C21C7C8E0F0051268F /* Debug */ = { 953 | isa = XCBuildConfiguration; 954 | buildSettings = { 955 | CURRENT_PROJECT_VERSION = 1; 956 | DEFINES_MODULE = YES; 957 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 958 | INFOPLIST_FILE = Sources/Info.plist; 959 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 960 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 961 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.tvOS; 962 | PRODUCT_NAME = RedBlackTree; 963 | SDKROOT = appletvos; 964 | SKIP_INSTALL = YES; 965 | TARGETED_DEVICE_FAMILY = 3; 966 | VERSIONING_SYSTEM = "apple-generic"; 967 | }; 968 | name = Debug; 969 | }; 970 | BB8FE9C31C7C8E0F0051268F /* Release */ = { 971 | isa = XCBuildConfiguration; 972 | buildSettings = { 973 | CURRENT_PROJECT_VERSION = 1; 974 | DEFINES_MODULE = YES; 975 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 976 | INFOPLIST_FILE = Sources/Info.plist; 977 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 978 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 979 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.tvOS; 980 | PRODUCT_NAME = RedBlackTree; 981 | SDKROOT = appletvos; 982 | SKIP_INSTALL = YES; 983 | TARGETED_DEVICE_FAMILY = 3; 984 | VERSIONING_SYSTEM = "apple-generic"; 985 | }; 986 | name = Release; 987 | }; 988 | BB8FE9C41C7C8E0F0051268F /* Profile */ = { 989 | isa = XCBuildConfiguration; 990 | buildSettings = { 991 | CURRENT_PROJECT_VERSION = 1; 992 | DEFINES_MODULE = YES; 993 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 994 | INFOPLIST_FILE = Sources/Info.plist; 995 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 996 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 997 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.tvOS; 998 | PRODUCT_NAME = RedBlackTree; 999 | SDKROOT = appletvos; 1000 | SKIP_INSTALL = YES; 1001 | TARGETED_DEVICE_FAMILY = 3; 1002 | VERSIONING_SYSTEM = "apple-generic"; 1003 | }; 1004 | name = Profile; 1005 | }; 1006 | BB8FE9C51C7C8E0F0051268F /* Unchecked */ = { 1007 | isa = XCBuildConfiguration; 1008 | buildSettings = { 1009 | CURRENT_PROJECT_VERSION = 1; 1010 | DEFINES_MODULE = YES; 1011 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1012 | INFOPLIST_FILE = Sources/Info.plist; 1013 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1014 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1015 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.tvOS; 1016 | PRODUCT_NAME = RedBlackTree; 1017 | SDKROOT = appletvos; 1018 | SKIP_INSTALL = YES; 1019 | TARGETED_DEVICE_FAMILY = 3; 1020 | VERSIONING_SYSTEM = "apple-generic"; 1021 | }; 1022 | name = Unchecked; 1023 | }; 1024 | BB8FE9C71C7C8E0F0051268F /* Debug */ = { 1025 | isa = XCBuildConfiguration; 1026 | buildSettings = { 1027 | INFOPLIST_FILE = Tests/Info.plist; 1028 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1029 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.tvOS; 1030 | PRODUCT_NAME = RedBlackTreeTests; 1031 | SDKROOT = appletvos; 1032 | }; 1033 | name = Debug; 1034 | }; 1035 | BB8FE9C81C7C8E0F0051268F /* Release */ = { 1036 | isa = XCBuildConfiguration; 1037 | buildSettings = { 1038 | INFOPLIST_FILE = Tests/Info.plist; 1039 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1040 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.tvOS; 1041 | PRODUCT_NAME = RedBlackTreeTests; 1042 | SDKROOT = appletvos; 1043 | }; 1044 | name = Release; 1045 | }; 1046 | BB8FE9C91C7C8E0F0051268F /* Profile */ = { 1047 | isa = XCBuildConfiguration; 1048 | buildSettings = { 1049 | INFOPLIST_FILE = Tests/Info.plist; 1050 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1051 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.tvOS; 1052 | PRODUCT_NAME = RedBlackTreeTests; 1053 | SDKROOT = appletvos; 1054 | }; 1055 | name = Profile; 1056 | }; 1057 | BB8FE9CA1C7C8E0F0051268F /* Unchecked */ = { 1058 | isa = XCBuildConfiguration; 1059 | buildSettings = { 1060 | INFOPLIST_FILE = Tests/Info.plist; 1061 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1062 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.tvOS; 1063 | PRODUCT_NAME = RedBlackTreeTests; 1064 | SDKROOT = appletvos; 1065 | }; 1066 | name = Unchecked; 1067 | }; 1068 | BB9AE4411C1FCC35002C5D63 /* Debug */ = { 1069 | isa = XCBuildConfiguration; 1070 | buildSettings = { 1071 | APPLICATION_EXTENSION_API_ONLY = YES; 1072 | CODE_SIGN_IDENTITY = "-"; 1073 | COMBINE_HIDPI_IMAGES = YES; 1074 | DEFINES_MODULE = YES; 1075 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1076 | FRAMEWORK_VERSION = A; 1077 | INFOPLIST_FILE = Sources/Info.plist; 1078 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1079 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 1080 | MACOSX_DEPLOYMENT_TARGET = 10.11; 1081 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Mac; 1082 | PRODUCT_NAME = RedBlackTree; 1083 | SDKROOT = macosx; 1084 | SKIP_INSTALL = YES; 1085 | }; 1086 | name = Debug; 1087 | }; 1088 | BB9AE4421C1FCC35002C5D63 /* Release */ = { 1089 | isa = XCBuildConfiguration; 1090 | buildSettings = { 1091 | APPLICATION_EXTENSION_API_ONLY = YES; 1092 | CODE_SIGN_IDENTITY = "-"; 1093 | COMBINE_HIDPI_IMAGES = YES; 1094 | DEFINES_MODULE = YES; 1095 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1096 | FRAMEWORK_VERSION = A; 1097 | INFOPLIST_FILE = Sources/Info.plist; 1098 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1099 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 1100 | MACOSX_DEPLOYMENT_TARGET = 10.11; 1101 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Mac; 1102 | PRODUCT_NAME = RedBlackTree; 1103 | SDKROOT = macosx; 1104 | SKIP_INSTALL = YES; 1105 | }; 1106 | name = Release; 1107 | }; 1108 | BB9AE4551C1FCE0B002C5D63 /* Debug */ = { 1109 | isa = XCBuildConfiguration; 1110 | buildSettings = { 1111 | CODE_SIGN_IDENTITY = "-"; 1112 | COMBINE_HIDPI_IMAGES = YES; 1113 | INFOPLIST_FILE = Tests/Info.plist; 1114 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1115 | MACOSX_DEPLOYMENT_TARGET = 10.11; 1116 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.Mac; 1117 | PRODUCT_NAME = RedBlackTreeTests; 1118 | SDKROOT = macosx; 1119 | }; 1120 | name = Debug; 1121 | }; 1122 | BB9AE4561C1FCE0B002C5D63 /* Release */ = { 1123 | isa = XCBuildConfiguration; 1124 | buildSettings = { 1125 | CODE_SIGN_IDENTITY = "-"; 1126 | COMBINE_HIDPI_IMAGES = YES; 1127 | INFOPLIST_FILE = Tests/Info.plist; 1128 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1129 | MACOSX_DEPLOYMENT_TARGET = 10.11; 1130 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.Mac; 1131 | PRODUCT_NAME = RedBlackTreeTests; 1132 | SDKROOT = macosx; 1133 | }; 1134 | name = Release; 1135 | }; 1136 | BB9AE4691C203A23002C5D63 /* Profile */ = { 1137 | isa = XCBuildConfiguration; 1138 | baseConfigurationReference = BB8FE9931C7C8BB70051268F /* version.xcconfig */; 1139 | buildSettings = { 1140 | ALWAYS_SEARCH_USER_PATHS = NO; 1141 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 1142 | CLANG_CXX_LIBRARY = "libc++"; 1143 | CLANG_ENABLE_MODULES = YES; 1144 | CLANG_ENABLE_OBJC_ARC = YES; 1145 | CLANG_WARN_BOOL_CONVERSION = YES; 1146 | CLANG_WARN_CONSTANT_CONVERSION = YES; 1147 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 1148 | CLANG_WARN_EMPTY_BODY = YES; 1149 | CLANG_WARN_ENUM_CONVERSION = YES; 1150 | CLANG_WARN_INT_CONVERSION = YES; 1151 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 1152 | CLANG_WARN_UNREACHABLE_CODE = YES; 1153 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 1154 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 1155 | COPY_PHASE_STRIP = NO; 1156 | CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)"; 1157 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 1158 | DYLIB_COMPATIBILITY_VERSION = "$(BUILD_NUMBER)"; 1159 | DYLIB_CURRENT_VERSION = "$(BUILD_NUMBER)"; 1160 | ENABLE_NS_ASSERTIONS = NO; 1161 | ENABLE_STRICT_OBJC_MSGSEND = YES; 1162 | ENABLE_TESTABILITY = YES; 1163 | GCC_C_LANGUAGE_STANDARD = gnu99; 1164 | GCC_NO_COMMON_BLOCKS = YES; 1165 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 1166 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 1167 | GCC_WARN_UNDECLARED_SELECTOR = YES; 1168 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 1169 | GCC_WARN_UNUSED_FUNCTION = YES; 1170 | GCC_WARN_UNUSED_VARIABLE = YES; 1171 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 1172 | MACOSX_DEPLOYMENT_TARGET = 10.9; 1173 | MTL_ENABLE_DEBUG_INFO = NO; 1174 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 1175 | TARGETED_DEVICE_FAMILY = "1,2"; 1176 | TVOS_DEPLOYMENT_TARGET = 9.0; 1177 | VALIDATE_PRODUCT = YES; 1178 | VERSIONING_SYSTEM = ""; 1179 | VERSION_INFO_PREFIX = ""; 1180 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 1181 | }; 1182 | name = Profile; 1183 | }; 1184 | BB9AE46A1C203A23002C5D63 /* Profile */ = { 1185 | isa = XCBuildConfiguration; 1186 | buildSettings = { 1187 | APPLICATION_EXTENSION_API_ONLY = YES; 1188 | CLANG_ENABLE_MODULES = YES; 1189 | DEFINES_MODULE = YES; 1190 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1191 | INFOPLIST_FILE = Sources/Info.plist; 1192 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1193 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1194 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.iOS; 1195 | PRODUCT_NAME = RedBlackTree; 1196 | SDKROOT = iphoneos; 1197 | SKIP_INSTALL = YES; 1198 | }; 1199 | name = Profile; 1200 | }; 1201 | BB9AE46B1C203A23002C5D63 /* Profile */ = { 1202 | isa = XCBuildConfiguration; 1203 | buildSettings = { 1204 | APPLICATION_EXTENSION_API_ONLY = YES; 1205 | CODE_SIGN_IDENTITY = "-"; 1206 | COMBINE_HIDPI_IMAGES = YES; 1207 | DEFINES_MODULE = YES; 1208 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1209 | FRAMEWORK_VERSION = A; 1210 | INFOPLIST_FILE = Sources/Info.plist; 1211 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1212 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 1213 | MACOSX_DEPLOYMENT_TARGET = 10.11; 1214 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Mac; 1215 | PRODUCT_NAME = RedBlackTree; 1216 | SDKROOT = macosx; 1217 | SKIP_INSTALL = YES; 1218 | }; 1219 | name = Profile; 1220 | }; 1221 | BB9AE46C1C203A23002C5D63 /* Profile */ = { 1222 | isa = XCBuildConfiguration; 1223 | buildSettings = { 1224 | INFOPLIST_FILE = Tests/Info.plist; 1225 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1226 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.iOS; 1227 | PRODUCT_NAME = RedBlackTreeTests; 1228 | SDKROOT = iphoneos; 1229 | }; 1230 | name = Profile; 1231 | }; 1232 | BB9AE46D1C203A23002C5D63 /* Profile */ = { 1233 | isa = XCBuildConfiguration; 1234 | buildSettings = { 1235 | CODE_SIGN_IDENTITY = "-"; 1236 | COMBINE_HIDPI_IMAGES = YES; 1237 | INFOPLIST_FILE = Tests/Info.plist; 1238 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1239 | MACOSX_DEPLOYMENT_TARGET = 10.11; 1240 | PRODUCT_BUNDLE_IDENTIFIER = com.github.lorentey.RedBlackTree.Tests.Mac; 1241 | PRODUCT_NAME = RedBlackTreeTests; 1242 | SDKROOT = macosx; 1243 | }; 1244 | name = Profile; 1245 | }; 1246 | /* End XCBuildConfiguration section */ 1247 | 1248 | /* Begin XCConfigurationList section */ 1249 | BB54D0A91C1F0357008A462B /* Build configuration list for PBXProject "RedBlackTree" */ = { 1250 | isa = XCConfigurationList; 1251 | buildConfigurations = ( 1252 | BB54D0C11C1F0357008A462B /* Debug */, 1253 | BB54D0C21C1F0357008A462B /* Release */, 1254 | BB9AE4691C203A23002C5D63 /* Profile */, 1255 | BB3EAADD1C2707E90028E5A7 /* Unchecked */, 1256 | ); 1257 | defaultConfigurationIsVisible = 0; 1258 | defaultConfigurationName = Release; 1259 | }; 1260 | BB54D0C31C1F0357008A462B /* Build configuration list for PBXNativeTarget "iOS" */ = { 1261 | isa = XCConfigurationList; 1262 | buildConfigurations = ( 1263 | BB54D0C41C1F0357008A462B /* Debug */, 1264 | BB54D0C51C1F0357008A462B /* Release */, 1265 | BB9AE46A1C203A23002C5D63 /* Profile */, 1266 | BB3EAADE1C2707E90028E5A7 /* Unchecked */, 1267 | ); 1268 | defaultConfigurationIsVisible = 0; 1269 | defaultConfigurationName = Release; 1270 | }; 1271 | BB54D0C61C1F0357008A462B /* Build configuration list for PBXNativeTarget "Tests-iOS" */ = { 1272 | isa = XCConfigurationList; 1273 | buildConfigurations = ( 1274 | BB54D0C71C1F0357008A462B /* Debug */, 1275 | BB54D0C81C1F0357008A462B /* Release */, 1276 | BB9AE46C1C203A23002C5D63 /* Profile */, 1277 | BB3EAAE01C2707E90028E5A7 /* Unchecked */, 1278 | ); 1279 | defaultConfigurationIsVisible = 0; 1280 | defaultConfigurationName = Release; 1281 | }; 1282 | BB8FE99E1C7C8CDC0051268F /* Build configuration list for PBXNativeTarget "watchOS" */ = { 1283 | isa = XCConfigurationList; 1284 | buildConfigurations = ( 1285 | BB8FE99F1C7C8CDC0051268F /* Debug */, 1286 | BB8FE9A01C7C8CDC0051268F /* Release */, 1287 | BB8FE9A11C7C8CDC0051268F /* Profile */, 1288 | BB8FE9A21C7C8CDC0051268F /* Unchecked */, 1289 | ); 1290 | defaultConfigurationIsVisible = 0; 1291 | defaultConfigurationName = Release; 1292 | }; 1293 | BB8FE9C11C7C8E0F0051268F /* Build configuration list for PBXNativeTarget "tvOS" */ = { 1294 | isa = XCConfigurationList; 1295 | buildConfigurations = ( 1296 | BB8FE9C21C7C8E0F0051268F /* Debug */, 1297 | BB8FE9C31C7C8E0F0051268F /* Release */, 1298 | BB8FE9C41C7C8E0F0051268F /* Profile */, 1299 | BB8FE9C51C7C8E0F0051268F /* Unchecked */, 1300 | ); 1301 | defaultConfigurationIsVisible = 0; 1302 | defaultConfigurationName = Release; 1303 | }; 1304 | BB8FE9C61C7C8E0F0051268F /* Build configuration list for PBXNativeTarget "Tests-tvOS" */ = { 1305 | isa = XCConfigurationList; 1306 | buildConfigurations = ( 1307 | BB8FE9C71C7C8E0F0051268F /* Debug */, 1308 | BB8FE9C81C7C8E0F0051268F /* Release */, 1309 | BB8FE9C91C7C8E0F0051268F /* Profile */, 1310 | BB8FE9CA1C7C8E0F0051268F /* Unchecked */, 1311 | ); 1312 | defaultConfigurationIsVisible = 0; 1313 | defaultConfigurationName = Release; 1314 | }; 1315 | BB9AE4401C1FCC35002C5D63 /* Build configuration list for PBXNativeTarget "Mac" */ = { 1316 | isa = XCConfigurationList; 1317 | buildConfigurations = ( 1318 | BB9AE4411C1FCC35002C5D63 /* Debug */, 1319 | BB9AE4421C1FCC35002C5D63 /* Release */, 1320 | BB9AE46B1C203A23002C5D63 /* Profile */, 1321 | BB3EAADF1C2707E90028E5A7 /* Unchecked */, 1322 | ); 1323 | defaultConfigurationIsVisible = 0; 1324 | defaultConfigurationName = Release; 1325 | }; 1326 | BB9AE4541C1FCE0B002C5D63 /* Build configuration list for PBXNativeTarget "Tests-Mac" */ = { 1327 | isa = XCConfigurationList; 1328 | buildConfigurations = ( 1329 | BB9AE4551C1FCE0B002C5D63 /* Debug */, 1330 | BB9AE4561C1FCE0B002C5D63 /* Release */, 1331 | BB9AE46D1C203A23002C5D63 /* Profile */, 1332 | BB3EAAE11C2707E90028E5A7 /* Unchecked */, 1333 | ); 1334 | defaultConfigurationIsVisible = 0; 1335 | defaultConfigurationName = Release; 1336 | }; 1337 | /* End XCConfigurationList section */ 1338 | }; 1339 | rootObject = BB54D0A61C1F0357008A462B /* Project object */; 1340 | } 1341 | -------------------------------------------------------------------------------- /RedBlackTree.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RedBlackTree.xcodeproj/xcshareddata/xcbaselines/BB9AE45C1C1FCEB3002C5D63.xcbaseline/38496774-E074-454C-A291-C4990C30544C.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | InsertionPerformanceTests 8 | 9 | testAppendingToList() 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 2.5114 15 | baselineIntegrationDisplayName 16 | 2015-12-17 04:28:16 17 | 18 | 19 | testAppendingToUnsortedArray() 20 | 21 | com.apple.XCTPerformanceMetric_WallClockTime 22 | 23 | baselineAverage 24 | 0.0018883 25 | baselineIntegrationDisplayName 26 | 2015-12-17 04:28:16 27 | 28 | 29 | testInsertingIntoMap() 30 | 31 | com.apple.XCTPerformanceMetric_WallClockTime 32 | 33 | baselineAverage 34 | 2.4649 35 | baselineIntegrationDisplayName 36 | 2015-12-17 04:28:16 37 | 38 | 39 | testInsertingIntoUnsortedDictionary() 40 | 41 | com.apple.XCTPerformanceMetric_WallClockTime 42 | 43 | baselineAverage 44 | 0.036603 45 | baselineIntegrationDisplayName 46 | 2015-12-17 04:28:16 47 | 48 | 49 | testInsertingToInlinedSortedArray() 50 | 51 | com.apple.XCTPerformanceMetric_WallClockTime 52 | 53 | baselineAverage 54 | 2.7839 55 | baselineIntegrationDisplayName 56 | 2015-12-17 04:28:16 57 | 58 | 59 | testInsertingToSortedArray() 60 | 61 | com.apple.XCTPerformanceMetric_WallClockTime 62 | 63 | baselineAverage 64 | 17.083 65 | baselineIntegrationDisplayName 66 | 2015-12-17 04:28:16 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /RedBlackTree.xcodeproj/xcshareddata/xcbaselines/BB9AE45C1C1FCEB3002C5D63.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | 38496774-E074-454C-A291-C4990C30544C 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 | MacBookPro10,1 23 | physicalCPUCoresPerPackage 24 | 4 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | x86_64 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /RedBlackTree.xcodeproj/xcshareddata/xcschemes/Mac.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /RedBlackTree.xcodeproj/xcshareddata/xcschemes/iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /RedBlackTree.xcodeproj/xcshareddata/xcschemes/tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /RedBlackTree.xcodeproj/xcshareddata/xcschemes/watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(VERSION_STRING) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015–2016 Károly Lőrentey. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/RedBlackKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedBlackKey.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-17. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol RedBlackKey: Comparable { 12 | typealias Summary: SummaryProtocol 13 | 14 | /// Returns a key that matches a node with `head` whose preceding nodes reduce into `summary`. 15 | /// - Complexity: Must be O(1). 16 | init(summary: Summary, head: Summary.Item) 17 | } 18 | 19 | public protocol RedBlackInsertionKey: RedBlackKey { 20 | /// The head value to store in nodes that are inserted for this key. 21 | /// - Requires: For all summaries `s`: `h == Key(summary: s, head: h).head` 22 | /// - Complexity: Must be O(1). 23 | var head: Summary.Item { get } 24 | } 25 | 26 | public struct StoredKey: RedBlackInsertionKey { 27 | public typealias Head = Summary.Item 28 | 29 | public let head: Head 30 | 31 | public init(_ head: Summary.Item) { 32 | self.head = head 33 | } 34 | public init(summary: Summary, head: Summary.Item) { 35 | self.init(head) 36 | } 37 | } 38 | public func == (a: StoredKey, b: StoredKey) -> Bool { 39 | return a.head == b.head 40 | } 41 | public func < (a: StoredKey, b: StoredKey) -> Bool { 42 | return a.head < b.head 43 | } 44 | extension StoredKey: CustomStringConvertible { 45 | public var description: String { return "\(self.head)" } 46 | } 47 | 48 | 49 | public struct PositionalKey: RedBlackInsertionKey { 50 | public typealias Summary = CountingSummary 51 | 52 | public let index: Int 53 | 54 | public init(_ index: Int) { 55 | self.index = index 56 | } 57 | public init(summary: Summary, head: Summary.Item) { 58 | self.index = summary.count 59 | } 60 | 61 | public var head: Void { return () } 62 | } 63 | 64 | public func ==(a: PositionalKey, b: PositionalKey) -> Bool { 65 | return a.index == b.index 66 | } 67 | public func <(a: PositionalKey, b: PositionalKey) -> Bool { 68 | return a.index < b.index 69 | } 70 | 71 | extension PositionalKey: CustomStringConvertible { 72 | public var description: String { return "\(self.index)" } 73 | } 74 | -------------------------------------------------------------------------------- /Sources/RedBlackTree descriptions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedBlackTree descriptions.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-19. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import Foundation 10 | 11 | extension RedBlackTree: CustomStringConvertible { 12 | public var description: String { 13 | return "RedBlackTree with \(count) nodes" 14 | } 15 | } -------------------------------------------------------------------------------- /Sources/RedBlackTree.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RedBlackTree.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-17. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct RedBlackHandle: Hashable { 12 | private let index: Int 13 | 14 | private init(_ index: Int) { 15 | self.index = index 16 | } 17 | 18 | public var hashValue: Int { return index.hashValue } 19 | } 20 | 21 | public func ==(a: RedBlackHandle, b: RedBlackHandle) -> Bool { 22 | return a.index == b.index 23 | } 24 | 25 | extension RedBlackHandle: CustomStringConvertible, CustomDebugStringConvertible { 26 | public var description: String { 27 | return "#\(self.index)" 28 | } 29 | public var debugDescription: String { 30 | return "#\(self.index)" 31 | } 32 | } 33 | 34 | internal enum Color { 35 | case Red 36 | case Black 37 | } 38 | 39 | private enum KeyMatchResult { 40 | case Before 41 | case Matching 42 | case After 43 | } 44 | 45 | /// A direction represents a choice between a left and right child in a binary tree. 46 | public enum RedBlackDirection { 47 | /// The left child. 48 | case Left 49 | /// The right child. 50 | case Right 51 | 52 | /// The opposite direction. 53 | var opposite: RedBlackDirection { 54 | switch self { 55 | case .Left: return .Right 56 | case .Right: return .Left 57 | } 58 | } 59 | } 60 | 61 | /// A slot in the binary tree represents a place into which you can put a node. 62 | /// The tree's root is one slot, and so is either child of another node. 63 | internal enum RedBlackSlot: Equatable { 64 | internal typealias Handle = RedBlackHandle 65 | 66 | /// A slot representing the place of the topmost node in the tree. 67 | case Root 68 | /// A slot representing the child towards a certain direction under a certain parent node in the tree. 69 | case Toward(RedBlackDirection, under: Handle) 70 | } 71 | internal func ==(a: RedBlackSlot, b: RedBlackSlot) -> Bool { 72 | return a == b 73 | } 74 | 75 | private struct _Handle { 76 | typealias Handle = RedBlackHandle 77 | 78 | var _index: UInt32 79 | 80 | init(_ handle: Handle?) { 81 | if let handle = handle { _index = UInt32(handle.index) } 82 | else { _index = UInt32.max } 83 | } 84 | 85 | var handle: Handle? { 86 | get { 87 | return (_index == UInt32.max ? nil : Handle(Int(_index))) 88 | } 89 | set(handle) { 90 | if let handle = handle { _index = UInt32(handle.index) } 91 | else { _index = UInt32.max } 92 | } 93 | } 94 | } 95 | 96 | 97 | internal struct RedBlackNode { 98 | typealias Handle = RedBlackHandle 99 | typealias Summary = Key.Summary 100 | typealias Head = Summary.Item 101 | 102 | private var _parent: _Handle 103 | private var _left: _Handle 104 | private var _right: _Handle 105 | private(set) var color: Color 106 | 107 | private(set) var head: Head 108 | private(set) var summary: Summary 109 | 110 | private(set) var payload: Payload 111 | 112 | 113 | private init(parent: Handle?, head: Head, payload: Payload) { 114 | self._parent = _Handle(parent) 115 | self._left = _Handle(nil) 116 | self._right = _Handle(nil) 117 | self.head = head 118 | self.summary = Summary(head) 119 | self.payload = payload 120 | self.color = .Red 121 | } 122 | 123 | internal subscript(direction: RedBlackDirection) -> Handle? { 124 | get { 125 | switch direction { 126 | case .Left: return left 127 | case .Right: return right 128 | } 129 | } 130 | mutating set(handle) { 131 | switch direction { 132 | case .Left: left = handle 133 | case .Right: right = handle 134 | } 135 | } 136 | } 137 | 138 | internal private(set) var parent: Handle? { 139 | get { return _parent.handle } 140 | set(h) { _parent.handle = h } 141 | } 142 | internal private(set) var left: Handle? { 143 | get { return _left.handle } 144 | set(h) { _left.handle = h } 145 | } 146 | internal private(set) var right: Handle? { 147 | get { return _right.handle } 148 | set(h) { _right.handle = h } 149 | } 150 | 151 | private mutating func replaceChild(old: Handle, with new: Handle?) { 152 | if left == old { 153 | left = new 154 | } 155 | else { 156 | assert(right == old) 157 | right = new 158 | } 159 | } 160 | } 161 | 162 | public struct RedBlackTree { 163 | //MARK: Type aliases 164 | 165 | public typealias Handle = RedBlackHandle 166 | public typealias Summary = InsertionKey.Summary 167 | public typealias Head = Summary.Item 168 | public typealias Element = (InsertionKey, Payload) 169 | 170 | internal typealias Node = RedBlackNode 171 | internal typealias Slot = RedBlackSlot 172 | 173 | //MARK: Stored properties 174 | 175 | internal private(set) var nodes: ContiguousArray 176 | 177 | /// The handle of the root node of the tree, or nil if the tree is empty. 178 | public private(set) var root: Handle? 179 | 180 | /// The handle of the leftmost node of the tree, or nil if the tree is empty. 181 | public private(set) var leftmost: Handle? 182 | 183 | /// The handle of the rightmost node of the tree, or nil if the tree is empty. 184 | public private(set) var rightmost: Handle? 185 | 186 | /// Initializes an empty tree. 187 | public init() { 188 | nodes = [] 189 | root = nil 190 | leftmost = nil 191 | rightmost = nil 192 | } 193 | } 194 | 195 | //MARK: Initializers 196 | 197 | public extension RedBlackTree { 198 | 199 | public init(_ elements: C) { 200 | self.init() 201 | self.reserveCapacity(Int(elements.count.toIntMax())) 202 | for (key, payload) in elements { 203 | self.insert(payload, forKey: key) 204 | } 205 | } 206 | 207 | public mutating func reserveCapacity(minimumCapacity: Int) { 208 | nodes.reserveCapacity(minimumCapacity) 209 | } 210 | } 211 | 212 | //MARK: Count of nodes 213 | 214 | public extension RedBlackTree { 215 | /// The number of nodes in the tree. 216 | public var count: Int { return nodes.count } 217 | public var isEmpty: Bool { return nodes.isEmpty } 218 | } 219 | 220 | //MARK: Looking up a handle. 221 | 222 | public extension RedBlackTree { 223 | 224 | /// Returns or updates the node at `handle`. 225 | /// - Complexity: O(1) 226 | internal private(set) subscript(handle: Handle) -> Node { 227 | get { 228 | return nodes[handle.index] 229 | } 230 | set(node) { 231 | nodes[handle.index] = node 232 | } 233 | } 234 | 235 | /// Returns the node at `handle`, or nil if `handle` is nil. 236 | /// - Complexity: O(1) 237 | internal subscript(handle: Handle?) -> Node? { 238 | guard let handle = handle else { return nil } 239 | return self[handle] as Node 240 | } 241 | 242 | /// Returns the payload of the node at `handle`. 243 | /// - Complexity: O(1) 244 | public func payloadAt(handle: Handle) -> Payload { 245 | return self[handle].payload 246 | } 247 | 248 | /// Returns the payload of the topmost node matching `key`, if any. 249 | /// - Complexity: O(log(`count`)) 250 | public func payloadOf(key: Key) -> Payload? { 251 | guard let handle = find(key) else { return nil } 252 | return self.payloadAt(handle) 253 | } 254 | 255 | /// Updates the payload of the node at `handle`. 256 | /// - Returns: The previous payload of the node. 257 | /// - Complexity: O(1) 258 | public mutating func setPayloadAt(handle: Handle, to payload: Payload) -> Payload { 259 | var node = self[handle] 260 | let old = node.payload 261 | node.payload = payload 262 | self[handle] = node 263 | return old 264 | } 265 | 266 | /// Returns the key of the node at `handle`. 267 | /// - Complexity: O(log(`count`)) if the summary is non-empty; O(1) otherwise. 268 | /// - Note: If you need to get the key for a range of nodes, and you have a non-empty summary, using a generator 269 | /// is faster than querying the keys of each node one by one. 270 | /// - SeeAlso: `generate`, `generateFrom` 271 | public func keyAt(handle: Handle) -> InsertionKey { 272 | let prefix = summaryBefore(handle) 273 | let node = self[handle] 274 | return InsertionKey(summary: prefix, head: node.head) 275 | } 276 | 277 | /// Returns a typle containing the key and payload of the node at `handle`. 278 | /// - Complexity: O(log(`count`)) if the summary is non-empty; O(1) otherwise. 279 | /// - Note: If you need to get the key for a range of nodes, and you have a non-empty summary, using a generator 280 | /// is faster than querying the keys of each node one by one. 281 | /// - SeeAlso: `generate`, `generateFrom` 282 | public func elementAt(handle: Handle) -> Element { 283 | let prefix = summaryBefore(handle) 284 | let node = self[handle] 285 | let key = InsertionKey(summary: prefix, head: node.head) 286 | return (key, node.payload) 287 | } 288 | 289 | /// Returns the head of the node at `handle`. 290 | /// - Complexity: O(1) 291 | public func headAt(handle: Handle) -> Head { 292 | return self[handle].head 293 | } 294 | 295 | /// Updates the head of the node at `handle`. 296 | /// 297 | /// It is only supported to change the head when a the new value does 298 | /// not affect the order of the nodes already in the tree. New keys of nodes before or equal to `handle` must match 299 | /// their previous ones, but keys of nodes above `handle` may be changed -- as long as the ordering stays constant. 300 | /// 301 | /// - Note: Being able to update the head is useful when the summary is a summation, 302 | /// like in a tree implementing a concatenation of arrays, where each array's handle range in the resulting 303 | /// collection is a count of elements in all arrays before it. Here, the head of node is the count of its 304 | /// payload array. When the count changes, handles after the modified array change too, but their ordering remains 305 | /// the same. Calling `setHead` is ~3 times faster than just removing and re-adding the node. 306 | /// 307 | /// - Requires: The key of the old node must match the new node. `compare(key(old, prefix), new, prefix) == .Match` 308 | /// 309 | /// - Warning: Changing the head to a value that changes the ordering of items will break ordering in the tree. 310 | /// In unoptimized builds, the implementation throws a fatal error if the above expression evaluates to false, 311 | /// but this is elided from optimized builds. You should know what you're doing. 312 | /// 313 | /// - Returns: The previous head of the node. 314 | /// 315 | /// - Complexity: O(log(`count`)) 316 | /// 317 | public mutating func setHeadAt(handle: Handle, to head: Head) -> Head { 318 | var node = self[handle] 319 | assert({ 320 | let prefix = summaryBefore(handle) // This is O(log(n)) -- which is why this is not in a precondition. 321 | let oldKey = InsertionKey(summary: prefix, head: node.head) 322 | let newKey = InsertionKey(summary: prefix, head: head) 323 | return oldKey == newKey 324 | }()) 325 | let old = node.head 326 | node.head = head 327 | self[handle] = node 328 | updateSummariesAtAndAbove(handle) 329 | return old 330 | } 331 | } 332 | 333 | //MARK: Inorder walk 334 | 335 | extension RedBlackTree { 336 | 337 | public func successor(handle: Handle) -> Handle? { 338 | return step(handle, toward: .Right) 339 | } 340 | 341 | public func predecessor(handle: Handle) -> Handle? { 342 | return step(handle, toward: .Left) 343 | } 344 | 345 | public func step(handle: Handle, toward direction: RedBlackDirection) -> Handle? { 346 | let node = self[handle] 347 | if let next = node[direction] { 348 | return furthestUnder(next, toward: direction.opposite) 349 | } 350 | 351 | var child = handle 352 | var parent = node.parent 353 | while let p = parent { 354 | let n = self[p] 355 | if n[direction] != child { return p } 356 | child = p 357 | parent = n.parent 358 | } 359 | return nil 360 | } 361 | 362 | public func leftmostUnder(handle: Handle) -> Handle { 363 | return furthestUnder(handle, toward: .Left) 364 | } 365 | 366 | public func rightmostUnder(handle: Handle) -> Handle { 367 | return furthestUnder(handle, toward: .Right) 368 | } 369 | 370 | public func furthestToward(direction: RedBlackDirection) -> Handle? { 371 | return (direction == .Left ? leftmost : rightmost) 372 | } 373 | 374 | public func furthestUnder(handle: Handle, toward direction: RedBlackDirection) -> Handle { 375 | var handle = handle 376 | while let next = self[handle][direction] { 377 | handle = next 378 | } 379 | return handle 380 | } 381 | } 382 | 383 | 384 | //MARK: Generating all items in the tree 385 | 386 | public struct RedBlackGenerator: GeneratorType { 387 | typealias Tree = RedBlackTree 388 | private let tree: Tree 389 | private var handle: Tree.Handle? 390 | private var summary: Tree.Summary 391 | 392 | public mutating func next() -> Tree.Element? { 393 | guard let handle = handle else { return nil } 394 | let node = tree[handle] 395 | let key = Key(summary: summary, head: node.head) 396 | summary += node.head 397 | self.handle = tree.successor(handle) 398 | return (key, node.payload) 399 | } 400 | } 401 | 402 | extension RedBlackTree: SequenceType { 403 | public typealias Generator = RedBlackGenerator 404 | 405 | /// Return a generator that provides an ordered list of all (key, payload) pairs that are currently in the tree. 406 | /// - Complexity: O(1) to get the generator; O(count) to retrieve all elements. 407 | public func generate() -> Generator { 408 | return RedBlackGenerator(tree: self, handle: leftmost, summary: Summary()) 409 | } 410 | 411 | /// Return a generator that provides an ordered list of (key, payload) pairs that are at or after `handle`. 412 | /// - Complexity: O(1) to get the generator; O(count) to retrieve all elements. 413 | public func generateFrom(handle: Handle) -> Generator { 414 | return RedBlackGenerator(tree: self, handle: handle, summary: Summary()) 415 | } 416 | } 417 | 418 | //MARK: Searching in the tree 419 | 420 | extension RedBlackTree { 421 | private func find(key: Key, @noescape step: (KeyMatchResult, Handle) -> KeyMatchResult) { 422 | var handle = self.root 423 | var summary = Summary() 424 | while let h = handle { 425 | let node = self[h] 426 | let s = summary + self[node.left]?.summary 427 | let k = Key(summary: s, head: node.head) 428 | let match: KeyMatchResult = (key < k ? .Before : key > k ? .After : .Matching) 429 | let next = step(match, h) 430 | switch next { 431 | case .Before: 432 | handle = node.left 433 | case .Matching: 434 | return 435 | case .After: 436 | summary = s + node.head 437 | handle = node.right 438 | } 439 | } 440 | } 441 | 442 | private func find(key: Key, winding: RedBlackDirection) -> (hit: Handle?, miss: Handle?) { 443 | var hit: Handle? = nil 444 | var miss: Handle? = nil 445 | var handle = self.root 446 | var summary = Summary() 447 | while let h = handle { 448 | let node = self[h] 449 | let s = summary + self[node.left]?.summary 450 | let k = Key(summary: s, head: node.head) 451 | if key < k { 452 | if winding == .Right { miss = h } 453 | handle = node.left 454 | } 455 | else if key > k { 456 | if winding == .Left { miss = h } 457 | summary = s + node.head 458 | handle = node.right 459 | } 460 | else { 461 | hit = h 462 | handle = (winding == .Left ? node.left : node.right) 463 | } 464 | } 465 | return (hit, miss) 466 | } 467 | 468 | /// Finds and returns the handle of a node which matches `key`, or nil if no such node exists. 469 | /// - Complexity: O(log(`count`)) 470 | public func find(key: Key) -> Handle? { 471 | // Topmost is the best, since it terminates on the first match. 472 | return topmostMatching(key) 473 | } 474 | 475 | /// Finds and returns the handle of the topmost node that matches `key`, or nil if no such node exists. 476 | /// - Complexity: O(log(`count`)) 477 | public func topmostMatching(key: Key) -> Handle? { 478 | var result: Handle? = nil 479 | find(key) { match, handle in 480 | if match == .Matching { result = handle } 481 | return match 482 | } 483 | return result 484 | } 485 | 486 | /// Finds and returns the handle of the rightmost node that sorts before `key`, or nil if no such node exists. 487 | /// - Complexity: O(log(`count`)) 488 | public func rightmostBefore(key: Key) -> Handle? { 489 | return find(key, winding: .Left).miss 490 | } 491 | 492 | /// Finds and returns the handle of the leftmost node that matches `key`, or nil if no such node exists. 493 | /// - Complexity: O(log(`count`)) 494 | public func leftmostMatching(key: Key) -> Handle? { 495 | return find(key, winding: .Left).hit 496 | } 497 | 498 | /// Finds and returns the handle of the rightmost node that matches `key`, or nil if no such node exists. 499 | /// - Complexity: O(log(`count`)) 500 | public func rightmostMatching(key: Key) -> Handle? { 501 | return find(key, winding: .Right).hit 502 | } 503 | 504 | /// Finds and returns the handle of the leftmost node that sorts after `key`, or nil if no such node exists. 505 | /// - Complexity: O(log(`count`)) 506 | public func leftmostAfter(key: Key) -> Handle? { 507 | return find(key, winding: .Right).miss 508 | } 509 | } 510 | 511 | //MARK: Managing the summary data 512 | 513 | extension RedBlackTree { 514 | /// Updates the summary cached at `handle`, assuming that the children have up-to-date data. 515 | /// - Complexity: O(1) - 3 lookups 516 | private mutating func updateSummaryAt(handle: Handle) -> Handle? { 517 | guard sizeof(Summary.self) > 0 else { return nil } 518 | var node = self[handle] 519 | node.summary = self[node.left]?.summary + node.head + self[node.right]?.summary 520 | self[handle] = node 521 | return node.parent 522 | } 523 | 524 | /// Updates the summary cached at `handle` and its ancestors, assuming that all other nodes have up-to-date data. 525 | /// - Complexity: O(log(`count`)) for nonempty summaries, O(1) when the summary is empty. 526 | private mutating func updateSummariesAtAndAbove(handle: Handle?) { 527 | guard sizeof(Summary.self) > 0 else { return } 528 | var handle: Handle? = handle 529 | while let h = handle { 530 | handle = self.updateSummaryAt(h) 531 | } 532 | } 533 | 534 | /// Returns the summary calculated over the sequence of all nodes below `handle`, including the top. 535 | /// - Complexity: O(1) 536 | public func summaryUnder(handle: Handle?) -> Summary { 537 | guard sizeof(Summary.self) > 0 else { return Summary() } 538 | guard let handle = handle else { return Summary() } 539 | return self[handle].summary 540 | } 541 | 542 | /// Returns the summary calculated over the sequence all nodes preceding `handle` in the tree. 543 | /// - Complexity: O(log(`count`) for nonempty summaries, O(1) when the summary is empty. 544 | public func summaryBefore(handle: Handle) -> Summary { 545 | guard sizeof(Summary.self) > 0 else { return Summary() } 546 | 547 | func summaryOfLeftSubtree(handle: Handle) -> Summary { 548 | return summaryUnder(self[handle].left) 549 | } 550 | 551 | var handle = handle 552 | var summary = summaryOfLeftSubtree(handle) 553 | while case .Toward(let direction, under: let parent) = slotOf(handle) { 554 | if direction == .Right { 555 | summary = summaryOfLeftSubtree(parent) + self[parent].head + summary 556 | } 557 | handle = parent 558 | } 559 | return summary 560 | } 561 | 562 | /// Returns the summary calculated over the sequence all nodes succeeding `handle` in the tree. 563 | /// - Complexity: O(log(`count`) for nonempty summaries, O(1) when the summary is empty. 564 | public func summaryAfter(handle: Handle) -> Summary { 565 | guard sizeof(Summary.self) > 0 else { return Summary() } 566 | 567 | func summaryOfRightSubtree(handle: Handle) -> Summary { 568 | return summaryUnder(self[handle].right) 569 | } 570 | 571 | var handle = handle 572 | var summary = summaryOfRightSubtree(handle) 573 | while case .Toward(let direction, under: let parent) = slotOf(handle) { 574 | if direction == .Left { 575 | summary = summary + self[parent].head + summaryOfRightSubtree(parent) 576 | } 577 | handle = parent 578 | } 579 | return summary 580 | } 581 | } 582 | 583 | 584 | //MARK: Color management 585 | 586 | extension RedBlackTree { 587 | /// Only non-nil nodes may be red. 588 | private func isRed(handle: Handle?) -> Bool { 589 | guard let handle = handle else { return false } 590 | return self[handle].color == .Red 591 | } 592 | /// Nil nodes are considered black. 593 | private func isBlack(handle: Handle?) -> Bool { 594 | guard let handle = handle else { return true } 595 | return self[handle].color == .Black 596 | } 597 | /// Only non-nil nodes may be set red. 598 | private mutating func setRed(handle: Handle) { 599 | self[handle].color = .Red 600 | } 601 | /// You can set a nil node black, but it's a noop. 602 | private mutating func setBlack(handle: Handle?) { 603 | guard let handle = handle else { return } 604 | self[handle].color = .Black 605 | } 606 | } 607 | 608 | //MARK: Rotation 609 | 610 | extension RedBlackTree { 611 | /// Rotates the subtree rooted at `handle` in the specified direction. Used when the tree implements 612 | /// a binary search tree. 613 | /// 614 | /// The child towards the opposite of `direction` under `handle` becomes the new root, 615 | /// and the previous root becomes its child towards `dir`. The rest of the children 616 | /// are linked up to preserve ordering in a binary search tree. 617 | /// 618 | /// - Returns: The handle of the new root of the subtree. 619 | internal mutating func rotate(handle: Handle, _ dir: RedBlackDirection) -> Handle { 620 | let x = handle 621 | let opp = dir.opposite 622 | guard let y = self[handle][opp] else { fatalError("Invalid rotation") } 623 | 624 | // x y 625 | // / \ / \ 626 | // a y <--> x c 627 | // / \ / \ 628 | // b c a b 629 | 630 | var xn = self[x] 631 | var yn = self[y] 632 | let b = yn[dir] 633 | 634 | if let p = xn.parent { self[p].replaceChild(x, with: y) } 635 | yn.parent = xn.parent 636 | xn.parent = y 637 | yn[dir] = x 638 | 639 | xn[opp] = b 640 | if let b = b { self[b].parent = x } 641 | 642 | self[x] = xn 643 | self[y] = yn 644 | 645 | if root == x { root = y } 646 | // leftmost, rightmost are invariant under rotations 647 | 648 | self.updateSummaryAt(x) 649 | self.updateSummaryAt(y) 650 | 651 | return y 652 | } 653 | } 654 | 655 | //MARK: Inserting an individual element 656 | extension RedBlackTree { 657 | internal func slotOf(handle: Handle) -> Slot { 658 | guard let parent = self[handle].parent else { return .Root } 659 | let pn = self[parent] 660 | let direction: RedBlackDirection = (handle == pn.left ? .Left : .Right) 661 | return .Toward(direction, under: parent) 662 | } 663 | 664 | /// - Note: This can be faster than finding the old node and inserting if not found. 665 | public mutating func setPayloadOf(key: Key, to payload: Payload) -> (Handle, Payload?) { 666 | var slot: Slot = .Root 667 | var handle: Handle? = nil 668 | self.find(key) { m, h in 669 | switch m { 670 | case .Before: 671 | slot = .Toward(.Left, under: h) 672 | return .Before 673 | case .Matching: 674 | handle = h 675 | return .Matching 676 | case .After: 677 | slot = .Toward(.Right, under: h) 678 | return .After 679 | } 680 | } 681 | 682 | if let handle = handle { 683 | let old = setPayloadAt(handle, to: payload) 684 | return (handle, old) 685 | } 686 | else { 687 | let handle = insert(payload, head: key.head, into: slot) 688 | return (handle, nil) 689 | } 690 | } 691 | 692 | public mutating func insert(payload: Payload, forKey key: InsertionKey) -> Handle { 693 | func insertionSlotOf(key: InsertionKey) -> Slot { 694 | var slot: Slot = .Root 695 | self.find(key) { match, handle in 696 | switch match { 697 | case .Before: 698 | slot = .Toward(.Left, under: handle) 699 | return .Before 700 | case .Matching: 701 | slot = .Toward(.Right, under: handle) 702 | return .After 703 | case .After: 704 | slot = .Toward(.Right, under: handle) 705 | return .After 706 | } 707 | } 708 | return slot 709 | } 710 | 711 | let slot = insertionSlotOf(key) 712 | return insert(payload, head: key.head, into: slot) 713 | } 714 | 715 | public mutating func insert(payload: Payload, forKey key: InsertionKey, after predecessor: Handle?) -> Handle { 716 | assert(predecessor == self.rightmostBefore(key) || key == self.keyAt(predecessor!)) 717 | return insert(payload, head: key.head, toward:.Right, from:predecessor) 718 | } 719 | 720 | public mutating func insert(payload: Payload, forKey key: InsertionKey, before successor: Handle?) -> Handle { 721 | assert(successor == self.leftmostAfter(key) || key == self.keyAt(successor!)) 722 | return insert(payload, head: key.head, toward:.Left, from:successor) 723 | } 724 | 725 | private mutating func insert(payload: Payload, head: Head, toward direction: RedBlackDirection, from neighbor: Handle?) -> Handle { 726 | if let neighbor: Handle = neighbor { 727 | if let child = self[neighbor][direction] { 728 | let next = furthestUnder(child, toward: direction.opposite) 729 | return insert(payload, head: head, into: .Toward(direction.opposite, under: next)) 730 | } 731 | else { 732 | return insert(payload, head: head, into: .Toward(direction, under: neighbor)) 733 | } 734 | } 735 | else if let furthest = furthestToward(direction.opposite) { 736 | return self.insert(payload, head: head, into: .Toward(direction.opposite, under: furthest)) 737 | } 738 | else { 739 | return self.insert(payload, head: head, into: .Root) 740 | } 741 | } 742 | 743 | private mutating func insert(payload: Payload, head: Head, into slot: Slot) -> Handle { 744 | let handle = Handle(nodes.count) 745 | switch slot { 746 | case .Root: 747 | assert(nodes.isEmpty) 748 | self.root = handle 749 | self.leftmost = handle 750 | self.rightmost = handle 751 | nodes.append(Node(parent: nil, head: head, payload: payload)) 752 | case .Toward(let direction, under: let parent): 753 | assert(self[parent][direction] == nil) 754 | nodes.append(Node(parent: parent, head: head, payload: payload)) 755 | self[parent][direction] = handle 756 | if leftmost == parent && direction == .Left { leftmost = handle } 757 | if rightmost == parent && direction == .Right { rightmost = handle } 758 | } 759 | 760 | updateSummariesAtAndAbove(handle) 761 | 762 | rebalanceAfterInsertion(handle) 763 | return handle 764 | } 765 | } 766 | 767 | //MARK: Rebalancing after an insertion 768 | extension RedBlackTree { 769 | 770 | private mutating func rebalanceAfterInsertion(new: Handle) { 771 | var child = new 772 | while case .Toward(let dir, under: let parent) = slotOf(child) { 773 | assert(isRed(child)) 774 | guard self[parent].color == .Red else { break } 775 | guard case .Toward(let pdir, under: let grandparent) = slotOf(parent) else { fatalError("Invalid tree: root is red") } 776 | let popp = pdir.opposite 777 | 778 | if let aunt = self[grandparent][popp] where isRed(aunt) { 779 | // grandparent(Black) 780 | // / \ 781 | // aunt(Red) parent(Red) 782 | // | 783 | // child(Red) 784 | // 785 | setBlack(parent) 786 | setBlack(aunt) 787 | setRed(grandparent) 788 | child = grandparent 789 | } 790 | else if dir == popp { 791 | // grandparent(Black) 792 | // / \ 793 | // aunt(Black) parent(Red) 794 | // / \ 795 | // child(Red) B 796 | // / \ 797 | // B B 798 | self.rotate(parent, pdir) 799 | self.rotate(grandparent, popp) 800 | setBlack(child) 801 | setRed(grandparent) 802 | break 803 | } 804 | else { 805 | // grandparent(Black) 806 | // / \ 807 | // aunt(Black) parent(Red) 808 | // / \ 809 | // B child(Red) 810 | // / \ 811 | // B B 812 | self.rotate(grandparent, popp) 813 | setBlack(parent) 814 | setRed(grandparent) 815 | break 816 | } 817 | } 818 | setBlack(root) 819 | } 820 | } 821 | 822 | //MARK: Append and merge 823 | 824 | extension RedBlackTree { 825 | 826 | public mutating func append(tree: RedBlackTree) { 827 | guard let b1 = rightmost else { self = tree; return } 828 | guard let c2 = tree.leftmost else { return } 829 | 830 | let sb = self.summaryBefore(b1) 831 | let sc = sb + self[b1].head 832 | precondition(InsertionKey(summary: sb, head: self[b1].head) <= InsertionKey(summary: sc, head: tree[c2].head)) 833 | 834 | self.reserveCapacity(self.count + tree.count) 835 | var summary = sc 836 | var previous1 = b1 837 | var next2: Handle? = c2 838 | while let h2 = next2 { 839 | let node2 = tree[h2] 840 | previous1 = self.insert(node2.payload, head: node2.head, toward: .Right, from: previous1) 841 | summary += node2.head 842 | next2 = tree.successor(h2) 843 | } 844 | } 845 | 846 | public mutating func merge(tree: RedBlackTree) { 847 | self.reserveCapacity(self.count + tree.count) 848 | 849 | for (key, payload) in tree { 850 | self.insert(payload, forKey: key) 851 | } 852 | } 853 | } 854 | 855 | //MARK: Removal of nodes 856 | 857 | extension RedBlackTree { 858 | 859 | public mutating func removeAll(keepCapacity keepCapacity: Bool = false) { 860 | nodes.removeAll(keepCapacity: keepCapacity) 861 | root = nil 862 | leftmost = nil 863 | rightmost = nil 864 | } 865 | 866 | /// Remove the node at `handle`, invalidating all existing handles. 867 | /// - Note: You need to discard your existing handles into the tree after you call this method. 868 | /// - SeeAlso: `removeAndReturnSuccessor` 869 | /// - Complexity: O(log(`count`)) 870 | public mutating func remove(handle: Handle) -> Payload { 871 | return _remove(handle, successor: nil).1 872 | } 873 | 874 | /// Remove the node at `handle`, invalidating all existing handles. 875 | /// - Note: You can use the returned handle to continue operating on the tree without having to find your place again. 876 | /// - Returns: The handle of the node that used to follow the removed node in the original tree, or nil if 877 | /// `handle` was at the rightmost position. 878 | /// - Complexity: O(log(`count`)) 879 | public mutating func removeAndReturnSuccessor(handle: Handle) -> (Handle?, Payload) { 880 | return _remove(handle, successor: successor(handle)) 881 | } 882 | 883 | /// Remove a node, keeping track of its successor. 884 | /// - Returns: The handle of `successor` after the removal. 885 | private mutating func _remove(handle: Handle, successor: Handle?) -> (Handle?, Payload) { 886 | assert(handle != successor) 887 | // Fixme: Removing from a red-black tree is one ugly algorithm. 888 | let node = self[handle] 889 | if let _ = node.left, r = node.right { 890 | // We can't directly remove a node with two children, but its successor is suitable. 891 | // Let's remove it instead, placing its payload into handle. 892 | let next = successor ?? leftmostUnder(r) 893 | let n = self[next] 894 | self[handle].head = n.head 895 | self[handle].payload = n.payload 896 | // Note that the above doesn't change root, leftmost, rightmost. 897 | // The summary will be updated on the way up. 898 | let handle = _remove(next, keeping: handle) 899 | return (handle, node.payload) 900 | } 901 | else { 902 | let handle = _remove(handle, keeping: successor) 903 | return (handle, node.payload) 904 | } 905 | } 906 | 907 | /// Remove a node with at most one child, while keeping track of another handle. 908 | /// - Returns: The handle of `marker` after the removal. 909 | private mutating func _remove(handle: Handle, keeping marker: Handle?) -> Handle? { 910 | let node = self[handle] 911 | var rebalance = node.color == .Black 912 | let slot = slotOf(handle) 913 | assert(node.left == nil || node.right == nil) 914 | 915 | let child = node.left ?? node.right 916 | if let child = child { 917 | var childNode = self[child] 918 | childNode.parent = node.parent 919 | if node.color == .Black && childNode.color == .Red { 920 | childNode.color = .Black 921 | rebalance = false 922 | } 923 | self[child] = childNode 924 | } 925 | if let parent = node.parent { 926 | self[parent].replaceChild(handle, with: child) 927 | } 928 | 929 | if root == handle { root = child } 930 | if leftmost == handle { leftmost = child ?? node.parent } 931 | if rightmost == handle { rightmost = child ?? node.parent } 932 | 933 | updateSummariesAtAndAbove(node.parent) 934 | 935 | if rebalance { 936 | rebalanceAfterRemoval(slot) 937 | } 938 | 939 | return deleteUnlinkedHandle(handle, keeping: marker) 940 | } 941 | 942 | private mutating func deleteUnlinkedHandle(removed: Handle, keeping marker: Handle?) -> Handle? { 943 | let last = Handle(nodes.count - 1) 944 | if removed == last { 945 | nodes.removeLast() 946 | return marker 947 | } 948 | else { 949 | // Move the last node into handle, and remove its original place instead. 950 | let node = nodes.removeLast() 951 | self[removed] = node 952 | if let p = node.parent { self[p].replaceChild(last, with: removed) } 953 | if let l = node.left { self[l].parent = removed } 954 | if let r = node.right { self[r].parent = removed } 955 | 956 | if root == last { root = removed } 957 | if leftmost == last { leftmost = removed } 958 | if rightmost == last { rightmost = removed } 959 | 960 | return marker == last ? removed : marker 961 | } 962 | } 963 | 964 | private mutating func rebalanceAfterRemoval(slot: Slot) { 965 | var slot = slot 966 | while case .Toward(let dir, under: let parent) = slot { 967 | let opp = dir.opposite 968 | let sibling = self[parent][opp]! // there's a missing black in slot, so it definitely has a sibling tree. 969 | let siblingNode = self[sibling] 970 | if siblingNode.color == .Red { // Case (1) in [CLRS] 971 | // legend: label(color)[rank] 972 | // 973 | // parent(B)[b+1] 974 | // / \ 975 | // slot sibling(R) 976 | // [b-1] / \ 977 | // [b] [b] 978 | assert(isBlack(parent) && self[sibling].left != nil && self[sibling].right != nil) 979 | self.rotate(parent, dir) 980 | setBlack(sibling) 981 | setRed(parent) 982 | // Old sibling is now above the parent; new sibling is black. 983 | continue 984 | } 985 | let farNephew = siblingNode[opp] 986 | if let farNephew = farNephew where isRed(farNephew) { // Case (4) in [CLRS] 987 | // parent[b+1] 988 | // / \ 989 | // slot sibling(B)[b] 990 | // [b-1] / \ 991 | // [b-1] farNephew(R)[b-1] 992 | self.rotate(parent, dir) 993 | self[sibling].color = self[parent].color 994 | setBlack(farNephew) 995 | setBlack(parent) 996 | // We sacrificed nephew's red to restore the black count above slot. We're done! 997 | return 998 | } 999 | let closeNephew = siblingNode[dir] 1000 | if let closeNephew = closeNephew where isRed(closeNephew) { // Case (3) in [CLRS] 1001 | // parent 1002 | // / \ 1003 | // slot sibling(B) 1004 | // [b-1] / \ 1005 | // closeNephew(R) farNephew(B) 1006 | // [b-1] [b-1] 1007 | self.rotate(sibling, opp) 1008 | self.rotate(parent, dir) 1009 | self[closeNephew].color = self[parent].color 1010 | setBlack(parent) 1011 | // We've sacrificed the close nephew's red to restore the black count above slot. We're done! 1012 | return 1013 | } 1014 | else { // Case (2) in [CLRS] 1015 | // parent 1016 | // / \ 1017 | // slot sibling(B) 1018 | // [b-1] / \ 1019 | // closeNephew(B) farNephew(B) 1020 | // [b-1] [b-1] 1021 | 1022 | // We are allowed to paint the sibling red, creating a missing black. 1023 | setRed(sibling) 1024 | 1025 | if isRed(parent) { // We can finish this right now. 1026 | setBlack(parent) 1027 | return 1028 | } 1029 | // Repeat one level higher. 1030 | slot = slotOf(parent) 1031 | } 1032 | } 1033 | } 1034 | } 1035 | -------------------------------------------------------------------------------- /Sources/Summary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Statistic.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-18. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import Foundation 10 | 11 | // Alternative names: 12 | // - Fold 13 | // - Statistic 14 | // - Smell 15 | // - Essence 16 | // - Fingerprint 17 | // - Reduction 18 | // - Accumulator 19 | // - Condensation 20 | // - Distillate 21 | // - Extract 22 | public protocol SummaryProtocol: Equatable { 23 | typealias Item 24 | 25 | init() // Identity 26 | init(_ item: Item) 27 | init(_ a: Self, _ b: Self) // Monoid operator 28 | } 29 | 30 | @warn_unused_result 31 | public func + (a: Summary?, b: Summary?) -> Summary { 32 | switch (a, b) { 33 | case (nil, nil): 34 | return Summary() 35 | case (nil, .Some(let b)): 36 | return b 37 | case (.Some(let a), nil): 38 | return a 39 | case (.Some(let a), .Some(let b)): 40 | return Summary(a, b) 41 | } 42 | } 43 | 44 | @warn_unused_result 45 | public func + (a: Summary.Item?, b: Summary?) -> Summary { 46 | switch (a, b) { 47 | case (nil, nil): 48 | return Summary() 49 | case (nil, .Some(let b)): 50 | return b 51 | case (.Some(let a), nil): 52 | return Summary(a) 53 | case (.Some(let a), .Some(let b)): 54 | return Summary(Summary(a), b) 55 | } 56 | } 57 | 58 | @warn_unused_result 59 | public func + (a: Summary?, b: Summary.Item?) -> Summary { 60 | switch (a, b) { 61 | case (nil, nil): 62 | return Summary() 63 | case (nil, .Some(let b)): 64 | return Summary(b) 65 | case (.Some(let a), nil): 66 | return a 67 | case (.Some(let a), .Some(let b)): 68 | return Summary(a, Summary(b)) 69 | } 70 | } 71 | 72 | public func += (inout left: S, right: S.Item?) { 73 | left = left + right 74 | } 75 | public func += (inout left: S, right: S?) { 76 | left = left + right 77 | } 78 | 79 | /// The empty summary is simply a summary that is a constant empty value. 80 | /// The empty summary is a free summary in that it doesn't constrain the type of its items. 81 | /// Instances of empty summarys are of zero size -- RedBlackTree relies on this to shortcut their maintenance. 82 | public enum EmptySummary: SummaryProtocol { 83 | public typealias Item = T 84 | 85 | case Null 86 | 87 | public init() { self = .Null } 88 | public init(_ item: Item) { self = .Null } 89 | public init(_ a: EmptySummary, _ b: EmptySummary) { self = .Null } 90 | } 91 | public func == (a: EmptySummary, b: EmptySummary) -> Bool { return true } 92 | 93 | 94 | /// The counting summary is summary that counts items. It is useful for implementing tree-based lists. 95 | /// The counting summary is a free summary in that it doesn't constrain the type of its items. 96 | public struct CountingSummary: SummaryProtocol { 97 | public let count: Int 98 | 99 | public init() { count = 0 } 100 | public init(_ item: Item) { count = 1 } 101 | public init(_ a: CountingSummary, _ b: CountingSummary) { count = a.count + b.count } 102 | } 103 | public func == (a: CountingSummary, b: CountingSummary) -> Bool { return a.count == b.count } 104 | 105 | extension CountingSummary: CustomStringConvertible { 106 | public var description: String { return "\(count) descendants" } 107 | } -------------------------------------------------------------------------------- /Tests/Helpers/PermutationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PermutationTests.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-21. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | 12 | class PermutationTests: XCTestCase { 13 | func testPermutations() { 14 | XCTAssertEqual(Array(generatePermutations(0)), []) 15 | XCTAssertEqual(Array(generatePermutations(1)), [[0]]) 16 | XCTAssertEqual(Array(generatePermutations(2)), [[0, 1], [1, 0]]) 17 | XCTAssertEqual(Array(generatePermutations(3)), [[0, 1, 2], [0, 2, 1], [2, 0, 1], [1, 0, 2], [1, 2, 0], [2, 1, 0]]) 18 | var count = 0 19 | for p in generatePermutations(6) { 20 | XCTAssertEqual(p.sort(), [0, 1, 2, 3, 4, 5]) 21 | count += 1 22 | } 23 | XCTAssertEqual(count, 6 * 5 * 4 * 3 * 2) 24 | } 25 | 26 | func testInversions() { 27 | XCTAssertEqual(Array(generateInversions(0)), []) 28 | XCTAssertEqual(Array(generateInversions(1)), [[0]]) 29 | XCTAssertEqual(Array(generateInversions(2)), [[0, 0], [0, 1]]) 30 | XCTAssertEqual(Array(generateInversions(3)), [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], [0, 1, 1], [0, 1, 2]]) 31 | 32 | var count = 0 33 | for inv in generateInversions(6) { 34 | XCTAssertEqual(inv.enumerate().filter { $0.index < $0.element }.count, 0) 35 | count += 1 36 | } 37 | XCTAssertEqual(count, 6 * 5 * 4 * 3 * 2) 38 | } 39 | } -------------------------------------------------------------------------------- /Tests/Helpers/Permutations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Permutations.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-21. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Generates all permutations of length `count`. 12 | /// - Returns: a generator that produces arrays of integers in range `0.. AnyGenerator<[Int]> { 14 | if count == 0 { 15 | return anyGenerator(EmptyCollection<[Int]>().generate()) 16 | } 17 | if count == 1 { 18 | return anyGenerator(CollectionOfOne([0]).generate()) 19 | } 20 | if count == 2 { 21 | return anyGenerator([[0, 1], [1, 0]].generate()) 22 | } 23 | let generator = generatePermutations(count - 1) 24 | var perm: [Int] = [] 25 | var next = -1 26 | return anyGenerator { 27 | if next < 0 { 28 | guard let p = generator.next() else { return nil } 29 | perm = p 30 | next = p.count 31 | } 32 | var r = perm 33 | r.insert(count - 1, atIndex: next) 34 | next -= 1 35 | return r 36 | } 37 | } 38 | 39 | /// Generates all inversion vectors of length `count`. The vectors returned all have an extra '0' element prepended for convenience. 40 | func generateInversions(count: Int) -> AnyGenerator<[Int]> { 41 | if count == 0 { 42 | return anyGenerator(EmptyCollection<[Int]>().generate()) 43 | } 44 | if count == 1 { 45 | return anyGenerator(CollectionOfOne([0]).generate()) 46 | } 47 | if count == 2 { 48 | return anyGenerator([[0, 0], [0, 1]].generate()) 49 | } 50 | let generator = generateInversions(count - 1) 51 | var inv: [Int] = [] 52 | var next = 1 53 | return anyGenerator { 54 | if next > inv.count { 55 | guard let i = generator.next() else { return nil } 56 | inv = i 57 | next = 0 58 | } 59 | var i = inv 60 | i.append(next) 61 | next += 1 62 | return i 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Tests/Helpers/Random.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Random.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-20. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Returns a random number sampled from a uniform distribution from 0 to limit. 12 | /// - Returns: A number in range `0.. Int { 14 | return Int(arc4random_uniform(UInt32(limit))) 15 | } 16 | 17 | extension Array { 18 | /// Returns a copy of this array with all elements randomly shuffled. 19 | func shuffle() -> Array { 20 | var copy = self 21 | copy.shuffleInPlace() 22 | return copy 23 | } 24 | 25 | /// Randomly shuffles the elements of this array. 26 | mutating func shuffleInPlace() { 27 | let count = self.count 28 | for i in 0.. [String] { 13 | let columnCount = lines.reduce(0) { a, l in max(a, l.count) } 14 | var columnWidths = [Int](count: columnCount, repeatedValue: 0) 15 | lines.lazy.flatMap { $0.enumerate() }.forEach { i, c in 16 | columnWidths[i] = max(columnWidths[i], c.characters.count) 17 | } 18 | 19 | var result: [String] = [] 20 | result.reserveCapacity(lines.count) 21 | for columns in lines { 22 | var line = "" 23 | columns.enumerate().forEach { i, c in 24 | if i > 0 { 25 | line += separator 26 | } 27 | line += c 28 | line += String(count: columnWidths[i] - c.characters.count, repeatedValue: " " as Character) 29 | } 30 | result.append(line) 31 | } 32 | return result 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Tests/Helpers/XCTest extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XCTest extensions.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-21. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import Foundation 10 | 11 | import XCTest 12 | 13 | #if Swift22 14 | typealias FileString = StaticString 15 | #else 16 | typealias FileString = String 17 | #endif 18 | 19 | // This basic overload is missing from XCTest, so it upgrades everything to Optional which makes failure reports harder to read. 20 | func XCTAssertEqual(@autoclosure expression1: () -> T, @autoclosure _ expression2: () -> T, _ message: String = "", file: FileString = __FILE__, line: UInt = __LINE__) { 21 | let a = expression1() 22 | let b = expression2() 23 | if a != b { 24 | let m = message.isEmpty ? "XCTAssertEqual failed: (\"\(a)\") is not equal to (\"\(b)\")" : message 25 | XCTFail(m, file: file, line: line) 26 | } 27 | } 28 | 29 | func XCTAssertElementsEqual(a: S1, _ b: S2, file: FileString = __FILE__, line: UInt = __LINE__) { 30 | let aa = Array(a) 31 | let ba = Array(b) 32 | if !aa.elementsEqual(ba) { 33 | XCTFail("XCTAssertEqual failed: \"\(aa)\" is not equal to \"\(ba)\"", file: file, line: line) 34 | } 35 | } 36 | 37 | func XCTAssertElementsEqual(a: S1, _ b: S2, file: FileString = __FILE__, line: UInt = __LINE__) { 38 | let aa = Array(a) 39 | let ba = Array(b) 40 | if !aa.elementsEqual(ba, isEquivalent: { a, b in a.0 == b.0 && a.1 == b.1 }) { 41 | XCTFail("XCTAssertEqual failed: \"\(aa)\" is not equal to \"\(b)\"", file: file, line: line) 42 | } 43 | } -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/RedBlackTreeTests.swift: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // RedBlackTreeTests.swift 4 | // RedBlackTree 5 | // 6 | // Created by Károly Lőrentey on 2015-12-19. 7 | // Copyright © 2015–2016 Károly Lőrentey. 8 | // 9 | 10 | import XCTest 11 | @testable import RedBlackTree 12 | 13 | public struct TestHead: Equatable, CustomStringConvertible { 14 | let key: Key 15 | let weight: Int 16 | 17 | public var description: String { return "(key: \(key), weight: \(weight))" } 18 | } 19 | public func ==(a: TestHead, b: TestHead) -> Bool { 20 | return a.key == b.key && a.weight == b.weight 21 | } 22 | 23 | public struct TestSummary: SummaryProtocol, CustomStringConvertible { 24 | public typealias Weight = Int 25 | public typealias Item = TestHead 26 | public let min: Key? 27 | public let max: Key? 28 | public let weight: Weight 29 | public let count: Int 30 | 31 | public init() { 32 | self.min = nil 33 | self.max = nil 34 | self.weight = 0 35 | self.count = 0 36 | } 37 | public init(_ item: Item) { 38 | self.min = item.key 39 | self.max = item.key 40 | self.weight = item.weight 41 | self.count = 1 42 | } 43 | public init(_ a: TestSummary, _ b: TestSummary) { 44 | if let amin = a.min, bmin = b.min where amin > bmin { 45 | XCTFail("Out of order summation: \(a) + \(b)") 46 | } 47 | if let amax = a.max, bmax = b.max where amax > bmax { 48 | XCTFail("Out of order summation: \(a) + \(b)") 49 | } 50 | 51 | switch (a.min, b.min) { 52 | case (nil, nil): 53 | self.min = nil 54 | case(.Some(let a), nil): 55 | self.min = a 56 | case (nil, .Some(let b)): 57 | self.min = b 58 | case (.Some(let a), .Some(let b)): 59 | self.min = Swift.min(a, b) 60 | } 61 | 62 | switch (a.max, b.max) { 63 | case (nil, nil): 64 | self.max = nil 65 | case(.Some(let a), nil): 66 | self.max = a 67 | case (nil, .Some(let b)): 68 | self.max = b 69 | case (.Some(let a), .Some(let b)): 70 | self.max = Swift.max(a, b) 71 | } 72 | 73 | self.weight = a.weight + b.weight 74 | self.count = a.count + b.count 75 | } 76 | 77 | public func dump(item: Key?) -> String { 78 | if let item = item { 79 | return String(item) 80 | } 81 | else { 82 | return "nil" 83 | } 84 | } 85 | 86 | public var description: String { 87 | return "(min: \(dump(min)), max: \(dump(max)), weight: \(weight), count: \(count))" 88 | } 89 | } 90 | public func ==(a: TestSummary, b: TestSummary) -> Bool { 91 | return a.min == b.min && a.max == b.max && a.count == b.count 92 | } 93 | 94 | internal struct ByKey: RedBlackInsertionKey, CustomStringConvertible { 95 | internal typealias Summary = TestSummary 96 | internal typealias Head = Summary.Item 97 | 98 | internal let key: K 99 | 100 | internal init(_ key: K) { self.key = key } 101 | internal init(summary: Summary, head: Head) { 102 | if summary.max > head.key { 103 | XCTFail("Invalid summary: \(summary) should is not before \(head)") 104 | } 105 | self.key = head.key 106 | } 107 | 108 | internal var head: Head { return Head(key: key, weight: 10) } 109 | 110 | internal var description: String { return "\(key)" } 111 | } 112 | internal func ==(a: ByKey, b: ByKey) -> Bool { 113 | return a.key == b.key 114 | } 115 | internal func < (a: ByKey, b: ByKey) -> Bool { 116 | return a.key < b.key 117 | } 118 | 119 | internal struct ByIndex: RedBlackKey { 120 | internal typealias Summary = TestSummary 121 | internal typealias Head = Summary.Item 122 | 123 | internal let index: Int 124 | 125 | internal init(_ index: Int) { 126 | self.index = index 127 | } 128 | 129 | internal init(summary: Summary, head: Head) { 130 | if summary.max > head.key { 131 | XCTFail("Invalid summary: \(summary) should is not before \(head)") 132 | } 133 | self.index = summary.count 134 | } 135 | } 136 | internal func ==(a: ByIndex, b: ByIndex) -> Bool { return a.index == b.index } 137 | internal func < (a: ByIndex, b: ByIndex) -> Bool { return a.index < b.index } 138 | 139 | internal struct ByIndexRange: RedBlackKey { 140 | internal typealias Summary = TestSummary 141 | internal typealias Head = Summary.Item 142 | 143 | internal let range: Range 144 | 145 | internal init(_ range: Range) { self.range = range } 146 | internal init(summary: Summary, head: Head) { self.range = summary.count ..< summary.count + 1 } 147 | } 148 | internal func ==(a: ByIndexRange, b: ByIndexRange) -> Bool { 149 | return a.range.intersects(b.range) 150 | } 151 | internal func < (a: ByIndexRange, b: ByIndexRange) -> Bool { 152 | return a.range.endIndex <= b.range.startIndex 153 | } 154 | 155 | internal struct ByWeightIndex: RedBlackKey { 156 | internal typealias Summary = TestSummary 157 | internal typealias Head = Summary.Item 158 | 159 | internal let weightIndexRange: Range 160 | 161 | internal init(_ weightIndex: Int) { self.weightIndexRange = weightIndex ..< weightIndex + 1 } 162 | internal init(summary: Summary, head: Head) { self.weightIndexRange = summary.weight ..< summary.weight + head.weight } 163 | } 164 | internal func ==(a: ByWeightIndex, b: ByWeightIndex) -> Bool { 165 | return a.weightIndexRange.intersects(b.weightIndexRange) 166 | } 167 | internal func < (a: ByWeightIndex, b: ByWeightIndex) -> Bool { 168 | return a.weightIndexRange.endIndex <= b.weightIndexRange.startIndex 169 | } 170 | 171 | extension Range where Element: Comparable { 172 | func intersects(range: Range) -> Bool { 173 | guard self.endIndex > range.startIndex else { return false } 174 | guard self.startIndex < range.endIndex else { return false } 175 | return true 176 | } 177 | } 178 | 179 | typealias Key = ByKey 180 | typealias TestTree = RedBlackTree 181 | 182 | class RedBlackTrivialTests: XCTestCase { 183 | 184 | func testPropertiesOfEmptyTree() { 185 | let tree = TestTree() 186 | 187 | tree.assertValid() 188 | XCTAssertEqual(tree.count, 0) 189 | XCTAssertTrue(tree.isEmpty) 190 | XCTAssertNil(tree.root) 191 | XCTAssertNil(tree.leftmost) 192 | XCTAssertNil(tree.rightmost) 193 | XCTAssertEqual(tree.show(), "") 194 | let key = Key(42) 195 | XCTAssertNil(tree.find(key)) 196 | var generator = tree.generate() 197 | XCTAssertNil(generator.next()) 198 | } 199 | 200 | func testPropertiesOfTreeWithOneNode() { 201 | let tree = TestTree([(Key(10), "root")]) 202 | 203 | tree.assertValid() 204 | XCTAssertEqual(tree.count, 1) 205 | XCTAssertFalse(tree.isEmpty) 206 | guard let ten = tree.find(Key(10)) else { XCTFail(); return } 207 | XCTAssertEqual(tree.root, ten) 208 | XCTAssertEqual(tree.leftmost, ten) 209 | XCTAssertEqual(tree.rightmost, ten) 210 | XCTAssertEqual(tree.show(), "(10)") 211 | 212 | var generator = tree.generate() 213 | guard let first = generator.next() else { XCTFail(); return } 214 | XCTAssertEqual(first.0, Key(10)) 215 | XCTAssertEqual(first.1, "root") 216 | } 217 | } 218 | 219 | class RedBlackTreeSimpleQueryTests: XCTestCase { 220 | var tree = TestTree() 221 | override func setUp() { 222 | super.setUp() 223 | tree = TestTree() 224 | tree.insert("three", forKey: Key(3)) 225 | tree.insert("ten", forKey: Key(10)) 226 | tree.insert("one", forKey: Key(1)) 227 | tree.insert("two", forKey: Key(2)) 228 | tree.insert("eight", forKey: Key(8)) 229 | tree.insert("four", forKey: Key(4)) 230 | tree.insert("six", forKey: Key(6)) 231 | tree.insert("five", forKey: Key(5)) 232 | tree.insert("nine", forKey: Key(9)) 233 | tree.insert("seven", forKey: Key(7)) 234 | 235 | tree.dump() 236 | tree.assertValid() 237 | } 238 | 239 | func testTreeIsValid() { 240 | tree.assertValid() 241 | } 242 | 243 | func testLeftMost() { 244 | XCTAssertNotNil(tree.leftmost) 245 | if let leftmost = tree.leftmost { 246 | XCTAssertEqual(tree.keyAt(leftmost), Key(1)) 247 | XCTAssertEqual(tree.payloadAt(leftmost), "one") 248 | } 249 | } 250 | 251 | func testRightMost() { 252 | XCTAssertNotNil(tree.rightmost) 253 | if let rightmost = tree.rightmost { 254 | XCTAssertEqual(tree.keyAt(rightmost), Key(10)) 255 | XCTAssertEqual(tree.payloadAt(rightmost), "ten") 256 | } 257 | } 258 | 259 | func testRoot() { 260 | XCTAssertNotNil(tree.root) 261 | } 262 | 263 | func testCount() { 264 | XCTAssertEqual(tree.count, 10) 265 | } 266 | 267 | func testIsEmpty() { 268 | XCTAssertFalse(tree.isEmpty) 269 | } 270 | 271 | func testFind() { 272 | XCTAssertNil(tree.find(Key(0))) 273 | XCTAssertNotNil(tree.find(Key(1))) 274 | XCTAssertNotNil(tree.find(Key(2))) 275 | XCTAssertNotNil(tree.find(Key(3))) 276 | XCTAssertNotNil(tree.find(Key(4))) 277 | XCTAssertNotNil(tree.find(Key(5))) 278 | XCTAssertNotNil(tree.find(Key(6))) 279 | XCTAssertNotNil(tree.find(Key(7))) 280 | XCTAssertNotNil(tree.find(Key(8))) 281 | XCTAssertNotNil(tree.find(Key(9))) 282 | XCTAssertNotNil(tree.find(Key(10))) 283 | XCTAssertNil(tree.find(Key(11))) 284 | } 285 | 286 | func testPayloadAt() { 287 | let handles = (1...10).flatMap { tree.find(Key($0)) } 288 | let payloads = handles.map { tree.payloadAt($0) } 289 | XCTAssertEqual(payloads, ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]) 290 | } 291 | 292 | 293 | func testSummaryBefore() { 294 | for i in 1...10 { 295 | guard let handle = tree.find(Key(i)) else { XCTFail(); continue } 296 | let summary = tree.summaryBefore(handle) 297 | 298 | XCTAssertEqual(summary.min, i > 1 ? 1 : nil) 299 | XCTAssertEqual(summary.max, i > 1 ? i - 1 : nil) 300 | XCTAssertEqual(summary.count, i - 1) 301 | } 302 | } 303 | 304 | func testSummaryAfter() { 305 | for i in 1...10 { 306 | guard let handle = tree.find(Key(i)) else { XCTFail(); continue } 307 | let summary = tree.summaryAfter(handle) 308 | 309 | XCTAssertEqual(summary.min, i < 10 ? i + 1 : nil) 310 | XCTAssertEqual(summary.max, i < 10 ? 10 : nil) 311 | XCTAssertEqual(summary.count, 10 - i) 312 | } 313 | } 314 | 315 | func testKeyAt() { 316 | let handles = (1...10).flatMap { tree.find(Key($0)) } 317 | let keys = handles.map { tree.keyAt($0) } 318 | XCTAssertEqual(keys, [Key(1), Key(2), Key(3), Key(4), Key(5), Key(6), Key(7), Key(8), Key(9), Key(10)]) 319 | } 320 | 321 | func testElementAt() { 322 | let handles = (1...10).flatMap { tree.find(Key($0)) } 323 | let expectedElements = [(Key(1), "one"), (Key(2), "two"), (Key(3), "three"), (Key(4), "four"), (Key(5), "five"), (Key(6), "six"), (Key(7), "seven"), (Key(8), "eight"), (Key(9), "nine"), (Key(10), "ten")] 324 | 325 | let elements = handles.map { tree.elementAt($0) } 326 | XCTAssertTrue(elements.elementsEqual(expectedElements, isEquivalent: { e1, e2 in e1.0 == e2.0 && e1.1 == e2.1 })) 327 | } 328 | 329 | func testHeadAt() { 330 | let handles = (1...10).flatMap { tree.find(Key($0)) } 331 | let expectedHeadKeys = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 332 | let heads = handles.map { tree.headAt($0) } 333 | XCTAssertEqual(heads.map { $0.key }, expectedHeadKeys) 334 | } 335 | 336 | func testSuccessor() { 337 | let handles = (1...10).flatMap { tree.find(Key($0)) } 338 | 339 | var next: TestTree.Handle? = nil 340 | for handle in handles.reverse() { 341 | XCTAssertEqual(tree.successor(handle), next) 342 | next = handle 343 | } 344 | } 345 | 346 | func testPredecessor() { 347 | let handles = (1...10).flatMap { tree.find(Key($0)) } 348 | 349 | var previous: TestTree.Handle? = nil 350 | for handle in handles { 351 | XCTAssertEqual(tree.predecessor(handle), previous) 352 | previous = handle 353 | } 354 | } 355 | 356 | func testStep() { 357 | let handles = (1...10).flatMap { tree.find(Key($0)) } 358 | 359 | for handle in handles { 360 | XCTAssertEqual(tree.step(handle, toward: .Left), tree.predecessor(handle)) 361 | XCTAssertEqual(tree.step(handle, toward: .Right), tree.successor(handle)) 362 | } 363 | } 364 | 365 | func testHandleOfLeftmostNodeUnder() { 366 | XCTAssertEqual(tree.leftmostUnder(tree.root!), tree.leftmost) 367 | 368 | let handles = (1...10).flatMap { tree.find(Key($0)) } 369 | 370 | for handle in handles { 371 | let key = tree.keyAt(handle) 372 | let minHandle = tree.leftmostUnder(handle) 373 | let minKey = tree.keyAt(minHandle) 374 | 375 | XCTAssertLessThanOrEqual(minKey, key) 376 | } 377 | } 378 | 379 | func testHandleOfRightmostNodeUnder() { 380 | XCTAssertEqual(tree.rightmostUnder(tree.root!), tree.rightmost) 381 | 382 | let handles = (1...10).flatMap { tree.find(Key($0)) } 383 | 384 | for handle in handles { 385 | let key = tree.keyAt(handle) 386 | let maxHandle = tree.rightmostUnder(handle) 387 | let maxKey = tree.keyAt(maxHandle) 388 | 389 | XCTAssertGreaterThanOrEqual(maxKey, key) 390 | } 391 | } 392 | 393 | func testFurthestNodeUnder() { 394 | let handles = (1...10).flatMap { tree.find(Key($0)) } 395 | 396 | for handle in handles { 397 | let min = tree.leftmostUnder(handle) 398 | let max = tree.rightmostUnder(handle) 399 | XCTAssertEqual(tree.furthestUnder(handle, toward: .Left), min) 400 | XCTAssertEqual(tree.furthestUnder(handle, toward: .Right), max) 401 | } 402 | } 403 | 404 | func testGenerate() { 405 | let expectedElements = [(Key(1), "one"), (Key(2), "two"), (Key(3), "three"), (Key(4), "four"), (Key(5), "five"), (Key(6), "six"), (Key(7), "seven"), (Key(8), "eight"), (Key(9), "nine"), (Key(10), "ten")] 406 | 407 | var elements = Array() 408 | var generator = tree.generate() 409 | while let element = generator.next() { 410 | elements.append(element) 411 | } 412 | 413 | XCTAssertTrue(expectedElements.elementsEqual(elements, isEquivalent: { e1, e2 in e1.0 == e2.0 && e1.1 == e2.1 })) 414 | } 415 | 416 | func testSequenceType() { 417 | let expectedElements = [(Key(1), "one"), (Key(2), "two"), (Key(3), "three"), (Key(4), "four"), (Key(5), "five"), (Key(6), "six"), (Key(7), "seven"), (Key(8), "eight"), (Key(9), "nine"), (Key(10), "ten")] 418 | 419 | var elements = Array() 420 | for element in tree { 421 | elements.append(element) 422 | } 423 | 424 | XCTAssertTrue(expectedElements.elementsEqual(elements, isEquivalent: { e1, e2 in e1.0 == e2.0 && e1.1 == e2.1 })) 425 | } 426 | 427 | func testGenerateFrom() { 428 | let expectedElements = [(Key(1), "one"), (Key(2), "two"), (Key(3), "three"), (Key(4), "four"), (Key(5), "five"), (Key(6), "six"), (Key(7), "seven"), (Key(8), "eight"), (Key(9), "nine"), (Key(10), "ten")] 429 | 430 | for i in 1...10 { 431 | guard let handle = tree.find(Key(i)) else { XCTFail(); continue } 432 | var elements = Array() 433 | var generator = tree.generateFrom(handle) 434 | while let e = generator.next() { 435 | elements.append(e) 436 | } 437 | 438 | XCTAssertTrue(expectedElements[i-1...9].elementsEqual(elements, isEquivalent: { e1, e2 in e1.0 == e2.0 && e1.1 == e2.1 })) 439 | } 440 | } 441 | 442 | func testHandleSearches() { 443 | for i in 1...10 { 444 | guard let handle = tree.find(Key(i)) else { XCTFail(); continue } 445 | 446 | let topmostMatching = tree.topmostMatching(Key(i)) 447 | XCTAssertEqual(handle, topmostMatching) 448 | 449 | let leftmostMatching = tree.leftmostMatching(Key(i)) 450 | XCTAssertEqual(leftmostMatching, handle) 451 | 452 | let leftmostAfter = tree.leftmostAfter(Key(i)) 453 | XCTAssertEqual(leftmostAfter, tree.successor(handle)) 454 | 455 | let rightmostBefore = tree.rightmostBefore(Key(i)) 456 | XCTAssertEqual(rightmostBefore, tree.predecessor(handle)) 457 | 458 | let rightmostMatching = tree.rightmostMatching(Key(i)) 459 | XCTAssertEqual(rightmostMatching, handle) 460 | } 461 | } 462 | 463 | func testDescription() { 464 | XCTAssertEqual(tree.description, "RedBlackTree with 10 nodes") 465 | let handle = tree.find(Key(1))! 466 | XCTAssertTrue(handle.description.hasPrefix("#")) 467 | } 468 | } 469 | 470 | class RedBlackTreeSimpleMutatorTests: XCTestCase { 471 | var tree = TestTree() 472 | override func setUp() { 473 | super.setUp() 474 | tree = TestTree() 475 | tree.insert("three", forKey: Key(3)) 476 | tree.insert("ten", forKey: Key(10)) 477 | tree.insert("one", forKey: Key(1)) 478 | tree.insert("two", forKey: Key(2)) 479 | tree.insert("eight", forKey: Key(8)) 480 | tree.insert("four", forKey: Key(4)) 481 | tree.insert("six", forKey: Key(6)) 482 | tree.insert("five", forKey: Key(5)) 483 | tree.insert("nine", forKey: Key(9)) 484 | tree.insert("seven", forKey: Key(7)) 485 | 486 | 487 | tree.dump() 488 | XCTAssertEqual(tree.map { $0.1 }, ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]) 489 | XCTAssertEqual(tree.map { $0.0 }, [Key(1), Key(2), Key(3), Key(4), Key(5), Key(6), Key(7), Key(8), Key(9), Key(10)]) 490 | tree.assertValid() 491 | } 492 | 493 | func testSetPayloadAt() { 494 | guard let handle = tree.find(Key(4)) else { XCTFail(); return } 495 | 496 | let old = tree.setPayloadAt(handle, to: "FOUR") 497 | XCTAssertEqual(old, "four") 498 | tree.assertValid() 499 | 500 | XCTAssertEqual(tree.map { $0.1 }, ["one", "two", "three", "FOUR", "five", "six", "seven", "eight", "nine", "ten"]) 501 | } 502 | 503 | func testSetHeadAt() { 504 | XCTAssertEqual(tree.find(ByWeightIndex(29)), tree.find(Key(3))) 505 | XCTAssertEqual(tree.find(ByWeightIndex(30)), tree.find(Key(4))) 506 | XCTAssertEqual(tree.find(ByWeightIndex(31)), tree.find(Key(4))) 507 | XCTAssertEqual(tree.find(ByWeightIndex(40)), tree.find(Key(5))) 508 | XCTAssertEqual(tree.find(ByWeightIndex(50)), tree.find(Key(6))) 509 | XCTAssertEqual(tree.find(ByWeightIndex(59)), tree.find(Key(6))) 510 | XCTAssertEqual(tree.find(ByWeightIndex(60)), tree.find(Key(7))) 511 | XCTAssertEqual(tree.find(ByWeightIndex(61)), tree.find(Key(7))) 512 | 513 | guard let handle = tree.find(Key(4)) else { XCTFail(); return } 514 | let old = tree.setHeadAt(handle, to: TestHead(key: 4, weight: 30)) 515 | XCTAssertEqual(old, TestHead(key: 4, weight: 10)) 516 | tree.assertValid() 517 | 518 | XCTAssertEqual(tree.find(ByWeightIndex(29)), tree.find(Key(3))) 519 | XCTAssertEqual(tree.find(ByWeightIndex(30)), tree.find(Key(4))) 520 | XCTAssertEqual(tree.find(ByWeightIndex(31)), tree.find(Key(4))) 521 | XCTAssertEqual(tree.find(ByWeightIndex(40)), tree.find(Key(4))) 522 | XCTAssertEqual(tree.find(ByWeightIndex(50)), tree.find(Key(4))) 523 | XCTAssertEqual(tree.find(ByWeightIndex(59)), tree.find(Key(4))) 524 | XCTAssertEqual(tree.find(ByWeightIndex(60)), tree.find(Key(5))) 525 | XCTAssertEqual(tree.find(ByWeightIndex(61)), tree.find(Key(5))) 526 | 527 | XCTAssertEqual(tree.map { $0.0 }, [Key(1), Key(2), Key(3), Key(4), Key(5), Key(6), Key(7), Key(8), Key(9), Key(10)]) 528 | } 529 | 530 | func testSetPayloadOf() { 531 | 532 | let (h1, p1) = tree.setPayloadOf(Key(5), to: "FIVE") 533 | XCTAssertEqual(h1, tree.find(Key(5))) 534 | XCTAssertEqual(p1, "five") 535 | tree.assertValid() 536 | 537 | let (h3, p3) = tree.setPayloadOf(Key(0), to: "zero") 538 | XCTAssertEqual(h3, tree.find(Key(0))) 539 | XCTAssertNil(p3) 540 | tree.assertValid() 541 | 542 | XCTAssertEqual(tree.map { $0.1 }, ["zero", "one", "two", "three", "four", "FIVE", "six", "seven", "eight", "nine", "ten"]) 543 | } 544 | 545 | func testInsert() { 546 | 547 | for i in (20...25).reverse() { 548 | let h = tree.insert("\(i)", forKey: Key(i)) 549 | XCTAssertEqual(h, tree.find(Key(i))) 550 | tree.assertValid() 551 | } 552 | XCTAssertEqual(tree.map { $0.1 }, ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "20", "21", "22", "23", "24", "25"]) 553 | } 554 | 555 | func testInsertAfter() { 556 | 557 | var prev = tree.rightmost 558 | for i in 20...25 { 559 | let h = tree.insert("\(i)", forKey: Key(i), after: prev) 560 | XCTAssertEqual(h, tree.find(Key(i))) 561 | tree.assertValid() 562 | prev = h 563 | } 564 | XCTAssertEqual(tree.map { $0.1 }, ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "20", "21", "22", "23", "24", "25"]) 565 | tree.dump() 566 | } 567 | 568 | func testInsertBefore() { 569 | 570 | var prev: TestTree.Handle? = nil 571 | for i in (20...25).reverse() { 572 | let h = tree.insert("\(i)", forKey: Key(i), before: prev) 573 | XCTAssertEqual(h, tree.find(Key(i))) 574 | tree.assertValid() 575 | prev = h 576 | } 577 | XCTAssertEqual(tree.map { $0.1 }, ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "20", "21", "22", "23", "24", "25"]) 578 | tree.dump() 579 | } 580 | 581 | func testAppend() { 582 | 583 | var tree2 = TestTree() 584 | for i in (20...25).reverse() { 585 | tree2.insert("\(i)", forKey: Key(i)) 586 | } 587 | 588 | tree.append(tree2) 589 | tree.assertValid() 590 | 591 | XCTAssertEqual(tree.map { $0.1 }, ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "20", "21", "22", "23", "24", "25"]) 592 | tree.dump() 593 | } 594 | 595 | func testMerge() { 596 | 597 | var tree2 = TestTree() 598 | for i in (5..<15).reverse() { 599 | tree2.insert("\(i)", forKey: Key(i)) 600 | } 601 | tree.merge(tree2) 602 | tree.assertValid() 603 | 604 | XCTAssertEqual(tree.map { $0.1 }, ["one", "two", "three", "four", "five", "5", "six", "6", "seven", "7", "eight", "8", "nine", "9", "ten", "10", "11", "12", "13", "14"]) 605 | tree.dump() 606 | } 607 | 608 | func testRemoveAll() { 609 | 610 | tree.removeAll() 611 | tree.assertValid() 612 | 613 | XCTAssertEqual(tree.map { $0.1 }, []) 614 | tree.dump() 615 | } 616 | 617 | func testRemove() { 618 | 619 | XCTAssertEqual(tree.remove(tree.find(Key(9))!), "nine") 620 | XCTAssertNil(tree.find(Key(9))) 621 | 622 | XCTAssertEqual(tree.remove(tree.find(Key(3))!), "three") 623 | XCTAssertEqual(tree.remove(tree.find(Key(1))!), "one") 624 | XCTAssertEqual(tree.remove(tree.find(Key(8))!), "eight") 625 | 626 | XCTAssertNil(tree.find(Key(3))) 627 | XCTAssertNil(tree.find(Key(1))) 628 | XCTAssertNil(tree.find(Key(8))) 629 | 630 | XCTAssertEqual(tree.map { $0.1 }, ["two", "four", "five", "six", "seven", "ten"]) 631 | tree.assertValid() 632 | 633 | tree.dump() 634 | } 635 | 636 | func testRemoveAndReturnSuccessor() { 637 | 638 | let (h9, p9) = tree.removeAndReturnSuccessor(tree.find(Key(9))!) 639 | XCTAssertEqual(h9, tree.find(Key(10))) 640 | XCTAssertEqual(p9, "nine") 641 | XCTAssertNil(tree.find(Key(9))) 642 | 643 | let (h3, p3) = tree.removeAndReturnSuccessor(tree.find(Key(3))!) 644 | XCTAssertEqual(h3, tree.find(Key(4))) 645 | XCTAssertEqual(p3, "three") 646 | XCTAssertNil(tree.find(Key(3))) 647 | 648 | let (h1, p1) = tree.removeAndReturnSuccessor(tree.find(Key(1))!) 649 | XCTAssertEqual(h1, tree.find(Key(2))) 650 | XCTAssertEqual(p1, "one") 651 | XCTAssertNil(tree.find(Key(1))) 652 | 653 | let (h8, p8) = tree.removeAndReturnSuccessor(tree.find(Key(8))!) 654 | XCTAssertEqual(h8, tree.find(Key(10))) 655 | XCTAssertEqual(p8, "eight") 656 | XCTAssertNil(tree.find(Key(8))) 657 | 658 | XCTAssertEqual(tree.map { $0.1 }, ["two", "four", "five", "six", "seven", "ten"]) 659 | tree.assertValid() 660 | 661 | tree.dump() 662 | } 663 | 664 | } 665 | 666 | class RedBlackTreeHasValueSemanticsTests: XCTestCase { 667 | let originalKeys = [Key(1), Key(2), Key(3), Key(4), Key(5), Key(6), Key(7), Key(8), Key(9), Key(10)] 668 | let originalPayloads = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"] 669 | 670 | func sampleTree() -> TestTree { 671 | return TestTree([(Key(1), "one"), (Key(2), "two"), (Key(3), "three"), (Key(4), "four"), (Key(5), "five"), (Key(6), "six"), (Key(7), "seven"), (Key(8), "eight"), (Key(9), "nine"), (Key(10), "ten")]) 672 | } 673 | 674 | func testSetPayloadAtHasValueSemantics() { 675 | let tree = sampleTree() 676 | 677 | var copy = tree 678 | guard let handle = copy.find(Key(5)) else { XCTFail(); return } 679 | copy.setPayloadAt(handle, to: "FIVE") 680 | 681 | XCTAssertEqual(copy.payloadAt(handle), "FIVE") 682 | XCTAssertEqual(tree.payloadAt(handle), "five") 683 | } 684 | 685 | func testSetHeadAtHasValueSemantics() { 686 | let tree = sampleTree() 687 | 688 | var copy = tree 689 | guard let handle = copy.find(Key(5)) else { XCTFail(); return } 690 | copy.setHeadAt(handle, to: TestHead(key: 5, weight: 100)) 691 | 692 | XCTAssertEqual(copy.headAt(handle), TestHead(key: 5, weight: 100)) 693 | XCTAssertEqual(tree.headAt(handle), TestHead(key: 5, weight: 10)) 694 | } 695 | 696 | func testInsertHasValueSemantics() { 697 | let tree = sampleTree() 698 | 699 | var copy = tree 700 | copy.insert("eleven", forKey:Key(11)) 701 | 702 | XCTAssertNotNil(copy.find(Key(11))) 703 | XCTAssertNil(tree.find(Key(11))) 704 | } 705 | 706 | func testRemoveHasValueSemantics() { 707 | let tree = sampleTree() 708 | 709 | var copy = tree 710 | guard let handle = tree.find(Key(5)) else { XCTFail(); return } 711 | copy.remove(handle) 712 | 713 | XCTAssertNil(copy.find(Key(5))) 714 | XCTAssertNotNil(tree.find(Key(5))) 715 | } 716 | } 717 | 718 | class RedBlackTreeSystematicChanges: XCTestCase { 719 | 720 | func testInsertingSequentially() { 721 | var tree = TestTree() 722 | 723 | for i in 1...100 { 724 | let handle = tree.insert(String(i * 100), forKey: Key(i)) 725 | XCTAssertEqual(tree.find(Key(i)), handle) 726 | tree.assertValid() 727 | } 728 | } 729 | 730 | func testRemovingSequentially() { 731 | var tree = TestTree() 732 | 733 | for i in 1...100 { 734 | tree.insert(String(i * 100), forKey: Key(i)) 735 | } 736 | tree.assertValid() 737 | 738 | for i in 1...100 { 739 | guard let handle = tree.find(Key(i)) else { XCTFail(); continue } 740 | tree.remove(handle) 741 | XCTAssertNil(tree.find(Key(i))) 742 | tree.assertValid() 743 | } 744 | } 745 | 746 | func testInsertionAndRemovalInRandomOrder() { 747 | for round in 1...10 { 748 | print("Round \(round)") 749 | 750 | var tree = TestTree() 751 | 752 | let insertions = Array(1...30).shuffle() 753 | print("Testing insertion permutation \(insertions)") 754 | for i in insertions { 755 | let handle = tree.insert(String(i * 100), forKey: Key(i)) 756 | XCTAssertEqual(tree.find(Key(i)), handle) 757 | tree.assertValid() 758 | } 759 | 760 | let removals = Array(1...30).shuffle() 761 | print("Testing removal permutation \(removals)") 762 | for i in removals { 763 | guard let handle = tree.find(Key(i)) else { XCTFail(); continue } 764 | tree.remove(handle) 765 | XCTAssertNil(tree.find(Key(i))) 766 | tree.assertValid() 767 | } 768 | } 769 | } 770 | 771 | func testInsertionsAndRemovalsExhaustively() { 772 | // Increase this to really exercise the tree. (It will take much longer.) 773 | let count = 4 774 | 775 | // Insert keys from 1 to count in all possible permutations, then remove them, again in every possible order. 776 | // Verify that red-black properties hold at every step. 777 | for order in generatePermutations(count) { 778 | var tree = TestTree() 779 | for i in order { 780 | tree.insert("\(i)", forKey: Key(i)) 781 | tree.assertValid() 782 | } 783 | 784 | XCTAssertEqual(tree.count, count) 785 | 786 | for removals in generatePermutations(count) { 787 | var t = tree 788 | for i in removals { 789 | guard let handle = t.find(Key(i)) else { XCTFail(); continue } 790 | t.remove(handle) 791 | t.assertValid() 792 | } 793 | 794 | XCTAssertEqual(t.count, 0) 795 | } 796 | } 797 | 798 | 799 | } 800 | } 801 | -------------------------------------------------------------------------------- /Tests/TestUtils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestUtils.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-15. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import XCTest 10 | @testable import RedBlackTree 11 | 12 | 13 | extension RedBlackTree { 14 | 15 | func show() -> String { 16 | func show(handle: Handle?, prefix: Summary) -> String { 17 | guard let handle = handle else { return "" } 18 | let node = self[handle] 19 | 20 | var s = prefix 21 | let left = show(node.left, prefix: s) 22 | 23 | s += self[node.left]?.summary 24 | let root = String(InsertionKey(summary: s, head: node.head)) 25 | 26 | s += node.head 27 | let right = show(node.right, prefix: s) 28 | return "(" + [left, root, right].filter { !$0.isEmpty }.joinWithSeparator(" ") + ")" 29 | } 30 | return show(root, prefix: Summary()) 31 | } 32 | 33 | func showNode(handle: Handle) -> String { 34 | let node = self[handle] 35 | return "\(handle): \(node.summary) ⟼ \(node.payload)" 36 | } 37 | func showNode(i: Int) -> String { 38 | let node = nodes[i] 39 | return "#\(i): \(node.summary) ⟼ \(node.payload)" 40 | } 41 | 42 | func lookup(directions: RedBlackDirection...) -> Handle? { 43 | return self.lookup(directions) 44 | } 45 | 46 | func lookup(directions: S) -> Handle? { 47 | var handle = self.root 48 | for direction in directions { 49 | guard let h = handle else { return nil } 50 | handle = self[h][direction] 51 | } 52 | return handle 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /Tests/TreeChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TreeChecker.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-20. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import XCTest 10 | @testable import RedBlackTree 11 | 12 | struct RedBlackInfo { 13 | typealias Tree = RedBlackTree 14 | typealias Handle = Tree.Handle 15 | typealias Summary = Tree.Summary 16 | 17 | var nodeCount: Int = 0 18 | var leftmost: Handle? = nil 19 | var rightmost: Handle? = nil 20 | 21 | var minDepth: Int = 0 22 | var maxDepth: Int = 0 23 | 24 | var minRank: Int = 0 25 | var maxRank: Int = 0 26 | 27 | var color: Color = .Black 28 | var summary: Summary = Summary() 29 | var minKey: Key? = nil 30 | var maxKey: Key? = nil 31 | 32 | var defects: [(Handle, String, FileString, UInt)] = [] 33 | 34 | mutating func addDefect(handle: Handle, _ description: String, file: FileString = __FILE__, line: UInt = __LINE__) { 35 | defects.append((handle, description, file, line)) 36 | } 37 | } 38 | 39 | extension RedBlackTree { 40 | typealias Info = RedBlackInfo 41 | 42 | private func collectInfo(blacklist: Set, handle: Handle?, parent: Handle?, prefix: Summary) -> Info { 43 | 44 | guard let handle = handle else { return Info() } 45 | 46 | var info = Info() 47 | let node = self[handle] 48 | 49 | if blacklist.contains(handle) { 50 | info.addDefect(handle, "node is linked more than once") 51 | return info 52 | } 53 | var blacklist = blacklist 54 | blacklist.insert(handle) 55 | 56 | let li = collectInfo(blacklist, handle: node.left, parent: handle, prefix: prefix) 57 | let ri = collectInfo(blacklist, handle: node.right, parent: handle, prefix: prefix + li.summary + node.head) 58 | info.summary = li.summary + node.head + ri.summary 59 | 60 | info.nodeCount = li.nodeCount + 1 + ri.nodeCount 61 | 62 | info.leftmost = li.leftmost ?? handle 63 | info.rightmost = ri.rightmost ?? handle 64 | 65 | info.minDepth = min(li.minDepth, ri.minDepth) + 1 66 | info.maxDepth = max(li.maxDepth, ri.maxDepth) + 1 67 | info.minRank = min(li.minRank, ri.minRank) + (node.color == .Black ? 1 : 0) 68 | info.maxRank = max(li.maxRank, ri.maxRank) + (node.color == .Black ? 1 : 0) 69 | 70 | info.defects = li.defects + ri.defects 71 | info.color = node.color 72 | 73 | if node.parent != parent { 74 | info.addDefect(handle, "parent is \(node.parent), expected \(parent)") 75 | } 76 | if node.color == .Red { 77 | if li.color != .Black { 78 | info.addDefect(handle, "color is red but left child(\(node.left) is also red") 79 | } 80 | if ri.color != .Black { 81 | info.addDefect(handle, "color is red but right child(\(node.left) is also red") 82 | } 83 | } 84 | if li.minRank != ri.minRank { 85 | info.addDefect(handle, "mismatching child subtree ranks: \(li.minRank) vs \(ri.minRank)") 86 | } 87 | if info.summary != node.summary { 88 | info.addDefect(handle, "summary is \(node.summary), expected \(info.summary)") 89 | } 90 | let key = InsertionKey(summary: prefix + li.summary, head: node.head) 91 | info.maxKey = ri.maxKey 92 | info.minKey = li.minKey 93 | if let lk = li.maxKey where lk > key { 94 | info.addDefect(handle, "node's key is ordered before its maximum left descendant: \(key) < \(lk)") 95 | } 96 | if let rk = ri.minKey where rk < key { 97 | info.addDefect(handle, "node's key is ordered after its minimum right descendant: \(key) > \(rk)") 98 | } 99 | return info 100 | } 101 | 102 | var debugInfo: Info { 103 | var info = collectInfo([], handle: root, parent: nil, prefix: Summary()) 104 | if info.color == .Red { 105 | info.addDefect(root!, "root is red") 106 | } 107 | if info.nodeCount != count { 108 | info.addDefect(root!, "count of reachable nodes is \(count), expected \(info.nodeCount)") 109 | } 110 | if info.leftmost != leftmost { 111 | info.addDefect(leftmost ?? info.leftmost!, "leftmost node is \(leftmost), expected \(info.leftmost)") 112 | } 113 | if info.rightmost != rightmost { 114 | info.addDefect(rightmost ?? info.rightmost!, "rightmost node is \(rightmost), expected \(info.rightmost)") 115 | } 116 | return info 117 | } 118 | 119 | func assertValid() { 120 | let info = debugInfo 121 | for (handle, explanation, file, line) in info.defects { 122 | XCTFail("\(handle): \(explanation)", file: file, line: line) 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Tests/TreeDumper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TreeDumper.swift 3 | // RedBlackTree 4 | // 5 | // Created by Károly Lőrentey on 2015-12-20. 6 | // Copyright © 2015–2016 Károly Lőrentey. 7 | // 8 | 9 | import Foundation 10 | @testable import RedBlackTree 11 | 12 | private func *(i: Int, s: String) -> String { 13 | var result = "" 14 | result.reserveCapacity(i * s.unicodeScalars.count) 15 | (0.. (Int, [(LineType, String, [String])]) { 34 | guard let handle = handle else { return (0, []) } 35 | 36 | let node = self[handle] 37 | let (leftTabs, leftLines) = dump(node.left, prefix: prefix) 38 | 39 | let p = prefix + self[node.left]?.summary 40 | let (rightTabs, rightLines) = dump(node.right, prefix: p + node.head) 41 | 42 | let tabs = max(leftTabs, rightTabs) 43 | 44 | let dot = (node.color == .Black ? "●" : "○") 45 | 46 | let root = [ 47 | "\(handle):", 48 | " \(InsertionKey(summary: p, head: node.head))", 49 | "⟼ \(node.payload)", 50 | "\t☺:\(node.head)", 51 | "\t∑:\(node.summary)"] 52 | 53 | if leftLines.isEmpty && rightLines.isEmpty { 54 | return (tabs, [(.Root, "\(dot)\t", root)]) 55 | } 56 | 57 | var lines: [(LineType, String, [String])] = [] 58 | 59 | let rightIndent = (tabs - rightTabs) * "\t" 60 | if rightLines.isEmpty { 61 | lines.append((.RightSubtree, "┏━\t" + "\t" + rightIndent, ["nil"])) 62 | } 63 | else { 64 | for (m, graphic, text) in rightLines { 65 | switch m { 66 | case .RightSubtree: 67 | lines.append((.RightSubtree, "\t" + graphic + rightIndent, text)) 68 | case .Root: 69 | lines.append((.RightSubtree, "┏━\t" + graphic + rightIndent, text)) 70 | case .LeftSubtree: 71 | lines.append((.RightSubtree, "┃\t" + graphic + rightIndent, text)) 72 | } 73 | } 74 | } 75 | 76 | lines.append((.Root, "\(dot)\t\t" + tabs * "\t", root)) 77 | 78 | let leftIndent = (tabs - leftTabs) * "\t" 79 | if leftLines.isEmpty { 80 | lines.append((.LeftSubtree, "┗━\t" + "\t" + leftIndent, ["nil"])) 81 | } 82 | else { 83 | for (m, graphic, text) in leftLines { 84 | switch m { 85 | case .RightSubtree: 86 | lines.append((.LeftSubtree, "┃\t" + graphic + leftIndent, text)) 87 | case .Root: 88 | lines.append((.LeftSubtree, "┗━\t" + graphic + leftIndent, text)) 89 | case .LeftSubtree: 90 | lines.append((.LeftSubtree, "\t" + graphic + leftIndent, text)) 91 | } 92 | } 93 | } 94 | return (tabs + 1, lines) 95 | } 96 | 97 | guard let top = top else { print("nil"); return } 98 | 99 | let prefix = summaryBefore(self.leftmostUnder(top)) 100 | let data = dump(top, prefix: prefix).1 101 | let lines = layoutColumns(data.map { $0.2 }) 102 | let graphics = data.map { $0.1 } 103 | 104 | for (graphic, line) in zip(graphics, lines) { 105 | print(graphic + line) 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /generate-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | module="RedBlackTree" 6 | scheme="RedBlackTree-Mac" 7 | 8 | version="$(grep VERSION_STRING version.xcconfig | sed 's/^VERSION_STRING = //' | sed 's/ *$//')" 9 | tag="v$version" 10 | 11 | jazzy \ 12 | --clean \ 13 | --author "Károly Lőrentey" \ 14 | --author_url "https://twitter.com/lorentey" \ 15 | --github_url "https://github.com/lorentey/$module" \ 16 | --github-file-prefix "https://github.com/lorentey/$module/tree/$tag" \ 17 | --module-version "$version" \ 18 | --xcodebuild-arguments "-scheme,$scheme" \ 19 | --module "$module" \ 20 | --root-url "https://lorentey.github.io/$module/reference/" \ 21 | --theme fullwidth \ 22 | --output gh-pages/api \ 23 | --swift-version 2.1.1 24 | -------------------------------------------------------------------------------- /version.xcconfig: -------------------------------------------------------------------------------- 1 | VERSION_STRING = 1.0.0 2 | BUILD_NUMBER = 1 3 | --------------------------------------------------------------------------------