├── .github └── workflows │ └── swift.yml ├── .gitignore ├── .swift-version ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── chunkediterator.swift ├── tree.swift ├── util.swift └── vector.swift ├── Tests ├── Info.plist └── ruminantTests │ ├── chunkediteratorTest.swift │ ├── exampleTests.swift │ ├── quickCheckTests.swift │ ├── utilTest.swift │ └── vectorTest.swift └── ruminant.xcodeproj ├── SwiftCheck_Info.plist ├── project.pbxproj ├── ruminantTests_Info.plist ├── ruminant_Info.plist └── xcshareddata └── xcschemes ├── ruminant-Package.xcscheme └── xcschememanagement.plist /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | name: Swift 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Build 17 | run: swift build -v 18 | - name: Run tests 19 | run: swift test -v 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xcworkspace 14 | *.xccheckout 15 | *.moved-aside 16 | DerivedData 17 | *.hmap 18 | *.ipa 19 | *.xcuserstate 20 | 21 | # CocoaPods 22 | # 23 | # We recommend against adding the Pods directory to your .gitignore. However 24 | # you should judge for yourself, the pros and cons are mentioned at: 25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 26 | # 27 | # Pods/ 28 | 29 | .build/ 30 | Package.resolved 31 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. 204 | 205 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "ruminant", 7 | 8 | products: [ 9 | .library( 10 | name: "ruminant", 11 | targets: ["ruminant"]), 12 | ], 13 | 14 | dependencies: [ 15 | .package(url: "https://github.com/typelift/SwiftCheck.git", from: "0.8.1") 16 | ], 17 | 18 | targets: [ 19 | .target( 20 | name: "ruminant", 21 | path: ".", 22 | sources: ["Sources"]), 23 | 24 | .testTarget( 25 | name: "ruminantTests", 26 | dependencies: ["ruminant", "SwiftCheck"], 27 | path: "./Tests", 28 | sources: ["ruminantTests"]), 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruminant 2 | 3 | A Swift implementation of [Clojure](http://clojure.org)'s [persistent](http://en.wikipedia.org/wiki/Persistent_data_structure) vectors. 4 | 5 | ## Persistent Vectors 6 | 7 | Core operations such as `conj`, `assoc`, `get` (using subscripts), `subvec` (using subscripts), and `concat` have been implemented. 8 | 9 | ```swift 10 | let v: PersistentVector = ["a", "b", "c"] 11 | let v2 = v.conj("d").assoc(index: 2, "C") 12 | XCTAssertEqual(v, ["a", "b", "c"]) 13 | XCTAssertEqual(v2, ["a", "b", "C", "d"]) 14 | XCTAssert(v.pop() == v2[0..<2]) 15 | XCTAssertEqual(v.map {$0.uppercased()}, ["A", "B", "C"]) 16 | XCTAssertEqual(v[1], "b") 17 | XCTAssertEqual(Array(v[1...2]), ["b", "c"]) 18 | ``` 19 | 20 | Transient vectors are included: 21 | 22 | ```swift 23 | let v: PersistentVector = ["a", "b", "c"] 24 | var tmp = v.transient() 25 | tmp = tmp.pop() 26 | tmp = tmp.conj("3") 27 | tmp = tmp.conj("4") 28 | XCTAssert(tmp.persistent() == ["a", "b", "3", "4"]) 29 | ``` 30 | 31 | ## Integration 32 | 33 | You can use the Swift-Package manager to integrate Ruminant. 34 | 35 | Add the following dependency in `Package.swift`: 36 | 37 | ```swift 38 | dependencies: [ 39 | .package(url: "https://github.com/jdevuyst/ruminant", from: "1.0.7") 40 | ], 41 | ``` 42 | 43 | ## Sample Usage 44 | 45 | Here is a sample walkthrough with Swift Package Manager to use this library. 46 | 47 | First, create a complete new directory from CLI named "Sample" and `cd` into it. 48 | 49 | ```bash 50 | mkdir sample 51 | cd sample 52 | ```` 53 | 54 | Next, create a new exectuable swift template inside this directory. 55 | 56 | ```bash 57 | swift package init --type executable 58 | ``` 59 | 60 | Now it's time to update `Package.swift`. 61 | 62 | ```swift 63 | // swift-tools-version:4.0 64 | // The swift-tools-version declares the minimum version of Swift required to build this package. 65 | 66 | import PackageDescription 67 | 68 | let package = Package( 69 | name: "sample", 70 | dependencies: [ 71 | // Dependencies declare other packages that this package depends on. 72 | .package(url: "https://github.com/jdevuyst/ruminant", from: "1.0.7") 73 | ], 74 | targets: [ 75 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 76 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 77 | .target( 78 | name: "sample", 79 | dependencies: ["ruminant"]) 80 | ] 81 | ) 82 | ``` 83 | 84 | Let's install the new dependency. 85 | 86 | ```bash 87 | swift package update 88 | ``` 89 | 90 | We'll also updatee `Main.swift` to test that Ruminant can be loaded. 91 | 92 | ```swift 93 | import ruminant 94 | 95 | print("Hello, Persistent Vector!") 96 | 97 | let sample = PersistentVector([1,2,3,4]).conj(45).conj(42) 98 | print(sample) 99 | ``` 100 | 101 | Finally, we can build and run the program from the command line. 102 | 103 | ```bash 104 | swift build 105 | swift run 106 | 107 | Hello, Persistent Vector! 108 | [1, 2, 3, 4, 45, 42] 109 | ``` 110 | 111 | That's it. Enjoy the world of persistent datastructures! 112 | 113 | ## License 114 | 115 | Copyright © 2015 Jonas De Vuyst 116 | 117 | Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version. 118 | -------------------------------------------------------------------------------- /Sources/chunkediterator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jonas De Vuyst on 18/02/15. 3 | // Copyright (c) 2015 Jonas De Vuyst. All rights reserved. 4 | // 5 | // chunkediterator.swift 6 | 7 | // n.b. ChunkedIterator copies can be advanced independently when backed by a persistent data structure 8 | 9 | // https://www.uraimo.com/2015/11/12/experimenting-with-swift-2-sequencetype-generatortype/ 10 | 11 | public class ChunkedIterator: IteratorProtocol { 12 | 13 | public typealias Element = T 14 | public typealias ChunkFunction = (Int) -> (chunk: [T], offset: Int) 15 | 16 | public let f: ChunkFunction 17 | private let end: Int 18 | 19 | private var i: Int 20 | private var j = 0 21 | private var chunk: [T] 22 | 23 | init(f: @escaping ChunkFunction, start: Int, end: Int) { 24 | assert(end >= start) 25 | self.f = f 26 | self.i = start 27 | self.end = end 28 | (chunk: self.chunk, offset: self.j) = end - start == 0 ? ([], 0) : f(start) 29 | } 30 | 31 | public func next() -> T? { 32 | guard i < end else { 33 | return nil 34 | } 35 | 36 | if j == chunk.count { 37 | (chunk: chunk, offset: j) = f(i) 38 | } 39 | 40 | i += 1 41 | defer { 42 | j += 1 43 | } 44 | return chunk[j] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/tree.swift: -------------------------------------------------------------------------------- 1 | // 2 | // tree.swift 3 | // RUMINANT – Swift persistent data structures à la Clojure 4 | // 5 | // Created by Jonas De Vuyst on 19/02/15. 6 | // Copyright (c) 2015 Jonas De Vuyst. All rights reserved. 7 | // 8 | 9 | // This function is used a lot and that is not a good thing. 10 | // 11 | // At the time when this project was started, @indirect enums were not yet 12 | // available in Swift. This was quite unfortunate, because sum types are 13 | // the usual way to implement trees. Using @indirect enums, a lot of illegal 14 | // states that are presently guarded by an `unreachable()` call, could be 15 | // made unrepresentable. 16 | private func unreachable() -> Never { 17 | preconditionFailure("This code should be unreachable") 18 | } 19 | 20 | // 21 | // MARK: - Node Types 22 | // 23 | 24 | class Node { 25 | var editID: Int 26 | 27 | init(transientID: Int) { 28 | self.editID = transientID 29 | } 30 | 31 | func transientVersion(_ transientID: Int) -> Node { unreachable() } 32 | 33 | func getChunk(_ index: Int, shift: Int) -> [T] { unreachable() } 34 | 35 | func transientChunk(_ transientID: Int, index: Int, shift: Int, chunk: inout [T]) { unreachable() } 36 | 37 | func newPath(_ transientID: Int, shift: Int) -> Node { 38 | return shift == 0 39 | ? self 40 | : TreeNode(transientID: transientID, children: [self]) 41 | .newPath(transientID, shift: shift - 5) 42 | } 43 | 44 | func pushTail(_ count: Int, shift: Int, tailNode: Node) -> Node { unreachable() } 45 | 46 | func transientPushTail(_ transientID: Int, count: Int, shift: Int, tailNode: Node) -> Node { unreachable() } 47 | 48 | func popTail(_ count: Int, shift: Int) -> Node? { unreachable() } 49 | 50 | func transientPopTail(_ transientID: Int, count: Int, shift: Int) -> Node? { unreachable() } 51 | 52 | func assoc(_ index: Int, shift: Int, element: T) -> Node { unreachable() } 53 | 54 | func transientAssoc(_ transientID: Int, index: Int, shift: Int, element: T) -> Node { unreachable() } 55 | 56 | func onlyChildNode() -> Node? { unreachable() } 57 | } 58 | 59 | internal class TreeNode : Node { 60 | private var children: [Node] 61 | 62 | init(transientID: Int, children: [Node]) { 63 | assert(children.count <= 32) 64 | assert(children.capacity <= 32) 65 | self.children = children 66 | super.init(transientID: transientID) 67 | } 68 | 69 | override init(transientID: Int) { 70 | self.children = [] 71 | if transientID != 0 { 72 | self.children.reserveCapacity(32) 73 | } 74 | super.init(transientID: transientID) 75 | } 76 | 77 | override func transientVersion(_ transientID: Int) -> TreeNode { 78 | if self.editID == transientID { 79 | return self 80 | } 81 | 82 | var newChildren = [Node]() 83 | newChildren.reserveCapacity(32) 84 | newChildren.append(contentsOf: children) 85 | return TreeNode(transientID: transientID, children: newChildren) 86 | } 87 | 88 | override func getChunk(_ index: Int, shift: Int) -> [T] { 89 | assert(shift > 0) 90 | return children[(index >> shift) & 0x01f].getChunk(index, shift: shift - 5) 91 | } 92 | 93 | override func transientChunk(_ transientID: Int, index: Int, shift: Int, chunk: inout [T]) { 94 | assert(shift > 0) 95 | children[(index >> shift) & 0x01f].transientChunk(transientID, index: index, shift: shift - 5, chunk: &chunk) 96 | } 97 | 98 | override func pushTail(_ count: Int, shift: Int, tailNode: Node) -> Node { 99 | assert(shift > 0) 100 | let subidx = ((count - 1) >> shift) & 0x01f 101 | 102 | let newChildren: [Node] 103 | 104 | if(shift == 5) { 105 | assert(subidx == children.count) 106 | newChildren = arrayConj(children, val: tailNode) 107 | } else if subidx == children.count { 108 | newChildren = arrayConj(children, val: tailNode.newPath(0, shift: shift - 5)) 109 | } else { 110 | newChildren = arrayAssoc(&children, idx: subidx, val: children[subidx].pushTail(count, shift: shift - 5, tailNode: tailNode)) 111 | } 112 | 113 | return TreeNode(transientID: self.editID, children: newChildren) 114 | } 115 | 116 | override func transientPushTail(_ transientID: Int, count: Int, shift: Int, tailNode: Node) -> Node { 117 | assert(shift > 0) 118 | 119 | let transientSelf = self.transientVersion(transientID) 120 | let subidx = ((count - 1) >> shift) & 0x01f 121 | 122 | if(shift == 5) { 123 | assert(subidx == children.count) 124 | children.append(tailNode) 125 | } else if subidx == children.count { 126 | children.append(tailNode.newPath(transientID, shift: shift - 5)) 127 | } else { 128 | children[subidx] = children[subidx].pushTail(count, shift: shift - 5, tailNode: tailNode) 129 | } 130 | 131 | return transientSelf 132 | } 133 | 134 | override func popTail(_ count: Int, shift: Int) -> Node? { 135 | let subidx = ((count - 2) >> shift) & 0x01f 136 | 137 | if shift > 5 { 138 | if let newChild = children[subidx].popTail(count, shift: shift - 5) { 139 | return TreeNode(transientID: self.editID, children: arrayAssoc(&children, idx: subidx, val: newChild)) 140 | } 141 | } 142 | 143 | assert(subidx == children.count - 1) 144 | return subidx == 0 ? nil : TreeNode(transientID: self.editID, children: arrayPop(children)) 145 | } 146 | 147 | override func transientPopTail(_ transientID: Int, count: Int, shift: Int) -> Node? { 148 | let subidx = ((count - 2) >> shift) & 0x01f 149 | 150 | if shift > 5 { 151 | if let newChild = children[subidx].transientPopTail(transientID, count: count, shift: shift - 5) { 152 | children[subidx] = newChild 153 | return self 154 | } 155 | } 156 | 157 | assert(subidx == children.count - 1) 158 | 159 | if subidx == 0 { 160 | return nil 161 | } 162 | 163 | children.removeLast() 164 | return self 165 | } 166 | 167 | override func assoc(_ index: Int, shift: Int, element: T) -> Node { 168 | assert(shift > 0) 169 | let subidx = (index >> shift) & 0x01f 170 | let newChildren = arrayAssoc(&children, idx: subidx, val: children[subidx].assoc(index, shift: shift - 5, element: element)) 171 | return TreeNode(transientID: self.editID, children: newChildren) 172 | } 173 | 174 | override func transientAssoc(_ transientID: Int, index: Int, shift: Int, element: T) -> Node { 175 | assert(shift > 0) 176 | let subidx = (index >> shift) & 0x01f 177 | let transientSelf = transientVersion(transientID) 178 | transientSelf.children[subidx] = children[subidx].transientAssoc(transientID, index: index, shift: shift - 5, element: element) 179 | return transientSelf 180 | } 181 | 182 | override func onlyChildNode() -> Node? { 183 | return children.count == 1 ? children[0] : nil 184 | } 185 | } 186 | 187 | internal class LeafNode : Node { 188 | private var children: [T] 189 | 190 | init(transientID: Int, children: [T]) { 191 | assert(children.count == 32) 192 | assert(children.capacity == 32) 193 | self.children = children 194 | super.init(transientID: transientID) 195 | } 196 | 197 | override func transientVersion(_ transientID: Int) -> LeafNode { 198 | assert(transientID != 0) 199 | return self.editID == transientID ? self : LeafNode(transientID: transientID, children: children) 200 | } 201 | 202 | override func getChunk(_ index: Int, shift: Int) -> [T] { 203 | assert(shift == 0) 204 | return children 205 | } 206 | 207 | override func transientChunk(_ transientID: Int, index: Int, shift: Int, chunk: inout [T]) { 208 | assert(shift == 0) 209 | let transientSelf = transientVersion(transientID) 210 | chunk = transientSelf.children 211 | } 212 | 213 | override func assoc(_ index: Int, shift: Int, element: T) -> Node { 214 | assert(shift == 0) 215 | let subidx = index & 0x01f 216 | return LeafNode(transientID: self.editID, children: arrayAssoc(&children, idx: subidx, val: element)) 217 | } 218 | 219 | override func transientAssoc(_ transientID: Int, index: Int, shift: Int, element: T) -> Node { 220 | assert(shift == 0) 221 | let subidx = index & 0x01f 222 | let transientSelf = transientVersion(transientID) 223 | transientSelf.children[subidx] = element 224 | return transientSelf 225 | } 226 | } 227 | 228 | -------------------------------------------------------------------------------- /Sources/util.swift: -------------------------------------------------------------------------------- 1 | // 2 | // util.swift 3 | // RUMINANT – Swift persistent data structures à la Clojure 4 | // 5 | // Created by Jonas De Vuyst on 19/02/15. 6 | // Copyright (c) 2015 Jonas De Vuyst. All rights reserved. 7 | // 8 | 9 | func arrayPop(_ arr: [T]) -> [T] { 10 | var arr2 = [T]() 11 | arr2.reserveCapacity(arr.count - 1) 12 | arr2.append(contentsOf:arr[0..(_ arr: [T], val: T) -> [T] { 17 | var arr2 = [T]() 18 | arr2.reserveCapacity(arr.count + 1) 19 | arr2.append(contentsOf: arr) 20 | arr2.append(val) 21 | return arr2 22 | } 23 | 24 | func arrayAssoc(_ arr: inout [T], idx: Int, val: T) -> [T] { 25 | arr[idx] = val 26 | return arr 27 | } 28 | 29 | func seqDescription(xs: T, ldelim: String, rdelim: String) -> String { 30 | let xs = xs.map {"\($0)"} 31 | let s = xs.joined(separator: ", ") 32 | return "\(ldelim)\(s)\(rdelim)" 33 | } 34 | 35 | func seqDebugDescription(xs: T, ldelim: String, rdelim: String) -> String { 36 | let xs: [String] = xs.map {x in 37 | if let x = x as? CustomDebugStringConvertible { 38 | return x.debugDescription 39 | } else { 40 | return "\(x)" 41 | } 42 | } 43 | let s = xs.joined(separator: ", ") 44 | return "\(ldelim)\(s)\(rdelim)" 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Sources/vector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // vector_impl.swift 3 | // RUMINANT – Swift persistent data structures à la Clojure 4 | // 5 | // Created by Jonas De Vuyst on 18/02/15. 6 | // Copyright (c) 2015 Jonas De Vuyst. All rights reserved. 7 | // 8 | // REFERENCES 9 | // http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation.html 10 | // http://hypirion.com/musings/understanding-persistent-vector-pt-1 11 | // http://hypirion.com/musings/understanding-persistent-vector-pt-2 12 | // http://hypirion.com/musings/understanding-persistent-vector-pt-3 13 | // http://hypirion.com/musings/understanding-clojure-transients 14 | // http://hypirion.com/musings/persistent-vector-performance 15 | // http://www.mattgreer.org/articles/clojurescript-internals-vectors/ 16 | // https://github.com/clojure/clojurescript/blob/22dd4fbeed72398cbc3336fccffe8196c56cd209/src/cljs/cljs/core.cljs#L4070 17 | // 18 | 19 | fileprivate final class LazyValue { 20 | private var cachedValue: T? 21 | 22 | func cached(_ newValue: () -> T) -> T { 23 | if let x = cachedValue { 24 | return x 25 | } else { 26 | let x = newValue() 27 | cachedValue = x 28 | return x 29 | } 30 | } 31 | } 32 | 33 | fileprivate protocol CachableSeqHashValue : Sequence where Element: Hashable { 34 | var cachedHashValue: LazyValue { get } 35 | } 36 | 37 | public protocol PersistentVectorType: Hashable, Collection { 38 | var count: Int { get } 39 | 40 | func conj(_ element: Element) -> Self 41 | 42 | func pop() -> Self 43 | 44 | subscript(index: Int) -> Element { get } 45 | 46 | func assoc(index: Int, _ element: Element) -> Self 47 | 48 | func concat(_ rhs: Other) -> Self where Other.Element == Element 49 | } 50 | 51 | public func ==(lhs: T, rhs: U) -> Bool 52 | where T.Iterator.Element == U.Iterator.Element, T.Iterator.Element: Equatable 53 | { 54 | guard lhs.count == rhs.count && lhs.hashValue == rhs.hashValue else { 55 | return false 56 | } 57 | 58 | return zip(lhs, rhs).allSatisfy({(x, y) in x == y}) 59 | } 60 | 61 | // 62 | // MARK: - PersistentVector 63 | // 64 | 65 | public struct PersistentVector : PersistentVectorType, ExpressibleByArrayLiteral, CachableSeqHashValue { 66 | 67 | public typealias Iterator = ChunkedIterator 68 | public typealias Index = Int 69 | public typealias SubSequence = Subvec 70 | 71 | public let count: Int 72 | let shift: Int 73 | let root: Node 74 | let tail: [T] 75 | 76 | fileprivate let cachedHashValue = LazyValue() 77 | 78 | internal init(count: Int, shift: Int, root: Node, tail: [T]) { 79 | assert(count >= 0) 80 | assert(shift > 0) 81 | assert(shift % 5 == 0) 82 | self.count = count 83 | self.shift = shift 84 | self.root = root 85 | self.tail = tail 86 | } 87 | 88 | public init() { 89 | self.init(count: 0, shift: 5, root: TreeNode(transientID: 0), tail: []) 90 | } 91 | 92 | public init(arrayLiteral xs: T...) { 93 | var v = PersistentVector() 94 | for x in xs { 95 | v = v.conj(x) 96 | } 97 | self.init(count: v.count, shift: v.shift, root: v.root, tail: v.tail) 98 | } 99 | 100 | public init(_ seq: S) where S.Element == Element 101 | { 102 | let v = seq.reduce(PersistentVector()) { (accu, current) in accu.conj(current) } 103 | self.init(count: v.count, shift: v.shift, root: v.root, tail: v.tail) 104 | } 105 | 106 | private func tailOffset() -> Int { 107 | let r = count < 32 ? 0 : (((count - 1) >> 5) << 5) 108 | assert(r == count - tail.count) 109 | return r 110 | } 111 | 112 | private func verifyBounds(index: Int) { 113 | precondition(index >= 0 && index < count, "Index \(index) is out of bounds for vector of size \(count)") 114 | } 115 | 116 | internal func getChunk(index: Index) -> (chunk: [T], offset: Int) { 117 | let chunk = index < tailOffset() ? root.getChunk(index, shift: self.shift) : tail 118 | return (chunk: chunk, offset: index & 0x01f) 119 | } 120 | 121 | public func conj(_ element: T) -> PersistentVector { 122 | var newShift = shift 123 | let newRoot: Node 124 | let newTail: [T] 125 | 126 | if count - tailOffset() < 32 { 127 | newRoot = root 128 | newTail = arrayConj(tail, val: element) 129 | } else { 130 | let tailNode = LeafNode(transientID: 0, children: tail) 131 | let rootOverflow = (count >> 5) > (1 << shift) 132 | 133 | if rootOverflow { 134 | newShift += 5 135 | newRoot = TreeNode(transientID: 0, children: [root, tailNode.newPath(0, shift: shift)]) 136 | } else { 137 | newRoot = root.pushTail(count, shift: shift, tailNode: tailNode) 138 | } 139 | 140 | newTail = [element] 141 | } 142 | 143 | return PersistentVector(count: count + 1, shift: newShift, root: newRoot, tail: newTail) 144 | } 145 | 146 | public func pop() -> PersistentVector { 147 | switch count { 148 | case 0: 149 | preconditionFailure("Cannot pop() an empty vector") 150 | case 1: 151 | return PersistentVector() 152 | case _ where count - tailOffset() > 1: 153 | return PersistentVector(count: count - 1, shift: shift, root: root, tail: arrayPop(tail)) 154 | default: 155 | assert(count - 2 < tailOffset()) 156 | let newTail = root.getChunk(count - 2, shift: shift) 157 | var newShift = shift 158 | let newRoot: Node 159 | if let r = root.popTail(count, shift: shift) { 160 | if let r2 = r.onlyChildNode(), shift > 5 { 161 | newShift -= 5 162 | newRoot = r2 163 | } else { 164 | newRoot = r 165 | } 166 | } else { 167 | assert(shift == 5) 168 | newRoot = TreeNode(transientID: 0) 169 | } 170 | return PersistentVector(count: count - 1, shift: newShift, root: newRoot, tail: newTail) 171 | } 172 | } 173 | 174 | public subscript(index: Index) -> T { 175 | verifyBounds(index: index) 176 | let t = getChunk(index: index) 177 | return t.chunk[t.offset] 178 | } 179 | 180 | public subscript(bounds: Range) -> SubSequence { 181 | return Subvec(vector: self, start: bounds.lowerBound, end: bounds.upperBound) 182 | } 183 | 184 | public func assoc(index: Index, _ element: T) -> PersistentVector { 185 | if index == count { 186 | return self.conj(element) 187 | } 188 | 189 | verifyBounds(index: index) 190 | 191 | if tailOffset() <= index { 192 | var newTail = tail 193 | newTail[index & 0x01f] = element 194 | return PersistentVector(count: count, shift: shift, root: root, tail: newTail) 195 | } 196 | 197 | let newRoot = root.assoc(index, shift: shift, element: element) 198 | return PersistentVector(count: count, shift: shift, root: newRoot, tail: tail) 199 | } 200 | 201 | public func makeIterator() -> Iterator { 202 | return ChunkedIterator(f: {self.getChunk(index: $0)}, start: 0, end: count) 203 | } 204 | 205 | public func transient() -> TransientVector { 206 | return TransientVector(vector: self) 207 | } 208 | 209 | public func concat(_ rhs: Other) -> PersistentVector where Other.Element == T { 210 | var v = transient() 211 | v = v.concat(rhs) 212 | return v.persistent() 213 | } 214 | } 215 | 216 | // 217 | // MARK: - Subvec 218 | // 219 | 220 | public struct Subvec: PersistentVectorType, CachableSeqHashValue { 221 | public typealias Index = Int 222 | public typealias Iterator = ChunkedIterator 223 | public typealias SubSequence = Subvec 224 | 225 | private let v: PersistentVector 226 | private let start: Index 227 | private let end: Index 228 | 229 | fileprivate let cachedHashValue = LazyValue() 230 | 231 | init(vector: PersistentVector, start: Index, end: Index) { 232 | precondition(end >= start) 233 | precondition(end - start <= vector.count) 234 | self.v = vector 235 | self.start = start 236 | self.end = end 237 | } 238 | 239 | init(vector: Subvec, start: Index, end: Index) { 240 | precondition(end >= start) 241 | precondition(end - start <= vector.count) 242 | self.v = vector.v 243 | self.start = vector.start + start 244 | self.end = vector.start + end 245 | } 246 | 247 | public var count: Int { return end - start } 248 | 249 | public func conj(_ element: T) -> SubSequence { 250 | return Subvec(vector: v.assoc(index: end, element), start: start, end: end + 1) 251 | } 252 | 253 | public func pop() -> SubSequence { 254 | precondition(count > 0, "Cannot pop() an empty vector") 255 | return Subvec(vector: v, start: start, end: end - 1) 256 | } 257 | 258 | public func assoc(index: Index, _ element: T) -> SubSequence { 259 | if index == count { 260 | return conj(element) 261 | } else { 262 | return Subvec(vector: v.assoc(index: index + start, element), start: start, end: end) 263 | } 264 | } 265 | 266 | public subscript(index: Index) -> T { return v[index + start] } 267 | 268 | public subscript(bounds: Range) -> SubSequence { 269 | return Subvec(vector: self, start: bounds.lowerBound, end: bounds.upperBound) 270 | } 271 | 272 | public func makeIterator() -> Iterator { 273 | return ChunkedIterator(f: {self.v.getChunk(index: $0)}, start: start, end: end) 274 | } 275 | 276 | public func concat(_ rhs: Other) -> Subvec where Other.Element == Element { 277 | return rhs.reduce(self) { $0.conj($1) } 278 | } 279 | } 280 | 281 | // 282 | // MARK: - TransientVector 283 | // 284 | 285 | private var transientVectorCounter = 0 286 | 287 | public struct TransientVector { 288 | public typealias Element = T 289 | public typealias Index = Int 290 | 291 | public var count: Int 292 | private var shift: Int 293 | private var root: Node 294 | private var tail: [T] 295 | 296 | private init(count: Int, shift: Int, root: Node, tail: [T]) { 297 | assert(count >= 0) 298 | assert(shift > 0) 299 | assert(shift % 5 == 0) 300 | assert(root.editID != 0) 301 | 302 | self.count = count 303 | self.shift = shift 304 | self.root = root 305 | 306 | self.tail = [] 307 | self.tail.reserveCapacity(32) 308 | self.tail.append(contentsOf: tail) 309 | } 310 | 311 | private init() { 312 | transientVectorCounter += 1 313 | self.init(count: 0, shift: 5, root: TreeNode(transientID: transientVectorCounter, children: []), tail: []) 314 | } 315 | 316 | init(vector: PersistentVector) { 317 | transientVectorCounter += 1 318 | self.init(count: vector.count, shift: vector.shift, root: vector.root.transientVersion(transientVectorCounter), tail: vector.tail) 319 | } 320 | 321 | private func tailOffset() -> Int { 322 | let r = count < 32 ? 0 : (((count - 1) >> 5) << 5) 323 | assert(r == count - tail.count) 324 | return r 325 | } 326 | 327 | private func verifyBounds(index: Int) { 328 | precondition(index >= 0 && index < count, "Index \(index) is out of bounds for vector of size \(count)") 329 | } 330 | 331 | private func verifyTransient() { 332 | precondition(root.editID != 0, "Cannot modify TransientVector after persistent()") 333 | assert(tail.capacity == 32) 334 | } 335 | 336 | private func transientChunk(index: Int, chunk: inout [T]) { 337 | chunk = index < tailOffset() ? root.getChunk(index, shift: self.shift) : tail 338 | } 339 | 340 | internal func getChunk(index: Index) -> (chunk: [T], offset: Int) { 341 | let chunk = index < tailOffset() ? root.getChunk(index, shift: self.shift) : tail 342 | return (chunk: chunk, offset: index & 0x01f) 343 | } 344 | 345 | public mutating func conj(_ element: T) -> TransientVector { 346 | verifyTransient() 347 | 348 | if count - tailOffset() < 32 { 349 | tail.append(element) 350 | } else { 351 | let tailNode = LeafNode(transientID: root.editID, children: tail) 352 | 353 | tail = [] 354 | tail.reserveCapacity(32) 355 | tail.append(element) 356 | 357 | if count >> 5 > 1 << shift { 358 | root = TreeNode(transientID: root.editID, children: [root, tailNode.newPath(root.editID, shift: shift)]) 359 | shift += 5 360 | } else { 361 | root = root.transientPushTail(root.editID, count: count, shift: shift, tailNode: tailNode) 362 | } 363 | } 364 | 365 | count += 1 366 | return self 367 | } 368 | 369 | public mutating func persistent() -> PersistentVector { 370 | verifyTransient() 371 | 372 | root.editID = 0 373 | 374 | var newTail = [T]() 375 | newTail.reserveCapacity(tail.count) 376 | newTail.append(contentsOf: tail) 377 | 378 | return PersistentVector(count: count, shift: shift, root: root, tail: newTail) 379 | } 380 | 381 | public mutating func pop() -> TransientVector { 382 | verifyTransient() 383 | 384 | switch count { 385 | case 0: 386 | preconditionFailure("Cannot pop() an empty vector") 387 | case 1: 388 | return TransientVector() 389 | case _ where count - tailOffset() > 1: 390 | tail.removeLast() 391 | default: 392 | assert(count - 2 < tailOffset()) 393 | 394 | transientChunk(index: count - 2, chunk: &tail) 395 | 396 | if let r = root.transientPopTail(root.editID, count: count, shift: shift) { 397 | if let r2 = r.onlyChildNode(), shift > 5 { 398 | shift -= 5 399 | root = r2 400 | } else { 401 | root = r 402 | } 403 | } else { 404 | assert(shift == 5) 405 | root = TreeNode(transientID: root.editID) 406 | } 407 | } 408 | 409 | count -= 1 410 | return self 411 | } 412 | 413 | public mutating func assoc(index: Int, _ element: T) -> TransientVector { 414 | verifyTransient() 415 | 416 | if index == count { 417 | return self.conj(element) 418 | } 419 | 420 | verifyBounds(index: index) 421 | 422 | if tailOffset() <= index { 423 | tail[index & 0x01f] = element 424 | } else { 425 | root = root.transientAssoc(root.editID, index: index, shift: shift, element: element) 426 | } 427 | 428 | return self 429 | } 430 | 431 | public var startIndex: Index { return 0 } 432 | 433 | public subscript(index: Index) -> T { 434 | verifyBounds(index: index) 435 | let t = getChunk(index: index) 436 | return t.chunk[t.offset] 437 | } 438 | 439 | public mutating func concat(_ rhs: Other) -> TransientVector where Other.Element == T { 440 | return rhs.reduce(into: self) { $0 = $0.conj($1) } 441 | } 442 | } 443 | 444 | // 445 | // MARK: - Extensions 446 | // 447 | 448 | extension CachableSeqHashValue { 449 | public func hash(into hasher: inout Hasher) { 450 | for x in self { 451 | hasher.combine(x) 452 | } 453 | } 454 | 455 | public var hashValue: Int { 456 | return cachedHashValue.cached { 457 | var hasher = Hasher() 458 | hash(into: &hasher) 459 | return hasher.finalize() 460 | } 461 | } 462 | } 463 | 464 | extension PersistentVector : CustomStringConvertible, CustomDebugStringConvertible 465 | { 466 | public var description: String { return seqDescription(xs: self, ldelim: "[", rdelim: "]") } 467 | 468 | public var debugDescription: String { return seqDebugDescription(xs: self, ldelim: "[", rdelim: "]") } 469 | } 470 | 471 | extension PersistentVector : Collection 472 | { 473 | public var startIndex: Index { return 0 } 474 | 475 | public var endIndex: Index { return self.count } 476 | 477 | public func index(after i: Index) -> Index { 478 | assert( i < endIndex) 479 | return i + 1 480 | } 481 | } 482 | 483 | extension Subvec : CustomStringConvertible, CustomDebugStringConvertible 484 | { 485 | public var description: String { return seqDescription(xs: self, ldelim: "[", rdelim: "]") } 486 | 487 | public var debugDescription: String { return seqDebugDescription(xs: self, ldelim: "[", rdelim: "]") } 488 | } 489 | 490 | extension Subvec : Collection 491 | { 492 | public var startIndex: Index { return 0 } 493 | public var endIndex: Index { return self.count } 494 | 495 | public func index(after i: Index) -> Index { 496 | assert( i < endIndex) 497 | return i + 1 498 | } 499 | } 500 | 501 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/ruminantTests/chunkediteratorTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // chunkediteratorTest.swift 3 | // ruminantTests 4 | // 5 | // Created by Stefan Boether on 28.12.17. 6 | 7 | 8 | import XCTest 9 | @testable import ruminant 10 | 11 | class ChunkedIteratorTest: XCTestCase { 12 | 13 | func testChunking() { 14 | 15 | // sequence of 32 elements chunked to size 5 (5,5,5,5,5,5,2) 16 | let fibonacciChunked = 17 | [ 18 | [0, 1, 1, 2, 3], 19 | [5, 8, 13, 21, 34], 20 | [55, 89, 144, 233, 377], 21 | [610, 987, 1597, 2584, 4181], 22 | [6765, 10946, 17711, 28657, 46368], 23 | [75025, 121393, 196418, 317811, 514229], 24 | [832040, 1346269] 25 | ] 26 | 27 | let flatten = fibonacciChunked.flatMap { $0 } 28 | 29 | let getChunk = 30 | { (chunk: fibonacciChunked[$0 / 5], offset: $0 % 5) } 31 | 32 | let sequence = AnySequence { ChunkedIterator(f: getChunk, start: 0, end: 32) }.map { $0} 33 | XCTAssertEqual(flatten, sequence, "ChunkedIterator") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Tests/ruminantTests/exampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // exampleTests.swift 3 | // ruminantTests 4 | // 5 | // Created by Jonas De Vuyst on 19/4/20. 6 | // 7 | 8 | import XCTest 9 | import ruminant 10 | 11 | class ExampleTest: XCTestCase { 12 | func testCoreOperations() { 13 | let v: PersistentVector = ["a", "b", "c"] 14 | let v2 = v.conj("d").assoc(index: 2, "C") 15 | XCTAssertEqual(v, ["a", "b", "c"]) 16 | XCTAssertEqual(v2, ["a", "b", "C", "d"]) 17 | XCTAssert(v.pop() == v2[0..<2]) 18 | XCTAssertEqual(v.map {$0.uppercased()}, ["A", "B", "C"]) 19 | XCTAssertEqual(v[1], "b") 20 | XCTAssert(v[0...1] == v.pop()) 21 | } 22 | 23 | func testTransientVectors() { 24 | let v: PersistentVector = ["a", "b", "c"] 25 | var tmp = v.transient() 26 | tmp = tmp.pop() 27 | tmp = tmp.conj("3") 28 | tmp = tmp.conj("4") 29 | XCTAssert(tmp.persistent() == ["a", "b", "3", "4"]) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/ruminantTests/quickCheckTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // quickCheckTests.swift 3 | // ruminantTests 4 | // 5 | // Created by Jonas De Vuyst on 18/4/20. 6 | // 7 | 8 | import XCTest 9 | @testable import SwiftCheck 10 | @testable import ruminant 11 | 12 | extension PersistentVector: Arbitrary where Element: Arbitrary { 13 | public static var arbitrary: Gen { 14 | return Array.arbitrary.map(Self.init) 15 | } 16 | } 17 | 18 | extension Subvec: Arbitrary where Element: Arbitrary { 19 | public static var arbitrary: Gen { 20 | return Gen 21 | .zip(PersistentVector.arbitrary, Int.arbitrary, Int.arbitrary) 22 | .suchThat { (v, l, u) in l >= 0 && l <= u && u < v.count } 23 | .map(Self.init) 24 | } 25 | } 26 | 27 | extension TransientVector: Arbitrary where Element: Arbitrary { 28 | public static var arbitrary: Gen { 29 | return PersistentVector.arbitrary.map(Self.init) 30 | } 31 | } 32 | 33 | extension Collection where Index: Arbitrary, Index: Hashable { 34 | var arbitraryPartition: Gen<[SubSequence]> { 35 | if isEmpty { 36 | let subseq = self[startIndex...fromInitialSegments(of: indices.shuffled()) 41 | .map { [startIndex, endIndex] cuts in 42 | Array(Set([startIndex, endIndex] + cuts)).sorted() 43 | }.map { cuts in 44 | return zip(cuts.dropLast(), cuts.dropFirst()).map { (l, u) in 45 | return self[l.. { 52 | typealias Element = T 53 | 54 | case conj(T) 55 | case pop 56 | case assoc(Int, T) 57 | case concat([T]) 58 | } 59 | 60 | extension Op: Arbitrary where Element: Arbitrary { 61 | typealias GenType = Gen> 62 | 63 | static var arbitrary: GenType { 64 | Int.arbitrary.suchThat({$0 >= 0}).flatMap(Self.arbitrary(forSize:)) 65 | } 66 | 67 | static func arbitrary(forSize size: Int) -> GenType { 68 | let conjOp: GenType = 69 | T.arbitrary.map { .conj($0) } 70 | let popOp: GenType? = 71 | size > 0 ? Gen.pure(.pop) : nil 72 | let assocOp: GenType = 73 | Gen.zip(Gen.choose((0, size)), Element.arbitrary).map{ .assoc($0, $1) } 74 | let concatOp: GenType = 75 | [T].arbitrary.map { .concat($0) } 76 | let ops = [(1, conjOp), (3, popOp), (5, assocOp), (1, concatOp)] 77 | return Gen.frequency(ops.compactMap { 78 | switch $0 { 79 | case let (n, .some(op)): return .some((n, op)) 80 | case (_, .none): return .none 81 | } 82 | }) 83 | } 84 | 85 | static func arbitraryList(forSize origSize: Int) -> Gen<[Op]> { 86 | Gen.sized { n in 87 | func next(size: Int, ops: [Op]) -> Gen<(Int, [Op])> { 88 | return Self.arbitrary(forSize: size).map { op in 89 | let newSize: Int 90 | switch op { 91 | case .pop: newSize = size - 1 92 | default: newSize = size 93 | } 94 | return (newSize, ops + [op]) 95 | } 96 | } 97 | 98 | var gen: Gen<(Int, [Op])> = Gen.pure((origSize, [])) 99 | for _ in 0 ..< n { 100 | gen = gen.flatMap { (size, ops) in 101 | return next(size: size, ops: ops) 102 | } 103 | } 104 | 105 | return gen.map {$0.1} 106 | } 107 | } 108 | } 109 | 110 | protocol CanApplyOp { 111 | associatedtype Element 112 | 113 | mutating func apply(op: Op) -> Self 114 | } 115 | 116 | extension CanApplyOp { 117 | mutating func applyAll(ops: [Op]) -> Self { 118 | for op in ops { 119 | self = self.apply(op: op) 120 | } 121 | return self 122 | } 123 | } 124 | 125 | extension PersistentVector: CanApplyOp { 126 | func apply(op: Op) -> PersistentVector { 127 | switch op { 128 | case let .conj(x): return conj(x) 129 | case .pop: return pop() 130 | case let .assoc(idx, x): return assoc(index: idx, x) 131 | case let .concat(xs): return concat(xs) 132 | } 133 | } 134 | } 135 | 136 | extension Subvec: CanApplyOp { 137 | func apply(op: Op) -> Subvec { 138 | switch op { 139 | case let .conj(x): return conj(x) 140 | case .pop: return pop() 141 | case let .assoc(idx, x): return assoc(index: idx, x) 142 | case let .concat(xs): return concat(xs) 143 | } 144 | } 145 | } 146 | 147 | extension TransientVector: CanApplyOp { 148 | mutating func apply(op: Op) -> TransientVector { 149 | switch op { 150 | case let .conj(x): return conj(x) 151 | case .pop: return pop() 152 | case let .assoc(idx, x): return assoc(index: idx, x) 153 | case let .concat(xs): return concat(xs) 154 | } 155 | } 156 | } 157 | 158 | class PersistentVectorTypeTests where T: PersistentVectorType, T: Arbitrary, T.Element: Arbitrary, T.Element: Hashable, T.SubSequence: PersistentVectorType, T.Index == Int { 159 | let name: String 160 | 161 | init(name: String) { 162 | self.name = name 163 | } 164 | 165 | func testEquality() { 166 | property(name + " == is an equivalence relation") <- ( 167 | (forAll { (xs: T) in xs == xs }) "Reflexivity" 168 | ^&&^ 169 | (forAll { (xs: T, ys: T) in (xs == ys) == (ys == xs) }) "Symmetry" 170 | ^&&^ 171 | (forAll { (xs: T, ys: T, zs: T) in !(xs == ys && ys == zs) || (xs == zs) }) "Transitivity" 172 | ) 173 | 174 | property(name + " == uses value equality") <- forAll { (xs: T, ys: T) in 175 | return (Array(xs) == Array(ys)) == (xs == ys) 176 | } 177 | 178 | property(name + " == implies identical hash value") <- forAll { (xs: T, ys: T) in 179 | return xs != ys || xs.hashValue == ys.hashValue 180 | } 181 | } 182 | 183 | func testGet() { 184 | property(name + " has consistent indices") <- forAll { (xs: T) in 185 | return Array(xs) == (0 ..< xs.count).map {xs[$0]} 186 | } 187 | } 188 | 189 | func testAssoc() { 190 | property(name + " assoc() sets elements") 191 | <- forAll(T.arbitrary.suchThat({$0.count > 0})) { xs in 192 | return forAll(Gen.fromElements(in: 0 ... xs.count - 1)) { i in 193 | return forAll { (y: T.Element) in 194 | let prev = xs[i] 195 | let new = xs.assoc(index: i, y) 196 | return 197 | (new[i] == y) "Set value" 198 | ^&&^ 199 | (new.assoc(index: i, prev) == xs) "Unset value" 200 | } 201 | } 202 | } 203 | } 204 | 205 | func testConjPop() { 206 | property(name + " conj() appends") <- forAll { (xs: T, y: T.Element) in 207 | return xs.conj(y)[xs.count] == y 208 | } 209 | 210 | property(name + " pop() after conj() is a no-op") <- forAll { (xs: T, y: T.Element) in 211 | return xs == xs.conj(y).pop() 212 | } 213 | } 214 | 215 | func testConjAssoc() { 216 | property(name + " assoc() can append elements") <- forAll { (xs: T, y: T.Element) in 217 | return xs.conj(y) == xs.assoc(index: xs.count, y) 218 | } 219 | } 220 | 221 | func testConcat() { 222 | property(name + " concat() concatenates vectors") <- forAll { (xs: T, ys: T) in 223 | return Array(xs) + Array(ys) == Array(xs.concat(ys)) 224 | } 225 | 226 | property(name + " concat([]) is a no-op") <- forAll { (xs: T) in 227 | return xs.concat([]) == xs 228 | } 229 | 230 | property(name + " xs.concat([y]) == xs.conj(y)") <- forAll { (xs: T, y: T.Element) in 231 | return xs.concat([y]) == xs.conj(y) 232 | } 233 | } 234 | 235 | func testSubvec() { 236 | property("Taking Subvec of full PersistentVector preserves equality") <- forAll { (xs: T) in 237 | return xs == xs[0.., _ rhs: T.SubSequence) -> PersistentVector { 246 | lhs.concat(rhs) 247 | } 248 | return xs.arbitraryPartition.map { xs == $0.reduce([], f) } 249 | } 250 | } 251 | 252 | func testAll() { 253 | testEquality() 254 | testGet() 255 | testAssoc() 256 | testConjPop() 257 | testConjAssoc() 258 | testConcat() 259 | testSubvec() 260 | } 261 | } 262 | 263 | class QuickCheckTest: XCTestCase { 264 | typealias ElementType = Int 265 | 266 | func testPersistentVectorType() { 267 | PersistentVectorTypeTests>(name: "PersistentVector").testAll() 268 | PersistentVectorTypeTests>(name: "Subvec").testAll() 269 | } 270 | 271 | func testToAndFromArray() { 272 | property("To-array after from-array is a no-op") <- forAll { (xs: [ElementType]) in 273 | return xs == Array(PersistentVector(xs)) 274 | } 275 | } 276 | 277 | func testManyOps() { 278 | property("All vector types behave alike") <- forAll { (subvec: Subvec) in 279 | return forAll(Op.arbitraryList(forSize: subvec.count)) { ops in 280 | var persistent = PersistentVector(subvec) 281 | var subvec = subvec 282 | var transient = persistent.transient() 283 | persistent = persistent.applyAll(ops: ops) 284 | subvec = subvec.applyAll(ops: ops) 285 | transient = transient.applyAll(ops: ops) 286 | return 287 | (persistent == transient.persistent()) "Transient" 288 | ^&&^ 289 | (persistent == subvec && persistent.hashValue == subvec.hashValue) "Subvec" 290 | } 291 | } 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /Tests/ruminantTests/utilTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UtilTest.swift 3 | // ruminant 4 | // 5 | // Created by Stefan Boether on 28.12.17. 6 | 7 | 8 | import XCTest 9 | @testable import ruminant 10 | 11 | class UtilTest: XCTestCase { 12 | 13 | func testArrayPop() { 14 | let fibonacci = [0,1,1,3,5,8,13] 15 | let newArray = arrayPop(fibonacci) 16 | XCTAssertEqual([0,1,1,3,5,8], newArray, "arrayPop") 17 | } 18 | 19 | func testArrayConj() { 20 | let fibonacci = [0,1,1,3,5,8] 21 | let newArray = arrayConj(fibonacci, val:13) 22 | XCTAssertEqual([0,1,1,3,5,8,13], newArray,"arrayConj") 23 | } 24 | 25 | func testArrayAssoc() { 26 | var fibonacci = [0,1,42,3,5,8,13] 27 | let newArray = arrayAssoc(&fibonacci, idx: 2, val: 1) 28 | XCTAssertEqual([0,1,1,3,5,8,13], newArray, "arrayAssoc") 29 | XCTAssertEqual(1, fibonacci[2]) 30 | } 31 | 32 | func testSeqDescription() { 33 | let fibonacci = [0,1,1,3,5,8,13] 34 | let description = seqDescription(xs: fibonacci, ldelim: "<", rdelim: ">") 35 | XCTAssertEqual("<0, 1, 1, 3, 5, 8, 13>", description, "seqDescription") 36 | } 37 | 38 | 39 | func testSeqDebugDescription() { 40 | let fibonacci : [DualInt] = [0,1,1,3,5,8,13] 41 | let description = seqDebugDescription(xs: fibonacci, ldelim: "[", rdelim: "]") 42 | XCTAssertEqual("[0, 1, 1, 11, 101, 1000, 1101]", description, "seqDebugDescription") 43 | } 44 | } 45 | 46 | struct DualInt : CustomDebugStringConvertible, ExpressibleByIntegerLiteral { 47 | typealias IntegerLiteralType = Int 48 | 49 | private let member : IntegerLiteralType 50 | 51 | public var debugDescription : String { 52 | return String(member, radix: 2) 53 | } 54 | 55 | public init(integerLiteral value: IntegerLiteralType) 56 | { 57 | self.member = value 58 | } 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /Tests/ruminantTests/vectorTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ruminantTests 3 | // 4 | // Created by Stefan Boether on 28.12.17. 5 | 6 | 7 | import XCTest 8 | @testable import ruminant 9 | 10 | private struct Randomized { 11 | typealias Element = UInt32 12 | 13 | static func element() -> Element { 14 | return arc4random() 15 | } 16 | 17 | static func index(forSize size: Int) -> Int { 18 | let idx = Int(arc4random_uniform(UInt32(size))) 19 | assert(idx >= 0) 20 | assert(idx < size) 21 | return idx 22 | } 23 | 24 | typealias ArrayUpdate = (inout [Element]) -> Void 25 | typealias TransientVectorUpdate = (inout TransientVector) -> TransientVector 26 | typealias PersistentVectorUpdate = (PersistentVector) -> PersistentVector 27 | 28 | static func assertEquivalent(initWithSize: (Int) -> Void = { _ in }, 29 | transientUpdate: TransientVectorUpdate = { $0 }, 30 | update: PersistentVectorUpdate, 31 | equivalentArrayUpdate: ArrayUpdate, 32 | minimumSize: UInt32 = 0, 33 | increment: UInt32 = 1, 34 | multiply: UInt32 = 2, 35 | maximumSize: UInt32 = UInt32(UInt16.max) * 4, 36 | caller: String = #function) { 37 | var testSizes: [Int] = [] 38 | let empty: PersistentVector = [] 39 | 40 | var lbound = minimumSize 41 | var ubound = minimumSize 42 | while ubound <= maximumSize { 43 | let size = Int(arc4random_uniform(ubound - lbound) + lbound) 44 | assert(size >= minimumSize) 45 | initWithSize(size) 46 | 47 | var arr: [Element] = [] 48 | arr.reserveCapacity(size) 49 | 50 | var tvec = TransientVector(vector: empty) 51 | 52 | for _ in 0 ..< size { 53 | let el = element() 54 | arr.append(el) 55 | tvec = tvec.conj(el) 56 | } 57 | 58 | assert(arr.count == size) 59 | assert(tvec.count == size) 60 | 61 | tvec = transientUpdate(&tvec) 62 | let vec = update(tvec.persistent()) 63 | equivalentArrayUpdate(&arr) 64 | 65 | XCTAssertEqual(vec.count, arr.count) 66 | 67 | var i = 0 68 | for el in vec { 69 | XCTAssertEqual(el, arr[i]) 70 | i += 1 71 | } 72 | XCTAssertEqual(i, vec.count) 73 | 74 | testSizes.append(size) // tested something meaningful 75 | 76 | lbound = ubound 77 | ubound = (lbound + increment) * multiply 78 | assert(ubound > lbound) 79 | } 80 | 81 | print("Ran", testSizes.count, "equivalence tests on behalf of", caller, 82 | "with vectors and arrays of size", testSizes) 83 | 84 | XCTAssertGreaterThan(testSizes.reduce(0, +), 0) 85 | } 86 | } 87 | 88 | class VectorTest: XCTestCase { 89 | 90 | func testConj() { 91 | 92 | let empty = PersistentVector() 93 | XCTAssertEqual(0, empty.count) 94 | 95 | let seq = empty.conj(0).conj(1).conj(3) 96 | XCTAssertEqual(3, seq.count) 97 | 98 | XCTAssertEqual("[0, 1, 3]", seq.description) 99 | } 100 | 101 | func testConjRandomized() { 102 | let el1 = Randomized.element() 103 | let el2 = Randomized.element() 104 | 105 | Randomized.assertEquivalent( 106 | transientUpdate: { $0.conj(el1) }, 107 | update: { $0.conj(el2) }, 108 | equivalentArrayUpdate: { $0 += [el1, el2] }) 109 | } 110 | 111 | func testPop() { 112 | let empty = PersistentVector() 113 | XCTAssertEqual(0, empty.count) 114 | 115 | let seq = empty.conj(0).conj(1).conj(3) 116 | XCTAssertEqual(3, seq.count) 117 | 118 | let smallerSeq = seq.pop() 119 | 120 | XCTAssertEqual("[0, 1]", smallerSeq.description) 121 | } 122 | 123 | func testPopRandomized() { 124 | Randomized.assertEquivalent( 125 | transientUpdate: { $0.pop() }, 126 | update: { $0.pop() }, 127 | equivalentArrayUpdate: { arr in 128 | arr.removeLast() 129 | arr.removeLast() }, 130 | minimumSize: 2) 131 | } 132 | 133 | func testAssoc() { 134 | 135 | let seq : PersistentVector = [0,1,3] 136 | 137 | let changedSeq = seq 138 | .assoc(index: 0, 42) // replace existing element 139 | .assoc(index: 3, 9000) // append new element 140 | 141 | XCTAssertEqual("[42, 1, 3, 9000]", changedSeq.description) 142 | XCTAssertEqual("[0, 1, 3]", seq.description) 143 | } 144 | 145 | func testAssocRandomized() { 146 | let el1 = Randomized.element() 147 | let el2 = Randomized.element() 148 | 149 | var idx1: Int! 150 | var idx2: Int! 151 | 152 | Randomized.assertEquivalent( 153 | initWithSize: { size in 154 | idx1 = Randomized.index(forSize: size) 155 | idx2 = Randomized.index(forSize: size) }, 156 | transientUpdate: { $0.assoc(index: idx1, el1) }, 157 | update: { $0.assoc(index: idx2, el2) }, 158 | equivalentArrayUpdate: { arr in 159 | arr[idx1] = el1 160 | arr[idx2] = el2 }, 161 | minimumSize: 2) 162 | } 163 | 164 | func testCollection() { 165 | 166 | let seq : PersistentVector = [0,1,3] 167 | 168 | XCTAssertEqual(0, seq[0], "subscript") 169 | XCTAssertEqual(1, seq[1], "subscript") 170 | XCTAssertEqual(3, seq[2], "subscript") 171 | 172 | func f(_ x: Int) -> Float { 173 | return sqrtf(Float(x)) 174 | } 175 | 176 | let size = Int(UInt16.max) 177 | let empty: PersistentVector = [] 178 | var tvec = TransientVector(vector: empty) 179 | for i in 0 ..< size { 180 | tvec = tvec.conj(f(i)) 181 | } 182 | let vec = tvec.persistent() 183 | 184 | for i in (0 ..< size).reversed() { 185 | XCTAssertEqual(f(i), vec[i]) 186 | } 187 | } 188 | 189 | func testCompare() { 190 | let seqA : PersistentVector = [0,1,2,3,5,6] 191 | 192 | let seqB = PersistentVector().conj(0).conj(1).conj(2).conj(3).conj(5).conj(6) 193 | 194 | XCTAssert(seqA == seqB) 195 | XCTAssert(seqA == seqA) 196 | XCTAssert(seqA != seqA.conj(7)) 197 | XCTAssert(seqA != seqA.assoc(index: 1, 9000)) 198 | } 199 | 200 | func testSubVector() { 201 | let arr = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 202 | 610, 987, 1597, 2584, 4181,6765, 10946, 17711, 28657, 46368, 203 | 75025, 121393, 196418, 317811, 514229,832040, 1346269] 204 | let seq = PersistentVector(arr) 205 | 206 | XCTAssert( PersistentVector(arr[...5]) == seq[...5]) 207 | XCTAssert( PersistentVector(arr[..<5]) == seq[..<5]) 208 | 209 | XCTAssert( PersistentVector(arr[5...15]) == seq[5...15]) 210 | XCTAssert( PersistentVector(arr[5..<15]) == seq[5..<15]) 211 | 212 | XCTAssert( PersistentVector(arr[5...]) == seq[5...]) 213 | 214 | let sum = seq.reduce(0,+) 215 | XCTAssertEqual(3524577, sum) 216 | 217 | let seq2 = seq.assoc(index: 30, 0) 218 | let sum2 = seq2.reduce(0,+) 219 | XCTAssertEqual(2692537, sum2) 220 | 221 | let sum3 = seq.reduce(0,+) 222 | XCTAssertEqual(3524577, sum3) 223 | 224 | } 225 | 226 | func testSubVectorRandomized() { 227 | var range: CountableClosedRange! 228 | 229 | Randomized.assertEquivalent( 230 | initWithSize: { size in 231 | let idx1 = Randomized.index(forSize: size) 232 | let idx2 = Randomized.index(forSize: size) 233 | range = min(idx1, idx2) ... max(idx1, idx2) }, 234 | update: { PersistentVector($0[range]) }, 235 | equivalentArrayUpdate: { arr in 236 | let subArray = arr[range] 237 | arr = Array(subArray) }, 238 | minimumSize: 1) 239 | } 240 | 241 | func testConcat() { 242 | let v1 : PersistentVector = [2, 3, 4, 5] 243 | let v2 : PersistentVector = [20, 30, 40, 50] 244 | XCTAssertEqual(v1.concat(v2), [2, 3, 4, 5, 20, 30, 40, 50]) 245 | XCTAssertEqual(PersistentVector(v1[1...2].concat(v2[1...2])), [3, 4, 30, 40]) 246 | } 247 | 248 | func testNoHashSharing() { 249 | let ops: [(inout TransientVector) -> TransientVector] = 250 | [{$0.pop()}, {$0.conj(9)}, {$0.assoc(index: 0, 9)}, {$0.concat([9])}] 251 | for f in ops { 252 | let xs: PersistentVector = [1, 2, 3] 253 | let h1 = xs.hashValue // force hash value to be computed 254 | var transient = xs.transient() 255 | let h2 = xs.hashValue 256 | transient = f(&transient) 257 | let ys = transient.persistent() 258 | let g = ys.hashValue 259 | let h3 = xs.hashValue 260 | XCTAssertEqual(h1, h2) 261 | XCTAssertEqual(h2, h3) 262 | XCTAssertNotEqual(h1, g) 263 | } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /ruminant.xcodeproj/SwiftCheck_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ruminant.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | "ruminant::ruminantPackageTests::ProductTarget" /* ruminantPackageTests */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = OBJ_93 /* Build configuration list for PBXAggregateTarget "ruminantPackageTests" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | OBJ_96 /* PBXTargetDependency */, 17 | ); 18 | name = ruminantPackageTests; 19 | productName = ruminantPackageTests; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | A4126EB6244C51B3000AEC79 /* exampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4126EB5244C51B3000AEC79 /* exampleTests.swift */; }; 25 | OBJ_102 /* chunkediteratorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_22 /* chunkediteratorTest.swift */; }; 26 | OBJ_103 /* quickCheckTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_23 /* quickCheckTests.swift */; }; 27 | OBJ_104 /* utilTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_24 /* utilTest.swift */; }; 28 | OBJ_105 /* vectorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_25 /* vectorTest.swift */; }; 29 | OBJ_107 /* SwiftCheck.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "SwiftCheck::SwiftCheck::Product" /* SwiftCheck.framework */; }; 30 | OBJ_108 /* ruminant.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "ruminant::ruminant::Product" /* ruminant.framework */; }; 31 | OBJ_54 /* Arbitrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_29 /* Arbitrary.swift */; }; 32 | OBJ_55 /* Cartesian.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* Cartesian.swift */; }; 33 | OBJ_56 /* Check.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* Check.swift */; }; 34 | OBJ_57 /* CoArbitrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_32 /* CoArbitrary.swift */; }; 35 | OBJ_58 /* Compose.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* Compose.swift */; }; 36 | OBJ_59 /* Gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_34 /* Gen.swift */; }; 37 | OBJ_60 /* Lattice.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_35 /* Lattice.swift */; }; 38 | OBJ_61 /* Modifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_36 /* Modifiers.swift */; }; 39 | OBJ_62 /* Property.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_37 /* Property.swift */; }; 40 | OBJ_63 /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_38 /* Random.swift */; }; 41 | OBJ_64 /* Rose.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_39 /* Rose.swift */; }; 42 | OBJ_65 /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_40 /* State.swift */; }; 43 | OBJ_66 /* Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_41 /* Test.swift */; }; 44 | OBJ_67 /* Testable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_42 /* Testable.swift */; }; 45 | OBJ_68 /* WitnessedArbitrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_43 /* WitnessedArbitrary.swift */; }; 46 | OBJ_75 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_44 /* Package.swift */; }; 47 | OBJ_81 /* chunkediterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* chunkediterator.swift */; }; 48 | OBJ_82 /* tree.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* tree.swift */; }; 49 | OBJ_83 /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* util.swift */; }; 50 | OBJ_84 /* vector.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* vector.swift */; }; 51 | OBJ_91 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; 52 | /* End PBXBuildFile section */ 53 | 54 | /* Begin PBXContainerItemProxy section */ 55 | A4126EB3244C47C6000AEC79 /* PBXContainerItemProxy */ = { 56 | isa = PBXContainerItemProxy; 57 | containerPortal = OBJ_1 /* Project object */; 58 | proxyType = 1; 59 | remoteGlobalIDString = "SwiftCheck::SwiftCheck"; 60 | remoteInfo = SwiftCheck; 61 | }; 62 | A4126EB4244C47C6000AEC79 /* PBXContainerItemProxy */ = { 63 | isa = PBXContainerItemProxy; 64 | containerPortal = OBJ_1 /* Project object */; 65 | proxyType = 1; 66 | remoteGlobalIDString = "ruminant::ruminant"; 67 | remoteInfo = ruminant; 68 | }; 69 | A4126EB7244C51B4000AEC79 /* PBXContainerItemProxy */ = { 70 | isa = PBXContainerItemProxy; 71 | containerPortal = OBJ_1 /* Project object */; 72 | proxyType = 1; 73 | remoteGlobalIDString = "ruminant::ruminantTests"; 74 | remoteInfo = ruminantTests; 75 | }; 76 | /* End PBXContainerItemProxy section */ 77 | 78 | /* Begin PBXFileReference section */ 79 | A4126EB5244C51B3000AEC79 /* exampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = exampleTests.swift; sourceTree = ""; }; 80 | OBJ_11 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 81 | OBJ_12 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 82 | OBJ_14 /* chunkediterator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = chunkediterator.swift; sourceTree = ""; }; 83 | OBJ_15 /* tree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = tree.swift; sourceTree = ""; }; 84 | OBJ_16 /* util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = util.swift; sourceTree = ""; }; 85 | OBJ_17 /* vector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = vector.swift; sourceTree = ""; }; 86 | OBJ_22 /* chunkediteratorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = chunkediteratorTest.swift; sourceTree = ""; }; 87 | OBJ_23 /* quickCheckTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = quickCheckTests.swift; sourceTree = ""; }; 88 | OBJ_24 /* utilTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = utilTest.swift; sourceTree = ""; }; 89 | OBJ_25 /* vectorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = vectorTest.swift; sourceTree = ""; }; 90 | OBJ_29 /* Arbitrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Arbitrary.swift; sourceTree = ""; }; 91 | OBJ_30 /* Cartesian.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cartesian.swift; sourceTree = ""; }; 92 | OBJ_31 /* Check.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Check.swift; sourceTree = ""; }; 93 | OBJ_32 /* CoArbitrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoArbitrary.swift; sourceTree = ""; }; 94 | OBJ_33 /* Compose.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Compose.swift; sourceTree = ""; }; 95 | OBJ_34 /* Gen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Gen.swift; sourceTree = ""; }; 96 | OBJ_35 /* Lattice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lattice.swift; sourceTree = ""; }; 97 | OBJ_36 /* Modifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modifiers.swift; sourceTree = ""; }; 98 | OBJ_37 /* Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Property.swift; sourceTree = ""; }; 99 | OBJ_38 /* Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = ""; }; 100 | OBJ_39 /* Rose.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rose.swift; sourceTree = ""; }; 101 | OBJ_40 /* State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = ""; }; 102 | OBJ_41 /* Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Test.swift; sourceTree = ""; }; 103 | OBJ_42 /* Testable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Testable.swift; sourceTree = ""; }; 104 | OBJ_43 /* WitnessedArbitrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WitnessedArbitrary.swift; sourceTree = ""; }; 105 | OBJ_44 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; name = Package.swift; path = /Users/jonasdevuyst/Development/ruminant/.build/checkouts/SwiftCheck/Package.swift; sourceTree = ""; }; 106 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 107 | "SwiftCheck::SwiftCheck::Product" /* SwiftCheck.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SwiftCheck.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 108 | "ruminant::ruminant::Product" /* ruminant.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ruminant.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 109 | "ruminant::ruminantTests::Product" /* ruminantTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; path = ruminantTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 110 | /* End PBXFileReference section */ 111 | 112 | /* Begin PBXFrameworksBuildPhase section */ 113 | OBJ_106 /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 0; 116 | files = ( 117 | OBJ_107 /* SwiftCheck.framework in Frameworks */, 118 | OBJ_108 /* ruminant.framework in Frameworks */, 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | OBJ_69 /* Frameworks */ = { 123 | isa = PBXFrameworksBuildPhase; 124 | buildActionMask = 0; 125 | files = ( 126 | ); 127 | runOnlyForDeploymentPostprocessing = 0; 128 | }; 129 | OBJ_85 /* Frameworks */ = { 130 | isa = PBXFrameworksBuildPhase; 131 | buildActionMask = 0; 132 | files = ( 133 | ); 134 | runOnlyForDeploymentPostprocessing = 0; 135 | }; 136 | /* End PBXFrameworksBuildPhase section */ 137 | 138 | /* Begin PBXGroup section */ 139 | OBJ_13 /* Sources */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | OBJ_14 /* chunkediterator.swift */, 143 | OBJ_15 /* tree.swift */, 144 | OBJ_16 /* util.swift */, 145 | OBJ_17 /* vector.swift */, 146 | ); 147 | path = Sources; 148 | sourceTree = ""; 149 | }; 150 | OBJ_18 /* Tests */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | OBJ_21 /* ruminantTests */, 154 | ); 155 | path = Tests; 156 | sourceTree = SOURCE_ROOT; 157 | }; 158 | OBJ_21 /* ruminantTests */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | OBJ_22 /* chunkediteratorTest.swift */, 162 | OBJ_23 /* quickCheckTests.swift */, 163 | OBJ_24 /* utilTest.swift */, 164 | OBJ_25 /* vectorTest.swift */, 165 | A4126EB5244C51B3000AEC79 /* exampleTests.swift */, 166 | ); 167 | path = ruminantTests; 168 | sourceTree = ""; 169 | }; 170 | OBJ_26 /* Dependencies */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | OBJ_27 /* SwiftCheck 0.12.0 */, 174 | ); 175 | name = Dependencies; 176 | sourceTree = ""; 177 | }; 178 | OBJ_27 /* SwiftCheck 0.12.0 */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | OBJ_28 /* SwiftCheck */, 182 | OBJ_44 /* Package.swift */, 183 | ); 184 | name = "SwiftCheck 0.12.0"; 185 | sourceTree = SOURCE_ROOT; 186 | }; 187 | OBJ_28 /* SwiftCheck */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | OBJ_29 /* Arbitrary.swift */, 191 | OBJ_30 /* Cartesian.swift */, 192 | OBJ_31 /* Check.swift */, 193 | OBJ_32 /* CoArbitrary.swift */, 194 | OBJ_33 /* Compose.swift */, 195 | OBJ_34 /* Gen.swift */, 196 | OBJ_35 /* Lattice.swift */, 197 | OBJ_36 /* Modifiers.swift */, 198 | OBJ_37 /* Property.swift */, 199 | OBJ_38 /* Random.swift */, 200 | OBJ_39 /* Rose.swift */, 201 | OBJ_40 /* State.swift */, 202 | OBJ_41 /* Test.swift */, 203 | OBJ_42 /* Testable.swift */, 204 | OBJ_43 /* WitnessedArbitrary.swift */, 205 | ); 206 | name = SwiftCheck; 207 | path = .build/checkouts/SwiftCheck/Sources/SwiftCheck; 208 | sourceTree = SOURCE_ROOT; 209 | }; 210 | OBJ_45 /* Products */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | "ruminant::ruminantTests::Product" /* ruminantTests.xctest */, 214 | "SwiftCheck::SwiftCheck::Product" /* SwiftCheck.framework */, 215 | "ruminant::ruminant::Product" /* ruminant.framework */, 216 | ); 217 | name = Products; 218 | sourceTree = BUILT_PRODUCTS_DIR; 219 | }; 220 | OBJ_5 /* */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | OBJ_6 /* Package.swift */, 224 | OBJ_7 /* Sources */, 225 | OBJ_18 /* Tests */, 226 | OBJ_26 /* Dependencies */, 227 | OBJ_45 /* Products */, 228 | ); 229 | name = ""; 230 | sourceTree = ""; 231 | }; 232 | OBJ_7 /* Sources */ = { 233 | isa = PBXGroup; 234 | children = ( 235 | OBJ_8 /* ruminant */, 236 | ); 237 | name = Sources; 238 | sourceTree = SOURCE_ROOT; 239 | }; 240 | OBJ_8 /* ruminant */ = { 241 | isa = PBXGroup; 242 | children = ( 243 | OBJ_11 /* LICENSE */, 244 | OBJ_12 /* README.md */, 245 | OBJ_13 /* Sources */, 246 | ); 247 | name = ruminant; 248 | sourceTree = SOURCE_ROOT; 249 | }; 250 | /* End PBXGroup section */ 251 | 252 | /* Begin PBXNativeTarget section */ 253 | "SwiftCheck::SwiftCheck" /* SwiftCheck */ = { 254 | isa = PBXNativeTarget; 255 | buildConfigurationList = OBJ_50 /* Build configuration list for PBXNativeTarget "SwiftCheck" */; 256 | buildPhases = ( 257 | OBJ_53 /* Sources */, 258 | OBJ_69 /* Frameworks */, 259 | ); 260 | buildRules = ( 261 | ); 262 | dependencies = ( 263 | ); 264 | name = SwiftCheck; 265 | productName = SwiftCheck; 266 | productReference = "SwiftCheck::SwiftCheck::Product" /* SwiftCheck.framework */; 267 | productType = "com.apple.product-type.framework"; 268 | }; 269 | "SwiftCheck::SwiftPMPackageDescription" /* SwiftCheckPackageDescription */ = { 270 | isa = PBXNativeTarget; 271 | buildConfigurationList = OBJ_71 /* Build configuration list for PBXNativeTarget "SwiftCheckPackageDescription" */; 272 | buildPhases = ( 273 | OBJ_74 /* Sources */, 274 | ); 275 | buildRules = ( 276 | ); 277 | dependencies = ( 278 | ); 279 | name = SwiftCheckPackageDescription; 280 | productName = SwiftCheckPackageDescription; 281 | productType = "com.apple.product-type.framework"; 282 | }; 283 | "ruminant::SwiftPMPackageDescription" /* ruminantPackageDescription */ = { 284 | isa = PBXNativeTarget; 285 | buildConfigurationList = OBJ_87 /* Build configuration list for PBXNativeTarget "ruminantPackageDescription" */; 286 | buildPhases = ( 287 | OBJ_90 /* Sources */, 288 | ); 289 | buildRules = ( 290 | ); 291 | dependencies = ( 292 | ); 293 | name = ruminantPackageDescription; 294 | productName = ruminantPackageDescription; 295 | productType = "com.apple.product-type.framework"; 296 | }; 297 | "ruminant::ruminant" /* ruminant */ = { 298 | isa = PBXNativeTarget; 299 | buildConfigurationList = OBJ_77 /* Build configuration list for PBXNativeTarget "ruminant" */; 300 | buildPhases = ( 301 | OBJ_80 /* Sources */, 302 | OBJ_85 /* Frameworks */, 303 | ); 304 | buildRules = ( 305 | ); 306 | dependencies = ( 307 | ); 308 | name = ruminant; 309 | productName = ruminant; 310 | productReference = "ruminant::ruminant::Product" /* ruminant.framework */; 311 | productType = "com.apple.product-type.framework"; 312 | }; 313 | "ruminant::ruminantTests" /* ruminantTests */ = { 314 | isa = PBXNativeTarget; 315 | buildConfigurationList = OBJ_98 /* Build configuration list for PBXNativeTarget "ruminantTests" */; 316 | buildPhases = ( 317 | OBJ_101 /* Sources */, 318 | OBJ_106 /* Frameworks */, 319 | ); 320 | buildRules = ( 321 | ); 322 | dependencies = ( 323 | OBJ_109 /* PBXTargetDependency */, 324 | OBJ_110 /* PBXTargetDependency */, 325 | ); 326 | name = ruminantTests; 327 | productName = ruminantTests; 328 | productReference = "ruminant::ruminantTests::Product" /* ruminantTests.xctest */; 329 | productType = "com.apple.product-type.bundle.unit-test"; 330 | }; 331 | /* End PBXNativeTarget section */ 332 | 333 | /* Begin PBXProject section */ 334 | OBJ_1 /* Project object */ = { 335 | isa = PBXProject; 336 | attributes = { 337 | LastSwiftMigration = 9999; 338 | LastUpgradeCheck = 9999; 339 | }; 340 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "ruminant" */; 341 | compatibilityVersion = "Xcode 3.2"; 342 | developmentRegion = en; 343 | hasScannedForEncodings = 0; 344 | knownRegions = ( 345 | en, 346 | ); 347 | mainGroup = OBJ_5 /* */; 348 | productRefGroup = OBJ_45 /* Products */; 349 | projectDirPath = ""; 350 | projectRoot = ""; 351 | targets = ( 352 | "SwiftCheck::SwiftCheck" /* SwiftCheck */, 353 | "SwiftCheck::SwiftPMPackageDescription" /* SwiftCheckPackageDescription */, 354 | "ruminant::ruminant" /* ruminant */, 355 | "ruminant::SwiftPMPackageDescription" /* ruminantPackageDescription */, 356 | "ruminant::ruminantPackageTests::ProductTarget" /* ruminantPackageTests */, 357 | "ruminant::ruminantTests" /* ruminantTests */, 358 | ); 359 | }; 360 | /* End PBXProject section */ 361 | 362 | /* Begin PBXSourcesBuildPhase section */ 363 | OBJ_101 /* Sources */ = { 364 | isa = PBXSourcesBuildPhase; 365 | buildActionMask = 0; 366 | files = ( 367 | A4126EB6244C51B3000AEC79 /* exampleTests.swift in Sources */, 368 | OBJ_102 /* chunkediteratorTest.swift in Sources */, 369 | OBJ_103 /* quickCheckTests.swift in Sources */, 370 | OBJ_104 /* utilTest.swift in Sources */, 371 | OBJ_105 /* vectorTest.swift in Sources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | OBJ_53 /* Sources */ = { 376 | isa = PBXSourcesBuildPhase; 377 | buildActionMask = 0; 378 | files = ( 379 | OBJ_54 /* Arbitrary.swift in Sources */, 380 | OBJ_55 /* Cartesian.swift in Sources */, 381 | OBJ_56 /* Check.swift in Sources */, 382 | OBJ_57 /* CoArbitrary.swift in Sources */, 383 | OBJ_58 /* Compose.swift in Sources */, 384 | OBJ_59 /* Gen.swift in Sources */, 385 | OBJ_60 /* Lattice.swift in Sources */, 386 | OBJ_61 /* Modifiers.swift in Sources */, 387 | OBJ_62 /* Property.swift in Sources */, 388 | OBJ_63 /* Random.swift in Sources */, 389 | OBJ_64 /* Rose.swift in Sources */, 390 | OBJ_65 /* State.swift in Sources */, 391 | OBJ_66 /* Test.swift in Sources */, 392 | OBJ_67 /* Testable.swift in Sources */, 393 | OBJ_68 /* WitnessedArbitrary.swift in Sources */, 394 | ); 395 | runOnlyForDeploymentPostprocessing = 0; 396 | }; 397 | OBJ_74 /* Sources */ = { 398 | isa = PBXSourcesBuildPhase; 399 | buildActionMask = 0; 400 | files = ( 401 | OBJ_75 /* Package.swift in Sources */, 402 | ); 403 | runOnlyForDeploymentPostprocessing = 0; 404 | }; 405 | OBJ_80 /* Sources */ = { 406 | isa = PBXSourcesBuildPhase; 407 | buildActionMask = 0; 408 | files = ( 409 | OBJ_81 /* chunkediterator.swift in Sources */, 410 | OBJ_82 /* tree.swift in Sources */, 411 | OBJ_83 /* util.swift in Sources */, 412 | OBJ_84 /* vector.swift in Sources */, 413 | ); 414 | runOnlyForDeploymentPostprocessing = 0; 415 | }; 416 | OBJ_90 /* Sources */ = { 417 | isa = PBXSourcesBuildPhase; 418 | buildActionMask = 0; 419 | files = ( 420 | OBJ_91 /* Package.swift in Sources */, 421 | ); 422 | runOnlyForDeploymentPostprocessing = 0; 423 | }; 424 | /* End PBXSourcesBuildPhase section */ 425 | 426 | /* Begin PBXTargetDependency section */ 427 | OBJ_109 /* PBXTargetDependency */ = { 428 | isa = PBXTargetDependency; 429 | target = "SwiftCheck::SwiftCheck" /* SwiftCheck */; 430 | targetProxy = A4126EB3244C47C6000AEC79 /* PBXContainerItemProxy */; 431 | }; 432 | OBJ_110 /* PBXTargetDependency */ = { 433 | isa = PBXTargetDependency; 434 | target = "ruminant::ruminant" /* ruminant */; 435 | targetProxy = A4126EB4244C47C6000AEC79 /* PBXContainerItemProxy */; 436 | }; 437 | OBJ_96 /* PBXTargetDependency */ = { 438 | isa = PBXTargetDependency; 439 | target = "ruminant::ruminantTests" /* ruminantTests */; 440 | targetProxy = A4126EB7244C51B4000AEC79 /* PBXContainerItemProxy */; 441 | }; 442 | /* End PBXTargetDependency section */ 443 | 444 | /* Begin XCBuildConfiguration section */ 445 | OBJ_100 /* Release */ = { 446 | isa = XCBuildConfiguration; 447 | buildSettings = { 448 | CLANG_ENABLE_MODULES = YES; 449 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 450 | FRAMEWORK_SEARCH_PATHS = ( 451 | "$(inherited)", 452 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 453 | ); 454 | HEADER_SEARCH_PATHS = "$(inherited)"; 455 | INFOPLIST_FILE = ruminant.xcodeproj/ruminantTests_Info.plist; 456 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 457 | OTHER_CFLAGS = "$(inherited)"; 458 | OTHER_LDFLAGS = "$(inherited)"; 459 | OTHER_SWIFT_FLAGS = "$(inherited)"; 460 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 461 | TARGET_NAME = ruminantTests; 462 | }; 463 | name = Release; 464 | }; 465 | OBJ_3 /* Debug */ = { 466 | isa = XCBuildConfiguration; 467 | buildSettings = { 468 | CLANG_ENABLE_OBJC_ARC = YES; 469 | COMBINE_HIDPI_IMAGES = YES; 470 | COPY_PHASE_STRIP = NO; 471 | DEBUG_INFORMATION_FORMAT = dwarf; 472 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 473 | ENABLE_NS_ASSERTIONS = YES; 474 | GCC_OPTIMIZATION_LEVEL = 0; 475 | GCC_PREPROCESSOR_DEFINITIONS = ( 476 | "$(inherited)", 477 | "SWIFT_PACKAGE=1", 478 | "DEBUG=1", 479 | ); 480 | MACOSX_DEPLOYMENT_TARGET = 10.10; 481 | ONLY_ACTIVE_ARCH = YES; 482 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | SDKROOT = macosx; 485 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 486 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE DEBUG"; 487 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 488 | SWIFT_VERSION = 5.0; 489 | USE_HEADERMAP = NO; 490 | }; 491 | name = Debug; 492 | }; 493 | OBJ_4 /* Release */ = { 494 | isa = XCBuildConfiguration; 495 | buildSettings = { 496 | CLANG_ENABLE_OBJC_ARC = YES; 497 | COMBINE_HIDPI_IMAGES = YES; 498 | COPY_PHASE_STRIP = YES; 499 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 500 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 501 | GCC_OPTIMIZATION_LEVEL = s; 502 | GCC_PREPROCESSOR_DEFINITIONS = ( 503 | "$(inherited)", 504 | "SWIFT_PACKAGE=1", 505 | ); 506 | MACOSX_DEPLOYMENT_TARGET = 10.10; 507 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; 508 | PRODUCT_NAME = "$(TARGET_NAME)"; 509 | SDKROOT = macosx; 510 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 511 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE"; 512 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 513 | SWIFT_VERSION = 5.0; 514 | USE_HEADERMAP = NO; 515 | }; 516 | name = Release; 517 | }; 518 | OBJ_51 /* Debug */ = { 519 | isa = XCBuildConfiguration; 520 | buildSettings = { 521 | ENABLE_TESTABILITY = YES; 522 | FRAMEWORK_SEARCH_PATHS = ( 523 | "$(inherited)", 524 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 525 | ); 526 | HEADER_SEARCH_PATHS = "$(inherited)"; 527 | INFOPLIST_FILE = ruminant.xcodeproj/SwiftCheck_Info.plist; 528 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 529 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 530 | MACOSX_DEPLOYMENT_TARGET = 10.10; 531 | OTHER_CFLAGS = "$(inherited)"; 532 | OTHER_LDFLAGS = "$(inherited)"; 533 | OTHER_SWIFT_FLAGS = "$(inherited)"; 534 | PRODUCT_BUNDLE_IDENTIFIER = SwiftCheck; 535 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 536 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 537 | SKIP_INSTALL = YES; 538 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 539 | SWIFT_VERSION = 5.0; 540 | TARGET_NAME = SwiftCheck; 541 | TVOS_DEPLOYMENT_TARGET = 9.0; 542 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 543 | }; 544 | name = Debug; 545 | }; 546 | OBJ_52 /* Release */ = { 547 | isa = XCBuildConfiguration; 548 | buildSettings = { 549 | ENABLE_TESTABILITY = YES; 550 | FRAMEWORK_SEARCH_PATHS = ( 551 | "$(inherited)", 552 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 553 | ); 554 | HEADER_SEARCH_PATHS = "$(inherited)"; 555 | INFOPLIST_FILE = ruminant.xcodeproj/SwiftCheck_Info.plist; 556 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 557 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 558 | MACOSX_DEPLOYMENT_TARGET = 10.10; 559 | OTHER_CFLAGS = "$(inherited)"; 560 | OTHER_LDFLAGS = "$(inherited)"; 561 | OTHER_SWIFT_FLAGS = "$(inherited)"; 562 | PRODUCT_BUNDLE_IDENTIFIER = SwiftCheck; 563 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 564 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 565 | SKIP_INSTALL = YES; 566 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 567 | SWIFT_VERSION = 5.0; 568 | TARGET_NAME = SwiftCheck; 569 | TVOS_DEPLOYMENT_TARGET = 9.0; 570 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 571 | }; 572 | name = Release; 573 | }; 574 | OBJ_72 /* Debug */ = { 575 | isa = XCBuildConfiguration; 576 | buildSettings = { 577 | LD = /usr/bin/true; 578 | OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 5.0.0"; 579 | }; 580 | name = Debug; 581 | }; 582 | OBJ_73 /* Release */ = { 583 | isa = XCBuildConfiguration; 584 | buildSettings = { 585 | LD = /usr/bin/true; 586 | OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 5.0.0"; 587 | }; 588 | name = Release; 589 | }; 590 | OBJ_78 /* Debug */ = { 591 | isa = XCBuildConfiguration; 592 | buildSettings = { 593 | ENABLE_TESTABILITY = YES; 594 | FRAMEWORK_SEARCH_PATHS = ( 595 | "$(inherited)", 596 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 597 | ); 598 | HEADER_SEARCH_PATHS = "$(inherited)"; 599 | INFOPLIST_FILE = ruminant.xcodeproj/ruminant_Info.plist; 600 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 601 | OTHER_CFLAGS = "$(inherited)"; 602 | OTHER_LDFLAGS = "$(inherited)"; 603 | OTHER_SWIFT_FLAGS = "$(inherited)"; 604 | PRODUCT_BUNDLE_IDENTIFIER = ruminant; 605 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 606 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 607 | SKIP_INSTALL = YES; 608 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 609 | TARGET_NAME = ruminant; 610 | }; 611 | name = Debug; 612 | }; 613 | OBJ_79 /* Release */ = { 614 | isa = XCBuildConfiguration; 615 | buildSettings = { 616 | ENABLE_TESTABILITY = YES; 617 | FRAMEWORK_SEARCH_PATHS = ( 618 | "$(inherited)", 619 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 620 | ); 621 | HEADER_SEARCH_PATHS = "$(inherited)"; 622 | INFOPLIST_FILE = ruminant.xcodeproj/ruminant_Info.plist; 623 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 624 | OTHER_CFLAGS = "$(inherited)"; 625 | OTHER_LDFLAGS = "$(inherited)"; 626 | OTHER_SWIFT_FLAGS = "$(inherited)"; 627 | PRODUCT_BUNDLE_IDENTIFIER = ruminant; 628 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 629 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 630 | SKIP_INSTALL = YES; 631 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 632 | TARGET_NAME = ruminant; 633 | }; 634 | name = Release; 635 | }; 636 | OBJ_88 /* Debug */ = { 637 | isa = XCBuildConfiguration; 638 | buildSettings = { 639 | LD = /usr/bin/true; 640 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 4.0.0"; 641 | }; 642 | name = Debug; 643 | }; 644 | OBJ_89 /* Release */ = { 645 | isa = XCBuildConfiguration; 646 | buildSettings = { 647 | LD = /usr/bin/true; 648 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 4.0.0"; 649 | }; 650 | name = Release; 651 | }; 652 | OBJ_94 /* Debug */ = { 653 | isa = XCBuildConfiguration; 654 | buildSettings = { 655 | }; 656 | name = Debug; 657 | }; 658 | OBJ_95 /* Release */ = { 659 | isa = XCBuildConfiguration; 660 | buildSettings = { 661 | }; 662 | name = Release; 663 | }; 664 | OBJ_99 /* Debug */ = { 665 | isa = XCBuildConfiguration; 666 | buildSettings = { 667 | CLANG_ENABLE_MODULES = YES; 668 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 669 | FRAMEWORK_SEARCH_PATHS = ( 670 | "$(inherited)", 671 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 672 | ); 673 | HEADER_SEARCH_PATHS = "$(inherited)"; 674 | INFOPLIST_FILE = ruminant.xcodeproj/ruminantTests_Info.plist; 675 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 676 | OTHER_CFLAGS = "$(inherited)"; 677 | OTHER_LDFLAGS = "$(inherited)"; 678 | OTHER_SWIFT_FLAGS = "$(inherited)"; 679 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 680 | TARGET_NAME = ruminantTests; 681 | }; 682 | name = Debug; 683 | }; 684 | /* End XCBuildConfiguration section */ 685 | 686 | /* Begin XCConfigurationList section */ 687 | OBJ_2 /* Build configuration list for PBXProject "ruminant" */ = { 688 | isa = XCConfigurationList; 689 | buildConfigurations = ( 690 | OBJ_3 /* Debug */, 691 | OBJ_4 /* Release */, 692 | ); 693 | defaultConfigurationIsVisible = 0; 694 | defaultConfigurationName = Release; 695 | }; 696 | OBJ_50 /* Build configuration list for PBXNativeTarget "SwiftCheck" */ = { 697 | isa = XCConfigurationList; 698 | buildConfigurations = ( 699 | OBJ_51 /* Debug */, 700 | OBJ_52 /* Release */, 701 | ); 702 | defaultConfigurationIsVisible = 0; 703 | defaultConfigurationName = Release; 704 | }; 705 | OBJ_71 /* Build configuration list for PBXNativeTarget "SwiftCheckPackageDescription" */ = { 706 | isa = XCConfigurationList; 707 | buildConfigurations = ( 708 | OBJ_72 /* Debug */, 709 | OBJ_73 /* Release */, 710 | ); 711 | defaultConfigurationIsVisible = 0; 712 | defaultConfigurationName = Release; 713 | }; 714 | OBJ_77 /* Build configuration list for PBXNativeTarget "ruminant" */ = { 715 | isa = XCConfigurationList; 716 | buildConfigurations = ( 717 | OBJ_78 /* Debug */, 718 | OBJ_79 /* Release */, 719 | ); 720 | defaultConfigurationIsVisible = 0; 721 | defaultConfigurationName = Release; 722 | }; 723 | OBJ_87 /* Build configuration list for PBXNativeTarget "ruminantPackageDescription" */ = { 724 | isa = XCConfigurationList; 725 | buildConfigurations = ( 726 | OBJ_88 /* Debug */, 727 | OBJ_89 /* Release */, 728 | ); 729 | defaultConfigurationIsVisible = 0; 730 | defaultConfigurationName = Release; 731 | }; 732 | OBJ_93 /* Build configuration list for PBXAggregateTarget "ruminantPackageTests" */ = { 733 | isa = XCConfigurationList; 734 | buildConfigurations = ( 735 | OBJ_94 /* Debug */, 736 | OBJ_95 /* Release */, 737 | ); 738 | defaultConfigurationIsVisible = 0; 739 | defaultConfigurationName = Release; 740 | }; 741 | OBJ_98 /* Build configuration list for PBXNativeTarget "ruminantTests" */ = { 742 | isa = XCConfigurationList; 743 | buildConfigurations = ( 744 | OBJ_99 /* Debug */, 745 | OBJ_100 /* Release */, 746 | ); 747 | defaultConfigurationIsVisible = 0; 748 | defaultConfigurationName = Release; 749 | }; 750 | /* End XCConfigurationList section */ 751 | }; 752 | rootObject = OBJ_1 /* Project object */; 753 | } 754 | -------------------------------------------------------------------------------- /ruminant.xcodeproj/ruminantTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ruminant.xcodeproj/ruminant_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0.6 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ruminant.xcodeproj/xcshareddata/xcschemes/ruminant-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 63 | 64 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /ruminant.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SchemeUserState 5 | 6 | ruminant-Package.xcscheme 7 | 8 | 9 | SuppressBuildableAutocreation 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------