diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ae15aca..243bd23 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -4,7 +4,10 @@ on: [push, pull_request] jobs: run: - runs-on: ubuntu-latest + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@v3 @@ -19,10 +22,12 @@ jobs: - name: Run Tests run: ./gradlew clean test - - name: Run Test Coverage + - if: matrix.os == 'ubuntu-latest' # Container action is only supported on Linux + name: Run Test Coverage run: ./gradlew jacocoTestReport - - name: Jacoco Code Coverage Report + - if: matrix.os == 'ubuntu-latest' + name: Jacoco Code Coverage Report uses: cicirello/jacoco-badge-generator@v2.8.0 with: generate-branches-badge: true diff --git a/.gitignore b/.gitignore index 7061064..16cba14 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /.gradle/ /.idea/ /build/ -/lib/build/ \ No newline at end of file +/lib/build/ +/lib/TEST_TEST/ +/gradle/ diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENCE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 6530b63..b073019 100644 --- a/README.md +++ b/README.md @@ -1 +1,43 @@ -Hi there +# Tree Structure + +This project will help the user to build an RBT, AVL or binary tree. Add, delete, or search for elements in it. + + +## Overview + +- You can create any tree of your choice (AVL, RB, Binary Tree) +- Insert, delete, check for any element +- Support for different types of elements + +## Setup + +To connect a project from Github using Gradle, you need to follow these steps: + +1. Open the build file.gradle your project +2. Find the "dependencies" block and add the dependency line to it: +```sh +dependencies { + implementation 'com.github.spbu-coding-2022:trees-1:1.0.0' +} +``` +3. Save the changes in the build file.gradle +4. Synchronize the project using Gradle + + + +## Project Status +Project is: _in progress_ + +## Room for Improvement +Room for improvement: +- preserving trees so that you can turn to history + +## Contacts +For all questions: [Click](http://telegram.me/LesokSupportbot) + + +## License +This project uses the APACHE LICENSE, VERSION 2.0. + +
+ diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..e08d4b3 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +kotlinVersion=1.8.10 +sqliteJdbcVersion=3.41.2.1 diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 09124dc..080c014 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -1,3 +1,4 @@ +val sqliteJdbcVersion: String by project plugins { java kotlin("jvm") version "1.8.10" @@ -19,11 +20,25 @@ repositories { } dependencies { + api("org.apache.commons:commons-math3:3.6.1") + implementation("com.google.guava:guava:31.1-jre") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") + + implementation("com.google.code.gson:gson:2.8.5") + + val neo4jCore = "4.0.5" + implementation("org.neo4j", "neo4j-ogm-core", neo4jCore) + implementation("org.neo4j", "neo4j-ogm-bolt-driver", neo4jCore) + + // JDBC Sqlite + implementation("org.xerial", "sqlite-jdbc", sqliteJdbcVersion) + testImplementation("io.mockk:mockk:1.13.4") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") - api("org.apache.commons:commons-math3:3.6.1") - implementation("com.google.guava:guava:31.1-jre") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") + implementation(kotlin("stdlib-jdk8")) + } tasks.test { @@ -76,7 +91,7 @@ tasks.jacocoTestReport { tasks.jacocoTestCoverageVerification { classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { - include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct") + include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class") } }) dependsOn(tasks.jacocoTestReport) violationRules { @@ -84,7 +99,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "BRANCH" - minimum = 0.4.toBigDecimal() + minimum = 0.5.toBigDecimal() } } rule { @@ -98,7 +113,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "METHOD" - minimum = 1.0.toBigDecimal() + minimum = 0.9.toBigDecimal() } } } @@ -113,4 +128,4 @@ publishing { from(components["java"]) } } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/controller/BINTreeManager.kt b/lib/src/main/kotlin/controller/BINTreeManager.kt new file mode 100644 index 0000000..7232c6e --- /dev/null +++ b/lib/src/main/kotlin/controller/BINTreeManager.kt @@ -0,0 +1,21 @@ +package controller + +import dbSave.jsonFormat.JsonRepository +import treelib.binTree.BINStruct + +class BINTreeManager: TreeManager() { + + /*** using json format files ***/ + + val jsonRep = JsonRepository(System.getProperty("user.dir")) + + + /*** 1.7.6 ***/ + fun initTree(treeName: String) { + TODO() + } + + fun > saveTree(tree: BINStruct) { + TODO() + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/controller/RBTreeManager.kt b/lib/src/main/kotlin/controller/RBTreeManager.kt new file mode 100644 index 0000000..0d3456e --- /dev/null +++ b/lib/src/main/kotlin/controller/RBTreeManager.kt @@ -0,0 +1,40 @@ +package controller + +import dbSave.neo4j.DrawRBVertex +import dbSave.neo4j.Neo4jRepository +import treelib.rbTree.RBStruct +import treelib.singleObjects.Container + +class RBTreeManager { + + /*** using neo4j ***/ + + fun initTree(treeName: String): RBStruct>> { + val neo4jDB = Neo4jRepository() + neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + + /*** orders.first = preOrder, orders.second = inOrder ***/ + val orders: Pair>>>, List>>>> = + neo4jDB.exportRBtree() + + val RBtree = RBStruct>>() + RBtree.restoreStruct(orders.first, orders.second) + neo4jDB.close() + + return RBtree + } + + fun > saveTree(tree: RBStruct) { + val neo4jDB = Neo4jRepository() + neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + + // вот тут плохо, потому что тут надо получать не base nodes, а для рисовалки + + val preOrder = tree.preOrder().map { DrawRBVertex(it.value, it.color) } + val inOrder = tree.inOrder().map { DrawRBVertex(it.value, it.color) } + + neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray()) + neo4jDB.close() + } + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/controller/SQLiteController.kt b/lib/src/main/kotlin/controller/SQLiteController.kt new file mode 100644 index 0000000..fa39c9d --- /dev/null +++ b/lib/src/main/kotlin/controller/SQLiteController.kt @@ -0,0 +1,58 @@ +package controller + +import dbSave.sqlite.DrawAVLVertex +import dbSave.sqlite.SQLiteRepository +import treelib.avlTree.AVLStruct +import treelib.avlTree.AVLVertex +import java.io.Closeable + +class SQLiteController>( + private var treeName: String, + private val dbPath: String, + private val serializeData: (input: Pack) -> String, + private val deSerializeData: (input: String) -> Pack, +) : Closeable { + private val db = SQLiteRepository(dbPath, serializeData, deSerializeData) + private var avlTree = AVLStruct() + + + private fun drawVertexToVertex(drawVertex: MutableList>): MutableList> { + //TODO: Rewrite while working on GUI + val ans = mutableListOf>() + for (el in drawVertex) ans.add(AVLVertex(value = el.value, height = el.height.toUInt())) + return ans + } + + private fun vertexToDrawVertex(drawVertex: List>): MutableList> { + //TODO: Rewrite while working on GUI + val ans = mutableListOf>() + for (el in drawVertex) ans.add(DrawAVLVertex(value = el.value, height = el.height.toInt(), x = 1.1, y = 1.1)) + return ans + } + + fun initTree() { + avlTree = AVLStruct() + if (db.getTreeId(treeName) == 0) { + db.addTree(treeName) + } else { + avlTree.restoreStruct(drawVertexToVertex(db.getAllVertexes(treeName))) + } + db.addTree(treeName) + } + + fun saveTree() { + db.addVertexes(vertexToDrawVertex(avlTree.preOrder()), treeName) + } + + fun deleteTree() { + if (db.getTreeId(treeName) != 0) { + db.deleteTree(treeName) + } + } + + fun insert(item: Pack) = avlTree.insert(item) + + fun delete(item: Pack) = avlTree.delete(item) + + override fun close() = db.close() +} diff --git a/lib/src/main/kotlin/controller/TreeManager.kt b/lib/src/main/kotlin/controller/TreeManager.kt new file mode 100644 index 0000000..cc751e0 --- /dev/null +++ b/lib/src/main/kotlin/controller/TreeManager.kt @@ -0,0 +1,4 @@ +package controller + +abstract class TreeManager { +} \ No newline at end of file diff --git a/lib/src/main/kotlin/dbSave/DrawVertex.kt b/lib/src/main/kotlin/dbSave/DrawVertex.kt new file mode 100644 index 0000000..4108556 --- /dev/null +++ b/lib/src/main/kotlin/dbSave/DrawVertex.kt @@ -0,0 +1,7 @@ +package dbSave + +interface DrawVertex> { + val value: Pack + val x: Double + val y: Double +} diff --git a/lib/src/main/kotlin/dbSave/jsonFormat/DrawBINVertex.kt b/lib/src/main/kotlin/dbSave/jsonFormat/DrawBINVertex.kt new file mode 100644 index 0000000..783a1de --- /dev/null +++ b/lib/src/main/kotlin/dbSave/jsonFormat/DrawBINVertex.kt @@ -0,0 +1,9 @@ +package dbSave.jsonFormat + +import treelib.binTree.BINVertex + +class DrawBINVertex>( + value: Pack, + val x: Double = 0.0, + val y: Double = 0.0 +) : BINVertex(value) \ No newline at end of file diff --git a/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt new file mode 100644 index 0000000..41a5971 --- /dev/null +++ b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt @@ -0,0 +1,38 @@ +package dbSave.jsonFormat + +import com.google.common.reflect.TypeToken +import com.google.gson.GsonBuilder +import java.io.File + +class JsonRepository(private val dirPath: String) { + + init { + File(dirPath).mkdirs() + } + + fun >saveChanges( + preOrder: Array>, + fileName: String + ) { + + val gson = GsonBuilder().setPrettyPrinting().create() + val json = gson.toJson(preOrder) + + File(dirPath, fileName).run { + createNewFile() + writeText(json) + } + + } + + fun >exportTree(fileName: String, typeToken: TypeToken>>) { + val gson = GsonBuilder().setPrettyPrinting().create() + val json = File(dirPath, fileName).readText() + + val preOrd = gson.fromJson>>(json, typeToken.type) + + + } + + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf b/lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf new file mode 100644 index 0000000..9b9fb0a --- /dev/null +++ b/lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf @@ -0,0 +1,3 @@ +CONTAINER_NAME=neo4j-db +PASSWORD="test-neo4j" +VOLUMEDIR="/volume" \ No newline at end of file diff --git a/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt b/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt new file mode 100644 index 0000000..37100de --- /dev/null +++ b/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt @@ -0,0 +1,11 @@ +package dbSave.neo4j + +import treelib.rbTree.RBVertex +import treelib.singleObjects.Markers + +class DrawRBVertex>( + value: Pack, + color: Markers, + val x: Double = 0.0, + val y: Double = 0.0 +) : RBVertex(value, color) \ No newline at end of file diff --git a/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt new file mode 100644 index 0000000..f04cf8c --- /dev/null +++ b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt @@ -0,0 +1,209 @@ +package dbSave.neo4j + + +import org.neo4j.driver.AuthTokens +import org.neo4j.driver.Driver +import org.neo4j.driver.GraphDatabase +import org.neo4j.driver.TransactionContext +import org.neo4j.driver.exceptions.SessionExpiredException +import treelib.singleObjects.Container +import treelib.singleObjects.Markers +import java.io.Closeable +import java.io.IOException +import java.util.* + +class Neo4jRepository : Closeable { + + private var driver: Driver? = null + + fun open(uri: String, username: String, password: String) { + try { + driver = GraphDatabase.driver(uri, AuthTokens.basic(username, password)) + } catch (ex: IllegalArgumentException) { + throw IOException() + } catch (ex: SessionExpiredException) { + throw IOException() + } + } + + fun > saveChanges(preOrder: Array>, inOrder: Array>) { + + /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ + val session = driver?.session() ?: throw IOException() + + var inOrderIndex = 0 + var preOrderIndex = 0 + val set = HashSet>() + val stack = LinkedList>() + var id = 0 + + while (preOrderIndex in preOrder.indices) { + do { + val currentNode = preOrder[preOrderIndex] + //currentNode.value as Container<*, *> + if (preOrderIndex == 0) { + session.executeWrite { tx -> + cleanDB(tx) + createRoot(tx, currentNode, id) + } + ++id + } + if (!stack.isEmpty()) { + if (set.contains(stack.peek())) { + set.remove(stack.peek()) + val parentNode = stack.pop() + //parentNode.value as Container<*, *> + session.executeWrite { tx -> + createRightSon(tx, parentNode, currentNode, id) + } + ++id + } else { + val parentNode = stack.peek() + parentNode.value as Container<*, *> + session.executeWrite { tx -> + createLeftSon(tx, parentNode, currentNode, id) + } + ++id + } + } + stack.push(currentNode) + } while (preOrder[preOrderIndex++].value != inOrder[inOrderIndex].value && preOrderIndex < preOrder.size) + + var currentNode: DrawRBVertex? = null + + while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { + currentNode = stack.pop() + ++inOrderIndex + } + + if (currentNode != null) { + set.add(currentNode) + stack.push(currentNode) + } + + } + + session.close() + } + + fun exportRBtree(): Pair>>>, List>>>> { + + /*** Плохо, что передаем Container с четко привязанными типами (K, V), потому что может быть так, что вместо контейнера будет просто инт ***/ + + val session = driver?.session() ?: throw IOException() + var preOrder: List>>> = listOf() + var inOrder: List>>> = listOf() + + session.executeRead { tx -> + preOrder = tx.run( + "MATCH (node: Node) " + + "RETURN node.value, node.key, node.color, node.x, node.y " + + "ORDER BY node.id" + ).list() + .map { + DrawRBVertex( + value = Container(Pair(it.values()[1].toString(), it.values()[0].toString())), + color = if (it.values()[2].toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values()[3].toString().toDouble(), + y = it.values()[4].toString().toDouble() + ) + } + + inOrder = tx.run( + "MATCH (node: Node) " + + "RETURN node.value, node.key, node.color, node.x, node.y " + + "ORDER BY node.key" + ).list() + .map { + DrawRBVertex( + value = Container(Pair(it.values()[1].toString(), it.values()[0].toString())), + color = if (it.values()[2].toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values()[3].toString().toDouble(), + y = it.values()[4].toString().toDouble() + ) + } + } + + session.close() + + return Pair(preOrder, inOrder) + + } + + private fun cleanDB(tx: TransactionContext) { + tx.run("MATCH (n: Node) DETACH DELETE n") + } + + private fun > createRoot(tx: TransactionContext, rootNode: DrawRBVertex, id: Int) { + rootNode.value as Container<*, *> + tx.run( + "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY, id: \$nodeID})", + mutableMapOf( + "nodeValue" to rootNode.value.pair.second, + "nodeKey" to rootNode.value.pair.first, + "nodeColor" to rootNode.color.toString(), + "nodeX" to rootNode.x, + "nodeY" to rootNode.y, + "nodeID" to id + ) + ) + } + + private fun > createRightSon( + tx: TransactionContext, parentNode: DrawRBVertex, + currentNode: DrawRBVertex, id: Int + ) { + parentNode.value as Container<*, *> + currentNode.value as Container<*, *> + tx.run( + "MERGE(parent:Node {value: \$parentNodeValue, key: \$parentNodeKey, " + + "color: \$parentNodeColor, x: \$parentNodeX, y: \$parentNodeY}) " + + "MERGE(son:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY, id: \$nodeID}) " + + "MERGE (parent)-[:RIGHT_SON]->(son)", + mutableMapOf( + "parentNodeValue" to parentNode.value.pair.second, + "parentNodeKey" to parentNode.value.pair.first, + "parentNodeColor" to parentNode.color.toString(), + "parentNodeX" to parentNode.x, + "parentNodeY" to parentNode.y, + "nodeValue" to currentNode.value.pair.second, + "nodeKey" to currentNode.value.pair.first, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y, + "nodeID" to id, + ) + ) + } + + private fun > createLeftSon( + tx: TransactionContext, parentNode: DrawRBVertex, + currentNode: DrawRBVertex, id: Int + ) { + parentNode.value as Container<*, *> + currentNode.value as Container<*, *> + tx.run( + "MERGE(parent:Node {value: \$parentNodeValue, key: \$parentNodeKey, " + + "color: \$parentNodeColor, x: \$parentNodeX, y: \$parentNodeY}) " + + "MERGE(son:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY, id: \$nodeID}) " + + "MERGE (parent)-[:LEFT_SON]->(son)", + mutableMapOf( + "parentNodeValue" to parentNode.value.pair.second, + "parentNodeKey" to parentNode.value.pair.first, + "parentNodeColor" to parentNode.color.toString(), + "parentNodeX" to parentNode.x, + "parentNodeY" to parentNode.y, + "nodeValue" to currentNode.value.pair.second, + "nodeKey" to currentNode.value.pair.first, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y, + "nodeID" to id, + ) + ) + } + + override fun close() { + driver?.close() + } +} diff --git a/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh b/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh new file mode 100644 index 0000000..6b07269 --- /dev/null +++ b/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh @@ -0,0 +1,13 @@ + +BASEDIR=$(realpath "$(dirname "$0")") + +. "${BASEDIR}/CONTAINER.conf" + +docker run \ + -i \ + --name "$CONTAINER_NAME" \ + --volume=$HOME/neo4j/data:/data \ + --volume=$HOME/neo4j/logs:/logs \ + --publish=7474:7474 --publish=7687:7687 \ + --env NEO4J_AUTH=neo4j/"$PASSWORD" \ + neo4j:latest \ diff --git a/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt b/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt new file mode 100644 index 0000000..e6feb46 --- /dev/null +++ b/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt @@ -0,0 +1,10 @@ +package dbSave.sqlite + +import dbSave.DrawVertex + +class DrawAVLVertex>( + override val value: Pack, + override val x: Double, + override val y: Double, + val height: Int, +) : DrawVertex diff --git a/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt b/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt new file mode 100644 index 0000000..1d4cf20 --- /dev/null +++ b/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt @@ -0,0 +1,227 @@ +package dbSave.sqlite + +import java.io.Closeable +import java.sql.DriverManager +import java.sql.SQLException + +class SQLiteRepository>( + private val dbPath: String, + private val serializeData: (input: Pack) -> String, + private val deSerializeData: (input: String) -> Pack, + private val logErrorMethod: (input: Exception) -> Unit = { throw it }, + private val logInfoMethod: (input: String) -> Unit = { /* Nothing to do */ }, +) : Closeable { + + private val treeTable = "AVLTreesTable" + private val avlTreeName = "name" + + private val value = "value" + private val height = "height" + private val xCord = "x" + private val yCord = "y" + + private val dbDriver = "jdbc:sqlite" + private val connection = DriverManager.getConnection("$dbDriver:$dbPath") + ?: throw SQLException("Cannot connect to database") + + init { + createTreeTable() + } + + fun createTreeTable() { + connection.createStatement().also { stmt -> + try { + stmt.execute("CREATE TABLE if not exists $treeTable(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name text);") + logInfoMethod("Table with trees created or already exists") + } catch (ex: SQLException) { + logErrorMethod(ex) + } finally { + stmt.close() + } + } + } + + fun clearTreeTable() { + connection.createStatement().also { stmt -> + try { + stmt.execute("DELETE FROM $treeTable;") + logInfoMethod("TreeTable: $treeTable has been deleted") + } catch (ex: SQLException) { + logErrorMethod(ex) + } finally { + stmt.close() + } + } + } + + fun addTree(treeName: String) { + val isInDB = isNameInDB(treeName, avlTreeName, treeTable) + + if (isInDB) { + logInfoMethod("Tree - $treeName, have been exist yet in treeTable - $treeTable") + return + } + + connection.createStatement().also { stmt -> + try { + stmt.execute("CREATE TABLE if not exists $treeName(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, $value text, $height INT, $xCord DOUBLE, $yCord DOUBLE);") + stmt.execute("INSERT INTO $treeTable ($avlTreeName) VALUES ('$treeName');") + logInfoMethod("Was created Tree: $treeName in table: $treeTable") + } catch (ex: SQLException) { + logErrorMethod(ex) + } finally { + stmt.close() + } + } + } + + fun getTreeNames(): MutableList { + val info = mutableListOf() + connection.createStatement().also { stmt -> + try { + val result = stmt.executeQuery("SELECT $treeTable.$avlTreeName as $avlTreeName FROM $treeTable;") + while (result.next()) { + info.add(result.getString(avlTreeName)) + } + logInfoMethod("Available tree is given") + } catch (ex: SQLException) { + logErrorMethod(ex) + } finally { + stmt.close() + } + return info + } + } + + fun deleteTree(treeName: String) { + val deleteId = getTreeId(treeName) + if (deleteId == 0) return + + connection.createStatement().also { stmt -> + try { + stmt.execute("DROP TABLE $treeName;") + stmt.execute("DELETE FROM $treeTable WHERE id=$deleteId;") + logInfoMethod("Tree: $treeName has been deleted") + } catch (ex: SQLException) { + logErrorMethod(ex) + } finally { + stmt.close() + } + } + } + + fun getTreeId(treeName: String): Int { + var id: Int? = null + try { + val statement = connection.prepareStatement("SELECT id FROM $treeTable WHERE name=?;") + statement.setString(1, treeName) + id = statement.executeQuery().getInt(1) + statement.close() + } catch (ex: SQLException) { + logErrorMethod(ex) + } + if (id != null) return id + else throw SQLException("Impossible case") + } + + fun addVertex(avlDVertex: DrawAVLVertex, treeName: String) { + val isInDB = getVertexId(avlDVertex, treeName) + if (isInDB != 0) { + deleteVertex(isInDB, treeName) + logInfoMethod("Attempt write duplicate of the vertex: value = ${avlDVertex.value}; hieght = ${avlDVertex.height}; x = ${avlDVertex.x}; y = ${avlDVertex.y}") + } + + try { + val stmt = connection.prepareStatement("INSERT INTO $treeName (value, height, x, y) VALUES (?, ?, ?, ?);") + val info = serializeData(avlDVertex.value) + stmt.setString(1, info) + stmt.setInt(2, avlDVertex.height) + stmt.setDouble(3, avlDVertex.x) + stmt.setDouble(4, avlDVertex.y) + stmt.execute() + stmt.close() + logInfoMethod("Vertex: value = $info has been saved") + } catch (ex: SQLException) { + logErrorMethod(ex) + } + + } + + fun addVertexes(list: MutableList>, treeName: String) { + for (el in list) addVertex(el, treeName) + } + + fun deleteVertex(id: Int, treeName: String) { + try { + val stmt = connection.prepareStatement("DELETE FROM $treeName WHERE id=?;") + stmt.setInt(1, id) + stmt.execute() + stmt.close() + logInfoMethod("Element: id = $id has been deleted in table: $treeName") + } catch (ex: SQLException) { + logErrorMethod(ex) + } + } + + fun getAllVertexes(treeName: String): MutableList> { + val info = mutableListOf>() + connection.createStatement().also { stmt -> + try { + val result = + stmt.executeQuery("SELECT $treeName.$value as $value, $treeName.$height as $height, $treeName.$xCord as $xCord, $treeName.$yCord as $yCord FROM $treeName;") + while (result.next()) { + info.add( + DrawAVLVertex( + value = deSerializeData(result.getString(value)), + height = result.getInt(height), + x = result.getDouble(xCord), + y = result.getDouble(yCord), + ) + ) + } + logInfoMethod("Vertexes from $treeName have been received") + } catch (ex: SQLException) { + logErrorMethod(ex) + } finally { + stmt.close() + } + return info + } + } + + private fun getVertexId(vertex: DrawAVLVertex, tableName: String): Int { + var id: Int? = null + try { + val stmt = + connection.prepareStatement("SELECT id FROM $tableName WHERE value=? AND height=? AND x=? AND y=?;") + stmt.setString(1, serializeData(vertex.value)) + stmt.setInt(2, vertex.height) + stmt.setDouble(3, vertex.x) + stmt.setDouble(4, vertex.y) + id = stmt.executeQuery().getInt(1) + stmt.close() + } catch (ex: SQLException) { + logErrorMethod(ex) + } + if (id != null) return id + else throw SQLException("Impossible case") + } + + private fun isNameInDB(rowName: String, columnName: String, tableName: String): Boolean { + var isInDB: Boolean? = null + try { + val statement = connection.prepareStatement("SELECT EXISTS(SELECT 1 FROM $tableName WHERE $columnName=?);") + statement.setString(1, rowName) + isInDB = statement.executeQuery().getBoolean(1) + statement.close() + } catch (ex: SQLException) { + logErrorMethod(ex) + } + if (isInDB != null) return isInDB + else throw SQLException("Impossible case") + } + + override fun close() { + connection.close() + } +} diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index b64cca1..fb0976a 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -4,44 +4,47 @@ import treelib.singleObjects.Container import treelib.singleObjects.exceptions.NonExistentValueException abstract class Tree< - Key : Comparable, - Value, - NodeType : Node, NodeType>, - State : StateContainer, NodeType> + K : Comparable, + V, + NodeType : Node, NodeType>, + State : StateContainer, NodeType>, + VertexType : Vertex> > { - protected abstract val treeStruct: TreeStruct, NodeType, State> + protected abstract val treeStruct: TreeStruct, NodeType, State, VertexType> - private fun wrapForFind(key: Key) = Container(key to null) + private fun wrapForFind(key: K) = Container(key to null) - fun putItem(item: Pair) { + fun putItem(item: Pair) { treeStruct.insert(Container(item)) } - fun putItems(vararg items: Pair) { + fun putItems(vararg items: Pair) { for (element in items) putItem(element) } - fun putItems(items: Iterable>) { + fun putItems(items: Iterable>) { for (element in items) putItem(element) } - fun getItem(key: Key): Value? = treeStruct.find(wrapForFind(key))?.value + fun getItem(key: K): V? = treeStruct.find(wrapForFind(key))?.value - fun deleteItem(key: Key) { + fun deleteItem(key: K) { if (getItem(key) == null) throw NonExistentValueException() treeStruct.delete(wrapForFind(key)) } - private fun createPoorList(info: List>): List> { - val returnInfo = mutableListOf>() - for (element in info) returnInfo.add(element.pair) + private fun createPoorList(info: List): List> { + val returnInfo = mutableListOf>() + for (element in info) { + returnInfo.add(element.value.pair) + } return returnInfo } - fun inOrder(): List> = createPoorList(treeStruct.inOrder()) + fun inOrder(): List> = createPoorList(treeStruct.inOrder()) - fun preOrder(): List> = createPoorList(treeStruct.preOrder()) + fun preOrder(): List> = createPoorList(treeStruct.preOrder()) - fun postOrder(): List> = createPoorList(treeStruct.postOrder()) + fun postOrder(): List> = createPoorList(treeStruct.postOrder()) } diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 2f21c10..fa403bc 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -6,11 +6,16 @@ import treelib.singleObjects.exceptions.MultithreadingException import treelib.singleObjects.exceptions.NonExistentValueException -abstract class TreeStruct, NodeType : Node, State : StateContainer> { +abstract class TreeStruct< + Pack : Comparable, + NodeType : Node, + State : StateContainer, + VertexType : Vertex + > { protected abstract var root: NodeType? - private fun getLeafForInsert(item: Pack): NodeType? { + protected fun getLeafForInsert(item: Pack): NodeType? { var currentNode: NodeType? = root ?: return null while (true) { @@ -47,7 +52,8 @@ abstract class TreeStruct, NodeType : Node + } + ?: throw BugInImplementException("getParentByValue shouldn't be used with value doesn't exist in tree")// (1)l -> } } @@ -70,7 +76,8 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node it.value) currentNode = it.right else currentNode = it.left - } ?: return generateStateFind(null) + } + if (currentNode == null) return generateStateFind(null, null) } } } @@ -158,11 +166,12 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node, NodeType : Node { - val arrayNodes = mutableListOf() + fun inOrder(): List { + val arrayNodes = mutableListOf() var flagVisited = 0 var current = root val parents = ArrayDeque() @@ -268,24 +281,26 @@ abstract class TreeStruct, NodeType : Node { + abstract fun toVertex(node: NodeType): VertexType + + fun postOrder(): List { val parents = ArrayDeque() - val arrayNodes = mutableListOf() + val arrayNodes = mutableListOf() var flagVisited = 0 var current = root @@ -306,9 +321,9 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node { - val arrayNodes = mutableListOf() + fun preOrder(): List { + val arrayNodes = mutableListOf() var current: NodeType val queue = ArrayDeque() @@ -329,7 +344,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node> { + abstract val value: Pack +} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt index 5dd24c3..5172f71 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt @@ -3,26 +3,28 @@ package treelib.abstractTree.balanced import treelib.abstractTree.Node import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct +import treelib.abstractTree.Vertex abstract class BalancedTreeStruct< Pack : Comparable, NodeType : Node, State : StateContainer, + VertexType : Vertex, BalancerType : Balancer, - > : TreeStruct() { + > : TreeStruct() { protected abstract val balancer: BalancerType - override fun insert(item: Pack){ + override fun insert(item: Pack) { val currentState = insertItem(item) if (currentState.contentNode != null) { root = balancer.balance(currentState) } } - override fun delete(item: Pack){ + override fun delete(item: Pack) { val currentState = deleteItem(item) - if (root == null){ + if (root == null) { return } if (currentState.contentNode != null) { diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt index afaf5d5..6cf9537 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt @@ -4,7 +4,8 @@ import treelib.abstractTree.NodeParent import treelib.abstractTree.StateContainer import treelib.singleObjects.exceptions.IllegalNodeStateException -abstract class BalancerParent, NodeType : NodeParent, StateContainerType: StateContainer>: Balancer { +abstract class BalancerParent, NodeType : NodeParent, StateContainerType : StateContainer> : + Balancer { override fun rightRotate(currentNode: NodeType): NodeType { val leftChild = currentNode.left ?: throw IllegalNodeStateException() diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 703ea69..1a3afb9 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -1,8 +1,6 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancerNoParent -import treelib.singleObjects.exceptions.IllegalBaseNodeException -import treelib.singleObjects.exceptions.IllegalNodeStateException class AVLBalancer>(private var root: AVLNode?) : BalancerNoParent, AVLStateContainer>() { @@ -19,16 +17,17 @@ class AVLBalancer>(private var root: AVLNode?) : currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u } - override fun balance(state: AVLStateContainer): AVLNode { - val node = state.contentNode - root = state.root - return balance(root, node?.value ?: throw IllegalBaseNodeException()) + override fun balance(stateContainer: AVLStateContainer): AVLNode { + val node = stateContainer.contentNode + ?: throw IllegalStateException("") // IllegalBaseNodeException("A non-existent node (null) was passed to the method") + root = stateContainer.root + return balance(root, node.value) } - // В баланс передаем родителя ноды, которую будем удалять + /*** In the method we pass the parent of the removed/inserted node ***/ private fun balance(currentNode: AVLNode?, value: Pack): AVLNode { if (currentNode == null) { - throw IllegalBaseNodeException() + throw NullPointerException() } when { currentNode.value < value -> currentNode.right = balance(currentNode.right, value) @@ -38,7 +37,8 @@ class AVLBalancer>(private var root: AVLNode?) : val balance = updateBalance(currentNode) if (balance == -2) { if (updateBalance(currentNode.right) == 1) { - currentNode.right = currentNode.right?.let { rightRotate(it) } ?: throw IllegalNodeStateException() + currentNode.right = currentNode.right?.let { rightRotate(it) } + ?: throw NullPointerException() // IllegalNodeStateException() updateHeight(currentNode.right?.right) } val balancedNode = leftRotate(currentNode) @@ -48,7 +48,8 @@ class AVLBalancer>(private var root: AVLNode?) : } if (balance == 2) { if (updateBalance(currentNode.left) == -1) { - currentNode.left = currentNode.left?.let { leftRotate(it) } ?: throw IllegalNodeStateException() + currentNode.left = currentNode.left?.let { leftRotate(it) } + ?: throw NullPointerException() // IllegalNodeStateException("There is no node required by the condition of the algorithm") updateHeight(currentNode.left?.left) } val balanceNode = rightRotate(currentNode) @@ -58,4 +59,5 @@ class AVLBalancer>(private var root: AVLNode?) : } return currentNode } + } diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index f275439..6d89668 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -1,9 +1,10 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancedTreeStruct +import treelib.singleObjects.exceptions.IncorrectUsage class AVLStruct> : - BalancedTreeStruct, AVLStateContainer, AVLBalancer>() { + BalancedTreeStruct, AVLStateContainer, AVLVertex, AVLBalancer>() { override var root: AVLNode? = null override val balancer = AVLBalancer(root) @@ -40,6 +41,10 @@ class AVLStruct> : } } + override fun toVertex(node: AVLNode): AVLVertex = AVLVertex(node.value, node.height) + + fun toNode(vertex: AVLVertex): AVLNode = AVLNode(value = vertex.value, height = vertex.height) + override fun createNode(item: Pack): AVLNode = AVLNode(item) override fun getNodeKernel(node: AVLNode): AVLNode = AVLNode(node.value, height = node.height) @@ -52,4 +57,13 @@ class AVLStruct> : } return node } + + fun > restoreStruct(preOrder: MutableList) { + if (root != null) throw IncorrectUsage("The tree already exists") + for (vertex in preOrder) { + val currentNode = toNode(vertex) + val leaf = getLeafForInsert(currentNode.value) + linkNewNode(currentNode, leaf) + } + } } diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt index 6155af6..5df840f 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt @@ -4,12 +4,12 @@ import treelib.abstractTree.Tree import treelib.singleObjects.Container -class AVLTree, Value> : - Tree>, AVLStateContainer>>() { +class AVLTree, V> : + Tree>, AVLStateContainer>, AVLVertex>>() { - override val treeStruct = AVLStruct>() + override val treeStruct = AVLStruct>() - operator fun AVLTree.get(key: Key): Value? = getItem(key) + operator fun AVLTree.get(key: K): V? = getItem(key) - operator fun AVLTree.set(key: Key, value: Value) = putItem(key to value) + operator fun AVLTree.set(key: K, value: V) = putItem(key to value) } diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt b/lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt new file mode 100644 index 0000000..774d064 --- /dev/null +++ b/lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt @@ -0,0 +1,8 @@ +package treelib.avlTree + +import treelib.abstractTree.Vertex + +class AVLVertex>( + override val value: Pack, + val height: UInt, +) : Vertex() diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index e555924..c033266 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -1,9 +1,11 @@ package treelib.binTree import treelib.abstractTree.TreeStruct +import treelib.abstractTree.Vertex +import treelib.singleObjects.exceptions.IncorrectUsage class BINStruct> : - TreeStruct, BINStateContainer>() { + TreeStruct, BINStateContainer, BINVertex>() { override var root: BINNode? = null @@ -53,6 +55,10 @@ class BINStruct> : return node } + override fun toVertex(node: BINNode): BINVertex { + return BINVertex(node.value) + } + override fun createNode(item: Pack) = BINNode(item) override fun delete(item: Pack) { @@ -62,4 +68,15 @@ class BINStruct> : override fun insert(item: Pack) { insertItem(item).contentNode } + + private fun toNode(vertex: BINVertex): BINNode = BINNode(value = vertex.value) + + fun > restoreStruct(preOrder: List) { + if (root != null) throw IncorrectUsage("The tree already exists") + for (vertex in preOrder) { + val currentNode = toNode(vertex) + val leaf = getLeafForInsert(currentNode.value) + linkNewNode(currentNode, leaf) + } + } } diff --git a/lib/src/main/kotlin/treelib/binTree/BINTree.kt b/lib/src/main/kotlin/treelib/binTree/BINTree.kt index f8ac560..f0a92d9 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINTree.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINTree.kt @@ -3,12 +3,12 @@ package treelib.binTree import treelib.abstractTree.Tree import treelib.singleObjects.Container -class BINTree, Value> - : Tree>, BINStateContainer>>() { +class BINTree, V> + : Tree>, BINStateContainer>, BINVertex>>() { - override val treeStruct = BINStruct>() + override val treeStruct = BINStruct>() - operator fun BINTree.get(key: Key): Value? = getItem(key) + operator fun BINTree.get(key: K): V? = getItem(key) - operator fun BINTree.set(key: Key, value: Value) = putItem(key to value) + operator fun BINTree.set(key: K, value: V) = putItem(key to value) } diff --git a/lib/src/main/kotlin/treelib/binTree/BINVertex.kt b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt new file mode 100644 index 0000000..cb489a0 --- /dev/null +++ b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt @@ -0,0 +1,5 @@ +package treelib.binTree + +import treelib.abstractTree.Vertex + +open class BINVertex>(override val value: Pack) : Vertex() \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index fe73c5b..c8c4cf6 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -4,9 +4,9 @@ import treelib.abstractTree.balanced.BalancerParent import treelib.singleObjects.Markers import treelib.singleObjects.exceptions.IllegalBaseNodeException import treelib.singleObjects.exceptions.IllegalNodeStateException -import treelib.singleObjects.exceptions.ImpossibleCaseException -class RBBalancer>(private var root: RBNode?): BalancerParent, RBStateContainer>() { +class RBBalancer>(private var root: RBNode?) : + BalancerParent, RBStateContainer>() { init { root?.color = Markers.BLACK @@ -30,7 +30,7 @@ class RBBalancer>(private var root: RBNode?): Balan private fun getRoot(node: RBNode): RBNode { var currentNode = node while (currentNode.parent != null) - currentNode = currentNode.parent ?: throw IllegalNodeStateException() + currentNode = currentNode.parent ?: throw NullPointerException() root = currentNode root?.color = Markers.BLACK return currentNode @@ -38,17 +38,17 @@ class RBBalancer>(private var root: RBNode?): Balan private fun nodeIsLeaf(node: RBNode?): Boolean { if (node == null) - throw IllegalBaseNodeException() + throw NullPointerException() return node.right == null && node.left == null } - override fun balance(state: RBStateContainer): RBNode { - val node = state.contentNode ?: throw IllegalBaseNodeException() + override fun balance(stateContainer: RBStateContainer): RBNode { + val node = stateContainer.contentNode + ?: throw IllegalBaseNodeException() val uncle = getUncle(node) when { /** node insertion case **/ - node.color == Markers.RED && node.right == null && node.left == null-> - { + node.color == Markers.RED && node.right == null && node.left == null -> { var currentNode = node if (currentNode.parent?.color == Markers.RED && uncle?.color == Markers.RED) { @@ -58,52 +58,57 @@ class RBBalancer>(private var root: RBNode?): Balan return getRoot(currentNode) } - var parent = currentNode.parent ?: throw IllegalNodeStateException() + var parent = + currentNode.parent ?: throw IllegalStateException() // в данном случае родитель не может быть null when (parent) { parent.parent?.left -> { if (currentNode == parent.right) { leftRotate(parent) currentNode = parent } - parent = currentNode.parent?.parent ?: throw IllegalNodeStateException() + parent = + currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED currentNode.left?.color = Markers.RED } + parent.parent?.right -> { if (currentNode == parent.left) { rightRotate(parent) currentNode = parent } - parent = currentNode.parent?.parent ?: throw IllegalNodeStateException() + parent = + currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED currentNode.left?.color = Markers.RED } - else -> throw IllegalNodeStateException() + + else -> throw IllegalStateException() // невозможное условие выполнения } if (currentNode.parent == null) root = currentNode - return root ?: throw IllegalNodeStateException() + return root ?: throw NullPointerException() } /** node removal cases **/ - node.color == Markers.RED && (node.right != null || node.left != null) -> - { + node.color == Markers.RED && (node.right != null || node.left != null) -> { when { /** black leaf removal case **/ node.right?.color == Markers.BLACK -> { return firstCase(node, node.left) } + node.left?.color == Markers.BLACK -> { return firstCase(node, node.right) } } } - node.color == Markers.BLACK -> - { + + node.color == Markers.BLACK -> { return when { /** red leaf removal case **/ (node.left == null && node.right == null) || @@ -112,16 +117,16 @@ class RBBalancer>(private var root: RBNode?): Balan getRoot(node) } /** black leaf removal case **/ - node.left == null || node.right == null-> { + node.left == null || node.right == null -> { firstCase(node, null) } - else -> throw IllegalNodeStateException() + else -> throw IllegalStateException() } } } - throw ImpossibleCaseException() + throw IllegalStateException() } private fun afterInsert(node: RBNode): RBNode { @@ -133,8 +138,7 @@ class RBBalancer>(private var root: RBNode?): Balan currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK - } - else if(uncle != null){ + } else if (uncle != null) { return currentNode } } @@ -145,7 +149,7 @@ class RBBalancer>(private var root: RBNode?): Balan /** black node removal case **/ private fun firstCase(parent: RBNode?, node: RBNode?): RBNode { return when { - parent == null && node == null -> throw IllegalBaseNodeException() + parent == null && node == null -> throw NullPointerException() parent != null -> { when (parent.color) { Markers.RED -> secondCase(parent, node) @@ -154,13 +158,13 @@ class RBBalancer>(private var root: RBNode?): Balan getRoot(parent) } - else -> getRoot(node ?: throw IllegalBaseNodeException()) + else -> getRoot(node ?: throw IllegalNodeStateException()) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw IllegalBaseNodeException() + var brother = getBrother(parent, node) ?: throw IllegalNodeStateException() if (brother.color == Markers.RED) throw NullPointerException() @@ -171,52 +175,48 @@ class RBBalancer>(private var root: RBNode?): Balan } when (node) { - parent.left -> - { + parent.left -> { if (brother.right?.color == Markers.RED) { leftRotate(parent) brother.left?.color = Markers.RED brother.right?.color = Markers.RED brother.color = Markers.BLACK - } - else if (brother.left?.color == Markers.RED) { + } else if (brother.left?.color == Markers.RED) { brother = rightRotate(brother) leftRotate(parent) brother.left?.color = Markers.BLACK brother.left?.color = Markers.BLACK brother.color = Markers.RED - } - else { - throw NullPointerException() + } else { + throw IllegalStateException() } } - parent.right -> - { + + parent.right -> { if (brother.left?.color == Markers.RED) { rightRotate(parent) brother.color = Markers.BLACK brother.left?.color = Markers.RED brother.right?.color = Markers.RED - } - else if (brother.right?.color == Markers.RED) { + } else if (brother.right?.color == Markers.RED) { brother = leftRotate(brother) rightRotate(parent) brother.color = Markers.RED brother.left?.color = Markers.BLACK brother.right?.color = Markers.BLACK - } - else { - throw NullPointerException() + } else { + throw IllegalStateException() } } - else -> throw NullPointerException() + + else -> throw IllegalStateException() } } /** parent is black **/ private fun thirdCase(parent: RBNode, node: RBNode?) { - val brother = getBrother(parent, node) ?: throw NullPointerException() + val brother = getBrother(parent, node) ?: throw IllegalNodeStateException() when (brother.color) { Markers.RED -> thirdCaseSubFirst(brother, parent) Markers.BLACK -> thirdCaseSubSecond(brother, parent) @@ -226,9 +226,8 @@ class RBBalancer>(private var root: RBNode?): Balan /** black parent and red brother **/ private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { - brother.parent?.left -> - { - var rightBrotherSon = brother.right ?: throw NullPointerException() + brother.parent?.left -> { + var rightBrotherSon = brother.right ?: throw IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED @@ -244,7 +243,8 @@ class RBBalancer>(private var root: RBNode?): Balan rightBrotherSon.color = Markers.RED leftRotate(rightBrotherSon) - rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() + rightBrotherSon = + rightBrotherSon.parent ?: throw IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -254,20 +254,21 @@ class RBBalancer>(private var root: RBNode?): Balan rightRotate(parent) } } - brother.parent?.right -> - { + + brother.parent?.right -> { var leftBrotherSon = brother.left ?: throw NullPointerException() if (leftBrotherSon.right?.color != Markers.RED && leftBrotherSon.left?.color != Markers.RED) { leftBrotherSon.color = Markers.RED brother.color = Markers.BLACK - leftRotate(brother.parent ?: throw NullPointerException()) + leftRotate(brother.parent ?: throw IllegalNodeStateException()) return } if (leftBrotherSon.left?.color == Markers.RED) { rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED - leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() + leftBrotherSon = + leftBrotherSon.parent ?: throw IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } @@ -277,7 +278,8 @@ class RBBalancer>(private var root: RBNode?): Balan leftRotate(parent) } } - else -> throw NullPointerException() + + else -> throw IllegalStateException() } } @@ -292,30 +294,28 @@ class RBBalancer>(private var root: RBNode?): Balan return } when { - brother.left?.color == Markers.RED -> - { + brother.left?.color == Markers.RED -> { brother.left?.color = Markers.BLACK if (brother == parent.left) { rightRotate(parent) - } - else { + } else { rightRotate(brother) leftRotate(parent) } } - brother.right?.color == Markers.RED -> - { + + brother.right?.color == Markers.RED -> { brother.right?.color = Markers.BLACK if (brother == parent.right) { leftRotate(parent) - } - else { + } else { leftRotate(brother) rightRotate(parent) } } - else -> throw NullPointerException() + + else -> throw IllegalStateException() } } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index efebbbf..a3cbe34 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -1,9 +1,13 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancedTreeStruct +import treelib.singleObjects.Markers +import treelib.singleObjects.exceptions.ImpossibleCaseException +import treelib.singleObjects.exceptions.MultithreadingException +import java.util.* class RBStruct> : - BalancedTreeStruct, RBStateContainer, RBBalancer>() { + BalancedTreeStruct, RBStateContainer, RBVertex, RBBalancer>() { override var root: RBNode? = null @@ -12,17 +16,17 @@ class RBStruct> : override fun generateStateDelete( deletedNode: RBNode?, contentNode: RBNode?, - ): RBStateContainer = RBStateContainer(contentNode) + ): RBStateContainer = RBStateContainer(contentNode) override fun generateStateInsert( insertNode: RBNode?, contentNode: RBNode?, - ): RBStateContainer = RBStateContainer(contentNode) + ): RBStateContainer = RBStateContainer(insertNode) override fun generateStateFind( findNode: RBNode?, contentNode: RBNode?, - ): RBStateContainer = RBStateContainer(contentNode) + ): RBStateContainer = RBStateContainer(findNode) override fun connectUnlinkedSubTreeWithParent( node: RBNode, @@ -36,29 +40,87 @@ class RBStruct> : (node.value < parent.value) -> { parent.left = childForLink } + (node.value > parent.value) -> { parent.right = childForLink } } - if (childForLink != null){ + if (childForLink != null) { childForLink.parent = parent } } else root?.let { root = childForLink + if (childForLink != null) childForLink.parent = null } } override fun getNodeKernel(node: RBNode): RBNode = RBNode(node.value, color = node.color) + override fun toVertex(node: RBNode): RBVertex { + return RBVertex(node.value, node.color) + } + override fun createNode(item: Pack): RBNode = RBNode(item) override fun linkNewNode(node: RBNode, parent: RBNode?): RBNode { - if (parent == null) root = node - else { + if (parent == null) { + root = node + root?.let { + it.color = Markers.BLACK + } ?: throw MultithreadingException(ImpossibleCaseException()) + } else { if (node.value > parent.value) parent.right = node else parent.left = node node.parent = parent } return node } + + fun > restoreStruct(preOrder: List, inOrder: List) { + var inOrderIndex = 0 + var preOrderIndex = 0 + val set = HashSet>() + val stack = LinkedList>() + + while (preOrderIndex in preOrder.indices) { + var currentNode: RBNode? + var drawNode: RBVertexType + + do { + drawNode = preOrder[preOrderIndex] + currentNode = createRBNode(drawNode) + if (root == null) { + root = currentNode + } + if (!stack.isEmpty()) { + if (set.contains(stack.peek())) { + set.remove(stack.peek()) + stack.pop().right = currentNode + } else { + stack.peek().left = currentNode + // связь с ролитилем + } + } + stack.push(currentNode) + } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) + + currentNode = null + while (stack.isEmpty() && inOrderIndex < inOrder.size && + stack.peek().value == inOrder[inOrderIndex].value + ) { + currentNode = stack.pop() + ++inOrderIndex + } + + if (currentNode != null) { + set.add(currentNode) + stack.push(currentNode) + } + } + } + + private fun > createRBNode(drawNode: RBVertexType): RBNode { + val node = RBNode(value = drawNode.value, color = drawNode.color) + return node + } } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt index 6ccfa32..a29234e 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt @@ -4,12 +4,12 @@ import treelib.abstractTree.Tree import treelib.singleObjects.Container -class RBTree, Value> : - Tree>, RBStateContainer>>() { +class RBTree, V> : + Tree>, RBStateContainer>, RBVertex>>() { - override val treeStruct = RBStruct>() + override val treeStruct = RBStruct>() - operator fun RBTree.get(key: Key): Value? = getItem(key) + operator fun RBTree.get(key: K): V? = getItem(key) - operator fun RBTree.set(key: Key, value: Value) = putItem(key to value) + operator fun RBTree.set(key: K, value: V) = putItem(key to value) } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBVertex.kt b/lib/src/main/kotlin/treelib/rbTree/RBVertex.kt new file mode 100644 index 0000000..aee1d95 --- /dev/null +++ b/lib/src/main/kotlin/treelib/rbTree/RBVertex.kt @@ -0,0 +1,9 @@ +package treelib.rbTree + +import treelib.abstractTree.Vertex +import treelib.singleObjects.Markers + +open class RBVertex>( + override val value: Pack, + val color: Markers, +) : Vertex() \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/singleObjects/Container.kt b/lib/src/main/kotlin/treelib/singleObjects/Container.kt index 5ad9da1..910cd24 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/Container.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/Container.kt @@ -1,5 +1,8 @@ package treelib.singleObjects +import kotlinx.serialization.Serializable + +@Serializable class Container, V>(val pair: Pair) : Comparable> { val key = pair.first diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt b/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt index 4744fc7..3303817 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt @@ -1,6 +1,6 @@ package treelib.singleObjects.exceptions -class IllegalBaseNodeException : Exception { +class IllegalBaseNodeException : Exception { constructor() : super( "A non-existent node (null) was passed to the method" ) diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt b/lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt new file mode 100644 index 0000000..3f16415 --- /dev/null +++ b/lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt @@ -0,0 +1,18 @@ +package treelib.singleObjects.exceptions + +class IncorrectUsage : Exception { + constructor() : super( + "Incorrect use of the tree" + ) + + constructor(message: String) : super( + "$message", + ) + + constructor(message: String, cause: Throwable) : super( + "$message", + cause, + ) + + constructor(cause: Throwable) : super(cause) +} \ No newline at end of file diff --git a/lib/src/test/kotlin/TestModelAVL.kt b/lib/src/test/kotlin/TestModelAVL.kt deleted file mode 100644 index c96773d..0000000 --- a/lib/src/test/kotlin/TestModelAVL.kt +++ /dev/null @@ -1,72 +0,0 @@ -import treelib.avlTree.AVLNode - -class TestModelAVL { - val firstTreeValues = - listOf(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - 1, null, null, null, - 14, null, - 23) - val secondTreeValues = - listOf(null, null, 45, null, null, null, null, null, null, null, null, null, null, null, null, null, - 1, 47, 63, 90, 163, 208, 315, 999, - 44, 72, 205, 507, - 57, 230, - 101) - val thirdTreeValues = - listOf(null, 33, null, null, 69, 72, null, null, null, null, 174, null, null, null, 293, 370, - 10, 63, 70, 93, 139, 180, 240, 351, - 59, 74, 163, 285, - 67, 192, - 104) - val thirdTreeValuesCase2 = - listOf(null, 33, null, null, 69, 72, null, null, null, null, 174, null, null, 293, null, null, - 10, 63, 70, 93, 139, 180, 285, 370, - 59, 74, 163, 351, - 67, 192, - 104) - val thirdTreeValuesCase3 = - listOf(null, 33, null, null, 69, 72, null, null, null, null, 174, null, null, null, null, null, - 10, 63, 70, 93, 139, 180, 285, 351, - 59, 74, 163, 293, - 67, 192, - 104) - val thirdTreeValuesCase4 = - listOf(null, 33, null, null, 69, 72, null, null, null, null, null, null, null, null, null, null, - 10, 63, 70, 93, 163, 180, 285, 351, - 59, 74, 174, 293, - 67, 192, - 104) - - fun >getTree(values: List): AVLNode { - val nodes = mutableListOf?>() - for (i in 0..15) { - if (values[i] != null) { - nodes.add(AVLNode(values[i]!!, null, null)) - } - else - nodes.add(null) - } - for (i in 16..29) { - if (values[i] != null) { - nodes.add(AVLNode(values[i]!!, nodes[2*(i-16)], nodes[2*(i-16)+1])) - updateHeight(nodes[i]) - } - else - nodes.add(null) - } - nodes.add(AVLNode(values[30]!!, nodes[28], nodes[29])) - - return nodes[30]!! - } - private fun >updateHeight(currentNode: AVLNode?) { - if (currentNode != null) - currentNode.height = ( maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u) - - } - private fun >getHeight(currentNode: AVLNode?): UInt { - return currentNode?.height ?: 0u - } - - -} \ No newline at end of file diff --git a/lib/src/test/kotlin/AVLBalancerTest.kt b/lib/src/test/kotlin/treelib/AVLBalancerTest.kt similarity index 98% rename from lib/src/test/kotlin/AVLBalancerTest.kt rename to lib/src/test/kotlin/treelib/AVLBalancerTest.kt index 862a497..43b6978 100644 --- a/lib/src/test/kotlin/AVLBalancerTest.kt +++ b/lib/src/test/kotlin/treelib/AVLBalancerTest.kt @@ -1,3 +1,5 @@ +package treelib + import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertEquals import treelib.avlTree.AVLBalancer @@ -124,7 +126,6 @@ class AVLBalancerTest { } - } @DisplayName("Tests to check the operation of the balancer after insertion") @@ -172,8 +173,8 @@ class AVLBalancerTest { { assertEquals(2, root.right?.left?.height?.toInt()) }, { assertEquals(3, root.left?.left?.height?.toInt()) }, { assertEquals(2, root.left?.left?.right?.height?.toInt()) }, - { assertEquals(1, root.left?.left?.right?.right?.height?.toInt())}, - { assertEquals(1, root.left?.left?.left?.height?.toInt())} + { assertEquals(1, root.left?.left?.right?.right?.height?.toInt()) }, + { assertEquals(1, root.left?.left?.left?.height?.toInt()) } ) assertAll( "Grouped Assertions of values", @@ -181,8 +182,8 @@ class AVLBalancerTest { { assertEquals(205, root.right?.left?.value) }, { assertEquals(44, root.left?.left?.value) }, { assertEquals(46, root.left?.left?.right?.value) }, - { assertEquals(47, root.left?.left?.right?.right?.value)}, - { assertEquals(null, root.left?.left?.right?.left?.left?.value)} + { assertEquals(47, root.left?.left?.right?.right?.value) }, + { assertEquals(null, root.left?.left?.right?.left?.left?.value) } ) /** Second insert **/ @@ -203,7 +204,7 @@ class AVLBalancerTest { { assertEquals(46, root.left?.left?.value) }, { assertEquals(1, root.left?.left?.left?.left?.value) }, { assertEquals(null, root.left?.left?.right?.left?.value) }, - { assertEquals(null, root.left?.left?.right?.right?.left?.value)} + { assertEquals(null, root.left?.left?.right?.right?.left?.value) } ) /** Third insert **/ @@ -223,11 +224,11 @@ class AVLBalancerTest { { assertEquals(101, root.value) }, { assertEquals(205, root.right?.left?.value) }, { assertEquals(46, root.left?.left?.value) }, - { assertEquals(49, root.left?.left?.right?.value)}, + { assertEquals(49, root.left?.left?.right?.value) }, { assertEquals(1, root.left?.left?.left?.left?.value) }, { assertEquals(47, root.left?.left?.right?.left?.value) }, - { assertEquals(null, root.left?.left?.right?.right?.left?.value)}, - { assertEquals(null, root.left?.left?.right?.right?.right?.value)} + { assertEquals(null, root.left?.left?.right?.right?.left?.value) }, + { assertEquals(null, root.left?.left?.right?.right?.right?.value) } ) } } diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt new file mode 100644 index 0000000..433618a --- /dev/null +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -0,0 +1,108 @@ +package treelib + +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.fail +import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer +import treelib.avlTree.AVLStruct +import treelib.avlTree.AVLVertex +import utils.AVLAnalyzer +import utils.TreeStructWrapper + +@DisplayName("Test: AVL Struct") +class AVLStructTest { + private val treeW = TreeStructWrapper, AVLVertex, AVLStateContainer, AVLStruct>() + private val treeH = AVLAnalyzer(::testAssert) + private var treeStruct = AVLStruct() + + private fun testAssert(msg: String): Nothing = fail(msg) + + @BeforeEach + fun reInitClassUnderTest() { + treeStruct = AVLStruct() + } + + @Test + fun `test one root`() { + val num = mutableListOf(1) + for (i in num) { + treeStruct.insert(i) + } + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } + } + + @Test + fun `test height 3`() { + val num = mutableListOf(3, 2, 1, 4) + for (i in num) { + treeStruct.insert(i) + } + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } + } + + @Test + fun `test height 3 with delete root`() { + val num = mutableListOf(3, 2, 1, 4) + for (i in num) { + treeStruct.insert(i) + } + treeStruct.delete(2) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } + } + + @Test + fun `test 100000 arguments`() { + for (i in 1..100000) { + treeStruct.insert(i) + } + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } + } + + @Test + fun `test 100000 arguments and delete`() { + for (i in 1..100000) { + treeStruct.insert(i) + } + treeStruct.delete(5000) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } + } + + @Test + fun `test two arguments`() { + treeStruct.insert(2) + treeStruct.insert(3) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } + } + + @Test + fun `test many arguments`() { + val num = mutableListOf(3, 2, 1, 4, 2343, 123213, 3213, 657, 534, 12432, 5676756, 321, 5436546, 5435) + for (i in num) { + treeStruct.insert(i) + } + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/AVLTreeTest.kt b/lib/src/test/kotlin/treelib/AVLTreeTest.kt new file mode 100644 index 0000000..63073da --- /dev/null +++ b/lib/src/test/kotlin/treelib/AVLTreeTest.kt @@ -0,0 +1,83 @@ +package treelib + +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import treelib.avlTree.* +import treelib.singleObjects.Container +import utils.TreeStructWrapper +import utils.TreeWrapper +import kotlin.test.assertEquals + + +class AVLTreeTest { + private val tree = AVLTree() + private val treeW = + TreeWrapper>, AVLVertex>, AVLStateContainer>, AVLStruct>, AVLTree>() + private val treeSW = + TreeStructWrapper, AVLNode>, AVLVertex>, AVLStateContainer>, AVLStruct>>() + + + @ParameterizedTest + @ValueSource(strings = ["5 3 8 9", "1 2 3 4", "4 3 5 2", "4 3 2 1", "2 3 1 4"]) + fun `test check root`(str: String) { + val numbers = str.split(" ").map { it.toInt() }.toMutableList() + val num = mutableListOf>() + for (i in numbers) { + num.add(Pair(i, 1)) + } + tree.putItems(num) + + numbers.sort() + val root = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key + if (root == numbers[1]) { + assertEquals(expected = root, actual = numbers[1]) + } else { + assertEquals(expected = root, actual = numbers[2]) + } + } + + @ParameterizedTest + @ValueSource(strings = ["1 1000", "1 10000", "1 100000", "1 1000000"]) + fun `test add many args and delete root`(str: String) { + val numbers = str.split(" ").map { it.toInt() }.toMutableList() + for (i in numbers[0]..numbers[1]) { + tree.putItem(Pair(i, i)) + } + val root = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key + if (root != null) { + treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.let { tree.deleteItem(it.key) } + } + + when (numbers[1]) { + 1000 -> assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, actual = 513) + 10000 -> assertEquals( + expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, + actual = 4097 + ) + + 100000 -> assertEquals( + expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, + actual = 65537 + ) + + else -> assertEquals( + expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, + actual = 524289 + ) + } + } + + @ParameterizedTest + @ValueSource(strings = ["5", "0"]) + fun `test delete root one arg`(str: String) { + val numbers = str.split(" ").map { it.toInt() }.toMutableList() + val num = mutableListOf>() + for (i in numbers) { + num.add(Pair(i, 1)) + } + tree.putItems(num) + tree.deleteItem(numbers[0]) + + assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, actual = null) + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt new file mode 100644 index 0000000..bab0d2d --- /dev/null +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -0,0 +1,495 @@ +package treelib + +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.fail +import treelib.binTree.BINNode +import treelib.binTree.BINStateContainer +import treelib.binTree.BINStruct +import treelib.binTree.BINVertex +import utils.BINAnalyzer +import utils.TreeStructWrapper +import kotlin.test.assertEquals + +@DisplayName("Test: Binary Search Tree Struct") +class BINStructTest { + val treeW = TreeStructWrapper, BINVertex, BINStateContainer, BINStruct>() + var treeStruct = BINStruct() + + private fun testAssert(msg: String): Nothing = fail(msg) + + private val analyzer = BINAnalyzer(::testAssert) + + @BeforeEach + fun reInitClassUnderTest() { + treeStruct = BINStruct() + } + + @Test + fun `test delete root`() { + val num = mutableListOf(5, 3, 7, 1, 9, -1, 4, 2, 0, 6) + for (i in num) { + treeStruct.insert(i) + } + treeStruct.delete(5) + val root = treeW.getPrivateNode(treeStruct)?.value + + assertEquals(expected = 6, actual = root) + } + + @Test + fun `test insert`() { + val num = mutableListOf(1, 2, 3, 4, 5, 8) + for (i in num) { + treeStruct.insert(i) + } + + val additional_num = mutableListOf(1, 2, 3, 5, 7, 8, 11) + for (i in additional_num) { + treeStruct.insert(i) + } + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_2 = treeW.getPrivateNode(treeStruct)?.right?.value + val node_null = treeW.getPrivateNode(treeStruct)?.left?.value + val node_3 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node_null1 = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node_4 = treeW.getPrivateNode(treeStruct)?.right?.right?.right?.value + + assertEquals(expected = root, actual = 1) + assertEquals(expected = node_null, actual = null) + assertEquals(expected = node_4, actual = 4) + assertEquals(expected = node_2, actual = 2) + assertEquals(expected = node_3, actual = 3) + assertEquals(expected = node_null1, actual = null) + } + + @Test + fun `test find ordinary`() { + val num = mutableListOf(2, 3, 1, 4, 5, 10) + + assertEquals(expected = treeStruct.find(2), actual = null) + + for (i in num) { + treeStruct.insert(i) + } + + assertEquals(expected = treeStruct.find(2), actual = 2) + } + + @Test + fun `test find null`() { + val num = mutableListOf(1) + treeStruct.insert(num[0]) + + assertEquals(treeStruct.find(2), null) + + } + + @Test + fun `test find root`() { + val num = mutableListOf(1) + treeStruct.insert(num[0]) + + assertEquals(treeStruct.find(1), 1) + } + + @Test + fun `test insert and delete root`() { + val num = mutableListOf(1, 2) + for (i in num) { + treeStruct.insert(i) + } + + treeStruct.delete(1) + + val additional_num = mutableListOf(1, 2, 11) + for (i in additional_num) { + treeStruct.insert(i) + } + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_1 = treeW.getPrivateNode(treeStruct)?.left?.value + val node_11 = treeW.getPrivateNode(treeStruct)?.right?.value + + + assertEquals(expected = root, actual = 2) + assertEquals(expected = node_1, actual = 1) + assertEquals(expected = node_11, actual = 11) + } + + @Test + fun `test delete nonexistent value right`() { + val num = mutableListOf(5, 6) + for (value in num) { + treeStruct.insert(value) + } + treeStruct.delete(6) + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_null_left = treeW.getPrivateNode(treeStruct)?.left?.value + val node_null_right = treeW.getPrivateNode(treeStruct)?.right?.value + + assertEquals(expected = null, actual = node_null_left) + assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = 5, actual = root) + } + + @Test + fun `test delete nonexistent value left`() { + val num = mutableListOf(6, 5) + for (value in num) { + treeStruct.insert(value) + } + treeStruct.delete(5) + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_null_left = treeW.getPrivateNode(treeStruct)?.left?.value + val node_null_right = treeW.getPrivateNode(treeStruct)?.right?.value + + assertEquals(expected = null, actual = node_null_left) + assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = 6, actual = root) + } + + @Test + fun `test delete no child root`() { + val num = mutableListOf(3) + for (i in num) { + treeStruct.insert(i) + } + treeStruct.delete(3) + + val node_null_left = treeW.getPrivateNode(treeStruct)?.left?.value + val node_null_right = treeW.getPrivateNode(treeStruct)?.right?.value + val root = treeW.getPrivateNode(treeStruct)?.value + + assertEquals(expected = null, actual = node_null_left) + assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = null, actual = root) + } + + @Test + fun `test delete no child right`() { + val num = mutableListOf(3, 10, 15) + for (i in num) { + treeStruct.insert(i) + } + treeStruct.delete(15) + + val node_10 = treeW.getPrivateNode(treeStruct)?.right?.value + val node_null_left = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node_null_right = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val root = treeW.getPrivateNode(treeStruct)?.value + + + assertEquals(expected = 10, actual = node_10) + assertEquals(expected = null, actual = node_null_left) + assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = 3, actual = root) + } + + @Test + fun `test delete no child left`() { + val num = mutableListOf(15, 10, 3) + for (i in num) { + treeStruct.insert(i) + } + treeStruct.delete(3) + + val node_10 = treeW.getPrivateNode(treeStruct)?.left?.value + val node_null_left = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val node_null_right = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val root = treeW.getPrivateNode(treeStruct)?.value + + + assertEquals(expected = 10, actual = node_10) + assertEquals(expected = null, actual = node_null_left) + assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = 15, actual = root) + } + + @Test + fun `test delete one child left`() { + val num = mutableListOf(3, 2, 1, 5) + for (value in num) { + treeStruct.insert(value) + } + treeStruct.delete(2) + + val node_1 = treeW.getPrivateNode(treeStruct)?.left?.value + val node_null_left = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val node_null_right = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val root = treeW.getPrivateNode(treeStruct)?.value + + assertEquals(expected = 1, actual = node_1) + assertEquals(expected = null, actual = node_null_left) + assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = 3, actual = root) + } + + @Test + fun `test delete one child right`() { + val num = mutableListOf(3, 1, 5, 6) + for (value in num) { + treeStruct.insert(value) + } + treeStruct.delete(5) + + val node_6 = treeW.getPrivateNode(treeStruct)?.right?.value + val node_null_left = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node_null_right = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val root = treeW.getPrivateNode(treeStruct)?.value + + assertEquals(expected = 6, actual = node_6) + assertEquals(expected = null, actual = node_null_left) + assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = 3, actual = root) + } + + @Test + fun `test delete one child root`() { + val num = mutableListOf(3, 6) + for (value in num) { + treeStruct.insert(value) + } + treeStruct.delete(3) + + val node_6 = treeW.getPrivateNode(treeStruct)?.value + val node_null_left = treeW.getPrivateNode(treeStruct)?.left?.value + val node_null_right = treeW.getPrivateNode(treeStruct)?.right?.value + + assertEquals(expected = 6, actual = node_6) + assertEquals(expected = null, actual = node_null_left) + assertEquals(expected = null, actual = node_null_right) + } + + @Test + fun `test delete one child with family`() { + val num = mutableListOf(10, 7, 13, 6, 3, 1, 5, 2, 4, 15) + for (value in num) { + treeStruct.insert(value) + } + treeStruct.delete(7) + val node_6 = treeW.getPrivateNode(treeStruct)?.left?.value + val node_3 = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val node_null_right = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val node_1 = treeW.getPrivateNode(treeStruct)?.left?.left?.left?.value + val node_2 = treeW.getPrivateNode(treeStruct)?.left?.left?.left?.right?.value + val node_5 = treeW.getPrivateNode(treeStruct)?.left?.left?.right?.value + val node_4 = treeW.getPrivateNode(treeStruct)?.left?.left?.right?.left?.value + val root = treeW.getPrivateNode(treeStruct)?.value + + assertEquals(expected = 6, actual = node_6) + assertEquals(expected = 3, actual = node_3) + assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = 1, actual = node_1) + assertEquals(expected = 2, actual = node_2) + assertEquals(expected = 5, actual = node_5) + assertEquals(expected = 4, actual = node_4) + assertEquals(expected = 10, actual = root) + } + + @Test + fun `test delete two child only three element`() { + val num = mutableListOf(2, 1, 3) + for (i in num) { + treeStruct.insert(i) + } + treeStruct.delete(2) + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_1 = treeW.getPrivateNode(treeStruct)?.left?.value + val node_null_left1 = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val node_null_right1 = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val node_null_right_root = treeW.getPrivateNode(treeStruct)?.right?.value + + assertEquals(expected = root, actual = 3) + assertEquals(expected = node_1, actual = 1) + assertEquals(expected = node_null_left1, actual = null) + assertEquals(expected = node_null_right1, actual = null) + assertEquals(expected = node_null_right_root, actual = null) + } + + @Test + fun `test delete two child without family`() { + val num = mutableListOf(10, 7, 5, 4, 6) + for (i in num) { + treeStruct.insert(i) + } + treeStruct.delete(7) + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_5 = treeW.getPrivateNode(treeStruct)?.left?.value + val node_4 = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val node_6 = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val node_null_left4 = treeW.getPrivateNode(treeStruct)?.left?.left?.left?.value + val node_null_right4 = treeW.getPrivateNode(treeStruct)?.left?.left?.right?.value + val node_null_left6 = treeW.getPrivateNode(treeStruct)?.left?.right?.left?.value + val node_null_right6 = treeW.getPrivateNode(treeStruct)?.left?.right?.left?.value + + assertEquals(expected = root, actual = 10) + assertEquals(expected = node_5, actual = 5) + assertEquals(expected = node_4, actual = 4) + assertEquals(expected = node_6, actual = 6) + assertEquals(expected = node_null_left4, actual = null) + assertEquals(expected = node_null_right4, actual = null) + assertEquals(expected = node_null_left6, actual = null) + assertEquals(expected = node_null_right6, actual = null) + } + + @Test + fun `test two child double delete and delete root`() { + val num = mutableListOf(6, 8, 10, 7) + for (i in num) { + treeStruct.insert(i) + } + + treeStruct.delete(6) + treeStruct.delete(7) + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_null_left = treeW.getPrivateNode(treeStruct)?.left?.value + val node_10 = treeW.getPrivateNode(treeStruct)?.right?.value + val node_null_right10 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node_null_left10 = treeW.getPrivateNode(treeStruct)?.right?.left?.value + + assertEquals(expected = root, actual = 8) + assertEquals(expected = node_10, actual = 10) + assertEquals(expected = node_null_left10, actual = null) + assertEquals(expected = node_null_right10, actual = null) + assertEquals(expected = node_null_left, actual = null) + } + + @Test + fun `test two child delete min element in right tree`() { + val num = mutableListOf(6, 8, 10, 7, 12, 9) + for (i in num) { + treeStruct.insert(i) + } + + treeStruct.delete(8) + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_null_left = treeW.getPrivateNode(treeStruct)?.left?.value + val node_9 = treeW.getPrivateNode(treeStruct)?.right?.value + val node_7 = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node_10 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node_12 = treeW.getPrivateNode(treeStruct)?.right?.right?.right?.value + val node_null = treeW.getPrivateNode(treeStruct)?.right?.right?.left?.value + + assertEquals(expected = root, actual = 6) + assertEquals(expected = node_9, actual = 9) + assertEquals(expected = node_null, actual = null) + assertEquals(expected = node_null_left, actual = null) + assertEquals(expected = node_7, actual = 7) + assertEquals(expected = node_10, actual = 10) + assertEquals(expected = node_12, actual = 12) + } + + @Test + fun `test two child delete min element in right tree for root`() { + val num = mutableListOf(8, 10, 7, 12, 9) + for (i in num) { + treeStruct.insert(i) + } + + treeStruct.delete(8) + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_7 = treeW.getPrivateNode(treeStruct)?.left?.value + val node_10 = treeW.getPrivateNode(treeStruct)?.right?.value + val node_12 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node_null = treeW.getPrivateNode(treeStruct)?.right?.left?.value + + assertEquals(expected = root, actual = 9) + assertEquals(expected = node_null, actual = null) + assertEquals(expected = node_7, actual = 7) + assertEquals(expected = node_10, actual = 10) + assertEquals(expected = node_12, actual = 12) + } + + @Test + fun `test two child delete min element in right tree for rightmost element`() { + val num = mutableListOf(8, 10, 7, 12, 13, 14) + for (i in num) { + treeStruct.insert(i) + } + + treeStruct.delete(8) + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_7 = treeW.getPrivateNode(treeStruct)?.left?.value + val node_12 = treeW.getPrivateNode(treeStruct)?.right?.value + val node_13 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node_14 = treeW.getPrivateNode(treeStruct)?.right?.right?.right?.value + val node_null = treeW.getPrivateNode(treeStruct)?.right?.left?.value + + assertEquals(expected = root, actual = 10) + assertEquals(expected = node_null, actual = null) + assertEquals(expected = node_7, actual = 7) + assertEquals(expected = node_13, actual = 13) + assertEquals(expected = node_14, actual = 14) + assertEquals(expected = node_12, actual = 12) + } + + @Test + fun `test two child delete min element in right tree but in Tree`() { + val num = mutableListOf(8, 12, 15, 13, 10, 11, 9) + for (i in num) { + treeStruct.insert(i) + } + + treeStruct.delete(12) + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_13 = treeW.getPrivateNode(treeStruct)?.right?.value + val node_15 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node_10 = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node_11 = treeW.getPrivateNode(treeStruct)?.right?.left?.right?.value + val node_9 = treeW.getPrivateNode(treeStruct)?.right?.left?.left?.value + val node_null = treeW.getPrivateNode(treeStruct)?.right?.right?.left?.value + + assertEquals(expected = root, actual = 8) + assertEquals(expected = node_10, actual = 10) + assertEquals(expected = node_11, actual = 11) + assertEquals(expected = node_13, actual = 13) + assertEquals(expected = node_9, actual = 9) + assertEquals(expected = node_15, actual = 15) + assertEquals(expected = node_null, actual = null) + } + + @Test + fun `test two child delete min element in right tree for leftmost element`() { + val num = mutableListOf(8, 10, 7, 6) + for (i in num) { + treeStruct.insert(i) + } + + treeStruct.delete(8) + + val root = treeW.getPrivateNode(treeStruct)?.value + val node_7 = treeW.getPrivateNode(treeStruct)?.left?.value + val node_6 = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val node_null = treeW.getPrivateNode(treeStruct)?.right?.value + + assertEquals(expected = root, actual = 10) + assertEquals(expected = node_null, actual = null) + assertEquals(expected = node_7, actual = 7) + assertEquals(expected = node_6, actual = 6) + } + + @Test + fun `test analyzer`() { + val num = mutableListOf(6, 8, 10, 7, 12, 9) + for (i in num) { + treeStruct.insert(i) + } + + treeStruct.delete(8) + + val root = treeW.getPrivateNode(treeStruct) + root?.let { analyzer.checkTree(root) } ?: Exception("CHzh") + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/BINTreeTest.kt b/lib/src/test/kotlin/treelib/BINTreeTest.kt new file mode 100644 index 0000000..a13d174 --- /dev/null +++ b/lib/src/test/kotlin/treelib/BINTreeTest.kt @@ -0,0 +1,89 @@ +package treelib + +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import treelib.binTree.* +import treelib.singleObjects.Container +import utils.TreeStructWrapper +import utils.TreeWrapper +import kotlin.test.assertEquals + +class BINTreeTest { + private val tree = BINTree() + private val treeW = + TreeWrapper>, BINVertex>, BINStateContainer>, BINStruct>, BINTree>() + private val treeSW = + TreeStructWrapper, BINNode>, BINVertex>, BINStateContainer>, BINStruct>>() + + + // line - 0.6, branch - 0.5, methods = 0.9 + @ParameterizedTest + @ValueSource(ints = [1, 2, 0, 6, 4]) + fun `test putItem`(str: Int) { + tree.putItem((Pair(str, 1))) + } + + @ParameterizedTest + @ValueSource(strings = ["1 2 3 4", "5 6 7 8 9"]) + fun `test putItems`(str: String) { + val numbers = str.split(" ").map { it.toInt() }.toMutableList() + val num = mutableListOf>() + for (i in numbers) { + num.add(Pair(i, 1)) + } + tree.putItems(num) + assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, actual = numbers[0]) + assertEquals( + expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.right?.value?.key, + actual = numbers[1] + ) + } + + @Test + fun `test getItem`() { + val num = mutableListOf(Pair(1, 1), Pair(2, 1), Pair(5, 1), Pair(4, 1), Pair(3, 5)) + tree.putItems(num) + val temp = tree.getItem(3) + + assertEquals( + expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.right?.right?.left?.left?.value?.value, + actual = temp + ) + } + + @ParameterizedTest + @ValueSource(strings = ["5 2 3", "5 6 7", "5 6 13", "5 6 1", "192 5 6"]) + fun `test putItems and delete`(str: String) { + val numbers = str.split(" ").map { it.toInt() }.toMutableList() + val num = mutableListOf>() + for (i in numbers) { + num.add(Pair(i, 1)) + } + tree.putItems(num) + tree.deleteItem(numbers[2]) + + assertEquals(expected = tree.getItem(numbers[2]), actual = null) + assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, actual = numbers[0]) + } + + @ParameterizedTest + @ValueSource(strings = ["5 2 3 9", "5 6 7 1", "5 6 13 4", "5 6 1", "192 5 6 222"]) + fun `test putItems and delete root`(str: String) { + val numbers = str.split(" ").map { it.toInt() }.toMutableList() + val num = mutableListOf>() + for (i in numbers) { + num.add(Pair(i, 1)) + } + tree.putItems(num) + val root = numbers[0] + numbers.sort() + val index = numbers.indexOf(root) + tree.deleteItem(root) + + assertEquals( + expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, + actual = numbers[index + 1] + ) + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/RBBalancerTest.kt b/lib/src/test/kotlin/treelib/RBBalancerTest.kt similarity index 96% rename from lib/src/test/kotlin/RBBalancerTest.kt rename to lib/src/test/kotlin/treelib/RBBalancerTest.kt index 0bc9e43..41f8bdd 100644 --- a/lib/src/test/kotlin/RBBalancerTest.kt +++ b/lib/src/test/kotlin/treelib/RBBalancerTest.kt @@ -1,3 +1,5 @@ +package treelib + import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested @@ -7,10 +9,11 @@ import treelib.rbTree.RBBalancer import treelib.rbTree.RBNode import treelib.rbTree.RBStateContainer import treelib.singleObjects.Markers + class RBBalancerTest { val testModel = TestModelRBT() - fun > countBlackNodes(node: RBNode) = testModel.countBlackNodes(node) + fun > countBlackNodes(node: RBNode) = testModel.countBlackNodes(node) @DisplayName("Tests to check the operation of the balancer after removal") @Nested @@ -20,7 +23,10 @@ class RBBalancerTest { val firstBalancer = RBBalancer(null) assertEquals(15, firstBalancer.balance(RBStateContainer(RBNode(15, null, null, null, Markers.BLACK))).value) val secondBalancer = RBBalancer(null) - assertEquals("Test", secondBalancer.balance(RBStateContainer(RBNode("Test", null, null, null, Markers.BLACK))).value) + assertEquals( + "Test", + secondBalancer.balance(RBStateContainer(RBNode("Test", null, null, null, Markers.BLACK))).value + ) } @Test @@ -98,7 +104,7 @@ class RBBalancerTest { { assertEquals(10, root?.left?.left?.value) }, { assertEquals(12, root?.left?.left?.right?.value) }, { assertEquals(14, nodes[17]?.right?.value) }, - { assertEquals(null, nodes[17]?.left)} + { assertEquals(null, nodes[17]?.left) } ) } @@ -116,7 +122,7 @@ class RBBalancerTest { { assertEquals(Markers.BLACK, root.color) }, { assertEquals(null, root.parent) }, { assertEquals(nodes[28]?.value, root.left?.value) }, - { assertEquals( nodes[29]?.value, root.right?.value ) } + { assertEquals(nodes[29]?.value, root.right?.value) } ) assertAll( @@ -135,7 +141,7 @@ class RBBalancerTest { { assertEquals(42, root.right?.left?.right?.value) }, { assertEquals(37, root.right?.left?.left?.value) }, { assertEquals(38, nodes[20]?.right?.value) }, - { assertEquals(null, nodes[20]?.left)} + { assertEquals(null, nodes[20]?.left) } ) } @@ -153,8 +159,8 @@ class RBBalancerTest { { assertEquals(Markers.BLACK, root.color) }, { assertEquals(29, root.value) }, { assertEquals(null, root.parent) }, - { assertEquals(null, root.left)}, - { assertEquals(null, root.right)} + { assertEquals(null, root.left) }, + { assertEquals(null, root.right) } ) } @@ -164,7 +170,7 @@ class RBBalancerTest { val root = nodes[30]!! nodes[13]?.value = 68 val rightBrotherSon = RBNode(70, null, null, nodes[13], Markers.RED) - val leftBrotherSon= RBNode(65, null, null, nodes[13], Markers.RED) + val leftBrotherSon = RBNode(65, null, null, nodes[13], Markers.RED) nodes[13]?.right = rightBrotherSon nodes[13]?.left = leftBrotherSon val balancer = RBBalancer(nodes[30]) @@ -188,8 +194,8 @@ class RBBalancerTest { { assertEquals(68, root.right?.right?.left?.value) }, { assertEquals(70, root.right?.right?.left?.right?.value) }, { assertEquals(63, root.right?.right?.left?.left?.value) }, - { assertEquals(65, nodes[22]?.right?.value)}, - { assertEquals(null, nodes[22]?.left)}, + { assertEquals(65, nodes[22]?.right?.value) }, + { assertEquals(null, nodes[22]?.left) }, ) diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt new file mode 100644 index 0000000..9f0b39e --- /dev/null +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -0,0 +1,153 @@ +package treelib + +import org.junit.jupiter.api.* +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct +import treelib.rbTree.RBVertex +import treelib.singleObjects.Markers +import utils.RBAnalyzer +import utils.TreeStructWrapper +import utils.fuzzers.RBStructFuzzer +import kotlin.test.assertEquals + +@DisplayName("Test: Red-Black Tree Struct") +class RBStructTest { + private val treeW = TreeStructWrapper, RBVertex, RBStateContainer, RBStruct>() + private var treeStruct = RBStruct() + + private fun testAssert(msg: String): Nothing = fail(msg) + + private val analyzer = RBAnalyzer(::testAssert) + + @BeforeEach + fun reInitClassUnderTest() { + treeStruct = RBStruct() + } + + @Test + fun `base test on creation root`() { + treeStruct.insert(6) + val root = treeW.getPrivateNode(treeStruct) + assertAll( + { assertEquals(root?.value, 6) }, + { assertEquals(root?.color, Markers.BLACK) }, + { analyzer.checkTree(root!!) } + ) + } + + @Test + fun `base test on creation root with left`() { + treeStruct.insert(6) + treeStruct.insert(3) + val root = treeW.getPrivateNode(treeStruct) + assertAll( + { assertEquals(root?.left?.value, 3) }, + { assertEquals(root?.left?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } + ) + } + + @Test + fun `base test on creation root with right`() { + treeStruct.insert(6) + treeStruct.insert(8) + val root = treeW.getPrivateNode(treeStruct) + assertAll( + { assertEquals(root?.right?.value, 8) }, + { assertEquals(root?.right?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } + ) + } + + @Test + fun `base test on creation children`() { + treeStruct.insert(6) + treeStruct.insert(8) + treeStruct.insert(3) + val root = treeW.getPrivateNode(treeStruct) + assertAll( + { assertEquals(root?.right?.value, 8) }, + { assertEquals(root?.left?.value, 3) }, + { assertEquals(root?.right?.color, Markers.RED) }, + { assertEquals(root?.left?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } + ) + } + + @Test + fun `base test delete root (left & right children)`() { + treeStruct.insert(6) + treeStruct.insert(8) + treeStruct.insert(3) + treeStruct.delete(6) + val root = treeW.getPrivateNode(treeStruct) + assertAll( + { assertEquals(root?.value, 8) }, + { assertEquals(root?.color, Markers.BLACK) }, + { analyzer.checkTree(root!!) } + ) + } + + @Test + fun `base test delete root (right child)`() { + treeStruct.insert(6) + treeStruct.insert(8) + treeStruct.delete(6) + val root = treeW.getPrivateNode(treeStruct) + assertAll( + { assertEquals(8, root?.value) }, + { assertEquals(Markers.BLACK, root?.color) }, + { analyzer.checkTree(root!!) } + ) + } + + @Test + fun `base test delete root (left child)`() { + treeStruct.insert(6) + treeStruct.insert(3) + treeStruct.delete(6) + val root = treeW.getPrivateNode(treeStruct) + assertAll( + { assertEquals(3, root?.value) }, + { assertEquals(Markers.BLACK, root?.color) }, + { analyzer.checkTree(root!!) } + ) + } + + @Test + fun `fazzer test`() { + val fazzer = RBStructFuzzer( + arrayOf( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 20, + 100, + 123, + 234, + 556, + 345677, + 88765, + 43, + 364, + 23456, + 2754 + ), ::testAssert + ) + fazzer.saveNextTestSets("TEST_TEST") + + assertAll( + { + fazzer.fuzzInvariantInsert(15, 10) + } + ) + } + +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/RBTreeTest.kt b/lib/src/test/kotlin/treelib/RBTreeTest.kt new file mode 100644 index 0000000..cd7ce2a --- /dev/null +++ b/lib/src/test/kotlin/treelib/RBTreeTest.kt @@ -0,0 +1,55 @@ +package treelib + +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import treelib.rbTree.* +import treelib.singleObjects.Container +import treelib.singleObjects.Markers +import utils.TreeStructWrapper +import utils.TreeWrapper +import kotlin.test.assertEquals + +class RBTreeTest { + private val tree = RBTree() + private val treeW = + TreeWrapper>, RBVertex>, RBStateContainer>, RBStruct>, RBTree>() + private val treeSW = + TreeStructWrapper, RBNode>, RBVertex>, RBStateContainer>, RBStruct>>() + + + @ParameterizedTest + @ValueSource(strings = ["1 2 3 4 6 5", "5 3 8 6 9 11 13", "10 11 15 12 17 18"]) + fun `test change color delete root`(str: String) { + val numbers = str.split(" ").map { it.toInt() }.toMutableList() + val num = mutableListOf>() + for (i in numbers) { + num.add(Pair(i, i)) + } + + tree.putItems(num) + + val rootR = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.right?.color + assertEquals(expected = rootR, actual = Markers.RED) + val root = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key + if (root != null) { + treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.let { tree.deleteItem(it.key) } + } + assertEquals(expected = rootR, actual = Markers.RED) + } + + @ParameterizedTest + @ValueSource(strings = ["1 2 5", "1 11 13", "10 11 18"]) + fun `test check color`(str: String) { + val numbers = str.split(" ").map { it.toInt() }.toMutableList() + val num = mutableListOf>() + for (i in numbers) { + num.add(Pair(i, i)) + } + + tree.putItems(num) + + assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.right?.color, actual = Markers.RED) + assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.left?.color, actual = Markers.RED) + assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.color, actual = Markers.BLACK) + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/TestModelAVL.kt b/lib/src/test/kotlin/treelib/TestModelAVL.kt new file mode 100644 index 0000000..9b4823a --- /dev/null +++ b/lib/src/test/kotlin/treelib/TestModelAVL.kt @@ -0,0 +1,86 @@ +package treelib + +import treelib.avlTree.AVLNode + +class TestModelAVL { + val firstTreeValues = + listOf( + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, + 1, null, null, null, + 14, null, + 23 + ) + val secondTreeValues = + listOf( + null, null, 45, null, null, null, null, null, null, null, null, null, null, null, null, null, + 1, 47, 63, 90, 163, 208, 315, 999, + 44, 72, 205, 507, + 57, 230, + 101 + ) + val thirdTreeValues = + listOf( + null, 33, null, null, 69, 72, null, null, null, null, 174, null, null, null, 293, 370, + 10, 63, 70, 93, 139, 180, 240, 351, + 59, 74, 163, 285, + 67, 192, + 104 + ) + val thirdTreeValuesCase2 = + listOf( + null, 33, null, null, 69, 72, null, null, null, null, 174, null, null, 293, null, null, + 10, 63, 70, 93, 139, 180, 285, 370, + 59, 74, 163, 351, + 67, 192, + 104 + ) + val thirdTreeValuesCase3 = + listOf( + null, 33, null, null, 69, 72, null, null, null, null, 174, null, null, null, null, null, + 10, 63, 70, 93, 139, 180, 285, 351, + 59, 74, 163, 293, + 67, 192, + 104 + ) + val thirdTreeValuesCase4 = + listOf( + null, 33, null, null, 69, 72, null, null, null, null, null, null, null, null, null, null, + 10, 63, 70, 93, 163, 180, 285, 351, + 59, 74, 174, 293, + 67, 192, + 104 + ) + + fun > getTree(values: List): AVLNode { + val nodes = mutableListOf?>() + for (i in 0..15) { + if (values[i] != null) { + nodes.add(AVLNode(values[i]!!, null, null)) + } else + nodes.add(null) + } + for (i in 16..29) { + if (values[i] != null) { + nodes.add(AVLNode(values[i]!!, nodes[2 * (i - 16)], nodes[2 * (i - 16) + 1])) + updateHeight(nodes[i]) + } else + nodes.add(null) + } + nodes.add(AVLNode(values[30]!!, nodes[28], nodes[29])) + + return nodes[30]!! + } + + private fun > updateHeight(currentNode: AVLNode?) { + if (currentNode != null) + currentNode.height = (maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u) + + } + + private fun > getHeight(currentNode: AVLNode?): UInt { + return currentNode?.height ?: 0u + } + + +} \ No newline at end of file diff --git a/lib/src/test/kotlin/TestModelRBT.kt b/lib/src/test/kotlin/treelib/TestModelRBT.kt similarity index 50% rename from lib/src/test/kotlin/TestModelRBT.kt rename to lib/src/test/kotlin/treelib/TestModelRBT.kt index ac86a6c..9e5a0ad 100644 --- a/lib/src/test/kotlin/TestModelRBT.kt +++ b/lib/src/test/kotlin/treelib/TestModelRBT.kt @@ -1,18 +1,20 @@ +package treelib + import treelib.rbTree.RBNode import treelib.singleObjects.Markers class TestModelRBT { - fun >countBlackNodes(node: RBNode): Int { + fun > countBlackNodes(node: RBNode): Int { var count = 0 var currentNode: RBNode? = node while (currentNode != null) { - count = if (currentNode.color == Markers.BLACK) count+1 else count + count = if (currentNode.color == Markers.BLACK) count + 1 else count currentNode = currentNode.parent } return count } - fun >deleteNode(remoteNode: RBNode, newNode: RBNode): RBNode { + fun > deleteNode(remoteNode: RBNode, newNode: RBNode): RBNode { remoteNode.left?.parent = newNode remoteNode.right?.parent = newNode newNode.left = remoteNode.left @@ -30,8 +32,7 @@ class TestModelRBT { else -> parent.right = newNode } newNode.parent = parent - } - else { + } else { newNode.parent = null } @@ -84,17 +85,42 @@ class TestModelRBT { fun getFirstTree(): MutableList?> { val values = listOf( 1, 7, 11, 14, 16, 20, 21, null, 29, 38, null, 45, 52, 70, null, null, - 4, 12, 18, 24, 37, 42, 63, 90, - 10, 19, 40, 71, - 15, 50, + 4, 12, 18, 24, 37, 42, 63, 90, + 10, 19, 40, 71, + 15, 50, 25 ) val markers = listOf( - Markers.BLACK, Markers.BLACK, Markers.BLACK, Markers.BLACK, Markers.RED, Markers.RED, Markers.RED, null, - Markers.RED, Markers.RED, null, Markers.RED, Markers.BLACK, Markers.BLACK, Markers.RED, Markers.RED, - Markers.RED, Markers.RED, Markers.BLACK, Markers.BLACK, Markers.BLACK, Markers.BLACK, Markers.RED, Markers.BLACK, - Markers.BLACK, Markers.BLACK, Markers.BLACK, Markers.BLACK, - Markers.RED, Markers.RED, + Markers.BLACK, + Markers.BLACK, + Markers.BLACK, + Markers.BLACK, + Markers.RED, + Markers.RED, + Markers.RED, + null, + Markers.RED, + Markers.RED, + null, + Markers.RED, + Markers.BLACK, + Markers.BLACK, + Markers.RED, + Markers.RED, + Markers.RED, + Markers.RED, + Markers.BLACK, + Markers.BLACK, + Markers.BLACK, + Markers.BLACK, + Markers.RED, + Markers.BLACK, + Markers.BLACK, + Markers.BLACK, + Markers.BLACK, + Markers.BLACK, + Markers.RED, + Markers.RED, Markers.BLACK ) @@ -102,38 +128,66 @@ class TestModelRBT { for (i in 1..31) { when (i) { in 1..16 -> { - if (values[i-1] != null ) - nodes.add(RBNode(values[i-1]!!, null, null, null, markers[i-1]!!)) + if (values[i - 1] != null) + nodes.add(RBNode(values[i - 1]!!, null, null, null, markers[i - 1]!!)) else nodes.add(null) } + in 17..24 -> { val delta = i - 17 - if (values[i-1] != null) - nodes.add(RBNode(values[i-1]!!, nodes[i-17+delta], nodes[i-17+delta+1], null, markers[i-1]!!)) + if (values[i - 1] != null) + nodes.add( + RBNode( + values[i - 1]!!, + nodes[i - 17 + delta], + nodes[i - 17 + delta + 1], + null, + markers[i - 1]!! + ) + ) else nodes.add(null) - nodes[i-17+delta]?.parent = nodes[i-1] - nodes[i-17+delta+1]?.parent = nodes[i-1] + nodes[i - 17 + delta]?.parent = nodes[i - 1] + nodes[i - 17 + delta + 1]?.parent = nodes[i - 1] } + in 25..28 -> { - val delta = i-25 - nodes.add( RBNode(values[i-1]!!, nodes[i-9+delta], nodes[i-9+delta+1], null, markers[i-1]!!)) - nodes[i-9+delta]?.parent = nodes[i-1] - nodes[i-9+delta+1]?.parent = nodes[i-1] + val delta = i - 25 + nodes.add( + RBNode( + values[i - 1]!!, + nodes[i - 9 + delta], + nodes[i - 9 + delta + 1], + null, + markers[i - 1]!! + ) + ) + nodes[i - 9 + delta]?.parent = nodes[i - 1] + nodes[i - 9 + delta + 1]?.parent = nodes[i - 1] } + in 29..30 -> { - val delta = i-29 - nodes.add(RBNode(values[i-1]!!, nodes[i-5+delta], nodes[i-5+delta+1], null, markers[i-1]!!)) - nodes[i-5+delta]?.parent = nodes[i-1] - nodes[i-5+delta+1]?.parent = nodes[i-1] + val delta = i - 29 + nodes.add( + RBNode( + values[i - 1]!!, + nodes[i - 5 + delta], + nodes[i - 5 + delta + 1], + null, + markers[i - 1]!! + ) + ) + nodes[i - 5 + delta]?.parent = nodes[i - 1] + nodes[i - 5 + delta + 1]?.parent = nodes[i - 1] } + else -> { - nodes.add(RBNode(values[i-1]!!, nodes[28], nodes[29], null, markers[i-1]!!)) - nodes[28]?.parent = nodes[i-1] - nodes[29]?.parent = nodes[i-1] + nodes.add(RBNode(values[i - 1]!!, nodes[28], nodes[29], null, markers[i - 1]!!)) + nodes[28]?.parent = nodes[i - 1] + nodes[29]?.parent = nodes[i - 1] } } } diff --git a/lib/src/test/kotlin/treelib/TreeStructsTest.kt b/lib/src/test/kotlin/treelib/TreeStructsTest.kt deleted file mode 100644 index a79b9ef..0000000 --- a/lib/src/test/kotlin/treelib/TreeStructsTest.kt +++ /dev/null @@ -1,513 +0,0 @@ -package treelib - -import org.junit.jupiter.api.* -import kotlin.test.assertEquals - -/* BINStruct */ -import treelib.binTree.BINNode -import treelib.binTree.BINStruct - -/* AVLStruct */ -import treelib.avlTree.AVLNode -import treelib.avlTree.AVLStateContainer -import treelib.avlTree.AVLStruct -import treelib.binTree.BINStateContainer - -/* RBStruct */ -import treelib.rbTree.RBNode -import treelib.rbTree.RBStateContainer -import treelib.rbTree.RBStruct - -@DisplayName("Test group: Structs testing") -class TreeStructsTest { - //TESTS ONLY WITH Comparable = INT, otherwise - errors - @Nested - @DisplayName("Test: Binary Search Tree Struct") - inner class BINStructTest { - val treeW = TreeStructWrapper, BINStateContainer, BINStruct>() - var classUnderTest = BINStruct() - - @BeforeEach - fun reInitClassUnderTest() { - classUnderTest = BINStruct() - } - @Test - fun `test delete root`() { - val num = mutableListOf(5, 3, 7, 1, 9, -1, 4 ,2, 0, 6) - for (i in num) { - classUnderTest.insert(i) - } - classUnderTest.delete(5) - val root = treeW.getPrivateNode(classUnderTest)?.value - - assertEquals(expected = 6, actual = root) - } - - @Test - fun `test insert`() { - val num = mutableListOf(1, 2, 3, 4, 5, 8) - for (i in num) { - classUnderTest.insert(i) - } - - val additional_num = mutableListOf(1, 2, 3, 5, 7, 8, 11) - for (i in additional_num) { - classUnderTest.insert(i) - } - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_2 = treeW.getPrivateNode(classUnderTest)?.right?.value - val node_null = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_3 = treeW.getPrivateNode(classUnderTest)?.right?.right?.value - val node_null1 = treeW.getPrivateNode(classUnderTest)?.right?.left?.value - val node_4 = treeW.getPrivateNode(classUnderTest)?.right?.right?.right?.value - - assertEquals(expected = root, actual = 1) - assertEquals(expected = node_null, actual = null) - assertEquals(expected = node_4, actual = 4) - assertEquals(expected = node_2, actual = 2) - assertEquals(expected = node_3, actual = 3) - assertEquals(expected = node_null1, actual = null) - } - - @Test - fun `test find ordinary`() { - val num = mutableListOf(2, 3, 1, 4, 5 ,10) - - assertEquals(expected = classUnderTest.find(2), actual = null) - - for (i in num) { - classUnderTest.insert(i) - } - - assertEquals(expected = classUnderTest.find(2), actual = 2) - } - - @Test - fun `test find null`() { - val num = mutableListOf(1) - classUnderTest.insert(num[0]) - - assertEquals(classUnderTest.find(2), null) - - } - - @Test - fun `test find root`() { - val num = mutableListOf(1) - classUnderTest.insert(num[0]) - - assertEquals(classUnderTest.find(1), 1) - } - - @Test - fun `test insert and delete root`() { - val num = mutableListOf(1, 2) - for (i in num) { - classUnderTest.insert(i) - } - - classUnderTest.delete(1) - - val additional_num = mutableListOf(1, 2, 11) - for (i in additional_num) { - classUnderTest.insert(i) - } - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_1 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_11 = treeW.getPrivateNode(classUnderTest)?.right?.value - - - assertEquals(expected = root, actual = 2) - assertEquals(expected = node_1, actual = 1) - assertEquals(expected = node_11, actual = 11) - } - - @Test - fun `test delete nonexistent value right`() { - val num = mutableListOf(5, 6) - for (value in num) { - classUnderTest.insert(value) - } - classUnderTest.delete(6) - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_null_left = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_null_right = treeW.getPrivateNode(classUnderTest)?.right?.value - - assertEquals(expected = null, actual = node_null_left) - assertEquals(expected = null, actual = node_null_right) - assertEquals(expected = 5, actual = root) - } - - @Test - fun `test delete nonexistent value left`() { - val num = mutableListOf(6, 5) - for (value in num) { - classUnderTest.insert(value) - } - classUnderTest.delete(5) - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_null_left = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_null_right = treeW.getPrivateNode(classUnderTest)?.right?.value - - assertEquals(expected = null, actual = node_null_left) - assertEquals(expected = null, actual = node_null_right) - assertEquals(expected = 6, actual = root) - } - - @Test - fun `test delete no child root`(){ - val num = mutableListOf(3) - for (i in num) { - classUnderTest.insert(i) - } - classUnderTest.delete(3) - - val node_null_left = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_null_right = treeW.getPrivateNode(classUnderTest)?.right?.value - val root = treeW.getPrivateNode(classUnderTest)?.value - - assertEquals(expected = null, actual = node_null_left) - assertEquals(expected = null, actual = node_null_right) - assertEquals(expected = null, actual = root) - } - - @Test - fun `test delete no child right`(){ - val num = mutableListOf(3, 10, 15) - for (i in num) { - classUnderTest.insert(i) - } - classUnderTest.delete(15) - - val node_10 = treeW.getPrivateNode(classUnderTest)?.right?.value - val node_null_left = treeW.getPrivateNode(classUnderTest)?.right?.left?.value - val node_null_right = treeW.getPrivateNode(classUnderTest)?.right?.right?.value - val root = treeW.getPrivateNode(classUnderTest)?.value - - - assertEquals(expected = 10, actual = node_10) - assertEquals(expected = null, actual = node_null_left) - assertEquals(expected = null, actual = node_null_right) - assertEquals(expected = 3, actual = root) - } - - @Test - fun `test delete no child left`(){ - val num = mutableListOf(15, 10, 3) - for (i in num) { - classUnderTest.insert(i) - } - classUnderTest.delete(3) - - val node_10 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_null_left = treeW.getPrivateNode(classUnderTest)?.left?.left?.value - val node_null_right = treeW.getPrivateNode(classUnderTest)?.left?.right?.value - val root = treeW.getPrivateNode(classUnderTest)?.value - - - assertEquals(expected = 10, actual = node_10) - assertEquals(expected = null, actual = node_null_left) - assertEquals(expected = null, actual = node_null_right) - assertEquals(expected = 15, actual = root) - } - - @Test - fun `test delete one child left`(){ - val num = mutableListOf(3, 2, 1, 5) - for (value in num) { - classUnderTest.insert(value) - } - classUnderTest.delete(2) - - val node_1 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_null_left = treeW.getPrivateNode(classUnderTest)?.left?.left?.value - val node_null_right = treeW.getPrivateNode(classUnderTest)?.left?.right?.value - val root = treeW.getPrivateNode(classUnderTest)?.value - - assertEquals(expected = 1 , actual = node_1) - assertEquals(expected = null , actual = node_null_left) - assertEquals(expected = null , actual = node_null_right) - assertEquals(expected = 3, actual = root) - } - - @Test - fun `test delete one child right`(){ - val num = mutableListOf(3, 1, 5, 6) - for (value in num) { - classUnderTest.insert(value) - } - classUnderTest.delete(5) - - val node_6 = treeW.getPrivateNode(classUnderTest)?.right?.value - val node_null_left = treeW.getPrivateNode(classUnderTest)?.right?.left?.value - val node_null_right = treeW.getPrivateNode(classUnderTest)?.right?.right?.value - val root = treeW.getPrivateNode(classUnderTest)?.value - - assertEquals(expected = 6 , actual = node_6) - assertEquals(expected = null , actual = node_null_left) - assertEquals(expected = null , actual = node_null_right) - assertEquals(expected = 3, actual = root) - } - - @Test - fun `test delete one child root`() { - val num = mutableListOf(3, 6) - for (value in num) { - classUnderTest.insert(value) - } - classUnderTest.delete(3) - - val node_6 = treeW.getPrivateNode(classUnderTest)?.value - val node_null_left = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_null_right = treeW.getPrivateNode(classUnderTest)?.right?.value - - assertEquals(expected = 6 , actual = node_6) - assertEquals(expected = null , actual = node_null_left) - assertEquals(expected = null , actual = node_null_right) - } - - @Test - fun `test delete one child with family`() { - val num = mutableListOf(10, 7, 13, 6, 3, 1, 5, 2, 4, 15) - for (value in num) { - classUnderTest.insert(value) - } - classUnderTest.delete(7) - val node_6 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_3 = treeW.getPrivateNode(classUnderTest)?.left?.left?.value - val node_null_right = treeW.getPrivateNode(classUnderTest)?.left?.right?.value - val node_1 = treeW.getPrivateNode(classUnderTest)?.left?.left?.left?.value - val node_2 = treeW.getPrivateNode(classUnderTest)?.left?.left?.left?.right?.value - val node_5 = treeW.getPrivateNode(classUnderTest)?.left?.left?.right?.value - val node_4 = treeW.getPrivateNode(classUnderTest)?.left?.left?.right?.left?.value - val root = treeW.getPrivateNode(classUnderTest)?.value - - assertEquals(expected = 6 , actual = node_6) - assertEquals(expected = 3 , actual = node_3) - assertEquals(expected = null , actual = node_null_right) - assertEquals(expected = 1 , actual = node_1) - assertEquals(expected = 2 , actual = node_2) - assertEquals(expected = 5 , actual = node_5) - assertEquals(expected = 4 , actual = node_4) - assertEquals(expected = 10, actual = root) - } - - @Test - fun `test delete two child only three element`() { - val num = mutableListOf(2, 1 ,3) - for (i in num) { - classUnderTest.insert(i) - } - classUnderTest.delete(2) - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_1 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_null_left1 = treeW.getPrivateNode(classUnderTest)?.left?.left?.value - val node_null_right1 = treeW.getPrivateNode(classUnderTest)?.left?.right?.value - val node_null_right_root = treeW.getPrivateNode(classUnderTest)?.right?.value - - assertEquals(expected = root, actual = 3) - assertEquals(expected = node_1, actual = 1) - assertEquals(expected = node_null_left1, actual = null) - assertEquals(expected = node_null_right1, actual = null) - assertEquals(expected = node_null_right_root, actual = null) - } - - @Test - fun `test delete two child without family`() { - val num = mutableListOf(10, 7, 5, 4, 6) - for (i in num) { - classUnderTest.insert(i) - } - classUnderTest.delete(7) - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_5 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_4 = treeW.getPrivateNode(classUnderTest)?.left?.left?.value - val node_6 = treeW.getPrivateNode(classUnderTest)?.left?.right?.value - val node_null_left4 = treeW.getPrivateNode(classUnderTest)?.left?.left?.left?.value - val node_null_right4 = treeW.getPrivateNode(classUnderTest)?.left?.left?.right?.value - val node_null_left6 = treeW.getPrivateNode(classUnderTest)?.left?.right?.left?.value - val node_null_right6 = treeW.getPrivateNode(classUnderTest)?.left?.right?.left?.value - - assertEquals(expected = root, actual = 10) - assertEquals(expected = node_5, actual = 5) - assertEquals(expected = node_4, actual = 4) - assertEquals(expected = node_6, actual = 6) - assertEquals(expected = node_null_left4, actual = null) - assertEquals(expected = node_null_right4, actual = null) - assertEquals(expected = node_null_left6, actual = null) - assertEquals(expected = node_null_right6, actual = null) - } - - @Test - fun `test two child double delete and delete root`() { - val num = mutableListOf(6, 8, 10, 7) - for (i in num) { - classUnderTest.insert(i) - } - - classUnderTest.delete(6) - classUnderTest.delete(7) - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_null_left = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_10 = treeW.getPrivateNode(classUnderTest)?.right?.value - val node_null_right10 = treeW.getPrivateNode(classUnderTest)?.right?.right?.value - val node_null_left10 = treeW.getPrivateNode(classUnderTest)?.right?.left?.value - - assertEquals(expected = root, actual = 8) - assertEquals(expected = node_10, actual = 10) - assertEquals(expected = node_null_left10, actual = null) - assertEquals(expected = node_null_right10, actual = null) - assertEquals(expected = node_null_left, actual = null) - } - - @Test - fun `test two child delete min element in right tree`() { - val num = mutableListOf(6, 8, 10, 7, 12, 9) - for (i in num) { - classUnderTest.insert(i) - } - - classUnderTest.delete(8) - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_null_left = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_9 = treeW.getPrivateNode(classUnderTest)?.right?.value - val node_7 = treeW.getPrivateNode(classUnderTest)?.right?.left?.value - val node_10 = treeW.getPrivateNode(classUnderTest)?.right?.right?.value - val node_12 = treeW.getPrivateNode(classUnderTest)?.right?.right?.right?.value - val node_null = treeW.getPrivateNode(classUnderTest)?.right?.right?.left?.value - - assertEquals(expected = root, actual = 6) - assertEquals(expected = node_9, actual = 9) - assertEquals(expected = node_null, actual = null) - assertEquals(expected = node_null_left, actual = null) - assertEquals(expected = node_7, actual = 7) - assertEquals(expected = node_10, actual = 10) - assertEquals(expected = node_12, actual = 12) - } - - @Test - fun `test two child delete min element in right tree for root`() { - val num = mutableListOf(8, 10, 7, 12, 9) - for (i in num) { - classUnderTest.insert(i) - } - - classUnderTest.delete(8) - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_7 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_10 = treeW.getPrivateNode(classUnderTest)?.right?.value - val node_12 = treeW.getPrivateNode(classUnderTest)?.right?.right?.value - val node_null = treeW.getPrivateNode(classUnderTest)?.right?.left?.value - - assertEquals(expected = root, actual = 9) - assertEquals(expected = node_null, actual = null) - assertEquals(expected = node_7, actual = 7) - assertEquals(expected = node_10, actual = 10) - assertEquals(expected = node_12, actual = 12) - } - - @Test - fun `test two child delete min element in right tree for rightmost element`() { - val num = mutableListOf(8, 10, 7, 12, 13, 14) - for (i in num) { - classUnderTest.insert(i) - } - - classUnderTest.delete(8) - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_7 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_12 = treeW.getPrivateNode(classUnderTest)?.right?.value - val node_13 = treeW.getPrivateNode(classUnderTest)?.right?.right?.value - val node_14 = treeW.getPrivateNode(classUnderTest)?.right?.right?.right?.value - val node_null = treeW.getPrivateNode(classUnderTest)?.right?.left?.value - - assertEquals(expected = root, actual = 10) - assertEquals(expected = node_null, actual = null) - assertEquals(expected = node_7, actual = 7) - assertEquals(expected = node_13, actual = 13) - assertEquals(expected = node_14, actual = 14) - assertEquals(expected = node_12, actual = 12) - } - - @Test - fun `test two child delete min element in right tree but in Tree`() { - val num = mutableListOf(8, 12, 15, 13, 10 , 11, 9) - for (i in num) { - classUnderTest.insert(i) - } - - classUnderTest.delete(12) - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_13 = treeW.getPrivateNode(classUnderTest)?.right?.value - val node_15 = treeW.getPrivateNode(classUnderTest)?.right?.right?.value - val node_10 = treeW.getPrivateNode(classUnderTest)?.right?.left?.value - val node_11 = treeW.getPrivateNode(classUnderTest)?.right?.left?.right?.value - val node_9 = treeW.getPrivateNode(classUnderTest)?.right?.left?.left?.value - val node_null = treeW.getPrivateNode(classUnderTest)?.right?.right?.left?.value - - assertEquals(expected = root, actual = 8) - assertEquals(expected = node_10, actual = 10) - assertEquals(expected = node_11, actual = 11) - assertEquals(expected = node_13, actual = 13) - assertEquals(expected = node_9, actual = 9) - assertEquals(expected = node_15, actual = 15) - assertEquals(expected = node_null, actual = null) - } - - @Test - fun `test two child delete min element in right tree for leftmost element`() { - val num = mutableListOf(8, 10, 7, 6) - for (i in num) { - classUnderTest.insert(i) - } - - classUnderTest.delete(8) - - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_7 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_6 = treeW.getPrivateNode(classUnderTest)?.left?.left?.value - val node_null = treeW.getPrivateNode(classUnderTest)?.right?.value - - assertEquals(expected = root, actual = 10) - assertEquals(expected = node_null, actual = null) - assertEquals(expected = node_7, actual = 7) - assertEquals(expected = node_6, actual = 6) - } - - } - - @Nested - @DisplayName("Test: AVL Struct") - inner class AVLStructTest { - val treeW = TreeStructWrapper, AVLStateContainer, AVLStruct>() - var classUnderTest = AVLStruct() - - @BeforeEach - fun reInitClassUnderTest() { - classUnderTest = AVLStruct() - } - } - - @Nested - @DisplayName("Test: Red-Black Tree Struct") - inner class RBStructTree { - val treeW = TreeStructWrapper, RBStateContainer,RBStruct>() - var classUnderTest = RBStruct() - - @BeforeEach - fun reInitClassUnderTest() { - classUnderTest = RBStruct() - } - } -} diff --git a/lib/src/test/kotlin/utils/AVLAnalyzer.kt b/lib/src/test/kotlin/utils/AVLAnalyzer.kt new file mode 100644 index 0000000..3d1a6fd --- /dev/null +++ b/lib/src/test/kotlin/utils/AVLAnalyzer.kt @@ -0,0 +1,40 @@ +package utils + +import treelib.avlTree.AVLNode +import kotlin.math.abs +import kotlin.math.max + +class AVLAnalyzer>(override val assertMethod: (input: String) -> Unit) : Analyzer>() { + private var heightL = 0 + private var heightR = 0 + private var heightMax = 0 + private var mainRight: AVLNode? = null + + override fun checkTree(root: AVLNode) { + mainRight = root.right + descent(root, 0) + heightR = heightMax + if (abs(heightR - heightL) > 1) { + wrappedAssertMethod("the invariant is not observed") + } + } + + private fun descent(node: AVLNode, height: Int) { + heightMax = max(heightMax, height) + val left = node.left + val right = node.right + + if (left != null) { + descent(left, height + 1) + } + + if (right == mainRight && heightR == 0) { + heightL = heightMax + heightMax = 0 + } + + if (right != null) { + descent(right, height + 1) + } + } +} diff --git a/lib/src/test/kotlin/utils/Analyzer.kt b/lib/src/test/kotlin/utils/Analyzer.kt new file mode 100644 index 0000000..6131d02 --- /dev/null +++ b/lib/src/test/kotlin/utils/Analyzer.kt @@ -0,0 +1,17 @@ +package utils + +import treelib.abstractTree.Node + +abstract class Analyzer< + Pack : Comparable, + NodeType : Node, + > { + + var message: String = "" + + protected abstract val assertMethod: (input: String) -> Unit + + protected fun wrappedAssertMethod(input: String) = assertMethod("$message$input") + + abstract fun checkTree(root: NodeType) +} \ No newline at end of file diff --git a/lib/src/test/kotlin/utils/BINAnalyzer.kt b/lib/src/test/kotlin/utils/BINAnalyzer.kt new file mode 100644 index 0000000..43dc5f5 --- /dev/null +++ b/lib/src/test/kotlin/utils/BINAnalyzer.kt @@ -0,0 +1,52 @@ +package utils + +import treelib.binTree.BINNode +import treelib.singleObjects.exceptions.BugInImplementException + +class BINAnalyzer>( + override val assertMethod: (input: String) -> Unit = { + throw BugInImplementException(it) + } +) : Analyzer>() { + + override fun checkTree(root: BINNode) { + checkInvariant(root) + } + + private fun checkInvariant(node: BINNode) { + if ((node.left == null) && (node.right == null)) return + + node.right?.let { + when { + it.value == node.value -> { + wrappedAssertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") + return@checkInvariant + } + + it.value < node.value -> { + wrappedAssertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") + return@checkInvariant + } + + else -> {} + } + } + + node.left?.let { + when { + it.value == node.value -> { + wrappedAssertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") + return@checkInvariant + } + + it.value > node.value -> { + wrappedAssertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") + return@checkInvariant + } + + else -> {} + } + } + } + +} \ No newline at end of file diff --git a/lib/src/test/kotlin/utils/RBAnalyzer.kt b/lib/src/test/kotlin/utils/RBAnalyzer.kt new file mode 100644 index 0000000..9b6b6db --- /dev/null +++ b/lib/src/test/kotlin/utils/RBAnalyzer.kt @@ -0,0 +1,85 @@ +package utils + +import treelib.rbTree.RBNode +import treelib.singleObjects.Markers +import treelib.singleObjects.exceptions.BugInImplementException + +class RBAnalyzer>( + override val assertMethod: (input: String) -> Unit = { + throw BugInImplementException(it) + } +) : Analyzer>() { + /** Magic number for error := -9999999 -> just an impossible value **/ + private val errorMagicNumber = -9999999 + + override fun checkTree(root: RBNode) { + if (root.color != Markers.BLACK) wrappedAssertMethod("The root isn't black!!!") + checkInvariant(root) + } + + private fun checkInvariant(node: RBNode): Int { + var leftBlackCount = 0 + var rightBlackCount = 0 + + if ((node.right == null) && (node.left == null)) { + if (node.color == Markers.RED) return 0 + else return 1 + } + node.right?.let { + when { + it.value == node.value -> { + wrappedAssertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") + return@checkInvariant errorMagicNumber + } + + it.value < node.value -> { + wrappedAssertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") + return@checkInvariant errorMagicNumber + } + + (it.color == Markers.RED) && (node.color == Markers.RED) -> { + wrappedAssertMethod("parent.color == RED == RightChild.color => [parent - ${node.value} RightChild - ${it.value}]") + return@checkInvariant errorMagicNumber + } + + else -> {} + } + } + + node.left?.let { + when { + it.value == node.value -> { + wrappedAssertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") + return@checkInvariant errorMagicNumber + } + + it.value > node.value -> { + wrappedAssertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") + return@checkInvariant errorMagicNumber + } + + (it.color == Markers.RED) && (node.color == Markers.RED) -> { + wrappedAssertMethod("parent.color == RED == LeftChild.color => [parent - ${node.value} LeftChild - ${it.value}]") + return@checkInvariant errorMagicNumber + } + + else -> {} + } + } + + leftBlackCount = node.left?.let { return@let checkInvariant(it) } ?: 0 + rightBlackCount = node.right?.let { return@let checkInvariant(it) } ?: 0 + + if (leftBlackCount < 0 || rightBlackCount < 0) return errorMagicNumber + + if (leftBlackCount != rightBlackCount) { + wrappedAssertMethod( + "Number of black nodes does not match in children: parent.value - ${node.value} =>[left - $leftBlackCount] != [right - $rightBlackCount]" + ) + return errorMagicNumber + } + + if (node.color == Markers.BLACK) return leftBlackCount + 1 + else return rightBlackCount + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/TreeStructWrapper.kt b/lib/src/test/kotlin/utils/TreeStructWrapper.kt similarity index 66% rename from lib/src/test/kotlin/treelib/TreeStructWrapper.kt rename to lib/src/test/kotlin/utils/TreeStructWrapper.kt index 88870ec..ba9fb10 100644 --- a/lib/src/test/kotlin/treelib/TreeStructWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeStructWrapper.kt @@ -1,10 +1,11 @@ -package treelib +package utils import treelib.abstractTree.Node import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct +import treelib.abstractTree.Vertex -class TreeStructWrapper, NodeType : Node, State: StateContainer, TStruct : TreeStruct> { +class TreeStructWrapper, NodeType : Node, VertexType : Vertex, State : StateContainer, TStruct : TreeStruct> { fun getPrivateNode(tree: TStruct, name: String = "root"): NodeType? { val field = tree.javaClass.getDeclaredField(name) @@ -14,7 +15,12 @@ class TreeStructWrapper, NodeType : Node, State: else root as? NodeType } - fun executePrivateMethod(tree: TStruct, name: String, parameterValues: Array?=null, vararg parameterTypes: Class<*>):Any? { + fun executePrivateMethod( + tree: TStruct, + name: String, + parameterValues: Array? = null, + vararg parameterTypes: Class<*> + ): Any? { val method = tree.javaClass.getDeclaredMethod(name, *parameterTypes) method.isAccessible = true return if (parameterValues != null) method.invoke(tree, *parameterValues) diff --git a/lib/src/test/kotlin/utils/TreeWrapper.kt b/lib/src/test/kotlin/utils/TreeWrapper.kt new file mode 100644 index 0000000..a0abe1c --- /dev/null +++ b/lib/src/test/kotlin/utils/TreeWrapper.kt @@ -0,0 +1,22 @@ +package utils + +import treelib.abstractTree.* +import treelib.singleObjects.Container + + +class TreeWrapper< + V : Comparable, + Value, + NodeType : Node, NodeType>, + VertexType : Vertex>, + State : StateContainer, NodeType>, + TStruct : TreeStruct, NodeType, State, VertexType>, + Wood : Tree> { + fun getPrivateNode(tree: Wood, name: String = "treeStruct"): TStruct { + val treeS = tree.javaClass.getDeclaredField(name) + treeS.isAccessible = true + val treeStruct = treeS.get(tree) + + return treeStruct as TStruct + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt new file mode 100644 index 0000000..17d52a2 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt @@ -0,0 +1,17 @@ +package utils.fuzzers + +import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer +import treelib.avlTree.AVLStruct +import treelib.avlTree.AVLVertex +import utils.AVLAnalyzer + +class AVLStructFuzzer>( + override val baseInput: Array, + override val assertMethod: (input: String) -> Unit +) : TreeStructFuzzer, AVLVertex, AVLAnalyzer, AVLStateContainer, AVLStruct>() { + + override fun createTreeStruct(): AVLStruct = AVLStruct() + + override fun createAnalyzer(): AVLAnalyzer = AVLAnalyzer(assertMethod) +} \ No newline at end of file diff --git a/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt new file mode 100644 index 0000000..720789c --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt @@ -0,0 +1,17 @@ +package utils.fuzzers + +import treelib.binTree.BINNode +import treelib.binTree.BINStateContainer +import treelib.binTree.BINStruct +import treelib.binTree.BINVertex +import utils.BINAnalyzer + +class BINStructFuzzer>( + override val baseInput: Array, + override val assertMethod: (input: String) -> Unit +) : TreeStructFuzzer, BINVertex, BINAnalyzer, BINStateContainer, BINStruct>() { + + override fun createTreeStruct(): BINStruct = BINStruct() + + override fun createAnalyzer(): BINAnalyzer = BINAnalyzer(assertMethod) +} \ No newline at end of file diff --git a/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt new file mode 100644 index 0000000..81d5278 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt @@ -0,0 +1,17 @@ +package utils.fuzzers + +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct +import treelib.rbTree.RBVertex +import utils.RBAnalyzer + +class RBStructFuzzer>( + override val baseInput: Array, + override val assertMethod: (input: String) -> Unit +) : TreeStructFuzzer, RBVertex, RBAnalyzer, RBStateContainer, RBStruct>() { + + override fun createTreeStruct(): RBStruct = RBStruct() + + override fun createAnalyzer(): RBAnalyzer = RBAnalyzer(assertMethod) +} \ No newline at end of file diff --git a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt new file mode 100644 index 0000000..69421d8 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt @@ -0,0 +1,126 @@ +package utils.fuzzers + +import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer +import treelib.abstractTree.TreeStruct +import treelib.abstractTree.Vertex +import treelib.singleObjects.exceptions.* +import utils.Analyzer +import utils.TreeStructWrapper +import java.io.File +import java.time.Instant +import kotlin.random.Random +import kotlin.random.nextInt + +abstract class TreeStructFuzzer< + Pack : Comparable, + NodeType : Node, + VertexType: Vertex, + AnalyzerType : Analyzer, + State : StateContainer, + TreeStructType : TreeStruct, + > { + abstract val baseInput: Array + + protected abstract val assertMethod: (input: String) -> Unit + + private var saveFlag: Boolean = false + + private var dirPath: String? = null + + protected val treeWrapper = TreeStructWrapper() + + protected abstract fun createTreeStruct(): TreeStructType + + protected abstract fun createAnalyzer(): AnalyzerType + + /** + * testNumbers - How many times fuzzer would be run + * inputSize - How many elements from baseInput would be used + * **/ + fun fuzzInvariantInsert(testNumbers: Int, inputSize: Int? = null) { + val dataSize: Int = checkCorrectInput(testNumbers, inputSize) + + for (iterations in 0 until testNumbers) { + val treeStruct = createTreeStruct() + val testSetIndexes = getInputSetIndexes(dataSize) + val testID = "${Instant.now().toEpochMilli() + iterations}-insert" + val analyzer = createAnalyzer() + + analyzer.message = "$testID: " + + if (saveFlag) saveCurrentTestSet(testID, testSetIndexes) + + try { + for (index in testSetIndexes) { + treeStruct.insert(baseInput[index]) + } + } catch (ex: Exception) { + exceptionsCatch(ex) + } + + val root = treeWrapper.getPrivateNode(treeStruct, "root") + // todo: probably won't work with junit.fail() + root?.let { analyzer.checkTree(it) } ?: assertMethod("The root was not created in the test $testID") + } + } + + fun fuzzInvariantDelete(testNumbers: Int, inputSize: Int? = null) { + val dataSize: Int = checkCorrectInput(testNumbers, inputSize) + TODO("DON'T IMPLEMENTED YET") + } + + fun fuzzInvariant(testNumbers: Int, inputSize: Int? = null) { + val dataSize: Int = checkCorrectInput(testNumbers, inputSize) + TODO("DON'T IMPLEMENTED YET") + } + + private fun checkCorrectInput(testNumbers: Int, inputSize: Int?): Int { + val dataSize: Int + + if (inputSize == null) dataSize = baseInput.size + else dataSize = inputSize + + if (dataSize > baseInput.size) throw BugInImplementException("inputSize > size of the baseInput") + return dataSize + } + + private fun getInputSetIndexes(inputSize: Int): List { + return generateSequence { Random.nextInt(baseInput.indices) }.distinct().take(inputSize).toList() + } + + private fun exceptionsCatch(ex: Exception) { + when (ex) { + is BugInImplementException, + is IllegalBaseNodeException, + is IllegalNodeStateException, + is ImpossibleCaseException, + is MultithreadingException, + is NonExistentValueException, + -> {/*TODO: Implement */ + } + + else -> throw ex + } + } + + fun saveNextTestSets(dirName: String) { + dirPath = dirName + val file = File("./$dirPath") + file.mkdir() + if (file.isDirectory) saveFlag = true + } + + fun dontSaveNextTestSets() { + saveFlag = false + } + + private fun saveCurrentTestSet(testId: String, testSet: List) { + val file = File("./$dirPath/$testId.txt") + file.createNewFile() + for (index in testSet) { + file.appendText("${baseInput[index]} \n") + } + } + +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 21643d4..26b0762 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,5 +7,5 @@ * in the user manual at https://docs.gradle.org/8.0/userguide/multi_project_builds.html */ -rootProject.name = "TreeLib" +rootProject.name = "treelib" include("lib")