From 7a8fd371926d684e419e6e1a67011976a2f0750f Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 10:49:11 +0000 Subject: [PATCH 001/212] Setting up GitHub Classroom Feedback From eb1adf0c6bb8d3c73b95f5bd01dd024e92d26b33 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Tue, 21 Mar 2023 23:27:15 +0300 Subject: [PATCH 002/212] Added architecture --- AVLtree/AVLBalancer.kt | 9 ++++++ AVLtree/AVLNode.kt | 10 ++++++ AVLtree/AVLStruct.kt | 8 +++++ AVLtree/AVLTree.kt | 10 ++++++ AbstractTree/Node.kt | 10 ++++++ AbstractTree/Tree.kt | 35 +++++++++++++++++++++ AbstractTree/TreeStruct.kt | 22 +++++++++++++ AbstractTree/Weighted/Balancer.kt | 11 +++++++ AbstractTree/Weighted/BalancerNoParent.kt | 13 ++++++++ AbstractTree/Weighted/BalancerParent.kt | 13 ++++++++ AbstractTree/Weighted/WeightedTreeStruct.kt | 28 +++++++++++++++++ BINtree/BINNode.kt | 9 ++++++ BINtree/BINStruct.kt | 15 +++++++++ BINtree/BINTree.kt | 8 +++++ RBtree/RBBalancer.kt | 9 ++++++ RBtree/RBNode.kt | 12 +++++++ RBtree/RBStruct.kt | 9 ++++++ RBtree/RBTree.kt | 9 ++++++ Single_Objects/Container.kt | 24 ++++++++++++++ Single_Objects/Markers.kt | 5 +++ 20 files changed, 269 insertions(+) create mode 100644 AVLtree/AVLBalancer.kt create mode 100644 AVLtree/AVLNode.kt create mode 100644 AVLtree/AVLStruct.kt create mode 100644 AVLtree/AVLTree.kt create mode 100644 AbstractTree/Node.kt create mode 100644 AbstractTree/Tree.kt create mode 100644 AbstractTree/TreeStruct.kt create mode 100644 AbstractTree/Weighted/Balancer.kt create mode 100644 AbstractTree/Weighted/BalancerNoParent.kt create mode 100644 AbstractTree/Weighted/BalancerParent.kt create mode 100644 AbstractTree/Weighted/WeightedTreeStruct.kt create mode 100644 BINtree/BINNode.kt create mode 100644 BINtree/BINStruct.kt create mode 100644 BINtree/BINTree.kt create mode 100644 RBtree/RBBalancer.kt create mode 100644 RBtree/RBNode.kt create mode 100644 RBtree/RBStruct.kt create mode 100644 RBtree/RBTree.kt create mode 100644 Single_Objects/Container.kt create mode 100644 Single_Objects/Markers.kt diff --git a/AVLtree/AVLBalancer.kt b/AVLtree/AVLBalancer.kt new file mode 100644 index 0000000..396a756 --- /dev/null +++ b/AVLtree/AVLBalancer.kt @@ -0,0 +1,9 @@ +package AVLtree + +import AbstractTree.Weighted.BalancerNoParent + +class AVLBalancer>: BalancerNoParent>() { + override fun balance(node: AVLNode) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/AVLtree/AVLNode.kt b/AVLtree/AVLNode.kt new file mode 100644 index 0000000..e86d3ec --- /dev/null +++ b/AVLtree/AVLNode.kt @@ -0,0 +1,10 @@ +package AVLtree + +import AbstractTree.Node + +class AVLNode>( + override var value: Pack?, + override var left: AVLNode, + override var right: AVLNode, + var height:Int, +) : Node> \ No newline at end of file diff --git a/AVLtree/AVLStruct.kt b/AVLtree/AVLStruct.kt new file mode 100644 index 0000000..becd333 --- /dev/null +++ b/AVLtree/AVLStruct.kt @@ -0,0 +1,8 @@ +package AVLtree + +import AbstractTree.Weighted.WeightedTreeStruct + +class AVLStruct> : WeightedTreeStruct, AVLBalancer>() { + override var root: AVLNode? = null + override val balancer = AVLBalancer() +} \ No newline at end of file diff --git a/AVLtree/AVLTree.kt b/AVLtree/AVLTree.kt new file mode 100644 index 0000000..8b20e40 --- /dev/null +++ b/AVLtree/AVLTree.kt @@ -0,0 +1,10 @@ +package AVLtree + +import AbstractTree.Tree + +import Single_Objects.Container + +class AVLTree, Value> : + Tree>>() { + override val treeStruct: AVLStruct> = AVLStruct() +} \ No newline at end of file diff --git a/AbstractTree/Node.kt b/AbstractTree/Node.kt new file mode 100644 index 0000000..ed50e35 --- /dev/null +++ b/AbstractTree/Node.kt @@ -0,0 +1,10 @@ +package AbstractTree + +interface Node, SubNode : Node>{ + var value: Pack? + var left: SubNode? + var right: SubNode? +} +/* +* TODO как реализовать leaf, может придется использовать null-able type для полей +* */ diff --git a/AbstractTree/Tree.kt b/AbstractTree/Tree.kt new file mode 100644 index 0000000..d947c6b --- /dev/null +++ b/AbstractTree/Tree.kt @@ -0,0 +1,35 @@ +package AbstractTree + +import Single_Objects.Container + +abstract class Tree, Value, NodeType: Node, NodeType>> { + protected abstract val treeStruct: TreeStruct, NodeType> + + fun putItem(item: Pair){ +// TODO("Not yet implemented") + } + + fun putItems(vararg items: Pair) { + TODO("Not yet implemented") + } + + fun deleteItem(key: Key){ + TODO("Not yet implemented") + } + + fun getItem(key: Key) { + TODO("Not yet implemented") + } + + fun inOrder() { + TODO("Not yet implemented") + } + + fun preOrder() { + TODO("Not yet implemented") + } + + fun postOrder() { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/AbstractTree/TreeStruct.kt b/AbstractTree/TreeStruct.kt new file mode 100644 index 0000000..a1d3dd0 --- /dev/null +++ b/AbstractTree/TreeStruct.kt @@ -0,0 +1,22 @@ +package AbstractTree + +abstract class TreeStruct, NodeType : Node> { + protected abstract var root: NodeType? //TODO root - not a null + + fun find(obj: Pack): Pack?{ + TODO("Сделать реализацию поиска, дергать реализацию из локализированного класса") + } + + fun inOrder(){ + TODO("inOrder - implementation") + } + fun postOrder(){ + TODO("postOrder - implementation") + } + fun preOrder(){ + TODO("preOrder - implementation") + } + + abstract fun insert(item: Pack) + abstract fun delete(item: Pack) +} diff --git a/AbstractTree/Weighted/Balancer.kt b/AbstractTree/Weighted/Balancer.kt new file mode 100644 index 0000000..7cf2a6d --- /dev/null +++ b/AbstractTree/Weighted/Balancer.kt @@ -0,0 +1,11 @@ +package AbstractTree.Weighted + +import AbstractTree.Node + +interface Balancer, NodeType : Node> { + fun rightRotate(currentNode: NodeType): NodeType + + fun leftRotate(currentNode: NodeType): NodeType + + fun balance(node: NodeType) +} \ No newline at end of file diff --git a/AbstractTree/Weighted/BalancerNoParent.kt b/AbstractTree/Weighted/BalancerNoParent.kt new file mode 100644 index 0000000..31506f1 --- /dev/null +++ b/AbstractTree/Weighted/BalancerNoParent.kt @@ -0,0 +1,13 @@ +package AbstractTree.Weighted + +import AbstractTree.Node + +abstract class BalancerNoParent, NodeType : Node>: Balancer { + override fun rightRotate(currentNode: NodeType): NodeType { + TODO("Not yet implemented") + } + + override fun leftRotate(currentNode: NodeType): NodeType { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/AbstractTree/Weighted/BalancerParent.kt b/AbstractTree/Weighted/BalancerParent.kt new file mode 100644 index 0000000..d33d53c --- /dev/null +++ b/AbstractTree/Weighted/BalancerParent.kt @@ -0,0 +1,13 @@ +package AbstractTree.Weighted + +import AbstractTree.Node + +abstract class BalancerParent, NodeType : Node>: Balancer { + override fun rightRotate(currentNode: NodeType): NodeType { + TODO("Not yet implemented") + } + + override fun leftRotate(currentNode: NodeType): NodeType { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/AbstractTree/Weighted/WeightedTreeStruct.kt b/AbstractTree/Weighted/WeightedTreeStruct.kt new file mode 100644 index 0000000..bdea3d4 --- /dev/null +++ b/AbstractTree/Weighted/WeightedTreeStruct.kt @@ -0,0 +1,28 @@ +package AbstractTree.Weighted + +import AbstractTree.Node +import AbstractTree.TreeStruct + +abstract class WeightedTreeStruct, NodeType : Node, BalancerType: Balancer>: + TreeStruct() { + + protected abstract val balancer: BalancerType + + private fun insertItem(item: Pack) { + TODO("Not yet implemented") + } + + private fun deleteItem(item: Pack) { + TODO("Not yet implemented") + } + + override fun insert(item: Pack) { + insertItem(item) + if (root != null) balancer.balance(root!!) + } + + override fun delete(item: Pack) { + deleteItem(item) + if (root != null) balancer.balance(root!!) + } +} \ No newline at end of file diff --git a/BINtree/BINNode.kt b/BINtree/BINNode.kt new file mode 100644 index 0000000..fa347f7 --- /dev/null +++ b/BINtree/BINNode.kt @@ -0,0 +1,9 @@ +package BINtree + +import AbstractTree.Node + +class BINNode>( + override var value: Pack?, + override var left: BINNode, + override var right: BINNode +) : Node> \ No newline at end of file diff --git a/BINtree/BINStruct.kt b/BINtree/BINStruct.kt new file mode 100644 index 0000000..bb20e6c --- /dev/null +++ b/BINtree/BINStruct.kt @@ -0,0 +1,15 @@ +package BINtree + +import AbstractTree.TreeStruct + +class BINStruct> : TreeStruct>() { + override var root: BINNode? = null + + override fun delete(item: Pack) { + TODO("Not yet implemented") + } + + override fun insert(item: Pack) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/BINtree/BINTree.kt b/BINtree/BINTree.kt new file mode 100644 index 0000000..ad29aa0 --- /dev/null +++ b/BINtree/BINTree.kt @@ -0,0 +1,8 @@ +package BINtree + +import AbstractTree.Tree +import Single_Objects.Container + +class BINTree, Value> : Tree>>() { + override val treeStruct: BINStruct> = BINStruct() +} \ No newline at end of file diff --git a/RBtree/RBBalancer.kt b/RBtree/RBBalancer.kt new file mode 100644 index 0000000..15329d2 --- /dev/null +++ b/RBtree/RBBalancer.kt @@ -0,0 +1,9 @@ +package RBtree + +import AbstractTree.Weighted.BalancerParent + +class RBBalancer>: BalancerParent>() { + override fun balance(node: RBNode) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/RBtree/RBNode.kt b/RBtree/RBNode.kt new file mode 100644 index 0000000..8acc7e0 --- /dev/null +++ b/RBtree/RBNode.kt @@ -0,0 +1,12 @@ +package RBtree + +import AbstractTree.Node +import Single_Objects.Markers + +class RBNode>( + override var value: Pack?, + override var left: RBNode, + override var right: RBNode, + var parent: RBNode, + var color: Markers, +) : Node> \ No newline at end of file diff --git a/RBtree/RBStruct.kt b/RBtree/RBStruct.kt new file mode 100644 index 0000000..975e2b1 --- /dev/null +++ b/RBtree/RBStruct.kt @@ -0,0 +1,9 @@ +package RBtree + +import AbstractTree.Weighted.WeightedTreeStruct + +class RBStruct > : WeightedTreeStruct, RBBalancer>(){ + override var root: RBNode? = null + override val balancer = RBBalancer() + +} \ No newline at end of file diff --git a/RBtree/RBTree.kt b/RBtree/RBTree.kt new file mode 100644 index 0000000..c0d179c --- /dev/null +++ b/RBtree/RBTree.kt @@ -0,0 +1,9 @@ +package RBtree + +import AbstractTree.Tree + +import Single_Objects.Container + +class RBTree, Value> : Tree>>() { + override val treeStruct: RBStruct> = RBStruct() +} \ No newline at end of file diff --git a/Single_Objects/Container.kt b/Single_Objects/Container.kt new file mode 100644 index 0000000..2ca7fe5 --- /dev/null +++ b/Single_Objects/Container.kt @@ -0,0 +1,24 @@ +package Single_Objects + +class Container, V>(private val pair: Pair) : Comparable> { + + override fun equals(other: Any?): Boolean { + if (this === other) + return true + + other as Container<*, *> + if (this.pair.first == other.pair.first) { + return true + } + + return false + } + + override fun hashCode(): Int { + return pair.hashCode() + } + + override fun compareTo(other: Container): Int { + return compareValuesBy(this, other) { it.pair.first } + } +} \ No newline at end of file diff --git a/Single_Objects/Markers.kt b/Single_Objects/Markers.kt new file mode 100644 index 0000000..4bd9c2e --- /dev/null +++ b/Single_Objects/Markers.kt @@ -0,0 +1,5 @@ +package Single_Objects + +enum class Markers { + RED, BLACK +} \ No newline at end of file From cace06025bb4adaef48e1c99a1fbc4a40bdcbf96 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 22 Mar 2023 01:17:14 +0300 Subject: [PATCH 003/212] Gradle add test --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61608 bytes gradle/wrapper/gradle-wrapper.properties | 6 +++ lib/build.gradle.kts | 39 ++++++++++++++++++ .../main/kotlin/Tree/AVLtree}/AVLBalancer.kt | 0 .../src/main/kotlin/Tree/AVLtree}/AVLNode.kt | 0 .../main/kotlin/Tree/AVLtree}/AVLStruct.kt | 0 .../src/main/kotlin/Tree/AVLtree}/AVLTree.kt | 0 .../main/kotlin/Tree/AbstractTree}/Node.kt | 0 .../main/kotlin/Tree/AbstractTree}/Tree.kt | 0 .../kotlin/Tree/AbstractTree}/TreeStruct.kt | 0 .../Tree/AbstractTree}/Weighted/Balancer.kt | 2 +- .../Weighted/BalancerNoParent.kt | 0 .../AbstractTree}/Weighted/BalancerParent.kt | 0 .../Weighted/WeightedTreeStruct.kt | 8 +++- .../src/main/kotlin/Tree/BINtree}/BINNode.kt | 0 .../main/kotlin/Tree/BINtree}/BINStruct.kt | 0 .../src/main/kotlin/Tree/BINtree}/BINTree.kt | 0 .../main/kotlin/Tree/RBtree}/RBBalancer.kt | 0 .../src/main/kotlin/Tree/RBtree}/RBNode.kt | 0 .../src/main/kotlin/Tree/RBtree}/RBStruct.kt | 0 .../src/main/kotlin/Tree/RBtree}/RBTree.kt | 0 .../kotlin/Tree/Single_Objects}/Container.kt | 0 .../kotlin/Tree/Single_Objects}/Markers.kt | 0 settings.gradle.kts | 11 +++++ 24 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 lib/build.gradle.kts rename {AVLtree => lib/src/main/kotlin/Tree/AVLtree}/AVLBalancer.kt (100%) rename {AVLtree => lib/src/main/kotlin/Tree/AVLtree}/AVLNode.kt (100%) rename {AVLtree => lib/src/main/kotlin/Tree/AVLtree}/AVLStruct.kt (100%) rename {AVLtree => lib/src/main/kotlin/Tree/AVLtree}/AVLTree.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Node.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Tree.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/TreeStruct.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Weighted/Balancer.kt (85%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Weighted/BalancerNoParent.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Weighted/BalancerParent.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Weighted/WeightedTreeStruct.kt (78%) rename {BINtree => lib/src/main/kotlin/Tree/BINtree}/BINNode.kt (100%) rename {BINtree => lib/src/main/kotlin/Tree/BINtree}/BINStruct.kt (100%) rename {BINtree => lib/src/main/kotlin/Tree/BINtree}/BINTree.kt (100%) rename {RBtree => lib/src/main/kotlin/Tree/RBtree}/RBBalancer.kt (100%) rename {RBtree => lib/src/main/kotlin/Tree/RBtree}/RBNode.kt (100%) rename {RBtree => lib/src/main/kotlin/Tree/RBtree}/RBStruct.kt (100%) rename {RBtree => lib/src/main/kotlin/Tree/RBtree}/RBTree.kt (100%) rename {Single_Objects => lib/src/main/kotlin/Tree/Single_Objects}/Container.kt (100%) rename {Single_Objects => lib/src/main/kotlin/Tree/Single_Objects}/Markers.kt (100%) create mode 100644 settings.gradle.kts diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..ccebba7710deaf9f98673a68957ea02138b60d0a GIT binary patch literal 61608 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..42defcc --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts new file mode 100644 index 0000000..b0aa745 --- /dev/null +++ b/lib/build.gradle.kts @@ -0,0 +1,39 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Kotlin library project to get you started. + * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle + * User Manual available at https://docs.gradle.org/8.0/userguide/building_java_projects.html + */ + +plugins { + // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. + id("org.jetbrains.kotlin.jvm") version "1.8.10" + + // Apply the java-library plugin for API and implementation separation. + `java-library` +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use the Kotlin JUnit 5 integration. + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + + // Use the JUnit 5 integration. + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") + + // This dependency is exported to consumers, that is to say found on their compile classpath. + api("org.apache.commons:commons-math3:3.6.1") + + // This dependency is used internally, and not exposed to consumers on their own compile classpath. + implementation("com.google.guava:guava:31.1-jre") +} + +tasks.named("test") { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt similarity index 100% rename from AVLtree/AVLBalancer.kt rename to lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt diff --git a/AVLtree/AVLNode.kt b/lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt similarity index 100% rename from AVLtree/AVLNode.kt rename to lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt diff --git a/AVLtree/AVLStruct.kt b/lib/src/main/kotlin/Tree/AVLtree/AVLStruct.kt similarity index 100% rename from AVLtree/AVLStruct.kt rename to lib/src/main/kotlin/Tree/AVLtree/AVLStruct.kt diff --git a/AVLtree/AVLTree.kt b/lib/src/main/kotlin/Tree/AVLtree/AVLTree.kt similarity index 100% rename from AVLtree/AVLTree.kt rename to lib/src/main/kotlin/Tree/AVLtree/AVLTree.kt diff --git a/AbstractTree/Node.kt b/lib/src/main/kotlin/Tree/AbstractTree/Node.kt similarity index 100% rename from AbstractTree/Node.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Node.kt diff --git a/AbstractTree/Tree.kt b/lib/src/main/kotlin/Tree/AbstractTree/Tree.kt similarity index 100% rename from AbstractTree/Tree.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Tree.kt diff --git a/AbstractTree/TreeStruct.kt b/lib/src/main/kotlin/Tree/AbstractTree/TreeStruct.kt similarity index 100% rename from AbstractTree/TreeStruct.kt rename to lib/src/main/kotlin/Tree/AbstractTree/TreeStruct.kt diff --git a/AbstractTree/Weighted/Balancer.kt b/lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt similarity index 85% rename from AbstractTree/Weighted/Balancer.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt index 7cf2a6d..2060826 100644 --- a/AbstractTree/Weighted/Balancer.kt +++ b/lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt @@ -7,5 +7,5 @@ interface Balancer, NodeType : Node> { fun leftRotate(currentNode: NodeType): NodeType - fun balance(node: NodeType) + fun balance(node: NodeType): NodeType } \ No newline at end of file diff --git a/AbstractTree/Weighted/BalancerNoParent.kt b/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerNoParent.kt similarity index 100% rename from AbstractTree/Weighted/BalancerNoParent.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerNoParent.kt diff --git a/AbstractTree/Weighted/BalancerParent.kt b/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerParent.kt similarity index 100% rename from AbstractTree/Weighted/BalancerParent.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerParent.kt diff --git a/AbstractTree/Weighted/WeightedTreeStruct.kt b/lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt similarity index 78% rename from AbstractTree/Weighted/WeightedTreeStruct.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt index bdea3d4..c256e22 100644 --- a/AbstractTree/Weighted/WeightedTreeStruct.kt +++ b/lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt @@ -18,11 +18,15 @@ abstract class WeightedTreeStruct, NodeType : Node Date: Wed, 22 Mar 2023 01:39:51 +0300 Subject: [PATCH 004/212] root problem fixed --- AbstractTree/Weighted/Balancer.kt | 2 +- AbstractTree/Weighted/WeightedTreeStruct.kt | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/AbstractTree/Weighted/Balancer.kt b/AbstractTree/Weighted/Balancer.kt index 7cf2a6d..2060826 100644 --- a/AbstractTree/Weighted/Balancer.kt +++ b/AbstractTree/Weighted/Balancer.kt @@ -7,5 +7,5 @@ interface Balancer, NodeType : Node> { fun leftRotate(currentNode: NodeType): NodeType - fun balance(node: NodeType) + fun balance(node: NodeType): NodeType } \ No newline at end of file diff --git a/AbstractTree/Weighted/WeightedTreeStruct.kt b/AbstractTree/Weighted/WeightedTreeStruct.kt index bdea3d4..c256e22 100644 --- a/AbstractTree/Weighted/WeightedTreeStruct.kt +++ b/AbstractTree/Weighted/WeightedTreeStruct.kt @@ -18,11 +18,15 @@ abstract class WeightedTreeStruct, NodeType : Node Date: Wed, 22 Mar 2023 01:52:12 +0300 Subject: [PATCH 005/212] fixed problems with nullable fields in Nodes --- lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt | 2 +- lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt | 4 ++-- lib/src/main/kotlin/Tree/BINtree/BINNode.kt | 4 ++-- lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt | 3 ++- lib/src/main/kotlin/Tree/RBtree/RBNode.kt | 4 ++-- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt index 396a756..b67b135 100644 --- a/lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt +++ b/lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt @@ -3,7 +3,7 @@ package AVLtree import AbstractTree.Weighted.BalancerNoParent class AVLBalancer>: BalancerNoParent>() { - override fun balance(node: AVLNode) { + override fun balance(node: AVLNode): AVLNode { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt b/lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt index e86d3ec..e2cb250 100644 --- a/lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt +++ b/lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt @@ -4,7 +4,7 @@ import AbstractTree.Node class AVLNode>( override var value: Pack?, - override var left: AVLNode, - override var right: AVLNode, + override var left: AVLNode?, + override var right: AVLNode?, var height:Int, ) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/Tree/BINtree/BINNode.kt b/lib/src/main/kotlin/Tree/BINtree/BINNode.kt index fa347f7..97ffb32 100644 --- a/lib/src/main/kotlin/Tree/BINtree/BINNode.kt +++ b/lib/src/main/kotlin/Tree/BINtree/BINNode.kt @@ -4,6 +4,6 @@ import AbstractTree.Node class BINNode>( override var value: Pack?, - override var left: BINNode, - override var right: BINNode + override var left: BINNode?, + override var right: BINNode? ) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt b/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt index 15329d2..4d94b63 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt @@ -1,9 +1,10 @@ package RBtree +import AVLtree.AVLNode import AbstractTree.Weighted.BalancerParent class RBBalancer>: BalancerParent>() { - override fun balance(node: RBNode) { + override fun balance(node: RBNode): RBNode { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/lib/src/main/kotlin/Tree/RBtree/RBNode.kt b/lib/src/main/kotlin/Tree/RBtree/RBNode.kt index 8acc7e0..f1a2565 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBNode.kt +++ b/lib/src/main/kotlin/Tree/RBtree/RBNode.kt @@ -5,8 +5,8 @@ import Single_Objects.Markers class RBNode>( override var value: Pack?, - override var left: RBNode, - override var right: RBNode, + override var left: RBNode?, + override var right: RBNode?, var parent: RBNode, var color: Markers, ) : Node> \ No newline at end of file From 20260651bdc28a5f497edb2678c6ae769df8ac53 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 22 Mar 2023 01:55:48 +0300 Subject: [PATCH 006/212] fixed problems with nullable fields in Nodes --- AVLtree/AVLBalancer.kt | 2 +- AVLtree/AVLNode.kt | 4 ++-- BINtree/BINNode.kt | 4 ++-- RBtree/RBBalancer.kt | 2 +- RBtree/RBNode.kt | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/AVLtree/AVLBalancer.kt b/AVLtree/AVLBalancer.kt index 396a756..b67b135 100644 --- a/AVLtree/AVLBalancer.kt +++ b/AVLtree/AVLBalancer.kt @@ -3,7 +3,7 @@ package AVLtree import AbstractTree.Weighted.BalancerNoParent class AVLBalancer>: BalancerNoParent>() { - override fun balance(node: AVLNode) { + override fun balance(node: AVLNode): AVLNode { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/AVLtree/AVLNode.kt b/AVLtree/AVLNode.kt index e86d3ec..e2cb250 100644 --- a/AVLtree/AVLNode.kt +++ b/AVLtree/AVLNode.kt @@ -4,7 +4,7 @@ import AbstractTree.Node class AVLNode>( override var value: Pack?, - override var left: AVLNode, - override var right: AVLNode, + override var left: AVLNode?, + override var right: AVLNode?, var height:Int, ) : Node> \ No newline at end of file diff --git a/BINtree/BINNode.kt b/BINtree/BINNode.kt index fa347f7..97ffb32 100644 --- a/BINtree/BINNode.kt +++ b/BINtree/BINNode.kt @@ -4,6 +4,6 @@ import AbstractTree.Node class BINNode>( override var value: Pack?, - override var left: BINNode, - override var right: BINNode + override var left: BINNode?, + override var right: BINNode? ) : Node> \ No newline at end of file diff --git a/RBtree/RBBalancer.kt b/RBtree/RBBalancer.kt index 15329d2..b6fe7e4 100644 --- a/RBtree/RBBalancer.kt +++ b/RBtree/RBBalancer.kt @@ -3,7 +3,7 @@ package RBtree import AbstractTree.Weighted.BalancerParent class RBBalancer>: BalancerParent>() { - override fun balance(node: RBNode) { + override fun balance(node: RBNode): RBNode{ TODO("Not yet implemented") } } \ No newline at end of file diff --git a/RBtree/RBNode.kt b/RBtree/RBNode.kt index 8acc7e0..1cda844 100644 --- a/RBtree/RBNode.kt +++ b/RBtree/RBNode.kt @@ -5,8 +5,8 @@ import Single_Objects.Markers class RBNode>( override var value: Pack?, - override var left: RBNode, - override var right: RBNode, - var parent: RBNode, + override var left: RBNode?, + override var right: RBNode?, + var parent: RBNode?, var color: Markers, ) : Node> \ No newline at end of file From f29b1332ad5868c5af52f62a2a79be1b6391994e Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 22 Mar 2023 15:25:23 +0300 Subject: [PATCH 007/212] The project is configured as a library. Added gradle wrapper files. --- gradlew | 243 ++++++++++++++++++ gradlew.bat | 92 +++++++ lib/build.gradle.kts | 23 +- .../{Tree => TreeLib}/AVLtree/AVLBalancer.kt | 4 +- .../{Tree => TreeLib}/AVLtree/AVLNode.kt | 4 +- .../{Tree => TreeLib}/AVLtree/AVLStruct.kt | 4 +- .../{Tree => TreeLib}/AVLtree/AVLTree.kt | 6 +- .../{Tree => TreeLib}/AbstractTree/Node.kt | 2 +- .../{Tree => TreeLib}/AbstractTree/Tree.kt | 4 +- .../AbstractTree/TreeStruct.kt | 2 +- .../AbstractTree/Weighted/Balancer.kt | 4 +- .../AbstractTree/Weighted/BalancerNoParent.kt | 4 +- .../AbstractTree/Weighted/BalancerParent.kt | 4 +- .../Weighted/WeightedTreeStruct.kt | 6 +- .../{Tree => TreeLib}/BINtree/BINNode.kt | 4 +- .../{Tree => TreeLib}/BINtree/BINStruct.kt | 4 +- .../{Tree => TreeLib}/BINtree/BINTree.kt | 6 +- .../{Tree => TreeLib}/RBtree/RBBalancer.kt | 5 +- .../kotlin/{Tree => TreeLib}/RBtree/RBNode.kt | 8 +- .../{Tree => TreeLib}/RBtree/RBStruct.kt | 4 +- .../kotlin/{Tree => TreeLib}/RBtree/RBTree.kt | 6 +- .../Single_Objects/Container.kt | 2 +- .../Single_Objects/Markers.kt | 2 +- settings.gradle.kts | 2 +- 24 files changed, 398 insertions(+), 47 deletions(-) create mode 100755 gradlew create mode 100644 gradlew.bat rename lib/src/main/kotlin/{Tree => TreeLib}/AVLtree/AVLBalancer.kt (71%) rename lib/src/main/kotlin/{Tree => TreeLib}/AVLtree/AVLNode.kt (77%) rename lib/src/main/kotlin/{Tree => TreeLib}/AVLtree/AVLStruct.kt (71%) rename lib/src/main/kotlin/{Tree => TreeLib}/AVLtree/AVLTree.kt (65%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Node.kt (90%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Tree.kt (91%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/TreeStruct.kt (95%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Weighted/Balancer.kt (76%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Weighted/BalancerNoParent.kt (82%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Weighted/BalancerParent.kt (82%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Weighted/WeightedTreeStruct.kt (86%) rename lib/src/main/kotlin/{Tree => TreeLib}/BINtree/BINNode.kt (75%) rename lib/src/main/kotlin/{Tree => TreeLib}/BINtree/BINStruct.kt (82%) rename lib/src/main/kotlin/{Tree => TreeLib}/BINtree/BINTree.kt (64%) rename lib/src/main/kotlin/{Tree => TreeLib}/RBtree/RBBalancer.kt (69%) rename lib/src/main/kotlin/{Tree => TreeLib}/RBtree/RBNode.kt (61%) rename lib/src/main/kotlin/{Tree => TreeLib}/RBtree/RBStruct.kt (71%) rename lib/src/main/kotlin/{Tree => TreeLib}/RBtree/RBTree.kt (64%) rename lib/src/main/kotlin/{Tree => TreeLib}/Single_Objects/Container.kt (94%) rename lib/src/main/kotlin/{Tree => TreeLib}/Single_Objects/Markers.kt (55%) diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..a9bd892 --- /dev/null +++ b/gradlew @@ -0,0 +1,243 @@ +#!/bin/sh +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index b0aa745..9513ece 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -8,10 +8,16 @@ plugins { // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. - id("org.jetbrains.kotlin.jvm") version "1.8.10" + kotlin("jvm") version "1.8.10" // Apply the java-library plugin for API and implementation separation. `java-library` + `maven-publish` +} +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(8)) + } } repositories { @@ -20,10 +26,10 @@ repositories { } dependencies { - // Use the Kotlin JUnit 5 integration. + // Use the Kotlin JUnit 5 integration. (TESTS support tools) testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") - // Use the JUnit 5 integration. + // Use the JUnit 5 integration. (TESTS support tools) testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") // This dependency is exported to consumers, that is to say found on their compile classpath. @@ -37,3 +43,14 @@ tasks.named("test") { // Use JUnit Platform for unit tests. useJUnitPlatform() } + +publishing { + publications { + create("maven") { + groupId = "Tree" + artifactId = "lib" + version = "1.1" + from(components["java"]) + } + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt similarity index 71% rename from lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt rename to lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt index b67b135..1078742 100644 --- a/lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt @@ -1,6 +1,6 @@ -package AVLtree +package TreeLib.AVLtree -import AbstractTree.Weighted.BalancerNoParent +import TreeLib.AbstractTree.Weighted.BalancerNoParent class AVLBalancer>: BalancerNoParent>() { override fun balance(node: AVLNode): AVLNode { diff --git a/lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt similarity index 77% rename from lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt rename to lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt index e2cb250..d92d1d3 100644 --- a/lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt @@ -1,6 +1,6 @@ -package AVLtree +package TreeLib.AVLtree -import AbstractTree.Node +import TreeLib.AbstractTree.Node class AVLNode>( override var value: Pack?, diff --git a/lib/src/main/kotlin/Tree/AVLtree/AVLStruct.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt similarity index 71% rename from lib/src/main/kotlin/Tree/AVLtree/AVLStruct.kt rename to lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt index becd333..41093b5 100644 --- a/lib/src/main/kotlin/Tree/AVLtree/AVLStruct.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt @@ -1,6 +1,6 @@ -package AVLtree +package TreeLib.AVLtree -import AbstractTree.Weighted.WeightedTreeStruct +import TreeLib.AbstractTree.Weighted.WeightedTreeStruct class AVLStruct> : WeightedTreeStruct, AVLBalancer>() { override var root: AVLNode? = null diff --git a/lib/src/main/kotlin/Tree/AVLtree/AVLTree.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt similarity index 65% rename from lib/src/main/kotlin/Tree/AVLtree/AVLTree.kt rename to lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt index 8b20e40..0c8f284 100644 --- a/lib/src/main/kotlin/Tree/AVLtree/AVLTree.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt @@ -1,8 +1,8 @@ -package AVLtree +package TreeLib.AVLtree -import AbstractTree.Tree +import TreeLib.AbstractTree.Tree -import Single_Objects.Container +import TreeLib.Single_Objects.Container class AVLTree, Value> : Tree>>() { diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Node.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt similarity index 90% rename from lib/src/main/kotlin/Tree/AbstractTree/Node.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt index ed50e35..fbb7462 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Node.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt @@ -1,4 +1,4 @@ -package AbstractTree +package TreeLib.AbstractTree interface Node, SubNode : Node>{ var value: Pack? diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Tree.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt similarity index 91% rename from lib/src/main/kotlin/Tree/AbstractTree/Tree.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt index d947c6b..b4fb4c2 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Tree.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt @@ -1,6 +1,6 @@ -package AbstractTree +package TreeLib.AbstractTree -import Single_Objects.Container +import TreeLib.Single_Objects.Container abstract class Tree, Value, NodeType: Node, NodeType>> { protected abstract val treeStruct: TreeStruct, NodeType> diff --git a/lib/src/main/kotlin/Tree/AbstractTree/TreeStruct.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt similarity index 95% rename from lib/src/main/kotlin/Tree/AbstractTree/TreeStruct.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt index a1d3dd0..2e800d3 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt @@ -1,4 +1,4 @@ -package AbstractTree +package TreeLib.AbstractTree abstract class TreeStruct, NodeType : Node> { protected abstract var root: NodeType? //TODO root - not a null diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt similarity index 76% rename from lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt index 2060826..9412b1c 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt @@ -1,6 +1,6 @@ -package AbstractTree.Weighted +package TreeLib.AbstractTree.Weighted -import AbstractTree.Node +import TreeLib.AbstractTree.Node interface Balancer, NodeType : Node> { fun rightRotate(currentNode: NodeType): NodeType diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerNoParent.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt similarity index 82% rename from lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerNoParent.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt index 31506f1..f7113dc 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerNoParent.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt @@ -1,6 +1,6 @@ -package AbstractTree.Weighted +package TreeLib.AbstractTree.Weighted -import AbstractTree.Node +import TreeLib.AbstractTree.Node abstract class BalancerNoParent, NodeType : Node>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerParent.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt similarity index 82% rename from lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerParent.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt index d33d53c..4016b35 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerParent.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt @@ -1,6 +1,6 @@ -package AbstractTree.Weighted +package TreeLib.AbstractTree.Weighted -import AbstractTree.Node +import TreeLib.AbstractTree.Node abstract class BalancerParent, NodeType : Node>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt similarity index 86% rename from lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt index c256e22..31cf644 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt @@ -1,7 +1,7 @@ -package AbstractTree.Weighted +package TreeLib.AbstractTree.Weighted -import AbstractTree.Node -import AbstractTree.TreeStruct +import TreeLib.AbstractTree.Node +import TreeLib.AbstractTree.TreeStruct abstract class WeightedTreeStruct, NodeType : Node, BalancerType: Balancer>: TreeStruct() { diff --git a/lib/src/main/kotlin/Tree/BINtree/BINNode.kt b/lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt similarity index 75% rename from lib/src/main/kotlin/Tree/BINtree/BINNode.kt rename to lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt index 97ffb32..3e5ca1e 100644 --- a/lib/src/main/kotlin/Tree/BINtree/BINNode.kt +++ b/lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt @@ -1,6 +1,6 @@ -package BINtree +package TreeLib.BINtree -import AbstractTree.Node +import TreeLib.AbstractTree.Node class BINNode>( override var value: Pack?, diff --git a/lib/src/main/kotlin/Tree/BINtree/BINStruct.kt b/lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt similarity index 82% rename from lib/src/main/kotlin/Tree/BINtree/BINStruct.kt rename to lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt index bb20e6c..ebd3008 100644 --- a/lib/src/main/kotlin/Tree/BINtree/BINStruct.kt +++ b/lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt @@ -1,6 +1,6 @@ -package BINtree +package TreeLib.BINtree -import AbstractTree.TreeStruct +import TreeLib.AbstractTree.TreeStruct class BINStruct> : TreeStruct>() { override var root: BINNode? = null diff --git a/lib/src/main/kotlin/Tree/BINtree/BINTree.kt b/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt similarity index 64% rename from lib/src/main/kotlin/Tree/BINtree/BINTree.kt rename to lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt index ad29aa0..fd34f78 100644 --- a/lib/src/main/kotlin/Tree/BINtree/BINTree.kt +++ b/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt @@ -1,7 +1,7 @@ -package BINtree +package TreeLib.BINtree -import AbstractTree.Tree -import Single_Objects.Container +import TreeLib.AbstractTree.Tree +import TreeLib.Single_Objects.Container class BINTree, Value> : Tree>>() { override val treeStruct: BINStruct> = BINStruct() diff --git a/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt similarity index 69% rename from lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt rename to lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt index 4d94b63..30a0488 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt @@ -1,7 +1,6 @@ -package RBtree +package TreeLib.RBtree -import AVLtree.AVLNode -import AbstractTree.Weighted.BalancerParent +import TreeLib.AbstractTree.Weighted.BalancerParent class RBBalancer>: BalancerParent>() { override fun balance(node: RBNode): RBNode { diff --git a/lib/src/main/kotlin/Tree/RBtree/RBNode.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt similarity index 61% rename from lib/src/main/kotlin/Tree/RBtree/RBNode.kt rename to lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt index f1a2565..ab2bf77 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBNode.kt +++ b/lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt @@ -1,12 +1,12 @@ -package RBtree +package TreeLib.RBtree -import AbstractTree.Node -import Single_Objects.Markers +import TreeLib.AbstractTree.Node +import TreeLib.Single_Objects.Markers class RBNode>( override var value: Pack?, override var left: RBNode?, override var right: RBNode?, - var parent: RBNode, + var parent: RBNode?, var color: Markers, ) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/Tree/RBtree/RBStruct.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt similarity index 71% rename from lib/src/main/kotlin/Tree/RBtree/RBStruct.kt rename to lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt index 975e2b1..1327be1 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBStruct.kt +++ b/lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt @@ -1,6 +1,6 @@ -package RBtree +package TreeLib.RBtree -import AbstractTree.Weighted.WeightedTreeStruct +import TreeLib.AbstractTree.Weighted.WeightedTreeStruct class RBStruct > : WeightedTreeStruct, RBBalancer>(){ override var root: RBNode? = null diff --git a/lib/src/main/kotlin/Tree/RBtree/RBTree.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt similarity index 64% rename from lib/src/main/kotlin/Tree/RBtree/RBTree.kt rename to lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt index c0d179c..bd103a6 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBTree.kt +++ b/lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt @@ -1,8 +1,8 @@ -package RBtree +package TreeLib.RBtree -import AbstractTree.Tree +import TreeLib.AbstractTree.Tree -import Single_Objects.Container +import TreeLib.Single_Objects.Container class RBTree, Value> : Tree>>() { override val treeStruct: RBStruct> = RBStruct() diff --git a/lib/src/main/kotlin/Tree/Single_Objects/Container.kt b/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt similarity index 94% rename from lib/src/main/kotlin/Tree/Single_Objects/Container.kt rename to lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt index 2ca7fe5..06e4a9c 100644 --- a/lib/src/main/kotlin/Tree/Single_Objects/Container.kt +++ b/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt @@ -1,4 +1,4 @@ -package Single_Objects +package TreeLib.Single_Objects class Container, V>(private val pair: Pair) : Comparable> { diff --git a/lib/src/main/kotlin/Tree/Single_Objects/Markers.kt b/lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt similarity index 55% rename from lib/src/main/kotlin/Tree/Single_Objects/Markers.kt rename to lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt index 4bd9c2e..1d880d1 100644 --- a/lib/src/main/kotlin/Tree/Single_Objects/Markers.kt +++ b/lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt @@ -1,4 +1,4 @@ -package Single_Objects +package TreeLib.Single_Objects enum class Markers { RED, BLACK diff --git a/settings.gradle.kts b/settings.gradle.kts index e5425d5..21643d4 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 = "Tree" +rootProject.name = "TreeLib" include("lib") From 13200f372866e381bf0c76555a6a3059c1f1a4c0 Mon Sep 17 00:00:00 2001 From: Aleksandr <112642012+RozhkovAleksandr@users.noreply.github.com> Date: Thu, 23 Mar 2023 16:26:40 +0300 Subject: [PATCH 008/212] Create README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..dafba64 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# trees-1 +trees-1 created by GitHub Classroom +Hi there From 4f75efc0998ca19a77154fa5b14279bbf4e5a27c Mon Sep 17 00:00:00 2001 From: Aleksandr <112642012+RozhkovAleksandr@users.noreply.github.com> Date: Thu, 23 Mar 2023 16:26:53 +0300 Subject: [PATCH 009/212] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index dafba64..6530b63 100644 --- a/README.md +++ b/README.md @@ -1,3 +1 @@ -# trees-1 -trees-1 created by GitHub Classroom Hi there From 9bc1f49a657e602277b4341701158fb615e584dd Mon Sep 17 00:00:00 2001 From: Georgy S Date: Fri, 24 Mar 2023 20:33:25 +0300 Subject: [PATCH 010/212] feat: Implement insert methods in AVLStruct and in RBStruct, make edits to the code style --- lib/build.gradle.kts | 15 +++--- .../kotlin/TreeLib/AVLtree/AVLBalancer.kt | 9 ---- .../main/kotlin/TreeLib/AVLtree/AVLNode.kt | 10 ---- .../main/kotlin/TreeLib/AVLtree/AVLStruct.kt | 8 ---- .../main/kotlin/TreeLib/AbstractTree/Node.kt | 10 ---- .../kotlin/TreeLib/AbstractTree/TreeStruct.kt | 22 --------- .../Weighted/WeightedTreeStruct.kt | 32 ------------- .../main/kotlin/TreeLib/BINtree/BINStruct.kt | 15 ------ .../main/kotlin/TreeLib/RBtree/RBBalancer.kt | 9 ---- lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt | 12 ----- .../main/kotlin/TreeLib/RBtree/RBStruct.kt | 9 ---- .../main/kotlin/treelib/abstractTree/Node.kt | 7 +++ .../abstractTree}/Tree.kt | 4 +- .../kotlin/treelib/abstractTree/TreeStruct.kt | 47 +++++++++++++++++++ .../balanced/BalancedTreeStruct.kt | 41 ++++++++++++++++ .../abstractTree/balanced}/Balancer.kt | 4 +- .../balanced}/BalancerNoParent.kt | 7 +-- .../abstractTree/balanced}/BalancerParent.kt | 7 +-- .../kotlin/treelib/avlTree/AVLBalancer.kt | 9 ++++ .../main/kotlin/treelib/avlTree/AVLNode.kt | 10 ++++ .../main/kotlin/treelib/avlTree/AVLStruct.kt | 34 ++++++++++++++ .../AVLtree => treelib/avlTree}/AVLTree.kt | 6 +-- .../BINtree => treelib/binTree}/BINNode.kt | 6 +-- .../main/kotlin/treelib/binTree/BINStruct.kt | 23 +++++++++ .../BINtree => treelib/binTree}/BINTree.kt | 6 +-- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 9 ++++ lib/src/main/kotlin/treelib/rbTree/RBNode.kt | 12 +++++ .../main/kotlin/treelib/rbTree/RBStruct.kt | 37 +++++++++++++++ .../RBtree => treelib/rbTree}/RBTree.kt | 6 +-- .../singleObjects}/Container.kt | 2 +- .../singleObjects}/Markers.kt | 2 +- 31 files changed, 263 insertions(+), 167 deletions(-) delete mode 100644 lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt delete mode 100644 lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt delete mode 100644 lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt delete mode 100644 lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt delete mode 100644 lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt delete mode 100644 lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt delete mode 100644 lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt delete mode 100644 lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt delete mode 100644 lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt delete mode 100644 lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt create mode 100644 lib/src/main/kotlin/treelib/abstractTree/Node.kt rename lib/src/main/kotlin/{TreeLib/AbstractTree => treelib/abstractTree}/Tree.kt (91%) create mode 100644 lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt create mode 100644 lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt rename lib/src/main/kotlin/{TreeLib/AbstractTree/Weighted => treelib/abstractTree/balanced}/Balancer.kt (76%) rename lib/src/main/kotlin/{TreeLib/AbstractTree/Weighted => treelib/abstractTree/balanced}/BalancerNoParent.kt (64%) rename lib/src/main/kotlin/{TreeLib/AbstractTree/Weighted => treelib/abstractTree/balanced}/BalancerParent.kt (65%) create mode 100644 lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt create mode 100644 lib/src/main/kotlin/treelib/avlTree/AVLNode.kt create mode 100644 lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt rename lib/src/main/kotlin/{TreeLib/AVLtree => treelib/avlTree}/AVLTree.kt (65%) rename lib/src/main/kotlin/{TreeLib/BINtree => treelib/binTree}/BINNode.kt (62%) create mode 100644 lib/src/main/kotlin/treelib/binTree/BINStruct.kt rename lib/src/main/kotlin/{TreeLib/BINtree => treelib/binTree}/BINTree.kt (64%) create mode 100644 lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt create mode 100644 lib/src/main/kotlin/treelib/rbTree/RBNode.kt create mode 100644 lib/src/main/kotlin/treelib/rbTree/RBStruct.kt rename lib/src/main/kotlin/{TreeLib/RBtree => treelib/rbTree}/RBTree.kt (64%) rename lib/src/main/kotlin/{TreeLib/Single_Objects => treelib/singleObjects}/Container.kt (94%) rename lib/src/main/kotlin/{TreeLib/Single_Objects => treelib/singleObjects}/Markers.kt (55%) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 9513ece..7767c9a 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -16,12 +16,13 @@ plugins { } java { toolchain { - languageVersion.set(JavaLanguageVersion.of(8)) + languageVersion.set(JavaLanguageVersion.of(17)) } } repositories { // Use Maven Central for resolving dependencies. + mavenLocal() mavenCentral() } @@ -39,11 +40,6 @@ dependencies { implementation("com.google.guava:guava:31.1-jre") } -tasks.named("test") { - // Use JUnit Platform for unit tests. - useJUnitPlatform() -} - publishing { publications { create("maven") { @@ -53,4 +49,9 @@ publishing { from(components["java"]) } } -} \ No newline at end of file +} + +tasks.named("test") { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt deleted file mode 100644 index 1078742..0000000 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt +++ /dev/null @@ -1,9 +0,0 @@ -package TreeLib.AVLtree - -import TreeLib.AbstractTree.Weighted.BalancerNoParent - -class AVLBalancer>: BalancerNoParent>() { - override fun balance(node: AVLNode): AVLNode { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt deleted file mode 100644 index d92d1d3..0000000 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt +++ /dev/null @@ -1,10 +0,0 @@ -package TreeLib.AVLtree - -import TreeLib.AbstractTree.Node - -class AVLNode>( - override var value: Pack?, - override var left: AVLNode?, - override var right: AVLNode?, - var height:Int, -) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt deleted file mode 100644 index 41093b5..0000000 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt +++ /dev/null @@ -1,8 +0,0 @@ -package TreeLib.AVLtree - -import TreeLib.AbstractTree.Weighted.WeightedTreeStruct - -class AVLStruct> : WeightedTreeStruct, AVLBalancer>() { - override var root: AVLNode? = null - override val balancer = AVLBalancer() -} \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt deleted file mode 100644 index fbb7462..0000000 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt +++ /dev/null @@ -1,10 +0,0 @@ -package TreeLib.AbstractTree - -interface Node, SubNode : Node>{ - var value: Pack? - var left: SubNode? - var right: SubNode? -} -/* -* TODO как реализовать leaf, может придется использовать null-able type для полей -* */ diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt deleted file mode 100644 index 2e800d3..0000000 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt +++ /dev/null @@ -1,22 +0,0 @@ -package TreeLib.AbstractTree - -abstract class TreeStruct, NodeType : Node> { - protected abstract var root: NodeType? //TODO root - not a null - - fun find(obj: Pack): Pack?{ - TODO("Сделать реализацию поиска, дергать реализацию из локализированного класса") - } - - fun inOrder(){ - TODO("inOrder - implementation") - } - fun postOrder(){ - TODO("postOrder - implementation") - } - fun preOrder(){ - TODO("preOrder - implementation") - } - - abstract fun insert(item: Pack) - abstract fun delete(item: Pack) -} diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt deleted file mode 100644 index 31cf644..0000000 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt +++ /dev/null @@ -1,32 +0,0 @@ -package TreeLib.AbstractTree.Weighted - -import TreeLib.AbstractTree.Node -import TreeLib.AbstractTree.TreeStruct - -abstract class WeightedTreeStruct, NodeType : Node, BalancerType: Balancer>: - TreeStruct() { - - protected abstract val balancer: BalancerType - - private fun insertItem(item: Pack) { - TODO("Not yet implemented") - } - - private fun deleteItem(item: Pack) { - TODO("Not yet implemented") - } - - override fun insert(item: Pack) { - insertItem(item) - if (root != null) { - root = balancer.balance(root!!) - } - } - - override fun delete(item: Pack) { - deleteItem(item) - if (root != null) { - root = balancer.balance(root!!) - } - } -} \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt b/lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt deleted file mode 100644 index ebd3008..0000000 --- a/lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt +++ /dev/null @@ -1,15 +0,0 @@ -package TreeLib.BINtree - -import TreeLib.AbstractTree.TreeStruct - -class BINStruct> : TreeStruct>() { - override var root: BINNode? = null - - override fun delete(item: Pack) { - TODO("Not yet implemented") - } - - override fun insert(item: Pack) { - TODO("Not yet implemented") - } -} \ 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 deleted file mode 100644 index 30a0488..0000000 --- a/lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt +++ /dev/null @@ -1,9 +0,0 @@ -package TreeLib.RBtree - -import TreeLib.AbstractTree.Weighted.BalancerParent - -class RBBalancer>: BalancerParent>() { - override fun balance(node: RBNode): RBNode { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt deleted file mode 100644 index ab2bf77..0000000 --- a/lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt +++ /dev/null @@ -1,12 +0,0 @@ -package TreeLib.RBtree - -import TreeLib.AbstractTree.Node -import TreeLib.Single_Objects.Markers - -class RBNode>( - override var value: Pack?, - override var left: RBNode?, - override var right: RBNode?, - var parent: RBNode?, - var color: Markers, -) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt deleted file mode 100644 index 1327be1..0000000 --- a/lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt +++ /dev/null @@ -1,9 +0,0 @@ -package TreeLib.RBtree - -import TreeLib.AbstractTree.Weighted.WeightedTreeStruct - -class RBStruct > : WeightedTreeStruct, RBBalancer>(){ - override var root: RBNode? = null - override val balancer = RBBalancer() - -} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/abstractTree/Node.kt b/lib/src/main/kotlin/treelib/abstractTree/Node.kt new file mode 100644 index 0000000..849e46e --- /dev/null +++ b/lib/src/main/kotlin/treelib/abstractTree/Node.kt @@ -0,0 +1,7 @@ +package treelib.abstractTree + +interface Node, SubNode : Node> { + var value: Pack + var left: SubNode? + var right: SubNode? +} diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt similarity index 91% rename from lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt rename to lib/src/main/kotlin/treelib/abstractTree/Tree.kt index b4fb4c2..b5684d6 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -1,6 +1,6 @@ -package TreeLib.AbstractTree +package treelib.abstractTree -import TreeLib.Single_Objects.Container +import treelib.singleObjects.Container abstract class Tree, Value, NodeType: Node, NodeType>> { protected abstract val treeStruct: TreeStruct, NodeType> diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt new file mode 100644 index 0000000..4aaf20d --- /dev/null +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -0,0 +1,47 @@ +package treelib.abstractTree + +abstract class TreeStruct, NodeType : Node> { + + protected abstract var root: NodeType? + + fun find(obj: Pack): Pack? { + if (findItem(obj) == null) return null + else return obj + } + protected fun findItem(obj: Pack): NodeType? { + var currentNode = root + if (root == null) { + return null + } + while (true) { + if (obj == currentNode?.value) return currentNode + else { + if (currentNode != null) { + if (obj > currentNode.value) currentNode = currentNode.right + else currentNode = currentNode.left + } else return null + } + } + } + + // TODO поведение: если find(item) == true => заменить value на item, иначе создать новую ноду + protected abstract fun insertItem(item: Pack): NodeType? + + abstract fun insert(item: Pack): Pack? + + abstract fun delete(item: Pack): Pack? + + protected abstract fun deleteItem(item: Pack): NodeType? + + fun inOrder() { + TODO("inOrder - implementation") + } + + fun postOrder() { + TODO("postOrder - implementation") + } + + fun preOrder() { + TODO("preOrder - implementation") + } +} diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt new file mode 100644 index 0000000..7e636a8 --- /dev/null +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt @@ -0,0 +1,41 @@ +package treelib.abstractTree.balanced + +import treelib.abstractTree.Node +import treelib.abstractTree.TreeStruct + +abstract class BalancedTreeStruct, NodeType : Node, BalancerType : Balancer> : + TreeStruct() { + + protected abstract val balancer: BalancerType + + protected fun getLeafForInsert(item: Pack): NodeType? { + var currentNode = root + while (true) { + if (currentNode != null) { + if (item > currentNode.value) { + if (currentNode.right != null) currentNode = currentNode.right + else return currentNode + } else { + if (currentNode.left != null) currentNode = currentNode.left + else return currentNode + } + } else return null + } + } + + override fun insert(item: Pack): Pack? { + val currenNode = insertItem(item) + if (currenNode != null) { + root = balancer.balance(currenNode) + return item + } else return null + } +// если удалил рут, то не запускаем баланс + override fun delete(item: Pack): Pack? { + val currenNode = deleteItem(item) + if (currenNode != null) { + root = balancer.balance(currenNode) + return item + } else return null + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt similarity index 76% rename from lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt rename to lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt index 9412b1c..dba1741 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt @@ -1,6 +1,6 @@ -package TreeLib.AbstractTree.Weighted +package treelib.abstractTree.balanced -import TreeLib.AbstractTree.Node +import treelib.abstractTree.Node interface Balancer, NodeType : Node> { fun rightRotate(currentNode: NodeType): NodeType diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt similarity index 64% rename from lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt rename to lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt index f7113dc..0dd8d62 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt @@ -1,8 +1,9 @@ -package TreeLib.AbstractTree.Weighted +package treelib.abstractTree.balanced -import TreeLib.AbstractTree.Node +import treelib.abstractTree.Node -abstract class BalancerNoParent, NodeType : Node>: Balancer { +abstract class BalancerNoParent, NodeType : Node>(val root: NodeType?) : + Balancer { override fun rightRotate(currentNode: NodeType): NodeType { TODO("Not yet implemented") } diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt similarity index 65% rename from lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt rename to lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt index 4016b35..74a0c6f 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt @@ -1,8 +1,9 @@ -package TreeLib.AbstractTree.Weighted +package treelib.abstractTree.balanced -import TreeLib.AbstractTree.Node +import treelib.abstractTree.Node -abstract class BalancerParent, NodeType : Node>: Balancer { +abstract class BalancerParent, NodeType : Node>(val root: NodeType?) : + Balancer { override fun rightRotate(currentNode: NodeType): NodeType { TODO("Not yet implemented") } diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt new file mode 100644 index 0000000..201cad5 --- /dev/null +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -0,0 +1,9 @@ +package treelib.avlTree + +import treelib.abstractTree.balanced.BalancerNoParent + +class AVLBalancer>(root: AVLNode?) : BalancerNoParent>(root) { + override fun balance(node: AVLNode): AVLNode { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt b/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt new file mode 100644 index 0000000..5f58f0d --- /dev/null +++ b/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt @@ -0,0 +1,10 @@ +package treelib.avlTree + +import treelib.abstractTree.Node + +class AVLNode>( + override var value: Pack, + override var left: AVLNode? = null, + override var right: AVLNode? = null, + var height: UInt = 1U, +) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt new file mode 100644 index 0000000..551b83a --- /dev/null +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -0,0 +1,34 @@ +package treelib.avlTree + +import treelib.abstractTree.balanced.BalancedTreeStruct + +class AVLStruct> : BalancedTreeStruct, AVLBalancer>() { + override var root: AVLNode? = null + override val balancer = AVLBalancer(root) + override fun deleteItem(item: Pack): AVLNode { + // возвращать того, от кого начинается балансировка. + TODO("Not yet implemented") + } + + override fun insertItem(item: Pack): AVLNode? { + val parentNode: AVLNode? + val currentNode: AVLNode + val updateNode: AVLNode? = findItem(item) + + if (updateNode == null) { + parentNode = getLeafForInsert(item) + currentNode = AVLNode(value = item) + if (parentNode != null) { + if (item > parentNode.value) parentNode.right = currentNode + else parentNode.left = currentNode + } else { + root = AVLNode(item) + return root + } + return currentNode + } else { + updateNode.value = item + return updateNode + } + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt similarity index 65% rename from lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt rename to lib/src/main/kotlin/treelib/avlTree/AVLTree.kt index 0c8f284..a83d8a2 100644 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt @@ -1,8 +1,8 @@ -package TreeLib.AVLtree +package treelib.avlTree -import TreeLib.AbstractTree.Tree +import treelib.abstractTree.Tree -import TreeLib.Single_Objects.Container +import treelib.singleObjects.Container class AVLTree, Value> : Tree>>() { diff --git a/lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt b/lib/src/main/kotlin/treelib/binTree/BINNode.kt similarity index 62% rename from lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt rename to lib/src/main/kotlin/treelib/binTree/BINNode.kt index 3e5ca1e..a503f34 100644 --- a/lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINNode.kt @@ -1,9 +1,9 @@ -package TreeLib.BINtree +package treelib.binTree -import TreeLib.AbstractTree.Node +import treelib.abstractTree.Node class BINNode>( - override var value: Pack?, + override var value: Pack, override var left: BINNode?, override var right: BINNode? ) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt new file mode 100644 index 0000000..54c56c1 --- /dev/null +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -0,0 +1,23 @@ +package treelib.binTree + +import treelib.abstractTree.TreeStruct + +class BINStruct> : TreeStruct>() { + override var root: BINNode? = null + + override fun delete(item: Pack): Pack? { + TODO("Not yet implemented") + } + + override fun deleteItem(item: Pack): BINNode? { + TODO("Not yet implemented") + } + + override fun insert(item: Pack): Pack? { + TODO("Not yet implemented") + } + + override fun insertItem(item: Pack): BINNode? { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt b/lib/src/main/kotlin/treelib/binTree/BINTree.kt similarity index 64% rename from lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt rename to lib/src/main/kotlin/treelib/binTree/BINTree.kt index fd34f78..9d0f4b3 100644 --- a/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINTree.kt @@ -1,7 +1,7 @@ -package TreeLib.BINtree +package treelib.binTree -import TreeLib.AbstractTree.Tree -import TreeLib.Single_Objects.Container +import treelib.abstractTree.Tree +import treelib.singleObjects.Container class BINTree, Value> : Tree>>() { override val treeStruct: BINStruct> = BINStruct() diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt new file mode 100644 index 0000000..e86ea0f --- /dev/null +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -0,0 +1,9 @@ +package treelib.rbTree + +import treelib.abstractTree.balanced.BalancerParent + +class RBBalancer>(root: RBNode?) : BalancerParent>(root) { + override fun balance(node: RBNode): RBNode { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/RBNode.kt b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt new file mode 100644 index 0000000..3453f69 --- /dev/null +++ b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt @@ -0,0 +1,12 @@ +package treelib.rbTree + +import treelib.abstractTree.Node +import treelib.singleObjects.Markers + +class RBNode>( + override var value: Pack, + override var left: RBNode? = null, + override var right: RBNode? = null, + var parent: RBNode? = null, + var color: Markers = Markers.RED, +) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt new file mode 100644 index 0000000..7924b51 --- /dev/null +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -0,0 +1,37 @@ +package treelib.rbTree + +import treelib.abstractTree.balanced.BalancedTreeStruct + +class RBStruct> : BalancedTreeStruct, RBBalancer>() { + override var root: RBNode? = null + override val balancer = RBBalancer(root) + + override fun deleteItem(item: Pack): RBNode? { + if (find(item) == null) { + TODO("Not yet implemented") + } else return null + } + +// передавать того, от кого начинается балансировка + override fun insertItem(item: Pack): RBNode? { + val parentNode: RBNode? + val currentNode: RBNode + val updateNode: RBNode? = findItem(item) + + if (updateNode == null) { + parentNode = getLeafForInsert(item) + currentNode = RBNode(value = item, parent = parentNode) + if (parentNode != null) { + if (item > parentNode.value) parentNode.right = currentNode + else parentNode.left = currentNode + } else { + root = RBNode(value = item) + return root + } + return currentNode + } else { + updateNode.value = item + return updateNode + } + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt similarity index 64% rename from lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt rename to lib/src/main/kotlin/treelib/rbTree/RBTree.kt index bd103a6..6b6c149 100644 --- a/lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt @@ -1,8 +1,8 @@ -package TreeLib.RBtree +package treelib.rbTree -import TreeLib.AbstractTree.Tree +import treelib.abstractTree.Tree -import TreeLib.Single_Objects.Container +import treelib.singleObjects.Container class RBTree, Value> : Tree>>() { override val treeStruct: RBStruct> = RBStruct() diff --git a/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt b/lib/src/main/kotlin/treelib/singleObjects/Container.kt similarity index 94% rename from lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt rename to lib/src/main/kotlin/treelib/singleObjects/Container.kt index 06e4a9c..9634eb1 100644 --- a/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/Container.kt @@ -1,4 +1,4 @@ -package TreeLib.Single_Objects +package treelib.singleObjects class Container, V>(private val pair: Pair) : Comparable> { diff --git a/lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt b/lib/src/main/kotlin/treelib/singleObjects/Markers.kt similarity index 55% rename from lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt rename to lib/src/main/kotlin/treelib/singleObjects/Markers.kt index 1d880d1..3e4e190 100644 --- a/lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/Markers.kt @@ -1,4 +1,4 @@ -package TreeLib.Single_Objects +package treelib.singleObjects enum class Markers { RED, BLACK From ea4f20c50162c9286987167653b89f0f18930515 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 25 Mar 2023 01:14:27 +0300 Subject: [PATCH 011/212] feat: Implement protected methods in TreeStruct, add init parameters in BINNode --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 120 +++++++++++++++++- .../main/kotlin/treelib/binTree/BINNode.kt | 4 +- 2 files changed, 116 insertions(+), 8 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 4aaf20d..c30394a 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -4,10 +4,104 @@ abstract class TreeStruct, NodeType : Node currentNode.value) { + if (currentNode.right != null) currentNode = currentNode.right + else return currentNode + } else { + if (currentNode.left != null) currentNode = currentNode.left + else return currentNode + } + } else return null + } + } + + protected fun getParentByValue(item: Pack): NodeType? { + //TODO test - getParentByValue [что вообще корректно отрабатывает] + /* + (1) - shouldn't be used with 'root == null' otherwise - incorrect behavior + (2) - shouldn't be used with a value doesn't exist in the tree + null - means root.value == item + */ + + var currentNode = root + if (findItem(item) == null) return null // (2) + + if ((currentNode != null) && (currentNode.value == item)) return null + + while (true) { + if (currentNode != null) { + when { + currentNode.right != null -> if (currentNode.right!!.value == item) return currentNode + currentNode.left != null -> if (currentNode.left!!.value == item) return currentNode + } + if (item > currentNode.value) currentNode = currentNode.right + else currentNode = currentNode.left + + } else return null // (1) + } + } + + protected infix fun Pack.inRightOf(node: NodeType): Boolean { + //TODO - test inInRightOf + if (node.right != null){ + if (node.right!!.value == this) return true + } + return false + } + + protected infix fun Pack.inLeftOf(node: NodeType): Boolean { + //TODO - test inInLeftOf + if (node.left != null){ + if (node.left!!.value == this) return true + } + return false } + + protected fun getLeafRightMin(localRoot: NodeType): NodeType? { + //TODO test - getLeafForDelete [проверить, что нет проблем с (->1)] + /* + null - means NodeType.right doesn't exist, another variant impossible + */ + var currentNode: NodeType? + if (localRoot.right != null) currentNode = localRoot.right + else return null + + while (true) { + if (currentNode != null) { + if ((currentNode.left == null) && (currentNode.right == null)) return currentNode + when { + currentNode.left != null -> currentNode = currentNode.left + currentNode.right != null -> currentNode = currentNode.right + } + } else return null //(->1) + } + } + + protected fun getLeafLeftMax(localRoot: NodeType): NodeType? { + //TODO test - getLeafLeftMax [проверить, что нет проблем с (->1)] + /* + null - means NodeType.left doesn't exist, another variant impossible + */ + var currentNode: NodeType? + if (localRoot.left != null) currentNode = localRoot.left + else return null + + while (true) { + if (currentNode != null) { + if ((currentNode.left == null) && (currentNode.right == null)) return currentNode + when { + currentNode.right != null -> currentNode = currentNode.right + currentNode.left != null -> currentNode = currentNode.left + } + } else return null //(->1) + } + } + protected fun findItem(obj: Pack): NodeType? { var currentNode = root if (root == null) { @@ -24,15 +118,29 @@ abstract class TreeStruct, NodeType : Node заменить value на item, иначе создать новую ноду protected abstract fun insertItem(item: Pack): NodeType? + protected abstract fun deleteItem(item: Pack): NodeType? + + protected abstract fun linkLeafAsNode( + //TODO linkLeafAsNode - [написал какой-то кринж, надо хорошо подумать, как это делать] + leaf: NodeType, + parent: NodeType?, + right: NodeType?, + left: NodeType? + ): NodeType? + + fun find(obj: Pack): Pack? { + /* Behavior: if find(item) == true => replace value with item, otherwise create a new node */ + //TODO test - find + if (findItem(obj) == null) return null + else return obj + } + abstract fun insert(item: Pack): Pack? abstract fun delete(item: Pack): Pack? - protected abstract fun deleteItem(item: Pack): NodeType? - fun inOrder() { TODO("inOrder - implementation") } diff --git a/lib/src/main/kotlin/treelib/binTree/BINNode.kt b/lib/src/main/kotlin/treelib/binTree/BINNode.kt index a503f34..d330529 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINNode.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINNode.kt @@ -4,6 +4,6 @@ import treelib.abstractTree.Node class BINNode>( override var value: Pack, - override var left: BINNode?, - override var right: BINNode? + override var left: BINNode? = null, + override var right: BINNode? = null, ) : Node> \ No newline at end of file From 32f3cc6a1aef0fa710a74bb27e36f1042f8b9a9c Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 27 Mar 2023 12:26:48 +0300 Subject: [PATCH 012/212] feat: Implement delete methods in BINStruct, add abstract methods: rebaseNode and unLink in TreeStruct --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 50 +++++---- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 18 ++- .../main/kotlin/treelib/binTree/BINStruct.kt | 106 ++++++++++++++++-- .../main/kotlin/treelib/rbTree/RBStruct.kt | 9 ++ 4 files changed, 149 insertions(+), 34 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index c30394a..d9a437c 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -46,23 +46,23 @@ abstract class TreeStruct, NodeType : Node1)] /* null - means NodeType.right doesn't exist, another variant impossible @@ -73,16 +73,13 @@ abstract class TreeStruct, NodeType : Node currentNode = currentNode.left - currentNode.right != null -> currentNode = currentNode.right - } + if (currentNode.left == null) return currentNode + else currentNode = currentNode.left } else return null //(->1) } } - protected fun getLeafLeftMax(localRoot: NodeType): NodeType? { + private fun getLeftMaxNode(localRoot: NodeType): NodeType? { //TODO test - getLeafLeftMax [проверить, что нет проблем с (->1)] /* null - means NodeType.left doesn't exist, another variant impossible @@ -93,15 +90,21 @@ abstract class TreeStruct, NodeType : Node currentNode = currentNode.right - currentNode.left != null -> currentNode = currentNode.left - } + if (currentNode.right == null) return currentNode + else currentNode = currentNode.right } else return null //(->1) } } + protected fun getNodeForReplace(localRoot: NodeType?): NodeType? { + /* Behaviour: null - localRoot doesn't have children */ + if (localRoot != null) { + val replaceNode = getRightMinNode(localRoot) + if (replaceNode != null) return replaceNode + else return getLeftMaxNode(localRoot) + } else return null + } + protected fun findItem(obj: Pack): NodeType? { var currentNode = root if (root == null) { @@ -122,13 +125,17 @@ abstract class TreeStruct, NodeType : Node replace value with item, otherwise create a new node */ @@ -139,6 +146,7 @@ abstract class TreeStruct, NodeType : Node> : BalancedTreeStruct, AVLBalancer>() { override var root: AVLNode? = null + override val balancer = AVLBalancer(root) + + override fun rebaseNode(node: AVLNode, parent: AVLNode?, replaceNode: AVLNode?): AVLNode? { + TODO("Not yet implemented rebaseNode") + } + + override fun unLink(node: AVLNode, parent: AVLNode?): AVLNode { + TODO("Not yet implemented unLink") + } + override fun deleteItem(item: Pack): AVLNode { // возвращать того, от кого начинается балансировка. TODO("Not yet implemented") } - override fun insertItem(item: Pack): AVLNode? { + override fun insertItem(item: Pack): AVLNode { val parentNode: AVLNode? val currentNode: AVLNode val updateNode: AVLNode? = findItem(item) @@ -21,10 +31,8 @@ class AVLStruct> : BalancedTreeStruct parentNode.value) parentNode.right = currentNode else parentNode.left = currentNode - } else { - root = AVLNode(item) - return root - } + } else root = currentNode + return currentNode } else { updateNode.value = item diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index 54c56c1..591a679 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -5,19 +5,109 @@ import treelib.abstractTree.TreeStruct class BINStruct> : TreeStruct>() { override var root: BINNode? = null - override fun delete(item: Pack): Pack? { - TODO("Not yet implemented") + override fun rebaseNode( + node: BINNode, + parent: BINNode?, + replaceNode: BINNode?, + ): BINNode? { + /*Behaviour: return - linked replaceNode*/ + //TODO: rebaseNode - test + if (replaceNode != null) { + replaceNode.left = node.left + node.left = null + replaceNode.right = node.right + node.right = null + } + if (parent != null){ + when{ + node.value inRightOf parent -> parent.right = replaceNode + node.value inLeftOf parent -> parent.left = replaceNode + else -> return null + } + } + return replaceNode } - override fun deleteItem(item: Pack): BINNode? { - TODO("Not yet implemented") + override fun unLink( + node: BINNode, + parent: BINNode? + ): BINNode { + /*Behaviour: + * 1) must be used with one or two children == null + * 2) return - Node without children */ + /*TODO: unLink - test*/ + val unLinkedNode: BINNode = node + val childForLink: BINNode? + + when { + (node.right != null) && (node.left != null) -> return node //means-error (in correct node input) + (node.right != null) -> childForLink = node.right + (node.left != null) -> childForLink = node.left + else -> childForLink = null + } + unLinkedNode.left = null + unLinkedNode.right = null + if (parent != null) { + when { + (node.value < parent.value) -> parent.right = childForLink + (node.value > parent.value) -> parent.left = childForLink + } + } + return unLinkedNode } - override fun insert(item: Pack): Pack? { - TODO("Not yet implemented") + override fun delete(item: Pack): Pack? = deleteItem(item)?.value + + override fun deleteItem(item: Pack): BINNode? { + /*return: null - error; value - deleted value*/ + /*TODO: test - deleteItem [невозможные ветки: - проверить, что они реально невозможны + (->1 & 2) => что невозможно достичь deleteNode = parentNode?. со значением null (потому что inRightOf/inLeftOf) + (->3) => странный кейс, когда удаляемое значение - null; такого вообще быть не должно + ] */ + val parentNode: BINNode? // parent node of the node for deleting + var replaceNode: BINNode? // node for relink on the place deleted node + var deleteNode: BINNode? = null // node for deleting + + if (findItem(item) != null) { + parentNode = getParentByValue(item) + when { + parentNode == null -> deleteNode = root + item inRightOf parentNode -> deleteNode = parentNode.right //(->1) + item inLeftOf parentNode -> deleteNode = parentNode.left //(->2) + } + if (deleteNode != null) { + replaceNode = getNodeForReplace(deleteNode) + if (replaceNode != null) { + replaceNode = unLink(replaceNode, getParentByValue(replaceNode.value)) + } else replaceNode = null // if deleteNode doesn't have children + + if (parentNode == null) root = rebaseNode(deleteNode, parentNode, replaceNode) + else rebaseNode(deleteNode, parentNode, replaceNode) + + return deleteNode + } else return null // (->3) + } else return null } - override fun insertItem(item: Pack): BINNode? { - TODO("Not yet implemented") + override fun insert(item: Pack): Pack = insertItem(item).value + + override fun insertItem(item: Pack): BINNode { + val parentNode: BINNode? + val currentNode: BINNode + val updateNode: BINNode? = findItem(item) + + if (updateNode == null) { + parentNode = getLeafForInsert(item) + currentNode = BINNode(value = item) + if (parentNode != null) { + if (item > parentNode.value) parentNode.right = currentNode + else parentNode.left = currentNode + } else root = currentNode + + return currentNode + } else { + updateNode.value = item + return updateNode + } } } \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 7924b51..49d8fff 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -4,8 +4,17 @@ import treelib.abstractTree.balanced.BalancedTreeStruct class RBStruct> : BalancedTreeStruct, RBBalancer>() { override var root: RBNode? = null + override val balancer = RBBalancer(root) + override fun rebaseNode(node: RBNode, parent: RBNode?, replaceNode: RBNode?): RBNode? { + TODO("Not yet implemented") + } + + override fun unLink(node: RBNode, parent: RBNode?): RBNode { + TODO("Not yet implemented") + } + override fun deleteItem(item: Pack): RBNode? { if (find(item) == null) { TODO("Not yet implemented") From c303533dfcffd32dbdaf94694e4b31c844f5c959 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 29 Mar 2023 17:38:41 +0300 Subject: [PATCH 013/212] feat: Implement TreeStructWrapper for executing private methods of TreeStructs in tests; Create template for TreeStruct tests --- .../test/kotlin/treelib/TreeStructWrapper.kt | 22 ++++++++ .../test/kotlin/treelib/TreeStructsTest.kt | 56 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 lib/src/test/kotlin/treelib/TreeStructWrapper.kt create mode 100644 lib/src/test/kotlin/treelib/TreeStructsTest.kt diff --git a/lib/src/test/kotlin/treelib/TreeStructWrapper.kt b/lib/src/test/kotlin/treelib/TreeStructWrapper.kt new file mode 100644 index 0000000..c19d193 --- /dev/null +++ b/lib/src/test/kotlin/treelib/TreeStructWrapper.kt @@ -0,0 +1,22 @@ +package treelib + +import treelib.abstractTree.Node +import treelib.abstractTree.TreeStruct + +class TreeStructWrapper, NodeType : Node, TStruct : TreeStruct> { + + fun getPrivateNode(tree: TStruct, name: String = "root"): NodeType? { + val field = tree.javaClass.getDeclaredField(name) + field.isAccessible = true + val root = field.get(tree) + return if (root == null) null + else root as? NodeType + } + + 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) + else method.invoke(tree) + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/TreeStructsTest.kt b/lib/src/test/kotlin/treelib/TreeStructsTest.kt new file mode 100644 index 0000000..a64d2a0 --- /dev/null +++ b/lib/src/test/kotlin/treelib/TreeStructsTest.kt @@ -0,0 +1,56 @@ +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.AVLStruct + +/* RBStruct */ +import treelib.rbTree.RBNode +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, BINStruct>() + var classUnderTest = BINStruct() + + @BeforeEach + fun reInitClassUnderTest() { + classUnderTest = BINStruct() + } + } + + @Nested + @DisplayName("Test: AVL Struct") + inner class AVLStructTest { + val treeW = TreeStructWrapper, AVLStruct>() + var classUnderTest = AVLStruct() + + @BeforeEach + fun reInitClassUnderTest() { + classUnderTest = AVLStruct() + } + } + + @Nested + @DisplayName("Test: Red-Black Tree Struct") + inner class RBStructTree { + val treeW = TreeStructWrapper, RBStruct>() + var classUnderTest = RBStruct() + + @BeforeEach + fun reInitClassUnderTest() { + classUnderTest = RBStruct() + } + } +} From b0f9acfce37fd1578d069616b6ad613830af1bdb Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 29 Mar 2023 18:48:59 +0300 Subject: [PATCH 014/212] fix: Add null-safety checks; Abstract deleteItem and insertItem --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 171 ++++++++++++------ .../main/kotlin/treelib/binTree/BINStruct.kt | 96 +++------- 2 files changed, 141 insertions(+), 126 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index d9a437c..66af56b 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -1,22 +1,25 @@ package treelib.abstractTree +import treelib.binTree.BINNode + abstract class TreeStruct, NodeType : Node> { protected abstract var root: NodeType? protected fun getLeafForInsert(item: Pack): NodeType? { - //TODO test - getParentByValue - var currentNode = root + //TODO test - getParentByValue; [не нравится, что currentNode = it.right не требует проверки на null] + var currentNode: NodeType? = root ?: return null + while (true) { - if (currentNode != null) { - if (item > currentNode.value) { - if (currentNode.right != null) currentNode = currentNode.right - else return currentNode + currentNode?.let { + if (item > it.value) { + if (it.right == null) return currentNode + currentNode = it.right } else { - if (currentNode.left != null) currentNode = currentNode.left - else return currentNode + if (it.left == null) return currentNode + currentNode = it.left } - } else return null + } ?: throw Exception("Impossible case or multithreading problem ") } } @@ -29,101 +32,154 @@ abstract class TreeStruct, NodeType : Node if (currentNode.right!!.value == item) return currentNode - currentNode.left != null -> if (currentNode.left!!.value == item) return currentNode + it.right != null -> { + if (it.right?.value == item) return currentNode + else throw Exception("impossible case, or multithreading threads problem") + } + + it.left != null -> { + if (it.left?.value == item) return currentNode + else throw Exception("impossible case, or multithreading threads problem") + } } - if (item > currentNode.value) currentNode = currentNode.right - else currentNode = currentNode.left + if (item > it.value) currentNode = it.right + else currentNode = it.left - } else return null // (1) + } ?: throw Exception("getParentByValue shouldn't be used with 'root == null'")// (1)l -> } } protected infix fun Pack.inRightOf(node: NodeType?): Boolean { //TODO - test inInRightOf - if ((node != null) && (node.right != null)) { - if (node.right!!.value == this) return true + if ((node == null) || (node.right == null)) return false + node.right?.let { + if (it.value == node.value) return true } return false } protected infix fun Pack.inLeftOf(node: NodeType?): Boolean { //TODO - test inInLeftOf - if ((node != null) && (node.left != null)) { - if (node.left!!.value == this) return true + if ((node == null) || (node.left == null)) return false + node.left?.let { + if (it.value == this) return true } return false } private fun getRightMinNode(localRoot: NodeType): NodeType? { + /* null - means NodeType.right doesn't exist, another variant impossible */ //TODO test - getLeafForDelete [проверить, что нет проблем с (->1)] - /* - null - means NodeType.right doesn't exist, another variant impossible - */ - var currentNode: NodeType? - if (localRoot.right != null) currentNode = localRoot.right - else return null + var currentNode: NodeType? = null + + localRoot.right?.let { + currentNode = localRoot.right + } ?: return null while (true) { - if (currentNode != null) { - if (currentNode.left == null) return currentNode - else currentNode = currentNode.left - } else return null //(->1) + currentNode?.let { curNode -> + if (curNode.left == null) return currentNode + else currentNode = curNode.left + } ?: throw Exception("impossible case, or multithreading threads problem") //(->1) } } private fun getLeftMaxNode(localRoot: NodeType): NodeType? { + /* null - means NodeType.left doesn't exist, another variant impossible */ //TODO test - getLeafLeftMax [проверить, что нет проблем с (->1)] - /* - null - means NodeType.left doesn't exist, another variant impossible - */ - var currentNode: NodeType? - if (localRoot.left != null) currentNode = localRoot.left - else return null + var currentNode: NodeType? = null + + localRoot.left?.let { + currentNode = localRoot.left + } ?: return null while (true) { - if (currentNode != null) { - if (currentNode.right == null) return currentNode - else currentNode = currentNode.right - } else return null //(->1) + currentNode?.let { curNode -> + if (curNode.right == null) return currentNode + else currentNode = curNode.right + } ?: throw Exception("impossible case, or multithreading threads problem") //(->1) } } protected fun getNodeForReplace(localRoot: NodeType?): NodeType? { /* Behaviour: null - localRoot doesn't have children */ - if (localRoot != null) { - val replaceNode = getRightMinNode(localRoot) + localRoot?.let { + val replaceNode = getRightMinNode(it) if (replaceNode != null) return replaceNode - else return getLeftMaxNode(localRoot) - } else return null + else return getLeftMaxNode(it) + } ?: return null } protected fun findItem(obj: Pack): NodeType? { var currentNode = root - if (root == null) { - return null - } + if (root == null) return null + while (true) { if (obj == currentNode?.value) return currentNode else { - if (currentNode != null) { - if (obj > currentNode.value) currentNode = currentNode.right - else currentNode = currentNode.left - } else return null + currentNode?.let { + if (obj > it.value) currentNode = it.right + else currentNode = it.left + } ?: return null } } } - protected abstract fun insertItem(item: Pack): NodeType? + protected fun insertItem(item: Pack): NodeType { + val parentNode: NodeType? + val currentNode: NodeType + val updateNode: NodeType? = findItem(item) + + if (updateNode == null) { + parentNode = getLeafForInsert(item) + currentNode = createNode(item) + + linkNewNode(currentNode, parentNode) + + return currentNode + } + updateNode.value = item + return updateNode + } + + protected fun deleteItem(item: Pack): NodeType? { + /*TODO: test - deleteItem [невозможные ветки: - проверить, что они реально невозможны + (->1 & 2) => что невозможно достичь deleteNode = parentNode?. со значением null (потому что inRightOf/inLeftOf) + (->3) => странный кейс, когда удаляемое значение - null; такого вообще быть не должно + ] */ + val parentNode: NodeType? // parent node of the node for deleting + val deleteNode: NodeType? // node for deleting + var replaceNode: NodeType? // node for relink on the place deleted node + + if (findItem(item) != null) return null + + parentNode = getParentByValue(item) + if (parentNode != null) { + deleteNode = when { + item inRightOf parentNode -> parentNode.right //(->1) + item inLeftOf parentNode -> parentNode.left //(->2) + else -> null + } + } else deleteNode = root + if (deleteNode == null) throw NullPointerException("impossible case") // (->3) + + replaceNode = getNodeForReplace(deleteNode) + replaceNode?.let { + replaceNode = unLink(it, getParentByValue(it.value)) + } // if deleteNode doesn't have children => replaceNode = null - protected abstract fun deleteItem(item: Pack): NodeType? + if (parentNode != null) rebaseNode(deleteNode, parentNode, replaceNode) + else root = rebaseNode(deleteNode, null, replaceNode) + + return deleteNode + } protected abstract fun unLink( node: NodeType, @@ -131,14 +187,19 @@ abstract class TreeStruct, NodeType : Node replace value with item, otherwise create a new node */ //TODO test - find if (findItem(obj) == null) return null else return obj diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index 591a679..efa5b89 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -1,3 +1,4 @@ +//TODO нужны ли let? везде package treelib.binTree import treelib.abstractTree.TreeStruct @@ -13,19 +14,9 @@ class BINStruct> : TreeStruct>() { /*Behaviour: return - linked replaceNode*/ //TODO: rebaseNode - test if (replaceNode != null) { - replaceNode.left = node.left - node.left = null - replaceNode.right = node.right - node.right = null - } - if (parent != null){ - when{ - node.value inRightOf parent -> parent.right = replaceNode - node.value inLeftOf parent -> parent.left = replaceNode - else -> return null - } - } - return replaceNode + node.value = replaceNode.value + return node + } else return null } override fun unLink( @@ -33,7 +24,7 @@ class BINStruct> : TreeStruct>() { parent: BINNode? ): BINNode { /*Behaviour: - * 1) must be used with one or two children == null + * 1) must be used with [one or two] children == null * 2) return - Node without children */ /*TODO: unLink - test*/ val unLinkedNode: BINNode = node @@ -41,73 +32,36 @@ class BINStruct> : TreeStruct>() { when { (node.right != null) && (node.left != null) -> return node //means-error (in correct node input) - (node.right != null) -> childForLink = node.right - (node.left != null) -> childForLink = node.left + node.right != null -> childForLink = node.right + node.left != null -> childForLink = node.left else -> childForLink = null } unLinkedNode.left = null unLinkedNode.right = null - if (parent != null) { - when { - (node.value < parent.value) -> parent.right = childForLink - (node.value > parent.value) -> parent.left = childForLink - } + if (parent == null) return unLinkedNode + when { + (node.value < parent.value) -> parent.right = childForLink + (node.value > parent.value) -> parent.left = childForLink } return unLinkedNode } - override fun delete(item: Pack): Pack? = deleteItem(item)?.value - - override fun deleteItem(item: Pack): BINNode? { - /*return: null - error; value - deleted value*/ - /*TODO: test - deleteItem [невозможные ветки: - проверить, что они реально невозможны - (->1 & 2) => что невозможно достичь deleteNode = parentNode?. со значением null (потому что inRightOf/inLeftOf) - (->3) => странный кейс, когда удаляемое значение - null; такого вообще быть не должно - ] */ - val parentNode: BINNode? // parent node of the node for deleting - var replaceNode: BINNode? // node for relink on the place deleted node - var deleteNode: BINNode? = null // node for deleting - - if (findItem(item) != null) { - parentNode = getParentByValue(item) - when { - parentNode == null -> deleteNode = root - item inRightOf parentNode -> deleteNode = parentNode.right //(->1) - item inLeftOf parentNode -> deleteNode = parentNode.left //(->2) - } - if (deleteNode != null) { - replaceNode = getNodeForReplace(deleteNode) - if (replaceNode != null) { - replaceNode = unLink(replaceNode, getParentByValue(replaceNode.value)) - } else replaceNode = null // if deleteNode doesn't have children - - if (parentNode == null) root = rebaseNode(deleteNode, parentNode, replaceNode) - else rebaseNode(deleteNode, parentNode, replaceNode) - - return deleteNode - } else return null // (->3) - } else return null + override fun linkNewNode( + //TODO make an abstract? + node: BINNode, + parent: BINNode?, + ): BINNode { + if (parent == null) root = node + else { + if (node.value > parent.value) parent.right = node + else parent.left = node + } + return node } - override fun insert(item: Pack): Pack = insertItem(item).value - - override fun insertItem(item: Pack): BINNode { - val parentNode: BINNode? - val currentNode: BINNode - val updateNode: BINNode? = findItem(item) + override fun createNode(item: Pack) = BINNode(item) - if (updateNode == null) { - parentNode = getLeafForInsert(item) - currentNode = BINNode(value = item) - if (parentNode != null) { - if (item > parentNode.value) parentNode.right = currentNode - else parentNode.left = currentNode - } else root = currentNode + override fun delete(item: Pack): Pack? = deleteItem(item)?.value - return currentNode - } else { - updateNode.value = item - return updateNode - } - } + override fun insert(item: Pack): Pack = insertItem(item).value } \ No newline at end of file From d7e13de9b7c040ad8d2156f9d25947064eb0ab7a Mon Sep 17 00:00:00 2001 From: Georgy S Date: Thu, 30 Mar 2023 17:15:05 +0300 Subject: [PATCH 015/212] feat: Implement StateClasses for balancers interactions with TreeStructs --- lib/build.gradle.kts | 7 ++++++- .../kotlin/treelib/abstractTree/StateContainer.kt | 5 +++++ .../main/kotlin/treelib/avlTree/AVLStateContainer.kt | 9 +++++++++ .../main/kotlin/treelib/binTree/BINStateContainer.kt | 8 ++++++++ .../main/kotlin/treelib/rbTree/RBStateContainer.kt | 11 +++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt create mode 100644 lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt create mode 100644 lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt create mode 100644 lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 7767c9a..9dc4d3c 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -30,6 +30,8 @@ dependencies { // Use the Kotlin JUnit 5 integration. (TESTS support tools) testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testImplementation("org.junit.jupiter:junit-jupiter-params") + // Use the JUnit 5 integration. (TESTS support tools) testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") @@ -53,5 +55,8 @@ publishing { tasks.named("test") { // Use JUnit Platform for unit tests. - useJUnitPlatform() + useJUnitPlatform{ +// includeTags("fast") +// excludeTags("slow") + } } diff --git a/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt b/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt new file mode 100644 index 0000000..d2fdb06 --- /dev/null +++ b/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt @@ -0,0 +1,5 @@ +package treelib.abstractTree + +interface StateContainer, NodeType: Node> { + val contentNode: NodeType +} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt new file mode 100644 index 0000000..f46b163 --- /dev/null +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt @@ -0,0 +1,9 @@ +package treelib.avlTree + +import treelib.abstractTree.StateContainer + +/*Content node - the parent of the node on which the action was performed*/ +class AVLStateContainer>( + override val contentNode: AVLNode, + val root : AVLNode, +): StateContainer> \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt new file mode 100644 index 0000000..43e2e8f --- /dev/null +++ b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt @@ -0,0 +1,8 @@ +package treelib.binTree + +import treelib.abstractTree.StateContainer + +/*Content node - the node on which the action was performed*/ +class BINStateContainer>( + override val contentNode: BINNode, +): StateContainer> \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt new file mode 100644 index 0000000..fa64f0e --- /dev/null +++ b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt @@ -0,0 +1,11 @@ +package treelib.rbTree + +import treelib.abstractTree.StateContainer + +/* +Insert: contentNode - the node on which the action was performed +Delete: contentNode - the parent of the node on which the action was performed +* */ +class RBStateContainer>( + override val contentNode: RBNode, +): StateContainer> From 2163225e8ffd813a90238e153b2324d0a88ff3bb Mon Sep 17 00:00:00 2001 From: Georgy S Date: Fri, 31 Mar 2023 00:31:39 +0300 Subject: [PATCH 016/212] feat: Integrate StateContainers in whole architecture. --- .../treelib/abstractTree/StateContainer.kt | 4 +- .../main/kotlin/treelib/abstractTree/Tree.kt | 16 +++-- .../kotlin/treelib/abstractTree/TreeStruct.kt | 60 ++++++++++++------- .../balanced/BalancedTreeStruct.kt | 35 +++++------ .../treelib/abstractTree/balanced/Balancer.kt | 13 +++- .../abstractTree/balanced/BalancerNoParent.kt | 15 +++-- .../abstractTree/balanced/BalancerParent.kt | 15 +++-- .../kotlin/treelib/avlTree/AVLBalancer.kt | 8 ++- .../main/kotlin/treelib/avlTree/AVLNode.kt | 2 +- .../treelib/avlTree/AVLStateContainer.kt | 6 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 58 +++++++++--------- .../main/kotlin/treelib/avlTree/AVLTree.kt | 7 ++- .../main/kotlin/treelib/binTree/BINNode.kt | 2 +- .../treelib/binTree/BINStateContainer.kt | 4 +- .../main/kotlin/treelib/binTree/BINStruct.kt | 29 +++++++-- .../main/kotlin/treelib/binTree/BINTree.kt | 8 ++- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 8 ++- lib/src/main/kotlin/treelib/rbTree/RBNode.kt | 2 +- .../kotlin/treelib/rbTree/RBStateContainer.kt | 4 +- .../main/kotlin/treelib/rbTree/RBStruct.kt | 57 +++++++++--------- lib/src/main/kotlin/treelib/rbTree/RBTree.kt | 8 ++- .../kotlin/treelib/singleObjects/Container.kt | 2 +- .../kotlin/treelib/singleObjects/Markers.kt | 2 +- .../test/kotlin/treelib/TreeStructWrapper.kt | 3 +- .../test/kotlin/treelib/TreeStructsTest.kt | 9 ++- 25 files changed, 223 insertions(+), 154 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt b/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt index d2fdb06..378a4c0 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt @@ -1,5 +1,5 @@ package treelib.abstractTree -interface StateContainer, NodeType: Node> { +interface StateContainer, NodeType : Node> { val contentNode: NodeType -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index b5684d6..03cd062 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -2,10 +2,16 @@ package treelib.abstractTree import treelib.singleObjects.Container -abstract class Tree, Value, NodeType: Node, NodeType>> { - protected abstract val treeStruct: TreeStruct, NodeType> +abstract class Tree< + Key : Comparable, + Value, + NodeType : Node, NodeType>, + State : StateContainer, NodeType> + > { - fun putItem(item: Pair){ + protected abstract val treeStruct: TreeStruct, NodeType, State> + + fun putItem(item: Pair) { // TODO("Not yet implemented") } @@ -13,7 +19,7 @@ abstract class Tree, Value, NodeType: Node, Value, NodeType: Node, NodeType : Node> { +abstract class TreeStruct< + Pack : Comparable, + NodeType : Node, + State : StateContainer + > { protected abstract var root: NodeType? - protected fun getLeafForInsert(item: Pack): NodeType? { + private fun getLeafForInsert(item: Pack): NodeType? { //TODO test - getParentByValue; [не нравится, что currentNode = it.right не требует проверки на null] var currentNode: NodeType? = root ?: return null @@ -23,7 +25,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node { if (it.right?.value == item) return currentNode - else throw Exception("impossible case, or multithreading threads problem") + else throw Exception("Impossible case, or multithreading threads problem") } it.left != null -> { if (it.left?.value == item) return currentNode - else throw Exception("impossible case, or multithreading threads problem") + else throw Exception("Impossible case, or multithreading threads problem") } } if (item > it.value) currentNode = it.right @@ -56,7 +58,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node, NodeType : Node if (curNode.left == null) return currentNode else currentNode = curNode.left - } ?: throw Exception("impossible case, or multithreading threads problem") //(->1) + } ?: throw Exception("Impossible case, or multithreading threads problem") //(->1) } } @@ -104,11 +106,11 @@ abstract class TreeStruct, NodeType : Node if (curNode.right == null) return currentNode else currentNode = curNode.right - } ?: throw Exception("impossible case, or multithreading threads problem") //(->1) + } ?: throw Exception("Impossible case, or multithreading threads problem") //(->1) } } - protected fun getNodeForReplace(localRoot: NodeType?): NodeType? { + private fun getNodeForReplace(localRoot: NodeType?): NodeType? { /* Behaviour: null - localRoot doesn't have children */ localRoot?.let { val replaceNode = getRightMinNode(it) @@ -117,12 +119,12 @@ abstract class TreeStruct, NodeType : Node it.value) currentNode = it.right @@ -132,10 +134,10 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node1 & 2) => что невозможно достичь deleteNode = parentNode?. со значением null (потому что inRightOf/inLeftOf) (->3) => странный кейс, когда удаляемое значение - null; такого вообще быть не должно @@ -157,6 +160,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node replaceNode = null + state = generateStateDelete(deleteNode, parentNode) + if (parentNode != null) rebaseNode(deleteNode, parentNode, replaceNode) else root = rebaseNode(deleteNode, null, replaceNode) - return deleteNode + return state } + protected abstract fun generateStateFind(foundNode: NodeType): State + + protected abstract fun generateStateInsert( + insertedNodeType: NodeType, + itsParent: NodeType?, + ): State + + protected abstract fun generateStateDelete( + deletedNodeType: NodeType, + itsParent: NodeType?, + ): State + protected abstract fun unLink( node: NodeType, - parent: NodeType? + parent: NodeType?, ): NodeType protected abstract fun rebaseNode( diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt index 7e636a8..1e3a1d7 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt @@ -1,41 +1,34 @@ package treelib.abstractTree.balanced import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct -abstract class BalancedTreeStruct, NodeType : Node, BalancerType : Balancer> : - TreeStruct() { +abstract class BalancedTreeStruct< + Pack : Comparable, + NodeType : Node, + State : StateContainer, + BalancerType : Balancer, + > : TreeStruct() { protected abstract val balancer: BalancerType - protected fun getLeafForInsert(item: Pack): NodeType? { - var currentNode = root - while (true) { - if (currentNode != null) { - if (item > currentNode.value) { - if (currentNode.right != null) currentNode = currentNode.right - else return currentNode - } else { - if (currentNode.left != null) currentNode = currentNode.left - else return currentNode - } - } else return null - } - } - override fun insert(item: Pack): Pack? { val currenNode = insertItem(item) if (currenNode != null) { root = balancer.balance(currenNode) - return item - } else return null + } + return item } -// если удалил рут, то не запускаем баланс + + // если удалил рут, то не запускаем баланс override fun delete(item: Pack): Pack? { + // TODO передаю родителя + что делать если: root null, root заменил я (а не Артем внутри) + // TODO если вернулся рут, то нифига не запускать val currenNode = deleteItem(item) if (currenNode != null) { root = balancer.balance(currenNode) return item } else return null } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt index dba1741..c76d677 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt @@ -1,11 +1,18 @@ package treelib.abstractTree.balanced import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer + +interface Balancer< + Pack : Comparable, + NodeType : Node, + State : StateContainer, + > { -interface Balancer, NodeType : Node> { fun rightRotate(currentNode: NodeType): NodeType fun leftRotate(currentNode: NodeType): NodeType - fun balance(node: NodeType): NodeType -} \ No newline at end of file + fun balance(state: State): NodeType +// TODO что возвращает балансер? +} diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt index 0dd8d62..03741d6 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt @@ -1,14 +1,19 @@ package treelib.abstractTree.balanced import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer + +abstract class BalancerNoParent< + Pack : Comparable, + NodeType : Node, + State : StateContainer, + >(val root: NodeType?) : Balancer { -abstract class BalancerNoParent, NodeType : Node>(val root: NodeType?) : - Balancer { override fun rightRotate(currentNode: NodeType): NodeType { - TODO("Not yet implemented") + TODO("rightRotate - Not yet implemented") } override fun leftRotate(currentNode: NodeType): NodeType { - TODO("Not yet implemented") + TODO("leftRotate - Not yet implemented") } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt index 74a0c6f..1a6a638 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt @@ -1,14 +1,19 @@ package treelib.abstractTree.balanced import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer + +abstract class BalancerParent< + Pack : Comparable, + NodeType : Node, + State : StateContainer + >(val root: NodeType?) : Balancer { -abstract class BalancerParent, NodeType : Node>(val root: NodeType?) : - Balancer { override fun rightRotate(currentNode: NodeType): NodeType { - TODO("Not yet implemented") + TODO("rightRotate - Not yet implemented") } override fun leftRotate(currentNode: NodeType): NodeType { - TODO("Not yet implemented") + TODO("leftRotate - Not yet implemented") } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 201cad5..7e892d0 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -2,8 +2,10 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancerNoParent -class AVLBalancer>(root: AVLNode?) : BalancerNoParent>(root) { - override fun balance(node: AVLNode): AVLNode { +class AVLBalancer>(root: AVLNode?) : + BalancerNoParent, AVLStateContainer>(root) { + + override fun balance(state: AVLStateContainer): AVLNode { TODO("Not yet implemented") } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt b/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt index 5f58f0d..bcbffc1 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt @@ -7,4 +7,4 @@ class AVLNode>( override var left: AVLNode? = null, override var right: AVLNode? = null, var height: UInt = 1U, -) : Node> \ No newline at end of file +) : Node> diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt index f46b163..2bd3658 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt @@ -3,7 +3,7 @@ package treelib.avlTree import treelib.abstractTree.StateContainer /*Content node - the parent of the node on which the action was performed*/ -class AVLStateContainer>( +class AVLStateContainer>( override val contentNode: AVLNode, - val root : AVLNode, -): StateContainer> \ No newline at end of file + val root: AVLNode, +) : StateContainer> diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index 99c1951..e1050c3 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -2,41 +2,45 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancedTreeStruct -class AVLStruct> : BalancedTreeStruct, AVLBalancer>() { +class AVLStruct> : + BalancedTreeStruct, AVLStateContainer, AVLBalancer>() { + override var root: AVLNode? = null - override val balancer = AVLBalancer(root) + override fun generateStateDelete( + deletedNodeType: AVLNode, + itsParent: AVLNode? + ): AVLStateContainer { + TODO("Not yet implemented") + } - override fun rebaseNode(node: AVLNode, parent: AVLNode?, replaceNode: AVLNode?): AVLNode? { - TODO("Not yet implemented rebaseNode") + override fun generateStateInsert( + insertedNodeType: AVLNode, + itsParent: AVLNode? + ): AVLStateContainer { + TODO("Not yet implemented") } - override fun unLink(node: AVLNode, parent: AVLNode?): AVLNode { - TODO("Not yet implemented unLink") + override fun generateStateFind(foundNode: AVLNode): AVLStateContainer { + TODO("Not yet implemented") + } + + override val balancer = AVLBalancer(root) + + + override fun createNode(item: Pack): AVLNode { + TODO("Not yet implemented") } - override fun deleteItem(item: Pack): AVLNode { - // возвращать того, от кого начинается балансировка. + override fun linkNewNode(node: AVLNode, parent: AVLNode?): AVLNode { TODO("Not yet implemented") } - override fun insertItem(item: Pack): AVLNode { - val parentNode: AVLNode? - val currentNode: AVLNode - val updateNode: AVLNode? = findItem(item) - - if (updateNode == null) { - parentNode = getLeafForInsert(item) - currentNode = AVLNode(value = item) - if (parentNode != null) { - if (item > parentNode.value) parentNode.right = currentNode - else parentNode.left = currentNode - } else root = currentNode - - return currentNode - } else { - updateNode.value = item - return updateNode - } + override fun rebaseNode(node: AVLNode, parent: AVLNode?, replaceNode: AVLNode?): AVLNode? { + TODO("Not yet implemented rebaseNode") + } + + override fun unLink(node: AVLNode, parent: AVLNode?): AVLNode { + TODO("Not yet implemented unLink") } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt index a83d8a2..4ad338e 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt @@ -5,6 +5,7 @@ import treelib.abstractTree.Tree import treelib.singleObjects.Container class AVLTree, Value> : - Tree>>() { - override val treeStruct: AVLStruct> = AVLStruct() -} \ No newline at end of file + Tree>, AVLStateContainer>>() { + + override val treeStruct = AVLStruct>() +} diff --git a/lib/src/main/kotlin/treelib/binTree/BINNode.kt b/lib/src/main/kotlin/treelib/binTree/BINNode.kt index d330529..5b764ff 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINNode.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINNode.kt @@ -6,4 +6,4 @@ class BINNode>( override var value: Pack, override var left: BINNode? = null, override var right: BINNode? = null, -) : Node> \ No newline at end of file +) : Node> diff --git a/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt index 43e2e8f..2a7fd7b 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt @@ -3,6 +3,6 @@ package treelib.binTree import treelib.abstractTree.StateContainer /*Content node - the node on which the action was performed*/ -class BINStateContainer>( +class BINStateContainer>( override val contentNode: BINNode, -): StateContainer> \ No newline at end of file +) : StateContainer> diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index efa5b89..9f4d6e9 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -3,9 +3,29 @@ package treelib.binTree import treelib.abstractTree.TreeStruct -class BINStruct> : TreeStruct>() { +class BINStruct> : + TreeStruct, BINStateContainer>() { + override var root: BINNode? = null + override fun generateStateDelete( + deletedNodeType: BINNode, + itsParent: BINNode? + ): BINStateContainer { + TODO("Not yet implemented") + } + + override fun generateStateInsert( + insertedNodeType: BINNode, + itsParent: BINNode? + ): BINStateContainer { + TODO("Not yet implemented") + } + + override fun generateStateFind(foundNode: BINNode): BINStateContainer { + TODO("Not yet implemented") + } + override fun rebaseNode( node: BINNode, parent: BINNode?, @@ -29,7 +49,6 @@ class BINStruct> : TreeStruct>() { /*TODO: unLink - test*/ val unLinkedNode: BINNode = node val childForLink: BINNode? - when { (node.right != null) && (node.left != null) -> return node //means-error (in correct node input) node.right != null -> childForLink = node.right @@ -61,7 +80,7 @@ class BINStruct> : TreeStruct>() { override fun createNode(item: Pack) = BINNode(item) - override fun delete(item: Pack): Pack? = deleteItem(item)?.value + override fun delete(item: Pack): Pack? = deleteItem(item)?.contentNode?.value - override fun insert(item: Pack): Pack = insertItem(item).value -} \ No newline at end of file + override fun insert(item: Pack): Pack? = insertItem(item)?.contentNode?.value +} diff --git a/lib/src/main/kotlin/treelib/binTree/BINTree.kt b/lib/src/main/kotlin/treelib/binTree/BINTree.kt index 9d0f4b3..ed24d26 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINTree.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINTree.kt @@ -3,6 +3,8 @@ package treelib.binTree import treelib.abstractTree.Tree import treelib.singleObjects.Container -class BINTree, Value> : Tree>>() { - override val treeStruct: BINStruct> = BINStruct() -} \ No newline at end of file +class BINTree, Value> + : Tree>, BINStateContainer>>() { + + override val treeStruct = BINStruct>() +} diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index e86ea0f..f7f24f8 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -2,8 +2,10 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent -class RBBalancer>(root: RBNode?) : BalancerParent>(root) { - override fun balance(node: RBNode): RBNode { +class RBBalancer>(root: RBNode?) : + BalancerParent, RBStateContainer>(root) { + + override fun balance(state: RBStateContainer): RBNode { TODO("Not yet implemented") } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/rbTree/RBNode.kt b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt index 3453f69..c50358e 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBNode.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt @@ -9,4 +9,4 @@ class RBNode>( override var right: RBNode? = null, var parent: RBNode? = null, var color: Markers = Markers.RED, -) : Node> \ No newline at end of file +) : Node> diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt index fa64f0e..b121ec4 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt @@ -6,6 +6,6 @@ import treelib.abstractTree.StateContainer Insert: contentNode - the node on which the action was performed Delete: contentNode - the parent of the node on which the action was performed * */ -class RBStateContainer>( +class RBStateContainer>( override val contentNode: RBNode, -): StateContainer> +) : StateContainer> diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 49d8fff..d01d2c8 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -2,45 +2,44 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancedTreeStruct -class RBStruct> : BalancedTreeStruct, RBBalancer>() { +class RBStruct> : + BalancedTreeStruct, RBStateContainer, RBBalancer>() { + override var root: RBNode? = null + override fun generateStateDelete( + deletedNodeType: RBNode, + itsParent: RBNode? + ): RBStateContainer { + TODO("Not yet implemented") + } + + override fun generateStateInsert( + insertedNodeType: RBNode, + itsParent: RBNode? + ): RBStateContainer { + TODO("Not yet implemented") + } + + override fun generateStateFind(foundNode: RBNode): RBStateContainer { + TODO("Not yet implemented") + } + override val balancer = RBBalancer(root) - override fun rebaseNode(node: RBNode, parent: RBNode?, replaceNode: RBNode?): RBNode? { + override fun createNode(item: Pack): RBNode { TODO("Not yet implemented") } - override fun unLink(node: RBNode, parent: RBNode?): RBNode { + override fun linkNewNode(node: RBNode, parent: RBNode?): RBNode { TODO("Not yet implemented") } - override fun deleteItem(item: Pack): RBNode? { - if (find(item) == null) { - TODO("Not yet implemented") - } else return null + override fun rebaseNode(node: RBNode, parent: RBNode?, replaceNode: RBNode?): RBNode? { + TODO("Not yet implemented") } -// передавать того, от кого начинается балансировка - override fun insertItem(item: Pack): RBNode? { - val parentNode: RBNode? - val currentNode: RBNode - val updateNode: RBNode? = findItem(item) - - if (updateNode == null) { - parentNode = getLeafForInsert(item) - currentNode = RBNode(value = item, parent = parentNode) - if (parentNode != null) { - if (item > parentNode.value) parentNode.right = currentNode - else parentNode.left = currentNode - } else { - root = RBNode(value = item) - return root - } - return currentNode - } else { - updateNode.value = item - return updateNode - } + override fun unLink(node: RBNode, parent: RBNode?): RBNode { + TODO("Not yet implemented") } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt index 6b6c149..7299d06 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt @@ -4,6 +4,8 @@ import treelib.abstractTree.Tree import treelib.singleObjects.Container -class RBTree, Value> : Tree>>() { - override val treeStruct: RBStruct> = RBStruct() -} \ No newline at end of file +class RBTree, Value> : + Tree>, RBStateContainer>>() { + + override val treeStruct = RBStruct>() +} diff --git a/lib/src/main/kotlin/treelib/singleObjects/Container.kt b/lib/src/main/kotlin/treelib/singleObjects/Container.kt index 9634eb1..9bac7a0 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/Container.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/Container.kt @@ -21,4 +21,4 @@ class Container, V>(private val pair: Pair) : Comparabl override fun compareTo(other: Container): Int { return compareValuesBy(this, other) { it.pair.first } } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/singleObjects/Markers.kt b/lib/src/main/kotlin/treelib/singleObjects/Markers.kt index 3e4e190..6ac4afd 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/Markers.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/Markers.kt @@ -2,4 +2,4 @@ package treelib.singleObjects enum class Markers { RED, BLACK -} \ No newline at end of file +} diff --git a/lib/src/test/kotlin/treelib/TreeStructWrapper.kt b/lib/src/test/kotlin/treelib/TreeStructWrapper.kt index c19d193..88870ec 100644 --- a/lib/src/test/kotlin/treelib/TreeStructWrapper.kt +++ b/lib/src/test/kotlin/treelib/TreeStructWrapper.kt @@ -1,9 +1,10 @@ package treelib import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct -class TreeStructWrapper, NodeType : Node, TStruct : TreeStruct> { +class TreeStructWrapper, NodeType : Node, State: StateContainer, TStruct : TreeStruct> { fun getPrivateNode(tree: TStruct, name: String = "root"): NodeType? { val field = tree.javaClass.getDeclaredField(name) diff --git a/lib/src/test/kotlin/treelib/TreeStructsTest.kt b/lib/src/test/kotlin/treelib/TreeStructsTest.kt index a64d2a0..30c7911 100644 --- a/lib/src/test/kotlin/treelib/TreeStructsTest.kt +++ b/lib/src/test/kotlin/treelib/TreeStructsTest.kt @@ -9,10 +9,13 @@ 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") @@ -21,7 +24,7 @@ class TreeStructsTest { @Nested @DisplayName("Test: Binary Search Tree Struct") inner class BINStructTest { - val treeW = TreeStructWrapper, BINStruct>() + val treeW = TreeStructWrapper, BINStateContainer, BINStruct>() var classUnderTest = BINStruct() @BeforeEach @@ -33,7 +36,7 @@ class TreeStructsTest { @Nested @DisplayName("Test: AVL Struct") inner class AVLStructTest { - val treeW = TreeStructWrapper, AVLStruct>() + val treeW = TreeStructWrapper, AVLStateContainer, AVLStruct>() var classUnderTest = AVLStruct() @BeforeEach @@ -45,7 +48,7 @@ class TreeStructsTest { @Nested @DisplayName("Test: Red-Black Tree Struct") inner class RBStructTree { - val treeW = TreeStructWrapper, RBStruct>() + val treeW = TreeStructWrapper, RBStateContainer,RBStruct>() var classUnderTest = RBStruct() @BeforeEach From 09b9dac2a06ed1a0f72ffd2191ed5c3e480d59f0 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Fri, 31 Mar 2023 17:55:17 +0300 Subject: [PATCH 017/212] feat: Implement ParentNode interface and BINStruct --- .../kotlin/treelib/abstractTree/NodeParent.kt | 7 +++ .../treelib/abstractTree/StateContainer.kt | 2 +- .../main/kotlin/treelib/abstractTree/Tree.kt | 2 +- .../kotlin/treelib/abstractTree/TreeStruct.kt | 48 ++++++++----------- .../balanced/BalancedTreeStruct.kt | 12 ++--- .../abstractTree/balanced/BalancerParent.kt | 4 +- .../treelib/avlTree/AVLStateContainer.kt | 4 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 6 +-- .../treelib/binTree/BINStateContainer.kt | 2 +- .../main/kotlin/treelib/binTree/BINStruct.kt | 22 +++------ lib/src/main/kotlin/treelib/rbTree/RBNode.kt | 6 +-- .../kotlin/treelib/rbTree/RBStateContainer.kt | 2 +- .../main/kotlin/treelib/rbTree/RBStruct.kt | 6 +-- .../kotlin/treelib/singleObjects/Container.kt | 6 +-- 14 files changed, 59 insertions(+), 70 deletions(-) create mode 100644 lib/src/main/kotlin/treelib/abstractTree/NodeParent.kt diff --git a/lib/src/main/kotlin/treelib/abstractTree/NodeParent.kt b/lib/src/main/kotlin/treelib/abstractTree/NodeParent.kt new file mode 100644 index 0000000..4d8c046 --- /dev/null +++ b/lib/src/main/kotlin/treelib/abstractTree/NodeParent.kt @@ -0,0 +1,7 @@ +package treelib.abstractTree + +interface NodeParent, SubNode : NodeParent> : Node { + var parent: SubNode? + override var right: SubNode? + override var left: SubNode? +} diff --git a/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt b/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt index 378a4c0..b835879 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt @@ -1,5 +1,5 @@ package treelib.abstractTree interface StateContainer, NodeType : Node> { - val contentNode: NodeType + val contentNode: NodeType? } diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index 03cd062..461c112 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -12,7 +12,7 @@ abstract class Tree< protected abstract val treeStruct: TreeStruct, NodeType, State> fun putItem(item: Pair) { -// TODO("Not yet implemented") + TODO("Not yet implemented") } fun putItems(vararg items: Pair) { diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index e305ece..176f688 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -1,14 +1,10 @@ package treelib.abstractTree -abstract class TreeStruct< - Pack : Comparable, - NodeType : Node, - State : StateContainer - > { +abstract class TreeStruct, NodeType : Node, State : StateContainer> { protected abstract var root: NodeType? - private fun getLeafForInsert(item: Pack): NodeType? { + protected fun getLeafForInsert(item: Pack): NodeType? { //TODO test - getParentByValue; [не нравится, что currentNode = it.right не требует проверки на null] var currentNode: NodeType? = root ?: return null @@ -25,7 +21,7 @@ abstract class TreeStruct< } } - private fun getParentByValue(item: Pack): NodeType? { + protected fun getParentByValue(item: Pack): NodeType? { //TODO test - getParentByValue [что вообще корректно отрабатывает] /* (1) - shouldn't be used with 'root == null' otherwise - incorrect behavior @@ -34,7 +30,7 @@ abstract class TreeStruct< */ var currentNode = root - if (findItem(item) == null) throw Exception("getParentByValue shouldn't be used with a value doesn't exist in the tree")// (2) + if (findItem(item).contentNode == null) throw Exception("getParentByValue shouldn't be used with a value doesn't exist in the tree")// (2) if ((currentNode != null) && (currentNode.value == item)) return null @@ -58,7 +54,7 @@ abstract class TreeStruct< } } - private infix fun Pack.inRightOf(node: NodeType?): Boolean { + protected infix fun Pack.inRightOf(node: NodeType?): Boolean { //TODO - test inInRightOf if ((node == null) || (node.right == null)) return false node.right?.let { @@ -67,7 +63,7 @@ abstract class TreeStruct< return false } - private infix fun Pack.inLeftOf(node: NodeType?): Boolean { + protected infix fun Pack.inLeftOf(node: NodeType?): Boolean { //TODO - test inInLeftOf if ((node == null) || (node.left == null)) return false node.left?.let { @@ -76,8 +72,7 @@ abstract class TreeStruct< return false } - private fun getRightMinNode(localRoot: NodeType): NodeType? { - /* null - means NodeType.right doesn't exist, another variant impossible */ + protected fun getRightMinNode(localRoot: NodeType): NodeType? {/* null - means NodeType.right doesn't exist, another variant impossible */ //TODO test - getLeafForDelete [проверить, что нет проблем с (->1)] var currentNode: NodeType? = null @@ -93,7 +88,7 @@ abstract class TreeStruct< } } - private fun getLeftMaxNode(localRoot: NodeType): NodeType? { + protected fun getLeftMaxNode(localRoot: NodeType): NodeType? { /* null - means NodeType.left doesn't exist, another variant impossible */ //TODO test - getLeafLeftMax [проверить, что нет проблем с (->1)] var currentNode: NodeType? = null @@ -110,8 +105,7 @@ abstract class TreeStruct< } } - private fun getNodeForReplace(localRoot: NodeType?): NodeType? { - /* Behaviour: null - localRoot doesn't have children */ + protected fun getNodeForReplace(localRoot: NodeType?): NodeType? {/* Behaviour: null - localRoot doesn't have children */ localRoot?.let { val replaceNode = getRightMinNode(it) if (replaceNode != null) return replaceNode @@ -119,9 +113,9 @@ abstract class TreeStruct< } ?: return null } - protected fun findItem(obj: Pack): State? { + protected fun findItem(obj: Pack): State { var currentNode = root - if (root == null) return null + if (root == null) return generateStateFind(null) while (true) { if (obj == currentNode?.value) return generateStateFind(currentNode) @@ -129,15 +123,15 @@ abstract class TreeStruct< currentNode?.let { if (obj > it.value) currentNode = it.right else currentNode = it.left - } ?: return null + } ?: return generateStateFind(null) } } } - protected fun insertItem(item: Pack): State? { + protected fun insertItem(item: Pack): State {/* Behaviour: if updateNode != null => return null */ val parentNode: NodeType? val currentNode: NodeType - val updateNode: NodeType? = findItem(item)?.contentNode + val updateNode: NodeType? = findItem(item).contentNode if (updateNode == null) { parentNode = getLeafForInsert(item) @@ -149,10 +143,10 @@ abstract class TreeStruct< } updateNode.value = item - return null + return generateStateInsert(null, null) } - protected fun deleteItem(item: Pack): State? { + protected fun deleteItem(item: Pack): State { /*TODO: test - deleteItem [невозможные ветки: - проверить, что они реально невозможны (->1 & 2) => что невозможно достичь deleteNode = parentNode?. со значением null (потому что inRightOf/inLeftOf) (->3) => странный кейс, когда удаляемое значение - null; такого вообще быть не должно @@ -162,7 +156,7 @@ abstract class TreeStruct< var replaceNode: NodeType? // node for relink on the place deleted node val state: State - if (findItem(item) != null) return null + if (findItem(item).contentNode != null) return generateStateDelete(null, null) parentNode = getParentByValue(item) if (parentNode != null) { @@ -187,15 +181,15 @@ abstract class TreeStruct< return state } - protected abstract fun generateStateFind(foundNode: NodeType): State + protected abstract fun generateStateFind(foundNode: NodeType?): State protected abstract fun generateStateInsert( - insertedNodeType: NodeType, + insertedNode: NodeType?, itsParent: NodeType?, ): State protected abstract fun generateStateDelete( - deletedNodeType: NodeType, + deletedNode: NodeType?, itsParent: NodeType?, ): State @@ -219,7 +213,7 @@ abstract class TreeStruct< fun find(obj: Pack): Pack? { //TODO test - find - if (findItem(obj) == null) return null + if (findItem(obj).contentNode == null) return null else return obj } diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt index 1e3a1d7..3e20aef 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt @@ -14,9 +14,9 @@ abstract class BalancedTreeStruct< protected abstract val balancer: BalancerType override fun insert(item: Pack): Pack? { - val currenNode = insertItem(item) - if (currenNode != null) { - root = balancer.balance(currenNode) + val currentState = insertItem(item) + if (currentState.contentNode != null) { + root = balancer.balance(currentState) } return item } @@ -25,9 +25,9 @@ abstract class BalancedTreeStruct< override fun delete(item: Pack): Pack? { // TODO передаю родителя + что делать если: root null, root заменил я (а не Артем внутри) // TODO если вернулся рут, то нифига не запускать - val currenNode = deleteItem(item) - if (currenNode != null) { - root = balancer.balance(currenNode) + val currentState = deleteItem(item) + if (currentState.contentNode != null) { + root = balancer.balance(currentState) return item } else return null } diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt index 1a6a638..129aab5 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt @@ -1,11 +1,11 @@ package treelib.abstractTree.balanced -import treelib.abstractTree.Node +import treelib.abstractTree.NodeParent import treelib.abstractTree.StateContainer abstract class BalancerParent< Pack : Comparable, - NodeType : Node, + NodeType : NodeParent, State : StateContainer >(val root: NodeType?) : Balancer { diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt index 2bd3658..df296f3 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt @@ -4,6 +4,6 @@ import treelib.abstractTree.StateContainer /*Content node - the parent of the node on which the action was performed*/ class AVLStateContainer>( - override val contentNode: AVLNode, - val root: AVLNode, + override val contentNode: AVLNode?, + val root: AVLNode?, ) : StateContainer> diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index e1050c3..2c0a30a 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -8,20 +8,20 @@ class AVLStruct> : override var root: AVLNode? = null override fun generateStateDelete( - deletedNodeType: AVLNode, + deletedNode: AVLNode?, itsParent: AVLNode? ): AVLStateContainer { TODO("Not yet implemented") } override fun generateStateInsert( - insertedNodeType: AVLNode, + insertedNode: AVLNode?, itsParent: AVLNode? ): AVLStateContainer { TODO("Not yet implemented") } - override fun generateStateFind(foundNode: AVLNode): AVLStateContainer { + override fun generateStateFind(foundNode: AVLNode?): AVLStateContainer { TODO("Not yet implemented") } diff --git a/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt index 2a7fd7b..fb51ed8 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt @@ -4,5 +4,5 @@ import treelib.abstractTree.StateContainer /*Content node - the node on which the action was performed*/ class BINStateContainer>( - override val contentNode: BINNode, + override val contentNode: BINNode?, ) : StateContainer> diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index 9f4d6e9..3157b6a 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -1,4 +1,3 @@ -//TODO нужны ли let? везде package treelib.binTree import treelib.abstractTree.TreeStruct @@ -9,22 +8,16 @@ class BINStruct> : override var root: BINNode? = null override fun generateStateDelete( - deletedNodeType: BINNode, + deletedNode: BINNode?, itsParent: BINNode? - ): BINStateContainer { - TODO("Not yet implemented") - } + ): BINStateContainer = BINStateContainer(deletedNode) override fun generateStateInsert( - insertedNodeType: BINNode, + insertedNode: BINNode?, itsParent: BINNode? - ): BINStateContainer { - TODO("Not yet implemented") - } + ): BINStateContainer = BINStateContainer(insertedNode) - override fun generateStateFind(foundNode: BINNode): BINStateContainer { - TODO("Not yet implemented") - } + override fun generateStateFind(foundNode: BINNode?): BINStateContainer = BINStateContainer(foundNode) override fun rebaseNode( node: BINNode, @@ -66,7 +59,6 @@ class BINStruct> : } override fun linkNewNode( - //TODO make an abstract? node: BINNode, parent: BINNode?, ): BINNode { @@ -80,7 +72,7 @@ class BINStruct> : override fun createNode(item: Pack) = BINNode(item) - override fun delete(item: Pack): Pack? = deleteItem(item)?.contentNode?.value + override fun delete(item: Pack): Pack? = deleteItem(item).contentNode?.value - override fun insert(item: Pack): Pack? = insertItem(item)?.contentNode?.value + override fun insert(item: Pack): Pack? = insertItem(item).contentNode?.value } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBNode.kt b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt index c50358e..1a60b26 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBNode.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt @@ -1,12 +1,12 @@ package treelib.rbTree -import treelib.abstractTree.Node +import treelib.abstractTree.NodeParent import treelib.singleObjects.Markers class RBNode>( override var value: Pack, override var left: RBNode? = null, override var right: RBNode? = null, - var parent: RBNode? = null, + override var parent: RBNode? = null, var color: Markers = Markers.RED, -) : Node> +) : NodeParent> diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt index b121ec4..7ec6200 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt @@ -7,5 +7,5 @@ Insert: contentNode - the node on which the action was performed Delete: contentNode - the parent of the node on which the action was performed * */ class RBStateContainer>( - override val contentNode: RBNode, + override val contentNode: RBNode?, ) : StateContainer> diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index d01d2c8..b34d59f 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -8,20 +8,20 @@ class RBStruct> : override var root: RBNode? = null override fun generateStateDelete( - deletedNodeType: RBNode, + deletedNode: RBNode?, itsParent: RBNode? ): RBStateContainer { TODO("Not yet implemented") } override fun generateStateInsert( - insertedNodeType: RBNode, + insertedNode: RBNode?, itsParent: RBNode? ): RBStateContainer { TODO("Not yet implemented") } - override fun generateStateFind(foundNode: RBNode): RBStateContainer { + override fun generateStateFind(foundNode: RBNode?): RBStateContainer { TODO("Not yet implemented") } diff --git a/lib/src/main/kotlin/treelib/singleObjects/Container.kt b/lib/src/main/kotlin/treelib/singleObjects/Container.kt index 9bac7a0..72e865c 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/Container.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/Container.kt @@ -7,11 +7,7 @@ class Container, V>(private val pair: Pair) : Comparabl return true other as Container<*, *> - if (this.pair.first == other.pair.first) { - return true - } - - return false + return this.pair.first == other.pair.first } override fun hashCode(): Int { From 162a899cb21e686ca2b911d34607133a1a1015e2 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Fri, 31 Mar 2023 20:27:17 +0300 Subject: [PATCH 018/212] fix: Behaviour of deleteItem --- lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt | 4 ++-- lib/src/main/kotlin/treelib/binTree/BINStruct.kt | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 176f688..8bec7d6 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -163,10 +163,10 @@ abstract class TreeStruct, NodeType : Node parentNode.right //(->1) item inLeftOf parentNode -> parentNode.left //(->2) - else -> null + else -> throw Exception("Impossible case in deleteItem (is turned out to be possible)") } } else deleteNode = root - if (deleteNode == null) throw NullPointerException("impossible case") // (->3) + if (deleteNode == null) throw Exception("Impossible case in deleteItem (is turned out to be possible)") // (->3) replaceNode = getNodeForReplace(deleteNode) replaceNode?.let { diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index 3157b6a..d36f642 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -36,14 +36,12 @@ class BINStruct> : node: BINNode, parent: BINNode? ): BINNode { - /*Behaviour: - * 1) must be used with [one or two] children == null - * 2) return - Node without children */ + /*Behaviour: return - Node without children */ /*TODO: unLink - test*/ val unLinkedNode: BINNode = node val childForLink: BINNode? when { - (node.right != null) && (node.left != null) -> return node //means-error (in correct node input) + (node.right != null) && (node.left != null) -> throw Exception("unLink - method Shouldn't be used with node with both children") node.right != null -> childForLink = node.right node.left != null -> childForLink = node.left else -> childForLink = null From bb969c0ece51a43bd8647e3fa2e2365e3f0da1d0 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 1 Apr 2023 13:48:15 +0300 Subject: [PATCH 019/212] fix: Rewrite protected method rebaseNode for BINStruct; Rewrite return values of abstract methods: insertItem, deleteItem, findItem --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 88 ++++++++++--------- .../balanced/BalancedTreeStruct.kt | 12 +-- .../treelib/avlTree/AVLStateContainer.kt | 8 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 18 ++-- .../treelib/binTree/BINStateContainer.kt | 6 +- .../main/kotlin/treelib/binTree/BINStruct.kt | 40 ++++++--- .../kotlin/treelib/rbTree/RBStateContainer.kt | 6 +- .../main/kotlin/treelib/rbTree/RBStruct.kt | 25 +++--- 8 files changed, 119 insertions(+), 84 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 8bec7d6..3e37eb6 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -10,14 +10,20 @@ abstract class TreeStruct, NodeType : Node it.value) { - if (it.right == null) return currentNode - currentNode = it.right - } else { - if (it.left == null) return currentNode - currentNode = it.left + when { + item > it.value -> { + if (it.right == null) return currentNode + currentNode = it.right + } + + item < it.value -> { + if (it.left == null) return currentNode + currentNode = it.left + } + + else -> throw Exception("getLeafForInsert shouldn't be used with a value exists in Struct") } - } ?: throw Exception("Impossible case or multithreading problem ") + } ?: throw Exception("Impossible case or multithreading problem") } } @@ -113,6 +119,11 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node return null */ + protected abstract fun generateStateInsert( + insertNode: NodeType?, + contentNode: NodeType? = null, + ): State + + protected fun insertItem(item: Pack): State { val parentNode: NodeType? val currentNode: NodeType val updateNode: NodeType? = findItem(item).contentNode @@ -143,20 +159,22 @@ abstract class TreeStruct, NodeType : Node1 & 2) => что невозможно достичь deleteNode = parentNode?. со значением null (потому что inRightOf/inLeftOf) - (->3) => странный кейс, когда удаляемое значение - null; такого вообще быть не должно - ] */ val parentNode: NodeType? // parent node of the node for deleting val deleteNode: NodeType? // node for deleting - var replaceNode: NodeType? // node for relink on the place deleted node val state: State + var replaceNodeParent: NodeType? = null + var replaceNode: NodeType? // node for relink on the place deleted node - if (findItem(item).contentNode != null) return generateStateDelete(null, null) + if (findItem(item).contentNode == null) throw Exception("Deleted value doesn't exist in tree") parentNode = getParentByValue(item) if (parentNode != null) { @@ -170,29 +188,17 @@ abstract class TreeStruct, NodeType : Node replaceNode = null - - state = generateStateDelete(deleteNode, parentNode) - - if (parentNode != null) rebaseNode(deleteNode, parentNode, replaceNode) - else root = rebaseNode(deleteNode, null, replaceNode) - + replaceNodeParent = getParentByValue(it.value) + replaceNode = unLink(it, replaceNodeParent) + }// if deleteNode doesn't have children => replaceNode = null + + state = generateStateDelete( + rebaseNode(deleteNode, parentNode, replaceNode), + replaceNodeParent + ) return state } - protected abstract fun generateStateFind(foundNode: NodeType?): State - - protected abstract fun generateStateInsert( - insertedNode: NodeType?, - itsParent: NodeType?, - ): State - - protected abstract fun generateStateDelete( - deletedNode: NodeType?, - itsParent: NodeType?, - ): State - protected abstract fun unLink( node: NodeType, parent: NodeType?, @@ -202,7 +208,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node>( override val contentNode: AVLNode?, val root: AVLNode?, diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index 2c0a30a..b1a4555 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -7,27 +7,29 @@ class AVLStruct> : override var root: AVLNode? = null + override val balancer = AVLBalancer(root) + override fun generateStateDelete( deletedNode: AVLNode?, - itsParent: AVLNode? + contentNode: AVLNode?, ): AVLStateContainer { TODO("Not yet implemented") } override fun generateStateInsert( - insertedNode: AVLNode?, - itsParent: AVLNode? + insertNode: AVLNode?, + contentNode: AVLNode? ): AVLStateContainer { TODO("Not yet implemented") } - override fun generateStateFind(foundNode: AVLNode?): AVLStateContainer { + override fun generateStateFind( + findNode: AVLNode?, + contentNode: AVLNode?, + ): AVLStateContainer { TODO("Not yet implemented") } - override val balancer = AVLBalancer(root) - - override fun createNode(item: Pack): AVLNode { TODO("Not yet implemented") } @@ -36,7 +38,7 @@ class AVLStruct> : TODO("Not yet implemented") } - override fun rebaseNode(node: AVLNode, parent: AVLNode?, replaceNode: AVLNode?): AVLNode? { + override fun rebaseNode(node: AVLNode, parent: AVLNode?, replaceNode: AVLNode?): AVLNode { TODO("Not yet implemented rebaseNode") } diff --git a/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt index fb51ed8..bcc0959 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt @@ -2,7 +2,11 @@ package treelib.binTree import treelib.abstractTree.StateContainer -/*Content node - the node on which the action was performed*/ +/* +DELETE: contentNode - deleted Node +INSERT: contentNode - inserted Node +FIND: contentNode - found Node +* */ class BINStateContainer>( override val contentNode: BINNode?, ) : StateContainer> diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index d36f642..2b1f86f 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -9,27 +9,39 @@ class BINStruct> : override fun generateStateDelete( deletedNode: BINNode?, - itsParent: BINNode? + contentNode: BINNode? ): BINStateContainer = BINStateContainer(deletedNode) override fun generateStateInsert( - insertedNode: BINNode?, - itsParent: BINNode? - ): BINStateContainer = BINStateContainer(insertedNode) + insertNode: BINNode?, + contentNode: BINNode?, + ): BINStateContainer = BINStateContainer(insertNode) - override fun generateStateFind(foundNode: BINNode?): BINStateContainer = BINStateContainer(foundNode) + override fun generateStateFind( + findNode: BINNode?, + contentNode: BINNode?, + ): BINStateContainer = BINStateContainer(findNode) override fun rebaseNode( node: BINNode, parent: BINNode?, replaceNode: BINNode?, - ): BINNode? { + ): BINNode { /*Behaviour: return - linked replaceNode*/ //TODO: rebaseNode - test - if (replaceNode != null) { - node.value = replaceNode.value - return node - } else return null + val deletedNode: BINNode = createNode(node.value) + when { + (parent == null) && (replaceNode == null) -> root = null + (parent != null) && (replaceNode == null) -> { + when { + node.value inLeftOf parent.left -> parent.left = null + node.value inRightOf parent.right -> parent.right = null + } + } + + replaceNode != null -> node.value = replaceNode.value + } + return deletedNode } override fun unLink( @@ -70,7 +82,11 @@ class BINStruct> : override fun createNode(item: Pack) = BINNode(item) - override fun delete(item: Pack): Pack? = deleteItem(item).contentNode?.value + override fun delete(item: Pack) { + deleteItem(item).contentNode + } - override fun insert(item: Pack): Pack? = insertItem(item).contentNode?.value + override fun insert(item: Pack) { + insertItem(item).contentNode + } } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt index 7ec6200..50ea3ca 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt @@ -3,8 +3,10 @@ package treelib.rbTree import treelib.abstractTree.StateContainer /* -Insert: contentNode - the node on which the action was performed -Delete: contentNode - the parent of the node on which the action was performed +DELETE: contentNode - parent of the MinRightLeaf (or MaxLeftLeaf) + - in case when has been deleted root - new root +INSERT: contentNode - inserted Node +FIND: contentNode - found value * */ class RBStateContainer>( override val contentNode: RBNode?, diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index b34d59f..6474c71 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -7,35 +7,36 @@ class RBStruct> : override var root: RBNode? = null + override val balancer = RBBalancer(root) + override fun generateStateDelete( deletedNode: RBNode?, - itsParent: RBNode? - ): RBStateContainer { + contentNode: RBNode?, + ): RBStateContainer { TODO("Not yet implemented") } override fun generateStateInsert( - insertedNode: RBNode?, - itsParent: RBNode? - ): RBStateContainer { + insertNode: RBNode?, + contentNode: RBNode?, + ): RBStateContainer { TODO("Not yet implemented") } - override fun generateStateFind(foundNode: RBNode?): RBStateContainer { + override fun generateStateFind( + findNode: RBNode?, + contentNode: RBNode?, + ): RBStateContainer { TODO("Not yet implemented") } - override val balancer = RBBalancer(root) - - override fun createNode(item: Pack): RBNode { - TODO("Not yet implemented") - } + override fun createNode(item: Pack): RBNode = RBNode(item) override fun linkNewNode(node: RBNode, parent: RBNode?): RBNode { TODO("Not yet implemented") } - override fun rebaseNode(node: RBNode, parent: RBNode?, replaceNode: RBNode?): RBNode? { + override fun rebaseNode(node: RBNode, parent: RBNode?, replaceNode: RBNode?): RBNode { TODO("Not yet implemented") } From ff05d9194350c04eebc2b124f903e379249a748f Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 2 Apr 2023 21:23:33 +0300 Subject: [PATCH 020/212] fix: Rewrite protected method deleteItem --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 147 +++++++++++------- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 21 +-- .../main/kotlin/treelib/binTree/BINStruct.kt | 53 ++----- .../main/kotlin/treelib/rbTree/RBStruct.kt | 20 ++- 4 files changed, 127 insertions(+), 114 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 3e37eb6..54e0def 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -78,47 +78,22 @@ abstract class TreeStruct, NodeType : Node1)] var currentNode: NodeType? = null - localRoot.right?.let { - currentNode = localRoot.right - } ?: return null + localRoot.right ?: throw Exception("Incorrect usage of the getRightMinNode") //(->1) - while (true) { - currentNode?.let { curNode -> - if (curNode.left == null) return currentNode - else currentNode = curNode.left - } ?: throw Exception("Impossible case, or multithreading threads problem") //(->1) - } - } - - protected fun getLeftMaxNode(localRoot: NodeType): NodeType? { - /* null - means NodeType.left doesn't exist, another variant impossible */ - //TODO test - getLeafLeftMax [проверить, что нет проблем с (->1)] - var currentNode: NodeType? = null - - localRoot.left?.let { - currentNode = localRoot.left - } ?: return null + currentNode = localRoot.right while (true) { currentNode?.let { curNode -> - if (curNode.right == null) return currentNode - else currentNode = curNode.right - } ?: throw Exception("Impossible case, or multithreading threads problem") //(->1) + if (curNode.left == null) return curNode + else currentNode = curNode.left + } ?: throw Exception("Impossible case or multithreading threads problem") //(->1) } } - protected fun getNodeForReplace(localRoot: NodeType?): NodeType? {/* Behaviour: null - localRoot doesn't have children */ - localRoot?.let { - val replaceNode = getRightMinNode(it) - if (replaceNode != null) return replaceNode - else return getLeftMaxNode(it) - } ?: return null - } - protected abstract fun generateStateFind( findNode: NodeType?, contentNode: NodeType? = null, @@ -168,47 +143,105 @@ abstract class TreeStruct, NodeType : Node parentNode.right //(->1) - item inLeftOf parentNode -> parentNode.left //(->2) + item inRightOf parentDeleteNode -> parentDeleteNode.right + item inLeftOf parentDeleteNode -> parentDeleteNode.left else -> throw Exception("Impossible case in deleteItem (is turned out to be possible)") } } else deleteNode = root - if (deleteNode == null) throw Exception("Impossible case in deleteItem (is turned out to be possible)") // (->3) - - replaceNode = getNodeForReplace(deleteNode) - replaceNode?.let { - replaceNodeParent = getParentByValue(it.value) - replaceNode = unLink(it, replaceNodeParent) - }// if deleteNode doesn't have children => replaceNode = null - - state = generateStateDelete( - rebaseNode(deleteNode, parentNode, replaceNode), - replaceNodeParent - ) - return state + + if (deleteNode == null) throw Exception("Impossible case in deleteItem (is turned out to be possible)") + + val nodeForReplace: NodeType? + val parentNodeForReplace: NodeType? + val deletedNodeWithoutLinks = getNodeKernel(deleteNode) + + when { + (deleteNode.left != null) && (deleteNode.right != null) -> { + val rightMinNode = getRightMinNode(deleteNode) + + parentNodeForReplace = getParentByValue(rightMinNode.value) + nodeForReplace = unLink(rightMinNode, parentNodeForReplace) + + rebaseNode(deleteNode, parentDeleteNode, nodeForReplace) + + return generateStateDelete(deletedNodeWithoutLinks, parentNodeForReplace) + } + + (deleteNode.left == null) && (deleteNode.right == null) -> { + if (parentDeleteNode == null) return generateStateDelete(deletedNodeWithoutLinks, null) + else { + parentDeleteNode.left = null + parentDeleteNode.right = null + return generateStateDelete(deletedNodeWithoutLinks, parentDeleteNode) + } + } + + else -> { + for (child in listOf(deleteNode.right, deleteNode.left)) child?.let { + connectUnlinkedSubTreeWithParent(deleteNode, parentDeleteNode, it) + return@deleteItem generateStateDelete(deletedNodeWithoutLinks, parentDeleteNode) + } + } + } + throw Exception("Impossible case") } - protected abstract fun unLink( + protected fun unLink( node: NodeType, parent: NodeType?, - ): NodeType + ): NodeType { + val unLinkedNode: NodeType = node + val childForLink: NodeType? + + when { + (node.right != null) && (node.left != null) -> throw Exception("unLink - method Shouldn't be used with node with both children") + node.right != null -> childForLink = node.right + node.left != null -> childForLink = node.left + else -> childForLink = null + } + unLinkedNode.left = null + unLinkedNode.right = null + + if (parent == null) return unLinkedNode + connectUnlinkedSubTreeWithParent(node, parent, childForLink) + + return unLinkedNode + } - protected abstract fun rebaseNode( + protected abstract fun connectUnlinkedSubTreeWithParent( + node: NodeType, + parent: NodeType?, + childForLink: NodeType? + ) /*Behaviour: link rebased node */ + + protected fun rebaseNode( node: NodeType, parent: NodeType?, replaceNode: NodeType?, - ): NodeType // return linked on the node place (null - if the replaceNode == null...) + ) { + when { + (parent == null) && (replaceNode == null) -> root = null + (parent != null) && (replaceNode == null) -> { + when { + node.value inLeftOf parent.left -> parent.left = null + node.value inRightOf parent.right -> parent.right = null + } + } + + replaceNode != null -> node.value = replaceNode.value + } + } + + /* Return node with fields: right == left == {parent} == null */ + protected abstract fun getNodeKernel(node: NodeType): NodeType protected abstract fun linkNewNode( node: NodeType, diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index b1a4555..89b77cd 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -6,7 +6,6 @@ class AVLStruct> : BalancedTreeStruct, AVLStateContainer, AVLBalancer>() { override var root: AVLNode? = null - override val balancer = AVLBalancer(root) override fun generateStateDelete( @@ -16,6 +15,18 @@ class AVLStruct> : TODO("Not yet implemented") } + override fun getNodeKernel(node: AVLNode): AVLNode { + TODO("Not yet implemented") + } + + override fun connectUnlinkedSubTreeWithParent( + node: AVLNode, + parent: AVLNode?, + childForLink: AVLNode? + ) { + TODO("Not yet implemented") + } + override fun generateStateInsert( insertNode: AVLNode?, contentNode: AVLNode? @@ -37,12 +48,4 @@ class AVLStruct> : override fun linkNewNode(node: AVLNode, parent: AVLNode?): AVLNode { TODO("Not yet implemented") } - - override fun rebaseNode(node: AVLNode, parent: AVLNode?, replaceNode: AVLNode?): AVLNode { - TODO("Not yet implemented rebaseNode") - } - - override fun unLink(node: AVLNode, parent: AVLNode?): AVLNode { - TODO("Not yet implemented unLink") - } } diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index 2b1f86f..abfba37 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -22,50 +22,23 @@ class BINStruct> : contentNode: BINNode?, ): BINStateContainer = BINStateContainer(findNode) - override fun rebaseNode( + override fun getNodeKernel(node: BINNode) = BINNode(node.value) + + override fun connectUnlinkedSubTreeWithParent( node: BINNode, parent: BINNode?, - replaceNode: BINNode?, - ): BINNode { - /*Behaviour: return - linked replaceNode*/ - //TODO: rebaseNode - test - val deletedNode: BINNode = createNode(node.value) - when { - (parent == null) && (replaceNode == null) -> root = null - (parent != null) && (replaceNode == null) -> { - when { - node.value inLeftOf parent.left -> parent.left = null - node.value inRightOf parent.right -> parent.right = null - } - } - - replaceNode != null -> node.value = replaceNode.value - } - return deletedNode - } + childForLink: BINNode?, + ) { + if (root == null) return - override fun unLink( - node: BINNode, - parent: BINNode? - ): BINNode { - /*Behaviour: return - Node without children */ - /*TODO: unLink - test*/ - val unLinkedNode: BINNode = node - val childForLink: BINNode? - when { - (node.right != null) && (node.left != null) -> throw Exception("unLink - method Shouldn't be used with node with both children") - node.right != null -> childForLink = node.right - node.left != null -> childForLink = node.left - else -> childForLink = null - } - unLinkedNode.left = null - unLinkedNode.right = null - if (parent == null) return unLinkedNode - when { - (node.value < parent.value) -> parent.right = childForLink - (node.value > parent.value) -> parent.left = childForLink + if (parent != null) { + when { + (node.value < parent.value) -> parent.right = childForLink + (node.value > parent.value) -> parent.left = childForLink + } + } else root?.let { + root = childForLink } - return unLinkedNode } override fun linkNewNode( diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 6474c71..2d37976 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -16,6 +16,18 @@ class RBStruct> : TODO("Not yet implemented") } + override fun getNodeKernel(node: RBNode): RBNode { + TODO("Not yet implemented") + } + + override fun connectUnlinkedSubTreeWithParent( + node: RBNode, + parent: RBNode?, + childForLink: RBNode? + ) { + TODO("Not yet implemented") + } + override fun generateStateInsert( insertNode: RBNode?, contentNode: RBNode?, @@ -35,12 +47,4 @@ class RBStruct> : override fun linkNewNode(node: RBNode, parent: RBNode?): RBNode { TODO("Not yet implemented") } - - override fun rebaseNode(node: RBNode, parent: RBNode?, replaceNode: RBNode?): RBNode { - TODO("Not yet implemented") - } - - override fun unLink(node: RBNode, parent: RBNode?): RBNode { - TODO("Not yet implemented") - } } From 4b9637cf584783c5b48966fa01d8e578c437bd51 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 3 Apr 2023 17:18:25 +0300 Subject: [PATCH 021/212] fix: Add proper return from lambda functions; Rewrite method getParentByValue --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 55 +++++++++---------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 54e0def..01d064b 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -1,5 +1,6 @@ package treelib.abstractTree + abstract class TreeStruct, NodeType : Node, State : StateContainer> { protected abstract var root: NodeType? @@ -12,15 +13,14 @@ abstract class TreeStruct, NodeType : Node it.value -> { - if (it.right == null) return currentNode + if (it.right == null) return@getLeafForInsert currentNode currentNode = it.right } item < it.value -> { - if (it.left == null) return currentNode + if (it.left == null) return@getLeafForInsert currentNode currentNode = it.left } - else -> throw Exception("getLeafForInsert shouldn't be used with a value exists in Struct") } } ?: throw Exception("Impossible case or multithreading problem") @@ -43,20 +43,14 @@ abstract class TreeStruct, NodeType : Node { - if (it.right?.value == item) return currentNode - else throw Exception("Impossible case, or multithreading threads problem") - } - - it.left != null -> { - if (it.left?.value == item) return currentNode - else throw Exception("Impossible case, or multithreading threads problem") + item inRightOf it -> return@getParentByValue currentNode + item inLeftOf it -> return@getParentByValue currentNode + else -> { + if (item > it.value) currentNode = it.right + else currentNode = it.left } } - if (item > it.value) currentNode = it.right - else currentNode = it.left - - } ?: throw Exception("getParentByValue shouldn't be used with 'root == null'")// (1)l -> + } ?: throw Exception("getParentByValue shouldn't be used with value doesn't exist in tree")// (1)l -> } } @@ -64,7 +58,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node1)] - var currentNode: NodeType? = null + var currentNode: NodeType? localRoot.right ?: throw Exception("Incorrect usage of the getRightMinNode") //(->1) @@ -88,7 +82,7 @@ abstract class TreeStruct, NodeType : Node - if (curNode.left == null) return curNode + if (curNode.left == null) return@getRightMinNode curNode else currentNode = curNode.left } ?: throw Exception("Impossible case or multithreading threads problem") //(->1) } @@ -185,10 +179,11 @@ abstract class TreeStruct, NodeType : Node { - for (child in listOf(deleteNode.right, deleteNode.left)) child?.let { - connectUnlinkedSubTreeWithParent(deleteNode, parentDeleteNode, it) - return@deleteItem generateStateDelete(deletedNodeWithoutLinks, parentDeleteNode) - } + for (child in listOf(deleteNode.right, deleteNode.left)) + child?.let { + connectUnlinkedSubTreeWithParent(deleteNode, parentDeleteNode, it) + return@deleteItem generateStateDelete(deletedNodeWithoutLinks, parentDeleteNode) + } } } throw Exception("Impossible case") @@ -220,7 +215,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node { + TODO() } - fun postOrder() { - TODO("postOrder - implementation") + fun postOrder(): List { + TODO() } - fun preOrder() { - TODO("preOrder - implementation") + fun preOrder(): List { + TODO() } } From 36a1267b8f64af3ae9f752b26093c3d51cbaae6e Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 3 Apr 2023 20:28:54 +0300 Subject: [PATCH 022/212] feat: Implement preOrder postOrder inOrder; fix bugs in deleteItem method --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 204 +++++++++++++----- .../main/kotlin/treelib/binTree/BINStruct.kt | 4 +- 2 files changed, 147 insertions(+), 61 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 01d064b..85ae65f 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -5,8 +5,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node throw Exception("getLeafForInsert shouldn't be used with a value exists in Struct") } } ?: throw Exception("Impossible case or multithreading problem") } } - protected fun getParentByValue(item: Pack): NodeType? { + private fun getParentByValue(item: Pack): NodeType? { //TODO test - getParentByValue [что вообще корректно отрабатывает] /* (1) - shouldn't be used with 'root == null' otherwise - incorrect behavior @@ -36,7 +36,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node1)] var currentNode: NodeType? @@ -88,6 +88,46 @@ abstract class TreeStruct, NodeType : Node throw Exception("unLink - method Shouldn't be used with node with both children") + node.right != null -> childForLink = node.right + node.left != null -> childForLink = node.left + else -> childForLink = null + } + unLinkedNode.left = null + unLinkedNode.right = null + + if (parent == null) return unLinkedNode + connectUnlinkedSubTreeWithParent(node, parent, childForLink) + + return unLinkedNode + } + + private fun rebaseNode( + node: NodeType, + parent: NodeType?, + replaceNode: NodeType?, + ) { + when { + (parent == null) && (replaceNode == null) -> root = null + (parent != null) && (replaceNode == null) -> { + when { + node.value inLeftOf parent.left -> parent.left = null + node.value inRightOf parent.right -> parent.right = null + } + } + + replaceNode != null -> node.value = replaceNode.value + } + } + protected abstract fun generateStateFind( findNode: NodeType?, contentNode: NodeType? = null, @@ -170,10 +210,14 @@ abstract class TreeStruct, NodeType : Node { - if (parentDeleteNode == null) return generateStateDelete(deletedNodeWithoutLinks, null) - else { - parentDeleteNode.left = null - parentDeleteNode.right = null + if (parentDeleteNode == null) { + root = null + return generateStateDelete(deletedNodeWithoutLinks, null) + } else { + when { + item inRightOf parentDeleteNode -> parentDeleteNode.right = null + item inLeftOf parentDeleteNode -> parentDeleteNode.left = null + } return generateStateDelete(deletedNodeWithoutLinks, parentDeleteNode) } } @@ -189,52 +233,12 @@ abstract class TreeStruct, NodeType : Node throw Exception("unLink - method Shouldn't be used with node with both children") - node.right != null -> childForLink = node.right - node.left != null -> childForLink = node.left - else -> childForLink = null - } - unLinkedNode.left = null - unLinkedNode.right = null - - if (parent == null) return unLinkedNode - connectUnlinkedSubTreeWithParent(node, parent, childForLink) - - return unLinkedNode - } - protected abstract fun connectUnlinkedSubTreeWithParent( node: NodeType, parent: NodeType?, childForLink: NodeType? ) /* Behaviour: link rebased node */ - protected fun rebaseNode( - node: NodeType, - parent: NodeType?, - replaceNode: NodeType?, - ) { - when { - (parent == null) && (replaceNode == null) -> root = null - (parent != null) && (replaceNode == null) -> { - when { - node.value inLeftOf parent.left -> parent.left = null - node.value inRightOf parent.right -> parent.right = null - } - } - - replaceNode != null -> node.value = replaceNode.value - } - } - /* Return node with fields: right == left == {parent} == null */ protected abstract fun getNodeKernel(node: NodeType): NodeType @@ -245,22 +249,104 @@ abstract class TreeStruct, NodeType : Node { - TODO() + val arrayNodes = mutableListOf() + var flagVisited = 0 + var current = root + val parents = ArrayDeque() + + while (current != null) { + if (flagVisited == 0) { + while (true) { + current?.let { + if (it.left == null) return@let null + parents.add(it) + current = it.left + return@let current + } ?: break + } + } + current?.let { + arrayNodes.add(it.value) + if (it.right != null) { + flagVisited = 0 + current = it.right + } else { + if (parents.isEmpty()) + return@inOrder arrayNodes + flagVisited = 1 + current = parents.removeLast() + } + } + } + return arrayNodes } fun postOrder(): List { - TODO() + val parents = ArrayDeque() + val arrayNodes = mutableListOf() + var flagVisited = 0 + var current = root + + while (current != null) { + if (flagVisited == 0) { + while (true) { + current?.let { + if (it.left == null) return@let null + parents.add(it) + current = it.left + return@let current + } ?: break + } + } + current?.let { + if (it.right != null && flagVisited != 2) { + parents.add(it) + current = it.right + flagVisited = 0 + } else { + arrayNodes.add(it.value) + if (parents.isEmpty()) + return@postOrder arrayNodes + val parent = parents.removeLast() + if (parent.right == it) { + flagVisited = 2 + } + current = parent + } + } ?: throw Exception("Impossible case or multithreading problem") + } + return arrayNodes } fun preOrder(): List { - TODO() + val arrayNodes = mutableListOf() + var current: NodeType + val queue = ArrayDeque() + + root?.let { root -> + queue.add(root) + while (queue.isNotEmpty()) { + current = queue.removeLast() + arrayNodes.add(current.value) + if (current.right != null) + current.right?.let { + queue.add(it) + } ?: throw Exception("Impossible case or multithreading threads problem") + + if (current.left != null) + current.left?.let { + queue.add(it) + } ?: throw Exception("Impossible case or multithreading threads problem") + } + } + return arrayNodes } } diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index abfba37..e555924 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -33,8 +33,8 @@ class BINStruct> : if (parent != null) { when { - (node.value < parent.value) -> parent.right = childForLink - (node.value > parent.value) -> parent.left = childForLink + (node.value < parent.value) -> parent.left = childForLink + (node.value > parent.value) -> parent.right = childForLink } } else root?.let { root = childForLink From 30f95f7a227e1c88d6e6b9da3d0b7626865dd36f Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 3 Apr 2023 20:31:10 +0300 Subject: [PATCH 023/212] feat: Implement Tree-classes public methods; add the ability to store null values --- .../main/kotlin/treelib/abstractTree/Tree.kt | 43 +++++++++++-------- .../main/kotlin/treelib/avlTree/AVLTree.kt | 8 +++- .../main/kotlin/treelib/binTree/BINTree.kt | 8 +++- lib/src/main/kotlin/treelib/rbTree/RBTree.kt | 8 +++- .../kotlin/treelib/singleObjects/Container.kt | 5 ++- 5 files changed, 46 insertions(+), 26 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index 461c112..7040cf5 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -5,37 +5,42 @@ import treelib.singleObjects.Container abstract class Tree< Key : Comparable, Value, - NodeType : Node, NodeType>, - State : StateContainer, NodeType> + NodeType : Node, NodeType>, + State : StateContainer, NodeType> > { - protected abstract val treeStruct: TreeStruct, NodeType, State> + protected abstract val treeStruct: TreeStruct, NodeType, State> - fun putItem(item: Pair) { - TODO("Not yet implemented") - } + private fun wrapForFind(key: Key) = Container(key to null) - fun putItems(vararg items: Pair) { - TODO("Not yet implemented") + fun putItem(item: Pair) { + treeStruct.insert(Container(item)) } - fun deleteItem(key: Key) { - TODO("Not yet implemented") + fun putItems(vararg items: Pair) { + for (element in items) putItem(element) } - fun getItem(key: Key) { - TODO("Not yet implemented") + fun putItems(items: Iterable>) { + for (element in items) putItem(element) } - fun inOrder() { - TODO("Not yet implemented") - } + fun getItem(key: Key): Value? = treeStruct.find(wrapForFind(key))?.value - fun preOrder() { - TODO("Not yet implemented") + fun deleteItem(key: Key) { + if (getItem(key) == null) throw Exception("Attempt to remove a non-existent element") + treeStruct.delete(wrapForFind(key)) } - fun postOrder() { - TODO("Not yet implemented") + private fun createPoorList(info: List>): List> { + val returnInfo = mutableListOf>() + for (element in info) returnInfo.add(element.pair) + return returnInfo } + + fun inOrder(): List> = createPoorList(treeStruct.inOrder()) + + fun preOrder(): List> = createPoorList(treeStruct.preOrder()) + + fun postOrder(): List> = createPoorList(treeStruct.postOrder()) } diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt index 4ad338e..6155af6 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt @@ -5,7 +5,11 @@ import treelib.abstractTree.Tree import treelib.singleObjects.Container class AVLTree, Value> : - Tree>, AVLStateContainer>>() { + Tree>, AVLStateContainer>>() { - override val treeStruct = AVLStruct>() + override val treeStruct = AVLStruct>() + + operator fun AVLTree.get(key: Key): Value? = getItem(key) + + operator fun AVLTree.set(key: Key, value: Value) = putItem(key to value) } diff --git a/lib/src/main/kotlin/treelib/binTree/BINTree.kt b/lib/src/main/kotlin/treelib/binTree/BINTree.kt index ed24d26..f8ac560 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINTree.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINTree.kt @@ -4,7 +4,11 @@ import treelib.abstractTree.Tree import treelib.singleObjects.Container class BINTree, Value> - : Tree>, BINStateContainer>>() { + : Tree>, BINStateContainer>>() { - override val treeStruct = BINStruct>() + override val treeStruct = BINStruct>() + + operator fun BINTree.get(key: Key): Value? = getItem(key) + + operator fun BINTree.set(key: Key, value: Value) = putItem(key to value) } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt index 7299d06..6ccfa32 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt @@ -5,7 +5,11 @@ import treelib.abstractTree.Tree import treelib.singleObjects.Container class RBTree, Value> : - Tree>, RBStateContainer>>() { + Tree>, RBStateContainer>>() { - override val treeStruct = RBStruct>() + override val treeStruct = RBStruct>() + + operator fun RBTree.get(key: Key): Value? = getItem(key) + + operator fun RBTree.set(key: Key, value: Value) = putItem(key to value) } diff --git a/lib/src/main/kotlin/treelib/singleObjects/Container.kt b/lib/src/main/kotlin/treelib/singleObjects/Container.kt index 72e865c..5ad9da1 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/Container.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/Container.kt @@ -1,6 +1,9 @@ package treelib.singleObjects -class Container, V>(private val pair: Pair) : Comparable> { +class Container, V>(val pair: Pair) : Comparable> { + + val key = pair.first + val value = pair.second override fun equals(other: Any?): Boolean { if (this === other) From 7c8ea1fccbec4fae8de39666c6a5b9a1ed1467b7 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Fri, 7 Apr 2023 19:43:18 +0300 Subject: [PATCH 024/212] feat: Add test for BINstruct --- .../test/kotlin/treelib/TreeStructsTest.kt | 509 ++++++++++++++++++ 1 file changed, 509 insertions(+) diff --git a/lib/src/test/kotlin/treelib/TreeStructsTest.kt b/lib/src/test/kotlin/treelib/TreeStructsTest.kt index 30c7911..1f3f457 100644 --- a/lib/src/test/kotlin/treelib/TreeStructsTest.kt +++ b/lib/src/test/kotlin/treelib/TreeStructsTest.kt @@ -31,6 +31,515 @@ class TreeStructsTest { 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 empty tree`() { + 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 in one root tree`() { + classUnderTest.insert(1) + 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 = 1, 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 double delete`() { + val num = mutableListOf(1, 2) + for (value in num) { + classUnderTest.insert(value) + } + classUnderTest.delete(2) + classUnderTest.delete(2) + + val root = treeW.getPrivateNode(classUnderTest)?.value + + assertEquals(expected = root, actual = 1) + } + + @Test + fun `test double delete root`() { + val num = mutableListOf(2) + for (value in num) { + classUnderTest.insert(value) + } + classUnderTest.delete(2) + classUnderTest.delete(2) + + val root = treeW.getPrivateNode(classUnderTest)?.value + + assertEquals(expected = root, 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 From 877f63e5e91a0a7319910d9b52bd0a415435dfd7 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Thu, 6 Apr 2023 20:37:57 +0300 Subject: [PATCH 025/212] feat: Implement AVLStruct and RBStruct --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 19 ++----- .../balanced/BalancedTreeStruct.kt | 3 -- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 52 +++++++++--------- .../main/kotlin/treelib/rbTree/RBStruct.kt | 54 ++++++++++++------- 4 files changed, 66 insertions(+), 62 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 85ae65f..38a5118 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -28,15 +28,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node, NodeType : Node, NodeType : Node1)] + private fun getRightMinNode(localRoot: NodeType): NodeType { var currentNode: NodeType? - localRoot.right ?: throw Exception("Incorrect usage of the getRightMinNode") //(->1) + localRoot.right ?: throw Exception("Incorrect usage of the getRightMinNode") currentNode = localRoot.right @@ -84,7 +73,7 @@ abstract class TreeStruct, NodeType : Node if (curNode.left == null) return@getRightMinNode curNode else currentNode = curNode.left - } ?: throw Exception("Impossible case or multithreading threads problem") //(->1) + } ?: throw Exception("Impossible case or multithreading threads problem") } } @@ -251,7 +240,7 @@ abstract class TreeStruct, NodeType : Node> : override fun generateStateDelete( deletedNode: AVLNode?, contentNode: AVLNode?, - ): AVLStateContainer { - TODO("Not yet implemented") - } - - override fun getNodeKernel(node: AVLNode): AVLNode { - TODO("Not yet implemented") - } - - override fun connectUnlinkedSubTreeWithParent( - node: AVLNode, - parent: AVLNode?, - childForLink: AVLNode? - ) { - TODO("Not yet implemented") - } + ): AVLStateContainer = AVLStateContainer(contentNode, root) override fun generateStateInsert( insertNode: AVLNode?, contentNode: AVLNode? - ): AVLStateContainer { - TODO("Not yet implemented") - } + ): AVLStateContainer = AVLStateContainer(contentNode, root) override fun generateStateFind( findNode: AVLNode?, contentNode: AVLNode?, - ): AVLStateContainer { - TODO("Not yet implemented") - } + ): AVLStateContainer = AVLStateContainer(contentNode, root) - override fun createNode(item: Pack): AVLNode { - TODO("Not yet implemented") + override fun connectUnlinkedSubTreeWithParent( + node: AVLNode, + parent: AVLNode?, + childForLink: AVLNode? + ) { + if (root == null) return + + if (parent != null) { + when { + (node.value < parent.value) -> parent.left = childForLink + (node.value > parent.value) -> parent.right = childForLink + } + } else root?.let { + root = childForLink + } } + override fun createNode(item: Pack): AVLNode = AVLNode(item) + + override fun getNodeKernel(node: AVLNode): AVLNode = AVLNode(node.value, height = node.height) + override fun linkNewNode(node: AVLNode, parent: AVLNode?): AVLNode { - TODO("Not yet implemented") + if (parent == null) root = node + else { + if (node.value > parent.value) parent.right = node + else parent.left = node + } + return node } } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 2d37976..efebbbf 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -12,39 +12,53 @@ class RBStruct> : override fun generateStateDelete( deletedNode: RBNode?, contentNode: RBNode?, - ): RBStateContainer { - TODO("Not yet implemented") - } + ): RBStateContainer = RBStateContainer(contentNode) - override fun getNodeKernel(node: RBNode): RBNode { - TODO("Not yet implemented") - } + override fun generateStateInsert( + insertNode: RBNode?, + contentNode: RBNode?, + ): RBStateContainer = RBStateContainer(contentNode) + + override fun generateStateFind( + findNode: RBNode?, + contentNode: RBNode?, + ): RBStateContainer = RBStateContainer(contentNode) override fun connectUnlinkedSubTreeWithParent( node: RBNode, parent: RBNode?, childForLink: RBNode? ) { - TODO("Not yet implemented") - } + if (root == null) return - override fun generateStateInsert( - insertNode: RBNode?, - contentNode: RBNode?, - ): RBStateContainer { - TODO("Not yet implemented") + if (parent != null) { + when { + (node.value < parent.value) -> { + parent.left = childForLink + } + (node.value > parent.value) -> { + parent.right = childForLink + } + } + if (childForLink != null){ + childForLink.parent = parent + } + } else root?.let { + root = childForLink + } } - override fun generateStateFind( - findNode: RBNode?, - contentNode: RBNode?, - ): RBStateContainer { - TODO("Not yet implemented") - } + override fun getNodeKernel(node: RBNode): RBNode = RBNode(node.value, color = node.color) override fun createNode(item: Pack): RBNode = RBNode(item) override fun linkNewNode(node: RBNode, parent: RBNode?): RBNode { - TODO("Not yet implemented") + if (parent == null) root = node + else { + if (node.value > parent.value) parent.right = node + else parent.left = node + node.parent = parent + } + return node } } From d5ca914cce0d1af06f3a3576dd07014d5b66b11e Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 7 Apr 2023 21:24:01 +0300 Subject: [PATCH 026/212] fixe: Fixe merge conflict --- RBtree/RBBalancer.kt | 1 + RBtree/RBNode.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/RBtree/RBBalancer.kt b/RBtree/RBBalancer.kt index b6fe7e4..b16777a 100644 --- a/RBtree/RBBalancer.kt +++ b/RBtree/RBBalancer.kt @@ -1,5 +1,6 @@ package RBtree +import AVLtree.AVLNode import AbstractTree.Weighted.BalancerParent class RBBalancer>: BalancerParent>() { diff --git a/RBtree/RBNode.kt b/RBtree/RBNode.kt index 1cda844..f1a2565 100644 --- a/RBtree/RBNode.kt +++ b/RBtree/RBNode.kt @@ -7,6 +7,6 @@ class RBNode>( override var value: Pack?, override var left: RBNode?, override var right: RBNode?, - var parent: RBNode?, + var parent: RBNode, var color: Markers, ) : Node> \ No newline at end of file From 0767b5c39a2de12da91a4acd3624768fcfecdada Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 22 Mar 2023 01:17:14 +0300 Subject: [PATCH 027/212] feat: Gradle add test --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61608 bytes gradle/wrapper/gradle-wrapper.properties | 6 +++ lib/build.gradle.kts | 39 ++++++++++++++++++ .../main/kotlin/Tree/AVLtree}/AVLBalancer.kt | 0 .../src/main/kotlin/Tree/AVLtree}/AVLNode.kt | 0 .../main/kotlin/Tree/AVLtree}/AVLStruct.kt | 0 .../src/main/kotlin/Tree/AVLtree}/AVLTree.kt | 0 .../main/kotlin/Tree/AbstractTree}/Node.kt | 0 .../main/kotlin/Tree/AbstractTree}/Tree.kt | 0 .../kotlin/Tree/AbstractTree}/TreeStruct.kt | 0 .../Tree/AbstractTree}/Weighted/Balancer.kt | 0 .../Weighted/BalancerNoParent.kt | 0 .../AbstractTree}/Weighted/BalancerParent.kt | 0 .../Weighted/WeightedTreeStruct.kt | 0 .../src/main/kotlin/Tree/BINtree}/BINNode.kt | 0 .../main/kotlin/Tree/BINtree}/BINStruct.kt | 0 .../src/main/kotlin/Tree/BINtree}/BINTree.kt | 0 .../main/kotlin/Tree/RBtree}/RBBalancer.kt | 0 .../src/main/kotlin/Tree/RBtree}/RBNode.kt | 0 .../src/main/kotlin/Tree/RBtree}/RBStruct.kt | 0 .../src/main/kotlin/Tree/RBtree}/RBTree.kt | 0 .../kotlin/Tree/Single_Objects}/Container.kt | 0 .../kotlin/Tree/Single_Objects}/Markers.kt | 0 settings.gradle.kts | 11 +++++ 24 files changed, 56 insertions(+) create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 lib/build.gradle.kts rename {AVLtree => lib/src/main/kotlin/Tree/AVLtree}/AVLBalancer.kt (100%) rename {AVLtree => lib/src/main/kotlin/Tree/AVLtree}/AVLNode.kt (100%) rename {AVLtree => lib/src/main/kotlin/Tree/AVLtree}/AVLStruct.kt (100%) rename {AVLtree => lib/src/main/kotlin/Tree/AVLtree}/AVLTree.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Node.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Tree.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/TreeStruct.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Weighted/Balancer.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Weighted/BalancerNoParent.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Weighted/BalancerParent.kt (100%) rename {AbstractTree => lib/src/main/kotlin/Tree/AbstractTree}/Weighted/WeightedTreeStruct.kt (100%) rename {BINtree => lib/src/main/kotlin/Tree/BINtree}/BINNode.kt (100%) rename {BINtree => lib/src/main/kotlin/Tree/BINtree}/BINStruct.kt (100%) rename {BINtree => lib/src/main/kotlin/Tree/BINtree}/BINTree.kt (100%) rename {RBtree => lib/src/main/kotlin/Tree/RBtree}/RBBalancer.kt (100%) rename {RBtree => lib/src/main/kotlin/Tree/RBtree}/RBNode.kt (100%) rename {RBtree => lib/src/main/kotlin/Tree/RBtree}/RBStruct.kt (100%) rename {RBtree => lib/src/main/kotlin/Tree/RBtree}/RBTree.kt (100%) rename {Single_Objects => lib/src/main/kotlin/Tree/Single_Objects}/Container.kt (100%) rename {Single_Objects => lib/src/main/kotlin/Tree/Single_Objects}/Markers.kt (100%) create mode 100644 settings.gradle.kts diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..ccebba7710deaf9f98673a68957ea02138b60d0a GIT binary patch literal 61608 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..42defcc --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts new file mode 100644 index 0000000..b0aa745 --- /dev/null +++ b/lib/build.gradle.kts @@ -0,0 +1,39 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Kotlin library project to get you started. + * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle + * User Manual available at https://docs.gradle.org/8.0/userguide/building_java_projects.html + */ + +plugins { + // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. + id("org.jetbrains.kotlin.jvm") version "1.8.10" + + // Apply the java-library plugin for API and implementation separation. + `java-library` +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use the Kotlin JUnit 5 integration. + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + + // Use the JUnit 5 integration. + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") + + // This dependency is exported to consumers, that is to say found on their compile classpath. + api("org.apache.commons:commons-math3:3.6.1") + + // This dependency is used internally, and not exposed to consumers on their own compile classpath. + implementation("com.google.guava:guava:31.1-jre") +} + +tasks.named("test") { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt similarity index 100% rename from AVLtree/AVLBalancer.kt rename to lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt diff --git a/AVLtree/AVLNode.kt b/lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt similarity index 100% rename from AVLtree/AVLNode.kt rename to lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt diff --git a/AVLtree/AVLStruct.kt b/lib/src/main/kotlin/Tree/AVLtree/AVLStruct.kt similarity index 100% rename from AVLtree/AVLStruct.kt rename to lib/src/main/kotlin/Tree/AVLtree/AVLStruct.kt diff --git a/AVLtree/AVLTree.kt b/lib/src/main/kotlin/Tree/AVLtree/AVLTree.kt similarity index 100% rename from AVLtree/AVLTree.kt rename to lib/src/main/kotlin/Tree/AVLtree/AVLTree.kt diff --git a/AbstractTree/Node.kt b/lib/src/main/kotlin/Tree/AbstractTree/Node.kt similarity index 100% rename from AbstractTree/Node.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Node.kt diff --git a/AbstractTree/Tree.kt b/lib/src/main/kotlin/Tree/AbstractTree/Tree.kt similarity index 100% rename from AbstractTree/Tree.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Tree.kt diff --git a/AbstractTree/TreeStruct.kt b/lib/src/main/kotlin/Tree/AbstractTree/TreeStruct.kt similarity index 100% rename from AbstractTree/TreeStruct.kt rename to lib/src/main/kotlin/Tree/AbstractTree/TreeStruct.kt diff --git a/AbstractTree/Weighted/Balancer.kt b/lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt similarity index 100% rename from AbstractTree/Weighted/Balancer.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt diff --git a/AbstractTree/Weighted/BalancerNoParent.kt b/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerNoParent.kt similarity index 100% rename from AbstractTree/Weighted/BalancerNoParent.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerNoParent.kt diff --git a/AbstractTree/Weighted/BalancerParent.kt b/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerParent.kt similarity index 100% rename from AbstractTree/Weighted/BalancerParent.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerParent.kt diff --git a/AbstractTree/Weighted/WeightedTreeStruct.kt b/lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt similarity index 100% rename from AbstractTree/Weighted/WeightedTreeStruct.kt rename to lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt diff --git a/BINtree/BINNode.kt b/lib/src/main/kotlin/Tree/BINtree/BINNode.kt similarity index 100% rename from BINtree/BINNode.kt rename to lib/src/main/kotlin/Tree/BINtree/BINNode.kt diff --git a/BINtree/BINStruct.kt b/lib/src/main/kotlin/Tree/BINtree/BINStruct.kt similarity index 100% rename from BINtree/BINStruct.kt rename to lib/src/main/kotlin/Tree/BINtree/BINStruct.kt diff --git a/BINtree/BINTree.kt b/lib/src/main/kotlin/Tree/BINtree/BINTree.kt similarity index 100% rename from BINtree/BINTree.kt rename to lib/src/main/kotlin/Tree/BINtree/BINTree.kt diff --git a/RBtree/RBBalancer.kt b/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt similarity index 100% rename from RBtree/RBBalancer.kt rename to lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt diff --git a/RBtree/RBNode.kt b/lib/src/main/kotlin/Tree/RBtree/RBNode.kt similarity index 100% rename from RBtree/RBNode.kt rename to lib/src/main/kotlin/Tree/RBtree/RBNode.kt diff --git a/RBtree/RBStruct.kt b/lib/src/main/kotlin/Tree/RBtree/RBStruct.kt similarity index 100% rename from RBtree/RBStruct.kt rename to lib/src/main/kotlin/Tree/RBtree/RBStruct.kt diff --git a/RBtree/RBTree.kt b/lib/src/main/kotlin/Tree/RBtree/RBTree.kt similarity index 100% rename from RBtree/RBTree.kt rename to lib/src/main/kotlin/Tree/RBtree/RBTree.kt diff --git a/Single_Objects/Container.kt b/lib/src/main/kotlin/Tree/Single_Objects/Container.kt similarity index 100% rename from Single_Objects/Container.kt rename to lib/src/main/kotlin/Tree/Single_Objects/Container.kt diff --git a/Single_Objects/Markers.kt b/lib/src/main/kotlin/Tree/Single_Objects/Markers.kt similarity index 100% rename from Single_Objects/Markers.kt rename to lib/src/main/kotlin/Tree/Single_Objects/Markers.kt diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..e5425d5 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/8.0/userguide/multi_project_builds.html + */ + +rootProject.name = "Tree" +include("lib") From 71f82aea48b37c9d9c9d4aabd21b1e996aa61e32 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 22 Mar 2023 01:52:12 +0300 Subject: [PATCH 028/212] fixe: Fixe problems with nullable fields in Nodes --- lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt b/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt index b16777a..4d94b63 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt @@ -4,7 +4,7 @@ import AVLtree.AVLNode import AbstractTree.Weighted.BalancerParent class RBBalancer>: BalancerParent>() { - override fun balance(node: RBNode): RBNode{ + override fun balance(node: RBNode): RBNode { TODO("Not yet implemented") } } \ No newline at end of file From dd801ce791f483daf16704bc142a236d73d9c399 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 22 Mar 2023 15:25:23 +0300 Subject: [PATCH 029/212] feat: Add gradle wrapper files | configure project as a library --- gradlew | 243 ++++++++++++++++++ gradlew.bat | 92 +++++++ lib/build.gradle.kts | 23 +- .../{Tree => TreeLib}/AVLtree/AVLBalancer.kt | 4 +- .../{Tree => TreeLib}/AVLtree/AVLNode.kt | 4 +- .../{Tree => TreeLib}/AVLtree/AVLStruct.kt | 4 +- .../{Tree => TreeLib}/AVLtree/AVLTree.kt | 6 +- .../{Tree => TreeLib}/AbstractTree/Node.kt | 2 +- .../{Tree => TreeLib}/AbstractTree/Tree.kt | 4 +- .../AbstractTree/TreeStruct.kt | 2 +- .../AbstractTree/Weighted/Balancer.kt | 4 +- .../AbstractTree/Weighted/BalancerNoParent.kt | 4 +- .../AbstractTree/Weighted/BalancerParent.kt | 4 +- .../Weighted/WeightedTreeStruct.kt | 6 +- .../{Tree => TreeLib}/BINtree/BINNode.kt | 4 +- .../{Tree => TreeLib}/BINtree/BINStruct.kt | 4 +- .../{Tree => TreeLib}/BINtree/BINTree.kt | 6 +- .../{Tree => TreeLib}/RBtree/RBBalancer.kt | 5 +- .../kotlin/{Tree => TreeLib}/RBtree/RBNode.kt | 8 +- .../{Tree => TreeLib}/RBtree/RBStruct.kt | 4 +- .../kotlin/{Tree => TreeLib}/RBtree/RBTree.kt | 6 +- .../Single_Objects/Container.kt | 2 +- .../Single_Objects/Markers.kt | 2 +- settings.gradle.kts | 2 +- 24 files changed, 398 insertions(+), 47 deletions(-) create mode 100755 gradlew create mode 100644 gradlew.bat rename lib/src/main/kotlin/{Tree => TreeLib}/AVLtree/AVLBalancer.kt (71%) rename lib/src/main/kotlin/{Tree => TreeLib}/AVLtree/AVLNode.kt (77%) rename lib/src/main/kotlin/{Tree => TreeLib}/AVLtree/AVLStruct.kt (71%) rename lib/src/main/kotlin/{Tree => TreeLib}/AVLtree/AVLTree.kt (65%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Node.kt (90%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Tree.kt (91%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/TreeStruct.kt (95%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Weighted/Balancer.kt (76%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Weighted/BalancerNoParent.kt (82%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Weighted/BalancerParent.kt (82%) rename lib/src/main/kotlin/{Tree => TreeLib}/AbstractTree/Weighted/WeightedTreeStruct.kt (86%) rename lib/src/main/kotlin/{Tree => TreeLib}/BINtree/BINNode.kt (75%) rename lib/src/main/kotlin/{Tree => TreeLib}/BINtree/BINStruct.kt (82%) rename lib/src/main/kotlin/{Tree => TreeLib}/BINtree/BINTree.kt (64%) rename lib/src/main/kotlin/{Tree => TreeLib}/RBtree/RBBalancer.kt (69%) rename lib/src/main/kotlin/{Tree => TreeLib}/RBtree/RBNode.kt (61%) rename lib/src/main/kotlin/{Tree => TreeLib}/RBtree/RBStruct.kt (71%) rename lib/src/main/kotlin/{Tree => TreeLib}/RBtree/RBTree.kt (64%) rename lib/src/main/kotlin/{Tree => TreeLib}/Single_Objects/Container.kt (94%) rename lib/src/main/kotlin/{Tree => TreeLib}/Single_Objects/Markers.kt (55%) diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..a9bd892 --- /dev/null +++ b/gradlew @@ -0,0 +1,243 @@ +#!/bin/sh +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index b0aa745..9513ece 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -8,10 +8,16 @@ plugins { // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. - id("org.jetbrains.kotlin.jvm") version "1.8.10" + kotlin("jvm") version "1.8.10" // Apply the java-library plugin for API and implementation separation. `java-library` + `maven-publish` +} +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(8)) + } } repositories { @@ -20,10 +26,10 @@ repositories { } dependencies { - // Use the Kotlin JUnit 5 integration. + // Use the Kotlin JUnit 5 integration. (TESTS support tools) testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") - // Use the JUnit 5 integration. + // Use the JUnit 5 integration. (TESTS support tools) testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") // This dependency is exported to consumers, that is to say found on their compile classpath. @@ -37,3 +43,14 @@ tasks.named("test") { // Use JUnit Platform for unit tests. useJUnitPlatform() } + +publishing { + publications { + create("maven") { + groupId = "Tree" + artifactId = "lib" + version = "1.1" + from(components["java"]) + } + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt similarity index 71% rename from lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt rename to lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt index b67b135..1078742 100644 --- a/lib/src/main/kotlin/Tree/AVLtree/AVLBalancer.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt @@ -1,6 +1,6 @@ -package AVLtree +package TreeLib.AVLtree -import AbstractTree.Weighted.BalancerNoParent +import TreeLib.AbstractTree.Weighted.BalancerNoParent class AVLBalancer>: BalancerNoParent>() { override fun balance(node: AVLNode): AVLNode { diff --git a/lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt similarity index 77% rename from lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt rename to lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt index e2cb250..d92d1d3 100644 --- a/lib/src/main/kotlin/Tree/AVLtree/AVLNode.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt @@ -1,6 +1,6 @@ -package AVLtree +package TreeLib.AVLtree -import AbstractTree.Node +import TreeLib.AbstractTree.Node class AVLNode>( override var value: Pack?, diff --git a/lib/src/main/kotlin/Tree/AVLtree/AVLStruct.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt similarity index 71% rename from lib/src/main/kotlin/Tree/AVLtree/AVLStruct.kt rename to lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt index becd333..41093b5 100644 --- a/lib/src/main/kotlin/Tree/AVLtree/AVLStruct.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt @@ -1,6 +1,6 @@ -package AVLtree +package TreeLib.AVLtree -import AbstractTree.Weighted.WeightedTreeStruct +import TreeLib.AbstractTree.Weighted.WeightedTreeStruct class AVLStruct> : WeightedTreeStruct, AVLBalancer>() { override var root: AVLNode? = null diff --git a/lib/src/main/kotlin/Tree/AVLtree/AVLTree.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt similarity index 65% rename from lib/src/main/kotlin/Tree/AVLtree/AVLTree.kt rename to lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt index 8b20e40..0c8f284 100644 --- a/lib/src/main/kotlin/Tree/AVLtree/AVLTree.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt @@ -1,8 +1,8 @@ -package AVLtree +package TreeLib.AVLtree -import AbstractTree.Tree +import TreeLib.AbstractTree.Tree -import Single_Objects.Container +import TreeLib.Single_Objects.Container class AVLTree, Value> : Tree>>() { diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Node.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt similarity index 90% rename from lib/src/main/kotlin/Tree/AbstractTree/Node.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt index ed50e35..fbb7462 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Node.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt @@ -1,4 +1,4 @@ -package AbstractTree +package TreeLib.AbstractTree interface Node, SubNode : Node>{ var value: Pack? diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Tree.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt similarity index 91% rename from lib/src/main/kotlin/Tree/AbstractTree/Tree.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt index d947c6b..b4fb4c2 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Tree.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt @@ -1,6 +1,6 @@ -package AbstractTree +package TreeLib.AbstractTree -import Single_Objects.Container +import TreeLib.Single_Objects.Container abstract class Tree, Value, NodeType: Node, NodeType>> { protected abstract val treeStruct: TreeStruct, NodeType> diff --git a/lib/src/main/kotlin/Tree/AbstractTree/TreeStruct.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt similarity index 95% rename from lib/src/main/kotlin/Tree/AbstractTree/TreeStruct.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt index a1d3dd0..2e800d3 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt @@ -1,4 +1,4 @@ -package AbstractTree +package TreeLib.AbstractTree abstract class TreeStruct, NodeType : Node> { protected abstract var root: NodeType? //TODO root - not a null diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt similarity index 76% rename from lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt index 2060826..9412b1c 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/Balancer.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt @@ -1,6 +1,6 @@ -package AbstractTree.Weighted +package TreeLib.AbstractTree.Weighted -import AbstractTree.Node +import TreeLib.AbstractTree.Node interface Balancer, NodeType : Node> { fun rightRotate(currentNode: NodeType): NodeType diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerNoParent.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt similarity index 82% rename from lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerNoParent.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt index 31506f1..f7113dc 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerNoParent.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt @@ -1,6 +1,6 @@ -package AbstractTree.Weighted +package TreeLib.AbstractTree.Weighted -import AbstractTree.Node +import TreeLib.AbstractTree.Node abstract class BalancerNoParent, NodeType : Node>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerParent.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt similarity index 82% rename from lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerParent.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt index d33d53c..4016b35 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/BalancerParent.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt @@ -1,6 +1,6 @@ -package AbstractTree.Weighted +package TreeLib.AbstractTree.Weighted -import AbstractTree.Node +import TreeLib.AbstractTree.Node abstract class BalancerParent, NodeType : Node>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { diff --git a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt similarity index 86% rename from lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt rename to lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt index c256e22..31cf644 100644 --- a/lib/src/main/kotlin/Tree/AbstractTree/Weighted/WeightedTreeStruct.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt @@ -1,7 +1,7 @@ -package AbstractTree.Weighted +package TreeLib.AbstractTree.Weighted -import AbstractTree.Node -import AbstractTree.TreeStruct +import TreeLib.AbstractTree.Node +import TreeLib.AbstractTree.TreeStruct abstract class WeightedTreeStruct, NodeType : Node, BalancerType: Balancer>: TreeStruct() { diff --git a/lib/src/main/kotlin/Tree/BINtree/BINNode.kt b/lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt similarity index 75% rename from lib/src/main/kotlin/Tree/BINtree/BINNode.kt rename to lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt index 97ffb32..3e5ca1e 100644 --- a/lib/src/main/kotlin/Tree/BINtree/BINNode.kt +++ b/lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt @@ -1,6 +1,6 @@ -package BINtree +package TreeLib.BINtree -import AbstractTree.Node +import TreeLib.AbstractTree.Node class BINNode>( override var value: Pack?, diff --git a/lib/src/main/kotlin/Tree/BINtree/BINStruct.kt b/lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt similarity index 82% rename from lib/src/main/kotlin/Tree/BINtree/BINStruct.kt rename to lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt index bb20e6c..ebd3008 100644 --- a/lib/src/main/kotlin/Tree/BINtree/BINStruct.kt +++ b/lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt @@ -1,6 +1,6 @@ -package BINtree +package TreeLib.BINtree -import AbstractTree.TreeStruct +import TreeLib.AbstractTree.TreeStruct class BINStruct> : TreeStruct>() { override var root: BINNode? = null diff --git a/lib/src/main/kotlin/Tree/BINtree/BINTree.kt b/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt similarity index 64% rename from lib/src/main/kotlin/Tree/BINtree/BINTree.kt rename to lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt index ad29aa0..fd34f78 100644 --- a/lib/src/main/kotlin/Tree/BINtree/BINTree.kt +++ b/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt @@ -1,7 +1,7 @@ -package BINtree +package TreeLib.BINtree -import AbstractTree.Tree -import Single_Objects.Container +import TreeLib.AbstractTree.Tree +import TreeLib.Single_Objects.Container class BINTree, Value> : Tree>>() { override val treeStruct: BINStruct> = BINStruct() diff --git a/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt similarity index 69% rename from lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt rename to lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt index 4d94b63..30a0488 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt @@ -1,7 +1,6 @@ -package RBtree +package TreeLib.RBtree -import AVLtree.AVLNode -import AbstractTree.Weighted.BalancerParent +import TreeLib.AbstractTree.Weighted.BalancerParent class RBBalancer>: BalancerParent>() { override fun balance(node: RBNode): RBNode { diff --git a/lib/src/main/kotlin/Tree/RBtree/RBNode.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt similarity index 61% rename from lib/src/main/kotlin/Tree/RBtree/RBNode.kt rename to lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt index f1a2565..ab2bf77 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBNode.kt +++ b/lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt @@ -1,12 +1,12 @@ -package RBtree +package TreeLib.RBtree -import AbstractTree.Node -import Single_Objects.Markers +import TreeLib.AbstractTree.Node +import TreeLib.Single_Objects.Markers class RBNode>( override var value: Pack?, override var left: RBNode?, override var right: RBNode?, - var parent: RBNode, + var parent: RBNode?, var color: Markers, ) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/Tree/RBtree/RBStruct.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt similarity index 71% rename from lib/src/main/kotlin/Tree/RBtree/RBStruct.kt rename to lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt index 975e2b1..1327be1 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBStruct.kt +++ b/lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt @@ -1,6 +1,6 @@ -package RBtree +package TreeLib.RBtree -import AbstractTree.Weighted.WeightedTreeStruct +import TreeLib.AbstractTree.Weighted.WeightedTreeStruct class RBStruct > : WeightedTreeStruct, RBBalancer>(){ override var root: RBNode? = null diff --git a/lib/src/main/kotlin/Tree/RBtree/RBTree.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt similarity index 64% rename from lib/src/main/kotlin/Tree/RBtree/RBTree.kt rename to lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt index c0d179c..bd103a6 100644 --- a/lib/src/main/kotlin/Tree/RBtree/RBTree.kt +++ b/lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt @@ -1,8 +1,8 @@ -package RBtree +package TreeLib.RBtree -import AbstractTree.Tree +import TreeLib.AbstractTree.Tree -import Single_Objects.Container +import TreeLib.Single_Objects.Container class RBTree, Value> : Tree>>() { override val treeStruct: RBStruct> = RBStruct() diff --git a/lib/src/main/kotlin/Tree/Single_Objects/Container.kt b/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt similarity index 94% rename from lib/src/main/kotlin/Tree/Single_Objects/Container.kt rename to lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt index 2ca7fe5..06e4a9c 100644 --- a/lib/src/main/kotlin/Tree/Single_Objects/Container.kt +++ b/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt @@ -1,4 +1,4 @@ -package Single_Objects +package TreeLib.Single_Objects class Container, V>(private val pair: Pair) : Comparable> { diff --git a/lib/src/main/kotlin/Tree/Single_Objects/Markers.kt b/lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt similarity index 55% rename from lib/src/main/kotlin/Tree/Single_Objects/Markers.kt rename to lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt index 4bd9c2e..1d880d1 100644 --- a/lib/src/main/kotlin/Tree/Single_Objects/Markers.kt +++ b/lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt @@ -1,4 +1,4 @@ -package Single_Objects +package TreeLib.Single_Objects enum class Markers { RED, BLACK diff --git a/settings.gradle.kts b/settings.gradle.kts index e5425d5..21643d4 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 = "Tree" +rootProject.name = "TreeLib" include("lib") From d65d76a384e8d5044192e34ac96e227e8ba93eae Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 22 Mar 2023 19:56:55 +0300 Subject: [PATCH 030/212] feat(Tree/TreeStruct): Implemented some methods including in/pre/post order --- .gitignore | 3 + .../main/kotlin/TreeLib/AbstractTree/Node.kt | 2 +- .../main/kotlin/TreeLib/AbstractTree/Tree.kt | 40 +++++---- .../kotlin/TreeLib/AbstractTree/TreeStruct.kt | 82 ++++++++++++++++--- .../TreeLib/Single_Objects/Container.kt | 1 + 5 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a26642 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.gradle/ +/.idea/ + diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt index fbb7462..b239e74 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt @@ -1,7 +1,7 @@ package TreeLib.AbstractTree interface Node, SubNode : Node>{ - var value: Pack? + var value: Pack var left: SubNode? var right: SubNode? } diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt index b4fb4c2..8e97f66 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt @@ -2,34 +2,42 @@ package TreeLib.AbstractTree import TreeLib.Single_Objects.Container -abstract class Tree, Value, NodeType: Node, NodeType>> { - protected abstract val treeStruct: TreeStruct, NodeType> +abstract class Tree, V, NodeType: Node, NodeType>> { + protected abstract val treeStruct: TreeStruct, NodeType> + var size: ULong = 0u - fun putItem(item: Pair){ -// TODO("Not yet implemented") + fun putItem(item: Pair){ + treeStruct.insert(Container(item)) + TODO("change size (if the added element is not a duplicate)") } - fun putItems(vararg items: Pair) { - TODO("Not yet implemented") + fun putItems(vararg items: Pair) { + for (item in items) { + putItem(item) + } } - fun deleteItem(key: Key){ - TODO("Not yet implemented") + fun deleteItem(key: K){ + treeStruct.delete( Container(key to null) ) + TODO("change size (if the deleted item is not a duplicate)") } - fun getItem(key: Key) { - TODO("Not yet implemented") + fun getItem(key: K) { + treeStruct.find( Container(key to null) ) } - fun inOrder() { - TODO("Not yet implemented") + fun inOrder(): List { + // https://stackoverflow.com/questions/46869353/kotlin-generics-arrayt-results-in-cannot-use-t-as-a-reified-type-parameter-u + return treeStruct.inOrder().map { it.key } } - fun preOrder() { - TODO("Not yet implemented") + fun preOrder(): List { + // Хорошо бы подумать над тем, чтобы array возвращать, хотя list вроде расширяет iterable, так что обход + // все равно O(n) будет + return treeStruct.preOrder().map { it.key } } - fun postOrder() { - TODO("Not yet implemented") + fun postOrder(): List { + return treeStruct.postOrder().map { it.key } } } \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt index 2e800d3..18f4539 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt @@ -1,22 +1,84 @@ package TreeLib.AbstractTree -abstract class TreeStruct, NodeType : Node> { +import sun.security.ec.point.ProjectivePoint.Mutable + +abstract class TreeStruct, NodeType : Node> { protected abstract var root: NodeType? //TODO root - not a null - fun find(obj: Pack): Pack?{ + fun find(obj: T): T?{ TODO("Сделать реализацию поиска, дергать реализацию из локализированного класса") } - fun inOrder(){ - TODO("inOrder - implementation") + fun inOrder(): MutableList { + val arrayNodes = mutableListOf() + var flagVisited = 0 + var current = root + val parents = ArrayDeque() + while(current != null) { + while(current!!.left != null && flagVisited == 0) { + parents.add(current) + current = current.left + } + arrayNodes.add(current.value) + if (current.right != null) { + flagVisited = 0 + current = current.right + } + else { + if (parents.isEmpty()) + break + flagVisited = 1 + current = parents.removeLast() + } + } + return arrayNodes } - fun postOrder(){ - TODO("postOrder - implementation") + + fun postOrder(): MutableList{ + val parents = ArrayDeque() + val arrayNodes = mutableListOf() + var flagVisited = 0 + var current = root + while(current != null) { + while (current!!.left != null && flagVisited == 0) { + parents.add(current) + current = current.left + } + if (current.right != null && flagVisited != 2) { + parents.add(current) + current = current.right + flagVisited = 0 + } + else { + arrayNodes.add(current.value) + if (parents.isEmpty()) + break + val parent = parents.removeLast() + if (parent.right == current) { + flagVisited = 2 + } + current = parent + } + } + return arrayNodes } - fun preOrder(){ - TODO("preOrder - implementation") + + fun preOrder(): MutableList{ + val arrayNodes = mutableListOf() + var current: NodeType + val queue = ArrayDeque() + queue.add(root!!) + while (queue.isNotEmpty()) { + current = queue.removeLast() + arrayNodes.add(current.value) + if (current.right != null) + queue.add(current.right!!) + if (current.left != null) + queue.add(current.left!!) + } + return arrayNodes } - abstract fun insert(item: Pack) - abstract fun delete(item: Pack) + abstract fun insert(item: T) + abstract fun delete(item: T) } diff --git a/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt b/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt index 06e4a9c..0ef5d55 100644 --- a/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt +++ b/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt @@ -1,6 +1,7 @@ package TreeLib.Single_Objects class Container, V>(private val pair: Pair) : Comparable> { + val key = pair.first override fun equals(other: Any?): Boolean { if (this === other) From 1551e372c67bf52b11e51a6ff58a361182f8a5c2 Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 24 Mar 2023 02:06:42 +0300 Subject: [PATCH 031/212] feat(AVLBalancer): Implement methods for AVLT balance --- .gitignore | 1 - .../kotlin/TreeLib/AVLtree/AVLBalancer.kt | 54 +++++++++++++++++-- .../main/kotlin/TreeLib/AVLtree/AVLNode.kt | 8 +-- .../main/kotlin/TreeLib/AVLtree/AVLStruct.kt | 2 +- .../main/kotlin/TreeLib/AVLtree/AVLTree.kt | 6 +-- .../AbstractTree/Weighted/BalancerNoParent.kt | 11 +++- .../Weighted/WeightedTreeStruct.kt | 4 +- .../main/kotlin/TreeLib/BINtree/BINNode.kt | 2 +- .../main/kotlin/TreeLib/BINtree/BINTree.kt | 4 +- 9 files changed, 74 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 8a26642..53f6083 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /.gradle/ /.idea/ - diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt index 1078742..31f0264 100644 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt @@ -1,9 +1,55 @@ package TreeLib.AVLtree import TreeLib.AbstractTree.Weighted.BalancerNoParent +// gradle init +class AVLBalancer>(private var root: AVLNode?): BalancerNoParent>() { + private fun updateBalance(node: AVLNode?): Int { + return (getHeight(node?.left) - getHeight(node?.right)).toInt() -class AVLBalancer>: BalancerNoParent>() { - override fun balance(node: AVLNode): AVLNode { - TODO("Not yet implemented") } -} \ No newline at end of file + + private fun getHeight(currentNode: AVLNode?): UInt { + return currentNode?.height ?: 0u + } + + private fun updateHeight(currentNode: AVLNode?): UInt { + return if (currentNode == null) 0u else ( maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u) + } + + override fun balance(node: AVLNode): AVLNode = balance(root, node.value) + + private fun balance(currentNode: AVLNode?, value: Pack): AVLNode { + if (currentNode == null) { + throw NullPointerException() + } + when { + currentNode.value < value -> currentNode.right = balance(currentNode.right, value) + currentNode.value > value -> currentNode.left = balance(currentNode.left, value) + currentNode.value == value -> return currentNode + } + currentNode.height = updateHeight(currentNode) + val balance = updateBalance(currentNode) + if (balance == -2) { + if (updateBalance(currentNode.right) == 1) { + currentNode.right = currentNode.right?.let { rightRotate(it) } ?: throw NullPointerException() + updateHeight(currentNode.right?.right) + } + val balancedNode = leftRotate(currentNode) + updateHeight(balancedNode.left) + updateHeight(balancedNode) + return balancedNode + } + if (balance == 2) { + if (updateBalance(currentNode.left) == -1) { + currentNode.left = currentNode.left?.let { leftRotate(it) } ?: throw NullPointerException() + updateHeight(currentNode.left?.left) + } + val balanceNode = rightRotate(currentNode) + updateHeight(currentNode.right) + updateHeight(currentNode) + return balanceNode + } + return currentNode + } + +} diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt index d92d1d3..50fd50f 100644 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt @@ -3,8 +3,10 @@ package TreeLib.AVLtree import TreeLib.AbstractTree.Node class AVLNode>( - override var value: Pack?, + override var value: Pack, override var left: AVLNode?, override var right: AVLNode?, - var height:Int, -) : Node> \ No newline at end of file + var height: UInt = 1u, +) : Node> { + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt index 41093b5..d725523 100644 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt @@ -4,5 +4,5 @@ import TreeLib.AbstractTree.Weighted.WeightedTreeStruct class AVLStruct> : WeightedTreeStruct, AVLBalancer>() { override var root: AVLNode? = null - override val balancer = AVLBalancer() + override val balancer = AVLBalancer(root) } \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt b/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt index 0c8f284..0ea3cb1 100644 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt +++ b/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt @@ -4,7 +4,7 @@ import TreeLib.AbstractTree.Tree import TreeLib.Single_Objects.Container -class AVLTree, Value> : - Tree>>() { - override val treeStruct: AVLStruct> = AVLStruct() +class AVLTree, V> : + Tree>>() { + override val treeStruct: AVLStruct> = AVLStruct() } \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt index f7113dc..7176fe8 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt @@ -4,10 +4,17 @@ import TreeLib.AbstractTree.Node abstract class BalancerNoParent, NodeType : Node>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { - TODO("Not yet implemented") + // вероятно можем полагаться на то, что сын не будет null + val leftSon = currentNode.left!! + currentNode.left = leftSon.right + leftSon.right = currentNode + return leftSon } override fun leftRotate(currentNode: NodeType): NodeType { - TODO("Not yet implemented") + val rightSon = currentNode.right!! + currentNode.right = rightSon.left + rightSon.left = currentNode + return rightSon } } \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt index 31cf644..83df9ea 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt +++ b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt @@ -23,10 +23,12 @@ abstract class WeightedTreeStruct, NodeType : Node>( - override var value: Pack?, + override var value: Pack, override var left: BINNode?, override var right: BINNode? ) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt b/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt index fd34f78..39d4c02 100644 --- a/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt +++ b/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt @@ -3,6 +3,6 @@ package TreeLib.BINtree import TreeLib.AbstractTree.Tree import TreeLib.Single_Objects.Container -class BINTree, Value> : Tree>>() { - override val treeStruct: BINStruct> = BINStruct() +class BINTree, V> : Tree>>() { + override val treeStruct: BINStruct> = BINStruct() } \ No newline at end of file From 15c81138d56a8ba9defed3764ffa78dc10c49e78 Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 24 Mar 2023 16:15:17 +0300 Subject: [PATCH 032/212] feat(BalancerParent): Implement right/left rotate methods --- .../AbstractTree/Weighted/BalancerParent.kt | 13 ------ lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt | 12 ------ .../AVLtree/AVLBalancer.kt | 4 +- .../{TreeLib => treeLib}/AVLtree/AVLNode.kt | 4 +- .../{TreeLib => treeLib}/AVLtree/AVLStruct.kt | 4 +- .../{TreeLib => treeLib}/AVLtree/AVLTree.kt | 6 +-- .../{TreeLib => treeLib}/AbstractTree/Node.kt | 2 +- .../{TreeLib => treeLib}/AbstractTree/Tree.kt | 4 +- .../AbstractTree/TreeStruct.kt | 4 +- .../AbstractTree/Weighted/Balancer.kt | 4 +- .../AbstractTree/Weighted/BalancerNoParent.kt | 8 ++-- .../AbstractTree/Weighted/BalancerParent.kt | 40 +++++++++++++++++++ .../Weighted/WeightedTreeStruct.kt | 6 +-- .../{TreeLib => treeLib}/BINtree/BINNode.kt | 4 +- .../{TreeLib => treeLib}/BINtree/BINStruct.kt | 4 +- .../{TreeLib => treeLib}/BINtree/BINTree.kt | 6 +-- .../{TreeLib => treeLib}/RBtree/RBBalancer.kt | 4 +- lib/src/main/kotlin/treeLib/RBtree/RBNode.kt | 12 ++++++ .../{TreeLib => treeLib}/RBtree/RBStruct.kt | 4 +- .../{TreeLib => treeLib}/RBtree/RBTree.kt | 6 +-- .../Single_Objects/Container.kt | 2 +- .../Single_Objects/Markers.kt | 2 +- 22 files changed, 90 insertions(+), 65 deletions(-) delete mode 100644 lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt delete mode 100644 lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt rename lib/src/main/kotlin/{TreeLib => treeLib}/AVLtree/AVLBalancer.kt (96%) rename lib/src/main/kotlin/{TreeLib => treeLib}/AVLtree/AVLNode.kt (78%) rename lib/src/main/kotlin/{TreeLib => treeLib}/AVLtree/AVLStruct.kt (72%) rename lib/src/main/kotlin/{TreeLib => treeLib}/AVLtree/AVLTree.kt (61%) rename lib/src/main/kotlin/{TreeLib => treeLib}/AbstractTree/Node.kt (90%) rename lib/src/main/kotlin/{TreeLib => treeLib}/AbstractTree/Tree.kt (95%) rename lib/src/main/kotlin/{TreeLib => treeLib}/AbstractTree/TreeStruct.kt (96%) rename lib/src/main/kotlin/{TreeLib => treeLib}/AbstractTree/Weighted/Balancer.kt (76%) rename lib/src/main/kotlin/{TreeLib => treeLib}/AbstractTree/Weighted/BalancerNoParent.kt (72%) create mode 100644 lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt rename lib/src/main/kotlin/{TreeLib => treeLib}/AbstractTree/Weighted/WeightedTreeStruct.kt (86%) rename lib/src/main/kotlin/{TreeLib => treeLib}/BINtree/BINNode.kt (75%) rename lib/src/main/kotlin/{TreeLib => treeLib}/BINtree/BINStruct.kt (82%) rename lib/src/main/kotlin/{TreeLib => treeLib}/BINtree/BINTree.kt (61%) rename lib/src/main/kotlin/{TreeLib => treeLib}/RBtree/RBBalancer.kt (71%) create mode 100644 lib/src/main/kotlin/treeLib/RBtree/RBNode.kt rename lib/src/main/kotlin/{TreeLib => treeLib}/RBtree/RBStruct.kt (71%) rename lib/src/main/kotlin/{TreeLib => treeLib}/RBtree/RBTree.kt (64%) rename lib/src/main/kotlin/{TreeLib => treeLib}/Single_Objects/Container.kt (94%) rename lib/src/main/kotlin/{TreeLib => treeLib}/Single_Objects/Markers.kt (55%) diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt b/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt deleted file mode 100644 index 4016b35..0000000 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerParent.kt +++ /dev/null @@ -1,13 +0,0 @@ -package TreeLib.AbstractTree.Weighted - -import TreeLib.AbstractTree.Node - -abstract class BalancerParent, NodeType : Node>: Balancer { - override fun rightRotate(currentNode: NodeType): NodeType { - TODO("Not yet implemented") - } - - override fun leftRotate(currentNode: NodeType): NodeType { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt b/lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt deleted file mode 100644 index ab2bf77..0000000 --- a/lib/src/main/kotlin/TreeLib/RBtree/RBNode.kt +++ /dev/null @@ -1,12 +0,0 @@ -package TreeLib.RBtree - -import TreeLib.AbstractTree.Node -import TreeLib.Single_Objects.Markers - -class RBNode>( - override var value: Pack?, - override var left: RBNode?, - override var right: RBNode?, - var parent: RBNode?, - var color: Markers, -) : Node> \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt similarity index 96% rename from lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt rename to lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt index 31f0264..53f1e0f 100644 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt @@ -1,6 +1,6 @@ -package TreeLib.AVLtree +package treeLib.AVLtree -import TreeLib.AbstractTree.Weighted.BalancerNoParent +import treeLib.AbstractTree.Weighted.BalancerNoParent // gradle init class AVLBalancer>(private var root: AVLNode?): BalancerNoParent>() { private fun updateBalance(node: AVLNode?): Int { diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt b/lib/src/main/kotlin/treeLib/AVLtree/AVLNode.kt similarity index 78% rename from lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt rename to lib/src/main/kotlin/treeLib/AVLtree/AVLNode.kt index 50fd50f..c858373 100644 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLNode.kt +++ b/lib/src/main/kotlin/treeLib/AVLtree/AVLNode.kt @@ -1,6 +1,6 @@ -package TreeLib.AVLtree +package treeLib.AVLtree -import TreeLib.AbstractTree.Node +import treeLib.AbstractTree.Node class AVLNode>( override var value: Pack, diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt b/lib/src/main/kotlin/treeLib/AVLtree/AVLStruct.kt similarity index 72% rename from lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt rename to lib/src/main/kotlin/treeLib/AVLtree/AVLStruct.kt index d725523..f7b167a 100644 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLStruct.kt +++ b/lib/src/main/kotlin/treeLib/AVLtree/AVLStruct.kt @@ -1,6 +1,6 @@ -package TreeLib.AVLtree +package treeLib.AVLtree -import TreeLib.AbstractTree.Weighted.WeightedTreeStruct +import treeLib.AbstractTree.Weighted.WeightedTreeStruct class AVLStruct> : WeightedTreeStruct, AVLBalancer>() { override var root: AVLNode? = null diff --git a/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt b/lib/src/main/kotlin/treeLib/AVLtree/AVLTree.kt similarity index 61% rename from lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt rename to lib/src/main/kotlin/treeLib/AVLtree/AVLTree.kt index 0ea3cb1..15205b4 100644 --- a/lib/src/main/kotlin/TreeLib/AVLtree/AVLTree.kt +++ b/lib/src/main/kotlin/treeLib/AVLtree/AVLTree.kt @@ -1,8 +1,8 @@ -package TreeLib.AVLtree +package treeLib.AVLtree -import TreeLib.AbstractTree.Tree +import treeLib.AbstractTree.Tree -import TreeLib.Single_Objects.Container +import treeLib.Single_Objects.Container class AVLTree, V> : Tree>>() { diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Node.kt similarity index 90% rename from lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt rename to lib/src/main/kotlin/treeLib/AbstractTree/Node.kt index b239e74..48c022a 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Node.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Node.kt @@ -1,4 +1,4 @@ -package TreeLib.AbstractTree +package treeLib.AbstractTree interface Node, SubNode : Node>{ var value: Pack diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Tree.kt similarity index 95% rename from lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt rename to lib/src/main/kotlin/treeLib/AbstractTree/Tree.kt index 8e97f66..0ab75da 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Tree.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Tree.kt @@ -1,6 +1,6 @@ -package TreeLib.AbstractTree +package treeLib.AbstractTree -import TreeLib.Single_Objects.Container +import treeLib.Single_Objects.Container abstract class Tree, V, NodeType: Node, NodeType>> { protected abstract val treeStruct: TreeStruct, NodeType> diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt b/lib/src/main/kotlin/treeLib/AbstractTree/TreeStruct.kt similarity index 96% rename from lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt rename to lib/src/main/kotlin/treeLib/AbstractTree/TreeStruct.kt index 18f4539..9cc9fa5 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/TreeStruct.kt @@ -1,6 +1,4 @@ -package TreeLib.AbstractTree - -import sun.security.ec.point.ProjectivePoint.Mutable +package treeLib.AbstractTree abstract class TreeStruct, NodeType : Node> { protected abstract var root: NodeType? //TODO root - not a null diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/Balancer.kt similarity index 76% rename from lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt rename to lib/src/main/kotlin/treeLib/AbstractTree/Weighted/Balancer.kt index 9412b1c..8df7518 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/Balancer.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/Balancer.kt @@ -1,6 +1,6 @@ -package TreeLib.AbstractTree.Weighted +package treeLib.AbstractTree.Weighted -import TreeLib.AbstractTree.Node +import treeLib.AbstractTree.Node interface Balancer, NodeType : Node> { fun rightRotate(currentNode: NodeType): NodeType diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerNoParent.kt similarity index 72% rename from lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt rename to lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerNoParent.kt index 7176fe8..bd96b8a 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/BalancerNoParent.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerNoParent.kt @@ -1,18 +1,18 @@ -package TreeLib.AbstractTree.Weighted +package treeLib.AbstractTree.Weighted -import TreeLib.AbstractTree.Node +import treeLib.AbstractTree.Node abstract class BalancerNoParent, NodeType : Node>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { // вероятно можем полагаться на то, что сын не будет null - val leftSon = currentNode.left!! + val leftSon = currentNode.left ?: throw NullPointerException() currentNode.left = leftSon.right leftSon.right = currentNode return leftSon } override fun leftRotate(currentNode: NodeType): NodeType { - val rightSon = currentNode.right!! + val rightSon = currentNode.right ?: throw NullPointerException() currentNode.right = rightSon.left rightSon.left = currentNode return rightSon diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt new file mode 100644 index 0000000..0dcef2e --- /dev/null +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt @@ -0,0 +1,40 @@ +package treeLib.AbstractTree.Weighted + +import treeLib.AbstractTree.NodeParent + +abstract class BalancerParent, NodeType : NodeParent>: Balancer { + override fun rightRotate(currentNode: NodeType): NodeType { + // пока верим в то, что currentNode не равно null + val leftChild = currentNode.left ?: throw NullPointerException() + val parent = currentNode.parent + leftChild.right?.parent = currentNode + leftChild.right = currentNode + currentNode.left = leftChild.right + + when { + parent?.left == currentNode -> parent.left = leftChild + parent?.right == currentNode -> parent.right = leftChild + } + currentNode.parent = leftChild + leftChild.parent = parent + return leftChild + } + + override fun leftRotate(currentNode: NodeType): NodeType { + val rightChild = currentNode.right ?: throw NullPointerException() + val parent = currentNode.parent + + rightChild.left?.parent = currentNode + rightChild.left = currentNode + currentNode.right = rightChild.left + + when { + parent?.left == currentNode -> parent.left = rightChild + parent?.right == currentNode -> parent.right = rightChild + } + currentNode.parent = rightChild + rightChild.parent = parent + return rightChild + } + +} diff --git a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt similarity index 86% rename from lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt rename to lib/src/main/kotlin/treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt index 83df9ea..ccba11a 100644 --- a/lib/src/main/kotlin/TreeLib/AbstractTree/Weighted/WeightedTreeStruct.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt @@ -1,7 +1,7 @@ -package TreeLib.AbstractTree.Weighted +package treeLib.AbstractTree.Weighted -import TreeLib.AbstractTree.Node -import TreeLib.AbstractTree.TreeStruct +import treeLib.AbstractTree.Node +import treeLib.AbstractTree.TreeStruct abstract class WeightedTreeStruct, NodeType : Node, BalancerType: Balancer>: TreeStruct() { diff --git a/lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt b/lib/src/main/kotlin/treeLib/BINtree/BINNode.kt similarity index 75% rename from lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt rename to lib/src/main/kotlin/treeLib/BINtree/BINNode.kt index a0dfbcb..9db9413 100644 --- a/lib/src/main/kotlin/TreeLib/BINtree/BINNode.kt +++ b/lib/src/main/kotlin/treeLib/BINtree/BINNode.kt @@ -1,6 +1,6 @@ -package TreeLib.BINtree +package treeLib.BINtree -import TreeLib.AbstractTree.Node +import treeLib.AbstractTree.Node class BINNode>( override var value: Pack, diff --git a/lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt b/lib/src/main/kotlin/treeLib/BINtree/BINStruct.kt similarity index 82% rename from lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt rename to lib/src/main/kotlin/treeLib/BINtree/BINStruct.kt index ebd3008..469a15c 100644 --- a/lib/src/main/kotlin/TreeLib/BINtree/BINStruct.kt +++ b/lib/src/main/kotlin/treeLib/BINtree/BINStruct.kt @@ -1,6 +1,6 @@ -package TreeLib.BINtree +package treeLib.BINtree -import TreeLib.AbstractTree.TreeStruct +import treeLib.AbstractTree.TreeStruct class BINStruct> : TreeStruct>() { override var root: BINNode? = null diff --git a/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt b/lib/src/main/kotlin/treeLib/BINtree/BINTree.kt similarity index 61% rename from lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt rename to lib/src/main/kotlin/treeLib/BINtree/BINTree.kt index 39d4c02..e5c0841 100644 --- a/lib/src/main/kotlin/TreeLib/BINtree/BINTree.kt +++ b/lib/src/main/kotlin/treeLib/BINtree/BINTree.kt @@ -1,7 +1,7 @@ -package TreeLib.BINtree +package treeLib.BINtree -import TreeLib.AbstractTree.Tree -import TreeLib.Single_Objects.Container +import treeLib.AbstractTree.Tree +import treeLib.Single_Objects.Container class BINTree, V> : Tree>>() { override val treeStruct: BINStruct> = BINStruct() diff --git a/lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt similarity index 71% rename from lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt rename to lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt index 30a0488..c9af93a 100644 --- a/lib/src/main/kotlin/TreeLib/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt @@ -1,6 +1,6 @@ -package TreeLib.RBtree +package treeLib.RBtree -import TreeLib.AbstractTree.Weighted.BalancerParent +import treeLib.AbstractTree.Weighted.BalancerParent class RBBalancer>: BalancerParent>() { override fun balance(node: RBNode): RBNode { diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBNode.kt b/lib/src/main/kotlin/treeLib/RBtree/RBNode.kt new file mode 100644 index 0000000..8b44097 --- /dev/null +++ b/lib/src/main/kotlin/treeLib/RBtree/RBNode.kt @@ -0,0 +1,12 @@ +package treeLib.RBtree + +import treeLib.AbstractTree.NodeParent +import treeLib.Single_Objects.Markers + +class RBNode>( + override var value: Pack, + override var left: RBNode?, + override var right: RBNode?, + override var parent: RBNode?, + var color: Markers, +) : NodeParent> \ No newline at end of file diff --git a/lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt b/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt similarity index 71% rename from lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt rename to lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt index 1327be1..ae5f4c6 100644 --- a/lib/src/main/kotlin/TreeLib/RBtree/RBStruct.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt @@ -1,6 +1,6 @@ -package TreeLib.RBtree +package treeLib.RBtree -import TreeLib.AbstractTree.Weighted.WeightedTreeStruct +import treeLib.AbstractTree.Weighted.WeightedTreeStruct class RBStruct > : WeightedTreeStruct, RBBalancer>(){ override var root: RBNode? = null diff --git a/lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt b/lib/src/main/kotlin/treeLib/RBtree/RBTree.kt similarity index 64% rename from lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt rename to lib/src/main/kotlin/treeLib/RBtree/RBTree.kt index bd103a6..798ed57 100644 --- a/lib/src/main/kotlin/TreeLib/RBtree/RBTree.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBTree.kt @@ -1,8 +1,8 @@ -package TreeLib.RBtree +package treeLib.RBtree -import TreeLib.AbstractTree.Tree +import treeLib.AbstractTree.Tree -import TreeLib.Single_Objects.Container +import treeLib.Single_Objects.Container class RBTree, Value> : Tree>>() { override val treeStruct: RBStruct> = RBStruct() diff --git a/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt b/lib/src/main/kotlin/treeLib/Single_Objects/Container.kt similarity index 94% rename from lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt rename to lib/src/main/kotlin/treeLib/Single_Objects/Container.kt index 0ef5d55..9c1511c 100644 --- a/lib/src/main/kotlin/TreeLib/Single_Objects/Container.kt +++ b/lib/src/main/kotlin/treeLib/Single_Objects/Container.kt @@ -1,4 +1,4 @@ -package TreeLib.Single_Objects +package treeLib.Single_Objects class Container, V>(private val pair: Pair) : Comparable> { val key = pair.first diff --git a/lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt b/lib/src/main/kotlin/treeLib/Single_Objects/Markers.kt similarity index 55% rename from lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt rename to lib/src/main/kotlin/treeLib/Single_Objects/Markers.kt index 1d880d1..2e7935a 100644 --- a/lib/src/main/kotlin/TreeLib/Single_Objects/Markers.kt +++ b/lib/src/main/kotlin/treeLib/Single_Objects/Markers.kt @@ -1,4 +1,4 @@ -package TreeLib.Single_Objects +package treeLib.Single_Objects enum class Markers { RED, BLACK From 81c26d644f2f0791a5552287413b1780cbbcb304 Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 24 Mar 2023 16:22:55 +0300 Subject: [PATCH 033/212] feat: Create NodeParent interface --- lib/src/main/kotlin/treeLib/AbstractTree/NodeParent.kt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 lib/src/main/kotlin/treeLib/AbstractTree/NodeParent.kt diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/NodeParent.kt b/lib/src/main/kotlin/treeLib/AbstractTree/NodeParent.kt new file mode 100644 index 0000000..2f01403 --- /dev/null +++ b/lib/src/main/kotlin/treeLib/AbstractTree/NodeParent.kt @@ -0,0 +1,8 @@ +package treeLib.AbstractTree + + +interface NodeParent, SubNode: NodeParent>: Node { + var parent: SubNode? + override var right: SubNode? + override var left: SubNode? +} \ No newline at end of file From c837d37fad71ed9aa42f1e1712e3d7b6d981c0ca Mon Sep 17 00:00:00 2001 From: Artem Date: Sun, 26 Mar 2023 17:18:59 +0300 Subject: [PATCH 034/212] feat(RBBalancer): Implement balance method --- .../kotlin/treeLib/AVLtree/AVLBalancer.kt | 2 +- .../main/kotlin/treeLib/RBtree/RBBalancer.kt | 323 +++++++++++++++++- .../main/kotlin/treeLib/RBtree/RBStruct.kt | 2 +- 3 files changed, 323 insertions(+), 4 deletions(-) diff --git a/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt index 53f1e0f..3e97a02 100644 --- a/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt @@ -17,7 +17,7 @@ class AVLBalancer>(private var root: AVLNode?): Bal } override fun balance(node: AVLNode): AVLNode = balance(root, node.value) - + // В баланс передаем родителя ноды, которую будем удалять private fun balance(currentNode: AVLNode?, value: Pack): AVLNode { if (currentNode == null) { throw NullPointerException() diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt index c9af93a..880a2aa 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt @@ -1,9 +1,328 @@ package treeLib.RBtree import treeLib.AbstractTree.Weighted.BalancerParent +import treeLib.Single_Objects.Markers + +class RBBalancer>(private var root: RBNode?): BalancerParent>() { + + private fun getUncle(node: RBNode): RBNode? { + val parent = node.parent + return when (parent?.parent?.left) { + parent -> parent?.parent?.right + else -> parent?.parent?.left + } + } + + private fun getBrother(parent: RBNode?, node: RBNode?): RBNode? { + return if (parent?.right == node) + parent?.left + else + parent?.right + } + + private fun getRoot(node: RBNode): RBNode { + var currentNode = node + while (currentNode.parent != null) + currentNode = currentNode.parent ?: throw NullPointerException() + root = currentNode + root?.color = Markers.BLACK + return currentNode + } + private fun nodeIsLeaf(node: RBNode?): Boolean { + if (node == null) + throw NullPointerException() + return node.right == null && node.left == null + } -class RBBalancer>: BalancerParent>() { override fun balance(node: RBNode): RBNode { + // Нужно жестко проверить случаи с root = null + val uncle = getUncle(node) + val brother = getBrother(node.parent, node) + when { + // возможно только при вставке листа или если у рута был один красный сын и мы удалили рут + node.color == Markers.RED && node.right == null && node.left == null && + brother?.color != Markers.BLACK && brother?.right?.color != Markers.BLACK-> + { + var currentNode = node + + if (uncle?.color != Markers.BLACK) { + currentNode = afterInsert(node) + } + if (currentNode.parent?.color != Markers.RED) { + return getRoot(currentNode) + } + var parent = currentNode.parent ?: throw NullPointerException() + when (parent) { + parent.parent?.left -> { + if (currentNode == parent.right) { + leftRotate(parent) + currentNode = parent + } + parent = currentNode.parent ?: throw NullPointerException() + 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 ?: throw NullPointerException() + currentNode = leftRotate(parent) + currentNode.color = Markers.BLACK + currentNode.right?.color = Markers.RED + currentNode.left?.color = Markers.RED + } + else -> throw NullPointerException() + } + if (currentNode.parent == null) + root = currentNode + return root ?: throw NullPointerException() + } + // учитывая прошлый случай,возможно только если удаляли черную вершину с одним красным сыном листом,заменили + // ее на сына и передали сына в качестве родителя + node.color == Markers.RED && (node.right != null || node.left != null) -> + { + when { + // удалили черную вершину, у которой один красный сын (черного сына быть не может) + // или же удалили черную вершину, у которой два сына - красные листы и на ее место + // место поставили лист, его же пеоедали в качестве родитедя + node.left?.color == Markers.RED -> { + when (node.right) { + null -> node.color = Markers.BLACK + else -> node.left?.color = Markers.BLACK + } + return getRoot(node) + } + node.right?.color == Markers.RED -> { + node.right?.color = Markers.RED // кинуть в случае чего экс + return getRoot(node) + } + // удалили черный лист + node.right?.color == Markers.BLACK -> { + return firstCase(node, node.left) + } + node.left?.color == Markers.BLACK -> { + return firstCase(node, node.right) + } + } + + } + node.color == Markers.BLACK -> + { + when { + // удалили красный лист + (node.left == null && node.right == null) || + (node.left == null && node.right?.color == Markers.RED && nodeIsLeaf(node.right)) || + (node.right == null && node.left?.color == Markers.RED && nodeIsLeaf(node.left)) -> + { + return root ?: throw NullPointerException() + } + // удалили черный лист + node.left == null -> { + return firstCase(node, null) + + } + node.right == null -> { + return firstCase(node, null) + + } + + } + + } + } TODO("Not yet implemented") } -} \ No newline at end of file + private fun afterInsert(node: RBNode): RBNode { + var currentNode = node + while (currentNode.parent?.color == Markers.RED) { + val uncle = getUncle(currentNode) + if (uncle?.color == Markers.RED) { + currentNode.parent?.color = Markers.BLACK + currentNode = currentNode.parent?.parent ?: throw NullPointerException() + currentNode.color = Markers.RED + uncle.color = Markers.BLACK + } + else if(uncle != null){ + return currentNode + } + } + root?.color = Markers.BLACK + return currentNode + } + + private fun firstCase(parent: RBNode?, node: RBNode?): RBNode { + when { + parent == null && node == null -> throw NullPointerException() + parent != null -> { + when (parent.color) { + Markers.RED -> secondCase(parent, node) + Markers.BLACK -> thirdCase(parent, node) + } + return getRoot(parent) + } + else -> return getRoot(node ?: throw NullPointerException()) + } + } + + // родитель вершины - красный, son - корень дерева с удаленной черной вершиной (поддерева node) + private fun secondCase(parent: RBNode, node: RBNode?) { + var brother = getBrother(parent, node) ?: throw NullPointerException() + // у красной вершины могут быть только черные дети + if (brother.color == Markers.RED) + throw NullPointerException() + // не зависит от того с какой стороны расположены сын и брат + if (brother.right?.color != Markers.RED && brother.left?.color != Markers.RED) { + brother.color = Markers.RED + parent.color = Markers.BLACK + return + } + + when (node) { + parent.left -> + { + 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 if (brother.right?.color == Markers.RED) { + leftRotate(parent) + brother.left?.color = Markers.RED + brother.right?.color = Markers.RED + brother.color = Markers.BLACK + } + else { + throw NullPointerException() + } + } + parent.right -> + { + 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 if (brother.left?.color == Markers.RED) { + rightRotate(parent) + brother.color = Markers.BLACK + brother.left?.color = Markers.RED + brother.right?.color = Markers.RED + + } + else { + throw NullPointerException() + } + + } + else -> throw NullPointerException() + } + } + // родитель вершины - черный + private fun thirdCase(parent: RBNode, node: RBNode?) { + val brother = getBrother(parent, node) ?: throw NullPointerException() + when (brother.color) { + Markers.RED -> thirdCaseSubFirst(brother, parent) + Markers.BLACK -> thirdCaseSubSecond(brother, parent) + } + } + // родитель - черный, брат - красный + private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { + when (brother) { + brother.parent?.left -> + { + var rightBrotherSon = brother.right ?: throw NullPointerException() + + if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { + rightBrotherSon.color = Markers.RED + brother.color = Markers.RED + rightRotate(parent) + return + } + // если правый сын правого сына брата - красный, то делаем его правым сыном брата, а правого сына брата + // левым сыном нового правого сына брата и перекрашиваем его в красный, чтоб сработало след условие + if (rightBrotherSon.right?.color == Markers.RED) { + rightBrotherSon.color = Markers.RED + leftRotate(rightBrotherSon) + + rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() + rightBrotherSon.color = Markers.BLACK + } + + if (rightBrotherSon.left?.color == Markers.RED) { + rightBrotherSon.left?.color = Markers.BLACK + leftRotate(brother) + rightRotate(parent) + } + } + 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()) + return + } + + if (leftBrotherSon.left?.color == Markers.RED) { + rightRotate(leftBrotherSon) + leftBrotherSon.color = Markers.RED + leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() + leftBrotherSon.color = Markers.BLACK + } + + if (leftBrotherSon.right?.color == Markers.RED) { + leftBrotherSon.right?.color = Markers.BLACK + rightRotate(brother) + leftRotate(parent) + } + } + else -> throw NullPointerException() + } + } + + // родитель - черный, брат - черный + private fun thirdCaseSubSecond(brother: RBNode, parent: RBNode) { + // если у брата нет красных детей, запускаем алгоритм заново от родителя, тк высота в поддереве уменьшилась на 1 + if (brother.left?.color != Markers.RED && brother.right?.color != Markers.RED) { + brother.color = Markers.RED + firstCase(parent.parent, parent) + return + } + when { + brother.left?.color == Markers.RED -> + { + brother.left?.color = Markers.BLACK + if (brother == parent.left) { + rightRotate(parent) + } + else { + rightRotate(brother) + leftRotate(parent) + } + } + brother.right?.color == Markers.RED -> + { + brother.right?.color = Markers.BLACK + if (brother == parent.right) { + leftRotate(parent) + } + else { + leftRotate(brother) + rightRotate(parent) + } + + } + else -> throw NullPointerException() + } + } + +} diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt b/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt index ae5f4c6..ef3ccb4 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt @@ -4,6 +4,6 @@ import treeLib.AbstractTree.Weighted.WeightedTreeStruct class RBStruct > : WeightedTreeStruct, RBBalancer>(){ override var root: RBNode? = null - override val balancer = RBBalancer() + override val balancer = RBBalancer(root) } \ No newline at end of file From 57b54ada71b7278be2282ab89ca60e4aa65db83c Mon Sep 17 00:00:00 2001 From: Artem Date: Sun, 26 Mar 2023 23:35:04 +0300 Subject: [PATCH 035/212] fix(BalancerParent): Get rid of node looping in right/left Rotate methods --- .gitignore | 2 ++ .../kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt | 6 +++--- lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 53f6083..7061064 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /.gradle/ /.idea/ +/build/ +/lib/build/ \ No newline at end of file diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt index 0dcef2e..92c1299 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt @@ -6,10 +6,11 @@ abstract class BalancerParent, NodeType : NodeParent parent.left = leftChild @@ -25,9 +26,8 @@ abstract class BalancerParent, NodeType : NodeParent parent.left = rightChild parent?.right == currentNode -> parent.right = rightChild diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt index 880a2aa..75415ed 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt @@ -35,7 +35,6 @@ class RBBalancer>(private var root: RBNode?): Balan } override fun balance(node: RBNode): RBNode { - // Нужно жестко проверить случаи с root = null val uncle = getUncle(node) val brother = getBrother(node.parent, node) when { @@ -134,7 +133,7 @@ class RBBalancer>(private var root: RBNode?): Balan } } - TODO("Not yet implemented") + throw NullPointerException() } private fun afterInsert(node: RBNode): RBNode { var currentNode = node From 53d6525a8f2d0926aa7adde3a94785745be8beb7 Mon Sep 17 00:00:00 2001 From: Artem Date: Sun, 26 Mar 2023 23:41:18 +0300 Subject: [PATCH 036/212] fix(RBBalancer): Change node repainting (del black leaf with black parent) --- lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt index 75415ed..5f4efc6 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt @@ -117,7 +117,7 @@ class RBBalancer>(private var root: RBNode?): Balan (node.left == null && node.right?.color == Markers.RED && nodeIsLeaf(node.right)) || (node.right == null && node.left?.color == Markers.RED && nodeIsLeaf(node.left)) -> { - return root ?: throw NullPointerException() + return root ?: throw NullPointerException() // return getRoot(node) } // удалили черный лист node.left == null -> { @@ -241,7 +241,8 @@ class RBBalancer>(private var root: RBNode?): Balan if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED - brother.color = Markers.RED + //brother.color = Markers.RED + brother.color = Markers.BLACK rightRotate(parent) return } From 7dc3413f0f48344f0d7a67d1cfe6bed3dcbe1820 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 27 Mar 2023 01:39:12 +0300 Subject: [PATCH 037/212] fix(RBBalancer): Change root when removing the old one --- lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt index 5f4efc6..456f82b 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt @@ -117,7 +117,7 @@ class RBBalancer>(private var root: RBNode?): Balan (node.left == null && node.right?.color == Markers.RED && nodeIsLeaf(node.right)) || (node.right == null && node.left?.color == Markers.RED && nodeIsLeaf(node.left)) -> { - return root ?: throw NullPointerException() // return getRoot(node) + return getRoot(node) } // удалили черный лист node.left == null -> { @@ -128,9 +128,7 @@ class RBBalancer>(private var root: RBNode?): Balan return firstCase(node, null) } - } - } } throw NullPointerException() @@ -241,7 +239,6 @@ class RBBalancer>(private var root: RBNode?): Balan if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED - //brother.color = Markers.RED brother.color = Markers.BLACK rightRotate(parent) return From c6597b355d43f2596db33bb48cc053cb5a58198f Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 28 Mar 2023 04:17:36 +0300 Subject: [PATCH 038/212] fix(RBBalancer): Fix logic for checking remote node cases --- .../main/kotlin/treeLib/RBtree/RBBalancer.kt | 77 ++++++++----------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt index 456f82b..2b75c02 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt @@ -28,6 +28,7 @@ class RBBalancer>(private var root: RBNode?): Balan root?.color = Markers.BLACK return currentNode } + private fun nodeIsLeaf(node: RBNode?): Boolean { if (node == null) throw NullPointerException() @@ -36,11 +37,9 @@ class RBBalancer>(private var root: RBNode?): Balan override fun balance(node: RBNode): RBNode { val uncle = getUncle(node) - val brother = getBrother(node.parent, node) when { - // возможно только при вставке листа или если у рута был один красный сын и мы удалили рут - node.color == Markers.RED && node.right == null && node.left == null && - brother?.color != Markers.BLACK && brother?.right?.color != Markers.BLACK-> + /** node insertion case **/ + node.color == Markers.RED && node.right == null && node.left == null-> { var currentNode = node @@ -80,26 +79,11 @@ class RBBalancer>(private var root: RBNode?): Balan root = currentNode return root ?: throw NullPointerException() } - // учитывая прошлый случай,возможно только если удаляли черную вершину с одним красным сыном листом,заменили - // ее на сына и передали сына в качестве родителя + /** node removal cases **/ node.color == Markers.RED && (node.right != null || node.left != null) -> { when { - // удалили черную вершину, у которой один красный сын (черного сына быть не может) - // или же удалили черную вершину, у которой два сына - красные листы и на ее место - // место поставили лист, его же пеоедали в качестве родитедя - node.left?.color == Markers.RED -> { - when (node.right) { - null -> node.color = Markers.BLACK - else -> node.left?.color = Markers.BLACK - } - return getRoot(node) - } - node.right?.color == Markers.RED -> { - node.right?.color = Markers.RED // кинуть в случае чего экс - return getRoot(node) - } - // удалили черный лист + /** black leaf removal case **/ node.right?.color == Markers.BLACK -> { return firstCase(node, node.left) } @@ -111,28 +95,26 @@ class RBBalancer>(private var root: RBNode?): Balan } node.color == Markers.BLACK -> { - when { - // удалили красный лист + return when { + /** red leaf removal case **/ (node.left == null && node.right == null) || (node.left == null && node.right?.color == Markers.RED && nodeIsLeaf(node.right)) || - (node.right == null && node.left?.color == Markers.RED && nodeIsLeaf(node.left)) -> - { - return getRoot(node) + (node.right == null && node.left?.color == Markers.RED && nodeIsLeaf(node.left)) -> { + getRoot(node) } - // удалили черный лист - node.left == null -> { - return firstCase(node, null) + /** black leaf removal case **/ + node.left == null || node.right == null-> { + firstCase(node, null) } - node.right == null -> { - return firstCase(node, null) - } + else -> throw NullPointerException() } } } throw NullPointerException() } + private fun afterInsert(node: RBNode): RBNode { var currentNode = node while (currentNode.parent?.color == Markers.RED) { @@ -151,27 +133,28 @@ class RBBalancer>(private var root: RBNode?): Balan return currentNode } + /** black node removal case **/ private fun firstCase(parent: RBNode?, node: RBNode?): RBNode { - when { + return when { parent == null && node == null -> throw NullPointerException() parent != null -> { when (parent.color) { Markers.RED -> secondCase(parent, node) Markers.BLACK -> thirdCase(parent, node) } - return getRoot(parent) + getRoot(parent) } - else -> return getRoot(node ?: throw NullPointerException()) + + else -> getRoot(node ?: throw NullPointerException()) } } - // родитель вершины - красный, son - корень дерева с удаленной черной вершиной (поддерева node) + /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { var brother = getBrother(parent, node) ?: throw NullPointerException() - // у красной вершины могут быть только черные дети if (brother.color == Markers.RED) throw NullPointerException() - // не зависит от того с какой стороны расположены сын и брат + if (brother.right?.color != Markers.RED && brother.left?.color != Markers.RED) { brother.color = Markers.RED parent.color = Markers.BLACK @@ -222,7 +205,8 @@ class RBBalancer>(private var root: RBNode?): Balan else -> throw NullPointerException() } } - // родитель вершины - черный + + /** parent is black **/ private fun thirdCase(parent: RBNode, node: RBNode?) { val brother = getBrother(parent, node) ?: throw NullPointerException() when (brother.color) { @@ -230,7 +214,8 @@ class RBBalancer>(private var root: RBNode?): Balan Markers.BLACK -> thirdCaseSubSecond(brother, parent) } } - // родитель - черный, брат - красный + + /** black parent and red brother **/ private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { brother.parent?.left -> @@ -243,8 +228,10 @@ class RBBalancer>(private var root: RBNode?): Balan rightRotate(parent) return } - // если правый сын правого сына брата - красный, то делаем его правым сыном брата, а правого сына брата - // левым сыном нового правого сына брата и перекрашиваем его в красный, чтоб сработало след условие + /** if the right son of the right son of the brother is red, then we make it the right son + * of the brother, and the right son of the brother is the left son of the new right son + * of the brother and repaint it red so that the following condition works **/ + if (rightBrotherSon.right?.color == Markers.RED) { rightBrotherSon.color = Markers.RED leftRotate(rightBrotherSon) @@ -286,9 +273,11 @@ class RBBalancer>(private var root: RBNode?): Balan } } - // родитель - черный, брат - черный + /** black parent and black brother **/ private fun thirdCaseSubSecond(brother: RBNode, parent: RBNode) { - // если у брата нет красных детей, запускаем алгоритм заново от родителя, тк высота в поддереве уменьшилась на 1 + /** if the brother hasn't read children, restart + * from the parent (the height in the subtree has decreased by 1) **/ + if (brother.left?.color != Markers.RED && brother.right?.color != Markers.RED) { brother.color = Markers.RED firstCase(parent.parent, parent) From d665547e655be321bcbfc0e2ac2c5ab50f88e563 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 28 Mar 2023 21:23:36 +0300 Subject: [PATCH 039/212] fix(RBBalancer): Optimize balancing (change 2 rotations to 1 --- .../main/kotlin/treeLib/RBtree/RBBalancer.kt | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt index 2b75c02..b2e45bb 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt @@ -164,39 +164,38 @@ class RBBalancer>(private var root: RBNode?): Balan when (node) { parent.left -> { - if (brother.left?.color == Markers.RED) { + 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) { brother = rightRotate(brother) leftRotate(parent) brother.left?.color = Markers.BLACK brother.left?.color = Markers.BLACK brother.color = Markers.RED } - else if (brother.right?.color == Markers.RED) { - leftRotate(parent) - brother.left?.color = Markers.RED - brother.right?.color = Markers.RED - brother.color = Markers.BLACK - } else { throw NullPointerException() } } parent.right -> { - if (brother.right?.color == Markers.RED) { + 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) { brother = leftRotate(brother) rightRotate(parent) brother.color = Markers.RED brother.left?.color = Markers.BLACK brother.right?.color = Markers.BLACK } - else if (brother.left?.color == Markers.RED) { - rightRotate(parent) - brother.color = Markers.BLACK - brother.left?.color = Markers.RED - brother.right?.color = Markers.RED - - } else { throw NullPointerException() } From 18b92ea63d08b2c487816786e16722288d6454af Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 29 Mar 2023 00:16:15 +0300 Subject: [PATCH 040/212] feat(RBBalancerTest): Implenment tests for the balance method --- lib/build.gradle.kts | 8 +- .../main/kotlin/treeLib/RBtree/RBBalancer.kt | 11 +- lib/src/test/kotlin/RBBalancerTest.kt | 614 ++++++++++++++++++ 3 files changed, 627 insertions(+), 6 deletions(-) create mode 100644 lib/src/test/kotlin/RBBalancerTest.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 9513ece..15a6539 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -8,7 +8,7 @@ plugins { // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. - kotlin("jvm") version "1.8.10" + kotlin("jvm") version "1.8.0" // Apply the java-library plugin for API and implementation separation. `java-library` @@ -22,15 +22,17 @@ java { repositories { // Use Maven Central for resolving dependencies. + mavenLocal() mavenCentral() } dependencies { + testImplementation("io.mockk:mockk:1.13.4") // Use the Kotlin JUnit 5 integration. (TESTS support tools) - testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") // Use the JUnit 5 integration. (TESTS support tools) - testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.1") + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") // This dependency is exported to consumers, that is to say found on their compile classpath. api("org.apache.commons:commons-math3:3.6.1") diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt index b2e45bb..a33d710 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt @@ -5,6 +5,10 @@ import treeLib.Single_Objects.Markers class RBBalancer>(private var root: RBNode?): BalancerParent>() { + init { + root?.color = Markers.BLACK + } + private fun getUncle(node: RBNode): RBNode? { val parent = node.parent return when (parent?.parent?.left) { @@ -43,12 +47,13 @@ class RBBalancer>(private var root: RBNode?): Balan { var currentNode = node - if (uncle?.color != Markers.BLACK) { + if (currentNode.parent?.color == Markers.RED && uncle?.color == Markers.RED) { currentNode = afterInsert(node) } if (currentNode.parent?.color != Markers.RED) { return getRoot(currentNode) } + var parent = currentNode.parent ?: throw NullPointerException() when (parent) { parent.parent?.left -> { @@ -56,7 +61,7 @@ class RBBalancer>(private var root: RBNode?): Balan leftRotate(parent) currentNode = parent } - parent = currentNode.parent ?: throw NullPointerException() + parent = currentNode.parent?.parent ?: throw NullPointerException() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -67,7 +72,7 @@ class RBBalancer>(private var root: RBNode?): Balan rightRotate(parent) currentNode = parent } - parent = currentNode.parent ?: throw NullPointerException() + parent = currentNode.parent?.parent ?: throw NullPointerException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED diff --git a/lib/src/test/kotlin/RBBalancerTest.kt b/lib/src/test/kotlin/RBBalancerTest.kt new file mode 100644 index 0000000..e3cc94b --- /dev/null +++ b/lib/src/test/kotlin/RBBalancerTest.kt @@ -0,0 +1,614 @@ +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll +import treeLib.RBtree.RBBalancer +import treeLib.RBtree.RBNode +import treeLib.Single_Objects.Markers + +class RBBalancerTest { + + /*** Tests to check the operation of the balancer after removal ***/ + + @Test + fun `init test`() { + val firstBalancer = RBBalancer(null) + assertEquals(15, firstBalancer.balance(RBNode(15, null, null, null, Markers.BLACK)).value) + val secondBalancer = RBBalancer(null) + assertEquals("Test", secondBalancer.balance(RBNode("Test", null, null, null, Markers.BLACK)).value) + } + + @Test + fun `remove red leaf`() { + val nodes = getFirstTree() + val root = nodes[30] + val balancer = RBBalancer(root) + nodes[18]!!.right = null + assertEquals(root, balancer.balance(nodes[18]!!)) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[4]!!)) }, + { assertEquals(3, countBlackNodes(nodes[5]!!)) }, + { assertEquals(3, countBlackNodes(nodes[18]!!)) }, + { assertEquals(3, countBlackNodes(nodes[19]!!)) }, + ) + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(19, root?.left?.right?.value) }, + { assertEquals(24, root?.left?.right?.right?.value) }, + { assertEquals(18, root?.left?.right?.left?.value) }, + { assertEquals(16, root?.left?.right?.left?.left?.value) }, + { assertEquals(null, root?.left?.right?.left?.right?.value) }, + ) + + } + + @Test + fun `remove black leaf (black parent)`() { + val nodes = getFirstTree() + val root = nodes[30] + val balancer = RBBalancer(root) + nodes[27]?.right = null + assertEquals(root, balancer.balance(nodes[27]!!)) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[12]!!)) }, + { assertEquals(3, countBlackNodes(nodes[13]!!)) }, + { assertEquals(3, countBlackNodes(nodes[27]!!)) }, + { assertEquals(2, countBlackNodes(nodes[22]!!)) }, + { assertEquals(3, countBlackNodes(nodes[11]!!)) }, + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(50, root?.right?.value) }, + { assertEquals(63, root?.right?.right?.value) }, + { assertEquals(71, root?.right?.right?.right?.value) }, + { assertEquals(52, root?.right?.right?.left?.value) }, + { assertEquals(nodes[13]?.value, nodes[27]?.left?.value) }, + { assertEquals(null, nodes[27]?.right) }, + ) + } + // 3 + + @Test + fun `remove black leaf(red parent)`() { + val nodes = getFirstTree() + val root = nodes[30] + val balancer = RBBalancer(root) + nodes[17]?.left = null + assertEquals(root, balancer.balance(nodes[17]!!)) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(3, countBlackNodes(nodes[1]!!)) }, + { assertEquals(3, countBlackNodes(nodes[3]!!)) }, + { assertEquals(3, countBlackNodes(nodes[17]!!)) }, + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(50, root?.right?.value) }, + { assertEquals(15, root?.left?.value) }, + { assertEquals(19, root?.left?.right?.value) }, + { assertEquals(10, root?.left?.left?.value) }, + { assertEquals(12, root?.left?.left?.right?.value) }, + { assertEquals(14, nodes[17]?.right?.value) }, + { assertEquals(null, nodes[17]?.left)} + ) + } + + @Test + fun `remove root (Case 1)`() { + val nodes = getFirstTree() + var root = nodes[30] + val balancer = RBBalancer(root) + root = deleteNode(nodes[30]!!, nodes[8]!!) + + root = balancer.balance(nodes[20]!!) + assertAll( + "Assertions of root", + { assertEquals(nodes[8]?.value, root.value) }, + { assertEquals(Markers.BLACK, root.color) }, + { assertEquals(null, root.parent) }, + { assertEquals(nodes[28]?.value, root.left?.value) }, + { assertEquals( nodes[29]?.value, root.right?.value ) } + ) + + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(3, countBlackNodes(nodes[9]!!)) }, + { assertEquals(1, countBlackNodes(nodes[29]!!)) }, + { assertEquals(3, countBlackNodes(nodes[23]!!)) }, + { assertEquals(3, countBlackNodes(nodes[20]!!)) } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(19, root.left?.right?.value) }, + { assertEquals(40, root.right?.left?.value) }, + { 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)} + ) + } + + @Test + fun `remove root (Case 2)`() { + var root = RBNode(100, RBNode(29, null, null, null, Markers.RED), null, null, Markers.BLACK) + root.left?.parent = root + val balance = RBBalancer(root) + root = deleteNode(root, root.left!!) + + root = balance.balance(root) + + assertAll( + "Assertions of root", + { assertEquals(Markers.BLACK, root.color) }, + { assertEquals(29, root.value) }, + { assertEquals(null, root.parent) }, + { assertEquals(null, root.left)}, + { assertEquals(null, root.right)} + ) + } + + @Test + fun `remove black leaf(red parent, black brother with two red child)`() { + val nodes = getFirstTree() + 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) + nodes[13]?.right = rightBrotherSon + nodes[13]?.left = leftBrotherSon + val balancer = RBBalancer(nodes[30]) + nodes[22]?.left = null + + assertEquals(root, balancer.balance(nodes[22]!!)) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(rightBrotherSon), "1") }, + { assertEquals(3, countBlackNodes(leftBrotherSon), "2") }, + { assertEquals(3, countBlackNodes(nodes[22]!!)) }, + { assertEquals(3, countBlackNodes(nodes[13]!!)) }, + { assertEquals(2, countBlackNodes(root.right?.right!!)) }, + { assertEquals(2, countBlackNodes(nodes[13]?.parent!!)) } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(50, root.right?.value) }, + { assertEquals(71, root.right?.right?.value) }, + { 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)}, + ) + + + } + + @Test + fun `multiple deletions`() { + val nodes = getFirstTree() + val root = nodes[30] + val balancer = RBBalancer(root) + + /** first delete **/ + deleteNode(nodes[24]!!, nodes[2]!!) + + assertEquals(root!!.value, balancer.balance(nodes[17]!!).value) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[3]!!)) }, + { assertEquals(3, countBlackNodes(nodes[17]!!)) }, + { assertEquals(2, countBlackNodes(nodes[26]!!)) }, + { assertEquals(2, countBlackNodes(nodes[24]!!)) } + ) + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(12, nodes[17]?.value) }, + { assertEquals(14, nodes[3]?.value) }, + { assertEquals(null, nodes[17]?.left) } + ) + + /** second delete **/ + deleteNode(nodes[16]!!, nodes[1]!!) + + assertEquals(root, balancer.balance(nodes[1]!!)) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(3, countBlackNodes(root.left?.left?.left!!)) }, + { assertEquals(2, countBlackNodes(nodes[24]!!), "2") }, + { assertEquals(1, countBlackNodes(nodes[28]!!)) }, + { assertEquals(1, countBlackNodes(nodes[30]!!)) }, + { assertEquals(3, countBlackNodes(nodes[17]!!), "1") } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(15, root.left?.value) }, + { assertEquals(11, root.left?.left?.value) }, + { assertEquals(12, root.left?.left?.right?.value) }, + { assertEquals(7, root.left?.left?.left?.value) }, + { assertEquals(1, root.left?.left?.left?.left?.value) }, + { assertEquals(null, root.left?.left?.left?.right) }, + ) + + /** third delete **/ + nodes[17]?.right = null + assertEquals(root, balancer.balance(nodes[17]!!)) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!), "1") }, + { assertEquals(3, countBlackNodes(nodes[1]!!), "2") }, + { assertEquals(3, countBlackNodes(nodes[17]!!), "3") }, + { assertEquals(2, countBlackNodes(nodes[2]!!)) }, + { assertEquals(1, countBlackNodes(nodes[28]!!)) }, + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(15, root.left?.value) }, + { assertEquals(11, root.left?.left?.value) }, + { assertEquals(7, root.left?.left?.left?.value) }, + { assertEquals(12, nodes[17]?.value) }, + { assertEquals(null, nodes[17]?.right) }, + { assertEquals(null, nodes[17]?.left) }, + ) + + /** fourth delete **/ + nodes[2]?.right = null + + assertEquals(root, balancer.balance(nodes[2]!!)) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(2, countBlackNodes(nodes[1]!!)) }, + { assertEquals(3, countBlackNodes(nodes[2]!!)) }, + { assertEquals(1, countBlackNodes(nodes[28]!!)) }, + { assertEquals(1, countBlackNodes(nodes[30]!!)) }, + { assertEquals(2, countBlackNodes(nodes[25]!!)) } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(15, root.left?.value) }, + { assertEquals(7, root.left?.left?.value) }, + { assertEquals(1, root.left?.left?.left?.value) }, + { assertEquals(11, root.left?.left?.right?.value) }, + { assertEquals(19, root.left?.right?.value) }, + { assertEquals(null, nodes[2]?.right) }, + { assertEquals(null, nodes[2]?.left) }, + ) + + /** fourth delete **/ + nodes[2]?.right = null + + assertEquals(root, balancer.balance(nodes[2]!!)) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(2, countBlackNodes(nodes[1]!!)) }, + { assertEquals(3, countBlackNodes(nodes[2]!!)) }, + { assertEquals(1, countBlackNodes(nodes[28]!!)) }, + { assertEquals(1, countBlackNodes(nodes[30]!!)) }, + { assertEquals(2, countBlackNodes(nodes[25]!!)) } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(15, root.left?.value) }, + { assertEquals(7, root.left?.left?.value) }, + { assertEquals(1, root.left?.left?.left?.value) }, + { assertEquals(11, root.left?.left?.right?.value) }, + { assertEquals(19, root.left?.right?.value) }, + { assertEquals(null, nodes[2]?.right) }, + { assertEquals(null, nodes[2]?.left) }, + ) + + /** fifth delete **/ + nodes[1]?.right = null + + assertEquals(root, balancer.balance(nodes[1]!!)) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(3, countBlackNodes(nodes[1]!!)) }, + { assertEquals(2, countBlackNodes(nodes[28]!!)) }, + { assertEquals(2, countBlackNodes(nodes[25]!!)) }, + { assertEquals(3, countBlackNodes(nodes[18]!!)) }, + { assertEquals(3, countBlackNodes(nodes[19]!!)) } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(15, root.left?.value) }, + { assertEquals(7, root.left?.left?.value) }, + { assertEquals(1, root.left?.left?.left?.value) }, + { assertEquals(19, root.left?.right?.value) }, + { assertEquals(24, root.left?.right?.right?.value) }, + { assertEquals(null, root.left?.left?.right) }, + ) + } + + /*** Tests to check the operation of the balancer after insertion ***/ + + @Test + fun `insertion root`() { + val root = RBNode(18, null, null, null, Markers.RED) + RBBalancer(root) + assertAll( + { assertEquals(Markers.BLACK, root.color) }, + { assertEquals(18, root.value) } + ) + } + + @Test + fun `insertion with black parent`() { + var root = RBNode(18, null, null, null, Markers.BLACK) + val balancer = RBBalancer(root) + root.right = RBNode(39, null, null, root, Markers.RED) + root = balancer.balance(root.right!!) + assertAll( + { assertEquals(18, root.value) }, + { assertEquals(Markers.BLACK, root.color) }, + { assertEquals(39, root.right?.value) }, + { assertEquals(null, root.left) }, + { assertEquals(null, root.right?.right) }, + { assertEquals(null, root.right?.left) } + ) + } + + @Test + fun `insertion with red uncle (Case 1)`() { + val nodes = getSecondTree() + var root = nodes[30] + val balancer = RBBalancer(root) + root = balancer.balance(nodes[0]!!) + assertAll( + "Assertions of root", + { assertEquals(25, root.value) }, + { assertEquals(Markers.BLACK, root.color) } + ) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!), "1") }, + { assertEquals(3, countBlackNodes(nodes[16]!!), "2") }, + { assertEquals(3, countBlackNodes(nodes[17]!!)) }, + { assertEquals(2, countBlackNodes(nodes[24]!!)) }, + { assertEquals(2, countBlackNodes(nodes[29]!!)) } + ) + assertAll( + "Grouped Assertions of values", + { assertEquals(1, root.left?.left?.left?.left?.value) }, + { assertEquals(4, root.left?.left?.left?.value) }, + { assertEquals(12, root.left?.left?.right?.value) }, + { assertEquals(15, root.left?.value) }, + { assertEquals(null, root.left?.left?.left?.left?.left) }, + { assertEquals(null, root.left?.left?.right?.left) }, + { assertEquals(null, root.left?.left?.left?.right) }, + ) + } + + @Test + fun `insertion with red uncle (Case 2)`() { + val nodes = getThirdTree() + var root = nodes[30] + val balancer = RBBalancer(root) + nodes[22]?.right = RBNode(65, null, null, nodes[22], Markers.RED) + root = balancer.balance(nodes[22]!!.right!!) + assertAll( + "Assertions of root", + { assertEquals(50, root.value) }, + { assertEquals(Markers.BLACK, root.color) } + ) + assertAll( + "Grouped Assertions of black height", + { assertEquals(2, countBlackNodes(nodes[23]!!)) }, + { assertEquals(2, countBlackNodes(nodes[22]!!)) }, + { assertEquals(2, countBlackNodes(nodes[26]!!)) }, + { assertEquals(2, countBlackNodes(nodes[28]!!)) }, + { assertEquals(2, countBlackNodes(nodes[22]!!.right!!)) }, + { assertEquals(1, countBlackNodes(nodes[30]!!)) }, + { assertEquals(1, countBlackNodes(nodes[27]!!)) } + ) + assertAll( + "Grouped Assertions of values", + { assertEquals(10, root.left?.left?.left?.value) }, + { assertEquals(19, root.left?.left?.right?.value) }, + { assertEquals(40, root.left?.right?.value) }, + { assertEquals(63, root.right?.left?.value) }, + { assertEquals(65, root.right?.left?.right?.value) }, + { assertEquals(90, root.right?.right?.value) }, + { assertEquals(71, root.right?.value) }, + { assertEquals(42, root.left?.right?.right?.value) }, + { assertEquals(null, root.right?.left?.left) }, + ) + + } + + @Test + fun `insertion with red uncle (Case 3)`() { + val nodes = getFourthTree() + var root = nodes[30] + val balancer = RBBalancer(root) + nodes[19]!!.left = RBNode(21, null, null, nodes[19], Markers.RED) + root = balancer.balance(nodes[19]!!.left!!) + + assertAll( + "Assertions of root", + { assertEquals(19, root.value) }, + { assertEquals(Markers.BLACK, root.color) } + ) + assertAll( + "Grouped Assertions of black height", + { assertEquals(2, countBlackNodes(nodes[24]!!)) }, + { assertEquals(1, countBlackNodes(nodes[28]!!)) }, + { assertEquals(2, countBlackNodes(nodes[18]!!)) }, + { assertEquals(2, countBlackNodes(nodes[19]!!)) }, + { assertEquals(2, countBlackNodes(nodes[19]!!.left!!)) }, + { assertEquals(2, countBlackNodes(nodes[27]!!)) }, + { assertEquals(2, countBlackNodes(nodes[26]!!)) } + ) + assertAll( + "Grouped Assertions of values", + { assertEquals(10, root.left?.left?.value) }, + { assertEquals(15, root.left?.value) }, + { assertEquals(18, root.left?.right?.value) }, + { assertEquals(25, root.right?.value) }, + { assertEquals(50, root.right?.right?.value) }, + { assertEquals(24, root.right?.left?.value) }, + { assertEquals(71, root.right?.right?.right?.value) }, + { assertEquals(21, root.right?.left?.left?.value) }, + { assertEquals(null, root.right?.left?.right) }, + { assertEquals(null, root.left?.left?.right) }, + { assertEquals(null, root.left?.right?.left) }, + ) + } + + + /*** RBT models for testing ***/ + + private 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 + currentNode = currentNode.parent + } + return count + } + + private fun >deleteNode(remoteNode: RBNode, newNode: RBNode): RBNode { + remoteNode.left?.parent = newNode + remoteNode.right?.parent = newNode + newNode.left = remoteNode.left + newNode.right = remoteNode.right + newNode.color = remoteNode.color + when (newNode) { + newNode.parent?.left -> newNode.parent?.left = null + else -> newNode.parent?.right = null + } + + if (remoteNode.parent != null) { + val parent = remoteNode.parent!! + when (remoteNode) { + parent.left -> parent.left = newNode + else -> parent.right = newNode + } + newNode.parent = parent + } + else { + newNode.parent = null + } + + return newNode + } + + private fun getFourthTree(): MutableList?> { + val nodes = getSecondTree() + for (i in 24..27) { + nodes[i]!!.right = null + nodes[i]!!.left = null + } + nodes[25]!!.right = nodes[19] + nodes[25]!!.left = nodes[18] + nodes[25]!!.color = Markers.RED + nodes[29]!!.color = Markers.BLACK + nodes[26]!!.color = Markers.RED + nodes[27]!!.color = Markers.RED + + return nodes + } + + private fun getThirdTree(): MutableList?> { + val nodes = getSecondTree() + nodes[28]!!.color = Markers.BLACK + nodes[24]!!.color = Markers.RED + nodes[24]?.right = null + nodes[24]?.left = null + nodes[25]!!.color = Markers.RED + nodes[25]?.right = null + nodes[25]?.left = null + return nodes + + } + + private fun getSecondTree(): MutableList?> { + val nodes = getFirstTree() + for (i in 16..23) { + nodes[i]!!.color = Markers.RED + nodes[i]!!.right = null + nodes[i]!!.left = null + } + nodes[0]!!.color = Markers.RED + nodes[16]!!.left = nodes[0] + + return nodes + } + + + private 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, + 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 + + ) + val nodes = mutableListOf?>() + 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]!!)) + 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]!!)) + else + nodes.add(null) + 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] + } + 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] + } + 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] + } + } + } + return nodes + } + +} From d17847d51e7a788a3746d8621a770358c07a8e44 Mon Sep 17 00:00:00 2001 From: Artem Date: Thu, 30 Mar 2023 21:07:44 +0300 Subject: [PATCH 041/212] feat: Implement a container to pass the tree current state to the balancer --- .../kotlin/treeLib/AVLtree/AVLBalancer.kt | 16 ++++++-- .../treeLib/AVLtree/AVLStateContainer.kt | 10 +++++ .../main/kotlin/treeLib/AVLtree/AVLStruct.kt | 2 +- .../treeLib/AbstractTree/StateContainer.kt | 5 +++ .../treeLib/AbstractTree/Weighted/Balancer.kt | 5 ++- .../AbstractTree/Weighted/BalancerNoParent.kt | 3 +- .../AbstractTree/Weighted/BalancerParent.kt | 3 +- .../Weighted/WeightedTreeStruct.kt | 9 +++-- .../main/kotlin/treeLib/RBtree/RBBalancer.kt | 5 ++- .../kotlin/treeLib/RBtree/RBStateContainer.kt | 11 ++++++ .../main/kotlin/treeLib/RBtree/RBStruct.kt | 2 +- lib/src/test/kotlin/RBBalancerTest.kt | 39 ++++++++++--------- 12 files changed, 76 insertions(+), 34 deletions(-) create mode 100644 lib/src/main/kotlin/treeLib/AVLtree/AVLStateContainer.kt create mode 100644 lib/src/main/kotlin/treeLib/AbstractTree/StateContainer.kt create mode 100644 lib/src/main/kotlin/treeLib/RBtree/RBStateContainer.kt diff --git a/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt index 3e97a02..ef62262 100644 --- a/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt @@ -1,11 +1,10 @@ package treeLib.AVLtree import treeLib.AbstractTree.Weighted.BalancerNoParent -// gradle init -class AVLBalancer>(private var root: AVLNode?): BalancerNoParent>() { + +class AVLBalancer>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { return (getHeight(node?.left) - getHeight(node?.right)).toInt() - } private fun getHeight(currentNode: AVLNode?): UInt { @@ -16,7 +15,16 @@ class AVLBalancer>(private var root: AVLNode?): Bal return if (currentNode == null) 0u else ( maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u) } - override fun balance(node: AVLNode): AVLNode = balance(root, node.value) + override fun balance(stateContainer: AVLStateContainer): AVLNode { + val node = stateContainer.contentNode + root = stateContainer.root + when (node.value) { + root?.value -> { + + } + } + return balance(root, node.value) + } // В баланс передаем родителя ноды, которую будем удалять private fun balance(currentNode: AVLNode?, value: Pack): AVLNode { if (currentNode == null) { diff --git a/lib/src/main/kotlin/treeLib/AVLtree/AVLStateContainer.kt b/lib/src/main/kotlin/treeLib/AVLtree/AVLStateContainer.kt new file mode 100644 index 0000000..77dc3cf --- /dev/null +++ b/lib/src/main/kotlin/treeLib/AVLtree/AVLStateContainer.kt @@ -0,0 +1,10 @@ +package treeLib.AVLtree + +import treeLib.AbstractTree.StateContainer + +/*Content node - the parent of the node on which the action was performed*/ + +class AVLStateContainer>( + override val contentNode: AVLNode, + val root : AVLNode, +): StateContainer> \ No newline at end of file diff --git a/lib/src/main/kotlin/treeLib/AVLtree/AVLStruct.kt b/lib/src/main/kotlin/treeLib/AVLtree/AVLStruct.kt index f7b167a..ad842d6 100644 --- a/lib/src/main/kotlin/treeLib/AVLtree/AVLStruct.kt +++ b/lib/src/main/kotlin/treeLib/AVLtree/AVLStruct.kt @@ -2,7 +2,7 @@ package treeLib.AVLtree import treeLib.AbstractTree.Weighted.WeightedTreeStruct -class AVLStruct> : WeightedTreeStruct, AVLBalancer>() { +class AVLStruct> : WeightedTreeStruct, AVLStateContainer, AVLBalancer>() { override var root: AVLNode? = null override val balancer = AVLBalancer(root) } \ No newline at end of file diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/StateContainer.kt b/lib/src/main/kotlin/treeLib/AbstractTree/StateContainer.kt new file mode 100644 index 0000000..920d0d7 --- /dev/null +++ b/lib/src/main/kotlin/treeLib/AbstractTree/StateContainer.kt @@ -0,0 +1,5 @@ +package treeLib.AbstractTree + +interface StateContainer, NodeType: Node> { + val contentNode: NodeType +} \ No newline at end of file diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/Balancer.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/Balancer.kt index 8df7518..ce53fc7 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/Balancer.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/Balancer.kt @@ -1,11 +1,12 @@ package treeLib.AbstractTree.Weighted import treeLib.AbstractTree.Node +import treeLib.AbstractTree.StateContainer -interface Balancer, NodeType : Node> { +interface Balancer , NodeType : Node, StateContainerType: StateContainer> { fun rightRotate(currentNode: NodeType): NodeType fun leftRotate(currentNode: NodeType): NodeType - fun balance(node: NodeType): NodeType + fun balance(node: StateContainerType): NodeType } \ No newline at end of file diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerNoParent.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerNoParent.kt index bd96b8a..bfa0be0 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerNoParent.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerNoParent.kt @@ -1,8 +1,9 @@ package treeLib.AbstractTree.Weighted import treeLib.AbstractTree.Node +import treeLib.AbstractTree.StateContainer -abstract class BalancerNoParent, NodeType : Node>: Balancer { +abstract class BalancerNoParent, NodeType : Node, StateContainerType: StateContainer>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { // вероятно можем полагаться на то, что сын не будет null val leftSon = currentNode.left ?: throw NullPointerException() diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt index 92c1299..1e6bd12 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt @@ -1,8 +1,9 @@ package treeLib.AbstractTree.Weighted import treeLib.AbstractTree.NodeParent +import treeLib.AbstractTree.StateContainer -abstract class BalancerParent, NodeType : NodeParent>: Balancer { +abstract class BalancerParent, NodeType : NodeParent, StateContainerType: StateContainer>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { // пока верим в то, что currentNode не равно null val leftChild = currentNode.left ?: throw NullPointerException() diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt index ccba11a..ca76866 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt +++ b/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt @@ -1,9 +1,10 @@ package treeLib.AbstractTree.Weighted import treeLib.AbstractTree.Node +import treeLib.AbstractTree.StateContainer import treeLib.AbstractTree.TreeStruct -abstract class WeightedTreeStruct, NodeType : Node, BalancerType: Balancer>: +abstract class WeightedTreeStruct, NodeType : Node, StateContainerType: StateContainer, BalancerType: Balancer>: TreeStruct() { protected abstract val balancer: BalancerType @@ -19,7 +20,8 @@ abstract class WeightedTreeStruct, NodeType : Node, NodeType : Node>(private var root: RBNode?): BalancerParent>() { +class RBBalancer>(private var root: RBNode?): BalancerParent, RBStateContainer>() { init { root?.color = Markers.BLACK @@ -39,7 +39,8 @@ class RBBalancer>(private var root: RBNode?): Balan return node.right == null && node.left == null } - override fun balance(node: RBNode): RBNode { + override fun balance(stateContainer: RBStateContainer): RBNode { + val node = stateContainer.contentNode val uncle = getUncle(node) when { /** node insertion case **/ diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBStateContainer.kt b/lib/src/main/kotlin/treeLib/RBtree/RBStateContainer.kt new file mode 100644 index 0000000..5dd4ce0 --- /dev/null +++ b/lib/src/main/kotlin/treeLib/RBtree/RBStateContainer.kt @@ -0,0 +1,11 @@ +package treeLib.RBtree + +import treeLib.AbstractTree.StateContainer + +/* +Insert: contentNode - the node on which the action was performed +Delete: contentNode - the parent of the node on which the action was performed +* */ +class RBStateContainer>( + override val contentNode: RBNode +): StateContainer> \ No newline at end of file diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt b/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt index ef3ccb4..e994208 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt +++ b/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt @@ -2,7 +2,7 @@ package treeLib.RBtree import treeLib.AbstractTree.Weighted.WeightedTreeStruct -class RBStruct > : WeightedTreeStruct, RBBalancer>(){ +class RBStruct > : WeightedTreeStruct, RBStateContainer, RBBalancer>(){ override var root: RBNode? = null override val balancer = RBBalancer(root) diff --git a/lib/src/test/kotlin/RBBalancerTest.kt b/lib/src/test/kotlin/RBBalancerTest.kt index e3cc94b..5124c7b 100644 --- a/lib/src/test/kotlin/RBBalancerTest.kt +++ b/lib/src/test/kotlin/RBBalancerTest.kt @@ -1,10 +1,11 @@ + import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll import treeLib.RBtree.RBBalancer import treeLib.RBtree.RBNode +import treeLib.RBtree.RBStateContainer import treeLib.Single_Objects.Markers - class RBBalancerTest { /*** Tests to check the operation of the balancer after removal ***/ @@ -12,9 +13,9 @@ class RBBalancerTest { @Test fun `init test`() { val firstBalancer = RBBalancer(null) - assertEquals(15, firstBalancer.balance(RBNode(15, null, null, null, Markers.BLACK)).value) + assertEquals(15, firstBalancer.balance(RBStateContainer(RBNode(15, null, null, null, Markers.BLACK))).value) val secondBalancer = RBBalancer(null) - assertEquals("Test", secondBalancer.balance(RBNode("Test", null, null, null, Markers.BLACK)).value) + assertEquals("Test", secondBalancer.balance(RBStateContainer(RBNode("Test", null, null, null, Markers.BLACK))).value) } @Test @@ -23,7 +24,7 @@ class RBBalancerTest { val root = nodes[30] val balancer = RBBalancer(root) nodes[18]!!.right = null - assertEquals(root, balancer.balance(nodes[18]!!)) + assertEquals(root, balancer.balance(RBStateContainer(nodes[18]!!))) assertAll( "Grouped Assertions of black height", { assertEquals(3, countBlackNodes(nodes[4]!!)) }, @@ -48,7 +49,7 @@ class RBBalancerTest { val root = nodes[30] val balancer = RBBalancer(root) nodes[27]?.right = null - assertEquals(root, balancer.balance(nodes[27]!!)) + assertEquals(root, balancer.balance(RBStateContainer(nodes[27]!!))) assertAll( "Grouped Assertions of black height", { assertEquals(3, countBlackNodes(nodes[12]!!)) }, @@ -76,7 +77,7 @@ class RBBalancerTest { val root = nodes[30] val balancer = RBBalancer(root) nodes[17]?.left = null - assertEquals(root, balancer.balance(nodes[17]!!)) + assertEquals(root, balancer.balance(RBStateContainer(nodes[17]!!))) assertAll( "Grouped Assertions of black height", { assertEquals(3, countBlackNodes(nodes[0]!!)) }, @@ -104,7 +105,7 @@ class RBBalancerTest { val balancer = RBBalancer(root) root = deleteNode(nodes[30]!!, nodes[8]!!) - root = balancer.balance(nodes[20]!!) + root = balancer.balance(RBStateContainer(nodes[20]!!)) assertAll( "Assertions of root", { assertEquals(nodes[8]?.value, root.value) }, @@ -141,7 +142,7 @@ class RBBalancerTest { val balance = RBBalancer(root) root = deleteNode(root, root.left!!) - root = balance.balance(root) + root = balance.balance(RBStateContainer(root)) assertAll( "Assertions of root", @@ -165,7 +166,7 @@ class RBBalancerTest { val balancer = RBBalancer(nodes[30]) nodes[22]?.left = null - assertEquals(root, balancer.balance(nodes[22]!!)) + assertEquals(root, balancer.balance(RBStateContainer(nodes[22]!!))) assertAll( "Grouped Assertions of black height", { assertEquals(3, countBlackNodes(rightBrotherSon), "1") }, @@ -199,7 +200,7 @@ class RBBalancerTest { /** first delete **/ deleteNode(nodes[24]!!, nodes[2]!!) - assertEquals(root!!.value, balancer.balance(nodes[17]!!).value) + assertEquals(root!!.value, balancer.balance(RBStateContainer(nodes[17]!!)).value) assertAll( "Grouped Assertions of black height", { assertEquals(3, countBlackNodes(nodes[3]!!)) }, @@ -217,7 +218,7 @@ class RBBalancerTest { /** second delete **/ deleteNode(nodes[16]!!, nodes[1]!!) - assertEquals(root, balancer.balance(nodes[1]!!)) + assertEquals(root, balancer.balance(RBStateContainer(nodes[1]!!))) assertAll( "Grouped Assertions of black height", { assertEquals(3, countBlackNodes(nodes[0]!!)) }, @@ -240,7 +241,7 @@ class RBBalancerTest { /** third delete **/ nodes[17]?.right = null - assertEquals(root, balancer.balance(nodes[17]!!)) + assertEquals(root, balancer.balance(RBStateContainer(nodes[17]!!))) assertAll( "Grouped Assertions of black height", { assertEquals(3, countBlackNodes(nodes[0]!!), "1") }, @@ -263,7 +264,7 @@ class RBBalancerTest { /** fourth delete **/ nodes[2]?.right = null - assertEquals(root, balancer.balance(nodes[2]!!)) + assertEquals(root, balancer.balance(RBStateContainer(nodes[2]!!))) assertAll( "Grouped Assertions of black height", { assertEquals(3, countBlackNodes(nodes[0]!!)) }, @@ -288,7 +289,7 @@ class RBBalancerTest { /** fourth delete **/ nodes[2]?.right = null - assertEquals(root, balancer.balance(nodes[2]!!)) + assertEquals(root, balancer.balance(RBStateContainer(nodes[2]!!))) assertAll( "Grouped Assertions of black height", { assertEquals(3, countBlackNodes(nodes[0]!!)) }, @@ -313,7 +314,7 @@ class RBBalancerTest { /** fifth delete **/ nodes[1]?.right = null - assertEquals(root, balancer.balance(nodes[1]!!)) + assertEquals(root, balancer.balance(RBStateContainer(nodes[1]!!))) assertAll( "Grouped Assertions of black height", { assertEquals(3, countBlackNodes(nodes[0]!!)) }, @@ -352,7 +353,7 @@ class RBBalancerTest { var root = RBNode(18, null, null, null, Markers.BLACK) val balancer = RBBalancer(root) root.right = RBNode(39, null, null, root, Markers.RED) - root = balancer.balance(root.right!!) + root = balancer.balance(RBStateContainer(root.right!!)) assertAll( { assertEquals(18, root.value) }, { assertEquals(Markers.BLACK, root.color) }, @@ -368,7 +369,7 @@ class RBBalancerTest { val nodes = getSecondTree() var root = nodes[30] val balancer = RBBalancer(root) - root = balancer.balance(nodes[0]!!) + root = balancer.balance(RBStateContainer(nodes[0]!!)) assertAll( "Assertions of root", { assertEquals(25, root.value) }, @@ -400,7 +401,7 @@ class RBBalancerTest { var root = nodes[30] val balancer = RBBalancer(root) nodes[22]?.right = RBNode(65, null, null, nodes[22], Markers.RED) - root = balancer.balance(nodes[22]!!.right!!) + root = balancer.balance(RBStateContainer(nodes[22]!!.right!!)) assertAll( "Assertions of root", { assertEquals(50, root.value) }, @@ -437,7 +438,7 @@ class RBBalancerTest { var root = nodes[30] val balancer = RBBalancer(root) nodes[19]!!.left = RBNode(21, null, null, nodes[19], Markers.RED) - root = balancer.balance(nodes[19]!!.left!!) + root = balancer.balance(RBStateContainer(nodes[19]!!.left!!)) assertAll( "Assertions of root", From f1ea5fbb71d3a8a7fdc5f44e41c404980b75ab93 Mon Sep 17 00:00:00 2001 From: Artem Date: Thu, 30 Mar 2023 23:18:02 +0300 Subject: [PATCH 042/212] refactor(RBBalancerTest): Move the RBT model to a separate class and separate insert/delete test cases --- lib/src/test/kotlin/RBBalancerTest.kt | 1030 +++++++++++-------------- lib/src/test/kotlin/TestModelRBT.kt | 142 ++++ 2 files changed, 591 insertions(+), 581 deletions(-) create mode 100644 lib/src/test/kotlin/TestModelRBT.kt diff --git a/lib/src/test/kotlin/RBBalancerTest.kt b/lib/src/test/kotlin/RBBalancerTest.kt index 5124c7b..ff3b431 100644 --- a/lib/src/test/kotlin/RBBalancerTest.kt +++ b/lib/src/test/kotlin/RBBalancerTest.kt @@ -1,5 +1,7 @@ import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll import treeLib.RBtree.RBBalancer @@ -8,608 +10,474 @@ import treeLib.RBtree.RBStateContainer import treeLib.Single_Objects.Markers class RBBalancerTest { - /*** Tests to check the operation of the balancer after removal ***/ - - @Test - fun `init test`() { - 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) - } - - @Test - fun `remove red leaf`() { - val nodes = getFirstTree() - val root = nodes[30] - val balancer = RBBalancer(root) - nodes[18]!!.right = null - assertEquals(root, balancer.balance(RBStateContainer(nodes[18]!!))) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[4]!!)) }, - { assertEquals(3, countBlackNodes(nodes[5]!!)) }, - { assertEquals(3, countBlackNodes(nodes[18]!!)) }, - { assertEquals(3, countBlackNodes(nodes[19]!!)) }, - ) - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(19, root?.left?.right?.value) }, - { assertEquals(24, root?.left?.right?.right?.value) }, - { assertEquals(18, root?.left?.right?.left?.value) }, - { assertEquals(16, root?.left?.right?.left?.left?.value) }, - { assertEquals(null, root?.left?.right?.left?.right?.value) }, - ) - - } - - @Test - fun `remove black leaf (black parent)`() { - val nodes = getFirstTree() - val root = nodes[30] - val balancer = RBBalancer(root) - nodes[27]?.right = null - assertEquals(root, balancer.balance(RBStateContainer(nodes[27]!!))) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[12]!!)) }, - { assertEquals(3, countBlackNodes(nodes[13]!!)) }, - { assertEquals(3, countBlackNodes(nodes[27]!!)) }, - { assertEquals(2, countBlackNodes(nodes[22]!!)) }, - { assertEquals(3, countBlackNodes(nodes[11]!!)) }, - ) - - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(50, root?.right?.value) }, - { assertEquals(63, root?.right?.right?.value) }, - { assertEquals(71, root?.right?.right?.right?.value) }, - { assertEquals(52, root?.right?.right?.left?.value) }, - { assertEquals(nodes[13]?.value, nodes[27]?.left?.value) }, - { assertEquals(null, nodes[27]?.right) }, - ) - } - // 3 - - @Test - fun `remove black leaf(red parent)`() { - val nodes = getFirstTree() - val root = nodes[30] - val balancer = RBBalancer(root) - nodes[17]?.left = null - assertEquals(root, balancer.balance(RBStateContainer(nodes[17]!!))) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[0]!!)) }, - { assertEquals(3, countBlackNodes(nodes[1]!!)) }, - { assertEquals(3, countBlackNodes(nodes[3]!!)) }, - { assertEquals(3, countBlackNodes(nodes[17]!!)) }, - ) - - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(50, root?.right?.value) }, - { assertEquals(15, root?.left?.value) }, - { assertEquals(19, root?.left?.right?.value) }, - { assertEquals(10, root?.left?.left?.value) }, - { assertEquals(12, root?.left?.left?.right?.value) }, - { assertEquals(14, nodes[17]?.right?.value) }, - { assertEquals(null, nodes[17]?.left)} - ) - } - - @Test - fun `remove root (Case 1)`() { - val nodes = getFirstTree() - var root = nodes[30] - val balancer = RBBalancer(root) - root = deleteNode(nodes[30]!!, nodes[8]!!) - - root = balancer.balance(RBStateContainer(nodes[20]!!)) - assertAll( - "Assertions of root", - { assertEquals(nodes[8]?.value, root.value) }, - { assertEquals(Markers.BLACK, root.color) }, - { assertEquals(null, root.parent) }, - { assertEquals(nodes[28]?.value, root.left?.value) }, - { assertEquals( nodes[29]?.value, root.right?.value ) } - ) - - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[0]!!)) }, - { assertEquals(3, countBlackNodes(nodes[9]!!)) }, - { assertEquals(1, countBlackNodes(nodes[29]!!)) }, - { assertEquals(3, countBlackNodes(nodes[23]!!)) }, - { assertEquals(3, countBlackNodes(nodes[20]!!)) } - ) - - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(19, root.left?.right?.value) }, - { assertEquals(40, root.right?.left?.value) }, - { 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)} - ) - } - - @Test - fun `remove root (Case 2)`() { - var root = RBNode(100, RBNode(29, null, null, null, Markers.RED), null, null, Markers.BLACK) - root.left?.parent = root - val balance = RBBalancer(root) - root = deleteNode(root, root.left!!) - - root = balance.balance(RBStateContainer(root)) - - assertAll( - "Assertions of root", - { assertEquals(Markers.BLACK, root.color) }, - { assertEquals(29, root.value) }, - { assertEquals(null, root.parent) }, - { assertEquals(null, root.left)}, - { assertEquals(null, root.right)} - ) - } - - @Test - fun `remove black leaf(red parent, black brother with two red child)`() { - val nodes = getFirstTree() - 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) - nodes[13]?.right = rightBrotherSon - nodes[13]?.left = leftBrotherSon - val balancer = RBBalancer(nodes[30]) - nodes[22]?.left = null - - assertEquals(root, balancer.balance(RBStateContainer(nodes[22]!!))) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(rightBrotherSon), "1") }, - { assertEquals(3, countBlackNodes(leftBrotherSon), "2") }, - { assertEquals(3, countBlackNodes(nodes[22]!!)) }, - { assertEquals(3, countBlackNodes(nodes[13]!!)) }, - { assertEquals(2, countBlackNodes(root.right?.right!!)) }, - { assertEquals(2, countBlackNodes(nodes[13]?.parent!!)) } - ) - - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(50, root.right?.value) }, - { assertEquals(71, root.right?.right?.value) }, - { 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)}, - ) - - - } - - @Test - fun `multiple deletions`() { - val nodes = getFirstTree() - val root = nodes[30] - val balancer = RBBalancer(root) - - /** first delete **/ - deleteNode(nodes[24]!!, nodes[2]!!) - - assertEquals(root!!.value, balancer.balance(RBStateContainer(nodes[17]!!)).value) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[3]!!)) }, - { assertEquals(3, countBlackNodes(nodes[17]!!)) }, - { assertEquals(2, countBlackNodes(nodes[26]!!)) }, - { assertEquals(2, countBlackNodes(nodes[24]!!)) } - ) - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(12, nodes[17]?.value) }, - { assertEquals(14, nodes[3]?.value) }, - { assertEquals(null, nodes[17]?.left) } - ) - - /** second delete **/ - deleteNode(nodes[16]!!, nodes[1]!!) - - assertEquals(root, balancer.balance(RBStateContainer(nodes[1]!!))) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[0]!!)) }, - { assertEquals(3, countBlackNodes(root.left?.left?.left!!)) }, - { assertEquals(2, countBlackNodes(nodes[24]!!), "2") }, - { assertEquals(1, countBlackNodes(nodes[28]!!)) }, - { assertEquals(1, countBlackNodes(nodes[30]!!)) }, - { assertEquals(3, countBlackNodes(nodes[17]!!), "1") } - ) - - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(15, root.left?.value) }, - { assertEquals(11, root.left?.left?.value) }, - { assertEquals(12, root.left?.left?.right?.value) }, - { assertEquals(7, root.left?.left?.left?.value) }, - { assertEquals(1, root.left?.left?.left?.left?.value) }, - { assertEquals(null, root.left?.left?.left?.right) }, - ) - - /** third delete **/ - nodes[17]?.right = null - assertEquals(root, balancer.balance(RBStateContainer(nodes[17]!!))) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[0]!!), "1") }, - { assertEquals(3, countBlackNodes(nodes[1]!!), "2") }, - { assertEquals(3, countBlackNodes(nodes[17]!!), "3") }, - { assertEquals(2, countBlackNodes(nodes[2]!!)) }, - { assertEquals(1, countBlackNodes(nodes[28]!!)) }, - ) - - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(15, root.left?.value) }, - { assertEquals(11, root.left?.left?.value) }, - { assertEquals(7, root.left?.left?.left?.value) }, - { assertEquals(12, nodes[17]?.value) }, - { assertEquals(null, nodes[17]?.right) }, - { assertEquals(null, nodes[17]?.left) }, - ) - - /** fourth delete **/ - nodes[2]?.right = null - - assertEquals(root, balancer.balance(RBStateContainer(nodes[2]!!))) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[0]!!)) }, - { assertEquals(2, countBlackNodes(nodes[1]!!)) }, - { assertEquals(3, countBlackNodes(nodes[2]!!)) }, - { assertEquals(1, countBlackNodes(nodes[28]!!)) }, - { assertEquals(1, countBlackNodes(nodes[30]!!)) }, - { assertEquals(2, countBlackNodes(nodes[25]!!)) } - ) - - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(15, root.left?.value) }, - { assertEquals(7, root.left?.left?.value) }, - { assertEquals(1, root.left?.left?.left?.value) }, - { assertEquals(11, root.left?.left?.right?.value) }, - { assertEquals(19, root.left?.right?.value) }, - { assertEquals(null, nodes[2]?.right) }, - { assertEquals(null, nodes[2]?.left) }, - ) - - /** fourth delete **/ - nodes[2]?.right = null - - assertEquals(root, balancer.balance(RBStateContainer(nodes[2]!!))) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[0]!!)) }, - { assertEquals(2, countBlackNodes(nodes[1]!!)) }, - { assertEquals(3, countBlackNodes(nodes[2]!!)) }, - { assertEquals(1, countBlackNodes(nodes[28]!!)) }, - { assertEquals(1, countBlackNodes(nodes[30]!!)) }, - { assertEquals(2, countBlackNodes(nodes[25]!!)) } - ) - - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(15, root.left?.value) }, - { assertEquals(7, root.left?.left?.value) }, - { assertEquals(1, root.left?.left?.left?.value) }, - { assertEquals(11, root.left?.left?.right?.value) }, - { assertEquals(19, root.left?.right?.value) }, - { assertEquals(null, nodes[2]?.right) }, - { assertEquals(null, nodes[2]?.left) }, - ) - - /** fifth delete **/ - nodes[1]?.right = null - - assertEquals(root, balancer.balance(RBStateContainer(nodes[1]!!))) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[0]!!)) }, - { assertEquals(3, countBlackNodes(nodes[1]!!)) }, - { assertEquals(2, countBlackNodes(nodes[28]!!)) }, - { assertEquals(2, countBlackNodes(nodes[25]!!)) }, - { assertEquals(3, countBlackNodes(nodes[18]!!)) }, - { assertEquals(3, countBlackNodes(nodes[19]!!)) } - ) - - assertAll( - "Grouped Assertions of nodes values", - { assertEquals(15, root.left?.value) }, - { assertEquals(7, root.left?.left?.value) }, - { assertEquals(1, root.left?.left?.left?.value) }, - { assertEquals(19, root.left?.right?.value) }, - { assertEquals(24, root.left?.right?.right?.value) }, - { assertEquals(null, root.left?.left?.right) }, - ) - } - - /*** Tests to check the operation of the balancer after insertion ***/ + val testModel = TestModelRBT() + fun > countBlackNodes(node: RBNode) = testModel.countBlackNodes(node) + + @DisplayName("Tests to check the operation of the balancer after removal") + @Nested + inner class RemovalTests { + @Test + fun `init test`() { + 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) + } - @Test - fun `insertion root`() { - val root = RBNode(18, null, null, null, Markers.RED) - RBBalancer(root) - assertAll( - { assertEquals(Markers.BLACK, root.color) }, - { assertEquals(18, root.value) } - ) - } + @Test + fun `remove red leaf`() { + val nodes = testModel.getFirstTree() + val root = nodes[30] + val balancer = RBBalancer(root) + nodes[18]!!.right = null + assertEquals(root, balancer.balance(RBStateContainer(nodes[18]!!))) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[4]!!)) }, + { assertEquals(3, countBlackNodes(nodes[5]!!)) }, + { assertEquals(3, countBlackNodes(nodes[18]!!)) }, + { assertEquals(3, countBlackNodes(nodes[19]!!)) }, + ) + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(19, root?.left?.right?.value) }, + { assertEquals(24, root?.left?.right?.right?.value) }, + { assertEquals(18, root?.left?.right?.left?.value) }, + { assertEquals(16, root?.left?.right?.left?.left?.value) }, + { assertEquals(null, root?.left?.right?.left?.right?.value) }, + ) - @Test - fun `insertion with black parent`() { - var root = RBNode(18, null, null, null, Markers.BLACK) - val balancer = RBBalancer(root) - root.right = RBNode(39, null, null, root, Markers.RED) - root = balancer.balance(RBStateContainer(root.right!!)) - assertAll( - { assertEquals(18, root.value) }, - { assertEquals(Markers.BLACK, root.color) }, - { assertEquals(39, root.right?.value) }, - { assertEquals(null, root.left) }, - { assertEquals(null, root.right?.right) }, - { assertEquals(null, root.right?.left) } - ) - } + } - @Test - fun `insertion with red uncle (Case 1)`() { - val nodes = getSecondTree() - var root = nodes[30] - val balancer = RBBalancer(root) - root = balancer.balance(RBStateContainer(nodes[0]!!)) - assertAll( - "Assertions of root", - { assertEquals(25, root.value) }, - { assertEquals(Markers.BLACK, root.color) } - ) - assertAll( - "Grouped Assertions of black height", - { assertEquals(3, countBlackNodes(nodes[0]!!), "1") }, - { assertEquals(3, countBlackNodes(nodes[16]!!), "2") }, - { assertEquals(3, countBlackNodes(nodes[17]!!)) }, - { assertEquals(2, countBlackNodes(nodes[24]!!)) }, - { assertEquals(2, countBlackNodes(nodes[29]!!)) } - ) - assertAll( - "Grouped Assertions of values", - { assertEquals(1, root.left?.left?.left?.left?.value) }, - { assertEquals(4, root.left?.left?.left?.value) }, - { assertEquals(12, root.left?.left?.right?.value) }, - { assertEquals(15, root.left?.value) }, - { assertEquals(null, root.left?.left?.left?.left?.left) }, - { assertEquals(null, root.left?.left?.right?.left) }, - { assertEquals(null, root.left?.left?.left?.right) }, - ) - } + @Test + fun `remove black leaf (black parent)`() { + val nodes = testModel.getFirstTree() + val root = nodes[30] + val balancer = RBBalancer(root) + nodes[27]?.right = null + assertEquals(root, balancer.balance(RBStateContainer(nodes[27]!!))) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[12]!!)) }, + { assertEquals(3, countBlackNodes(nodes[13]!!)) }, + { assertEquals(3, countBlackNodes(nodes[27]!!)) }, + { assertEquals(2, countBlackNodes(nodes[22]!!)) }, + { assertEquals(3, countBlackNodes(nodes[11]!!)) }, + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(50, root?.right?.value) }, + { assertEquals(63, root?.right?.right?.value) }, + { assertEquals(71, root?.right?.right?.right?.value) }, + { assertEquals(52, root?.right?.right?.left?.value) }, + { assertEquals(nodes[13]?.value, nodes[27]?.left?.value) }, + { assertEquals(null, nodes[27]?.right) }, + ) + } + // 3 + + @Test + fun `remove black leaf(red parent)`() { + val nodes = testModel.getFirstTree() + val root = nodes[30] + val balancer = RBBalancer(root) + nodes[17]?.left = null + assertEquals(root, balancer.balance(RBStateContainer(nodes[17]!!))) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(3, countBlackNodes(nodes[1]!!)) }, + { assertEquals(3, countBlackNodes(nodes[3]!!)) }, + { assertEquals(3, countBlackNodes(nodes[17]!!)) }, + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(50, root?.right?.value) }, + { assertEquals(15, root?.left?.value) }, + { assertEquals(19, root?.left?.right?.value) }, + { assertEquals(10, root?.left?.left?.value) }, + { assertEquals(12, root?.left?.left?.right?.value) }, + { assertEquals(14, nodes[17]?.right?.value) }, + { assertEquals(null, nodes[17]?.left)} + ) + } - @Test - fun `insertion with red uncle (Case 2)`() { - val nodes = getThirdTree() - var root = nodes[30] - val balancer = RBBalancer(root) - nodes[22]?.right = RBNode(65, null, null, nodes[22], Markers.RED) - root = balancer.balance(RBStateContainer(nodes[22]!!.right!!)) - assertAll( - "Assertions of root", - { assertEquals(50, root.value) }, - { assertEquals(Markers.BLACK, root.color) } - ) - assertAll( - "Grouped Assertions of black height", - { assertEquals(2, countBlackNodes(nodes[23]!!)) }, - { assertEquals(2, countBlackNodes(nodes[22]!!)) }, - { assertEquals(2, countBlackNodes(nodes[26]!!)) }, - { assertEquals(2, countBlackNodes(nodes[28]!!)) }, - { assertEquals(2, countBlackNodes(nodes[22]!!.right!!)) }, - { assertEquals(1, countBlackNodes(nodes[30]!!)) }, - { assertEquals(1, countBlackNodes(nodes[27]!!)) } - ) - assertAll( - "Grouped Assertions of values", - { assertEquals(10, root.left?.left?.left?.value) }, - { assertEquals(19, root.left?.left?.right?.value) }, - { assertEquals(40, root.left?.right?.value) }, - { assertEquals(63, root.right?.left?.value) }, - { assertEquals(65, root.right?.left?.right?.value) }, - { assertEquals(90, root.right?.right?.value) }, - { assertEquals(71, root.right?.value) }, - { assertEquals(42, root.left?.right?.right?.value) }, - { assertEquals(null, root.right?.left?.left) }, - ) + @Test + fun `remove root (Case 1)`() { + val nodes = testModel.getFirstTree() + var root = nodes[30] + val balancer = RBBalancer(root) + root = testModel.deleteNode(nodes[30]!!, nodes[8]!!) + + root = balancer.balance(RBStateContainer(nodes[20]!!)) + assertAll( + "Assertions of root", + { assertEquals(nodes[8]?.value, root.value) }, + { assertEquals(Markers.BLACK, root.color) }, + { assertEquals(null, root.parent) }, + { assertEquals(nodes[28]?.value, root.left?.value) }, + { assertEquals( nodes[29]?.value, root.right?.value ) } + ) + + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(3, countBlackNodes(nodes[9]!!)) }, + { assertEquals(1, countBlackNodes(nodes[29]!!)) }, + { assertEquals(3, countBlackNodes(nodes[23]!!)) }, + { assertEquals(3, countBlackNodes(nodes[20]!!)) } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(19, root.left?.right?.value) }, + { assertEquals(40, root.right?.left?.value) }, + { 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)} + ) + } - } + @Test + fun `remove root (Case 2)`() { + var root = RBNode(100, RBNode(29, null, null, null, Markers.RED), null, null, Markers.BLACK) + root.left?.parent = root + val balance = RBBalancer(root) + root = testModel.deleteNode(root, root.left!!) + + root = balance.balance(RBStateContainer(root)) + + assertAll( + "Assertions of root", + { assertEquals(Markers.BLACK, root.color) }, + { assertEquals(29, root.value) }, + { assertEquals(null, root.parent) }, + { assertEquals(null, root.left)}, + { assertEquals(null, root.right)} + ) + } - @Test - fun `insertion with red uncle (Case 3)`() { - val nodes = getFourthTree() - var root = nodes[30] - val balancer = RBBalancer(root) - nodes[19]!!.left = RBNode(21, null, null, nodes[19], Markers.RED) - root = balancer.balance(RBStateContainer(nodes[19]!!.left!!)) - - assertAll( - "Assertions of root", - { assertEquals(19, root.value) }, - { assertEquals(Markers.BLACK, root.color) } - ) - assertAll( - "Grouped Assertions of black height", - { assertEquals(2, countBlackNodes(nodes[24]!!)) }, - { assertEquals(1, countBlackNodes(nodes[28]!!)) }, - { assertEquals(2, countBlackNodes(nodes[18]!!)) }, - { assertEquals(2, countBlackNodes(nodes[19]!!)) }, - { assertEquals(2, countBlackNodes(nodes[19]!!.left!!)) }, - { assertEquals(2, countBlackNodes(nodes[27]!!)) }, - { assertEquals(2, countBlackNodes(nodes[26]!!)) } - ) - assertAll( - "Grouped Assertions of values", - { assertEquals(10, root.left?.left?.value) }, - { assertEquals(15, root.left?.value) }, - { assertEquals(18, root.left?.right?.value) }, - { assertEquals(25, root.right?.value) }, - { assertEquals(50, root.right?.right?.value) }, - { assertEquals(24, root.right?.left?.value) }, - { assertEquals(71, root.right?.right?.right?.value) }, - { assertEquals(21, root.right?.left?.left?.value) }, - { assertEquals(null, root.right?.left?.right) }, - { assertEquals(null, root.left?.left?.right) }, - { assertEquals(null, root.left?.right?.left) }, - ) - } + @Test + fun `remove black leaf(red parent, black brother with two red child)`() { + val nodes = testModel.getFirstTree() + 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) + nodes[13]?.right = rightBrotherSon + nodes[13]?.left = leftBrotherSon + val balancer = RBBalancer(nodes[30]) + nodes[22]?.left = null + + assertEquals(root, balancer.balance(RBStateContainer(nodes[22]!!))) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(rightBrotherSon), "1") }, + { assertEquals(3, countBlackNodes(leftBrotherSon), "2") }, + { assertEquals(3, countBlackNodes(nodes[22]!!)) }, + { assertEquals(3, countBlackNodes(nodes[13]!!)) }, + { assertEquals(2, countBlackNodes(root.right?.right!!)) }, + { assertEquals(2, countBlackNodes(nodes[13]?.parent!!)) } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(50, root.right?.value) }, + { assertEquals(71, root.right?.right?.value) }, + { 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)}, + ) - /*** RBT models for testing ***/ + } - private 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 - currentNode = currentNode.parent + @Test + fun `multiple deletions`() { + val nodes = testModel.getFirstTree() + val root = nodes[30] + val balancer = RBBalancer(root) + + /** first delete **/ + testModel.deleteNode(nodes[24]!!, nodes[2]!!) + + assertEquals(root!!.value, balancer.balance(RBStateContainer(nodes[17]!!)).value) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[3]!!)) }, + { assertEquals(3, countBlackNodes(nodes[17]!!)) }, + { assertEquals(2, countBlackNodes(nodes[26]!!)) }, + { assertEquals(2, countBlackNodes(nodes[24]!!)) } + ) + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(12, nodes[17]?.value) }, + { assertEquals(14, nodes[3]?.value) }, + { assertEquals(null, nodes[17]?.left) } + ) + + /** second delete **/ + testModel.deleteNode(nodes[16]!!, nodes[1]!!) + + assertEquals(root, balancer.balance(RBStateContainer(nodes[1]!!))) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(3, countBlackNodes(root.left?.left?.left!!)) }, + { assertEquals(2, countBlackNodes(nodes[24]!!), "2") }, + { assertEquals(1, countBlackNodes(nodes[28]!!)) }, + { assertEquals(1, countBlackNodes(nodes[30]!!)) }, + { assertEquals(3, countBlackNodes(nodes[17]!!), "1") } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(15, root.left?.value) }, + { assertEquals(11, root.left?.left?.value) }, + { assertEquals(12, root.left?.left?.right?.value) }, + { assertEquals(7, root.left?.left?.left?.value) }, + { assertEquals(1, root.left?.left?.left?.left?.value) }, + { assertEquals(null, root.left?.left?.left?.right) }, + ) + + /** third delete **/ + nodes[17]?.right = null + assertEquals(root, balancer.balance(RBStateContainer(nodes[17]!!))) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!), "1") }, + { assertEquals(3, countBlackNodes(nodes[1]!!), "2") }, + { assertEquals(3, countBlackNodes(nodes[17]!!), "3") }, + { assertEquals(2, countBlackNodes(nodes[2]!!)) }, + { assertEquals(1, countBlackNodes(nodes[28]!!)) }, + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(15, root.left?.value) }, + { assertEquals(11, root.left?.left?.value) }, + { assertEquals(7, root.left?.left?.left?.value) }, + { assertEquals(12, nodes[17]?.value) }, + { assertEquals(null, nodes[17]?.right) }, + { assertEquals(null, nodes[17]?.left) }, + ) + + /** fourth delete **/ + nodes[2]?.right = null + + assertEquals(root, balancer.balance(RBStateContainer(nodes[2]!!))) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(2, countBlackNodes(nodes[1]!!)) }, + { assertEquals(3, countBlackNodes(nodes[2]!!)) }, + { assertEquals(1, countBlackNodes(nodes[28]!!)) }, + { assertEquals(1, countBlackNodes(nodes[30]!!)) }, + { assertEquals(2, countBlackNodes(nodes[25]!!)) } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(15, root.left?.value) }, + { assertEquals(7, root.left?.left?.value) }, + { assertEquals(1, root.left?.left?.left?.value) }, + { assertEquals(11, root.left?.left?.right?.value) }, + { assertEquals(19, root.left?.right?.value) }, + { assertEquals(null, nodes[2]?.right) }, + { assertEquals(null, nodes[2]?.left) }, + ) + + /** fourth delete **/ + nodes[2]?.right = null + + assertEquals(root, balancer.balance(RBStateContainer(nodes[2]!!))) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(2, countBlackNodes(nodes[1]!!)) }, + { assertEquals(3, countBlackNodes(nodes[2]!!)) }, + { assertEquals(1, countBlackNodes(nodes[28]!!)) }, + { assertEquals(1, countBlackNodes(nodes[30]!!)) }, + { assertEquals(2, countBlackNodes(nodes[25]!!)) } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(15, root.left?.value) }, + { assertEquals(7, root.left?.left?.value) }, + { assertEquals(1, root.left?.left?.left?.value) }, + { assertEquals(11, root.left?.left?.right?.value) }, + { assertEquals(19, root.left?.right?.value) }, + { assertEquals(null, nodes[2]?.right) }, + { assertEquals(null, nodes[2]?.left) }, + ) + + /** fifth delete **/ + nodes[1]?.right = null + + assertEquals(root, balancer.balance(RBStateContainer(nodes[1]!!))) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!)) }, + { assertEquals(3, countBlackNodes(nodes[1]!!)) }, + { assertEquals(2, countBlackNodes(nodes[28]!!)) }, + { assertEquals(2, countBlackNodes(nodes[25]!!)) }, + { assertEquals(3, countBlackNodes(nodes[18]!!)) }, + { assertEquals(3, countBlackNodes(nodes[19]!!)) } + ) + + assertAll( + "Grouped Assertions of nodes values", + { assertEquals(15, root.left?.value) }, + { assertEquals(7, root.left?.left?.value) }, + { assertEquals(1, root.left?.left?.left?.value) }, + { assertEquals(19, root.left?.right?.value) }, + { assertEquals(24, root.left?.right?.right?.value) }, + { assertEquals(null, root.left?.left?.right) }, + ) } - return count } - private fun >deleteNode(remoteNode: RBNode, newNode: RBNode): RBNode { - remoteNode.left?.parent = newNode - remoteNode.right?.parent = newNode - newNode.left = remoteNode.left - newNode.right = remoteNode.right - newNode.color = remoteNode.color - when (newNode) { - newNode.parent?.left -> newNode.parent?.left = null - else -> newNode.parent?.right = null + @DisplayName("Tests to check the operation of the balancer after insertion") + @Nested + inner class InsertionTests { + @Test + fun `insertion root`() { + val root = RBNode(18, null, null, null, Markers.RED) + RBBalancer(root) + assertAll( + { assertEquals(Markers.BLACK, root.color) }, + { assertEquals(18, root.value) } + ) } - if (remoteNode.parent != null) { - val parent = remoteNode.parent!! - when (remoteNode) { - parent.left -> parent.left = newNode - else -> parent.right = newNode - } - newNode.parent = parent + @Test + fun `insertion with black parent`() { + var root = RBNode(18, null, null, null, Markers.BLACK) + val balancer = RBBalancer(root) + root.right = RBNode(39, null, null, root, Markers.RED) + root = balancer.balance(RBStateContainer(root.right!!)) + assertAll( + { assertEquals(18, root.value) }, + { assertEquals(Markers.BLACK, root.color) }, + { assertEquals(39, root.right?.value) }, + { assertEquals(null, root.left) }, + { assertEquals(null, root.right?.right) }, + { assertEquals(null, root.right?.left) } + ) } - else { - newNode.parent = null - } - - return newNode - } - private fun getFourthTree(): MutableList?> { - val nodes = getSecondTree() - for (i in 24..27) { - nodes[i]!!.right = null - nodes[i]!!.left = null + @Test + fun `insertion with red uncle (Case 1)`() { + val nodes = testModel.getSecondTree() + var root = nodes[30] + val balancer = RBBalancer(root) + root = balancer.balance(RBStateContainer(nodes[0]!!)) + assertAll( + "Assertions of root", + { assertEquals(25, root.value) }, + { assertEquals(Markers.BLACK, root.color) } + ) + assertAll( + "Grouped Assertions of black height", + { assertEquals(3, countBlackNodes(nodes[0]!!), "1") }, + { assertEquals(3, countBlackNodes(nodes[16]!!), "2") }, + { assertEquals(3, countBlackNodes(nodes[17]!!)) }, + { assertEquals(2, countBlackNodes(nodes[24]!!)) }, + { assertEquals(2, countBlackNodes(nodes[29]!!)) } + ) + assertAll( + "Grouped Assertions of values", + { assertEquals(1, root.left?.left?.left?.left?.value) }, + { assertEquals(4, root.left?.left?.left?.value) }, + { assertEquals(12, root.left?.left?.right?.value) }, + { assertEquals(15, root.left?.value) }, + { assertEquals(null, root.left?.left?.left?.left?.left) }, + { assertEquals(null, root.left?.left?.right?.left) }, + { assertEquals(null, root.left?.left?.left?.right) }, + ) } - nodes[25]!!.right = nodes[19] - nodes[25]!!.left = nodes[18] - nodes[25]!!.color = Markers.RED - nodes[29]!!.color = Markers.BLACK - nodes[26]!!.color = Markers.RED - nodes[27]!!.color = Markers.RED - - return nodes - } - - private fun getThirdTree(): MutableList?> { - val nodes = getSecondTree() - nodes[28]!!.color = Markers.BLACK - nodes[24]!!.color = Markers.RED - nodes[24]?.right = null - nodes[24]?.left = null - nodes[25]!!.color = Markers.RED - nodes[25]?.right = null - nodes[25]?.left = null - return nodes - } + @Test + fun `insertion with red uncle (Case 2)`() { + val nodes = testModel.getThirdTree() + var root = nodes[30] + val balancer = RBBalancer(root) + nodes[22]?.right = RBNode(65, null, null, nodes[22], Markers.RED) + root = balancer.balance(RBStateContainer(nodes[22]!!.right!!)) + assertAll( + "Assertions of root", + { assertEquals(50, root.value) }, + { assertEquals(Markers.BLACK, root.color) } + ) + assertAll( + "Grouped Assertions of black height", + { assertEquals(2, countBlackNodes(nodes[23]!!)) }, + { assertEquals(2, countBlackNodes(nodes[22]!!)) }, + { assertEquals(2, countBlackNodes(nodes[26]!!)) }, + { assertEquals(2, countBlackNodes(nodes[28]!!)) }, + { assertEquals(2, countBlackNodes(nodes[22]!!.right!!)) }, + { assertEquals(1, countBlackNodes(nodes[30]!!)) }, + { assertEquals(1, countBlackNodes(nodes[27]!!)) } + ) + assertAll( + "Grouped Assertions of values", + { assertEquals(10, root.left?.left?.left?.value) }, + { assertEquals(19, root.left?.left?.right?.value) }, + { assertEquals(40, root.left?.right?.value) }, + { assertEquals(63, root.right?.left?.value) }, + { assertEquals(65, root.right?.left?.right?.value) }, + { assertEquals(90, root.right?.right?.value) }, + { assertEquals(71, root.right?.value) }, + { assertEquals(42, root.left?.right?.right?.value) }, + { assertEquals(null, root.right?.left?.left) }, + ) - private fun getSecondTree(): MutableList?> { - val nodes = getFirstTree() - for (i in 16..23) { - nodes[i]!!.color = Markers.RED - nodes[i]!!.right = null - nodes[i]!!.left = null } - nodes[0]!!.color = Markers.RED - nodes[16]!!.left = nodes[0] - - return nodes - } - - private 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, - 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 - - ) - val nodes = mutableListOf?>() - 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]!!)) - 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]!!)) - else - nodes.add(null) - 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] - } - 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] - } - 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] - } - } + @Test + fun `insertion with red uncle (Case 3)`() { + val nodes = testModel.getFourthTree() + var root = nodes[30] + val balancer = RBBalancer(root) + nodes[19]!!.left = RBNode(21, null, null, nodes[19], Markers.RED) + root = balancer.balance(RBStateContainer(nodes[19]!!.left!!)) + + assertAll( + "Assertions of root", + { assertEquals(19, root.value) }, + { assertEquals(Markers.BLACK, root.color) } + ) + assertAll( + "Grouped Assertions of black height", + { assertEquals(2, countBlackNodes(nodes[24]!!)) }, + { assertEquals(1, countBlackNodes(nodes[28]!!)) }, + { assertEquals(2, countBlackNodes(nodes[18]!!)) }, + { assertEquals(2, countBlackNodes(nodes[19]!!)) }, + { assertEquals(2, countBlackNodes(nodes[19]!!.left!!)) }, + { assertEquals(2, countBlackNodes(nodes[27]!!)) }, + { assertEquals(2, countBlackNodes(nodes[26]!!)) } + ) + assertAll( + "Grouped Assertions of values", + { assertEquals(10, root.left?.left?.value) }, + { assertEquals(15, root.left?.value) }, + { assertEquals(18, root.left?.right?.value) }, + { assertEquals(25, root.right?.value) }, + { assertEquals(50, root.right?.right?.value) }, + { assertEquals(24, root.right?.left?.value) }, + { assertEquals(71, root.right?.right?.right?.value) }, + { assertEquals(21, root.right?.left?.left?.value) }, + { assertEquals(null, root.right?.left?.right) }, + { assertEquals(null, root.left?.left?.right) }, + { assertEquals(null, root.left?.right?.left) }, + ) } - return nodes } - + } diff --git a/lib/src/test/kotlin/TestModelRBT.kt b/lib/src/test/kotlin/TestModelRBT.kt new file mode 100644 index 0000000..6f1efa1 --- /dev/null +++ b/lib/src/test/kotlin/TestModelRBT.kt @@ -0,0 +1,142 @@ +import treeLib.RBtree.RBNode +import treeLib.Single_Objects.Markers + +class TestModelRBT { + 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 + currentNode = currentNode.parent + } + return count + } + + fun >deleteNode(remoteNode: RBNode, newNode: RBNode): RBNode { + remoteNode.left?.parent = newNode + remoteNode.right?.parent = newNode + newNode.left = remoteNode.left + newNode.right = remoteNode.right + newNode.color = remoteNode.color + when (newNode) { + newNode.parent?.left -> newNode.parent?.left = null + else -> newNode.parent?.right = null + } + + if (remoteNode.parent != null) { + val parent = remoteNode.parent!! + when (remoteNode) { + parent.left -> parent.left = newNode + else -> parent.right = newNode + } + newNode.parent = parent + } + else { + newNode.parent = null + } + + return newNode + } + + fun getFourthTree(): MutableList?> { + val nodes = getSecondTree() + for (i in 24..27) { + nodes[i]!!.right = null + nodes[i]!!.left = null + } + nodes[25]!!.right = nodes[19] + nodes[25]!!.left = nodes[18] + nodes[25]!!.color = Markers.RED + nodes[29]!!.color = Markers.BLACK + nodes[26]!!.color = Markers.RED + nodes[27]!!.color = Markers.RED + + return nodes + } + + fun getThirdTree(): MutableList?> { + val nodes = getSecondTree() + nodes[28]!!.color = Markers.BLACK + nodes[24]!!.color = Markers.RED + nodes[24]?.right = null + nodes[24]?.left = null + nodes[25]!!.color = Markers.RED + nodes[25]?.right = null + nodes[25]?.left = null + return nodes + + } + + fun getSecondTree(): MutableList?> { + val nodes = getFirstTree() + for (i in 16..23) { + nodes[i]!!.color = Markers.RED + nodes[i]!!.right = null + nodes[i]!!.left = null + } + nodes[0]!!.color = Markers.RED + nodes[16]!!.left = nodes[0] + + return nodes + } + + + 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, + 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 + + ) + val nodes = mutableListOf?>() + 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]!!)) + 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]!!)) + else + nodes.add(null) + 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] + } + 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] + } + 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] + } + } + } + return nodes + } +} \ No newline at end of file From ba271f4cf7a93c2b12e5fdbeaa8fae84811c42c6 Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 31 Mar 2023 19:27:56 +0300 Subject: [PATCH 043/212] feat: Implement tests for AVLBalancer --- .../kotlin/treeLib/AVLtree/AVLBalancer.kt | 17 +- lib/src/test/kotlin/AVLBalancerTest.kt | 236 ++++++++++++++++++ lib/src/test/kotlin/RBBalancerTest.kt | 3 +- lib/src/test/kotlin/TestModelAVL.kt | 72 ++++++ 4 files changed, 315 insertions(+), 13 deletions(-) create mode 100644 lib/src/test/kotlin/AVLBalancerTest.kt create mode 100644 lib/src/test/kotlin/TestModelAVL.kt diff --git a/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt index ef62262..02f5127 100644 --- a/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt @@ -11,18 +11,14 @@ class AVLBalancer>(private var root: AVLNode?): Bal return currentNode?.height ?: 0u } - private fun updateHeight(currentNode: AVLNode?): UInt { - return if (currentNode == null) 0u else ( maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u) + private fun updateHeight(currentNode: AVLNode?) { + if (currentNode != null) + currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right))+1u } override fun balance(stateContainer: AVLStateContainer): AVLNode { val node = stateContainer.contentNode root = stateContainer.root - when (node.value) { - root?.value -> { - - } - } return balance(root, node.value) } // В баланс передаем родителя ноды, которую будем удалять @@ -33,9 +29,8 @@ class AVLBalancer>(private var root: AVLNode?): Bal when { currentNode.value < value -> currentNode.right = balance(currentNode.right, value) currentNode.value > value -> currentNode.left = balance(currentNode.left, value) - currentNode.value == value -> return currentNode } - currentNode.height = updateHeight(currentNode) + updateHeight(currentNode) val balance = updateBalance(currentNode) if (balance == -2) { if (updateBalance(currentNode.right) == 1) { @@ -53,8 +48,8 @@ class AVLBalancer>(private var root: AVLNode?): Bal updateHeight(currentNode.left?.left) } val balanceNode = rightRotate(currentNode) - updateHeight(currentNode.right) - updateHeight(currentNode) + updateHeight(balanceNode.right) + updateHeight(balanceNode) return balanceNode } return currentNode diff --git a/lib/src/test/kotlin/AVLBalancerTest.kt b/lib/src/test/kotlin/AVLBalancerTest.kt new file mode 100644 index 0000000..ef95564 --- /dev/null +++ b/lib/src/test/kotlin/AVLBalancerTest.kt @@ -0,0 +1,236 @@ + +import org.junit.jupiter.api.* +import org.junit.jupiter.api.Assertions.assertEquals +import treeLib.AVLtree.AVLBalancer +import treeLib.AVLtree.AVLNode +import treeLib.AVLtree.AVLStateContainer + +class AVLBalancerTest { + + val testModel = TestModelAVL() + + @DisplayName("Tests to check the operation of the balancer after removal") + @Nested + inner class RemovalTests { + + @Test + fun `first remove (RightHeight-LeftHeight=2)`() { + val balancer = AVLBalancer(null) + var root = testModel.getTree(testModel.thirdTreeValues) + root.right?.right?.left = null + root = balancer.balance(AVLStateContainer(root.right?.right!!, root)) + assertAll( + "Grouped Assertions of height", + { assertEquals(3, root.left?.left?.height?.toInt()) }, + { assertEquals(5, root.height.toInt()) }, + { assertEquals(3, root.right?.right?.height?.toInt()) }, + { assertEquals(1, root.right?.right?.right?.height?.toInt()) }, + { assertEquals(3, root.right?.left?.height?.toInt()) } + ) + + assertAll( + "Grouped Assertions of values", + { assertEquals(104, root.value) }, + { assertEquals(74, root.left?.right?.value) }, + { assertEquals(163, root.right?.left?.value) }, + { assertEquals(370, root.right?.right?.right?.value) }, + { assertEquals(293, root.right?.right?.left?.right?.value) }, + { assertEquals(null, root.right?.right?.left?.left?.value) } + ) + } + + @Test + fun `second remove`() { + val balancer = AVLBalancer(null) + var root = testModel.getTree(testModel.thirdTreeValuesCase2) + root.right?.right?.right = null + root = balancer.balance(AVLStateContainer(root.right?.right!!, root)) + assertAll( + "Grouped Assertions of height", + { assertEquals(3, root.left?.left?.height?.toInt()) }, + { assertEquals(5, root.height.toInt()) }, + { assertEquals(2, root.right?.right?.height?.toInt()) }, + { assertEquals(1, root.right?.right?.right?.height?.toInt()) }, + { assertEquals(1, root.right?.right?.left?.height?.toInt()) }, + { assertEquals(3, root.right?.left?.height?.toInt()) } + ) + + assertAll( + "Grouped Assertions of values", + { assertEquals(104, root.value) }, + { assertEquals(74, root.left?.right?.value) }, + { assertEquals(163, root.right?.left?.value) }, + { assertEquals(351, root.right?.right?.right?.value) }, + { assertEquals(293, root.right?.right?.value) }, + { assertEquals(174, root.right?.left?.right?.left?.value) }, + { assertEquals(null, root.right?.right?.left?.left?.value) } + ) + } + + @Test + fun `third remove`() { + val balancer = AVLBalancer(null) + var root = testModel.getTree(testModel.thirdTreeValuesCase3) + root.right?.left?.left = null + root = balancer.balance(AVLStateContainer(root.right?.left!!, root)) + assertAll( + "Grouped Assertions of height", + { assertEquals(3, root.left?.left?.height?.toInt()) }, + { assertEquals(5, root.height.toInt()) }, + { assertEquals(2, root.right?.right?.height?.toInt(), "2") }, + { assertEquals(1, root.right?.left?.left?.height?.toInt()) }, + { assertEquals(2, root.right?.left?.height?.toInt()) } + ) + + assertAll( + "Grouped Assertions of values", + { assertEquals(104, root.value) }, + { assertEquals(74, root.left?.right?.value) }, + { assertEquals(174, root.right?.left?.value) }, + { assertEquals(180, root.right?.left?.right?.value) }, + { assertEquals(163, root.right?.left?.left?.value) }, + { assertEquals(293, root.right?.right?.value) }, + { assertEquals(null, root.right?.left?.left?.left?.value) }, + { assertEquals(null, root.right?.left?.right?.left?.value) } + ) + } + + @Test + fun `fourth remove (without the need for balancing)`() { + val balancer = AVLBalancer(null) + var root = testModel.getTree(testModel.thirdTreeValuesCase4) + root.right?.right?.right = null + root = balancer.balance(AVLStateContainer(root.right?.right!!, root)) + assertAll( + "Grouped Assertions of height", + { assertEquals(3, root.left?.left?.height?.toInt()) }, + { assertEquals(5, root.height.toInt()) }, + { assertEquals(2, root.right?.right?.height?.toInt()) }, + { assertEquals(1, root.right?.left?.right?.height?.toInt()) }, + { assertEquals(1, root.right?.left?.left?.height?.toInt()) }, + { assertEquals(2, root.right?.left?.height?.toInt()) } + ) + + assertAll( + "Grouped Assertions of values", + { assertEquals(104, root.value) }, + { assertEquals(74, root.left?.right?.value) }, + { assertEquals(174, root.right?.left?.value) }, + { assertEquals(180, root.right?.left?.right?.value) }, + { assertEquals(163, root.right?.left?.left?.value) }, + { assertEquals(293, root.right?.right?.value) }, + { assertEquals(285, root.right?.right?.left?.value) }, + { assertEquals(null, root.right?.right?.right?.value) } + ) + } + + + + } + + @DisplayName("Tests to check the operation of the balancer after insertion") + @Nested + inner class InsertionTests { + @Test + fun `insert case 1(LeftHeight - RightHeight = 2, delta(leftSon)=1)`() { + val balancer = AVLBalancer(null) + var root = testModel.getTree(testModel.firstTreeValues) + root = balancer.balance(AVLStateContainer(root.left!!, root)) + assertAll( + { assertEquals(14, root.value, "Value of the root") }, + { assertEquals(1, root.left?.value, "Value of the left son of the root") }, + { assertEquals(23, root.right?.value, "Value of the right son of the root") } + ) + } + + @Test + fun `insert case 2(LeftHeight- RightHeight =-2, delta(rightSon)=-1`() { + val balancer = AVLBalancer(null) + var root = testModel.getTree(testModel.secondTreeValues) + root.left?.left?.right?.right = AVLNode(53, AVLNode(49, null, null), null) + root = balancer.balance(AVLStateContainer(root.left?.left?.right?.right!!, root)) + assertAll( + { assertEquals(101, root.value) }, + { assertEquals(230, root.right?.value) }, + { assertEquals(47, root.left?.left?.value) }, + { assertEquals(44, root.left?.left?.left?.value) }, + { assertEquals(1, root.left?.left?.left?.left?.value) }, + { assertEquals(53, root.left?.left?.right?.value) }, + { assertEquals(49, root.left?.left?.right?.left?.value) } + ) + } + + @Test + fun `multiple insertions`() { + val balancer = AVLBalancer(null) + var root = testModel.getTree(testModel.secondTreeValues) + /** First insert **/ + root.left?.left?.right?.left?.right = AVLNode(46, null, null) + root = balancer.balance(AVLStateContainer(root.left?.left?.right?.left!!, root)) + assertAll( + "Grouped Assertions of height", + { assertEquals(5, root.height.toInt()) }, + { 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())} + ) + assertAll( + "Grouped Assertions of values", + { assertEquals(101, root.value) }, + { 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)} + ) + + /** Second insert **/ + root.left?.left?.right?.right?.right = AVLNode(50, null, null) + root = balancer.balance(AVLStateContainer(root.left?.left?.right?.right!!, root)) + assertAll( + "Grouped Assertions of height", + { assertEquals(1, root.left?.left?.right?.right?.height?.toInt()) }, + { assertEquals(1, root.left?.left?.left?.left?.height?.toInt()) }, + { assertEquals(2, root.right?.right?.height?.toInt()) }, + { assertEquals(2, root.left?.left?.right?.height?.toInt()) }, + { assertEquals(3, root.left?.left?.height?.toInt()) } + ) + assertAll( + "Grouped Assertions of values", + { assertEquals(101, root.value) }, + { assertEquals(205, root.right?.left?.value) }, + { 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)} + ) + + /** Third insert **/ + root.left?.left?.right?.right?.left = AVLNode(49, null, null) + root = balancer.balance(AVLStateContainer(root.left?.left?.right?.right!!, root)) + assertAll( + "Grouped Assertions of height", + { assertEquals(1, root.left?.left?.right?.right?.height?.toInt()) }, + { assertEquals(1, root.left?.left?.left?.left?.height?.toInt()) }, + { assertEquals(1, root.left?.left?.right?.left?.height?.toInt()) }, + { assertEquals(2, root.right?.right?.height?.toInt()) }, + { assertEquals(2, root.left?.left?.right?.height?.toInt()) }, + { assertEquals(3, root.left?.left?.height?.toInt()) } + ) + assertAll( + "Grouped Assertions of values", + { assertEquals(101, root.value) }, + { assertEquals(205, root.right?.left?.value) }, + { assertEquals(46, root.left?.left?.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)} + ) + } + } + +} diff --git a/lib/src/test/kotlin/RBBalancerTest.kt b/lib/src/test/kotlin/RBBalancerTest.kt index ff3b431..d6551f3 100644 --- a/lib/src/test/kotlin/RBBalancerTest.kt +++ b/lib/src/test/kotlin/RBBalancerTest.kt @@ -75,7 +75,6 @@ class RBBalancerTest { { assertEquals(null, nodes[27]?.right) }, ) } - // 3 @Test fun `remove black leaf(red parent)`() { @@ -479,5 +478,5 @@ class RBBalancerTest { ) } } - + } diff --git a/lib/src/test/kotlin/TestModelAVL.kt b/lib/src/test/kotlin/TestModelAVL.kt new file mode 100644 index 0000000..80860f3 --- /dev/null +++ b/lib/src/test/kotlin/TestModelAVL.kt @@ -0,0 +1,72 @@ +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 From 9f9785984d14fb924c9f8a10a1f1491468d920f5 Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 5 Apr 2023 18:21:07 +0300 Subject: [PATCH 044/212] fix(build.gradle): Update test task and add jacocoTestReport --- lib/build.gradle.kts | 104 +++++++++++++++++++++++++++++++++---------- 1 file changed, 81 insertions(+), 23 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 15a6539..3764b13 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -1,49 +1,107 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * This generated file contains a sample Kotlin library project to get you started. - * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle - * User Manual available at https://docs.gradle.org/8.0/userguide/building_java_projects.html - */ - plugins { - // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. - kotlin("jvm") version "1.8.0" - - // Apply the java-library plugin for API and implementation separation. + java + kotlin("jvm") version "1.8.10" + jacoco `java-library` `maven-publish` + // checkstyle } + java { toolchain { - languageVersion.set(JavaLanguageVersion.of(8)) + languageVersion.set(JavaLanguageVersion.of(17)) } } repositories { - // Use Maven Central for resolving dependencies. mavenLocal() mavenCentral() } dependencies { testImplementation("io.mockk:mockk:1.13.4") - // Use the Kotlin JUnit 5 integration. (TESTS support tools) testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") - - // Use the JUnit 5 integration. (TESTS support tools) testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") - - // This dependency is exported to consumers, that is to say found on their compile classpath. api("org.apache.commons:commons-math3:3.6.1") - - // This dependency is used internally, and not exposed to consumers on their own compile classpath. implementation("com.google.guava:guava:31.1-jre") } -tasks.named("test") { - // Use JUnit Platform for unit tests. +tasks.test { + finalizedBy(tasks.jacocoTestReport) useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + showStandardStreams = true + } + val failedTests = mutableListOf() + val skippedTests = mutableListOf() + + addTestListener (object: TestListener { + override fun beforeSuite(suite: TestDescriptor?) { } + override fun beforeTest(testDescriptor: TestDescriptor?) { } + override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { + when (result.resultType) { + TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor) + TestResult.ResultType.SKIPPED -> skippedTests.add(testDescriptor) + else -> {} + } + } + override fun afterSuite(suite: TestDescriptor, result: TestResult) { + if (suite.parent == null) { // root suite + logger.lifecycle("####################################################################################") + logger.lifecycle("Test result: ${result.resultType}") + logger.lifecycle( + "Test summary: ${result.testCount} tests, " + + "${result.successfulTestCount} succeed, " + + "${result.skippedTestCount} skipped, " + + "${result.failedTestCount} failed." + ) + } + } + }) + +} + +tasks.jacocoTestReport { + dependsOn(tasks.test) + reports { + xml.required.set(true) + xml.outputLocation.set(layout.buildDirectory.file("jacoco/report.xml")) + csv.required.set(true) + csv.outputLocation.set(layout.buildDirectory.file("jacoco/report.csv")) + html.required.set(true) + html.outputLocation.set(layout.buildDirectory.dir("jacocoHtml")) + } +} + +tasks.jacocoTestCoverageVerification { + classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { + include("**/RBBalancer.class", "**/AVLBalancer.class") + } }) + dependsOn(tasks.jacocoTestReport) + violationRules { + rule { + element = "CLASS" + limit { + counter = "BRANCH" + minimum = 0.4.toBigDecimal() + } + } + rule { + element = "CLASS" + limit { + counter = "LINE" + minimum = 0.6.toBigDecimal() + } + } + rule { + element = "CLASS" + limit { + counter = "METHOD" + minimum = 1.0.toBigDecimal() + } + } + } } publishing { From 8debbd5524116700c123e746bcf94731f60fd841 Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 5 Apr 2023 18:27:30 +0300 Subject: [PATCH 045/212] feat(CI): Implement CI.yml for Github Actions --- .github/workflows/CI.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/CI.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..287cbb6 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,26 @@ +name: build project + +on: [push, pull_request] + +jobs: + run: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 8 + - name: Run Tests + run: ./gradlew clean test + + - name: Run Test Coverage + run: ./gradlew jacocoTestReport + + - name: Jacoco Code Coverage Report + uses: cicirello/jacoco-badge-generator@v2.8.0 + with: + generate-branches-badge: true + jacoco-csv-file: lib/build/jacoco/report.csv From ba03f3e4e48d9e10073ed2e2307548790b02aa13 Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 7 Apr 2023 16:47:02 +0300 Subject: [PATCH 046/212] feat: Add mergeable.yml feat: Add mergeable.yml --- .github/mergeable.yml | 20 ++++++++++++++++++++ .github/workflows/CI.yml | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 .github/mergeable.yml diff --git a/.github/mergeable.yml b/.github/mergeable.yml new file mode 100644 index 0000000..2201f84 --- /dev/null +++ b/.github/mergeable.yml @@ -0,0 +1,20 @@ +version: 2 +mergeable: + - when: pull_request.* + name: 'Check decoration' + validate: + - do: approvals + min: + count: 2 + limit: + users: ['Artem-Rzhankoff', 'ItIsMrLaG', 'RozhkovAleksandr'] + - do: description + no_empty: + enabled: true + message: Description matter and should not be empty. Provide detail with **what** was changed, **why** it was changed, and **how** it was changed. + - do: title + must_exclude: + regex: ^\[WIP\] + - do: label + must_exclude: + regex: 'wip' diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 287cbb6..1233f55 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -12,7 +12,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 8 + java-version: 17 - name: Run Tests run: ./gradlew clean test From 981184b3e03b730de41eaeabf2c977dc1e42387b Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 7 Apr 2023 19:01:26 +0300 Subject: [PATCH 047/212] fix(CI): Add cache for gradle --- .github/workflows/CI.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1233f55..ae15aca 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -8,11 +8,14 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: temurin java-version: 17 + cache: gradle + - name: Run Tests run: ./gradlew clean test From 96b2474ddf3e9c65ad51b6b3257df12411c10085 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 9 Apr 2023 17:49:35 +0300 Subject: [PATCH 048/212] fix: Refactor packages names --- .../AbstractTree => treelib/abstractTree}/Node.kt | 2 +- .../abstractTree}/NodeParent.kt | 2 +- .../abstractTree}/StateContainer.kt | 2 +- .../AbstractTree => treelib/abstractTree}/Tree.kt | 4 ++-- .../abstractTree}/TreeStruct.kt | 2 +- .../abstractTree/balanced/BalancedTreeStruct.kt} | 10 +++++----- .../abstractTree/balanced}/Balancer.kt | 6 +++--- .../abstractTree/balanced}/BalancerNoParent.kt | 6 +++--- .../abstractTree/balanced}/BalancerParent.kt | 6 +++--- .../AVLtree => treelib/avlTree}/AVLBalancer.kt | 4 ++-- .../{treeLib/AVLtree => treelib/avlTree}/AVLNode.kt | 4 ++-- .../AVLtree => treelib/avlTree}/AVLStateContainer.kt | 4 ++-- .../{treeLib/AVLtree => treelib/avlTree}/AVLStruct.kt | 6 +++--- .../{treeLib/AVLtree => treelib/avlTree}/AVLTree.kt | 6 +++--- .../{treeLib/BINtree => treelib/binTree}/BINNode.kt | 4 ++-- .../{treeLib/BINtree => treelib/binTree}/BINStruct.kt | 4 ++-- .../{treeLib/BINtree => treelib/binTree}/BINTree.kt | 6 +++--- .../{treeLib/RBtree => treelib/rbTree}/RBBalancer.kt | 6 +++--- .../{treeLib/RBtree => treelib/rbTree}/RBNode.kt | 6 +++--- .../RBtree => treelib/rbTree}/RBStateContainer.kt | 4 ++-- .../{treeLib/RBtree => treelib/rbTree}/RBStruct.kt | 6 +++--- .../{treeLib/RBtree => treelib/rbTree}/RBTree.kt | 6 +++--- .../singleObjects}/Container.kt | 2 +- .../singleObjects}/Markers.kt | 2 +- lib/src/test/kotlin/AVLBalancerTest.kt | 7 +++---- lib/src/test/kotlin/RBBalancerTest.kt | 9 ++++----- lib/src/test/kotlin/TestModelAVL.kt | 2 +- lib/src/test/kotlin/TestModelRBT.kt | 4 ++-- 28 files changed, 65 insertions(+), 67 deletions(-) rename lib/src/main/kotlin/{treeLib/AbstractTree => treelib/abstractTree}/Node.kt (90%) rename lib/src/main/kotlin/{treeLib/AbstractTree => treelib/abstractTree}/NodeParent.kt (87%) rename lib/src/main/kotlin/{treeLib/AbstractTree => treelib/abstractTree}/StateContainer.kt (78%) rename lib/src/main/kotlin/{treeLib/AbstractTree => treelib/abstractTree}/Tree.kt (95%) rename lib/src/main/kotlin/{treeLib/AbstractTree => treelib/abstractTree}/TreeStruct.kt (98%) rename lib/src/main/kotlin/{treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt => treelib/abstractTree/balanced/BalancedTreeStruct.kt} (77%) rename lib/src/main/kotlin/{treeLib/AbstractTree/Weighted => treelib/abstractTree/balanced}/Balancer.kt (72%) rename lib/src/main/kotlin/{treeLib/AbstractTree/Weighted => treelib/abstractTree/balanced}/BalancerNoParent.kt (87%) rename lib/src/main/kotlin/{treeLib/AbstractTree/Weighted => treelib/abstractTree/balanced}/BalancerParent.kt (92%) rename lib/src/main/kotlin/{treeLib/AVLtree => treelib/avlTree}/AVLBalancer.kt (96%) rename lib/src/main/kotlin/{treeLib/AVLtree => treelib/avlTree}/AVLNode.kt (78%) rename lib/src/main/kotlin/{treeLib/AVLtree => treelib/avlTree}/AVLStateContainer.kt (76%) rename lib/src/main/kotlin/{treeLib/AVLtree => treelib/avlTree}/AVLStruct.kt (53%) rename lib/src/main/kotlin/{treeLib/AVLtree => treelib/avlTree}/AVLTree.kt (61%) rename lib/src/main/kotlin/{treeLib/BINtree => treelib/binTree}/BINNode.kt (75%) rename lib/src/main/kotlin/{treeLib/BINtree => treelib/binTree}/BINStruct.kt (82%) rename lib/src/main/kotlin/{treeLib/BINtree => treelib/binTree}/BINTree.kt (61%) rename lib/src/main/kotlin/{treeLib/RBtree => treelib/rbTree}/RBBalancer.kt (99%) rename lib/src/main/kotlin/{treeLib/RBtree => treelib/rbTree}/RBNode.kt (71%) rename lib/src/main/kotlin/{treeLib/RBtree => treelib/rbTree}/RBStateContainer.kt (80%) rename lib/src/main/kotlin/{treeLib/RBtree => treelib/rbTree}/RBStruct.kt (53%) rename lib/src/main/kotlin/{treeLib/RBtree => treelib/rbTree}/RBTree.kt (64%) rename lib/src/main/kotlin/{treeLib/Single_Objects => treelib/singleObjects}/Container.kt (94%) rename lib/src/main/kotlin/{treeLib/Single_Objects => treelib/singleObjects}/Markers.kt (55%) diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Node.kt b/lib/src/main/kotlin/treelib/abstractTree/Node.kt similarity index 90% rename from lib/src/main/kotlin/treeLib/AbstractTree/Node.kt rename to lib/src/main/kotlin/treelib/abstractTree/Node.kt index 48c022a..cd0d323 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Node.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Node.kt @@ -1,4 +1,4 @@ -package treeLib.AbstractTree +package treelib.abstractTree interface Node, SubNode : Node>{ var value: Pack diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/NodeParent.kt b/lib/src/main/kotlin/treelib/abstractTree/NodeParent.kt similarity index 87% rename from lib/src/main/kotlin/treeLib/AbstractTree/NodeParent.kt rename to lib/src/main/kotlin/treelib/abstractTree/NodeParent.kt index 2f01403..e6d444a 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/NodeParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/NodeParent.kt @@ -1,4 +1,4 @@ -package treeLib.AbstractTree +package treelib.abstractTree interface NodeParent, SubNode: NodeParent>: Node { diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/StateContainer.kt b/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt similarity index 78% rename from lib/src/main/kotlin/treeLib/AbstractTree/StateContainer.kt rename to lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt index 920d0d7..d2fdb06 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/StateContainer.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt @@ -1,4 +1,4 @@ -package treeLib.AbstractTree +package treelib.abstractTree interface StateContainer, NodeType: Node> { val contentNode: NodeType diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt similarity index 95% rename from lib/src/main/kotlin/treeLib/AbstractTree/Tree.kt rename to lib/src/main/kotlin/treelib/abstractTree/Tree.kt index 0ab75da..d84e839 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -1,6 +1,6 @@ -package treeLib.AbstractTree +package treelib.abstractTree -import treeLib.Single_Objects.Container +import treelib.singleObjects.Container abstract class Tree, V, NodeType: Node, NodeType>> { protected abstract val treeStruct: TreeStruct, NodeType> diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt similarity index 98% rename from lib/src/main/kotlin/treeLib/AbstractTree/TreeStruct.kt rename to lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 9cc9fa5..8d81cfa 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -1,4 +1,4 @@ -package treeLib.AbstractTree +package treelib.abstractTree abstract class TreeStruct, NodeType : Node> { protected abstract var root: NodeType? //TODO root - not a null diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt similarity index 77% rename from lib/src/main/kotlin/treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt rename to lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt index ca76866..b58db07 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/WeightedTreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt @@ -1,10 +1,10 @@ -package treeLib.AbstractTree.Weighted +package treelib.abstractTree.balanced -import treeLib.AbstractTree.Node -import treeLib.AbstractTree.StateContainer -import treeLib.AbstractTree.TreeStruct +import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer +import treelib.abstractTree.TreeStruct -abstract class WeightedTreeStruct, NodeType : Node, StateContainerType: StateContainer, BalancerType: Balancer>: +abstract class BalancedTreeStruct, NodeType : Node, StateContainerType: StateContainer, BalancerType: Balancer>: TreeStruct() { protected abstract val balancer: BalancerType diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/Balancer.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt similarity index 72% rename from lib/src/main/kotlin/treeLib/AbstractTree/Weighted/Balancer.kt rename to lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt index ce53fc7..9ceccf1 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/Balancer.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt @@ -1,7 +1,7 @@ -package treeLib.AbstractTree.Weighted +package treelib.abstractTree.balanced -import treeLib.AbstractTree.Node -import treeLib.AbstractTree.StateContainer +import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer interface Balancer , NodeType : Node, StateContainerType: StateContainer> { fun rightRotate(currentNode: NodeType): NodeType diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerNoParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt similarity index 87% rename from lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerNoParent.kt rename to lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt index bfa0be0..5378523 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerNoParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt @@ -1,7 +1,7 @@ -package treeLib.AbstractTree.Weighted +package treelib.abstractTree.balanced -import treeLib.AbstractTree.Node -import treeLib.AbstractTree.StateContainer +import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer abstract class BalancerNoParent, NodeType : Node, StateContainerType: StateContainer>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { diff --git a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt similarity index 92% rename from lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt rename to lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt index 1e6bd12..059314e 100644 --- a/lib/src/main/kotlin/treeLib/AbstractTree/Weighted/BalancerParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt @@ -1,7 +1,7 @@ -package treeLib.AbstractTree.Weighted +package treelib.abstractTree.balanced -import treeLib.AbstractTree.NodeParent -import treeLib.AbstractTree.StateContainer +import treelib.abstractTree.NodeParent +import treelib.abstractTree.StateContainer abstract class BalancerParent, NodeType : NodeParent, StateContainerType: StateContainer>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { diff --git a/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt similarity index 96% rename from lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt rename to lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 02f5127..ed929e8 100644 --- a/lib/src/main/kotlin/treeLib/AVLtree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -1,6 +1,6 @@ -package treeLib.AVLtree +package treelib.avlTree -import treeLib.AbstractTree.Weighted.BalancerNoParent +import treelib.abstractTree.balanced.BalancerNoParent class AVLBalancer>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { diff --git a/lib/src/main/kotlin/treeLib/AVLtree/AVLNode.kt b/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt similarity index 78% rename from lib/src/main/kotlin/treeLib/AVLtree/AVLNode.kt rename to lib/src/main/kotlin/treelib/avlTree/AVLNode.kt index c858373..b0da173 100644 --- a/lib/src/main/kotlin/treeLib/AVLtree/AVLNode.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt @@ -1,6 +1,6 @@ -package treeLib.AVLtree +package treelib.avlTree -import treeLib.AbstractTree.Node +import treelib.abstractTree.Node class AVLNode>( override var value: Pack, diff --git a/lib/src/main/kotlin/treeLib/AVLtree/AVLStateContainer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt similarity index 76% rename from lib/src/main/kotlin/treeLib/AVLtree/AVLStateContainer.kt rename to lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt index 77dc3cf..60e7636 100644 --- a/lib/src/main/kotlin/treeLib/AVLtree/AVLStateContainer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt @@ -1,6 +1,6 @@ -package treeLib.AVLtree +package treelib.avlTree -import treeLib.AbstractTree.StateContainer +import treelib.abstractTree.StateContainer /*Content node - the parent of the node on which the action was performed*/ diff --git a/lib/src/main/kotlin/treeLib/AVLtree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt similarity index 53% rename from lib/src/main/kotlin/treeLib/AVLtree/AVLStruct.kt rename to lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index ad842d6..2f5cee3 100644 --- a/lib/src/main/kotlin/treeLib/AVLtree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -1,8 +1,8 @@ -package treeLib.AVLtree +package treelib.avlTree -import treeLib.AbstractTree.Weighted.WeightedTreeStruct +import treelib.abstractTree.balanced.BalancedTreeStruct -class AVLStruct> : WeightedTreeStruct, AVLStateContainer, AVLBalancer>() { +class AVLStruct> : BalancedTreeStruct, AVLStateContainer, AVLBalancer>() { override var root: AVLNode? = null override val balancer = AVLBalancer(root) } \ No newline at end of file diff --git a/lib/src/main/kotlin/treeLib/AVLtree/AVLTree.kt b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt similarity index 61% rename from lib/src/main/kotlin/treeLib/AVLtree/AVLTree.kt rename to lib/src/main/kotlin/treelib/avlTree/AVLTree.kt index 15205b4..ba4da32 100644 --- a/lib/src/main/kotlin/treeLib/AVLtree/AVLTree.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt @@ -1,8 +1,8 @@ -package treeLib.AVLtree +package treelib.avlTree -import treeLib.AbstractTree.Tree +import treelib.abstractTree.Tree -import treeLib.Single_Objects.Container +import treelib.singleObjects.Container class AVLTree, V> : Tree>>() { diff --git a/lib/src/main/kotlin/treeLib/BINtree/BINNode.kt b/lib/src/main/kotlin/treelib/binTree/BINNode.kt similarity index 75% rename from lib/src/main/kotlin/treeLib/BINtree/BINNode.kt rename to lib/src/main/kotlin/treelib/binTree/BINNode.kt index 9db9413..a503f34 100644 --- a/lib/src/main/kotlin/treeLib/BINtree/BINNode.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINNode.kt @@ -1,6 +1,6 @@ -package treeLib.BINtree +package treelib.binTree -import treeLib.AbstractTree.Node +import treelib.abstractTree.Node class BINNode>( override var value: Pack, diff --git a/lib/src/main/kotlin/treeLib/BINtree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt similarity index 82% rename from lib/src/main/kotlin/treeLib/BINtree/BINStruct.kt rename to lib/src/main/kotlin/treelib/binTree/BINStruct.kt index 469a15c..84c2c06 100644 --- a/lib/src/main/kotlin/treeLib/BINtree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -1,6 +1,6 @@ -package treeLib.BINtree +package treelib.binTree -import treeLib.AbstractTree.TreeStruct +import treelib.abstractTree.TreeStruct class BINStruct> : TreeStruct>() { override var root: BINNode? = null diff --git a/lib/src/main/kotlin/treeLib/BINtree/BINTree.kt b/lib/src/main/kotlin/treelib/binTree/BINTree.kt similarity index 61% rename from lib/src/main/kotlin/treeLib/BINtree/BINTree.kt rename to lib/src/main/kotlin/treelib/binTree/BINTree.kt index e5c0841..863f2ca 100644 --- a/lib/src/main/kotlin/treeLib/BINtree/BINTree.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINTree.kt @@ -1,7 +1,7 @@ -package treeLib.BINtree +package treelib.binTree -import treeLib.AbstractTree.Tree -import treeLib.Single_Objects.Container +import treelib.abstractTree.Tree +import treelib.singleObjects.Container class BINTree, V> : Tree>>() { override val treeStruct: BINStruct> = BINStruct() diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt similarity index 99% rename from lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt rename to lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 8a7b9b5..ad605c8 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -1,7 +1,7 @@ -package treeLib.RBtree +package treelib.rbTree -import treeLib.AbstractTree.Weighted.BalancerParent -import treeLib.Single_Objects.Markers +import treelib.abstractTree.balanced.BalancerParent +import treelib.singleObjects.Markers class RBBalancer>(private var root: RBNode?): BalancerParent, RBStateContainer>() { diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBNode.kt b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt similarity index 71% rename from lib/src/main/kotlin/treeLib/RBtree/RBNode.kt rename to lib/src/main/kotlin/treelib/rbTree/RBNode.kt index 8b44097..f069e54 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBNode.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt @@ -1,7 +1,7 @@ -package treeLib.RBtree +package treelib.rbTree -import treeLib.AbstractTree.NodeParent -import treeLib.Single_Objects.Markers +import treelib.abstractTree.NodeParent +import treelib.singleObjects.Markers class RBNode>( override var value: Pack, diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBStateContainer.kt b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt similarity index 80% rename from lib/src/main/kotlin/treeLib/RBtree/RBStateContainer.kt rename to lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt index 5dd4ce0..a855ed8 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBStateContainer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt @@ -1,6 +1,6 @@ -package treeLib.RBtree +package treelib.rbTree -import treeLib.AbstractTree.StateContainer +import treelib.abstractTree.StateContainer /* Insert: contentNode - the node on which the action was performed diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt similarity index 53% rename from lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt rename to lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index e994208..066e39a 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -1,8 +1,8 @@ -package treeLib.RBtree +package treelib.rbTree -import treeLib.AbstractTree.Weighted.WeightedTreeStruct +import treelib.abstractTree.balanced.BalancedTreeStruct -class RBStruct > : WeightedTreeStruct, RBStateContainer, RBBalancer>(){ +class RBStruct > : BalancedTreeStruct, RBStateContainer, RBBalancer>(){ override var root: RBNode? = null override val balancer = RBBalancer(root) diff --git a/lib/src/main/kotlin/treeLib/RBtree/RBTree.kt b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt similarity index 64% rename from lib/src/main/kotlin/treeLib/RBtree/RBTree.kt rename to lib/src/main/kotlin/treelib/rbTree/RBTree.kt index 798ed57..6b6c149 100644 --- a/lib/src/main/kotlin/treeLib/RBtree/RBTree.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt @@ -1,8 +1,8 @@ -package treeLib.RBtree +package treelib.rbTree -import treeLib.AbstractTree.Tree +import treelib.abstractTree.Tree -import treeLib.Single_Objects.Container +import treelib.singleObjects.Container class RBTree, Value> : Tree>>() { override val treeStruct: RBStruct> = RBStruct() diff --git a/lib/src/main/kotlin/treeLib/Single_Objects/Container.kt b/lib/src/main/kotlin/treelib/singleObjects/Container.kt similarity index 94% rename from lib/src/main/kotlin/treeLib/Single_Objects/Container.kt rename to lib/src/main/kotlin/treelib/singleObjects/Container.kt index 9c1511c..7e69192 100644 --- a/lib/src/main/kotlin/treeLib/Single_Objects/Container.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/Container.kt @@ -1,4 +1,4 @@ -package treeLib.Single_Objects +package treelib.singleObjects class Container, V>(private val pair: Pair) : Comparable> { val key = pair.first diff --git a/lib/src/main/kotlin/treeLib/Single_Objects/Markers.kt b/lib/src/main/kotlin/treelib/singleObjects/Markers.kt similarity index 55% rename from lib/src/main/kotlin/treeLib/Single_Objects/Markers.kt rename to lib/src/main/kotlin/treelib/singleObjects/Markers.kt index 2e7935a..3e4e190 100644 --- a/lib/src/main/kotlin/treeLib/Single_Objects/Markers.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/Markers.kt @@ -1,4 +1,4 @@ -package treeLib.Single_Objects +package treelib.singleObjects enum class Markers { RED, BLACK diff --git a/lib/src/test/kotlin/AVLBalancerTest.kt b/lib/src/test/kotlin/AVLBalancerTest.kt index ef95564..862a497 100644 --- a/lib/src/test/kotlin/AVLBalancerTest.kt +++ b/lib/src/test/kotlin/AVLBalancerTest.kt @@ -1,9 +1,8 @@ - import org.junit.jupiter.api.* import org.junit.jupiter.api.Assertions.assertEquals -import treeLib.AVLtree.AVLBalancer -import treeLib.AVLtree.AVLNode -import treeLib.AVLtree.AVLStateContainer +import treelib.avlTree.AVLBalancer +import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer class AVLBalancerTest { diff --git a/lib/src/test/kotlin/RBBalancerTest.kt b/lib/src/test/kotlin/RBBalancerTest.kt index d6551f3..0bc9e43 100644 --- a/lib/src/test/kotlin/RBBalancerTest.kt +++ b/lib/src/test/kotlin/RBBalancerTest.kt @@ -1,13 +1,12 @@ - import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertAll -import treeLib.RBtree.RBBalancer -import treeLib.RBtree.RBNode -import treeLib.RBtree.RBStateContainer -import treeLib.Single_Objects.Markers +import treelib.rbTree.RBBalancer +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.singleObjects.Markers class RBBalancerTest { val testModel = TestModelRBT() diff --git a/lib/src/test/kotlin/TestModelAVL.kt b/lib/src/test/kotlin/TestModelAVL.kt index 80860f3..c96773d 100644 --- a/lib/src/test/kotlin/TestModelAVL.kt +++ b/lib/src/test/kotlin/TestModelAVL.kt @@ -1,4 +1,4 @@ -import treeLib.AVLtree.AVLNode +import treelib.avlTree.AVLNode class TestModelAVL { val firstTreeValues = diff --git a/lib/src/test/kotlin/TestModelRBT.kt b/lib/src/test/kotlin/TestModelRBT.kt index 6f1efa1..ac86a6c 100644 --- a/lib/src/test/kotlin/TestModelRBT.kt +++ b/lib/src/test/kotlin/TestModelRBT.kt @@ -1,5 +1,5 @@ -import treeLib.RBtree.RBNode -import treeLib.Single_Objects.Markers +import treelib.rbTree.RBNode +import treelib.singleObjects.Markers class TestModelRBT { fun >countBlackNodes(node: RBNode): Int { From 24ca1755883a6c6da4c9bee106d6fcd931f3731a Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 9 Apr 2023 18:19:06 +0300 Subject: [PATCH 049/212] fix: Resolve some problems after merge --- lib/build.gradle.kts | 2 +- .../kotlin/treelib/avlTree/AVLBalancer.kt | 2 +- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 2 +- .../test/kotlin/treelib/TreeStructsTest.kt | 55 ------------------- 4 files changed, 3 insertions(+), 58 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 3764b13..09124dc 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -76,7 +76,7 @@ tasks.jacocoTestReport { tasks.jacocoTestCoverageVerification { classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { - include("**/RBBalancer.class", "**/AVLBalancer.class") + include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct") } }) dependsOn(tasks.jacocoTestReport) violationRules { diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index ed929e8..96a938b 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -19,7 +19,7 @@ class AVLBalancer>(private var root: AVLNode?): Bal override fun balance(stateContainer: AVLStateContainer): AVLNode { val node = stateContainer.contentNode root = stateContainer.root - return balance(root, node.value) + return balance(root, node?.value ?: throw NullPointerException()) } // В баланс передаем родителя ноды, которую будем удалять private fun balance(currentNode: AVLNode?, value: Pack): AVLNode { diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index ad605c8..229425d 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -40,7 +40,7 @@ class RBBalancer>(private var root: RBNode?): Balan } override fun balance(stateContainer: RBStateContainer): RBNode { - val node = stateContainer.contentNode + val node = stateContainer.contentNode ?: throw NullPointerException() val uncle = getUncle(node) when { /** node insertion case **/ diff --git a/lib/src/test/kotlin/treelib/TreeStructsTest.kt b/lib/src/test/kotlin/treelib/TreeStructsTest.kt index 1f3f457..a79b9ef 100644 --- a/lib/src/test/kotlin/treelib/TreeStructsTest.kt +++ b/lib/src/test/kotlin/treelib/TreeStructsTest.kt @@ -215,33 +215,6 @@ class TreeStructsTest { assertEquals(expected = 15, actual = root) } - @Test - fun `test delete empty tree`() { - 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 in one root tree`() { - classUnderTest.insert(1) - 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 = 1, actual = root) - } - @Test fun `test delete one child left`(){ val num = mutableListOf(3, 2, 1, 5) @@ -371,34 +344,6 @@ class TreeStructsTest { assertEquals(expected = node_null_right6, actual = null) } - @Test - fun `test double delete`() { - val num = mutableListOf(1, 2) - for (value in num) { - classUnderTest.insert(value) - } - classUnderTest.delete(2) - classUnderTest.delete(2) - - val root = treeW.getPrivateNode(classUnderTest)?.value - - assertEquals(expected = root, actual = 1) - } - - @Test - fun `test double delete root`() { - val num = mutableListOf(2) - for (value in num) { - classUnderTest.insert(value) - } - classUnderTest.delete(2) - classUnderTest.delete(2) - - val root = treeW.getPrivateNode(classUnderTest)?.value - - assertEquals(expected = root, actual = null) - } - @Test fun `test two child double delete and delete root`() { val num = mutableListOf(6, 8, 10, 7) From c771f57077b2a42ad79f130521c7889540c5f786 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 15 Apr 2023 16:17:24 +0300 Subject: [PATCH 050/212] feat: Add custom Exceptions --- .../exceptions/BugInImplementException.kt | 16 ++++++++++++++++ .../exceptions/IllegalBaseNodeException.kt | 18 ++++++++++++++++++ .../exceptions/IllegalNodeStateException.kt | 18 ++++++++++++++++++ .../exceptions/ImpossibleCaseException.kt | 18 ++++++++++++++++++ .../exceptions/MultithreadingException.kt | 18 ++++++++++++++++++ .../exceptions/NonExistentValueException.kt | 18 ++++++++++++++++++ 6 files changed, 106 insertions(+) create mode 100644 lib/src/main/kotlin/treelib/singleObjects/exceptions/BugInImplementException.kt create mode 100644 lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt create mode 100644 lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalNodeStateException.kt create mode 100644 lib/src/main/kotlin/treelib/singleObjects/exceptions/ImpossibleCaseException.kt create mode 100644 lib/src/main/kotlin/treelib/singleObjects/exceptions/MultithreadingException.kt create mode 100644 lib/src/main/kotlin/treelib/singleObjects/exceptions/NonExistentValueException.kt diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/BugInImplementException.kt b/lib/src/main/kotlin/treelib/singleObjects/exceptions/BugInImplementException.kt new file mode 100644 index 0000000..69b9875 --- /dev/null +++ b/lib/src/main/kotlin/treelib/singleObjects/exceptions/BugInImplementException.kt @@ -0,0 +1,16 @@ +package treelib.singleObjects.exceptions + +class BugInImplementException : Exception { + constructor() : super() + + constructor(message: String) : super( + "Error: $message", + ) + + constructor(message: String, cause: Throwable) : super( + "Error: $message", + cause, + ) + + constructor(cause: Throwable) : super(cause) +} diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt b/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt new file mode 100644 index 0000000..4744fc7 --- /dev/null +++ b/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt @@ -0,0 +1,18 @@ +package treelib.singleObjects.exceptions + +class IllegalBaseNodeException : Exception { + constructor() : super( + "A non-existent node (null) was passed to the method" + ) + + constructor(message: String) : super( + "A non-existent node (null) was passed to the method: $message", + ) + + constructor(message: String, cause: Throwable) : super( + "A non-existent node (null) was passed to the method: $message", + cause, + ) + + constructor(cause: Throwable) : super(cause) +} diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalNodeStateException.kt b/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalNodeStateException.kt new file mode 100644 index 0000000..26080b8 --- /dev/null +++ b/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalNodeStateException.kt @@ -0,0 +1,18 @@ +package treelib.singleObjects.exceptions + +class IllegalNodeStateException : Exception { + constructor() : super( + "There is no node required by the condition of the algorithm" + ) + + constructor(message: String) : super( + "There is no node required by the condition of the algorithm: $message", + ) + + constructor(message: String, cause: Throwable) : super( + "There is no node required by the condition of the algorithm: $message", + cause, + ) + + constructor(cause: Throwable) : super(cause) +} diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/ImpossibleCaseException.kt b/lib/src/main/kotlin/treelib/singleObjects/exceptions/ImpossibleCaseException.kt new file mode 100644 index 0000000..02556f3 --- /dev/null +++ b/lib/src/main/kotlin/treelib/singleObjects/exceptions/ImpossibleCaseException.kt @@ -0,0 +1,18 @@ +package treelib.singleObjects.exceptions + +open class ImpossibleCaseException : Exception { + constructor() : super( + "This case is impossible, otherwise there is an error in the program code", + ) + + constructor(message: String) : super( + "This case is impossible, otherwise there is an error in the program code -> $message", + ) + + constructor(message: String, cause: Throwable) : super( + "This case is impossible, otherwise there is an error in the program code -> $message", + cause, + ) + + constructor(cause: Throwable) : super(cause) +} diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/MultithreadingException.kt b/lib/src/main/kotlin/treelib/singleObjects/exceptions/MultithreadingException.kt new file mode 100644 index 0000000..a51fee4 --- /dev/null +++ b/lib/src/main/kotlin/treelib/singleObjects/exceptions/MultithreadingException.kt @@ -0,0 +1,18 @@ +package treelib.singleObjects.exceptions + +open class MultithreadingException : Exception { + constructor() : super( + "Possible problem with multithreading", + ) + + constructor(message: String) : super( + "Possible problem with multithreading -> $message", + ) + + constructor(message: String, cause: Throwable) : super( + "Possible problem with multithreading -> $message", + cause, + ) + + constructor(cause: Throwable) : super(cause) +} diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/NonExistentValueException.kt b/lib/src/main/kotlin/treelib/singleObjects/exceptions/NonExistentValueException.kt new file mode 100644 index 0000000..af6b632 --- /dev/null +++ b/lib/src/main/kotlin/treelib/singleObjects/exceptions/NonExistentValueException.kt @@ -0,0 +1,18 @@ +package treelib.singleObjects.exceptions + +open class NonExistentValueException : Exception { + constructor() : super( + "Value doesn't exist in the tree" + ) + + constructor(message: String) : super( + "Value doesn't exist in the tree $message", + ) + + constructor(message: String, cause: Throwable) : super( + "Value doesn't exist in the tree -> $message", + cause, + ) + + constructor(cause: Throwable) : super(cause) +} From 4c8194846b11017284ae638994280158cbeb03c7 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 15 Apr 2023 16:27:13 +0300 Subject: [PATCH 051/212] feat: Rewrite thrown exceptions to custom ones --- .../main/kotlin/treelib/abstractTree/Tree.kt | 3 +- .../kotlin/treelib/abstractTree/TreeStruct.kt | 31 +++++++++-------- .../treelib/abstractTree/balanced/Balancer.kt | 1 - .../abstractTree/balanced/BalancerNoParent.kt | 11 ++++--- .../abstractTree/balanced/BalancerParent.kt | 6 ++-- .../kotlin/treelib/avlTree/AVLBalancer.kt | 23 +++++++------ .../treelib/avlTree/AVLStateContainer.kt | 7 ---- .../treelib/binTree/BINStateContainer.kt | 5 --- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 33 ++++++++++--------- .../kotlin/treelib/rbTree/RBStateContainer.kt | 6 ---- 10 files changed, 60 insertions(+), 66 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index 7040cf5..b64cca1 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -1,6 +1,7 @@ package treelib.abstractTree import treelib.singleObjects.Container +import treelib.singleObjects.exceptions.NonExistentValueException abstract class Tree< Key : Comparable, @@ -28,7 +29,7 @@ abstract class Tree< fun getItem(key: Key): Value? = treeStruct.find(wrapForFind(key))?.value fun deleteItem(key: Key) { - if (getItem(key) == null) throw Exception("Attempt to remove a non-existent element") + if (getItem(key) == null) throw NonExistentValueException() treeStruct.delete(wrapForFind(key)) } diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 38a5118..2f21c10 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -1,5 +1,10 @@ package treelib.abstractTree +import treelib.singleObjects.exceptions.BugInImplementException +import treelib.singleObjects.exceptions.ImpossibleCaseException +import treelib.singleObjects.exceptions.MultithreadingException +import treelib.singleObjects.exceptions.NonExistentValueException + abstract class TreeStruct, NodeType : Node, State : StateContainer> { @@ -21,9 +26,9 @@ abstract class TreeStruct, NodeType : Node throw Exception("getLeafForInsert shouldn't be used with a value exists in Struct") + else -> throw BugInImplementException("getLeafForInsert shouldn't be used with a value exists in Struct") } - } ?: throw Exception("Impossible case or multithreading problem") + } ?: throw MultithreadingException(ImpossibleCaseException()) } } @@ -42,7 +47,7 @@ abstract class TreeStruct, NodeType : Node + } ?: throw BugInImplementException("getParentByValue shouldn't be used with value doesn't exist in tree")// (1)l -> } } @@ -65,7 +70,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node if (curNode.left == null) return@getRightMinNode curNode else currentNode = curNode.left - } ?: throw Exception("Impossible case or multithreading threads problem") + } ?: throw MultithreadingException(ImpossibleCaseException()) } } @@ -85,7 +90,7 @@ abstract class TreeStruct, NodeType : Node throw Exception("unLink - method Shouldn't be used with node with both children") + (node.right != null) && (node.left != null) -> throw BugInImplementException("unLink - method Shouldn't be used with node with both children") node.right != null -> childForLink = node.right node.left != null -> childForLink = node.left else -> childForLink = null @@ -169,18 +174,18 @@ abstract class TreeStruct, NodeType : Node parentDeleteNode.right item inLeftOf parentDeleteNode -> parentDeleteNode.left - else -> throw Exception("Impossible case in deleteItem (is turned out to be possible)") + else -> throw ImpossibleCaseException() } } else deleteNode = root - if (deleteNode == null) throw Exception("Impossible case in deleteItem (is turned out to be possible)") + if (deleteNode == null) throw ImpossibleCaseException() val nodeForReplace: NodeType? val parentNodeForReplace: NodeType? @@ -219,7 +224,7 @@ abstract class TreeStruct, NodeType : Node, NodeType : Node, NodeType : Node, NodeType : Node, StateContainerType: StateContainer>: Balancer { +abstract class BalancerNoParent, NodeType : Node, StateContainerType : StateContainer> : + Balancer { override fun rightRotate(currentNode: NodeType): NodeType { - // вероятно можем полагаться на то, что сын не будет null - val leftSon = currentNode.left ?: throw NullPointerException() + val leftSon = currentNode.left ?: throw IllegalNodeStateException() currentNode.left = leftSon.right leftSon.right = currentNode return leftSon } override fun leftRotate(currentNode: NodeType): NodeType { - val rightSon = currentNode.right ?: throw NullPointerException() + val rightSon = currentNode.right ?: throw IllegalNodeStateException() currentNode.right = rightSon.left rightSon.left = currentNode return rightSon } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt index 059314e..afaf5d5 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt @@ -2,11 +2,11 @@ package treelib.abstractTree.balanced import treelib.abstractTree.NodeParent import treelib.abstractTree.StateContainer +import treelib.singleObjects.exceptions.IllegalNodeStateException abstract class BalancerParent, NodeType : NodeParent, StateContainerType: StateContainer>: Balancer { override fun rightRotate(currentNode: NodeType): NodeType { - // пока верим в то, что currentNode не равно null - val leftChild = currentNode.left ?: throw NullPointerException() + val leftChild = currentNode.left ?: throw IllegalNodeStateException() val parent = currentNode.parent leftChild.right?.parent = currentNode @@ -23,7 +23,7 @@ abstract class BalancerParent, NodeType : NodeParent>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { +class AVLBalancer>(private var root: AVLNode?) : + BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { return (getHeight(node?.left) - getHeight(node?.right)).toInt() } @@ -13,18 +16,19 @@ class AVLBalancer>(private var root: AVLNode?): Bal private fun updateHeight(currentNode: AVLNode?) { if (currentNode != null) - currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right))+1u + currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u } - override fun balance(stateContainer: AVLStateContainer): AVLNode { - val node = stateContainer.contentNode - root = stateContainer.root - return balance(root, node?.value ?: throw NullPointerException()) + override fun balance(state: AVLStateContainer): AVLNode { + val node = state.contentNode + root = state.root + return balance(root, node?.value ?: throw IllegalBaseNodeException()) } + // В баланс передаем родителя ноды, которую будем удалять private fun balance(currentNode: AVLNode?, value: Pack): AVLNode { if (currentNode == null) { - throw NullPointerException() + throw IllegalBaseNodeException() } when { currentNode.value < value -> currentNode.right = balance(currentNode.right, value) @@ -34,7 +38,7 @@ class AVLBalancer>(private var root: AVLNode?): Bal val balance = updateBalance(currentNode) if (balance == -2) { if (updateBalance(currentNode.right) == 1) { - currentNode.right = currentNode.right?.let { rightRotate(it) } ?: throw NullPointerException() + currentNode.right = currentNode.right?.let { rightRotate(it) } ?: throw IllegalNodeStateException() updateHeight(currentNode.right?.right) } val balancedNode = leftRotate(currentNode) @@ -44,7 +48,7 @@ class AVLBalancer>(private var root: AVLNode?): Bal } if (balance == 2) { if (updateBalance(currentNode.left) == -1) { - currentNode.left = currentNode.left?.let { leftRotate(it) } ?: throw NullPointerException() + currentNode.left = currentNode.left?.let { leftRotate(it) } ?: throw IllegalNodeStateException() updateHeight(currentNode.left?.left) } val balanceNode = rightRotate(currentNode) @@ -54,5 +58,4 @@ class AVLBalancer>(private var root: AVLNode?): Bal } return currentNode } - } diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt index 923487c..121a614 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt @@ -2,13 +2,6 @@ package treelib.avlTree import treelib.abstractTree.StateContainer -/* -DELETE: contentNode - parent of the MinRightLeaf (or MaxLeftLeaf) - - in case when has been deleted root - new root -INSERT: contentNode - parent of the inserted Node - - in case when has been inserted root - new root -FIND: contentNode - found value -* */ class AVLStateContainer>( override val contentNode: AVLNode?, val root: AVLNode?, diff --git a/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt index bcc0959..4a1266e 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt @@ -2,11 +2,6 @@ package treelib.binTree import treelib.abstractTree.StateContainer -/* -DELETE: contentNode - deleted Node -INSERT: contentNode - inserted Node -FIND: contentNode - found Node -* */ class BINStateContainer>( override val contentNode: BINNode?, ) : StateContainer> diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 229425d..fe73c5b 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -2,6 +2,9 @@ package treelib.rbTree 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>() { @@ -27,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 NullPointerException() + currentNode = currentNode.parent ?: throw IllegalNodeStateException() root = currentNode root?.color = Markers.BLACK return currentNode @@ -35,12 +38,12 @@ class RBBalancer>(private var root: RBNode?): Balan private fun nodeIsLeaf(node: RBNode?): Boolean { if (node == null) - throw NullPointerException() + throw IllegalBaseNodeException() return node.right == null && node.left == null } - override fun balance(stateContainer: RBStateContainer): RBNode { - val node = stateContainer.contentNode ?: throw NullPointerException() + override fun balance(state: RBStateContainer): RBNode { + val node = state.contentNode ?: throw IllegalBaseNodeException() val uncle = getUncle(node) when { /** node insertion case **/ @@ -55,14 +58,14 @@ class RBBalancer>(private var root: RBNode?): Balan return getRoot(currentNode) } - var parent = currentNode.parent ?: throw NullPointerException() + var parent = currentNode.parent ?: throw IllegalNodeStateException() when (parent) { parent.parent?.left -> { if (currentNode == parent.right) { leftRotate(parent) currentNode = parent } - parent = currentNode.parent?.parent ?: throw NullPointerException() + parent = currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -73,17 +76,17 @@ class RBBalancer>(private var root: RBNode?): Balan rightRotate(parent) currentNode = parent } - parent = currentNode.parent?.parent ?: throw NullPointerException() + 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 NullPointerException() + else -> throw IllegalNodeStateException() } if (currentNode.parent == null) root = currentNode - return root ?: throw NullPointerException() + return root ?: throw IllegalNodeStateException() } /** node removal cases **/ node.color == Markers.RED && (node.right != null || node.left != null) -> @@ -114,11 +117,11 @@ class RBBalancer>(private var root: RBNode?): Balan } - else -> throw NullPointerException() + else -> throw IllegalNodeStateException() } } } - throw NullPointerException() + throw ImpossibleCaseException() } private fun afterInsert(node: RBNode): RBNode { @@ -127,7 +130,7 @@ class RBBalancer>(private var root: RBNode?): Balan val uncle = getUncle(currentNode) if (uncle?.color == Markers.RED) { currentNode.parent?.color = Markers.BLACK - currentNode = currentNode.parent?.parent ?: throw NullPointerException() + currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK } @@ -142,7 +145,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 NullPointerException() + parent == null && node == null -> throw IllegalBaseNodeException() parent != null -> { when (parent.color) { Markers.RED -> secondCase(parent, node) @@ -151,13 +154,13 @@ class RBBalancer>(private var root: RBNode?): Balan getRoot(parent) } - else -> getRoot(node ?: throw NullPointerException()) + else -> getRoot(node ?: throw IllegalBaseNodeException()) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw NullPointerException() + var brother = getBrother(parent, node) ?: throw IllegalBaseNodeException() if (brother.color == Markers.RED) throw NullPointerException() diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt index 50ea3ca..18e01e4 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt @@ -2,12 +2,6 @@ package treelib.rbTree import treelib.abstractTree.StateContainer -/* -DELETE: contentNode - parent of the MinRightLeaf (or MaxLeftLeaf) - - in case when has been deleted root - new root -INSERT: contentNode - inserted Node -FIND: contentNode - found value -* */ class RBStateContainer>( override val contentNode: RBNode?, ) : StateContainer> From ffc7278f565f076254bbf7bb1b0d7e3c1c655436 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 15 Apr 2023 17:42:33 +0300 Subject: [PATCH 052/212] refactor: Separate tests for TreeStructs into different files. --- lib/src/test/kotlin/treelib/AVLStructTest.kt | 18 + lib/src/test/kotlin/treelib/BINStructTest.kt | 474 ++++++++++++++++ lib/src/test/kotlin/treelib/RBStructTree.kt | 18 + .../test/kotlin/treelib/TreeStructsTest.kt | 513 ------------------ 4 files changed, 510 insertions(+), 513 deletions(-) create mode 100644 lib/src/test/kotlin/treelib/AVLStructTest.kt create mode 100644 lib/src/test/kotlin/treelib/BINStructTest.kt create mode 100644 lib/src/test/kotlin/treelib/RBStructTree.kt delete mode 100644 lib/src/test/kotlin/treelib/TreeStructsTest.kt diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt new file mode 100644 index 0000000..00215f2 --- /dev/null +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -0,0 +1,18 @@ +package treelib + +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer +import treelib.avlTree.AVLStruct + +@DisplayName("Test: AVL Struct") +class AVLStructTest { + val treeW = TreeStructWrapper, AVLStateContainer, AVLStruct>() + var classUnderTest = AVLStruct() + + @BeforeEach + fun reInitClassUnderTest() { + classUnderTest = AVLStruct() + } +} \ 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..194096e --- /dev/null +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -0,0 +1,474 @@ +package treelib + +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import treelib.binTree.BINNode +import treelib.binTree.BINStateContainer +import treelib.binTree.BINStruct +import kotlin.test.assertEquals + +@DisplayName("Test: Binary Search Tree Struct") +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) + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/RBStructTree.kt b/lib/src/test/kotlin/treelib/RBStructTree.kt new file mode 100644 index 0000000..c1e040b --- /dev/null +++ b/lib/src/test/kotlin/treelib/RBStructTree.kt @@ -0,0 +1,18 @@ +package treelib + +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct + +@DisplayName("Test: Red-Black Tree Struct") +class RBStructTree { + val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() + var classUnderTest = RBStruct() + + @BeforeEach + fun reInitClassUnderTest() { + classUnderTest = RBStruct() + } +} \ No newline at end of file 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() - } - } -} From 6377db78126e76ec49e9aee36d7bddd1e8f8238a Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 15 Apr 2023 23:28:10 +0300 Subject: [PATCH 053/212] fix: Fix bugs in RBStruct; write base tests for RBStruct. --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 6 +- .../main/kotlin/treelib/rbTree/RBStruct.kt | 15 ++- lib/src/test/kotlin/treelib/RBStructTest.kt | 103 ++++++++++++++++++ lib/src/test/kotlin/treelib/RBStructTree.kt | 18 --- 4 files changed, 120 insertions(+), 22 deletions(-) create mode 100644 lib/src/test/kotlin/treelib/RBStructTest.kt delete mode 100644 lib/src/test/kotlin/treelib/RBStructTree.kt diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 2f21c10..422177e 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -220,7 +220,11 @@ abstract class TreeStruct, NodeType : Node> : BalancedTreeStruct, RBStateContainer, RBBalancer>() { @@ -17,12 +20,12 @@ class RBStruct> : 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, @@ -45,6 +48,7 @@ class RBStruct> : } } else root?.let { root = childForLink + if (childForLink != null) childForLink.parent = null } } @@ -53,7 +57,12 @@ class RBStruct> : override fun createNode(item: Pack): RBNode = RBNode(item) override fun linkNewNode(node: RBNode, parent: RBNode?): RBNode { - if (parent == null) root = node + 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 diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt new file mode 100644 index 0000000..807a1d6 --- /dev/null +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -0,0 +1,103 @@ +package treelib + +import org.junit.jupiter.api.* +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct +import treelib.singleObjects.Markers +import kotlin.test.assertEquals + +@DisplayName("Test: Red-Black Tree Struct") +class RBStructTest { + val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() + var classUnderTest = RBStruct() + + @BeforeEach + fun reInitClassUnderTest() { + classUnderTest = RBStruct() + } + + @Test + fun `base test on creation root`() { + classUnderTest.insert(6) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(root?.value, 6) }, + { assertEquals(root?.color, Markers.BLACK) }, + ) + } + + @Test + fun `base test on creation root with left`() { + classUnderTest.insert(6) + classUnderTest.insert(3) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(root?.left?.value, 3) }, + { assertEquals(root?.left?.color, Markers.RED) }, + ) + } + + @Test + fun `base test on creation root with right`() { + classUnderTest.insert(6) + classUnderTest.insert(8) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(root?.right?.value, 8) }, + { assertEquals(root?.right?.color, Markers.RED) }, + ) + } + + @Test + fun `base test on creation children`() { + classUnderTest.insert(6) + classUnderTest.insert(8) + classUnderTest.insert(3) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(root?.right?.value, 8) }, + { assertEquals(root?.left?.value, 3) }, + { assertEquals(root?.right?.color, Markers.RED) }, + { assertEquals(root?.left?.color, Markers.RED) }, + ) + } + + @Test + fun `base test delete root (left & right children)`() { + classUnderTest.insert(6) + classUnderTest.insert(8) + classUnderTest.insert(3) + classUnderTest.delete(6) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(root?.value, 8) }, + { assertEquals(root?.color, Markers.BLACK) }, + ) + } + + @Test + fun `base test delete root (right child)`() { + classUnderTest.insert(6) + classUnderTest.insert(8) + classUnderTest.delete(6) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(8, root?.value) }, + { assertEquals(Markers.BLACK, root?.color) }, + ) + } + + @Test + fun `base test delete root (left child)`() { + classUnderTest.insert(6) + classUnderTest.insert(3) + classUnderTest.delete(6) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(3, root?.value) }, + { assertEquals(Markers.BLACK, root?.color) }, + ) + } + +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/RBStructTree.kt b/lib/src/test/kotlin/treelib/RBStructTree.kt deleted file mode 100644 index c1e040b..0000000 --- a/lib/src/test/kotlin/treelib/RBStructTree.kt +++ /dev/null @@ -1,18 +0,0 @@ -package treelib - -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName -import treelib.rbTree.RBNode -import treelib.rbTree.RBStateContainer -import treelib.rbTree.RBStruct - -@DisplayName("Test: Red-Black Tree Struct") -class RBStructTree { - val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() - var classUnderTest = RBStruct() - - @BeforeEach - fun reInitClassUnderTest() { - classUnderTest = RBStruct() - } -} \ No newline at end of file From c58589caffa3f266caca5cc54aa3069f39bc4542 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 16 Apr 2023 14:50:38 +0300 Subject: [PATCH 054/212] feat: Add RBAnalyzer class for testing invariant of a RBStruct. --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 6 +- lib/src/test/kotlin/treelib/AVLStructTest.kt | 1 + lib/src/test/kotlin/treelib/BINStructTest.kt | 1 + lib/src/test/kotlin/treelib/RBStructTest.kt | 11 ++- lib/src/test/kotlin/utils/Analyzer.kt | 10 +++ lib/src/test/kotlin/utils/RBAnalyzer.kt | 85 +++++++++++++++++++ .../{treelib => utils}/TreeStructWrapper.kt | 2 +- 7 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 lib/src/test/kotlin/utils/Analyzer.kt create mode 100644 lib/src/test/kotlin/utils/RBAnalyzer.kt rename lib/src/test/kotlin/{treelib => utils}/TreeStructWrapper.kt (98%) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 422177e..898a40e 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -6,7 +6,11 @@ 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 + > { protected abstract var root: NodeType? diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 00215f2..23237a8 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -5,6 +5,7 @@ import org.junit.jupiter.api.DisplayName import treelib.avlTree.AVLNode import treelib.avlTree.AVLStateContainer import treelib.avlTree.AVLStruct +import utils.TreeStructWrapper @DisplayName("Test: AVL Struct") class AVLStructTest { diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 194096e..38b5a71 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test import treelib.binTree.BINNode import treelib.binTree.BINStateContainer import treelib.binTree.BINStruct +import utils.TreeStructWrapper import kotlin.test.assertEquals @DisplayName("Test: Binary Search Tree Struct") diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index 807a1d6..da9e9c5 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -5,12 +5,18 @@ import treelib.rbTree.RBNode import treelib.rbTree.RBStateContainer import treelib.rbTree.RBStruct import treelib.singleObjects.Markers +import utils.RBAnalyzer +import utils.TreeStructWrapper import kotlin.test.assertEquals @DisplayName("Test: Red-Black Tree Struct") class RBStructTest { - val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() - var classUnderTest = RBStruct() + private val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() + private var classUnderTest = RBStruct() + + private fun testAssert(msg: String): Nothing = fail(msg) + + private val analyzer = RBAnalyzer(::testAssert) @BeforeEach fun reInitClassUnderTest() { @@ -99,5 +105,4 @@ class RBStructTest { { assertEquals(Markers.BLACK, root?.color) }, ) } - } \ No newline at end of file diff --git a/lib/src/test/kotlin/utils/Analyzer.kt b/lib/src/test/kotlin/utils/Analyzer.kt new file mode 100644 index 0000000..5a342f9 --- /dev/null +++ b/lib/src/test/kotlin/utils/Analyzer.kt @@ -0,0 +1,10 @@ +package utils + +import treelib.abstractTree.Node + +interface Analyzer< + Pack : Comparable, + NodeType : Node, + > { + fun checkTree(root: NodeType) +} \ 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..1932026 --- /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>( + 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) assertMethod("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 -> { + assertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") + return@checkInvariant errorMagicNumber + } + + it.value < node.value -> { + assertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") + return@checkInvariant errorMagicNumber + } + + (it.color == Markers.RED) && (node.color == Markers.RED) -> { + assertMethod("parent.color == RED == RightChild.color => [parent - ${node.value} RightChild - ${it.value}]") + return@checkInvariant errorMagicNumber + } + + else -> {} + } + } + + node.left?.let { + when { + it.value == node.value -> { + assertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") + return@checkInvariant errorMagicNumber + } + + it.value > node.value -> { + assertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") + return@checkInvariant errorMagicNumber + } + + (it.color == Markers.RED) && (node.color == Markers.RED) -> { + assertMethod("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) { + assertMethod( + "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 98% rename from lib/src/test/kotlin/treelib/TreeStructWrapper.kt rename to lib/src/test/kotlin/utils/TreeStructWrapper.kt index 88870ec..1bdb93e 100644 --- a/lib/src/test/kotlin/treelib/TreeStructWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeStructWrapper.kt @@ -1,4 +1,4 @@ -package treelib +package utils import treelib.abstractTree.Node import treelib.abstractTree.StateContainer From bee3da40b439ba0728ad76378a075a186c044024 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 16 Apr 2023 15:36:48 +0300 Subject: [PATCH 055/212] refactor: Rename variables in test Classes. --- lib/src/test/kotlin/treelib/AVLStructTest.kt | 4 +- lib/src/test/kotlin/treelib/RBStructTest.kt | 54 ++++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 23237a8..25ce4cb 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -10,10 +10,10 @@ import utils.TreeStructWrapper @DisplayName("Test: AVL Struct") class AVLStructTest { val treeW = TreeStructWrapper, AVLStateContainer, AVLStruct>() - var classUnderTest = AVLStruct() + var treeStruct = AVLStruct() @BeforeEach fun reInitClassUnderTest() { - classUnderTest = AVLStruct() + treeStruct = AVLStruct() } } \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index da9e9c5..5995dd4 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -12,7 +12,7 @@ import kotlin.test.assertEquals @DisplayName("Test: Red-Black Tree Struct") class RBStructTest { private val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() - private var classUnderTest = RBStruct() + private var treeStruct = RBStruct() private fun testAssert(msg: String): Nothing = fail(msg) @@ -20,13 +20,13 @@ class RBStructTest { @BeforeEach fun reInitClassUnderTest() { - classUnderTest = RBStruct() + treeStruct = RBStruct() } @Test fun `base test on creation root`() { - classUnderTest.insert(6) - val root = treeW.getPrivateNode(classUnderTest) + treeStruct.insert(6) + val root = treeW.getPrivateNode(treeStruct) assertAll( { assertEquals(root?.value, 6) }, { assertEquals(root?.color, Markers.BLACK) }, @@ -35,9 +35,9 @@ class RBStructTest { @Test fun `base test on creation root with left`() { - classUnderTest.insert(6) - classUnderTest.insert(3) - val root = treeW.getPrivateNode(classUnderTest) + treeStruct.insert(6) + treeStruct.insert(3) + val root = treeW.getPrivateNode(treeStruct) assertAll( { assertEquals(root?.left?.value, 3) }, { assertEquals(root?.left?.color, Markers.RED) }, @@ -46,9 +46,9 @@ class RBStructTest { @Test fun `base test on creation root with right`() { - classUnderTest.insert(6) - classUnderTest.insert(8) - val root = treeW.getPrivateNode(classUnderTest) + treeStruct.insert(6) + treeStruct.insert(8) + val root = treeW.getPrivateNode(treeStruct) assertAll( { assertEquals(root?.right?.value, 8) }, { assertEquals(root?.right?.color, Markers.RED) }, @@ -57,10 +57,10 @@ class RBStructTest { @Test fun `base test on creation children`() { - classUnderTest.insert(6) - classUnderTest.insert(8) - classUnderTest.insert(3) - val root = treeW.getPrivateNode(classUnderTest) + 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) }, @@ -71,11 +71,11 @@ class RBStructTest { @Test fun `base test delete root (left & right children)`() { - classUnderTest.insert(6) - classUnderTest.insert(8) - classUnderTest.insert(3) - classUnderTest.delete(6) - val root = treeW.getPrivateNode(classUnderTest) + 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) }, @@ -84,10 +84,10 @@ class RBStructTest { @Test fun `base test delete root (right child)`() { - classUnderTest.insert(6) - classUnderTest.insert(8) - classUnderTest.delete(6) - val root = treeW.getPrivateNode(classUnderTest) + treeStruct.insert(6) + treeStruct.insert(8) + treeStruct.delete(6) + val root = treeW.getPrivateNode(treeStruct) assertAll( { assertEquals(8, root?.value) }, { assertEquals(Markers.BLACK, root?.color) }, @@ -96,10 +96,10 @@ class RBStructTest { @Test fun `base test delete root (left child)`() { - classUnderTest.insert(6) - classUnderTest.insert(3) - classUnderTest.delete(6) - val root = treeW.getPrivateNode(classUnderTest) + treeStruct.insert(6) + treeStruct.insert(3) + treeStruct.delete(6) + val root = treeW.getPrivateNode(treeStruct) assertAll( { assertEquals(3, root?.value) }, { assertEquals(Markers.BLACK, root?.color) }, From 9b834d6803882caedccb2a057c28b376d5898a29 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 16 Apr 2023 15:37:20 +0300 Subject: [PATCH 056/212] feat: Add BINAnalyzer class for testing invariant of the BINStruct. --- lib/src/test/kotlin/treelib/BINStructTest.kt | 307 ++++++++++--------- lib/src/test/kotlin/utils/BINAnalyzer.kt | 52 ++++ 2 files changed, 215 insertions(+), 144 deletions(-) create mode 100644 lib/src/test/kotlin/utils/BINAnalyzer.kt diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 38b5a71..952a6fa 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -3,30 +3,36 @@ 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 utils.BINAnalyzer import utils.TreeStructWrapper import kotlin.test.assertEquals @DisplayName("Test: Binary Search Tree Struct") class BINStructTest { val treeW = TreeStructWrapper, BINStateContainer, BINStruct>() - var classUnderTest = BINStruct() + var treeStruct = BINStruct() + + private fun testAssert(msg: String): Nothing = fail(msg) + + private val analyzer = BINAnalyzer(::testAssert) @BeforeEach fun reInitClassUnderTest() { - classUnderTest = BINStruct() + treeStruct = 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) + treeStruct.insert(i) } - classUnderTest.delete(5) - val root = treeW.getPrivateNode(classUnderTest)?.value + treeStruct.delete(5) + val root = treeW.getPrivateNode(treeStruct)?.value assertEquals(expected = 6, actual = root) } @@ -35,20 +41,20 @@ class BINStructTest { fun `test insert`() { val num = mutableListOf(1, 2, 3, 4, 5, 8) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } val additional_num = mutableListOf(1, 2, 3, 5, 7, 8, 11) for (i in additional_num) { - classUnderTest.insert(i) + treeStruct.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 + 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) @@ -62,49 +68,49 @@ class BINStructTest { fun `test find ordinary`() { val num = mutableListOf(2, 3, 1, 4, 5, 10) - assertEquals(expected = classUnderTest.find(2), actual = null) + assertEquals(expected = treeStruct.find(2), actual = null) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - assertEquals(expected = classUnderTest.find(2), actual = 2) + assertEquals(expected = treeStruct.find(2), actual = 2) } @Test fun `test find null`() { val num = mutableListOf(1) - classUnderTest.insert(num[0]) + treeStruct.insert(num[0]) - assertEquals(classUnderTest.find(2), null) + assertEquals(treeStruct.find(2), null) } @Test fun `test find root`() { val num = mutableListOf(1) - classUnderTest.insert(num[0]) + treeStruct.insert(num[0]) - assertEquals(classUnderTest.find(1), 1) + assertEquals(treeStruct.find(1), 1) } @Test fun `test insert and delete root`() { val num = mutableListOf(1, 2) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(1) + treeStruct.delete(1) val additional_num = mutableListOf(1, 2, 11) for (i in additional_num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_1 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_11 = treeW.getPrivateNode(classUnderTest)?.right?.value + 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) @@ -116,13 +122,13 @@ class BINStructTest { fun `test delete nonexistent value right`() { val num = mutableListOf(5, 6) for (value in num) { - classUnderTest.insert(value) + treeStruct.insert(value) } - classUnderTest.delete(6) + treeStruct.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 + 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) @@ -133,13 +139,13 @@ class BINStructTest { fun `test delete nonexistent value left`() { val num = mutableListOf(6, 5) for (value in num) { - classUnderTest.insert(value) + treeStruct.insert(value) } - classUnderTest.delete(5) + treeStruct.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 + 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) @@ -150,13 +156,13 @@ class BINStructTest { fun `test delete no child root`() { val num = mutableListOf(3) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(3) + treeStruct.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 + 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) @@ -167,14 +173,14 @@ class BINStructTest { fun `test delete no child right`() { val num = mutableListOf(3, 10, 15) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(15) + treeStruct.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 + 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) @@ -187,14 +193,14 @@ class BINStructTest { fun `test delete no child left`() { val num = mutableListOf(15, 10, 3) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(3) + treeStruct.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 + 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) @@ -207,14 +213,14 @@ class BINStructTest { fun `test delete one child left`() { val num = mutableListOf(3, 2, 1, 5) for (value in num) { - classUnderTest.insert(value) + treeStruct.insert(value) } - classUnderTest.delete(2) + treeStruct.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 + 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) @@ -226,14 +232,14 @@ class BINStructTest { fun `test delete one child right`() { val num = mutableListOf(3, 1, 5, 6) for (value in num) { - classUnderTest.insert(value) + treeStruct.insert(value) } - classUnderTest.delete(5) + treeStruct.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 + 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) @@ -245,13 +251,13 @@ class BINStructTest { fun `test delete one child root`() { val num = mutableListOf(3, 6) for (value in num) { - classUnderTest.insert(value) + treeStruct.insert(value) } - classUnderTest.delete(3) + treeStruct.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 + 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) @@ -262,17 +268,17 @@ class BINStructTest { 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) + treeStruct.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 + 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) @@ -288,15 +294,15 @@ class BINStructTest { fun `test delete two child only three element`() { val num = mutableListOf(2, 1, 3) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(2) + treeStruct.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 + 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) @@ -309,18 +315,18 @@ class BINStructTest { fun `test delete two child without family`() { val num = mutableListOf(10, 7, 5, 4, 6) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(7) + treeStruct.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 + 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) @@ -336,17 +342,17 @@ class BINStructTest { fun `test two child double delete and delete root`() { val num = mutableListOf(6, 8, 10, 7) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(6) - classUnderTest.delete(7) + treeStruct.delete(6) + treeStruct.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 + 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) @@ -359,18 +365,18 @@ class BINStructTest { 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) + treeStruct.insert(i) } - classUnderTest.delete(8) + treeStruct.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 + 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) @@ -385,16 +391,16 @@ class BINStructTest { 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) + treeStruct.insert(i) } - classUnderTest.delete(8) + treeStruct.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 + 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) @@ -407,17 +413,17 @@ class BINStructTest { 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) + treeStruct.insert(i) } - classUnderTest.delete(8) + treeStruct.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 + 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) @@ -431,18 +437,18 @@ class BINStructTest { 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) + treeStruct.insert(i) } - classUnderTest.delete(12) + treeStruct.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 + 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) @@ -457,19 +463,32 @@ class BINStructTest { 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) + treeStruct.insert(i) } - classUnderTest.delete(8) + treeStruct.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 + 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/utils/BINAnalyzer.kt b/lib/src/test/kotlin/utils/BINAnalyzer.kt new file mode 100644 index 0000000..0584b70 --- /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>( + val assertMethod: (input: String) -> Unit = { + throw BugInImplementException(it) + } +) : Analyzer> { + + override fun checkTree(root: BINNode) { + checkInvariant(root) + } + + private tailrec fun checkInvariant(node: BINNode) { + if ((node.left == null) && (node.right == null)) return + + node.right?.let { + when { + it.value == node.value -> { + assertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") + return@checkInvariant + } + + it.value < node.value -> { + assertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") + return@checkInvariant + } + + else -> {} + } + } + + node.left?.let { + when { + it.value == node.value -> { + assertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") + return@checkInvariant + } + + it.value > node.value -> { + assertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") + return@checkInvariant + } + + else -> {} + } + } + } + +} \ No newline at end of file From 191bd5d695d445307ee788d5fb71bc81676ab66a Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 16 Apr 2023 19:07:21 +0300 Subject: [PATCH 057/212] fix: Rewrite interface Analyzer. --- lib/src/test/kotlin/utils/Analyzer.kt | 11 +++++++++-- lib/src/test/kotlin/utils/BINAnalyzer.kt | 14 +++++++------- lib/src/test/kotlin/utils/RBAnalyzer.kt | 20 ++++++++++---------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/lib/src/test/kotlin/utils/Analyzer.kt b/lib/src/test/kotlin/utils/Analyzer.kt index 5a342f9..6131d02 100644 --- a/lib/src/test/kotlin/utils/Analyzer.kt +++ b/lib/src/test/kotlin/utils/Analyzer.kt @@ -2,9 +2,16 @@ package utils import treelib.abstractTree.Node -interface Analyzer< +abstract class Analyzer< Pack : Comparable, NodeType : Node, > { - fun checkTree(root: NodeType) + + 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 index 0584b70..43dc5f5 100644 --- a/lib/src/test/kotlin/utils/BINAnalyzer.kt +++ b/lib/src/test/kotlin/utils/BINAnalyzer.kt @@ -4,27 +4,27 @@ import treelib.binTree.BINNode import treelib.singleObjects.exceptions.BugInImplementException class BINAnalyzer>( - val assertMethod: (input: String) -> Unit = { + override val assertMethod: (input: String) -> Unit = { throw BugInImplementException(it) } -) : Analyzer> { +) : Analyzer>() { override fun checkTree(root: BINNode) { checkInvariant(root) } - private tailrec fun checkInvariant(node: BINNode) { + private fun checkInvariant(node: BINNode) { if ((node.left == null) && (node.right == null)) return node.right?.let { when { it.value == node.value -> { - assertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") + wrappedAssertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") return@checkInvariant } it.value < node.value -> { - assertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") + wrappedAssertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") return@checkInvariant } @@ -35,12 +35,12 @@ class BINAnalyzer>( node.left?.let { when { it.value == node.value -> { - assertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") + wrappedAssertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") return@checkInvariant } it.value > node.value -> { - assertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") + wrappedAssertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") return@checkInvariant } diff --git a/lib/src/test/kotlin/utils/RBAnalyzer.kt b/lib/src/test/kotlin/utils/RBAnalyzer.kt index 1932026..1b04aff 100644 --- a/lib/src/test/kotlin/utils/RBAnalyzer.kt +++ b/lib/src/test/kotlin/utils/RBAnalyzer.kt @@ -5,15 +5,15 @@ import treelib.singleObjects.Markers import treelib.singleObjects.exceptions.BugInImplementException class RBAnalyzer>( - val assertMethod: (input: String) -> Unit = { + override val assertMethod: (input: String) -> Unit = { throw BugInImplementException(it) } -) : Analyzer> { +) : Analyzer>() { /** Magic number for error := -9999999 -> just an impossible value **/ private val errorMagicNumber = -9999999 override fun checkTree(root: RBNode) { - if (root.color != Markers.BLACK) assertMethod("The root isn't black!!!") + if (root.color != Markers.BLACK) wrappedAssertMethod("The root isn't black!!!") checkInvariant(root) } @@ -28,17 +28,17 @@ class RBAnalyzer>( node.right?.let { when { it.value == node.value -> { - assertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") + wrappedAssertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") return@checkInvariant errorMagicNumber } it.value < node.value -> { - assertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") + wrappedAssertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") return@checkInvariant errorMagicNumber } (it.color == Markers.RED) && (node.color == Markers.RED) -> { - assertMethod("parent.color == RED == RightChild.color => [parent - ${node.value} RightChild - ${it.value}]") + wrappedAssertMethod("parent.color == RED == RightChild.color => [parent - ${node.value} RightChild - ${it.value}]") return@checkInvariant errorMagicNumber } @@ -49,17 +49,17 @@ class RBAnalyzer>( node.left?.let { when { it.value == node.value -> { - assertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") + wrappedAssertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") return@checkInvariant errorMagicNumber } it.value > node.value -> { - assertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") + wrappedAssertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") return@checkInvariant errorMagicNumber } (it.color == Markers.RED) && (node.color == Markers.RED) -> { - assertMethod("parent.color == RED == LeftChild.color => [parent - ${node.value} LeftChild - ${it.value}]") + wrappedAssertMethod("parent.color == RED == LeftChild.color => [parent - ${node.value} LeftChild - ${it.value}]") return@checkInvariant errorMagicNumber } @@ -73,7 +73,7 @@ class RBAnalyzer>( if (leftBlackCount < 0 || rightBlackCount < 0) return errorMagicNumber if (leftBlackCount != rightBlackCount) { - assertMethod( + wrappedAssertMethod( "Number of black nodes does not match in children: parent.value - ${node.value} =>[left - $leftBlackCount] != [right - $rightBlackCount]" ) return errorMagicNumber From c9b46aaf2e355cbd79fb48137e7f69f6880923bf Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Sun, 16 Apr 2023 22:26:04 +0300 Subject: [PATCH 058/212] feat: added tests for the invariant --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 2 +- lib/src/test/kotlin/treelib/AVLStructTest.kt | 70 ++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 898a40e..ebdb05c 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -136,7 +136,7 @@ abstract class TreeStruct< if (root == null) return generateStateFind(null) while (true) { - if (obj == currentNode?.value) return generateStateFind(currentNode) + if (obj == currentNode?.value) return generateStateFind(currentNode, currentNode) else { currentNode?.let { if (obj > it.value) currentNode = it.right diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 25ce4cb..347892c 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -2,18 +2,84 @@ 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 utils.AVLAnalyzer import utils.TreeStructWrapper @DisplayName("Test: AVL Struct") class AVLStructTest { - val treeW = TreeStructWrapper, AVLStateContainer, AVLStruct>() - var treeStruct = AVLStruct() + private val treeW = TreeStructWrapper, 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) + } + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @Test + fun `test height 3`() { + val num = mutableListOf(3, 2, 1, 4) + for (i in num) { + treeStruct.insert(i) + } + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @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) + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @Test + fun `test 100000 arguments`() { + for (i in 1..100000) { + treeStruct.insert(i) + } + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @Test + fun `test 100000 arguments and delete`() { + for (i in 1..100000) { + treeStruct.insert(i) + } + treeStruct.delete(5000) + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @Test + fun `test two arguments`() { + treeStruct.insert(2) + treeStruct.insert(3) + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @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) + } + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } } \ No newline at end of file From 117e68d522e6aaf713d57ccd4d62e8cd07b544e9 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Sun, 16 Apr 2023 22:26:39 +0300 Subject: [PATCH 059/212] feat: Added invariant analyzer. --- lib/src/test/kotlin/utils/AVLAnalyzer.kt | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 lib/src/test/kotlin/utils/AVLAnalyzer.kt diff --git a/lib/src/test/kotlin/utils/AVLAnalyzer.kt b/lib/src/test/kotlin/utils/AVLAnalyzer.kt new file mode 100644 index 0000000..b8a9ac4 --- /dev/null +++ b/lib/src/test/kotlin/utils/AVLAnalyzer.kt @@ -0,0 +1,42 @@ +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) + } + } +} From b8cc4828e308554ca72f6ed7a1a7de944c64f894 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 17 Apr 2023 14:07:00 +0300 Subject: [PATCH 060/212] feat: Add fuzzers with partial functionality. --- .../kotlin/utils/fuzzers/AVLStructFuzzer.kt | 7 + .../kotlin/utils/fuzzers/BINStructFuzzer.kt | 16 +++ .../kotlin/utils/fuzzers/RBStructFuzzer.kt | 16 +++ .../kotlin/utils/fuzzers/TreeStructFuzzer.kt | 128 ++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt create mode 100644 lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt create mode 100644 lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt create mode 100644 lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt 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..01d49c5 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt @@ -0,0 +1,7 @@ +package utils.fuzzers + +import treelib.avlTree.AVLNode + +class AVLStructFuzzer>(){ +// TODO +} \ 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..5ce3959 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt @@ -0,0 +1,16 @@ +package utils.fuzzers + +import treelib.binTree.BINNode +import treelib.binTree.BINStateContainer +import treelib.binTree.BINStruct +import utils.BINAnalyzer + +class BINStructFuzzer>( + override val baseInput: Array, + override val assertMethod: (input: String) -> Unit +): TreeStructFuzzer, 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..6b59799 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt @@ -0,0 +1,16 @@ +package utils.fuzzers + +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct +import utils.RBAnalyzer + +class RBStructFuzzer>( + override val baseInput: Array, + override val assertMethod: (input: String) -> Unit +): TreeStructFuzzer, 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..a5390b9 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt @@ -0,0 +1,128 @@ +package utils.fuzzers + +import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer +import treelib.abstractTree.TreeStruct +import kotlin.random.Random +import utils.Analyzer +import treelib.singleObjects.exceptions.* +import kotlin.random.nextInt +import utils.TreeStructWrapper +import java.io.File +import java.time.Instant + +abstract class TreeStructFuzzer< + Pack : Comparable, + NodeType : Node, + 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() + println(dataSize) + 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() +// println("./$dirPath/$testId.txt") + for (index in testSet) { +// print("${baseInput[index]} ") + file.appendText("${baseInput[index]} \n") + } +// println() + } + +} \ No newline at end of file From d1cba855e5185c4da0d2094e8de0c543e184ebdd Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 17 Apr 2023 14:13:04 +0300 Subject: [PATCH 061/212] fix: Fix bug in AVLStruct.find method. --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 6 +++--- lib/src/test/kotlin/treelib/RBStructTest.kt | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index ebdb05c..d2fe865 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -133,7 +133,7 @@ abstract class TreeStruct< protected fun findItem(obj: Pack): State { var currentNode = root - if (root == null) return generateStateFind(null) + if (root == null) return generateStateFind(null, null) while (true) { if (obj == currentNode?.value) return generateStateFind(currentNode, currentNode) @@ -141,7 +141,7 @@ abstract class TreeStruct< currentNode?.let { if (obj > it.value) currentNode = it.right else currentNode = it.left - } ?: return generateStateFind(null) + } ?: return generateStateFind(null, null) } } } @@ -166,7 +166,7 @@ abstract class TreeStruct< } updateNode.value = item - return generateStateInsert(null) + return generateStateInsert(null, null) } protected abstract fun generateStateDelete( diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index 5995dd4..606ca40 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -7,6 +7,7 @@ import treelib.rbTree.RBStruct import treelib.singleObjects.Markers import utils.RBAnalyzer import utils.TreeStructWrapper +import utils.fuzzers.RBStructFuzzer import kotlin.test.assertEquals @DisplayName("Test: Red-Black Tree Struct") @@ -30,6 +31,7 @@ class RBStructTest { assertAll( { assertEquals(root?.value, 6) }, { assertEquals(root?.color, Markers.BLACK) }, + { analyzer.checkTree(root!!) } ) } @@ -41,6 +43,7 @@ class RBStructTest { assertAll( { assertEquals(root?.left?.value, 3) }, { assertEquals(root?.left?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } ) } @@ -52,6 +55,7 @@ class RBStructTest { assertAll( { assertEquals(root?.right?.value, 8) }, { assertEquals(root?.right?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } ) } @@ -66,6 +70,7 @@ class RBStructTest { { assertEquals(root?.left?.value, 3) }, { assertEquals(root?.right?.color, Markers.RED) }, { assertEquals(root?.left?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } ) } @@ -79,6 +84,7 @@ class RBStructTest { assertAll( { assertEquals(root?.value, 8) }, { assertEquals(root?.color, Markers.BLACK) }, + { analyzer.checkTree(root!!) } ) } @@ -91,6 +97,7 @@ class RBStructTest { assertAll( { assertEquals(8, root?.value) }, { assertEquals(Markers.BLACK, root?.color) }, + { analyzer.checkTree(root!!) } ) } @@ -103,6 +110,20 @@ class RBStructTest { 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 From 660c98c15c0ae297d81fe86b66f0281b4be2b97b Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 15 Apr 2023 15:27:37 +0300 Subject: [PATCH 062/212] fix: Partially change exceptions --- .../kotlin/treelib/avlTree/AVLBalancer.kt | 25 ++++----- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 55 +++++++++---------- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 703ea69..5f80a87 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -1,11 +1,8 @@ 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>() { +class AVLBalancer>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { return (getHeight(node?.left) - getHeight(node?.right)).toInt() } @@ -16,19 +13,18 @@ class AVLBalancer>(private var root: AVLNode?) : private fun updateHeight(currentNode: AVLNode?) { if (currentNode != null) - currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u + 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 +34,7 @@ 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 +44,7 @@ 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 +54,5 @@ class AVLBalancer>(private var root: AVLNode?) : } return currentNode } + } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index fe73c5b..67e5427 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -2,9 +2,6 @@ package treelib.rbTree 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>() { @@ -30,7 +27,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,12 +35,13 @@ 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 IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") val uncle = getUncle(node) when { /** node insertion case **/ @@ -58,14 +56,14 @@ 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 IllegalStateException() // IllegalNodeStateException() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -76,17 +74,17 @@ class RBBalancer>(private var root: RBNode?): Balan rightRotate(parent) currentNode = parent } - parent = currentNode.parent?.parent ?: throw IllegalNodeStateException() + parent = currentNode.parent?.parent ?: throw NullPointerException() // 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) -> @@ -116,12 +114,11 @@ class RBBalancer>(private var root: RBNode?): Balan firstCase(node, null) } - - else -> throw IllegalNodeStateException() + else -> throw IllegalStateException() } } } - throw ImpossibleCaseException() + throw IllegalStateException() } private fun afterInsert(node: RBNode): RBNode { @@ -130,7 +127,7 @@ class RBBalancer>(private var root: RBNode?): Balan val uncle = getUncle(currentNode) if (uncle?.color == Markers.RED) { currentNode.parent?.color = Markers.BLACK - currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() + currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK } @@ -145,7 +142,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 +151,13 @@ class RBBalancer>(private var root: RBNode?): Balan getRoot(parent) } - else -> getRoot(node ?: throw IllegalBaseNodeException()) + else -> getRoot(node ?: throw NullPointerException() /* IllegalNodeStateException() */) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw IllegalBaseNodeException() + var brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() if (brother.color == Markers.RED) throw NullPointerException() @@ -187,7 +184,7 @@ class RBBalancer>(private var root: RBNode?): Balan brother.color = Markers.RED } else { - throw NullPointerException() + throw IllegalStateException() } } parent.right -> @@ -206,17 +203,17 @@ class RBBalancer>(private var root: RBNode?): Balan brother.right?.color = Markers.BLACK } else { - throw NullPointerException() + 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 NullPointerException() // IllegalNodeStateException() when (brother.color) { Markers.RED -> thirdCaseSubFirst(brother, parent) Markers.BLACK -> thirdCaseSubSecond(brother, parent) @@ -228,7 +225,7 @@ class RBBalancer>(private var root: RBNode?): Balan when (brother) { brother.parent?.left -> { - var rightBrotherSon = brother.right ?: throw NullPointerException() + var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED @@ -244,7 +241,7 @@ class RBBalancer>(private var root: RBNode?): Balan rightBrotherSon.color = Markers.RED leftRotate(rightBrotherSon) - rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() + rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -260,14 +257,14 @@ class RBBalancer>(private var root: RBNode?): Balan 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 NullPointerException()) // IllegalNodeStateException() return } if (leftBrotherSon.left?.color == Markers.RED) { rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED - leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() + leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } @@ -277,7 +274,7 @@ class RBBalancer>(private var root: RBNode?): Balan leftRotate(parent) } } - else -> throw NullPointerException() + else -> throw IllegalStateException() } } @@ -315,7 +312,7 @@ class RBBalancer>(private var root: RBNode?): Balan } } - else -> throw NullPointerException() + else -> throw IllegalStateException() } } From 3270e8db74b38d72d77a4daf1121e38f76839e59 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 14:37:18 +0300 Subject: [PATCH 063/212] feat: Implement neo4j database sketch --- lib/build.gradle.kts | 10 +- lib/src/main/kotlin/CONTAINER.conf | 3 + lib/src/main/kotlin/DataBase.kt | 172 ++++++++++++++++++++++++ lib/src/main/kotlin/testNeo4j.sh | 15 +++ lib/src/main/kotlin/treelib/DBNodeRB.kt | 14 ++ settings.gradle.kts | 2 +- 6 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 lib/src/main/kotlin/CONTAINER.conf create mode 100644 lib/src/main/kotlin/DataBase.kt create mode 100644 lib/src/main/kotlin/testNeo4j.sh create mode 100644 lib/src/main/kotlin/treelib/DBNodeRB.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 09124dc..0f9f88e 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -19,11 +19,17 @@ repositories { } dependencies { + api("org.apache.commons:commons-math3:3.6.1") + implementation("com.google.guava:guava:31.1-jre") + + val neo4jCore = "4.0.5" + implementation("org.neo4j", "neo4j-ogm-core", neo4jCore) + implementation("org.neo4j", "neo4j-ogm-bolt-driver", neo4jCore) + 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") + implementation(kotlin("stdlib-jdk8")) } tasks.test { diff --git a/lib/src/main/kotlin/CONTAINER.conf b/lib/src/main/kotlin/CONTAINER.conf new file mode 100644 index 0000000..9b9fb0a --- /dev/null +++ b/lib/src/main/kotlin/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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt new file mode 100644 index 0000000..ddc72a3 --- /dev/null +++ b/lib/src/main/kotlin/DataBase.kt @@ -0,0 +1,172 @@ + +import org.neo4j.driver.AuthTokens +import org.neo4j.driver.Driver +import org.neo4j.driver.GraphDatabase +import org.neo4j.driver.exceptions.SessionExpiredException +import treelib.DBNodeRB +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() + } + + } + + // я наверное смогу получить рут, используя фильтр что-то вроде: на данный узел не указывает ни один другой узел с отношением [left_child] / [right_child] + + fun , V> saveChanges(preOrder: Array>, inOrder: Array>) { + + /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ + val session = driver?.session() ?: throw IOException() + + var inOrderIndex = 0 + var preOrderIndex = 0 + val set = HashSet>() + val stack = LinkedList>() + + while (preOrderIndex in preOrder.indices) { + do { + val currentNode = preOrder[preOrderIndex] + if (preOrderIndex == 0) { + session.executeWrite { tx -> + tx.run( + "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", + mutableMapOf( + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + if (!stack.isEmpty()) { + if ( set.contains( stack.peek() ) ) { + set.remove(stack.peek()) + val parentNode = stack.pop() + session.executeWrite {tx -> + 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}) " + + "MERGE (parent)-[:RIGHT_SON]->(son)", + mutableMapOf( + "parentNodeValue" to parentNode.value, + "parentNodeKey" to parentNode.key, + "parentNodeColor" to parentNode.color.toString(), + "parentNodeX" to parentNode.x, + "parentNodeY" to parentNode.y, + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + else { + val parentNode = stack.peek() + session.executeWrite {tx -> + 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}) " + + "MERGE (parent)-[:LEFT_SON]->(son)", + mutableMapOf( + "parentNodeValue" to parentNode.value, + "parentNodeKey" to parentNode.key, + "parentNodeColor" to parentNode.color.toString(), + "parentNodeX" to parentNode.x, + "parentNodeY" to parentNode.y, + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + } + stack.push(currentNode) + } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) + + var currentNode: DBNodeRB? = null + + while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().key == inOrder[inOrderIndex].key) { + currentNode = stack.pop() + ++inOrderIndex + } + + if (currentNode != null) { + set.add(currentNode) + stack.push(currentNode) + } + + } + + session.close() + } + + // Это скорее всего будем запускать при открытии приложения, нужно будет сразу восстановить дерево + + fun exportRBtree() { + + 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 ").list() + .map{ DBNodeRB( + value = it.values().get(0).toString(), + key = it.values().get(1).toString(), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(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{ DBNodeRB( + value = it.values().get(0).toString(), + key = it.values().get(1).toString(), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(4).toString().toDouble()) + } + } + + session.close() + + } + + override fun close() { + driver?.close() + } +} + +// neo4j-admin, backup, restore +/* +у меня есть вершины и ребра между ними, надо уметь сохранять дерево в базе, но как? +вопрос: когда заносить изменения в бд и как это реализовывать? +надо еще подумать над оптимизацией, те чтобы не пересобирать дерево в бд каждый раз, когда мы добавили за сессию всего один узел + +root = tx.run("MATCH (parent: Node)-->(son: Node) " + + "WHERE NOT ()-->(parent) " + + "RETURN parent.value, parent.key, parent.color, parent.x, parent.y").list().map {it.values()}.get(0) + */ diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/testNeo4j.sh new file mode 100644 index 0000000..97ad8db --- /dev/null +++ b/lib/src/main/kotlin/testNeo4j.sh @@ -0,0 +1,15 @@ + +BASEDIR=$(realpath "$(dirname "$0")") + +. "${BASEDIR}/CONTAINER.conf" + +docker run \ + --rm \ + --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 \ + +#docker stop neo4j:latest diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt new file mode 100644 index 0000000..d048930 --- /dev/null +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -0,0 +1,14 @@ +package treelib + +import treelib.singleObjects.Markers + + +class DBNodeRB, V>(val value: V, + val key: K, + val color: Markers = Markers.RED, + val x: Double = 0.0, + val y: Double = 0.0) { + + + +} 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") From b7ed0c41654991f3a6e2ec99f22b422cbe7987e9 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 19:32:32 +0300 Subject: [PATCH 064/212] feat: Implement initialize tree from DB --- lib/src/main/kotlin/Controller.kt | 50 +++++++++++++++++ lib/src/main/kotlin/DataBase.kt | 55 +++++++++---------- lib/src/main/kotlin/treelib/DBNodeRB.kt | 9 +-- lib/src/main/kotlin/treelib/Main.kt | 39 +++++++++++++ .../main/kotlin/treelib/abstractTree/Tree.kt | 12 ++-- .../kotlin/treelib/abstractTree/TreeStruct.kt | 21 +++---- 6 files changed, 135 insertions(+), 51 deletions(-) create mode 100644 lib/src/main/kotlin/Controller.kt create mode 100644 lib/src/main/kotlin/treelib/Main.kt diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/Controller.kt new file mode 100644 index 0000000..cc4e699 --- /dev/null +++ b/lib/src/main/kotlin/Controller.kt @@ -0,0 +1,50 @@ +import treelib.DBNodeRB +import treelib.rbTree.RBStruct +import treelib.rbTree.RBTree +import treelib.singleObjects.Container + +class Controller { + + fun initTree() { + 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.restoreTreeFromDatabase(orders.first, orders.second) + } + + fun saveTree() { + val tree = RBTree() + tree.putItem(Pair(25, 1)) + tree.putItem(Pair(15, 1)) + tree.putItem(Pair(50, 1)) + tree.putItem(Pair(10, 1)) + tree.putItem(Pair(22, 1)) + tree.putItem(Pair(35, 1)) + tree.putItem(Pair(70, 1)) + tree.putItem(Pair(4, 1)) + tree.putItem(Pair(12, 1)) + tree.putItem(Pair(18, 1)) + tree.putItem(Pair(24, 1)) + tree.putItem(Pair(31, 1)) + tree.putItem(Pair(44, 1)) + tree.putItem(Pair(66, 1)) + tree.putItem(Pair(90, 1)) + + val neo4jDB = Neo4jRepository() + neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + + val preOrder = tree.preOrder().map { DBNodeRB(it.value, it.color) } + val inOrder = tree.inOrder().map { DBNodeRB(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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index ddc72a3..3e94aac 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -4,6 +4,7 @@ import org.neo4j.driver.Driver import org.neo4j.driver.GraphDatabase import org.neo4j.driver.exceptions.SessionExpiredException import treelib.DBNodeRB +import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable import java.io.IOException @@ -21,31 +22,29 @@ class Neo4jRepository: Closeable { } catch(ex: SessionExpiredException) { throw IOException() } - } - // я наверное смогу получить рут, используя фильтр что-то вроде: на данный узел не указывает ни один другой узел с отношением [left_child] / [right_child] - - fun , V> saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() var inOrderIndex = 0 var preOrderIndex = 0 - val set = HashSet>() - val stack = LinkedList>() + val set = HashSet>() + val stack = LinkedList>() while (preOrderIndex in preOrder.indices) { do { val currentNode = preOrder[preOrderIndex] + currentNode.value as Container<*, *> if (preOrderIndex == 0) { session.executeWrite { tx -> tx.run( "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", mutableMapOf( - "nodeValue" to currentNode.value, - "nodeKey" to currentNode.key, + "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 @@ -57,19 +56,20 @@ class Neo4jRepository: Closeable { if ( set.contains( stack.peek() ) ) { set.remove(stack.peek()) val parentNode = stack.pop() + parentNode.value as Container<*, *> session.executeWrite {tx -> 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}) " + "MERGE (parent)-[:RIGHT_SON]->(son)", mutableMapOf( - "parentNodeValue" to parentNode.value, - "parentNodeKey" to parentNode.key, + "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, - "nodeKey" to currentNode.key, + "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 @@ -79,19 +79,20 @@ class Neo4jRepository: Closeable { } else { val parentNode = stack.peek() + parentNode.value as Container<*, *> session.executeWrite {tx -> 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}) " + "MERGE (parent)-[:LEFT_SON]->(son)", mutableMapOf( - "parentNodeValue" to parentNode.value, - "parentNodeKey" to parentNode.key, + "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, - "nodeKey" to currentNode.key, + "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 @@ -103,9 +104,9 @@ class Neo4jRepository: Closeable { stack.push(currentNode) } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) - var currentNode: DBNodeRB? = null + var currentNode: DBNodeRB? = null - while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().key == inOrder[inOrderIndex].key) { + while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() ++inOrderIndex } @@ -120,20 +121,17 @@ class Neo4jRepository: Closeable { session.close() } - // Это скорее всего будем запускать при открытии приложения, нужно будет сразу восстановить дерево - - fun exportRBtree() { + fun exportRBtree(): Pair>>>, List>>>>{ val session = driver?.session() ?: throw IOException() - var preOrder: List>> = listOf() - var inOrder: List>> = listOf() + 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 ").list() .map{ DBNodeRB( - value = it.values().get(0).toString(), - key = it.values().get(1).toString(), + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, x = it.values().get(3).toString().toDouble(), y = it.values().get(4).toString().toDouble()) @@ -143,16 +141,17 @@ class Neo4jRepository: Closeable { "RETURN node.value, node.key, node.color, node.x, node.y " + "ORDER BY node.key").list() .map{ DBNodeRB( - value = it.values().get(0).toString(), - key = it.values().get(1).toString(), + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, x = it.values().get(3).toString().toDouble(), y = it.values().get(4).toString().toDouble()) } } - + session.close() + return Pair(preOrder, inOrder) + } override fun close() { diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt index d048930..b28228f 100644 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -3,12 +3,7 @@ package treelib import treelib.singleObjects.Markers -class DBNodeRB, V>(val value: V, - val key: K, +class DBNodeRB>(val value: Pack, val color: Markers = Markers.RED, val x: Double = 0.0, - val y: Double = 0.0) { - - - -} + val y: Double = 0.0) diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt new file mode 100644 index 0000000..0aeb3e0 --- /dev/null +++ b/lib/src/main/kotlin/treelib/Main.kt @@ -0,0 +1,39 @@ +package treelib + +import Controller + + +fun main() { + // 25, 15, 10, 4, 12, 22, 18, 24, 50, 35, 31, 44, 70, 66, 90 + // 4, 10, 12, 15, 18, 22, 24, 25, 31, 35, 44, 50, 66, 70, 90 + + /* + + val neo4jRep = Neo4jRepository() + val a1 = DBNodeRB(Container(Pair(1, 25))) + val a2 = DBNodeRB(Container(Pair(1, 15))) + val a3 = DBNodeRB(Container(Pair(1, 10))) + val a4 = DBNodeRB(Container(Pair(1, 4))) + val a5 = DBNodeRB(Container(Pair(1, 12))) + val a6 = DBNodeRB(Container(Pair(1, 22))) + val a7 = DBNodeRB(Container(Pair(1, 18))) + val a8 = DBNodeRB(Container(Pair(1, 24))) + val a9 = DBNodeRB(Container(Pair(1, 50))) + val a10 = DBNodeRB(Container(Pair(1, 35))) + val a11 = DBNodeRB(Container(Pair(1, 31))) + val a12 = DBNodeRB(Container(Pair(1, 44))) + val a13 = DBNodeRB(Container(Pair(1, 70))) + val a14 = DBNodeRB(Container(Pair(1, 66))) + val a15 = DBNodeRB(Container(Pair(1, 90))) + val preArr = arrayOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) + val inArr = arrayOf(a4, a3, a5, a2, a7, a6, a8, a1, a11, a10, a12, a9, a14, a13, a15) + neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") + neo4jRep.saveChanges(preArr, inArr) + */ + val controller = Controller() + controller.saveTree() + + //neo4jRep.exportRBtree() + + //neo4jRep.close() +} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index b64cca1..1607caa 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -33,15 +33,15 @@ abstract class Tree< 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) 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 d2fe865..2f536d8 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -162,7 +162,8 @@ abstract class TreeStruct< linkNewNode(currentNode, parentNode) - return generateStateInsert(currentNode, parentNode) + if (parentNode != null) return generateStateInsert(currentNode, parentNode) + else return generateStateInsert(currentNode, currentNode) } updateNode.value = item @@ -258,8 +259,8 @@ abstract class TreeStruct< fun find(obj: Pack): Pack? = findItem(obj).contentNode?.value - fun inOrder(): List { - val arrayNodes = mutableListOf() + fun inOrder(): List { + val arrayNodes = mutableListOf() var flagVisited = 0 var current = root val parents = ArrayDeque() @@ -276,7 +277,7 @@ abstract class TreeStruct< } } current?.let { - arrayNodes.add(it.value) + arrayNodes.add(it) if (it.right != null) { flagVisited = 0 current = it.right @@ -291,9 +292,9 @@ abstract class TreeStruct< return arrayNodes } - fun postOrder(): List { + fun postOrder(): List { val parents = ArrayDeque() - val arrayNodes = mutableListOf() + val arrayNodes = mutableListOf() var flagVisited = 0 var current = root @@ -314,7 +315,7 @@ abstract class TreeStruct< current = it.right flagVisited = 0 } else { - arrayNodes.add(it.value) + arrayNodes.add(it) if (parents.isEmpty()) return@postOrder arrayNodes val parent = parents.removeLast() @@ -328,8 +329,8 @@ abstract class TreeStruct< return arrayNodes } - fun preOrder(): List { - val arrayNodes = mutableListOf() + fun preOrder(): List { + val arrayNodes = mutableListOf() var current: NodeType val queue = ArrayDeque() @@ -337,7 +338,7 @@ abstract class TreeStruct< queue.add(root) while (queue.isNotEmpty()) { current = queue.removeLast() - arrayNodes.add(current.value) + arrayNodes.add(current) if (current.right != null) current.right?.let { queue.add(it) From 366949e530cc6a9c522b28cb9eb3fb8f958a8450 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 20:21:27 +0300 Subject: [PATCH 065/212] fix: Fix adding to database (neo4j) --- lib/src/main/kotlin/DataBase.kt | 82 ++++++++++-------- lib/src/main/kotlin/treelib/DBNodeRB.kt | 10 ++- .../kotlin/treelib/avlTree/AVLBalancer.kt | 15 ++-- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 85 ++++++++++--------- .../main/kotlin/treelib/rbTree/RBStruct.kt | 63 ++++++++++++-- 5 files changed, 161 insertions(+), 94 deletions(-) diff --git a/lib/src/main/kotlin/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index 3e94aac..e7ca4c6 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -1,4 +1,3 @@ - import org.neo4j.driver.AuthTokens import org.neo4j.driver.Driver import org.neo4j.driver.GraphDatabase @@ -10,21 +9,21 @@ import java.io.Closeable import java.io.IOException import java.util.* -class Neo4jRepository: Closeable { +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) { + } catch (ex: IllegalArgumentException) { + throw IOException() + } catch (ex: SessionExpiredException) { throw IOException() } } - fun > saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() @@ -53,15 +52,16 @@ class Neo4jRepository: Closeable { } } if (!stack.isEmpty()) { - if ( set.contains( stack.peek() ) ) { + if (set.contains(stack.peek())) { set.remove(stack.peek()) val parentNode = stack.pop() parentNode.value as Container<*, *> - session.executeWrite {tx -> - 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}) " + - "MERGE (parent)-[:RIGHT_SON]->(son)", + session.executeWrite { tx -> + 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}) " + + "MERGE (parent)-[:RIGHT_SON]->(son)", mutableMapOf( "parentNodeValue" to parentNode.value.pair.second, "parentNodeKey" to parentNode.value.pair.first, @@ -76,15 +76,15 @@ class Neo4jRepository: Closeable { ) ) } - } - else { + } else { val parentNode = stack.peek() parentNode.value as Container<*, *> - session.executeWrite {tx -> - 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}) " + - "MERGE (parent)-[:LEFT_SON]->(son)", + session.executeWrite { tx -> + 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}) " + + "MERGE (parent)-[:LEFT_SON]->(son)", mutableMapOf( "parentNodeValue" to parentNode.value.pair.second, "parentNodeKey" to parentNode.value.pair.first, @@ -106,7 +106,7 @@ class Neo4jRepository: Closeable { var currentNode: DBNodeRB? = null - while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { + while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() ++inOrderIndex } @@ -121,30 +121,38 @@ class Neo4jRepository: Closeable { session.close() } - fun exportRBtree(): Pair>>>, List>>>>{ + fun exportRBtree(): Pair>>>, List>>>> { 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 ").list() - .map{ DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble()) + session.executeRead { tx -> + preOrder = tx.run( + "MATCH (node: Node) " + + "RETURN node.value, node.key, node.color, node.x, node.y " + ).list() + .map { + DBNodeRB( + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(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{ DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(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 { + DBNodeRB( + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(4).toString().toDouble() + ) } } diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt index b28228f..cf067e1 100644 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -3,7 +3,9 @@ package treelib import treelib.singleObjects.Markers -class DBNodeRB>(val value: Pack, - val color: Markers = Markers.RED, - val x: Double = 0.0, - val y: Double = 0.0) +class DBNodeRB>( + val value: Pack, + val color: Markers = Markers.RED, + val x: Double = 0.0, + val y: Double = 0.0 +) diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 5f80a87..1a3afb9 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -2,7 +2,8 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancerNoParent -class AVLBalancer>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { +class AVLBalancer>(private var root: AVLNode?) : + BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { return (getHeight(node?.left) - getHeight(node?.right)).toInt() } @@ -13,14 +14,16 @@ class AVLBalancer>(private var root: AVLNode?): Bal private fun updateHeight(currentNode: AVLNode?) { if (currentNode != null) - currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right))+1u + currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u } override fun balance(stateContainer: AVLStateContainer): AVLNode { - val node = stateContainer.contentNode ?: throw IllegalStateException("") // IllegalBaseNodeException("A non-existent node (null) was passed to the method") + 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) { @@ -34,7 +37,8 @@ class AVLBalancer>(private var root: AVLNode?): Bal val balance = updateBalance(currentNode) if (balance == -2) { if (updateBalance(currentNode.right) == 1) { - currentNode.right = currentNode.right?.let { rightRotate(it) } ?: throw NullPointerException() // IllegalNodeStateException() + currentNode.right = currentNode.right?.let { rightRotate(it) } + ?: throw NullPointerException() // IllegalNodeStateException() updateHeight(currentNode.right?.right) } val balancedNode = leftRotate(currentNode) @@ -44,7 +48,8 @@ class AVLBalancer>(private var root: AVLNode?): Bal } if (balance == 2) { if (updateBalance(currentNode.left) == -1) { - currentNode.left = currentNode.left?.let { leftRotate(it) } ?: throw NullPointerException() // IllegalNodeStateException("There is no node required by the condition of the algorithm") + 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) diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 67e5427..211e007 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -3,7 +3,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent import treelib.singleObjects.Markers -class RBBalancer>(private var root: RBNode?): BalancerParent, RBStateContainer>() { +class RBBalancer>(private var root: RBNode?) : + BalancerParent, RBStateContainer>() { init { root?.color = Markers.BLACK @@ -39,14 +40,13 @@ class RBBalancer>(private var root: RBNode?): Balan return node.right == null && node.left == null } - override fun balance(stateContainer: RBStateContainer): RBNode { - val node = stateContainer.contentNode ?: - throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") + override fun balance(stateContainer: RBStateContainer): RBNode { + val node = stateContainer.contentNode + ?: throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") 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) { @@ -56,30 +56,35 @@ class RBBalancer>(private var root: RBNode?): Balan return getRoot(currentNode) } - var parent = currentNode.parent ?: throw IllegalStateException() // в данном случае родитель не может быть null + 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 IllegalStateException() // IllegalNodeStateException() + parent = + currentNode.parent?.parent ?: throw IllegalStateException() // 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 NullPointerException() // IllegalNodeStateException() + parent = + currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED currentNode.left?.color = Markers.RED } + else -> throw IllegalStateException() // невозможное условие выполнения } if (currentNode.parent == null) @@ -87,21 +92,21 @@ class RBBalancer>(private var root: RBNode?): Balan 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) || @@ -110,10 +115,11 @@ 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 IllegalStateException() } } @@ -130,8 +136,7 @@ class RBBalancer>(private var root: RBNode?): Balan currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK - } - else if(uncle != null){ + } else if (uncle != null) { return currentNode } } @@ -168,45 +173,41 @@ 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 { + } 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 { + } else { throw IllegalStateException() } } + else -> throw IllegalStateException() } } @@ -223,8 +224,7 @@ class RBBalancer>(private var root: RBNode?): Balan /** black parent and red brother **/ private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { - brother.parent?.left -> - { + brother.parent?.left -> { var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { @@ -241,7 +241,8 @@ class RBBalancer>(private var root: RBNode?): Balan rightBrotherSon.color = Markers.RED leftRotate(rightBrotherSon) - rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + rightBrotherSon = + rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -251,8 +252,8 @@ 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 @@ -264,7 +265,8 @@ class RBBalancer>(private var root: RBNode?): Balan if (leftBrotherSon.left?.color == Markers.RED) { rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED - leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + leftBrotherSon = + leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } @@ -274,6 +276,7 @@ class RBBalancer>(private var root: RBNode?): Balan leftRotate(parent) } } + else -> throw IllegalStateException() } } @@ -289,29 +292,27 @@ 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 IllegalStateException() } } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 67ffc27..ee6a216 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -1,9 +1,11 @@ package treelib.rbTree +import treelib.DBNodeRB 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>() { @@ -15,17 +17,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(insertNode) + ): RBStateContainer = RBStateContainer(insertNode) override fun generateStateFind( findNode: RBNode?, contentNode: RBNode?, - ): RBStateContainer = RBStateContainer(findNode) + ): RBStateContainer = RBStateContainer(findNode) override fun connectUnlinkedSubTreeWithParent( node: RBNode, @@ -39,11 +41,12 @@ 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 { @@ -62,12 +65,60 @@ class RBStruct> : root?.let { it.color = Markers.BLACK } ?: throw MultithreadingException(ImpossibleCaseException()) - } - else { + } else { if (node.value > parent.value) parent.right = node else parent.left = node node.parent = parent } return node } + + fun restoreTreeFromDatabase(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: DBNodeRB + + 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: DBNodeRB): RBNode { + val node = RBNode(value = drawNode.value, color = drawNode.color) + return node + } } From bac244a5eb8711ff910b7adbd6f317cd5ea5b34e Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 17 Apr 2023 20:53:59 +0300 Subject: [PATCH 066/212] fix: Fix bug in TreeStruct.insert() method (test: after insert of two containers). --- lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index d2fe865..5b96f98 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -141,7 +141,8 @@ abstract class TreeStruct< currentNode?.let { if (obj > it.value) currentNode = it.right else currentNode = it.left - } ?: return generateStateFind(null, null) + } + if (currentNode == null) return generateStateFind(null, null) } } } @@ -162,7 +163,8 @@ abstract class TreeStruct< linkNewNode(currentNode, parentNode) - return generateStateInsert(currentNode, parentNode) + if (parentNode == null) return generateStateInsert(currentNode, currentNode) + else return generateStateInsert(currentNode, parentNode) } updateNode.value = item From a2fe60fdb9815e134c729cd48694733f140ebc53 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 17 Apr 2023 20:56:33 +0300 Subject: [PATCH 067/212] feat: Implement partial functionality of AVLStructFuzzer. --- lib/src/test/kotlin/utils/AVLAnalyzer.kt | 2 -- .../test/kotlin/utils/fuzzers/AVLStructFuzzer.kt | 13 +++++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/src/test/kotlin/utils/AVLAnalyzer.kt b/lib/src/test/kotlin/utils/AVLAnalyzer.kt index b8a9ac4..15e7825 100644 --- a/lib/src/test/kotlin/utils/AVLAnalyzer.kt +++ b/lib/src/test/kotlin/utils/AVLAnalyzer.kt @@ -4,8 +4,6 @@ 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 diff --git a/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt index 01d49c5..7cbed69 100644 --- a/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt @@ -1,7 +1,16 @@ package utils.fuzzers import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer +import treelib.avlTree.AVLStruct +import utils.AVLAnalyzer -class AVLStructFuzzer>(){ -// TODO +class AVLStructFuzzer>( + override val baseInput: Array, + override val assertMethod: (input: String) -> Unit +) : TreeStructFuzzer, AVLAnalyzer, AVLStateContainer, AVLStruct>() { + + override fun createTreeStruct(): AVLStruct = AVLStruct() + + override fun createAnalyzer(): AVLAnalyzer = AVLAnalyzer(assertMethod) } \ No newline at end of file From d33f15b54226ba98c124dff5d907e6d549f4fe66 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 21:33:22 +0300 Subject: [PATCH 068/212] fix: Fix database interaction --- lib/src/main/kotlin/Controller.kt | 35 +--- lib/src/main/kotlin/DataBase.kt | 186 ++++++++++-------- lib/src/main/kotlin/initNeo4j.sh | 0 lib/src/main/kotlin/loadNeo4j.sh | 8 + lib/src/main/kotlin/testNeo4j.sh | 13 +- lib/src/main/kotlin/treelib/Main.kt | 29 ++- .../main/kotlin/treelib/abstractTree/Tree.kt | 37 ++-- .../kotlin/treelib/abstractTree/TreeStruct.kt | 26 ++- .../kotlin/treelib/abstractTree/Vertex.kt | 5 + .../balanced/BalancedTreeStruct.kt | 4 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 6 +- .../main/kotlin/treelib/avlTree/AVLTree.kt | 10 +- .../main/kotlin/treelib/avlTree/AVLVertex.kt | 8 + .../main/kotlin/treelib/binTree/BINStruct.kt | 6 +- .../main/kotlin/treelib/binTree/BINTree.kt | 10 +- .../main/kotlin/treelib/binTree/BINVertex.kt | 5 + .../kotlin/treelib/rbTree/DrawRBVertex.kt | 10 + .../main/kotlin/treelib/rbTree/RBBalancer.kt | 24 +-- .../main/kotlin/treelib/rbTree/RBStruct.kt | 13 +- lib/src/main/kotlin/treelib/rbTree/RBTree.kt | 10 +- .../main/kotlin/treelib/rbTree/RBVertex.kt | 9 + 21 files changed, 286 insertions(+), 168 deletions(-) create mode 100644 lib/src/main/kotlin/initNeo4j.sh create mode 100644 lib/src/main/kotlin/loadNeo4j.sh create mode 100644 lib/src/main/kotlin/treelib/abstractTree/Vertex.kt create mode 100644 lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt create mode 100644 lib/src/main/kotlin/treelib/binTree/BINVertex.kt create mode 100644 lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt create mode 100644 lib/src/main/kotlin/treelib/rbTree/RBVertex.kt diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/Controller.kt index cc4e699..a59a3b0 100644 --- a/lib/src/main/kotlin/Controller.kt +++ b/lib/src/main/kotlin/Controller.kt @@ -1,6 +1,6 @@ -import treelib.DBNodeRB + +import treelib.rbTree.DrawRBVertex import treelib.rbTree.RBStruct -import treelib.rbTree.RBTree import treelib.singleObjects.Container class Controller { @@ -8,43 +8,28 @@ class Controller { fun initTree() { val neo4jDB = Neo4jRepository() neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + /*** orders.first = preOrder, orders.second = inOrder ***/ - val orders: Pair>>>, List>>>> = + val orders: Pair>>>, List>>>> = neo4jDB.exportRBtree() val RBtree = RBStruct>>() RBtree.restoreTreeFromDatabase(orders.first, orders.second) + neo4jDB.close() } - fun saveTree() { - val tree = RBTree() - tree.putItem(Pair(25, 1)) - tree.putItem(Pair(15, 1)) - tree.putItem(Pair(50, 1)) - tree.putItem(Pair(10, 1)) - tree.putItem(Pair(22, 1)) - tree.putItem(Pair(35, 1)) - tree.putItem(Pair(70, 1)) - tree.putItem(Pair(4, 1)) - tree.putItem(Pair(12, 1)) - tree.putItem(Pair(18, 1)) - tree.putItem(Pair(24, 1)) - tree.putItem(Pair(31, 1)) - tree.putItem(Pair(44, 1)) - tree.putItem(Pair(66, 1)) - tree.putItem(Pair(90, 1)) - + fun > saveTree(tree: RBStruct) { val neo4jDB = Neo4jRepository() neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") - val preOrder = tree.preOrder().map { DBNodeRB(it.value, it.color) } - val inOrder = tree.inOrder().map { DBNodeRB(it.value, it.color) } + // вот тут плохо, потому что тут надо получать не base nodes, а для рисовалки - neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray()) + 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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index e7ca4c6..809eb64 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -1,8 +1,9 @@ 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.DBNodeRB +import treelib.rbTree.DrawRBVertex import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable @@ -23,88 +24,50 @@ class Neo4jRepository : Closeable { } } - fun > saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() var inOrderIndex = 0 var preOrderIndex = 0 - val set = HashSet>() - val stack = LinkedList>() + val set = HashSet>() + val stack = LinkedList>() + var id = 0 while (preOrderIndex in preOrder.indices) { do { val currentNode = preOrder[preOrderIndex] - currentNode.value as Container<*, *> + //currentNode.value as Container<*, *> if (preOrderIndex == 0) { session.executeWrite { tx -> - tx.run( - "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", - mutableMapOf( - "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 - ) - ) + 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<*, *> + //parentNode.value as Container<*, *> session.executeWrite { tx -> - 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}) " + - "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 - ) - ) + createRightSon(tx, parentNode, currentNode, id) } + ++id } else { val parentNode = stack.peek() parentNode.value as Container<*, *> session.executeWrite { tx -> - 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}) " + - "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 - ) - ) + createLeftSon(tx, parentNode, currentNode, id) } + ++id } } stack.push(currentNode) - } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) + } while (preOrder[preOrderIndex++].value != inOrder[inOrderIndex].value && preOrderIndex < preOrder.size) - var currentNode: DBNodeRB? = null + var currentNode: DrawRBVertex? = null while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() @@ -121,23 +84,26 @@ class Neo4jRepository : Closeable { session.close() } - fun exportRBtree(): Pair>>>, List>>>> { + fun exportRBtree(): Pair>>>, List>>>> { + + /*** Вот тут короче надо обращаться к neo4j с темЮ чтобы она инициализировала себя как-то ***/ val session = driver?.session() ?: throw IOException() - var preOrder: List>>> = listOf() - var inOrder: List>>> = listOf() + 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 " + "RETURN node.value, node.key, node.color, node.x, node.y " + + "ORDER BY node.id" ).list() .map { - DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble() + 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() ) } @@ -147,11 +113,11 @@ class Neo4jRepository : Closeable { "ORDER BY node.key" ).list() .map { - DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble() + 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() ) } } @@ -162,18 +128,80 @@ class Neo4jRepository : Closeable { } + 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() } } - -// neo4j-admin, backup, restore -/* -у меня есть вершины и ребра между ними, надо уметь сохранять дерево в базе, но как? -вопрос: когда заносить изменения в бд и как это реализовывать? -надо еще подумать над оптимизацией, те чтобы не пересобирать дерево в бд каждый раз, когда мы добавили за сессию всего один узел - -root = tx.run("MATCH (parent: Node)-->(son: Node) " + - "WHERE NOT ()-->(parent) " + - "RETURN parent.value, parent.key, parent.color, parent.x, parent.y").list().map {it.values()}.get(0) - */ diff --git a/lib/src/main/kotlin/initNeo4j.sh b/lib/src/main/kotlin/initNeo4j.sh new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/main/kotlin/loadNeo4j.sh b/lib/src/main/kotlin/loadNeo4j.sh new file mode 100644 index 0000000..d4d9fcd --- /dev/null +++ b/lib/src/main/kotlin/loadNeo4j.sh @@ -0,0 +1,8 @@ + +BASEDIR=$(realpath "$(dirname "$0")") + +. "${BASEDIR}/CONTAINER.conf" + +docker stop "$CONTAINER_NAME" + +neo4j-admin dump --database=neo4j --to=/data/backups/ \ No newline at end of file diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/testNeo4j.sh index 97ad8db..87b69c4 100644 --- a/lib/src/main/kotlin/testNeo4j.sh +++ b/lib/src/main/kotlin/testNeo4j.sh @@ -3,13 +3,22 @@ BASEDIR=$(realpath "$(dirname "$0")") . "${BASEDIR}/CONTAINER.conf" +# -d docker run \ - --rm \ - --name "CONTAINER_NAME" \ + -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 \ +# -c /bin/bash +#neo4j-admin database dump neo4j --to-path=$HOME/neo4j/data:/data + +#docker ps -a + +#docker stop "$CONTAINER_NAME" + +#cp $HOME/neo4j/data:/data $HOME/ #docker stop neo4j:latest diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt index 0aeb3e0..15e0eab 100644 --- a/lib/src/main/kotlin/treelib/Main.kt +++ b/lib/src/main/kotlin/treelib/Main.kt @@ -1,6 +1,8 @@ package treelib import Controller +import treelib.rbTree.RBStruct +import treelib.singleObjects.Container fun main() { @@ -30,10 +32,35 @@ fun main() { neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") neo4jRep.saveChanges(preArr, inArr) */ + + val tree = RBStruct>() + tree.insert(Container(Pair(25 , 1))) + tree.insert(Container(Pair(15 , 1))) + tree.insert(Container(Pair(50 , 1))) + tree.insert(Container(Pair(10 , 1))) + tree.insert(Container(Pair(22 , 1))) + tree.insert(Container(Pair(35 , 1))) + tree.insert(Container(Pair(70 , 1))) + tree.insert(Container(Pair(4 , 1))) + tree.insert(Container(Pair(12 , 1))) + tree.insert(Container(Pair(18 , 1))) + tree.insert(Container(Pair(24 , 1))) + tree.insert(Container(Pair(31 , 1))) + tree.insert(Container(Pair(44 , 1))) + tree.insert(Container(Pair(66 , 1))) + tree.insert(Container(Pair(90 , 1))) val controller = Controller() - controller.saveTree() + controller.saveTree(tree) + tree.insert(Container(Pair(100, 1))) + controller.saveTree(tree) + controller.initTree() //neo4jRep.exportRBtree() //neo4jRep.close() +} + +fun test() { + val cont = Controller() + cont.initTree() } \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index 1607caa..1116ad6 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) + 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 2f536d8..c91c6ed 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -9,7 +9,8 @@ import treelib.singleObjects.exceptions.NonExistentValueException abstract class TreeStruct< Pack : Comparable, NodeType : Node, - State : StateContainer + State : StateContainer, + VertexType: Vertex > { protected abstract var root: NodeType? @@ -141,7 +142,8 @@ abstract class TreeStruct< currentNode?.let { if (obj > it.value) currentNode = it.right else currentNode = it.left - } ?: return generateStateFind(null, null) + } + if (currentNode == null) return generateStateFind(null, null) } } } @@ -259,7 +261,7 @@ abstract class TreeStruct< fun find(obj: Pack): Pack? = findItem(obj).contentNode?.value - fun inOrder(): List { + fun inOrder(): List { val arrayNodes = mutableListOf() var flagVisited = 0 var current = root @@ -283,16 +285,18 @@ abstract class TreeStruct< current = it.right } else { if (parents.isEmpty()) - return@inOrder arrayNodes + return@inOrder arrayNodes.map {toVertex(it)} flagVisited = 1 current = parents.removeLast() } } } - return arrayNodes + return arrayNodes.map{toVertex(it)} } - fun postOrder(): List { + abstract fun toVertex(node: NodeType): VertexType + + fun postOrder(): List { val parents = ArrayDeque() val arrayNodes = mutableListOf() var flagVisited = 0 @@ -317,7 +321,7 @@ abstract class TreeStruct< } else { arrayNodes.add(it) if (parents.isEmpty()) - return@postOrder arrayNodes + return@postOrder arrayNodes.map{toVertex(it)} val parent = parents.removeLast() if (parent.right == it) { flagVisited = 2 @@ -326,10 +330,10 @@ abstract class TreeStruct< } } ?: throw MultithreadingException(ImpossibleCaseException()) } - return arrayNodes + return arrayNodes.map{toVertex(it)} } - fun preOrder(): List { + fun preOrder(): List { val arrayNodes = mutableListOf() var current: NodeType val queue = ArrayDeque() @@ -350,6 +354,8 @@ abstract class TreeStruct< } ?: throw MultithreadingException(ImpossibleCaseException()) } } - return arrayNodes + return arrayNodes.map {toVertex(it)} } + + } diff --git a/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt b/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt new file mode 100644 index 0000000..abbb7a5 --- /dev/null +++ b/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt @@ -0,0 +1,5 @@ +package treelib.abstractTree + +abstract class Vertex>{ + 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..96e8458 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt @@ -3,13 +3,15 @@ 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 diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index f275439..7893341 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -3,7 +3,7 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancedTreeStruct class AVLStruct> : - BalancedTreeStruct, AVLStateContainer, AVLBalancer>() { + BalancedTreeStruct, AVLStateContainer, AVLVertex, AVLBalancer>() { override var root: AVLNode? = null override val balancer = AVLBalancer(root) @@ -40,6 +40,10 @@ class AVLStruct> : } } + override fun toVertex(node: AVLNode): AVLVertex { + return AVLVertex(node.value, node.height) + } + override fun createNode(item: Pack): AVLNode = AVLNode(item) override fun getNodeKernel(node: AVLNode): AVLNode = AVLNode(node.value, height = node.height) 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..a43ea26 --- /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() \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index e555924..f3bfc09 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -3,7 +3,7 @@ package treelib.binTree import treelib.abstractTree.TreeStruct class BINStruct> : - TreeStruct, BINStateContainer>() { + TreeStruct, BINStateContainer, BINVertex>() { override var root: BINNode? = null @@ -53,6 +53,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) { 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..e660d8c --- /dev/null +++ b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt @@ -0,0 +1,5 @@ +package treelib.binTree + +import treelib.abstractTree.Vertex + +class BINVertex>(override val value: Pack): Vertex() \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt b/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt new file mode 100644 index 0000000..b3ee499 --- /dev/null +++ b/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt @@ -0,0 +1,10 @@ +package treelib.rbTree + +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/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 211e007..c8c4cf6 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -2,6 +2,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent import treelib.singleObjects.Markers +import treelib.singleObjects.exceptions.IllegalBaseNodeException +import treelib.singleObjects.exceptions.IllegalNodeStateException class RBBalancer>(private var root: RBNode?) : BalancerParent, RBStateContainer>() { @@ -42,7 +44,7 @@ class RBBalancer>(private var root: RBNode?) : override fun balance(stateContainer: RBStateContainer): RBNode { val node = stateContainer.contentNode - ?: throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") + ?: throw IllegalBaseNodeException() val uncle = getUncle(node) when { /** node insertion case **/ @@ -65,7 +67,7 @@ class RBBalancer>(private var root: RBNode?) : currentNode = parent } parent = - currentNode.parent?.parent ?: throw IllegalStateException() // IllegalNodeStateException() + currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -78,7 +80,7 @@ class RBBalancer>(private var root: RBNode?) : currentNode = parent } parent = - currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() + currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -133,7 +135,7 @@ class RBBalancer>(private var root: RBNode?) : val uncle = getUncle(currentNode) if (uncle?.color == Markers.RED) { currentNode.parent?.color = Markers.BLACK - currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() + currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK } else if (uncle != null) { @@ -156,13 +158,13 @@ class RBBalancer>(private var root: RBNode?) : getRoot(parent) } - else -> getRoot(node ?: throw NullPointerException() /* IllegalNodeStateException() */) + else -> getRoot(node ?: throw IllegalNodeStateException()) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() + var brother = getBrother(parent, node) ?: throw IllegalNodeStateException() if (brother.color == Markers.RED) throw NullPointerException() @@ -214,7 +216,7 @@ class RBBalancer>(private var root: RBNode?) : /** parent is black **/ private fun thirdCase(parent: RBNode, node: RBNode?) { - val brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() + val brother = getBrother(parent, node) ?: throw IllegalNodeStateException() when (brother.color) { Markers.RED -> thirdCaseSubFirst(brother, parent) Markers.BLACK -> thirdCaseSubSecond(brother, parent) @@ -225,7 +227,7 @@ class RBBalancer>(private var root: RBNode?) : private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { brother.parent?.left -> { - var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() + var rightBrotherSon = brother.right ?: throw IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED @@ -242,7 +244,7 @@ class RBBalancer>(private var root: RBNode?) : leftRotate(rightBrotherSon) rightBrotherSon = - rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + rightBrotherSon.parent ?: throw IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -258,7 +260,7 @@ class RBBalancer>(private var root: RBNode?) : if (leftBrotherSon.right?.color != Markers.RED && leftBrotherSon.left?.color != Markers.RED) { leftBrotherSon.color = Markers.RED brother.color = Markers.BLACK - leftRotate(brother.parent ?: throw NullPointerException()) // IllegalNodeStateException() + leftRotate(brother.parent ?: throw IllegalNodeStateException()) return } @@ -266,7 +268,7 @@ class RBBalancer>(private var root: RBNode?) : rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED leftBrotherSon = - leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + leftBrotherSon.parent ?: throw IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index ee6a216..47dd91d 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -1,6 +1,5 @@ package treelib.rbTree -import treelib.DBNodeRB import treelib.abstractTree.balanced.BalancedTreeStruct import treelib.singleObjects.Markers import treelib.singleObjects.exceptions.ImpossibleCaseException @@ -8,7 +7,7 @@ import treelib.singleObjects.exceptions.MultithreadingException import java.util.* class RBStruct> : - BalancedTreeStruct, RBStateContainer, RBBalancer>() { + BalancedTreeStruct, RBStateContainer, RBVertex, RBBalancer>() { override var root: RBNode? = null @@ -57,6 +56,10 @@ class RBStruct> : 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 { @@ -73,7 +76,7 @@ class RBStruct> : return node } - fun restoreTreeFromDatabase(preOrder: List>, inOrder: List>) { + fun > restoreTreeFromDatabase(preOrder: List, inOrder: List) { var inOrderIndex = 0 var preOrderIndex = 0 val set = HashSet>() @@ -81,7 +84,7 @@ class RBStruct> : while (preOrderIndex in preOrder.indices) { var currentNode: RBNode? - var drawNode: DBNodeRB + var drawNode: RBVertexType do { drawNode = preOrder[preOrderIndex] @@ -117,7 +120,7 @@ class RBStruct> : } - private fun createRBNode(drawNode: DBNodeRB): RBNode { + 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..5d9d161 --- /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 From 8a03667eef9c13706d2496abadedc6029576561c Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 22:04:52 +0300 Subject: [PATCH 069/212] fix: Add running on multiple OS --- .github/workflows/CI.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ae15aca..8697f86 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 From 6cee47dd2b3fcfcfea6bba407f174c0670942c82 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 22:37:53 +0300 Subject: [PATCH 070/212] fix: Fix tree class definitions --- .gitignore | 3 ++- lib/src/test/kotlin/treelib/AVLStructTest.kt | 3 ++- lib/src/test/kotlin/treelib/BINStructTest.kt | 3 ++- lib/src/test/kotlin/treelib/RBStructTest.kt | 3 ++- lib/src/test/kotlin/utils/TreeStructWrapper.kt | 3 ++- lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt | 3 ++- lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt | 3 ++- .../test/kotlin/utils/fuzzers/TreeStructFuzzer.kt | 12 +++++++----- 8 files changed, 21 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 7061064..38f0b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.gradle/ /.idea/ /build/ -/lib/build/ \ No newline at end of file +/lib/build/ +/lib/TEST_TEST/ diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 347892c..0e4f7dc 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -7,12 +7,13 @@ 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, AVLStateContainer, AVLStruct>() + private val treeW = TreeStructWrapper, AVLVertex, AVLStateContainer, AVLStruct>() private val treeH = AVLAnalyzer(::testAssert) private var treeStruct = AVLStruct() diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 952a6fa..1075631 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -7,13 +7,14 @@ 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, BINStateContainer, BINStruct>() + val treeW = TreeStructWrapper, BINVertex, BINStateContainer, BINStruct>() var treeStruct = BINStruct() private fun testAssert(msg: String): Nothing = fail(msg) diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index 606ca40..3196207 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -4,6 +4,7 @@ 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 @@ -12,7 +13,7 @@ import kotlin.test.assertEquals @DisplayName("Test: Red-Black Tree Struct") class RBStructTest { - private val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() + private val treeW = TreeStructWrapper, RBVertex, RBStateContainer, RBStruct>() private var treeStruct = RBStruct() private fun testAssert(msg: String): Nothing = fail(msg) diff --git a/lib/src/test/kotlin/utils/TreeStructWrapper.kt b/lib/src/test/kotlin/utils/TreeStructWrapper.kt index 1bdb93e..64fea41 100644 --- a/lib/src/test/kotlin/utils/TreeStructWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeStructWrapper.kt @@ -3,8 +3,9 @@ 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) diff --git a/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt index 5ce3959..bc79507 100644 --- a/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt @@ -3,12 +3,13 @@ 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, BINAnalyzer, BINStateContainer, BINStruct>() { +): TreeStructFuzzer, BINVertex, BINAnalyzer, BINStateContainer, BINStruct>() { override fun createTreeStruct(): BINStruct = BINStruct() diff --git a/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt index 6b59799..2a6bb7f 100644 --- a/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt @@ -3,12 +3,13 @@ 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, RBAnalyzer, RBStateContainer, RBStruct>() { +): TreeStructFuzzer, RBVertex, RBAnalyzer, RBStateContainer, RBStruct>() { override fun createTreeStruct(): RBStruct = RBStruct() diff --git a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt index a5390b9..7d5e0d6 100644 --- a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt @@ -3,20 +3,22 @@ package utils.fuzzers import treelib.abstractTree.Node import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct -import kotlin.random.Random -import utils.Analyzer +import treelib.abstractTree.Vertex import treelib.singleObjects.exceptions.* -import kotlin.random.nextInt +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, + TreeStructType : TreeStruct, > { abstract val baseInput: Array @@ -26,7 +28,7 @@ abstract class TreeStructFuzzer< private var dirPath: String? = null - protected val treeWrapper = TreeStructWrapper() + protected val treeWrapper = TreeStructWrapper() protected abstract fun createTreeStruct(): TreeStructType From fc047c023cf71694ed9bce559a8ca91ad2e7974a Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 23:33:36 +0300 Subject: [PATCH 071/212] fix(CI): Add a condition to run jacoco --- .github/workflows/CI.yml | 6 ++++-- lib/src/test/kotlin/{ => treelib}/AVLBalancerTest.kt | 0 lib/src/test/kotlin/{ => treelib}/RBBalancerTest.kt | 0 lib/src/test/kotlin/{ => treelib}/TestModelAVL.kt | 0 lib/src/test/kotlin/{ => treelib}/TestModelRBT.kt | 0 5 files changed, 4 insertions(+), 2 deletions(-) rename lib/src/test/kotlin/{ => treelib}/AVLBalancerTest.kt (100%) rename lib/src/test/kotlin/{ => treelib}/RBBalancerTest.kt (100%) rename lib/src/test/kotlin/{ => treelib}/TestModelAVL.kt (100%) rename lib/src/test/kotlin/{ => treelib}/TestModelRBT.kt (100%) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8697f86..243bd23 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -22,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/lib/src/test/kotlin/AVLBalancerTest.kt b/lib/src/test/kotlin/treelib/AVLBalancerTest.kt similarity index 100% rename from lib/src/test/kotlin/AVLBalancerTest.kt rename to lib/src/test/kotlin/treelib/AVLBalancerTest.kt diff --git a/lib/src/test/kotlin/RBBalancerTest.kt b/lib/src/test/kotlin/treelib/RBBalancerTest.kt similarity index 100% rename from lib/src/test/kotlin/RBBalancerTest.kt rename to lib/src/test/kotlin/treelib/RBBalancerTest.kt diff --git a/lib/src/test/kotlin/TestModelAVL.kt b/lib/src/test/kotlin/treelib/TestModelAVL.kt similarity index 100% rename from lib/src/test/kotlin/TestModelAVL.kt rename to lib/src/test/kotlin/treelib/TestModelAVL.kt diff --git a/lib/src/test/kotlin/TestModelRBT.kt b/lib/src/test/kotlin/treelib/TestModelRBT.kt similarity index 100% rename from lib/src/test/kotlin/TestModelRBT.kt rename to lib/src/test/kotlin/treelib/TestModelRBT.kt From f4af1f2c95d51badae5a3057c2cb6bd53e43f00b Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 23:36:42 +0300 Subject: [PATCH 072/212] refactor: Rename some methods in test classes --- lib/build.gradle.kts | 6 +++--- lib/src/test/kotlin/treelib/AVLBalancerTest.kt | 2 ++ lib/src/test/kotlin/treelib/RBBalancerTest.kt | 2 ++ lib/src/test/kotlin/treelib/TestModelAVL.kt | 2 ++ lib/src/test/kotlin/treelib/TestModelRBT.kt | 2 ++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 0f9f88e..6c76505 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -82,7 +82,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 { @@ -90,7 +90,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "BRANCH" - minimum = 0.4.toBigDecimal() + minimum = 0.5.toBigDecimal() } } rule { @@ -104,7 +104,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "METHOD" - minimum = 1.0.toBigDecimal() + minimum = 0.9.toBigDecimal() } } } diff --git a/lib/src/test/kotlin/treelib/AVLBalancerTest.kt b/lib/src/test/kotlin/treelib/AVLBalancerTest.kt index 862a497..3ee5452 100644 --- a/lib/src/test/kotlin/treelib/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 diff --git a/lib/src/test/kotlin/treelib/RBBalancerTest.kt b/lib/src/test/kotlin/treelib/RBBalancerTest.kt index 0bc9e43..d18134e 100644 --- a/lib/src/test/kotlin/treelib/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 diff --git a/lib/src/test/kotlin/treelib/TestModelAVL.kt b/lib/src/test/kotlin/treelib/TestModelAVL.kt index c96773d..8a3ba26 100644 --- a/lib/src/test/kotlin/treelib/TestModelAVL.kt +++ b/lib/src/test/kotlin/treelib/TestModelAVL.kt @@ -1,3 +1,5 @@ +package treelib + import treelib.avlTree.AVLNode class TestModelAVL { diff --git a/lib/src/test/kotlin/treelib/TestModelRBT.kt b/lib/src/test/kotlin/treelib/TestModelRBT.kt index ac86a6c..ca7efe7 100644 --- a/lib/src/test/kotlin/treelib/TestModelRBT.kt +++ b/lib/src/test/kotlin/treelib/TestModelRBT.kt @@ -1,3 +1,5 @@ +package treelib + import treelib.rbTree.RBNode import treelib.singleObjects.Markers From c56546bfc5411ca77b4522f7a6992f39aee6f064 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 19 Apr 2023 01:51:44 +0300 Subject: [PATCH 073/212] feat: Implement class for saving AVLStruct in SQLite. --- gradle.properties | 2 + lib/build.gradle.kts | 7 +- .../kotlin/treelib/controller/DrawVertex.kt | 7 + .../treelib/dbSave/avlSQLite/AVLDrawVertex.kt | 10 + .../treelib/dbSave/avlSQLite/SQLiteAVL.kt | 227 ++++++++++++++++++ 5 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 gradle.properties create mode 100644 lib/src/main/kotlin/treelib/controller/DrawVertex.kt create mode 100644 lib/src/main/kotlin/treelib/dbSave/avlSQLite/AVLDrawVertex.kt create mode 100644 lib/src/main/kotlin/treelib/dbSave/avlSQLite/SQLiteAVL.kt 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..3782254 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -1,3 +1,5 @@ +val sqliteJdbcVersion: String by project + plugins { java kotlin("jvm") version "1.8.10" @@ -24,6 +26,9 @@ dependencies { 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") + + // JDBC Sqlite + implementation("org.xerial", "sqlite-jdbc", sqliteJdbcVersion) } tasks.test { @@ -113,4 +118,4 @@ publishing { from(components["java"]) } } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/controller/DrawVertex.kt b/lib/src/main/kotlin/treelib/controller/DrawVertex.kt new file mode 100644 index 0000000..8ccd5dc --- /dev/null +++ b/lib/src/main/kotlin/treelib/controller/DrawVertex.kt @@ -0,0 +1,7 @@ +package treelib.controller + +interface DrawVertex> { + val value: Pack + val x: Double + val y: Double +} diff --git a/lib/src/main/kotlin/treelib/dbSave/avlSQLite/AVLDrawVertex.kt b/lib/src/main/kotlin/treelib/dbSave/avlSQLite/AVLDrawVertex.kt new file mode 100644 index 0000000..be2938c --- /dev/null +++ b/lib/src/main/kotlin/treelib/dbSave/avlSQLite/AVLDrawVertex.kt @@ -0,0 +1,10 @@ +package treelib.dbSave.avlSQLite + +import treelib.controller.DrawVertex + +class AVLDrawVertex>( + override val value: Pack, + override val x: Double, + override val y: Double, + val height: Int, +) : DrawVertex diff --git a/lib/src/main/kotlin/treelib/dbSave/avlSQLite/SQLiteAVL.kt b/lib/src/main/kotlin/treelib/dbSave/avlSQLite/SQLiteAVL.kt new file mode 100644 index 0000000..c125768 --- /dev/null +++ b/lib/src/main/kotlin/treelib/dbSave/avlSQLite/SQLiteAVL.kt @@ -0,0 +1,227 @@ +package treelib.dbSave.avlSQLite + +import java.io.Closeable +import java.sql.DriverManager +import java.sql.SQLException + +class SQLiteAVL>( + 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("") + } 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() + } + } + } + + private 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: AVLDrawVertex, 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: List>, 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( + AVLDrawVertex( + 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: AVLDrawVertex, 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() + } +} From f964e3d659af14af5d9a489b4b92dceb039af329 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Wed, 19 Apr 2023 01:59:08 +0300 Subject: [PATCH 074/212] feat:add TreeWrapper and BINTreeTest --- lib/build.gradle.kts | 2 + lib/src/test/kotlin/treelib/BINTreeTest.kt | 86 ++++++++++++++++++++++ lib/src/test/kotlin/utils/TreeWrapper.kt | 24 ++++++ 3 files changed, 112 insertions(+) create mode 100644 lib/src/test/kotlin/treelib/BINTreeTest.kt create mode 100644 lib/src/test/kotlin/utils/TreeWrapper.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 09124dc..99f9538 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -19,6 +19,7 @@ repositories { } dependencies { + testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") 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") @@ -26,6 +27,7 @@ dependencies { implementation("com.google.guava:guava:31.1-jre") } + tasks.test { finalizedBy(tasks.jacocoTestReport) useJUnitPlatform() diff --git a/lib/src/test/kotlin/treelib/BINTreeTest.kt b/lib/src/test/kotlin/treelib/BINTreeTest.kt new file mode 100644 index 0000000..172c545 --- /dev/null +++ b/lib/src/test/kotlin/treelib/BINTreeTest.kt @@ -0,0 +1,86 @@ +package treelib + +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import treelib.binTree.BINNode +import treelib.binTree.BINStateContainer +import treelib.binTree.BINStruct +import treelib.binTree.BINTree +import utils.TreeWrapper +import treelib.singleObjects.Container +import utils.TreeStructWrapper +import kotlin.test.assertEquals + +class BINTreeTest { + private val tree = BINTree() + private val treeW = + TreeWrapper>, BINStateContainer>, BINStruct>, BINTree>() + private val treeSW = + TreeStructWrapper, BINNode>, 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/utils/TreeWrapper.kt b/lib/src/test/kotlin/utils/TreeWrapper.kt new file mode 100644 index 0000000..5ca8ea7 --- /dev/null +++ b/lib/src/test/kotlin/utils/TreeWrapper.kt @@ -0,0 +1,24 @@ +package utils + +import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer +import treelib.abstractTree.Tree +import treelib.abstractTree.TreeStruct +import treelib.singleObjects.Container + + +class TreeWrapper < + V : Comparable, + Value, + NodeType: Node, NodeType>, + State: StateContainer, NodeType>, + TStruct: TreeStruct, NodeType, State>, + 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 From f1d668a041292f6f613aee4fd599c8175f3a49f3 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Wed, 19 Apr 2023 03:15:39 +0300 Subject: [PATCH 075/212] fix: delete !! --- lib/src/test/kotlin/treelib/AVLStructTest.kt | 35 ++++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 347892c..1e522fe 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -28,7 +28,10 @@ class AVLStructTest { for (i in num) { treeStruct.insert(i) } - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } @Test @@ -37,7 +40,10 @@ class AVLStructTest { for (i in num) { treeStruct.insert(i) } - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } @Test @@ -47,7 +53,10 @@ class AVLStructTest { treeStruct.insert(i) } treeStruct.delete(2) - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } @Test @@ -55,7 +64,10 @@ class AVLStructTest { for (i in 1..100000) { treeStruct.insert(i) } - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } @Test @@ -64,14 +76,20 @@ class AVLStructTest { treeStruct.insert(i) } treeStruct.delete(5000) - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + 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) - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } @Test @@ -80,6 +98,9 @@ class AVLStructTest { for (i in num) { treeStruct.insert(i) } - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } } \ No newline at end of file From e90c4fd0b4281d404457f6e9ef336b8791fc4c6f Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 19 Apr 2023 03:20:10 +0300 Subject: [PATCH 076/212] feat: Implement AVLStruct.restoreStruct and BINStruct.restoreStruct. --- lib/src/main/kotlin/Controller.kt | 2 +- .../kotlin/treelib/abstractTree/TreeStruct.kt | 2 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 15 ++++++++++++--- .../main/kotlin/treelib/binTree/BINStruct.kt | 13 +++++++++++++ lib/src/main/kotlin/treelib/rbTree/RBStruct.kt | 3 +-- .../singleObjects/exceptions/IncorrectUsage.kt | 18 ++++++++++++++++++ lib/src/test/kotlin/treeTests/RBTreeTest.kt | 16 ++++++++++++++++ 7 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt create mode 100644 lib/src/test/kotlin/treeTests/RBTreeTest.kt diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/Controller.kt index a59a3b0..e32442e 100644 --- a/lib/src/main/kotlin/Controller.kt +++ b/lib/src/main/kotlin/Controller.kt @@ -14,7 +14,7 @@ class Controller { neo4jDB.exportRBtree() val RBtree = RBStruct>>() - RBtree.restoreTreeFromDatabase(orders.first, orders.second) + RBtree.restoreStruct(orders.first, orders.second) neo4jDB.close() } diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index c91c6ed..d9b223b 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -15,7 +15,7 @@ abstract class TreeStruct< 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) { diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index 7893341..b0ee0ae 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -1,6 +1,7 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancedTreeStruct +import treelib.singleObjects.exceptions.IncorrectUsage class AVLStruct> : BalancedTreeStruct, AVLStateContainer, AVLVertex, AVLBalancer>() { @@ -40,9 +41,9 @@ class AVLStruct> : } } - override fun toVertex(node: AVLNode): AVLVertex { - return AVLVertex(node.value, node.height) - } + 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) @@ -56,4 +57,12 @@ class AVLStruct> : } return node } + 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/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index f3bfc09..ea03cb6 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -1,6 +1,8 @@ package treelib.binTree import treelib.abstractTree.TreeStruct +import treelib.abstractTree.Vertex +import treelib.singleObjects.exceptions.IncorrectUsage class BINStruct> : TreeStruct, BINStateContainer, BINVertex>() { @@ -66,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/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 47dd91d..999c113 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -76,7 +76,7 @@ class RBStruct> : return node } - fun > restoreTreeFromDatabase(preOrder: List, inOrder: List) { + fun > restoreStruct(preOrder: List, inOrder: List) { var inOrderIndex = 0 var preOrderIndex = 0 val set = HashSet>() @@ -117,7 +117,6 @@ class RBStruct> : stack.push(currentNode) } } - } private fun > createRBNode(drawNode: RBVertexType): RBNode { 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..9fce7ef --- /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/treeTests/RBTreeTest.kt b/lib/src/test/kotlin/treeTests/RBTreeTest.kt new file mode 100644 index 0000000..fa56cc6 --- /dev/null +++ b/lib/src/test/kotlin/treeTests/RBTreeTest.kt @@ -0,0 +1,16 @@ +package treeTests + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import treelib.rbTree.RBTree + +@DisplayName("Test: Red-Black Tree") +class RBTreeTest { + private var rbTree = RBTree() + + @Test + fun `insert two elements`(){ + rbTree.putItem(Pair(25, 1)) + rbTree.putItem(Pair(15, 1)) + } +} \ No newline at end of file From b2d93053df81c05a92019f856ce3d183080dc0cf Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Wed, 19 Apr 2023 03:29:17 +0300 Subject: [PATCH 077/212] feat: AVLTreeTest --- lib/src/test/kotlin/treelib/AVLTreeTest.kt | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 lib/src/test/kotlin/treelib/AVLTreeTest.kt diff --git a/lib/src/test/kotlin/treelib/AVLTreeTest.kt b/lib/src/test/kotlin/treelib/AVLTreeTest.kt new file mode 100644 index 0000000..5531d35 --- /dev/null +++ b/lib/src/test/kotlin/treelib/AVLTreeTest.kt @@ -0,0 +1,76 @@ +package treelib + +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer +import treelib.avlTree.AVLStruct +import treelib.avlTree.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>, AVLStateContainer>, AVLStruct>, AVLTree>() + private val treeSW = + TreeStructWrapper, AVLNode>, 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 From ac11aa1898aaaa67f3f0fbb5b1fa331208281eda Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Wed, 19 Apr 2023 04:26:57 +0300 Subject: [PATCH 078/212] feat: RBTreeTest --- lib/src/test/kotlin/treelib/RBTreeTest.kt | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 lib/src/test/kotlin/treelib/RBTreeTest.kt diff --git a/lib/src/test/kotlin/treelib/RBTreeTest.kt b/lib/src/test/kotlin/treelib/RBTreeTest.kt new file mode 100644 index 0000000..5a3ceee --- /dev/null +++ b/lib/src/test/kotlin/treelib/RBTreeTest.kt @@ -0,0 +1,58 @@ +package treelib + +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct +import treelib.rbTree.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>, RBStateContainer>, RBStruct>, RBTree>() + private val treeSW = + TreeStructWrapper, RBNode>, 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 From cd10d0d0801ecd71ddf3f44900d8c5ec9d272013 Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 19 Apr 2023 04:39:37 +0300 Subject: [PATCH 079/212] feat: Implement sketch of jsonRepository --- .gitignore | 1 + lib/build.gradle.kts | 3 + .../Neo4jController.kt} | 6 +- .../kotlin/dbSave/jsonFormat/DrawBINVertex.kt | 9 +++ .../dbSave/jsonFormat/JsonRepository.kt | 34 ++++++++++ .../kotlin/{ => dbSave/neo4j}/CONTAINER.conf | 0 .../rbTree => dbSave/neo4j}/DrawRBVertex.kt | 3 +- .../neo4j/Neo4jRepository.kt} | 4 +- .../kotlin/{ => dbSave/neo4j}/testNeo4j.sh | 11 ---- lib/src/main/kotlin/initNeo4j.sh | 0 lib/src/main/kotlin/loadNeo4j.sh | 8 --- lib/src/main/kotlin/treelib/DBNodeRB.kt | 11 ---- lib/src/main/kotlin/treelib/Main.kt | 66 ------------------- .../main/kotlin/treelib/binTree/BINVertex.kt | 2 +- 14 files changed, 57 insertions(+), 101 deletions(-) rename lib/src/main/kotlin/{Controller.kt => controller/Neo4jController.kt} (91%) create mode 100644 lib/src/main/kotlin/dbSave/jsonFormat/DrawBINVertex.kt create mode 100644 lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt rename lib/src/main/kotlin/{ => dbSave/neo4j}/CONTAINER.conf (100%) rename lib/src/main/kotlin/{treelib/rbTree => dbSave/neo4j}/DrawRBVertex.kt (79%) rename lib/src/main/kotlin/{DataBase.kt => dbSave/neo4j/Neo4jRepository.kt} (99%) rename lib/src/main/kotlin/{ => dbSave/neo4j}/testNeo4j.sh (59%) delete mode 100644 lib/src/main/kotlin/initNeo4j.sh delete mode 100644 lib/src/main/kotlin/loadNeo4j.sh delete mode 100644 lib/src/main/kotlin/treelib/DBNodeRB.kt delete mode 100644 lib/src/main/kotlin/treelib/Main.kt diff --git a/.gitignore b/.gitignore index 38f0b3d..16cba14 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /build/ /lib/build/ /lib/TEST_TEST/ +/gradle/ diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 6c76505..db61e37 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -22,6 +22,8 @@ dependencies { api("org.apache.commons:commons-math3:3.6.1") implementation("com.google.guava:guava:31.1-jre") + 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) @@ -30,6 +32,7 @@ dependencies { testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") implementation(kotlin("stdlib-jdk8")) + } tasks.test { diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/controller/Neo4jController.kt similarity index 91% rename from lib/src/main/kotlin/Controller.kt rename to lib/src/main/kotlin/controller/Neo4jController.kt index a59a3b0..3744351 100644 --- a/lib/src/main/kotlin/Controller.kt +++ b/lib/src/main/kotlin/controller/Neo4jController.kt @@ -1,9 +1,11 @@ +package controller -import treelib.rbTree.DrawRBVertex +import dbSave.neo4j.DrawRBVertex +import dbSave.neo4j.Neo4jRepository import treelib.rbTree.RBStruct import treelib.singleObjects.Container -class Controller { +class Neo4jController { fun initTree() { val neo4jDB = Neo4jRepository() 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..88a1396 --- /dev/null +++ b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt @@ -0,0 +1,34 @@ +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>, typeToken: TypeToken>>, fileName: String) { + + val gson = GsonBuilder().setPrettyPrinting().create() + val json = gson.toJson(preOrder) + + File(dirPath, fileName).run { + createNewFile() + writeText(json) + } + + val preOrd = gson.fromJson>>(json, typeToken.type) + + } + + fun exportTree(fileName: String) { + val gson = GsonBuilder().setPrettyPrinting().create() + //val json = gson.fromJson(File(dirPath, fileName).readText(), ArrayVertices::class.java) + + + } + + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/CONTAINER.conf b/lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf similarity index 100% rename from lib/src/main/kotlin/CONTAINER.conf rename to lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf diff --git a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt b/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt similarity index 79% rename from lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt rename to lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt index b3ee499..37100de 100644 --- a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt +++ b/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt @@ -1,5 +1,6 @@ -package treelib.rbTree +package dbSave.neo4j +import treelib.rbTree.RBVertex import treelib.singleObjects.Markers class DrawRBVertex>( diff --git a/lib/src/main/kotlin/DataBase.kt b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt similarity index 99% rename from lib/src/main/kotlin/DataBase.kt rename to lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt index 809eb64..316dc4c 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt @@ -1,9 +1,11 @@ +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.rbTree.DrawRBVertex import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh similarity index 59% rename from lib/src/main/kotlin/testNeo4j.sh rename to lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh index 87b69c4..6b07269 100644 --- a/lib/src/main/kotlin/testNeo4j.sh +++ b/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh @@ -3,7 +3,6 @@ BASEDIR=$(realpath "$(dirname "$0")") . "${BASEDIR}/CONTAINER.conf" -# -d docker run \ -i \ --name "$CONTAINER_NAME" \ @@ -12,13 +11,3 @@ docker run \ --publish=7474:7474 --publish=7687:7687 \ --env NEO4J_AUTH=neo4j/"$PASSWORD" \ neo4j:latest \ -# -c /bin/bash -#neo4j-admin database dump neo4j --to-path=$HOME/neo4j/data:/data - -#docker ps -a - -#docker stop "$CONTAINER_NAME" - -#cp $HOME/neo4j/data:/data $HOME/ - -#docker stop neo4j:latest diff --git a/lib/src/main/kotlin/initNeo4j.sh b/lib/src/main/kotlin/initNeo4j.sh deleted file mode 100644 index e69de29..0000000 diff --git a/lib/src/main/kotlin/loadNeo4j.sh b/lib/src/main/kotlin/loadNeo4j.sh deleted file mode 100644 index d4d9fcd..0000000 --- a/lib/src/main/kotlin/loadNeo4j.sh +++ /dev/null @@ -1,8 +0,0 @@ - -BASEDIR=$(realpath "$(dirname "$0")") - -. "${BASEDIR}/CONTAINER.conf" - -docker stop "$CONTAINER_NAME" - -neo4j-admin dump --database=neo4j --to=/data/backups/ \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt deleted file mode 100644 index cf067e1..0000000 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ /dev/null @@ -1,11 +0,0 @@ -package treelib - -import treelib.singleObjects.Markers - - -class DBNodeRB>( - val value: Pack, - val color: Markers = Markers.RED, - val x: Double = 0.0, - val y: Double = 0.0 -) diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt deleted file mode 100644 index 15e0eab..0000000 --- a/lib/src/main/kotlin/treelib/Main.kt +++ /dev/null @@ -1,66 +0,0 @@ -package treelib - -import Controller -import treelib.rbTree.RBStruct -import treelib.singleObjects.Container - - -fun main() { - // 25, 15, 10, 4, 12, 22, 18, 24, 50, 35, 31, 44, 70, 66, 90 - // 4, 10, 12, 15, 18, 22, 24, 25, 31, 35, 44, 50, 66, 70, 90 - - /* - - val neo4jRep = Neo4jRepository() - val a1 = DBNodeRB(Container(Pair(1, 25))) - val a2 = DBNodeRB(Container(Pair(1, 15))) - val a3 = DBNodeRB(Container(Pair(1, 10))) - val a4 = DBNodeRB(Container(Pair(1, 4))) - val a5 = DBNodeRB(Container(Pair(1, 12))) - val a6 = DBNodeRB(Container(Pair(1, 22))) - val a7 = DBNodeRB(Container(Pair(1, 18))) - val a8 = DBNodeRB(Container(Pair(1, 24))) - val a9 = DBNodeRB(Container(Pair(1, 50))) - val a10 = DBNodeRB(Container(Pair(1, 35))) - val a11 = DBNodeRB(Container(Pair(1, 31))) - val a12 = DBNodeRB(Container(Pair(1, 44))) - val a13 = DBNodeRB(Container(Pair(1, 70))) - val a14 = DBNodeRB(Container(Pair(1, 66))) - val a15 = DBNodeRB(Container(Pair(1, 90))) - val preArr = arrayOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) - val inArr = arrayOf(a4, a3, a5, a2, a7, a6, a8, a1, a11, a10, a12, a9, a14, a13, a15) - neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") - neo4jRep.saveChanges(preArr, inArr) - */ - - val tree = RBStruct>() - tree.insert(Container(Pair(25 , 1))) - tree.insert(Container(Pair(15 , 1))) - tree.insert(Container(Pair(50 , 1))) - tree.insert(Container(Pair(10 , 1))) - tree.insert(Container(Pair(22 , 1))) - tree.insert(Container(Pair(35 , 1))) - tree.insert(Container(Pair(70 , 1))) - tree.insert(Container(Pair(4 , 1))) - tree.insert(Container(Pair(12 , 1))) - tree.insert(Container(Pair(18 , 1))) - tree.insert(Container(Pair(24 , 1))) - tree.insert(Container(Pair(31 , 1))) - tree.insert(Container(Pair(44 , 1))) - tree.insert(Container(Pair(66 , 1))) - tree.insert(Container(Pair(90 , 1))) - val controller = Controller() - controller.saveTree(tree) - tree.insert(Container(Pair(100, 1))) - controller.saveTree(tree) - controller.initTree() - - //neo4jRep.exportRBtree() - - //neo4jRep.close() -} - -fun test() { - val cont = Controller() - cont.initTree() -} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/binTree/BINVertex.kt b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt index e660d8c..8939461 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINVertex.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt @@ -2,4 +2,4 @@ package treelib.binTree import treelib.abstractTree.Vertex -class BINVertex>(override val value: Pack): Vertex() \ No newline at end of file +open class BINVertex>(override val value: Pack): Vertex() \ No newline at end of file From 242481be85a1af14a2b61ac288073fb463b3e055 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 15 Apr 2023 15:27:37 +0300 Subject: [PATCH 080/212] fix: Partially change exceptions --- .../kotlin/treelib/avlTree/AVLBalancer.kt | 25 ++++----- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 55 +++++++++---------- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 703ea69..5f80a87 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -1,11 +1,8 @@ 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>() { +class AVLBalancer>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { return (getHeight(node?.left) - getHeight(node?.right)).toInt() } @@ -16,19 +13,18 @@ class AVLBalancer>(private var root: AVLNode?) : private fun updateHeight(currentNode: AVLNode?) { if (currentNode != null) - currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u + 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 +34,7 @@ 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 +44,7 @@ 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 +54,5 @@ class AVLBalancer>(private var root: AVLNode?) : } return currentNode } + } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index fe73c5b..67e5427 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -2,9 +2,6 @@ package treelib.rbTree 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>() { @@ -30,7 +27,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,12 +35,13 @@ 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 IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") val uncle = getUncle(node) when { /** node insertion case **/ @@ -58,14 +56,14 @@ 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 IllegalStateException() // IllegalNodeStateException() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -76,17 +74,17 @@ class RBBalancer>(private var root: RBNode?): Balan rightRotate(parent) currentNode = parent } - parent = currentNode.parent?.parent ?: throw IllegalNodeStateException() + parent = currentNode.parent?.parent ?: throw NullPointerException() // 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) -> @@ -116,12 +114,11 @@ class RBBalancer>(private var root: RBNode?): Balan firstCase(node, null) } - - else -> throw IllegalNodeStateException() + else -> throw IllegalStateException() } } } - throw ImpossibleCaseException() + throw IllegalStateException() } private fun afterInsert(node: RBNode): RBNode { @@ -130,7 +127,7 @@ class RBBalancer>(private var root: RBNode?): Balan val uncle = getUncle(currentNode) if (uncle?.color == Markers.RED) { currentNode.parent?.color = Markers.BLACK - currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() + currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK } @@ -145,7 +142,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 +151,13 @@ class RBBalancer>(private var root: RBNode?): Balan getRoot(parent) } - else -> getRoot(node ?: throw IllegalBaseNodeException()) + else -> getRoot(node ?: throw NullPointerException() /* IllegalNodeStateException() */) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw IllegalBaseNodeException() + var brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() if (brother.color == Markers.RED) throw NullPointerException() @@ -187,7 +184,7 @@ class RBBalancer>(private var root: RBNode?): Balan brother.color = Markers.RED } else { - throw NullPointerException() + throw IllegalStateException() } } parent.right -> @@ -206,17 +203,17 @@ class RBBalancer>(private var root: RBNode?): Balan brother.right?.color = Markers.BLACK } else { - throw NullPointerException() + 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 NullPointerException() // IllegalNodeStateException() when (brother.color) { Markers.RED -> thirdCaseSubFirst(brother, parent) Markers.BLACK -> thirdCaseSubSecond(brother, parent) @@ -228,7 +225,7 @@ class RBBalancer>(private var root: RBNode?): Balan when (brother) { brother.parent?.left -> { - var rightBrotherSon = brother.right ?: throw NullPointerException() + var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED @@ -244,7 +241,7 @@ class RBBalancer>(private var root: RBNode?): Balan rightBrotherSon.color = Markers.RED leftRotate(rightBrotherSon) - rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() + rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -260,14 +257,14 @@ class RBBalancer>(private var root: RBNode?): Balan 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 NullPointerException()) // IllegalNodeStateException() return } if (leftBrotherSon.left?.color == Markers.RED) { rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED - leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() + leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } @@ -277,7 +274,7 @@ class RBBalancer>(private var root: RBNode?): Balan leftRotate(parent) } } - else -> throw NullPointerException() + else -> throw IllegalStateException() } } @@ -315,7 +312,7 @@ class RBBalancer>(private var root: RBNode?): Balan } } - else -> throw NullPointerException() + else -> throw IllegalStateException() } } From 19980973101302b5b78d7ea9c0e90bfd4b5cf6c3 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 14:37:18 +0300 Subject: [PATCH 081/212] feat: Implement neo4j database sketch --- lib/build.gradle.kts | 12 +- lib/src/main/kotlin/CONTAINER.conf | 3 + lib/src/main/kotlin/DataBase.kt | 172 ++++++++++++++++++++++++ lib/src/main/kotlin/testNeo4j.sh | 15 +++ lib/src/main/kotlin/treelib/DBNodeRB.kt | 14 ++ settings.gradle.kts | 2 +- 6 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 lib/src/main/kotlin/CONTAINER.conf create mode 100644 lib/src/main/kotlin/DataBase.kt create mode 100644 lib/src/main/kotlin/testNeo4j.sh create mode 100644 lib/src/main/kotlin/treelib/DBNodeRB.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 99f9538..0f9f88e 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -19,15 +19,19 @@ repositories { } dependencies { - testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") + api("org.apache.commons:commons-math3:3.6.1") + implementation("com.google.guava:guava:31.1-jre") + + val neo4jCore = "4.0.5" + implementation("org.neo4j", "neo4j-ogm-core", neo4jCore) + implementation("org.neo4j", "neo4j-ogm-bolt-driver", neo4jCore) + 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") + implementation(kotlin("stdlib-jdk8")) } - tasks.test { finalizedBy(tasks.jacocoTestReport) useJUnitPlatform() diff --git a/lib/src/main/kotlin/CONTAINER.conf b/lib/src/main/kotlin/CONTAINER.conf new file mode 100644 index 0000000..9b9fb0a --- /dev/null +++ b/lib/src/main/kotlin/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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt new file mode 100644 index 0000000..ddc72a3 --- /dev/null +++ b/lib/src/main/kotlin/DataBase.kt @@ -0,0 +1,172 @@ + +import org.neo4j.driver.AuthTokens +import org.neo4j.driver.Driver +import org.neo4j.driver.GraphDatabase +import org.neo4j.driver.exceptions.SessionExpiredException +import treelib.DBNodeRB +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() + } + + } + + // я наверное смогу получить рут, используя фильтр что-то вроде: на данный узел не указывает ни один другой узел с отношением [left_child] / [right_child] + + fun , V> saveChanges(preOrder: Array>, inOrder: Array>) { + + /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ + val session = driver?.session() ?: throw IOException() + + var inOrderIndex = 0 + var preOrderIndex = 0 + val set = HashSet>() + val stack = LinkedList>() + + while (preOrderIndex in preOrder.indices) { + do { + val currentNode = preOrder[preOrderIndex] + if (preOrderIndex == 0) { + session.executeWrite { tx -> + tx.run( + "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", + mutableMapOf( + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + if (!stack.isEmpty()) { + if ( set.contains( stack.peek() ) ) { + set.remove(stack.peek()) + val parentNode = stack.pop() + session.executeWrite {tx -> + 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}) " + + "MERGE (parent)-[:RIGHT_SON]->(son)", + mutableMapOf( + "parentNodeValue" to parentNode.value, + "parentNodeKey" to parentNode.key, + "parentNodeColor" to parentNode.color.toString(), + "parentNodeX" to parentNode.x, + "parentNodeY" to parentNode.y, + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + else { + val parentNode = stack.peek() + session.executeWrite {tx -> + 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}) " + + "MERGE (parent)-[:LEFT_SON]->(son)", + mutableMapOf( + "parentNodeValue" to parentNode.value, + "parentNodeKey" to parentNode.key, + "parentNodeColor" to parentNode.color.toString(), + "parentNodeX" to parentNode.x, + "parentNodeY" to parentNode.y, + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + } + stack.push(currentNode) + } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) + + var currentNode: DBNodeRB? = null + + while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().key == inOrder[inOrderIndex].key) { + currentNode = stack.pop() + ++inOrderIndex + } + + if (currentNode != null) { + set.add(currentNode) + stack.push(currentNode) + } + + } + + session.close() + } + + // Это скорее всего будем запускать при открытии приложения, нужно будет сразу восстановить дерево + + fun exportRBtree() { + + 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 ").list() + .map{ DBNodeRB( + value = it.values().get(0).toString(), + key = it.values().get(1).toString(), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(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{ DBNodeRB( + value = it.values().get(0).toString(), + key = it.values().get(1).toString(), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(4).toString().toDouble()) + } + } + + session.close() + + } + + override fun close() { + driver?.close() + } +} + +// neo4j-admin, backup, restore +/* +у меня есть вершины и ребра между ними, надо уметь сохранять дерево в базе, но как? +вопрос: когда заносить изменения в бд и как это реализовывать? +надо еще подумать над оптимизацией, те чтобы не пересобирать дерево в бд каждый раз, когда мы добавили за сессию всего один узел + +root = tx.run("MATCH (parent: Node)-->(son: Node) " + + "WHERE NOT ()-->(parent) " + + "RETURN parent.value, parent.key, parent.color, parent.x, parent.y").list().map {it.values()}.get(0) + */ diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/testNeo4j.sh new file mode 100644 index 0000000..97ad8db --- /dev/null +++ b/lib/src/main/kotlin/testNeo4j.sh @@ -0,0 +1,15 @@ + +BASEDIR=$(realpath "$(dirname "$0")") + +. "${BASEDIR}/CONTAINER.conf" + +docker run \ + --rm \ + --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 \ + +#docker stop neo4j:latest diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt new file mode 100644 index 0000000..d048930 --- /dev/null +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -0,0 +1,14 @@ +package treelib + +import treelib.singleObjects.Markers + + +class DBNodeRB, V>(val value: V, + val key: K, + val color: Markers = Markers.RED, + val x: Double = 0.0, + val y: Double = 0.0) { + + + +} 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") From ad94694da73585be3e768e78ddeb68516353cddd Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 19:32:32 +0300 Subject: [PATCH 082/212] feat: Implement initialize tree from DB --- lib/src/main/kotlin/Controller.kt | 50 +++++++++++++++++ lib/src/main/kotlin/DataBase.kt | 55 +++++++++---------- lib/src/main/kotlin/treelib/DBNodeRB.kt | 9 +-- lib/src/main/kotlin/treelib/Main.kt | 39 +++++++++++++ .../main/kotlin/treelib/abstractTree/Tree.kt | 12 ++-- 5 files changed, 124 insertions(+), 41 deletions(-) create mode 100644 lib/src/main/kotlin/Controller.kt create mode 100644 lib/src/main/kotlin/treelib/Main.kt diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/Controller.kt new file mode 100644 index 0000000..cc4e699 --- /dev/null +++ b/lib/src/main/kotlin/Controller.kt @@ -0,0 +1,50 @@ +import treelib.DBNodeRB +import treelib.rbTree.RBStruct +import treelib.rbTree.RBTree +import treelib.singleObjects.Container + +class Controller { + + fun initTree() { + 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.restoreTreeFromDatabase(orders.first, orders.second) + } + + fun saveTree() { + val tree = RBTree() + tree.putItem(Pair(25, 1)) + tree.putItem(Pair(15, 1)) + tree.putItem(Pair(50, 1)) + tree.putItem(Pair(10, 1)) + tree.putItem(Pair(22, 1)) + tree.putItem(Pair(35, 1)) + tree.putItem(Pair(70, 1)) + tree.putItem(Pair(4, 1)) + tree.putItem(Pair(12, 1)) + tree.putItem(Pair(18, 1)) + tree.putItem(Pair(24, 1)) + tree.putItem(Pair(31, 1)) + tree.putItem(Pair(44, 1)) + tree.putItem(Pair(66, 1)) + tree.putItem(Pair(90, 1)) + + val neo4jDB = Neo4jRepository() + neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + + val preOrder = tree.preOrder().map { DBNodeRB(it.value, it.color) } + val inOrder = tree.inOrder().map { DBNodeRB(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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index ddc72a3..3e94aac 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -4,6 +4,7 @@ import org.neo4j.driver.Driver import org.neo4j.driver.GraphDatabase import org.neo4j.driver.exceptions.SessionExpiredException import treelib.DBNodeRB +import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable import java.io.IOException @@ -21,31 +22,29 @@ class Neo4jRepository: Closeable { } catch(ex: SessionExpiredException) { throw IOException() } - } - // я наверное смогу получить рут, используя фильтр что-то вроде: на данный узел не указывает ни один другой узел с отношением [left_child] / [right_child] - - fun , V> saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() var inOrderIndex = 0 var preOrderIndex = 0 - val set = HashSet>() - val stack = LinkedList>() + val set = HashSet>() + val stack = LinkedList>() while (preOrderIndex in preOrder.indices) { do { val currentNode = preOrder[preOrderIndex] + currentNode.value as Container<*, *> if (preOrderIndex == 0) { session.executeWrite { tx -> tx.run( "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", mutableMapOf( - "nodeValue" to currentNode.value, - "nodeKey" to currentNode.key, + "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 @@ -57,19 +56,20 @@ class Neo4jRepository: Closeable { if ( set.contains( stack.peek() ) ) { set.remove(stack.peek()) val parentNode = stack.pop() + parentNode.value as Container<*, *> session.executeWrite {tx -> 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}) " + "MERGE (parent)-[:RIGHT_SON]->(son)", mutableMapOf( - "parentNodeValue" to parentNode.value, - "parentNodeKey" to parentNode.key, + "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, - "nodeKey" to currentNode.key, + "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 @@ -79,19 +79,20 @@ class Neo4jRepository: Closeable { } else { val parentNode = stack.peek() + parentNode.value as Container<*, *> session.executeWrite {tx -> 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}) " + "MERGE (parent)-[:LEFT_SON]->(son)", mutableMapOf( - "parentNodeValue" to parentNode.value, - "parentNodeKey" to parentNode.key, + "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, - "nodeKey" to currentNode.key, + "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 @@ -103,9 +104,9 @@ class Neo4jRepository: Closeable { stack.push(currentNode) } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) - var currentNode: DBNodeRB? = null + var currentNode: DBNodeRB? = null - while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().key == inOrder[inOrderIndex].key) { + while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() ++inOrderIndex } @@ -120,20 +121,17 @@ class Neo4jRepository: Closeable { session.close() } - // Это скорее всего будем запускать при открытии приложения, нужно будет сразу восстановить дерево - - fun exportRBtree() { + fun exportRBtree(): Pair>>>, List>>>>{ val session = driver?.session() ?: throw IOException() - var preOrder: List>> = listOf() - var inOrder: List>> = listOf() + 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 ").list() .map{ DBNodeRB( - value = it.values().get(0).toString(), - key = it.values().get(1).toString(), + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, x = it.values().get(3).toString().toDouble(), y = it.values().get(4).toString().toDouble()) @@ -143,16 +141,17 @@ class Neo4jRepository: Closeable { "RETURN node.value, node.key, node.color, node.x, node.y " + "ORDER BY node.key").list() .map{ DBNodeRB( - value = it.values().get(0).toString(), - key = it.values().get(1).toString(), + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, x = it.values().get(3).toString().toDouble(), y = it.values().get(4).toString().toDouble()) } } - + session.close() + return Pair(preOrder, inOrder) + } override fun close() { diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt index d048930..b28228f 100644 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -3,12 +3,7 @@ package treelib import treelib.singleObjects.Markers -class DBNodeRB, V>(val value: V, - val key: K, +class DBNodeRB>(val value: Pack, val color: Markers = Markers.RED, val x: Double = 0.0, - val y: Double = 0.0) { - - - -} + val y: Double = 0.0) diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt new file mode 100644 index 0000000..0aeb3e0 --- /dev/null +++ b/lib/src/main/kotlin/treelib/Main.kt @@ -0,0 +1,39 @@ +package treelib + +import Controller + + +fun main() { + // 25, 15, 10, 4, 12, 22, 18, 24, 50, 35, 31, 44, 70, 66, 90 + // 4, 10, 12, 15, 18, 22, 24, 25, 31, 35, 44, 50, 66, 70, 90 + + /* + + val neo4jRep = Neo4jRepository() + val a1 = DBNodeRB(Container(Pair(1, 25))) + val a2 = DBNodeRB(Container(Pair(1, 15))) + val a3 = DBNodeRB(Container(Pair(1, 10))) + val a4 = DBNodeRB(Container(Pair(1, 4))) + val a5 = DBNodeRB(Container(Pair(1, 12))) + val a6 = DBNodeRB(Container(Pair(1, 22))) + val a7 = DBNodeRB(Container(Pair(1, 18))) + val a8 = DBNodeRB(Container(Pair(1, 24))) + val a9 = DBNodeRB(Container(Pair(1, 50))) + val a10 = DBNodeRB(Container(Pair(1, 35))) + val a11 = DBNodeRB(Container(Pair(1, 31))) + val a12 = DBNodeRB(Container(Pair(1, 44))) + val a13 = DBNodeRB(Container(Pair(1, 70))) + val a14 = DBNodeRB(Container(Pair(1, 66))) + val a15 = DBNodeRB(Container(Pair(1, 90))) + val preArr = arrayOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) + val inArr = arrayOf(a4, a3, a5, a2, a7, a6, a8, a1, a11, a10, a12, a9, a14, a13, a15) + neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") + neo4jRep.saveChanges(preArr, inArr) + */ + val controller = Controller() + controller.saveTree() + + //neo4jRep.exportRBtree() + + //neo4jRep.close() +} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index b64cca1..1607caa 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -33,15 +33,15 @@ abstract class Tree< 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) 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()) } From 42fca1200f72bd355d9b75558209b6a5e26f895e Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 20:21:27 +0300 Subject: [PATCH 083/212] fix: Fix adding to database (neo4j) --- lib/src/main/kotlin/DataBase.kt | 82 ++++++++++-------- lib/src/main/kotlin/treelib/DBNodeRB.kt | 10 ++- .../kotlin/treelib/avlTree/AVLBalancer.kt | 15 ++-- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 85 ++++++++++--------- .../main/kotlin/treelib/rbTree/RBStruct.kt | 63 ++++++++++++-- 5 files changed, 161 insertions(+), 94 deletions(-) diff --git a/lib/src/main/kotlin/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index 3e94aac..e7ca4c6 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -1,4 +1,3 @@ - import org.neo4j.driver.AuthTokens import org.neo4j.driver.Driver import org.neo4j.driver.GraphDatabase @@ -10,21 +9,21 @@ import java.io.Closeable import java.io.IOException import java.util.* -class Neo4jRepository: Closeable { +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) { + } catch (ex: IllegalArgumentException) { + throw IOException() + } catch (ex: SessionExpiredException) { throw IOException() } } - fun > saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() @@ -53,15 +52,16 @@ class Neo4jRepository: Closeable { } } if (!stack.isEmpty()) { - if ( set.contains( stack.peek() ) ) { + if (set.contains(stack.peek())) { set.remove(stack.peek()) val parentNode = stack.pop() parentNode.value as Container<*, *> - session.executeWrite {tx -> - 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}) " + - "MERGE (parent)-[:RIGHT_SON]->(son)", + session.executeWrite { tx -> + 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}) " + + "MERGE (parent)-[:RIGHT_SON]->(son)", mutableMapOf( "parentNodeValue" to parentNode.value.pair.second, "parentNodeKey" to parentNode.value.pair.first, @@ -76,15 +76,15 @@ class Neo4jRepository: Closeable { ) ) } - } - else { + } else { val parentNode = stack.peek() parentNode.value as Container<*, *> - session.executeWrite {tx -> - 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}) " + - "MERGE (parent)-[:LEFT_SON]->(son)", + session.executeWrite { tx -> + 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}) " + + "MERGE (parent)-[:LEFT_SON]->(son)", mutableMapOf( "parentNodeValue" to parentNode.value.pair.second, "parentNodeKey" to parentNode.value.pair.first, @@ -106,7 +106,7 @@ class Neo4jRepository: Closeable { var currentNode: DBNodeRB? = null - while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { + while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() ++inOrderIndex } @@ -121,30 +121,38 @@ class Neo4jRepository: Closeable { session.close() } - fun exportRBtree(): Pair>>>, List>>>>{ + fun exportRBtree(): Pair>>>, List>>>> { 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 ").list() - .map{ DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble()) + session.executeRead { tx -> + preOrder = tx.run( + "MATCH (node: Node) " + + "RETURN node.value, node.key, node.color, node.x, node.y " + ).list() + .map { + DBNodeRB( + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(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{ DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(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 { + DBNodeRB( + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(4).toString().toDouble() + ) } } diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt index b28228f..cf067e1 100644 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -3,7 +3,9 @@ package treelib import treelib.singleObjects.Markers -class DBNodeRB>(val value: Pack, - val color: Markers = Markers.RED, - val x: Double = 0.0, - val y: Double = 0.0) +class DBNodeRB>( + val value: Pack, + val color: Markers = Markers.RED, + val x: Double = 0.0, + val y: Double = 0.0 +) diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 5f80a87..1a3afb9 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -2,7 +2,8 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancerNoParent -class AVLBalancer>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { +class AVLBalancer>(private var root: AVLNode?) : + BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { return (getHeight(node?.left) - getHeight(node?.right)).toInt() } @@ -13,14 +14,16 @@ class AVLBalancer>(private var root: AVLNode?): Bal private fun updateHeight(currentNode: AVLNode?) { if (currentNode != null) - currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right))+1u + currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u } override fun balance(stateContainer: AVLStateContainer): AVLNode { - val node = stateContainer.contentNode ?: throw IllegalStateException("") // IllegalBaseNodeException("A non-existent node (null) was passed to the method") + 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) { @@ -34,7 +37,8 @@ class AVLBalancer>(private var root: AVLNode?): Bal val balance = updateBalance(currentNode) if (balance == -2) { if (updateBalance(currentNode.right) == 1) { - currentNode.right = currentNode.right?.let { rightRotate(it) } ?: throw NullPointerException() // IllegalNodeStateException() + currentNode.right = currentNode.right?.let { rightRotate(it) } + ?: throw NullPointerException() // IllegalNodeStateException() updateHeight(currentNode.right?.right) } val balancedNode = leftRotate(currentNode) @@ -44,7 +48,8 @@ class AVLBalancer>(private var root: AVLNode?): Bal } if (balance == 2) { if (updateBalance(currentNode.left) == -1) { - currentNode.left = currentNode.left?.let { leftRotate(it) } ?: throw NullPointerException() // IllegalNodeStateException("There is no node required by the condition of the algorithm") + 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) diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 67e5427..211e007 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -3,7 +3,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent import treelib.singleObjects.Markers -class RBBalancer>(private var root: RBNode?): BalancerParent, RBStateContainer>() { +class RBBalancer>(private var root: RBNode?) : + BalancerParent, RBStateContainer>() { init { root?.color = Markers.BLACK @@ -39,14 +40,13 @@ class RBBalancer>(private var root: RBNode?): Balan return node.right == null && node.left == null } - override fun balance(stateContainer: RBStateContainer): RBNode { - val node = stateContainer.contentNode ?: - throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") + override fun balance(stateContainer: RBStateContainer): RBNode { + val node = stateContainer.contentNode + ?: throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") 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) { @@ -56,30 +56,35 @@ class RBBalancer>(private var root: RBNode?): Balan return getRoot(currentNode) } - var parent = currentNode.parent ?: throw IllegalStateException() // в данном случае родитель не может быть null + 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 IllegalStateException() // IllegalNodeStateException() + parent = + currentNode.parent?.parent ?: throw IllegalStateException() // 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 NullPointerException() // IllegalNodeStateException() + parent = + currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED currentNode.left?.color = Markers.RED } + else -> throw IllegalStateException() // невозможное условие выполнения } if (currentNode.parent == null) @@ -87,21 +92,21 @@ class RBBalancer>(private var root: RBNode?): Balan 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) || @@ -110,10 +115,11 @@ 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 IllegalStateException() } } @@ -130,8 +136,7 @@ class RBBalancer>(private var root: RBNode?): Balan currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK - } - else if(uncle != null){ + } else if (uncle != null) { return currentNode } } @@ -168,45 +173,41 @@ 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 { + } 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 { + } else { throw IllegalStateException() } } + else -> throw IllegalStateException() } } @@ -223,8 +224,7 @@ class RBBalancer>(private var root: RBNode?): Balan /** black parent and red brother **/ private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { - brother.parent?.left -> - { + brother.parent?.left -> { var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { @@ -241,7 +241,8 @@ class RBBalancer>(private var root: RBNode?): Balan rightBrotherSon.color = Markers.RED leftRotate(rightBrotherSon) - rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + rightBrotherSon = + rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -251,8 +252,8 @@ 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 @@ -264,7 +265,8 @@ class RBBalancer>(private var root: RBNode?): Balan if (leftBrotherSon.left?.color == Markers.RED) { rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED - leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + leftBrotherSon = + leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } @@ -274,6 +276,7 @@ class RBBalancer>(private var root: RBNode?): Balan leftRotate(parent) } } + else -> throw IllegalStateException() } } @@ -289,29 +292,27 @@ 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 IllegalStateException() } } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 67ffc27..ee6a216 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -1,9 +1,11 @@ package treelib.rbTree +import treelib.DBNodeRB 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>() { @@ -15,17 +17,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(insertNode) + ): RBStateContainer = RBStateContainer(insertNode) override fun generateStateFind( findNode: RBNode?, contentNode: RBNode?, - ): RBStateContainer = RBStateContainer(findNode) + ): RBStateContainer = RBStateContainer(findNode) override fun connectUnlinkedSubTreeWithParent( node: RBNode, @@ -39,11 +41,12 @@ 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 { @@ -62,12 +65,60 @@ class RBStruct> : root?.let { it.color = Markers.BLACK } ?: throw MultithreadingException(ImpossibleCaseException()) - } - else { + } else { if (node.value > parent.value) parent.right = node else parent.left = node node.parent = parent } return node } + + fun restoreTreeFromDatabase(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: DBNodeRB + + 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: DBNodeRB): RBNode { + val node = RBNode(value = drawNode.value, color = drawNode.color) + return node + } } From 1e7f2a8151abe81d3a34958ec5a99150c97a8735 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 21:33:22 +0300 Subject: [PATCH 084/212] fix: Fix database interaction --- lib/src/main/kotlin/Controller.kt | 35 +--- lib/src/main/kotlin/DataBase.kt | 186 ++++++++++-------- lib/src/main/kotlin/initNeo4j.sh | 0 lib/src/main/kotlin/loadNeo4j.sh | 8 + lib/src/main/kotlin/testNeo4j.sh | 13 +- lib/src/main/kotlin/treelib/Main.kt | 29 ++- .../main/kotlin/treelib/abstractTree/Tree.kt | 37 ++-- .../kotlin/treelib/abstractTree/TreeStruct.kt | 39 ++-- .../kotlin/treelib/abstractTree/Vertex.kt | 5 + .../balanced/BalancedTreeStruct.kt | 4 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 6 +- .../main/kotlin/treelib/avlTree/AVLTree.kt | 10 +- .../main/kotlin/treelib/avlTree/AVLVertex.kt | 8 + .../main/kotlin/treelib/binTree/BINStruct.kt | 6 +- .../main/kotlin/treelib/binTree/BINTree.kt | 10 +- .../main/kotlin/treelib/binTree/BINVertex.kt | 5 + .../kotlin/treelib/rbTree/DrawRBVertex.kt | 10 + .../main/kotlin/treelib/rbTree/RBBalancer.kt | 24 +-- .../main/kotlin/treelib/rbTree/RBStruct.kt | 13 +- lib/src/main/kotlin/treelib/rbTree/RBTree.kt | 10 +- .../main/kotlin/treelib/rbTree/RBVertex.kt | 9 + 21 files changed, 292 insertions(+), 175 deletions(-) create mode 100644 lib/src/main/kotlin/initNeo4j.sh create mode 100644 lib/src/main/kotlin/loadNeo4j.sh create mode 100644 lib/src/main/kotlin/treelib/abstractTree/Vertex.kt create mode 100644 lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt create mode 100644 lib/src/main/kotlin/treelib/binTree/BINVertex.kt create mode 100644 lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt create mode 100644 lib/src/main/kotlin/treelib/rbTree/RBVertex.kt diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/Controller.kt index cc4e699..a59a3b0 100644 --- a/lib/src/main/kotlin/Controller.kt +++ b/lib/src/main/kotlin/Controller.kt @@ -1,6 +1,6 @@ -import treelib.DBNodeRB + +import treelib.rbTree.DrawRBVertex import treelib.rbTree.RBStruct -import treelib.rbTree.RBTree import treelib.singleObjects.Container class Controller { @@ -8,43 +8,28 @@ class Controller { fun initTree() { val neo4jDB = Neo4jRepository() neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + /*** orders.first = preOrder, orders.second = inOrder ***/ - val orders: Pair>>>, List>>>> = + val orders: Pair>>>, List>>>> = neo4jDB.exportRBtree() val RBtree = RBStruct>>() RBtree.restoreTreeFromDatabase(orders.first, orders.second) + neo4jDB.close() } - fun saveTree() { - val tree = RBTree() - tree.putItem(Pair(25, 1)) - tree.putItem(Pair(15, 1)) - tree.putItem(Pair(50, 1)) - tree.putItem(Pair(10, 1)) - tree.putItem(Pair(22, 1)) - tree.putItem(Pair(35, 1)) - tree.putItem(Pair(70, 1)) - tree.putItem(Pair(4, 1)) - tree.putItem(Pair(12, 1)) - tree.putItem(Pair(18, 1)) - tree.putItem(Pair(24, 1)) - tree.putItem(Pair(31, 1)) - tree.putItem(Pair(44, 1)) - tree.putItem(Pair(66, 1)) - tree.putItem(Pair(90, 1)) - + fun > saveTree(tree: RBStruct) { val neo4jDB = Neo4jRepository() neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") - val preOrder = tree.preOrder().map { DBNodeRB(it.value, it.color) } - val inOrder = tree.inOrder().map { DBNodeRB(it.value, it.color) } + // вот тут плохо, потому что тут надо получать не base nodes, а для рисовалки - neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray()) + 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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index e7ca4c6..809eb64 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -1,8 +1,9 @@ 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.DBNodeRB +import treelib.rbTree.DrawRBVertex import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable @@ -23,88 +24,50 @@ class Neo4jRepository : Closeable { } } - fun > saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() var inOrderIndex = 0 var preOrderIndex = 0 - val set = HashSet>() - val stack = LinkedList>() + val set = HashSet>() + val stack = LinkedList>() + var id = 0 while (preOrderIndex in preOrder.indices) { do { val currentNode = preOrder[preOrderIndex] - currentNode.value as Container<*, *> + //currentNode.value as Container<*, *> if (preOrderIndex == 0) { session.executeWrite { tx -> - tx.run( - "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", - mutableMapOf( - "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 - ) - ) + 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<*, *> + //parentNode.value as Container<*, *> session.executeWrite { tx -> - 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}) " + - "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 - ) - ) + createRightSon(tx, parentNode, currentNode, id) } + ++id } else { val parentNode = stack.peek() parentNode.value as Container<*, *> session.executeWrite { tx -> - 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}) " + - "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 - ) - ) + createLeftSon(tx, parentNode, currentNode, id) } + ++id } } stack.push(currentNode) - } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) + } while (preOrder[preOrderIndex++].value != inOrder[inOrderIndex].value && preOrderIndex < preOrder.size) - var currentNode: DBNodeRB? = null + var currentNode: DrawRBVertex? = null while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() @@ -121,23 +84,26 @@ class Neo4jRepository : Closeable { session.close() } - fun exportRBtree(): Pair>>>, List>>>> { + fun exportRBtree(): Pair>>>, List>>>> { + + /*** Вот тут короче надо обращаться к neo4j с темЮ чтобы она инициализировала себя как-то ***/ val session = driver?.session() ?: throw IOException() - var preOrder: List>>> = listOf() - var inOrder: List>>> = listOf() + 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 " + "RETURN node.value, node.key, node.color, node.x, node.y " + + "ORDER BY node.id" ).list() .map { - DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble() + 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() ) } @@ -147,11 +113,11 @@ class Neo4jRepository : Closeable { "ORDER BY node.key" ).list() .map { - DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble() + 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() ) } } @@ -162,18 +128,80 @@ class Neo4jRepository : Closeable { } + 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() } } - -// neo4j-admin, backup, restore -/* -у меня есть вершины и ребра между ними, надо уметь сохранять дерево в базе, но как? -вопрос: когда заносить изменения в бд и как это реализовывать? -надо еще подумать над оптимизацией, те чтобы не пересобирать дерево в бд каждый раз, когда мы добавили за сессию всего один узел - -root = tx.run("MATCH (parent: Node)-->(son: Node) " + - "WHERE NOT ()-->(parent) " + - "RETURN parent.value, parent.key, parent.color, parent.x, parent.y").list().map {it.values()}.get(0) - */ diff --git a/lib/src/main/kotlin/initNeo4j.sh b/lib/src/main/kotlin/initNeo4j.sh new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/main/kotlin/loadNeo4j.sh b/lib/src/main/kotlin/loadNeo4j.sh new file mode 100644 index 0000000..d4d9fcd --- /dev/null +++ b/lib/src/main/kotlin/loadNeo4j.sh @@ -0,0 +1,8 @@ + +BASEDIR=$(realpath "$(dirname "$0")") + +. "${BASEDIR}/CONTAINER.conf" + +docker stop "$CONTAINER_NAME" + +neo4j-admin dump --database=neo4j --to=/data/backups/ \ No newline at end of file diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/testNeo4j.sh index 97ad8db..87b69c4 100644 --- a/lib/src/main/kotlin/testNeo4j.sh +++ b/lib/src/main/kotlin/testNeo4j.sh @@ -3,13 +3,22 @@ BASEDIR=$(realpath "$(dirname "$0")") . "${BASEDIR}/CONTAINER.conf" +# -d docker run \ - --rm \ - --name "CONTAINER_NAME" \ + -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 \ +# -c /bin/bash +#neo4j-admin database dump neo4j --to-path=$HOME/neo4j/data:/data + +#docker ps -a + +#docker stop "$CONTAINER_NAME" + +#cp $HOME/neo4j/data:/data $HOME/ #docker stop neo4j:latest diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt index 0aeb3e0..15e0eab 100644 --- a/lib/src/main/kotlin/treelib/Main.kt +++ b/lib/src/main/kotlin/treelib/Main.kt @@ -1,6 +1,8 @@ package treelib import Controller +import treelib.rbTree.RBStruct +import treelib.singleObjects.Container fun main() { @@ -30,10 +32,35 @@ fun main() { neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") neo4jRep.saveChanges(preArr, inArr) */ + + val tree = RBStruct>() + tree.insert(Container(Pair(25 , 1))) + tree.insert(Container(Pair(15 , 1))) + tree.insert(Container(Pair(50 , 1))) + tree.insert(Container(Pair(10 , 1))) + tree.insert(Container(Pair(22 , 1))) + tree.insert(Container(Pair(35 , 1))) + tree.insert(Container(Pair(70 , 1))) + tree.insert(Container(Pair(4 , 1))) + tree.insert(Container(Pair(12 , 1))) + tree.insert(Container(Pair(18 , 1))) + tree.insert(Container(Pair(24 , 1))) + tree.insert(Container(Pair(31 , 1))) + tree.insert(Container(Pair(44 , 1))) + tree.insert(Container(Pair(66 , 1))) + tree.insert(Container(Pair(90 , 1))) val controller = Controller() - controller.saveTree() + controller.saveTree(tree) + tree.insert(Container(Pair(100, 1))) + controller.saveTree(tree) + controller.initTree() //neo4jRep.exportRBtree() //neo4jRep.close() +} + +fun test() { + val cont = Controller() + cont.initTree() } \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index 1607caa..1116ad6 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) + 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 5b96f98..c91c6ed 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -9,7 +9,8 @@ import treelib.singleObjects.exceptions.NonExistentValueException abstract class TreeStruct< Pack : Comparable, NodeType : Node, - State : StateContainer + State : StateContainer, + VertexType: Vertex > { protected abstract var root: NodeType? @@ -163,8 +164,8 @@ abstract class TreeStruct< linkNewNode(currentNode, parentNode) - if (parentNode == null) return generateStateInsert(currentNode, currentNode) - else return generateStateInsert(currentNode, parentNode) + if (parentNode != null) return generateStateInsert(currentNode, parentNode) + else return generateStateInsert(currentNode, currentNode) } updateNode.value = item @@ -260,8 +261,8 @@ abstract class TreeStruct< fun find(obj: Pack): Pack? = findItem(obj).contentNode?.value - fun inOrder(): List { - val arrayNodes = mutableListOf() + fun inOrder(): List { + val arrayNodes = mutableListOf() var flagVisited = 0 var current = root val parents = ArrayDeque() @@ -278,24 +279,26 @@ abstract class TreeStruct< } } current?.let { - arrayNodes.add(it.value) + arrayNodes.add(it) if (it.right != null) { flagVisited = 0 current = it.right } else { if (parents.isEmpty()) - return@inOrder arrayNodes + return@inOrder arrayNodes.map {toVertex(it)} flagVisited = 1 current = parents.removeLast() } } } - return arrayNodes + return arrayNodes.map{toVertex(it)} } - fun postOrder(): List { + abstract fun toVertex(node: NodeType): VertexType + + fun postOrder(): List { val parents = ArrayDeque() - val arrayNodes = mutableListOf() + val arrayNodes = mutableListOf() var flagVisited = 0 var current = root @@ -316,9 +319,9 @@ abstract class TreeStruct< current = it.right flagVisited = 0 } else { - arrayNodes.add(it.value) + arrayNodes.add(it) if (parents.isEmpty()) - return@postOrder arrayNodes + return@postOrder arrayNodes.map{toVertex(it)} val parent = parents.removeLast() if (parent.right == it) { flagVisited = 2 @@ -327,11 +330,11 @@ abstract class TreeStruct< } } ?: throw MultithreadingException(ImpossibleCaseException()) } - return arrayNodes + return arrayNodes.map{toVertex(it)} } - fun preOrder(): List { - val arrayNodes = mutableListOf() + fun preOrder(): List { + val arrayNodes = mutableListOf() var current: NodeType val queue = ArrayDeque() @@ -339,7 +342,7 @@ abstract class TreeStruct< queue.add(root) while (queue.isNotEmpty()) { current = queue.removeLast() - arrayNodes.add(current.value) + arrayNodes.add(current) if (current.right != null) current.right?.let { queue.add(it) @@ -351,6 +354,8 @@ abstract class TreeStruct< } ?: throw MultithreadingException(ImpossibleCaseException()) } } - return arrayNodes + return arrayNodes.map {toVertex(it)} } + + } diff --git a/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt b/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt new file mode 100644 index 0000000..abbb7a5 --- /dev/null +++ b/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt @@ -0,0 +1,5 @@ +package treelib.abstractTree + +abstract class Vertex>{ + 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..96e8458 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt @@ -3,13 +3,15 @@ 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 diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index f275439..7893341 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -3,7 +3,7 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancedTreeStruct class AVLStruct> : - BalancedTreeStruct, AVLStateContainer, AVLBalancer>() { + BalancedTreeStruct, AVLStateContainer, AVLVertex, AVLBalancer>() { override var root: AVLNode? = null override val balancer = AVLBalancer(root) @@ -40,6 +40,10 @@ class AVLStruct> : } } + override fun toVertex(node: AVLNode): AVLVertex { + return AVLVertex(node.value, node.height) + } + override fun createNode(item: Pack): AVLNode = AVLNode(item) override fun getNodeKernel(node: AVLNode): AVLNode = AVLNode(node.value, height = node.height) 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..a43ea26 --- /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() \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index e555924..f3bfc09 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -3,7 +3,7 @@ package treelib.binTree import treelib.abstractTree.TreeStruct class BINStruct> : - TreeStruct, BINStateContainer>() { + TreeStruct, BINStateContainer, BINVertex>() { override var root: BINNode? = null @@ -53,6 +53,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) { 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..e660d8c --- /dev/null +++ b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt @@ -0,0 +1,5 @@ +package treelib.binTree + +import treelib.abstractTree.Vertex + +class BINVertex>(override val value: Pack): Vertex() \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt b/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt new file mode 100644 index 0000000..b3ee499 --- /dev/null +++ b/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt @@ -0,0 +1,10 @@ +package treelib.rbTree + +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/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 211e007..c8c4cf6 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -2,6 +2,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent import treelib.singleObjects.Markers +import treelib.singleObjects.exceptions.IllegalBaseNodeException +import treelib.singleObjects.exceptions.IllegalNodeStateException class RBBalancer>(private var root: RBNode?) : BalancerParent, RBStateContainer>() { @@ -42,7 +44,7 @@ class RBBalancer>(private var root: RBNode?) : override fun balance(stateContainer: RBStateContainer): RBNode { val node = stateContainer.contentNode - ?: throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") + ?: throw IllegalBaseNodeException() val uncle = getUncle(node) when { /** node insertion case **/ @@ -65,7 +67,7 @@ class RBBalancer>(private var root: RBNode?) : currentNode = parent } parent = - currentNode.parent?.parent ?: throw IllegalStateException() // IllegalNodeStateException() + currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -78,7 +80,7 @@ class RBBalancer>(private var root: RBNode?) : currentNode = parent } parent = - currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() + currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -133,7 +135,7 @@ class RBBalancer>(private var root: RBNode?) : val uncle = getUncle(currentNode) if (uncle?.color == Markers.RED) { currentNode.parent?.color = Markers.BLACK - currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() + currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK } else if (uncle != null) { @@ -156,13 +158,13 @@ class RBBalancer>(private var root: RBNode?) : getRoot(parent) } - else -> getRoot(node ?: throw NullPointerException() /* IllegalNodeStateException() */) + else -> getRoot(node ?: throw IllegalNodeStateException()) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() + var brother = getBrother(parent, node) ?: throw IllegalNodeStateException() if (brother.color == Markers.RED) throw NullPointerException() @@ -214,7 +216,7 @@ class RBBalancer>(private var root: RBNode?) : /** parent is black **/ private fun thirdCase(parent: RBNode, node: RBNode?) { - val brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() + val brother = getBrother(parent, node) ?: throw IllegalNodeStateException() when (brother.color) { Markers.RED -> thirdCaseSubFirst(brother, parent) Markers.BLACK -> thirdCaseSubSecond(brother, parent) @@ -225,7 +227,7 @@ class RBBalancer>(private var root: RBNode?) : private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { brother.parent?.left -> { - var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() + var rightBrotherSon = brother.right ?: throw IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED @@ -242,7 +244,7 @@ class RBBalancer>(private var root: RBNode?) : leftRotate(rightBrotherSon) rightBrotherSon = - rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + rightBrotherSon.parent ?: throw IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -258,7 +260,7 @@ class RBBalancer>(private var root: RBNode?) : if (leftBrotherSon.right?.color != Markers.RED && leftBrotherSon.left?.color != Markers.RED) { leftBrotherSon.color = Markers.RED brother.color = Markers.BLACK - leftRotate(brother.parent ?: throw NullPointerException()) // IllegalNodeStateException() + leftRotate(brother.parent ?: throw IllegalNodeStateException()) return } @@ -266,7 +268,7 @@ class RBBalancer>(private var root: RBNode?) : rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED leftBrotherSon = - leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + leftBrotherSon.parent ?: throw IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index ee6a216..47dd91d 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -1,6 +1,5 @@ package treelib.rbTree -import treelib.DBNodeRB import treelib.abstractTree.balanced.BalancedTreeStruct import treelib.singleObjects.Markers import treelib.singleObjects.exceptions.ImpossibleCaseException @@ -8,7 +7,7 @@ import treelib.singleObjects.exceptions.MultithreadingException import java.util.* class RBStruct> : - BalancedTreeStruct, RBStateContainer, RBBalancer>() { + BalancedTreeStruct, RBStateContainer, RBVertex, RBBalancer>() { override var root: RBNode? = null @@ -57,6 +56,10 @@ class RBStruct> : 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 { @@ -73,7 +76,7 @@ class RBStruct> : return node } - fun restoreTreeFromDatabase(preOrder: List>, inOrder: List>) { + fun > restoreTreeFromDatabase(preOrder: List, inOrder: List) { var inOrderIndex = 0 var preOrderIndex = 0 val set = HashSet>() @@ -81,7 +84,7 @@ class RBStruct> : while (preOrderIndex in preOrder.indices) { var currentNode: RBNode? - var drawNode: DBNodeRB + var drawNode: RBVertexType do { drawNode = preOrder[preOrderIndex] @@ -117,7 +120,7 @@ class RBStruct> : } - private fun createRBNode(drawNode: DBNodeRB): RBNode { + 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..5d9d161 --- /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 From 8c05840c6f810f1baed01431a3d972aae0fabbfa Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 22:04:52 +0300 Subject: [PATCH 085/212] fix: Add running on multiple OS --- .github/workflows/CI.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ae15aca..8697f86 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 From 93b4cf76b6ba15fdbb0adcb3901cd20ae25aca9e Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 22:37:53 +0300 Subject: [PATCH 086/212] fix: Fix tree class definitions --- .gitignore | 3 ++- lib/src/test/kotlin/treelib/AVLStructTest.kt | 3 ++- lib/src/test/kotlin/treelib/BINStructTest.kt | 3 ++- lib/src/test/kotlin/treelib/RBStructTest.kt | 3 ++- lib/src/test/kotlin/utils/TreeStructWrapper.kt | 3 ++- lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt | 3 ++- lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt | 3 ++- .../test/kotlin/utils/fuzzers/TreeStructFuzzer.kt | 12 +++++++----- 8 files changed, 21 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 7061064..38f0b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.gradle/ /.idea/ /build/ -/lib/build/ \ No newline at end of file +/lib/build/ +/lib/TEST_TEST/ diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 1e522fe..f003b60 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -7,12 +7,13 @@ 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, AVLStateContainer, AVLStruct>() + private val treeW = TreeStructWrapper, AVLVertex, AVLStateContainer, AVLStruct>() private val treeH = AVLAnalyzer(::testAssert) private var treeStruct = AVLStruct() diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 952a6fa..1075631 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -7,13 +7,14 @@ 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, BINStateContainer, BINStruct>() + val treeW = TreeStructWrapper, BINVertex, BINStateContainer, BINStruct>() var treeStruct = BINStruct() private fun testAssert(msg: String): Nothing = fail(msg) diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index 606ca40..3196207 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -4,6 +4,7 @@ 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 @@ -12,7 +13,7 @@ import kotlin.test.assertEquals @DisplayName("Test: Red-Black Tree Struct") class RBStructTest { - private val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() + private val treeW = TreeStructWrapper, RBVertex, RBStateContainer, RBStruct>() private var treeStruct = RBStruct() private fun testAssert(msg: String): Nothing = fail(msg) diff --git a/lib/src/test/kotlin/utils/TreeStructWrapper.kt b/lib/src/test/kotlin/utils/TreeStructWrapper.kt index 1bdb93e..64fea41 100644 --- a/lib/src/test/kotlin/utils/TreeStructWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeStructWrapper.kt @@ -3,8 +3,9 @@ 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) diff --git a/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt index 5ce3959..bc79507 100644 --- a/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt @@ -3,12 +3,13 @@ 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, BINAnalyzer, BINStateContainer, BINStruct>() { +): TreeStructFuzzer, BINVertex, BINAnalyzer, BINStateContainer, BINStruct>() { override fun createTreeStruct(): BINStruct = BINStruct() diff --git a/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt index 6b59799..2a6bb7f 100644 --- a/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt @@ -3,12 +3,13 @@ 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, RBAnalyzer, RBStateContainer, RBStruct>() { +): TreeStructFuzzer, RBVertex, RBAnalyzer, RBStateContainer, RBStruct>() { override fun createTreeStruct(): RBStruct = RBStruct() diff --git a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt index a5390b9..7d5e0d6 100644 --- a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt @@ -3,20 +3,22 @@ package utils.fuzzers import treelib.abstractTree.Node import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct -import kotlin.random.Random -import utils.Analyzer +import treelib.abstractTree.Vertex import treelib.singleObjects.exceptions.* -import kotlin.random.nextInt +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, + TreeStructType : TreeStruct, > { abstract val baseInput: Array @@ -26,7 +28,7 @@ abstract class TreeStructFuzzer< private var dirPath: String? = null - protected val treeWrapper = TreeStructWrapper() + protected val treeWrapper = TreeStructWrapper() protected abstract fun createTreeStruct(): TreeStructType From b5f52408959763f6ee8caaf54ab00d01f2cc9f05 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 23:33:36 +0300 Subject: [PATCH 087/212] fix(CI): Add a condition to run jacoco --- .github/workflows/CI.yml | 6 ++++-- lib/src/test/kotlin/{ => treelib}/AVLBalancerTest.kt | 0 lib/src/test/kotlin/{ => treelib}/RBBalancerTest.kt | 0 lib/src/test/kotlin/{ => treelib}/TestModelAVL.kt | 0 lib/src/test/kotlin/{ => treelib}/TestModelRBT.kt | 0 5 files changed, 4 insertions(+), 2 deletions(-) rename lib/src/test/kotlin/{ => treelib}/AVLBalancerTest.kt (100%) rename lib/src/test/kotlin/{ => treelib}/RBBalancerTest.kt (100%) rename lib/src/test/kotlin/{ => treelib}/TestModelAVL.kt (100%) rename lib/src/test/kotlin/{ => treelib}/TestModelRBT.kt (100%) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8697f86..243bd23 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -22,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/lib/src/test/kotlin/AVLBalancerTest.kt b/lib/src/test/kotlin/treelib/AVLBalancerTest.kt similarity index 100% rename from lib/src/test/kotlin/AVLBalancerTest.kt rename to lib/src/test/kotlin/treelib/AVLBalancerTest.kt diff --git a/lib/src/test/kotlin/RBBalancerTest.kt b/lib/src/test/kotlin/treelib/RBBalancerTest.kt similarity index 100% rename from lib/src/test/kotlin/RBBalancerTest.kt rename to lib/src/test/kotlin/treelib/RBBalancerTest.kt diff --git a/lib/src/test/kotlin/TestModelAVL.kt b/lib/src/test/kotlin/treelib/TestModelAVL.kt similarity index 100% rename from lib/src/test/kotlin/TestModelAVL.kt rename to lib/src/test/kotlin/treelib/TestModelAVL.kt diff --git a/lib/src/test/kotlin/TestModelRBT.kt b/lib/src/test/kotlin/treelib/TestModelRBT.kt similarity index 100% rename from lib/src/test/kotlin/TestModelRBT.kt rename to lib/src/test/kotlin/treelib/TestModelRBT.kt From 52ef73c68d9608474f97e331808c2dc60c92bcea Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 23:36:42 +0300 Subject: [PATCH 088/212] refactor: Rename some methods in test classes --- lib/build.gradle.kts | 6 +++--- lib/src/test/kotlin/treelib/AVLBalancerTest.kt | 2 ++ lib/src/test/kotlin/treelib/RBBalancerTest.kt | 2 ++ lib/src/test/kotlin/treelib/TestModelAVL.kt | 2 ++ lib/src/test/kotlin/treelib/TestModelRBT.kt | 2 ++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 0f9f88e..6c76505 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -82,7 +82,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 { @@ -90,7 +90,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "BRANCH" - minimum = 0.4.toBigDecimal() + minimum = 0.5.toBigDecimal() } } rule { @@ -104,7 +104,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "METHOD" - minimum = 1.0.toBigDecimal() + minimum = 0.9.toBigDecimal() } } } diff --git a/lib/src/test/kotlin/treelib/AVLBalancerTest.kt b/lib/src/test/kotlin/treelib/AVLBalancerTest.kt index 862a497..3ee5452 100644 --- a/lib/src/test/kotlin/treelib/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 diff --git a/lib/src/test/kotlin/treelib/RBBalancerTest.kt b/lib/src/test/kotlin/treelib/RBBalancerTest.kt index 0bc9e43..d18134e 100644 --- a/lib/src/test/kotlin/treelib/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 diff --git a/lib/src/test/kotlin/treelib/TestModelAVL.kt b/lib/src/test/kotlin/treelib/TestModelAVL.kt index c96773d..8a3ba26 100644 --- a/lib/src/test/kotlin/treelib/TestModelAVL.kt +++ b/lib/src/test/kotlin/treelib/TestModelAVL.kt @@ -1,3 +1,5 @@ +package treelib + import treelib.avlTree.AVLNode class TestModelAVL { diff --git a/lib/src/test/kotlin/treelib/TestModelRBT.kt b/lib/src/test/kotlin/treelib/TestModelRBT.kt index ac86a6c..ca7efe7 100644 --- a/lib/src/test/kotlin/treelib/TestModelRBT.kt +++ b/lib/src/test/kotlin/treelib/TestModelRBT.kt @@ -1,3 +1,5 @@ +package treelib + import treelib.rbTree.RBNode import treelib.singleObjects.Markers From b983b6c5e60340c1e139c6d6c656bfbff8393184 Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 19 Apr 2023 04:39:37 +0300 Subject: [PATCH 089/212] feat: Implement sketch of jsonRepository --- .gitignore | 1 + lib/build.gradle.kts | 3 + .../Neo4jController.kt} | 6 +- .../kotlin/dbSave/jsonFormat/DrawBINVertex.kt | 9 +++ .../dbSave/jsonFormat/JsonRepository.kt | 34 ++++++++++ .../kotlin/{ => dbSave/neo4j}/CONTAINER.conf | 0 .../rbTree => dbSave/neo4j}/DrawRBVertex.kt | 3 +- .../neo4j/Neo4jRepository.kt} | 4 +- .../kotlin/{ => dbSave/neo4j}/testNeo4j.sh | 11 ---- lib/src/main/kotlin/initNeo4j.sh | 0 lib/src/main/kotlin/loadNeo4j.sh | 8 --- lib/src/main/kotlin/treelib/DBNodeRB.kt | 11 ---- lib/src/main/kotlin/treelib/Main.kt | 66 ------------------- .../main/kotlin/treelib/binTree/BINVertex.kt | 2 +- 14 files changed, 57 insertions(+), 101 deletions(-) rename lib/src/main/kotlin/{Controller.kt => controller/Neo4jController.kt} (91%) create mode 100644 lib/src/main/kotlin/dbSave/jsonFormat/DrawBINVertex.kt create mode 100644 lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt rename lib/src/main/kotlin/{ => dbSave/neo4j}/CONTAINER.conf (100%) rename lib/src/main/kotlin/{treelib/rbTree => dbSave/neo4j}/DrawRBVertex.kt (79%) rename lib/src/main/kotlin/{DataBase.kt => dbSave/neo4j/Neo4jRepository.kt} (99%) rename lib/src/main/kotlin/{ => dbSave/neo4j}/testNeo4j.sh (59%) delete mode 100644 lib/src/main/kotlin/initNeo4j.sh delete mode 100644 lib/src/main/kotlin/loadNeo4j.sh delete mode 100644 lib/src/main/kotlin/treelib/DBNodeRB.kt delete mode 100644 lib/src/main/kotlin/treelib/Main.kt diff --git a/.gitignore b/.gitignore index 38f0b3d..16cba14 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /build/ /lib/build/ /lib/TEST_TEST/ +/gradle/ diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 6c76505..db61e37 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -22,6 +22,8 @@ dependencies { api("org.apache.commons:commons-math3:3.6.1") implementation("com.google.guava:guava:31.1-jre") + 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) @@ -30,6 +32,7 @@ dependencies { testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") implementation(kotlin("stdlib-jdk8")) + } tasks.test { diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/controller/Neo4jController.kt similarity index 91% rename from lib/src/main/kotlin/Controller.kt rename to lib/src/main/kotlin/controller/Neo4jController.kt index a59a3b0..3744351 100644 --- a/lib/src/main/kotlin/Controller.kt +++ b/lib/src/main/kotlin/controller/Neo4jController.kt @@ -1,9 +1,11 @@ +package controller -import treelib.rbTree.DrawRBVertex +import dbSave.neo4j.DrawRBVertex +import dbSave.neo4j.Neo4jRepository import treelib.rbTree.RBStruct import treelib.singleObjects.Container -class Controller { +class Neo4jController { fun initTree() { val neo4jDB = Neo4jRepository() 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..88a1396 --- /dev/null +++ b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt @@ -0,0 +1,34 @@ +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>, typeToken: TypeToken>>, fileName: String) { + + val gson = GsonBuilder().setPrettyPrinting().create() + val json = gson.toJson(preOrder) + + File(dirPath, fileName).run { + createNewFile() + writeText(json) + } + + val preOrd = gson.fromJson>>(json, typeToken.type) + + } + + fun exportTree(fileName: String) { + val gson = GsonBuilder().setPrettyPrinting().create() + //val json = gson.fromJson(File(dirPath, fileName).readText(), ArrayVertices::class.java) + + + } + + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/CONTAINER.conf b/lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf similarity index 100% rename from lib/src/main/kotlin/CONTAINER.conf rename to lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf diff --git a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt b/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt similarity index 79% rename from lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt rename to lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt index b3ee499..37100de 100644 --- a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt +++ b/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt @@ -1,5 +1,6 @@ -package treelib.rbTree +package dbSave.neo4j +import treelib.rbTree.RBVertex import treelib.singleObjects.Markers class DrawRBVertex>( diff --git a/lib/src/main/kotlin/DataBase.kt b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt similarity index 99% rename from lib/src/main/kotlin/DataBase.kt rename to lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt index 809eb64..316dc4c 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt @@ -1,9 +1,11 @@ +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.rbTree.DrawRBVertex import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh similarity index 59% rename from lib/src/main/kotlin/testNeo4j.sh rename to lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh index 87b69c4..6b07269 100644 --- a/lib/src/main/kotlin/testNeo4j.sh +++ b/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh @@ -3,7 +3,6 @@ BASEDIR=$(realpath "$(dirname "$0")") . "${BASEDIR}/CONTAINER.conf" -# -d docker run \ -i \ --name "$CONTAINER_NAME" \ @@ -12,13 +11,3 @@ docker run \ --publish=7474:7474 --publish=7687:7687 \ --env NEO4J_AUTH=neo4j/"$PASSWORD" \ neo4j:latest \ -# -c /bin/bash -#neo4j-admin database dump neo4j --to-path=$HOME/neo4j/data:/data - -#docker ps -a - -#docker stop "$CONTAINER_NAME" - -#cp $HOME/neo4j/data:/data $HOME/ - -#docker stop neo4j:latest diff --git a/lib/src/main/kotlin/initNeo4j.sh b/lib/src/main/kotlin/initNeo4j.sh deleted file mode 100644 index e69de29..0000000 diff --git a/lib/src/main/kotlin/loadNeo4j.sh b/lib/src/main/kotlin/loadNeo4j.sh deleted file mode 100644 index d4d9fcd..0000000 --- a/lib/src/main/kotlin/loadNeo4j.sh +++ /dev/null @@ -1,8 +0,0 @@ - -BASEDIR=$(realpath "$(dirname "$0")") - -. "${BASEDIR}/CONTAINER.conf" - -docker stop "$CONTAINER_NAME" - -neo4j-admin dump --database=neo4j --to=/data/backups/ \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt deleted file mode 100644 index cf067e1..0000000 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ /dev/null @@ -1,11 +0,0 @@ -package treelib - -import treelib.singleObjects.Markers - - -class DBNodeRB>( - val value: Pack, - val color: Markers = Markers.RED, - val x: Double = 0.0, - val y: Double = 0.0 -) diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt deleted file mode 100644 index 15e0eab..0000000 --- a/lib/src/main/kotlin/treelib/Main.kt +++ /dev/null @@ -1,66 +0,0 @@ -package treelib - -import Controller -import treelib.rbTree.RBStruct -import treelib.singleObjects.Container - - -fun main() { - // 25, 15, 10, 4, 12, 22, 18, 24, 50, 35, 31, 44, 70, 66, 90 - // 4, 10, 12, 15, 18, 22, 24, 25, 31, 35, 44, 50, 66, 70, 90 - - /* - - val neo4jRep = Neo4jRepository() - val a1 = DBNodeRB(Container(Pair(1, 25))) - val a2 = DBNodeRB(Container(Pair(1, 15))) - val a3 = DBNodeRB(Container(Pair(1, 10))) - val a4 = DBNodeRB(Container(Pair(1, 4))) - val a5 = DBNodeRB(Container(Pair(1, 12))) - val a6 = DBNodeRB(Container(Pair(1, 22))) - val a7 = DBNodeRB(Container(Pair(1, 18))) - val a8 = DBNodeRB(Container(Pair(1, 24))) - val a9 = DBNodeRB(Container(Pair(1, 50))) - val a10 = DBNodeRB(Container(Pair(1, 35))) - val a11 = DBNodeRB(Container(Pair(1, 31))) - val a12 = DBNodeRB(Container(Pair(1, 44))) - val a13 = DBNodeRB(Container(Pair(1, 70))) - val a14 = DBNodeRB(Container(Pair(1, 66))) - val a15 = DBNodeRB(Container(Pair(1, 90))) - val preArr = arrayOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) - val inArr = arrayOf(a4, a3, a5, a2, a7, a6, a8, a1, a11, a10, a12, a9, a14, a13, a15) - neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") - neo4jRep.saveChanges(preArr, inArr) - */ - - val tree = RBStruct>() - tree.insert(Container(Pair(25 , 1))) - tree.insert(Container(Pair(15 , 1))) - tree.insert(Container(Pair(50 , 1))) - tree.insert(Container(Pair(10 , 1))) - tree.insert(Container(Pair(22 , 1))) - tree.insert(Container(Pair(35 , 1))) - tree.insert(Container(Pair(70 , 1))) - tree.insert(Container(Pair(4 , 1))) - tree.insert(Container(Pair(12 , 1))) - tree.insert(Container(Pair(18 , 1))) - tree.insert(Container(Pair(24 , 1))) - tree.insert(Container(Pair(31 , 1))) - tree.insert(Container(Pair(44 , 1))) - tree.insert(Container(Pair(66 , 1))) - tree.insert(Container(Pair(90 , 1))) - val controller = Controller() - controller.saveTree(tree) - tree.insert(Container(Pair(100, 1))) - controller.saveTree(tree) - controller.initTree() - - //neo4jRep.exportRBtree() - - //neo4jRep.close() -} - -fun test() { - val cont = Controller() - cont.initTree() -} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/binTree/BINVertex.kt b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt index e660d8c..8939461 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINVertex.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt @@ -2,4 +2,4 @@ package treelib.binTree import treelib.abstractTree.Vertex -class BINVertex>(override val value: Pack): Vertex() \ No newline at end of file +open class BINVertex>(override val value: Pack): Vertex() \ No newline at end of file From 155ff6defca0f7f9236ef3f183bef4b92ea4173a Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 19 Apr 2023 03:20:10 +0300 Subject: [PATCH 090/212] feat: Implement AVLStruct.restoreStruct and BINStruct.restoreStruct. --- .../main/kotlin/controller/Neo4jController.kt | 2 +- .../kotlin/treelib/abstractTree/TreeStruct.kt | 2 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 15 ++++++++++++--- .../main/kotlin/treelib/binTree/BINStruct.kt | 13 +++++++++++++ lib/src/main/kotlin/treelib/rbTree/RBStruct.kt | 3 +-- .../singleObjects/exceptions/IncorrectUsage.kt | 18 ++++++++++++++++++ lib/src/test/kotlin/treeTests/RBTreeTest.kt | 16 ++++++++++++++++ 7 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt create mode 100644 lib/src/test/kotlin/treeTests/RBTreeTest.kt diff --git a/lib/src/main/kotlin/controller/Neo4jController.kt b/lib/src/main/kotlin/controller/Neo4jController.kt index 3744351..5c47c63 100644 --- a/lib/src/main/kotlin/controller/Neo4jController.kt +++ b/lib/src/main/kotlin/controller/Neo4jController.kt @@ -16,7 +16,7 @@ class Neo4jController { neo4jDB.exportRBtree() val RBtree = RBStruct>>() - RBtree.restoreTreeFromDatabase(orders.first, orders.second) + RBtree.restoreStruct(orders.first, orders.second) neo4jDB.close() } diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index c91c6ed..d9b223b 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -15,7 +15,7 @@ abstract class TreeStruct< 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) { diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index 7893341..b0ee0ae 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -1,6 +1,7 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancedTreeStruct +import treelib.singleObjects.exceptions.IncorrectUsage class AVLStruct> : BalancedTreeStruct, AVLStateContainer, AVLVertex, AVLBalancer>() { @@ -40,9 +41,9 @@ class AVLStruct> : } } - override fun toVertex(node: AVLNode): AVLVertex { - return AVLVertex(node.value, node.height) - } + 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) @@ -56,4 +57,12 @@ class AVLStruct> : } return node } + 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/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index f3bfc09..ea03cb6 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -1,6 +1,8 @@ package treelib.binTree import treelib.abstractTree.TreeStruct +import treelib.abstractTree.Vertex +import treelib.singleObjects.exceptions.IncorrectUsage class BINStruct> : TreeStruct, BINStateContainer, BINVertex>() { @@ -66,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/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 47dd91d..999c113 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -76,7 +76,7 @@ class RBStruct> : return node } - fun > restoreTreeFromDatabase(preOrder: List, inOrder: List) { + fun > restoreStruct(preOrder: List, inOrder: List) { var inOrderIndex = 0 var preOrderIndex = 0 val set = HashSet>() @@ -117,7 +117,6 @@ class RBStruct> : stack.push(currentNode) } } - } private fun > createRBNode(drawNode: RBVertexType): RBNode { 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..9fce7ef --- /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/treeTests/RBTreeTest.kt b/lib/src/test/kotlin/treeTests/RBTreeTest.kt new file mode 100644 index 0000000..fa56cc6 --- /dev/null +++ b/lib/src/test/kotlin/treeTests/RBTreeTest.kt @@ -0,0 +1,16 @@ +package treeTests + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import treelib.rbTree.RBTree + +@DisplayName("Test: Red-Black Tree") +class RBTreeTest { + private var rbTree = RBTree() + + @Test + fun `insert two elements`(){ + rbTree.putItem(Pair(25, 1)) + rbTree.putItem(Pair(15, 1)) + } +} \ No newline at end of file From 342e6c3e08e71b6c7d4871b105b09fcc19791b1d Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 19 Apr 2023 05:19:33 +0300 Subject: [PATCH 091/212] fix: Fix some problems after rebase onto Tests branch --- lib/build.gradle.kts | 1 + lib/src/test/kotlin/treelib/AVLTreeTest.kt | 9 +++------ lib/src/test/kotlin/treelib/BINTreeTest.kt | 11 ++++------- lib/src/test/kotlin/treelib/RBTreeTest.kt | 9 +++------ lib/src/test/kotlin/utils/TreeWrapper.kt | 10 ++++------ lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt | 3 ++- 6 files changed, 17 insertions(+), 26 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index db61e37..127daa0 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -30,6 +30,7 @@ dependencies { testImplementation("io.mockk:mockk:1.13.4") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") implementation(kotlin("stdlib-jdk8")) diff --git a/lib/src/test/kotlin/treelib/AVLTreeTest.kt b/lib/src/test/kotlin/treelib/AVLTreeTest.kt index 5531d35..5915d99 100644 --- a/lib/src/test/kotlin/treelib/AVLTreeTest.kt +++ b/lib/src/test/kotlin/treelib/AVLTreeTest.kt @@ -2,10 +2,7 @@ package treelib import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -import treelib.avlTree.AVLNode -import treelib.avlTree.AVLStateContainer -import treelib.avlTree.AVLStruct -import treelib.avlTree.AVLTree +import treelib.avlTree.* import treelib.singleObjects.Container import utils.TreeStructWrapper import utils.TreeWrapper @@ -15,9 +12,9 @@ import kotlin.test.assertEquals class AVLTreeTest { private val tree = AVLTree() private val treeW = - TreeWrapper>, AVLStateContainer>, AVLStruct>, AVLTree>() + TreeWrapper>, AVLVertex>, AVLStateContainer>, AVLStruct>, AVLTree>() private val treeSW = - TreeStructWrapper, AVLNode>, AVLStateContainer>, AVLStruct>>() + TreeStructWrapper, AVLNode>, AVLVertex>, AVLStateContainer>, AVLStruct>>() @ParameterizedTest diff --git a/lib/src/test/kotlin/treelib/BINTreeTest.kt b/lib/src/test/kotlin/treelib/BINTreeTest.kt index 172c545..5157c95 100644 --- a/lib/src/test/kotlin/treelib/BINTreeTest.kt +++ b/lib/src/test/kotlin/treelib/BINTreeTest.kt @@ -3,21 +3,18 @@ package treelib import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -import treelib.binTree.BINNode -import treelib.binTree.BINStateContainer -import treelib.binTree.BINStruct -import treelib.binTree.BINTree -import utils.TreeWrapper +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>, BINStateContainer>, BINStruct>, BINTree>() + TreeWrapper>, BINVertex>, BINStateContainer>, BINStruct>, BINTree>() private val treeSW = - TreeStructWrapper, BINNode>, BINStateContainer>, BINStruct>>() + TreeStructWrapper, BINNode>, BINVertex>, BINStateContainer>, BINStruct>>() // line - 0.6, branch - 0.5, methods = 0.9 diff --git a/lib/src/test/kotlin/treelib/RBTreeTest.kt b/lib/src/test/kotlin/treelib/RBTreeTest.kt index 5a3ceee..cd7ce2a 100644 --- a/lib/src/test/kotlin/treelib/RBTreeTest.kt +++ b/lib/src/test/kotlin/treelib/RBTreeTest.kt @@ -2,10 +2,7 @@ package treelib import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -import treelib.rbTree.RBNode -import treelib.rbTree.RBStateContainer -import treelib.rbTree.RBStruct -import treelib.rbTree.RBTree +import treelib.rbTree.* import treelib.singleObjects.Container import treelib.singleObjects.Markers import utils.TreeStructWrapper @@ -15,9 +12,9 @@ import kotlin.test.assertEquals class RBTreeTest { private val tree = RBTree() private val treeW = - TreeWrapper>, RBStateContainer>, RBStruct>, RBTree>() + TreeWrapper>, RBVertex>, RBStateContainer>, RBStruct>, RBTree>() private val treeSW = - TreeStructWrapper, RBNode>, RBStateContainer>, RBStruct>>() + TreeStructWrapper, RBNode>, RBVertex>, RBStateContainer>, RBStruct>>() @ParameterizedTest diff --git a/lib/src/test/kotlin/utils/TreeWrapper.kt b/lib/src/test/kotlin/utils/TreeWrapper.kt index 5ca8ea7..47858d9 100644 --- a/lib/src/test/kotlin/utils/TreeWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeWrapper.kt @@ -1,9 +1,6 @@ package utils -import treelib.abstractTree.Node -import treelib.abstractTree.StateContainer -import treelib.abstractTree.Tree -import treelib.abstractTree.TreeStruct +import treelib.abstractTree.* import treelib.singleObjects.Container @@ -11,9 +8,10 @@ class TreeWrapper < V : Comparable, Value, NodeType: Node, NodeType>, + VertexType: Vertex>, State: StateContainer, NodeType>, - TStruct: TreeStruct, NodeType, State>, - Wood : Tree> { + TStruct: TreeStruct, NodeType, State, VertexType>, + Wood : Tree> { fun getPrivateNode(tree: Wood, name: String = "treeStruct"): TStruct { val treeS = tree.javaClass.getDeclaredField(name) treeS.isAccessible = true diff --git a/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt index 7cbed69..17d52a2 100644 --- a/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt @@ -3,12 +3,13 @@ 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, AVLAnalyzer, AVLStateContainer, AVLStruct>() { +) : TreeStructFuzzer, AVLVertex, AVLAnalyzer, AVLStateContainer, AVLStruct>() { override fun createTreeStruct(): AVLStruct = AVLStruct() From 947e297629fc64c3d4b8c0483467aedf4578690e Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 15 Apr 2023 17:42:33 +0300 Subject: [PATCH 092/212] refactor: Separate tests for TreeStructs into different files. --- lib/src/test/kotlin/treelib/AVLStructTest.kt | 18 + lib/src/test/kotlin/treelib/BINStructTest.kt | 474 ++++++++++++++++ lib/src/test/kotlin/treelib/RBStructTree.kt | 18 + .../test/kotlin/treelib/TreeStructsTest.kt | 513 ------------------ 4 files changed, 510 insertions(+), 513 deletions(-) create mode 100644 lib/src/test/kotlin/treelib/AVLStructTest.kt create mode 100644 lib/src/test/kotlin/treelib/BINStructTest.kt create mode 100644 lib/src/test/kotlin/treelib/RBStructTree.kt delete mode 100644 lib/src/test/kotlin/treelib/TreeStructsTest.kt diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt new file mode 100644 index 0000000..00215f2 --- /dev/null +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -0,0 +1,18 @@ +package treelib + +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer +import treelib.avlTree.AVLStruct + +@DisplayName("Test: AVL Struct") +class AVLStructTest { + val treeW = TreeStructWrapper, AVLStateContainer, AVLStruct>() + var classUnderTest = AVLStruct() + + @BeforeEach + fun reInitClassUnderTest() { + classUnderTest = AVLStruct() + } +} \ 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..194096e --- /dev/null +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -0,0 +1,474 @@ +package treelib + +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import treelib.binTree.BINNode +import treelib.binTree.BINStateContainer +import treelib.binTree.BINStruct +import kotlin.test.assertEquals + +@DisplayName("Test: Binary Search Tree Struct") +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) + } +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/RBStructTree.kt b/lib/src/test/kotlin/treelib/RBStructTree.kt new file mode 100644 index 0000000..c1e040b --- /dev/null +++ b/lib/src/test/kotlin/treelib/RBStructTree.kt @@ -0,0 +1,18 @@ +package treelib + +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct + +@DisplayName("Test: Red-Black Tree Struct") +class RBStructTree { + val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() + var classUnderTest = RBStruct() + + @BeforeEach + fun reInitClassUnderTest() { + classUnderTest = RBStruct() + } +} \ No newline at end of file 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() - } - } -} From 4803d92925a4c235464c2712fb43ae913d9e6dd3 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 15 Apr 2023 23:28:10 +0300 Subject: [PATCH 093/212] fix: Fix bugs in RBStruct; write base tests for RBStruct. --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 6 +- .../main/kotlin/treelib/rbTree/RBStruct.kt | 15 ++- lib/src/test/kotlin/treelib/RBStructTest.kt | 103 ++++++++++++++++++ lib/src/test/kotlin/treelib/RBStructTree.kt | 18 --- 4 files changed, 120 insertions(+), 22 deletions(-) create mode 100644 lib/src/test/kotlin/treelib/RBStructTest.kt delete mode 100644 lib/src/test/kotlin/treelib/RBStructTree.kt diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 2f21c10..422177e 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -220,7 +220,11 @@ abstract class TreeStruct, NodeType : Node> : BalancedTreeStruct, RBStateContainer, RBBalancer>() { @@ -17,12 +20,12 @@ class RBStruct> : 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, @@ -45,6 +48,7 @@ class RBStruct> : } } else root?.let { root = childForLink + if (childForLink != null) childForLink.parent = null } } @@ -53,7 +57,12 @@ class RBStruct> : override fun createNode(item: Pack): RBNode = RBNode(item) override fun linkNewNode(node: RBNode, parent: RBNode?): RBNode { - if (parent == null) root = node + 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 diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt new file mode 100644 index 0000000..807a1d6 --- /dev/null +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -0,0 +1,103 @@ +package treelib + +import org.junit.jupiter.api.* +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct +import treelib.singleObjects.Markers +import kotlin.test.assertEquals + +@DisplayName("Test: Red-Black Tree Struct") +class RBStructTest { + val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() + var classUnderTest = RBStruct() + + @BeforeEach + fun reInitClassUnderTest() { + classUnderTest = RBStruct() + } + + @Test + fun `base test on creation root`() { + classUnderTest.insert(6) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(root?.value, 6) }, + { assertEquals(root?.color, Markers.BLACK) }, + ) + } + + @Test + fun `base test on creation root with left`() { + classUnderTest.insert(6) + classUnderTest.insert(3) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(root?.left?.value, 3) }, + { assertEquals(root?.left?.color, Markers.RED) }, + ) + } + + @Test + fun `base test on creation root with right`() { + classUnderTest.insert(6) + classUnderTest.insert(8) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(root?.right?.value, 8) }, + { assertEquals(root?.right?.color, Markers.RED) }, + ) + } + + @Test + fun `base test on creation children`() { + classUnderTest.insert(6) + classUnderTest.insert(8) + classUnderTest.insert(3) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(root?.right?.value, 8) }, + { assertEquals(root?.left?.value, 3) }, + { assertEquals(root?.right?.color, Markers.RED) }, + { assertEquals(root?.left?.color, Markers.RED) }, + ) + } + + @Test + fun `base test delete root (left & right children)`() { + classUnderTest.insert(6) + classUnderTest.insert(8) + classUnderTest.insert(3) + classUnderTest.delete(6) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(root?.value, 8) }, + { assertEquals(root?.color, Markers.BLACK) }, + ) + } + + @Test + fun `base test delete root (right child)`() { + classUnderTest.insert(6) + classUnderTest.insert(8) + classUnderTest.delete(6) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(8, root?.value) }, + { assertEquals(Markers.BLACK, root?.color) }, + ) + } + + @Test + fun `base test delete root (left child)`() { + classUnderTest.insert(6) + classUnderTest.insert(3) + classUnderTest.delete(6) + val root = treeW.getPrivateNode(classUnderTest) + assertAll( + { assertEquals(3, root?.value) }, + { assertEquals(Markers.BLACK, root?.color) }, + ) + } + +} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/RBStructTree.kt b/lib/src/test/kotlin/treelib/RBStructTree.kt deleted file mode 100644 index c1e040b..0000000 --- a/lib/src/test/kotlin/treelib/RBStructTree.kt +++ /dev/null @@ -1,18 +0,0 @@ -package treelib - -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName -import treelib.rbTree.RBNode -import treelib.rbTree.RBStateContainer -import treelib.rbTree.RBStruct - -@DisplayName("Test: Red-Black Tree Struct") -class RBStructTree { - val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() - var classUnderTest = RBStruct() - - @BeforeEach - fun reInitClassUnderTest() { - classUnderTest = RBStruct() - } -} \ No newline at end of file From 5075e51cc02b6e2a0ca61d7f5260b941e918a5ad Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 16 Apr 2023 14:50:38 +0300 Subject: [PATCH 094/212] feat: Add RBAnalyzer class for testing invariant of a RBStruct. --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 6 +- lib/src/test/kotlin/treelib/AVLStructTest.kt | 1 + lib/src/test/kotlin/treelib/BINStructTest.kt | 1 + lib/src/test/kotlin/treelib/RBStructTest.kt | 11 ++- lib/src/test/kotlin/utils/Analyzer.kt | 10 +++ lib/src/test/kotlin/utils/RBAnalyzer.kt | 85 +++++++++++++++++++ .../{treelib => utils}/TreeStructWrapper.kt | 2 +- 7 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 lib/src/test/kotlin/utils/Analyzer.kt create mode 100644 lib/src/test/kotlin/utils/RBAnalyzer.kt rename lib/src/test/kotlin/{treelib => utils}/TreeStructWrapper.kt (98%) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 422177e..898a40e 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -6,7 +6,11 @@ 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 + > { protected abstract var root: NodeType? diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 00215f2..23237a8 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -5,6 +5,7 @@ import org.junit.jupiter.api.DisplayName import treelib.avlTree.AVLNode import treelib.avlTree.AVLStateContainer import treelib.avlTree.AVLStruct +import utils.TreeStructWrapper @DisplayName("Test: AVL Struct") class AVLStructTest { diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 194096e..38b5a71 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test import treelib.binTree.BINNode import treelib.binTree.BINStateContainer import treelib.binTree.BINStruct +import utils.TreeStructWrapper import kotlin.test.assertEquals @DisplayName("Test: Binary Search Tree Struct") diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index 807a1d6..da9e9c5 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -5,12 +5,18 @@ import treelib.rbTree.RBNode import treelib.rbTree.RBStateContainer import treelib.rbTree.RBStruct import treelib.singleObjects.Markers +import utils.RBAnalyzer +import utils.TreeStructWrapper import kotlin.test.assertEquals @DisplayName("Test: Red-Black Tree Struct") class RBStructTest { - val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() - var classUnderTest = RBStruct() + private val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() + private var classUnderTest = RBStruct() + + private fun testAssert(msg: String): Nothing = fail(msg) + + private val analyzer = RBAnalyzer(::testAssert) @BeforeEach fun reInitClassUnderTest() { @@ -99,5 +105,4 @@ class RBStructTest { { assertEquals(Markers.BLACK, root?.color) }, ) } - } \ No newline at end of file diff --git a/lib/src/test/kotlin/utils/Analyzer.kt b/lib/src/test/kotlin/utils/Analyzer.kt new file mode 100644 index 0000000..5a342f9 --- /dev/null +++ b/lib/src/test/kotlin/utils/Analyzer.kt @@ -0,0 +1,10 @@ +package utils + +import treelib.abstractTree.Node + +interface Analyzer< + Pack : Comparable, + NodeType : Node, + > { + fun checkTree(root: NodeType) +} \ 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..1932026 --- /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>( + 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) assertMethod("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 -> { + assertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") + return@checkInvariant errorMagicNumber + } + + it.value < node.value -> { + assertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") + return@checkInvariant errorMagicNumber + } + + (it.color == Markers.RED) && (node.color == Markers.RED) -> { + assertMethod("parent.color == RED == RightChild.color => [parent - ${node.value} RightChild - ${it.value}]") + return@checkInvariant errorMagicNumber + } + + else -> {} + } + } + + node.left?.let { + when { + it.value == node.value -> { + assertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") + return@checkInvariant errorMagicNumber + } + + it.value > node.value -> { + assertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") + return@checkInvariant errorMagicNumber + } + + (it.color == Markers.RED) && (node.color == Markers.RED) -> { + assertMethod("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) { + assertMethod( + "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 98% rename from lib/src/test/kotlin/treelib/TreeStructWrapper.kt rename to lib/src/test/kotlin/utils/TreeStructWrapper.kt index 88870ec..1bdb93e 100644 --- a/lib/src/test/kotlin/treelib/TreeStructWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeStructWrapper.kt @@ -1,4 +1,4 @@ -package treelib +package utils import treelib.abstractTree.Node import treelib.abstractTree.StateContainer From b728bdde1d440435336cd1a7441cb975d715f6b1 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 16 Apr 2023 15:36:48 +0300 Subject: [PATCH 095/212] refactor: Rename variables in test Classes. --- lib/src/test/kotlin/treelib/AVLStructTest.kt | 4 +- lib/src/test/kotlin/treelib/RBStructTest.kt | 54 ++++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 23237a8..25ce4cb 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -10,10 +10,10 @@ import utils.TreeStructWrapper @DisplayName("Test: AVL Struct") class AVLStructTest { val treeW = TreeStructWrapper, AVLStateContainer, AVLStruct>() - var classUnderTest = AVLStruct() + var treeStruct = AVLStruct() @BeforeEach fun reInitClassUnderTest() { - classUnderTest = AVLStruct() + treeStruct = AVLStruct() } } \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index da9e9c5..5995dd4 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -12,7 +12,7 @@ import kotlin.test.assertEquals @DisplayName("Test: Red-Black Tree Struct") class RBStructTest { private val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() - private var classUnderTest = RBStruct() + private var treeStruct = RBStruct() private fun testAssert(msg: String): Nothing = fail(msg) @@ -20,13 +20,13 @@ class RBStructTest { @BeforeEach fun reInitClassUnderTest() { - classUnderTest = RBStruct() + treeStruct = RBStruct() } @Test fun `base test on creation root`() { - classUnderTest.insert(6) - val root = treeW.getPrivateNode(classUnderTest) + treeStruct.insert(6) + val root = treeW.getPrivateNode(treeStruct) assertAll( { assertEquals(root?.value, 6) }, { assertEquals(root?.color, Markers.BLACK) }, @@ -35,9 +35,9 @@ class RBStructTest { @Test fun `base test on creation root with left`() { - classUnderTest.insert(6) - classUnderTest.insert(3) - val root = treeW.getPrivateNode(classUnderTest) + treeStruct.insert(6) + treeStruct.insert(3) + val root = treeW.getPrivateNode(treeStruct) assertAll( { assertEquals(root?.left?.value, 3) }, { assertEquals(root?.left?.color, Markers.RED) }, @@ -46,9 +46,9 @@ class RBStructTest { @Test fun `base test on creation root with right`() { - classUnderTest.insert(6) - classUnderTest.insert(8) - val root = treeW.getPrivateNode(classUnderTest) + treeStruct.insert(6) + treeStruct.insert(8) + val root = treeW.getPrivateNode(treeStruct) assertAll( { assertEquals(root?.right?.value, 8) }, { assertEquals(root?.right?.color, Markers.RED) }, @@ -57,10 +57,10 @@ class RBStructTest { @Test fun `base test on creation children`() { - classUnderTest.insert(6) - classUnderTest.insert(8) - classUnderTest.insert(3) - val root = treeW.getPrivateNode(classUnderTest) + 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) }, @@ -71,11 +71,11 @@ class RBStructTest { @Test fun `base test delete root (left & right children)`() { - classUnderTest.insert(6) - classUnderTest.insert(8) - classUnderTest.insert(3) - classUnderTest.delete(6) - val root = treeW.getPrivateNode(classUnderTest) + 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) }, @@ -84,10 +84,10 @@ class RBStructTest { @Test fun `base test delete root (right child)`() { - classUnderTest.insert(6) - classUnderTest.insert(8) - classUnderTest.delete(6) - val root = treeW.getPrivateNode(classUnderTest) + treeStruct.insert(6) + treeStruct.insert(8) + treeStruct.delete(6) + val root = treeW.getPrivateNode(treeStruct) assertAll( { assertEquals(8, root?.value) }, { assertEquals(Markers.BLACK, root?.color) }, @@ -96,10 +96,10 @@ class RBStructTest { @Test fun `base test delete root (left child)`() { - classUnderTest.insert(6) - classUnderTest.insert(3) - classUnderTest.delete(6) - val root = treeW.getPrivateNode(classUnderTest) + treeStruct.insert(6) + treeStruct.insert(3) + treeStruct.delete(6) + val root = treeW.getPrivateNode(treeStruct) assertAll( { assertEquals(3, root?.value) }, { assertEquals(Markers.BLACK, root?.color) }, From 10385faae248adabf1e09bdf42b38dedbf761a5e Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 16 Apr 2023 15:37:20 +0300 Subject: [PATCH 096/212] feat: Add BINAnalyzer class for testing invariant of the BINStruct. --- lib/src/test/kotlin/treelib/BINStructTest.kt | 307 ++++++++++--------- lib/src/test/kotlin/utils/BINAnalyzer.kt | 52 ++++ 2 files changed, 215 insertions(+), 144 deletions(-) create mode 100644 lib/src/test/kotlin/utils/BINAnalyzer.kt diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 38b5a71..952a6fa 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -3,30 +3,36 @@ 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 utils.BINAnalyzer import utils.TreeStructWrapper import kotlin.test.assertEquals @DisplayName("Test: Binary Search Tree Struct") class BINStructTest { val treeW = TreeStructWrapper, BINStateContainer, BINStruct>() - var classUnderTest = BINStruct() + var treeStruct = BINStruct() + + private fun testAssert(msg: String): Nothing = fail(msg) + + private val analyzer = BINAnalyzer(::testAssert) @BeforeEach fun reInitClassUnderTest() { - classUnderTest = BINStruct() + treeStruct = 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) + treeStruct.insert(i) } - classUnderTest.delete(5) - val root = treeW.getPrivateNode(classUnderTest)?.value + treeStruct.delete(5) + val root = treeW.getPrivateNode(treeStruct)?.value assertEquals(expected = 6, actual = root) } @@ -35,20 +41,20 @@ class BINStructTest { fun `test insert`() { val num = mutableListOf(1, 2, 3, 4, 5, 8) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } val additional_num = mutableListOf(1, 2, 3, 5, 7, 8, 11) for (i in additional_num) { - classUnderTest.insert(i) + treeStruct.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 + 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) @@ -62,49 +68,49 @@ class BINStructTest { fun `test find ordinary`() { val num = mutableListOf(2, 3, 1, 4, 5, 10) - assertEquals(expected = classUnderTest.find(2), actual = null) + assertEquals(expected = treeStruct.find(2), actual = null) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - assertEquals(expected = classUnderTest.find(2), actual = 2) + assertEquals(expected = treeStruct.find(2), actual = 2) } @Test fun `test find null`() { val num = mutableListOf(1) - classUnderTest.insert(num[0]) + treeStruct.insert(num[0]) - assertEquals(classUnderTest.find(2), null) + assertEquals(treeStruct.find(2), null) } @Test fun `test find root`() { val num = mutableListOf(1) - classUnderTest.insert(num[0]) + treeStruct.insert(num[0]) - assertEquals(classUnderTest.find(1), 1) + assertEquals(treeStruct.find(1), 1) } @Test fun `test insert and delete root`() { val num = mutableListOf(1, 2) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(1) + treeStruct.delete(1) val additional_num = mutableListOf(1, 2, 11) for (i in additional_num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - val root = treeW.getPrivateNode(classUnderTest)?.value - val node_1 = treeW.getPrivateNode(classUnderTest)?.left?.value - val node_11 = treeW.getPrivateNode(classUnderTest)?.right?.value + 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) @@ -116,13 +122,13 @@ class BINStructTest { fun `test delete nonexistent value right`() { val num = mutableListOf(5, 6) for (value in num) { - classUnderTest.insert(value) + treeStruct.insert(value) } - classUnderTest.delete(6) + treeStruct.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 + 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) @@ -133,13 +139,13 @@ class BINStructTest { fun `test delete nonexistent value left`() { val num = mutableListOf(6, 5) for (value in num) { - classUnderTest.insert(value) + treeStruct.insert(value) } - classUnderTest.delete(5) + treeStruct.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 + 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) @@ -150,13 +156,13 @@ class BINStructTest { fun `test delete no child root`() { val num = mutableListOf(3) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(3) + treeStruct.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 + 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) @@ -167,14 +173,14 @@ class BINStructTest { fun `test delete no child right`() { val num = mutableListOf(3, 10, 15) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(15) + treeStruct.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 + 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) @@ -187,14 +193,14 @@ class BINStructTest { fun `test delete no child left`() { val num = mutableListOf(15, 10, 3) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(3) + treeStruct.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 + 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) @@ -207,14 +213,14 @@ class BINStructTest { fun `test delete one child left`() { val num = mutableListOf(3, 2, 1, 5) for (value in num) { - classUnderTest.insert(value) + treeStruct.insert(value) } - classUnderTest.delete(2) + treeStruct.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 + 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) @@ -226,14 +232,14 @@ class BINStructTest { fun `test delete one child right`() { val num = mutableListOf(3, 1, 5, 6) for (value in num) { - classUnderTest.insert(value) + treeStruct.insert(value) } - classUnderTest.delete(5) + treeStruct.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 + 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) @@ -245,13 +251,13 @@ class BINStructTest { fun `test delete one child root`() { val num = mutableListOf(3, 6) for (value in num) { - classUnderTest.insert(value) + treeStruct.insert(value) } - classUnderTest.delete(3) + treeStruct.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 + 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) @@ -262,17 +268,17 @@ class BINStructTest { 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) + treeStruct.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 + 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) @@ -288,15 +294,15 @@ class BINStructTest { fun `test delete two child only three element`() { val num = mutableListOf(2, 1, 3) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(2) + treeStruct.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 + 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) @@ -309,18 +315,18 @@ class BINStructTest { fun `test delete two child without family`() { val num = mutableListOf(10, 7, 5, 4, 6) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(7) + treeStruct.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 + 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) @@ -336,17 +342,17 @@ class BINStructTest { fun `test two child double delete and delete root`() { val num = mutableListOf(6, 8, 10, 7) for (i in num) { - classUnderTest.insert(i) + treeStruct.insert(i) } - classUnderTest.delete(6) - classUnderTest.delete(7) + treeStruct.delete(6) + treeStruct.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 + 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) @@ -359,18 +365,18 @@ class BINStructTest { 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) + treeStruct.insert(i) } - classUnderTest.delete(8) + treeStruct.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 + 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) @@ -385,16 +391,16 @@ class BINStructTest { 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) + treeStruct.insert(i) } - classUnderTest.delete(8) + treeStruct.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 + 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) @@ -407,17 +413,17 @@ class BINStructTest { 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) + treeStruct.insert(i) } - classUnderTest.delete(8) + treeStruct.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 + 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) @@ -431,18 +437,18 @@ class BINStructTest { 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) + treeStruct.insert(i) } - classUnderTest.delete(12) + treeStruct.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 + 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) @@ -457,19 +463,32 @@ class BINStructTest { 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) + treeStruct.insert(i) } - classUnderTest.delete(8) + treeStruct.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 + 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/utils/BINAnalyzer.kt b/lib/src/test/kotlin/utils/BINAnalyzer.kt new file mode 100644 index 0000000..0584b70 --- /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>( + val assertMethod: (input: String) -> Unit = { + throw BugInImplementException(it) + } +) : Analyzer> { + + override fun checkTree(root: BINNode) { + checkInvariant(root) + } + + private tailrec fun checkInvariant(node: BINNode) { + if ((node.left == null) && (node.right == null)) return + + node.right?.let { + when { + it.value == node.value -> { + assertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") + return@checkInvariant + } + + it.value < node.value -> { + assertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") + return@checkInvariant + } + + else -> {} + } + } + + node.left?.let { + when { + it.value == node.value -> { + assertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") + return@checkInvariant + } + + it.value > node.value -> { + assertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") + return@checkInvariant + } + + else -> {} + } + } + } + +} \ No newline at end of file From ebf90632ceb510f377db65806c02e44fa4bbbe7e Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 16 Apr 2023 19:07:21 +0300 Subject: [PATCH 097/212] fix: Rewrite interface Analyzer. --- lib/src/test/kotlin/utils/Analyzer.kt | 11 +++++++++-- lib/src/test/kotlin/utils/BINAnalyzer.kt | 14 +++++++------- lib/src/test/kotlin/utils/RBAnalyzer.kt | 20 ++++++++++---------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/lib/src/test/kotlin/utils/Analyzer.kt b/lib/src/test/kotlin/utils/Analyzer.kt index 5a342f9..6131d02 100644 --- a/lib/src/test/kotlin/utils/Analyzer.kt +++ b/lib/src/test/kotlin/utils/Analyzer.kt @@ -2,9 +2,16 @@ package utils import treelib.abstractTree.Node -interface Analyzer< +abstract class Analyzer< Pack : Comparable, NodeType : Node, > { - fun checkTree(root: NodeType) + + 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 index 0584b70..43dc5f5 100644 --- a/lib/src/test/kotlin/utils/BINAnalyzer.kt +++ b/lib/src/test/kotlin/utils/BINAnalyzer.kt @@ -4,27 +4,27 @@ import treelib.binTree.BINNode import treelib.singleObjects.exceptions.BugInImplementException class BINAnalyzer>( - val assertMethod: (input: String) -> Unit = { + override val assertMethod: (input: String) -> Unit = { throw BugInImplementException(it) } -) : Analyzer> { +) : Analyzer>() { override fun checkTree(root: BINNode) { checkInvariant(root) } - private tailrec fun checkInvariant(node: BINNode) { + private fun checkInvariant(node: BINNode) { if ((node.left == null) && (node.right == null)) return node.right?.let { when { it.value == node.value -> { - assertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") + wrappedAssertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") return@checkInvariant } it.value < node.value -> { - assertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") + wrappedAssertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") return@checkInvariant } @@ -35,12 +35,12 @@ class BINAnalyzer>( node.left?.let { when { it.value == node.value -> { - assertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") + wrappedAssertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") return@checkInvariant } it.value > node.value -> { - assertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") + wrappedAssertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") return@checkInvariant } diff --git a/lib/src/test/kotlin/utils/RBAnalyzer.kt b/lib/src/test/kotlin/utils/RBAnalyzer.kt index 1932026..1b04aff 100644 --- a/lib/src/test/kotlin/utils/RBAnalyzer.kt +++ b/lib/src/test/kotlin/utils/RBAnalyzer.kt @@ -5,15 +5,15 @@ import treelib.singleObjects.Markers import treelib.singleObjects.exceptions.BugInImplementException class RBAnalyzer>( - val assertMethod: (input: String) -> Unit = { + override val assertMethod: (input: String) -> Unit = { throw BugInImplementException(it) } -) : Analyzer> { +) : Analyzer>() { /** Magic number for error := -9999999 -> just an impossible value **/ private val errorMagicNumber = -9999999 override fun checkTree(root: RBNode) { - if (root.color != Markers.BLACK) assertMethod("The root isn't black!!!") + if (root.color != Markers.BLACK) wrappedAssertMethod("The root isn't black!!!") checkInvariant(root) } @@ -28,17 +28,17 @@ class RBAnalyzer>( node.right?.let { when { it.value == node.value -> { - assertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") + wrappedAssertMethod("parent.value == RightChild.value => [${node.value} == ${it.value}]") return@checkInvariant errorMagicNumber } it.value < node.value -> { - assertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") + wrappedAssertMethod("parent.value > RightChild.value => [${node.value} > ${it.value}]") return@checkInvariant errorMagicNumber } (it.color == Markers.RED) && (node.color == Markers.RED) -> { - assertMethod("parent.color == RED == RightChild.color => [parent - ${node.value} RightChild - ${it.value}]") + wrappedAssertMethod("parent.color == RED == RightChild.color => [parent - ${node.value} RightChild - ${it.value}]") return@checkInvariant errorMagicNumber } @@ -49,17 +49,17 @@ class RBAnalyzer>( node.left?.let { when { it.value == node.value -> { - assertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") + wrappedAssertMethod("parent.value == LeftChild.value => [${node.value} == ${it.value}]") return@checkInvariant errorMagicNumber } it.value > node.value -> { - assertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") + wrappedAssertMethod("parent.value < LeftChild.value => [${node.value} < ${it.value}]") return@checkInvariant errorMagicNumber } (it.color == Markers.RED) && (node.color == Markers.RED) -> { - assertMethod("parent.color == RED == LeftChild.color => [parent - ${node.value} LeftChild - ${it.value}]") + wrappedAssertMethod("parent.color == RED == LeftChild.color => [parent - ${node.value} LeftChild - ${it.value}]") return@checkInvariant errorMagicNumber } @@ -73,7 +73,7 @@ class RBAnalyzer>( if (leftBlackCount < 0 || rightBlackCount < 0) return errorMagicNumber if (leftBlackCount != rightBlackCount) { - assertMethod( + wrappedAssertMethod( "Number of black nodes does not match in children: parent.value - ${node.value} =>[left - $leftBlackCount] != [right - $rightBlackCount]" ) return errorMagicNumber From 47de4ab7ab167ed8c95e59d5a605ab650548d2a3 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Sun, 16 Apr 2023 22:26:04 +0300 Subject: [PATCH 098/212] feat: added tests for the invariant --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 2 +- lib/src/test/kotlin/treelib/AVLStructTest.kt | 70 ++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 898a40e..ebdb05c 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -136,7 +136,7 @@ abstract class TreeStruct< if (root == null) return generateStateFind(null) while (true) { - if (obj == currentNode?.value) return generateStateFind(currentNode) + if (obj == currentNode?.value) return generateStateFind(currentNode, currentNode) else { currentNode?.let { if (obj > it.value) currentNode = it.right diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 25ce4cb..347892c 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -2,18 +2,84 @@ 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 utils.AVLAnalyzer import utils.TreeStructWrapper @DisplayName("Test: AVL Struct") class AVLStructTest { - val treeW = TreeStructWrapper, AVLStateContainer, AVLStruct>() - var treeStruct = AVLStruct() + private val treeW = TreeStructWrapper, 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) + } + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @Test + fun `test height 3`() { + val num = mutableListOf(3, 2, 1, 4) + for (i in num) { + treeStruct.insert(i) + } + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @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) + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @Test + fun `test 100000 arguments`() { + for (i in 1..100000) { + treeStruct.insert(i) + } + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @Test + fun `test 100000 arguments and delete`() { + for (i in 1..100000) { + treeStruct.insert(i) + } + treeStruct.delete(5000) + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @Test + fun `test two arguments`() { + treeStruct.insert(2) + treeStruct.insert(3) + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } + + @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) + } + treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + } } \ No newline at end of file From fb955245def9c6ce60b8215d8a041475ed6b7a6d Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Sun, 16 Apr 2023 22:26:39 +0300 Subject: [PATCH 099/212] feat: Added invariant analyzer. --- lib/src/test/kotlin/utils/AVLAnalyzer.kt | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 lib/src/test/kotlin/utils/AVLAnalyzer.kt diff --git a/lib/src/test/kotlin/utils/AVLAnalyzer.kt b/lib/src/test/kotlin/utils/AVLAnalyzer.kt new file mode 100644 index 0000000..b8a9ac4 --- /dev/null +++ b/lib/src/test/kotlin/utils/AVLAnalyzer.kt @@ -0,0 +1,42 @@ +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) + } + } +} From 07afaa2f25cd8ac8e41895241779ed054c6b2b58 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 17 Apr 2023 14:07:00 +0300 Subject: [PATCH 100/212] feat: Add fuzzers with partial functionality. --- .../kotlin/utils/fuzzers/AVLStructFuzzer.kt | 7 + .../kotlin/utils/fuzzers/BINStructFuzzer.kt | 16 +++ .../kotlin/utils/fuzzers/RBStructFuzzer.kt | 16 +++ .../kotlin/utils/fuzzers/TreeStructFuzzer.kt | 128 ++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt create mode 100644 lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt create mode 100644 lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt create mode 100644 lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt 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..01d49c5 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt @@ -0,0 +1,7 @@ +package utils.fuzzers + +import treelib.avlTree.AVLNode + +class AVLStructFuzzer>(){ +// TODO +} \ 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..5ce3959 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt @@ -0,0 +1,16 @@ +package utils.fuzzers + +import treelib.binTree.BINNode +import treelib.binTree.BINStateContainer +import treelib.binTree.BINStruct +import utils.BINAnalyzer + +class BINStructFuzzer>( + override val baseInput: Array, + override val assertMethod: (input: String) -> Unit +): TreeStructFuzzer, 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..6b59799 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt @@ -0,0 +1,16 @@ +package utils.fuzzers + +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct +import utils.RBAnalyzer + +class RBStructFuzzer>( + override val baseInput: Array, + override val assertMethod: (input: String) -> Unit +): TreeStructFuzzer, 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..a5390b9 --- /dev/null +++ b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt @@ -0,0 +1,128 @@ +package utils.fuzzers + +import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer +import treelib.abstractTree.TreeStruct +import kotlin.random.Random +import utils.Analyzer +import treelib.singleObjects.exceptions.* +import kotlin.random.nextInt +import utils.TreeStructWrapper +import java.io.File +import java.time.Instant + +abstract class TreeStructFuzzer< + Pack : Comparable, + NodeType : Node, + 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() + println(dataSize) + 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() +// println("./$dirPath/$testId.txt") + for (index in testSet) { +// print("${baseInput[index]} ") + file.appendText("${baseInput[index]} \n") + } +// println() + } + +} \ No newline at end of file From a7b8400edd68056bb81097fd98e26640245ffbb2 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 17 Apr 2023 14:13:04 +0300 Subject: [PATCH 101/212] fix: Fix bug in AVLStruct.find method. --- .../kotlin/treelib/abstractTree/TreeStruct.kt | 6 +++--- lib/src/test/kotlin/treelib/RBStructTest.kt | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index ebdb05c..d2fe865 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -133,7 +133,7 @@ abstract class TreeStruct< protected fun findItem(obj: Pack): State { var currentNode = root - if (root == null) return generateStateFind(null) + if (root == null) return generateStateFind(null, null) while (true) { if (obj == currentNode?.value) return generateStateFind(currentNode, currentNode) @@ -141,7 +141,7 @@ abstract class TreeStruct< currentNode?.let { if (obj > it.value) currentNode = it.right else currentNode = it.left - } ?: return generateStateFind(null) + } ?: return generateStateFind(null, null) } } } @@ -166,7 +166,7 @@ abstract class TreeStruct< } updateNode.value = item - return generateStateInsert(null) + return generateStateInsert(null, null) } protected abstract fun generateStateDelete( diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index 5995dd4..606ca40 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -7,6 +7,7 @@ import treelib.rbTree.RBStruct import treelib.singleObjects.Markers import utils.RBAnalyzer import utils.TreeStructWrapper +import utils.fuzzers.RBStructFuzzer import kotlin.test.assertEquals @DisplayName("Test: Red-Black Tree Struct") @@ -30,6 +31,7 @@ class RBStructTest { assertAll( { assertEquals(root?.value, 6) }, { assertEquals(root?.color, Markers.BLACK) }, + { analyzer.checkTree(root!!) } ) } @@ -41,6 +43,7 @@ class RBStructTest { assertAll( { assertEquals(root?.left?.value, 3) }, { assertEquals(root?.left?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } ) } @@ -52,6 +55,7 @@ class RBStructTest { assertAll( { assertEquals(root?.right?.value, 8) }, { assertEquals(root?.right?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } ) } @@ -66,6 +70,7 @@ class RBStructTest { { assertEquals(root?.left?.value, 3) }, { assertEquals(root?.right?.color, Markers.RED) }, { assertEquals(root?.left?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } ) } @@ -79,6 +84,7 @@ class RBStructTest { assertAll( { assertEquals(root?.value, 8) }, { assertEquals(root?.color, Markers.BLACK) }, + { analyzer.checkTree(root!!) } ) } @@ -91,6 +97,7 @@ class RBStructTest { assertAll( { assertEquals(8, root?.value) }, { assertEquals(Markers.BLACK, root?.color) }, + { analyzer.checkTree(root!!) } ) } @@ -103,6 +110,20 @@ class RBStructTest { 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 From 3a5c9fd11933a935a2b4009d39a393d7273da151 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 17 Apr 2023 20:53:59 +0300 Subject: [PATCH 102/212] fix: Fix bug in TreeStruct.insert() method (test: after insert of two containers). --- lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index d2fe865..5b96f98 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -141,7 +141,8 @@ abstract class TreeStruct< currentNode?.let { if (obj > it.value) currentNode = it.right else currentNode = it.left - } ?: return generateStateFind(null, null) + } + if (currentNode == null) return generateStateFind(null, null) } } } @@ -162,7 +163,8 @@ abstract class TreeStruct< linkNewNode(currentNode, parentNode) - return generateStateInsert(currentNode, parentNode) + if (parentNode == null) return generateStateInsert(currentNode, currentNode) + else return generateStateInsert(currentNode, parentNode) } updateNode.value = item From dbe672ac208b97998019e462221fd7aef73f13ef Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 17 Apr 2023 20:56:33 +0300 Subject: [PATCH 103/212] feat: Implement partial functionality of AVLStructFuzzer. --- lib/src/test/kotlin/utils/AVLAnalyzer.kt | 2 -- .../test/kotlin/utils/fuzzers/AVLStructFuzzer.kt | 13 +++++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/src/test/kotlin/utils/AVLAnalyzer.kt b/lib/src/test/kotlin/utils/AVLAnalyzer.kt index b8a9ac4..15e7825 100644 --- a/lib/src/test/kotlin/utils/AVLAnalyzer.kt +++ b/lib/src/test/kotlin/utils/AVLAnalyzer.kt @@ -4,8 +4,6 @@ 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 diff --git a/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt index 01d49c5..7cbed69 100644 --- a/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt @@ -1,7 +1,16 @@ package utils.fuzzers import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer +import treelib.avlTree.AVLStruct +import utils.AVLAnalyzer -class AVLStructFuzzer>(){ -// TODO +class AVLStructFuzzer>( + override val baseInput: Array, + override val assertMethod: (input: String) -> Unit +) : TreeStructFuzzer, AVLAnalyzer, AVLStateContainer, AVLStruct>() { + + override fun createTreeStruct(): AVLStruct = AVLStruct() + + override fun createAnalyzer(): AVLAnalyzer = AVLAnalyzer(assertMethod) } \ No newline at end of file From d5de9e37c655fffcdf4164d13065b46ff97bd8ed Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Wed, 19 Apr 2023 01:59:08 +0300 Subject: [PATCH 104/212] feat:add TreeWrapper and BINTreeTest --- lib/build.gradle.kts | 2 + lib/src/test/kotlin/treelib/BINTreeTest.kt | 86 ++++++++++++++++++++++ lib/src/test/kotlin/utils/TreeWrapper.kt | 24 ++++++ 3 files changed, 112 insertions(+) create mode 100644 lib/src/test/kotlin/treelib/BINTreeTest.kt create mode 100644 lib/src/test/kotlin/utils/TreeWrapper.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 3782254..9a5b1b8 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -21,6 +21,7 @@ repositories { } dependencies { + testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") 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") @@ -31,6 +32,7 @@ dependencies { implementation("org.xerial", "sqlite-jdbc", sqliteJdbcVersion) } + tasks.test { finalizedBy(tasks.jacocoTestReport) useJUnitPlatform() diff --git a/lib/src/test/kotlin/treelib/BINTreeTest.kt b/lib/src/test/kotlin/treelib/BINTreeTest.kt new file mode 100644 index 0000000..172c545 --- /dev/null +++ b/lib/src/test/kotlin/treelib/BINTreeTest.kt @@ -0,0 +1,86 @@ +package treelib + +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import treelib.binTree.BINNode +import treelib.binTree.BINStateContainer +import treelib.binTree.BINStruct +import treelib.binTree.BINTree +import utils.TreeWrapper +import treelib.singleObjects.Container +import utils.TreeStructWrapper +import kotlin.test.assertEquals + +class BINTreeTest { + private val tree = BINTree() + private val treeW = + TreeWrapper>, BINStateContainer>, BINStruct>, BINTree>() + private val treeSW = + TreeStructWrapper, BINNode>, 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/utils/TreeWrapper.kt b/lib/src/test/kotlin/utils/TreeWrapper.kt new file mode 100644 index 0000000..5ca8ea7 --- /dev/null +++ b/lib/src/test/kotlin/utils/TreeWrapper.kt @@ -0,0 +1,24 @@ +package utils + +import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer +import treelib.abstractTree.Tree +import treelib.abstractTree.TreeStruct +import treelib.singleObjects.Container + + +class TreeWrapper < + V : Comparable, + Value, + NodeType: Node, NodeType>, + State: StateContainer, NodeType>, + TStruct: TreeStruct, NodeType, State>, + 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 From 00353821ddec40a94459a5bcaa887ac849b36281 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Wed, 19 Apr 2023 03:15:39 +0300 Subject: [PATCH 105/212] fix: delete !! --- lib/src/test/kotlin/treelib/AVLStructTest.kt | 35 ++++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 347892c..1e522fe 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -28,7 +28,10 @@ class AVLStructTest { for (i in num) { treeStruct.insert(i) } - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } @Test @@ -37,7 +40,10 @@ class AVLStructTest { for (i in num) { treeStruct.insert(i) } - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } @Test @@ -47,7 +53,10 @@ class AVLStructTest { treeStruct.insert(i) } treeStruct.delete(2) - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } @Test @@ -55,7 +64,10 @@ class AVLStructTest { for (i in 1..100000) { treeStruct.insert(i) } - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } @Test @@ -64,14 +76,20 @@ class AVLStructTest { treeStruct.insert(i) } treeStruct.delete(5000) - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + 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) - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } @Test @@ -80,6 +98,9 @@ class AVLStructTest { for (i in num) { treeStruct.insert(i) } - treeH.checkTree(treeW.getPrivateNode(treeStruct)!!) + val node = treeW.getPrivateNode(treeStruct) + if (node != null) { + treeW.getPrivateNode(treeStruct)?.let { treeH.checkTree(it) } + } } } \ No newline at end of file From 1bdaf192bdffc9d06520053b103be8bda9920d8d Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Wed, 19 Apr 2023 03:29:17 +0300 Subject: [PATCH 106/212] feat: AVLTreeTest --- lib/src/test/kotlin/treelib/AVLTreeTest.kt | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 lib/src/test/kotlin/treelib/AVLTreeTest.kt diff --git a/lib/src/test/kotlin/treelib/AVLTreeTest.kt b/lib/src/test/kotlin/treelib/AVLTreeTest.kt new file mode 100644 index 0000000..5531d35 --- /dev/null +++ b/lib/src/test/kotlin/treelib/AVLTreeTest.kt @@ -0,0 +1,76 @@ +package treelib + +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer +import treelib.avlTree.AVLStruct +import treelib.avlTree.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>, AVLStateContainer>, AVLStruct>, AVLTree>() + private val treeSW = + TreeStructWrapper, AVLNode>, 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 From d8ecce0e2a1bcd168715f2d69d49fe1122f9145a Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Wed, 19 Apr 2023 04:26:57 +0300 Subject: [PATCH 107/212] feat: RBTreeTest --- lib/src/test/kotlin/treelib/RBTreeTest.kt | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 lib/src/test/kotlin/treelib/RBTreeTest.kt diff --git a/lib/src/test/kotlin/treelib/RBTreeTest.kt b/lib/src/test/kotlin/treelib/RBTreeTest.kt new file mode 100644 index 0000000..5a3ceee --- /dev/null +++ b/lib/src/test/kotlin/treelib/RBTreeTest.kt @@ -0,0 +1,58 @@ +package treelib + +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct +import treelib.rbTree.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>, RBStateContainer>, RBStruct>, RBTree>() + private val treeSW = + TreeStructWrapper, RBNode>, 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 From ac0b6426599cd0cdd8e7c0ce9dd734053217339a Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 15 Apr 2023 15:27:37 +0300 Subject: [PATCH 108/212] fix: Partially change exceptions --- .../kotlin/treelib/avlTree/AVLBalancer.kt | 25 ++++----- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 55 +++++++++---------- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 703ea69..5f80a87 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -1,11 +1,8 @@ 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>() { +class AVLBalancer>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { return (getHeight(node?.left) - getHeight(node?.right)).toInt() } @@ -16,19 +13,18 @@ class AVLBalancer>(private var root: AVLNode?) : private fun updateHeight(currentNode: AVLNode?) { if (currentNode != null) - currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u + 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 +34,7 @@ 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 +44,7 @@ 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 +54,5 @@ class AVLBalancer>(private var root: AVLNode?) : } return currentNode } + } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index fe73c5b..67e5427 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -2,9 +2,6 @@ package treelib.rbTree 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>() { @@ -30,7 +27,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,12 +35,13 @@ 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 IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") val uncle = getUncle(node) when { /** node insertion case **/ @@ -58,14 +56,14 @@ 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 IllegalStateException() // IllegalNodeStateException() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -76,17 +74,17 @@ class RBBalancer>(private var root: RBNode?): Balan rightRotate(parent) currentNode = parent } - parent = currentNode.parent?.parent ?: throw IllegalNodeStateException() + parent = currentNode.parent?.parent ?: throw NullPointerException() // 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) -> @@ -116,12 +114,11 @@ class RBBalancer>(private var root: RBNode?): Balan firstCase(node, null) } - - else -> throw IllegalNodeStateException() + else -> throw IllegalStateException() } } } - throw ImpossibleCaseException() + throw IllegalStateException() } private fun afterInsert(node: RBNode): RBNode { @@ -130,7 +127,7 @@ class RBBalancer>(private var root: RBNode?): Balan val uncle = getUncle(currentNode) if (uncle?.color == Markers.RED) { currentNode.parent?.color = Markers.BLACK - currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() + currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK } @@ -145,7 +142,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 +151,13 @@ class RBBalancer>(private var root: RBNode?): Balan getRoot(parent) } - else -> getRoot(node ?: throw IllegalBaseNodeException()) + else -> getRoot(node ?: throw NullPointerException() /* IllegalNodeStateException() */) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw IllegalBaseNodeException() + var brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() if (brother.color == Markers.RED) throw NullPointerException() @@ -187,7 +184,7 @@ class RBBalancer>(private var root: RBNode?): Balan brother.color = Markers.RED } else { - throw NullPointerException() + throw IllegalStateException() } } parent.right -> @@ -206,17 +203,17 @@ class RBBalancer>(private var root: RBNode?): Balan brother.right?.color = Markers.BLACK } else { - throw NullPointerException() + 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 NullPointerException() // IllegalNodeStateException() when (brother.color) { Markers.RED -> thirdCaseSubFirst(brother, parent) Markers.BLACK -> thirdCaseSubSecond(brother, parent) @@ -228,7 +225,7 @@ class RBBalancer>(private var root: RBNode?): Balan when (brother) { brother.parent?.left -> { - var rightBrotherSon = brother.right ?: throw NullPointerException() + var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED @@ -244,7 +241,7 @@ class RBBalancer>(private var root: RBNode?): Balan rightBrotherSon.color = Markers.RED leftRotate(rightBrotherSon) - rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() + rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -260,14 +257,14 @@ class RBBalancer>(private var root: RBNode?): Balan 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 NullPointerException()) // IllegalNodeStateException() return } if (leftBrotherSon.left?.color == Markers.RED) { rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED - leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() + leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } @@ -277,7 +274,7 @@ class RBBalancer>(private var root: RBNode?): Balan leftRotate(parent) } } - else -> throw NullPointerException() + else -> throw IllegalStateException() } } @@ -315,7 +312,7 @@ class RBBalancer>(private var root: RBNode?): Balan } } - else -> throw NullPointerException() + else -> throw IllegalStateException() } } From b1b4f1da9c220c83bd561c7b62282b1b22963704 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 14:37:18 +0300 Subject: [PATCH 109/212] feat: Implement neo4j database sketch --- lib/build.gradle.kts | 16 ++- lib/src/main/kotlin/CONTAINER.conf | 3 + lib/src/main/kotlin/DataBase.kt | 172 ++++++++++++++++++++++++ lib/src/main/kotlin/testNeo4j.sh | 15 +++ lib/src/main/kotlin/treelib/DBNodeRB.kt | 14 ++ settings.gradle.kts | 2 +- 6 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 lib/src/main/kotlin/CONTAINER.conf create mode 100644 lib/src/main/kotlin/DataBase.kt create mode 100644 lib/src/main/kotlin/testNeo4j.sh create mode 100644 lib/src/main/kotlin/treelib/DBNodeRB.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 9a5b1b8..0dfd5f1 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -1,5 +1,4 @@ val sqliteJdbcVersion: String by project - plugins { java kotlin("jvm") version "1.8.10" @@ -21,15 +20,20 @@ repositories { } dependencies { - testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") - 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") - // JDBC Sqlite + 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") + implementation(kotlin("stdlib-jdk8")) } diff --git a/lib/src/main/kotlin/CONTAINER.conf b/lib/src/main/kotlin/CONTAINER.conf new file mode 100644 index 0000000..9b9fb0a --- /dev/null +++ b/lib/src/main/kotlin/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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt new file mode 100644 index 0000000..ddc72a3 --- /dev/null +++ b/lib/src/main/kotlin/DataBase.kt @@ -0,0 +1,172 @@ + +import org.neo4j.driver.AuthTokens +import org.neo4j.driver.Driver +import org.neo4j.driver.GraphDatabase +import org.neo4j.driver.exceptions.SessionExpiredException +import treelib.DBNodeRB +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() + } + + } + + // я наверное смогу получить рут, используя фильтр что-то вроде: на данный узел не указывает ни один другой узел с отношением [left_child] / [right_child] + + fun , V> saveChanges(preOrder: Array>, inOrder: Array>) { + + /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ + val session = driver?.session() ?: throw IOException() + + var inOrderIndex = 0 + var preOrderIndex = 0 + val set = HashSet>() + val stack = LinkedList>() + + while (preOrderIndex in preOrder.indices) { + do { + val currentNode = preOrder[preOrderIndex] + if (preOrderIndex == 0) { + session.executeWrite { tx -> + tx.run( + "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", + mutableMapOf( + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + if (!stack.isEmpty()) { + if ( set.contains( stack.peek() ) ) { + set.remove(stack.peek()) + val parentNode = stack.pop() + session.executeWrite {tx -> + 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}) " + + "MERGE (parent)-[:RIGHT_SON]->(son)", + mutableMapOf( + "parentNodeValue" to parentNode.value, + "parentNodeKey" to parentNode.key, + "parentNodeColor" to parentNode.color.toString(), + "parentNodeX" to parentNode.x, + "parentNodeY" to parentNode.y, + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + else { + val parentNode = stack.peek() + session.executeWrite {tx -> + 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}) " + + "MERGE (parent)-[:LEFT_SON]->(son)", + mutableMapOf( + "parentNodeValue" to parentNode.value, + "parentNodeKey" to parentNode.key, + "parentNodeColor" to parentNode.color.toString(), + "parentNodeX" to parentNode.x, + "parentNodeY" to parentNode.y, + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + } + stack.push(currentNode) + } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) + + var currentNode: DBNodeRB? = null + + while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().key == inOrder[inOrderIndex].key) { + currentNode = stack.pop() + ++inOrderIndex + } + + if (currentNode != null) { + set.add(currentNode) + stack.push(currentNode) + } + + } + + session.close() + } + + // Это скорее всего будем запускать при открытии приложения, нужно будет сразу восстановить дерево + + fun exportRBtree() { + + 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 ").list() + .map{ DBNodeRB( + value = it.values().get(0).toString(), + key = it.values().get(1).toString(), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(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{ DBNodeRB( + value = it.values().get(0).toString(), + key = it.values().get(1).toString(), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(4).toString().toDouble()) + } + } + + session.close() + + } + + override fun close() { + driver?.close() + } +} + +// neo4j-admin, backup, restore +/* +у меня есть вершины и ребра между ними, надо уметь сохранять дерево в базе, но как? +вопрос: когда заносить изменения в бд и как это реализовывать? +надо еще подумать над оптимизацией, те чтобы не пересобирать дерево в бд каждый раз, когда мы добавили за сессию всего один узел + +root = tx.run("MATCH (parent: Node)-->(son: Node) " + + "WHERE NOT ()-->(parent) " + + "RETURN parent.value, parent.key, parent.color, parent.x, parent.y").list().map {it.values()}.get(0) + */ diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/testNeo4j.sh new file mode 100644 index 0000000..97ad8db --- /dev/null +++ b/lib/src/main/kotlin/testNeo4j.sh @@ -0,0 +1,15 @@ + +BASEDIR=$(realpath "$(dirname "$0")") + +. "${BASEDIR}/CONTAINER.conf" + +docker run \ + --rm \ + --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 \ + +#docker stop neo4j:latest diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt new file mode 100644 index 0000000..d048930 --- /dev/null +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -0,0 +1,14 @@ +package treelib + +import treelib.singleObjects.Markers + + +class DBNodeRB, V>(val value: V, + val key: K, + val color: Markers = Markers.RED, + val x: Double = 0.0, + val y: Double = 0.0) { + + + +} 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") From 45159c3e1663edfa45c5f4b70d9e3da0bddb11d7 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 19:32:32 +0300 Subject: [PATCH 110/212] feat: Implement initialize tree from DB --- lib/src/main/kotlin/Controller.kt | 50 +++++++++++++++++ lib/src/main/kotlin/DataBase.kt | 55 +++++++++---------- lib/src/main/kotlin/treelib/DBNodeRB.kt | 9 +-- lib/src/main/kotlin/treelib/Main.kt | 39 +++++++++++++ .../main/kotlin/treelib/abstractTree/Tree.kt | 12 ++-- 5 files changed, 124 insertions(+), 41 deletions(-) create mode 100644 lib/src/main/kotlin/Controller.kt create mode 100644 lib/src/main/kotlin/treelib/Main.kt diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/Controller.kt new file mode 100644 index 0000000..cc4e699 --- /dev/null +++ b/lib/src/main/kotlin/Controller.kt @@ -0,0 +1,50 @@ +import treelib.DBNodeRB +import treelib.rbTree.RBStruct +import treelib.rbTree.RBTree +import treelib.singleObjects.Container + +class Controller { + + fun initTree() { + 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.restoreTreeFromDatabase(orders.first, orders.second) + } + + fun saveTree() { + val tree = RBTree() + tree.putItem(Pair(25, 1)) + tree.putItem(Pair(15, 1)) + tree.putItem(Pair(50, 1)) + tree.putItem(Pair(10, 1)) + tree.putItem(Pair(22, 1)) + tree.putItem(Pair(35, 1)) + tree.putItem(Pair(70, 1)) + tree.putItem(Pair(4, 1)) + tree.putItem(Pair(12, 1)) + tree.putItem(Pair(18, 1)) + tree.putItem(Pair(24, 1)) + tree.putItem(Pair(31, 1)) + tree.putItem(Pair(44, 1)) + tree.putItem(Pair(66, 1)) + tree.putItem(Pair(90, 1)) + + val neo4jDB = Neo4jRepository() + neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + + val preOrder = tree.preOrder().map { DBNodeRB(it.value, it.color) } + val inOrder = tree.inOrder().map { DBNodeRB(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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index ddc72a3..3e94aac 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -4,6 +4,7 @@ import org.neo4j.driver.Driver import org.neo4j.driver.GraphDatabase import org.neo4j.driver.exceptions.SessionExpiredException import treelib.DBNodeRB +import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable import java.io.IOException @@ -21,31 +22,29 @@ class Neo4jRepository: Closeable { } catch(ex: SessionExpiredException) { throw IOException() } - } - // я наверное смогу получить рут, используя фильтр что-то вроде: на данный узел не указывает ни один другой узел с отношением [left_child] / [right_child] - - fun , V> saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() var inOrderIndex = 0 var preOrderIndex = 0 - val set = HashSet>() - val stack = LinkedList>() + val set = HashSet>() + val stack = LinkedList>() while (preOrderIndex in preOrder.indices) { do { val currentNode = preOrder[preOrderIndex] + currentNode.value as Container<*, *> if (preOrderIndex == 0) { session.executeWrite { tx -> tx.run( "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", mutableMapOf( - "nodeValue" to currentNode.value, - "nodeKey" to currentNode.key, + "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 @@ -57,19 +56,20 @@ class Neo4jRepository: Closeable { if ( set.contains( stack.peek() ) ) { set.remove(stack.peek()) val parentNode = stack.pop() + parentNode.value as Container<*, *> session.executeWrite {tx -> 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}) " + "MERGE (parent)-[:RIGHT_SON]->(son)", mutableMapOf( - "parentNodeValue" to parentNode.value, - "parentNodeKey" to parentNode.key, + "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, - "nodeKey" to currentNode.key, + "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 @@ -79,19 +79,20 @@ class Neo4jRepository: Closeable { } else { val parentNode = stack.peek() + parentNode.value as Container<*, *> session.executeWrite {tx -> 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}) " + "MERGE (parent)-[:LEFT_SON]->(son)", mutableMapOf( - "parentNodeValue" to parentNode.value, - "parentNodeKey" to parentNode.key, + "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, - "nodeKey" to currentNode.key, + "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 @@ -103,9 +104,9 @@ class Neo4jRepository: Closeable { stack.push(currentNode) } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) - var currentNode: DBNodeRB? = null + var currentNode: DBNodeRB? = null - while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().key == inOrder[inOrderIndex].key) { + while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() ++inOrderIndex } @@ -120,20 +121,17 @@ class Neo4jRepository: Closeable { session.close() } - // Это скорее всего будем запускать при открытии приложения, нужно будет сразу восстановить дерево - - fun exportRBtree() { + fun exportRBtree(): Pair>>>, List>>>>{ val session = driver?.session() ?: throw IOException() - var preOrder: List>> = listOf() - var inOrder: List>> = listOf() + 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 ").list() .map{ DBNodeRB( - value = it.values().get(0).toString(), - key = it.values().get(1).toString(), + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, x = it.values().get(3).toString().toDouble(), y = it.values().get(4).toString().toDouble()) @@ -143,16 +141,17 @@ class Neo4jRepository: Closeable { "RETURN node.value, node.key, node.color, node.x, node.y " + "ORDER BY node.key").list() .map{ DBNodeRB( - value = it.values().get(0).toString(), - key = it.values().get(1).toString(), + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, x = it.values().get(3).toString().toDouble(), y = it.values().get(4).toString().toDouble()) } } - + session.close() + return Pair(preOrder, inOrder) + } override fun close() { diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt index d048930..b28228f 100644 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -3,12 +3,7 @@ package treelib import treelib.singleObjects.Markers -class DBNodeRB, V>(val value: V, - val key: K, +class DBNodeRB>(val value: Pack, val color: Markers = Markers.RED, val x: Double = 0.0, - val y: Double = 0.0) { - - - -} + val y: Double = 0.0) diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt new file mode 100644 index 0000000..0aeb3e0 --- /dev/null +++ b/lib/src/main/kotlin/treelib/Main.kt @@ -0,0 +1,39 @@ +package treelib + +import Controller + + +fun main() { + // 25, 15, 10, 4, 12, 22, 18, 24, 50, 35, 31, 44, 70, 66, 90 + // 4, 10, 12, 15, 18, 22, 24, 25, 31, 35, 44, 50, 66, 70, 90 + + /* + + val neo4jRep = Neo4jRepository() + val a1 = DBNodeRB(Container(Pair(1, 25))) + val a2 = DBNodeRB(Container(Pair(1, 15))) + val a3 = DBNodeRB(Container(Pair(1, 10))) + val a4 = DBNodeRB(Container(Pair(1, 4))) + val a5 = DBNodeRB(Container(Pair(1, 12))) + val a6 = DBNodeRB(Container(Pair(1, 22))) + val a7 = DBNodeRB(Container(Pair(1, 18))) + val a8 = DBNodeRB(Container(Pair(1, 24))) + val a9 = DBNodeRB(Container(Pair(1, 50))) + val a10 = DBNodeRB(Container(Pair(1, 35))) + val a11 = DBNodeRB(Container(Pair(1, 31))) + val a12 = DBNodeRB(Container(Pair(1, 44))) + val a13 = DBNodeRB(Container(Pair(1, 70))) + val a14 = DBNodeRB(Container(Pair(1, 66))) + val a15 = DBNodeRB(Container(Pair(1, 90))) + val preArr = arrayOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) + val inArr = arrayOf(a4, a3, a5, a2, a7, a6, a8, a1, a11, a10, a12, a9, a14, a13, a15) + neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") + neo4jRep.saveChanges(preArr, inArr) + */ + val controller = Controller() + controller.saveTree() + + //neo4jRep.exportRBtree() + + //neo4jRep.close() +} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index b64cca1..1607caa 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -33,15 +33,15 @@ abstract class Tree< 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) 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()) } From 5f07feaee390805c32f9750f4225e3721ce2c4e7 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 20:21:27 +0300 Subject: [PATCH 111/212] fix: Fix adding to database (neo4j) --- lib/src/main/kotlin/DataBase.kt | 82 ++++++++++-------- lib/src/main/kotlin/treelib/DBNodeRB.kt | 10 ++- .../kotlin/treelib/avlTree/AVLBalancer.kt | 15 ++-- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 85 ++++++++++--------- .../main/kotlin/treelib/rbTree/RBStruct.kt | 63 ++++++++++++-- 5 files changed, 161 insertions(+), 94 deletions(-) diff --git a/lib/src/main/kotlin/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index 3e94aac..e7ca4c6 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -1,4 +1,3 @@ - import org.neo4j.driver.AuthTokens import org.neo4j.driver.Driver import org.neo4j.driver.GraphDatabase @@ -10,21 +9,21 @@ import java.io.Closeable import java.io.IOException import java.util.* -class Neo4jRepository: Closeable { +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) { + } catch (ex: IllegalArgumentException) { + throw IOException() + } catch (ex: SessionExpiredException) { throw IOException() } } - fun > saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() @@ -53,15 +52,16 @@ class Neo4jRepository: Closeable { } } if (!stack.isEmpty()) { - if ( set.contains( stack.peek() ) ) { + if (set.contains(stack.peek())) { set.remove(stack.peek()) val parentNode = stack.pop() parentNode.value as Container<*, *> - session.executeWrite {tx -> - 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}) " + - "MERGE (parent)-[:RIGHT_SON]->(son)", + session.executeWrite { tx -> + 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}) " + + "MERGE (parent)-[:RIGHT_SON]->(son)", mutableMapOf( "parentNodeValue" to parentNode.value.pair.second, "parentNodeKey" to parentNode.value.pair.first, @@ -76,15 +76,15 @@ class Neo4jRepository: Closeable { ) ) } - } - else { + } else { val parentNode = stack.peek() parentNode.value as Container<*, *> - session.executeWrite {tx -> - 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}) " + - "MERGE (parent)-[:LEFT_SON]->(son)", + session.executeWrite { tx -> + 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}) " + + "MERGE (parent)-[:LEFT_SON]->(son)", mutableMapOf( "parentNodeValue" to parentNode.value.pair.second, "parentNodeKey" to parentNode.value.pair.first, @@ -106,7 +106,7 @@ class Neo4jRepository: Closeable { var currentNode: DBNodeRB? = null - while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { + while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() ++inOrderIndex } @@ -121,30 +121,38 @@ class Neo4jRepository: Closeable { session.close() } - fun exportRBtree(): Pair>>>, List>>>>{ + fun exportRBtree(): Pair>>>, List>>>> { 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 ").list() - .map{ DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble()) + session.executeRead { tx -> + preOrder = tx.run( + "MATCH (node: Node) " + + "RETURN node.value, node.key, node.color, node.x, node.y " + ).list() + .map { + DBNodeRB( + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(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{ DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(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 { + DBNodeRB( + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(4).toString().toDouble() + ) } } diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt index b28228f..cf067e1 100644 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -3,7 +3,9 @@ package treelib import treelib.singleObjects.Markers -class DBNodeRB>(val value: Pack, - val color: Markers = Markers.RED, - val x: Double = 0.0, - val y: Double = 0.0) +class DBNodeRB>( + val value: Pack, + val color: Markers = Markers.RED, + val x: Double = 0.0, + val y: Double = 0.0 +) diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 5f80a87..1a3afb9 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -2,7 +2,8 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancerNoParent -class AVLBalancer>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { +class AVLBalancer>(private var root: AVLNode?) : + BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { return (getHeight(node?.left) - getHeight(node?.right)).toInt() } @@ -13,14 +14,16 @@ class AVLBalancer>(private var root: AVLNode?): Bal private fun updateHeight(currentNode: AVLNode?) { if (currentNode != null) - currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right))+1u + currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u } override fun balance(stateContainer: AVLStateContainer): AVLNode { - val node = stateContainer.contentNode ?: throw IllegalStateException("") // IllegalBaseNodeException("A non-existent node (null) was passed to the method") + 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) { @@ -34,7 +37,8 @@ class AVLBalancer>(private var root: AVLNode?): Bal val balance = updateBalance(currentNode) if (balance == -2) { if (updateBalance(currentNode.right) == 1) { - currentNode.right = currentNode.right?.let { rightRotate(it) } ?: throw NullPointerException() // IllegalNodeStateException() + currentNode.right = currentNode.right?.let { rightRotate(it) } + ?: throw NullPointerException() // IllegalNodeStateException() updateHeight(currentNode.right?.right) } val balancedNode = leftRotate(currentNode) @@ -44,7 +48,8 @@ class AVLBalancer>(private var root: AVLNode?): Bal } if (balance == 2) { if (updateBalance(currentNode.left) == -1) { - currentNode.left = currentNode.left?.let { leftRotate(it) } ?: throw NullPointerException() // IllegalNodeStateException("There is no node required by the condition of the algorithm") + 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) diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 67e5427..211e007 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -3,7 +3,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent import treelib.singleObjects.Markers -class RBBalancer>(private var root: RBNode?): BalancerParent, RBStateContainer>() { +class RBBalancer>(private var root: RBNode?) : + BalancerParent, RBStateContainer>() { init { root?.color = Markers.BLACK @@ -39,14 +40,13 @@ class RBBalancer>(private var root: RBNode?): Balan return node.right == null && node.left == null } - override fun balance(stateContainer: RBStateContainer): RBNode { - val node = stateContainer.contentNode ?: - throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") + override fun balance(stateContainer: RBStateContainer): RBNode { + val node = stateContainer.contentNode + ?: throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") 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) { @@ -56,30 +56,35 @@ class RBBalancer>(private var root: RBNode?): Balan return getRoot(currentNode) } - var parent = currentNode.parent ?: throw IllegalStateException() // в данном случае родитель не может быть null + 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 IllegalStateException() // IllegalNodeStateException() + parent = + currentNode.parent?.parent ?: throw IllegalStateException() // 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 NullPointerException() // IllegalNodeStateException() + parent = + currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED currentNode.left?.color = Markers.RED } + else -> throw IllegalStateException() // невозможное условие выполнения } if (currentNode.parent == null) @@ -87,21 +92,21 @@ class RBBalancer>(private var root: RBNode?): Balan 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) || @@ -110,10 +115,11 @@ 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 IllegalStateException() } } @@ -130,8 +136,7 @@ class RBBalancer>(private var root: RBNode?): Balan currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK - } - else if(uncle != null){ + } else if (uncle != null) { return currentNode } } @@ -168,45 +173,41 @@ 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 { + } 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 { + } else { throw IllegalStateException() } } + else -> throw IllegalStateException() } } @@ -223,8 +224,7 @@ class RBBalancer>(private var root: RBNode?): Balan /** black parent and red brother **/ private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { - brother.parent?.left -> - { + brother.parent?.left -> { var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { @@ -241,7 +241,8 @@ class RBBalancer>(private var root: RBNode?): Balan rightBrotherSon.color = Markers.RED leftRotate(rightBrotherSon) - rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + rightBrotherSon = + rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -251,8 +252,8 @@ 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 @@ -264,7 +265,8 @@ class RBBalancer>(private var root: RBNode?): Balan if (leftBrotherSon.left?.color == Markers.RED) { rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED - leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + leftBrotherSon = + leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } @@ -274,6 +276,7 @@ class RBBalancer>(private var root: RBNode?): Balan leftRotate(parent) } } + else -> throw IllegalStateException() } } @@ -289,29 +292,27 @@ 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 IllegalStateException() } } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 67ffc27..ee6a216 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -1,9 +1,11 @@ package treelib.rbTree +import treelib.DBNodeRB 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>() { @@ -15,17 +17,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(insertNode) + ): RBStateContainer = RBStateContainer(insertNode) override fun generateStateFind( findNode: RBNode?, contentNode: RBNode?, - ): RBStateContainer = RBStateContainer(findNode) + ): RBStateContainer = RBStateContainer(findNode) override fun connectUnlinkedSubTreeWithParent( node: RBNode, @@ -39,11 +41,12 @@ 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 { @@ -62,12 +65,60 @@ class RBStruct> : root?.let { it.color = Markers.BLACK } ?: throw MultithreadingException(ImpossibleCaseException()) - } - else { + } else { if (node.value > parent.value) parent.right = node else parent.left = node node.parent = parent } return node } + + fun restoreTreeFromDatabase(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: DBNodeRB + + 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: DBNodeRB): RBNode { + val node = RBNode(value = drawNode.value, color = drawNode.color) + return node + } } From 129661be4f88797a851aad0e7d226785881e40c9 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 21:33:22 +0300 Subject: [PATCH 112/212] fix: Fix database interaction --- lib/src/main/kotlin/Controller.kt | 35 +--- lib/src/main/kotlin/DataBase.kt | 186 ++++++++++-------- lib/src/main/kotlin/initNeo4j.sh | 0 lib/src/main/kotlin/loadNeo4j.sh | 8 + lib/src/main/kotlin/testNeo4j.sh | 13 +- lib/src/main/kotlin/treelib/Main.kt | 29 ++- .../main/kotlin/treelib/abstractTree/Tree.kt | 37 ++-- .../kotlin/treelib/abstractTree/TreeStruct.kt | 39 ++-- .../kotlin/treelib/abstractTree/Vertex.kt | 5 + .../balanced/BalancedTreeStruct.kt | 4 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 6 +- .../main/kotlin/treelib/avlTree/AVLTree.kt | 10 +- .../main/kotlin/treelib/avlTree/AVLVertex.kt | 8 + .../main/kotlin/treelib/binTree/BINStruct.kt | 6 +- .../main/kotlin/treelib/binTree/BINTree.kt | 10 +- .../main/kotlin/treelib/binTree/BINVertex.kt | 5 + .../kotlin/treelib/rbTree/DrawRBVertex.kt | 10 + .../main/kotlin/treelib/rbTree/RBBalancer.kt | 24 +-- .../main/kotlin/treelib/rbTree/RBStruct.kt | 13 +- lib/src/main/kotlin/treelib/rbTree/RBTree.kt | 10 +- .../main/kotlin/treelib/rbTree/RBVertex.kt | 9 + 21 files changed, 292 insertions(+), 175 deletions(-) create mode 100644 lib/src/main/kotlin/initNeo4j.sh create mode 100644 lib/src/main/kotlin/loadNeo4j.sh create mode 100644 lib/src/main/kotlin/treelib/abstractTree/Vertex.kt create mode 100644 lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt create mode 100644 lib/src/main/kotlin/treelib/binTree/BINVertex.kt create mode 100644 lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt create mode 100644 lib/src/main/kotlin/treelib/rbTree/RBVertex.kt diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/Controller.kt index cc4e699..a59a3b0 100644 --- a/lib/src/main/kotlin/Controller.kt +++ b/lib/src/main/kotlin/Controller.kt @@ -1,6 +1,6 @@ -import treelib.DBNodeRB + +import treelib.rbTree.DrawRBVertex import treelib.rbTree.RBStruct -import treelib.rbTree.RBTree import treelib.singleObjects.Container class Controller { @@ -8,43 +8,28 @@ class Controller { fun initTree() { val neo4jDB = Neo4jRepository() neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + /*** orders.first = preOrder, orders.second = inOrder ***/ - val orders: Pair>>>, List>>>> = + val orders: Pair>>>, List>>>> = neo4jDB.exportRBtree() val RBtree = RBStruct>>() RBtree.restoreTreeFromDatabase(orders.first, orders.second) + neo4jDB.close() } - fun saveTree() { - val tree = RBTree() - tree.putItem(Pair(25, 1)) - tree.putItem(Pair(15, 1)) - tree.putItem(Pair(50, 1)) - tree.putItem(Pair(10, 1)) - tree.putItem(Pair(22, 1)) - tree.putItem(Pair(35, 1)) - tree.putItem(Pair(70, 1)) - tree.putItem(Pair(4, 1)) - tree.putItem(Pair(12, 1)) - tree.putItem(Pair(18, 1)) - tree.putItem(Pair(24, 1)) - tree.putItem(Pair(31, 1)) - tree.putItem(Pair(44, 1)) - tree.putItem(Pair(66, 1)) - tree.putItem(Pair(90, 1)) - + fun > saveTree(tree: RBStruct) { val neo4jDB = Neo4jRepository() neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") - val preOrder = tree.preOrder().map { DBNodeRB(it.value, it.color) } - val inOrder = tree.inOrder().map { DBNodeRB(it.value, it.color) } + // вот тут плохо, потому что тут надо получать не base nodes, а для рисовалки - neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray()) + 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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index e7ca4c6..809eb64 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -1,8 +1,9 @@ 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.DBNodeRB +import treelib.rbTree.DrawRBVertex import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable @@ -23,88 +24,50 @@ class Neo4jRepository : Closeable { } } - fun > saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() var inOrderIndex = 0 var preOrderIndex = 0 - val set = HashSet>() - val stack = LinkedList>() + val set = HashSet>() + val stack = LinkedList>() + var id = 0 while (preOrderIndex in preOrder.indices) { do { val currentNode = preOrder[preOrderIndex] - currentNode.value as Container<*, *> + //currentNode.value as Container<*, *> if (preOrderIndex == 0) { session.executeWrite { tx -> - tx.run( - "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", - mutableMapOf( - "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 - ) - ) + 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<*, *> + //parentNode.value as Container<*, *> session.executeWrite { tx -> - 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}) " + - "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 - ) - ) + createRightSon(tx, parentNode, currentNode, id) } + ++id } else { val parentNode = stack.peek() parentNode.value as Container<*, *> session.executeWrite { tx -> - 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}) " + - "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 - ) - ) + createLeftSon(tx, parentNode, currentNode, id) } + ++id } } stack.push(currentNode) - } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) + } while (preOrder[preOrderIndex++].value != inOrder[inOrderIndex].value && preOrderIndex < preOrder.size) - var currentNode: DBNodeRB? = null + var currentNode: DrawRBVertex? = null while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() @@ -121,23 +84,26 @@ class Neo4jRepository : Closeable { session.close() } - fun exportRBtree(): Pair>>>, List>>>> { + fun exportRBtree(): Pair>>>, List>>>> { + + /*** Вот тут короче надо обращаться к neo4j с темЮ чтобы она инициализировала себя как-то ***/ val session = driver?.session() ?: throw IOException() - var preOrder: List>>> = listOf() - var inOrder: List>>> = listOf() + 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 " + "RETURN node.value, node.key, node.color, node.x, node.y " + + "ORDER BY node.id" ).list() .map { - DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble() + 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() ) } @@ -147,11 +113,11 @@ class Neo4jRepository : Closeable { "ORDER BY node.key" ).list() .map { - DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble() + 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() ) } } @@ -162,18 +128,80 @@ class Neo4jRepository : Closeable { } + 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() } } - -// neo4j-admin, backup, restore -/* -у меня есть вершины и ребра между ними, надо уметь сохранять дерево в базе, но как? -вопрос: когда заносить изменения в бд и как это реализовывать? -надо еще подумать над оптимизацией, те чтобы не пересобирать дерево в бд каждый раз, когда мы добавили за сессию всего один узел - -root = tx.run("MATCH (parent: Node)-->(son: Node) " + - "WHERE NOT ()-->(parent) " + - "RETURN parent.value, parent.key, parent.color, parent.x, parent.y").list().map {it.values()}.get(0) - */ diff --git a/lib/src/main/kotlin/initNeo4j.sh b/lib/src/main/kotlin/initNeo4j.sh new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/main/kotlin/loadNeo4j.sh b/lib/src/main/kotlin/loadNeo4j.sh new file mode 100644 index 0000000..d4d9fcd --- /dev/null +++ b/lib/src/main/kotlin/loadNeo4j.sh @@ -0,0 +1,8 @@ + +BASEDIR=$(realpath "$(dirname "$0")") + +. "${BASEDIR}/CONTAINER.conf" + +docker stop "$CONTAINER_NAME" + +neo4j-admin dump --database=neo4j --to=/data/backups/ \ No newline at end of file diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/testNeo4j.sh index 97ad8db..87b69c4 100644 --- a/lib/src/main/kotlin/testNeo4j.sh +++ b/lib/src/main/kotlin/testNeo4j.sh @@ -3,13 +3,22 @@ BASEDIR=$(realpath "$(dirname "$0")") . "${BASEDIR}/CONTAINER.conf" +# -d docker run \ - --rm \ - --name "CONTAINER_NAME" \ + -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 \ +# -c /bin/bash +#neo4j-admin database dump neo4j --to-path=$HOME/neo4j/data:/data + +#docker ps -a + +#docker stop "$CONTAINER_NAME" + +#cp $HOME/neo4j/data:/data $HOME/ #docker stop neo4j:latest diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt index 0aeb3e0..15e0eab 100644 --- a/lib/src/main/kotlin/treelib/Main.kt +++ b/lib/src/main/kotlin/treelib/Main.kt @@ -1,6 +1,8 @@ package treelib import Controller +import treelib.rbTree.RBStruct +import treelib.singleObjects.Container fun main() { @@ -30,10 +32,35 @@ fun main() { neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") neo4jRep.saveChanges(preArr, inArr) */ + + val tree = RBStruct>() + tree.insert(Container(Pair(25 , 1))) + tree.insert(Container(Pair(15 , 1))) + tree.insert(Container(Pair(50 , 1))) + tree.insert(Container(Pair(10 , 1))) + tree.insert(Container(Pair(22 , 1))) + tree.insert(Container(Pair(35 , 1))) + tree.insert(Container(Pair(70 , 1))) + tree.insert(Container(Pair(4 , 1))) + tree.insert(Container(Pair(12 , 1))) + tree.insert(Container(Pair(18 , 1))) + tree.insert(Container(Pair(24 , 1))) + tree.insert(Container(Pair(31 , 1))) + tree.insert(Container(Pair(44 , 1))) + tree.insert(Container(Pair(66 , 1))) + tree.insert(Container(Pair(90 , 1))) val controller = Controller() - controller.saveTree() + controller.saveTree(tree) + tree.insert(Container(Pair(100, 1))) + controller.saveTree(tree) + controller.initTree() //neo4jRep.exportRBtree() //neo4jRep.close() +} + +fun test() { + val cont = Controller() + cont.initTree() } \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index 1607caa..1116ad6 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) + 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 5b96f98..c91c6ed 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -9,7 +9,8 @@ import treelib.singleObjects.exceptions.NonExistentValueException abstract class TreeStruct< Pack : Comparable, NodeType : Node, - State : StateContainer + State : StateContainer, + VertexType: Vertex > { protected abstract var root: NodeType? @@ -163,8 +164,8 @@ abstract class TreeStruct< linkNewNode(currentNode, parentNode) - if (parentNode == null) return generateStateInsert(currentNode, currentNode) - else return generateStateInsert(currentNode, parentNode) + if (parentNode != null) return generateStateInsert(currentNode, parentNode) + else return generateStateInsert(currentNode, currentNode) } updateNode.value = item @@ -260,8 +261,8 @@ abstract class TreeStruct< fun find(obj: Pack): Pack? = findItem(obj).contentNode?.value - fun inOrder(): List { - val arrayNodes = mutableListOf() + fun inOrder(): List { + val arrayNodes = mutableListOf() var flagVisited = 0 var current = root val parents = ArrayDeque() @@ -278,24 +279,26 @@ abstract class TreeStruct< } } current?.let { - arrayNodes.add(it.value) + arrayNodes.add(it) if (it.right != null) { flagVisited = 0 current = it.right } else { if (parents.isEmpty()) - return@inOrder arrayNodes + return@inOrder arrayNodes.map {toVertex(it)} flagVisited = 1 current = parents.removeLast() } } } - return arrayNodes + return arrayNodes.map{toVertex(it)} } - fun postOrder(): List { + abstract fun toVertex(node: NodeType): VertexType + + fun postOrder(): List { val parents = ArrayDeque() - val arrayNodes = mutableListOf() + val arrayNodes = mutableListOf() var flagVisited = 0 var current = root @@ -316,9 +319,9 @@ abstract class TreeStruct< current = it.right flagVisited = 0 } else { - arrayNodes.add(it.value) + arrayNodes.add(it) if (parents.isEmpty()) - return@postOrder arrayNodes + return@postOrder arrayNodes.map{toVertex(it)} val parent = parents.removeLast() if (parent.right == it) { flagVisited = 2 @@ -327,11 +330,11 @@ abstract class TreeStruct< } } ?: throw MultithreadingException(ImpossibleCaseException()) } - return arrayNodes + return arrayNodes.map{toVertex(it)} } - fun preOrder(): List { - val arrayNodes = mutableListOf() + fun preOrder(): List { + val arrayNodes = mutableListOf() var current: NodeType val queue = ArrayDeque() @@ -339,7 +342,7 @@ abstract class TreeStruct< queue.add(root) while (queue.isNotEmpty()) { current = queue.removeLast() - arrayNodes.add(current.value) + arrayNodes.add(current) if (current.right != null) current.right?.let { queue.add(it) @@ -351,6 +354,8 @@ abstract class TreeStruct< } ?: throw MultithreadingException(ImpossibleCaseException()) } } - return arrayNodes + return arrayNodes.map {toVertex(it)} } + + } diff --git a/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt b/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt new file mode 100644 index 0000000..abbb7a5 --- /dev/null +++ b/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt @@ -0,0 +1,5 @@ +package treelib.abstractTree + +abstract class Vertex>{ + 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..96e8458 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt @@ -3,13 +3,15 @@ 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 diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index f275439..7893341 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -3,7 +3,7 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancedTreeStruct class AVLStruct> : - BalancedTreeStruct, AVLStateContainer, AVLBalancer>() { + BalancedTreeStruct, AVLStateContainer, AVLVertex, AVLBalancer>() { override var root: AVLNode? = null override val balancer = AVLBalancer(root) @@ -40,6 +40,10 @@ class AVLStruct> : } } + override fun toVertex(node: AVLNode): AVLVertex { + return AVLVertex(node.value, node.height) + } + override fun createNode(item: Pack): AVLNode = AVLNode(item) override fun getNodeKernel(node: AVLNode): AVLNode = AVLNode(node.value, height = node.height) 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..a43ea26 --- /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() \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index e555924..f3bfc09 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -3,7 +3,7 @@ package treelib.binTree import treelib.abstractTree.TreeStruct class BINStruct> : - TreeStruct, BINStateContainer>() { + TreeStruct, BINStateContainer, BINVertex>() { override var root: BINNode? = null @@ -53,6 +53,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) { 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..e660d8c --- /dev/null +++ b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt @@ -0,0 +1,5 @@ +package treelib.binTree + +import treelib.abstractTree.Vertex + +class BINVertex>(override val value: Pack): Vertex() \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt b/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt new file mode 100644 index 0000000..b3ee499 --- /dev/null +++ b/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt @@ -0,0 +1,10 @@ +package treelib.rbTree + +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/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 211e007..c8c4cf6 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -2,6 +2,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent import treelib.singleObjects.Markers +import treelib.singleObjects.exceptions.IllegalBaseNodeException +import treelib.singleObjects.exceptions.IllegalNodeStateException class RBBalancer>(private var root: RBNode?) : BalancerParent, RBStateContainer>() { @@ -42,7 +44,7 @@ class RBBalancer>(private var root: RBNode?) : override fun balance(stateContainer: RBStateContainer): RBNode { val node = stateContainer.contentNode - ?: throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") + ?: throw IllegalBaseNodeException() val uncle = getUncle(node) when { /** node insertion case **/ @@ -65,7 +67,7 @@ class RBBalancer>(private var root: RBNode?) : currentNode = parent } parent = - currentNode.parent?.parent ?: throw IllegalStateException() // IllegalNodeStateException() + currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -78,7 +80,7 @@ class RBBalancer>(private var root: RBNode?) : currentNode = parent } parent = - currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() + currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -133,7 +135,7 @@ class RBBalancer>(private var root: RBNode?) : val uncle = getUncle(currentNode) if (uncle?.color == Markers.RED) { currentNode.parent?.color = Markers.BLACK - currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() + currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK } else if (uncle != null) { @@ -156,13 +158,13 @@ class RBBalancer>(private var root: RBNode?) : getRoot(parent) } - else -> getRoot(node ?: throw NullPointerException() /* IllegalNodeStateException() */) + else -> getRoot(node ?: throw IllegalNodeStateException()) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() + var brother = getBrother(parent, node) ?: throw IllegalNodeStateException() if (brother.color == Markers.RED) throw NullPointerException() @@ -214,7 +216,7 @@ class RBBalancer>(private var root: RBNode?) : /** parent is black **/ private fun thirdCase(parent: RBNode, node: RBNode?) { - val brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() + val brother = getBrother(parent, node) ?: throw IllegalNodeStateException() when (brother.color) { Markers.RED -> thirdCaseSubFirst(brother, parent) Markers.BLACK -> thirdCaseSubSecond(brother, parent) @@ -225,7 +227,7 @@ class RBBalancer>(private var root: RBNode?) : private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { brother.parent?.left -> { - var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() + var rightBrotherSon = brother.right ?: throw IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED @@ -242,7 +244,7 @@ class RBBalancer>(private var root: RBNode?) : leftRotate(rightBrotherSon) rightBrotherSon = - rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + rightBrotherSon.parent ?: throw IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -258,7 +260,7 @@ class RBBalancer>(private var root: RBNode?) : if (leftBrotherSon.right?.color != Markers.RED && leftBrotherSon.left?.color != Markers.RED) { leftBrotherSon.color = Markers.RED brother.color = Markers.BLACK - leftRotate(brother.parent ?: throw NullPointerException()) // IllegalNodeStateException() + leftRotate(brother.parent ?: throw IllegalNodeStateException()) return } @@ -266,7 +268,7 @@ class RBBalancer>(private var root: RBNode?) : rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED leftBrotherSon = - leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + leftBrotherSon.parent ?: throw IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index ee6a216..47dd91d 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -1,6 +1,5 @@ package treelib.rbTree -import treelib.DBNodeRB import treelib.abstractTree.balanced.BalancedTreeStruct import treelib.singleObjects.Markers import treelib.singleObjects.exceptions.ImpossibleCaseException @@ -8,7 +7,7 @@ import treelib.singleObjects.exceptions.MultithreadingException import java.util.* class RBStruct> : - BalancedTreeStruct, RBStateContainer, RBBalancer>() { + BalancedTreeStruct, RBStateContainer, RBVertex, RBBalancer>() { override var root: RBNode? = null @@ -57,6 +56,10 @@ class RBStruct> : 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 { @@ -73,7 +76,7 @@ class RBStruct> : return node } - fun restoreTreeFromDatabase(preOrder: List>, inOrder: List>) { + fun > restoreTreeFromDatabase(preOrder: List, inOrder: List) { var inOrderIndex = 0 var preOrderIndex = 0 val set = HashSet>() @@ -81,7 +84,7 @@ class RBStruct> : while (preOrderIndex in preOrder.indices) { var currentNode: RBNode? - var drawNode: DBNodeRB + var drawNode: RBVertexType do { drawNode = preOrder[preOrderIndex] @@ -117,7 +120,7 @@ class RBStruct> : } - private fun createRBNode(drawNode: DBNodeRB): RBNode { + 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..5d9d161 --- /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 From f38f068df4581969a4958e9953928ee6efd9cc8b Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 22:04:52 +0300 Subject: [PATCH 113/212] fix: Add running on multiple OS --- .github/workflows/CI.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ae15aca..8697f86 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 From 64ad32044afe4ea44b02a6ad074d84d3c80affe5 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 22:37:53 +0300 Subject: [PATCH 114/212] fix: Fix tree class definitions --- .gitignore | 3 ++- lib/src/test/kotlin/treelib/AVLStructTest.kt | 3 ++- lib/src/test/kotlin/treelib/BINStructTest.kt | 3 ++- lib/src/test/kotlin/treelib/RBStructTest.kt | 3 ++- lib/src/test/kotlin/utils/TreeStructWrapper.kt | 3 ++- lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt | 3 ++- lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt | 3 ++- .../test/kotlin/utils/fuzzers/TreeStructFuzzer.kt | 12 +++++++----- 8 files changed, 21 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 7061064..38f0b3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.gradle/ /.idea/ /build/ -/lib/build/ \ No newline at end of file +/lib/build/ +/lib/TEST_TEST/ diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 1e522fe..f003b60 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -7,12 +7,13 @@ 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, AVLStateContainer, AVLStruct>() + private val treeW = TreeStructWrapper, AVLVertex, AVLStateContainer, AVLStruct>() private val treeH = AVLAnalyzer(::testAssert) private var treeStruct = AVLStruct() diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 952a6fa..1075631 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -7,13 +7,14 @@ 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, BINStateContainer, BINStruct>() + val treeW = TreeStructWrapper, BINVertex, BINStateContainer, BINStruct>() var treeStruct = BINStruct() private fun testAssert(msg: String): Nothing = fail(msg) diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index 606ca40..3196207 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -4,6 +4,7 @@ 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 @@ -12,7 +13,7 @@ import kotlin.test.assertEquals @DisplayName("Test: Red-Black Tree Struct") class RBStructTest { - private val treeW = TreeStructWrapper, RBStateContainer, RBStruct>() + private val treeW = TreeStructWrapper, RBVertex, RBStateContainer, RBStruct>() private var treeStruct = RBStruct() private fun testAssert(msg: String): Nothing = fail(msg) diff --git a/lib/src/test/kotlin/utils/TreeStructWrapper.kt b/lib/src/test/kotlin/utils/TreeStructWrapper.kt index 1bdb93e..64fea41 100644 --- a/lib/src/test/kotlin/utils/TreeStructWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeStructWrapper.kt @@ -3,8 +3,9 @@ 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) diff --git a/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt index 5ce3959..bc79507 100644 --- a/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt @@ -3,12 +3,13 @@ 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, BINAnalyzer, BINStateContainer, BINStruct>() { +): TreeStructFuzzer, BINVertex, BINAnalyzer, BINStateContainer, BINStruct>() { override fun createTreeStruct(): BINStruct = BINStruct() diff --git a/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt index 6b59799..2a6bb7f 100644 --- a/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt @@ -3,12 +3,13 @@ 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, RBAnalyzer, RBStateContainer, RBStruct>() { +): TreeStructFuzzer, RBVertex, RBAnalyzer, RBStateContainer, RBStruct>() { override fun createTreeStruct(): RBStruct = RBStruct() diff --git a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt index a5390b9..7d5e0d6 100644 --- a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt @@ -3,20 +3,22 @@ package utils.fuzzers import treelib.abstractTree.Node import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct -import kotlin.random.Random -import utils.Analyzer +import treelib.abstractTree.Vertex import treelib.singleObjects.exceptions.* -import kotlin.random.nextInt +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, + TreeStructType : TreeStruct, > { abstract val baseInput: Array @@ -26,7 +28,7 @@ abstract class TreeStructFuzzer< private var dirPath: String? = null - protected val treeWrapper = TreeStructWrapper() + protected val treeWrapper = TreeStructWrapper() protected abstract fun createTreeStruct(): TreeStructType From 48b670d37be64be073a176926306810690c83538 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 23:33:36 +0300 Subject: [PATCH 115/212] fix(CI): Add a condition to run jacoco --- .github/workflows/CI.yml | 6 ++++-- lib/src/test/kotlin/{ => treelib}/AVLBalancerTest.kt | 0 lib/src/test/kotlin/{ => treelib}/RBBalancerTest.kt | 0 lib/src/test/kotlin/{ => treelib}/TestModelAVL.kt | 0 lib/src/test/kotlin/{ => treelib}/TestModelRBT.kt | 0 5 files changed, 4 insertions(+), 2 deletions(-) rename lib/src/test/kotlin/{ => treelib}/AVLBalancerTest.kt (100%) rename lib/src/test/kotlin/{ => treelib}/RBBalancerTest.kt (100%) rename lib/src/test/kotlin/{ => treelib}/TestModelAVL.kt (100%) rename lib/src/test/kotlin/{ => treelib}/TestModelRBT.kt (100%) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 8697f86..243bd23 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -22,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/lib/src/test/kotlin/AVLBalancerTest.kt b/lib/src/test/kotlin/treelib/AVLBalancerTest.kt similarity index 100% rename from lib/src/test/kotlin/AVLBalancerTest.kt rename to lib/src/test/kotlin/treelib/AVLBalancerTest.kt diff --git a/lib/src/test/kotlin/RBBalancerTest.kt b/lib/src/test/kotlin/treelib/RBBalancerTest.kt similarity index 100% rename from lib/src/test/kotlin/RBBalancerTest.kt rename to lib/src/test/kotlin/treelib/RBBalancerTest.kt diff --git a/lib/src/test/kotlin/TestModelAVL.kt b/lib/src/test/kotlin/treelib/TestModelAVL.kt similarity index 100% rename from lib/src/test/kotlin/TestModelAVL.kt rename to lib/src/test/kotlin/treelib/TestModelAVL.kt diff --git a/lib/src/test/kotlin/TestModelRBT.kt b/lib/src/test/kotlin/treelib/TestModelRBT.kt similarity index 100% rename from lib/src/test/kotlin/TestModelRBT.kt rename to lib/src/test/kotlin/treelib/TestModelRBT.kt From f3f514c04ea8835cbc9569b47a969b12a3b7176e Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 23:36:42 +0300 Subject: [PATCH 116/212] refactor: Rename some methods in test classes --- lib/build.gradle.kts | 6 +++--- lib/src/test/kotlin/treelib/AVLBalancerTest.kt | 2 ++ lib/src/test/kotlin/treelib/RBBalancerTest.kt | 2 ++ lib/src/test/kotlin/treelib/TestModelAVL.kt | 2 ++ lib/src/test/kotlin/treelib/TestModelRBT.kt | 2 ++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 0dfd5f1..08851db 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -87,7 +87,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 { @@ -95,7 +95,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "BRANCH" - minimum = 0.4.toBigDecimal() + minimum = 0.5.toBigDecimal() } } rule { @@ -109,7 +109,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "METHOD" - minimum = 1.0.toBigDecimal() + minimum = 0.9.toBigDecimal() } } } diff --git a/lib/src/test/kotlin/treelib/AVLBalancerTest.kt b/lib/src/test/kotlin/treelib/AVLBalancerTest.kt index 862a497..3ee5452 100644 --- a/lib/src/test/kotlin/treelib/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 diff --git a/lib/src/test/kotlin/treelib/RBBalancerTest.kt b/lib/src/test/kotlin/treelib/RBBalancerTest.kt index 0bc9e43..d18134e 100644 --- a/lib/src/test/kotlin/treelib/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 diff --git a/lib/src/test/kotlin/treelib/TestModelAVL.kt b/lib/src/test/kotlin/treelib/TestModelAVL.kt index c96773d..8a3ba26 100644 --- a/lib/src/test/kotlin/treelib/TestModelAVL.kt +++ b/lib/src/test/kotlin/treelib/TestModelAVL.kt @@ -1,3 +1,5 @@ +package treelib + import treelib.avlTree.AVLNode class TestModelAVL { diff --git a/lib/src/test/kotlin/treelib/TestModelRBT.kt b/lib/src/test/kotlin/treelib/TestModelRBT.kt index ac86a6c..ca7efe7 100644 --- a/lib/src/test/kotlin/treelib/TestModelRBT.kt +++ b/lib/src/test/kotlin/treelib/TestModelRBT.kt @@ -1,3 +1,5 @@ +package treelib + import treelib.rbTree.RBNode import treelib.singleObjects.Markers From dfb677243edfc418b618a7fb8623e4cc753d7032 Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 19 Apr 2023 04:39:37 +0300 Subject: [PATCH 117/212] feat: Implement sketch of jsonRepository --- .gitignore | 1 + lib/build.gradle.kts | 3 + .../Neo4jController.kt} | 6 +- .../kotlin/dbSave/jsonFormat/DrawBINVertex.kt | 9 +++ .../dbSave/jsonFormat/JsonRepository.kt | 34 ++++++++++ .../kotlin/{ => dbSave/neo4j}/CONTAINER.conf | 0 .../rbTree => dbSave/neo4j}/DrawRBVertex.kt | 3 +- .../neo4j/Neo4jRepository.kt} | 4 +- .../kotlin/{ => dbSave/neo4j}/testNeo4j.sh | 11 ---- lib/src/main/kotlin/initNeo4j.sh | 0 lib/src/main/kotlin/loadNeo4j.sh | 8 --- lib/src/main/kotlin/treelib/DBNodeRB.kt | 11 ---- lib/src/main/kotlin/treelib/Main.kt | 66 ------------------- .../main/kotlin/treelib/binTree/BINVertex.kt | 2 +- 14 files changed, 57 insertions(+), 101 deletions(-) rename lib/src/main/kotlin/{Controller.kt => controller/Neo4jController.kt} (91%) create mode 100644 lib/src/main/kotlin/dbSave/jsonFormat/DrawBINVertex.kt create mode 100644 lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt rename lib/src/main/kotlin/{ => dbSave/neo4j}/CONTAINER.conf (100%) rename lib/src/main/kotlin/{treelib/rbTree => dbSave/neo4j}/DrawRBVertex.kt (79%) rename lib/src/main/kotlin/{DataBase.kt => dbSave/neo4j/Neo4jRepository.kt} (99%) rename lib/src/main/kotlin/{ => dbSave/neo4j}/testNeo4j.sh (59%) delete mode 100644 lib/src/main/kotlin/initNeo4j.sh delete mode 100644 lib/src/main/kotlin/loadNeo4j.sh delete mode 100644 lib/src/main/kotlin/treelib/DBNodeRB.kt delete mode 100644 lib/src/main/kotlin/treelib/Main.kt diff --git a/.gitignore b/.gitignore index 38f0b3d..16cba14 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /build/ /lib/build/ /lib/TEST_TEST/ +/gradle/ diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 08851db..fd77a45 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -23,6 +23,8 @@ dependencies { api("org.apache.commons:commons-math3:3.6.1") implementation("com.google.guava:guava:31.1-jre") + 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) @@ -34,6 +36,7 @@ dependencies { testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") implementation(kotlin("stdlib-jdk8")) + } diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/controller/Neo4jController.kt similarity index 91% rename from lib/src/main/kotlin/Controller.kt rename to lib/src/main/kotlin/controller/Neo4jController.kt index a59a3b0..3744351 100644 --- a/lib/src/main/kotlin/Controller.kt +++ b/lib/src/main/kotlin/controller/Neo4jController.kt @@ -1,9 +1,11 @@ +package controller -import treelib.rbTree.DrawRBVertex +import dbSave.neo4j.DrawRBVertex +import dbSave.neo4j.Neo4jRepository import treelib.rbTree.RBStruct import treelib.singleObjects.Container -class Controller { +class Neo4jController { fun initTree() { val neo4jDB = Neo4jRepository() 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..88a1396 --- /dev/null +++ b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt @@ -0,0 +1,34 @@ +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>, typeToken: TypeToken>>, fileName: String) { + + val gson = GsonBuilder().setPrettyPrinting().create() + val json = gson.toJson(preOrder) + + File(dirPath, fileName).run { + createNewFile() + writeText(json) + } + + val preOrd = gson.fromJson>>(json, typeToken.type) + + } + + fun exportTree(fileName: String) { + val gson = GsonBuilder().setPrettyPrinting().create() + //val json = gson.fromJson(File(dirPath, fileName).readText(), ArrayVertices::class.java) + + + } + + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/CONTAINER.conf b/lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf similarity index 100% rename from lib/src/main/kotlin/CONTAINER.conf rename to lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf diff --git a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt b/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt similarity index 79% rename from lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt rename to lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt index b3ee499..37100de 100644 --- a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt +++ b/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt @@ -1,5 +1,6 @@ -package treelib.rbTree +package dbSave.neo4j +import treelib.rbTree.RBVertex import treelib.singleObjects.Markers class DrawRBVertex>( diff --git a/lib/src/main/kotlin/DataBase.kt b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt similarity index 99% rename from lib/src/main/kotlin/DataBase.kt rename to lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt index 809eb64..316dc4c 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt @@ -1,9 +1,11 @@ +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.rbTree.DrawRBVertex import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh similarity index 59% rename from lib/src/main/kotlin/testNeo4j.sh rename to lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh index 87b69c4..6b07269 100644 --- a/lib/src/main/kotlin/testNeo4j.sh +++ b/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh @@ -3,7 +3,6 @@ BASEDIR=$(realpath "$(dirname "$0")") . "${BASEDIR}/CONTAINER.conf" -# -d docker run \ -i \ --name "$CONTAINER_NAME" \ @@ -12,13 +11,3 @@ docker run \ --publish=7474:7474 --publish=7687:7687 \ --env NEO4J_AUTH=neo4j/"$PASSWORD" \ neo4j:latest \ -# -c /bin/bash -#neo4j-admin database dump neo4j --to-path=$HOME/neo4j/data:/data - -#docker ps -a - -#docker stop "$CONTAINER_NAME" - -#cp $HOME/neo4j/data:/data $HOME/ - -#docker stop neo4j:latest diff --git a/lib/src/main/kotlin/initNeo4j.sh b/lib/src/main/kotlin/initNeo4j.sh deleted file mode 100644 index e69de29..0000000 diff --git a/lib/src/main/kotlin/loadNeo4j.sh b/lib/src/main/kotlin/loadNeo4j.sh deleted file mode 100644 index d4d9fcd..0000000 --- a/lib/src/main/kotlin/loadNeo4j.sh +++ /dev/null @@ -1,8 +0,0 @@ - -BASEDIR=$(realpath "$(dirname "$0")") - -. "${BASEDIR}/CONTAINER.conf" - -docker stop "$CONTAINER_NAME" - -neo4j-admin dump --database=neo4j --to=/data/backups/ \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt deleted file mode 100644 index cf067e1..0000000 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ /dev/null @@ -1,11 +0,0 @@ -package treelib - -import treelib.singleObjects.Markers - - -class DBNodeRB>( - val value: Pack, - val color: Markers = Markers.RED, - val x: Double = 0.0, - val y: Double = 0.0 -) diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt deleted file mode 100644 index 15e0eab..0000000 --- a/lib/src/main/kotlin/treelib/Main.kt +++ /dev/null @@ -1,66 +0,0 @@ -package treelib - -import Controller -import treelib.rbTree.RBStruct -import treelib.singleObjects.Container - - -fun main() { - // 25, 15, 10, 4, 12, 22, 18, 24, 50, 35, 31, 44, 70, 66, 90 - // 4, 10, 12, 15, 18, 22, 24, 25, 31, 35, 44, 50, 66, 70, 90 - - /* - - val neo4jRep = Neo4jRepository() - val a1 = DBNodeRB(Container(Pair(1, 25))) - val a2 = DBNodeRB(Container(Pair(1, 15))) - val a3 = DBNodeRB(Container(Pair(1, 10))) - val a4 = DBNodeRB(Container(Pair(1, 4))) - val a5 = DBNodeRB(Container(Pair(1, 12))) - val a6 = DBNodeRB(Container(Pair(1, 22))) - val a7 = DBNodeRB(Container(Pair(1, 18))) - val a8 = DBNodeRB(Container(Pair(1, 24))) - val a9 = DBNodeRB(Container(Pair(1, 50))) - val a10 = DBNodeRB(Container(Pair(1, 35))) - val a11 = DBNodeRB(Container(Pair(1, 31))) - val a12 = DBNodeRB(Container(Pair(1, 44))) - val a13 = DBNodeRB(Container(Pair(1, 70))) - val a14 = DBNodeRB(Container(Pair(1, 66))) - val a15 = DBNodeRB(Container(Pair(1, 90))) - val preArr = arrayOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) - val inArr = arrayOf(a4, a3, a5, a2, a7, a6, a8, a1, a11, a10, a12, a9, a14, a13, a15) - neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") - neo4jRep.saveChanges(preArr, inArr) - */ - - val tree = RBStruct>() - tree.insert(Container(Pair(25 , 1))) - tree.insert(Container(Pair(15 , 1))) - tree.insert(Container(Pair(50 , 1))) - tree.insert(Container(Pair(10 , 1))) - tree.insert(Container(Pair(22 , 1))) - tree.insert(Container(Pair(35 , 1))) - tree.insert(Container(Pair(70 , 1))) - tree.insert(Container(Pair(4 , 1))) - tree.insert(Container(Pair(12 , 1))) - tree.insert(Container(Pair(18 , 1))) - tree.insert(Container(Pair(24 , 1))) - tree.insert(Container(Pair(31 , 1))) - tree.insert(Container(Pair(44 , 1))) - tree.insert(Container(Pair(66 , 1))) - tree.insert(Container(Pair(90 , 1))) - val controller = Controller() - controller.saveTree(tree) - tree.insert(Container(Pair(100, 1))) - controller.saveTree(tree) - controller.initTree() - - //neo4jRep.exportRBtree() - - //neo4jRep.close() -} - -fun test() { - val cont = Controller() - cont.initTree() -} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/binTree/BINVertex.kt b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt index e660d8c..8939461 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINVertex.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt @@ -2,4 +2,4 @@ package treelib.binTree import treelib.abstractTree.Vertex -class BINVertex>(override val value: Pack): Vertex() \ No newline at end of file +open class BINVertex>(override val value: Pack): Vertex() \ No newline at end of file From b9ef5e8788dc071a4308b090b56963fa7b03326d Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 19 Apr 2023 03:20:10 +0300 Subject: [PATCH 118/212] feat: Implement AVLStruct.restoreStruct and BINStruct.restoreStruct. --- .../main/kotlin/controller/Neo4jController.kt | 2 +- .../kotlin/treelib/abstractTree/TreeStruct.kt | 2 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 15 ++++++++++++--- .../main/kotlin/treelib/binTree/BINStruct.kt | 13 +++++++++++++ lib/src/main/kotlin/treelib/rbTree/RBStruct.kt | 3 +-- .../singleObjects/exceptions/IncorrectUsage.kt | 18 ++++++++++++++++++ lib/src/test/kotlin/treeTests/RBTreeTest.kt | 16 ++++++++++++++++ 7 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt create mode 100644 lib/src/test/kotlin/treeTests/RBTreeTest.kt diff --git a/lib/src/main/kotlin/controller/Neo4jController.kt b/lib/src/main/kotlin/controller/Neo4jController.kt index 3744351..5c47c63 100644 --- a/lib/src/main/kotlin/controller/Neo4jController.kt +++ b/lib/src/main/kotlin/controller/Neo4jController.kt @@ -16,7 +16,7 @@ class Neo4jController { neo4jDB.exportRBtree() val RBtree = RBStruct>>() - RBtree.restoreTreeFromDatabase(orders.first, orders.second) + RBtree.restoreStruct(orders.first, orders.second) neo4jDB.close() } diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index c91c6ed..d9b223b 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -15,7 +15,7 @@ abstract class TreeStruct< 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) { diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index 7893341..b0ee0ae 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -1,6 +1,7 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancedTreeStruct +import treelib.singleObjects.exceptions.IncorrectUsage class AVLStruct> : BalancedTreeStruct, AVLStateContainer, AVLVertex, AVLBalancer>() { @@ -40,9 +41,9 @@ class AVLStruct> : } } - override fun toVertex(node: AVLNode): AVLVertex { - return AVLVertex(node.value, node.height) - } + 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) @@ -56,4 +57,12 @@ class AVLStruct> : } return node } + 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/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index f3bfc09..ea03cb6 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -1,6 +1,8 @@ package treelib.binTree import treelib.abstractTree.TreeStruct +import treelib.abstractTree.Vertex +import treelib.singleObjects.exceptions.IncorrectUsage class BINStruct> : TreeStruct, BINStateContainer, BINVertex>() { @@ -66,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/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 47dd91d..999c113 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -76,7 +76,7 @@ class RBStruct> : return node } - fun > restoreTreeFromDatabase(preOrder: List, inOrder: List) { + fun > restoreStruct(preOrder: List, inOrder: List) { var inOrderIndex = 0 var preOrderIndex = 0 val set = HashSet>() @@ -117,7 +117,6 @@ class RBStruct> : stack.push(currentNode) } } - } private fun > createRBNode(drawNode: RBVertexType): RBNode { 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..9fce7ef --- /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/treeTests/RBTreeTest.kt b/lib/src/test/kotlin/treeTests/RBTreeTest.kt new file mode 100644 index 0000000..fa56cc6 --- /dev/null +++ b/lib/src/test/kotlin/treeTests/RBTreeTest.kt @@ -0,0 +1,16 @@ +package treeTests + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import treelib.rbTree.RBTree + +@DisplayName("Test: Red-Black Tree") +class RBTreeTest { + private var rbTree = RBTree() + + @Test + fun `insert two elements`(){ + rbTree.putItem(Pair(25, 1)) + rbTree.putItem(Pair(15, 1)) + } +} \ No newline at end of file From 92d064c583ebe7901ce0cbe9fdd3828e7cac50c1 Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 19 Apr 2023 05:19:33 +0300 Subject: [PATCH 119/212] fix: Fix some problems after rebase onto Tests branch --- lib/build.gradle.kts | 1 + lib/src/test/kotlin/treelib/AVLTreeTest.kt | 9 +++------ lib/src/test/kotlin/treelib/BINTreeTest.kt | 11 ++++------- lib/src/test/kotlin/treelib/RBTreeTest.kt | 9 +++------ lib/src/test/kotlin/utils/TreeWrapper.kt | 10 ++++------ lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt | 3 ++- 6 files changed, 17 insertions(+), 26 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index fd77a45..f528253 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { testImplementation("io.mockk:mockk:1.13.4") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") implementation(kotlin("stdlib-jdk8")) diff --git a/lib/src/test/kotlin/treelib/AVLTreeTest.kt b/lib/src/test/kotlin/treelib/AVLTreeTest.kt index 5531d35..5915d99 100644 --- a/lib/src/test/kotlin/treelib/AVLTreeTest.kt +++ b/lib/src/test/kotlin/treelib/AVLTreeTest.kt @@ -2,10 +2,7 @@ package treelib import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -import treelib.avlTree.AVLNode -import treelib.avlTree.AVLStateContainer -import treelib.avlTree.AVLStruct -import treelib.avlTree.AVLTree +import treelib.avlTree.* import treelib.singleObjects.Container import utils.TreeStructWrapper import utils.TreeWrapper @@ -15,9 +12,9 @@ import kotlin.test.assertEquals class AVLTreeTest { private val tree = AVLTree() private val treeW = - TreeWrapper>, AVLStateContainer>, AVLStruct>, AVLTree>() + TreeWrapper>, AVLVertex>, AVLStateContainer>, AVLStruct>, AVLTree>() private val treeSW = - TreeStructWrapper, AVLNode>, AVLStateContainer>, AVLStruct>>() + TreeStructWrapper, AVLNode>, AVLVertex>, AVLStateContainer>, AVLStruct>>() @ParameterizedTest diff --git a/lib/src/test/kotlin/treelib/BINTreeTest.kt b/lib/src/test/kotlin/treelib/BINTreeTest.kt index 172c545..5157c95 100644 --- a/lib/src/test/kotlin/treelib/BINTreeTest.kt +++ b/lib/src/test/kotlin/treelib/BINTreeTest.kt @@ -3,21 +3,18 @@ package treelib import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -import treelib.binTree.BINNode -import treelib.binTree.BINStateContainer -import treelib.binTree.BINStruct -import treelib.binTree.BINTree -import utils.TreeWrapper +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>, BINStateContainer>, BINStruct>, BINTree>() + TreeWrapper>, BINVertex>, BINStateContainer>, BINStruct>, BINTree>() private val treeSW = - TreeStructWrapper, BINNode>, BINStateContainer>, BINStruct>>() + TreeStructWrapper, BINNode>, BINVertex>, BINStateContainer>, BINStruct>>() // line - 0.6, branch - 0.5, methods = 0.9 diff --git a/lib/src/test/kotlin/treelib/RBTreeTest.kt b/lib/src/test/kotlin/treelib/RBTreeTest.kt index 5a3ceee..cd7ce2a 100644 --- a/lib/src/test/kotlin/treelib/RBTreeTest.kt +++ b/lib/src/test/kotlin/treelib/RBTreeTest.kt @@ -2,10 +2,7 @@ package treelib import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -import treelib.rbTree.RBNode -import treelib.rbTree.RBStateContainer -import treelib.rbTree.RBStruct -import treelib.rbTree.RBTree +import treelib.rbTree.* import treelib.singleObjects.Container import treelib.singleObjects.Markers import utils.TreeStructWrapper @@ -15,9 +12,9 @@ import kotlin.test.assertEquals class RBTreeTest { private val tree = RBTree() private val treeW = - TreeWrapper>, RBStateContainer>, RBStruct>, RBTree>() + TreeWrapper>, RBVertex>, RBStateContainer>, RBStruct>, RBTree>() private val treeSW = - TreeStructWrapper, RBNode>, RBStateContainer>, RBStruct>>() + TreeStructWrapper, RBNode>, RBVertex>, RBStateContainer>, RBStruct>>() @ParameterizedTest diff --git a/lib/src/test/kotlin/utils/TreeWrapper.kt b/lib/src/test/kotlin/utils/TreeWrapper.kt index 5ca8ea7..47858d9 100644 --- a/lib/src/test/kotlin/utils/TreeWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeWrapper.kt @@ -1,9 +1,6 @@ package utils -import treelib.abstractTree.Node -import treelib.abstractTree.StateContainer -import treelib.abstractTree.Tree -import treelib.abstractTree.TreeStruct +import treelib.abstractTree.* import treelib.singleObjects.Container @@ -11,9 +8,10 @@ class TreeWrapper < V : Comparable, Value, NodeType: Node, NodeType>, + VertexType: Vertex>, State: StateContainer, NodeType>, - TStruct: TreeStruct, NodeType, State>, - Wood : Tree> { + TStruct: TreeStruct, NodeType, State, VertexType>, + Wood : Tree> { fun getPrivateNode(tree: Wood, name: String = "treeStruct"): TStruct { val treeS = tree.javaClass.getDeclaredField(name) treeS.isAccessible = true diff --git a/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt index 7cbed69..17d52a2 100644 --- a/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/AVLStructFuzzer.kt @@ -3,12 +3,13 @@ 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, AVLAnalyzer, AVLStateContainer, AVLStruct>() { +) : TreeStructFuzzer, AVLVertex, AVLAnalyzer, AVLStateContainer, AVLStruct>() { override fun createTreeStruct(): AVLStruct = AVLStruct() From 4a67e300b727f3331539142d95d60eb081f88b58 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 15 Apr 2023 15:27:37 +0300 Subject: [PATCH 120/212] fix: Partially change exceptions --- .../kotlin/treelib/avlTree/AVLBalancer.kt | 15 +-- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 99 +++++++++---------- 2 files changed, 53 insertions(+), 61 deletions(-) diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 1a3afb9..5f80a87 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -2,8 +2,7 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancerNoParent -class AVLBalancer>(private var root: AVLNode?) : - BalancerNoParent, AVLStateContainer>() { +class AVLBalancer>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { return (getHeight(node?.left) - getHeight(node?.right)).toInt() } @@ -14,16 +13,14 @@ class AVLBalancer>(private var root: AVLNode?) : private fun updateHeight(currentNode: AVLNode?) { if (currentNode != null) - currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u + currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right))+1u } override fun balance(stateContainer: AVLStateContainer): AVLNode { - val node = stateContainer.contentNode - ?: throw IllegalStateException("") // IllegalBaseNodeException("A non-existent node (null) was passed to the method") + 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) { @@ -37,8 +34,7 @@ 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 NullPointerException() // IllegalNodeStateException() + currentNode.right = currentNode.right?.let { rightRotate(it) } ?: throw NullPointerException() // IllegalNodeStateException() updateHeight(currentNode.right?.right) } val balancedNode = leftRotate(currentNode) @@ -48,8 +44,7 @@ class AVLBalancer>(private var root: AVLNode?) : } if (balance == 2) { if (updateBalance(currentNode.left) == -1) { - currentNode.left = currentNode.left?.let { leftRotate(it) } - ?: throw NullPointerException() // IllegalNodeStateException("There is no node required by the condition of the algorithm") + 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) diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index c8c4cf6..67e5427 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -2,11 +2,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent import treelib.singleObjects.Markers -import treelib.singleObjects.exceptions.IllegalBaseNodeException -import treelib.singleObjects.exceptions.IllegalNodeStateException -class RBBalancer>(private var root: RBNode?) : - BalancerParent, RBStateContainer>() { +class RBBalancer>(private var root: RBNode?): BalancerParent, RBStateContainer>() { init { root?.color = Markers.BLACK @@ -42,13 +39,14 @@ class RBBalancer>(private var root: RBNode?) : return node.right == null && node.left == null } - override fun balance(stateContainer: RBStateContainer): RBNode { - val node = stateContainer.contentNode - ?: throw IllegalBaseNodeException() + override fun balance(stateContainer: RBStateContainer): RBNode { + val node = stateContainer.contentNode ?: + throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") 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,35 +56,30 @@ class RBBalancer>(private var root: RBNode?) : return getRoot(currentNode) } - var parent = - currentNode.parent ?: throw IllegalStateException() // в данном случае родитель не может быть null + 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 IllegalStateException() // 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 NullPointerException() // IllegalNodeStateException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED currentNode.left?.color = Markers.RED } - else -> throw IllegalStateException() // невозможное условие выполнения } if (currentNode.parent == null) @@ -94,21 +87,21 @@ class RBBalancer>(private var root: RBNode?) : 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) || @@ -117,11 +110,10 @@ class RBBalancer>(private var root: RBNode?) : getRoot(node) } /** black leaf removal case **/ - node.left == null || node.right == null -> { + node.left == null || node.right == null-> { firstCase(node, null) } - else -> throw IllegalStateException() } } @@ -135,10 +127,11 @@ class RBBalancer>(private var root: RBNode?) : val uncle = getUncle(currentNode) if (uncle?.color == Markers.RED) { currentNode.parent?.color = Markers.BLACK - currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() + currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK - } else if (uncle != null) { + } + else if(uncle != null){ return currentNode } } @@ -158,13 +151,13 @@ class RBBalancer>(private var root: RBNode?) : getRoot(parent) } - else -> getRoot(node ?: throw IllegalNodeStateException()) + else -> getRoot(node ?: throw NullPointerException() /* IllegalNodeStateException() */) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw IllegalNodeStateException() + var brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() if (brother.color == Markers.RED) throw NullPointerException() @@ -175,48 +168,52 @@ class RBBalancer>(private var root: RBNode?) : } 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 { + } + 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 { + } + else { throw IllegalStateException() } } - else -> throw IllegalStateException() } } /** parent is black **/ private fun thirdCase(parent: RBNode, node: RBNode?) { - val brother = getBrother(parent, node) ?: throw IllegalNodeStateException() + val brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() when (brother.color) { Markers.RED -> thirdCaseSubFirst(brother, parent) Markers.BLACK -> thirdCaseSubSecond(brother, parent) @@ -226,8 +223,9 @@ class RBBalancer>(private var root: RBNode?) : /** black parent and red brother **/ private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { - brother.parent?.left -> { - var rightBrotherSon = brother.right ?: throw IllegalNodeStateException() + brother.parent?.left -> + { + var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED @@ -243,8 +241,7 @@ class RBBalancer>(private var root: RBNode?) : rightBrotherSon.color = Markers.RED leftRotate(rightBrotherSon) - rightBrotherSon = - rightBrotherSon.parent ?: throw IllegalNodeStateException() + rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -254,21 +251,20 @@ class RBBalancer>(private var root: RBNode?) : 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 IllegalNodeStateException()) + leftRotate(brother.parent ?: throw NullPointerException()) // IllegalNodeStateException() return } if (leftBrotherSon.left?.color == Markers.RED) { rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED - leftBrotherSon = - leftBrotherSon.parent ?: throw IllegalNodeStateException() + leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } @@ -278,7 +274,6 @@ class RBBalancer>(private var root: RBNode?) : leftRotate(parent) } } - else -> throw IllegalStateException() } } @@ -294,27 +289,29 @@ class RBBalancer>(private var root: RBNode?) : 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 IllegalStateException() } } From 8b43bd144e2a3007c14c281906af30c317beddcd Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 14:37:18 +0300 Subject: [PATCH 121/212] feat: Implement neo4j database sketch --- lib/build.gradle.kts | 10 +- lib/src/main/kotlin/CONTAINER.conf | 3 + lib/src/main/kotlin/DataBase.kt | 172 ++++++++++++++++++++++++ lib/src/main/kotlin/testNeo4j.sh | 15 +++ lib/src/main/kotlin/treelib/DBNodeRB.kt | 14 ++ 5 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 lib/src/main/kotlin/CONTAINER.conf create mode 100644 lib/src/main/kotlin/DataBase.kt create mode 100644 lib/src/main/kotlin/testNeo4j.sh create mode 100644 lib/src/main/kotlin/treelib/DBNodeRB.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index f528253..891ea29 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -34,13 +34,11 @@ dependencies { testImplementation("io.mockk:mockk:1.13.4") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") - testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") implementation(kotlin("stdlib-jdk8")) - } - tasks.test { finalizedBy(tasks.jacocoTestReport) useJUnitPlatform() @@ -91,7 +89,7 @@ tasks.jacocoTestReport { tasks.jacocoTestCoverageVerification { classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { - include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class") + include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct") } }) dependsOn(tasks.jacocoTestReport) violationRules { @@ -99,7 +97,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "BRANCH" - minimum = 0.5.toBigDecimal() + minimum = 0.4.toBigDecimal() } } rule { @@ -113,7 +111,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "METHOD" - minimum = 0.9.toBigDecimal() + minimum = 1.0.toBigDecimal() } } } diff --git a/lib/src/main/kotlin/CONTAINER.conf b/lib/src/main/kotlin/CONTAINER.conf new file mode 100644 index 0000000..9b9fb0a --- /dev/null +++ b/lib/src/main/kotlin/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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt new file mode 100644 index 0000000..ddc72a3 --- /dev/null +++ b/lib/src/main/kotlin/DataBase.kt @@ -0,0 +1,172 @@ + +import org.neo4j.driver.AuthTokens +import org.neo4j.driver.Driver +import org.neo4j.driver.GraphDatabase +import org.neo4j.driver.exceptions.SessionExpiredException +import treelib.DBNodeRB +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() + } + + } + + // я наверное смогу получить рут, используя фильтр что-то вроде: на данный узел не указывает ни один другой узел с отношением [left_child] / [right_child] + + fun , V> saveChanges(preOrder: Array>, inOrder: Array>) { + + /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ + val session = driver?.session() ?: throw IOException() + + var inOrderIndex = 0 + var preOrderIndex = 0 + val set = HashSet>() + val stack = LinkedList>() + + while (preOrderIndex in preOrder.indices) { + do { + val currentNode = preOrder[preOrderIndex] + if (preOrderIndex == 0) { + session.executeWrite { tx -> + tx.run( + "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", + mutableMapOf( + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + if (!stack.isEmpty()) { + if ( set.contains( stack.peek() ) ) { + set.remove(stack.peek()) + val parentNode = stack.pop() + session.executeWrite {tx -> + 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}) " + + "MERGE (parent)-[:RIGHT_SON]->(son)", + mutableMapOf( + "parentNodeValue" to parentNode.value, + "parentNodeKey" to parentNode.key, + "parentNodeColor" to parentNode.color.toString(), + "parentNodeX" to parentNode.x, + "parentNodeY" to parentNode.y, + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + else { + val parentNode = stack.peek() + session.executeWrite {tx -> + 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}) " + + "MERGE (parent)-[:LEFT_SON]->(son)", + mutableMapOf( + "parentNodeValue" to parentNode.value, + "parentNodeKey" to parentNode.key, + "parentNodeColor" to parentNode.color.toString(), + "parentNodeX" to parentNode.x, + "parentNodeY" to parentNode.y, + "nodeValue" to currentNode.value, + "nodeKey" to currentNode.key, + "nodeColor" to currentNode.color.toString(), + "nodeX" to currentNode.x, + "nodeY" to currentNode.y + ) + ) + } + } + } + stack.push(currentNode) + } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) + + var currentNode: DBNodeRB? = null + + while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().key == inOrder[inOrderIndex].key) { + currentNode = stack.pop() + ++inOrderIndex + } + + if (currentNode != null) { + set.add(currentNode) + stack.push(currentNode) + } + + } + + session.close() + } + + // Это скорее всего будем запускать при открытии приложения, нужно будет сразу восстановить дерево + + fun exportRBtree() { + + 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 ").list() + .map{ DBNodeRB( + value = it.values().get(0).toString(), + key = it.values().get(1).toString(), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(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{ DBNodeRB( + value = it.values().get(0).toString(), + key = it.values().get(1).toString(), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(4).toString().toDouble()) + } + } + + session.close() + + } + + override fun close() { + driver?.close() + } +} + +// neo4j-admin, backup, restore +/* +у меня есть вершины и ребра между ними, надо уметь сохранять дерево в базе, но как? +вопрос: когда заносить изменения в бд и как это реализовывать? +надо еще подумать над оптимизацией, те чтобы не пересобирать дерево в бд каждый раз, когда мы добавили за сессию всего один узел + +root = tx.run("MATCH (parent: Node)-->(son: Node) " + + "WHERE NOT ()-->(parent) " + + "RETURN parent.value, parent.key, parent.color, parent.x, parent.y").list().map {it.values()}.get(0) + */ diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/testNeo4j.sh new file mode 100644 index 0000000..97ad8db --- /dev/null +++ b/lib/src/main/kotlin/testNeo4j.sh @@ -0,0 +1,15 @@ + +BASEDIR=$(realpath "$(dirname "$0")") + +. "${BASEDIR}/CONTAINER.conf" + +docker run \ + --rm \ + --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 \ + +#docker stop neo4j:latest diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt new file mode 100644 index 0000000..d048930 --- /dev/null +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -0,0 +1,14 @@ +package treelib + +import treelib.singleObjects.Markers + + +class DBNodeRB, V>(val value: V, + val key: K, + val color: Markers = Markers.RED, + val x: Double = 0.0, + val y: Double = 0.0) { + + + +} From f67c7f7b9839dbee9d1dae9712c0b29d8ea4bfe6 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 19:32:32 +0300 Subject: [PATCH 122/212] feat: Implement initialize tree from DB --- lib/src/main/kotlin/Controller.kt | 50 ++++++++++++++++++++++ lib/src/main/kotlin/DataBase.kt | 55 ++++++++++++------------- lib/src/main/kotlin/treelib/DBNodeRB.kt | 9 +--- lib/src/main/kotlin/treelib/Main.kt | 39 ++++++++++++++++++ 4 files changed, 118 insertions(+), 35 deletions(-) create mode 100644 lib/src/main/kotlin/Controller.kt create mode 100644 lib/src/main/kotlin/treelib/Main.kt diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/Controller.kt new file mode 100644 index 0000000..cc4e699 --- /dev/null +++ b/lib/src/main/kotlin/Controller.kt @@ -0,0 +1,50 @@ +import treelib.DBNodeRB +import treelib.rbTree.RBStruct +import treelib.rbTree.RBTree +import treelib.singleObjects.Container + +class Controller { + + fun initTree() { + 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.restoreTreeFromDatabase(orders.first, orders.second) + } + + fun saveTree() { + val tree = RBTree() + tree.putItem(Pair(25, 1)) + tree.putItem(Pair(15, 1)) + tree.putItem(Pair(50, 1)) + tree.putItem(Pair(10, 1)) + tree.putItem(Pair(22, 1)) + tree.putItem(Pair(35, 1)) + tree.putItem(Pair(70, 1)) + tree.putItem(Pair(4, 1)) + tree.putItem(Pair(12, 1)) + tree.putItem(Pair(18, 1)) + tree.putItem(Pair(24, 1)) + tree.putItem(Pair(31, 1)) + tree.putItem(Pair(44, 1)) + tree.putItem(Pair(66, 1)) + tree.putItem(Pair(90, 1)) + + val neo4jDB = Neo4jRepository() + neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + + val preOrder = tree.preOrder().map { DBNodeRB(it.value, it.color) } + val inOrder = tree.inOrder().map { DBNodeRB(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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index ddc72a3..3e94aac 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -4,6 +4,7 @@ import org.neo4j.driver.Driver import org.neo4j.driver.GraphDatabase import org.neo4j.driver.exceptions.SessionExpiredException import treelib.DBNodeRB +import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable import java.io.IOException @@ -21,31 +22,29 @@ class Neo4jRepository: Closeable { } catch(ex: SessionExpiredException) { throw IOException() } - } - // я наверное смогу получить рут, используя фильтр что-то вроде: на данный узел не указывает ни один другой узел с отношением [left_child] / [right_child] - - fun , V> saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() var inOrderIndex = 0 var preOrderIndex = 0 - val set = HashSet>() - val stack = LinkedList>() + val set = HashSet>() + val stack = LinkedList>() while (preOrderIndex in preOrder.indices) { do { val currentNode = preOrder[preOrderIndex] + currentNode.value as Container<*, *> if (preOrderIndex == 0) { session.executeWrite { tx -> tx.run( "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", mutableMapOf( - "nodeValue" to currentNode.value, - "nodeKey" to currentNode.key, + "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 @@ -57,19 +56,20 @@ class Neo4jRepository: Closeable { if ( set.contains( stack.peek() ) ) { set.remove(stack.peek()) val parentNode = stack.pop() + parentNode.value as Container<*, *> session.executeWrite {tx -> 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}) " + "MERGE (parent)-[:RIGHT_SON]->(son)", mutableMapOf( - "parentNodeValue" to parentNode.value, - "parentNodeKey" to parentNode.key, + "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, - "nodeKey" to currentNode.key, + "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 @@ -79,19 +79,20 @@ class Neo4jRepository: Closeable { } else { val parentNode = stack.peek() + parentNode.value as Container<*, *> session.executeWrite {tx -> 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}) " + "MERGE (parent)-[:LEFT_SON]->(son)", mutableMapOf( - "parentNodeValue" to parentNode.value, - "parentNodeKey" to parentNode.key, + "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, - "nodeKey" to currentNode.key, + "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 @@ -103,9 +104,9 @@ class Neo4jRepository: Closeable { stack.push(currentNode) } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) - var currentNode: DBNodeRB? = null + var currentNode: DBNodeRB? = null - while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().key == inOrder[inOrderIndex].key) { + while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() ++inOrderIndex } @@ -120,20 +121,17 @@ class Neo4jRepository: Closeable { session.close() } - // Это скорее всего будем запускать при открытии приложения, нужно будет сразу восстановить дерево - - fun exportRBtree() { + fun exportRBtree(): Pair>>>, List>>>>{ val session = driver?.session() ?: throw IOException() - var preOrder: List>> = listOf() - var inOrder: List>> = listOf() + 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 ").list() .map{ DBNodeRB( - value = it.values().get(0).toString(), - key = it.values().get(1).toString(), + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, x = it.values().get(3).toString().toDouble(), y = it.values().get(4).toString().toDouble()) @@ -143,16 +141,17 @@ class Neo4jRepository: Closeable { "RETURN node.value, node.key, node.color, node.x, node.y " + "ORDER BY node.key").list() .map{ DBNodeRB( - value = it.values().get(0).toString(), - key = it.values().get(1).toString(), + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, x = it.values().get(3).toString().toDouble(), y = it.values().get(4).toString().toDouble()) } } - + session.close() + return Pair(preOrder, inOrder) + } override fun close() { diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt index d048930..b28228f 100644 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -3,12 +3,7 @@ package treelib import treelib.singleObjects.Markers -class DBNodeRB, V>(val value: V, - val key: K, +class DBNodeRB>(val value: Pack, val color: Markers = Markers.RED, val x: Double = 0.0, - val y: Double = 0.0) { - - - -} + val y: Double = 0.0) diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt new file mode 100644 index 0000000..0aeb3e0 --- /dev/null +++ b/lib/src/main/kotlin/treelib/Main.kt @@ -0,0 +1,39 @@ +package treelib + +import Controller + + +fun main() { + // 25, 15, 10, 4, 12, 22, 18, 24, 50, 35, 31, 44, 70, 66, 90 + // 4, 10, 12, 15, 18, 22, 24, 25, 31, 35, 44, 50, 66, 70, 90 + + /* + + val neo4jRep = Neo4jRepository() + val a1 = DBNodeRB(Container(Pair(1, 25))) + val a2 = DBNodeRB(Container(Pair(1, 15))) + val a3 = DBNodeRB(Container(Pair(1, 10))) + val a4 = DBNodeRB(Container(Pair(1, 4))) + val a5 = DBNodeRB(Container(Pair(1, 12))) + val a6 = DBNodeRB(Container(Pair(1, 22))) + val a7 = DBNodeRB(Container(Pair(1, 18))) + val a8 = DBNodeRB(Container(Pair(1, 24))) + val a9 = DBNodeRB(Container(Pair(1, 50))) + val a10 = DBNodeRB(Container(Pair(1, 35))) + val a11 = DBNodeRB(Container(Pair(1, 31))) + val a12 = DBNodeRB(Container(Pair(1, 44))) + val a13 = DBNodeRB(Container(Pair(1, 70))) + val a14 = DBNodeRB(Container(Pair(1, 66))) + val a15 = DBNodeRB(Container(Pair(1, 90))) + val preArr = arrayOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) + val inArr = arrayOf(a4, a3, a5, a2, a7, a6, a8, a1, a11, a10, a12, a9, a14, a13, a15) + neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") + neo4jRep.saveChanges(preArr, inArr) + */ + val controller = Controller() + controller.saveTree() + + //neo4jRep.exportRBtree() + + //neo4jRep.close() +} \ No newline at end of file From f2681ab1c269d85d063d6747f2984b52d8a9f41c Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 17 Apr 2023 20:21:27 +0300 Subject: [PATCH 123/212] fix: Fix adding to database (neo4j) --- lib/src/main/kotlin/DataBase.kt | 82 ++++++++++-------- lib/src/main/kotlin/treelib/DBNodeRB.kt | 10 ++- .../kotlin/treelib/avlTree/AVLBalancer.kt | 15 ++-- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 85 ++++++++++--------- 4 files changed, 104 insertions(+), 88 deletions(-) diff --git a/lib/src/main/kotlin/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index 3e94aac..e7ca4c6 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -1,4 +1,3 @@ - import org.neo4j.driver.AuthTokens import org.neo4j.driver.Driver import org.neo4j.driver.GraphDatabase @@ -10,21 +9,21 @@ import java.io.Closeable import java.io.IOException import java.util.* -class Neo4jRepository: Closeable { +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) { + } catch (ex: IllegalArgumentException) { + throw IOException() + } catch (ex: SessionExpiredException) { throw IOException() } } - fun > saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() @@ -53,15 +52,16 @@ class Neo4jRepository: Closeable { } } if (!stack.isEmpty()) { - if ( set.contains( stack.peek() ) ) { + if (set.contains(stack.peek())) { set.remove(stack.peek()) val parentNode = stack.pop() parentNode.value as Container<*, *> - session.executeWrite {tx -> - 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}) " + - "MERGE (parent)-[:RIGHT_SON]->(son)", + session.executeWrite { tx -> + 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}) " + + "MERGE (parent)-[:RIGHT_SON]->(son)", mutableMapOf( "parentNodeValue" to parentNode.value.pair.second, "parentNodeKey" to parentNode.value.pair.first, @@ -76,15 +76,15 @@ class Neo4jRepository: Closeable { ) ) } - } - else { + } else { val parentNode = stack.peek() parentNode.value as Container<*, *> - session.executeWrite {tx -> - 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}) " + - "MERGE (parent)-[:LEFT_SON]->(son)", + session.executeWrite { tx -> + 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}) " + + "MERGE (parent)-[:LEFT_SON]->(son)", mutableMapOf( "parentNodeValue" to parentNode.value.pair.second, "parentNodeKey" to parentNode.value.pair.first, @@ -106,7 +106,7 @@ class Neo4jRepository: Closeable { var currentNode: DBNodeRB? = null - while(!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { + while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() ++inOrderIndex } @@ -121,30 +121,38 @@ class Neo4jRepository: Closeable { session.close() } - fun exportRBtree(): Pair>>>, List>>>>{ + fun exportRBtree(): Pair>>>, List>>>> { 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 ").list() - .map{ DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble()) + session.executeRead { tx -> + preOrder = tx.run( + "MATCH (node: Node) " + + "RETURN node.value, node.key, node.color, node.x, node.y " + ).list() + .map { + DBNodeRB( + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(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{ DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(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 { + DBNodeRB( + value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), + color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, + x = it.values().get(3).toString().toDouble(), + y = it.values().get(4).toString().toDouble() + ) } } diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt index b28228f..cf067e1 100644 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ b/lib/src/main/kotlin/treelib/DBNodeRB.kt @@ -3,7 +3,9 @@ package treelib import treelib.singleObjects.Markers -class DBNodeRB>(val value: Pack, - val color: Markers = Markers.RED, - val x: Double = 0.0, - val y: Double = 0.0) +class DBNodeRB>( + val value: Pack, + val color: Markers = Markers.RED, + val x: Double = 0.0, + val y: Double = 0.0 +) diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 5f80a87..1a3afb9 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -2,7 +2,8 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancerNoParent -class AVLBalancer>(private var root: AVLNode?): BalancerNoParent, AVLStateContainer>() { +class AVLBalancer>(private var root: AVLNode?) : + BalancerNoParent, AVLStateContainer>() { private fun updateBalance(node: AVLNode?): Int { return (getHeight(node?.left) - getHeight(node?.right)).toInt() } @@ -13,14 +14,16 @@ class AVLBalancer>(private var root: AVLNode?): Bal private fun updateHeight(currentNode: AVLNode?) { if (currentNode != null) - currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right))+1u + currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u } override fun balance(stateContainer: AVLStateContainer): AVLNode { - val node = stateContainer.contentNode ?: throw IllegalStateException("") // IllegalBaseNodeException("A non-existent node (null) was passed to the method") + 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) { @@ -34,7 +37,8 @@ class AVLBalancer>(private var root: AVLNode?): Bal val balance = updateBalance(currentNode) if (balance == -2) { if (updateBalance(currentNode.right) == 1) { - currentNode.right = currentNode.right?.let { rightRotate(it) } ?: throw NullPointerException() // IllegalNodeStateException() + currentNode.right = currentNode.right?.let { rightRotate(it) } + ?: throw NullPointerException() // IllegalNodeStateException() updateHeight(currentNode.right?.right) } val balancedNode = leftRotate(currentNode) @@ -44,7 +48,8 @@ class AVLBalancer>(private var root: AVLNode?): Bal } if (balance == 2) { if (updateBalance(currentNode.left) == -1) { - currentNode.left = currentNode.left?.let { leftRotate(it) } ?: throw NullPointerException() // IllegalNodeStateException("There is no node required by the condition of the algorithm") + 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) diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 67e5427..211e007 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -3,7 +3,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent import treelib.singleObjects.Markers -class RBBalancer>(private var root: RBNode?): BalancerParent, RBStateContainer>() { +class RBBalancer>(private var root: RBNode?) : + BalancerParent, RBStateContainer>() { init { root?.color = Markers.BLACK @@ -39,14 +40,13 @@ class RBBalancer>(private var root: RBNode?): Balan return node.right == null && node.left == null } - override fun balance(stateContainer: RBStateContainer): RBNode { - val node = stateContainer.contentNode ?: - throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") + override fun balance(stateContainer: RBStateContainer): RBNode { + val node = stateContainer.contentNode + ?: throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") 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) { @@ -56,30 +56,35 @@ class RBBalancer>(private var root: RBNode?): Balan return getRoot(currentNode) } - var parent = currentNode.parent ?: throw IllegalStateException() // в данном случае родитель не может быть null + 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 IllegalStateException() // IllegalNodeStateException() + parent = + currentNode.parent?.parent ?: throw IllegalStateException() // 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 NullPointerException() // IllegalNodeStateException() + parent = + currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED currentNode.left?.color = Markers.RED } + else -> throw IllegalStateException() // невозможное условие выполнения } if (currentNode.parent == null) @@ -87,21 +92,21 @@ class RBBalancer>(private var root: RBNode?): Balan 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) || @@ -110,10 +115,11 @@ 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 IllegalStateException() } } @@ -130,8 +136,7 @@ class RBBalancer>(private var root: RBNode?): Balan currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK - } - else if(uncle != null){ + } else if (uncle != null) { return currentNode } } @@ -168,45 +173,41 @@ 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 { + } 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 { + } else { throw IllegalStateException() } } + else -> throw IllegalStateException() } } @@ -223,8 +224,7 @@ class RBBalancer>(private var root: RBNode?): Balan /** black parent and red brother **/ private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { - brother.parent?.left -> - { + brother.parent?.left -> { var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { @@ -241,7 +241,8 @@ class RBBalancer>(private var root: RBNode?): Balan rightBrotherSon.color = Markers.RED leftRotate(rightBrotherSon) - rightBrotherSon = rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + rightBrotherSon = + rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -251,8 +252,8 @@ 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 @@ -264,7 +265,8 @@ class RBBalancer>(private var root: RBNode?): Balan if (leftBrotherSon.left?.color == Markers.RED) { rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED - leftBrotherSon = leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + leftBrotherSon = + leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } @@ -274,6 +276,7 @@ class RBBalancer>(private var root: RBNode?): Balan leftRotate(parent) } } + else -> throw IllegalStateException() } } @@ -289,29 +292,27 @@ 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 IllegalStateException() } } From 95afccd6041bfd3e94b63dce254a278d246ab5ac Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 21:33:22 +0300 Subject: [PATCH 124/212] fix: Fix database interaction --- lib/src/main/kotlin/Controller.kt | 35 +--- lib/src/main/kotlin/DataBase.kt | 186 ++++++++++-------- lib/src/main/kotlin/initNeo4j.sh | 0 lib/src/main/kotlin/loadNeo4j.sh | 8 + lib/src/main/kotlin/testNeo4j.sh | 13 +- lib/src/main/kotlin/treelib/Main.kt | 29 ++- .../kotlin/treelib/rbTree/DrawRBVertex.kt | 10 + .../main/kotlin/treelib/rbTree/RBBalancer.kt | 24 +-- 8 files changed, 187 insertions(+), 118 deletions(-) create mode 100644 lib/src/main/kotlin/initNeo4j.sh create mode 100644 lib/src/main/kotlin/loadNeo4j.sh create mode 100644 lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/Controller.kt index cc4e699..a59a3b0 100644 --- a/lib/src/main/kotlin/Controller.kt +++ b/lib/src/main/kotlin/Controller.kt @@ -1,6 +1,6 @@ -import treelib.DBNodeRB + +import treelib.rbTree.DrawRBVertex import treelib.rbTree.RBStruct -import treelib.rbTree.RBTree import treelib.singleObjects.Container class Controller { @@ -8,43 +8,28 @@ class Controller { fun initTree() { val neo4jDB = Neo4jRepository() neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + /*** orders.first = preOrder, orders.second = inOrder ***/ - val orders: Pair>>>, List>>>> = + val orders: Pair>>>, List>>>> = neo4jDB.exportRBtree() val RBtree = RBStruct>>() RBtree.restoreTreeFromDatabase(orders.first, orders.second) + neo4jDB.close() } - fun saveTree() { - val tree = RBTree() - tree.putItem(Pair(25, 1)) - tree.putItem(Pair(15, 1)) - tree.putItem(Pair(50, 1)) - tree.putItem(Pair(10, 1)) - tree.putItem(Pair(22, 1)) - tree.putItem(Pair(35, 1)) - tree.putItem(Pair(70, 1)) - tree.putItem(Pair(4, 1)) - tree.putItem(Pair(12, 1)) - tree.putItem(Pair(18, 1)) - tree.putItem(Pair(24, 1)) - tree.putItem(Pair(31, 1)) - tree.putItem(Pair(44, 1)) - tree.putItem(Pair(66, 1)) - tree.putItem(Pair(90, 1)) - + fun > saveTree(tree: RBStruct) { val neo4jDB = Neo4jRepository() neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") - val preOrder = tree.preOrder().map { DBNodeRB(it.value, it.color) } - val inOrder = tree.inOrder().map { DBNodeRB(it.value, it.color) } + // вот тут плохо, потому что тут надо получать не base nodes, а для рисовалки - neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray()) + 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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt index e7ca4c6..809eb64 100644 --- a/lib/src/main/kotlin/DataBase.kt +++ b/lib/src/main/kotlin/DataBase.kt @@ -1,8 +1,9 @@ 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.DBNodeRB +import treelib.rbTree.DrawRBVertex import treelib.singleObjects.Container import treelib.singleObjects.Markers import java.io.Closeable @@ -23,88 +24,50 @@ class Neo4jRepository : Closeable { } } - fun > saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges(preOrder: Array>, inOrder: Array>) { /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() var inOrderIndex = 0 var preOrderIndex = 0 - val set = HashSet>() - val stack = LinkedList>() + val set = HashSet>() + val stack = LinkedList>() + var id = 0 while (preOrderIndex in preOrder.indices) { do { val currentNode = preOrder[preOrderIndex] - currentNode.value as Container<*, *> + //currentNode.value as Container<*, *> if (preOrderIndex == 0) { session.executeWrite { tx -> - tx.run( - "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY})", - mutableMapOf( - "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 - ) - ) + 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<*, *> + //parentNode.value as Container<*, *> session.executeWrite { tx -> - 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}) " + - "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 - ) - ) + createRightSon(tx, parentNode, currentNode, id) } + ++id } else { val parentNode = stack.peek() parentNode.value as Container<*, *> session.executeWrite { tx -> - 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}) " + - "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 - ) - ) + createLeftSon(tx, parentNode, currentNode, id) } + ++id } } stack.push(currentNode) - } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) + } while (preOrder[preOrderIndex++].value != inOrder[inOrderIndex].value && preOrderIndex < preOrder.size) - var currentNode: DBNodeRB? = null + var currentNode: DrawRBVertex? = null while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() @@ -121,23 +84,26 @@ class Neo4jRepository : Closeable { session.close() } - fun exportRBtree(): Pair>>>, List>>>> { + fun exportRBtree(): Pair>>>, List>>>> { + + /*** Вот тут короче надо обращаться к neo4j с темЮ чтобы она инициализировала себя как-то ***/ val session = driver?.session() ?: throw IOException() - var preOrder: List>>> = listOf() - var inOrder: List>>> = listOf() + 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 " + "RETURN node.value, node.key, node.color, node.x, node.y " + + "ORDER BY node.id" ).list() .map { - DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble() + 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() ) } @@ -147,11 +113,11 @@ class Neo4jRepository : Closeable { "ORDER BY node.key" ).list() .map { - DBNodeRB( - value = Container(Pair(it.values().get(0).toString(), it.values().get(1).toString())), - color = if (it.values().get(2).toString() == """RED""") Markers.RED else Markers.BLACK, - x = it.values().get(3).toString().toDouble(), - y = it.values().get(4).toString().toDouble() + 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() ) } } @@ -162,18 +128,80 @@ class Neo4jRepository : Closeable { } + 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() } } - -// neo4j-admin, backup, restore -/* -у меня есть вершины и ребра между ними, надо уметь сохранять дерево в базе, но как? -вопрос: когда заносить изменения в бд и как это реализовывать? -надо еще подумать над оптимизацией, те чтобы не пересобирать дерево в бд каждый раз, когда мы добавили за сессию всего один узел - -root = tx.run("MATCH (parent: Node)-->(son: Node) " + - "WHERE NOT ()-->(parent) " + - "RETURN parent.value, parent.key, parent.color, parent.x, parent.y").list().map {it.values()}.get(0) - */ diff --git a/lib/src/main/kotlin/initNeo4j.sh b/lib/src/main/kotlin/initNeo4j.sh new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/main/kotlin/loadNeo4j.sh b/lib/src/main/kotlin/loadNeo4j.sh new file mode 100644 index 0000000..d4d9fcd --- /dev/null +++ b/lib/src/main/kotlin/loadNeo4j.sh @@ -0,0 +1,8 @@ + +BASEDIR=$(realpath "$(dirname "$0")") + +. "${BASEDIR}/CONTAINER.conf" + +docker stop "$CONTAINER_NAME" + +neo4j-admin dump --database=neo4j --to=/data/backups/ \ No newline at end of file diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/testNeo4j.sh index 97ad8db..87b69c4 100644 --- a/lib/src/main/kotlin/testNeo4j.sh +++ b/lib/src/main/kotlin/testNeo4j.sh @@ -3,13 +3,22 @@ BASEDIR=$(realpath "$(dirname "$0")") . "${BASEDIR}/CONTAINER.conf" +# -d docker run \ - --rm \ - --name "CONTAINER_NAME" \ + -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 \ +# -c /bin/bash +#neo4j-admin database dump neo4j --to-path=$HOME/neo4j/data:/data + +#docker ps -a + +#docker stop "$CONTAINER_NAME" + +#cp $HOME/neo4j/data:/data $HOME/ #docker stop neo4j:latest diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt index 0aeb3e0..15e0eab 100644 --- a/lib/src/main/kotlin/treelib/Main.kt +++ b/lib/src/main/kotlin/treelib/Main.kt @@ -1,6 +1,8 @@ package treelib import Controller +import treelib.rbTree.RBStruct +import treelib.singleObjects.Container fun main() { @@ -30,10 +32,35 @@ fun main() { neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") neo4jRep.saveChanges(preArr, inArr) */ + + val tree = RBStruct>() + tree.insert(Container(Pair(25 , 1))) + tree.insert(Container(Pair(15 , 1))) + tree.insert(Container(Pair(50 , 1))) + tree.insert(Container(Pair(10 , 1))) + tree.insert(Container(Pair(22 , 1))) + tree.insert(Container(Pair(35 , 1))) + tree.insert(Container(Pair(70 , 1))) + tree.insert(Container(Pair(4 , 1))) + tree.insert(Container(Pair(12 , 1))) + tree.insert(Container(Pair(18 , 1))) + tree.insert(Container(Pair(24 , 1))) + tree.insert(Container(Pair(31 , 1))) + tree.insert(Container(Pair(44 , 1))) + tree.insert(Container(Pair(66 , 1))) + tree.insert(Container(Pair(90 , 1))) val controller = Controller() - controller.saveTree() + controller.saveTree(tree) + tree.insert(Container(Pair(100, 1))) + controller.saveTree(tree) + controller.initTree() //neo4jRep.exportRBtree() //neo4jRep.close() +} + +fun test() { + val cont = Controller() + cont.initTree() } \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt b/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt new file mode 100644 index 0000000..b3ee499 --- /dev/null +++ b/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt @@ -0,0 +1,10 @@ +package treelib.rbTree + +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/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 211e007..c8c4cf6 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -2,6 +2,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent import treelib.singleObjects.Markers +import treelib.singleObjects.exceptions.IllegalBaseNodeException +import treelib.singleObjects.exceptions.IllegalNodeStateException class RBBalancer>(private var root: RBNode?) : BalancerParent, RBStateContainer>() { @@ -42,7 +44,7 @@ class RBBalancer>(private var root: RBNode?) : override fun balance(stateContainer: RBStateContainer): RBNode { val node = stateContainer.contentNode - ?: throw IllegalStateException() //IllegalBaseNodeException("A non-existent node (null) was passed to the method") + ?: throw IllegalBaseNodeException() val uncle = getUncle(node) when { /** node insertion case **/ @@ -65,7 +67,7 @@ class RBBalancer>(private var root: RBNode?) : currentNode = parent } parent = - currentNode.parent?.parent ?: throw IllegalStateException() // IllegalNodeStateException() + currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -78,7 +80,7 @@ class RBBalancer>(private var root: RBNode?) : currentNode = parent } parent = - currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() + currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -133,7 +135,7 @@ class RBBalancer>(private var root: RBNode?) : val uncle = getUncle(currentNode) if (uncle?.color == Markers.RED) { currentNode.parent?.color = Markers.BLACK - currentNode = currentNode.parent?.parent ?: throw NullPointerException() // IllegalNodeStateException() + currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() currentNode.color = Markers.RED uncle.color = Markers.BLACK } else if (uncle != null) { @@ -156,13 +158,13 @@ class RBBalancer>(private var root: RBNode?) : getRoot(parent) } - else -> getRoot(node ?: throw NullPointerException() /* IllegalNodeStateException() */) + else -> getRoot(node ?: throw IllegalNodeStateException()) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() + var brother = getBrother(parent, node) ?: throw IllegalNodeStateException() if (brother.color == Markers.RED) throw NullPointerException() @@ -214,7 +216,7 @@ class RBBalancer>(private var root: RBNode?) : /** parent is black **/ private fun thirdCase(parent: RBNode, node: RBNode?) { - val brother = getBrother(parent, node) ?: throw NullPointerException() // IllegalNodeStateException() + val brother = getBrother(parent, node) ?: throw IllegalNodeStateException() when (brother.color) { Markers.RED -> thirdCaseSubFirst(brother, parent) Markers.BLACK -> thirdCaseSubSecond(brother, parent) @@ -225,7 +227,7 @@ class RBBalancer>(private var root: RBNode?) : private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { brother.parent?.left -> { - var rightBrotherSon = brother.right ?: throw NullPointerException() // IllegalNodeStateException() + var rightBrotherSon = brother.right ?: throw IllegalNodeStateException() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED @@ -242,7 +244,7 @@ class RBBalancer>(private var root: RBNode?) : leftRotate(rightBrotherSon) rightBrotherSon = - rightBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + rightBrotherSon.parent ?: throw IllegalNodeStateException() rightBrotherSon.color = Markers.BLACK } @@ -258,7 +260,7 @@ class RBBalancer>(private var root: RBNode?) : if (leftBrotherSon.right?.color != Markers.RED && leftBrotherSon.left?.color != Markers.RED) { leftBrotherSon.color = Markers.RED brother.color = Markers.BLACK - leftRotate(brother.parent ?: throw NullPointerException()) // IllegalNodeStateException() + leftRotate(brother.parent ?: throw IllegalNodeStateException()) return } @@ -266,7 +268,7 @@ class RBBalancer>(private var root: RBNode?) : rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED leftBrotherSon = - leftBrotherSon.parent ?: throw NullPointerException() // IllegalNodeStateException() + leftBrotherSon.parent ?: throw IllegalNodeStateException() leftBrotherSon.color = Markers.BLACK } From c7c39d8383311a3d2d2212e0f0c404bb9e0029e7 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 22:37:53 +0300 Subject: [PATCH 125/212] fix: Fix tree class definitions --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 16cba14..2b25734 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ /build/ /lib/build/ /lib/TEST_TEST/ -/gradle/ +/gradle/ \ No newline at end of file From 74fb9789e51f002c0d33781935dd248df90a227c Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 18 Apr 2023 23:36:42 +0300 Subject: [PATCH 126/212] refactor: Rename some methods in test classes --- lib/build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 891ea29..d0c56d7 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -89,7 +89,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 { @@ -97,7 +97,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "BRANCH" - minimum = 0.4.toBigDecimal() + minimum = 0.5.toBigDecimal() } } rule { @@ -111,7 +111,7 @@ tasks.jacocoTestCoverageVerification { element = "CLASS" limit { counter = "METHOD" - minimum = 1.0.toBigDecimal() + minimum = 0.9.toBigDecimal() } } } From b43054a73c8020b1b535bb4f3e11cc3c4288ec8c Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 19 Apr 2023 04:39:37 +0300 Subject: [PATCH 127/212] feat: Implement sketch of jsonRepository --- .gitignore | 2 +- lib/build.gradle.kts | 1 + lib/src/main/kotlin/CONTAINER.conf | 3 - lib/src/main/kotlin/Controller.kt | 35 --- lib/src/main/kotlin/DataBase.kt | 207 ------------------ .../main/kotlin/controller/Neo4jController.kt | 2 +- lib/src/main/kotlin/initNeo4j.sh | 0 lib/src/main/kotlin/loadNeo4j.sh | 8 - lib/src/main/kotlin/testNeo4j.sh | 24 -- lib/src/main/kotlin/treelib/DBNodeRB.kt | 11 - lib/src/main/kotlin/treelib/Main.kt | 66 ------ .../kotlin/treelib/rbTree/DrawRBVertex.kt | 10 - 12 files changed, 3 insertions(+), 366 deletions(-) delete mode 100644 lib/src/main/kotlin/CONTAINER.conf delete mode 100644 lib/src/main/kotlin/Controller.kt delete mode 100644 lib/src/main/kotlin/DataBase.kt delete mode 100644 lib/src/main/kotlin/initNeo4j.sh delete mode 100644 lib/src/main/kotlin/loadNeo4j.sh delete mode 100644 lib/src/main/kotlin/testNeo4j.sh delete mode 100644 lib/src/main/kotlin/treelib/DBNodeRB.kt delete mode 100644 lib/src/main/kotlin/treelib/Main.kt delete mode 100644 lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt diff --git a/.gitignore b/.gitignore index 2b25734..16cba14 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ /build/ /lib/build/ /lib/TEST_TEST/ -/gradle/ \ No newline at end of file +/gradle/ diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index d0c56d7..9d6fcd3 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -37,6 +37,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") implementation(kotlin("stdlib-jdk8")) + } tasks.test { diff --git a/lib/src/main/kotlin/CONTAINER.conf b/lib/src/main/kotlin/CONTAINER.conf deleted file mode 100644 index 9b9fb0a..0000000 --- a/lib/src/main/kotlin/CONTAINER.conf +++ /dev/null @@ -1,3 +0,0 @@ -CONTAINER_NAME=neo4j-db -PASSWORD="test-neo4j" -VOLUMEDIR="/volume" \ No newline at end of file diff --git a/lib/src/main/kotlin/Controller.kt b/lib/src/main/kotlin/Controller.kt deleted file mode 100644 index a59a3b0..0000000 --- a/lib/src/main/kotlin/Controller.kt +++ /dev/null @@ -1,35 +0,0 @@ - -import treelib.rbTree.DrawRBVertex -import treelib.rbTree.RBStruct -import treelib.singleObjects.Container - -class Controller { - - fun initTree() { - 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.restoreTreeFromDatabase(orders.first, orders.second) - neo4jDB.close() - } - - 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/DataBase.kt b/lib/src/main/kotlin/DataBase.kt deleted file mode 100644 index 809eb64..0000000 --- a/lib/src/main/kotlin/DataBase.kt +++ /dev/null @@ -1,207 +0,0 @@ -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.rbTree.DrawRBVertex -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>>>> { - - /*** Вот тут короче надо обращаться к neo4j с темЮ чтобы она инициализировала себя как-то ***/ - - 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/controller/Neo4jController.kt b/lib/src/main/kotlin/controller/Neo4jController.kt index 5c47c63..3744351 100644 --- a/lib/src/main/kotlin/controller/Neo4jController.kt +++ b/lib/src/main/kotlin/controller/Neo4jController.kt @@ -16,7 +16,7 @@ class Neo4jController { neo4jDB.exportRBtree() val RBtree = RBStruct>>() - RBtree.restoreStruct(orders.first, orders.second) + RBtree.restoreTreeFromDatabase(orders.first, orders.second) neo4jDB.close() } diff --git a/lib/src/main/kotlin/initNeo4j.sh b/lib/src/main/kotlin/initNeo4j.sh deleted file mode 100644 index e69de29..0000000 diff --git a/lib/src/main/kotlin/loadNeo4j.sh b/lib/src/main/kotlin/loadNeo4j.sh deleted file mode 100644 index d4d9fcd..0000000 --- a/lib/src/main/kotlin/loadNeo4j.sh +++ /dev/null @@ -1,8 +0,0 @@ - -BASEDIR=$(realpath "$(dirname "$0")") - -. "${BASEDIR}/CONTAINER.conf" - -docker stop "$CONTAINER_NAME" - -neo4j-admin dump --database=neo4j --to=/data/backups/ \ No newline at end of file diff --git a/lib/src/main/kotlin/testNeo4j.sh b/lib/src/main/kotlin/testNeo4j.sh deleted file mode 100644 index 87b69c4..0000000 --- a/lib/src/main/kotlin/testNeo4j.sh +++ /dev/null @@ -1,24 +0,0 @@ - -BASEDIR=$(realpath "$(dirname "$0")") - -. "${BASEDIR}/CONTAINER.conf" - -# -d -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 \ -# -c /bin/bash -#neo4j-admin database dump neo4j --to-path=$HOME/neo4j/data:/data - -#docker ps -a - -#docker stop "$CONTAINER_NAME" - -#cp $HOME/neo4j/data:/data $HOME/ - -#docker stop neo4j:latest diff --git a/lib/src/main/kotlin/treelib/DBNodeRB.kt b/lib/src/main/kotlin/treelib/DBNodeRB.kt deleted file mode 100644 index cf067e1..0000000 --- a/lib/src/main/kotlin/treelib/DBNodeRB.kt +++ /dev/null @@ -1,11 +0,0 @@ -package treelib - -import treelib.singleObjects.Markers - - -class DBNodeRB>( - val value: Pack, - val color: Markers = Markers.RED, - val x: Double = 0.0, - val y: Double = 0.0 -) diff --git a/lib/src/main/kotlin/treelib/Main.kt b/lib/src/main/kotlin/treelib/Main.kt deleted file mode 100644 index 15e0eab..0000000 --- a/lib/src/main/kotlin/treelib/Main.kt +++ /dev/null @@ -1,66 +0,0 @@ -package treelib - -import Controller -import treelib.rbTree.RBStruct -import treelib.singleObjects.Container - - -fun main() { - // 25, 15, 10, 4, 12, 22, 18, 24, 50, 35, 31, 44, 70, 66, 90 - // 4, 10, 12, 15, 18, 22, 24, 25, 31, 35, 44, 50, 66, 70, 90 - - /* - - val neo4jRep = Neo4jRepository() - val a1 = DBNodeRB(Container(Pair(1, 25))) - val a2 = DBNodeRB(Container(Pair(1, 15))) - val a3 = DBNodeRB(Container(Pair(1, 10))) - val a4 = DBNodeRB(Container(Pair(1, 4))) - val a5 = DBNodeRB(Container(Pair(1, 12))) - val a6 = DBNodeRB(Container(Pair(1, 22))) - val a7 = DBNodeRB(Container(Pair(1, 18))) - val a8 = DBNodeRB(Container(Pair(1, 24))) - val a9 = DBNodeRB(Container(Pair(1, 50))) - val a10 = DBNodeRB(Container(Pair(1, 35))) - val a11 = DBNodeRB(Container(Pair(1, 31))) - val a12 = DBNodeRB(Container(Pair(1, 44))) - val a13 = DBNodeRB(Container(Pair(1, 70))) - val a14 = DBNodeRB(Container(Pair(1, 66))) - val a15 = DBNodeRB(Container(Pair(1, 90))) - val preArr = arrayOf(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) - val inArr = arrayOf(a4, a3, a5, a2, a7, a6, a8, a1, a11, a10, a12, a9, a14, a13, a15) - neo4jRep.open("bolt://localhost:7687", "neo4j", "test-neo4j") - neo4jRep.saveChanges(preArr, inArr) - */ - - val tree = RBStruct>() - tree.insert(Container(Pair(25 , 1))) - tree.insert(Container(Pair(15 , 1))) - tree.insert(Container(Pair(50 , 1))) - tree.insert(Container(Pair(10 , 1))) - tree.insert(Container(Pair(22 , 1))) - tree.insert(Container(Pair(35 , 1))) - tree.insert(Container(Pair(70 , 1))) - tree.insert(Container(Pair(4 , 1))) - tree.insert(Container(Pair(12 , 1))) - tree.insert(Container(Pair(18 , 1))) - tree.insert(Container(Pair(24 , 1))) - tree.insert(Container(Pair(31 , 1))) - tree.insert(Container(Pair(44 , 1))) - tree.insert(Container(Pair(66 , 1))) - tree.insert(Container(Pair(90 , 1))) - val controller = Controller() - controller.saveTree(tree) - tree.insert(Container(Pair(100, 1))) - controller.saveTree(tree) - controller.initTree() - - //neo4jRep.exportRBtree() - - //neo4jRep.close() -} - -fun test() { - val cont = Controller() - cont.initTree() -} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt b/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt deleted file mode 100644 index b3ee499..0000000 --- a/lib/src/main/kotlin/treelib/rbTree/DrawRBVertex.kt +++ /dev/null @@ -1,10 +0,0 @@ -package treelib.rbTree - -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 From 4b87eeedfc4a6f8dc8c210f221c5d8d6230fb3c1 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 19 Apr 2023 03:20:10 +0300 Subject: [PATCH 128/212] feat: Implement AVLStruct.restoreStruct and BINStruct.restoreStruct. --- lib/src/main/kotlin/controller/Neo4jController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/controller/Neo4jController.kt b/lib/src/main/kotlin/controller/Neo4jController.kt index 3744351..5c47c63 100644 --- a/lib/src/main/kotlin/controller/Neo4jController.kt +++ b/lib/src/main/kotlin/controller/Neo4jController.kt @@ -16,7 +16,7 @@ class Neo4jController { neo4jDB.exportRBtree() val RBtree = RBStruct>>() - RBtree.restoreTreeFromDatabase(orders.first, orders.second) + RBtree.restoreStruct(orders.first, orders.second) neo4jDB.close() } From f6f213785aa561cde7be4af80b850825d9ad9127 Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 19 Apr 2023 06:06:19 +0300 Subject: [PATCH 129/212] refactor: Rename some classes after rebase onto SQLiteDB branch --- lib/build.gradle.kts | 1 + .../{treelib/controller => dbSave}/DrawVertex.kt | 2 +- .../sqlite/DrawAVLVertex.kt} | 6 +++--- .../sqlite/SQLiteRepository.kt} | 16 ++++++++-------- .../kotlin/treelib/singleObjects/Container.kt | 3 +++ 5 files changed, 16 insertions(+), 12 deletions(-) rename lib/src/main/kotlin/{treelib/controller => dbSave}/DrawVertex.kt (79%) rename lib/src/main/kotlin/{treelib/dbSave/avlSQLite/AVLDrawVertex.kt => dbSave/sqlite/DrawAVLVertex.kt} (53%) rename lib/src/main/kotlin/{treelib/dbSave/avlSQLite/SQLiteAVL.kt => dbSave/sqlite/SQLiteRepository.kt} (94%) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 9d6fcd3..080c014 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -22,6 +22,7 @@ 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") diff --git a/lib/src/main/kotlin/treelib/controller/DrawVertex.kt b/lib/src/main/kotlin/dbSave/DrawVertex.kt similarity index 79% rename from lib/src/main/kotlin/treelib/controller/DrawVertex.kt rename to lib/src/main/kotlin/dbSave/DrawVertex.kt index 8ccd5dc..ab0ed41 100644 --- a/lib/src/main/kotlin/treelib/controller/DrawVertex.kt +++ b/lib/src/main/kotlin/dbSave/DrawVertex.kt @@ -1,4 +1,4 @@ -package treelib.controller +package dbSave interface DrawVertex> { val value: Pack diff --git a/lib/src/main/kotlin/treelib/dbSave/avlSQLite/AVLDrawVertex.kt b/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt similarity index 53% rename from lib/src/main/kotlin/treelib/dbSave/avlSQLite/AVLDrawVertex.kt rename to lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt index be2938c..6b773db 100644 --- a/lib/src/main/kotlin/treelib/dbSave/avlSQLite/AVLDrawVertex.kt +++ b/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt @@ -1,8 +1,8 @@ -package treelib.dbSave.avlSQLite +package dbSave.sqlite -import treelib.controller.DrawVertex +import dbSave.DrawVertex -class AVLDrawVertex>( +class DrawAVLVertex>( override val value: Pack, override val x: Double, override val y: Double, diff --git a/lib/src/main/kotlin/treelib/dbSave/avlSQLite/SQLiteAVL.kt b/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt similarity index 94% rename from lib/src/main/kotlin/treelib/dbSave/avlSQLite/SQLiteAVL.kt rename to lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt index c125768..f12f015 100644 --- a/lib/src/main/kotlin/treelib/dbSave/avlSQLite/SQLiteAVL.kt +++ b/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt @@ -1,10 +1,10 @@ -package treelib.dbSave.avlSQLite +package dbSave.sqlite import java.io.Closeable import java.sql.DriverManager import java.sql.SQLException -class SQLiteAVL>( +class SQLiteRepository>( private val dbPath: String, private val serializeData: (input: Pack) -> String, private val deSerializeData: (input: String) -> Pack, @@ -124,7 +124,7 @@ class SQLiteAVL>( else throw SQLException("Impossible case") } - fun addVertex(avlDVertex: AVLDrawVertex, treeName: String) { + fun addVertex(avlDVertex: DrawAVLVertex, treeName: String) { val isInDB = getVertexId(avlDVertex, treeName) if (isInDB != 0) { deleteVertex(isInDB, treeName) @@ -147,7 +147,7 @@ class SQLiteAVL>( } - fun addVertexes(list: List>, treeName: String) { + fun addVertexes(list: List>, treeName: String) { for (el in list) addVertex(el, treeName) } @@ -163,15 +163,15 @@ class SQLiteAVL>( } } - fun getAllVertexes(treeName: String): MutableList> { - val info = mutableListOf>() + 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( - AVLDrawVertex( + DrawAVLVertex( value = deSerializeData(result.getString(value)), height = result.getInt(height), x = result.getDouble(xCord), @@ -189,7 +189,7 @@ class SQLiteAVL>( } } - private fun getVertexId(vertex: AVLDrawVertex, tableName: String): Int { + private fun getVertexId(vertex: DrawAVLVertex, tableName: String): Int { var id: Int? = null try { val stmt = 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 From 66835546e4e6a1fbc31e86d49a0b0af6c7ffda97 Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 19 Apr 2023 07:49:38 +0300 Subject: [PATCH 130/212] refactor: Change the names of some classes --- .../main/kotlin/controller/BINTreeManager.kt | 21 +++++++++++++++++++ .../{Neo4jController.kt => RBTreeManager.kt} | 9 +++++--- lib/src/main/kotlin/controller/TreeManager.kt | 4 ++++ .../dbSave/jsonFormat/JsonRepository.kt | 14 ++++++------- .../kotlin/dbSave/neo4j/Neo4jRepository.kt | 2 +- 5 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 lib/src/main/kotlin/controller/BINTreeManager.kt rename lib/src/main/kotlin/controller/{Neo4jController.kt => RBTreeManager.kt} (88%) create mode 100644 lib/src/main/kotlin/controller/TreeManager.kt 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/Neo4jController.kt b/lib/src/main/kotlin/controller/RBTreeManager.kt similarity index 88% rename from lib/src/main/kotlin/controller/Neo4jController.kt rename to lib/src/main/kotlin/controller/RBTreeManager.kt index 5c47c63..0d3456e 100644 --- a/lib/src/main/kotlin/controller/Neo4jController.kt +++ b/lib/src/main/kotlin/controller/RBTreeManager.kt @@ -5,9 +5,11 @@ import dbSave.neo4j.Neo4jRepository import treelib.rbTree.RBStruct import treelib.singleObjects.Container -class Neo4jController { +class RBTreeManager { - fun initTree() { + /*** using neo4j ***/ + + fun initTree(treeName: String): RBStruct>> { val neo4jDB = Neo4jRepository() neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") @@ -18,6 +20,8 @@ class Neo4jController { val RBtree = RBStruct>>() RBtree.restoreStruct(orders.first, orders.second) neo4jDB.close() + + return RBtree } fun > saveTree(tree: RBStruct) { @@ -31,7 +35,6 @@ class Neo4jController { neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray()) neo4jDB.close() - } } \ No newline at end of file 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/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt index 88a1396..7f58420 100644 --- a/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt +++ b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt @@ -3,13 +3,13 @@ import com.google.common.reflect.TypeToken import com.google.gson.GsonBuilder import java.io.File -class JsonRepository>(private val dirPath: String) { +class JsonRepository(private val dirPath: String) { init { File(dirPath).mkdirs() } - fun saveChanges(preOrder: Array>, typeToken: TypeToken>>, fileName: String) { + fun >saveChanges(preOrder: Array>, fileName: String) { val gson = GsonBuilder().setPrettyPrinting().create() val json = gson.toJson(preOrder) @@ -19,16 +19,14 @@ class JsonRepository>(private val dirPath: String) { writeText(json) } - val preOrd = gson.fromJson>>(json, typeToken.type) - } - fun exportTree(fileName: String) { + fun> exportTree(fileName: String, typeToken: TypeToken>>): Array> { val gson = GsonBuilder().setPrettyPrinting().create() - //val json = gson.fromJson(File(dirPath, fileName).readText(), ArrayVertices::class.java) - + val json = File(dirPath, fileName).readText() + val preOrder = gson.fromJson>>(json, typeToken.type) + return preOrder } - } \ 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 index 316dc4c..f04cf8c 100644 --- a/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt @@ -88,7 +88,7 @@ class Neo4jRepository : Closeable { fun exportRBtree(): Pair>>>, List>>>> { - /*** Вот тут короче надо обращаться к neo4j с темЮ чтобы она инициализировала себя как-то ***/ + /*** Плохо, что передаем Container с четко привязанными типами (K, V), потому что может быть так, что вместо контейнера будет просто инт ***/ val session = driver?.session() ?: throw IOException() var preOrder: List>>> = listOf() From 0b26e58e05a82ae44538c7cd93084ac90bc2220c Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 19 Apr 2023 08:11:53 +0300 Subject: [PATCH 131/212] feat: Implement Beta-SQLiteController; refactor code-style. --- .../kotlin/controller/SQLiteController.kt | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 lib/src/main/kotlin/controller/SQLiteController.kt 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() +} From a15abed7790c0c0585f0647f4d83bd42befa983c Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 19 Apr 2023 08:15:55 +0300 Subject: [PATCH 132/212] refactor: Correct code style. --- lib/src/main/kotlin/dbSave/DrawVertex.kt | 2 +- .../dbSave/jsonFormat/JsonRepository.kt | 19 ++- .../kotlin/dbSave/sqlite/DrawAVLVertex.kt | 2 +- .../kotlin/dbSave/sqlite/SQLiteRepository.kt | 6 +- .../main/kotlin/treelib/abstractTree/Tree.kt | 2 +- .../kotlin/treelib/abstractTree/TreeStruct.kt | 28 +++-- .../kotlin/treelib/abstractTree/Vertex.kt | 2 +- .../balanced/BalancedTreeStruct.kt | 8 +- .../abstractTree/balanced/BalancerParent.kt | 3 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 5 +- .../main/kotlin/treelib/avlTree/AVLVertex.kt | 2 +- .../main/kotlin/treelib/binTree/BINStruct.kt | 4 +- .../main/kotlin/treelib/binTree/BINVertex.kt | 2 +- .../main/kotlin/treelib/rbTree/RBStruct.kt | 4 +- .../main/kotlin/treelib/rbTree/RBVertex.kt | 4 +- .../exceptions/IllegalBaseNodeException.kt | 2 +- .../exceptions/IncorrectUsage.kt | 2 +- lib/src/test/kotlin/treeTests/RBTreeTest.kt | 16 --- .../test/kotlin/treelib/AVLBalancerTest.kt | 17 ++- lib/src/test/kotlin/treelib/AVLStructTest.kt | 1 + lib/src/test/kotlin/treelib/AVLTreeTest.kt | 28 +++-- lib/src/test/kotlin/treelib/BINStructTest.kt | 4 +- lib/src/test/kotlin/treelib/BINTreeTest.kt | 18 ++- lib/src/test/kotlin/treelib/RBBalancerTest.kt | 24 ++-- lib/src/test/kotlin/treelib/RBStructTest.kt | 25 +++- lib/src/test/kotlin/treelib/TestModelAVL.kt | 90 ++++++++------ lib/src/test/kotlin/treelib/TestModelRBT.kt | 112 +++++++++++++----- lib/src/test/kotlin/utils/AVLAnalyzer.kt | 2 +- lib/src/test/kotlin/utils/RBAnalyzer.kt | 2 +- .../test/kotlin/utils/TreeStructWrapper.kt | 9 +- lib/src/test/kotlin/utils/TreeWrapper.kt | 12 +- .../kotlin/utils/fuzzers/BINStructFuzzer.kt | 4 +- .../kotlin/utils/fuzzers/RBStructFuzzer.kt | 4 +- .../kotlin/utils/fuzzers/TreeStructFuzzer.kt | 6 +- 34 files changed, 287 insertions(+), 184 deletions(-) delete mode 100644 lib/src/test/kotlin/treeTests/RBTreeTest.kt diff --git a/lib/src/main/kotlin/dbSave/DrawVertex.kt b/lib/src/main/kotlin/dbSave/DrawVertex.kt index ab0ed41..4108556 100644 --- a/lib/src/main/kotlin/dbSave/DrawVertex.kt +++ b/lib/src/main/kotlin/dbSave/DrawVertex.kt @@ -1,6 +1,6 @@ package dbSave -interface DrawVertex> { +interface DrawVertex> { val value: Pack val x: Double val y: Double diff --git a/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt index 7f58420..c2ca1c1 100644 --- a/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt +++ b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt @@ -1,15 +1,20 @@ package dbSave.jsonFormat + import com.google.common.reflect.TypeToken import com.google.gson.GsonBuilder import java.io.File -class JsonRepository(private val dirPath: String) { +class JsonRepository>(private val dirPath: String) { init { File(dirPath).mkdirs() } - fun >saveChanges(preOrder: Array>, fileName: String) { + fun saveChanges( + preOrder: Array>, + typeToken: TypeToken>>, + fileName: String + ) { val gson = GsonBuilder().setPrettyPrinting().create() val json = gson.toJson(preOrder) @@ -19,14 +24,16 @@ class JsonRepository(private val dirPath: String) { writeText(json) } + val preOrd = gson.fromJson>>(json, typeToken.type) + } - fun> exportTree(fileName: String, typeToken: TypeToken>>): Array> { + fun exportTree(fileName: String) { val gson = GsonBuilder().setPrettyPrinting().create() - val json = File(dirPath, fileName).readText() - val preOrder = gson.fromJson>>(json, typeToken.type) + //val json = gson.fromJson(File(dirPath, fileName).readText(), ArrayVertices::class.java) + - return preOrder } + } \ No newline at end of file diff --git a/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt b/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt index 6b773db..e6feb46 100644 --- a/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt +++ b/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt @@ -2,7 +2,7 @@ package dbSave.sqlite import dbSave.DrawVertex -class DrawAVLVertex>( +class DrawAVLVertex>( override val value: Pack, override val x: Double, override val y: Double, diff --git a/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt b/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt index f12f015..1d4cf20 100644 --- a/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt +++ b/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt @@ -83,7 +83,7 @@ class SQLiteRepository>( while (result.next()) { info.add(result.getString(avlTreeName)) } - logInfoMethod("") + logInfoMethod("Available tree is given") } catch (ex: SQLException) { logErrorMethod(ex) } finally { @@ -110,7 +110,7 @@ class SQLiteRepository>( } } - private fun getTreeId(treeName: String): Int { + fun getTreeId(treeName: String): Int { var id: Int? = null try { val statement = connection.prepareStatement("SELECT id FROM $treeTable WHERE name=?;") @@ -147,7 +147,7 @@ class SQLiteRepository>( } - fun addVertexes(list: List>, treeName: String) { + fun addVertexes(list: MutableList>, treeName: String) { for (el in list) addVertex(el, treeName) } diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index 1116ad6..fb0976a 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -8,7 +8,7 @@ abstract class Tree< V, NodeType : Node, NodeType>, State : StateContainer, NodeType>, - VertexType: Vertex> + VertexType : Vertex> > { protected abstract val treeStruct: TreeStruct, NodeType, State, VertexType> diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index d9b223b..fa403bc 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -10,7 +10,7 @@ abstract class TreeStruct< Pack : Comparable, NodeType : Node, State : StateContainer, - VertexType: Vertex + VertexType : Vertex > { protected abstract var root: NodeType? @@ -52,7 +52,8 @@ abstract class TreeStruct< else currentNode = it.left } } - } ?: throw BugInImplementException("getParentByValue shouldn't be used with value doesn't exist in tree")// (1)l -> + } + ?: throw BugInImplementException("getParentByValue shouldn't be used with value doesn't exist in tree")// (1)l -> } } @@ -75,7 +76,8 @@ abstract class TreeStruct< private fun getRightMinNode(localRoot: NodeType): NodeType { var currentNode: NodeType? - localRoot.right ?: throw BugInImplementException("Incorrect usage of the getRightMinNode: right node doesn't exist") + localRoot.right + ?: throw BugInImplementException("Incorrect usage of the getRightMinNode: right node doesn't exist") currentNode = localRoot.right @@ -227,11 +229,11 @@ abstract class TreeStruct< for (child in listOf(deleteNode.right, deleteNode.left)) child?.let { connectUnlinkedSubTreeWithParent(deleteNode, parentDeleteNode, it) - if (parentDeleteNode != null) { - return@deleteItem generateStateDelete(deletedNodeWithoutLinks, parentDeleteNode) - } else { - return@deleteItem generateStateDelete(deletedNodeWithoutLinks, root) - } + if (parentDeleteNode != null) { + return@deleteItem generateStateDelete(deletedNodeWithoutLinks, parentDeleteNode) + } else { + return@deleteItem generateStateDelete(deletedNodeWithoutLinks, root) + } } } } @@ -285,13 +287,13 @@ abstract class TreeStruct< current = it.right } else { if (parents.isEmpty()) - return@inOrder arrayNodes.map {toVertex(it)} + return@inOrder arrayNodes.map { toVertex(it) } flagVisited = 1 current = parents.removeLast() } } } - return arrayNodes.map{toVertex(it)} + return arrayNodes.map { toVertex(it) } } abstract fun toVertex(node: NodeType): VertexType @@ -321,7 +323,7 @@ abstract class TreeStruct< } else { arrayNodes.add(it) if (parents.isEmpty()) - return@postOrder arrayNodes.map{toVertex(it)} + return@postOrder arrayNodes.map { toVertex(it) } val parent = parents.removeLast() if (parent.right == it) { flagVisited = 2 @@ -330,7 +332,7 @@ abstract class TreeStruct< } } ?: throw MultithreadingException(ImpossibleCaseException()) } - return arrayNodes.map{toVertex(it)} + return arrayNodes.map { toVertex(it) } } fun preOrder(): List { @@ -354,7 +356,7 @@ abstract class TreeStruct< } ?: throw MultithreadingException(ImpossibleCaseException()) } } - return arrayNodes.map {toVertex(it)} + return arrayNodes.map { toVertex(it) } } diff --git a/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt b/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt index abbb7a5..68f3726 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt @@ -1,5 +1,5 @@ package treelib.abstractTree -abstract class Vertex>{ +abstract class Vertex> { 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 96e8458..5172f71 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt @@ -9,22 +9,22 @@ abstract class BalancedTreeStruct< Pack : Comparable, NodeType : Node, State : StateContainer, - VertexType: Vertex, + VertexType : Vertex, BalancerType : Balancer, > : 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/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index b0ee0ae..6d89668 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -57,9 +57,10 @@ class AVLStruct> : } return node } - fun > restoreStruct(preOrder: List){ + + fun > restoreStruct(preOrder: MutableList) { if (root != null) throw IncorrectUsage("The tree already exists") - for (vertex in preOrder){ + 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/AVLVertex.kt b/lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt index a43ea26..774d064 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt @@ -5,4 +5,4 @@ import treelib.abstractTree.Vertex class AVLVertex>( override val value: Pack, val height: UInt, -) : Vertex() \ No newline at end of file +) : Vertex() diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index ea03cb6..c033266 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -71,9 +71,9 @@ class BINStruct> : private fun toNode(vertex: BINVertex): BINNode = BINNode(value = vertex.value) - fun > restoreStruct(preOrder: List){ + fun > restoreStruct(preOrder: List) { if (root != null) throw IncorrectUsage("The tree already exists") - for (vertex in preOrder){ + 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/BINVertex.kt b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt index 8939461..cb489a0 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINVertex.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt @@ -2,4 +2,4 @@ package treelib.binTree import treelib.abstractTree.Vertex -open class BINVertex>(override val value: Pack): Vertex() \ No newline at end of file +open class BINVertex>(override val value: Pack) : Vertex() \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 999c113..a3cbe34 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -76,7 +76,7 @@ class RBStruct> : return node } - fun > restoreStruct(preOrder: List, inOrder: List) { + fun > restoreStruct(preOrder: List, inOrder: List) { var inOrderIndex = 0 var preOrderIndex = 0 val set = HashSet>() @@ -119,7 +119,7 @@ class RBStruct> : } } - private fun > createRBNode(drawNode: RBVertexType): RBNode { + 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/RBVertex.kt b/lib/src/main/kotlin/treelib/rbTree/RBVertex.kt index 5d9d161..aee1d95 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBVertex.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBVertex.kt @@ -3,7 +3,7 @@ package treelib.rbTree import treelib.abstractTree.Vertex import treelib.singleObjects.Markers -open class RBVertex>( +open class RBVertex>( override val value: Pack, val color: Markers, -):Vertex() \ No newline at end of file +) : Vertex() \ No newline at end of file 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 index 9fce7ef..3f16415 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt @@ -1,6 +1,6 @@ package treelib.singleObjects.exceptions -class IncorrectUsage : Exception { +class IncorrectUsage : Exception { constructor() : super( "Incorrect use of the tree" ) diff --git a/lib/src/test/kotlin/treeTests/RBTreeTest.kt b/lib/src/test/kotlin/treeTests/RBTreeTest.kt deleted file mode 100644 index fa56cc6..0000000 --- a/lib/src/test/kotlin/treeTests/RBTreeTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package treeTests - -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import treelib.rbTree.RBTree - -@DisplayName("Test: Red-Black Tree") -class RBTreeTest { - private var rbTree = RBTree() - - @Test - fun `insert two elements`(){ - rbTree.putItem(Pair(25, 1)) - rbTree.putItem(Pair(15, 1)) - } -} \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/AVLBalancerTest.kt b/lib/src/test/kotlin/treelib/AVLBalancerTest.kt index 3ee5452..43b6978 100644 --- a/lib/src/test/kotlin/treelib/AVLBalancerTest.kt +++ b/lib/src/test/kotlin/treelib/AVLBalancerTest.kt @@ -126,7 +126,6 @@ class AVLBalancerTest { } - } @DisplayName("Tests to check the operation of the balancer after insertion") @@ -174,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", @@ -183,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 **/ @@ -205,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 **/ @@ -225,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 index f003b60..433618a 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -18,6 +18,7 @@ class AVLStructTest { private var treeStruct = AVLStruct() private fun testAssert(msg: String): Nothing = fail(msg) + @BeforeEach fun reInitClassUnderTest() { treeStruct = AVLStruct() diff --git a/lib/src/test/kotlin/treelib/AVLTreeTest.kt b/lib/src/test/kotlin/treelib/AVLTreeTest.kt index 5915d99..63073da 100644 --- a/lib/src/test/kotlin/treelib/AVLTreeTest.kt +++ b/lib/src/test/kotlin/treelib/AVLTreeTest.kt @@ -20,7 +20,7 @@ class AVLTreeTest { @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 numbers = str.split(" ").map { it.toInt() }.toMutableList() val num = mutableListOf>() for (i in numbers) { num.add(Pair(i, 1)) @@ -31,8 +31,7 @@ class AVLTreeTest { val root = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key if (root == numbers[1]) { assertEquals(expected = root, actual = numbers[1]) - } - else { + } else { assertEquals(expected = root, actual = numbers[2]) } } @@ -40,8 +39,8 @@ class AVLTreeTest { @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]) { + 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 @@ -51,16 +50,27 @@ class AVLTreeTest { 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) + 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 numbers = str.split(" ").map { it.toInt() }.toMutableList() val num = mutableListOf>() for (i in numbers) { num.add(Pair(i, 1)) diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 1075631..bab0d2d 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -14,7 +14,7 @@ import kotlin.test.assertEquals @DisplayName("Test: Binary Search Tree Struct") class BINStructTest { - val treeW = TreeStructWrapper, BINVertex, BINStateContainer, BINStruct>() + val treeW = TreeStructWrapper, BINVertex, BINStateContainer, BINStruct>() var treeStruct = BINStruct() private fun testAssert(msg: String): Nothing = fail(msg) @@ -481,7 +481,7 @@ class BINStructTest { } @Test - fun `test analyzer`(){ + fun `test analyzer`() { val num = mutableListOf(6, 8, 10, 7, 12, 9) for (i in num) { treeStruct.insert(i) diff --git a/lib/src/test/kotlin/treelib/BINTreeTest.kt b/lib/src/test/kotlin/treelib/BINTreeTest.kt index 5157c95..a13d174 100644 --- a/lib/src/test/kotlin/treelib/BINTreeTest.kt +++ b/lib/src/test/kotlin/treelib/BINTreeTest.kt @@ -17,7 +17,7 @@ class BINTreeTest { TreeStructWrapper, BINNode>, BINVertex>, BINStateContainer>, BINStruct>>() -// line - 0.6, branch - 0.5, methods = 0.9 + // line - 0.6, branch - 0.5, methods = 0.9 @ParameterizedTest @ValueSource(ints = [1, 2, 0, 6, 4]) fun `test putItem`(str: Int) { @@ -27,14 +27,17 @@ class BINTreeTest { @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 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]) + assertEquals( + expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.right?.value?.key, + actual = numbers[1] + ) } @Test @@ -52,7 +55,7 @@ class BINTreeTest { @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 numbers = str.split(" ").map { it.toInt() }.toMutableList() val num = mutableListOf>() for (i in numbers) { num.add(Pair(i, 1)) @@ -67,7 +70,7 @@ class BINTreeTest { @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 numbers = str.split(" ").map { it.toInt() }.toMutableList() val num = mutableListOf>() for (i in numbers) { num.add(Pair(i, 1)) @@ -78,6 +81,9 @@ class BINTreeTest { val index = numbers.indexOf(root) tree.deleteItem(root) - assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, actual = numbers[index + 1]) + 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/treelib/RBBalancerTest.kt b/lib/src/test/kotlin/treelib/RBBalancerTest.kt index d18134e..41f8bdd 100644 --- a/lib/src/test/kotlin/treelib/RBBalancerTest.kt +++ b/lib/src/test/kotlin/treelib/RBBalancerTest.kt @@ -9,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 @@ -22,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 @@ -100,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) } ) } @@ -118,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( @@ -137,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) } ) } @@ -155,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) } ) } @@ -166,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]) @@ -190,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 index 3196207..9f0b39e 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -117,7 +117,30 @@ class RBStructTest { @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) + 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( diff --git a/lib/src/test/kotlin/treelib/TestModelAVL.kt b/lib/src/test/kotlin/treelib/TestModelAVL.kt index 8a3ba26..9b4823a 100644 --- a/lib/src/test/kotlin/treelib/TestModelAVL.kt +++ b/lib/src/test/kotlin/treelib/TestModelAVL.kt @@ -4,69 +4,81 @@ 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) + 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) + 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) + 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) + 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) + 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) + 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 { + 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 + } 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])) + nodes.add(AVLNode(values[i]!!, nodes[2 * (i - 16)], nodes[2 * (i - 16) + 1])) updateHeight(nodes[i]) - } - else + } else nodes.add(null) } nodes.add(AVLNode(values[30]!!, nodes[28], nodes[29])) return nodes[30]!! } - private fun >updateHeight(currentNode: AVLNode?) { + + private fun > updateHeight(currentNode: AVLNode?) { if (currentNode != null) - currentNode.height = ( maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u) + currentNode.height = (maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u) } - private fun >getHeight(currentNode: AVLNode?): UInt { + + private fun > getHeight(currentNode: AVLNode?): UInt { return currentNode?.height ?: 0u } diff --git a/lib/src/test/kotlin/treelib/TestModelRBT.kt b/lib/src/test/kotlin/treelib/TestModelRBT.kt index ca7efe7..9e5a0ad 100644 --- a/lib/src/test/kotlin/treelib/TestModelRBT.kt +++ b/lib/src/test/kotlin/treelib/TestModelRBT.kt @@ -4,17 +4,17 @@ 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 @@ -32,8 +32,7 @@ class TestModelRBT { else -> parent.right = newNode } newNode.parent = parent - } - else { + } else { newNode.parent = null } @@ -86,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 ) @@ -104,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/utils/AVLAnalyzer.kt b/lib/src/test/kotlin/utils/AVLAnalyzer.kt index 15e7825..3d1a6fd 100644 --- a/lib/src/test/kotlin/utils/AVLAnalyzer.kt +++ b/lib/src/test/kotlin/utils/AVLAnalyzer.kt @@ -4,7 +4,7 @@ import treelib.avlTree.AVLNode import kotlin.math.abs import kotlin.math.max -class AVLAnalyzer>(override val assertMethod: (input: String) -> Unit) : Analyzer>() { +class AVLAnalyzer>(override val assertMethod: (input: String) -> Unit) : Analyzer>() { private var heightL = 0 private var heightR = 0 private var heightMax = 0 diff --git a/lib/src/test/kotlin/utils/RBAnalyzer.kt b/lib/src/test/kotlin/utils/RBAnalyzer.kt index 1b04aff..9b6b6db 100644 --- a/lib/src/test/kotlin/utils/RBAnalyzer.kt +++ b/lib/src/test/kotlin/utils/RBAnalyzer.kt @@ -79,7 +79,7 @@ class RBAnalyzer>( return errorMagicNumber } - if(node.color == Markers.BLACK) return leftBlackCount + 1 + if (node.color == Markers.BLACK) return leftBlackCount + 1 else return rightBlackCount } } \ No newline at end of file diff --git a/lib/src/test/kotlin/utils/TreeStructWrapper.kt b/lib/src/test/kotlin/utils/TreeStructWrapper.kt index 64fea41..ba9fb10 100644 --- a/lib/src/test/kotlin/utils/TreeStructWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeStructWrapper.kt @@ -5,7 +5,7 @@ import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct import treelib.abstractTree.Vertex -class TreeStructWrapper, NodeType : Node, VertexType: Vertex, 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) @@ -15,7 +15,12 @@ class TreeStructWrapper, NodeType : Node, VertexT 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 index 47858d9..a0abe1c 100644 --- a/lib/src/test/kotlin/utils/TreeWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeWrapper.kt @@ -4,14 +4,14 @@ import treelib.abstractTree.* import treelib.singleObjects.Container -class TreeWrapper < +class TreeWrapper< V : Comparable, Value, - NodeType: Node, NodeType>, - VertexType: Vertex>, - State: StateContainer, NodeType>, - TStruct: TreeStruct, NodeType, State, VertexType>, - Wood : Tree> { + 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 diff --git a/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt index bc79507..720789c 100644 --- a/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/BINStructFuzzer.kt @@ -6,10 +6,10 @@ import treelib.binTree.BINStruct import treelib.binTree.BINVertex import utils.BINAnalyzer -class BINStructFuzzer>( +class BINStructFuzzer>( override val baseInput: Array, override val assertMethod: (input: String) -> Unit -): TreeStructFuzzer, BINVertex, BINAnalyzer, BINStateContainer, BINStruct>() { +) : TreeStructFuzzer, BINVertex, BINAnalyzer, BINStateContainer, BINStruct>() { override fun createTreeStruct(): BINStruct = BINStruct() diff --git a/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt index 2a6bb7f..81d5278 100644 --- a/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/RBStructFuzzer.kt @@ -6,10 +6,10 @@ import treelib.rbTree.RBStruct import treelib.rbTree.RBVertex import utils.RBAnalyzer -class RBStructFuzzer>( +class RBStructFuzzer>( override val baseInput: Array, override val assertMethod: (input: String) -> Unit -): TreeStructFuzzer, RBVertex, RBAnalyzer, RBStateContainer, RBStruct>() { +) : TreeStructFuzzer, RBVertex, RBAnalyzer, RBStateContainer, RBStruct>() { override fun createTreeStruct(): RBStruct = RBStruct() diff --git a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt index 7d5e0d6..69421d8 100644 --- a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt @@ -43,7 +43,6 @@ abstract class TreeStructFuzzer< for (iterations in 0 until testNumbers) { val treeStruct = createTreeStruct() - println(dataSize) val testSetIndexes = getInputSetIndexes(dataSize) val testID = "${Instant.now().toEpochMilli() + iterations}-insert" val analyzer = createAnalyzer() @@ -119,12 +118,9 @@ abstract class TreeStructFuzzer< private fun saveCurrentTestSet(testId: String, testSet: List) { val file = File("./$dirPath/$testId.txt") file.createNewFile() -// println("./$dirPath/$testId.txt") for (index in testSet) { -// print("${baseInput[index]} ") file.appendText("${baseInput[index]} \n") } -// println() } -} \ No newline at end of file +} From 28f3c3a1a3020dd05d184ec46b3b105d14ba63bd Mon Sep 17 00:00:00 2001 From: Artem-Rzhankoff <113193908+Artem-Rzhankoff@users.noreply.github.com> Date: Wed, 19 Apr 2023 08:22:19 +0300 Subject: [PATCH 133/212] feat: Create LICENCE --- LICENCE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENCE 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. From 2045d29e62b4e7aeaabe4360d2624db0b35aa5bc Mon Sep 17 00:00:00 2001 From: Artem-Rzhankoff <113193908+Artem-Rzhankoff@users.noreply.github.com> Date: Wed, 19 Apr 2023 08:25:22 +0300 Subject: [PATCH 134/212] Update README.md --- README.md | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) 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. + +
+ From 14ea8b8ea377a2b5be5fae44e94f2a73f5df344f Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 19 Apr 2023 08:34:03 +0300 Subject: [PATCH 135/212] fix: Fix bugs after pull. --- .../main/kotlin/dbSave/jsonFormat/JsonRepository.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt index c2ca1c1..41a5971 100644 --- a/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt +++ b/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt @@ -4,15 +4,14 @@ import com.google.common.reflect.TypeToken import com.google.gson.GsonBuilder import java.io.File -class JsonRepository>(private val dirPath: String) { +class JsonRepository(private val dirPath: String) { init { File(dirPath).mkdirs() } - fun saveChanges( + fun >saveChanges( preOrder: Array>, - typeToken: TypeToken>>, fileName: String ) { @@ -24,13 +23,13 @@ class JsonRepository>(private val dirPath: String) { writeText(json) } - val preOrd = gson.fromJson>>(json, typeToken.type) - } - fun exportTree(fileName: String) { + fun >exportTree(fileName: String, typeToken: TypeToken>>) { val gson = GsonBuilder().setPrettyPrinting().create() - //val json = gson.fromJson(File(dirPath, fileName).readText(), ArrayVertices::class.java) + val json = File(dirPath, fileName).readText() + + val preOrd = gson.fromJson>>(json, typeToken.type) } From a90a715602f245791f99ff363db10bf478032a59 Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 21 Apr 2023 18:10:52 +0300 Subject: [PATCH 136/212] feat: Implement saving data between program runs --- .gitignore | 1 + docker-compose.yml | 13 +++++++++++ .../kotlin/dbSave/neo4j/Neo4jRepository.kt | 1 - lib/src/main/kotlin/dbSave/neo4j/main.kt | 22 +++++++++++++++++++ .../kotlin/treelib/singleObjects/Container.kt | 1 - 5 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 docker-compose.yml create mode 100644 lib/src/main/kotlin/dbSave/neo4j/main.kt diff --git a/.gitignore b/.gitignore index 16cba14..c03a298 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /lib/build/ /lib/TEST_TEST/ /gradle/ +/neo4jDB/ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6833a23 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.9' + +services: + neo4j: + image: neo4j:latest + volumes: + - ./neo4jDB:/data + container_name: init_neo4j + ports: + - "7474:7474" + - "7687:7687" + environment: + - NEO4J_AUTH=neo4j/password \ 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 index f04cf8c..4694f47 100644 --- a/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt @@ -40,7 +40,6 @@ class Neo4jRepository : Closeable { while (preOrderIndex in preOrder.indices) { do { val currentNode = preOrder[preOrderIndex] - //currentNode.value as Container<*, *> if (preOrderIndex == 0) { session.executeWrite { tx -> cleanDB(tx) diff --git a/lib/src/main/kotlin/dbSave/neo4j/main.kt b/lib/src/main/kotlin/dbSave/neo4j/main.kt new file mode 100644 index 0000000..e7a550b --- /dev/null +++ b/lib/src/main/kotlin/dbSave/neo4j/main.kt @@ -0,0 +1,22 @@ +package dbSave.neo4j + +import treelib.singleObjects.Container +import treelib.singleObjects.Markers + +fun main() { + val a1 = DrawRBVertex(Container(Pair(5, 1)), Markers.BLACK) + val a2 = DrawRBVertex(Container(Pair(4, 1)), Markers.BLACK) + val a3 = DrawRBVertex(Container(Pair(6, 1)), Markers.BLACK) + val a4 = DrawRBVertex(Container(Pair(7, 1)), Markers.BLACK) + val a5 = DrawRBVertex(Container(Pair(2, 1)), Markers.BLACK) + val inOrder = arrayOf(a5, a2, a1, a3, a4) + val preOrder = arrayOf(a1, a2, a5, a3, a4) + val p = Neo4jRepository() + p.open("bolt://localhost:7687", "neo4j", "password") + //p.saveChanges(preOrder, inOrder) + val a = p.exportRBtree() + val pre = a.first + for (i in pre.indices) + println(pre[i].value.key) + +} \ 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 910cd24..e794ab5 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/Container.kt +++ b/lib/src/main/kotlin/treelib/singleObjects/Container.kt @@ -2,7 +2,6 @@ package treelib.singleObjects import kotlinx.serialization.Serializable -@Serializable class Container, V>(val pair: Pair) : Comparable> { val key = pair.first From 4a02c0267ecd0b06b667d1f835bc5aed88c72eeb Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 21 Apr 2023 21:51:08 +0300 Subject: [PATCH 137/212] fix(RBStruct): Remove infinite loop in postorder, correct comparison in restoreStruct --- lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf | 3 --- lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh | 13 ------------- lib/src/main/kotlin/treelib/rbTree/RBStruct.kt | 5 ++--- 3 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf delete mode 100644 lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh diff --git a/lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf b/lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf deleted file mode 100644 index 9b9fb0a..0000000 --- a/lib/src/main/kotlin/dbSave/neo4j/CONTAINER.conf +++ /dev/null @@ -1,3 +0,0 @@ -CONTAINER_NAME=neo4j-db -PASSWORD="test-neo4j" -VOLUMEDIR="/volume" \ No newline at end of file diff --git a/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh b/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh deleted file mode 100644 index 6b07269..0000000 --- a/lib/src/main/kotlin/dbSave/neo4j/testNeo4j.sh +++ /dev/null @@ -1,13 +0,0 @@ - -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/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index a3cbe34..dca6eaa 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -98,14 +98,13 @@ class RBStruct> : stack.pop().right = currentNode } else { stack.peek().left = currentNode - // связь с ролитилем } } stack.push(currentNode) - } while (preOrder[preOrderIndex++] != inOrder[inOrderIndex] && preOrderIndex < preOrder.size) + } while (preOrder[preOrderIndex++].value != inOrder[inOrderIndex].value && preOrderIndex < preOrder.size) currentNode = null - while (stack.isEmpty() && inOrderIndex < inOrder.size && + while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value ) { currentNode = stack.pop() From d29defd6b4ff083f341b1309639d798127c05394 Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 21 Apr 2023 23:20:18 +0300 Subject: [PATCH 138/212] feat(neo4j): Implement saving many trees --- docker-compose.yml | 2 +- .../main/kotlin/controller/RBTreeManager.kt | 29 ++++--- .../kotlin/dbSave/neo4j/Neo4jRepository.kt | 77 +++++++++++++------ lib/src/main/kotlin/dbSave/neo4j/main.kt | 22 ------ .../kotlin/treelib/abstractTree/TreeStruct.kt | 3 + 5 files changed, 75 insertions(+), 58 deletions(-) delete mode 100644 lib/src/main/kotlin/dbSave/neo4j/main.kt diff --git a/docker-compose.yml b/docker-compose.yml index 6833a23..5fce390 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: image: neo4j:latest volumes: - ./neo4jDB:/data - container_name: init_neo4j + container_name: neo4j-db ports: - "7474:7474" - "7687:7687" diff --git a/lib/src/main/kotlin/controller/RBTreeManager.kt b/lib/src/main/kotlin/controller/RBTreeManager.kt index 0d3456e..061dc79 100644 --- a/lib/src/main/kotlin/controller/RBTreeManager.kt +++ b/lib/src/main/kotlin/controller/RBTreeManager.kt @@ -9,13 +9,16 @@ class RBTreeManager { /*** using neo4j ***/ - fun initTree(treeName: String): RBStruct>> { - val neo4jDB = Neo4jRepository() - neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") + private val neo4jDB = Neo4jRepository() + + init { + neo4jDB.open("bolt://localhost:7687", "neo4j", "password") + } + fun initTree(treeName: String): RBStruct>> { /*** orders.first = preOrder, orders.second = inOrder ***/ val orders: Pair>>>, List>>>> = - neo4jDB.exportRBtree() + neo4jDB.exportRBtree(treeName) val RBtree = RBStruct>>() RBtree.restoreStruct(orders.first, orders.second) @@ -24,17 +27,23 @@ class RBTreeManager { return RBtree } - fun > saveTree(tree: RBStruct) { - val neo4jDB = Neo4jRepository() - neo4jDB.open("bolt://localhost:7687", "neo4j", "test-neo4j") - - // вот тут плохо, потому что тут надо получать не base nodes, а для рисовалки + fun > saveTree(tree: RBStruct, treeName: String) { 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.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray(), treeName) neo4jDB.close() } + fun deleteTree(treeName: String) { + + neo4jDB.removeTree(treeName) + + } + + fun cleanDB() { + neo4jDB.clean() + } + } \ 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 index 4694f47..ceb3b95 100644 --- a/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt @@ -26,9 +26,12 @@ class Neo4jRepository : Closeable { } } - fun > saveChanges(preOrder: Array>, inOrder: Array>) { + fun > saveChanges( + preOrder: Array>, + inOrder: Array>, + treeName: String + ) { - /*** сюда по ощущениям лучше всего добавлять именно то поддерево исходного дерева, которое было изменено ***/ val session = driver?.session() ?: throw IOException() var inOrderIndex = 0 @@ -42,8 +45,8 @@ class Neo4jRepository : Closeable { val currentNode = preOrder[preOrderIndex] if (preOrderIndex == 0) { session.executeWrite { tx -> - cleanDB(tx) - createRoot(tx, currentNode, id) + //cleanDB(tx) + createRoot(tx, currentNode, id, treeName) } ++id } @@ -51,16 +54,15 @@ class Neo4jRepository : Closeable { 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) + createRightSon(tx, parentNode, currentNode, id, treeName) } ++id } else { val parentNode = stack.peek() parentNode.value as Container<*, *> session.executeWrite { tx -> - createLeftSon(tx, parentNode, currentNode, id) + createLeftSon(tx, parentNode, currentNode, id, treeName) } ++id } @@ -85,9 +87,7 @@ class Neo4jRepository : Closeable { session.close() } - fun exportRBtree(): Pair>>>, List>>>> { - - /*** Плохо, что передаем Container с четко привязанными типами (K, V), потому что может быть так, что вместо контейнера будет просто инт ***/ + fun exportRBtree(treeName: String): Pair>>>, List>>>> { val session = driver?.session() ?: throw IOException() var preOrder: List>>> = listOf() @@ -95,9 +95,10 @@ class Neo4jRepository : Closeable { session.executeRead { tx -> preOrder = tx.run( - "MATCH (node: Node) " + + "MATCH (node: Node {treeName: \$name}) " + "RETURN node.value, node.key, node.color, node.x, node.y " + - "ORDER BY node.id" + "ORDER BY node.id", + mutableMapOf("name" to treeName) as Map? ).list() .map { DrawRBVertex( @@ -109,9 +110,10 @@ class Neo4jRepository : Closeable { } inOrder = tx.run( - "MATCH (node: Node) " + + "MATCH (node: Node {treeName: \$name}) " + "RETURN node.value, node.key, node.color, node.x, node.y " + - "ORDER BY node.key" + "ORDER BY node.key", + mutableMapOf("name" to treeName) as Map? ).list() .map { DrawRBVertex( @@ -129,35 +131,58 @@ class Neo4jRepository : Closeable { } - private fun cleanDB(tx: TransactionContext) { - tx.run("MATCH (n: Node) DETACH DELETE n") + fun removeTree(treeName: String) { + + val session = driver?.session() ?: throw IOException() + + session.executeWrite { tx -> + tx.run( + "MATCH (n: Node {treeName: \$name}) DETACH DELETE n", + mutableMapOf("name" to treeName) as Map? + ) + } + } - private fun > createRoot(tx: TransactionContext, rootNode: DrawRBVertex, id: Int) { + fun clean() { + val session = driver?.session() ?: throw IOException() + + session.executeWrite { tx -> + tx.run("MATCH (n: Node) DETACH DELETE n") + } + } + + private fun > createRoot( + tx: TransactionContext, + rootNode: DrawRBVertex, + id: Int, + treeName: String + ) { rootNode.value as Container<*, *> tx.run( - "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY, id: \$nodeID})", + "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY, id: \$nodeID, treeName: \$name})", 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 + "nodeID" to id, + "name" to treeName ) ) } private fun > createRightSon( tx: TransactionContext, parentNode: DrawRBVertex, - currentNode: DrawRBVertex, id: Int + currentNode: DrawRBVertex, id: Int, treeName: String ) { 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}) " + + "color: \$parentNodeColor, x: \$parentNodeX, y: \$parentNodeY, treeName: \$name}) " + + "MERGE(son:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY, id: \$nodeID, treeName: \$name}) " + "MERGE (parent)-[:RIGHT_SON]->(son)", mutableMapOf( "parentNodeValue" to parentNode.value.pair.second, @@ -171,20 +196,21 @@ class Neo4jRepository : Closeable { "nodeX" to currentNode.x, "nodeY" to currentNode.y, "nodeID" to id, + "name" to treeName ) ) } private fun > createLeftSon( tx: TransactionContext, parentNode: DrawRBVertex, - currentNode: DrawRBVertex, id: Int + currentNode: DrawRBVertex, id: Int, treeName: String ) { 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}) " + + "color: \$parentNodeColor, x: \$parentNodeX, y: \$parentNodeY, treeName: \$name}) " + + "MERGE(son:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY, id: \$nodeID, treeName: \$name}) " + "MERGE (parent)-[:LEFT_SON]->(son)", mutableMapOf( "parentNodeValue" to parentNode.value.pair.second, @@ -198,6 +224,7 @@ class Neo4jRepository : Closeable { "nodeX" to currentNode.x, "nodeY" to currentNode.y, "nodeID" to id, + "name" to treeName ) ) } diff --git a/lib/src/main/kotlin/dbSave/neo4j/main.kt b/lib/src/main/kotlin/dbSave/neo4j/main.kt deleted file mode 100644 index e7a550b..0000000 --- a/lib/src/main/kotlin/dbSave/neo4j/main.kt +++ /dev/null @@ -1,22 +0,0 @@ -package dbSave.neo4j - -import treelib.singleObjects.Container -import treelib.singleObjects.Markers - -fun main() { - val a1 = DrawRBVertex(Container(Pair(5, 1)), Markers.BLACK) - val a2 = DrawRBVertex(Container(Pair(4, 1)), Markers.BLACK) - val a3 = DrawRBVertex(Container(Pair(6, 1)), Markers.BLACK) - val a4 = DrawRBVertex(Container(Pair(7, 1)), Markers.BLACK) - val a5 = DrawRBVertex(Container(Pair(2, 1)), Markers.BLACK) - val inOrder = arrayOf(a5, a2, a1, a3, a4) - val preOrder = arrayOf(a1, a2, a5, a3, a4) - val p = Neo4jRepository() - p.open("bolt://localhost:7687", "neo4j", "password") - //p.saveChanges(preOrder, inOrder) - val a = p.exportRBtree() - val pre = a.first - for (i in pre.indices) - println(pre[i].value.key) - -} \ No newline at end of file diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index fa403bc..f3a14da 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -328,6 +328,9 @@ abstract class TreeStruct< if (parent.right == it) { flagVisited = 2 } + else { + flagVisited = 1 + } current = parent } } ?: throw MultithreadingException(ImpossibleCaseException()) From a20427e71a996f53e51a71fd3927008501423fbf Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 21 Apr 2023 23:29:31 +0300 Subject: [PATCH 139/212] refactor: Rename some classes and packages --- .../AVLTreeManager.kt} | 8 ++--- .../BINTreeManager.kt | 4 +-- .../RBTreeManager.kt | 12 +++---- .../TreeManager.kt | 2 +- .../kotlin/databaseSave/DrawableVertex.kt | 7 ++++ .../jsonFormat/DrawableBINVertex.kt} | 4 +-- .../jsonFormat/JsonRepository.kt | 8 ++--- .../neo4j/DrawableRBVertex.kt} | 4 +-- .../neo4j/Neo4jRepository.kt | 32 +++++++++---------- .../sqlite/DrawAVLVertex.kt | 6 ++-- .../sqlite/SQLiteRepository.kt | 2 +- lib/src/main/kotlin/dbSave/DrawVertex.kt | 7 ---- 12 files changed, 48 insertions(+), 48 deletions(-) rename lib/src/main/kotlin/{controller/SQLiteController.kt => databaseManage/AVLTreeManager.kt} (91%) rename lib/src/main/kotlin/{controller => databaseManage}/BINTreeManager.kt (83%) rename lib/src/main/kotlin/{controller => databaseManage}/RBTreeManager.kt (69%) rename lib/src/main/kotlin/{controller => databaseManage}/TreeManager.kt (57%) create mode 100644 lib/src/main/kotlin/databaseSave/DrawableVertex.kt rename lib/src/main/kotlin/{dbSave/jsonFormat/DrawBINVertex.kt => databaseSave/jsonFormat/DrawableBINVertex.kt} (60%) rename lib/src/main/kotlin/{dbSave => databaseSave}/jsonFormat/JsonRepository.kt (75%) rename lib/src/main/kotlin/{dbSave/neo4j/DrawRBVertex.kt => databaseSave/neo4j/DrawableRBVertex.kt} (71%) rename lib/src/main/kotlin/{dbSave => databaseSave}/neo4j/Neo4jRepository.kt (88%) rename lib/src/main/kotlin/{dbSave => databaseSave}/sqlite/DrawAVLVertex.kt (63%) rename lib/src/main/kotlin/{dbSave => databaseSave}/sqlite/SQLiteRepository.kt (99%) delete mode 100644 lib/src/main/kotlin/dbSave/DrawVertex.kt diff --git a/lib/src/main/kotlin/controller/SQLiteController.kt b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt similarity index 91% rename from lib/src/main/kotlin/controller/SQLiteController.kt rename to lib/src/main/kotlin/databaseManage/AVLTreeManager.kt index fa39c9d..3b1c7d9 100644 --- a/lib/src/main/kotlin/controller/SQLiteController.kt +++ b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt @@ -1,12 +1,12 @@ -package controller +package databaseManage -import dbSave.sqlite.DrawAVLVertex -import dbSave.sqlite.SQLiteRepository +import databaseSave.sqlite.DrawAVLVertex +import databaseSave.sqlite.SQLiteRepository import treelib.avlTree.AVLStruct import treelib.avlTree.AVLVertex import java.io.Closeable -class SQLiteController>( +class AVLTreeManager>( private var treeName: String, private val dbPath: String, private val serializeData: (input: Pack) -> String, diff --git a/lib/src/main/kotlin/controller/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt similarity index 83% rename from lib/src/main/kotlin/controller/BINTreeManager.kt rename to lib/src/main/kotlin/databaseManage/BINTreeManager.kt index 7232c6e..0c6de63 100644 --- a/lib/src/main/kotlin/controller/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -1,6 +1,6 @@ -package controller +package databaseManage -import dbSave.jsonFormat.JsonRepository +import databaseSave.jsonFormat.JsonRepository import treelib.binTree.BINStruct class BINTreeManager: TreeManager() { diff --git a/lib/src/main/kotlin/controller/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt similarity index 69% rename from lib/src/main/kotlin/controller/RBTreeManager.kt rename to lib/src/main/kotlin/databaseManage/RBTreeManager.kt index 061dc79..1aff74e 100644 --- a/lib/src/main/kotlin/controller/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -1,7 +1,7 @@ -package controller +package databaseManage -import dbSave.neo4j.DrawRBVertex -import dbSave.neo4j.Neo4jRepository +import databaseSave.neo4j.DrawableRBVertex +import databaseSave.neo4j.Neo4jRepository import treelib.rbTree.RBStruct import treelib.singleObjects.Container @@ -17,7 +17,7 @@ class RBTreeManager { fun initTree(treeName: String): RBStruct>> { /*** orders.first = preOrder, orders.second = inOrder ***/ - val orders: Pair>>>, List>>>> = + val orders: Pair>>>, List>>>> = neo4jDB.exportRBtree(treeName) val RBtree = RBStruct>>() @@ -29,8 +29,8 @@ class RBTreeManager { fun > saveTree(tree: RBStruct, treeName: String) { - val preOrder = tree.preOrder().map { DrawRBVertex(it.value, it.color) } - val inOrder = tree.inOrder().map { DrawRBVertex(it.value, it.color) } + val preOrder = tree.preOrder().map { DrawableRBVertex(it.value, it.color) } + val inOrder = tree.inOrder().map { DrawableRBVertex(it.value, it.color) } neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray(), treeName) neo4jDB.close() diff --git a/lib/src/main/kotlin/controller/TreeManager.kt b/lib/src/main/kotlin/databaseManage/TreeManager.kt similarity index 57% rename from lib/src/main/kotlin/controller/TreeManager.kt rename to lib/src/main/kotlin/databaseManage/TreeManager.kt index cc751e0..819d058 100644 --- a/lib/src/main/kotlin/controller/TreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/TreeManager.kt @@ -1,4 +1,4 @@ -package controller +package databaseManage abstract class TreeManager { } \ No newline at end of file diff --git a/lib/src/main/kotlin/databaseSave/DrawableVertex.kt b/lib/src/main/kotlin/databaseSave/DrawableVertex.kt new file mode 100644 index 0000000..ee98220 --- /dev/null +++ b/lib/src/main/kotlin/databaseSave/DrawableVertex.kt @@ -0,0 +1,7 @@ +package databaseSave + +interface DrawableVertex> { + 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/databaseSave/jsonFormat/DrawableBINVertex.kt similarity index 60% rename from lib/src/main/kotlin/dbSave/jsonFormat/DrawBINVertex.kt rename to lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt index 783a1de..339d29d 100644 --- a/lib/src/main/kotlin/dbSave/jsonFormat/DrawBINVertex.kt +++ b/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt @@ -1,8 +1,8 @@ -package dbSave.jsonFormat +package databaseSave.jsonFormat import treelib.binTree.BINVertex -class DrawBINVertex>( +class DrawableBINVertex>( value: Pack, val x: Double = 0.0, val y: Double = 0.0 diff --git a/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt similarity index 75% rename from lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt rename to lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt index 41a5971..45f3f26 100644 --- a/lib/src/main/kotlin/dbSave/jsonFormat/JsonRepository.kt +++ b/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt @@ -1,4 +1,4 @@ -package dbSave.jsonFormat +package databaseSave.jsonFormat import com.google.common.reflect.TypeToken import com.google.gson.GsonBuilder @@ -11,7 +11,7 @@ class JsonRepository(private val dirPath: String) { } fun >saveChanges( - preOrder: Array>, + preOrder: Array>, fileName: String ) { @@ -25,11 +25,11 @@ class JsonRepository(private val dirPath: String) { } - fun >exportTree(fileName: String, typeToken: TypeToken>>) { + fun >exportTree(fileName: String, typeToken: TypeToken>>) { val gson = GsonBuilder().setPrettyPrinting().create() val json = File(dirPath, fileName).readText() - val preOrd = gson.fromJson>>(json, typeToken.type) + val preOrd = gson.fromJson>>(json, typeToken.type) } diff --git a/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt b/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt similarity index 71% rename from lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt rename to lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt index 37100de..7f1c5b1 100644 --- a/lib/src/main/kotlin/dbSave/neo4j/DrawRBVertex.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt @@ -1,9 +1,9 @@ -package dbSave.neo4j +package databaseSave.neo4j import treelib.rbTree.RBVertex import treelib.singleObjects.Markers -class DrawRBVertex>( +class DrawableRBVertex>( value: Pack, color: Markers, val x: Double = 0.0, diff --git a/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt similarity index 88% rename from lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt rename to lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index ceb3b95..cc30f0c 100644 --- a/lib/src/main/kotlin/dbSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -1,4 +1,4 @@ -package dbSave.neo4j +package databaseSave.neo4j import org.neo4j.driver.AuthTokens @@ -27,8 +27,8 @@ class Neo4jRepository : Closeable { } fun > saveChanges( - preOrder: Array>, - inOrder: Array>, + preOrder: Array>, + inOrder: Array>, treeName: String ) { @@ -36,8 +36,8 @@ class Neo4jRepository : Closeable { var inOrderIndex = 0 var preOrderIndex = 0 - val set = HashSet>() - val stack = LinkedList>() + val set = HashSet>() + val stack = LinkedList>() var id = 0 while (preOrderIndex in preOrder.indices) { @@ -70,7 +70,7 @@ class Neo4jRepository : Closeable { stack.push(currentNode) } while (preOrder[preOrderIndex++].value != inOrder[inOrderIndex].value && preOrderIndex < preOrder.size) - var currentNode: DrawRBVertex? = null + var currentNode: DrawableRBVertex? = null while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) { currentNode = stack.pop() @@ -87,11 +87,11 @@ class Neo4jRepository : Closeable { session.close() } - fun exportRBtree(treeName: String): Pair>>>, List>>>> { + fun exportRBtree(treeName: String): Pair>>>, List>>>> { val session = driver?.session() ?: throw IOException() - var preOrder: List>>> = listOf() - var inOrder: List>>> = listOf() + var preOrder: List>>> = listOf() + var inOrder: List>>> = listOf() session.executeRead { tx -> preOrder = tx.run( @@ -101,7 +101,7 @@ class Neo4jRepository : Closeable { mutableMapOf("name" to treeName) as Map? ).list() .map { - DrawRBVertex( + DrawableRBVertex( 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(), @@ -116,7 +116,7 @@ class Neo4jRepository : Closeable { mutableMapOf("name" to treeName) as Map? ).list() .map { - DrawRBVertex( + DrawableRBVertex( 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(), @@ -154,7 +154,7 @@ class Neo4jRepository : Closeable { private fun > createRoot( tx: TransactionContext, - rootNode: DrawRBVertex, + rootNode: DrawableRBVertex, id: Int, treeName: String ) { @@ -174,8 +174,8 @@ class Neo4jRepository : Closeable { } private fun > createRightSon( - tx: TransactionContext, parentNode: DrawRBVertex, - currentNode: DrawRBVertex, id: Int, treeName: String + tx: TransactionContext, parentNode: DrawableRBVertex, + currentNode: DrawableRBVertex, id: Int, treeName: String ) { parentNode.value as Container<*, *> currentNode.value as Container<*, *> @@ -202,8 +202,8 @@ class Neo4jRepository : Closeable { } private fun > createLeftSon( - tx: TransactionContext, parentNode: DrawRBVertex, - currentNode: DrawRBVertex, id: Int, treeName: String + tx: TransactionContext, parentNode: DrawableRBVertex, + currentNode: DrawableRBVertex, id: Int, treeName: String ) { parentNode.value as Container<*, *> currentNode.value as Container<*, *> diff --git a/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt b/lib/src/main/kotlin/databaseSave/sqlite/DrawAVLVertex.kt similarity index 63% rename from lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt rename to lib/src/main/kotlin/databaseSave/sqlite/DrawAVLVertex.kt index e6feb46..e0ce9fa 100644 --- a/lib/src/main/kotlin/dbSave/sqlite/DrawAVLVertex.kt +++ b/lib/src/main/kotlin/databaseSave/sqlite/DrawAVLVertex.kt @@ -1,10 +1,10 @@ -package dbSave.sqlite +package databaseSave.sqlite -import dbSave.DrawVertex +import databaseSave.DrawableVertex class DrawAVLVertex>( override val value: Pack, override val x: Double, override val y: Double, val height: Int, -) : DrawVertex +) : DrawableVertex diff --git a/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepository.kt similarity index 99% rename from lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt rename to lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepository.kt index 1d4cf20..7882502 100644 --- a/lib/src/main/kotlin/dbSave/sqlite/SQLiteRepository.kt +++ b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepository.kt @@ -1,4 +1,4 @@ -package dbSave.sqlite +package databaseSave.sqlite import java.io.Closeable import java.sql.DriverManager diff --git a/lib/src/main/kotlin/dbSave/DrawVertex.kt b/lib/src/main/kotlin/dbSave/DrawVertex.kt deleted file mode 100644 index 4108556..0000000 --- a/lib/src/main/kotlin/dbSave/DrawVertex.kt +++ /dev/null @@ -1,7 +0,0 @@ -package dbSave - -interface DrawVertex> { - val value: Pack - val x: Double - val y: Double -} From 57cbfa96894c72396e273f90781f88cc77a3f9a7 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 22 Apr 2023 00:03:11 +0300 Subject: [PATCH 140/212] feat: Implement checking code coverage in CI --- .github/workflows/CI.yml | 4 ++++ lib/build.gradle.kts | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 243bd23..094f7ac 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -32,3 +32,7 @@ jobs: with: generate-branches-badge: true jacoco-csv-file: lib/build/jacoco/report.csv + + - if: github.event_name == "pull_request" + name: Jacoco Test Coverage Verification + run: ./gradlew jacocoTestCoverageVerification diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 080c014..ce1b92a 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -91,7 +91,8 @@ tasks.jacocoTestReport { tasks.jacocoTestCoverageVerification { classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { - include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class") + include("**/treelib/**") + exclude("**/singleObjects/**") } }) dependsOn(tasks.jacocoTestReport) violationRules { From 8929af3cdfa46fd99cd787d229c56cfc9ac6f880 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 22 Apr 2023 00:09:44 +0300 Subject: [PATCH 141/212] feat: Implement checking code coverage in CI --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 094f7ac..cbb1d05 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -33,6 +33,6 @@ jobs: generate-branches-badge: true jacoco-csv-file: lib/build/jacoco/report.csv - - if: github.event_name == "pull_request" + - if: ${{ github.event_name == 'pull_request' }} name: Jacoco Test Coverage Verification run: ./gradlew jacocoTestCoverageVerification From efef7d9b3c575371fe578c6459ec3562677d53d2 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 22 Apr 2023 13:51:10 +0300 Subject: [PATCH 142/212] fix: Deleting node with one child --- lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index c8c4cf6..62cd5cd 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -121,7 +121,14 @@ class RBBalancer>(private var root: RBNode?) : firstCase(node, null) } - + node.left?.color == Markers.BLACK && node.right?.color == Markers.RED && nodeIsLeaf(node.right) -> { + node.right?.color = Markers.BLACK + getRoot(node) + } + node.right?.color == Markers.BLACK && node.left?.color == Markers.RED && nodeIsLeaf(node.left) -> { + node.left?.color = Markers.BLACK + getRoot(node) + } else -> throw IllegalStateException() } } From 638a1c7aa4e6185570284f6bfed56d235335f210 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 22 Apr 2023 13:18:47 +0300 Subject: [PATCH 143/212] fix: Problems with Serializable connection; Refactor commonObjects package and Tree.putItem method; fix #21 - issue fix: Problems with Serializable connection; Refactor commonObjects package and Tree.putItem method; fix #21 - issue --- lib/build.gradle.kts | 1 + .../kotlin/databaseManage/RBTreeManager.kt | 4 +- .../databaseSave/neo4j/DrawableRBVertex.kt | 4 +- .../databaseSave/neo4j/Neo4jRepository.kt | 4 +- .../main/kotlin/treelib/abstractTree/Tree.kt | 12 ++- .../kotlin/treelib/abstractTree/TreeStruct.kt | 10 +- .../abstractTree/balanced/BalancerNoParent.kt | 2 +- .../abstractTree/balanced/BalancerParent.kt | 2 +- .../main/kotlin/treelib/avlTree/AVLStruct.kt | 2 +- .../main/kotlin/treelib/avlTree/AVLTree.kt | 2 +- .../main/kotlin/treelib/binTree/BINStruct.kt | 3 +- .../main/kotlin/treelib/binTree/BINTree.kt | 2 +- .../Container.kt | 5 +- .../exceptions/BugInImplementException.kt | 2 +- .../exceptions/IllegalBaseNodeException.kt | 2 +- .../exceptions/IllegalNodeStateException.kt | 2 +- .../exceptions/ImpossibleCaseException.kt | 2 +- .../exceptions/IncorrectUsage.kt | 4 +- .../exceptions/MultithreadingException.kt | 2 +- .../exceptions/NonExistentValueException.kt | 2 +- .../{singleObjects => rbTree}/Markers.kt | 2 +- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 5 +- lib/src/main/kotlin/treelib/rbTree/RBNode.kt | 1 - .../main/kotlin/treelib/rbTree/RBStruct.kt | 5 +- lib/src/main/kotlin/treelib/rbTree/RBTree.kt | 2 +- .../main/kotlin/treelib/rbTree/RBVertex.kt | 3 +- lib/src/test/kotlin/treelib/AVLStructTest.kt | 2 +- lib/src/test/kotlin/treelib/AVLTreeTest.kt | 8 +- lib/src/test/kotlin/treelib/BINStructTest.kt | 2 +- lib/src/test/kotlin/treelib/BINTreeTest.kt | 12 +-- lib/src/test/kotlin/treelib/RBBalancerTest.kt | 5 +- lib/src/test/kotlin/treelib/RBStructTest.kt | 101 +++++++++--------- lib/src/test/kotlin/treelib/RBTreeTest.kt | 10 +- lib/src/test/kotlin/treelib/TestModelRBT.kt | 4 +- lib/src/test/kotlin/utils/BINAnalyzer.kt | 4 +- lib/src/test/kotlin/utils/RBAnalyzer.kt | 6 +- lib/src/test/kotlin/utils/TreeWrapper.kt | 4 +- .../kotlin/utils/fuzzers/TreeStructFuzzer.kt | 3 +- 38 files changed, 122 insertions(+), 126 deletions(-) rename lib/src/main/kotlin/treelib/{singleObjects => commonObjects}/Container.kt (77%) rename lib/src/main/kotlin/treelib/{singleObjects => commonObjects}/exceptions/BugInImplementException.kt (88%) rename lib/src/main/kotlin/treelib/{singleObjects => commonObjects}/exceptions/IllegalBaseNodeException.kt (91%) rename lib/src/main/kotlin/treelib/{singleObjects => commonObjects}/exceptions/IllegalNodeStateException.kt (92%) rename lib/src/main/kotlin/treelib/{singleObjects => commonObjects}/exceptions/ImpossibleCaseException.kt (92%) rename lib/src/main/kotlin/treelib/{singleObjects => commonObjects}/exceptions/IncorrectUsage.kt (88%) rename lib/src/main/kotlin/treelib/{singleObjects => commonObjects}/exceptions/MultithreadingException.kt (91%) rename lib/src/main/kotlin/treelib/{singleObjects => commonObjects}/exceptions/NonExistentValueException.kt (91%) rename lib/src/main/kotlin/treelib/{singleObjects => rbTree}/Markers.kt (56%) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index ce1b92a..d95a5a2 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -5,6 +5,7 @@ plugins { jacoco `java-library` `maven-publish` + kotlin("plugin.serialization") version "1.5.0" // checkstyle } diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt index 1aff74e..f95bf9b 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -3,7 +3,7 @@ package databaseManage import databaseSave.neo4j.DrawableRBVertex import databaseSave.neo4j.Neo4jRepository import treelib.rbTree.RBStruct -import treelib.singleObjects.Container +import treelib.commonObjects.Container class RBTreeManager { @@ -46,4 +46,4 @@ class RBTreeManager { neo4jDB.clean() } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt b/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt index 7f1c5b1..904c8a8 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt @@ -1,11 +1,11 @@ package databaseSave.neo4j import treelib.rbTree.RBVertex -import treelib.singleObjects.Markers +import treelib.rbTree.Markers class DrawableRBVertex>( value: Pack, color: Markers, val x: Double = 0.0, val y: Double = 0.0 -) : RBVertex(value, color) \ No newline at end of file +) : RBVertex(value, color) diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index cc30f0c..68cb834 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -6,8 +6,8 @@ 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 treelib.commonObjects.Container +import treelib.rbTree.Markers import java.io.Closeable import java.io.IOException import java.util.* diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index fb0976a..f061ba5 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -1,7 +1,7 @@ package treelib.abstractTree -import treelib.singleObjects.Container -import treelib.singleObjects.exceptions.NonExistentValueException +import treelib.commonObjects.Container +import treelib.commonObjects.exceptions.NonExistentValueException abstract class Tree< K : Comparable, @@ -19,18 +19,20 @@ abstract class Tree< treeStruct.insert(Container(item)) } - fun putItems(vararg items: Pair) { + fun putItem(vararg items: Pair) { for (element in items) putItem(element) } - fun putItems(items: Iterable>) { + fun putItem(items: Iterable>) { for (element in items) putItem(element) } fun getItem(key: K): V? = treeStruct.find(wrapForFind(key))?.value + operator fun get(key: K): V? = treeStruct.find(wrapForFind(key))?.value + fun deleteItem(key: K) { - if (getItem(key) == null) throw NonExistentValueException() + if (treeStruct.find(wrapForFind(key)) == null) throw NonExistentValueException() treeStruct.delete(wrapForFind(key)) } diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index f3a14da..95d8907 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -1,9 +1,9 @@ package treelib.abstractTree -import treelib.singleObjects.exceptions.BugInImplementException -import treelib.singleObjects.exceptions.ImpossibleCaseException -import treelib.singleObjects.exceptions.MultithreadingException -import treelib.singleObjects.exceptions.NonExistentValueException +import treelib.commonObjects.exceptions.BugInImplementException +import treelib.commonObjects.exceptions.ImpossibleCaseException +import treelib.commonObjects.exceptions.MultithreadingException +import treelib.commonObjects.exceptions.NonExistentValueException abstract class TreeStruct< @@ -361,6 +361,4 @@ abstract class TreeStruct< } return arrayNodes.map { toVertex(it) } } - - } diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt index 31cfb37..e70ba48 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt @@ -2,7 +2,7 @@ package treelib.abstractTree.balanced import treelib.abstractTree.Node import treelib.abstractTree.StateContainer -import treelib.singleObjects.exceptions.IllegalNodeStateException +import treelib.commonObjects.exceptions.IllegalNodeStateException abstract class BalancerNoParent, NodeType : Node, StateContainerType : StateContainer> : Balancer { diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt index 6cf9537..9dcca42 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt @@ -2,7 +2,7 @@ package treelib.abstractTree.balanced import treelib.abstractTree.NodeParent import treelib.abstractTree.StateContainer -import treelib.singleObjects.exceptions.IllegalNodeStateException +import treelib.commonObjects.exceptions.IllegalNodeStateException abstract class BalancerParent, NodeType : NodeParent, StateContainerType : StateContainer> : Balancer { diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt index 6d89668..d977ba0 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt @@ -1,7 +1,7 @@ package treelib.avlTree import treelib.abstractTree.balanced.BalancedTreeStruct -import treelib.singleObjects.exceptions.IncorrectUsage +import treelib.commonObjects.exceptions.IncorrectUsage class AVLStruct> : BalancedTreeStruct, AVLStateContainer, AVLVertex, AVLBalancer>() { diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt index 5df840f..dda8b48 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt @@ -2,7 +2,7 @@ package treelib.avlTree import treelib.abstractTree.Tree -import treelib.singleObjects.Container +import treelib.commonObjects.Container class AVLTree, V> : Tree>, AVLStateContainer>, AVLVertex>>() { diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt index c033266..b66ca85 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt @@ -1,8 +1,7 @@ package treelib.binTree import treelib.abstractTree.TreeStruct -import treelib.abstractTree.Vertex -import treelib.singleObjects.exceptions.IncorrectUsage +import treelib.commonObjects.exceptions.IncorrectUsage class BINStruct> : TreeStruct, BINStateContainer, BINVertex>() { diff --git a/lib/src/main/kotlin/treelib/binTree/BINTree.kt b/lib/src/main/kotlin/treelib/binTree/BINTree.kt index f0a92d9..a38adc9 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINTree.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINTree.kt @@ -1,7 +1,7 @@ package treelib.binTree import treelib.abstractTree.Tree -import treelib.singleObjects.Container +import treelib.commonObjects.Container class BINTree, V> : Tree>, BINStateContainer>, BINVertex>>() { diff --git a/lib/src/main/kotlin/treelib/singleObjects/Container.kt b/lib/src/main/kotlin/treelib/commonObjects/Container.kt similarity index 77% rename from lib/src/main/kotlin/treelib/singleObjects/Container.kt rename to lib/src/main/kotlin/treelib/commonObjects/Container.kt index e794ab5..8b7bc35 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/Container.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/Container.kt @@ -1,8 +1,9 @@ -package treelib.singleObjects +package treelib.commonObjects import kotlinx.serialization.Serializable -class Container, V>(val pair: Pair) : Comparable> { +@Serializable +data class Container, V>(val pair: Pair) : Comparable> { val key = pair.first val value = pair.second diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/BugInImplementException.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/BugInImplementException.kt similarity index 88% rename from lib/src/main/kotlin/treelib/singleObjects/exceptions/BugInImplementException.kt rename to lib/src/main/kotlin/treelib/commonObjects/exceptions/BugInImplementException.kt index 69b9875..3748b96 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/exceptions/BugInImplementException.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/BugInImplementException.kt @@ -1,4 +1,4 @@ -package treelib.singleObjects.exceptions +package treelib.commonObjects.exceptions class BugInImplementException : Exception { constructor() : super() diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/IllegalBaseNodeException.kt similarity index 91% rename from lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt rename to lib/src/main/kotlin/treelib/commonObjects/exceptions/IllegalBaseNodeException.kt index 3303817..28d7106 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalBaseNodeException.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/IllegalBaseNodeException.kt @@ -1,4 +1,4 @@ -package treelib.singleObjects.exceptions +package treelib.commonObjects.exceptions class IllegalBaseNodeException : Exception { constructor() : super( diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalNodeStateException.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/IllegalNodeStateException.kt similarity index 92% rename from lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalNodeStateException.kt rename to lib/src/main/kotlin/treelib/commonObjects/exceptions/IllegalNodeStateException.kt index 26080b8..1ef615a 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IllegalNodeStateException.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/IllegalNodeStateException.kt @@ -1,4 +1,4 @@ -package treelib.singleObjects.exceptions +package treelib.commonObjects.exceptions class IllegalNodeStateException : Exception { constructor() : super( diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/ImpossibleCaseException.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/ImpossibleCaseException.kt similarity index 92% rename from lib/src/main/kotlin/treelib/singleObjects/exceptions/ImpossibleCaseException.kt rename to lib/src/main/kotlin/treelib/commonObjects/exceptions/ImpossibleCaseException.kt index 02556f3..568cf04 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/exceptions/ImpossibleCaseException.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/ImpossibleCaseException.kt @@ -1,4 +1,4 @@ -package treelib.singleObjects.exceptions +package treelib.commonObjects.exceptions open class ImpossibleCaseException : Exception { constructor() : super( diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/IncorrectUsage.kt similarity index 88% rename from lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt rename to lib/src/main/kotlin/treelib/commonObjects/exceptions/IncorrectUsage.kt index 3f16415..1bcd994 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/exceptions/IncorrectUsage.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/IncorrectUsage.kt @@ -1,4 +1,4 @@ -package treelib.singleObjects.exceptions +package treelib.commonObjects.exceptions class IncorrectUsage : Exception { constructor() : super( @@ -15,4 +15,4 @@ class IncorrectUsage : Exception { ) constructor(cause: Throwable) : super(cause) -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/MultithreadingException.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/MultithreadingException.kt similarity index 91% rename from lib/src/main/kotlin/treelib/singleObjects/exceptions/MultithreadingException.kt rename to lib/src/main/kotlin/treelib/commonObjects/exceptions/MultithreadingException.kt index a51fee4..61d62ea 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/exceptions/MultithreadingException.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/MultithreadingException.kt @@ -1,4 +1,4 @@ -package treelib.singleObjects.exceptions +package treelib.commonObjects.exceptions open class MultithreadingException : Exception { constructor() : super( diff --git a/lib/src/main/kotlin/treelib/singleObjects/exceptions/NonExistentValueException.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/NonExistentValueException.kt similarity index 91% rename from lib/src/main/kotlin/treelib/singleObjects/exceptions/NonExistentValueException.kt rename to lib/src/main/kotlin/treelib/commonObjects/exceptions/NonExistentValueException.kt index af6b632..95f8c6c 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/exceptions/NonExistentValueException.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/NonExistentValueException.kt @@ -1,4 +1,4 @@ -package treelib.singleObjects.exceptions +package treelib.commonObjects.exceptions open class NonExistentValueException : Exception { constructor() : super( diff --git a/lib/src/main/kotlin/treelib/singleObjects/Markers.kt b/lib/src/main/kotlin/treelib/rbTree/Markers.kt similarity index 56% rename from lib/src/main/kotlin/treelib/singleObjects/Markers.kt rename to lib/src/main/kotlin/treelib/rbTree/Markers.kt index 6ac4afd..7ae37fc 100644 --- a/lib/src/main/kotlin/treelib/singleObjects/Markers.kt +++ b/lib/src/main/kotlin/treelib/rbTree/Markers.kt @@ -1,4 +1,4 @@ -package treelib.singleObjects +package treelib.rbTree enum class Markers { RED, BLACK diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index c8c4cf6..b12bf6d 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -1,9 +1,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent -import treelib.singleObjects.Markers -import treelib.singleObjects.exceptions.IllegalBaseNodeException -import treelib.singleObjects.exceptions.IllegalNodeStateException +import treelib.commonObjects.exceptions.IllegalBaseNodeException +import treelib.commonObjects.exceptions.IllegalNodeStateException class RBBalancer>(private var root: RBNode?) : BalancerParent, RBStateContainer>() { diff --git a/lib/src/main/kotlin/treelib/rbTree/RBNode.kt b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt index 1a60b26..f62abe2 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBNode.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt @@ -1,7 +1,6 @@ package treelib.rbTree import treelib.abstractTree.NodeParent -import treelib.singleObjects.Markers class RBNode>( override var value: Pack, diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index dca6eaa..2a4a40e 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -1,9 +1,8 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancedTreeStruct -import treelib.singleObjects.Markers -import treelib.singleObjects.exceptions.ImpossibleCaseException -import treelib.singleObjects.exceptions.MultithreadingException +import treelib.commonObjects.exceptions.ImpossibleCaseException +import treelib.commonObjects.exceptions.MultithreadingException import java.util.* class RBStruct> : diff --git a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt index a29234e..f96bd07 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt @@ -2,7 +2,7 @@ package treelib.rbTree import treelib.abstractTree.Tree -import treelib.singleObjects.Container +import treelib.commonObjects.Container class RBTree, V> : Tree>, RBStateContainer>, RBVertex>>() { diff --git a/lib/src/main/kotlin/treelib/rbTree/RBVertex.kt b/lib/src/main/kotlin/treelib/rbTree/RBVertex.kt index aee1d95..f2e4609 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBVertex.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBVertex.kt @@ -1,9 +1,8 @@ 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 +) : Vertex() diff --git a/lib/src/test/kotlin/treelib/AVLStructTest.kt b/lib/src/test/kotlin/treelib/AVLStructTest.kt index 433618a..b7d1f4b 100644 --- a/lib/src/test/kotlin/treelib/AVLStructTest.kt +++ b/lib/src/test/kotlin/treelib/AVLStructTest.kt @@ -105,4 +105,4 @@ class AVLStructTest { 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 index 63073da..ef4fe89 100644 --- a/lib/src/test/kotlin/treelib/AVLTreeTest.kt +++ b/lib/src/test/kotlin/treelib/AVLTreeTest.kt @@ -3,7 +3,7 @@ package treelib import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import treelib.avlTree.* -import treelib.singleObjects.Container +import treelib.commonObjects.Container import utils.TreeStructWrapper import utils.TreeWrapper import kotlin.test.assertEquals @@ -25,7 +25,7 @@ class AVLTreeTest { for (i in numbers) { num.add(Pair(i, 1)) } - tree.putItems(num) + tree.putItem(num) numbers.sort() val root = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key @@ -75,9 +75,9 @@ class AVLTreeTest { for (i in numbers) { num.add(Pair(i, 1)) } - tree.putItems(num) + tree.putItem(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 index bab0d2d..1256a4d 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -492,4 +492,4 @@ class BINStructTest { 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 index a13d174..7163d09 100644 --- a/lib/src/test/kotlin/treelib/BINTreeTest.kt +++ b/lib/src/test/kotlin/treelib/BINTreeTest.kt @@ -4,7 +4,7 @@ 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 treelib.commonObjects.Container import utils.TreeStructWrapper import utils.TreeWrapper import kotlin.test.assertEquals @@ -32,7 +32,7 @@ class BINTreeTest { for (i in numbers) { num.add(Pair(i, 1)) } - tree.putItems(num) + tree.putItem(num) assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, actual = numbers[0]) assertEquals( expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.right?.value?.key, @@ -43,7 +43,7 @@ class BINTreeTest { @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) + tree.putItem(num) val temp = tree.getItem(3) assertEquals( @@ -60,7 +60,7 @@ class BINTreeTest { for (i in numbers) { num.add(Pair(i, 1)) } - tree.putItems(num) + tree.putItem(num) tree.deleteItem(numbers[2]) assertEquals(expected = tree.getItem(numbers[2]), actual = null) @@ -75,7 +75,7 @@ class BINTreeTest { for (i in numbers) { num.add(Pair(i, 1)) } - tree.putItems(num) + tree.putItem(num) val root = numbers[0] numbers.sort() val index = numbers.indexOf(root) @@ -86,4 +86,4 @@ class BINTreeTest { actual = numbers[index + 1] ) } -} \ No newline at end of file +} diff --git a/lib/src/test/kotlin/treelib/RBBalancerTest.kt b/lib/src/test/kotlin/treelib/RBBalancerTest.kt index 41f8bdd..d0cea6d 100644 --- a/lib/src/test/kotlin/treelib/RBBalancerTest.kt +++ b/lib/src/test/kotlin/treelib/RBBalancerTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.assertAll import treelib.rbTree.RBBalancer import treelib.rbTree.RBNode import treelib.rbTree.RBStateContainer -import treelib.singleObjects.Markers +import treelib.rbTree.Markers class RBBalancerTest { @@ -18,6 +18,7 @@ class RBBalancerTest { @DisplayName("Tests to check the operation of the balancer after removal") @Nested inner class RemovalTests { + @Test fun `init test`() { val firstBalancer = RBBalancer(null) @@ -350,6 +351,7 @@ class RBBalancerTest { @DisplayName("Tests to check the operation of the balancer after insertion") @Nested inner class InsertionTests { + @Test fun `insertion root`() { val root = RBNode(18, null, null, null, Markers.RED) @@ -483,5 +485,4 @@ class RBBalancerTest { ) } } - } diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index 9f0b39e..5b153bb 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -5,7 +5,7 @@ import treelib.rbTree.RBNode import treelib.rbTree.RBStateContainer import treelib.rbTree.RBStruct import treelib.rbTree.RBVertex -import treelib.singleObjects.Markers +import treelib.rbTree.Markers import utils.RBAnalyzer import utils.TreeStructWrapper import utils.fuzzers.RBStructFuzzer @@ -30,9 +30,9 @@ class RBStructTest { treeStruct.insert(6) val root = treeW.getPrivateNode(treeStruct) assertAll( - { assertEquals(root?.value, 6) }, - { assertEquals(root?.color, Markers.BLACK) }, - { analyzer.checkTree(root!!) } + { assertEquals(root?.value, 6) }, + { assertEquals(root?.color, Markers.BLACK) }, + { analyzer.checkTree(root!!) } ) } @@ -42,9 +42,9 @@ class RBStructTest { treeStruct.insert(3) val root = treeW.getPrivateNode(treeStruct) assertAll( - { assertEquals(root?.left?.value, 3) }, - { assertEquals(root?.left?.color, Markers.RED) }, - { analyzer.checkTree(root!!) } + { assertEquals(root?.left?.value, 3) }, + { assertEquals(root?.left?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } ) } @@ -54,9 +54,9 @@ class RBStructTest { treeStruct.insert(8) val root = treeW.getPrivateNode(treeStruct) assertAll( - { assertEquals(root?.right?.value, 8) }, - { assertEquals(root?.right?.color, Markers.RED) }, - { analyzer.checkTree(root!!) } + { assertEquals(root?.right?.value, 8) }, + { assertEquals(root?.right?.color, Markers.RED) }, + { analyzer.checkTree(root!!) } ) } @@ -67,11 +67,11 @@ class RBStructTest { 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!!) } + { 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!!) } ) } @@ -83,9 +83,9 @@ class RBStructTest { treeStruct.delete(6) val root = treeW.getPrivateNode(treeStruct) assertAll( - { assertEquals(root?.value, 8) }, - { assertEquals(root?.color, Markers.BLACK) }, - { analyzer.checkTree(root!!) } + { assertEquals(root?.value, 8) }, + { assertEquals(root?.color, Markers.BLACK) }, + { analyzer.checkTree(root!!) } ) } @@ -96,9 +96,9 @@ class RBStructTest { treeStruct.delete(6) val root = treeW.getPrivateNode(treeStruct) assertAll( - { assertEquals(8, root?.value) }, - { assertEquals(Markers.BLACK, root?.color) }, - { analyzer.checkTree(root!!) } + { assertEquals(8, root?.value) }, + { assertEquals(Markers.BLACK, root?.color) }, + { analyzer.checkTree(root!!) } ) } @@ -109,45 +109,44 @@ class RBStructTest { treeStruct.delete(6) val root = treeW.getPrivateNode(treeStruct) assertAll( - { assertEquals(3, root?.value) }, - { assertEquals(Markers.BLACK, root?.color) }, - { analyzer.checkTree(root!!) } + { 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 + 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) - } + { + 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 index cd7ce2a..c4fec29 100644 --- a/lib/src/test/kotlin/treelib/RBTreeTest.kt +++ b/lib/src/test/kotlin/treelib/RBTreeTest.kt @@ -3,8 +3,8 @@ 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 treelib.commonObjects.Container +import treelib.rbTree.Markers import utils.TreeStructWrapper import utils.TreeWrapper import kotlin.test.assertEquals @@ -26,7 +26,7 @@ class RBTreeTest { num.add(Pair(i, i)) } - tree.putItems(num) + tree.putItem(num) val rootR = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.right?.color assertEquals(expected = rootR, actual = Markers.RED) @@ -46,10 +46,10 @@ class RBTreeTest { num.add(Pair(i, i)) } - tree.putItems(num) + tree.putItem(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/TestModelRBT.kt b/lib/src/test/kotlin/treelib/TestModelRBT.kt index 9e5a0ad..b58fea3 100644 --- a/lib/src/test/kotlin/treelib/TestModelRBT.kt +++ b/lib/src/test/kotlin/treelib/TestModelRBT.kt @@ -1,7 +1,7 @@ package treelib import treelib.rbTree.RBNode -import treelib.singleObjects.Markers +import treelib.rbTree.Markers class TestModelRBT { fun > countBlackNodes(node: RBNode): Int { @@ -193,4 +193,4 @@ class TestModelRBT { } return nodes } -} \ No newline at end of file +} diff --git a/lib/src/test/kotlin/utils/BINAnalyzer.kt b/lib/src/test/kotlin/utils/BINAnalyzer.kt index 43dc5f5..479e205 100644 --- a/lib/src/test/kotlin/utils/BINAnalyzer.kt +++ b/lib/src/test/kotlin/utils/BINAnalyzer.kt @@ -1,7 +1,7 @@ package utils import treelib.binTree.BINNode -import treelib.singleObjects.exceptions.BugInImplementException +import treelib.commonObjects.exceptions.BugInImplementException class BINAnalyzer>( override val assertMethod: (input: String) -> Unit = { @@ -49,4 +49,4 @@ class BINAnalyzer>( } } -} \ No newline at end of file +} diff --git a/lib/src/test/kotlin/utils/RBAnalyzer.kt b/lib/src/test/kotlin/utils/RBAnalyzer.kt index 9b6b6db..f22c12c 100644 --- a/lib/src/test/kotlin/utils/RBAnalyzer.kt +++ b/lib/src/test/kotlin/utils/RBAnalyzer.kt @@ -1,8 +1,8 @@ package utils import treelib.rbTree.RBNode -import treelib.singleObjects.Markers -import treelib.singleObjects.exceptions.BugInImplementException +import treelib.rbTree.Markers +import treelib.commonObjects.exceptions.BugInImplementException class RBAnalyzer>( override val assertMethod: (input: String) -> Unit = { @@ -82,4 +82,4 @@ class RBAnalyzer>( if (node.color == Markers.BLACK) return leftBlackCount + 1 else return rightBlackCount } -} \ No newline at end of file +} diff --git a/lib/src/test/kotlin/utils/TreeWrapper.kt b/lib/src/test/kotlin/utils/TreeWrapper.kt index a0abe1c..a5b5d8f 100644 --- a/lib/src/test/kotlin/utils/TreeWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeWrapper.kt @@ -1,7 +1,7 @@ package utils import treelib.abstractTree.* -import treelib.singleObjects.Container +import treelib.commonObjects.Container class TreeWrapper< @@ -19,4 +19,4 @@ class TreeWrapper< return treeStruct as TStruct } -} \ 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 index 69421d8..815a4b4 100644 --- a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt @@ -4,7 +4,7 @@ import treelib.abstractTree.Node import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct import treelib.abstractTree.Vertex -import treelib.singleObjects.exceptions.* +import treelib.commonObjects.exceptions.* import utils.Analyzer import utils.TreeStructWrapper import java.io.File @@ -122,5 +122,4 @@ abstract class TreeStructFuzzer< file.appendText("${baseInput[index]} \n") } } - } From ad42865b85c925af600e97ce35a3263b72a2e9ae Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 22 Apr 2023 15:12:12 +0300 Subject: [PATCH 144/212] feat(.json files): Implement saving many trees --- .gitignore | 1 + lib/build.gradle.kts | 2 +- .../kotlin/databaseManage/BINTreeManager.kt | 25 ++++++++++++++----- .../kotlin/databaseManage/RBTreeManager.kt | 16 ++++++------ .../databaseSave/jsonFormat/JsonRepository.kt | 15 ++++++++--- .../databaseSave/neo4j/Neo4jRepository.kt | 10 ++++---- 6 files changed, 46 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index c03a298..f2de55c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /lib/TEST_TEST/ /gradle/ /neo4jDB/ +/jsonFormat/ diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index ce1b92a..3c476f8 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -92,7 +92,7 @@ tasks.jacocoTestReport { tasks.jacocoTestCoverageVerification { classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { include("**/treelib/**") - exclude("**/singleObjects/**") + exclude("**/singleObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class") } }) dependsOn(tasks.jacocoTestReport) violationRules { diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index 0c6de63..088f02a 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -1,21 +1,34 @@ package databaseManage +import com.google.gson.reflect.TypeToken +import databaseSave.jsonFormat.DrawableBINVertex import databaseSave.jsonFormat.JsonRepository import treelib.binTree.BINStruct +import treelib.singleObjects.Container class BINTreeManager: TreeManager() { /*** using json format files ***/ - val jsonRep = JsonRepository(System.getProperty("user.dir")) + private val jsonRep = JsonRepository(System.getProperty("user.dir") + "/jsonFormatFiles") + fun initTree(treeName: String): BINStruct> { + val typeToken = object : TypeToken>>>() {} + val preOrder = jsonRep.exportTree(treeName, typeToken) + val BINtree = BINStruct>() + BINtree.restoreStruct(preOrder.toList()) - /*** 1.7.6 ***/ - fun initTree(treeName: String) { - TODO() + return BINtree } - fun > saveTree(tree: BINStruct) { - TODO() + fun > saveTree(preOrder: Array>, treeName: String) { + + jsonRep.saveChanges(preOrder, treeName) + } + + fun deleteTree(treeName: String) = jsonRep.removeTree(treeName) + + fun cleanDB() = jsonRep.clean() + } \ No newline at end of file diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt index 1aff74e..a16a9c0 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -15,35 +15,35 @@ class RBTreeManager { neo4jDB.open("bolt://localhost:7687", "neo4j", "password") } - fun initTree(treeName: String): RBStruct>> { + fun initTree(treeName: String): RBStruct> { /*** orders.first = preOrder, orders.second = inOrder ***/ - val orders: Pair>>>, List>>>> = + val orders: Pair>>, List>>> = neo4jDB.exportRBtree(treeName) - val RBtree = RBStruct>>() + val RBtree = RBStruct>() RBtree.restoreStruct(orders.first, orders.second) neo4jDB.close() return RBtree } - fun > saveTree(tree: RBStruct, treeName: String) { + fun > saveTree(preOrder: Array>, inOrder: Array>, treeName: String) { - val preOrder = tree.preOrder().map { DrawableRBVertex(it.value, it.color) } - val inOrder = tree.inOrder().map { DrawableRBVertex(it.value, it.color) } - - neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray(), treeName) + neo4jDB.saveChanges(preOrder, inOrder, treeName) neo4jDB.close() } fun deleteTree(treeName: String) { neo4jDB.removeTree(treeName) + neo4jDB.close() } fun cleanDB() { + neo4jDB.clean() + neo4jDB.close() } } \ No newline at end of file diff --git a/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt index 45f3f26..7aa791b 100644 --- a/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt +++ b/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt @@ -1,7 +1,7 @@ package databaseSave.jsonFormat -import com.google.common.reflect.TypeToken import com.google.gson.GsonBuilder +import com.google.gson.reflect.TypeToken import java.io.File class JsonRepository(private val dirPath: String) { @@ -25,13 +25,22 @@ class JsonRepository(private val dirPath: String) { } - fun >exportTree(fileName: String, typeToken: TypeToken>>) { + fun >exportTree(fileName: String, typeToken: TypeToken>>): Array> { val gson = GsonBuilder().setPrettyPrinting().create() val json = File(dirPath, fileName).readText() - val preOrd = gson.fromJson>>(json, typeToken.type) + val preOrder = gson.fromJson>>(json, typeToken.type) + return preOrder + } + + fun removeTree(treeName: String) { + File(dirPath, treeName).delete() + } + fun clean() { + //Runtime.getRuntime().exec(arrayOf("echo ${dirPath} > 1.txt")) + File(dirPath).deleteRecursively() } diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index cc30f0c..5abce42 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -87,11 +87,11 @@ class Neo4jRepository : Closeable { session.close() } - fun exportRBtree(treeName: String): Pair>>>, List>>>> { + fun exportRBtree(treeName: String): Pair>>, List>>> { val session = driver?.session() ?: throw IOException() - var preOrder: List>>> = listOf() - var inOrder: List>>> = listOf() + var preOrder: List>> = listOf() + var inOrder: List>> = listOf() session.executeRead { tx -> preOrder = tx.run( @@ -102,7 +102,7 @@ class Neo4jRepository : Closeable { ).list() .map { DrawableRBVertex( - value = Container(Pair(it.values()[1].toString(), it.values()[0].toString())), + value = Container(Pair(it.values()[1].toString().toInt(), 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() @@ -117,7 +117,7 @@ class Neo4jRepository : Closeable { ).list() .map { DrawableRBVertex( - value = Container(Pair(it.values()[1].toString(), it.values()[0].toString())), + value = Container(Pair(it.values()[1].toString().toInt(), 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() From c29c14c82bb7b8959c30f05312ec90cd2e25c5e7 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 22 Apr 2023 15:25:17 +0300 Subject: [PATCH 145/212] refactor: Change argument name in balance method --- lib/src/main/kotlin/databaseManage/BINTreeManager.kt | 2 +- lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt | 6 +++--- lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index 088f02a..def873f 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -4,7 +4,7 @@ import com.google.gson.reflect.TypeToken import databaseSave.jsonFormat.DrawableBINVertex import databaseSave.jsonFormat.JsonRepository import treelib.binTree.BINStruct -import treelib.singleObjects.Container +import treelib.commonObjects.Container class BINTreeManager: TreeManager() { diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt index 1a3afb9..65157da 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt @@ -17,10 +17,10 @@ class AVLBalancer>(private var root: AVLNode?) : currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u } - override fun balance(stateContainer: AVLStateContainer): AVLNode { - val node = stateContainer.contentNode + override fun balance(state: AVLStateContainer): AVLNode { + val node = state.contentNode ?: throw IllegalStateException("") // IllegalBaseNodeException("A non-existent node (null) was passed to the method") - root = stateContainer.root + root = state.root return balance(root, node.value) } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 10d1ecc..961ae7e 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -41,8 +41,8 @@ class RBBalancer>(private var root: RBNode?) : return node.right == null && node.left == null } - override fun balance(stateContainer: RBStateContainer): RBNode { - val node = stateContainer.contentNode + override fun balance(state: RBStateContainer): RBNode { + val node = state.contentNode ?: throw IllegalBaseNodeException() val uncle = getUncle(node) when { @@ -132,7 +132,7 @@ class RBBalancer>(private var root: RBNode?) : } } } - throw IllegalStateException() + throw IllegalStateException("") } private fun afterInsert(node: RBNode): RBNode { From 76cf242aa287f08646df9fb76db54c62dc61bf40 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 22 Apr 2023 21:53:58 +0300 Subject: [PATCH 146/212] refactor: Exceptions throwing - #22. --- .../main/kotlin/treelib/abstractTree/Tree.kt | 4 +- .../kotlin/treelib/abstractTree/TreeStruct.kt | 24 +++++------ .../abstractTree/balanced/BalancerNoParent.kt | 5 +-- .../abstractTree/balanced/BalancerParent.kt | 5 +-- .../exceptions/BugInImplementException.kt | 16 -------- .../exceptions/IllegalBaseNodeException.kt | 18 --------- .../exceptions/IllegalNodeStateException.kt | 18 --------- .../exceptions/MultithreadingException.kt | 18 --------- ...tentValueException.kt => VauleNotFound.kt} | 2 +- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 40 ++++++++----------- .../main/kotlin/treelib/rbTree/RBStruct.kt | 3 +- lib/src/test/kotlin/utils/BINAnalyzer.kt | 3 +- lib/src/test/kotlin/utils/RBAnalyzer.kt | 3 +- .../kotlin/utils/fuzzers/TreeStructFuzzer.kt | 9 +---- 14 files changed, 39 insertions(+), 129 deletions(-) delete mode 100644 lib/src/main/kotlin/treelib/commonObjects/exceptions/BugInImplementException.kt delete mode 100644 lib/src/main/kotlin/treelib/commonObjects/exceptions/IllegalBaseNodeException.kt delete mode 100644 lib/src/main/kotlin/treelib/commonObjects/exceptions/IllegalNodeStateException.kt delete mode 100644 lib/src/main/kotlin/treelib/commonObjects/exceptions/MultithreadingException.kt rename lib/src/main/kotlin/treelib/commonObjects/exceptions/{NonExistentValueException.kt => VauleNotFound.kt} (88%) diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index f061ba5..2ecfa46 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -1,7 +1,7 @@ package treelib.abstractTree import treelib.commonObjects.Container -import treelib.commonObjects.exceptions.NonExistentValueException +import treelib.commonObjects.exceptions.VauleNotFound abstract class Tree< K : Comparable, @@ -32,7 +32,7 @@ abstract class Tree< operator fun get(key: K): V? = treeStruct.find(wrapForFind(key))?.value fun deleteItem(key: K) { - if (treeStruct.find(wrapForFind(key)) == null) throw NonExistentValueException() + if (treeStruct.find(wrapForFind(key)) == null) throw VauleNotFound() treeStruct.delete(wrapForFind(key)) } diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt index 95d8907..272eed4 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt @@ -1,9 +1,7 @@ package treelib.abstractTree -import treelib.commonObjects.exceptions.BugInImplementException import treelib.commonObjects.exceptions.ImpossibleCaseException -import treelib.commonObjects.exceptions.MultithreadingException -import treelib.commonObjects.exceptions.NonExistentValueException +import treelib.commonObjects.exceptions.VauleNotFound abstract class TreeStruct< @@ -31,9 +29,9 @@ abstract class TreeStruct< currentNode = it.left } - else -> throw BugInImplementException("getLeafForInsert shouldn't be used with a value exists in Struct") + else -> throw InternalError("getLeafForInsert shouldn't be used with a value exists in Struct") } - } ?: throw MultithreadingException(ImpossibleCaseException()) + } ?: throw ImpossibleCaseException() } } @@ -53,7 +51,7 @@ abstract class TreeStruct< } } } - ?: throw BugInImplementException("getParentByValue shouldn't be used with value doesn't exist in tree")// (1)l -> + ?: throw InternalError("getParentByValue shouldn't be used with value doesn't exist in tree")// (1)l -> } } @@ -77,7 +75,7 @@ abstract class TreeStruct< var currentNode: NodeType? localRoot.right - ?: throw BugInImplementException("Incorrect usage of the getRightMinNode: right node doesn't exist") + ?: throw InternalError("Incorrect usage of the getRightMinNode: right node doesn't exist") currentNode = localRoot.right @@ -85,7 +83,7 @@ abstract class TreeStruct< currentNode?.let { curNode -> if (curNode.left == null) return@getRightMinNode curNode else currentNode = curNode.left - } ?: throw MultithreadingException(ImpossibleCaseException()) + } ?: throw ImpossibleCaseException() } } @@ -97,7 +95,7 @@ abstract class TreeStruct< val childForLink: NodeType? when { - (node.right != null) && (node.left != null) -> throw BugInImplementException("unLink - method Shouldn't be used with node with both children") + (node.right != null) && (node.left != null) -> throw InternalError("unLink - method Shouldn't be used with node with both children") node.right != null -> childForLink = node.right node.left != null -> childForLink = node.left else -> childForLink = null @@ -183,7 +181,7 @@ abstract class TreeStruct< val parentDeleteNode: NodeType? val deleteNode: NodeType? - if (findItem(item).contentNode == null) throw NonExistentValueException() + if (findItem(item).contentNode == null) throw VauleNotFound() parentDeleteNode = getParentByValue(item) if (parentDeleteNode != null) { @@ -333,7 +331,7 @@ abstract class TreeStruct< } current = parent } - } ?: throw MultithreadingException(ImpossibleCaseException()) + } ?: throw ImpossibleCaseException() } return arrayNodes.map { toVertex(it) } } @@ -351,12 +349,12 @@ abstract class TreeStruct< if (current.right != null) current.right?.let { queue.add(it) - } ?: throw MultithreadingException(ImpossibleCaseException()) + } ?: throw ImpossibleCaseException() if (current.left != null) current.left?.let { queue.add(it) - } ?: throw MultithreadingException(ImpossibleCaseException()) + } ?: throw ImpossibleCaseException() } } return arrayNodes.map { toVertex(it) } diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt index e70ba48..2779682 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt @@ -2,19 +2,18 @@ package treelib.abstractTree.balanced import treelib.abstractTree.Node import treelib.abstractTree.StateContainer -import treelib.commonObjects.exceptions.IllegalNodeStateException abstract class BalancerNoParent, NodeType : Node, StateContainerType : StateContainer> : Balancer { override fun rightRotate(currentNode: NodeType): NodeType { - val leftSon = currentNode.left ?: throw IllegalNodeStateException() + val leftSon = currentNode.left ?: throw InternalError() currentNode.left = leftSon.right leftSon.right = currentNode return leftSon } override fun leftRotate(currentNode: NodeType): NodeType { - val rightSon = currentNode.right ?: throw IllegalNodeStateException() + val rightSon = currentNode.right ?: throw InternalError() currentNode.right = rightSon.left rightSon.left = currentNode return rightSon diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt index 9dcca42..f6b1e96 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt @@ -2,12 +2,11 @@ package treelib.abstractTree.balanced import treelib.abstractTree.NodeParent import treelib.abstractTree.StateContainer -import treelib.commonObjects.exceptions.IllegalNodeStateException abstract class BalancerParent, NodeType : NodeParent, StateContainerType : StateContainer> : Balancer { override fun rightRotate(currentNode: NodeType): NodeType { - val leftChild = currentNode.left ?: throw IllegalNodeStateException() + val leftChild = currentNode.left ?: throw InternalError() val parent = currentNode.parent leftChild.right?.parent = currentNode @@ -24,7 +23,7 @@ abstract class BalancerParent, NodeType : NodeParent $message", - ) - - constructor(message: String, cause: Throwable) : super( - "Possible problem with multithreading -> $message", - cause, - ) - - constructor(cause: Throwable) : super(cause) -} diff --git a/lib/src/main/kotlin/treelib/commonObjects/exceptions/NonExistentValueException.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/VauleNotFound.kt similarity index 88% rename from lib/src/main/kotlin/treelib/commonObjects/exceptions/NonExistentValueException.kt rename to lib/src/main/kotlin/treelib/commonObjects/exceptions/VauleNotFound.kt index 95f8c6c..00efb04 100644 --- a/lib/src/main/kotlin/treelib/commonObjects/exceptions/NonExistentValueException.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/VauleNotFound.kt @@ -1,6 +1,6 @@ package treelib.commonObjects.exceptions -open class NonExistentValueException : Exception { +open class VauleNotFound : Exception { constructor() : super( "Value doesn't exist in the tree" ) diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 961ae7e..e2f609c 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -1,8 +1,7 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent -import treelib.commonObjects.exceptions.IllegalBaseNodeException -import treelib.commonObjects.exceptions.IllegalNodeStateException + class RBBalancer>(private var root: RBNode?) : BalancerParent, RBStateContainer>() { @@ -41,9 +40,9 @@ class RBBalancer>(private var root: RBNode?) : 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 InternalError() val uncle = getUncle(node) when { /** node insertion case **/ @@ -66,7 +65,7 @@ class RBBalancer>(private var root: RBNode?) : currentNode = parent } parent = - currentNode.parent?.parent ?: throw IllegalNodeStateException() + currentNode.parent?.parent ?: throw InternalError() currentNode = rightRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -79,7 +78,7 @@ class RBBalancer>(private var root: RBNode?) : currentNode = parent } parent = - currentNode.parent?.parent ?: throw IllegalNodeStateException() + currentNode.parent?.parent ?: throw InternalError() currentNode = leftRotate(parent) currentNode.color = Markers.BLACK currentNode.right?.color = Markers.RED @@ -120,19 +119,12 @@ class RBBalancer>(private var root: RBNode?) : firstCase(node, null) } - node.left?.color == Markers.BLACK && node.right?.color == Markers.RED && nodeIsLeaf(node.right) -> { - node.right?.color = Markers.BLACK - getRoot(node) - } - node.right?.color == Markers.BLACK && node.left?.color == Markers.RED && nodeIsLeaf(node.left) -> { - node.left?.color = Markers.BLACK - getRoot(node) - } + else -> throw IllegalStateException() } } } - throw IllegalStateException("") + throw IllegalStateException() } private fun afterInsert(node: RBNode): RBNode { @@ -141,7 +133,7 @@ class RBBalancer>(private var root: RBNode?) : val uncle = getUncle(currentNode) if (uncle?.color == Markers.RED) { currentNode.parent?.color = Markers.BLACK - currentNode = currentNode.parent?.parent ?: throw IllegalNodeStateException() + currentNode = currentNode.parent?.parent ?: throw InternalError() currentNode.color = Markers.RED uncle.color = Markers.BLACK } else if (uncle != null) { @@ -164,13 +156,13 @@ class RBBalancer>(private var root: RBNode?) : getRoot(parent) } - else -> getRoot(node ?: throw IllegalNodeStateException()) + else -> getRoot(node ?: throw InternalError()) } } /** parent is red **/ private fun secondCase(parent: RBNode, node: RBNode?) { - var brother = getBrother(parent, node) ?: throw IllegalNodeStateException() + var brother = getBrother(parent, node) ?: throw InternalError() if (brother.color == Markers.RED) throw NullPointerException() @@ -222,7 +214,7 @@ class RBBalancer>(private var root: RBNode?) : /** parent is black **/ private fun thirdCase(parent: RBNode, node: RBNode?) { - val brother = getBrother(parent, node) ?: throw IllegalNodeStateException() + val brother = getBrother(parent, node) ?: throw InternalError() when (brother.color) { Markers.RED -> thirdCaseSubFirst(brother, parent) Markers.BLACK -> thirdCaseSubSecond(brother, parent) @@ -233,7 +225,7 @@ class RBBalancer>(private var root: RBNode?) : private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) { when (brother) { brother.parent?.left -> { - var rightBrotherSon = brother.right ?: throw IllegalNodeStateException() + var rightBrotherSon = brother.right ?: throw InternalError() if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) { rightBrotherSon.color = Markers.RED @@ -250,7 +242,7 @@ class RBBalancer>(private var root: RBNode?) : leftRotate(rightBrotherSon) rightBrotherSon = - rightBrotherSon.parent ?: throw IllegalNodeStateException() + rightBrotherSon.parent ?: throw InternalError() rightBrotherSon.color = Markers.BLACK } @@ -266,7 +258,7 @@ class RBBalancer>(private var root: RBNode?) : if (leftBrotherSon.right?.color != Markers.RED && leftBrotherSon.left?.color != Markers.RED) { leftBrotherSon.color = Markers.RED brother.color = Markers.BLACK - leftRotate(brother.parent ?: throw IllegalNodeStateException()) + leftRotate(brother.parent ?: throw InternalError()) return } @@ -274,7 +266,7 @@ class RBBalancer>(private var root: RBNode?) : rightRotate(leftBrotherSon) leftBrotherSon.color = Markers.RED leftBrotherSon = - leftBrotherSon.parent ?: throw IllegalNodeStateException() + leftBrotherSon.parent ?: throw InternalError() leftBrotherSon.color = Markers.BLACK } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 2a4a40e..0d04ae6 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -2,7 +2,6 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancedTreeStruct import treelib.commonObjects.exceptions.ImpossibleCaseException -import treelib.commonObjects.exceptions.MultithreadingException import java.util.* class RBStruct> : @@ -66,7 +65,7 @@ class RBStruct> : root = node root?.let { it.color = Markers.BLACK - } ?: throw MultithreadingException(ImpossibleCaseException()) + } ?: throw ImpossibleCaseException() } else { if (node.value > parent.value) parent.right = node else parent.left = node diff --git a/lib/src/test/kotlin/utils/BINAnalyzer.kt b/lib/src/test/kotlin/utils/BINAnalyzer.kt index 479e205..8ad0b8f 100644 --- a/lib/src/test/kotlin/utils/BINAnalyzer.kt +++ b/lib/src/test/kotlin/utils/BINAnalyzer.kt @@ -1,11 +1,10 @@ package utils import treelib.binTree.BINNode -import treelib.commonObjects.exceptions.BugInImplementException class BINAnalyzer>( override val assertMethod: (input: String) -> Unit = { - throw BugInImplementException(it) + throw InternalError(it) } ) : Analyzer>() { diff --git a/lib/src/test/kotlin/utils/RBAnalyzer.kt b/lib/src/test/kotlin/utils/RBAnalyzer.kt index f22c12c..1b5d9b5 100644 --- a/lib/src/test/kotlin/utils/RBAnalyzer.kt +++ b/lib/src/test/kotlin/utils/RBAnalyzer.kt @@ -2,11 +2,10 @@ package utils import treelib.rbTree.RBNode import treelib.rbTree.Markers -import treelib.commonObjects.exceptions.BugInImplementException class RBAnalyzer>( override val assertMethod: (input: String) -> Unit = { - throw BugInImplementException(it) + throw InternalError(it) } ) : Analyzer>() { /** Magic number for error := -9999999 -> just an impossible value **/ diff --git a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt index 815a4b4..5ae952e 100644 --- a/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt +++ b/lib/src/test/kotlin/utils/fuzzers/TreeStructFuzzer.kt @@ -81,7 +81,7 @@ abstract class TreeStructFuzzer< if (inputSize == null) dataSize = baseInput.size else dataSize = inputSize - if (dataSize > baseInput.size) throw BugInImplementException("inputSize > size of the baseInput") + if (dataSize > baseInput.size) throw InternalError("inputSize > size of the baseInput") return dataSize } @@ -91,12 +91,7 @@ abstract class TreeStructFuzzer< private fun exceptionsCatch(ex: Exception) { when (ex) { - is BugInImplementException, - is IllegalBaseNodeException, - is IllegalNodeStateException, - is ImpossibleCaseException, - is MultithreadingException, - is NonExistentValueException, + is VauleNotFound, -> {/*TODO: Implement */ } From f1f0daba6ef3efcd3070fd8d0585fe507959b062 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sat, 22 Apr 2023 21:55:39 +0300 Subject: [PATCH 147/212] refactor: Remove "turns" from interface Balancer. --- lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt | 4 ---- .../kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt | 4 ++-- .../kotlin/treelib/abstractTree/balanced/BalancerParent.kt | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt index 57054ce..6931a5b 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt @@ -9,9 +9,5 @@ interface Balancer< State : StateContainer, > { - fun rightRotate(currentNode: NodeType): NodeType - - fun leftRotate(currentNode: NodeType): NodeType - fun balance(state: State): NodeType } diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt index 2779682..c479e2b 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt @@ -5,14 +5,14 @@ import treelib.abstractTree.StateContainer abstract class BalancerNoParent, NodeType : Node, StateContainerType : StateContainer> : Balancer { - override fun rightRotate(currentNode: NodeType): NodeType { + fun rightRotate(currentNode: NodeType): NodeType { val leftSon = currentNode.left ?: throw InternalError() currentNode.left = leftSon.right leftSon.right = currentNode return leftSon } - override fun leftRotate(currentNode: NodeType): NodeType { + fun leftRotate(currentNode: NodeType): NodeType { val rightSon = currentNode.right ?: throw InternalError() currentNode.right = rightSon.left rightSon.left = currentNode diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt index f6b1e96..e72e763 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt @@ -5,7 +5,7 @@ import treelib.abstractTree.StateContainer abstract class BalancerParent, NodeType : NodeParent, StateContainerType : StateContainer> : Balancer { - override fun rightRotate(currentNode: NodeType): NodeType { + fun rightRotate(currentNode: NodeType): NodeType { val leftChild = currentNode.left ?: throw InternalError() val parent = currentNode.parent @@ -22,7 +22,7 @@ abstract class BalancerParent, NodeType : NodeParent Date: Sun, 23 Apr 2023 21:05:44 +0300 Subject: [PATCH 148/212] Feat: add tests for BINStruct, AVLTree --- lib/src/test/kotlin/treelib/AVLTreeTest.kt | 10 +++++ lib/src/test/kotlin/treelib/BINStructTest.kt | 39 ++++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/src/test/kotlin/treelib/AVLTreeTest.kt b/lib/src/test/kotlin/treelib/AVLTreeTest.kt index ef4fe89..c5ce268 100644 --- a/lib/src/test/kotlin/treelib/AVLTreeTest.kt +++ b/lib/src/test/kotlin/treelib/AVLTreeTest.kt @@ -1,5 +1,6 @@ package treelib +import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import treelib.avlTree.* @@ -80,4 +81,13 @@ class AVLTreeTest { assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, actual = null) } + + @Test + fun `test get arg`(){ + for (i in (1..10000).shuffled()) { + tree.putItem(Pair(i, i)) + } + + assertEquals(expected = tree.get(542), actual = 542) + } } diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 1256a4d..07ab528 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -1,13 +1,11 @@ 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 org.junit.jupiter.api.* import treelib.binTree.BINNode import treelib.binTree.BINStateContainer import treelib.binTree.BINStruct import treelib.binTree.BINVertex +import treelib.commonObjects.exceptions.VauleNotFound import utils.BINAnalyzer import utils.TreeStructWrapper import kotlin.test.assertEquals @@ -492,4 +490,37 @@ class BINStructTest { val root = treeW.getPrivateNode(treeStruct) root?.let { analyzer.checkTree(root) } ?: Exception("CHzh") } + + @Test + fun `test delete a node that is not in tree`() { + Assertions.assertThrows(VauleNotFound::class.java) { + for (i in (0..10000).shuffled()) { + treeStruct.insert(i) + } + treeStruct.delete(100001) + } + } + + @Test + fun `test add and delete 10000 arg`() { + for (i in (0..10000).shuffled()) { + treeStruct.insert(i) + } + for (i in (0..10000).shuffled()) { + treeStruct.delete(i) + } + + val root = treeW.getPrivateNode(treeStruct)?.value + assertEquals(expected = root, actual = null) + } + + @Test + fun `test delete a node that is not in tree with message`() { + Assertions.assertThrows(VauleNotFound(". Repeat again.")::class.java) { + for (i in (0..10000).shuffled()) { + treeStruct.insert(i) + } + treeStruct.delete(100001) + } + } } From 1aba9e436b92fc45bce2e58acad83c3be3be97af Mon Sep 17 00:00:00 2001 From: Artem Date: Sun, 23 Apr 2023 21:24:28 +0300 Subject: [PATCH 149/212] feat: Implement a sketch of the application start window --- lib/build.gradle.kts | 13 ++++- lib/src/main/kotlin/main.kt | 105 ++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 lib/src/main/kotlin/main.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 5873afc..96fe750 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -7,6 +7,7 @@ plugins { `maven-publish` kotlin("plugin.serialization") version "1.5.0" // checkstyle + id("org.jetbrains.compose") version "1.4.0" } java { @@ -17,6 +18,7 @@ java { repositories { mavenLocal() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") mavenCentral() } @@ -34,14 +36,23 @@ dependencies { // JDBC Sqlite implementation("org.xerial", "sqlite-jdbc", sqliteJdbcVersion) + implementation(kotlin("stdlib-jdk8")) + implementation(compose.desktop.currentOs) + implementation(compose.material3) + 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") testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") - implementation(kotlin("stdlib-jdk8")) } +compose.desktop { + application { + mainClass = "MainKt" + } +} + tasks.test { finalizedBy(tasks.jacocoTestReport) useJUnitPlatform() diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt new file mode 100644 index 0000000..9a994dc --- /dev/null +++ b/lib/src/main/kotlin/main.kt @@ -0,0 +1,105 @@ + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.material.TextField +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.key.* +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.MenuBar +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import java.awt.Dimension + +// я передумал, будет три маленьких кнопки где-нибудь +@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) +fun main() = application { + + Window( + onCloseRequest = ::exitApplication, + title = "LESOK", + ) { + this.window.minimumSize = Dimension(800, 600) + val treeNames = listOf("RED BLACK TREE", "AVL TREE", "BINARY TREE") + MenuBar { + Menu(text = "File", mnemonic = 'T') { + Menu(text = "Open") { + Item("Red Black tree", onClick = {}) + Item("AVL tree", onClick = {}) + Item("Binary tree", onClick = {}) + } + Menu(text = "Create") { + Item("Red Black tree", onClick = {}) + Item("AVL tree", onClick = {}) + Item("Binary tree", onClick = {}) + } + } + } + Box(modifier = Modifier.background(Color(234, 231, 220)).fillMaxSize()) { + Column(verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth(0.2f).fillMaxHeight(0.3f) + ) { + val options = listOf("INSERT", "DELETE", "FIND") + repeat(3) {index -> + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier.weight(1f) + ) { + var consumedText by remember { mutableStateOf(0) } + var text by remember { mutableStateOf("") } + Box( + modifier = Modifier + .clip(RoundedCornerShape(5.dp)) + .background(Color(216, 195, 165)) + .weight(1f) + .align(Alignment.CenterVertically) + ) { + Text( + text = options[index], + color = Color(142, 141, 138), + fontWeight = FontWeight.ExtraBold, + textAlign = TextAlign.Center + ) + } + TextField( + value = text, + onValueChange = { text = it }, + modifier = Modifier + .onPreviewKeyEvent { + when { + (!it.isShiftPressed && it.key == Key.Enter && it.type == KeyEventType.KeyUp) -> { + consumedText -= text.length + text = "" + true + // вот тут походу отправляем запрос в контроллер + } + else -> false + } + } + .background(Color(216, 195, 165)) + .weight(1f) + .align(Alignment.CenterVertically) + ) + } + Spacer(Modifier.height(5.dp)) + } + + } + } + } +} + +// .wrapContentSize() From 18000694e2cab1104df7331da31e56ebb8442ce3 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 24 Apr 2023 00:00:56 +0300 Subject: [PATCH 150/212] feat: Implement a name getter for all trees (BIN, RBT) --- .../main/kotlin/databaseManage/BINTreeManager.kt | 15 ++++++++++++--- .../main/kotlin/databaseManage/RBTreeManager.kt | 13 +++++++++++-- .../databaseSave/jsonFormat/JsonRepository.kt | 9 ++++++--- .../kotlin/databaseSave/neo4j/Neo4jRepository.kt | 11 +++++++++++ 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index def873f..bcc0d00 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -5,12 +5,15 @@ import databaseSave.jsonFormat.DrawableBINVertex import databaseSave.jsonFormat.JsonRepository import treelib.binTree.BINStruct import treelib.commonObjects.Container +import java.io.File -class BINTreeManager: TreeManager() { +class BINTreeManager { /*** using json format files ***/ - private val jsonRep = JsonRepository(System.getProperty("user.dir") + "/jsonFormatFiles") + private val dirPath = System.getProperty("user.dir") + "/jsonFormatFiles" + + private val jsonRep = JsonRepository(dirPath) fun initTree(treeName: String): BINStruct> { val typeToken = object : TypeToken>>>() {} @@ -21,7 +24,7 @@ class BINTreeManager: TreeManager() { return BINtree } - fun > saveTree(preOrder: Array>, treeName: String) { + fun > saveTree(preOrder: Array>, treeName: String) { jsonRep.saveChanges(preOrder, treeName) @@ -29,6 +32,12 @@ class BINTreeManager: TreeManager() { fun deleteTree(treeName: String) = jsonRep.removeTree(treeName) + fun getNamesTrees(): List? { + val filesNames = File(dirPath).list()?.map { it.replace(".json", "") } + + return filesNames?.subList(0, 3) + } + fun cleanDB() = jsonRep.clean() } \ No newline at end of file diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt index e893be7..ca52d49 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -2,8 +2,8 @@ package databaseManage import databaseSave.neo4j.DrawableRBVertex import databaseSave.neo4j.Neo4jRepository -import treelib.rbTree.RBStruct import treelib.commonObjects.Container +import treelib.rbTree.RBStruct class RBTreeManager { @@ -27,7 +27,11 @@ class RBTreeManager { return RBtree } - fun > saveTree(preOrder: Array>, inOrder: Array>, treeName: String) { + fun > saveTree( + preOrder: Array>, + inOrder: Array>, + treeName: String + ) { neo4jDB.saveChanges(preOrder, inOrder, treeName) neo4jDB.close() @@ -40,6 +44,11 @@ class RBTreeManager { } + fun getNamesTrees(): List? { + val treesNames = neo4jDB.findNamesTrees() + return treesNames?.subList(0, 3) + } + fun cleanDB() { neo4jDB.clean() diff --git a/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt index 7aa791b..60b4aed 100644 --- a/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt +++ b/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt @@ -10,7 +10,7 @@ class JsonRepository(private val dirPath: String) { File(dirPath).mkdirs() } - fun >saveChanges( + fun > saveChanges( preOrder: Array>, fileName: String ) { @@ -25,7 +25,10 @@ class JsonRepository(private val dirPath: String) { } - fun >exportTree(fileName: String, typeToken: TypeToken>>): Array> { + fun > exportTree( + fileName: String, + typeToken: TypeToken>> + ): Array> { val gson = GsonBuilder().setPrettyPrinting().create() val json = File(dirPath, fileName).readText() @@ -39,7 +42,7 @@ class JsonRepository(private val dirPath: String) { } fun clean() { - //Runtime.getRuntime().exec(arrayOf("echo ${dirPath} > 1.txt")) + File(dirPath).deleteRecursively() } diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index a634326..d61dc8a 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -144,6 +144,17 @@ class Neo4jRepository : Closeable { } + fun findNamesTrees(): List? { + val session = driver?.session() ?: throw IOException() + var treesNames: List? = null + session.executeRead { tx -> + treesNames = tx.run("MATCH (n: Node) WHERE NOT(:Node)-->(n) RETURN n.treeName") + .list().map { it.toString() }.filter { it != "null" } + } + + return treesNames + } + fun clean() { val session = driver?.session() ?: throw IOException() From f4f01f1f7d4e3fc23b5073572b3f55b17073ee44 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 23 Apr 2023 23:35:50 +0300 Subject: [PATCH 151/212] feat: Implement saving AVLStruct in SQLite via exposed-ORM (#23). refactor: .... --- gradle.properties | 1 + lib/build.gradle.kts | 31 +++-- .../kotlin/databaseManage/AVLTreeManager.kt | 4 +- .../sqlite/SQLiteRepositoryExposed.kt | 113 ++++++++++++++++++ ...eRepository.kt => SQLiteRepositoryJDBC.kt} | 2 +- .../sqlite/treeEntities/TreeTableEntity.kt | 11 ++ .../sqlite/treeEntities/TreesTable.kt | 13 ++ .../sqlite/vertexEntities/VertexTable.kt | 13 ++ .../vertexEntities/VertexTableEntity.kt | 16 +++ 9 files changed, 193 insertions(+), 11 deletions(-) create mode 100644 lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt rename lib/src/main/kotlin/databaseSave/sqlite/{SQLiteRepository.kt => SQLiteRepositoryJDBC.kt} (99%) create mode 100644 lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreeTableEntity.kt create mode 100644 lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreesTable.kt create mode 100644 lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTable.kt create mode 100644 lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt diff --git a/gradle.properties b/gradle.properties index e08d4b3..f1a74ac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,3 @@ kotlinVersion=1.8.10 sqliteJdbcVersion=3.41.2.1 +exposedVersion=0.40.1 diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 5873afc..93adb67 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -1,4 +1,3 @@ -val sqliteJdbcVersion: String by project plugins { java kotlin("jvm") version "1.8.10" @@ -32,8 +31,15 @@ dependencies { implementation("org.neo4j", "neo4j-ogm-bolt-driver", neo4jCore) // JDBC Sqlite + val sqliteJdbcVersion: String by project implementation("org.xerial", "sqlite-jdbc", sqliteJdbcVersion) + // JetBrains Exposed + val exposedVersion: String by project + implementation("org.jetbrains.exposed:exposed-core:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") + 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") @@ -52,9 +58,9 @@ tasks.test { val failedTests = mutableListOf() val skippedTests = mutableListOf() - addTestListener (object: TestListener { - override fun beforeSuite(suite: TestDescriptor?) { } - override fun beforeTest(testDescriptor: TestDescriptor?) { } + addTestListener(object : TestListener { + override fun beforeSuite(suite: TestDescriptor?) {} + override fun beforeTest(testDescriptor: TestDescriptor?) {} override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { when (result.resultType) { TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor) @@ -62,6 +68,7 @@ tasks.test { else -> {} } } + override fun afterSuite(suite: TestDescriptor, result: TestResult) { if (suite.parent == null) { // root suite logger.lifecycle("####################################################################################") @@ -91,10 +98,18 @@ tasks.jacocoTestReport { } tasks.jacocoTestCoverageVerification { - classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { - include("**/treelib/**") - exclude("**/singleObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class") - } }) + classDirectories.setFrom(classDirectories.files.flatMap { + fileTree(it) { + include("**/treelib/**") + exclude( + "**/singleObjects/**", + "**/RBVertex.class", + "**/AVLVertex.class", + "**/BINVertex.class", + "**/Vertex.class" + ) + } + }) dependsOn(tasks.jacocoTestReport) violationRules { rule { diff --git a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt index 3b1c7d9..2914ce3 100644 --- a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt @@ -1,7 +1,7 @@ package databaseManage import databaseSave.sqlite.DrawAVLVertex -import databaseSave.sqlite.SQLiteRepository +import databaseSave.sqlite.SQLiteRepositoryJDBC import treelib.avlTree.AVLStruct import treelib.avlTree.AVLVertex import java.io.Closeable @@ -12,7 +12,7 @@ class AVLTreeManager>( private val serializeData: (input: Pack) -> String, private val deSerializeData: (input: String) -> Pack, ) : Closeable { - private val db = SQLiteRepository(dbPath, serializeData, deSerializeData) + private val db = SQLiteRepositoryJDBC(dbPath, serializeData, deSerializeData) private var avlTree = AVLStruct() diff --git a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt new file mode 100644 index 0000000..81477c9 --- /dev/null +++ b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt @@ -0,0 +1,113 @@ +package databaseSave.sqlite + +import databaseSave.sqlite.vertexEntities.VertexTableEntity +import databaseSave.sqlite.vertexEntities.VertexTable +import databaseSave.sqlite.treeEntities.TreeTableEntity +import databaseSave.sqlite.treeEntities.TreesTable +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.transactions.transaction +import java.sql.SQLException + +class SQLiteRepositoryExposed( + private val dbPath: String = "exposed_database.db" +) { + val db = Database.connect("jdbc:sqlite:$dbPath", driver = "org.sqlite.JDBC") + + init { + if (!isEmptyDB()) createTables() + } + + private fun createTables() { + transaction(db) { + when { + !TreesTable.exists() && !VertexTable.exists() -> { + SchemaUtils.create(TreesTable) + SchemaUtils.create(VertexTable) + } + + !TreesTable.exists() -> SchemaUtils.create(TreesTable) + !VertexTable.exists() -> SchemaUtils.create(VertexTable) + else -> {} + } + } + } + + fun > save( + treeName: String, + vertexes: MutableList>, + serializeData: (input: Pack) -> String, + ): Unit = transaction(db) { + interDelete(treeName) + + val id = TreeTableEntity.new { + name = treeName + } + + for (index in vertexes.indices) VertexTableEntity.new { + height = vertexes[index].height + value = serializeData(vertexes[index].value) + order = index + x = vertexes[index].x + y = vertexes[index].y + tree = id.id.value + } + } + + fun getAllSavedTrees(): List = transaction(db) { + return@transaction TreeTableEntity.all().map { el -> el.name } + } + + fun > getTree( + name: String, + deSerializeData: (input: String) -> Pack, + ): MutableList> = transaction(db) { + val ans = mutableListOf>() + val treeId = interIsTreeExist(name) ?: throw SQLException("Tree doesn't exist") + + for (el in VertexTableEntity.find(VertexTable.tree eq treeId).orderBy(VertexTable.order to SortOrder.ASC)){ + ans.add( + DrawAVLVertex( + value = deSerializeData(el.value), + height = el.height, + x = el.x, + y = el.y + ) + ) + } + return@transaction ans + } + + fun delete(name: String): Boolean = + transaction(db) { + val treeId = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return@transaction false + TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.delete() + + VertexTableEntity.find(VertexTable.tree eq treeId.value).firstOrNull() ?: return@transaction false + VertexTable.deleteWhere { tree eq treeId.value } + return@transaction true + } + + fun isEmptyDB(): Boolean = transaction(db) { + if (TreesTable.exists() && VertexTable.exists()) return@transaction true + else false + } + + fun isTreeExist(name: String): Boolean = transaction(db) { + return@transaction TreeTableEntity.find(TreesTable.name eq name).firstOrNull() != null + } + + private fun interIsTreeExist(name: String): Int? { + val id = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return null + return id.value + } + + private fun interDelete(name: String) { + val treeId = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return + TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.delete() + + VertexTableEntity.find(VertexTable.tree eq treeId.value).firstOrNull() ?: return + VertexTable.deleteWhere { tree eq treeId.value } + return + } +} diff --git a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepository.kt b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt similarity index 99% rename from lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepository.kt rename to lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt index 7882502..061fa7e 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepository.kt +++ b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt @@ -4,7 +4,7 @@ import java.io.Closeable import java.sql.DriverManager import java.sql.SQLException -class SQLiteRepository>( +class SQLiteRepositoryJDBC>( private val dbPath: String, private val serializeData: (input: Pack) -> String, private val deSerializeData: (input: String) -> Pack, diff --git a/lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreeTableEntity.kt b/lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreeTableEntity.kt new file mode 100644 index 0000000..bd3d030 --- /dev/null +++ b/lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreeTableEntity.kt @@ -0,0 +1,11 @@ +package databaseSave.sqlite.treeEntities + +import org.jetbrains.exposed.dao.IntEntity +import org.jetbrains.exposed.dao.IntEntityClass +import org.jetbrains.exposed.dao.id.EntityID + +class TreeTableEntity(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(TreesTable) + + var name by TreesTable.name +} diff --git a/lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreesTable.kt b/lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreesTable.kt new file mode 100644 index 0000000..3bfecf4 --- /dev/null +++ b/lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreesTable.kt @@ -0,0 +1,13 @@ +package databaseSave.sqlite.treeEntities + +import org.jetbrains.exposed.dao.id.IntIdTable + + +object TreesTable : IntIdTable("trees") { + + var name = text("name") + + init { + uniqueIndex(name) + } +} diff --git a/lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTable.kt b/lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTable.kt new file mode 100644 index 0000000..3f878fc --- /dev/null +++ b/lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTable.kt @@ -0,0 +1,13 @@ +package databaseSave.sqlite.vertexEntities + +import org.jetbrains.exposed.dao.id.IntIdTable + + +object VertexTable : IntIdTable("vertex") { + var height = integer("height") + var value = text("data") + var order = integer("orderId") + var x = double("xCord") + var y = double("yCord") + var tree = integer("treeId") +} diff --git a/lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt b/lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt new file mode 100644 index 0000000..4c9d6ac --- /dev/null +++ b/lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt @@ -0,0 +1,16 @@ +package databaseSave.sqlite.vertexEntities + +import org.jetbrains.exposed.dao.IntEntity +import org.jetbrains.exposed.dao.IntEntityClass +import org.jetbrains.exposed.dao.id.EntityID + +class VertexTableEntity(id: EntityID) : IntEntity(id) { + companion object : IntEntityClass(VertexTable) + + var height by VertexTable.height + var value by VertexTable.value + var order by VertexTable.order + var x by VertexTable.x + var y by VertexTable.y + var tree by VertexTable.tree +} From b4dc42dfadd15ace7a642f2d05b889fa8116681e Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 24 Apr 2023 01:38:01 +0300 Subject: [PATCH 152/212] feat: Implement TreeManager interface for interacting with controller --- .../kotlin/databaseManage/BINTreeManager.kt | 27 +++++++++++-------- .../kotlin/databaseManage/RBTreeManager.kt | 26 +++++++++--------- .../main/kotlin/databaseManage/TreeManager.kt | 15 ++++++++++- .../jsonFormat/DrawableBINVertex.kt | 7 ++--- .../databaseSave/neo4j/DrawableRBVertex.kt | 9 ++++--- 5 files changed, 52 insertions(+), 32 deletions(-) diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index bcc0d00..be85f8c 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -3,11 +3,10 @@ package databaseManage import com.google.gson.reflect.TypeToken import databaseSave.jsonFormat.DrawableBINVertex import databaseSave.jsonFormat.JsonRepository -import treelib.binTree.BINStruct import treelib.commonObjects.Container import java.io.File -class BINTreeManager { +class BINTreeManager : TreeManager, DrawableBINVertex>> { /*** using json format files ***/ @@ -15,27 +14,33 @@ class BINTreeManager { private val jsonRep = JsonRepository(dirPath) - fun initTree(treeName: String): BINStruct> { + override fun getTree(treeName: String): Pair>>, List>>> { val typeToken = object : TypeToken>>>() {} - val preOrder = jsonRep.exportTree(treeName, typeToken) - val BINtree = BINStruct>() - BINtree.restoreStruct(preOrder.toList()) + val preOrder = jsonRep.exportTree(treeName, typeToken).toList() - return BINtree + return Pair(preOrder, listOf()) } - fun > saveTree(preOrder: Array>, treeName: String) { + override fun saveTree( + preOrder: Array>>, + inOrder: Array>>, + treeName: String + ) { jsonRep.saveChanges(preOrder, treeName) } - fun deleteTree(treeName: String) = jsonRep.removeTree(treeName) + override fun deleteTree(treeName: String) = jsonRep.removeTree(treeName) - fun getNamesTrees(): List? { + override fun getSavedTreesNames(): List { val filesNames = File(dirPath).list()?.map { it.replace(".json", "") } - return filesNames?.subList(0, 3) + return filesNames?.subList(0, 3) ?: listOf() + } + + override fun isTreeExist(): Boolean { + TODO() } fun cleanDB() = jsonRep.clean() diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt index ca52d49..c8c0bd2 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -3,9 +3,8 @@ package databaseManage import databaseSave.neo4j.DrawableRBVertex import databaseSave.neo4j.Neo4jRepository import treelib.commonObjects.Container -import treelib.rbTree.RBStruct -class RBTreeManager { +class RBTreeManager : TreeManager, DrawableRBVertex>> { /*** using neo4j ***/ @@ -15,21 +14,18 @@ class RBTreeManager { neo4jDB.open("bolt://localhost:7687", "neo4j", "password") } - fun initTree(treeName: String): RBStruct> { + override fun getTree(treeName: String): Pair>>, List>>> { /*** orders.first = preOrder, orders.second = inOrder ***/ val orders: Pair>>, List>>> = neo4jDB.exportRBtree(treeName) - - val RBtree = RBStruct>() - RBtree.restoreStruct(orders.first, orders.second) neo4jDB.close() - return RBtree + return orders } - fun > saveTree( - preOrder: Array>, - inOrder: Array>, + override fun saveTree( + preOrder: Array>>, + inOrder: Array>>, treeName: String ) { @@ -37,16 +33,20 @@ class RBTreeManager { neo4jDB.close() } - fun deleteTree(treeName: String) { + override fun deleteTree(treeName: String) { neo4jDB.removeTree(treeName) neo4jDB.close() } - fun getNamesTrees(): List? { + override fun getSavedTreesNames(): List { val treesNames = neo4jDB.findNamesTrees() - return treesNames?.subList(0, 3) + return treesNames?.subList(0, 3) ?: listOf() + } + + override fun isTreeExist(): Boolean { + TODO() } fun cleanDB() { diff --git a/lib/src/main/kotlin/databaseManage/TreeManager.kt b/lib/src/main/kotlin/databaseManage/TreeManager.kt index 819d058..0db64f8 100644 --- a/lib/src/main/kotlin/databaseManage/TreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/TreeManager.kt @@ -1,4 +1,17 @@ package databaseManage -abstract class TreeManager { +import databaseSave.DrawableVertex +import treelib.commonObjects.Container + +interface TreeManager, DrawableVertexType : DrawableVertex> { + + fun getTree(treeName: String): Pair, List> + + fun saveTree(preOrder: Array, inOrder: Array, treeName: String) + + fun deleteTree(treeName: String) + + fun getSavedTreesNames(): List + + fun isTreeExist(): Boolean } \ No newline at end of file diff --git a/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt b/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt index 339d29d..06458aa 100644 --- a/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt +++ b/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt @@ -1,9 +1,10 @@ package databaseSave.jsonFormat +import databaseSave.DrawableVertex import treelib.binTree.BINVertex class DrawableBINVertex>( value: Pack, - val x: Double = 0.0, - val y: Double = 0.0 -) : BINVertex(value) \ No newline at end of file + override val x: Double = 0.0, + override val y: Double = 0.0 +) : BINVertex(value), DrawableVertex \ No newline at end of file diff --git a/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt b/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt index 904c8a8..0e32a6d 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt @@ -1,11 +1,12 @@ package databaseSave.neo4j -import treelib.rbTree.RBVertex +import databaseSave.DrawableVertex import treelib.rbTree.Markers +import treelib.rbTree.RBVertex class DrawableRBVertex>( value: Pack, color: Markers, - val x: Double = 0.0, - val y: Double = 0.0 -) : RBVertex(value, color) + override val x: Double = 0.0, + override val y: Double = 0.0 +) : RBVertex(value, color), DrawableVertex From e08142ba30b086e24f646f8f8178bb5a94ba432d Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 24 Apr 2023 01:55:00 +0300 Subject: [PATCH 153/212] refactor: Remove warnings after intellij hints --- lib/build.gradle.kts | 24 +++++++++++++------ .../main/kotlin/treelib/rbTree/RBBalancer.kt | 4 ++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 4fb50d1..7849257 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -28,6 +28,7 @@ dependencies { implementation("com.google.code.gson:gson:2.8.5") + // Neo4j val neo4jCore = "4.0.5" implementation("org.neo4j", "neo4j-ogm-core", neo4jCore) implementation("org.neo4j", "neo4j-ogm-bolt-driver", neo4jCore) @@ -70,9 +71,9 @@ tasks.test { val failedTests = mutableListOf() val skippedTests = mutableListOf() - addTestListener (object: TestListener { - override fun beforeSuite(suite: TestDescriptor?) { } - override fun beforeTest(testDescriptor: TestDescriptor?) { } + addTestListener(object : TestListener { + override fun beforeSuite(suite: TestDescriptor?) {} + override fun beforeTest(testDescriptor: TestDescriptor?) {} override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { when (result.resultType) { TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor) @@ -80,6 +81,7 @@ tasks.test { else -> {} } } + override fun afterSuite(suite: TestDescriptor, result: TestResult) { if (suite.parent == null) { // root suite logger.lifecycle("####################################################################################") @@ -109,10 +111,18 @@ tasks.jacocoTestReport { } tasks.jacocoTestCoverageVerification { - classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { - include("**/treelib/**") - exclude("**/singleObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class") - } }) + classDirectories.setFrom(classDirectories.files.flatMap { + fileTree(it) { + include("**/treelib/**") + exclude( + "**/commonObjects/**", + "**/RBVertex.class", + "**/AVLVertex.class", + "**/BINVertex.class", + "**/Vertex.class" + ) + } + }) dependsOn(tasks.jacocoTestReport) violationRules { rule { diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index e2f609c..0667d9d 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -40,8 +40,8 @@ class RBBalancer>(private var root: RBNode?) : return node.right == null && node.left == null } - override fun balance(stateContainer: RBStateContainer): RBNode { - val node = stateContainer.contentNode + override fun balance(state: RBStateContainer): RBNode { + val node = state.contentNode ?: throw InternalError() val uncle = getUncle(node) when { From 8bb0968ba2dd9ff1e8f8cd2331375acd93acffb2 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 24 Apr 2023 20:48:27 +0300 Subject: [PATCH 154/212] feat: Redesign interface TreeManager; Implement AVLTreeManager --- .../kotlin/databaseManage/AVLTreeManager.kt | 90 ++++++++++++------- .../main/kotlin/databaseManage/TreeManager.kt | 20 +++-- 2 files changed, 74 insertions(+), 36 deletions(-) diff --git a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt index 2914ce3..2319233 100644 --- a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt @@ -1,58 +1,88 @@ package databaseManage import databaseSave.sqlite.DrawAVLVertex -import databaseSave.sqlite.SQLiteRepositoryJDBC +import databaseSave.sqlite.SQLiteRepositoryExposed +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import treelib.avlTree.AVLStruct import treelib.avlTree.AVLVertex -import java.io.Closeable +import treelib.commonObjects.Container -class AVLTreeManager>( - private var treeName: String, - private val dbPath: String, - private val serializeData: (input: Pack) -> String, - private val deSerializeData: (input: String) -> Pack, -) : Closeable { - private val db = SQLiteRepositoryJDBC(dbPath, serializeData, deSerializeData) - private var avlTree = AVLStruct() +const val AVL_DEFAULT_NAME = "NewTreeAVL" +const val DB_DEFAULT_NAME = "avlDB.db" +class AVLTreeManager: TreeManager, DrawAVLVertex>> { + private val db = SQLiteRepositoryExposed() + private var avlTree = AVLStruct>() - private fun drawVertexToVertex(drawVertex: MutableList>): MutableList> { + override var currentTreeName = AVL_DEFAULT_NAME + private set + + var dataBaseName = DB_DEFAULT_NAME + private set + + private fun drawVertexToVertex(drawVertex: MutableList>>): MutableList>> { //TODO: Rewrite while working on GUI - val ans = mutableListOf>() + 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> { + 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)) + val ans = mutableListOf>>() + for (el in drawVertex) ans.add(DrawAVLVertex(value = el.value, height = el.height.toInt(), x = -0.0, y = -0.0)) return ans } - fun initTree() { + private fun serialization(data: Container): String = Json.encodeToString(data) + + private fun deserialization(data: String): Container = + Json.decodeFromString>(data) + + fun initDatabase(dbName: String) { + db.initDataBase(dbName) + dataBaseName = dbName + } + + override fun initTree(treeName: String): MutableList>> { avlTree = AVLStruct() - if (db.getTreeId(treeName) == 0) { - db.addTree(treeName) - } else { - avlTree.restoreStruct(drawVertexToVertex(db.getAllVertexes(treeName))) + currentTreeName = treeName + + if (db.isTreeExist(treeName)) { + val vertexes = db.getTree(treeName, ::deserialization) + avlTree.restoreStruct(drawVertexToVertex(vertexes)) + return vertexes } - db.addTree(treeName) + return mutableListOf() } - fun saveTree() { - db.addVertexes(vertexToDrawVertex(avlTree.preOrder()), treeName) + override fun getVertexesForDrawFromTree(): MutableList>> { + return vertexToDrawVertex(avlTree.preOrder()) } - fun deleteTree() { - if (db.getTreeId(treeName) != 0) { - db.deleteTree(treeName) - } + override fun getVertexesForDrawFromDB(): MutableList>> { + return db.getTree(currentTreeName, ::deserialization) + } + + override fun saveTree( + vertexes: MutableList>>, + inOrder: MutableList>> + ) { + db.deleteTree(currentTreeName) + db.saveTree(currentTreeName, vertexes, ::serialization) } - fun insert(item: Pack) = avlTree.insert(item) + override fun deleteTree() { + db.deleteTree(currentTreeName) + } + + override fun getSavedTreesNames() = db.getAllSavedTrees() - fun delete(item: Pack) = avlTree.delete(item) + override fun insert(item: Container) = avlTree.insert(item) - override fun close() = db.close() + override fun delete(item: Container) { + if (avlTree.find(item) != null) avlTree.delete(item) + } } diff --git a/lib/src/main/kotlin/databaseManage/TreeManager.kt b/lib/src/main/kotlin/databaseManage/TreeManager.kt index 0db64f8..6508c7f 100644 --- a/lib/src/main/kotlin/databaseManage/TreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/TreeManager.kt @@ -3,15 +3,23 @@ package databaseManage import databaseSave.DrawableVertex import treelib.commonObjects.Container -interface TreeManager, DrawableVertexType : DrawableVertex> { +interface TreeManager, DrawableVertexType : DrawableVertex> { - fun getTree(treeName: String): Pair, List> + val currentTreeName: String - fun saveTree(preOrder: Array, inOrder: Array, treeName: String) + fun initTree(treeName: String): MutableList - fun deleteTree(treeName: String) + fun getVertexesForDrawFromTree(): MutableList + + fun getVertexesForDrawFromDB(): MutableList + + fun saveTree(vertexes: MutableList, inOrder: MutableList) + + fun deleteTree() fun getSavedTreesNames(): List - fun isTreeExist(): Boolean -} \ No newline at end of file + fun insert(item: Container) + + fun delete(item: Container) +} From caed218a3c89d8e928a4c4b93fe3e70ff9d9e420 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 24 Apr 2023 20:50:48 +0300 Subject: [PATCH 155/212] refactor: Redesign SQLiteRepositoryExposed --- .../sqlite/SQLiteRepositoryExposed.kt | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt index 81477c9..b6d4404 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt +++ b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt @@ -9,15 +9,20 @@ import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.transactions.transaction import java.sql.SQLException -class SQLiteRepositoryExposed( - private val dbPath: String = "exposed_database.db" -) { - val db = Database.connect("jdbc:sqlite:$dbPath", driver = "org.sqlite.JDBC") +class SQLiteRepositoryExposed { + private var db: Database? = null + var dbName: String? = null + private set + + fun initDataBase(name: String) { + if ((dbName != null) && (dbName == name)) return + + db = Database.connect("jdbc:sqlite:$name", driver = "org.sqlite.JDBC") - init { if (!isEmptyDB()) createTables() } + private fun createTables() { transaction(db) { when { @@ -33,7 +38,21 @@ class SQLiteRepositoryExposed( } } - fun > save( + private fun interIsTreeExist(name: String): Int? { + val id = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return null + return id.value + } + + private fun interDelete(name: String) { + val treeId = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return + TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.delete() + + VertexTableEntity.find(VertexTable.tree eq treeId.value).firstOrNull() ?: return + VertexTable.deleteWhere { tree eq treeId.value } + return + } + + fun > saveTree( treeName: String, vertexes: MutableList>, serializeData: (input: Pack) -> String, @@ -78,7 +97,7 @@ class SQLiteRepositoryExposed( return@transaction ans } - fun delete(name: String): Boolean = + fun deleteTree(name: String): Boolean = transaction(db) { val treeId = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return@transaction false TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.delete() @@ -96,18 +115,4 @@ class SQLiteRepositoryExposed( fun isTreeExist(name: String): Boolean = transaction(db) { return@transaction TreeTableEntity.find(TreesTable.name eq name).firstOrNull() != null } - - private fun interIsTreeExist(name: String): Int? { - val id = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return null - return id.value - } - - private fun interDelete(name: String) { - val treeId = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return - TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.delete() - - VertexTableEntity.find(VertexTable.tree eq treeId.value).firstOrNull() ?: return - VertexTable.deleteWhere { tree eq treeId.value } - return - } } From 94593bb6778b6c2fab519c75651243d422cc031b Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 24 Apr 2023 21:06:34 +0300 Subject: [PATCH 156/212] feat: Implement searching file in neo4j and json repo --- .gitignore | 1 + .../main/kotlin/databaseManage/BINTreeManager.kt | 8 ++++++-- .../main/kotlin/databaseManage/RBTreeManager.kt | 15 +++++++++++++++ .../kotlin/databaseSave/neo4j/Neo4jRepository.kt | 13 ++++++++++++- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index f2de55c..d1fbe5a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /gradle/ /neo4jDB/ /jsonFormat/ +/jsonFormatFiles/ diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index be85f8c..d50dad9 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -36,11 +36,15 @@ class BINTreeManager : TreeManager, DrawableBINVertex { val filesNames = File(dirPath).list()?.map { it.replace(".json", "") } - return filesNames?.subList(0, 3) ?: listOf() + return if ((filesNames?.size ?: 0) > 3) filesNames?.subList(0, 3) ?: listOf() else filesNames ?: listOf() } override fun isTreeExist(): Boolean { - TODO() + TODO("Not yet implemented") + } + + fun isTreeExist(treeName: String): Boolean { + return File(dirPath, treeName).exists() } fun cleanDB() = jsonRep.clean() diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt index c8c0bd2..22f655c 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -3,6 +3,7 @@ package databaseManage import databaseSave.neo4j.DrawableRBVertex import databaseSave.neo4j.Neo4jRepository import treelib.commonObjects.Container +import java.io.File class RBTreeManager : TreeManager, DrawableRBVertex>> { @@ -14,6 +15,7 @@ class RBTreeManager : TreeManager, DrawableRBVertex>>, List>>> { /*** orders.first = preOrder, orders.second = inOrder ***/ val orders: Pair>>, List>>> = @@ -42,6 +44,15 @@ class RBTreeManager : TreeManager, DrawableRBVertex { val treesNames = neo4jDB.findNamesTrees() + val dirPath = System.getProperty("user.dir") + "/neo4jDB/neo4jFormatFiles" + File(dirPath).mkdirs() + if (treesNames != null) { + for (name in treesNames) { + File(dirPath, name).run { + createNewFile() + } + } + } return treesNames?.subList(0, 3) ?: listOf() } @@ -49,6 +60,10 @@ class RBTreeManager : TreeManager, DrawableRBVertex? = null session.executeRead { tx -> treesNames = tx.run("MATCH (n: Node) WHERE NOT(:Node)-->(n) RETURN n.treeName") - .list().map { it.toString() }.filter { it != "null" } + .list().map { it.values()[0].toString().replace("\"", "") }.filter { it != "null" } } return treesNames @@ -240,6 +240,17 @@ class Neo4jRepository : Closeable { ) } + fun findTree(treeName: String): Boolean { + val session = driver?.session() ?: throw IOException() + var name: String = "NULL" + + session.executeRead { tx -> + name = tx.run("MATCH (n: Node {treeName: \$treeName}) WHERE NOT (:Node)-->(n) RETURN n.treeName").list()[0].values()[0].toString().replace("\"", "") + } + return name == treeName + + } + override fun close() { driver?.close() } From 719d44382bb54526b47ed94a8135858df5c1733b Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 24 Apr 2023 21:08:01 +0300 Subject: [PATCH 157/212] feat: Implement Controller sketch --- lib/src/main/kotlin/controller/Controller.kt | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 lib/src/main/kotlin/controller/Controller.kt diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt new file mode 100644 index 0000000..e70246a --- /dev/null +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -0,0 +1,37 @@ +package controller + +import databaseManage.BINTreeManager +import databaseManage.RBTreeManager +import java.io.IOException + +class Controller { + //val avlManager = AVLTreeManager() + private val rbManager = RBTreeManager() + private val binManager = BINTreeManager() + + fun showFiles(): List> { + return listOf(rbManager.getSavedTreesNames(), listOf(), binManager.getSavedTreesNames()) + } + + fun foo(fileName: String) { + if (fileName == "myTree.json") + throw IOException() + } + +} + +/* +, NodeType : Node, State : StateContainer, + VertexType : Vertex, TreeType : TreeStruct> + */ + +/* + val RBtree = RBStruct>() + RBtree.restoreStruct(orders.first, orders.second) + neo4jDB.close() +*/ + +/* +val BINtree = BINStruct>() + BINtree.restoreStruct(preOrder.toList()) + */ \ No newline at end of file From e9a9df740941f0458c4a7cf4c6f386765f419bf9 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 24 Apr 2023 21:08:58 +0300 Subject: [PATCH 158/212] feat: Implement some MenuItems --- lib/src/main/kotlin/main.kt | 69 +++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index 9a994dc..b53356a 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -21,7 +21,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.MenuBar import androidx.compose.ui.window.Window import androidx.compose.ui.window.application +import controller.Controller import java.awt.Dimension +import javax.swing.JFileChooser // я передумал, будет три маленьких кнопки где-нибудь @OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) @@ -32,28 +34,58 @@ fun main() = application { title = "LESOK", ) { this.window.minimumSize = Dimension(800, 600) - val treeNames = listOf("RED BLACK TREE", "AVL TREE", "BINARY TREE") + val treeNames = listOf("Red black tree", "AVL tree", "Binary tree") + + val dirPath = System.getProperty("user.dir") + val dirFiles = listOf("$dirPath/neo4jDB/neo4jFormatFiles", dirPath, "$dirPath/jsonFormatFiles") + + val controller = Controller() + val showFiles = controller.showFiles() + MenuBar { - Menu(text = "File", mnemonic = 'T') { - Menu(text = "Open") { - Item("Red Black tree", onClick = {}) - Item("AVL tree", onClick = {}) - Item("Binary tree", onClick = {}) - } - Menu(text = "Create") { - Item("Red Black tree", onClick = {}) - Item("AVL tree", onClick = {}) - Item("Binary tree", onClick = {}) + Menu(text = "Open") { + repeat(3) { indexTree -> + Menu(treeNames[indexTree]) { + // поле для самостоятельного ввода имени + Item("Search") { + val fd = JFileChooser(dirFiles[indexTree]) + fd.isMultiSelectionEnabled = false + fd.fileSelectionMode = JFileChooser.FILES_AND_DIRECTORIES + fd.isFileHidingEnabled = false + if (fd.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { + val name = fd.selectedFile + } + + + } + repeat(showFiles[indexTree].size) { index -> + Item( + showFiles[indexTree][index], + onClick = { }) // контроллер создает новое дерево и начинает работу с ним + } + } } } + Menu(text = " Create") { + Item("Red Black tree", onClick = {}) // контроллер создает новое дерево и начинает работу с ним + Item("AVL tree", onClick = {}) + Item("Binary tree", onClick = {}) + } + + Menu(text = "Save & delete") { + Item("Save as", onClick = {}) + Item("Delete", onClick = {}) + } } + Box(modifier = Modifier.background(Color(234, 231, 220)).fillMaxSize()) { - Column(verticalArrangement = Arrangement.Center, + Column( + verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth(0.2f).fillMaxHeight(0.3f) ) { val options = listOf("INSERT", "DELETE", "FIND") - repeat(3) {index -> + repeat(3) { index -> Row( horizontalArrangement = Arrangement.Center, modifier = Modifier.weight(1f) @@ -86,6 +118,7 @@ fun main() = application { true // вот тут походу отправляем запрос в контроллер } + else -> false } } @@ -101,5 +134,13 @@ fun main() = application { } } } - // .wrapContentSize() + +/* +val fd = FileDialog(this@Window.window, "Open", FileDialog.LOAD) +fd.isVisible = true +fd.file = dirFiles[indexTree] ?: dirPath +val fileName = fd.file ?: "" +if (fileName != "") + controller.foo(fileName) +*/ \ No newline at end of file From d5e0e94266df4e611cc0c502290d4ca8ebee2afb Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 25 Apr 2023 01:32:47 +0300 Subject: [PATCH 159/212] feat: Implement common methods of Managers classes --- .../kotlin/databaseManage/AVLTreeManager.kt | 42 ++++++++----- .../kotlin/databaseManage/BINTreeManager.kt | 57 ++++++++++++----- .../kotlin/databaseManage/RBTreeManager.kt | 62 +++++++++++++------ .../main/kotlin/databaseManage/TreeManager.kt | 8 +-- ...{DrawAVLVertex.kt => DrawableAVLVertex.kt} | 0 5 files changed, 118 insertions(+), 51 deletions(-) rename lib/src/main/kotlin/databaseSave/sqlite/{DrawAVLVertex.kt => DrawableAVLVertex.kt} (100%) diff --git a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt index 2319233..99cb887 100644 --- a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt @@ -1,6 +1,6 @@ package databaseManage -import databaseSave.sqlite.DrawAVLVertex +import databaseSave.sqlite.DrawableAVLVertex import databaseSave.sqlite.SQLiteRepositoryExposed import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString @@ -8,31 +8,32 @@ import kotlinx.serialization.json.Json import treelib.avlTree.AVLStruct import treelib.avlTree.AVLVertex import treelib.commonObjects.Container +import java.io.File const val AVL_DEFAULT_NAME = "NewTreeAVL" const val DB_DEFAULT_NAME = "avlDB.db" -class AVLTreeManager: TreeManager, DrawAVLVertex>> { +class AVLTreeManager: TreeManager, DrawableAVLVertex>> { private val db = SQLiteRepositoryExposed() private var avlTree = AVLStruct>() override var currentTreeName = AVL_DEFAULT_NAME private set - var dataBaseName = DB_DEFAULT_NAME + private var dataBaseName = DB_DEFAULT_NAME private set - private fun drawVertexToVertex(drawVertex: MutableList>>): MutableList>> { + 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>> { + 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 = -0.0, y = -0.0)) + val ans = mutableListOf>>() + for (el in drawVertex) ans.add(DrawableAVLVertex(value = el.value, height = el.height.toInt(), x = -0.0, y = -0.0)) return ans } @@ -46,7 +47,7 @@ class AVLTreeManager: TreeManager, DrawAVLVertex>> { + override fun initTree(treeName: String): MutableList>> { avlTree = AVLStruct() currentTreeName = treeName @@ -58,27 +59,40 @@ class AVLTreeManager: TreeManager, DrawAVLVertex>> { + override fun getVertexesForDrawFromTree(): MutableList>> { return vertexToDrawVertex(avlTree.preOrder()) } - override fun getVertexesForDrawFromDB(): MutableList>> { + override fun getVertexesForDrawFromDB(): MutableList>> { return db.getTree(currentTreeName, ::deserialization) } override fun saveTree( - vertexes: MutableList>>, - inOrder: MutableList>> + preOrder: List>>, + inOrder: List>> ) { db.deleteTree(currentTreeName) - db.saveTree(currentTreeName, vertexes, ::serialization) + db.saveTree(currentTreeName, preOrder.toMutableList(), ::serialization) } override fun deleteTree() { db.deleteTree(currentTreeName) } - override fun getSavedTreesNames() = db.getAllSavedTrees() + override fun getSavedTreesNames(): List { + val treesNames = db.getAllSavedTrees() + val dirPath = System.getProperty("user.dir") + "/saved-trees/AVL-trees" + File(dirPath).mkdirs() + if (treesNames.isNotEmpty()) { + for (name in treesNames) { + File(dirPath, name).run { + createNewFile() + } + } + } + + return treesNames.subList(0, treesNames.size) + } override fun insert(item: Container) = avlTree.insert(item) diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index d50dad9..42e5132 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -3,6 +3,7 @@ package databaseManage import com.google.gson.reflect.TypeToken import databaseSave.jsonFormat.DrawableBINVertex import databaseSave.jsonFormat.JsonRepository +import treelib.binTree.BINStruct import treelib.commonObjects.Container import java.io.File @@ -10,43 +11,69 @@ class BINTreeManager : TreeManager, DrawableBINVertex>() + + override fun initTree(treeName: String): List>> { + binTree = BINStruct() + currentTreeName = treeName + + if (this.isTreeExist(treeName)) { + val typeToken = object : TypeToken>>>() {} + val preOrder = jsonRep.exportTree(treeName, typeToken).toList() + + binTree.restoreStruct(preOrder.toList()) - override fun getTree(treeName: String): Pair>>, List>>> { - val typeToken = object : TypeToken>>>() {} - val preOrder = jsonRep.exportTree(treeName, typeToken).toList() + return preOrder + } - return Pair(preOrder, listOf()) + return listOf() } override fun saveTree( - preOrder: Array>>, - inOrder: Array>>, - treeName: String + preOrder: List>>, + inOrder: List>> ) { - jsonRep.saveChanges(preOrder, treeName) + jsonRep.saveChanges(preOrder.toTypedArray(), currentTreeName) } - override fun deleteTree(treeName: String) = jsonRep.removeTree(treeName) + override fun deleteTree() = jsonRep.removeTree(currentTreeName) override fun getSavedTreesNames(): List { val filesNames = File(dirPath).list()?.map { it.replace(".json", "") } - return if ((filesNames?.size ?: 0) > 3) filesNames?.subList(0, 3) ?: listOf() else filesNames ?: listOf() + return filesNames?.subList(0, filesNames.size) ?: listOf() } - override fun isTreeExist(): Boolean { - TODO("Not yet implemented") + private fun isTreeExist(treeName: String): Boolean { + return File(dirPath, treeName).exists() } - fun isTreeExist(treeName: String): Boolean { - return File(dirPath, treeName).exists() + override fun delete(item: Container) { + if (binTree.find(item) != null) + binTree.delete(item) } + override fun insert(item: Container) = binTree.insert(item) + + override fun getVertexesForDrawFromDB(): List>> { + TODO("Not yet implemented") + } + + override fun getVertexesForDrawFromTree(): List>> = + binTree.preOrder().map { DrawableBINVertex(it.value) } + fun cleanDB() = jsonRep.clean() + companion object { + private const val BIN_DEFAULT_NAME = "NewTreeBIN" + } + } \ No newline at end of file diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt index 22f655c..aacb20b 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -3,48 +3,56 @@ package databaseManage import databaseSave.neo4j.DrawableRBVertex import databaseSave.neo4j.Neo4jRepository import treelib.commonObjects.Container +import treelib.rbTree.RBStruct import java.io.File class RBTreeManager : TreeManager, DrawableRBVertex>> { - /*** using neo4j ***/ + override var currentTreeName = RB_DEFAULT_NAME + private set private val neo4jDB = Neo4jRepository() + private var rbTree = RBStruct>() init { neo4jDB.open("bolt://localhost:7687", "neo4j", "password") } - // добавить проверку на существование дерева - override fun getTree(treeName: String): Pair>>, List>>> { + override fun initTree(treeName: String): List>> { /*** orders.first = preOrder, orders.second = inOrder ***/ - val orders: Pair>>, List>>> = - neo4jDB.exportRBtree(treeName) - neo4jDB.close() + rbTree = RBStruct() + currentTreeName = treeName + + if (this.isTreeExist(treeName)) { + val orders: Pair>>, List>>> = + neo4jDB.exportRBtree(treeName) + rbTree.restoreStruct(orders.first, orders.second) + neo4jDB.close() + return orders.first + } - return orders + return listOf() } override fun saveTree( - preOrder: Array>>, - inOrder: Array>>, - treeName: String + preOrder: List>>, + inOrder: List>> ) { - neo4jDB.saveChanges(preOrder, inOrder, treeName) + neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray(), currentTreeName) neo4jDB.close() } - override fun deleteTree(treeName: String) { + override fun deleteTree() { - neo4jDB.removeTree(treeName) + neo4jDB.removeTree(currentTreeName) neo4jDB.close() } override fun getSavedTreesNames(): List { val treesNames = neo4jDB.findNamesTrees() - val dirPath = System.getProperty("user.dir") + "/neo4jDB/neo4jFormatFiles" + val dirPath = System.getProperty("user.dir") + "/saved-trees/RB-trees" File(dirPath).mkdirs() if (treesNames != null) { for (name in treesNames) { @@ -53,14 +61,28 @@ class RBTreeManager : TreeManager, DrawableRBVertex) = rbTree.insert(item) + + override fun insert(item: Container) { + if (rbTree.find(item) != null) + rbTree.delete(item) + } - override fun isTreeExist(): Boolean { - TODO() + override fun getVertexesForDrawFromDB(): List>> { + return neo4jDB.exportRBtree(currentTreeName).first.map { DrawableRBVertex(it.value, it.color) } } - fun isTreeExist(treeName: String): Boolean { + override fun getVertexesForDrawFromTree(): List>> { + return rbTree.preOrder().map { DrawableRBVertex(it.value, it.color) } + } + + + + private fun isTreeExist(treeName: String): Boolean { return neo4jDB.findTree(treeName) } @@ -70,4 +92,8 @@ class RBTreeManager : TreeManager, DrawableRBVertex, DrawableVertexType : Drawabl val currentTreeName: String - fun initTree(treeName: String): MutableList + fun initTree(treeName: String): List - fun getVertexesForDrawFromTree(): MutableList + fun getVertexesForDrawFromTree(): List - fun getVertexesForDrawFromDB(): MutableList + fun getVertexesForDrawFromDB(): List - fun saveTree(vertexes: MutableList, inOrder: MutableList) + fun saveTree(preOrder: List, inOrder: List) fun deleteTree() diff --git a/lib/src/main/kotlin/databaseSave/sqlite/DrawAVLVertex.kt b/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt similarity index 100% rename from lib/src/main/kotlin/databaseSave/sqlite/DrawAVLVertex.kt rename to lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt From 2293c52a36b6f21119863c317f1b0e129c4fbfb0 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 25 Apr 2023 01:35:40 +0300 Subject: [PATCH 160/212] fix: Change some input parameters --- .../databaseSave/neo4j/Neo4jRepository.kt | 2 +- .../databaseSave/sqlite/DrawableAVLVertex.kt | 6 +++--- .../sqlite/SQLiteRepositoryExposed.kt | 19 +++++++++++-------- .../sqlite/SQLiteRepositoryJDBC.kt | 12 ++++++------ 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index 07824bf..4022b62 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -242,7 +242,7 @@ class Neo4jRepository : Closeable { fun findTree(treeName: String): Boolean { val session = driver?.session() ?: throw IOException() - var name: String = "NULL" + var name = "NULL" session.executeRead { tx -> name = tx.run("MATCH (n: Node {treeName: \$treeName}) WHERE NOT (:Node)-->(n) RETURN n.treeName").list()[0].values()[0].toString().replace("\"", "") diff --git a/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt b/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt index e0ce9fa..87576ea 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt +++ b/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt @@ -2,9 +2,9 @@ package databaseSave.sqlite import databaseSave.DrawableVertex -class DrawAVLVertex>( +class DrawableAVLVertex>( override val value: Pack, - override val x: Double, - override val y: Double, + override val x: Double = 0.0, + override val y: Double = 0.0, val height: Int, ) : DrawableVertex diff --git a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt index b6d4404..09e0c33 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt +++ b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt @@ -1,12 +1,13 @@ package databaseSave.sqlite -import databaseSave.sqlite.vertexEntities.VertexTableEntity -import databaseSave.sqlite.vertexEntities.VertexTable import databaseSave.sqlite.treeEntities.TreeTableEntity import databaseSave.sqlite.treeEntities.TreesTable +import databaseSave.sqlite.vertexEntities.VertexTable +import databaseSave.sqlite.vertexEntities.VertexTableEntity import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.transactions.transaction +import java.io.File import java.sql.SQLException class SQLiteRepositoryExposed { @@ -17,7 +18,9 @@ class SQLiteRepositoryExposed { fun initDataBase(name: String) { if ((dbName != null) && (dbName == name)) return - db = Database.connect("jdbc:sqlite:$name", driver = "org.sqlite.JDBC") + File(System.getProperty("user.dir") + "/sqliteDB").mkdirs() + + db = Database.connect("jdbc:sqlite:sqliteDB/$name", driver = "org.sqlite.JDBC") if (!isEmptyDB()) createTables() } @@ -54,7 +57,7 @@ class SQLiteRepositoryExposed { fun > saveTree( treeName: String, - vertexes: MutableList>, + vertexes: MutableList>, serializeData: (input: Pack) -> String, ): Unit = transaction(db) { interDelete(treeName) @@ -74,19 +77,19 @@ class SQLiteRepositoryExposed { } fun getAllSavedTrees(): List = transaction(db) { - return@transaction TreeTableEntity.all().map { el -> el.name } + return@transaction TreeTableEntity.all().map { el -> el.name } } fun > getTree( name: String, deSerializeData: (input: String) -> Pack, - ): MutableList> = transaction(db) { - val ans = mutableListOf>() + ): MutableList> = transaction(db) { + val ans = mutableListOf>() val treeId = interIsTreeExist(name) ?: throw SQLException("Tree doesn't exist") for (el in VertexTableEntity.find(VertexTable.tree eq treeId).orderBy(VertexTable.order to SortOrder.ASC)){ ans.add( - DrawAVLVertex( + DrawableAVLVertex( value = deSerializeData(el.value), height = el.height, x = el.x, diff --git a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt index 061fa7e..ac6fa24 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt +++ b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt @@ -124,7 +124,7 @@ class SQLiteRepositoryJDBC>( else throw SQLException("Impossible case") } - fun addVertex(avlDVertex: DrawAVLVertex, treeName: String) { + fun addVertex(avlDVertex: DrawableAVLVertex, treeName: String) { val isInDB = getVertexId(avlDVertex, treeName) if (isInDB != 0) { deleteVertex(isInDB, treeName) @@ -147,7 +147,7 @@ class SQLiteRepositoryJDBC>( } - fun addVertexes(list: MutableList>, treeName: String) { + fun addVertexes(list: MutableList>, treeName: String) { for (el in list) addVertex(el, treeName) } @@ -163,15 +163,15 @@ class SQLiteRepositoryJDBC>( } } - fun getAllVertexes(treeName: String): MutableList> { - val info = mutableListOf>() + 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( + DrawableAVLVertex( value = deSerializeData(result.getString(value)), height = result.getInt(height), x = result.getDouble(xCord), @@ -189,7 +189,7 @@ class SQLiteRepositoryJDBC>( } } - private fun getVertexId(vertex: DrawAVLVertex, tableName: String): Int { + private fun getVertexId(vertex: DrawableAVLVertex, tableName: String): Int { var id: Int? = null try { val stmt = From c6c84f10e2984905f034e7dabfcb5cf3a0db16eb Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 25 Apr 2023 01:37:15 +0300 Subject: [PATCH 161/212] fix: Add functionality to the menu --- .gitignore | 4 +- lib/src/main/kotlin/controller/Controller.kt | 6 +- lib/src/main/kotlin/main.kt | 57 ++++++++++++------- lib/src/main/resources/magnifier.png | Bin 0 -> 12154 bytes 4 files changed, 42 insertions(+), 25 deletions(-) create mode 100644 lib/src/main/resources/magnifier.png diff --git a/.gitignore b/.gitignore index d1fbe5a..3df0a47 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,5 @@ /lib/TEST_TEST/ /gradle/ /neo4jDB/ -/jsonFormat/ -/jsonFormatFiles/ +/saved-trees/ +/sqliteDB/ diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index e70246a..8071dab 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -1,16 +1,18 @@ package controller +import databaseManage.AVLTreeManager import databaseManage.BINTreeManager import databaseManage.RBTreeManager import java.io.IOException class Controller { - //val avlManager = AVLTreeManager() + private val avlManager = AVLTreeManager() private val rbManager = RBTreeManager() private val binManager = BINTreeManager() fun showFiles(): List> { - return listOf(rbManager.getSavedTreesNames(), listOf(), binManager.getSavedTreesNames()) + avlManager.initDatabase("DBname") + return listOf(rbManager.getSavedTreesNames(), avlManager.getSavedTreesNames(), binManager.getSavedTreesNames()) } fun foo(fileName: String) { diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index b53356a..96c2043 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -5,6 +5,8 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Text import androidx.compose.material.TextField +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Search import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -14,6 +16,7 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.input.key.* import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -25,7 +28,6 @@ import controller.Controller import java.awt.Dimension import javax.swing.JFileChooser -// я передумал, будет три маленьких кнопки где-нибудь @OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) fun main() = application { @@ -36,23 +38,27 @@ fun main() = application { this.window.minimumSize = Dimension(800, 600) val treeNames = listOf("Red black tree", "AVL tree", "Binary tree") - val dirPath = System.getProperty("user.dir") - val dirFiles = listOf("$dirPath/neo4jDB/neo4jFormatFiles", dirPath, "$dirPath/jsonFormatFiles") + val dirPath = System.getProperty("user.dir") + "/saved-trees" + val dirFiles = listOf("$dirPath/RB-trees", "$dirPath/AVL-trees", "$dirPath/BIN-trees") val controller = Controller() val showFiles = controller.showFiles() + var numTree = 0 + MenuBar { Menu(text = "Open") { repeat(3) { indexTree -> Menu(treeNames[indexTree]) { // поле для самостоятельного ввода имени - Item("Search") { + val icon = Icons.Outlined.Search + Item("Search", rememberVectorPainter(icon)) { val fd = JFileChooser(dirFiles[indexTree]) fd.isMultiSelectionEnabled = false - fd.fileSelectionMode = JFileChooser.FILES_AND_DIRECTORIES + fd.fileSelectionMode = JFileChooser.FILES_ONLY fd.isFileHidingEnabled = false if (fd.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { + numTree = indexTree // убедиться, что user сделал "корректный" выбор val name = fd.selectedFile } @@ -61,20 +67,38 @@ fun main() = application { repeat(showFiles[indexTree].size) { index -> Item( showFiles[indexTree][index], - onClick = { }) // контроллер создает новое дерево и начинает работу с ним + onClick = { + numTree = index + }) // контроллер создает новое дерево и начинает работу с ним } } } } Menu(text = " Create") { - Item("Red Black tree", onClick = {}) // контроллер создает новое дерево и начинает работу с ним - Item("AVL tree", onClick = {}) - Item("Binary tree", onClick = {}) + Item("Red Black tree", onClick = { + numTree = 0 + }) // контроллер создает новое дерево и начинает работу с ним + Item("AVL tree", onClick = { + numTree = 1 + }) + Item("Binary tree", onClick = { + numTree = 2 + }) } Menu(text = "Save & delete") { - Item("Save as", onClick = {}) - Item("Delete", onClick = {}) + // надо как-то узнавать с каким деревом работаем + Item("Save as") { + val fd = JFileChooser(dirFiles[numTree]) + fd.fileSelectionMode = JFileChooser.FILES_ONLY + + if (fd.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { + val path = fd.selectedFile.name + } + // отправить name в балансер + + } + Item("Delete", onClick = { /* тут запрос в балансер на удаление и предоставление нового дерева*/}) } } @@ -134,13 +158,4 @@ fun main() = application { } } } -// .wrapContentSize() - -/* -val fd = FileDialog(this@Window.window, "Open", FileDialog.LOAD) -fd.isVisible = true -fd.file = dirFiles[indexTree] ?: dirPath -val fileName = fd.file ?: "" -if (fileName != "") - controller.foo(fileName) -*/ \ No newline at end of file +// .wrapContentSize() \ No newline at end of file diff --git a/lib/src/main/resources/magnifier.png b/lib/src/main/resources/magnifier.png new file mode 100644 index 0000000000000000000000000000000000000000..841599d36baefd6290842210e287dafe6ad62352 GIT binary patch literal 12154 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

JT0?F=a(o7FytdoMfDFax`)G>d*J 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 literal 0 HcmV?d00001 From 61b8edc76b4a2fe2f926a339207c224f259f60fc Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 26 Apr 2023 15:34:54 +0300 Subject: [PATCH 162/212] fix: Completely redesigned homepage --- lib/build.gradle.kts | 5 +- lib/src/main/kotlin/main.kt | 372 ++++++++++++------ .../main/resources/font/HelveticaMedium.ttf | Bin 0 -> 24388 bytes 3 files changed, 247 insertions(+), 130 deletions(-) create mode 100644 lib/src/main/resources/font/HelveticaMedium.ttf diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 7849257..5f1f447 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -19,6 +19,7 @@ repositories { mavenLocal() maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") mavenCentral() + google() } dependencies { @@ -26,7 +27,8 @@ dependencies { 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") + implementation("com.google.code.gson:gson:2.10.1") + // Neo4j val neo4jCore = "4.0.5" @@ -47,7 +49,6 @@ dependencies { implementation(compose.desktop.currentOs) implementation(compose.material3) - 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") testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index 96c2043..26a737f 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -1,161 +1,277 @@ -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text -import androidx.compose.material.TextField +import androidx.compose.animation.AnimatedVisibility import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.KeyboardArrowRight import androidx.compose.material.icons.outlined.Search -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.rememberVectorPainter -import androidx.compose.ui.input.key.* +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.MenuBar +import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import controller.Controller -import java.awt.Dimension -import javax.swing.JFileChooser -@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class) +@OptIn(ExperimentalMaterial3Api::class) fun main() = application { Window( - onCloseRequest = ::exitApplication, - title = "LESOK", + onCloseRequest = ::exitApplication ) { - this.window.minimumSize = Dimension(800, 600) - val treeNames = listOf("Red black tree", "AVL tree", "Binary tree") - - val dirPath = System.getProperty("user.dir") + "/saved-trees" - val dirFiles = listOf("$dirPath/RB-trees", "$dirPath/AVL-trees", "$dirPath/BIN-trees") - - val controller = Controller() - val showFiles = controller.showFiles() - - var numTree = 0 - - MenuBar { - Menu(text = "Open") { - repeat(3) { indexTree -> - Menu(treeNames[indexTree]) { - // поле для самостоятельного ввода имени - val icon = Icons.Outlined.Search - Item("Search", rememberVectorPainter(icon)) { - val fd = JFileChooser(dirFiles[indexTree]) - fd.isMultiSelectionEnabled = false - fd.fileSelectionMode = JFileChooser.FILES_ONLY - fd.isFileHidingEnabled = false - if (fd.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { - numTree = indexTree // убедиться, что user сделал "корректный" выбор - val name = fd.selectedFile - } + Scaffold( + topBar = { + myTopAppBar() + }, + content = { + } + ) + + } +} + +private val lightColors = lightColorScheme( + primary = Color(255, 255, 255), + secondary = Color(34, 35, 41), + onSecondary = Color(148, 150, 166) +) + +private val myTypography = Typography( + headlineMedium = TextStyle( + fontFamily = FontFamily.Monospace, + fontWeight = FontWeight.Bold, + fontSize = 96.sp + ) +) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun myTopAppBar() { + val controller = Controller() + val showFiles = controller.showFiles() + + MaterialTheme( + colorScheme = lightColors, + typography = myTypography + ) { + val expanded = remember { mutableStateOf(false) } + val expandedCreateNested = remember { mutableStateOf(false) } + val expandedOpenNested = remember { mutableStateOf(false) } + var mainMenuWidth by remember { mutableStateOf(0.dp) } + TopAppBar( + title = { Text("") }, + colors = TopAppBarDefaults.centerAlignedTopAppBarColors( + containerColor = MaterialTheme.colorScheme.secondary, + navigationIconContentColor = MaterialTheme.colorScheme.primary + ), + navigationIcon = { + IconButton( + onClick = { + expanded.value = !expanded.value + }, + colors = IconButtonDefaults.iconButtonColors( + containerColor = MaterialTheme.colorScheme.secondary, + contentColor = MaterialTheme.colorScheme.primary + ), + enabled = true, + ) { + Icon( + imageVector = Icons.Filled.Menu, + contentDescription = "Localized description", + ) + } + + DropdownMenu( + expanded = expanded.value, + onDismissRequest = { expanded.value = false }, + focusable = false, + ) { + DropdownMenuItem( + text = { Text("Create") }, + trailingIcon = { arrowIcon() }, + onClick = { + expandedCreateNested.value = !expandedCreateNested.value // !!! + expandedOpenNested.value = false + // main.value = true + }, + modifier = Modifier.onGloballyPositioned { + mainMenuWidth = it.size.width.dp - 40.dp } - repeat(showFiles[indexTree].size) { index -> - Item( - showFiles[indexTree][index], - onClick = { - numTree = index - }) // контроллер создает новое дерево и начинает работу с ним + ) + createNestedMenu(expandedCreateNested, expanded, mainMenuWidth) + + DropdownMenuItem( + text = { Text("Open") }, + trailingIcon = { arrowIcon() }, + onClick = { + expandedOpenNested.value = !expandedOpenNested.value + //showFiles = controller.showFiles() + // main.value } - } + ) + openNestedMenu(expandedOpenNested, expanded, mainMenuWidth, showFiles) + + DropdownMenuItem(text = { Text("Save") }, onClick = {}) + + DropdownMenuItem(leadingIcon = { deleteIcon() }, text = { Text("Delete") }, onClick = {}) } } - Menu(text = " Create") { - Item("Red Black tree", onClick = { - numTree = 0 - }) // контроллер создает новое дерево и начинает работу с ним - Item("AVL tree", onClick = { - numTree = 1 - }) - Item("Binary tree", onClick = { - numTree = 2 - }) - } + ) + } + +} - Menu(text = "Save & delete") { - // надо как-то узнавать с каким деревом работаем - Item("Save as") { - val fd = JFileChooser(dirFiles[numTree]) - fd.fileSelectionMode = JFileChooser.FILES_ONLY +@Composable +fun deleteIcon() = Icon( + imageVector = Icons.Outlined.Delete, + contentDescription = null +) - if (fd.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) { - val path = fd.selectedFile.name +@Composable +fun arrowIcon() = Icon( + imageVector = Icons.Outlined.KeyboardArrowRight, + contentDescription = null +) + +@Composable +fun createNestedMenu( + expandedNested: MutableState, + expandedMainMenu: MutableState, + mainMenuWidth: Dp, +) { + val treesNames = listOf("Red black tree", "AVL tree", "Binary tree") + AnimatedVisibility( + visible = expandedNested.value + ) { + DropdownMenu( + expanded = expandedNested.value, + offset = DpOffset(mainMenuWidth, 0.dp), + onDismissRequest = { + expandedNested.value = false + expandedMainMenu.value = false + } + ) { + + repeat(3) { index -> + DropdownMenuItem( + text = { Text(treesNames[index]) }, + onClick = { + expandedNested.value = false + expandedMainMenu.value = false } - // отправить name в балансер + ) + } - } - Item("Delete", onClick = { /* тут запрос в балансер на удаление и предоставление нового дерева*/}) + } + + } +} + +@Composable +fun openNestedMenu( + expandedNested: MutableState, + expandedMainMenu: MutableState, + mainMenuWidth: Dp, + showFiles: List>, +) { + val treesNames = listOf("Red black tree", "AVL tree", "Binary tree") + val a = remember { mutableStateOf(true) } // поменять на лист из 3?? + val expandedTreesNames = listOf(remember { mutableStateOf(false) }, + remember { mutableStateOf(false) }, + remember { mutableStateOf(false) }) + var offsetTreesNames by remember { mutableStateOf(0.dp) } + + AnimatedVisibility( + visible = expandedNested.value + ) { + DropdownMenu( + expanded = expandedNested.value, + offset = DpOffset(mainMenuWidth, 0.dp), + onDismissRequest = { + expandedNested.value = false + }, + modifier = Modifier.onGloballyPositioned { + offsetTreesNames = it.size.width.dp - 40.dp + } + ) { + repeat(3) { index -> + DropdownMenuItem( + text = { Text(treesNames[index]) }, + onClick = { + expandedTreesNames[index].value = a.value + a.value = true + }, + trailingIcon = { arrowIcon() } + ) + treesNames( + expandedTreesNames[index], + expandedNested, + showFiles[index], + offsetTreesNames, a + ) + expandedMainMenu.value = expandedNested.value } + } - Box(modifier = Modifier.background(Color(234, 231, 220)).fillMaxSize()) { - Column( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth(0.2f).fillMaxHeight(0.3f) - ) { - val options = listOf("INSERT", "DELETE", "FIND") - repeat(3) { index -> - Row( - horizontalArrangement = Arrangement.Center, - modifier = Modifier.weight(1f) - ) { - var consumedText by remember { mutableStateOf(0) } - var text by remember { mutableStateOf("") } - Box( - modifier = Modifier - .clip(RoundedCornerShape(5.dp)) - .background(Color(216, 195, 165)) - .weight(1f) - .align(Alignment.CenterVertically) - ) { - Text( - text = options[index], - color = Color(142, 141, 138), - fontWeight = FontWeight.ExtraBold, - textAlign = TextAlign.Center - ) - } - TextField( - value = text, - onValueChange = { text = it }, - modifier = Modifier - .onPreviewKeyEvent { - when { - (!it.isShiftPressed && it.key == Key.Enter && it.type == KeyEventType.KeyUp) -> { - consumedText -= text.length - text = "" - true - // вот тут походу отправляем запрос в контроллер - } - - else -> false - } - } - .background(Color(216, 195, 165)) - .weight(1f) - .align(Alignment.CenterVertically) - ) - } - Spacer(Modifier.height(5.dp)) - } + } + +} +@Composable +fun treesNames( + expandedNested: MutableState, + expandedOpenNested: MutableState, + showFiles: List, + offset: Dp, + a: MutableState +) { + + AnimatedVisibility( + visible = expandedNested.value + ) { + DropdownMenu( + expanded = expandedNested.value, + offset = DpOffset(offset, 0.dp), + onDismissRequest = { + a.value = !expandedNested.value + expandedNested.value = false + } + ) { + DropdownMenuItem( + leadingIcon = { searchIcon() }, + text = { Text("Search") }, + onClick = { + expandedOpenNested.value = false + } + ) + repeat(showFiles.size) { index -> + DropdownMenuItem( + text = { Text(showFiles[index]) }, + onClick = { + expandedOpenNested.value = false + } + ) } + } + } + } -// .wrapContentSize() \ No newline at end of file + +@Composable +fun searchIcon() = Icon( + imageVector = Icons.Outlined.Search, + contentDescription = null +) diff --git a/lib/src/main/resources/font/HelveticaMedium.ttf b/lib/src/main/resources/font/HelveticaMedium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..cf97ecc36239c59300cb0d90b13cbcae9bf6a7e8 GIT binary patch literal 24388 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 loFu2PH}@8P7xFBu;k$e)g Date: Wed, 26 Apr 2023 17:45:42 +0300 Subject: [PATCH 163/212] feat: add set, get methods. Add test for him --- .../main/kotlin/treelib/avlTree/AVLTree.kt | 4 +-- .../main/kotlin/treelib/binTree/BINTree.kt | 4 +-- lib/src/main/kotlin/treelib/rbTree/RBTree.kt | 4 +-- lib/src/test/kotlin/treelib/AVLTreeTest.kt | 12 +++++++++ lib/src/test/kotlin/treelib/BINTreeTest.kt | 26 +++++++++++++++++++ lib/src/test/kotlin/treelib/RBTreeTest.kt | 12 +++++++++ 6 files changed, 56 insertions(+), 6 deletions(-) diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt index 5df840f..eb5ef12 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt @@ -9,7 +9,7 @@ class AVLTree, V> : override val treeStruct = AVLStruct>() - operator fun AVLTree.get(key: K): V? = getItem(key) + operator fun set(key: K, value: V) = putItem(key to value) - operator fun AVLTree.set(key: K, value: V) = putItem(key to value) + operator fun get(key: K) = getItem(key) } diff --git a/lib/src/main/kotlin/treelib/binTree/BINTree.kt b/lib/src/main/kotlin/treelib/binTree/BINTree.kt index f0a92d9..a9e7fc3 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINTree.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINTree.kt @@ -8,7 +8,7 @@ class BINTree, V> override val treeStruct = BINStruct>() - operator fun BINTree.get(key: K): V? = getItem(key) + operator fun set(key: K, value: V) = putItem(key to value) - operator fun BINTree.set(key: K, value: V) = putItem(key to value) + operator fun get(key: K) = getItem(key) } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt index a29234e..f3f52ba 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt @@ -9,7 +9,7 @@ class RBTree, V> : override val treeStruct = RBStruct>() - operator fun RBTree.get(key: K): V? = getItem(key) + operator fun set(key: K, value: V) = putItem(key to value) - operator fun RBTree.set(key: K, value: V) = putItem(key to value) + operator fun get(key: K) = getItem(key) } diff --git a/lib/src/test/kotlin/treelib/AVLTreeTest.kt b/lib/src/test/kotlin/treelib/AVLTreeTest.kt index 63073da..7a1cf2a 100644 --- a/lib/src/test/kotlin/treelib/AVLTreeTest.kt +++ b/lib/src/test/kotlin/treelib/AVLTreeTest.kt @@ -1,5 +1,6 @@ package treelib +import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import treelib.avlTree.* @@ -80,4 +81,15 @@ class AVLTreeTest { assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.value?.key, actual = null) } + + @Test + fun `test get set methods`() { + for (i in 1..3) { + tree[i] = i + } + + assertEquals(expected = tree[1], actual = 1) + assertEquals(expected = tree[2], actual = 2) + assertEquals(expected = tree[3], actual = 3) + } } \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/BINTreeTest.kt b/lib/src/test/kotlin/treelib/BINTreeTest.kt index a13d174..1642137 100644 --- a/lib/src/test/kotlin/treelib/BINTreeTest.kt +++ b/lib/src/test/kotlin/treelib/BINTreeTest.kt @@ -86,4 +86,30 @@ class BINTreeTest { actual = numbers[index + 1] ) } + + @Test + fun `test get set methods`() { + for (i in 1..3) { + tree[i] = i + } + + assertEquals(expected = tree[1], actual = 1) + assertEquals(expected = tree[2], actual = 2) + assertEquals(expected = tree[3], actual = 3) + } + + + @Test + fun `test preorders and inorder`() { + val num = mutableListOf(5, 4, 6, 3, 7) + for (i in num) { + tree.putItem(Pair(i, i)) + } + + val listPreorder = tree.preOrder() + val listInorder = tree.inOrder() + + assertEquals(listPreorder[0], Pair(5, 5)) + assertEquals(listInorder[0], Pair(3, 3)) + } } \ No newline at end of file diff --git a/lib/src/test/kotlin/treelib/RBTreeTest.kt b/lib/src/test/kotlin/treelib/RBTreeTest.kt index cd7ce2a..004bc9b 100644 --- a/lib/src/test/kotlin/treelib/RBTreeTest.kt +++ b/lib/src/test/kotlin/treelib/RBTreeTest.kt @@ -1,5 +1,6 @@ package treelib +import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import treelib.rbTree.* @@ -52,4 +53,15 @@ class RBTreeTest { assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.left?.color, actual = Markers.RED) assertEquals(expected = treeSW.getPrivateNode(treeW.getPrivateNode(tree))?.color, actual = Markers.BLACK) } + + @Test + fun `test set method`() { + for (i in (1..3)) { + tree[i] = i + } + + assertEquals(expected = tree[3], actual = 3) + assertEquals(expected = tree[2], actual = 2) + assertEquals(expected = tree[1], actual = 1) + } } \ No newline at end of file From dd91b248a43fcab37f5167a741c61b35853b83e5 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Wed, 26 Apr 2023 18:26:16 +0300 Subject: [PATCH 164/212] fix: add untested classes --- lib/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 080c014..b0441df 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -92,6 +92,7 @@ tasks.jacocoTestReport { tasks.jacocoTestCoverageVerification { classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class") + exclude("**/singleObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class", "**/BINStruct.class", "**/AVLStruct.class", "**/RBStruct.class", "**/TreeStruct.class") } }) dependsOn(tasks.jacocoTestReport) violationRules { From 74f4285ad20542d67f2ee881dc72c9d380e3708e Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 26 Apr 2023 22:33:38 +0300 Subject: [PATCH 165/212] feat: Implement basic architecture of trees rendering without positioning; Create an implementation of BIN rendering without padding. (#24) --- lib/build.gradle.kts | 2 +- .../kotlin/viewPart/nodes/AVLDrawableNode.kt | 30 +++++++ .../kotlin/viewPart/nodes/BINDrawableNode.kt | 73 +++++++++++++++ .../kotlin/viewPart/nodes/DrawableNode.kt | 90 +++++++++++++++++++ .../kotlin/viewPart/nodes/RBDrawableNode.kt | 30 +++++++ .../viewPart/nodes/design/AVLNodeDesign.kt | 5 ++ .../viewPart/nodes/design/BINNodeDesign.kt | 35 ++++++++ .../viewPart/nodes/design/RBNodeDesign.kt | 5 ++ 8 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 lib/src/main/kotlin/viewPart/nodes/AVLDrawableNode.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/BINDrawableNode.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/DrawableNode.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/RBDrawableNode.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/design/AVLNodeDesign.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/design/BINNodeDesign.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/design/RBNodeDesign.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 7849257..724bdb7 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -17,8 +17,8 @@ java { repositories { mavenLocal() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } dependencies { diff --git a/lib/src/main/kotlin/viewPart/nodes/AVLDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/AVLDrawableNode.kt new file mode 100644 index 0000000..12bbf0f --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/AVLDrawableNode.kt @@ -0,0 +1,30 @@ +package viewPart.nodes + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState + +class AVLDrawableNode( + override val value: Pack, + override var leftChild: AVLDrawableNode? = null, + override var rightChild: AVLDrawableNode? = null, + val height: Int, + override val xState: MutableState, + override val yState: MutableState, +) : DrawableNode>() { + + @Composable + override fun nodeView( + information: String, + offsetX: MutableState, + offsetY: MutableState + ) { + TODO("Not yet implemented") + } + + @Composable + override fun edgeView(child: AVLDrawableNode) { + TODO("Not yet implemented") + } + + +} diff --git a/lib/src/main/kotlin/viewPart/nodes/BINDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/BINDrawableNode.kt new file mode 100644 index 0000000..d7789ea --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/BINDrawableNode.kt @@ -0,0 +1,73 @@ +package viewPart.nodes + +import viewPart.nodes.design.BINNodeDesign +import androidx.compose.foundation.Canvas +import androidx.compose.runtime.MutableState +import androidx.compose.ui.graphics.Color +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.boundsInParent +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import kotlin.math.roundToInt + +class BINDrawableNode( + override val value: Pack, + override var leftChild: BINDrawableNode? = null, + override var rightChild: BINDrawableNode? = null, + override val xState: MutableState, + override val yState: MutableState, +) : DrawableNode>() { + + @Composable + override fun nodeView( + information: String, + offsetX: MutableState, + offsetY: MutableState + ) { + val modifier = Modifier + .offset { + IntOffset( + x = offsetX.value.roundToInt(), + y = offsetY.value.roundToInt() + ) + } + .pointerInput(Unit) { + detectDragGestures { _, dragAmount -> + offsetX.value += dragAmount.x + offsetY.value += dragAmount.y + } + } + .size(BINNodeDesign.nodeSize.dp) + .clip(BINNodeDesign.shape) + .background(BINNodeDesign.colorNode) + .onGloballyPositioned { layoutCoordinates -> + val rect = layoutCoordinates.boundsInParent() + xState.value = rect.center.x + yState.value = rect.center.y + } + BINNodeDesign.infoView(information, modifier) + } + + @Composable + override fun edgeView(child: BINDrawableNode) { + Canvas(modifier = Modifier.fillMaxSize().zIndex(-1f)) { + drawLine( + color = BINNodeDesign.lineColor, + start = Offset(xState.value, yState.value), + end = Offset(child.xState.value, child.yState.value), + strokeWidth = BINNodeDesign.lineStrokeWidth, //TODO вынести в конфиг ноды + ) + } + } +} diff --git a/lib/src/main/kotlin/viewPart/nodes/DrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/DrawableNode.kt new file mode 100644 index 0000000..3626591 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/DrawableNode.kt @@ -0,0 +1,90 @@ +package viewPart.nodes + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember + +abstract class DrawableNode> { + abstract val value: Pack + abstract var leftChild: NodeType? + abstract var rightChild: NodeType? + abstract val xState: MutableState + abstract val yState: MutableState + + /* md should be added (for deleting)???? + * abstract val xDragState: MutableState + * abstract val yDragState: MutableState + * */ + + @Composable + fun display(width: Float? = null, height: Float? = null) { + // TODO: Выпилить рекурсивный дисплей + nodeView( + information = value.toString(), + remember { mutableStateOf(0f) }, + remember { mutableStateOf(0f) }, + ) + + leftChild?.let { + edgeView(it) + it.display() + } + + rightChild?.let { + edgeView(it) + it.display() + } + } + + @Composable + protected abstract fun nodeView( + information: String, + offsetX: MutableState, + offsetY: MutableState, + ) + + @Composable + protected abstract fun edgeView(child: NodeType) +} + +/* Usage example (put it inside block): + + val a = BINDrawableNode( + 1, + null, + null, + remember { mutableStateOf(0f) }, + remember { mutableStateOf(0f) } + ) + + val b = BINDrawableNode( + 2, + null, + null, + remember { mutableStateOf(0f) }, + remember { mutableStateOf(0f) } + ) + + val c = BINDrawableNode( + 3, + null, + null, + remember { mutableStateOf(0f) }, + remember { mutableStateOf(0f) } + ) + + val d = BINDrawableNode( + 4, + null, + null, + remember { mutableStateOf(0f) }, + remember { mutableStateOf(0f) } + ) + + a.leftChild = b + a.rightChild = c + c.leftChild = d + + a.display() +* */ diff --git a/lib/src/main/kotlin/viewPart/nodes/RBDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/RBDrawableNode.kt new file mode 100644 index 0000000..f78c439 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/RBDrawableNode.kt @@ -0,0 +1,30 @@ +package viewPart.nodes + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.graphics.Color +import treelib.rbTree.Markers + +class RBDrawableNode( + override val value: Pack, + override var leftChild: RBDrawableNode? = null, + override var rightChild: RBDrawableNode? = null, + val color: Markers, + override val xState: MutableState, + override val yState: MutableState, +) : DrawableNode>() { + + @Composable + override fun nodeView( + information: String, + offsetX: MutableState, + offsetY: MutableState + ) { + TODO("Not yet implemented") + } + + @Composable + override fun edgeView(child: RBDrawableNode) { + TODO("Not yet implemented") + } +} diff --git a/lib/src/main/kotlin/viewPart/nodes/design/AVLNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/design/AVLNodeDesign.kt new file mode 100644 index 0000000..33e5b69 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/design/AVLNodeDesign.kt @@ -0,0 +1,5 @@ +package viewPart.nodes.design + +object AVLNodeDesign { + // TODO: Реализовать, как BINNodeDesign +} diff --git a/lib/src/main/kotlin/viewPart/nodes/design/BINNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/design/BINNodeDesign.kt new file mode 100644 index 0000000..0b2531c --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/design/BINNodeDesign.kt @@ -0,0 +1,35 @@ +package viewPart.nodes.design + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.GenericShape +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +object BINNodeDesign { + var colorNode = Color.Green + var lineColor = Color.Black + var nodeSize = 80f + var shape = CircleShape + var lineStrokeWidth = 15f + + @Composable + fun infoView(information: String, modifier: Modifier) { + Box( + modifier = modifier, + contentAlignment = Alignment.Center + ) { + Text( + text = information, + fontSize = 30.sp, + color = Color.White, + fontWeight = FontWeight.Bold + ) + } + } +} diff --git a/lib/src/main/kotlin/viewPart/nodes/design/RBNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/design/RBNodeDesign.kt new file mode 100644 index 0000000..aa8d6c9 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/design/RBNodeDesign.kt @@ -0,0 +1,5 @@ +package viewPart.nodes.design + +object RBNodeDesign { + // TODO: Реализовать, как BINNodeDesign +} From 360a084d4d5325b6ae6b15f9ca7c7dc68174ed54 Mon Sep 17 00:00:00 2001 From: Artem Date: Fri, 28 Apr 2023 05:34:07 +0300 Subject: [PATCH 166/212] feat: Implement FileDialog to select files --- lib/build.gradle.kts | 1 + lib/src/main/kotlin/FileDialog.kt | 347 ++++++++++++++++++ lib/src/main/kotlin/main.kt | 41 ++- lib/src/main/resources/drawable/dir.png | Bin 0 -> 379 bytes lib/src/main/resources/drawable/folder.webp | Bin 0 -> 16522 bytes .../main/resources/drawable/jsonFormat.png | Bin 0 -> 514 bytes .../main/resources/drawable/neo4jFormat.png | Bin 0 -> 1355 bytes .../main/resources/drawable/second_folder.png | Bin 0 -> 5978 bytes lib/src/main/resources/drawable/sqlFormat.png | Bin 0 -> 1131 bytes lib/src/main/resources/magnifier.png | Bin 12154 -> 0 bytes 10 files changed, 372 insertions(+), 17 deletions(-) create mode 100644 lib/src/main/kotlin/FileDialog.kt create mode 100644 lib/src/main/resources/drawable/dir.png create mode 100644 lib/src/main/resources/drawable/folder.webp create mode 100644 lib/src/main/resources/drawable/jsonFormat.png create mode 100644 lib/src/main/resources/drawable/neo4jFormat.png create mode 100644 lib/src/main/resources/drawable/second_folder.png create mode 100644 lib/src/main/resources/drawable/sqlFormat.png delete mode 100644 lib/src/main/resources/magnifier.png diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 5f1f447..2cf29af 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { implementation("com.google.code.gson:gson:2.10.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.6.0") // Neo4j val neo4jCore = "4.0.5" diff --git a/lib/src/main/kotlin/FileDialog.kt b/lib/src/main/kotlin/FileDialog.kt new file mode 100644 index 0000000..69e7198 --- /dev/null +++ b/lib/src/main/kotlin/FileDialog.kt @@ -0,0 +1,347 @@ + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.forEachGesture +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.window.WindowDraggableArea +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Close +import androidx.compose.material.icons.rounded.KeyboardArrowDown +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.awt.awtEventOrNull +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.WindowPosition +import androidx.compose.ui.window.WindowState +import androidx.compose.ui.window.rememberDialogState +import java.awt.Dimension +import java.awt.event.MouseEvent +import java.io.File + + +// +@Composable +fun searchItem(dirPath: String, expandedOpenNested: MutableState, selectedTree: MutableState) { + val dialogState = remember { mutableStateOf(false) } + val minWidth = remember { mutableStateOf(200) } + //val selectFile = remember { mutableStateOf("null") } + + val enabledButtonIndex = remember { mutableStateOf(-1) } + + val files = findFiles(dirPath) + + if (dialogState.value) { + + MaterialTheme(colorScheme = lightColors) { // add typography + Dialog(//focusable = true, + onCloseRequest = { + dialogState.value = false + expandedOpenNested.value = false + selectedTree.value = if (enabledButtonIndex.value >= 0) files[enabledButtonIndex.value] else "null" + }, visible = dialogState.value, + content = { + WindowDraggableArea { + this.window.minimumSize = Dimension(minWidth.value, 150) + CompleteDialogContent("Open file", dialogState, dirPath, minWidth, files, enabledButtonIndex) + } + }, + undecorated = true, + state = rememberDialogState( + position = WindowPosition(Alignment.Center), + size = DpSize(450.dp, 450.dp) + ) + ) + + } + + } + DropdownMenuItem( + leadingIcon = { searchIcon() }, + text = { Text("Search") }, + onClick = { + dialogState.value = true + //expandedOpenNested.value = false + } + ) + +} + +fun findFiles(dirPath: String): List { + + val listFiles = File(dirPath).list() ?: throw IllegalStateException() + + return listFiles.map { it.split(".")[0] } +} + +@Composable +fun CompleteDialogContent( + title: String, + dialogState: MutableState, + dirPath: String, + minWidth: MutableState, + listFiles: List, + enabledButtonIndex: MutableState +) { + Card( + modifier = Modifier.fillMaxHeight(1f).fillMaxWidth(1f).shadow(elevation = 1.dp, ambientColor = Color.LightGray), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background, + contentColor = MaterialTheme.colorScheme.primary + ), + shape = RectangleShape, + border = BorderStroke(0.5.dp, Color.LightGray) + ) { + Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.SpaceBetween) { + + Column( + modifier = Modifier + .height(50.dp) + ) { + TittleAndButton(title, dialogState) + } + + Column(modifier = Modifier.fillMaxWidth().weight(1f)) { + mainBody(dirPath, listFiles, minWidth, enabledButtonIndex) + } + + Row( + modifier = Modifier + .height(50.dp) + .fillMaxWidth(1f) + .padding(horizontal = 10.dp, vertical = 5.dp), + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.Bottom + ) { + BottomButtons(dialogState) + } + } + } + +} + +@Composable +fun TittleAndButton(title: String, dialogState: MutableState) { + Row( + modifier = Modifier + .fillMaxWidth(1f) + .padding(horizontal = 20.dp, vertical = 10.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text(text = title, fontSize = 20.sp, color = MaterialTheme.colorScheme.primary) + IconButton(modifier = Modifier.then(Modifier.size(20.dp)), // при наведении должна краситься в красный + onClick = { dialogState.value = false }) { + Icon(Icons.Outlined.Close, null) + } + } + Divider(modifier = Modifier.height(1.dp), color = Color.LightGray) + +} + +@Composable +fun BottomButtons(dialogState: MutableState) { + Button( + modifier = Modifier + .width(90.dp), + shape = RoundedCornerShape(3.dp), + onClick = { + dialogState.value = false + }, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.secondary, + contentColor = MaterialTheme.colorScheme.background + ) + ) { + Text(text = "Ok", fontSize = 13.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold) + } + Spacer(modifier = Modifier.width(5.dp)) + Button( + modifier = Modifier + .width(90.dp), + shape = RoundedCornerShape(3.dp), + colors = ButtonDefaults.buttonColors( + contentColor = MaterialTheme.colorScheme.primary, + containerColor = MaterialTheme.colorScheme.background + ), + border = BorderStroke(1.dp, Color(208, 210, 219)), + onClick = { dialogState.value = false }, + contentPadding = PaddingValues(start = 2.dp, end = 2.dp) + ) { + Text(text = "Cancel", fontSize = 13.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold) + } +} + +@Composable +fun mainBody( + dirPath: String, + listFiles: List, + minWidth: MutableState, + enabledButtonIndex: MutableState +) { + + val dirName = dirPath.split("/").last() + + Card( + shape = RoundedCornerShape(3.dp), + border = BorderStroke(1.dp, Color(47, 105, 215)), + modifier = Modifier.fillMaxWidth().height(50.dp).padding(horizontal = 10.dp), // для него + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background, + contentColor = MaterialTheme.colorScheme.primary + ) + ) { + Text( + maxLines = 1, + text = dirPath, + modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp) + .onGloballyPositioned { coordinates -> minWidth.value = coordinates.size.width }, + fontFamily = FontFamily.Monospace, + fontSize = 15.sp, + fontWeight = FontWeight.Bold + ) + } + + Spacer(modifier = Modifier.height(1.dp).fillMaxWidth()) + + //val enabledButtonIndex = remember { mutableStateOf(0) } + + LazyColumn( + modifier = Modifier.fillMaxWidth().padding(horizontal = 10.dp, vertical = 10.dp) + .border(0.5.dp, Color.LightGray), + contentPadding = PaddingValues(top = 10.dp, bottom = 10.dp) + ) { + item { + Row(verticalAlignment = Alignment.Bottom) { + Icon( + Icons.Rounded.KeyboardArrowDown, + contentDescription = null, + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + Icon( + painterResource("/drawable/dir.png"), + contentDescription = null, + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(5.dp)) + Text( + "${dirName}:", + fontFamily = FontFamily.Monospace, + fontSize = 13.sp, + fontWeight = FontWeight.SemiBold, + color = MaterialTheme.colorScheme.primary + ) + } + } + + item { + Spacer(modifier = Modifier.height(4.dp)) + } + + items(listFiles.size) { index -> + val checked = remember { mutableStateOf(false) } + Button( + onClick = { + checked.value = !checked.value + enabledButtonIndex.value = index + }, + colors = ButtonDefaults.buttonColors(containerColor = if (checked.value && enabledButtonIndex.value == index) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.background), + modifier = Modifier + .fillParentMaxWidth() + .clickable(interactionSource = MutableInteractionSource(), indication = null, onClick = {}), // ?? + shape = RectangleShape + ) { + + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.CenterStart + ) { + Row( + modifier = Modifier.fillParentMaxWidth().align(Alignment.CenterStart) + ) { + Spacer(modifier = Modifier.width(25.dp)) + Icon(fileIcon(dirName), contentDescription = null, tint = Color.Gray) + Spacer(modifier = Modifier.width(5.dp)) + Text( + text = listFiles[index], fontSize = 13.sp, fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.primary, fontWeight = FontWeight.Normal, maxLines = 1 + ) + } + + } + + } + } + + } + +} + +@Composable +fun fileIcon(dirName: String): Painter { + + return when (dirName) { + "RB-trees" -> painterResource("/drawable/neo4jFormat.png") + "AVL-trees" -> painterResource("/drawable/sqlFormat.png") + "BIN-trees" -> painterResource("/drawable/jsonFormat.png") + else -> throw IllegalStateException() + } + +} + +@Composable +fun Modifier.windowDraggable(windowProvider: () -> WindowState): Modifier { + return pointerInput(Unit) { + forEachGesture { + awaitPointerEventScope { + val window = windowProvider() + val firstEvent = awaitPointerEvent() + when (firstEvent.awtEventOrNull?.id) { + MouseEvent.MOUSE_PRESSED -> Unit + else -> return@awaitPointerEventScope + } + val firstWindowPoint = firstEvent.awtEventOrNull?.locationOnScreen ?: return@awaitPointerEventScope + val startPosition = window.position + while (true) { + val event = awaitPointerEvent() + val currentPoint = event.awtEventOrNull?.locationOnScreen ?: break + + window.position = WindowPosition( + x = startPosition.x + (currentPoint.x - firstWindowPoint.x).dp, + y = startPosition.y + (currentPoint.y - firstWindowPoint.y).dp, + ) + + when (event.awtEventOrNull?.id) { + null, + MouseEvent.MOUSE_RELEASED -> { + break + } + } + } + } + } + } +} + diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index 26a737f..505c45a 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import controller.Controller +import java.awt.Dimension @OptIn(ExperimentalMaterial3Api::class) fun main() = application { @@ -27,6 +28,7 @@ fun main() = application { Window( onCloseRequest = ::exitApplication ) { + this.window.minimumSize = Dimension(800, 600) Scaffold( topBar = { myTopAppBar() @@ -39,10 +41,13 @@ fun main() = application { } } -private val lightColors = lightColorScheme( - primary = Color(255, 255, 255), - secondary = Color(34, 35, 41), - onSecondary = Color(148, 150, 166) +val lightColors = lightColorScheme( + background = Color(255, 255, 255), + primary = Color(34, 35, 41), + secondary = Color(47, 105, 215), + onSecondary = Color(148, 150, 166), // 208, 223, 252 + tertiary = Color(208, 223, 252) + ) private val myTypography = Typography( @@ -71,8 +76,8 @@ fun myTopAppBar() { TopAppBar( title = { Text("") }, colors = TopAppBarDefaults.centerAlignedTopAppBarColors( - containerColor = MaterialTheme.colorScheme.secondary, - navigationIconContentColor = MaterialTheme.colorScheme.primary + containerColor = MaterialTheme.colorScheme.primary, + navigationIconContentColor = MaterialTheme.colorScheme.background ), navigationIcon = { IconButton( @@ -80,8 +85,8 @@ fun myTopAppBar() { expanded.value = !expanded.value }, colors = IconButtonDefaults.iconButtonColors( - containerColor = MaterialTheme.colorScheme.secondary, - contentColor = MaterialTheme.colorScheme.primary + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.background ), enabled = true, ) { @@ -217,7 +222,7 @@ fun openNestedMenu( expandedTreesNames[index], expandedNested, showFiles[index], - offsetTreesNames, a + offsetTreesNames, a, index // remove a !!!! ) expandedMainMenu.value = expandedNested.value } @@ -234,9 +239,17 @@ fun treesNames( expandedOpenNested: MutableState, showFiles: List, offset: Dp, - a: MutableState + a: MutableState, + index: Int ) { + val dirPath = System.getProperty("user.dir") + "/saved-trees" + val dirFiles = listOf("$dirPath/RB-trees", "$dirPath/AVL-trees", "$dirPath/BIN-trees") + + val chooseSearch = remember { mutableStateOf(false) } + + val selectedTree = remember { mutableStateOf("null") } + AnimatedVisibility( visible = expandedNested.value ) { @@ -248,13 +261,7 @@ fun treesNames( expandedNested.value = false } ) { - DropdownMenuItem( - leadingIcon = { searchIcon() }, - text = { Text("Search") }, - onClick = { - expandedOpenNested.value = false - } - ) + searchItem(dirFiles[index], expandedOpenNested, selectedTree) repeat(showFiles.size) { index -> DropdownMenuItem( text = { Text(showFiles[index]) }, diff --git a/lib/src/main/resources/drawable/dir.png b/lib/src/main/resources/drawable/dir.png new file mode 100644 index 0000000000000000000000000000000000000000..f6f65e7de1d3b27af4c2c2e48fb5fa39af8132e6 GIT binary patch literal 379 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#Xx!^2s0kj_3Q;QBuiW) zN`mv#O3D+9QW?t2%k?tzvWt@w3sUv+i_&Mmvyoz8VC3+0aSZY3d^=&I7qg>4+y9eq zRTmV9Z8WT8d(FJlDLdM6Vn`*2yraC+8vf30vo1LaShKQfYjble#waKrfBPmP)pJwp z$&Fz<*Uw6Q_&=>~!6W{RZxfe##`K)k_1~eYQ(W)DvUR21VGWy}J~_KEv6M`q=gp5! zzWDjTHOukrmBqhQgX^xnvEiRC_9y0T?aPUI`nzWei%ekQ{VJOmQ2zSM#(eQvd^Qim zZWb^Ih+TS|q%JQ~9y#ksU&6-EnoGnl82(D=Uoz0-mQsugHmkFc3fK}MS literal 0 HcmV?d00001 diff --git a/lib/src/main/resources/drawable/folder.webp b/lib/src/main/resources/drawable/folder.webp new file mode 100644 index 0000000000000000000000000000000000000000..bf0a8ab69b0da44629b6f9983a155831f00f410d GIT binary patch literal 16522 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 literal 0 HcmV?d00001 diff --git a/lib/src/main/resources/drawable/jsonFormat.png b/lib/src/main/resources/drawable/jsonFormat.png new file mode 100644 index 0000000000000000000000000000000000000000..71582ae44455cbc4197d86e680b391f62c7cd7d8 GIT binary patch literal 514 zcmV+d0{#7oP)1C1SsskV?8WTwfF1Uj{Je+X-#06W-s!(bJD_ zVhnNWC318~ZhaGhg&9=eG2+CFe|C~+z^_U0<5Oh#|Lp$0KuUfjc?eaowzi&-&sP+; z?h1q|P*wFqctc}jvo1k0IOyns7I?}6t?-ltUx5QW1CMr^l>`0drT_o{07*qoM6N<$ Ef_pXL9RL6T literal 0 HcmV?d00001 diff --git a/lib/src/main/resources/drawable/neo4jFormat.png b/lib/src/main/resources/drawable/neo4jFormat.png new file mode 100644 index 0000000000000000000000000000000000000000..26063a8ee9b5eb28d055a6d7f05b0022992d8861 GIT binary patch literal 1355 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 literal 0 HcmV?d00001 diff --git a/lib/src/main/resources/drawable/sqlFormat.png b/lib/src/main/resources/drawable/sqlFormat.png new file mode 100644 index 0000000000000000000000000000000000000000..625793a4ae3cd7acfc4a9990100ebde9d116ced3 GIT binary patch literal 1131 zcmV-x1eE)UP)~62L$} z6BHlx(P((()jvVsebfgZc~OIJ#^5cfArTrfYDBP9#ZuBs=w&*+OlQuVvzHHrR>Dk2 z62D|;=VX81`p#NwuPxli=;-LMF~JFO{FhOC5X1vCMY2uq@jN73dFQB=KD zC=?di1u`;nWW@8lPrYPkTRO8p=O;Rim*_ym)D_scLRgunx-nB(TmEGO;_rM<&y9_Z zRqF9w*H3)eIq+`Y^)kEt$Tp0VAd&7Pk?u=m@<$S-#ql>+7q86%@73koXrp6w>1X0- z{XZG(K&foprCgfyP@ZnYa>b;Io7Gyb{P*V=G5Co*ezFtCO<|k>;{>RUP#a?7Dp9yX zr94LzuAnB3xSgo3*oFowSQGR@&7K_f)oyT`5v&b~ zs>=<}Yt<#Nwbs$m(P7{Brvbt+>=6+ym&>=@I9hX}a0S}NdL*M*n{7fzu) z|4hGPFAW?RDuMFX&6^oib$W7g*;Tc*`Kd2FFUXK?-Aj<}!t-+=&Q^*ZoIUq;vqW?E z_jZ8&heiPSswPShL*NqJId6=@3@}FQv-WZRi-8; z5D|I@2U(b%A={D1^ZkdMw|gLN25}#KQB{8g;QEy-+_`m&S}kN{afvvNxI6g=RxNAA zBJ+2rdG4hn?CI{VZ_o_ur4s!^L-g(2$Ncm(w{P6w(8-ek{C4$fL!A+|i*Yz>n@#cy z$B*;p^oCCGrh|2%~002ovPDHLkV1k~h8hHQ! literal 0 HcmV?d00001 diff --git a/lib/src/main/resources/magnifier.png b/lib/src/main/resources/magnifier.png deleted file mode 100644 index 841599d36baefd6290842210e287dafe6ad62352..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12154 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

JT0?F=a(o7FytdoMfDFax`)G>d*J zs?bhpCSg|Qpq$J=nQKlR+!lulZI(_sJmruCgbtVIQU`B%N+BYD-zufR%*6o31xgQ- zHKt5K^zDt!v5;;zLMR1mS~O%hP6$EF^oBfkU_H#}cQzc-%0kim`!-Iak?#(j?h|`# zXT~>G9_2WA%BU(4`Z=qsD(`_4qSvtto+7J8ihkb0p$;y`F$mdvF{Z!*rYV`bsJ5QSc``Z0ui2Oqv>Vw2kPMZ`Yu2y)DuLWqPR_h9`QnD@I&PH22n)<%*k-pVqo{Aw3|tCna>wM?`AM{&v@tE zs-qCG_kCzn($ECmJxvznaNklAQi>oQwOaf3 zyG3s=h8L3J8@|*suU&h+6Xx}?Vjjz5bYWT0)E$)k!2SVr;EA%PMp67AYKeDFm7?-n zn3q64k3IZUT~6q+6hf^wq8jB@x^CzOe`!N4_FF(*tqALcmow?}r;P;*hEa~rdZ_bm zxF|vgirE~rYkzRyfof~dlDAFoBXb0OIgI~3V75{jsV3_&b|74aDem!R2FNyPB+TFS zH(yQEZGJqG+)+=k0Jd|Xg8P0^0HIT*=TYv9c#7Y*q`>$6ig^E!>i?LSe}k5$|u0Hf2?Hueu-3! z_s-(d4^O>9d4;pi^JQYkh^)HTupB+Q8PR}c;up&p{h3LCmDZ8FY|7*^n-?CNeI~$` zGAT%k>f6tC=LR0RsN%7fXMJ#i)dm8l;avtye@HD4bd~=tHu~jwa6LZF0j5Tgt}2i0 zjjnvdBFt<{No?lqMIj8gWCfTgO>MBi1?;5(8Q|7WJnz1t@K{5<@ck5)htc6K33@85 zwxf4`<#B{$vfATNanI_&ugO~7aAYsvu?kDdWQ~URg2jx21MV{qBlG#Uz*XLNS7SB{ z&P1WwtUb#pOwE+6lrem}g1{{P99-^NPJ_0Z$_u#j!$M98CW7keogl3wXsi0xTE2I6 z=CCH4EiO!N(pVT~V-iL#oa54q+6pkhCTnf7(=eMqALpiCW|7_JfQq{*tubxE-N z>FH&1Z=(c4)kpD&AIGhj<=i7Tz2BnSu1ux$IDuiSoJ&`t^oF)-L@0G!D z#_}^D=IDQKjpC%MJp5I=Vw;|;vz|&-hj|g7T^ZoK3)xJ$YceO{npOw~=0L0c5X#|X z#;5NnI<;-I>&B=qImMoO@8^Mo#uJ5rZsn08S5FN&x9=+Ut7 z#W3ZJ>Wl!+^xGpe(ciuEUk}vfA5Kv$Fh(hB=}wwlg`EfeaOcbIFa&ynBwUhH-p{C# z_Rl-DQVQoL68RliQ5e^<#{>U*{0n)dBF{qU@5w|PuZiPhnX4h`x6Mw8#GZDCPcKZ( zeB$E}Uba_8-Vjb)SH<{%ZGjKLu-VAEfwPscbCb8DkBN*e%&2E{a_6hyF50h=H`rSR z!{p(UkcFpj1y^{hYsbdI>g(Brz$gJ5Wz?yXd~;r@i_f$eW0USvNLg`B{P)bb|+> z@mFaTz~A|?S!Y$ z&Ny6qI~7UVICdfEgTHaNK!zRVAKokHHzar{0LvHP`Oe`6M81uJ@X3rs0X!X_FfsGv z&aCNZL=^TMQ#5eCIKHMv`Dju2U`ss>C@s5TmV?c>FN?GB32SpKDlnWCwf+A#7Af0j3OZGtyBRYjSU)QL|R!WeEr^K0y%5h zgJZ=|arNjcizK2ai>w|E#pV?Pn#^vOfPcClZ#S8-B2`$#>>82!KT*k|*eX zOE8hXQhD?Cf`4j=$9OJEjuU1xY8ks;5b;a(-P;c@3nTQWTLL4ZUA|q>%8R&}KPXF{ zuYmcC7Bc<7ryk_C4ygXL|5(J|GV^2aYVe6SlI8d}-?xp@LDKE`;bePioFUBLVZIUK$#3M7*z z{gAns;VyJF-bi_c;IjMgjn8|<*THTCFm%J71Ydfz>9;zPvZOOTzu$Joi{&993q}>9 z`a(avdu_qgn{-fK8@1@k-3N(4(6Ec8Z2J6iiHvG+c(>B0;^pCoXQL>`1+{rOlemH8 z8+RE*K7Jx~dtL4*Q>PwJ=l|wPPF-WozfOp7^rxd%3Wq)1dQ6ql`KNZ`jEB~Tfa9mc zq!q=X_jNQ_(1Iiy6a~&VJLwL(>l&Fy6-Nc~Q|BNERyFY3r_T?|G6a@ZN7i2g<16nf zS^_})Sk>AbmS>too1Nzk&+?NVo7KvJM}GzdO7=eQyZrVJkD_o=cbsX^UH36QKzky% z+9o)QU{PBhQK$=lklbNsqHePHhsdxC;)J&7!#}m5>^R_?q56x9R+h9BS$^hG!B;C! zXjdiXuBx&bO>w(LM_pi%wf5tC2(H`q4-bS=7c(@yED*8lpMxY3=4Yj(5f}bviZ3-X zap5~JUJxTtWj8K#MqaUBD#9*aecidQI>{L#(ZmSaHHg{$zLdydy|7qCMq?W7(foyZ zHK?xCksh?GXZii|5W?1?H9q4ED>K|SJAX#A!Bt;b0X)Oi412xFiV-vURKN<_r2G6p zTWu0z`L~6}PM&5aaiHypiN%-Py3F5*p0wfz(7-SQp)`d3Qf(H4M_CT}D}Ukt^`J;k zhhNVni~^Xbi}#jz$q~rUJ`;O}R|F-1TdaoSEDF>@f)oFIw$d-xUVBLDM|*q-I!gdQ zjr<0BuKMnqLWl$Y@UugX%|q^(2#U1{-_u=x%BR?EhR+3RSr>X-F9I?@x@L`bFo}TY zU(HM{Z#=V!1sOGY{o9}PbA4uGd*k>0)Ez3-m-Bb>)r9XrC zB#!d8!>&O2dE~sk5Qc#=iVBD+R~k}V5c2@IK|by1OHRoDuQB*2S|L;OsZW+kM8pE< zZQ#7txuehQa!F?23637-x2CzP35uAH^@DC=jzSh$ zd(*E&sLJRj{Efi!8NxeLpZDeiR?97H5H>dT`J8RfFJsv@1juuBV6uhsR|+-l8Ac6N zAm<2O%~o$5eXc?O_t(yAQ|HC`PBzv*QB+wXzb?_X$CJAwXK_Q5o!f>jm+hg4fg9l7 z^v|_4W!Tc?EMn70o`d6$hVz+{MSjsFv~wz&bQYCPYjZyY`Y-(_&b1xL8IUl=N)gh~ z?zL4HA80&)@u0ZnBmaOhU!-$akp@Sov^QAz12HnC|^%m@@dr0>4gAzS?U^ z+ZADPPNi%MDf;lm!DE*=^uDKlRE#%+cttnL!pS8##aTT`AXDr+dq?{`Wgb?v(5mZ* zSGC^%Rw-H^94Hnva7pdMe$SJ~q}E8R{8b2~EW@wA%K!LT_np3(!qAifq73$Zxl9Bi zjECD7&yV4!3Lf1XVH8Y~kI3!&0u{if`euGt1os2;F7F3Ld8^#_afbD#ANoSdGW~vP zX7v630tr==W@V?9-x2FQePstJ%bLJ@q`0k1;QG02Yk)Kq*?BRwih&9#L{H*#zA67o zitq`-Hwe^hNbyg*PH62qu-(P$!Y1FV? z&a*&3xU97!rjquVzdBIff^xO{N$we$eUO|eGYn-rGQ?)0X_{1I5+5|S(k{%)q@*#q zYd}euR8XnxtNsl*#^bj4ZP#u=dT0-(HVH0`4s%>6*7j(!igX_gbi6z+mG>;o(z`<< zmFke&zlRDQ&(UctWc%V^J$I-pCug}G2g8cK36ulng}Xq{{uP7CXH&lKw;%Y)JF5`9 z8E4cHG8@QVg!xm@sM*Y5b8H0)voYxAi3!E!!Pf;be-cW-y=ltz6r`LHBNyG@0=fHK zQ-8a`8P%uK3x@)!Np)jq&Evdv&THPG1Q&yUO!X_e+~fBlO%Ymk%SI@4W|IQR>l-tQ ztc(Y6_x2xTCCkswUo@+9#ARGucaF;t`Xk;Wf?m`1rpxjZ!xBn$QytfK2f?2SbzO^w;FYoR>MJO0vUOb;lRg2J6TT6B=O$is=$e1v6 z=J@Z3-L%RfIsx1ZDW#?X82)rRZoQESdVG16fd-pob_wVTWxQ-O?-x)XU)|{wIF6w{ z9-?G7m1|v8Mug?t9%nE}wjV=Ung4d}+7Oy?nFAjHTcb->1 zAwL{DSP+N|yrjAdy~8?5MX1{!)&=fpRR^JSTDP-_m{RpHi87bt2i08qTvOEnRy$=- zTc@L+ z@ugII)5S01FNSz**qeku_6&frqBl=M%W!hm@Pt#vOBz-s_d~7(E-B{~J#~_kz3J}; z#THNA`g?{}`>`Mz{yFjB%d9?FYhgvbd>N3YA4l$j?x(MOnMu(t_48&?wIzm>t6@fs zveA8rhM#n5kC3JbE+PGq-a3z#)xwM(79M!f_3}F;(GY6QN+%?x6k4PJTS zV`}-6PKdN-o5t^0n4bMScZU?MsD<6>e&+E%NH$WvzSs5~P?3q90-*g@*=Hbh?;8bo z&1PJ7@V79d<^jiQp^WRNe&ToCjhx2;S8M0iSAgi2jVbGf0;};Y>o@qa?A9IN{a*X; zhgFQsZ$^A!ZmP|$85iXEGPe2EPb{n8K@k02PakM3jl?#tpm$hjGZG5DzVlm^rlO|?#joG75AnQViHFsD#+1aYt1he?k4-~ zE=bmLeiFoeH`zl^t~*AKI0Cs=DH<3vC9X zm96;g9c90M-xr$CU2J8CR+MtQ)l-|?1<@>o8OhWToM~e9-pa`VnBN=)@cc^jo*W?| z1r)mzh&$YUl#2E-T+oVcV4}YIB<@mt6J;bLwx32SG=IWlaW@1?X0D_|YxaJEV)q+5 z_rkZ17X-!Lt#>)sC2>WR(W7}UKO@<`*hCm~^Y5i@QKWYohsWJPqkG5?u2`XrjaNA& zcc`Hfcc_^(G7Bi9Mm&=g1gj&t8~;BV#AC{ZvhB3#kfRBd?5XJ5GTa>C zX1*-3)+(W(mG)$9h8CdMdyY?BSIBH>3kDVWun{^Q=Y2g)#yqxlQ37wev4= zO;l)P(vya{ex%{+bJc*9s8?Y|iq_eBj9C6Xbq?s}2bbGQ;OZyNBf@SXvfRWhqeM$p zynp0J(q2SVXBOT+%3V2g$VE+Q0q@7l`+d`xyOez3iVS5~daX!hW53Ava{aHX4QVy4LWKU{QYXT2i^jIr<&iKCng!ihUm2Fho2WlRibVW2tC`%ljop|g zJ;a-WTZ7L8>0|%3p@rM}6A4Pe2RqlRJN<;I26e3Qd5}F8E};G@uYVW%=0eTasdr;< z&ShELbHC;WM5D0MJ*%8ycXtVu-$(PA&r3TWd-)gM_IC+D5-KSvDj_Kfl`(}%%1g+| sOUm7cLgk^*P`?i(|A&B^$4e)Nfd9JyjR;rEod7^b^RdQ9HDvhz0{gT+ZU6uP From 98803f302b948aaa493a33dc9ad38b03e35083c8 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Fri, 28 Apr 2023 22:58:18 +0300 Subject: [PATCH 167/212] refactor: architecture of database interaction with trees - rewrite logic TreeManagers (#24). --- lib/src/main/kotlin/controller/Controller.kt | 4 +- .../kotlin/databaseManage/AVLTreeManager.kt | 102 ++++++++++-------- .../kotlin/databaseManage/BINTreeManager.kt | 82 +++++++++----- .../kotlin/databaseManage/RBTreeManager.kt | 63 +++++------ .../main/kotlin/databaseManage/TreeManager.kt | 31 ++++-- .../databaseSave/jsonFormat/JsonRepository.kt | 2 +- 6 files changed, 169 insertions(+), 115 deletions(-) diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index 8071dab..9059f60 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -11,7 +11,7 @@ class Controller { private val binManager = BINTreeManager() fun showFiles(): List> { - avlManager.initDatabase("DBname") +// avlManager.initDataBase("DBname") return listOf(rbManager.getSavedTreesNames(), avlManager.getSavedTreesNames(), binManager.getSavedTreesNames()) } @@ -36,4 +36,4 @@ class Controller { /* val BINtree = BINStruct>() BINtree.restoreStruct(preOrder.toList()) - */ \ No newline at end of file + */ diff --git a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt index 99cb887..9cc335f 100644 --- a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt @@ -5,35 +5,43 @@ import databaseSave.sqlite.SQLiteRepositoryExposed import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json +import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer import treelib.avlTree.AVLStruct import treelib.avlTree.AVLVertex import treelib.commonObjects.Container -import java.io.File -const val AVL_DEFAULT_NAME = "NewTreeAVL" -const val DB_DEFAULT_NAME = "avlDB.db" -class AVLTreeManager: TreeManager, DrawableAVLVertex>> { +class AVLTreeManager : TreeManager< + Container, + DrawableAVLVertex>, + AVLNode>, + AVLStateContainer>, + AVLVertex>, + AVLStruct>, + > { private val db = SQLiteRepositoryExposed() - private var avlTree = AVLStruct>() - override var currentTreeName = AVL_DEFAULT_NAME - private set - - private var dataBaseName = DB_DEFAULT_NAME - private set + init { + db.initDataBase(AVL_DB_DEFAULT_NAME) + } 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(DrawableAVLVertex(value = el.value, height = el.height.toInt(), x = -0.0, y = -0.0)) + for (el in drawVertex) ans.add( + DrawableAVLVertex( + value = el.value, + height = el.height.toInt(), + x = -0.0, + y = -0.0 + ) + ) return ans } @@ -42,54 +50,55 @@ class AVLTreeManager: TreeManager, DrawableAVLVertex = Json.decodeFromString>(data) - fun initDatabase(dbName: String) { - db.initDataBase(dbName) - dataBaseName = dbName - } - - override fun initTree(treeName: String): MutableList>> { - avlTree = AVLStruct() - currentTreeName = treeName - - if (db.isTreeExist(treeName)) { - val vertexes = db.getTree(treeName, ::deserialization) - avlTree.restoreStruct(drawVertexToVertex(vertexes)) + override fun initTree(name: String, tree: AVLStruct>): MutableList>> { + if (db.isTreeExist(name)) { + val vertexes = db.getTree(name, ::deserialization) + tree.restoreStruct(drawVertexToVertex(vertexes)) return vertexes } return mutableListOf() } - override fun getVertexesForDrawFromTree(): MutableList>> { - return vertexToDrawVertex(avlTree.preOrder()) + override fun getVertexesForDrawFromTree(tree: AVLStruct>): MutableList>> { + return vertexToDrawVertex(tree.preOrder()) } - override fun getVertexesForDrawFromDB(): MutableList>> { - return db.getTree(currentTreeName, ::deserialization) + override fun getVertexesForDrawFromDB(name: String): MutableList>> { + return db.getTree(name, ::deserialization) } - override fun saveTree( + override fun saveTreeToDB( + name: String, preOrder: List>>, inOrder: List>> ) { - db.deleteTree(currentTreeName) - db.saveTree(currentTreeName, preOrder.toMutableList(), ::serialization) + db.deleteTree(name) + db.saveTree(name, preOrder.toMutableList(), ::serialization) } - override fun deleteTree() { - db.deleteTree(currentTreeName) + override fun saveTreeToDB(name: String, tree: AVLStruct>) { + val info = vertexToDrawVertex(tree.preOrder()) + db.deleteTree(name) + db.saveTree(name, info.toMutableList(), ::serialization) } - override fun getSavedTreesNames(): List { + override fun deleteTreeFormDB(name: String) { + if (db.isTreeExist(name)) db.deleteTree(name) + } + + override fun getSavedTreesNames(): List = db.getAllSavedTrees() +/* { val treesNames = db.getAllSavedTrees() - val dirPath = System.getProperty("user.dir") + "/saved-trees/AVL-trees" - File(dirPath).mkdirs() - if (treesNames.isNotEmpty()) { - for (name in treesNames) { - File(dirPath, name).run { - createNewFile() - } - } - } + +// val dirPath = System.getProperty("user.dir") + "/saved-trees/AVL-trees" +// File(dirPath).mkdirs() +// if (treesNames.isNotEmpty()) { +// for (name in treesNames) { +// File(dirPath, name).run { +// createNewFile() +// } +// } +// } return treesNames.subList(0, treesNames.size) } @@ -99,4 +108,9 @@ class AVLTreeManager: TreeManager, DrawableAVLVertex) { if (avlTree.find(item) != null) avlTree.delete(item) } + */ + + companion object { + const val AVL_DB_DEFAULT_NAME = "avlDB.db" + } } diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index 42e5132..36140e8 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -3,31 +3,38 @@ package databaseManage import com.google.gson.reflect.TypeToken import databaseSave.jsonFormat.DrawableBINVertex import databaseSave.jsonFormat.JsonRepository +import treelib.avlTree.AVLStruct +import treelib.binTree.BINNode +import treelib.binTree.BINStateContainer import treelib.binTree.BINStruct +import treelib.binTree.BINVertex import treelib.commonObjects.Container import java.io.File -class BINTreeManager : TreeManager, DrawableBINVertex>> { +class BINTreeManager : TreeManager< + Container, + DrawableBINVertex>, + BINNode>, + BINStateContainer>, + BINVertex>, + BINStruct> + > { /*** using json format files ***/ - override var currentTreeName = BIN_DEFAULT_NAME - private set - private val dirPath = System.getProperty("user.dir") + "/saved-trees/BIN-trees" - private val jsonRep = JsonRepository(dirPath) - private var binTree = BINStruct>() - - override fun initTree(treeName: String): List>> { - binTree = BINStruct() - currentTreeName = treeName + private val jsonRep = JsonRepository(BIN_DB_DEFAULT_NAME) - if (this.isTreeExist(treeName)) { + override fun initTree( + name: String, + tree: BINStruct> + ): List>> { + if (this.isTreeExist(name)) { val typeToken = object : TypeToken>>>() {} - val preOrder = jsonRep.exportTree(treeName, typeToken).toList() + val preOrder = jsonRep.exportTree(name, typeToken).toList() - binTree.restoreStruct(preOrder.toList()) + tree.restoreStruct(preOrder.toList()) return preOrder } @@ -35,16 +42,38 @@ class BINTreeManager : TreeManager, DrawableBINVertex>>, inOrder: List>> ) { + jsonRep.saveChanges(preOrder.toTypedArray(), name) + } - jsonRep.saveChanges(preOrder.toTypedArray(), currentTreeName) + override fun saveTreeToDB(name: String, tree: BINStruct>) { + val info = vertexToDrawVertex(tree.preOrder()) + jsonRep.saveChanges(info.toTypedArray(), name) + } + private fun drawVertexToVertex(drawVertex: MutableList>>): MutableList>> { + val ans = mutableListOf>>() + for (el in drawVertex) ans.add(BINVertex(value = el.value)) + return ans } - override fun deleteTree() = jsonRep.removeTree(currentTreeName) + private fun vertexToDrawVertex(drawVertex: List>>): MutableList>> { + val ans = mutableListOf>>() + for (el in drawVertex) ans.add( + DrawableBINVertex( + value = el.value, + x = -0.0, + y = -0.0 + ) + ) + return ans + } + + override fun deleteTreeFormDB(name: String) = jsonRep.removeTree(name) override fun getSavedTreesNames(): List { val filesNames = File(dirPath).list()?.map { it.replace(".json", "") } @@ -56,24 +85,23 @@ class BINTreeManager : TreeManager, DrawableBINVertex) { - if (binTree.find(item) != null) - binTree.delete(item) - } + /* override fun delete(item: Container) { + if (binTree.find(item) != null) + binTree.delete(item) + } - override fun insert(item: Container) = binTree.insert(item) + override fun insert(item: Container) = binTree.insert(item)*/ - override fun getVertexesForDrawFromDB(): List>> { + override fun getVertexesForDrawFromDB(name: String): List>> { TODO("Not yet implemented") } - override fun getVertexesForDrawFromTree(): List>> = - binTree.preOrder().map { DrawableBINVertex(it.value) } + override fun getVertexesForDrawFromTree(tree: BINStruct>): List>> = + tree.preOrder().map { DrawableBINVertex(it.value) } fun cleanDB() = jsonRep.clean() companion object { - private const val BIN_DEFAULT_NAME = "NewTreeBIN" + const val BIN_DB_DEFAULT_NAME = "binDB" } - -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt index aacb20b..ca2778b 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -3,30 +3,33 @@ package databaseManage import databaseSave.neo4j.DrawableRBVertex import databaseSave.neo4j.Neo4jRepository import treelib.commonObjects.Container +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer import treelib.rbTree.RBStruct +import treelib.rbTree.RBVertex import java.io.File -class RBTreeManager : TreeManager, DrawableRBVertex>> { - - override var currentTreeName = RB_DEFAULT_NAME - private set +class RBTreeManager : TreeManager< + Container, + DrawableRBVertex>, + RBNode>, + RBStateContainer>, + RBVertex>, + RBStruct>, + > { private val neo4jDB = Neo4jRepository() - private var rbTree = RBStruct>() - + init { neo4jDB.open("bolt://localhost:7687", "neo4j", "password") } - override fun initTree(treeName: String): List>> { + override fun initTree(name: String, tree: RBStruct>): List>> { /*** orders.first = preOrder, orders.second = inOrder ***/ - rbTree = RBStruct() - currentTreeName = treeName - - if (this.isTreeExist(treeName)) { + if (this.isTreeExist(name)) { val orders: Pair>>, List>>> = - neo4jDB.exportRBtree(treeName) - rbTree.restoreStruct(orders.first, orders.second) + neo4jDB.exportRBtree(name) + tree.restoreStruct(orders.first, orders.second) neo4jDB.close() return orders.first } @@ -34,18 +37,23 @@ class RBTreeManager : TreeManager, DrawableRBVertex>>, inOrder: List>> ) { - - neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray(), currentTreeName) + neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray(), name) neo4jDB.close() } - override fun deleteTree() { + override fun saveTreeToDB(name: String, tree: RBStruct>) { + TODO() + } + + + override fun deleteTreeFormDB(name: String) { - neo4jDB.removeTree(currentTreeName) + neo4jDB.removeTree(name) neo4jDB.close() } @@ -63,37 +71,30 @@ class RBTreeManager : TreeManager, DrawableRBVertex) = rbTree.insert(item) override fun insert(item: Container) { if (rbTree.find(item) != null) rbTree.delete(item) - } +*/ - override fun getVertexesForDrawFromDB(): List>> { - return neo4jDB.exportRBtree(currentTreeName).first.map { DrawableRBVertex(it.value, it.color) } + override fun getVertexesForDrawFromDB(name: String): List>> { + return neo4jDB.exportRBtree(name).first.map { DrawableRBVertex(it.value, it.color) } } - override fun getVertexesForDrawFromTree(): List>> { - return rbTree.preOrder().map { DrawableRBVertex(it.value, it.color) } + override fun getVertexesForDrawFromTree(tree: RBStruct>): List>> { + return tree.preOrder().map { DrawableRBVertex(it.value, it.color) } } - - private fun isTreeExist(treeName: String): Boolean { return neo4jDB.findTree(treeName) } fun cleanDB() { - neo4jDB.clean() neo4jDB.close() } - - companion object { - private const val RB_DEFAULT_NAME = "NewTreeRB" - } - } diff --git a/lib/src/main/kotlin/databaseManage/TreeManager.kt b/lib/src/main/kotlin/databaseManage/TreeManager.kt index bd7cfe5..5847a23 100644 --- a/lib/src/main/kotlin/databaseManage/TreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/TreeManager.kt @@ -1,25 +1,36 @@ package databaseManage import databaseSave.DrawableVertex +import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer +import treelib.abstractTree.TreeStruct +import treelib.abstractTree.Vertex import treelib.commonObjects.Container -interface TreeManager, DrawableVertexType : DrawableVertex> { +interface TreeManager< + Pack: Container, + DVertexType : DrawableVertex, + NodeType : Node, + State : StateContainer, + VertexType : Vertex, + StructType: TreeStruct + > { - val currentTreeName: String + fun initTree(name: String, tree: StructType): List - fun initTree(treeName: String): List + fun getVertexesForDrawFromTree(tree: StructType): List - fun getVertexesForDrawFromTree(): List + fun getVertexesForDrawFromDB(name: String): List - fun getVertexesForDrawFromDB(): List + fun saveTreeToDB(name: String, preOrder: List, inOrder: List) - fun saveTree(preOrder: List, inOrder: List) + fun saveTreeToDB(name: String, tree: StructType) - fun deleteTree() + fun deleteTreeFormDB(name: String) fun getSavedTreesNames(): List - fun insert(item: Container) - - fun delete(item: Container) +// fun insert(item: Container) +// +// fun delete(item: Container) } diff --git a/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt index 60b4aed..0db748e 100644 --- a/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt +++ b/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt @@ -47,4 +47,4 @@ class JsonRepository(private val dirPath: String) { } -} \ No newline at end of file +} From 80e5388895aeef0d7afe6ffb01595736048a2a44 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 29 Apr 2023 02:09:25 +0300 Subject: [PATCH 168/212] feat: Implement a user input validator --- lib/src/main/kotlin/UserInputValidation.kt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 lib/src/main/kotlin/UserInputValidation.kt diff --git a/lib/src/main/kotlin/UserInputValidation.kt b/lib/src/main/kotlin/UserInputValidation.kt new file mode 100644 index 0000000..bf997cc --- /dev/null +++ b/lib/src/main/kotlin/UserInputValidation.kt @@ -0,0 +1,22 @@ +import java.util.* + +private val INVALID_WINDOWS_SPECIFIC_CHAR = arrayOf('"', '*', ':', '<', '>', '/', '\"', '|', '?') +private val INVALID_UNIX_SPECIFIC_CHAR = arrayOf('/') // add '\000' + +fun validate(fileName: String): Boolean { + if (fileName == "" || fileName.length > 255) { + return false + } + val os = System.getProperty("os.name").lowercase(Locale.getDefault()) + return when { + os.contains("win") -> Arrays.stream(INVALID_WINDOWS_SPECIFIC_CHAR) + .noneMatch { ch -> fileName.contains(ch.toString()) } + + os.contains("unix") || os.contains("linux") || os.contains("mac") -> Arrays.stream( + INVALID_UNIX_SPECIFIC_CHAR + ).noneMatch { ch -> fileName.contains(ch.toString()) } + + else -> true + } + +} \ No newline at end of file From a004976e75efbd6a6c8ce9394308b72bca557d24 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 29 Apr 2023 14:19:46 +0300 Subject: [PATCH 169/212] Implement saveDialog to save trees --- lib/src/main/kotlin/SaveDialog.kt | 152 ++++++++++++++++++ lib/src/main/resources/drawable/folder.webp | Bin 16522 -> 0 bytes lib/src/main/resources/drawable/maximize.png | Bin 0 -> 299 bytes lib/src/main/resources/drawable/minimize.png | Bin 0 -> 191 bytes lib/src/main/resources/drawable/save.png | Bin 0 -> 454 bytes .../main/resources/drawable/second_folder.png | Bin 5978 -> 0 bytes 6 files changed, 152 insertions(+) create mode 100644 lib/src/main/kotlin/SaveDialog.kt delete mode 100644 lib/src/main/resources/drawable/folder.webp create mode 100644 lib/src/main/resources/drawable/maximize.png create mode 100644 lib/src/main/resources/drawable/minimize.png create mode 100644 lib/src/main/resources/drawable/save.png delete mode 100644 lib/src/main/resources/drawable/second_folder.png diff --git a/lib/src/main/kotlin/SaveDialog.kt b/lib/src/main/kotlin/SaveDialog.kt new file mode 100644 index 0000000..c478c96 --- /dev/null +++ b/lib/src/main/kotlin/SaveDialog.kt @@ -0,0 +1,152 @@ + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.input.key.* +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.WindowPosition +import androidx.compose.ui.window.rememberDialogState + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) +@Composable +fun saveDialog( + buttonState: List>, + clickButtonState: MutableState, + fileName: MutableState +) { + + var validateInputState by remember { mutableStateOf(true) } + var successDialogState by remember { mutableStateOf(false) } + var warningDialogState by remember { mutableStateOf(false) } + val saveButtonState = remember { mutableStateOf(false) } + + + + if (clickButtonState.value) { + Dialog( + onCloseRequest = { clickButtonState.value = false }, undecorated = true, + state = rememberDialogState( + position = WindowPosition(Alignment.Center), // поменять ?? + size = DpSize(300.dp, 200.dp) + ), + resizable = false + ) + { + Card( + modifier = Modifier.fillMaxHeight(1f).fillMaxWidth(1f) + .shadow(elevation = 10.dp, ambientColor = Color.Gray), + shape = RectangleShape, + border = BorderStroke(0.5.dp, Color.LightGray), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background, + contentColor = MaterialTheme.colorScheme.primary + ) + ) { + + Column(modifier = Modifier.fillMaxSize()) { + + Column(modifier = Modifier.height(50.dp)) { + tittleAndButton("Save tree", clickButtonState) + } + + Divider(modifier = Modifier.height(1.dp).fillMaxWidth()) + + Column( + modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp, vertical = 5.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween + ) { + TextField( + value = fileName.value, + singleLine = true, + shape = RoundedCornerShape(4.dp), + onValueChange = { fileName.value = it }, + modifier = Modifier + .onPreviewKeyEvent { + when { + (!it.isShiftPressed && it.key == Key.Enter && it.type == KeyEventType.KeyUp) -> { + validateInputState = validate(fileName.value) + if (validateInputState) { + successDialogState = true + } else { + warningDialogState = true + } + true + } + else -> { + warningDialogState = false + successDialogState = false + saveButtonState.value = false + false + } + } + } + .align(Alignment.CenterHorizontally).border(2.dp, Color(47, 105, 215)), + colors = TextFieldDefaults.textFieldColors( + containerColor = if (!warningDialogState) MaterialTheme.colorScheme.background + else Color(232, 169, 169) + ), + isError = !validateInputState, + placeholder = { Text(text = "Enter a filename", color = Color.Gray) }, + ) + Text( + text = when { + successDialogState -> "Tree successfully saved to ${fileName.value}" + warningDialogState -> "Invalid tree name for saving" + else -> "" + }, + color = if (successDialogState) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error, + fontFamily = FontFamily.Monospace, + fontSize = 12.sp + ) + + Row(horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.Bottom) { + BottomButtons( + clickButtonState, + saveButtonState, + "Save", + "Cancel" + ) + } + + if (saveButtonState.value) { + validateInputState = validate(fileName.value) + if (validateInputState) { + successDialogState = true + } else { + warningDialogState = true + } + } + + } + + } + + } + } + } + + topAppIconButton( + painterResource("/drawable/save.png"), + buttonState, + 1, + MaterialTheme.colorScheme.onPrimary, + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.background, + clickButtonState + ) +} diff --git a/lib/src/main/resources/drawable/folder.webp b/lib/src/main/resources/drawable/folder.webp deleted file mode 100644 index bf0a8ab69b0da44629b6f9983a155831f00f410d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16522 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 diff --git a/lib/src/main/resources/drawable/maximize.png b/lib/src/main/resources/drawable/maximize.png new file mode 100644 index 0000000000000000000000000000000000000000..906ffe37101b215bae246e8ca5fb4ed17385cbba GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEX7WqAsj$Z!;#Vf2?- zjUdc;OxLp)$dD{?jVKAuPb(=;EJ|f4FE7{2%*!rLPAo{(%P&fw{mw=TsAz?!i(`mK z=i94>e2k6~Z4cWySUWXDxO$ty6!RX0qH@P0~MitiNWlKF*R+*T1uq7E018_1RkvTC%mCc1r`b}BhG`TvdWhUagX zAK=)%tX~Tmq=Z1OmkJzd{3y1CYoAwmwW(H4JKbLh*2~7ZTs&1YD literal 0 HcmV?d00001 diff --git a/lib/src/main/resources/drawable/minimize.png b/lib/src/main/resources/drawable/minimize.png new file mode 100644 index 0000000000000000000000000000000000000000..c4abda9d761a3d2b37a3a3bc6c5fe5af1e86380c GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEX7WqAsj$Z!;#Vf2?- zjUdc;OxLp)$dD{?jVKAuPb(=;EJ|f4FE7{2%*!rLPAo{(%P&fw{mw=TsL00C#WBRA zGdV$mb#a0SPwjyd2M#2?- zjUdc;OxLp)$dD{?jVKAuPb(=;EJ|f4FE7{2%*!rLPAo{(%P&fw{mw>;fq~K4)5S5w zqx0>x-CoR&B5V)zm6ix-%R6vrIc?Z@;o58F8xb5?&x?Nu#BphF6h3+=NH8}y$9YXq zN5qYamyed-h>V$b_iR>^!jHoS#?|M3{x6>K@Jv`uqR8R8K53h0lI0&RKHYJ=V%82J zqx>ZPcFo^XVH;;mz9Gq1&~x{IdAHl8Otln)$&MQ&b1Hvu*cr50+boc?{vyg!FYX)o zEjQ+f(q54c*JmHwig~`4b5!tucH*2b!JG3yM0hUCIv3UXPjB4bapuQAA^lSvJso*b zVtE&}lwPPP2#8D)S$MtukN$_+C$gV}t{DbQdM^6g!c%&a){J8lUF^}y*1&7_KAIS)wSZ^DzIJVo8k7{ tyY|0cZgl|^ayNv(< literal 0 HcmV?d00001 diff --git a/lib/src/main/resources/drawable/second_folder.png b/lib/src/main/resources/drawable/second_folder.png deleted file mode 100644 index d2516d6005a9973c70e32b3b810c8e7a0f2d9653..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5978 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 From 58216ebaab18ff61a3bbfd6333a19cd363592cc9 Mon Sep 17 00:00:00 2001 From: Artem Date: Sat, 29 Apr 2023 16:10:15 +0300 Subject: [PATCH 170/212] feat: Implement DeleteDialog for tree removal --- lib/src/main/kotlin/DeleteAlertDialog.kt | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 lib/src/main/kotlin/DeleteAlertDialog.kt diff --git a/lib/src/main/kotlin/DeleteAlertDialog.kt b/lib/src/main/kotlin/DeleteAlertDialog.kt new file mode 100644 index 0000000..ea3914b --- /dev/null +++ b/lib/src/main/kotlin/DeleteAlertDialog.kt @@ -0,0 +1,59 @@ + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.AlertDialog +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.unit.dp + + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun deleteDialog( + treeName: String, + buttonState: List>, + clickButtonsState: MutableState +) { + + val deleteDialogState = remember { mutableStateOf(true) } + + if (clickButtonsState.value) { + AlertDialog( + backgroundColor = MaterialTheme.colorScheme.background, + onDismissRequest = { clickButtonsState.value = false}, + text = { Text(text = " Are you sure you want to delete $treeName ?", color = MaterialTheme.colorScheme.primary) }, + buttons = { + + Row(modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp), horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.Bottom) { + BottomButtons(deleteDialogState, clickButtonsState,"Cancel", "Delete") + } + } + ) + } + + if (!deleteDialogState.value) { + // отправляем запрос в контроллер на удаление + deleteDialogState.value = true + clickButtonsState.value = false + } + + topAppIconButton( + rememberVectorPainter(Icons.Outlined.Delete), + buttonState, + 0, + MaterialTheme.colorScheme.onPrimary, + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.background, + clickButtonsState + ) + +} From 0feaeb9bf21309a986979f9f080768622f207354 Mon Sep 17 00:00:00 2001 From: Artem Date: Sun, 30 Apr 2023 10:01:03 +0300 Subject: [PATCH 171/212] refactor: Structure files (ui) --- lib/src/main/kotlin/main.kt | 339 ++++++------------ lib/src/main/kotlin/ui/ControlFields.kt | 100 ++++++ .../main/kotlin/{ => ui}/DeleteAlertDialog.kt | 3 +- lib/src/main/kotlin/{ => ui}/FileDialog.kt | 153 ++++---- lib/src/main/kotlin/ui/Foo.kt | 25 ++ lib/src/main/kotlin/{ => ui}/SaveDialog.kt | 16 +- lib/src/main/kotlin/ui/TopAppBar.kt | 265 ++++++++++++++ .../kotlin/{ => ui}/UserInputValidation.kt | 4 +- 8 files changed, 585 insertions(+), 320 deletions(-) create mode 100644 lib/src/main/kotlin/ui/ControlFields.kt rename lib/src/main/kotlin/{ => ui}/DeleteAlertDialog.kt (98%) rename lib/src/main/kotlin/{ => ui}/FileDialog.kt (72%) create mode 100644 lib/src/main/kotlin/ui/Foo.kt rename lib/src/main/kotlin/{ => ui}/SaveDialog.kt (92%) create mode 100644 lib/src/main/kotlin/ui/TopAppBar.kt rename lib/src/main/kotlin/{ => ui}/UserInputValidation.kt (96%) diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index 505c45a..b0c7c84 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -1,56 +1,109 @@ -import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.window.WindowDraggableArea import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Menu -import androidx.compose.material.icons.outlined.Delete -import androidx.compose.material.icons.outlined.KeyboardArrowRight -import androidx.compose.material.icons.outlined.Search +import androidx.compose.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.input.key.* +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.onPointerEvent +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpOffset -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.compose.ui.window.Window -import androidx.compose.ui.window.application +import androidx.compose.ui.unit.* +import androidx.compose.ui.window.* import controller.Controller +import ui.* +import ui.State import java.awt.Dimension -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) fun main() = application { - Window( - onCloseRequest = ::exitApplication - ) { - this.window.minimumSize = Dimension(800, 600) - Scaffold( - topBar = { - myTopAppBar() - }, - content = { + val clickButtonsState = List(7) { remember { mutableStateOf(false) } } - } - ) + val windowState = rememberWindowState(placement = WindowPlacement.Maximized) + + val controller = Controller() + + var state = remember { State() } + if (!clickButtonsState[6].value) { + Window( + onCloseRequest = ::exitApplication, + undecorated = true, + state = windowState + ) { + + this.window.minimumSize = Dimension(800, 600) + Scaffold( + topBar = { + WindowDraggableArea { + myTopAppBar(clickButtonsState, windowState) + } + }, + content = { + MaterialTheme( + colorScheme = lightColors, + typography = myTypography + ) { + + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + .offset(0.dp, 50.dp) + .verticalScroll(rememberScrollState()) + .horizontalScroll(rememberScrollState()) + .pointerInput(Unit) { + detectDragGestures { change, dragAmount -> + change.consume() + state.handleScreenDrag(dragAmount) + + } + } + + ) { + controlFields(controller) + + } + + } + + } + ) + + + } } + } val lightColors = lightColorScheme( background = Color(255, 255, 255), primary = Color(34, 35, 41), secondary = Color(47, 105, 215), - onSecondary = Color(148, 150, 166), // 208, 223, 252 - tertiary = Color(208, 223, 252) + onSecondary = Color(148, 150, 166), + tertiary = Color(208, 223, 252), + onTertiary = Color(51, 51, 51), + onPrimary = Color(53, 55, 63) ) -private val myTypography = Typography( +val myTypography = Typography( headlineMedium = TextStyle( fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold, @@ -58,227 +111,49 @@ private val myTypography = Typography( ) ) -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun myTopAppBar() { - val controller = Controller() - val showFiles = controller.showFiles() - MaterialTheme( - colorScheme = lightColors, - typography = myTypography - ) { - val expanded = remember { mutableStateOf(false) } - val expandedCreateNested = remember { mutableStateOf(false) } - val expandedOpenNested = remember { mutableStateOf(false) } - var mainMenuWidth by remember { mutableStateOf(0.dp) } - TopAppBar( - title = { Text("") }, - colors = TopAppBarDefaults.centerAlignedTopAppBarColors( - containerColor = MaterialTheme.colorScheme.primary, - navigationIconContentColor = MaterialTheme.colorScheme.background - ), - navigationIcon = { - IconButton( - onClick = { - expanded.value = !expanded.value - }, - colors = IconButtonDefaults.iconButtonColors( - containerColor = MaterialTheme.colorScheme.primary, - contentColor = MaterialTheme.colorScheme.background - ), - enabled = true, - ) { - Icon( - imageVector = Icons.Filled.Menu, - contentDescription = "Localized description", - ) - } - - DropdownMenu( - expanded = expanded.value, - onDismissRequest = { expanded.value = false }, - focusable = false, - ) { - DropdownMenuItem( - text = { Text("Create") }, - trailingIcon = { arrowIcon() }, - onClick = { - expandedCreateNested.value = !expandedCreateNested.value // !!! - expandedOpenNested.value = false - // main.value = true - }, - modifier = Modifier.onGloballyPositioned { - mainMenuWidth = it.size.width.dp - 40.dp - } - ) - createNestedMenu(expandedCreateNested, expanded, mainMenuWidth) - - DropdownMenuItem( - text = { Text("Open") }, - trailingIcon = { arrowIcon() }, - onClick = { - expandedOpenNested.value = !expandedOpenNested.value - //showFiles = controller.showFiles() - // main.value - } - ) - openNestedMenu(expandedOpenNested, expanded, mainMenuWidth, showFiles) - - DropdownMenuItem(text = { Text("Save") }, onClick = {}) +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun topAppIconButton( + painter: Painter, + buttonsState: List>, + index: Int, + color: Color, + disabledColor: Color, + iconColor: Color, + clickButtonState: MutableState, +) { - DropdownMenuItem(leadingIcon = { deleteIcon() }, text = { Text("Delete") }, onClick = {}) - } - } + IconButton(onClick = { + clickButtonState.value = !clickButtonState.value + }, modifier = Modifier.fillMaxHeight().width(50.dp).clip(RectangleShape) + .onPointerEvent(PointerEventType.Enter) { buttonsState[index].value = true } + .onPointerEvent(PointerEventType.Exit) { buttonsState[index].value = false }, + colors = IconButtonDefaults.iconButtonColors( + contentColor = if (buttonsState[index].value) MaterialTheme.colorScheme.background else iconColor, + containerColor = if (buttonsState[index].value) color else disabledColor ) + ) { + Icon(painter, contentDescription = null) } } @Composable -fun deleteIcon() = Icon( - imageVector = Icons.Outlined.Delete, +fun searchIcon() = Icon( + imageVector = Icons.Outlined.Search, contentDescription = null ) @Composable fun arrowIcon() = Icon( imageVector = Icons.Outlined.KeyboardArrowRight, - contentDescription = null + contentDescription = null, + modifier = Modifier.size(16.dp) ) @Composable -fun createNestedMenu( - expandedNested: MutableState, - expandedMainMenu: MutableState, - mainMenuWidth: Dp, -) { - val treesNames = listOf("Red black tree", "AVL tree", "Binary tree") - AnimatedVisibility( - visible = expandedNested.value - ) { - DropdownMenu( - expanded = expandedNested.value, - offset = DpOffset(mainMenuWidth, 0.dp), - onDismissRequest = { - expandedNested.value = false - expandedMainMenu.value = false - } - ) { - - repeat(3) { index -> - DropdownMenuItem( - text = { Text(treesNames[index]) }, - onClick = { - expandedNested.value = false - expandedMainMenu.value = false - } - ) - } - - } - - } -} - -@Composable -fun openNestedMenu( - expandedNested: MutableState, - expandedMainMenu: MutableState, - mainMenuWidth: Dp, - showFiles: List>, -) { - val treesNames = listOf("Red black tree", "AVL tree", "Binary tree") - val a = remember { mutableStateOf(true) } // поменять на лист из 3?? - val expandedTreesNames = listOf(remember { mutableStateOf(false) }, - remember { mutableStateOf(false) }, - remember { mutableStateOf(false) }) - var offsetTreesNames by remember { mutableStateOf(0.dp) } - - AnimatedVisibility( - visible = expandedNested.value - ) { - DropdownMenu( - expanded = expandedNested.value, - offset = DpOffset(mainMenuWidth, 0.dp), - onDismissRequest = { - expandedNested.value = false - }, - modifier = Modifier.onGloballyPositioned { - offsetTreesNames = it.size.width.dp - 40.dp - } - ) { - repeat(3) { index -> - DropdownMenuItem( - text = { Text(treesNames[index]) }, - onClick = { - expandedTreesNames[index].value = a.value - a.value = true - }, - trailingIcon = { arrowIcon() } - ) - treesNames( - expandedTreesNames[index], - expandedNested, - showFiles[index], - offsetTreesNames, a, index // remove a !!!! - ) - expandedMainMenu.value = expandedNested.value - } - - } - - } - -} - -@Composable -fun treesNames( - expandedNested: MutableState, - expandedOpenNested: MutableState, - showFiles: List, - offset: Dp, - a: MutableState, - index: Int -) { - - val dirPath = System.getProperty("user.dir") + "/saved-trees" - val dirFiles = listOf("$dirPath/RB-trees", "$dirPath/AVL-trees", "$dirPath/BIN-trees") - - val chooseSearch = remember { mutableStateOf(false) } - - val selectedTree = remember { mutableStateOf("null") } - - AnimatedVisibility( - visible = expandedNested.value - ) { - DropdownMenu( - expanded = expandedNested.value, - offset = DpOffset(offset, 0.dp), - onDismissRequest = { - a.value = !expandedNested.value - expandedNested.value = false - } - ) { - searchItem(dirFiles[index], expandedOpenNested, selectedTree) - repeat(showFiles.size) { index -> - DropdownMenuItem( - text = { Text(showFiles[index]) }, - onClick = { - expandedOpenNested.value = false - } - ) - } - - } - - } - +fun menuItemBackgroundColor(state: MutableState): Color { + return if (state.value) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.background } - -@Composable -fun searchIcon() = Icon( - imageVector = Icons.Outlined.Search, - contentDescription = null -) diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt new file mode 100644 index 0000000..ab46b98 --- /dev/null +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -0,0 +1,100 @@ +package ui + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.key.* +import androidx.compose.ui.unit.dp +import controller.Controller + +@Composable +fun controlFields(controller: Controller) { + + val addFieldState = remember { mutableStateOf(false) } + val findFieldState = remember { mutableStateOf(false) } + val deleteFieldState = remember { mutableStateOf(false) } + + Column(modifier = Modifier.offset(0.dp, 0.dp).padding(horizontal = 10.dp)) { + // add + controlField("Add", addFieldState) + Spacer(modifier = Modifier.height(2.dp)) + + // find + controlField("Find", findFieldState) + Spacer(modifier = Modifier.height(2.dp)) + + // delete + controlField("Delete", deleteFieldState) + + } + + if (addFieldState.value) { + // запрос на вставку + addFieldState.value = false + } + if (findFieldState.value) { + // запрос на поиск + findFieldState.value = false + } + if (deleteFieldState.value) { + // запрос на удаление + deleteFieldState.value = false + } + +} + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) +@Composable +fun controlField(buttonName: String, buttonState: MutableState) { + + var text by remember { mutableStateOf("") } + + var containerColor by remember { mutableStateOf(Color(237, 232, 232)) } + + OutlinedTextField( + value = text, + onValueChange = { text = it }, + singleLine = true, + label = { Text(buttonName) }, + colors = TextFieldDefaults.outlinedTextFieldColors( + containerColor = containerColor, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.onTertiary, + focusedLabelColor = Color(99, 95, 95), + unfocusedLabelColor = MaterialTheme.colorScheme.onTertiary, + ), + modifier = Modifier + .padding(start = 3.dp) + .width(150.dp) + .onFocusChanged { + containerColor = if (it.isFocused) { + Color(255, 255, 255) + } else { + Color(237, 232, 232) + } + }.onPreviewKeyEvent { + when { + (!it.isShiftPressed && it.key == Key.Enter && it.type == KeyEventType.KeyUp) -> { + buttonState.value = true + text = "" + true + } + + else -> { + false + } + } + + }, + trailingIcon = { + + }, + shape = RoundedCornerShape(8.dp) + ) + +} diff --git a/lib/src/main/kotlin/DeleteAlertDialog.kt b/lib/src/main/kotlin/ui/DeleteAlertDialog.kt similarity index 98% rename from lib/src/main/kotlin/DeleteAlertDialog.kt rename to lib/src/main/kotlin/ui/DeleteAlertDialog.kt index ea3914b..7078d41 100644 --- a/lib/src/main/kotlin/DeleteAlertDialog.kt +++ b/lib/src/main/kotlin/ui/DeleteAlertDialog.kt @@ -1,4 +1,4 @@ - +package ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -14,6 +14,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.unit.dp +import topAppIconButton @OptIn(ExperimentalMaterialApi::class) diff --git a/lib/src/main/kotlin/FileDialog.kt b/lib/src/main/kotlin/ui/FileDialog.kt similarity index 72% rename from lib/src/main/kotlin/FileDialog.kt rename to lib/src/main/kotlin/ui/FileDialog.kt index 69e7198..4479a27 100644 --- a/lib/src/main/kotlin/FileDialog.kt +++ b/lib/src/main/kotlin/ui/FileDialog.kt @@ -1,8 +1,9 @@ +package ui import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.gestures.forEachGesture import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -12,18 +13,17 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Close import androidx.compose.material.icons.rounded.KeyboardArrowDown import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.awt.awtEventOrNull import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontFamily @@ -33,32 +33,38 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.WindowPosition -import androidx.compose.ui.window.WindowState import androidx.compose.ui.window.rememberDialogState +import lightColors +import menuItemBackgroundColor +import searchIcon +import topAppIconButton import java.awt.Dimension -import java.awt.event.MouseEvent import java.io.File - -// +@OptIn(ExperimentalComposeUiApi::class) @Composable -fun searchItem(dirPath: String, expandedOpenNested: MutableState, selectedTree: MutableState) { +fun searchItem( + dirPath: String, + expandedNested: MutableState, + selectedTree: MutableState, + expandedOpenNested: MutableState +) { val dialogState = remember { mutableStateOf(false) } val minWidth = remember { mutableStateOf(200) } - //val selectFile = remember { mutableStateOf("null") } - + var pressedState by remember { mutableStateOf(false) } + val backgroundColorState = remember { mutableStateOf(false) } val enabledButtonIndex = remember { mutableStateOf(-1) } val files = findFiles(dirPath) if (dialogState.value) { - - MaterialTheme(colorScheme = lightColors) { // add typography - Dialog(//focusable = true, + MaterialTheme(colorScheme = lightColors) { + Dialog( onCloseRequest = { dialogState.value = false expandedOpenNested.value = false - selectedTree.value = if (enabledButtonIndex.value >= 0) files[enabledButtonIndex.value] else "null" + selectedTree.value = + if (enabledButtonIndex.value >= 0) files[enabledButtonIndex.value] else "null" // "" }, visible = dialogState.value, content = { WindowDraggableArea { @@ -74,15 +80,24 @@ fun searchItem(dirPath: String, expandedOpenNested: MutableState, selec ) } + } else if (pressedState){ + expandedNested.value = false + expandedOpenNested.value = false + selectedTree.value = + if (enabledButtonIndex.value >= 0) files[enabledButtonIndex.value] else "" // "" } + DropdownMenuItem( leadingIcon = { searchIcon() }, text = { Text("Search") }, onClick = { dialogState.value = true - //expandedOpenNested.value = false - } + pressedState = true + }, + modifier = Modifier.onPointerEvent(PointerEventType.Enter) { backgroundColorState.value = true } + .onPointerEvent(PointerEventType.Exit) { backgroundColorState.value = false } + .background(color = menuItemBackgroundColor(backgroundColorState)) ) } @@ -104,7 +119,7 @@ fun CompleteDialogContent( enabledButtonIndex: MutableState ) { Card( - modifier = Modifier.fillMaxHeight(1f).fillMaxWidth(1f).shadow(elevation = 1.dp, ambientColor = Color.LightGray), + modifier = Modifier.fillMaxHeight(1f).fillMaxWidth(1f).shadow(elevation = 10.dp, ambientColor = Color.Gray), colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.background, contentColor = MaterialTheme.colorScheme.primary @@ -118,13 +133,15 @@ fun CompleteDialogContent( modifier = Modifier .height(50.dp) ) { - TittleAndButton(title, dialogState) + tittleAndButton(title, dialogState) } + Column(modifier = Modifier.fillMaxWidth().weight(1f)) { mainBody(dirPath, listFiles, minWidth, enabledButtonIndex) } + Row( modifier = Modifier .height(50.dp) @@ -133,52 +150,66 @@ fun CompleteDialogContent( horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.Bottom ) { - BottomButtons(dialogState) + BottomButtons(dialogState, dialogState,"Ok", "Cancel") // поменять !! } + + } } } @Composable -fun TittleAndButton(title: String, dialogState: MutableState) { +fun tittleAndButton(title: String, dialogState: MutableState) { + + val buttonState = listOf(remember { mutableStateOf(false) }) + + val clickButtonState = remember { mutableStateOf(false) } + Row( modifier = Modifier .fillMaxWidth(1f) - .padding(horizontal = 20.dp, vertical = 10.dp), + .padding(start = 20.dp, top = 0.dp, end = 0.dp, bottom = 10.dp), + //.padding(horizontal = 20.dp, vertical = 10.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text(text = title, fontSize = 20.sp, color = MaterialTheme.colorScheme.primary) - IconButton(modifier = Modifier.then(Modifier.size(20.dp)), // при наведении должна краситься в красный - onClick = { dialogState.value = false }) { - Icon(Icons.Outlined.Close, null) - } + + topAppIconButton( + rememberVectorPainter(Icons.Outlined.Close), + buttonState, + 0, + Color(233, 8, 28), + MaterialTheme.colorScheme.background, + MaterialTheme.colorScheme.primary, + clickButtonState + ) + dialogState.value = !clickButtonState.value } Divider(modifier = Modifier.height(1.dp), color = Color.LightGray) } @Composable -fun BottomButtons(dialogState: MutableState) { +fun BottomButtons(dialogState: MutableState, firstButtonState: MutableState, firstButtonText: String, secondButtonText: String) { Button( - modifier = Modifier - .width(90.dp), + modifier = Modifier.width(90.dp), shape = RoundedCornerShape(3.dp), onClick = { - dialogState.value = false + firstButtonState.value = !firstButtonState.value + //dialogState.value = false }, colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.secondary, contentColor = MaterialTheme.colorScheme.background ) ) { - Text(text = "Ok", fontSize = 13.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold) + Text(text = firstButtonText, fontSize = 12.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold) } Spacer(modifier = Modifier.width(5.dp)) Button( - modifier = Modifier - .width(90.dp), + modifier = Modifier.width(90.dp), shape = RoundedCornerShape(3.dp), colors = ButtonDefaults.buttonColors( contentColor = MaterialTheme.colorScheme.primary, @@ -188,7 +219,7 @@ fun BottomButtons(dialogState: MutableState) { onClick = { dialogState.value = false }, contentPadding = PaddingValues(start = 2.dp, end = 2.dp) ) { - Text(text = "Cancel", fontSize = 13.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold) + Text(text = secondButtonText, fontSize = 12.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold) } } @@ -224,12 +255,10 @@ fun mainBody( Spacer(modifier = Modifier.height(1.dp).fillMaxWidth()) - //val enabledButtonIndex = remember { mutableStateOf(0) } - LazyColumn( - modifier = Modifier.fillMaxWidth().padding(horizontal = 10.dp, vertical = 10.dp) + modifier = Modifier.fillMaxWidth().padding(horizontal = 10.dp, vertical = 10.dp).fillMaxHeight() .border(0.5.dp, Color.LightGray), - contentPadding = PaddingValues(top = 10.dp, bottom = 10.dp) + contentPadding = PaddingValues(0.dp) ) { item { Row(verticalAlignment = Alignment.Bottom) { @@ -267,7 +296,11 @@ fun mainBody( checked.value = !checked.value enabledButtonIndex.value = index }, - colors = ButtonDefaults.buttonColors(containerColor = if (checked.value && enabledButtonIndex.value == index) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.background), + colors = ButtonDefaults.buttonColors( + containerColor = + if (checked.value && enabledButtonIndex.value == index) MaterialTheme.colorScheme.tertiary + else MaterialTheme.colorScheme.background + ), modifier = Modifier .fillParentMaxWidth() .clickable(interactionSource = MutableInteractionSource(), indication = null, onClick = {}), // ?? @@ -291,7 +324,6 @@ fun mainBody( } } - } } @@ -310,38 +342,3 @@ fun fileIcon(dirName: String): Painter { } } - -@Composable -fun Modifier.windowDraggable(windowProvider: () -> WindowState): Modifier { - return pointerInput(Unit) { - forEachGesture { - awaitPointerEventScope { - val window = windowProvider() - val firstEvent = awaitPointerEvent() - when (firstEvent.awtEventOrNull?.id) { - MouseEvent.MOUSE_PRESSED -> Unit - else -> return@awaitPointerEventScope - } - val firstWindowPoint = firstEvent.awtEventOrNull?.locationOnScreen ?: return@awaitPointerEventScope - val startPosition = window.position - while (true) { - val event = awaitPointerEvent() - val currentPoint = event.awtEventOrNull?.locationOnScreen ?: break - - window.position = WindowPosition( - x = startPosition.x + (currentPoint.x - firstWindowPoint.x).dp, - y = startPosition.y + (currentPoint.y - firstWindowPoint.y).dp, - ) - - when (event.awtEventOrNull?.id) { - null, - MouseEvent.MOUSE_RELEASED -> { - break - } - } - } - } - } - } -} - diff --git a/lib/src/main/kotlin/ui/Foo.kt b/lib/src/main/kotlin/ui/Foo.kt new file mode 100644 index 0000000..f3a5369 --- /dev/null +++ b/lib/src/main/kotlin/ui/Foo.kt @@ -0,0 +1,25 @@ +package ui + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.geometry.Offset + +data class ScreenScale( + val scale: Float +) + +class State() { + + var screenScale by mutableStateOf(ScreenScale(1f)) + + var screenDrag by mutableStateOf(Offset(0f, 0f)) + private set + + fun handleScreenDrag(dragAmount: Offset) { + screenDrag += Offset( + dragAmount.x / screenScale.scale, + dragAmount.y / screenScale.scale + ) + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/SaveDialog.kt b/lib/src/main/kotlin/ui/SaveDialog.kt similarity index 92% rename from lib/src/main/kotlin/SaveDialog.kt rename to lib/src/main/kotlin/ui/SaveDialog.kt index c478c96..a27d89e 100644 --- a/lib/src/main/kotlin/SaveDialog.kt +++ b/lib/src/main/kotlin/ui/SaveDialog.kt @@ -1,4 +1,4 @@ - +package ui import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.border import androidx.compose.foundation.layout.* @@ -20,6 +20,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.rememberDialogState +import topAppIconButton @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Composable @@ -41,7 +42,7 @@ fun saveDialog( onCloseRequest = { clickButtonState.value = false }, undecorated = true, state = rememberDialogState( position = WindowPosition(Alignment.Center), // поменять ?? - size = DpSize(300.dp, 200.dp) + size = DpSize(300.dp, 180.dp) ), resizable = false ) @@ -57,18 +58,17 @@ fun saveDialog( ) ) { - Column(modifier = Modifier.fillMaxSize()) { + Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.SpaceBetween) { Column(modifier = Modifier.height(50.dp)) { tittleAndButton("Save tree", clickButtonState) + Divider(modifier = Modifier.height(1.dp).fillMaxWidth()) } - Divider(modifier = Modifier.height(1.dp).fillMaxWidth()) - Column( - modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp, vertical = 5.dp), + modifier = Modifier.fillMaxWidth().padding(start = 5.dp, end = 5.dp, top = 5.dp), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceBetween + verticalArrangement = Arrangement.Bottom ) { TextField( value = fileName.value, @@ -114,7 +114,7 @@ fun saveDialog( fontSize = 12.sp ) - Row(horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.Bottom) { + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.Bottom) { BottomButtons( clickButtonState, saveButtonState, diff --git a/lib/src/main/kotlin/ui/TopAppBar.kt b/lib/src/main/kotlin/ui/TopAppBar.kt new file mode 100644 index 0000000..b5c6683 --- /dev/null +++ b/lib/src/main/kotlin/ui/TopAppBar.kt @@ -0,0 +1,265 @@ +package ui + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Add +import androidx.compose.material.icons.outlined.Close +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.onPointerEvent +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.WindowPlacement +import androidx.compose.ui.window.WindowState +import arrowIcon +import controller.Controller +import lightColors +import menuItemBackgroundColor +import myTypography +import topAppIconButton + +@Composable +fun myTopAppBar(clickButtonsState: List>, windowState: WindowState) { + + val controller = Controller() + val showFiles = controller.showFiles() + + val fileName = remember { mutableStateOf("") } // имя файла в диалоге save + + MaterialTheme( + colorScheme = lightColors, + typography = myTypography + ) { + val buttonState = List(7) { remember { mutableStateOf(false) } } + + TopAppBar( + backgroundColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.background, + modifier = Modifier.height(50.dp), + contentPadding = PaddingValues(0.dp) + ) { + + Row( + modifier = Modifier.fillMaxWidth().fillMaxHeight(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Row { + + deleteDialog( + "A", + buttonState, + clickButtonsState[0] + ) // будем иметь имя текущего дерево и передавать его сюды + + saveDialog(buttonState, clickButtonsState[1], fileName) + // после работы в контролере опять поменять filename на "" + + + topAppIconButton( + painterResource("/drawable/dir.png"), + buttonState, + 2, + MaterialTheme.colorScheme.onPrimary, + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.background, + clickButtonsState[2] + ) + openMenu(clickButtonsState[2], showFiles) + + + topAppIconButton( + rememberVectorPainter(Icons.Outlined.Add), + buttonState, + 3, + MaterialTheme.colorScheme.onPrimary, + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.background, + clickButtonsState[3] + ) + createMenu(clickButtonsState[3]) + + } + + Row(horizontalArrangement = Arrangement.End) { + + topAppIconButton( + painterResource("/drawable/minimize.png"), + buttonState, + 4, + MaterialTheme.colorScheme.onPrimary, + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.background, + clickButtonsState[4] + ) + if (clickButtonsState[4].value) { + minimizeWindow(windowState) + clickButtonsState[4].value = false + } + + topAppIconButton( + painterResource("/drawable/maximize.png"), + buttonState, + 5, + MaterialTheme.colorScheme.onPrimary, + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.background, + clickButtonsState[5] + ) + if (clickButtonsState[5].value) { + maximizeWindow(windowState) + clickButtonsState[5].value = false + } + + topAppIconButton( + rememberVectorPainter(Icons.Outlined.Close), + buttonState, + 6, + Color(233, 8, 28), + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.background, + clickButtonsState[6] + ) + + } + + } + + + } + + } +} + +@Composable +private fun minimizeWindow(state: WindowState) { + state.placement = WindowPlacement.Floating +} + +@Composable +private fun maximizeWindow(state: WindowState) { + if (state.placement == WindowPlacement.Maximized) { + state.placement = WindowPlacement.Floating + } else { + state.placement = WindowPlacement.Maximized + } +} + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun createMenu(expandedNested: MutableState) { + val treesNames = listOf("Red black tree", "AVL tree", "Binary tree") + + val backgroundColorState = List(3) { remember { mutableStateOf(false) } } + + DropdownMenu( + expanded = expandedNested.value, + onDismissRequest = { expandedNested.value = false }, + offset = DpOffset(150.dp, 0.dp), + modifier = Modifier.background(MaterialTheme.colorScheme.background) + ) { + + repeat(3) { index -> + DropdownMenuItem(text = { Text(treesNames[index]) }, + onClick = { + //expandedNested.value = false + // вызываю контроллер на создание + }, + modifier = Modifier.onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } + .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } + .background(color = menuItemBackgroundColor(backgroundColorState[index]))) + } + } +} + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun openMenu(expandedNested: MutableState, showFiles: List>) { + val treesNames = listOf("Red black tree", "AVL tree", "Binary tree") + + val expandedTreesNames = List(3) { remember { mutableStateOf(false) } } + + val backgroundColorState = List(3) { remember { mutableStateOf(false) } } + + DropdownMenu( + expanded = expandedNested.value, + onDismissRequest = { expandedNested.value = !expandedNested.value }, + offset = DpOffset(100.dp, 0.dp), + modifier = Modifier.width(150.dp).background(MaterialTheme.colorScheme.background) + ) { + repeat(3) { index -> + DropdownMenuItem( + text = { + Text( + treesNames[index], + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.height(50.dp), + maxLines = 1 + ) + }, + onClick = { + expandedTreesNames[index].value = !expandedTreesNames[index].value + }, + trailingIcon = { arrowIcon() }, + modifier = Modifier.onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } + .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } + .background(color = menuItemBackgroundColor(backgroundColorState[index])), + colors = MenuDefaults.itemColors(textColor = MaterialTheme.colorScheme.primary)) + treesNames(expandedTreesNames[index], expandedNested, showFiles[index], index, 150.dp) + } + } + +} + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun treesNames( + expandedNested: MutableState, + expandedOpenNested: MutableState, + showFiles: List, + treeID: Int, + offset: Dp +) { + + val dirPath = System.getProperty("user.dir") + "/saved-trees" + val dirFiles = listOf("$dirPath/RB-trees", "$dirPath/AVL-trees", "$dirPath/BIN-trees") + + val selectedTree = remember { mutableStateOf("") } + + val backgroundColorState = List(showFiles.size) { remember { mutableStateOf(false) } } + + AnimatedVisibility(visible = expandedNested.value) { + DropdownMenu( + expanded = expandedNested.value, + onDismissRequest = { expandedNested.value = false }, + offset = DpOffset(offset, (-50).dp), + modifier = Modifier.background(MaterialTheme.colorScheme.background) + ) { + searchItem(dirFiles[treeID], expandedNested, selectedTree, expandedOpenNested) + + repeat(showFiles.size) { index -> + DropdownMenuItem(onClick = { /*expandedNested.value = false*/ }, + text = { Text(showFiles[index]) }, + modifier = Modifier + .onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } + .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } + .background(color = menuItemBackgroundColor(backgroundColorState[index])), + colors = MenuDefaults.itemColors(textColor = MaterialTheme.colorScheme.primary)) + } + + } + } + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/UserInputValidation.kt b/lib/src/main/kotlin/ui/UserInputValidation.kt similarity index 96% rename from lib/src/main/kotlin/UserInputValidation.kt rename to lib/src/main/kotlin/ui/UserInputValidation.kt index bf997cc..9674e1e 100644 --- a/lib/src/main/kotlin/UserInputValidation.kt +++ b/lib/src/main/kotlin/ui/UserInputValidation.kt @@ -1,7 +1,9 @@ +package ui + import java.util.* private val INVALID_WINDOWS_SPECIFIC_CHAR = arrayOf('"', '*', ':', '<', '>', '/', '\"', '|', '?') -private val INVALID_UNIX_SPECIFIC_CHAR = arrayOf('/') // add '\000' +private val INVALID_UNIX_SPECIFIC_CHAR = arrayOf('/') // add '\000' // single . fun validate(fileName: String): Boolean { if (fileName == "" || fileName.length > 255) { From 0d15b1787501c2f49a4e2947057ace5022020371 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 30 Apr 2023 11:45:15 +0300 Subject: [PATCH 172/212] refactor: Rewrite architecture responsible for working with databases, interacting with data structures (TreeStructs); implement binary tree coordinates counting (#24). --- .../kotlin/databaseManage/AVLTreeManager.kt | 2 +- .../kotlin/databaseManage/BINTreeManager.kt | 2 +- .../kotlin/databaseManage/RBTreeManager.kt | 2 +- .../main/kotlin/databaseManage/TreeManager.kt | 2 +- .../kotlin/treelib/commonObjects/Container.kt | 4 + .../kotlin/viewPart/nodes/AVLDrawableNode.kt | 30 ---- .../kotlin/viewPart/nodes/BINDrawableNode.kt | 73 ---------- .../kotlin/viewPart/nodes/DrawableNode.kt | 90 ------------ .../kotlin/viewPart/nodes/RBDrawableNode.kt | 30 ---- .../viewPart/nodes/design/AVLNodeDesign.kt | 5 - .../viewPart/nodes/design/RBNodeDesign.kt | 5 - .../nodes/drawableAVL/AVLDrawableNode.kt | 14 ++ .../nodes/drawableAVL/AVLDrawableTree.kt | 43 ++++++ .../nodes/drawableAVL/AVLNodeDesign.kt | 28 ++++ .../nodes/drawableBIN/BINDrawableNode.kt | 27 ++++ .../nodes/drawableBIN/BINDrawableTree.kt | 128 ++++++++++++++++++ .../{design => drawableBIN}/BINNodeDesign.kt | 17 +-- .../nodes/drawableRB/RBDrawableNode.kt | 16 +++ .../nodes/drawableRB/RBDrawableTree.kt | 43 ++++++ .../viewPart/nodes/drawableRB/RBNodeDesign.kt | 28 ++++ .../nodes/drawableTree/DrawableNode.kt | 41 ++++++ .../nodes/drawableTree/DrawableTree.kt | 124 +++++++++++++++++ .../viewPart/nodes/drawableTree/NodeDesign.kt | 16 +++ 23 files changed, 525 insertions(+), 245 deletions(-) delete mode 100644 lib/src/main/kotlin/viewPart/nodes/AVLDrawableNode.kt delete mode 100644 lib/src/main/kotlin/viewPart/nodes/BINDrawableNode.kt delete mode 100644 lib/src/main/kotlin/viewPart/nodes/DrawableNode.kt delete mode 100644 lib/src/main/kotlin/viewPart/nodes/RBDrawableNode.kt delete mode 100644 lib/src/main/kotlin/viewPart/nodes/design/AVLNodeDesign.kt delete mode 100644 lib/src/main/kotlin/viewPart/nodes/design/RBNodeDesign.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt rename lib/src/main/kotlin/viewPart/nodes/{design => drawableBIN}/BINNodeDesign.kt (65%) create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt diff --git a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt index 9cc335f..65a9104 100644 --- a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt @@ -82,7 +82,7 @@ class AVLTreeManager : TreeManager< db.saveTree(name, info.toMutableList(), ::serialization) } - override fun deleteTreeFormDB(name: String) { + override fun deleteTreeFromDB(name: String) { if (db.isTreeExist(name)) db.deleteTree(name) } diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index 36140e8..01c16a7 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -73,7 +73,7 @@ class BINTreeManager : TreeManager< return ans } - override fun deleteTreeFormDB(name: String) = jsonRep.removeTree(name) + override fun deleteTreeFromDB(name: String) = jsonRep.removeTree(name) override fun getSavedTreesNames(): List { val filesNames = File(dirPath).list()?.map { it.replace(".json", "") } diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt index ca2778b..84f80e2 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -51,7 +51,7 @@ class RBTreeManager : TreeManager< } - override fun deleteTreeFormDB(name: String) { + override fun deleteTreeFromDB(name: String) { neo4jDB.removeTree(name) neo4jDB.close() diff --git a/lib/src/main/kotlin/databaseManage/TreeManager.kt b/lib/src/main/kotlin/databaseManage/TreeManager.kt index 5847a23..982aada 100644 --- a/lib/src/main/kotlin/databaseManage/TreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/TreeManager.kt @@ -26,7 +26,7 @@ interface TreeManager< fun saveTreeToDB(name: String, tree: StructType) - fun deleteTreeFormDB(name: String) + fun deleteTreeFromDB(name: String) fun getSavedTreesNames(): List diff --git a/lib/src/main/kotlin/treelib/commonObjects/Container.kt b/lib/src/main/kotlin/treelib/commonObjects/Container.kt index 8b7bc35..6699b16 100644 --- a/lib/src/main/kotlin/treelib/commonObjects/Container.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/Container.kt @@ -23,4 +23,8 @@ data class Container, V>(val pair: Pair) : Comparable): Int { return compareValuesBy(this, other) { it.pair.first } } + + override fun toString(): String { + return "$key \n $value" + } } diff --git a/lib/src/main/kotlin/viewPart/nodes/AVLDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/AVLDrawableNode.kt deleted file mode 100644 index 12bbf0f..0000000 --- a/lib/src/main/kotlin/viewPart/nodes/AVLDrawableNode.kt +++ /dev/null @@ -1,30 +0,0 @@ -package viewPart.nodes - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState - -class AVLDrawableNode( - override val value: Pack, - override var leftChild: AVLDrawableNode? = null, - override var rightChild: AVLDrawableNode? = null, - val height: Int, - override val xState: MutableState, - override val yState: MutableState, -) : DrawableNode>() { - - @Composable - override fun nodeView( - information: String, - offsetX: MutableState, - offsetY: MutableState - ) { - TODO("Not yet implemented") - } - - @Composable - override fun edgeView(child: AVLDrawableNode) { - TODO("Not yet implemented") - } - - -} diff --git a/lib/src/main/kotlin/viewPart/nodes/BINDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/BINDrawableNode.kt deleted file mode 100644 index d7789ea..0000000 --- a/lib/src/main/kotlin/viewPart/nodes/BINDrawableNode.kt +++ /dev/null @@ -1,73 +0,0 @@ -package viewPart.nodes - -import viewPart.nodes.design.BINNodeDesign -import androidx.compose.foundation.Canvas -import androidx.compose.runtime.MutableState -import androidx.compose.ui.graphics.Color -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.detectDragGestures -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.size -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.layout.boundsInParent -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex -import kotlin.math.roundToInt - -class BINDrawableNode( - override val value: Pack, - override var leftChild: BINDrawableNode? = null, - override var rightChild: BINDrawableNode? = null, - override val xState: MutableState, - override val yState: MutableState, -) : DrawableNode>() { - - @Composable - override fun nodeView( - information: String, - offsetX: MutableState, - offsetY: MutableState - ) { - val modifier = Modifier - .offset { - IntOffset( - x = offsetX.value.roundToInt(), - y = offsetY.value.roundToInt() - ) - } - .pointerInput(Unit) { - detectDragGestures { _, dragAmount -> - offsetX.value += dragAmount.x - offsetY.value += dragAmount.y - } - } - .size(BINNodeDesign.nodeSize.dp) - .clip(BINNodeDesign.shape) - .background(BINNodeDesign.colorNode) - .onGloballyPositioned { layoutCoordinates -> - val rect = layoutCoordinates.boundsInParent() - xState.value = rect.center.x - yState.value = rect.center.y - } - BINNodeDesign.infoView(information, modifier) - } - - @Composable - override fun edgeView(child: BINDrawableNode) { - Canvas(modifier = Modifier.fillMaxSize().zIndex(-1f)) { - drawLine( - color = BINNodeDesign.lineColor, - start = Offset(xState.value, yState.value), - end = Offset(child.xState.value, child.yState.value), - strokeWidth = BINNodeDesign.lineStrokeWidth, //TODO вынести в конфиг ноды - ) - } - } -} diff --git a/lib/src/main/kotlin/viewPart/nodes/DrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/DrawableNode.kt deleted file mode 100644 index 3626591..0000000 --- a/lib/src/main/kotlin/viewPart/nodes/DrawableNode.kt +++ /dev/null @@ -1,90 +0,0 @@ -package viewPart.nodes - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember - -abstract class DrawableNode> { - abstract val value: Pack - abstract var leftChild: NodeType? - abstract var rightChild: NodeType? - abstract val xState: MutableState - abstract val yState: MutableState - - /* md should be added (for deleting)???? - * abstract val xDragState: MutableState - * abstract val yDragState: MutableState - * */ - - @Composable - fun display(width: Float? = null, height: Float? = null) { - // TODO: Выпилить рекурсивный дисплей - nodeView( - information = value.toString(), - remember { mutableStateOf(0f) }, - remember { mutableStateOf(0f) }, - ) - - leftChild?.let { - edgeView(it) - it.display() - } - - rightChild?.let { - edgeView(it) - it.display() - } - } - - @Composable - protected abstract fun nodeView( - information: String, - offsetX: MutableState, - offsetY: MutableState, - ) - - @Composable - protected abstract fun edgeView(child: NodeType) -} - -/* Usage example (put it inside block): - - val a = BINDrawableNode( - 1, - null, - null, - remember { mutableStateOf(0f) }, - remember { mutableStateOf(0f) } - ) - - val b = BINDrawableNode( - 2, - null, - null, - remember { mutableStateOf(0f) }, - remember { mutableStateOf(0f) } - ) - - val c = BINDrawableNode( - 3, - null, - null, - remember { mutableStateOf(0f) }, - remember { mutableStateOf(0f) } - ) - - val d = BINDrawableNode( - 4, - null, - null, - remember { mutableStateOf(0f) }, - remember { mutableStateOf(0f) } - ) - - a.leftChild = b - a.rightChild = c - c.leftChild = d - - a.display() -* */ diff --git a/lib/src/main/kotlin/viewPart/nodes/RBDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/RBDrawableNode.kt deleted file mode 100644 index f78c439..0000000 --- a/lib/src/main/kotlin/viewPart/nodes/RBDrawableNode.kt +++ /dev/null @@ -1,30 +0,0 @@ -package viewPart.nodes - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.ui.graphics.Color -import treelib.rbTree.Markers - -class RBDrawableNode( - override val value: Pack, - override var leftChild: RBDrawableNode? = null, - override var rightChild: RBDrawableNode? = null, - val color: Markers, - override val xState: MutableState, - override val yState: MutableState, -) : DrawableNode>() { - - @Composable - override fun nodeView( - information: String, - offsetX: MutableState, - offsetY: MutableState - ) { - TODO("Not yet implemented") - } - - @Composable - override fun edgeView(child: RBDrawableNode) { - TODO("Not yet implemented") - } -} diff --git a/lib/src/main/kotlin/viewPart/nodes/design/AVLNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/design/AVLNodeDesign.kt deleted file mode 100644 index 33e5b69..0000000 --- a/lib/src/main/kotlin/viewPart/nodes/design/AVLNodeDesign.kt +++ /dev/null @@ -1,5 +0,0 @@ -package viewPart.nodes.design - -object AVLNodeDesign { - // TODO: Реализовать, как BINNodeDesign -} diff --git a/lib/src/main/kotlin/viewPart/nodes/design/RBNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/design/RBNodeDesign.kt deleted file mode 100644 index aa8d6c9..0000000 --- a/lib/src/main/kotlin/viewPart/nodes/design/RBNodeDesign.kt +++ /dev/null @@ -1,5 +0,0 @@ -package viewPart.nodes.design - -object RBNodeDesign { - // TODO: Реализовать, как BINNodeDesign -} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt new file mode 100644 index 0000000..c23b96a --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt @@ -0,0 +1,14 @@ +package viewPart.nodes.drawableAVL + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import viewPart.nodes.drawableTree.DrawableNode + +class AVLDrawableNode( + override val value: Pack, + override var leftChild: AVLDrawableNode? = null, + override var rightChild: AVLDrawableNode? = null, + val height: Int, + override val xState: MutableState, + override val yState: MutableState, +) : DrawableNode>() diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt new file mode 100644 index 0000000..da753c0 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt @@ -0,0 +1,43 @@ +package viewPart.nodes.drawableAVL + +import databaseManage.TreeManager +import databaseSave.sqlite.DrawableAVLVertex +import treelib.avlTree.AVLNode +import treelib.avlTree.AVLStateContainer +import treelib.avlTree.AVLStruct +import treelib.avlTree.AVLVertex +import treelib.commonObjects.Container +import viewPart.nodes.drawableTree.DrawableTree + +class AVLDrawableTree( + override val name: String, + override val treeManager: TreeManager, DrawableAVLVertex>, AVLNode>, AVLStateContainer>, AVLVertex>, AVLStruct>>, +) : DrawableTree< + AVLDrawableNode>, + DrawableAVLVertex>, + AVLNode>, + AVLStateContainer>, + AVLVertex>, + AVLStruct> + >() { + override var root: AVLDrawableNode>? = null + override var drawablePreOrder: List>>? = null + override val treeStruct: AVLStruct> = AVLStruct() + override val designNode = AVLNodeDesign + + override fun initTree() { + TODO("Not yet implemented") + } + + override fun deleteTree() { + TODO("Not yet implemented") + } + + override fun saveTree() { + TODO("Not yet implemented") + } + + override fun updateTree() { + TODO("Not yet implemented") + } +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt new file mode 100644 index 0000000..6fef610 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt @@ -0,0 +1,28 @@ +package viewPart.nodes.drawableAVL + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import viewPart.nodes.drawableTree.NodeDesign + +object AVLNodeDesign: NodeDesign { + // TODO: Реализовать, как BINNodeDesign + override var nodeSize: Float + get() = TODO("Not yet implemented") + set(value) {} + override var shape: Shape + get() = TODO("Not yet implemented") + set(value) {} + override var lineStrokeWidth: Float + get() = TODO("Not yet implemented") + set(value) {} + override var lineColor: Color + get() = TODO("Not yet implemented") + set(value) {} + + @Composable + override fun infoView(information: String, modifier: Modifier) { + TODO("Not yet implemented") + } +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt new file mode 100644 index 0000000..06ee527 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt @@ -0,0 +1,27 @@ +package viewPart.nodes.drawableBIN + +import androidx.compose.foundation.Canvas +import androidx.compose.runtime.MutableState +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import viewPart.nodes.drawableTree.DrawableNode +import kotlin.math.roundToInt + +class BINDrawableNode( + override val value: Pack, + override var leftChild: BINDrawableNode? = null, + override var rightChild: BINDrawableNode? = null, + override var xState: MutableState, + override var yState: MutableState, +) : DrawableNode>() diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt new file mode 100644 index 0000000..454739d --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt @@ -0,0 +1,128 @@ +package viewPart.nodes.drawableBIN + +import androidx.compose.runtime.mutableStateOf +import databaseManage.BINTreeManager +import databaseSave.jsonFormat.DrawableBINVertex +import treelib.binTree.BINNode +import treelib.binTree.BINStateContainer +import treelib.binTree.BINStruct +import treelib.binTree.BINVertex +import treelib.commonObjects.Container +import viewPart.nodes.drawableTree.DrawableTree + +class BINDrawableTree( + override val name: String, + override val treeManager: BINTreeManager, +) : + DrawableTree< + BINDrawableNode>, + DrawableBINVertex>, + BINNode>, + BINStateContainer>, + BINVertex>, + BINStruct> + >() { + + override var root: BINDrawableNode>? = null + override val treeStruct = BINStruct>() + override var drawablePreOrder: List>>? = null + override val designNode = BINNodeDesign + + /* DB methods */ + override fun initTree() { + val binVertexes = treeManager.initTree(name, treeStruct) + drawablePreOrder = binVertexes.map { drawableVertexToNode(it) } + + drawablePreOrder?.let { + if (it.isNotEmpty()) { + restoreTree(it) + } + } + } + + override fun updateTree() { + for (el in vertexesToNodes(treeStruct.preOrder())) { + restoreInsert(el) + } + } + + private fun vertexesToNodes(preOrder: List>>) = sequence { + for (el in preOrder) { + yield( + vertexToNode(el) + ) + } + } + + private fun vertexToNode(vertex: BINVertex>): BINDrawableNode> { + return BINDrawableNode( + value = vertex.value, + xState = mutableStateOf(0f), + yState = mutableStateOf(0f), + ) + } + + private fun drawableVertexToNode(vertex: DrawableBINVertex>) = BINDrawableNode( + value = vertex.value, + xState = mutableStateOf(vertex.x.toFloat()), + yState = mutableStateOf(vertex.y.toFloat()), + ) + + private fun nodeToDrawableVertex(node: BINDrawableNode>) = DrawableBINVertex>( + value = node.value, + x = node.xState.value.toDouble(), + y = node.yState.value.toDouble(), + ) + + private fun restoreTree(preOrder: List>>) { + root = null + for (el in preOrder) { + restoreInsert(el) + } + } + + private fun restoreInsert(preOrderNode: BINDrawableNode>) { + if (root == null) { + root = preOrderNode + return + } + var currentParent = root + while (currentParent != null) { + currentParent?.let { + when { + it.value < preOrderNode.value -> { + if (it.rightChild == null) { + it.rightChild = preOrderNode + return@restoreInsert + } + else currentParent = it.rightChild + } + + it.value > preOrderNode.value -> { + if (it.leftChild == null) { + it.leftChild = preOrderNode + return@restoreInsert + } + else currentParent = it.leftChild + } + + else -> { + println(currentParent!!.value) + println(preOrderNode.value) + throw InternalError("Can't restore tree from preOrder :(") + } + } + } + } + } + + override fun deleteTree() = treeManager.deleteTreeFromDB(name) + + override fun saveTree() { + if (root != null) { + treeManager.saveTreeToDB(name, preOrder().map { nodeToDrawableVertex(it) }.toList(), listOf()) + } else { + treeManager.saveTreeToDB(name, treeStruct) + } + } +} diff --git a/lib/src/main/kotlin/viewPart/nodes/design/BINNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt similarity index 65% rename from lib/src/main/kotlin/viewPart/nodes/design/BINNodeDesign.kt rename to lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt index 0b2531c..c8ba9ce 100644 --- a/lib/src/main/kotlin/viewPart/nodes/design/BINNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt @@ -1,25 +1,26 @@ -package viewPart.nodes.design +package viewPart.nodes.drawableBIN import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.GenericShape import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp +import viewPart.nodes.drawableTree.NodeDesign -object BINNodeDesign { +object BINNodeDesign: NodeDesign { var colorNode = Color.Green - var lineColor = Color.Black - var nodeSize = 80f - var shape = CircleShape - var lineStrokeWidth = 15f + override var lineColor = Color.Black + override var nodeSize = 100f + override var shape: Shape = CircleShape + override var lineStrokeWidth = 15f @Composable - fun infoView(information: String, modifier: Modifier) { + override fun infoView(information: String, modifier: Modifier) { Box( modifier = modifier, contentAlignment = Alignment.Center diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt new file mode 100644 index 0000000..19217b2 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt @@ -0,0 +1,16 @@ +package viewPart.nodes.drawableRB + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import treelib.rbTree.Markers +import viewPart.nodes.drawableTree.DrawableNode + +class RBDrawableNode( + override val value: Pack, + override var leftChild: RBDrawableNode? = null, + override var rightChild: RBDrawableNode? = null, + val color: Markers, + override val xState: MutableState, + override val yState: MutableState, +) : DrawableNode>() diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt new file mode 100644 index 0000000..bb8aa0e --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt @@ -0,0 +1,43 @@ +package viewPart.nodes.drawableRB + +import databaseManage.TreeManager +import databaseSave.neo4j.DrawableRBVertex +import treelib.commonObjects.Container +import treelib.rbTree.RBNode +import treelib.rbTree.RBStateContainer +import treelib.rbTree.RBStruct +import treelib.rbTree.RBVertex +import viewPart.nodes.drawableTree.DrawableTree + +class RBDrawableTree( + override val name: String, + override val treeManager: TreeManager, DrawableRBVertex>, RBNode>, RBStateContainer>, RBVertex>, RBStruct>>, +) : DrawableTree< + RBDrawableNode>, + DrawableRBVertex>, + RBNode>, + RBStateContainer>, + RBVertex>, + RBStruct> + >() { + override var root: RBDrawableNode>? = null + override var drawablePreOrder: List>>? = null + override val treeStruct: RBStruct> = RBStruct() + override val designNode = RBNodeDesign + + override fun initTree() { + TODO("Not yet implemented") + } + + override fun deleteTree() { + TODO("Not yet implemented") + } + + override fun saveTree() { + TODO("Not yet implemented") + } + + override fun updateTree() { + TODO("Not yet implemented") + } +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt new file mode 100644 index 0000000..916df02 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt @@ -0,0 +1,28 @@ +package viewPart.nodes.drawableRB + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import viewPart.nodes.drawableTree.NodeDesign + +object RBNodeDesign: NodeDesign { + // TODO: Реализовать, как BINNodeDesign + override var nodeSize: Float + get() = TODO("Not yet implemented") + set(value) {} + override var shape: Shape + get() = TODO("Not yet implemented") + set(value) {} + override var lineStrokeWidth: Float + get() = TODO("Not yet implemented") + set(value) {} + override var lineColor: Color + get() = TODO("Not yet implemented") + set(value) {} + + @Composable + override fun infoView(information: String, modifier: Modifier) { + TODO("Not yet implemented") + } +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt new file mode 100644 index 0000000..08f0de3 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt @@ -0,0 +1,41 @@ +package viewPart.nodes.drawableTree + +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +import viewPart.nodes.drawableBIN.BINNodeDesign +import kotlin.math.roundToInt + +abstract class DrawableNode> { + abstract val value: Pack + abstract var leftChild: NodeType? + abstract var rightChild: NodeType? + abstract val xState: MutableState + abstract val yState: MutableState + var modifier = Modifier + .offset { + IntOffset( + x = xState.value.roundToInt(), + y = yState.value.roundToInt() + ) + } + .pointerInput(Unit) { + detectDragGestures { _, dragAmount -> + xState.value += dragAmount.x + yState.value += dragAmount.y + } + } + .size(BINNodeDesign.nodeSize.dp) + .clip(BINNodeDesign.shape) + .background(BINNodeDesign.colorNode) +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt new file mode 100644 index 0000000..f86691e --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -0,0 +1,124 @@ +package viewPart.nodes.drawableTree + +import databaseManage.TreeManager +import databaseSave.DrawableVertex +import treelib.abstractTree.Node +import treelib.abstractTree.StateContainer +import treelib.abstractTree.TreeStruct +import treelib.abstractTree.Vertex +import treelib.commonObjects.Container +import treelib.commonObjects.exceptions.ImpossibleCaseException + +abstract class DrawableTree< + DNodeType : DrawableNode, DNodeType>, + DVertexType : DrawableVertex>, + NodeType : Node, NodeType>, + State : StateContainer, NodeType>, + VertexType : Vertex>, + StructType : TreeStruct, NodeType, State, VertexType> + > { + /**How you should it use: + * + * + * + * OUTSIDE A COMPOSABLE CONTEXT { + * * val manager = TreeManager() + * * val tree = DrawableTree("name", manager) + * * tree.insert + * * tree.delete + * * ... + * * tree.update <- (means moving information: TreeStruct.root -> DrawableTree.root) + * * tree.repositisonTree <- (set "beautiful" coordinates without display) + * + * } + * + * IN A COMPOSABLE CONTEXT { + * * TreeDrawingUtils.displayTree(tree) + * + * } + * **/ + abstract val name: String + internal abstract var root: DNodeType? + protected abstract var drawablePreOrder: List? + protected abstract val treeManager: TreeManager, DVertexType, NodeType, State, VertexType, StructType> + protected abstract val treeStruct: StructType + abstract val designNode: NodeDesign + + val yShiftBetweenNodes = 10f + + abstract fun initTree() + abstract fun deleteTree() + abstract fun saveTree() + + abstract fun updateTree() + + fun insert(item: Container) = treeStruct.insert(item) + + fun delete(item: Container) { + if (treeStruct.find(item) != null) treeStruct.delete(item) + } + + fun find(item: Container): Container? = treeStruct.find(item) + + fun repositisonTree(xBase: Float = 0f, yBase: Float = 0f) { + root?.let { + createCordsState1(it, xBase, yBase) + createCordsState2(it) + } + } + + private fun createCordsState1(node: DNodeType, x: Float, y: Float): Float { + if (node.leftChild == null && node.rightChild == null) { + node.xState.value = x + node.yState.value = y + return x + designNode.nodeSize + } + + var xInfo = node.leftChild?.let { + return@let createCordsState1(it, x, y + designNode.nodeSize + yShiftBetweenNodes) + } ?: x + + node.xState.value = xInfo + node.yState.value = y + xInfo += designNode.nodeSize + + node.rightChild?.let { + return@createCordsState1 createCordsState1(it, xInfo, y + designNode.nodeSize + yShiftBetweenNodes) + } + return xInfo + } + + private fun createCordsState2(node: DNodeType): Float { + if (node.leftChild == null && node.rightChild == null) return node.xState.value + val xLeft = node.leftChild?.let { return@let createCordsState2(it) } ?: -1f + val xRight = node.rightChild?.let { return@let createCordsState2(it) } ?: -1f + + if ((xLeft == -1f) || (xRight == -1f)) return node.xState.value + + val n = xLeft + (xRight - xLeft) / 2 + node.xState.value = n + return n + } + + protected fun preOrder() = sequence{ + var current: DNodeType + val queue = ArrayDeque() + + root?.let { root -> + queue.add(root) + while (queue.isNotEmpty()) { + current = queue.removeLast() + yield(current) + if (current.rightChild != null) + current.rightChild?.let { + queue.add(it) + } ?: throw ImpossibleCaseException() + + if (current.leftChild != null) + current.leftChild?.let { + queue.add(it) + } ?: throw ImpossibleCaseException() + } + } + } +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt new file mode 100644 index 0000000..973d6b3 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt @@ -0,0 +1,16 @@ +package viewPart.nodes.drawableTree + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape + +interface NodeDesign { + var nodeSize: Float + var shape: Shape + var lineStrokeWidth: Float + var lineColor: Color + + @Composable + fun infoView(information: String, modifier: Modifier) +} From 3196920dbc0d87604ebf976752027f24e13b716f Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 30 Apr 2023 11:52:21 +0300 Subject: [PATCH 173/212] feat: Implement methods for DrawableTrees displaying - TreeDrawingUtils (#24). --- .../kotlin/viewPart/nodes/TreeDrawingUtils.kt | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt new file mode 100644 index 0000000..e8542a4 --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -0,0 +1,73 @@ +package viewPart.nodes + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.zIndex +import viewPart.nodes.drawableAVL.AVLDrawableTree +import viewPart.nodes.drawableAVL.AVLNodeDesign +import viewPart.nodes.drawableBIN.BINDrawableTree +import viewPart.nodes.drawableBIN.BINNodeDesign +import viewPart.nodes.drawableRB.RBDrawableTree +import viewPart.nodes.drawableRB.RBNodeDesign +import viewPart.nodes.drawableTree.DrawableNode +import viewPart.nodes.drawableTree.NodeDesign + +@Composable +fun displayTree(tree: RBDrawableTree) { + val root = tree.root + root?.let { + displayNode(it, RBNodeDesign) + } +} + +@Composable +fun displayTree(tree: AVLDrawableTree) { + val root = tree.root + root?.let { + displayNode(it, AVLNodeDesign) + } +} + +@Composable +fun displayTree(tree: BINDrawableTree) { + val root = tree.root + root?.let { + displayNode(it, BINNodeDesign) + } +} + +@Composable +fun , NodeD : NodeDesign> displayNode(node: DNode, design: NodeD) { + design.infoView(node.value.toString(), node.modifier) + + node.leftChild?.let { + edgeView(node, it, design) + displayNode(it, design) + } + + node.rightChild?.let { + edgeView(node, it, design) + displayNode(it, design) + } +} + +@Composable +fun , NodeD : NodeDesign> edgeView(node: DNode, child: DNode, design: NodeD) { + Canvas(modifier = Modifier.fillMaxSize().zIndex(-1f)) { + drawLine( + color = design.lineColor, + start = Offset( + node.xState.value + design.nodeSize / 2, + node.yState.value + design.nodeSize / 2, + ), + end = Offset( + child.xState.value + design.nodeSize / 2, + child.yState.value + design.nodeSize / 2, + ), + strokeWidth = design.lineStrokeWidth, + ) + } +} From 7cf23601a467569a93e32a215ad8a3e52bbf2b2a Mon Sep 17 00:00:00 2001 From: Artem Date: Sun, 30 Apr 2023 12:11:52 +0300 Subject: [PATCH 174/212] refactor: Structure files (ui) --- lib/src/main/kotlin/main.kt | 17 ----------------- lib/src/main/kotlin/ui/Foo.kt | 25 ------------------------- 2 files changed, 42 deletions(-) delete mode 100644 lib/src/main/kotlin/ui/Foo.kt diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index b0c7c84..d9d0829 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -1,10 +1,6 @@ import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.detectDragGestures -import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.window.WindowDraggableArea import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.* @@ -19,7 +15,6 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.input.key.* import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight @@ -27,7 +22,6 @@ import androidx.compose.ui.unit.* import androidx.compose.ui.window.* import controller.Controller import ui.* -import ui.State import java.awt.Dimension @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @@ -39,8 +33,6 @@ fun main() = application { val controller = Controller() - var state = remember { State() } - if (!clickButtonsState[6].value) { Window( onCloseRequest = ::exitApplication, @@ -66,15 +58,6 @@ fun main() = application { .fillMaxSize() .background(MaterialTheme.colorScheme.background) .offset(0.dp, 50.dp) - .verticalScroll(rememberScrollState()) - .horizontalScroll(rememberScrollState()) - .pointerInput(Unit) { - detectDragGestures { change, dragAmount -> - change.consume() - state.handleScreenDrag(dragAmount) - - } - } ) { controlFields(controller) diff --git a/lib/src/main/kotlin/ui/Foo.kt b/lib/src/main/kotlin/ui/Foo.kt deleted file mode 100644 index f3a5369..0000000 --- a/lib/src/main/kotlin/ui/Foo.kt +++ /dev/null @@ -1,25 +0,0 @@ -package ui - -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.geometry.Offset - -data class ScreenScale( - val scale: Float -) - -class State() { - - var screenScale by mutableStateOf(ScreenScale(1f)) - - var screenDrag by mutableStateOf(Offset(0f, 0f)) - private set - - fun handleScreenDrag(dragAmount: Offset) { - screenDrag += Offset( - dragAmount.x / screenScale.scale, - dragAmount.y / screenScale.scale - ) - } -} \ No newline at end of file From 2f399d09bcada695d83bd5310e4668bfad2055a4 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Sun, 30 Apr 2023 23:08:20 +0300 Subject: [PATCH 175/212] feat: Implement displaying of RBDrawableTree and AVLDrawableTree (#24). --- .../kotlin/databaseManage/AVLTreeManager.kt | 2 +- .../jsonFormat/DrawableBINVertex.kt | 3 +- .../databaseSave/sqlite/DrawableAVLVertex.kt | 2 +- .../sqlite/SQLiteRepositoryExposed.kt | 4 +- .../sqlite/SQLiteRepositoryJDBC.kt | 6 +- .../nodes/drawableAVL/AVLDrawableNode.kt | 1 - .../nodes/drawableAVL/AVLDrawableTree.kt | 41 ++++---- .../nodes/drawableAVL/AVLNodeDesign.kt | 37 ++++--- .../nodes/drawableBIN/BINDrawableTree.kt | 99 ++----------------- .../nodes/drawableRB/RBDrawableNode.kt | 2 - .../nodes/drawableRB/RBDrawableTree.kt | 32 +++--- .../viewPart/nodes/drawableRB/RBNodeDesign.kt | 37 ++++--- .../nodes/drawableTree/DrawableNode.kt | 4 +- .../nodes/drawableTree/DrawableTree.kt | 89 ++++++++++++++++- 14 files changed, 187 insertions(+), 172 deletions(-) diff --git a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt index 65a9104..5bc3a0e 100644 --- a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt @@ -37,7 +37,7 @@ class AVLTreeManager : TreeManager< for (el in drawVertex) ans.add( DrawableAVLVertex( value = el.value, - height = el.height.toInt(), + height = el.height, x = -0.0, y = -0.0 ) diff --git a/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt b/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt index 06458aa..cdc42b2 100644 --- a/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt +++ b/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt @@ -7,4 +7,5 @@ class DrawableBINVertex>( value: Pack, override val x: Double = 0.0, override val y: Double = 0.0 -) : BINVertex(value), DrawableVertex \ No newline at end of file +) : BINVertex(value), DrawableVertex + diff --git a/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt b/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt index 87576ea..83f10bf 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt +++ b/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt @@ -6,5 +6,5 @@ class DrawableAVLVertex>( override val value: Pack, override val x: Double = 0.0, override val y: Double = 0.0, - val height: Int, + val height: UInt, ) : DrawableVertex diff --git a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt index 09e0c33..3fb8f1f 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt +++ b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt @@ -67,7 +67,7 @@ class SQLiteRepositoryExposed { } for (index in vertexes.indices) VertexTableEntity.new { - height = vertexes[index].height + height = vertexes[index].height.toInt() value = serializeData(vertexes[index].value) order = index x = vertexes[index].x @@ -91,7 +91,7 @@ class SQLiteRepositoryExposed { ans.add( DrawableAVLVertex( value = deSerializeData(el.value), - height = el.height, + height = el.height.toUInt(), x = el.x, y = el.y ) diff --git a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt index ac6fa24..dc4ac75 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt +++ b/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt @@ -135,7 +135,7 @@ class SQLiteRepositoryJDBC>( 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.setInt(2, avlDVertex.height.toInt()) stmt.setDouble(3, avlDVertex.x) stmt.setDouble(4, avlDVertex.y) stmt.execute() @@ -173,7 +173,7 @@ class SQLiteRepositoryJDBC>( info.add( DrawableAVLVertex( value = deSerializeData(result.getString(value)), - height = result.getInt(height), + height = result.getInt(height).toUInt(), x = result.getDouble(xCord), y = result.getDouble(yCord), ) @@ -195,7 +195,7 @@ class SQLiteRepositoryJDBC>( 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.setInt(2, vertex.height.toInt()) stmt.setDouble(3, vertex.x) stmt.setDouble(4, vertex.y) id = stmt.executeQuery().getInt(1) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt index c23b96a..934092e 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt @@ -1,6 +1,5 @@ package viewPart.nodes.drawableAVL -import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import viewPart.nodes.drawableTree.DrawableNode diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt index da753c0..b39b3ff 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt @@ -1,5 +1,6 @@ package viewPart.nodes.drawableAVL +import androidx.compose.runtime.mutableStateOf import databaseManage.TreeManager import databaseSave.sqlite.DrawableAVLVertex import treelib.avlTree.AVLNode @@ -12,32 +13,30 @@ import viewPart.nodes.drawableTree.DrawableTree class AVLDrawableTree( override val name: String, override val treeManager: TreeManager, DrawableAVLVertex>, AVLNode>, AVLStateContainer>, AVLVertex>, AVLStruct>>, -) : DrawableTree< - AVLDrawableNode>, - DrawableAVLVertex>, - AVLNode>, - AVLStateContainer>, - AVLVertex>, - AVLStruct> - >() { +) : DrawableTree>, DrawableAVLVertex>, AVLNode>, AVLStateContainer>, AVLVertex>, AVLStruct>>() { override var root: AVLDrawableNode>? = null override var drawablePreOrder: List>>? = null override val treeStruct: AVLStruct> = AVLStruct() override val designNode = AVLNodeDesign - override fun initTree() { - TODO("Not yet implemented") - } + override fun drawableVertexToNode(vertex: DrawableAVLVertex>) = AVLDrawableNode( + value = vertex.value, + xState = mutableStateOf(0f), + yState = mutableStateOf(0f), + height = vertex.height.toInt() + ) - override fun deleteTree() { - TODO("Not yet implemented") - } + override fun vertexToNode(vertex: AVLVertex>) = AVLDrawableNode( + value = vertex.value, + xState = mutableStateOf(0f), + yState = mutableStateOf(0f), + height = vertex.height.toInt() + ) - override fun saveTree() { - TODO("Not yet implemented") - } - - override fun updateTree() { - TODO("Not yet implemented") - } + override fun nodeToDrawableVertex(node: AVLDrawableNode>) = DrawableAVLVertex( + value = node.value, + x = node.xState.value.toDouble(), + y = node.yState.value.toDouble(), + height = node.height.toUInt(), + ) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt index 6fef610..d1ebac0 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt @@ -1,28 +1,35 @@ package viewPart.nodes.drawableAVL +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp import viewPart.nodes.drawableTree.NodeDesign -object AVLNodeDesign: NodeDesign { - // TODO: Реализовать, как BINNodeDesign - override var nodeSize: Float - get() = TODO("Not yet implemented") - set(value) {} - override var shape: Shape - get() = TODO("Not yet implemented") - set(value) {} - override var lineStrokeWidth: Float - get() = TODO("Not yet implemented") - set(value) {} - override var lineColor: Color - get() = TODO("Not yet implemented") - set(value) {} +object AVLNodeDesign : NodeDesign { + override var lineColor = Color.Black + override var nodeSize = 100f + override var shape: Shape = CircleShape + override var lineStrokeWidth = 15f @Composable override fun infoView(information: String, modifier: Modifier) { - TODO("Not yet implemented") + Box( + modifier = modifier, + contentAlignment = Alignment.Center + ) { + Text( + text = information, + fontSize = 30.sp, + color = Color.White, + fontWeight = FontWeight.Bold + ) + } } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt index 454739d..fdfdc68 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt @@ -27,102 +27,21 @@ class BINDrawableTree( override val treeStruct = BINStruct>() override var drawablePreOrder: List>>? = null override val designNode = BINNodeDesign + override fun drawableVertexToNode(vertex: DrawableBINVertex>) = BINDrawableNode( + value = vertex.value, + xState = mutableStateOf(0f), + yState = mutableStateOf(0f), + ) - /* DB methods */ - override fun initTree() { - val binVertexes = treeManager.initTree(name, treeStruct) - drawablePreOrder = binVertexes.map { drawableVertexToNode(it) } - - drawablePreOrder?.let { - if (it.isNotEmpty()) { - restoreTree(it) - } - } - } - - override fun updateTree() { - for (el in vertexesToNodes(treeStruct.preOrder())) { - restoreInsert(el) - } - } - - private fun vertexesToNodes(preOrder: List>>) = sequence { - for (el in preOrder) { - yield( - vertexToNode(el) - ) - } - } - - private fun vertexToNode(vertex: BINVertex>): BINDrawableNode> { - return BINDrawableNode( - value = vertex.value, - xState = mutableStateOf(0f), - yState = mutableStateOf(0f), - ) - } - - private fun drawableVertexToNode(vertex: DrawableBINVertex>) = BINDrawableNode( + override fun vertexToNode(vertex: BINVertex>) = BINDrawableNode( value = vertex.value, - xState = mutableStateOf(vertex.x.toFloat()), - yState = mutableStateOf(vertex.y.toFloat()), + xState = mutableStateOf(0f), + yState = mutableStateOf(0f), ) - private fun nodeToDrawableVertex(node: BINDrawableNode>) = DrawableBINVertex>( + override fun nodeToDrawableVertex(node: BINDrawableNode>) = DrawableBINVertex( value = node.value, x = node.xState.value.toDouble(), y = node.yState.value.toDouble(), ) - - private fun restoreTree(preOrder: List>>) { - root = null - for (el in preOrder) { - restoreInsert(el) - } - } - - private fun restoreInsert(preOrderNode: BINDrawableNode>) { - if (root == null) { - root = preOrderNode - return - } - var currentParent = root - while (currentParent != null) { - currentParent?.let { - when { - it.value < preOrderNode.value -> { - if (it.rightChild == null) { - it.rightChild = preOrderNode - return@restoreInsert - } - else currentParent = it.rightChild - } - - it.value > preOrderNode.value -> { - if (it.leftChild == null) { - it.leftChild = preOrderNode - return@restoreInsert - } - else currentParent = it.leftChild - } - - else -> { - println(currentParent!!.value) - println(preOrderNode.value) - throw InternalError("Can't restore tree from preOrder :(") - } - } - } - } - } - - override fun deleteTree() = treeManager.deleteTreeFromDB(name) - - override fun saveTree() { - if (root != null) { - treeManager.saveTreeToDB(name, preOrder().map { nodeToDrawableVertex(it) }.toList(), listOf()) - } else { - treeManager.saveTreeToDB(name, treeStruct) - } - } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt index 19217b2..8b0fe77 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt @@ -1,8 +1,6 @@ package viewPart.nodes.drawableRB -import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import androidx.compose.ui.Modifier import treelib.rbTree.Markers import viewPart.nodes.drawableTree.DrawableNode diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt index bb8aa0e..9a6f18f 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt @@ -1,5 +1,6 @@ package viewPart.nodes.drawableRB +import androidx.compose.runtime.mutableStateOf import databaseManage.TreeManager import databaseSave.neo4j.DrawableRBVertex import treelib.commonObjects.Container @@ -25,19 +26,24 @@ class RBDrawableTree( override val treeStruct: RBStruct> = RBStruct() override val designNode = RBNodeDesign - override fun initTree() { - TODO("Not yet implemented") - } + override fun drawableVertexToNode(vertex: DrawableRBVertex>) = RBDrawableNode( + value = vertex.value, + xState = mutableStateOf(0f), + yState = mutableStateOf(0f), + color = vertex.color, + ) - override fun deleteTree() { - TODO("Not yet implemented") - } + override fun vertexToNode(vertex: RBVertex>) = RBDrawableNode( + value = vertex.value, + xState = mutableStateOf(0f), + yState = mutableStateOf(0f), + color = vertex.color, + ) - override fun saveTree() { - TODO("Not yet implemented") - } - - override fun updateTree() { - TODO("Not yet implemented") - } + override fun nodeToDrawableVertex(node: RBDrawableNode>) = DrawableRBVertex( + value = node.value, + x = node.xState.value.toDouble(), + y = node.yState.value.toDouble(), + color = node.color, + ) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt index 916df02..d3b5672 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt @@ -1,28 +1,37 @@ package viewPart.nodes.drawableRB +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp import viewPart.nodes.drawableTree.NodeDesign object RBNodeDesign: NodeDesign { - // TODO: Реализовать, как BINNodeDesign - override var nodeSize: Float - get() = TODO("Not yet implemented") - set(value) {} - override var shape: Shape - get() = TODO("Not yet implemented") - set(value) {} - override var lineStrokeWidth: Float - get() = TODO("Not yet implemented") - set(value) {} - override var lineColor: Color - get() = TODO("Not yet implemented") - set(value) {} + val redMarker = Color.Red + val blackMarker = Color.Black + override var nodeSize = 100f + override var shape: Shape = CircleShape + override var lineStrokeWidth = 15f + override var lineColor = Color.Black @Composable override fun infoView(information: String, modifier: Modifier) { - TODO("Not yet implemented") + Box( + modifier = modifier, + contentAlignment = Alignment.Center + ) { + Text( + text = information, + fontSize = 30.sp, + color = Color.White, + fontWeight = FontWeight.Bold + ) + } } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt index 08f0de3..0b93bf9 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt @@ -4,10 +4,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size -import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.input.pointer.pointerInput @@ -22,6 +19,7 @@ abstract class DrawableNode> { abstract var rightChild: NodeType? abstract val xState: MutableState abstract val yState: MutableState + var modifier = Modifier .offset { IntOffset( diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index f86691e..770137c 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -46,11 +46,34 @@ abstract class DrawableTree< val yShiftBetweenNodes = 10f - abstract fun initTree() - abstract fun deleteTree() - abstract fun saveTree() + fun initTree() { + val binVertexes = treeManager.initTree(name, treeStruct) + drawablePreOrder = binVertexes.map { drawableVertexToNode(it) } - abstract fun updateTree() + drawablePreOrder?.let { + if (it.isNotEmpty()) { + restoreTree(it) + } + } + } + + fun updateTree() { + root = null + val ded = treeStruct.preOrder() + for (el in vertexesToNodes(ded)) { + restoreInsert(el) + } + } + + fun deleteTree() = treeManager.deleteTreeFromDB(name) + + fun saveTree() { + if (root != null) { + treeManager.saveTreeToDB(name, preOrder().map { nodeToDrawableVertex(it) }.toList(), listOf()) + } else { + treeManager.saveTreeToDB(name, treeStruct) + } + } fun insert(item: Container) = treeStruct.insert(item) @@ -67,6 +90,54 @@ abstract class DrawableTree< } } + protected abstract fun vertexToNode(vertex: VertexType): DNodeType + + protected abstract fun nodeToDrawableVertex(node: DNodeType): DVertexType + + protected abstract fun drawableVertexToNode(vertex: DVertexType): DNodeType + + private fun restoreTree(preOrder: List) { + root = null + for (el in preOrder) { + restoreInsert(el) + } + } + + private fun restoreInsert(preOrderNode: DNodeType) { + if (root == null) { + root = preOrderNode + return + } + var currentParent = root + while (currentParent != null) { + currentParent?.let { + when { + it.value < preOrderNode.value -> { + if (it.rightChild == null) { + it.rightChild = preOrderNode + return@restoreInsert + } + else currentParent = it.rightChild + } + + it.value > preOrderNode.value -> { + if (it.leftChild == null) { + it.leftChild = preOrderNode + return@restoreInsert + } + else currentParent = it.leftChild + } + + else -> { + println(currentParent!!.value) + println(preOrderNode.value) + throw InternalError("Can't restore tree from preOrder :(") + } + } + } + } + } + private fun createCordsState1(node: DNodeType, x: Float, y: Float): Float { if (node.leftChild == null && node.rightChild == null) { node.xState.value = x @@ -100,7 +171,7 @@ abstract class DrawableTree< return n } - protected fun preOrder() = sequence{ + private fun preOrder() = sequence{ var current: DNodeType val queue = ArrayDeque() @@ -121,4 +192,12 @@ abstract class DrawableTree< } } } + + private fun vertexesToNodes(preOrder: List) = sequence { + for (el in preOrder) { + yield( + vertexToNode(el) + ) + } + } } From a852cbe3c07d3175619e65b7dee2dafe39712ea1 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 1 May 2023 00:25:48 +0300 Subject: [PATCH 176/212] fix: Fix the visual representation of the tree --- lib/src/main/kotlin/controller/Controller.kt | 124 ++++++++++++++---- .../kotlin/databaseManage/RBTreeManager.kt | 28 ++-- .../databaseSave/neo4j/Neo4jRepository.kt | 4 +- lib/src/main/kotlin/main.kt | 5 +- .../kotlin/treelib/commonObjects/Container.kt | 2 +- lib/src/main/kotlin/ui/ControlFields.kt | 31 ++++- lib/src/main/kotlin/ui/TopAppBar.kt | 13 +- .../kotlin/viewPart/nodes/TreeDrawingUtils.kt | 44 ++++++- .../nodes/drawableAVL/AVLDrawableNode.kt | 6 +- .../nodes/drawableBIN/BINDrawableNode.kt | 22 +--- .../nodes/drawableBIN/BINDrawableTree.kt | 3 +- .../nodes/drawableBIN/BINNodeDesign.kt | 24 ++-- .../nodes/drawableRB/RBDrawableNode.kt | 7 +- .../nodes/drawableTree/DrawableNode.kt | 9 +- .../nodes/drawableTree/DrawableTree.kt | 41 +++--- 15 files changed, 247 insertions(+), 116 deletions(-) diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index 9059f60..5361537 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -3,37 +3,117 @@ package controller import databaseManage.AVLTreeManager import databaseManage.BINTreeManager import databaseManage.RBTreeManager -import java.io.IOException +import treelib.commonObjects.Container +import viewPart.nodes.drawableAVL.AVLDrawableTree +import viewPart.nodes.drawableBIN.BINDrawableTree +import viewPart.nodes.drawableRB.RBDrawableTree +import java.io.File +import java.lang.Integer.min -class Controller { + +class Controller(/* */) { private val avlManager = AVLTreeManager() private val rbManager = RBTreeManager() private val binManager = BINTreeManager() + //val tree: test? = null + + + // lazy + private var binTree = BINDrawableTree("baseName.json", binManager) + private var avlTree = AVLDrawableTree("baseName", avlManager) + private var rbTree = RBDrawableTree("baseName", rbManager) + + //private var num by remember { mutableStateOf(0) } + fun showFiles(): List> { -// avlManager.initDataBase("DBname") - return listOf(rbManager.getSavedTreesNames(), avlManager.getSavedTreesNames(), binManager.getSavedTreesNames()) + val avlTrees = avlManager.getSavedTreesNames() + val rbTrees = rbManager.getSavedTreesNames() + + createDummyFiles(avlTrees, rbTrees) + + return listOf( + rbTrees.subList(0, min(3, rbTrees.size)), + avlTrees.subList(0, min(3, avlTrees.size)), + binManager.getSavedTreesNames() + ) + } + + private fun createDummyFiles(avlTrees: List, rbTrees: List) { + val avlPath = System.getProperty("user.dir") + "/saved-trees/AVL-trees" + val rbPath = System.getProperty("user.dir") + "/saved-trees/RB-trees" + + File(avlPath).mkdirs() + File(rbPath).mkdirs() + + if (avlTrees.isNotEmpty()) { + for (name in avlTrees) { + File(avlPath, name).run { + createNewFile() + } + } + } + + if (rbTrees.isNotEmpty()) { + for (name in rbTrees) { + File(rbPath, name).run { + createNewFile() + } + } + } + + } + + fun createTree(treeName: String, /*id*/) { + binTree = BINDrawableTree(treeName, binManager) // тут буду создавать один из 3х видов + //num = index + + // tree = AVLDrawableTree(...) + + // tree = RBDrawableTree(..) + + //tree.value = RBDrawableTree(tree, rbManager) + } + + fun insert(value: String): BINDrawableTree { // DrawableTreeType + val key = if (isInt(value.trim())) value.toInt() else value.hashCode() + + binTree.insert(Container(Pair(key, value))) + binTree.updateTree() + binTree.repositisonTree(800f, 10f) + + //return tree + return binTree + } + + fun find(value: String): BINDrawableTree { + val key = if (isInt(value.trim())) value.toInt() else value.hashCode() + + binTree.find(key) + + return binTree + + } + + fun delete(value: String): BINDrawableTree { + val key = if (isInt(value.trim())) value.toInt() else value.hashCode() + + binTree.delete(Container(Pair(key, ""))) + binTree.updateTree() + binTree.repositisonTree(800f, 10f) + + return binTree } - fun foo(fileName: String) { - if (fileName == "myTree.json") - throw IOException() + + private fun isInt(value: String): Boolean { + return try { + Integer.parseInt(value.trim()) + true + } catch (ex: NumberFormatException) { + false + } } } -/* -, NodeType : Node, State : StateContainer, - VertexType : Vertex, TreeType : TreeStruct> - */ - -/* - val RBtree = RBStruct>() - RBtree.restoreStruct(orders.first, orders.second) - neo4jDB.close() -*/ - -/* -val BINtree = BINStruct>() - BINtree.restoreStruct(preOrder.toList()) - */ diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt index 84f80e2..f898817 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -7,7 +7,6 @@ import treelib.rbTree.RBNode import treelib.rbTree.RBStateContainer import treelib.rbTree.RBStruct import treelib.rbTree.RBVertex -import java.io.File class RBTreeManager : TreeManager< Container, @@ -58,19 +57,20 @@ class RBTreeManager : TreeManager< } - override fun getSavedTreesNames(): List { - val treesNames = neo4jDB.findNamesTrees() - val dirPath = System.getProperty("user.dir") + "/saved-trees/RB-trees" - File(dirPath).mkdirs() - if (treesNames != null) { - for (name in treesNames) { - File(dirPath, name).run { - createNewFile() - } - } - } - return treesNames?.subList(0, treesNames.size) ?: listOf() - } + override fun getSavedTreesNames(): List = neo4jDB.findNamesTrees() + //{ + // val treesNames = neo4jDB.findNamesTrees() + // val dirPath = System.getProperty("user.dir") + "/saved-trees/RB-trees" + // File(dirPath).mkdirs() + // if (treesNames.isNotEmpty()) { + // for (name in treesNames) { + // File(dirPath, name).run { + // createNewFile() + // } + // } + // } + // return treesNames.subList(0, treesNames.size) + //} /* override fun delete(item: Container) = rbTree.insert(item) diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index 4022b62..8920e55 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -144,9 +144,9 @@ class Neo4jRepository : Closeable { } - fun findNamesTrees(): List? { + fun findNamesTrees(): List { val session = driver?.session() ?: throw IOException() - var treesNames: List? = null + var treesNames: List = listOf() session.executeRead { tx -> treesNames = tx.run("MATCH (n: Node) WHERE NOT(:Node)-->(n) RETURN n.treeName") .list().map { it.values()[0].toString().replace("\"", "") }.filter { it != "null" } diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index d9d0829..a0015a6 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -24,7 +24,7 @@ import controller.Controller import ui.* import java.awt.Dimension -@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) +@OptIn(ExperimentalMaterial3Api::class) fun main() = application { val clickButtonsState = List(7) { remember { mutableStateOf(false) } } @@ -44,7 +44,7 @@ fun main() = application { Scaffold( topBar = { WindowDraggableArea { - myTopAppBar(clickButtonsState, windowState) + myTopAppBar(clickButtonsState, windowState, controller) } }, content = { @@ -62,6 +62,7 @@ fun main() = application { ) { controlFields(controller) + } } diff --git a/lib/src/main/kotlin/treelib/commonObjects/Container.kt b/lib/src/main/kotlin/treelib/commonObjects/Container.kt index 6699b16..20530cf 100644 --- a/lib/src/main/kotlin/treelib/commonObjects/Container.kt +++ b/lib/src/main/kotlin/treelib/commonObjects/Container.kt @@ -25,6 +25,6 @@ data class Container, V>(val pair: Pair) : Comparable) { +fun controlField(buttonName: String, buttonState: MutableState, value: MutableState) { var text by remember { mutableStateOf("") } @@ -80,6 +96,7 @@ fun controlField(buttonName: String, buttonState: MutableState) { }.onPreviewKeyEvent { when { (!it.isShiftPressed && it.key == Key.Enter && it.type == KeyEventType.KeyUp) -> { + value.value = text // change !!! buttonState.value = true text = "" true diff --git a/lib/src/main/kotlin/ui/TopAppBar.kt b/lib/src/main/kotlin/ui/TopAppBar.kt index b5c6683..cc17d2c 100644 --- a/lib/src/main/kotlin/ui/TopAppBar.kt +++ b/lib/src/main/kotlin/ui/TopAppBar.kt @@ -32,9 +32,9 @@ import myTypography import topAppIconButton @Composable -fun myTopAppBar(clickButtonsState: List>, windowState: WindowState) { +fun myTopAppBar(clickButtonsState: List>, windowState: WindowState, controller: Controller) { - val controller = Controller() + //val controller = Controller() val showFiles = controller.showFiles() val fileName = remember { mutableStateOf("") } // имя файла в диалоге save @@ -89,7 +89,7 @@ fun myTopAppBar(clickButtonsState: List>, windowState: Win MaterialTheme.colorScheme.background, clickButtonsState[3] ) - createMenu(clickButtonsState[3]) + createMenu(clickButtonsState[3], controller) } @@ -159,7 +159,7 @@ private fun maximizeWindow(state: WindowState) { @OptIn(ExperimentalComposeUiApi::class) @Composable -fun createMenu(expandedNested: MutableState) { +fun createMenu(expandedNested: MutableState, controller: Controller) { val treesNames = listOf("Red black tree", "AVL tree", "Binary tree") val backgroundColorState = List(3) { remember { mutableStateOf(false) } } @@ -174,8 +174,9 @@ fun createMenu(expandedNested: MutableState) { repeat(3) { index -> DropdownMenuItem(text = { Text(treesNames[index]) }, onClick = { - //expandedNested.value = false - // вызываю контроллер на создание + // тут определяем тип дерева и передаем в контроллер + controller.createTree("baseName.json") + expandedNested.value = false }, modifier = Modifier.onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index e8542a4..6d91a53 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -1,19 +1,31 @@ package viewPart.nodes import androidx.compose.foundation.Canvas -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex +import treelib.commonObjects.Container +import viewPart.nodes.drawableAVL.AVLDrawableNode import viewPart.nodes.drawableAVL.AVLDrawableTree import viewPart.nodes.drawableAVL.AVLNodeDesign import viewPart.nodes.drawableBIN.BINDrawableTree import viewPart.nodes.drawableBIN.BINNodeDesign +import viewPart.nodes.drawableRB.RBDrawableNode import viewPart.nodes.drawableRB.RBDrawableTree import viewPart.nodes.drawableRB.RBNodeDesign import viewPart.nodes.drawableTree.DrawableNode import viewPart.nodes.drawableTree.NodeDesign +import kotlin.math.roundToInt @Composable fun displayTree(tree: RBDrawableTree) { @@ -40,7 +52,35 @@ fun displayTree(tree: BINDrawableTree) { } @Composable -fun , NodeD : NodeDesign> displayNode(node: DNode, design: NodeD) { +fun , DNode>, NodeD : NodeDesign> displayNode(node: DNode, design: NodeD) { + + if (node.clickState.value) { + Box(modifier = Modifier + .height(60.dp).width(80.dp) + .offset { + IntOffset( + node.xState.value.roundToInt() + 71, + node.yState.value.roundToInt() + ) + } + .background(color = Color(237, 232, 232)) + .border(2.dp, MaterialTheme.colorScheme.primary, AbsoluteRoundedCornerShape(5.dp)) + .padding(horizontal = 4.dp, vertical = 2.dp) + .zIndex(1f) + ) { + Column(modifier = Modifier.zIndex(1f)) { + Text(text = "key: ${node.value.key}") + Text(text = "value: ${node.value.value}") + when (node ) { + is AVLDrawableNode<*> -> Text(text = "height: ${node.height}") + is RBDrawableNode<*> -> Text(text = "color: ${node.color}") + } + + } + + } + } + design.infoView(node.value.toString(), node.modifier) node.leftChild?.let { diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt index c23b96a..979bd21 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt @@ -1,7 +1,7 @@ package viewPart.nodes.drawableAVL -import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf import viewPart.nodes.drawableTree.DrawableNode class AVLDrawableNode( @@ -11,4 +11,6 @@ class AVLDrawableNode( val height: Int, override val xState: MutableState, override val yState: MutableState, -) : DrawableNode>() +) : DrawableNode>() { + override val clickState = mutableStateOf(false) +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt index 06ee527..7760293 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt @@ -1,22 +1,8 @@ package viewPart.nodes.drawableBIN -import androidx.compose.foundation.Canvas import androidx.compose.runtime.MutableState -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.detectDragGestures -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.size -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex +import androidx.compose.runtime.mutableStateOf import viewPart.nodes.drawableTree.DrawableNode -import kotlin.math.roundToInt class BINDrawableNode( override val value: Pack, @@ -24,4 +10,8 @@ class BINDrawableNode( override var rightChild: BINDrawableNode? = null, override var xState: MutableState, override var yState: MutableState, -) : DrawableNode>() + + ) : DrawableNode>() { + override val clickState = mutableStateOf(false) + +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt index 454739d..c569952 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt @@ -41,6 +41,7 @@ class BINDrawableTree( } override fun updateTree() { + root = null for (el in vertexesToNodes(treeStruct.preOrder())) { restoreInsert(el) } @@ -68,7 +69,7 @@ class BINDrawableTree( yState = mutableStateOf(vertex.y.toFloat()), ) - private fun nodeToDrawableVertex(node: BINDrawableNode>) = DrawableBINVertex>( + private fun nodeToDrawableVertex(node: BINDrawableNode>) = DrawableBINVertex( value = node.value, x = node.xState.value.toDouble(), y = node.yState.value.toDouble(), diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt index c8ba9ce..8f4eda6 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt @@ -2,35 +2,31 @@ package viewPart.nodes.drawableBIN import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp import viewPart.nodes.drawableTree.NodeDesign object BINNodeDesign: NodeDesign { - var colorNode = Color.Green - override var lineColor = Color.Black - override var nodeSize = 100f + var colorNode = Color(208, 223, 252) + override var lineColor = Color(34, 35, 41) + override var nodeSize = 60f override var shape: Shape = CircleShape - override var lineStrokeWidth = 15f - + override var lineStrokeWidth = 10f @Composable override fun infoView(information: String, modifier: Modifier) { Box( modifier = modifier, contentAlignment = Alignment.Center ) { - Text( - text = information, - fontSize = 30.sp, - color = Color.White, - fontWeight = FontWeight.Bold - ) + //Text( + // text = information, + // fontSize = 30.sp, + // color = Color.White, + // fontWeight = FontWeight.Bold + //) } } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt index 19217b2..e3d4e50 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt @@ -1,8 +1,7 @@ package viewPart.nodes.drawableRB -import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import androidx.compose.ui.Modifier +import androidx.compose.runtime.mutableStateOf import treelib.rbTree.Markers import viewPart.nodes.drawableTree.DrawableNode @@ -13,4 +12,6 @@ class RBDrawableNode( val color: Markers, override val xState: MutableState, override val yState: MutableState, -) : DrawableNode>() +) : DrawableNode>() { + override val clickState = mutableStateOf(false) +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt index 08f0de3..d748a54 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt @@ -1,13 +1,12 @@ package viewPart.nodes.drawableTree +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size -import androidx.compose.runtime.Composable +import androidx.compose.foundation.onClick import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.input.pointer.pointerInput @@ -22,6 +21,8 @@ abstract class DrawableNode> { abstract var rightChild: NodeType? abstract val xState: MutableState abstract val yState: MutableState + abstract val clickState: MutableState + @OptIn(ExperimentalFoundationApi::class) var modifier = Modifier .offset { IntOffset( @@ -38,4 +39,6 @@ abstract class DrawableNode> { .size(BINNodeDesign.nodeSize.dp) .clip(BINNodeDesign.shape) .background(BINNodeDesign.colorNode) + .onClick { clickState.value = !clickState.value } + } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index f86691e..7eda2d5 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -9,6 +9,7 @@ import treelib.abstractTree.Vertex import treelib.commonObjects.Container import treelib.commonObjects.exceptions.ImpossibleCaseException + abstract class DrawableTree< DNodeType : DrawableNode, DNodeType>, DVertexType : DrawableVertex>, @@ -17,26 +18,7 @@ abstract class DrawableTree< VertexType : Vertex>, StructType : TreeStruct, NodeType, State, VertexType> > { - /**How you should it use: - * - * - * - * OUTSIDE A COMPOSABLE CONTEXT { - * * val manager = TreeManager() - * * val tree = DrawableTree("name", manager) - * * tree.insert - * * tree.delete - * * ... - * * tree.update <- (means moving information: TreeStruct.root -> DrawableTree.root) - * * tree.repositisonTree <- (set "beautiful" coordinates without display) - * - * } - * - * IN A COMPOSABLE CONTEXT { - * * TreeDrawingUtils.displayTree(tree) - * - * } - * **/ + abstract val name: String internal abstract var root: DNodeType? protected abstract var drawablePreOrder: List? @@ -58,7 +40,24 @@ abstract class DrawableTree< if (treeStruct.find(item) != null) treeStruct.delete(item) } - fun find(item: Container): Container? = treeStruct.find(item) + //fun find(item: Container): Container? = treeStruct.find(item) + fun find(item: Int): Container? { + var currentNode = root + if (root == null) + return null + while(true) { + if (item == currentNode?.value?.key) { + currentNode.clickState.value = true + return Container(Pair(item, currentNode.value.value)) + } + currentNode?.let { + if (item > it.value.key) currentNode = it.rightChild + else currentNode = it.leftChild + } + if (currentNode == null) return null + } + + } fun repositisonTree(xBase: Float = 0f, yBase: Float = 0f) { root?.let { From 9abc69d14f9c583fc933c330f9d1e8ca689929b1 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 1 May 2023 00:29:29 +0300 Subject: [PATCH 177/212] feat: Implement DrawTree interface (#24). --- .../viewPart/nodes/drawableTree/DrawTree.kt | 19 ++++++++ .../nodes/drawableTree/DrawableTree.kt | 45 +++++-------------- 2 files changed, 31 insertions(+), 33 deletions(-) create mode 100644 lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt new file mode 100644 index 0000000..115693e --- /dev/null +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt @@ -0,0 +1,19 @@ +package viewPart.nodes.drawableTree + +import treelib.commonObjects.Container + +interface DrawTree, DNodeType>> { + val name: String + var root: DNodeType? + val designNode: NodeDesign + var yShiftBetweenNodes: Float + + fun initTree() + fun updateTree() + fun deleteTree() + fun saveTree() + fun insert(item: Container) + fun delete(item: Container) + fun find(item: Container): Container? + fun repositisonTree(xBase: Float, yBase: Float) +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index 770137c..f3d1042 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -16,37 +16,15 @@ abstract class DrawableTree< State : StateContainer, NodeType>, VertexType : Vertex>, StructType : TreeStruct, NodeType, State, VertexType> - > { - /**How you should it use: - * - * - * - * OUTSIDE A COMPOSABLE CONTEXT { - * * val manager = TreeManager() - * * val tree = DrawableTree("name", manager) - * * tree.insert - * * tree.delete - * * ... - * * tree.update <- (means moving information: TreeStruct.root -> DrawableTree.root) - * * tree.repositisonTree <- (set "beautiful" coordinates without display) - * - * } - * - * IN A COMPOSABLE CONTEXT { - * * TreeDrawingUtils.displayTree(tree) - * - * } - * **/ - abstract val name: String - internal abstract var root: DNodeType? + >: DrawTree { + protected abstract var drawablePreOrder: List? protected abstract val treeManager: TreeManager, DVertexType, NodeType, State, VertexType, StructType> protected abstract val treeStruct: StructType - abstract val designNode: NodeDesign - val yShiftBetweenNodes = 10f + override var yShiftBetweenNodes = 10f - fun initTree() { + override fun initTree() { val binVertexes = treeManager.initTree(name, treeStruct) drawablePreOrder = binVertexes.map { drawableVertexToNode(it) } @@ -57,7 +35,7 @@ abstract class DrawableTree< } } - fun updateTree() { + override fun updateTree() { root = null val ded = treeStruct.preOrder() for (el in vertexesToNodes(ded)) { @@ -65,9 +43,9 @@ abstract class DrawableTree< } } - fun deleteTree() = treeManager.deleteTreeFromDB(name) + override fun deleteTree() = treeManager.deleteTreeFromDB(name) - fun saveTree() { + override fun saveTree() { if (root != null) { treeManager.saveTreeToDB(name, preOrder().map { nodeToDrawableVertex(it) }.toList(), listOf()) } else { @@ -75,15 +53,16 @@ abstract class DrawableTree< } } - fun insert(item: Container) = treeStruct.insert(item) + override fun insert(item: Container) = treeStruct.insert(item) - fun delete(item: Container) { + override fun delete(item: Container) { if (treeStruct.find(item) != null) treeStruct.delete(item) } - fun find(item: Container): Container? = treeStruct.find(item) + override fun find(item: Container): Container? = treeStruct.find(item) - fun repositisonTree(xBase: Float = 0f, yBase: Float = 0f) { + override fun repositisonTree(xBase: Float, yBase: Float) { + /*xBase: Float = 0f, yBase: Float = 0f*/ root?.let { createCordsState1(it, xBase, yBase) createCordsState2(it) From e8c12a3fe3c6b922b15d8ee1995670c9add8b46d Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 1 May 2023 13:23:11 +0300 Subject: [PATCH 178/212] fix: Move displayTree method inside DrawableTree (#24). --- .../kotlin/viewPart/nodes/TreeDrawingUtils.kt | 26 ++----------------- .../viewPart/nodes/drawableTree/DrawTree.kt | 4 +++ .../nodes/drawableTree/DrawableTree.kt | 20 +++++++++----- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index e8542a4..4943778 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -6,39 +6,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.zIndex +import treelib.commonObjects.Container import viewPart.nodes.drawableAVL.AVLDrawableTree import viewPart.nodes.drawableAVL.AVLNodeDesign import viewPart.nodes.drawableBIN.BINDrawableTree import viewPart.nodes.drawableBIN.BINNodeDesign import viewPart.nodes.drawableRB.RBDrawableTree import viewPart.nodes.drawableRB.RBNodeDesign +import viewPart.nodes.drawableTree.DrawTree import viewPart.nodes.drawableTree.DrawableNode import viewPart.nodes.drawableTree.NodeDesign -@Composable -fun displayTree(tree: RBDrawableTree) { - val root = tree.root - root?.let { - displayNode(it, RBNodeDesign) - } -} - -@Composable -fun displayTree(tree: AVLDrawableTree) { - val root = tree.root - root?.let { - displayNode(it, AVLNodeDesign) - } -} - -@Composable -fun displayTree(tree: BINDrawableTree) { - val root = tree.root - root?.let { - displayNode(it, BINNodeDesign) - } -} - @Composable fun , NodeD : NodeDesign> displayNode(node: DNode, design: NodeD) { design.infoView(node.value.toString(), node.modifier) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt index 115693e..9e2fe62 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt @@ -1,5 +1,6 @@ package viewPart.nodes.drawableTree +import androidx.compose.runtime.Composable import treelib.commonObjects.Container interface DrawTree, DNodeType>> { @@ -8,6 +9,9 @@ interface DrawTree, DNodeType>> val designNode: NodeDesign var yShiftBetweenNodes: Float + @Composable + fun displayTree() + fun initTree() fun updateTree() fun deleteTree() diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index f3d1042..f000885 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -1,5 +1,6 @@ package viewPart.nodes.drawableTree +import androidx.compose.runtime.Composable import databaseManage.TreeManager import databaseSave.DrawableVertex import treelib.abstractTree.Node @@ -8,6 +9,8 @@ import treelib.abstractTree.TreeStruct import treelib.abstractTree.Vertex import treelib.commonObjects.Container import treelib.commonObjects.exceptions.ImpossibleCaseException +import viewPart.nodes.displayNode +import viewPart.nodes.drawableBIN.BINNodeDesign abstract class DrawableTree< DNodeType : DrawableNode, DNodeType>, @@ -16,7 +19,7 @@ abstract class DrawableTree< State : StateContainer, NodeType>, VertexType : Vertex>, StructType : TreeStruct, NodeType, State, VertexType> - >: DrawTree { + > : DrawTree { protected abstract var drawablePreOrder: List? protected abstract val treeManager: TreeManager, DVertexType, NodeType, State, VertexType, StructType> @@ -24,6 +27,13 @@ abstract class DrawableTree< override var yShiftBetweenNodes = 10f + @Composable + override fun displayTree() { + root?.let { + displayNode(it, designNode) + } + } + override fun initTree() { val binVertexes = treeManager.initTree(name, treeStruct) drawablePreOrder = binVertexes.map { drawableVertexToNode(it) } @@ -95,16 +105,14 @@ abstract class DrawableTree< if (it.rightChild == null) { it.rightChild = preOrderNode return@restoreInsert - } - else currentParent = it.rightChild + } else currentParent = it.rightChild } it.value > preOrderNode.value -> { if (it.leftChild == null) { it.leftChild = preOrderNode return@restoreInsert - } - else currentParent = it.leftChild + } else currentParent = it.leftChild } else -> { @@ -150,7 +158,7 @@ abstract class DrawableTree< return n } - private fun preOrder() = sequence{ + private fun preOrder() = sequence { var current: DNodeType val queue = ArrayDeque() From 7a408cebc390e869e32d6e1e5acac27db74d0d1d Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 1 May 2023 14:02:28 +0300 Subject: [PATCH 179/212] feat: Update DrawTree interface: make name and root of a DrawableTree var instead of val; create method - split methods deleteTree and deleteTreeFromDB (#24). --- .../nodes/drawableAVL/AVLDrawableTree.kt | 9 +++++-- .../nodes/drawableBIN/BINDrawableTree.kt | 10 +++++-- .../nodes/drawableRB/RBDrawableTree.kt | 9 +++++-- .../viewPart/nodes/drawableTree/DrawTree.kt | 26 +++++++++++++++++-- .../nodes/drawableTree/DrawableTree.kt | 6 ++--- 5 files changed, 49 insertions(+), 11 deletions(-) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt index b39b3ff..8fa5cb2 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt @@ -11,14 +11,19 @@ import treelib.commonObjects.Container import viewPart.nodes.drawableTree.DrawableTree class AVLDrawableTree( - override val name: String, + override var name: String, override val treeManager: TreeManager, DrawableAVLVertex>, AVLNode>, AVLStateContainer>, AVLVertex>, AVLStruct>>, ) : DrawableTree>, DrawableAVLVertex>, AVLNode>, AVLStateContainer>, AVLVertex>, AVLStruct>>() { override var root: AVLDrawableNode>? = null override var drawablePreOrder: List>>? = null - override val treeStruct: AVLStruct> = AVLStruct() + override var treeStruct = AVLStruct>() override val designNode = AVLNodeDesign + override fun deleteTree() { + root = null + treeStruct = AVLStruct() + } + override fun drawableVertexToNode(vertex: DrawableAVLVertex>) = AVLDrawableNode( value = vertex.value, xState = mutableStateOf(0f), diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt index fdfdc68..c3e03ae 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt @@ -11,7 +11,7 @@ import treelib.commonObjects.Container import viewPart.nodes.drawableTree.DrawableTree class BINDrawableTree( - override val name: String, + override var name: String, override val treeManager: BINTreeManager, ) : DrawableTree< @@ -24,9 +24,15 @@ class BINDrawableTree( >() { override var root: BINDrawableNode>? = null - override val treeStruct = BINStruct>() + override var treeStruct = BINStruct>() override var drawablePreOrder: List>>? = null override val designNode = BINNodeDesign + + override fun deleteTree() { + root = null + treeStruct = BINStruct() + } + override fun drawableVertexToNode(vertex: DrawableBINVertex>) = BINDrawableNode( value = vertex.value, xState = mutableStateOf(0f), diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt index 9a6f18f..1a0cf3c 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt @@ -11,7 +11,7 @@ import treelib.rbTree.RBVertex import viewPart.nodes.drawableTree.DrawableTree class RBDrawableTree( - override val name: String, + override var name: String, override val treeManager: TreeManager, DrawableRBVertex>, RBNode>, RBStateContainer>, RBVertex>, RBStruct>>, ) : DrawableTree< RBDrawableNode>, @@ -23,9 +23,14 @@ class RBDrawableTree( >() { override var root: RBDrawableNode>? = null override var drawablePreOrder: List>>? = null - override val treeStruct: RBStruct> = RBStruct() + override var treeStruct = RBStruct>() override val designNode = RBNodeDesign + override fun deleteTree() { + root = null + treeStruct = RBStruct() + } + override fun drawableVertexToNode(vertex: DrawableRBVertex>) = RBDrawableNode( value = vertex.value, xState = mutableStateOf(0f), diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt index 9e2fe62..d2ac753 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt @@ -4,7 +4,28 @@ import androidx.compose.runtime.Composable import treelib.commonObjects.Container interface DrawTree, DNodeType>> { - val name: String + /**How you should it use: + * + * + * + * OUTSIDE A COMPOSABLE CONTEXT { + * * val manager = TreeManager() + * * val tree = DrawableTree("name", manager) + * * tree.insert + * * tree.delete + * * ... + * * tree.update <- (means moving information: TreeStruct.root -> DrawableTree.root) + * * tree.repositisonTree <- (set "beautiful" coordinates without display) + * + * } + * + * IN A COMPOSABLE CONTEXT { + * * tree.displayTree() + * + * } + * **/ + + var name: String var root: DNodeType? val designNode: NodeDesign var yShiftBetweenNodes: Float @@ -15,7 +36,8 @@ interface DrawTree, DNodeType>> fun initTree() fun updateTree() fun deleteTree() - fun saveTree() + fun deleteTreeFromBD() + fun saveTreeToDB() fun insert(item: Container) fun delete(item: Container) fun find(item: Container): Container? diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index f000885..ed9a48a 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -23,7 +23,7 @@ abstract class DrawableTree< protected abstract var drawablePreOrder: List? protected abstract val treeManager: TreeManager, DVertexType, NodeType, State, VertexType, StructType> - protected abstract val treeStruct: StructType + protected abstract var treeStruct: StructType override var yShiftBetweenNodes = 10f @@ -53,9 +53,9 @@ abstract class DrawableTree< } } - override fun deleteTree() = treeManager.deleteTreeFromDB(name) + override fun deleteTreeFromBD() = treeManager.deleteTreeFromDB(name) - override fun saveTree() { + override fun saveTreeToDB() { if (root != null) { treeManager.saveTreeToDB(name, preOrder().map { nodeToDrawableVertex(it) }.toList(), listOf()) } else { From d57d45c8a00bbfa515f08911b27968415a04f716 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 1 May 2023 14:07:17 +0300 Subject: [PATCH 180/212] feat: Implement the integration of trees in the main interface --- lib/src/main/kotlin/controller/Controller.kt | 66 ++++++++++--------- lib/src/main/kotlin/main.kt | 50 +++++++------- lib/src/main/kotlin/ui/ControlFields.kt | 34 +++++----- lib/src/main/kotlin/ui/DeleteAlertDialog.kt | 43 +++++++++--- lib/src/main/kotlin/ui/FileDialog.kt | 4 +- lib/src/main/kotlin/ui/SaveDialog.kt | 27 ++++++-- lib/src/main/kotlin/ui/TopAppBar.kt | 60 ++++++++++------- .../kotlin/viewPart/nodes/TreeDrawingUtils.kt | 18 ++--- .../nodes/drawableAVL/AVLDrawableNode.kt | 2 +- .../nodes/drawableAVL/AVLNodeDesign.kt | 22 +++---- .../nodes/drawableBIN/BINDrawableNode.kt | 2 +- .../nodes/drawableBIN/BINNodeDesign.kt | 2 +- .../nodes/drawableRB/RBDrawableNode.kt | 2 +- .../viewPart/nodes/drawableRB/RBNodeDesign.kt | 26 ++++---- .../viewPart/nodes/drawableTree/DrawTree.kt | 8 ++- .../nodes/drawableTree/DrawableNode.kt | 10 +-- .../nodes/drawableTree/DrawableTree.kt | 30 ++++++++- .../viewPart/nodes/drawableTree/NodeDesign.kt | 1 + 18 files changed, 244 insertions(+), 163 deletions(-) diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index 5361537..e33ee57 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -7,24 +7,19 @@ import treelib.commonObjects.Container import viewPart.nodes.drawableAVL.AVLDrawableTree import viewPart.nodes.drawableBIN.BINDrawableTree import viewPart.nodes.drawableRB.RBDrawableTree +import viewPart.nodes.drawableTree.DrawTree import java.io.File import java.lang.Integer.min -class Controller(/* */) { +class Controller { + private val avlManager = AVLTreeManager() private val rbManager = RBTreeManager() private val binManager = BINTreeManager() - //val tree: test? = null - - - // lazy - private var binTree = BINDrawableTree("baseName.json", binManager) - private var avlTree = AVLDrawableTree("baseName", avlManager) - private var rbTree = RBDrawableTree("baseName", rbManager) - //private var num by remember { mutableStateOf(0) } + var tree: DrawTree? = null fun showFiles(): List> { val avlTrees = avlManager.getSavedTreesNames() @@ -64,45 +59,42 @@ class Controller(/* */) { } - fun createTree(treeName: String, /*id*/) { - binTree = BINDrawableTree(treeName, binManager) // тут буду создавать один из 3х видов - //num = index - - // tree = AVLDrawableTree(...) - - // tree = RBDrawableTree(..) + fun createTree(treeName: String, id: Int) { + when (id) { + 0 -> tree = RBDrawableTree(treeName, rbManager) + 1 -> tree = AVLDrawableTree(treeName, avlManager) + 2 -> tree = BINDrawableTree(treeName, binManager) + } - //tree.value = RBDrawableTree(tree, rbManager) } - fun insert(value: String): BINDrawableTree { // DrawableTreeType + fun insert(value: String): DrawTree { val key = if (isInt(value.trim())) value.toInt() else value.hashCode() - binTree.insert(Container(Pair(key, value))) - binTree.updateTree() - binTree.repositisonTree(800f, 10f) + tree?.insert(Container(Pair(key, value))) ?: throw NullPointerException() + tree?.updateTree() ?: throw NullPointerException() + tree?.repositisonTree(800f, 10f) ?: throw NullPointerException() - //return tree - return binTree + return tree ?: throw NullPointerException() } - fun find(value: String): BINDrawableTree { + fun find(value: String): DrawTree { val key = if (isInt(value.trim())) value.toInt() else value.hashCode() - binTree.find(key) + tree?.find(key) ?: throw NullPointerException() - return binTree + return tree ?: throw NullPointerException() } - fun delete(value: String): BINDrawableTree { + fun delete(value: String): DrawTree { val key = if (isInt(value.trim())) value.toInt() else value.hashCode() - binTree.delete(Container(Pair(key, ""))) - binTree.updateTree() - binTree.repositisonTree(800f, 10f) + tree?.delete(Container(Pair(key, ""))) ?: throw NullPointerException() + tree?.updateTree() ?: throw NullPointerException() + tree?.repositisonTree(800f, 10f) ?: throw NullPointerException() - return binTree + return tree ?: throw NullPointerException() } @@ -115,5 +107,17 @@ class Controller(/* */) { } } + fun deleteTree() { + //tree.deleteTree() + tree = null + tree?.updateTree() + } + + fun saveTree(fileName: String) { + //tree.name = fileName + tree?.saveTree() ?: throw Exception("Tree not initialized") + + } + } diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index a0015a6..8d83e99 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -1,4 +1,3 @@ - import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.window.WindowDraggableArea @@ -33,6 +32,8 @@ fun main() = application { val controller = Controller() + val activeTree = remember { mutableStateOf(false) } + if (!clickButtonsState[6].value) { Window( onCloseRequest = ::exitApplication, @@ -40,35 +41,35 @@ fun main() = application { state = windowState ) { - this.window.minimumSize = Dimension(800, 600) - Scaffold( - topBar = { - WindowDraggableArea { - myTopAppBar(clickButtonsState, windowState, controller) - } - }, - content = { - MaterialTheme( - colorScheme = lightColors, - typography = myTypography - ) { - - Box( - modifier = Modifier - .fillMaxSize() - .background(MaterialTheme.colorScheme.background) - .offset(0.dp, 50.dp) - - ) { - controlFields(controller) + this.window.minimumSize = Dimension(800, 600) + Scaffold( + topBar = { + WindowDraggableArea { + myTopAppBar(clickButtonsState, windowState, controller, activeTree) + } + }, + content = { + MaterialTheme( + colorScheme = lightColors, + typography = myTypography + ) { + + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + .offset(0.dp, 50.dp) + ) { + controlFields(controller, activeTree) - } } } - ) + + } + ) } @@ -96,7 +97,6 @@ val myTypography = Typography( ) - @OptIn(ExperimentalComposeUiApi::class) @Composable fun topAppIconButton( diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt index 83c97dd..e2597e3 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -11,39 +11,36 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.key.* import androidx.compose.ui.unit.dp import controller.Controller -import databaseManage.BINTreeManager -import viewPart.nodes.displayTree -import viewPart.nodes.drawableBIN.BINDrawableTree +import viewPart.nodes.drawableTree.DrawTree @Composable -fun controlFields(controller: Controller) { +fun controlFields(controller: Controller, activeTree: MutableState) { val addFieldState = remember { mutableStateOf(false) } val findFieldState = remember { mutableStateOf(false) } val deleteFieldState = remember { mutableStateOf(false) } - // скорее всего использовать by remember - val value = remember { mutableStateOf("") } Column(modifier = Modifier.offset(0.dp, 0.dp).padding(horizontal = 10.dp)) { // add - controlField("Add", addFieldState, value) + controlField("Add", addFieldState, value, activeTree) Spacer(modifier = Modifier.height(2.dp)) // find - controlField("Find", findFieldState, value) + controlField("Find", findFieldState, value, activeTree) Spacer(modifier = Modifier.height(2.dp)) // delete - controlField("Delete", deleteFieldState, value) - + controlField("Delete", deleteFieldState, value, activeTree) } - var tree by remember { mutableStateOf(BINDrawableTree("a", BINTreeManager())) } + var id by remember { mutableStateOf(0) } + + var tree by remember { mutableStateOf(null) } if (!addFieldState.value && !findFieldState.value && !deleteFieldState.value) { - displayTree(tree) + tree?.displayTree() addFieldState.value = false } @@ -66,11 +63,16 @@ fun controlFields(controller: Controller) { @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Composable -fun controlField(buttonName: String, buttonState: MutableState, value: MutableState) { +fun controlField( + buttonName: String, + buttonState: MutableState, + value: MutableState, + activeTree: MutableState +) { var text by remember { mutableStateOf("") } - var containerColor by remember { mutableStateOf(Color(237, 232, 232)) } + var containerColor by remember { mutableStateOf(Color(206, 211, 216)) } OutlinedTextField( value = text, @@ -84,6 +86,7 @@ fun controlField(buttonName: String, buttonState: MutableState, value: focusedLabelColor = Color(99, 95, 95), unfocusedLabelColor = MaterialTheme.colorScheme.onTertiary, ), + enabled = activeTree.value, modifier = Modifier .padding(start = 3.dp) .width(150.dp) @@ -91,7 +94,7 @@ fun controlField(buttonName: String, buttonState: MutableState, value: containerColor = if (it.isFocused) { Color(255, 255, 255) } else { - Color(237, 232, 232) + Color(206, 211, 216) } }.onPreviewKeyEvent { when { @@ -115,3 +118,4 @@ fun controlField(buttonName: String, buttonState: MutableState, value: ) } + diff --git a/lib/src/main/kotlin/ui/DeleteAlertDialog.kt b/lib/src/main/kotlin/ui/DeleteAlertDialog.kt index 7078d41..c117d42 100644 --- a/lib/src/main/kotlin/ui/DeleteAlertDialog.kt +++ b/lib/src/main/kotlin/ui/DeleteAlertDialog.kt @@ -1,4 +1,5 @@ package ui + import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -14,15 +15,17 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.unit.dp +import controller.Controller import topAppIconButton @OptIn(ExperimentalMaterialApi::class) @Composable fun deleteDialog( - treeName: String, buttonState: List>, - clickButtonsState: MutableState + clickButtonsState: MutableState, + controller: Controller, + activeTree: MutableState ) { val deleteDialogState = remember { mutableStateOf(true) } @@ -30,21 +33,33 @@ fun deleteDialog( if (clickButtonsState.value) { AlertDialog( backgroundColor = MaterialTheme.colorScheme.background, - onDismissRequest = { clickButtonsState.value = false}, - text = { Text(text = " Are you sure you want to delete $treeName ?", color = MaterialTheme.colorScheme.primary) }, + onDismissRequest = { clickButtonsState.value = false }, + text = { + Text( + text = deleteDialogText(activeTree, controller.tree?.name ?: ""), + color = MaterialTheme.colorScheme.primary + ) + }, buttons = { - Row(modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp), horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.Bottom) { - BottomButtons(deleteDialogState, clickButtonsState,"Cancel", "Delete") + Row( + modifier = Modifier.fillMaxWidth().padding(horizontal = 5.dp), + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.Bottom + ) { + BottomButtons(deleteDialogState, clickButtonsState, "Cancel", "Delete") } } ) } - if (!deleteDialogState.value) { - // отправляем запрос в контроллер на удаление + if (!deleteDialogState.value && activeTree.value) { deleteDialogState.value = true - clickButtonsState.value = false + clickButtonsState.value = false // ? + + controller.deleteTree() + + activeTree.value = false } topAppIconButton( @@ -58,3 +73,13 @@ fun deleteDialog( ) } + +@Composable +fun deleteDialogText(activeTree: MutableState, treeName: String): String { + return if (activeTree.value) { + "Are you sure you want to delete $treeName ?" + } else { + "Nothing to delete" + } + +} diff --git a/lib/src/main/kotlin/ui/FileDialog.kt b/lib/src/main/kotlin/ui/FileDialog.kt index 4479a27..779d6d9 100644 --- a/lib/src/main/kotlin/ui/FileDialog.kt +++ b/lib/src/main/kotlin/ui/FileDialog.kt @@ -64,7 +64,7 @@ fun searchItem( dialogState.value = false expandedOpenNested.value = false selectedTree.value = - if (enabledButtonIndex.value >= 0) files[enabledButtonIndex.value] else "null" // "" + if (enabledButtonIndex.value >= 0) files[enabledButtonIndex.value] else "" }, visible = dialogState.value, content = { WindowDraggableArea { @@ -84,7 +84,7 @@ fun searchItem( expandedNested.value = false expandedOpenNested.value = false selectedTree.value = - if (enabledButtonIndex.value >= 0) files[enabledButtonIndex.value] else "" // "" + if (enabledButtonIndex.value >= 0) files[enabledButtonIndex.value] else "" } diff --git a/lib/src/main/kotlin/ui/SaveDialog.kt b/lib/src/main/kotlin/ui/SaveDialog.kt index a27d89e..6831e45 100644 --- a/lib/src/main/kotlin/ui/SaveDialog.kt +++ b/lib/src/main/kotlin/ui/SaveDialog.kt @@ -1,4 +1,5 @@ package ui + import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.border import androidx.compose.foundation.layout.* @@ -20,6 +21,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.rememberDialogState +import controller.Controller import topAppIconButton @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @@ -27,7 +29,9 @@ import topAppIconButton fun saveDialog( buttonState: List>, clickButtonState: MutableState, - fileName: MutableState + fileName: MutableState, + controller: Controller, + activeTree: MutableState ) { var validateInputState by remember { mutableStateOf(true) } @@ -35,8 +39,6 @@ fun saveDialog( var warningDialogState by remember { mutableStateOf(false) } val saveButtonState = remember { mutableStateOf(false) } - - if (clickButtonState.value) { Dialog( onCloseRequest = { clickButtonState.value = false }, undecorated = true, @@ -71,6 +73,7 @@ fun saveDialog( verticalArrangement = Arrangement.Bottom ) { TextField( + enabled = activeTree.value, value = fileName.value, singleLine = true, shape = RoundedCornerShape(4.dp), @@ -87,6 +90,7 @@ fun saveDialog( } true } + else -> { warningDialogState = false successDialogState = false @@ -101,7 +105,12 @@ fun saveDialog( else Color(232, 169, 169) ), isError = !validateInputState, - placeholder = { Text(text = "Enter a filename", color = Color.Gray) }, + placeholder = { + Text( + text = if (activeTree.value) "Enter a filename" else "Nothing to save", + color = Color.Gray + ) + }, ) Text( text = when { @@ -114,7 +123,11 @@ fun saveDialog( fontSize = 12.sp ) - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.Bottom) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.Bottom + ) { BottomButtons( clickButtonState, saveButtonState, @@ -123,7 +136,9 @@ fun saveDialog( ) } - if (saveButtonState.value) { + if (saveButtonState.value && activeTree.value) { + + controller.saveTree(fileName.value) validateInputState = validate(fileName.value) if (validateInputState) { successDialogState = true diff --git a/lib/src/main/kotlin/ui/TopAppBar.kt b/lib/src/main/kotlin/ui/TopAppBar.kt index cc17d2c..7babc02 100644 --- a/lib/src/main/kotlin/ui/TopAppBar.kt +++ b/lib/src/main/kotlin/ui/TopAppBar.kt @@ -32,10 +32,14 @@ import myTypography import topAppIconButton @Composable -fun myTopAppBar(clickButtonsState: List>, windowState: WindowState, controller: Controller) { +fun myTopAppBar( + clickButtonsState: List>, + windowState: WindowState, + controller: Controller, + activeTree: MutableState +) { - //val controller = Controller() - val showFiles = controller.showFiles() + val showFiles = controller.showFiles() // показывает <=3 деревьев каждого вида в диалоге open val fileName = remember { mutableStateOf("") } // имя файла в диалоге save @@ -59,14 +63,14 @@ fun myTopAppBar(clickButtonsState: List>, windowState: Win Row { deleteDialog( - "A", buttonState, - clickButtonsState[0] - ) // будем иметь имя текущего дерево и передавать его сюды + clickButtonsState[0], + controller, + activeTree + ) - saveDialog(buttonState, clickButtonsState[1], fileName) - // после работы в контролере опять поменять filename на "" + saveDialog(buttonState, clickButtonsState[1], fileName, controller, activeTree) topAppIconButton( painterResource("/drawable/dir.png"), @@ -77,7 +81,7 @@ fun myTopAppBar(clickButtonsState: List>, windowState: Win MaterialTheme.colorScheme.background, clickButtonsState[2] ) - openMenu(clickButtonsState[2], showFiles) + openMenu(clickButtonsState[2], showFiles, controller, activeTree) topAppIconButton( @@ -89,7 +93,7 @@ fun myTopAppBar(clickButtonsState: List>, windowState: Win MaterialTheme.colorScheme.background, clickButtonsState[3] ) - createMenu(clickButtonsState[3], controller) + createMenu(clickButtonsState[3], controller, activeTree) } @@ -137,7 +141,6 @@ fun myTopAppBar(clickButtonsState: List>, windowState: Win } - } } @@ -145,7 +148,7 @@ fun myTopAppBar(clickButtonsState: List>, windowState: Win @Composable private fun minimizeWindow(state: WindowState) { - state.placement = WindowPlacement.Floating + state.isMinimized = !state.isMinimized } @Composable @@ -159,8 +162,10 @@ private fun maximizeWindow(state: WindowState) { @OptIn(ExperimentalComposeUiApi::class) @Composable -fun createMenu(expandedNested: MutableState, controller: Controller) { - val treesNames = listOf("Red black tree", "AVL tree", "Binary tree") +fun createMenu(expandedNested: MutableState, controller: Controller, activeTree: MutableState) { + val trees = listOf("Red black tree", "AVL tree", "Binary tree") + + val treesNames = listOf("rbTree", "avlTree", "binTree.json") val backgroundColorState = List(3) { remember { mutableStateOf(false) } } @@ -172,10 +177,10 @@ fun createMenu(expandedNested: MutableState, controller: Controller) { ) { repeat(3) { index -> - DropdownMenuItem(text = { Text(treesNames[index]) }, + DropdownMenuItem(text = { Text(trees[index]) }, onClick = { - // тут определяем тип дерева и передаем в контроллер - controller.createTree("baseName.json") + controller.createTree(treesNames[index], index) + activeTree.value = true expandedNested.value = false }, modifier = Modifier.onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } @@ -187,13 +192,20 @@ fun createMenu(expandedNested: MutableState, controller: Controller) { @OptIn(ExperimentalComposeUiApi::class) @Composable -fun openMenu(expandedNested: MutableState, showFiles: List>) { - val treesNames = listOf("Red black tree", "AVL tree", "Binary tree") +fun openMenu( + expandedNested: MutableState, + showFiles: List>, + controller: Controller, + activeTree: MutableState +) { + val trees = listOf("Red black tree", "AVL tree", "Binary tree") val expandedTreesNames = List(3) { remember { mutableStateOf(false) } } val backgroundColorState = List(3) { remember { mutableStateOf(false) } } + val selectedTree = remember { mutableStateOf("") } // дерево, которое выбрал юзер (очев) + DropdownMenu( expanded = expandedNested.value, onDismissRequest = { expandedNested.value = !expandedNested.value }, @@ -204,7 +216,7 @@ fun openMenu(expandedNested: MutableState, showFiles: List DropdownMenuItem( text = { Text( - treesNames[index], + trees[index], color = MaterialTheme.colorScheme.primary, modifier = Modifier.height(50.dp), maxLines = 1 @@ -218,7 +230,8 @@ fun openMenu(expandedNested: MutableState, showFiles: List .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } .background(color = menuItemBackgroundColor(backgroundColorState[index])), colors = MenuDefaults.itemColors(textColor = MaterialTheme.colorScheme.primary)) - treesNames(expandedTreesNames[index], expandedNested, showFiles[index], index, 150.dp) + + treesNames(expandedTreesNames[index], expandedNested, showFiles[index], index, 150.dp, selectedTree) } } @@ -231,14 +244,13 @@ fun treesNames( expandedOpenNested: MutableState, showFiles: List, treeID: Int, - offset: Dp + offset: Dp, + selectedTree: MutableState ) { val dirPath = System.getProperty("user.dir") + "/saved-trees" val dirFiles = listOf("$dirPath/RB-trees", "$dirPath/AVL-trees", "$dirPath/BIN-trees") - val selectedTree = remember { mutableStateOf("") } - val backgroundColorState = List(showFiles.size) { remember { mutableStateOf(false) } } AnimatedVisibility(visible = expandedNested.value) { diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index 6d91a53..a0491f6 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -16,17 +16,13 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import treelib.commonObjects.Container import viewPart.nodes.drawableAVL.AVLDrawableNode -import viewPart.nodes.drawableAVL.AVLDrawableTree -import viewPart.nodes.drawableAVL.AVLNodeDesign -import viewPart.nodes.drawableBIN.BINDrawableTree -import viewPart.nodes.drawableBIN.BINNodeDesign +import viewPart.nodes.drawableBIN.BINDrawableNode import viewPart.nodes.drawableRB.RBDrawableNode -import viewPart.nodes.drawableRB.RBDrawableTree -import viewPart.nodes.drawableRB.RBNodeDesign import viewPart.nodes.drawableTree.DrawableNode import viewPart.nodes.drawableTree.NodeDesign +import java.util.* import kotlin.math.roundToInt - +/* @Composable fun displayTree(tree: RBDrawableTree) { val root = tree.root @@ -50,20 +46,20 @@ fun displayTree(tree: BINDrawableTree) { displayNode(it, BINNodeDesign) } } - +*/ @Composable fun , DNode>, NodeD : NodeDesign> displayNode(node: DNode, design: NodeD) { if (node.clickState.value) { Box(modifier = Modifier - .height(60.dp).width(80.dp) + .height(if (node is BINDrawableNode<*>) 60.dp else 90.dp).width(100.dp) .offset { IntOffset( node.xState.value.roundToInt() + 71, node.yState.value.roundToInt() ) } - .background(color = Color(237, 232, 232)) + .background(color = Color(206, 211, 216)) .border(2.dp, MaterialTheme.colorScheme.primary, AbsoluteRoundedCornerShape(5.dp)) .padding(horizontal = 4.dp, vertical = 2.dp) .zIndex(1f) @@ -73,7 +69,7 @@ fun , DNode>, NodeD : NodeDesign> di Text(text = "value: ${node.value.value}") when (node ) { is AVLDrawableNode<*> -> Text(text = "height: ${node.height}") - is RBDrawableNode<*> -> Text(text = "color: ${node.color}") + is RBDrawableNode<*> -> Text(text = "color: ${node.color.toString().lowercase(Locale.getDefault())}") } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt index 979bd21..40b5866 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt @@ -11,6 +11,6 @@ class AVLDrawableNode( val height: Int, override val xState: MutableState, override val yState: MutableState, -) : DrawableNode>() { +) : DrawableNode>(AVLNodeDesign, AVLNodeDesign.colorNode) { override val clickState = mutableStateOf(false) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt index d1ebac0..640d765 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt @@ -2,21 +2,19 @@ package viewPart.nodes.drawableAVL import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp import viewPart.nodes.drawableTree.NodeDesign object AVLNodeDesign : NodeDesign { - override var lineColor = Color.Black - override var nodeSize = 100f + override var colorNode = Color(208, 223, 252) + override var lineColor = Color(34, 35, 41) + override var nodeSize = 60f override var shape: Shape = CircleShape - override var lineStrokeWidth = 15f + override var lineStrokeWidth = 10f @Composable override fun infoView(information: String, modifier: Modifier) { @@ -24,12 +22,12 @@ object AVLNodeDesign : NodeDesign { modifier = modifier, contentAlignment = Alignment.Center ) { - Text( - text = information, - fontSize = 30.sp, - color = Color.White, - fontWeight = FontWeight.Bold - ) + //Text( + // text = information, + // fontSize = 30.sp, + // color = Color.White, + // fontWeight = FontWeight.Bold + //) } } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt index 7760293..43b3a6b 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt @@ -11,7 +11,7 @@ class BINDrawableNode( override var xState: MutableState, override var yState: MutableState, - ) : DrawableNode>() { + ) : DrawableNode>(BINNodeDesign, BINNodeDesign.colorNode) { override val clickState = mutableStateOf(false) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt index 8f4eda6..c4b21ad 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt @@ -10,7 +10,7 @@ import androidx.compose.ui.graphics.Shape import viewPart.nodes.drawableTree.NodeDesign object BINNodeDesign: NodeDesign { - var colorNode = Color(208, 223, 252) + override var colorNode = Color(208, 223, 252) override var lineColor = Color(34, 35, 41) override var nodeSize = 60f override var shape: Shape = CircleShape diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt index e3d4e50..eef520b 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt @@ -12,6 +12,6 @@ class RBDrawableNode( val color: Markers, override val xState: MutableState, override val yState: MutableState, -) : DrawableNode>() { +) : DrawableNode>(RBNodeDesign, if (color == Markers.BLACK) RBNodeDesign.blackMarker else RBNodeDesign.redMarker) { override val clickState = mutableStateOf(false) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt index d3b5672..774f073 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt @@ -2,23 +2,21 @@ package viewPart.nodes.drawableRB import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp import viewPart.nodes.drawableTree.NodeDesign object RBNodeDesign: NodeDesign { - val redMarker = Color.Red - val blackMarker = Color.Black - override var nodeSize = 100f + val redMarker = Color(233, 8, 28) + val blackMarker = Color(51, 51, 51) + override var colorNode: Color = Color(34, 35, 41) + override var nodeSize = 60f override var shape: Shape = CircleShape - override var lineStrokeWidth = 15f - override var lineColor = Color.Black + override var lineStrokeWidth = 10f + override var lineColor = Color(34, 35, 41) @Composable override fun infoView(information: String, modifier: Modifier) { @@ -26,12 +24,12 @@ object RBNodeDesign: NodeDesign { modifier = modifier, contentAlignment = Alignment.Center ) { - Text( - text = information, - fontSize = 30.sp, - color = Color.White, - fontWeight = FontWeight.Bold - ) + //Text( + // text = information, + // fontSize = 30.sp, + // color = Color.White, + // fontWeight = FontWeight.Bold + //) } } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt index 115693e..c7976ef 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt @@ -1,19 +1,21 @@ package viewPart.nodes.drawableTree +import androidx.compose.runtime.Composable import treelib.commonObjects.Container -interface DrawTree, DNodeType>> { +interface DrawTree { val name: String - var root: DNodeType? val designNode: NodeDesign var yShiftBetweenNodes: Float + @Composable + fun displayTree() fun initTree() fun updateTree() fun deleteTree() fun saveTree() fun insert(item: Container) fun delete(item: Container) - fun find(item: Container): Container? + fun find(item: Int) fun repositisonTree(xBase: Float, yBase: Float) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt index d748a54..c2ac7c4 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt @@ -9,13 +9,13 @@ import androidx.compose.foundation.onClick import androidx.compose.runtime.MutableState import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import viewPart.nodes.drawableBIN.BINNodeDesign import kotlin.math.roundToInt -abstract class DrawableNode> { +abstract class DrawableNode>(design : NodeDesign, color : Color) { abstract val value: Pack abstract var leftChild: NodeType? abstract var rightChild: NodeType? @@ -36,9 +36,9 @@ abstract class DrawableNode> { yState.value += dragAmount.y } } - .size(BINNodeDesign.nodeSize.dp) - .clip(BINNodeDesign.shape) - .background(BINNodeDesign.colorNode) + .size(design.nodeSize.dp) + .clip(design.shape) + .background(color) .onClick { clickState.value = !clickState.value } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index f3d1042..a84fe29 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -1,5 +1,6 @@ package viewPart.nodes.drawableTree +import androidx.compose.runtime.Composable import databaseManage.TreeManager import databaseSave.DrawableVertex import treelib.abstractTree.Node @@ -8,6 +9,7 @@ import treelib.abstractTree.TreeStruct import treelib.abstractTree.Vertex import treelib.commonObjects.Container import treelib.commonObjects.exceptions.ImpossibleCaseException +import viewPart.nodes.displayNode abstract class DrawableTree< DNodeType : DrawableNode, DNodeType>, @@ -16,14 +18,22 @@ abstract class DrawableTree< State : StateContainer, NodeType>, VertexType : Vertex>, StructType : TreeStruct, NodeType, State, VertexType> - >: DrawTree { + >: DrawTree { protected abstract var drawablePreOrder: List? protected abstract val treeManager: TreeManager, DVertexType, NodeType, State, VertexType, StructType> protected abstract val treeStruct: StructType + protected abstract var root: DNodeType? override var yShiftBetweenNodes = 10f + @Composable + override fun displayTree() { + root?.let { + displayNode(it, designNode) + } + } + override fun initTree() { val binVertexes = treeManager.initTree(name, treeStruct) drawablePreOrder = binVertexes.map { drawableVertexToNode(it) } @@ -59,7 +69,23 @@ abstract class DrawableTree< if (treeStruct.find(item) != null) treeStruct.delete(item) } - override fun find(item: Container): Container? = treeStruct.find(item) + override fun find(item: Int) { + var currentNode = root + if (root == null) + return + while(true) { + if (item == currentNode?.value?.key) { + currentNode.clickState.value = true + return + } + currentNode?.let { + currentNode = if (item > it.value.key) it.rightChild + else it.leftChild + } + if (currentNode == null) return + } + + } override fun repositisonTree(xBase: Float, yBase: Float) { /*xBase: Float = 0f, yBase: Float = 0f*/ diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt index 973d6b3..9ae741d 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt @@ -6,6 +6,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape interface NodeDesign { + var colorNode: Color var nodeSize: Float var shape: Shape var lineStrokeWidth: Float From 48f4696df8822b18551e26fde7139a0a5627f81c Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 1 May 2023 14:19:23 +0300 Subject: [PATCH 181/212] fix: Remove generics from DrawTree interface (#24). --- lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt | 3 +-- .../main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt index d2ac753..56e7eec 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt @@ -3,7 +3,7 @@ package viewPart.nodes.drawableTree import androidx.compose.runtime.Composable import treelib.commonObjects.Container -interface DrawTree, DNodeType>> { +interface DrawTree { /**How you should it use: * * @@ -26,7 +26,6 @@ interface DrawTree, DNodeType>> * **/ var name: String - var root: DNodeType? val designNode: NodeDesign var yShiftBetweenNodes: Float diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index ed9a48a..7b26fe8 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -10,7 +10,6 @@ import treelib.abstractTree.Vertex import treelib.commonObjects.Container import treelib.commonObjects.exceptions.ImpossibleCaseException import viewPart.nodes.displayNode -import viewPart.nodes.drawableBIN.BINNodeDesign abstract class DrawableTree< DNodeType : DrawableNode, DNodeType>, @@ -19,11 +18,12 @@ abstract class DrawableTree< State : StateContainer, NodeType>, VertexType : Vertex>, StructType : TreeStruct, NodeType, State, VertexType> - > : DrawTree { + > : DrawTree { protected abstract var drawablePreOrder: List? protected abstract val treeManager: TreeManager, DVertexType, NodeType, State, VertexType, StructType> protected abstract var treeStruct: StructType + protected abstract var root: DNodeType? override var yShiftBetweenNodes = 10f From 01f33dad9f32823ae457288a07fced9c8c1db4d6 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 1 May 2023 14:27:45 +0300 Subject: [PATCH 182/212] fix: Fix the display of the node data element --- lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt | 2 +- .../kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt | 7 +------ .../kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt | 6 ------ .../main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt | 6 ------ 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index a0491f6..e990cda 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -59,7 +59,7 @@ fun , DNode>, NodeD : NodeDesign> di node.yState.value.roundToInt() ) } - .background(color = Color(206, 211, 216)) + .background(color = Color(206, 211, 216), shape = AbsoluteRoundedCornerShape(5.dp)) .border(2.dp, MaterialTheme.colorScheme.primary, AbsoluteRoundedCornerShape(5.dp)) .padding(horizontal = 4.dp, vertical = 2.dp) .zIndex(1f) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt index 640d765..6b8acd2 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt @@ -22,12 +22,7 @@ object AVLNodeDesign : NodeDesign { modifier = modifier, contentAlignment = Alignment.Center ) { - //Text( - // text = information, - // fontSize = 30.sp, - // color = Color.White, - // fontWeight = FontWeight.Bold - //) + } } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt index c4b21ad..2248abd 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt @@ -21,12 +21,6 @@ object BINNodeDesign: NodeDesign { modifier = modifier, contentAlignment = Alignment.Center ) { - //Text( - // text = information, - // fontSize = 30.sp, - // color = Color.White, - // fontWeight = FontWeight.Bold - //) } } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt index 774f073..c5b7ed7 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt @@ -24,12 +24,6 @@ object RBNodeDesign: NodeDesign { modifier = modifier, contentAlignment = Alignment.Center ) { - //Text( - // text = information, - // fontSize = 30.sp, - // color = Color.White, - // fontWeight = FontWeight.Bold - //) } } } From edf59674a76c3f51b963e7f7a59b8b9aacfd9ced Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 1 May 2023 18:32:40 +0300 Subject: [PATCH 183/212] fix: Fix interacting with neo4j (find) --- lib/src/main/kotlin/controller/Controller.kt | 19 ++++--- .../kotlin/databaseManage/BINTreeManager.kt | 13 ++--- .../kotlin/databaseManage/RBTreeManager.kt | 28 +-------- .../databaseSave/neo4j/Neo4jRepository.kt | 12 +++- lib/src/main/kotlin/main.kt | 20 +++++-- lib/src/main/kotlin/ui/ControlFields.kt | 17 ++++-- lib/src/main/kotlin/ui/DeleteAlertDialog.kt | 8 +-- lib/src/main/kotlin/ui/TopAppBar.kt | 45 ++++++++++++--- .../kotlin/viewPart/nodes/TreeDrawingUtils.kt | 53 +++++++++++++++--- .../nodes/drawableTree/DrawableTree.kt | 2 - lib/src/main/resources/appIcon.png | Bin 0 -> 761803 bytes 11 files changed, 140 insertions(+), 77 deletions(-) create mode 100644 lib/src/main/resources/appIcon.png diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index e33ee57..beb2c99 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -59,13 +59,15 @@ class Controller { } - fun createTree(treeName: String, id: Int) { + fun createTree(treeName: String, id: Int): DrawTree { when (id) { 0 -> tree = RBDrawableTree(treeName, rbManager) 1 -> tree = AVLDrawableTree(treeName, avlManager) 2 -> tree = BINDrawableTree(treeName, binManager) } + tree?.initTree() + return tree ?: throw NullPointerException() } fun insert(value: String): DrawTree { @@ -107,17 +109,20 @@ class Controller { } } - fun deleteTree() { - //tree.deleteTree() - tree = null - tree?.updateTree() + fun deleteTree(): DrawTree { + tree?.deleteTree() + return tree ?: throw Exception("Tree not initialized") } fun saveTree(fileName: String) { - //tree.name = fileName - tree?.saveTree() ?: throw Exception("Tree not initialized") + if (tree == null) + throw Exception("Tree not initialized") + + tree?.name = fileName + tree?.saveTreeToDB() ?: throw Exception() } + } diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index 01c16a7..da152c3 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -3,7 +3,6 @@ package databaseManage import com.google.gson.reflect.TypeToken import databaseSave.jsonFormat.DrawableBINVertex import databaseSave.jsonFormat.JsonRepository -import treelib.avlTree.AVLStruct import treelib.binTree.BINNode import treelib.binTree.BINStateContainer import treelib.binTree.BINStruct @@ -24,7 +23,7 @@ class BINTreeManager : TreeManager< private val dirPath = System.getProperty("user.dir") + "/saved-trees/BIN-trees" - private val jsonRep = JsonRepository(BIN_DB_DEFAULT_NAME) + private val jsonRep = JsonRepository(dirPath) override fun initTree( name: String, @@ -85,13 +84,6 @@ class BINTreeManager : TreeManager< return File(dirPath, treeName).exists() } - /* override fun delete(item: Container) { - if (binTree.find(item) != null) - binTree.delete(item) - } - - override fun insert(item: Container) = binTree.insert(item)*/ - override fun getVertexesForDrawFromDB(name: String): List>> { TODO("Not yet implemented") } @@ -101,7 +93,10 @@ class BINTreeManager : TreeManager< fun cleanDB() = jsonRep.clean() + /* + companion object { const val BIN_DB_DEFAULT_NAME = "binDB" } + */ } diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt index f898817..e24edd2 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/RBTreeManager.kt @@ -29,7 +29,7 @@ class RBTreeManager : TreeManager< val orders: Pair>>, List>>> = neo4jDB.exportRBtree(name) tree.restoreStruct(orders.first, orders.second) - neo4jDB.close() + //neo4jDB.close() return orders.first } @@ -42,7 +42,7 @@ class RBTreeManager : TreeManager< inOrder: List>> ) { neo4jDB.saveChanges(preOrder.toTypedArray(), inOrder.toTypedArray(), name) - neo4jDB.close() + //neo4jDB.close() } override fun saveTreeToDB(name: String, tree: RBStruct>) { @@ -53,33 +53,11 @@ class RBTreeManager : TreeManager< override fun deleteTreeFromDB(name: String) { neo4jDB.removeTree(name) - neo4jDB.close() + //neo4jDB.close() } override fun getSavedTreesNames(): List = neo4jDB.findNamesTrees() - //{ - // val treesNames = neo4jDB.findNamesTrees() - // val dirPath = System.getProperty("user.dir") + "/saved-trees/RB-trees" - // File(dirPath).mkdirs() - // if (treesNames.isNotEmpty()) { - // for (name in treesNames) { - // File(dirPath, name).run { - // createNewFile() - // } - // } - // } - // return treesNames.subList(0, treesNames.size) - //} -/* - - override fun delete(item: Container) = rbTree.insert(item) - - override fun insert(item: Container) { - if (rbTree.find(item) != null) - rbTree.delete(item) - } -*/ override fun getVertexesForDrawFromDB(name: String): List>> { return neo4jDB.exportRBtree(name).first.map { DrawableRBVertex(it.value, it.color) } diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index 8920e55..825950f 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -242,12 +242,18 @@ class Neo4jRepository : Closeable { fun findTree(treeName: String): Boolean { val session = driver?.session() ?: throw IOException() - var name = "NULL" + var name = "" session.executeRead { tx -> - name = tx.run("MATCH (n: Node {treeName: \$treeName}) WHERE NOT (:Node)-->(n) RETURN n.treeName").list()[0].values()[0].toString().replace("\"", "") + name = tx.run( + "MATCH (n: Node {treeName: \$treeName}) WHERE NOT (:Node)-->(n) RETURN n.treeName", + mutableMapOf( + "treeName" to treeName + ) as Map? + ).list().singleOrNull()?.values()?.get(0)?.toString()?.replace("\"", "") ?: "" + } - return name == treeName + return name == treeName } diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index 8d83e99..71041ed 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -1,3 +1,4 @@ + import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.window.WindowDraggableArea @@ -14,6 +15,7 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.input.key.* import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight @@ -34,18 +36,23 @@ fun main() = application { val activeTree = remember { mutableStateOf(false) } + val deleteTreeState = remember { mutableStateOf(false) } + + val openTreeState = remember { mutableStateOf(false) } + if (!clickButtonsState[6].value) { Window( onCloseRequest = ::exitApplication, undecorated = true, - state = windowState + state = windowState, + icon = appIcon() ) { this.window.minimumSize = Dimension(800, 600) Scaffold( topBar = { WindowDraggableArea { - myTopAppBar(clickButtonsState, windowState, controller, activeTree) + myTopAppBar(clickButtonsState, windowState, controller, activeTree, deleteTreeState, openTreeState) } }, content = { @@ -61,9 +68,7 @@ fun main() = application { .offset(0.dp, 50.dp) ) { - controlFields(controller, activeTree) - - + controlFields(controller, activeTree, deleteTreeState, openTreeState) } } @@ -141,3 +146,8 @@ fun arrowIcon() = Icon( fun menuItemBackgroundColor(state: MutableState): Color { return if (state.value) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.background } + +@Composable +fun appIcon(): Painter { + return painterResource("appIcon.png") +} diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt index e2597e3..2ff52c1 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -14,7 +14,12 @@ import controller.Controller import viewPart.nodes.drawableTree.DrawTree @Composable -fun controlFields(controller: Controller, activeTree: MutableState) { +fun controlFields( + controller: Controller, + activeTree: MutableState, + deleteTreeState: MutableState, + openTreeState: MutableState +) { val addFieldState = remember { mutableStateOf(false) } val findFieldState = remember { mutableStateOf(false) } @@ -35,16 +40,20 @@ fun controlFields(controller: Controller, activeTree: MutableState) { controlField("Delete", deleteFieldState, value, activeTree) } - var id by remember { mutableStateOf(0) } - var tree by remember { mutableStateOf(null) } if (!addFieldState.value && !findFieldState.value && !deleteFieldState.value) { + if (deleteTreeState.value) { + tree = controller.deleteTree() + deleteTreeState.value = false + } + if (openTreeState.value) { + tree = controller.tree // ?? + } tree?.displayTree() addFieldState.value = false } - // тут мы наверное можем быть уверены, что тип дерева не поменялся if (addFieldState.value) { tree = controller.insert(value.value) addFieldState.value = false diff --git a/lib/src/main/kotlin/ui/DeleteAlertDialog.kt b/lib/src/main/kotlin/ui/DeleteAlertDialog.kt index c117d42..c981951 100644 --- a/lib/src/main/kotlin/ui/DeleteAlertDialog.kt +++ b/lib/src/main/kotlin/ui/DeleteAlertDialog.kt @@ -25,7 +25,8 @@ fun deleteDialog( buttonState: List>, clickButtonsState: MutableState, controller: Controller, - activeTree: MutableState + activeTree: MutableState, + deleteTreeState: MutableState ) { val deleteDialogState = remember { mutableStateOf(true) } @@ -54,11 +55,8 @@ fun deleteDialog( } if (!deleteDialogState.value && activeTree.value) { + deleteTreeState.value = true deleteDialogState.value = true - clickButtonsState.value = false // ? - - controller.deleteTree() - activeTree.value = false } diff --git a/lib/src/main/kotlin/ui/TopAppBar.kt b/lib/src/main/kotlin/ui/TopAppBar.kt index 7babc02..51c3572 100644 --- a/lib/src/main/kotlin/ui/TopAppBar.kt +++ b/lib/src/main/kotlin/ui/TopAppBar.kt @@ -36,7 +36,9 @@ fun myTopAppBar( clickButtonsState: List>, windowState: WindowState, controller: Controller, - activeTree: MutableState + activeTree: MutableState, + deleteTreeState: MutableState, + openTreeState: MutableState ) { val showFiles = controller.showFiles() // показывает <=3 деревьев каждого вида в диалоге open @@ -66,7 +68,8 @@ fun myTopAppBar( buttonState, clickButtonsState[0], controller, - activeTree + activeTree, + deleteTreeState ) @@ -81,7 +84,7 @@ fun myTopAppBar( MaterialTheme.colorScheme.background, clickButtonsState[2] ) - openMenu(clickButtonsState[2], showFiles, controller, activeTree) + openMenu(clickButtonsState[2], showFiles, activeTree, openTreeState, controller) topAppIconButton( @@ -195,8 +198,9 @@ fun createMenu(expandedNested: MutableState, controller: Controller, ac fun openMenu( expandedNested: MutableState, showFiles: List>, - controller: Controller, - activeTree: MutableState + activeTree: MutableState, + openTreeState: MutableState, + controller: Controller ) { val trees = listOf("Red black tree", "AVL tree", "Binary tree") @@ -231,10 +235,21 @@ fun openMenu( .background(color = menuItemBackgroundColor(backgroundColorState[index])), colors = MenuDefaults.itemColors(textColor = MaterialTheme.colorScheme.primary)) - treesNames(expandedTreesNames[index], expandedNested, showFiles[index], index, 150.dp, selectedTree) + treesNames( + expandedTreesNames[index], + expandedNested, + showFiles[index], + index, + 150.dp, + selectedTree, + activeTree, + openTreeState, + controller + ) } } + } @OptIn(ExperimentalComposeUiApi::class) @@ -245,7 +260,10 @@ fun treesNames( showFiles: List, treeID: Int, offset: Dp, - selectedTree: MutableState + selectedTree: MutableState, + activeTree: MutableState, + openTreeState: MutableState, + controller: Controller ) { val dirPath = System.getProperty("user.dir") + "/saved-trees" @@ -263,7 +281,9 @@ fun treesNames( searchItem(dirFiles[treeID], expandedNested, selectedTree, expandedOpenNested) repeat(showFiles.size) { index -> - DropdownMenuItem(onClick = { /*expandedNested.value = false*/ }, + DropdownMenuItem(onClick = { + selectedTree.value = showFiles[index] + }, text = { Text(showFiles[index]) }, modifier = Modifier .onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } @@ -271,8 +291,15 @@ fun treesNames( .background(color = menuItemBackgroundColor(backgroundColorState[index])), colors = MenuDefaults.itemColors(textColor = MaterialTheme.colorScheme.primary)) } - } } + if (selectedTree.value.isNotEmpty()) { + controller.createTree(selectedTree.value, treeID) + openTreeState.value = true + activeTree.value = true + expandedNested.value = false + selectedTree.value = "" + } + } \ No newline at end of file diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index 5a4590e..f48cc57 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -1,19 +1,28 @@ package viewPart.nodes import androidx.compose.foundation.Canvas -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex -import viewPart.nodes.drawableAVL.AVLDrawableTree -import viewPart.nodes.drawableAVL.AVLNodeDesign -import viewPart.nodes.drawableBIN.BINDrawableTree -import viewPart.nodes.drawableBIN.BINNodeDesign -import viewPart.nodes.drawableRB.RBDrawableTree -import viewPart.nodes.drawableRB.RBNodeDesign +import treelib.commonObjects.Container +import viewPart.nodes.drawableAVL.AVLDrawableNode +import viewPart.nodes.drawableBIN.BINDrawableNode +import viewPart.nodes.drawableRB.RBDrawableNode import viewPart.nodes.drawableTree.DrawableNode import viewPart.nodes.drawableTree.NodeDesign +import java.util.* +import kotlin.math.roundToInt + /* @Composable fun displayTree(tree: RBDrawableTree) { @@ -40,7 +49,35 @@ fun displayTree(tree: BINDrawableTree) { } */ @Composable -fun , NodeD : NodeDesign> displayNode(node: DNode, design: NodeD) { +fun , DNode>, NodeD : NodeDesign> displayNode(node: DNode, design: NodeD) { + + if (node.clickState.value) { + Box(modifier = Modifier + .height(if (node is BINDrawableNode<*>) 60.dp else 90.dp).width(100.dp) + .offset { + IntOffset( + node.xState.value.roundToInt() + 71, + node.yState.value.roundToInt() + ) + } + .background(color = Color(206, 211, 216), shape = AbsoluteRoundedCornerShape(5.dp)) + .border(2.dp, MaterialTheme.colorScheme.primary, AbsoluteRoundedCornerShape(5.dp)) + .padding(horizontal = 4.dp, vertical = 2.dp) + .zIndex(1f) + ) { + Column(modifier = Modifier.zIndex(1f)) { + Text(text = "key: ${node.value.key}") + Text(text = "value: ${node.value.value}") + when (node ) { + is AVLDrawableNode<*> -> Text(text = "height: ${node.height}") + is RBDrawableNode<*> -> Text(text = "color: ${node.color.toString().lowercase(Locale.getDefault())}") + } + + } + + } + } + design.infoView(node.value.toString(), node.modifier) node.leftChild?.let { diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index 0bff57b..9cd5645 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -53,8 +53,6 @@ abstract class DrawableTree< } } - override fun deleteTree() = treeManager.deleteTreeFromDB(name) - override fun deleteTreeFromBD() = treeManager.deleteTreeFromDB(name) override fun saveTreeToDB() { diff --git a/lib/src/main/resources/appIcon.png b/lib/src/main/resources/appIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..452a71c3df1a1f22db02c2ce742cbcdffd561064 GIT binary patch literal 761803 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

L~luQE;x=v%g)xFYj|Z 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

i!B-GTA78o5_i-wja0p`9~ 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⁢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�Nv-LT9jL~#op?e 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%0D`$zQqjMttQv-;8hl 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 literal 0 HcmV?d00001 From 0f8f24f31a36468234a52268baee876ea20f50de Mon Sep 17 00:00:00 2001 From: Georgy S Date: Mon, 1 May 2023 18:33:33 +0300 Subject: [PATCH 184/212] fix: Bugs in saving trees in databases. --- .../kotlin/databaseManage/BINTreeManager.kt | 3 +- .../databaseSave/neo4j/Neo4jRepository.kt | 4 ++- .../nodes/drawableAVL/AVLDrawableTree.kt | 4 +-- .../nodes/drawableBIN/BINDrawableTree.kt | 12 +++++-- .../nodes/drawableRB/RBDrawableTree.kt | 14 ++++++-- .../viewPart/nodes/drawableTree/DrawTree.kt | 2 ++ .../nodes/drawableTree/DrawableTree.kt | 36 +++++++++++++++++-- 7 files changed, 63 insertions(+), 12 deletions(-) diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index 01c16a7..a86cbd4 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -22,7 +22,8 @@ class BINTreeManager : TreeManager< /*** using json format files ***/ - private val dirPath = System.getProperty("user.dir") + "/saved-trees/BIN-trees" +// private val dirPath = System.getProperty("user.dir") + "/saved-trees/BIN-trees" + private val dirPath = BIN_DB_DEFAULT_NAME private val jsonRep = JsonRepository(BIN_DB_DEFAULT_NAME) diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index 4022b62..31b22dd 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -245,7 +245,9 @@ class Neo4jRepository : Closeable { var name = "NULL" session.executeRead { tx -> - name = tx.run("MATCH (n: Node {treeName: \$treeName}) WHERE NOT (:Node)-->(n) RETURN n.treeName").list()[0].values()[0].toString().replace("\"", "") + name = tx.run("MATCH (n: Node {treeName: \$treeName}) WHERE NOT (:Node)-->(n) RETURN n.treeName", + mutableMapOf("treeName" to treeName) as Map? + ).list().singleOrNull()?.values()?.get(0)?.toString()?.replace("\"", "") ?: "" } return name == treeName diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt index 8fa5cb2..345e4f1 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt @@ -26,8 +26,8 @@ class AVLDrawableTree( override fun drawableVertexToNode(vertex: DrawableAVLVertex>) = AVLDrawableNode( value = vertex.value, - xState = mutableStateOf(0f), - yState = mutableStateOf(0f), + xState = mutableStateOf(vertex.x.toFloat()), + yState = mutableStateOf(vertex.y.toFloat()), height = vertex.height.toInt() ) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt index c3e03ae..c5c9f06 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt @@ -33,10 +33,18 @@ class BINDrawableTree( treeStruct = BINStruct() } + override fun saveTreeToDB() { + if (root != null) { + treeManager.saveTreeToDB(name, preOrder().map { nodeToDrawableVertex(it) }.toList(), listOf()) + } else { + treeManager.saveTreeToDB(name, treeStruct) + } + } + override fun drawableVertexToNode(vertex: DrawableBINVertex>) = BINDrawableNode( value = vertex.value, - xState = mutableStateOf(0f), - yState = mutableStateOf(0f), + xState = mutableStateOf(vertex.x.toFloat()), + yState = mutableStateOf(vertex.y.toFloat()), ) override fun vertexToNode(vertex: BINVertex>) = BINDrawableNode( diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt index 1a0cf3c..a0e842c 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt @@ -21,7 +21,7 @@ class RBDrawableTree( RBVertex>, RBStruct> >() { - override var root: RBDrawableNode>? = null + override var root: RBDrawableNode>? = null override var drawablePreOrder: List>>? = null override var treeStruct = RBStruct>() override val designNode = RBNodeDesign @@ -31,10 +31,18 @@ class RBDrawableTree( treeStruct = RBStruct() } + override fun saveTreeToDB() { + if (root != null) { + treeManager.saveTreeToDB(name, preOrder().map { nodeToDrawableVertex(it) }.toList(), inOrder().map { nodeToDrawableVertex(it) }.toList()) + } else { + treeManager.saveTreeToDB(name, treeStruct) + } + } + override fun drawableVertexToNode(vertex: DrawableRBVertex>) = RBDrawableNode( value = vertex.value, - xState = mutableStateOf(0f), - yState = mutableStateOf(0f), + xState = mutableStateOf(vertex.x.toFloat()), + yState = mutableStateOf(vertex.y.toFloat()), color = vertex.color, ) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt index 56e7eec..6f84a96 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt @@ -17,6 +17,8 @@ interface DrawTree { * * tree.update <- (means moving information: TreeStruct.root -> DrawableTree.root) * * tree.repositisonTree <- (set "beautiful" coordinates without display) * + * After tree.initTree() you should not write tree.updateTree() or tree.repositisonTree(...), otherwise coordinates from db would be missed + * * } * * IN A COMPOSABLE CONTEXT { diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index 7b26fe8..223cb4c 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -53,8 +53,6 @@ abstract class DrawableTree< } } - override fun deleteTreeFromBD() = treeManager.deleteTreeFromDB(name) - override fun saveTreeToDB() { if (root != null) { treeManager.saveTreeToDB(name, preOrder().map { nodeToDrawableVertex(it) }.toList(), listOf()) @@ -63,6 +61,8 @@ abstract class DrawableTree< } } + override fun deleteTreeFromBD() = treeManager.deleteTreeFromDB(name) + override fun insert(item: Container) = treeStruct.insert(item) override fun delete(item: Container) { @@ -158,7 +158,7 @@ abstract class DrawableTree< return n } - private fun preOrder() = sequence { + protected fun preOrder() = sequence { var current: DNodeType val queue = ArrayDeque() @@ -180,6 +180,36 @@ abstract class DrawableTree< } } + protected fun inOrder() = sequence { + var flagVisited = 0 + var current = root + val parents = ArrayDeque() + + while (current != null) { + if (flagVisited == 0) { + while (true) { + current?.let { + if (it.leftChild == null) return@let null + parents.add(it) + current = it.leftChild + return@let current + } ?: break + } + } + current?.let { + yield(it) + if (it.rightChild != null) { + flagVisited = 0 + current = it.rightChild + } else { + if (parents.isEmpty()) return@sequence + flagVisited = 1 + current = parents.removeLast() + } + } + } + } + private fun vertexesToNodes(preOrder: List) = sequence { for (el in preOrder) { yield( From c09deccdbe18402b6b92773156c26aa0b0181a66 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 1 May 2023 20:00:07 +0300 Subject: [PATCH 185/212] feat: Implement saving trees to the database using ui --- lib/src/main/kotlin/controller/Controller.kt | 1 + .../main/kotlin/databaseManage/BINTreeManager.kt | 4 ++-- lib/src/main/kotlin/databaseManage/TreeManager.kt | 3 --- .../kotlin/databaseSave/neo4j/Neo4jRepository.kt | 4 ++-- lib/src/main/kotlin/ui/ControlFields.kt | 3 ++- lib/src/main/kotlin/ui/SaveDialog.kt | 6 ++++-- lib/src/main/kotlin/ui/TopAppBar.kt | 13 ++++++++----- 7 files changed, 19 insertions(+), 15 deletions(-) diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index beb2c99..5fd3c23 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -65,6 +65,7 @@ class Controller { 1 -> tree = AVLDrawableTree(treeName, avlManager) 2 -> tree = BINDrawableTree(treeName, binManager) } + tree?.initTree() return tree ?: throw NullPointerException() diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index a091eb9..69578d8 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -21,8 +21,8 @@ class BINTreeManager : TreeManager< /*** using json format files ***/ -// private val dirPath = System.getProperty("user.dir") + "/saved-trees/BIN-trees" - private val dirPath = BIN_DB_DEFAULT_NAME + private val dirPath = System.getProperty("user.dir") + "/saved-trees/BIN-trees" + //private val dirPath = BIN_DB_DEFAULT_NAME private val jsonRep = JsonRepository(dirPath) diff --git a/lib/src/main/kotlin/databaseManage/TreeManager.kt b/lib/src/main/kotlin/databaseManage/TreeManager.kt index 982aada..b5bb61d 100644 --- a/lib/src/main/kotlin/databaseManage/TreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/TreeManager.kt @@ -30,7 +30,4 @@ interface TreeManager< fun getSavedTreesNames(): List -// fun insert(item: Container) -// -// fun delete(item: Container) } diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index 825950f..13161a9 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -102,8 +102,8 @@ class Neo4jRepository : Closeable { ).list() .map { DrawableRBVertex( - value = Container(Pair(it.values()[1].toString().toInt(), it.values()[0].toString())), - color = if (it.values()[2].toString() == """RED""") Markers.RED else Markers.BLACK, + value = Container(Pair(it.values()[1].toString().toInt(), it.values()[0].toString().replace("\"", ""))), + color = if (it.values()[2].toString().replace("\"", "") == "RED") Markers.RED else Markers.BLACK, x = it.values()[3].toString().toDouble(), y = it.values()[4].toString().toDouble() ) diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt index 2ff52c1..ca5258f 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -48,9 +48,10 @@ fun controlFields( deleteTreeState.value = false } if (openTreeState.value) { - tree = controller.tree // ?? + tree = controller.tree } tree?.displayTree() + addFieldState.value = false } diff --git a/lib/src/main/kotlin/ui/SaveDialog.kt b/lib/src/main/kotlin/ui/SaveDialog.kt index 6831e45..f8dec7c 100644 --- a/lib/src/main/kotlin/ui/SaveDialog.kt +++ b/lib/src/main/kotlin/ui/SaveDialog.kt @@ -137,8 +137,6 @@ fun saveDialog( } if (saveButtonState.value && activeTree.value) { - - controller.saveTree(fileName.value) validateInputState = validate(fileName.value) if (validateInputState) { successDialogState = true @@ -147,6 +145,10 @@ fun saveDialog( } } + if (successDialogState) { + controller.saveTree(fileName.value) + } + } } diff --git a/lib/src/main/kotlin/ui/TopAppBar.kt b/lib/src/main/kotlin/ui/TopAppBar.kt index 51c3572..4e0de6a 100644 --- a/lib/src/main/kotlin/ui/TopAppBar.kt +++ b/lib/src/main/kotlin/ui/TopAppBar.kt @@ -210,6 +210,8 @@ fun openMenu( val selectedTree = remember { mutableStateOf("") } // дерево, которое выбрал юзер (очев) + val treeID = remember { mutableStateOf(0) } + DropdownMenu( expanded = expandedNested.value, onDismissRequest = { expandedNested.value = !expandedNested.value }, @@ -228,6 +230,7 @@ fun openMenu( }, onClick = { expandedTreesNames[index].value = !expandedTreesNames[index].value + treeID.value = index }, trailingIcon = { arrowIcon() }, modifier = Modifier.onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } @@ -239,7 +242,7 @@ fun openMenu( expandedTreesNames[index], expandedNested, showFiles[index], - index, + treeID, 150.dp, selectedTree, activeTree, @@ -258,7 +261,7 @@ fun treesNames( expandedNested: MutableState, expandedOpenNested: MutableState, showFiles: List, - treeID: Int, + treeID: MutableState, offset: Dp, selectedTree: MutableState, activeTree: MutableState, @@ -278,11 +281,11 @@ fun treesNames( offset = DpOffset(offset, (-50).dp), modifier = Modifier.background(MaterialTheme.colorScheme.background) ) { - searchItem(dirFiles[treeID], expandedNested, selectedTree, expandedOpenNested) + searchItem(dirFiles[treeID.value], expandedNested, selectedTree, expandedOpenNested) repeat(showFiles.size) { index -> DropdownMenuItem(onClick = { - selectedTree.value = showFiles[index] + selectedTree.value = if (treeID.value == 2) showFiles[index] + ".json" else showFiles[index] }, text = { Text(showFiles[index]) }, modifier = Modifier @@ -295,7 +298,7 @@ fun treesNames( } if (selectedTree.value.isNotEmpty()) { - controller.createTree(selectedTree.value, treeID) + controller.createTree(selectedTree.value, treeID.value) openTreeState.value = true activeTree.value = true expandedNested.value = false From eac92a161ec8730da76ffd64eb74f6890afd2349 Mon Sep 17 00:00:00 2001 From: Artem Date: Mon, 1 May 2023 23:42:48 +0300 Subject: [PATCH 186/212] fix: Fix tests for trees --- lib/src/main/kotlin/treelib/abstractTree/Tree.kt | 2 +- lib/src/main/kotlin/treelib/avlTree/AVLTree.kt | 2 +- lib/src/main/kotlin/treelib/binTree/BINTree.kt | 2 +- lib/src/main/kotlin/treelib/rbTree/RBTree.kt | 2 +- lib/src/test/kotlin/treelib/BINTreeTest.kt | 6 ++++++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt index 2ecfa46..01bfc48 100644 --- a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt +++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt @@ -29,7 +29,7 @@ abstract class Tree< fun getItem(key: K): V? = treeStruct.find(wrapForFind(key))?.value - operator fun get(key: K): V? = treeStruct.find(wrapForFind(key))?.value + open operator fun get(key: K): V? = treeStruct.find(wrapForFind(key))?.value fun deleteItem(key: K) { if (treeStruct.find(wrapForFind(key)) == null) throw VauleNotFound() diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt index 45cebce..a5f1af3 100644 --- a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt +++ b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt @@ -11,5 +11,5 @@ class AVLTree, V> : operator fun set(key: K, value: V) = putItem(key to value) - operator fun get(key: K) = getItem(key) + override operator fun get(key: K) = getItem(key) } diff --git a/lib/src/main/kotlin/treelib/binTree/BINTree.kt b/lib/src/main/kotlin/treelib/binTree/BINTree.kt index 57196a2..ebc792c 100644 --- a/lib/src/main/kotlin/treelib/binTree/BINTree.kt +++ b/lib/src/main/kotlin/treelib/binTree/BINTree.kt @@ -10,5 +10,5 @@ class BINTree, V> operator fun set(key: K, value: V) = putItem(key to value) - operator fun get(key: K) = getItem(key) + override operator fun get(key: K) = getItem(key) } diff --git a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt index 138efe7..829b42f 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBTree.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBTree.kt @@ -11,5 +11,5 @@ class RBTree, V> : operator fun set(key: K, value: V) = putItem(key to value) - operator fun get(key: K) = getItem(key) + override operator fun get(key: K) = getItem(key) } diff --git a/lib/src/test/kotlin/treelib/BINTreeTest.kt b/lib/src/test/kotlin/treelib/BINTreeTest.kt index 08f5060..d3e03e3 100644 --- a/lib/src/test/kotlin/treelib/BINTreeTest.kt +++ b/lib/src/test/kotlin/treelib/BINTreeTest.kt @@ -92,4 +92,10 @@ class BINTreeTest { for (i in 1..3) { tree.putItem(Pair(i, i)) } + + assertEquals(expected = tree[1], actual = 1) + assertEquals(expected = tree[2], actual = 2) + assertEquals(expected = tree[3], actual = 3) + + } } \ No newline at end of file From 72d19ee1a90b23e9f29bb7b66c51a92cd5b0c193 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 2 May 2023 15:14:03 +0300 Subject: [PATCH 187/212] fix: Restore tree from db --- lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt | 2 +- lib/src/main/kotlin/treelib/rbTree/RBStruct.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 0667d9d..3aa22cb 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -3,7 +3,7 @@ package treelib.rbTree import treelib.abstractTree.balanced.BalancerParent -class RBBalancer>(private var root: RBNode?) : +class RBBalancer>(var root: RBNode?) : BalancerParent, RBStateContainer>() { init { diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt index 0d04ae6..090b5d1 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt @@ -93,9 +93,11 @@ class RBStruct> : if (!stack.isEmpty()) { if (set.contains(stack.peek())) { set.remove(stack.peek()) + currentNode.parent = stack.peek() stack.pop().right = currentNode } else { stack.peek().left = currentNode + currentNode.parent = stack.peek() } } stack.push(currentNode) @@ -114,6 +116,7 @@ class RBStruct> : stack.push(currentNode) } } + balancer.root = root } private fun > createRBNode(drawNode: RBVertexType): RBNode { From dbca492416ee7630b7e24cedd1a276743fda7e96 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 2 May 2023 15:17:04 +0300 Subject: [PATCH 188/212] refactor: Fix code style according to review comments --- lib/build.gradle.kts | 2 +- lib/src/main/kotlin/controller/Controller.kt | 2 +- .../kotlin/databaseManage/BINTreeManager.kt | 13 +- lib/src/main/kotlin/main.kt | 96 ++----- lib/src/main/kotlin/ui/ControlFields.kt | 31 ++- lib/src/main/kotlin/ui/DeleteAlertDialog.kt | 15 +- lib/src/main/kotlin/ui/Design.kt | 73 ++++++ lib/src/main/kotlin/ui/FileDialog.kt | 30 +-- lib/src/main/kotlin/ui/SaveDialog.kt | 28 +- lib/src/main/kotlin/ui/Submenu.kt | 165 ++++++++++++ lib/src/main/kotlin/ui/TopAppBar.kt | 247 ++++-------------- lib/src/main/kotlin/ui/UserInputValidation.kt | 11 +- .../nodes/drawableRB/RBDrawableTree.kt | 1 + 13 files changed, 373 insertions(+), 341 deletions(-) create mode 100644 lib/src/main/kotlin/ui/Design.kt create mode 100644 lib/src/main/kotlin/ui/Submenu.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 2c99498..1f19090 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -26,7 +26,7 @@ dependencies { 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") + implementation("com.google.code.gson:gson:2.10.1") val neo4jCore = "4.0.5" implementation("org.neo4j", "neo4j-ogm-core", neo4jCore) diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index 5fd3c23..ea00760 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -21,7 +21,7 @@ class Controller { var tree: DrawTree? = null - fun showFiles(): List> { + fun getSavedTreesNames(): List> { val avlTrees = avlManager.getSavedTreesNames() val rbTrees = rbManager.getSavedTreesNames() diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index 69578d8..af460e9 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -22,7 +22,6 @@ class BINTreeManager : TreeManager< /*** using json format files ***/ private val dirPath = System.getProperty("user.dir") + "/saved-trees/BIN-trees" - //private val dirPath = BIN_DB_DEFAULT_NAME private val jsonRep = JsonRepository(dirPath) @@ -32,7 +31,7 @@ class BINTreeManager : TreeManager< ): List>> { if (this.isTreeExist(name)) { val typeToken = object : TypeToken>>>() {} - val preOrder = jsonRep.exportTree(name, typeToken).toList() + val preOrder = jsonRep.exportTree("$name.json", typeToken).toList() tree.restoreStruct(preOrder.toList()) @@ -52,7 +51,7 @@ class BINTreeManager : TreeManager< override fun saveTreeToDB(name: String, tree: BINStruct>) { val info = vertexToDrawVertex(tree.preOrder()) - jsonRep.saveChanges(info.toTypedArray(), name) + jsonRep.saveChanges(info.toTypedArray(), "$name.json") } private fun drawVertexToVertex(drawVertex: MutableList>>): MutableList>> { @@ -82,7 +81,7 @@ class BINTreeManager : TreeManager< } private fun isTreeExist(treeName: String): Boolean { - return File(dirPath, treeName).exists() + return File(dirPath, "${treeName}.json").exists() } override fun getVertexesForDrawFromDB(name: String): List>> { @@ -94,10 +93,4 @@ class BINTreeManager : TreeManager< fun cleanDB() = jsonRep.clean() - /* - - companion object { - const val BIN_DB_DEFAULT_NAME = "binDB" - } - */ } diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index 71041ed..c8bc9ed 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -2,20 +2,12 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.window.WindowDraggableArea -import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.input.key.* -import androidx.compose.ui.input.pointer.PointerEventType -import androidx.compose.ui.input.pointer.onPointerEvent -import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight @@ -28,31 +20,47 @@ import java.awt.Dimension @OptIn(ExperimentalMaterial3Api::class) fun main() = application { - val clickButtonsState = List(7) { remember { mutableStateOf(false) } } - val windowState = rememberWindowState(placement = WindowPlacement.Maximized) + val deleteButton: MutableState = remember { mutableStateOf(false) } + val saveButton: MutableState = remember { mutableStateOf(false) } + val openButton: MutableState = remember { mutableStateOf(false) } + val createButton: MutableState = remember { mutableStateOf(false) } + val minimizeButton: MutableState = remember { mutableStateOf(false) } + val maximizeButton: MutableState = remember { mutableStateOf(false) } + val closeButton: MutableState = remember { mutableStateOf(false) } val controller = Controller() - - val activeTree = remember { mutableStateOf(false) } - + val windowState = rememberWindowState(placement = WindowPlacement.Maximized) val deleteTreeState = remember { mutableStateOf(false) } - val openTreeState = remember { mutableStateOf(false) } + val activeTree = remember { mutableStateOf(false) } - if (!clickButtonsState[6].value) { + if (!closeButton.value) { Window( onCloseRequest = ::exitApplication, undecorated = true, state = windowState, - icon = appIcon() + icon = AppIcon() ) { this.window.minimumSize = Dimension(800, 600) Scaffold( topBar = { WindowDraggableArea { - myTopAppBar(clickButtonsState, windowState, controller, activeTree, deleteTreeState, openTreeState) + MyTopAppBar( + deleteButton, + saveButton, + openButton, + createButton, + minimizeButton, + maximizeButton, + closeButton, + windowState, + controller, + activeTree, + deleteTreeState, + openTreeState + ) } }, content = { @@ -68,7 +76,7 @@ fun main() = application { .offset(0.dp, 50.dp) ) { - controlFields(controller, activeTree, deleteTreeState, openTreeState) + ControlFields(controller, activeTree, deleteTreeState, openTreeState) } } @@ -82,6 +90,7 @@ fun main() = application { } + val lightColors = lightColorScheme( background = Color(255, 255, 255), primary = Color(34, 35, 41), @@ -100,54 +109,3 @@ val myTypography = Typography( fontSize = 96.sp ) ) - - -@OptIn(ExperimentalComposeUiApi::class) -@Composable -fun topAppIconButton( - painter: Painter, - buttonsState: List>, - index: Int, - color: Color, - disabledColor: Color, - iconColor: Color, - clickButtonState: MutableState, -) { - - IconButton(onClick = { - clickButtonState.value = !clickButtonState.value - }, modifier = Modifier.fillMaxHeight().width(50.dp).clip(RectangleShape) - .onPointerEvent(PointerEventType.Enter) { buttonsState[index].value = true } - .onPointerEvent(PointerEventType.Exit) { buttonsState[index].value = false }, - colors = IconButtonDefaults.iconButtonColors( - contentColor = if (buttonsState[index].value) MaterialTheme.colorScheme.background else iconColor, - containerColor = if (buttonsState[index].value) color else disabledColor - ) - ) { - Icon(painter, contentDescription = null) - } - -} - -@Composable -fun searchIcon() = Icon( - imageVector = Icons.Outlined.Search, - contentDescription = null -) - -@Composable -fun arrowIcon() = Icon( - imageVector = Icons.Outlined.KeyboardArrowRight, - contentDescription = null, - modifier = Modifier.size(16.dp) -) - -@Composable -fun menuItemBackgroundColor(state: MutableState): Color { - return if (state.value) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.background -} - -@Composable -fun appIcon(): Painter { - return painterResource("appIcon.png") -} diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt index ca5258f..f337959 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -14,7 +14,7 @@ import controller.Controller import viewPart.nodes.drawableTree.DrawTree @Composable -fun controlFields( +fun ControlFields( controller: Controller, activeTree: MutableState, deleteTreeState: MutableState, @@ -29,15 +29,15 @@ fun controlFields( Column(modifier = Modifier.offset(0.dp, 0.dp).padding(horizontal = 10.dp)) { // add - controlField("Add", addFieldState, value, activeTree) + ControlField("Add", addFieldState, value, activeTree) Spacer(modifier = Modifier.height(2.dp)) // find - controlField("Find", findFieldState, value, activeTree) + ControlField("Find", findFieldState, value, activeTree) Spacer(modifier = Modifier.height(2.dp)) // delete - controlField("Delete", deleteFieldState, value, activeTree) + ControlField("Delete", deleteFieldState, value, activeTree) } var tree by remember { mutableStateOf(null) } @@ -49,6 +49,7 @@ fun controlFields( } if (openTreeState.value) { tree = controller.tree + openTreeState.value = false } tree?.displayTree() @@ -73,24 +74,24 @@ fun controlFields( @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Composable -fun controlField( +fun ControlField( buttonName: String, buttonState: MutableState, value: MutableState, activeTree: MutableState ) { - var text by remember { mutableStateOf("") } + var userInput by remember { mutableStateOf("") } - var containerColor by remember { mutableStateOf(Color(206, 211, 216)) } + var fieldBackgroundColor by remember { mutableStateOf(Color(206, 211, 216)) } OutlinedTextField( - value = text, - onValueChange = { text = it }, + value = userInput, + onValueChange = { userInput = it }, singleLine = true, label = { Text(buttonName) }, colors = TextFieldDefaults.outlinedTextFieldColors( - containerColor = containerColor, + containerColor = fieldBackgroundColor, focusedBorderColor = MaterialTheme.colorScheme.primary, unfocusedBorderColor = MaterialTheme.colorScheme.onTertiary, focusedLabelColor = Color(99, 95, 95), @@ -101,7 +102,7 @@ fun controlField( .padding(start = 3.dp) .width(150.dp) .onFocusChanged { - containerColor = if (it.isFocused) { + fieldBackgroundColor = if (it.isFocused) { Color(255, 255, 255) } else { Color(206, 211, 216) @@ -109,9 +110,9 @@ fun controlField( }.onPreviewKeyEvent { when { (!it.isShiftPressed && it.key == Key.Enter && it.type == KeyEventType.KeyUp) -> { - value.value = text // change !!! + value.value = userInput // change !!! buttonState.value = true - text = "" + userInput = "" true } @@ -121,9 +122,7 @@ fun controlField( } }, - trailingIcon = { - - }, + trailingIcon = { }, shape = RoundedCornerShape(8.dp) ) diff --git a/lib/src/main/kotlin/ui/DeleteAlertDialog.kt b/lib/src/main/kotlin/ui/DeleteAlertDialog.kt index c981951..6e34245 100644 --- a/lib/src/main/kotlin/ui/DeleteAlertDialog.kt +++ b/lib/src/main/kotlin/ui/DeleteAlertDialog.kt @@ -1,5 +1,6 @@ package ui + import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -16,13 +17,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.unit.dp import controller.Controller -import topAppIconButton @OptIn(ExperimentalMaterialApi::class) @Composable -fun deleteDialog( - buttonState: List>, +fun DeleteDialog( + hoverButtonState: MutableState, clickButtonsState: MutableState, controller: Controller, activeTree: MutableState, @@ -37,7 +37,7 @@ fun deleteDialog( onDismissRequest = { clickButtonsState.value = false }, text = { Text( - text = deleteDialogText(activeTree, controller.tree?.name ?: ""), + text = DeleteDialogText(activeTree, controller.tree?.name ?: ""), color = MaterialTheme.colorScheme.primary ) }, @@ -60,10 +60,9 @@ fun deleteDialog( activeTree.value = false } - topAppIconButton( + TopAppIconButton( rememberVectorPainter(Icons.Outlined.Delete), - buttonState, - 0, + hoverButtonState, MaterialTheme.colorScheme.onPrimary, MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.background, @@ -73,7 +72,7 @@ fun deleteDialog( } @Composable -fun deleteDialogText(activeTree: MutableState, treeName: String): String { +fun DeleteDialogText(activeTree: MutableState, treeName: String): String { return if (activeTree.value) { "Are you sure you want to delete $treeName ?" } else { diff --git a/lib/src/main/kotlin/ui/Design.kt b/lib/src/main/kotlin/ui/Design.kt new file mode 100644 index 0000000..b928e0a --- /dev/null +++ b/lib/src/main/kotlin/ui/Design.kt @@ -0,0 +1,73 @@ +package ui + +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.KeyboardArrowRight +import androidx.compose.material.icons.outlined.Search +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.IconButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.onPointerEvent +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp + +@Composable +fun SearchIcon() = Icon( + imageVector = Icons.Outlined.Search, + contentDescription = null +) + +@Composable +fun ArrowIcon() = Icon( + imageVector = Icons.Outlined.KeyboardArrowRight, + contentDescription = null, + modifier = Modifier.size(16.dp) +) + +@Composable +fun MenuItemBackgroundColor(state: MutableState): Color { + return if (state.value) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.background +} + +@Composable +fun AppIcon(): Painter { + return painterResource("appIcon.png") +} + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun TopAppIconButton( + painter: Painter, + hoverButtonState: MutableState, + color: Color, + disabledColor: Color, + iconColor: Color, + clickButtonState: MutableState, +) { + + IconButton(onClick = { + clickButtonState.value = !clickButtonState.value + }, modifier = Modifier.fillMaxHeight().width(50.dp).clip(RectangleShape) + .onPointerEvent(PointerEventType.Enter) { hoverButtonState.value = true } + .onPointerEvent(PointerEventType.Exit) { hoverButtonState.value = false }, + colors = IconButtonDefaults.iconButtonColors( + contentColor = if (hoverButtonState.value) MaterialTheme.colorScheme.background else iconColor, + containerColor = if (hoverButtonState.value) color else disabledColor + ) + ) { + Icon(painter, contentDescription = null) + } + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/ui/FileDialog.kt b/lib/src/main/kotlin/ui/FileDialog.kt index 779d6d9..b9da6ea 100644 --- a/lib/src/main/kotlin/ui/FileDialog.kt +++ b/lib/src/main/kotlin/ui/FileDialog.kt @@ -35,20 +35,18 @@ import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.rememberDialogState import lightColors -import menuItemBackgroundColor -import searchIcon -import topAppIconButton import java.awt.Dimension import java.io.File @OptIn(ExperimentalComposeUiApi::class) @Composable -fun searchItem( +fun SearchItem( dirPath: String, expandedNested: MutableState, selectedTree: MutableState, expandedOpenNested: MutableState ) { + val dialogState = remember { mutableStateOf(false) } val minWidth = remember { mutableStateOf(200) } var pressedState by remember { mutableStateOf(false) } @@ -89,7 +87,7 @@ fun searchItem( } DropdownMenuItem( - leadingIcon = { searchIcon() }, + leadingIcon = { SearchIcon() }, text = { Text("Search") }, onClick = { dialogState.value = true @@ -97,7 +95,7 @@ fun searchItem( }, modifier = Modifier.onPointerEvent(PointerEventType.Enter) { backgroundColorState.value = true } .onPointerEvent(PointerEventType.Exit) { backgroundColorState.value = false } - .background(color = menuItemBackgroundColor(backgroundColorState)) + .background(color = MenuItemBackgroundColor(backgroundColorState)) ) } @@ -133,7 +131,7 @@ fun CompleteDialogContent( modifier = Modifier .height(50.dp) ) { - tittleAndButton(title, dialogState) + TittleAndButton(title, dialogState) } @@ -160,9 +158,9 @@ fun CompleteDialogContent( } @Composable -fun tittleAndButton(title: String, dialogState: MutableState) { +fun TittleAndButton(title: String, dialogState: MutableState) { - val buttonState = listOf(remember { mutableStateOf(false) }) + val hoverButtonState = remember { mutableStateOf(false) } val clickButtonState = remember { mutableStateOf(false) } @@ -170,16 +168,14 @@ fun tittleAndButton(title: String, dialogState: MutableState) { modifier = Modifier .fillMaxWidth(1f) .padding(start = 20.dp, top = 0.dp, end = 0.dp, bottom = 10.dp), - //.padding(horizontal = 20.dp, vertical = 10.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text(text = title, fontSize = 20.sp, color = MaterialTheme.colorScheme.primary) - topAppIconButton( + TopAppIconButton( rememberVectorPainter(Icons.Outlined.Close), - buttonState, - 0, + hoverButtonState, Color(233, 8, 28), MaterialTheme.colorScheme.background, MaterialTheme.colorScheme.primary, @@ -205,7 +201,7 @@ fun BottomButtons(dialogState: MutableState, firstButtonState: MutableS contentColor = MaterialTheme.colorScheme.background ) ) { - Text(text = firstButtonText, fontSize = 12.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold) + Text(text = firstButtonText, fontSize = 12.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold, maxLines = 1) } Spacer(modifier = Modifier.width(5.dp)) Button( @@ -219,7 +215,7 @@ fun BottomButtons(dialogState: MutableState, firstButtonState: MutableS onClick = { dialogState.value = false }, contentPadding = PaddingValues(start = 2.dp, end = 2.dp) ) { - Text(text = secondButtonText, fontSize = 12.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold) + Text(text = secondButtonText, fontSize = 12.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold, maxLines = 1) } } @@ -315,7 +311,7 @@ fun mainBody( modifier = Modifier.fillParentMaxWidth().align(Alignment.CenterStart) ) { Spacer(modifier = Modifier.width(25.dp)) - Icon(fileIcon(dirName), contentDescription = null, tint = Color.Gray) + Icon(FileIcon(dirName), contentDescription = null, tint = Color.Gray) Spacer(modifier = Modifier.width(5.dp)) Text( text = listFiles[index], fontSize = 13.sp, fontFamily = FontFamily.Monospace, @@ -332,7 +328,7 @@ fun mainBody( } @Composable -fun fileIcon(dirName: String): Painter { +fun FileIcon(dirName: String): Painter { return when (dirName) { "RB-trees" -> painterResource("/drawable/neo4jFormat.png") diff --git a/lib/src/main/kotlin/ui/SaveDialog.kt b/lib/src/main/kotlin/ui/SaveDialog.kt index f8dec7c..fb660b8 100644 --- a/lib/src/main/kotlin/ui/SaveDialog.kt +++ b/lib/src/main/kotlin/ui/SaveDialog.kt @@ -22,14 +22,13 @@ import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.rememberDialogState import controller.Controller -import topAppIconButton @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Composable -fun saveDialog( - buttonState: List>, +fun SaveDialog( + hoverButtonState: MutableState, clickButtonState: MutableState, - fileName: MutableState, + selectFileName: MutableState, controller: Controller, activeTree: MutableState ) { @@ -43,7 +42,7 @@ fun saveDialog( Dialog( onCloseRequest = { clickButtonState.value = false }, undecorated = true, state = rememberDialogState( - position = WindowPosition(Alignment.Center), // поменять ?? + position = WindowPosition(Alignment.Center), size = DpSize(300.dp, 180.dp) ), resizable = false @@ -63,7 +62,7 @@ fun saveDialog( Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.SpaceBetween) { Column(modifier = Modifier.height(50.dp)) { - tittleAndButton("Save tree", clickButtonState) + TittleAndButton("Save tree", clickButtonState) Divider(modifier = Modifier.height(1.dp).fillMaxWidth()) } @@ -74,15 +73,15 @@ fun saveDialog( ) { TextField( enabled = activeTree.value, - value = fileName.value, + value = selectFileName.value, singleLine = true, shape = RoundedCornerShape(4.dp), - onValueChange = { fileName.value = it }, + onValueChange = { selectFileName.value = it }, modifier = Modifier .onPreviewKeyEvent { when { (!it.isShiftPressed && it.key == Key.Enter && it.type == KeyEventType.KeyUp) -> { - validateInputState = validate(fileName.value) + validateInputState = validate(selectFileName.value) if (validateInputState) { successDialogState = true } else { @@ -114,7 +113,7 @@ fun saveDialog( ) Text( text = when { - successDialogState -> "Tree successfully saved to ${fileName.value}" + successDialogState -> "Tree successfully saved to ${selectFileName.value}" warningDialogState -> "Invalid tree name for saving" else -> "" }, @@ -137,7 +136,7 @@ fun saveDialog( } if (saveButtonState.value && activeTree.value) { - validateInputState = validate(fileName.value) + validateInputState = validate(selectFileName.value) if (validateInputState) { successDialogState = true } else { @@ -146,7 +145,7 @@ fun saveDialog( } if (successDialogState) { - controller.saveTree(fileName.value) + controller.saveTree(selectFileName.value) } } @@ -157,10 +156,9 @@ fun saveDialog( } } - topAppIconButton( + TopAppIconButton( painterResource("/drawable/save.png"), - buttonState, - 1, + hoverButtonState, MaterialTheme.colorScheme.onPrimary, MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.background, diff --git a/lib/src/main/kotlin/ui/Submenu.kt b/lib/src/main/kotlin/ui/Submenu.kt new file mode 100644 index 0000000..fd16105 --- /dev/null +++ b/lib/src/main/kotlin/ui/Submenu.kt @@ -0,0 +1,165 @@ +package ui + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.onPointerEvent +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.dp +import controller.Controller + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun CreateMenu(expandedNested: MutableState, controller: Controller, activeTree: MutableState) { + + val trees = listOf("Red black tree", "AVL tree", "Binary tree") + val defaultTreesNames = listOf("rbTree", "avlTree", "binTree") + + val backgroundColorState = List(3) { remember { mutableStateOf(false) } } + + DropdownMenu( + expanded = expandedNested.value, + onDismissRequest = { expandedNested.value = false }, + offset = DpOffset(150.dp, 0.dp), + modifier = Modifier.background(MaterialTheme.colorScheme.background) + ) { + + repeat(3) { index -> + DropdownMenuItem(text = { Text(trees[index]) }, + onClick = { + controller.createTree(defaultTreesNames[index], index) + activeTree.value = true + expandedNested.value = false + }, + modifier = Modifier + .onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } + .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } + .background(color = MenuItemBackgroundColor(backgroundColorState[index]))) + } + } +} + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun OpenMenu( + expandedNested: MutableState, + treesNames: List>, + activeTree: MutableState, + openTreeState: MutableState, + controller: Controller +) { + val trees = listOf("Red black tree", "AVL tree", "Binary tree") + + val expandedTreesNames = List(3) { remember { mutableStateOf(false) } } // !! + val backgroundColorState = List(3) { remember { mutableStateOf(false) } } // !! + + val selectedTreeName = remember { mutableStateOf("") } + val selectedTreeID = remember { mutableStateOf(0) } + + DropdownMenu( + expanded = expandedNested.value, + onDismissRequest = { + expandedNested.value = !expandedNested.value + }, + offset = DpOffset(100.dp, 0.dp), + modifier = Modifier.width(150.dp).background(MaterialTheme.colorScheme.background) + ) { + repeat(3) { index -> + DropdownMenuItem( + text = { + Text( + trees[index], + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.height(50.dp), + maxLines = 1 + ) + }, + onClick = { + expandedTreesNames[index].value = !expandedTreesNames[index].value + selectedTreeID.value = index + }, + trailingIcon = { ArrowIcon() }, + modifier = Modifier.onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } + .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } + .background(color = MenuItemBackgroundColor(backgroundColorState[index])), + colors = MenuDefaults.itemColors(textColor = MaterialTheme.colorScheme.primary)) + + SelectTree( + expandedTreesNames[index], + expandedNested, + treesNames[index], + selectedTreeID, + 150.dp, + selectedTreeName, + activeTree, + openTreeState, + controller + ) + } + } + + +} + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun SelectTree( + expandedNested: MutableState, + expandedOpenNested: MutableState, + treesNames: List, + selectedTreeID: MutableState, + offset: Dp, + selectedTreeName: MutableState, + activeTree: MutableState, + openTreeState: MutableState, + controller: Controller +) { + + val dirPath = System.getProperty("user.dir") + "/saved-trees" // !! + val dirFiles = listOf("$dirPath/RB-trees", "$dirPath/AVL-trees", "$dirPath/BIN-trees") // !! + + val backgroundColorState = List(treesNames.size) { remember { mutableStateOf(false) } } // !! + + AnimatedVisibility(visible = expandedNested.value) { + DropdownMenu( + expanded = expandedNested.value, + onDismissRequest = { expandedNested.value = false }, + offset = DpOffset(offset, (-50).dp), + modifier = Modifier.background(MaterialTheme.colorScheme.background) + ) { + SearchItem(dirFiles[selectedTreeID.value], expandedNested, selectedTreeName, expandedOpenNested) + + repeat(treesNames.size) { index -> + DropdownMenuItem(onClick = { + selectedTreeName.value = treesNames[index] + expandedNested.value = false + expandedOpenNested.value = false + }, + text = { Text(treesNames[index]) }, + modifier = Modifier + .onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } + .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } + .background(color = MenuItemBackgroundColor(backgroundColorState[index])), + colors = MenuDefaults.itemColors(textColor = MaterialTheme.colorScheme.primary)) + } + } + } + + if (selectedTreeName.value.isNotEmpty()) { + controller.createTree(selectedTreeName.value, selectedTreeID.value) + openTreeState.value = true + activeTree.value = true + selectedTreeName.value = "" + } + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/ui/TopAppBar.kt b/lib/src/main/kotlin/ui/TopAppBar.kt index 4e0de6a..2597555 100644 --- a/lib/src/main/kotlin/ui/TopAppBar.kt +++ b/lib/src/main/kotlin/ui/TopAppBar.kt @@ -1,39 +1,35 @@ package ui -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.Close -import androidx.compose.material3.* +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.rememberVectorPainter -import androidx.compose.ui.input.pointer.PointerEventType -import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.WindowPlacement import androidx.compose.ui.window.WindowState -import arrowIcon import controller.Controller import lightColors -import menuItemBackgroundColor import myTypography -import topAppIconButton @Composable -fun myTopAppBar( - clickButtonsState: List>, +fun MyTopAppBar( + clickDeleteButton: MutableState, + clickSaveButton: MutableState, + clickOpenButton: MutableState, + clickCreateButton: MutableState, + clickMinimizeButton: MutableState, + clickMaximizeButton: MutableState, + clickCloseButton: MutableState, windowState: WindowState, controller: Controller, activeTree: MutableState, @@ -41,15 +37,22 @@ fun myTopAppBar( openTreeState: MutableState ) { - val showFiles = controller.showFiles() // показывает <=3 деревьев каждого вида в диалоге open + val treesNames = controller.getSavedTreesNames() - val fileName = remember { mutableStateOf("") } // имя файла в диалоге save + val selectFileName = remember { mutableStateOf("") } MaterialTheme( colorScheme = lightColors, typography = myTypography ) { - val buttonState = List(7) { remember { mutableStateOf(false) } } + + val hoverDeleteButton: MutableState = remember { mutableStateOf(false) } + val hoverSaveButton: MutableState = remember { mutableStateOf(false) } + val hoverOpenButton: MutableState = remember { mutableStateOf(false) } + val hoverCreateButton: MutableState = remember { mutableStateOf(false) } + val hoverMinimizeButton: MutableState = remember { mutableStateOf(false) } + val hoverMaximizeButton: MutableState = remember { mutableStateOf(false) } + val hoverCloseButton: MutableState = remember { mutableStateOf(false) } TopAppBar( backgroundColor = MaterialTheme.colorScheme.primary, @@ -63,81 +66,72 @@ fun myTopAppBar( horizontalArrangement = Arrangement.SpaceBetween ) { Row { - - deleteDialog( - buttonState, - clickButtonsState[0], + DeleteDialog( + hoverDeleteButton, + clickDeleteButton, controller, activeTree, deleteTreeState ) + SaveDialog(hoverSaveButton, clickSaveButton, selectFileName, controller, activeTree) - saveDialog(buttonState, clickButtonsState[1], fileName, controller, activeTree) - - topAppIconButton( + TopAppIconButton( painterResource("/drawable/dir.png"), - buttonState, - 2, + hoverOpenButton, MaterialTheme.colorScheme.onPrimary, MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.background, - clickButtonsState[2] + clickOpenButton ) - openMenu(clickButtonsState[2], showFiles, activeTree, openTreeState, controller) + OpenMenu(clickOpenButton, treesNames, activeTree, openTreeState, controller) - - topAppIconButton( + TopAppIconButton( rememberVectorPainter(Icons.Outlined.Add), - buttonState, - 3, + hoverCreateButton, MaterialTheme.colorScheme.onPrimary, MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.background, - clickButtonsState[3] + clickCreateButton ) - createMenu(clickButtonsState[3], controller, activeTree) - + CreateMenu(clickCreateButton, controller, activeTree) } Row(horizontalArrangement = Arrangement.End) { - topAppIconButton( + TopAppIconButton( painterResource("/drawable/minimize.png"), - buttonState, - 4, + hoverMinimizeButton, MaterialTheme.colorScheme.onPrimary, MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.background, - clickButtonsState[4] + clickMinimizeButton ) - if (clickButtonsState[4].value) { - minimizeWindow(windowState) - clickButtonsState[4].value = false + if (clickMinimizeButton.value) { + MinimizeWindow(windowState) + clickMinimizeButton.value = false } - topAppIconButton( + TopAppIconButton( painterResource("/drawable/maximize.png"), - buttonState, - 5, + hoverMaximizeButton, MaterialTheme.colorScheme.onPrimary, MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.background, - clickButtonsState[5] + clickMaximizeButton ) - if (clickButtonsState[5].value) { - maximizeWindow(windowState) - clickButtonsState[5].value = false + if (clickMaximizeButton.value) { + MaximizeWindow(windowState) + clickMaximizeButton.value = false } - topAppIconButton( + TopAppIconButton( rememberVectorPainter(Icons.Outlined.Close), - buttonState, - 6, + hoverCloseButton, Color(233, 8, 28), MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.background, - clickButtonsState[6] + clickCloseButton ) } @@ -145,164 +139,19 @@ fun myTopAppBar( } } - } } @Composable -private fun minimizeWindow(state: WindowState) { +private fun MinimizeWindow(state: WindowState) { state.isMinimized = !state.isMinimized } @Composable -private fun maximizeWindow(state: WindowState) { +private fun MaximizeWindow(state: WindowState) { if (state.placement == WindowPlacement.Maximized) { state.placement = WindowPlacement.Floating } else { state.placement = WindowPlacement.Maximized } } - -@OptIn(ExperimentalComposeUiApi::class) -@Composable -fun createMenu(expandedNested: MutableState, controller: Controller, activeTree: MutableState) { - val trees = listOf("Red black tree", "AVL tree", "Binary tree") - - val treesNames = listOf("rbTree", "avlTree", "binTree.json") - - val backgroundColorState = List(3) { remember { mutableStateOf(false) } } - - DropdownMenu( - expanded = expandedNested.value, - onDismissRequest = { expandedNested.value = false }, - offset = DpOffset(150.dp, 0.dp), - modifier = Modifier.background(MaterialTheme.colorScheme.background) - ) { - - repeat(3) { index -> - DropdownMenuItem(text = { Text(trees[index]) }, - onClick = { - controller.createTree(treesNames[index], index) - activeTree.value = true - expandedNested.value = false - }, - modifier = Modifier.onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } - .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } - .background(color = menuItemBackgroundColor(backgroundColorState[index]))) - } - } -} - -@OptIn(ExperimentalComposeUiApi::class) -@Composable -fun openMenu( - expandedNested: MutableState, - showFiles: List>, - activeTree: MutableState, - openTreeState: MutableState, - controller: Controller -) { - val trees = listOf("Red black tree", "AVL tree", "Binary tree") - - val expandedTreesNames = List(3) { remember { mutableStateOf(false) } } - - val backgroundColorState = List(3) { remember { mutableStateOf(false) } } - - val selectedTree = remember { mutableStateOf("") } // дерево, которое выбрал юзер (очев) - - val treeID = remember { mutableStateOf(0) } - - DropdownMenu( - expanded = expandedNested.value, - onDismissRequest = { expandedNested.value = !expandedNested.value }, - offset = DpOffset(100.dp, 0.dp), - modifier = Modifier.width(150.dp).background(MaterialTheme.colorScheme.background) - ) { - repeat(3) { index -> - DropdownMenuItem( - text = { - Text( - trees[index], - color = MaterialTheme.colorScheme.primary, - modifier = Modifier.height(50.dp), - maxLines = 1 - ) - }, - onClick = { - expandedTreesNames[index].value = !expandedTreesNames[index].value - treeID.value = index - }, - trailingIcon = { arrowIcon() }, - modifier = Modifier.onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } - .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } - .background(color = menuItemBackgroundColor(backgroundColorState[index])), - colors = MenuDefaults.itemColors(textColor = MaterialTheme.colorScheme.primary)) - - treesNames( - expandedTreesNames[index], - expandedNested, - showFiles[index], - treeID, - 150.dp, - selectedTree, - activeTree, - openTreeState, - controller - ) - } - } - - -} - -@OptIn(ExperimentalComposeUiApi::class) -@Composable -fun treesNames( - expandedNested: MutableState, - expandedOpenNested: MutableState, - showFiles: List, - treeID: MutableState, - offset: Dp, - selectedTree: MutableState, - activeTree: MutableState, - openTreeState: MutableState, - controller: Controller -) { - - val dirPath = System.getProperty("user.dir") + "/saved-trees" - val dirFiles = listOf("$dirPath/RB-trees", "$dirPath/AVL-trees", "$dirPath/BIN-trees") - - val backgroundColorState = List(showFiles.size) { remember { mutableStateOf(false) } } - - AnimatedVisibility(visible = expandedNested.value) { - DropdownMenu( - expanded = expandedNested.value, - onDismissRequest = { expandedNested.value = false }, - offset = DpOffset(offset, (-50).dp), - modifier = Modifier.background(MaterialTheme.colorScheme.background) - ) { - searchItem(dirFiles[treeID.value], expandedNested, selectedTree, expandedOpenNested) - - repeat(showFiles.size) { index -> - DropdownMenuItem(onClick = { - selectedTree.value = if (treeID.value == 2) showFiles[index] + ".json" else showFiles[index] - }, - text = { Text(showFiles[index]) }, - modifier = Modifier - .onPointerEvent(PointerEventType.Enter) { backgroundColorState[index].value = true } - .onPointerEvent(PointerEventType.Exit) { backgroundColorState[index].value = false } - .background(color = menuItemBackgroundColor(backgroundColorState[index])), - colors = MenuDefaults.itemColors(textColor = MaterialTheme.colorScheme.primary)) - } - } - } - - if (selectedTree.value.isNotEmpty()) { - controller.createTree(selectedTree.value, treeID.value) - openTreeState.value = true - activeTree.value = true - expandedNested.value = false - selectedTree.value = "" - } - -} \ No newline at end of file diff --git a/lib/src/main/kotlin/ui/UserInputValidation.kt b/lib/src/main/kotlin/ui/UserInputValidation.kt index 9674e1e..673ee5f 100644 --- a/lib/src/main/kotlin/ui/UserInputValidation.kt +++ b/lib/src/main/kotlin/ui/UserInputValidation.kt @@ -11,12 +11,13 @@ fun validate(fileName: String): Boolean { } val os = System.getProperty("os.name").lowercase(Locale.getDefault()) return when { - os.contains("win") -> Arrays.stream(INVALID_WINDOWS_SPECIFIC_CHAR) - .noneMatch { ch -> fileName.contains(ch.toString()) } + os.contains("win") -> + Arrays.stream(INVALID_WINDOWS_SPECIFIC_CHAR) + .noneMatch { ch -> fileName.contains(ch.toString()) } - os.contains("unix") || os.contains("linux") || os.contains("mac") -> Arrays.stream( - INVALID_UNIX_SPECIFIC_CHAR - ).noneMatch { ch -> fileName.contains(ch.toString()) } + os.contains("unix") || os.contains("linux") || os.contains("mac") -> + Arrays.stream(INVALID_UNIX_SPECIFIC_CHAR) + .noneMatch { ch -> fileName.contains(ch.toString()) } else -> true } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt index a0e842c..b222594 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt @@ -27,6 +27,7 @@ class RBDrawableTree( override val designNode = RBNodeDesign override fun deleteTree() { + this.deleteTreeFromBD() root = null treeStruct = RBStruct() } From 1573029b8ce3cd7b55935cecc325b83bd55e0e9f Mon Sep 17 00:00:00 2001 From: Georgy S Date: Tue, 2 May 2023 15:48:51 +0300 Subject: [PATCH 189/212] refactor: Fixe typos; separate business logic and view; remove wild cards (#26). --- lib/src/main/kotlin/controller/Controller.kt | 4 +-- .../databaseSave/neo4j/Neo4jRepository.kt | 4 ++- lib/src/main/kotlin/ui/ControlFields.kt | 3 +- lib/src/main/kotlin/ui/FileDialog.kt | 1 + .../kotlin/viewPart/nodes/TreeDrawingUtils.kt | 33 ++++++++++++++----- .../viewPart/nodes/drawableTree/DrawTree.kt | 6 +--- .../nodes/drawableTree/DrawableTree.kt | 14 ++------ lib/src/test/kotlin/treelib/RBStructTest.kt | 8 ++--- .../test/kotlin/utils/TreeStructWrapper.kt | 3 +- lib/src/test/kotlin/utils/TreeWrapper.kt | 1 + 10 files changed, 42 insertions(+), 35 deletions(-) diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index ea00760..0709f84 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -76,7 +76,7 @@ class Controller { tree?.insert(Container(Pair(key, value))) ?: throw NullPointerException() tree?.updateTree() ?: throw NullPointerException() - tree?.repositisonTree(800f, 10f) ?: throw NullPointerException() + tree?.repositionTree(800f, 10f) ?: throw NullPointerException() return tree ?: throw NullPointerException() } @@ -95,7 +95,7 @@ class Controller { tree?.delete(Container(Pair(key, ""))) ?: throw NullPointerException() tree?.updateTree() ?: throw NullPointerException() - tree?.repositisonTree(800f, 10f) ?: throw NullPointerException() + tree?.repositionTree(800f, 10f) ?: throw NullPointerException() return tree ?: throw NullPointerException() } diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index 13161a9..86ac99e 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -10,7 +10,9 @@ import treelib.commonObjects.Container import treelib.rbTree.Markers import java.io.Closeable import java.io.IOException -import java.util.* +import java.util.LinkedList +import kotlin.collections.HashSet + class Neo4jRepository : Closeable { diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt index f337959..797a93c 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -11,6 +11,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.key.* import androidx.compose.ui.unit.dp import controller.Controller +import viewPart.nodes.displayTree import viewPart.nodes.drawableTree.DrawTree @Composable @@ -51,7 +52,7 @@ fun ControlFields( tree = controller.tree openTreeState.value = false } - tree?.displayTree() + tree?.let { displayTree(it) } addFieldState.value = false } diff --git a/lib/src/main/kotlin/ui/FileDialog.kt b/lib/src/main/kotlin/ui/FileDialog.kt index b9da6ea..dcbfc25 100644 --- a/lib/src/main/kotlin/ui/FileDialog.kt +++ b/lib/src/main/kotlin/ui/FileDialog.kt @@ -196,6 +196,7 @@ fun BottomButtons(dialogState: MutableState, firstButtonState: MutableS firstButtonState.value = !firstButtonState.value //dialogState.value = false }, + contentPadding = PaddingValues(start = 2.dp, end = 2.dp), colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.secondary, contentColor = MaterialTheme.colorScheme.background diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index f48cc57..7f86eba 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -16,16 +16,32 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import treelib.commonObjects.Container import viewPart.nodes.drawableAVL.AVLDrawableNode +import viewPart.nodes.drawableAVL.AVLDrawableTree +import viewPart.nodes.drawableAVL.AVLNodeDesign import viewPart.nodes.drawableBIN.BINDrawableNode +import viewPart.nodes.drawableBIN.BINDrawableTree +import viewPart.nodes.drawableBIN.BINNodeDesign import viewPart.nodes.drawableRB.RBDrawableNode +import viewPart.nodes.drawableRB.RBDrawableTree +import viewPart.nodes.drawableRB.RBNodeDesign +import viewPart.nodes.drawableTree.DrawTree import viewPart.nodes.drawableTree.DrawableNode import viewPart.nodes.drawableTree.NodeDesign -import java.util.* +import java.util.Locale import kotlin.math.roundToInt -/* @Composable -fun displayTree(tree: RBDrawableTree) { +fun displayTree(tree: DrawTree){ + when(tree){ + is BINDrawableTree -> displayBIN(tree) + is RBDrawableTree -> displayRB(tree) + is AVLDrawableTree -> displayAVL(tree) + else -> throw NullPointerException("Wrong DrawableTree type") + } +} + +@Composable +fun displayRB(tree: RBDrawableTree) { val root = tree.root root?.let { displayNode(it, RBNodeDesign) @@ -33,7 +49,7 @@ fun displayTree(tree: RBDrawableTree) { } @Composable -fun displayTree(tree: AVLDrawableTree) { +fun displayAVL(tree: AVLDrawableTree) { val root = tree.root root?.let { displayNode(it, AVLNodeDesign) @@ -41,13 +57,14 @@ fun displayTree(tree: AVLDrawableTree) { } @Composable -fun displayTree(tree: BINDrawableTree) { +fun displayBIN(tree: BINDrawableTree) { val root = tree.root root?.let { displayNode(it, BINNodeDesign) } } -*/ + + @Composable fun , DNode>, NodeD : NodeDesign> displayNode(node: DNode, design: NodeD) { @@ -68,13 +85,11 @@ fun , DNode>, NodeD : NodeDesign> di Column(modifier = Modifier.zIndex(1f)) { Text(text = "key: ${node.value.key}") Text(text = "value: ${node.value.value}") - when (node ) { + when (node) { is AVLDrawableNode<*> -> Text(text = "height: ${node.height}") is RBDrawableNode<*> -> Text(text = "color: ${node.color.toString().lowercase(Locale.getDefault())}") } - } - } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt index cfbdd7c..af823c4 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt @@ -1,6 +1,5 @@ package viewPart.nodes.drawableTree -import androidx.compose.runtime.Composable import treelib.commonObjects.Container interface DrawTree { @@ -31,9 +30,6 @@ interface DrawTree { val designNode: NodeDesign var yShiftBetweenNodes: Float - @Composable - fun displayTree() - fun initTree() fun updateTree() fun deleteTree() @@ -42,5 +38,5 @@ interface DrawTree { fun insert(item: Container) fun delete(item: Container) fun find(item: Int) - fun repositisonTree(xBase: Float, yBase: Float) + fun repositionTree(xBase: Float, yBase: Float) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index d649578..86078c0 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -1,6 +1,5 @@ package viewPart.nodes.drawableTree -import androidx.compose.runtime.Composable import databaseManage.TreeManager import databaseSave.DrawableVertex import treelib.abstractTree.Node @@ -9,7 +8,6 @@ import treelib.abstractTree.TreeStruct import treelib.abstractTree.Vertex import treelib.commonObjects.Container import treelib.commonObjects.exceptions.ImpossibleCaseException -import viewPart.nodes.displayNode abstract class DrawableTree< DNodeType : DrawableNode, DNodeType>, @@ -23,17 +21,10 @@ abstract class DrawableTree< protected abstract var drawablePreOrder: List? protected abstract val treeManager: TreeManager, DVertexType, NodeType, State, VertexType, StructType> protected abstract var treeStruct: StructType - protected abstract var root: DNodeType? + abstract var root: DNodeType? override var yShiftBetweenNodes = 10f - @Composable - override fun displayTree() { - root?.let { - displayNode(it, designNode) - } - } - override fun initTree() { val binVertexes = treeManager.initTree(name, treeStruct) drawablePreOrder = binVertexes.map { drawableVertexToNode(it) } @@ -84,10 +75,9 @@ abstract class DrawableTree< } if (currentNode == null) return } - } - override fun repositisonTree(xBase: Float, yBase: Float) { + override fun repositionTree(xBase: Float, yBase: Float) { /*xBase: Float = 0f, yBase: Float = 0f*/ root?.let { createCordsState1(it, xBase, yBase) diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index 5b153bb..6a941de 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -116,8 +116,8 @@ class RBStructTest { } @Test - fun `fazzer test`() { - val fazzer = RBStructFuzzer( + fun `fuzzer test`() { + val fuzzer = RBStructFuzzer( arrayOf( 1, 2, @@ -141,11 +141,11 @@ class RBStructTest { 2754 ), ::testAssert ) - fazzer.saveNextTestSets("TEST_TEST") + fuzzer.saveNextTestSets("TEST_TEST") assertAll( { - fazzer.fuzzInvariantInsert(15, 10) + fuzzer.fuzzInvariantInsert(15, 10) } ) } diff --git a/lib/src/test/kotlin/utils/TreeStructWrapper.kt b/lib/src/test/kotlin/utils/TreeStructWrapper.kt index ba9fb10..c0e05bb 100644 --- a/lib/src/test/kotlin/utils/TreeStructWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeStructWrapper.kt @@ -5,6 +5,7 @@ import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct import treelib.abstractTree.Vertex +@Suppress("UNCHECKED_CAST") class TreeStructWrapper, NodeType : Node, VertexType : Vertex, State : StateContainer, TStruct : TreeStruct> { fun getPrivateNode(tree: TStruct, name: String = "root"): NodeType? { @@ -26,4 +27,4 @@ class TreeStructWrapper, NodeType : Node, VertexT return if (parameterValues != null) method.invoke(tree, *parameterValues) else method.invoke(tree) } -} \ No newline at end of file +} diff --git a/lib/src/test/kotlin/utils/TreeWrapper.kt b/lib/src/test/kotlin/utils/TreeWrapper.kt index a5b5d8f..e8f8285 100644 --- a/lib/src/test/kotlin/utils/TreeWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeWrapper.kt @@ -4,6 +4,7 @@ import treelib.abstractTree.* import treelib.commonObjects.Container +@Suppress("UNCHECKED_CAST") class TreeWrapper< V : Comparable, Value, From 3844ee9b42cb0bc61dcb1cdf4ac35f3a8321b072 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 2 May 2023 19:20:08 +0300 Subject: [PATCH 190/212] fix: Some visual defects on the main screen --- lib/build.gradle.kts | 2 +- lib/src/main/kotlin/main.kt | 8 +++++--- lib/src/main/kotlin/ui/ControlFields.kt | 7 ++++++- lib/src/main/kotlin/ui/Submenu.kt | 8 +++++++- lib/src/main/kotlin/ui/TopAppBar.kt | 5 +++-- .../kotlin/viewPart/nodes/TreeDrawingUtils.kt | 11 ++++++----- lib/src/main/resources/appIcon.png | Bin 761803 -> 29675 bytes 7 files changed, 28 insertions(+), 13 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 1f19090..25447de 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -8,7 +8,7 @@ plugins { // checkstyle id("org.jetbrains.compose") version "1.4.0" } - +// ? java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index c8bc9ed..24cc37e 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -30,10 +30,11 @@ fun main() = application { val closeButton: MutableState = remember { mutableStateOf(false) } val controller = Controller() + val activeTree = remember { mutableStateOf(false) } val windowState = rememberWindowState(placement = WindowPlacement.Maximized) val deleteTreeState = remember { mutableStateOf(false) } val openTreeState = remember { mutableStateOf(false) } - val activeTree = remember { mutableStateOf(false) } + val createTreeState = remember { mutableStateOf(false) } if (!closeButton.value) { Window( @@ -59,7 +60,8 @@ fun main() = application { controller, activeTree, deleteTreeState, - openTreeState + openTreeState, + createTreeState ) } }, @@ -76,7 +78,7 @@ fun main() = application { .offset(0.dp, 50.dp) ) { - ControlFields(controller, activeTree, deleteTreeState, openTreeState) + ControlFields(controller, activeTree, deleteTreeState, openTreeState, createTreeState) } } diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt index f337959..470b38d 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -18,7 +18,8 @@ fun ControlFields( controller: Controller, activeTree: MutableState, deleteTreeState: MutableState, - openTreeState: MutableState + openTreeState: MutableState, + createTreeState: MutableState ) { val addFieldState = remember { mutableStateOf(false) } @@ -51,6 +52,10 @@ fun ControlFields( tree = controller.tree openTreeState.value = false } + if (createTreeState.value) { + tree = controller.tree + createTreeState.value = false + } tree?.displayTree() addFieldState.value = false diff --git a/lib/src/main/kotlin/ui/Submenu.kt b/lib/src/main/kotlin/ui/Submenu.kt index fd16105..96190d5 100644 --- a/lib/src/main/kotlin/ui/Submenu.kt +++ b/lib/src/main/kotlin/ui/Submenu.kt @@ -20,7 +20,12 @@ import controller.Controller @OptIn(ExperimentalComposeUiApi::class) @Composable -fun CreateMenu(expandedNested: MutableState, controller: Controller, activeTree: MutableState) { +fun CreateMenu( + expandedNested: MutableState, + controller: Controller, + activeTree: MutableState, + createTreeState: MutableState +) { val trees = listOf("Red black tree", "AVL tree", "Binary tree") val defaultTreesNames = listOf("rbTree", "avlTree", "binTree") @@ -38,6 +43,7 @@ fun CreateMenu(expandedNested: MutableState, controller: Controller, ac DropdownMenuItem(text = { Text(trees[index]) }, onClick = { controller.createTree(defaultTreesNames[index], index) + createTreeState.value = true activeTree.value = true expandedNested.value = false }, diff --git a/lib/src/main/kotlin/ui/TopAppBar.kt b/lib/src/main/kotlin/ui/TopAppBar.kt index 2597555..30c0642 100644 --- a/lib/src/main/kotlin/ui/TopAppBar.kt +++ b/lib/src/main/kotlin/ui/TopAppBar.kt @@ -34,7 +34,8 @@ fun MyTopAppBar( controller: Controller, activeTree: MutableState, deleteTreeState: MutableState, - openTreeState: MutableState + openTreeState: MutableState, + createTreeState: MutableState ) { val treesNames = controller.getSavedTreesNames() @@ -94,7 +95,7 @@ fun MyTopAppBar( MaterialTheme.colorScheme.background, clickCreateButton ) - CreateMenu(clickCreateButton, controller, activeTree) + CreateMenu(clickCreateButton, controller, activeTree, createTreeState) } Row(horizontalArrangement = Arrangement.End) { diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index f48cc57..87a45be 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -8,9 +8,11 @@ import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex @@ -53,7 +55,7 @@ fun , DNode>, NodeD : NodeDesign> di if (node.clickState.value) { Box(modifier = Modifier - .height(if (node is BINDrawableNode<*>) 60.dp else 90.dp).width(100.dp) + .height(if (node is BINDrawableNode<*>) 60.dp else 80.dp).width(100.dp) .offset { IntOffset( node.xState.value.roundToInt() + 71, @@ -62,12 +64,11 @@ fun , DNode>, NodeD : NodeDesign> di } .background(color = Color(206, 211, 216), shape = AbsoluteRoundedCornerShape(5.dp)) .border(2.dp, MaterialTheme.colorScheme.primary, AbsoluteRoundedCornerShape(5.dp)) - .padding(horizontal = 4.dp, vertical = 2.dp) .zIndex(1f) ) { - Column(modifier = Modifier.zIndex(1f)) { - Text(text = "key: ${node.value.key}") - Text(text = "value: ${node.value.value}") + Column(modifier = Modifier.fillMaxSize().padding(horizontal = 4.dp, vertical = 2.dp), horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.Top) { + Text(text = "key: ${node.value.key} ", maxLines = 1, overflow = TextOverflow.Ellipsis) + Text(text = "value: ${node.value.value}", maxLines = 2, overflow = TextOverflow.Ellipsis) when (node ) { is AVLDrawableNode<*> -> Text(text = "height: ${node.height}") is RBDrawableNode<*> -> Text(text = "color: ${node.color.toString().lowercase(Locale.getDefault())}") diff --git a/lib/src/main/resources/appIcon.png b/lib/src/main/resources/appIcon.png index 452a71c3df1a1f22db02c2ce742cbcdffd561064..47bdd13ee96e30abd609874f5499d4e22073296d 100644 GIT binary patch literal 29675 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

L~luQE;x=v%g)xFYj|Z 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�Nv-LT9jL~#op?e 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%0D`$zQqjMttQv-;8hl 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 From bc95af659f77fd714af6d89f493efa8a7c9f2ded Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Tue, 2 May 2023 19:43:30 +0300 Subject: [PATCH 191/212] fix: delete snake case(made the code more beautiful) --- lib/src/test/kotlin/treelib/BINStructTest.kt | 308 +++++++++---------- 1 file changed, 154 insertions(+), 154 deletions(-) diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 07ab528..2213b19 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -12,8 +12,8 @@ import kotlin.test.assertEquals @DisplayName("Test: Binary Search Tree Struct") class BINStructTest { - val treeW = TreeStructWrapper, BINVertex, BINStateContainer, BINStruct>() - var treeStruct = BINStruct() + private val treeW = TreeStructWrapper, BINVertex, BINStateContainer, BINStruct>() + private var treeStruct = BINStruct() private fun testAssert(msg: String): Nothing = fail(msg) @@ -43,24 +43,24 @@ class BINStructTest { treeStruct.insert(i) } - val additional_num = mutableListOf(1, 2, 3, 5, 7, 8, 11) - for (i in additional_num) { + val additionalNum = mutableListOf(1, 2, 3, 5, 7, 8, 11) + for (i in additionalNum) { 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 + val node2 = treeW.getPrivateNode(treeStruct)?.right?.value + val nodeNull = treeW.getPrivateNode(treeStruct)?.left?.value + val node3 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val nodeNull1 = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node4 = 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) + assertEquals(expected = nodeNull, actual = null) + assertEquals(expected = node4, actual = 4) + assertEquals(expected = node2, actual = 2) + assertEquals(expected = node3, actual = 3) + assertEquals(expected = nodeNull1, actual = null) } @Test @@ -102,19 +102,19 @@ class BINStructTest { treeStruct.delete(1) - val additional_num = mutableListOf(1, 2, 11) - for (i in additional_num) { + val additionalNum = mutableListOf(1, 2, 11) + for (i in additionalNum) { 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 + val node1 = treeW.getPrivateNode(treeStruct)?.left?.value + val node11 = treeW.getPrivateNode(treeStruct)?.right?.value assertEquals(expected = root, actual = 2) - assertEquals(expected = node_1, actual = 1) - assertEquals(expected = node_11, actual = 11) + assertEquals(expected = node1, actual = 1) + assertEquals(expected = node11, actual = 11) } @Test @@ -126,11 +126,11 @@ class BINStructTest { 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 + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullRight = treeW.getPrivateNode(treeStruct)?.right?.value - assertEquals(expected = null, actual = node_null_left) - assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 5, actual = root) } @@ -143,11 +143,11 @@ class BINStructTest { 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 + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullRight = treeW.getPrivateNode(treeStruct)?.right?.value - assertEquals(expected = null, actual = node_null_left) - assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 6, actual = root) } @@ -159,12 +159,12 @@ class BINStructTest { } treeStruct.delete(3) - val node_null_left = treeW.getPrivateNode(treeStruct)?.left?.value - val node_null_right = treeW.getPrivateNode(treeStruct)?.right?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullRight = 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 = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = null, actual = root) } @@ -176,15 +176,15 @@ class BINStructTest { } 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 node10 = treeW.getPrivateNode(treeStruct)?.right?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val nodeNullRight = 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 = 10, actual = node10) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 3, actual = root) } @@ -196,15 +196,15 @@ class BINStructTest { } 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 node10 = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val nodeNullRight = 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 = 10, actual = node10) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 15, actual = root) } @@ -216,14 +216,14 @@ class BINStructTest { } 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 node1 = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val nodeNullRight = 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 = 1, actual = node1) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 3, actual = root) } @@ -235,14 +235,14 @@ class BINStructTest { } 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 node6 = treeW.getPrivateNode(treeStruct)?.right?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val nodeNullRight = 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 = 6, actual = node6) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 3, actual = root) } @@ -254,13 +254,13 @@ class BINStructTest { } 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 + val node6 = treeW.getPrivateNode(treeStruct)?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullRight = 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) + assertEquals(expected = 6, actual = node6) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) } @Test @@ -270,22 +270,22 @@ class BINStructTest { 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 node6 = treeW.getPrivateNode(treeStruct)?.left?.value + val node3 = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val nodeNullRight = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val node1 = treeW.getPrivateNode(treeStruct)?.left?.left?.left?.value + val node2 = treeW.getPrivateNode(treeStruct)?.left?.left?.left?.right?.value + val node5 = treeW.getPrivateNode(treeStruct)?.left?.left?.right?.value + val node4 = 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 = 6, actual = node6) + assertEquals(expected = 3, actual = node3) + assertEquals(expected = null, actual = nodeNullRight) + assertEquals(expected = 1, actual = node1) + assertEquals(expected = 2, actual = node2) + assertEquals(expected = 5, actual = node5) + assertEquals(expected = 4, actual = node4) assertEquals(expected = 10, actual = root) } @@ -298,16 +298,16 @@ class BINStructTest { 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 + val node1 = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val nodeNullRight1 = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val nodeNullRightRoot = 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) + assertEquals(expected = node1, actual = 1) + assertEquals(expected = nodeNullLeft, actual = null) + assertEquals(expected = nodeNullRight1, actual = null) + assertEquals(expected = nodeNullRightRoot, actual = null) } @Test @@ -319,22 +319,22 @@ class BINStructTest { 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 + val node5 = treeW.getPrivateNode(treeStruct)?.left?.value + val node4 = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val node6 = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val nodeNullLeft4 = treeW.getPrivateNode(treeStruct)?.left?.left?.left?.value + val nodeNullRight4 = treeW.getPrivateNode(treeStruct)?.left?.left?.right?.value + val nodeNullLeft6 = treeW.getPrivateNode(treeStruct)?.left?.right?.left?.value + val nodeNullRight6 = 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) + assertEquals(expected = node5, actual = 5) + assertEquals(expected = node4, actual = 4) + assertEquals(expected = node6, actual = 6) + assertEquals(expected = nodeNullLeft4, actual = null) + assertEquals(expected = nodeNullRight4, actual = null) + assertEquals(expected = nodeNullLeft6, actual = null) + assertEquals(expected = nodeNullRight6, actual = null) } @Test @@ -348,16 +348,16 @@ class BINStructTest { 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 + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val node10 = treeW.getPrivateNode(treeStruct)?.right?.value + val nodeNullRight10 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val nodeNullLeft10 = 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) + assertEquals(expected = node10, actual = 10) + assertEquals(expected = nodeNullLeft10, actual = null) + assertEquals(expected = nodeNullRight10, actual = null) + assertEquals(expected = nodeNullLeft, actual = null) } @Test @@ -370,20 +370,20 @@ class BINStructTest { 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 + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val node9 = treeW.getPrivateNode(treeStruct)?.right?.value + val node7 = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node10 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node12 = treeW.getPrivateNode(treeStruct)?.right?.right?.right?.value + val nodeNull = 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) + assertEquals(expected = node9, actual = 9) + assertEquals(expected = nodeNull, actual = null) + assertEquals(expected = nodeNullLeft, actual = null) + assertEquals(expected = node7, actual = 7) + assertEquals(expected = node10, actual = 10) + assertEquals(expected = node12, actual = 12) } @Test @@ -396,16 +396,16 @@ class BINStructTest { 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 + val node7 = treeW.getPrivateNode(treeStruct)?.left?.value + val node10 = treeW.getPrivateNode(treeStruct)?.right?.value + val node12 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val nodeNull = 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) + assertEquals(expected = nodeNull, actual = null) + assertEquals(expected = node7, actual = 7) + assertEquals(expected = node10, actual = 10) + assertEquals(expected = node12, actual = 12) } @Test @@ -418,18 +418,18 @@ class BINStructTest { 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 + val node7 = treeW.getPrivateNode(treeStruct)?.left?.value + val node12 = treeW.getPrivateNode(treeStruct)?.right?.value + val node13 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node14 = treeW.getPrivateNode(treeStruct)?.right?.right?.right?.value + val nodeNull = 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) + assertEquals(expected = nodeNull, actual = null) + assertEquals(expected = node7, actual = 7) + assertEquals(expected = node13, actual = 13) + assertEquals(expected = node14, actual = 14) + assertEquals(expected = node12, actual = 12) } @Test @@ -442,20 +442,20 @@ class BINStructTest { 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 + val node13 = treeW.getPrivateNode(treeStruct)?.right?.value + val node15 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node10 = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node11 = treeW.getPrivateNode(treeStruct)?.right?.left?.right?.value + val node9 = treeW.getPrivateNode(treeStruct)?.right?.left?.left?.value + val nodeNull = 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) + assertEquals(expected = node10, actual = 10) + assertEquals(expected = node11, actual = 11) + assertEquals(expected = node13, actual = 13) + assertEquals(expected = node9, actual = 9) + assertEquals(expected = node15, actual = 15) + assertEquals(expected = nodeNull, actual = null) } @Test @@ -468,14 +468,14 @@ class BINStructTest { 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 + val node7 = treeW.getPrivateNode(treeStruct)?.left?.value + val node6 = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val nodeNull = 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) + assertEquals(expected = nodeNull, actual = null) + assertEquals(expected = node7, actual = 7) + assertEquals(expected = node6, actual = 6) } @Test From 139933ba5cc6af256f77b86687d57d0684a20c07 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Tue, 2 May 2023 19:48:42 +0300 Subject: [PATCH 192/212] fix: add exclude for tests --- lib/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 1f19090..1ca0016 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -110,7 +110,7 @@ tasks.jacocoTestReport { tasks.jacocoTestCoverageVerification { classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class") - exclude("**/commonObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class", "**/BINStruct.class", "**/AVLStruct.class", "**/RBStruct.class", "**/TreeStruct.class") + exclude("**/singleObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class", "**/BINStruct.class", "**/AVLStruct.class", "**/RBStruct.class", "**/TreeStruct.class") } }) dependsOn(tasks.jacocoTestReport) violationRules { From 17320d90fddea707eb42abddd60cc512edb6a808 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 2 May 2023 21:24:07 +0300 Subject: [PATCH 193/212] fix: Bugs after remove in RBTree :) --- .../main/kotlin/treelib/rbTree/RBBalancer.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 3aa22cb..4923c1e 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -120,6 +120,30 @@ class RBBalancer>(var root: RBNode?) : } + node.left?.color == Markers.RED && node.right?.color == Markers.RED -> { + if (nodeIsLeaf(node.right)) { + node.right?.color = Markers.BLACK + } + else { + node.left?.color = Markers.BLACK + } + getRoot(node) + } + + node.left?.color == Markers.RED -> { + if (node.right?.right?.color != Markers.BLACK && node.right?.left?.color != Markers.BLACK) { + node.left?.color = Markers.BLACK + } + getRoot(node) + } + + node.right?.color == Markers.RED -> { + if (node.left?.right?.color != Markers.BLACK && node.left?.left?.color != Markers.BLACK) { + node.right?.color = Markers.BLACK + } + getRoot(node) + } + else -> throw IllegalStateException() } } From b91fe37738e7b5eb6b2cb3e5a2412baefeb6e580 Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 2 May 2023 22:41:55 +0300 Subject: [PATCH 194/212] feat: Add button for back to root --- lib/src/main/kotlin/ui/ControlFields.kt | 81 +++++++++++++++++- lib/src/main/kotlin/ui/FileDialog.kt | 3 +- .../main/resources/drawable/go-back-arrow.png | Bin 0 -> 468 bytes 3 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 lib/src/main/resources/drawable/go-back-arrow.png diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt index 470b38d..8c22d02 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -1,15 +1,28 @@ package ui +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.input.key.* +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.onPointerEvent +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Popup import controller.Controller import viewPart.nodes.drawableTree.DrawTree @@ -25,20 +38,36 @@ fun ControlFields( val addFieldState = remember { mutableStateOf(false) } val findFieldState = remember { mutableStateOf(false) } val deleteFieldState = remember { mutableStateOf(false) } + val returnButtonClickState = remember { mutableStateOf(false) } + val returnButtonHoverState = remember { mutableStateOf(false) } + val value = remember { mutableStateOf("") } - Column(modifier = Modifier.offset(0.dp, 0.dp).padding(horizontal = 10.dp)) { - // add + Column( + modifier = Modifier.offset(0.dp, 0.dp).padding(horizontal = 10.dp), + horizontalAlignment = Alignment.Start, + //verticalArrangement = Arrangement.Top + ) { ControlField("Add", addFieldState, value, activeTree) Spacer(modifier = Modifier.height(2.dp)) - // find ControlField("Find", findFieldState, value, activeTree) Spacer(modifier = Modifier.height(2.dp)) - // delete ControlField("Delete", deleteFieldState, value, activeTree) + Spacer(modifier = Modifier.height(5.dp)) + + ReturnButton( + returnButtonHoverState, + returnButtonClickState, + painterResource("drawable/go-back-arrow.png"), + MaterialTheme.colorScheme.onPrimary, + MaterialTheme.colorScheme.background, + MaterialTheme.colorScheme.primary, + 50.dp, 50.dp, + CircleShape + ) } var tree by remember { mutableStateOf(null) } @@ -56,6 +85,11 @@ fun ControlFields( tree = controller.tree createTreeState.value = false } + if (returnButtonClickState.value) { + tree?.updateTree() + tree?.repositisonTree(800f, 10f) + returnButtonClickState.value = false + } tree?.displayTree() addFieldState.value = false @@ -133,3 +167,42 @@ fun ControlField( } +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun ReturnButton( + hoverButtonState: MutableState, + clickButtonState: MutableState, + painter: Painter, + hoverColor: Color, + color: Color, + iconColor: Color, + height: Dp, + width: Dp, + shape: Shape, +) { + val interactionSource = remember { MutableInteractionSource() } + + Row() { + IconButton( + onClick = { clickButtonState.value = !clickButtonState.value }, + modifier = Modifier.height(height).width(width) + .onPointerEvent(PointerEventType.Enter) { hoverButtonState.value = true } + .onPointerEvent(PointerEventType.Exit) { hoverButtonState.value = false } + .clickable(indication = null, interactionSource = interactionSource) {} + .clip(shape), + colors = IconButtonDefaults.iconButtonColors( + contentColor = if (hoverButtonState.value) iconColor else MaterialTheme.colorScheme.background, + containerColor = if (hoverButtonState.value) color else hoverColor + ) + ) { + Icon(painter, contentDescription = null) + } + + if (hoverButtonState.value) { + Popup(offset = IntOffset(x = 60, y = 50)) { + Text(text = "Back to root") + } + } + } +} + diff --git a/lib/src/main/kotlin/ui/FileDialog.kt b/lib/src/main/kotlin/ui/FileDialog.kt index b9da6ea..cb73ec9 100644 --- a/lib/src/main/kotlin/ui/FileDialog.kt +++ b/lib/src/main/kotlin/ui/FileDialog.kt @@ -148,7 +148,7 @@ fun CompleteDialogContent( horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.Bottom ) { - BottomButtons(dialogState, dialogState,"Ok", "Cancel") // поменять !! + BottomButtons(dialogState, dialogState,"Ok", "Cancel") } @@ -194,7 +194,6 @@ fun BottomButtons(dialogState: MutableState, firstButtonState: MutableS shape = RoundedCornerShape(3.dp), onClick = { firstButtonState.value = !firstButtonState.value - //dialogState.value = false }, colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.secondary, diff --git a/lib/src/main/resources/drawable/go-back-arrow.png b/lib/src/main/resources/drawable/go-back-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..97d8c5bb7af53733139582207af86fe66a1096ae GIT binary patch literal 468 zcmV;_0W1EAP)ZrDUD; zV6EvJ1&D}tL^SUm5D~SA=;1S-E()m62yfvCPllHk1ysI4M07C7!k4NEKor%*3AR=V zhse)CImAAmSLi?DE3WVp-|;0)J4`7h<^9LFL|U-Vae(b%R^G$#eOxaQp7&q8!^05( z4p$5w?*SeS0qZ!)yZ8_hMfN=|(Rg?N&tSzNSM@K{jSZxU<0+;K#rFI+C*;}Wwkkdc zUB#{#H@qx04eVeKukao>cj)^QfDF_rpLPOJp>N?0eq{Pjm4M82Iu9W8H1M|6f14^U z4K?)2b$>BSpZQ+n&&aoET17+~XDOxMa~TRxXKui Date: Tue, 2 May 2023 23:26:29 +0300 Subject: [PATCH 195/212] refactor: Names in test methods --- lib/build.gradle.kts | 2 +- lib/src/main/kotlin/controller/Controller.kt | 4 +- .../databaseSave/neo4j/Neo4jRepository.kt | 4 +- lib/src/main/kotlin/ui/ControlFields.kt | 5 +- lib/src/main/kotlin/ui/FileDialog.kt | 1 + .../kotlin/viewPart/nodes/TreeDrawingUtils.kt | 33 +- .../viewPart/nodes/drawableTree/DrawTree.kt | 6 +- .../nodes/drawableTree/DrawableTree.kt | 16 +- lib/src/test/kotlin/treelib/BINStructTest.kt | 308 +++++++++--------- lib/src/test/kotlin/treelib/RBStructTest.kt | 8 +- .../test/kotlin/utils/TreeStructWrapper.kt | 3 +- lib/src/test/kotlin/utils/TreeWrapper.kt | 1 + 12 files changed, 201 insertions(+), 190 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 25447de..1c0fc95 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -110,7 +110,7 @@ tasks.jacocoTestReport { tasks.jacocoTestCoverageVerification { classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class") - exclude("**/commonObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class", "**/BINStruct.class", "**/AVLStruct.class", "**/RBStruct.class", "**/TreeStruct.class") + exclude("**/singleObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class", "**/BINStruct.class", "**/AVLStruct.class", "**/RBStruct.class", "**/TreeStruct.class") } }) dependsOn(tasks.jacocoTestReport) violationRules { diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index ea00760..0709f84 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -76,7 +76,7 @@ class Controller { tree?.insert(Container(Pair(key, value))) ?: throw NullPointerException() tree?.updateTree() ?: throw NullPointerException() - tree?.repositisonTree(800f, 10f) ?: throw NullPointerException() + tree?.repositionTree(800f, 10f) ?: throw NullPointerException() return tree ?: throw NullPointerException() } @@ -95,7 +95,7 @@ class Controller { tree?.delete(Container(Pair(key, ""))) ?: throw NullPointerException() tree?.updateTree() ?: throw NullPointerException() - tree?.repositisonTree(800f, 10f) ?: throw NullPointerException() + tree?.repositionTree(800f, 10f) ?: throw NullPointerException() return tree ?: throw NullPointerException() } diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt index 13161a9..86ac99e 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt @@ -10,7 +10,9 @@ import treelib.commonObjects.Container import treelib.rbTree.Markers import java.io.Closeable import java.io.IOException -import java.util.* +import java.util.LinkedList +import kotlin.collections.HashSet + class Neo4jRepository : Closeable { diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt index 8c22d02..d8ba5ea 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Popup import controller.Controller +import viewPart.nodes.displayTree import viewPart.nodes.drawableTree.DrawTree @Composable @@ -87,10 +88,10 @@ fun ControlFields( } if (returnButtonClickState.value) { tree?.updateTree() - tree?.repositisonTree(800f, 10f) + tree?.repositionTree(800f, 10f) returnButtonClickState.value = false } - tree?.displayTree() + tree?.let { displayTree(it) } addFieldState.value = false } diff --git a/lib/src/main/kotlin/ui/FileDialog.kt b/lib/src/main/kotlin/ui/FileDialog.kt index cb73ec9..1a915f6 100644 --- a/lib/src/main/kotlin/ui/FileDialog.kt +++ b/lib/src/main/kotlin/ui/FileDialog.kt @@ -195,6 +195,7 @@ fun BottomButtons(dialogState: MutableState, firstButtonState: MutableS onClick = { firstButtonState.value = !firstButtonState.value }, + contentPadding = PaddingValues(start = 2.dp, end = 2.dp), colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.secondary, contentColor = MaterialTheme.colorScheme.background diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index 87a45be..c4a7ff2 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -18,16 +18,32 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import treelib.commonObjects.Container import viewPart.nodes.drawableAVL.AVLDrawableNode +import viewPart.nodes.drawableAVL.AVLDrawableTree +import viewPart.nodes.drawableAVL.AVLNodeDesign import viewPart.nodes.drawableBIN.BINDrawableNode +import viewPart.nodes.drawableBIN.BINDrawableTree +import viewPart.nodes.drawableBIN.BINNodeDesign import viewPart.nodes.drawableRB.RBDrawableNode +import viewPart.nodes.drawableRB.RBDrawableTree +import viewPart.nodes.drawableRB.RBNodeDesign +import viewPart.nodes.drawableTree.DrawTree import viewPart.nodes.drawableTree.DrawableNode import viewPart.nodes.drawableTree.NodeDesign import java.util.* import kotlin.math.roundToInt -/* @Composable -fun displayTree(tree: RBDrawableTree) { +fun displayTree(tree: DrawTree){ + when(tree){ + is BINDrawableTree -> displayBIN(tree) + is RBDrawableTree -> displayRB(tree) + is AVLDrawableTree -> displayAVL(tree) + else -> throw NullPointerException("Wrong DrawableTree type") + } +} + +@Composable +fun displayRB(tree: RBDrawableTree) { val root = tree.root root?.let { displayNode(it, RBNodeDesign) @@ -35,7 +51,7 @@ fun displayTree(tree: RBDrawableTree) { } @Composable -fun displayTree(tree: AVLDrawableTree) { +fun displayAVL(tree: AVLDrawableTree) { val root = tree.root root?.let { displayNode(it, AVLNodeDesign) @@ -43,13 +59,14 @@ fun displayTree(tree: AVLDrawableTree) { } @Composable -fun displayTree(tree: BINDrawableTree) { +fun displayBIN(tree: BINDrawableTree) { val root = tree.root root?.let { displayNode(it, BINNodeDesign) } } -*/ + + @Composable fun , DNode>, NodeD : NodeDesign> displayNode(node: DNode, design: NodeD) { @@ -70,12 +87,14 @@ fun , DNode>, NodeD : NodeDesign> di Text(text = "key: ${node.value.key} ", maxLines = 1, overflow = TextOverflow.Ellipsis) Text(text = "value: ${node.value.value}", maxLines = 2, overflow = TextOverflow.Ellipsis) when (node ) { +// Column(modifier = Modifier.zIndex(1f)) { +// Text(text = "key: ${node.value.key}") +// Text(text = "value: ${node.value.value}") +// when (node) { is AVLDrawableNode<*> -> Text(text = "height: ${node.height}") is RBDrawableNode<*> -> Text(text = "color: ${node.color.toString().lowercase(Locale.getDefault())}") } - } - } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt index cfbdd7c..af823c4 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt @@ -1,6 +1,5 @@ package viewPart.nodes.drawableTree -import androidx.compose.runtime.Composable import treelib.commonObjects.Container interface DrawTree { @@ -31,9 +30,6 @@ interface DrawTree { val designNode: NodeDesign var yShiftBetweenNodes: Float - @Composable - fun displayTree() - fun initTree() fun updateTree() fun deleteTree() @@ -42,5 +38,5 @@ interface DrawTree { fun insert(item: Container) fun delete(item: Container) fun find(item: Int) - fun repositisonTree(xBase: Float, yBase: Float) + fun repositionTree(xBase: Float, yBase: Float) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index d649578..5118431 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -1,6 +1,5 @@ package viewPart.nodes.drawableTree -import androidx.compose.runtime.Composable import databaseManage.TreeManager import databaseSave.DrawableVertex import treelib.abstractTree.Node @@ -9,7 +8,6 @@ import treelib.abstractTree.TreeStruct import treelib.abstractTree.Vertex import treelib.commonObjects.Container import treelib.commonObjects.exceptions.ImpossibleCaseException -import viewPart.nodes.displayNode abstract class DrawableTree< DNodeType : DrawableNode, DNodeType>, @@ -23,17 +21,10 @@ abstract class DrawableTree< protected abstract var drawablePreOrder: List? protected abstract val treeManager: TreeManager, DVertexType, NodeType, State, VertexType, StructType> protected abstract var treeStruct: StructType - protected abstract var root: DNodeType? + abstract var root: DNodeType? override var yShiftBetweenNodes = 10f - @Composable - override fun displayTree() { - root?.let { - displayNode(it, designNode) - } - } - override fun initTree() { val binVertexes = treeManager.initTree(name, treeStruct) drawablePreOrder = binVertexes.map { drawableVertexToNode(it) } @@ -84,10 +75,9 @@ abstract class DrawableTree< } if (currentNode == null) return } - } - override fun repositisonTree(xBase: Float, yBase: Float) { + override fun repositionTree(xBase: Float, yBase: Float) { /*xBase: Float = 0f, yBase: Float = 0f*/ root?.let { createCordsState1(it, xBase, yBase) @@ -115,7 +105,7 @@ abstract class DrawableTree< } var currentParent = root while (currentParent != null) { - currentParent?.let { + currentParent.let { when { it.value < preOrderNode.value -> { if (it.rightChild == null) { diff --git a/lib/src/test/kotlin/treelib/BINStructTest.kt b/lib/src/test/kotlin/treelib/BINStructTest.kt index 07ab528..2213b19 100644 --- a/lib/src/test/kotlin/treelib/BINStructTest.kt +++ b/lib/src/test/kotlin/treelib/BINStructTest.kt @@ -12,8 +12,8 @@ import kotlin.test.assertEquals @DisplayName("Test: Binary Search Tree Struct") class BINStructTest { - val treeW = TreeStructWrapper, BINVertex, BINStateContainer, BINStruct>() - var treeStruct = BINStruct() + private val treeW = TreeStructWrapper, BINVertex, BINStateContainer, BINStruct>() + private var treeStruct = BINStruct() private fun testAssert(msg: String): Nothing = fail(msg) @@ -43,24 +43,24 @@ class BINStructTest { treeStruct.insert(i) } - val additional_num = mutableListOf(1, 2, 3, 5, 7, 8, 11) - for (i in additional_num) { + val additionalNum = mutableListOf(1, 2, 3, 5, 7, 8, 11) + for (i in additionalNum) { 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 + val node2 = treeW.getPrivateNode(treeStruct)?.right?.value + val nodeNull = treeW.getPrivateNode(treeStruct)?.left?.value + val node3 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val nodeNull1 = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node4 = 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) + assertEquals(expected = nodeNull, actual = null) + assertEquals(expected = node4, actual = 4) + assertEquals(expected = node2, actual = 2) + assertEquals(expected = node3, actual = 3) + assertEquals(expected = nodeNull1, actual = null) } @Test @@ -102,19 +102,19 @@ class BINStructTest { treeStruct.delete(1) - val additional_num = mutableListOf(1, 2, 11) - for (i in additional_num) { + val additionalNum = mutableListOf(1, 2, 11) + for (i in additionalNum) { 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 + val node1 = treeW.getPrivateNode(treeStruct)?.left?.value + val node11 = treeW.getPrivateNode(treeStruct)?.right?.value assertEquals(expected = root, actual = 2) - assertEquals(expected = node_1, actual = 1) - assertEquals(expected = node_11, actual = 11) + assertEquals(expected = node1, actual = 1) + assertEquals(expected = node11, actual = 11) } @Test @@ -126,11 +126,11 @@ class BINStructTest { 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 + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullRight = treeW.getPrivateNode(treeStruct)?.right?.value - assertEquals(expected = null, actual = node_null_left) - assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 5, actual = root) } @@ -143,11 +143,11 @@ class BINStructTest { 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 + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullRight = treeW.getPrivateNode(treeStruct)?.right?.value - assertEquals(expected = null, actual = node_null_left) - assertEquals(expected = null, actual = node_null_right) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 6, actual = root) } @@ -159,12 +159,12 @@ class BINStructTest { } treeStruct.delete(3) - val node_null_left = treeW.getPrivateNode(treeStruct)?.left?.value - val node_null_right = treeW.getPrivateNode(treeStruct)?.right?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullRight = 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 = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = null, actual = root) } @@ -176,15 +176,15 @@ class BINStructTest { } 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 node10 = treeW.getPrivateNode(treeStruct)?.right?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val nodeNullRight = 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 = 10, actual = node10) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 3, actual = root) } @@ -196,15 +196,15 @@ class BINStructTest { } 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 node10 = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val nodeNullRight = 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 = 10, actual = node10) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 15, actual = root) } @@ -216,14 +216,14 @@ class BINStructTest { } 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 node1 = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val nodeNullRight = 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 = 1, actual = node1) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 3, actual = root) } @@ -235,14 +235,14 @@ class BINStructTest { } 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 node6 = treeW.getPrivateNode(treeStruct)?.right?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val nodeNullRight = 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 = 6, actual = node6) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) assertEquals(expected = 3, actual = root) } @@ -254,13 +254,13 @@ class BINStructTest { } 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 + val node6 = treeW.getPrivateNode(treeStruct)?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullRight = 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) + assertEquals(expected = 6, actual = node6) + assertEquals(expected = null, actual = nodeNullLeft) + assertEquals(expected = null, actual = nodeNullRight) } @Test @@ -270,22 +270,22 @@ class BINStructTest { 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 node6 = treeW.getPrivateNode(treeStruct)?.left?.value + val node3 = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val nodeNullRight = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val node1 = treeW.getPrivateNode(treeStruct)?.left?.left?.left?.value + val node2 = treeW.getPrivateNode(treeStruct)?.left?.left?.left?.right?.value + val node5 = treeW.getPrivateNode(treeStruct)?.left?.left?.right?.value + val node4 = 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 = 6, actual = node6) + assertEquals(expected = 3, actual = node3) + assertEquals(expected = null, actual = nodeNullRight) + assertEquals(expected = 1, actual = node1) + assertEquals(expected = 2, actual = node2) + assertEquals(expected = 5, actual = node5) + assertEquals(expected = 4, actual = node4) assertEquals(expected = 10, actual = root) } @@ -298,16 +298,16 @@ class BINStructTest { 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 + val node1 = treeW.getPrivateNode(treeStruct)?.left?.value + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val nodeNullRight1 = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val nodeNullRightRoot = 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) + assertEquals(expected = node1, actual = 1) + assertEquals(expected = nodeNullLeft, actual = null) + assertEquals(expected = nodeNullRight1, actual = null) + assertEquals(expected = nodeNullRightRoot, actual = null) } @Test @@ -319,22 +319,22 @@ class BINStructTest { 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 + val node5 = treeW.getPrivateNode(treeStruct)?.left?.value + val node4 = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val node6 = treeW.getPrivateNode(treeStruct)?.left?.right?.value + val nodeNullLeft4 = treeW.getPrivateNode(treeStruct)?.left?.left?.left?.value + val nodeNullRight4 = treeW.getPrivateNode(treeStruct)?.left?.left?.right?.value + val nodeNullLeft6 = treeW.getPrivateNode(treeStruct)?.left?.right?.left?.value + val nodeNullRight6 = 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) + assertEquals(expected = node5, actual = 5) + assertEquals(expected = node4, actual = 4) + assertEquals(expected = node6, actual = 6) + assertEquals(expected = nodeNullLeft4, actual = null) + assertEquals(expected = nodeNullRight4, actual = null) + assertEquals(expected = nodeNullLeft6, actual = null) + assertEquals(expected = nodeNullRight6, actual = null) } @Test @@ -348,16 +348,16 @@ class BINStructTest { 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 + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val node10 = treeW.getPrivateNode(treeStruct)?.right?.value + val nodeNullRight10 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val nodeNullLeft10 = 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) + assertEquals(expected = node10, actual = 10) + assertEquals(expected = nodeNullLeft10, actual = null) + assertEquals(expected = nodeNullRight10, actual = null) + assertEquals(expected = nodeNullLeft, actual = null) } @Test @@ -370,20 +370,20 @@ class BINStructTest { 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 + val nodeNullLeft = treeW.getPrivateNode(treeStruct)?.left?.value + val node9 = treeW.getPrivateNode(treeStruct)?.right?.value + val node7 = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node10 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node12 = treeW.getPrivateNode(treeStruct)?.right?.right?.right?.value + val nodeNull = 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) + assertEquals(expected = node9, actual = 9) + assertEquals(expected = nodeNull, actual = null) + assertEquals(expected = nodeNullLeft, actual = null) + assertEquals(expected = node7, actual = 7) + assertEquals(expected = node10, actual = 10) + assertEquals(expected = node12, actual = 12) } @Test @@ -396,16 +396,16 @@ class BINStructTest { 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 + val node7 = treeW.getPrivateNode(treeStruct)?.left?.value + val node10 = treeW.getPrivateNode(treeStruct)?.right?.value + val node12 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val nodeNull = 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) + assertEquals(expected = nodeNull, actual = null) + assertEquals(expected = node7, actual = 7) + assertEquals(expected = node10, actual = 10) + assertEquals(expected = node12, actual = 12) } @Test @@ -418,18 +418,18 @@ class BINStructTest { 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 + val node7 = treeW.getPrivateNode(treeStruct)?.left?.value + val node12 = treeW.getPrivateNode(treeStruct)?.right?.value + val node13 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node14 = treeW.getPrivateNode(treeStruct)?.right?.right?.right?.value + val nodeNull = 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) + assertEquals(expected = nodeNull, actual = null) + assertEquals(expected = node7, actual = 7) + assertEquals(expected = node13, actual = 13) + assertEquals(expected = node14, actual = 14) + assertEquals(expected = node12, actual = 12) } @Test @@ -442,20 +442,20 @@ class BINStructTest { 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 + val node13 = treeW.getPrivateNode(treeStruct)?.right?.value + val node15 = treeW.getPrivateNode(treeStruct)?.right?.right?.value + val node10 = treeW.getPrivateNode(treeStruct)?.right?.left?.value + val node11 = treeW.getPrivateNode(treeStruct)?.right?.left?.right?.value + val node9 = treeW.getPrivateNode(treeStruct)?.right?.left?.left?.value + val nodeNull = 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) + assertEquals(expected = node10, actual = 10) + assertEquals(expected = node11, actual = 11) + assertEquals(expected = node13, actual = 13) + assertEquals(expected = node9, actual = 9) + assertEquals(expected = node15, actual = 15) + assertEquals(expected = nodeNull, actual = null) } @Test @@ -468,14 +468,14 @@ class BINStructTest { 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 + val node7 = treeW.getPrivateNode(treeStruct)?.left?.value + val node6 = treeW.getPrivateNode(treeStruct)?.left?.left?.value + val nodeNull = 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) + assertEquals(expected = nodeNull, actual = null) + assertEquals(expected = node7, actual = 7) + assertEquals(expected = node6, actual = 6) } @Test diff --git a/lib/src/test/kotlin/treelib/RBStructTest.kt b/lib/src/test/kotlin/treelib/RBStructTest.kt index 5b153bb..6a941de 100644 --- a/lib/src/test/kotlin/treelib/RBStructTest.kt +++ b/lib/src/test/kotlin/treelib/RBStructTest.kt @@ -116,8 +116,8 @@ class RBStructTest { } @Test - fun `fazzer test`() { - val fazzer = RBStructFuzzer( + fun `fuzzer test`() { + val fuzzer = RBStructFuzzer( arrayOf( 1, 2, @@ -141,11 +141,11 @@ class RBStructTest { 2754 ), ::testAssert ) - fazzer.saveNextTestSets("TEST_TEST") + fuzzer.saveNextTestSets("TEST_TEST") assertAll( { - fazzer.fuzzInvariantInsert(15, 10) + fuzzer.fuzzInvariantInsert(15, 10) } ) } diff --git a/lib/src/test/kotlin/utils/TreeStructWrapper.kt b/lib/src/test/kotlin/utils/TreeStructWrapper.kt index ba9fb10..c0e05bb 100644 --- a/lib/src/test/kotlin/utils/TreeStructWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeStructWrapper.kt @@ -5,6 +5,7 @@ import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct import treelib.abstractTree.Vertex +@Suppress("UNCHECKED_CAST") class TreeStructWrapper, NodeType : Node, VertexType : Vertex, State : StateContainer, TStruct : TreeStruct> { fun getPrivateNode(tree: TStruct, name: String = "root"): NodeType? { @@ -26,4 +27,4 @@ class TreeStructWrapper, NodeType : Node, VertexT return if (parameterValues != null) method.invoke(tree, *parameterValues) else method.invoke(tree) } -} \ No newline at end of file +} diff --git a/lib/src/test/kotlin/utils/TreeWrapper.kt b/lib/src/test/kotlin/utils/TreeWrapper.kt index a5b5d8f..e8f8285 100644 --- a/lib/src/test/kotlin/utils/TreeWrapper.kt +++ b/lib/src/test/kotlin/utils/TreeWrapper.kt @@ -4,6 +4,7 @@ import treelib.abstractTree.* import treelib.commonObjects.Container +@Suppress("UNCHECKED_CAST") class TreeWrapper< V : Comparable, Value, From f692d1e447e83817e71313e70c144a292c44058d Mon Sep 17 00:00:00 2001 From: Georgy S Date: Tue, 2 May 2023 21:11:54 +0300 Subject: [PATCH 196/212] fix: Bug in saving binary tree (BINTreeManager). --- lib/src/main/kotlin/databaseManage/BINTreeManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt index af460e9..d649ffd 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/databaseManage/BINTreeManager.kt @@ -46,7 +46,7 @@ class BINTreeManager : TreeManager< preOrder: List>>, inOrder: List>> ) { - jsonRep.saveChanges(preOrder.toTypedArray(), name) + jsonRep.saveChanges(preOrder.toTypedArray(), "$name.json") } override fun saveTreeToDB(name: String, tree: BINStruct>) { From 7d46ba7842b74492b1cc2aa16f92174cbea31566 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Tue, 2 May 2023 21:17:35 +0300 Subject: [PATCH 197/212] feat: Implement the possibility to scroll. --- .../kotlin/viewPart/nodes/TreeDrawingUtils.kt | 35 ++++++++++++++----- .../nodes/drawableAVL/AVLDrawableTree.kt | 4 +-- .../nodes/drawableRB/RBDrawableTree.kt | 8 +++-- .../viewPart/nodes/drawableTree/DrawTree.kt | 1 + .../nodes/drawableTree/DrawableTree.kt | 13 +++++-- 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index 7f86eba..e465b16 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -3,14 +3,17 @@ package viewPart.nodes import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.border +import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex @@ -32,11 +35,20 @@ import kotlin.math.roundToInt @Composable fun displayTree(tree: DrawTree){ - when(tree){ - is BINDrawableTree -> displayBIN(tree) - is RBDrawableTree -> displayRB(tree) - is AVLDrawableTree -> displayAVL(tree) - else -> throw NullPointerException("Wrong DrawableTree type") + Box(modifier = Modifier.fillMaxSize() + .pointerInput(Unit) { + detectDragGestures { change, dragAmount -> + change.consume() + tree.addOffset(dragAmount.x, dragAmount.y) + } + }.zIndex(-1000f) + ){ + when (tree) { + is BINDrawableTree -> displayBIN(tree) + is RBDrawableTree -> displayRB(tree) + is AVLDrawableTree -> displayAVL(tree) + else -> throw NullPointerException("Wrong DrawableTree type") + } } } @@ -66,7 +78,10 @@ fun displayBIN(tree: BINDrawableTree) { @Composable -fun , DNode>, NodeD : NodeDesign> displayNode(node: DNode, design: NodeD) { +fun , DNode>, NodeD : NodeDesign> displayNode( + node: DNode, + design: NodeD, +) { if (node.clickState.value) { Box(modifier = Modifier @@ -74,7 +89,7 @@ fun , DNode>, NodeD : NodeDesign> di .offset { IntOffset( node.xState.value.roundToInt() + 71, - node.yState.value.roundToInt() + node.yState.value.roundToInt(), ) } .background(color = Color(206, 211, 216), shape = AbsoluteRoundedCornerShape(5.dp)) @@ -107,7 +122,11 @@ fun , DNode>, NodeD : NodeDesign> di } @Composable -fun , NodeD : NodeDesign> edgeView(node: DNode, child: DNode, design: NodeD) { +fun , NodeD : NodeDesign> edgeView( + node: DNode, + child: DNode, + design: NodeD, +) { Canvas(modifier = Modifier.fillMaxSize().zIndex(-1f)) { drawLine( color = design.lineColor, diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt index 345e4f1..f0398cc 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt @@ -28,14 +28,14 @@ class AVLDrawableTree( value = vertex.value, xState = mutableStateOf(vertex.x.toFloat()), yState = mutableStateOf(vertex.y.toFloat()), - height = vertex.height.toInt() + height = vertex.height.toInt(), ) override fun vertexToNode(vertex: AVLVertex>) = AVLDrawableNode( value = vertex.value, xState = mutableStateOf(0f), yState = mutableStateOf(0f), - height = vertex.height.toInt() + height = vertex.height.toInt(), ) override fun nodeToDrawableVertex(node: AVLDrawableNode>) = DrawableAVLVertex( diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt index b222594..d6f4211 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt @@ -22,7 +22,7 @@ class RBDrawableTree( RBStruct> >() { override var root: RBDrawableNode>? = null - override var drawablePreOrder: List>>? = null + override var drawablePreOrder: List>>? = null override var treeStruct = RBStruct>() override val designNode = RBNodeDesign @@ -34,7 +34,11 @@ class RBDrawableTree( override fun saveTreeToDB() { if (root != null) { - treeManager.saveTreeToDB(name, preOrder().map { nodeToDrawableVertex(it) }.toList(), inOrder().map { nodeToDrawableVertex(it) }.toList()) + treeManager.saveTreeToDB( + name, + preOrder().map { nodeToDrawableVertex(it) }.toList(), + inOrder().map { nodeToDrawableVertex(it) }.toList() + ) } else { treeManager.saveTreeToDB(name, treeStruct) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt index af823c4..f3d4b7c 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt @@ -39,4 +39,5 @@ interface DrawTree { fun delete(item: Container) fun find(item: Int) fun repositionTree(xBase: Float, yBase: Float) + fun addOffset(xOffset: Float, yOffset: Float) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index 5118431..a8bd674 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -25,6 +25,17 @@ abstract class DrawableTree< override var yShiftBetweenNodes = 10f + override fun addOffset(xOffset: Float, yOffset: Float) { + root?.let { addSet(it, xOffset, yOffset) } + } + + private fun addSet(root: DNodeType, xOffset: Float, yOffset: Float){ + root.xState.value += xOffset + root.yState.value += yOffset + root.leftChild?.let { addSet(it, xOffset, yOffset) } + root.rightChild?.let { addSet(it, xOffset, yOffset) } + } + override fun initTree() { val binVertexes = treeManager.initTree(name, treeStruct) drawablePreOrder = binVertexes.map { drawableVertexToNode(it) } @@ -122,8 +133,6 @@ abstract class DrawableTree< } else -> { - println(currentParent!!.value) - println(preOrderNode.value) throw InternalError("Can't restore tree from preOrder :(") } } From a84049b4f4aa9e7e237d2ec6f1b7b21866a825aa Mon Sep 17 00:00:00 2001 From: Georgy S Date: Tue, 2 May 2023 22:00:52 +0300 Subject: [PATCH 198/212] fix: Freeze after opening new RBtree in scroll implementation. --- lib/src/main/kotlin/main.kt | 13 +++++++++---- lib/src/main/kotlin/ui/ControlFields.kt | 5 +++-- .../main/kotlin/viewPart/nodes/TreeDrawingUtils.kt | 5 +++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index 24cc37e..35e5cf0 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -1,13 +1,13 @@ +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.onDrag import androidx.compose.foundation.layout.* import androidx.compose.foundation.window.WindowDraggableArea -import androidx.compose.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.key.* import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight @@ -17,7 +17,7 @@ import controller.Controller import ui.* import java.awt.Dimension -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) fun main() = application { @@ -36,6 +36,8 @@ fun main() = application { val openTreeState = remember { mutableStateOf(false) } val createTreeState = remember { mutableStateOf(false) } + val dragState = remember { mutableStateOf(false) } + if (!closeButton.value) { Window( onCloseRequest = ::exitApplication, @@ -76,9 +78,12 @@ fun main() = application { .fillMaxSize() .background(MaterialTheme.colorScheme.background) .offset(0.dp, 50.dp) + .onDrag { + dragState.value = true + } ) { - ControlFields(controller, activeTree, deleteTreeState, openTreeState, createTreeState) + ControlFields(controller, activeTree, deleteTreeState, openTreeState, createTreeState, dragState) } } diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt index b2ba175..a778730 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -33,7 +33,8 @@ fun ControlFields( activeTree: MutableState, deleteTreeState: MutableState, openTreeState: MutableState, - createTreeState: MutableState + createTreeState: MutableState, + dragState: MutableState, ) { val addFieldState = remember { mutableStateOf(false) } @@ -89,7 +90,7 @@ fun ControlFields( tree?.repositionTree(800f, 10f) returnButtonClickState.value = false } - tree?.let { displayTree(it) } + tree?.let { displayTree(it, dragState) } addFieldState.value = false } diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index e465b16..a65e2fe 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -34,12 +34,13 @@ import java.util.Locale import kotlin.math.roundToInt @Composable -fun displayTree(tree: DrawTree){ +fun displayTree(tree: DrawTree, state: MutableState){ Box(modifier = Modifier.fillMaxSize() - .pointerInput(Unit) { + .pointerInput(state) { detectDragGestures { change, dragAmount -> change.consume() tree.addOffset(dragAmount.x, dragAmount.y) + state.value = false } }.zIndex(-1000f) ){ From b2836149264b6d232a2a71f6275f8dafae11560e Mon Sep 17 00:00:00 2001 From: Georgy S Date: Tue, 2 May 2023 22:01:46 +0300 Subject: [PATCH 199/212] refactor: Code style (#26). --- lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt index 4923c1e..6c20f2f 100644 --- a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt +++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt @@ -57,7 +57,7 @@ class RBBalancer>(var root: RBNode?) : } var parent = - currentNode.parent ?: throw IllegalStateException() // в данном случае родитель не может быть null + currentNode.parent ?: throw IllegalStateException() when (parent) { parent.parent?.left -> { if (currentNode == parent.right) { @@ -85,7 +85,7 @@ class RBBalancer>(var root: RBNode?) : currentNode.left?.color = Markers.RED } - else -> throw IllegalStateException() // невозможное условие выполнения + else -> throw IllegalStateException() } if (currentNode.parent == null) root = currentNode From a6c5cd9fbe5ed53a6a6f8d9a25e8dca3944fe290 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Tue, 2 May 2023 22:59:09 +0300 Subject: [PATCH 200/212] refactor: Remove dead-code from NodeDesign (#26). --- lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt | 2 +- .../kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt | 9 +-------- .../kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt | 8 +------- .../kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt | 8 +------- .../kotlin/viewPart/nodes/drawableTree/NodeDesign.kt | 2 +- 5 files changed, 5 insertions(+), 24 deletions(-) diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index a65e2fe..275160f 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -109,7 +109,7 @@ fun , DNode>, NodeD : NodeDesign> di } } - design.infoView(node.value.toString(), node.modifier) + design.infoView(node.modifier) node.leftChild?.let { edgeView(node, it, design) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt index 6b8acd2..b4edcb5 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt @@ -17,12 +17,5 @@ object AVLNodeDesign : NodeDesign { override var lineStrokeWidth = 10f @Composable - override fun infoView(information: String, modifier: Modifier) { - Box( - modifier = modifier, - contentAlignment = Alignment.Center - ) { - - } - } + override fun infoView(modifier: Modifier) = Box(modifier = modifier) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt index 2248abd..2c96e81 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt @@ -16,11 +16,5 @@ object BINNodeDesign: NodeDesign { override var shape: Shape = CircleShape override var lineStrokeWidth = 10f @Composable - override fun infoView(information: String, modifier: Modifier) { - Box( - modifier = modifier, - contentAlignment = Alignment.Center - ) { - } - } + override fun infoView(modifier: Modifier) = Box(modifier = modifier) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt index c5b7ed7..6c253e0 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt @@ -19,11 +19,5 @@ object RBNodeDesign: NodeDesign { override var lineColor = Color(34, 35, 41) @Composable - override fun infoView(information: String, modifier: Modifier) { - Box( - modifier = modifier, - contentAlignment = Alignment.Center - ) { - } - } + override fun infoView(modifier: Modifier) = Box(modifier = modifier) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt index 9ae741d..f555001 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt @@ -13,5 +13,5 @@ interface NodeDesign { var lineColor: Color @Composable - fun infoView(information: String, modifier: Modifier) + fun infoView(modifier: Modifier) } From ae36e094c34f184edd847b9009cbe013956fc68c Mon Sep 17 00:00:00 2001 From: Georgy S Date: Tue, 2 May 2023 23:56:45 +0300 Subject: [PATCH 201/212] fix: Freeze in node drawing (#24). --- .../viewPart/nodes/drawableAVL/AVLDrawableNode.kt | 6 +++--- .../viewPart/nodes/drawableBIN/BINDrawableNode.kt | 6 +++--- .../viewPart/nodes/drawableRB/RBDrawableNode.kt | 6 +++--- .../viewPart/nodes/drawableTree/DrawableNode.kt | 11 +++++++---- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt index 40b5866..34b8d30 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt @@ -9,8 +9,8 @@ class AVLDrawableNode( override var leftChild: AVLDrawableNode? = null, override var rightChild: AVLDrawableNode? = null, val height: Int, - override val xState: MutableState, - override val yState: MutableState, -) : DrawableNode>(AVLNodeDesign, AVLNodeDesign.colorNode) { + xState: MutableState, + yState: MutableState, +) : DrawableNode>(AVLNodeDesign, AVLNodeDesign.colorNode, xState, yState) { override val clickState = mutableStateOf(false) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt index 43b3a6b..e9bfa26 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt @@ -8,10 +8,10 @@ class BINDrawableNode( override val value: Pack, override var leftChild: BINDrawableNode? = null, override var rightChild: BINDrawableNode? = null, - override var xState: MutableState, - override var yState: MutableState, + xState: MutableState, + yState: MutableState, - ) : DrawableNode>(BINNodeDesign, BINNodeDesign.colorNode) { + ) : DrawableNode>(BINNodeDesign, BINNodeDesign.colorNode, xState, yState) { override val clickState = mutableStateOf(false) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt index eef520b..ab44640 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt @@ -10,8 +10,8 @@ class RBDrawableNode( override var leftChild: RBDrawableNode? = null, override var rightChild: RBDrawableNode? = null, val color: Markers, - override val xState: MutableState, - override val yState: MutableState, -) : DrawableNode>(RBNodeDesign, if (color == Markers.BLACK) RBNodeDesign.blackMarker else RBNodeDesign.redMarker) { + xState: MutableState, + yState: MutableState, +) : DrawableNode>(RBNodeDesign, if (color == Markers.BLACK) RBNodeDesign.blackMarker else RBNodeDesign.redMarker, xState, yState) { override val clickState = mutableStateOf(false) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt index c2ac7c4..87ce044 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt @@ -15,12 +15,15 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import kotlin.math.roundToInt -abstract class DrawableNode>(design : NodeDesign, color : Color) { +abstract class DrawableNode>( + design : NodeDesign, + color : Color, + val xState: MutableState, + val yState: MutableState, +) { abstract val value: Pack abstract var leftChild: NodeType? abstract var rightChild: NodeType? - abstract val xState: MutableState - abstract val yState: MutableState abstract val clickState: MutableState @OptIn(ExperimentalFoundationApi::class) var modifier = Modifier @@ -30,7 +33,7 @@ abstract class DrawableNode>(desig y = yState.value.roundToInt() ) } - .pointerInput(Unit) { + .pointerInput(xState, yState) { detectDragGestures { _, dragAmount -> xState.value += dragAmount.x yState.value += dragAmount.y From 9d7e91be5cb22b3e111d3f1e89bdb77565a4dbfa Mon Sep 17 00:00:00 2001 From: Artem Date: Tue, 2 May 2023 23:57:20 +0300 Subject: [PATCH 202/212] fix: Implement updating files with trees --- lib/src/main/kotlin/controller/Controller.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index 0709f84..ddd39da 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -120,6 +120,7 @@ class Controller { throw Exception("Tree not initialized") tree?.name = fileName + getSavedTreesNames() tree?.saveTreeToDB() ?: throw Exception() } From 627e4c2e75c3b31939917016674450218a22db47 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Wed, 3 May 2023 01:01:01 +0300 Subject: [PATCH 203/212] fix: add icon and bages --- README.md | 16 +++++++++++++++- lib/src/main/resources/img_1.png | Bin 0 -> 364 bytes lib/src/main/resources/readmeIcon.png | Bin 0 -> 236724 bytes 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 lib/src/main/resources/img_1.png create mode 100644 lib/src/main/resources/readmeIcon.png diff --git a/README.md b/README.md index b073019..ab02c68 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,18 @@ -# Tree Structure +#

logo

+ + + +

badge +badge +badge +badge +badge

+ + + +## Tree Structure + + This project will help the user to build an RBT, AVL or binary tree. Add, delete, or search for elements in it. diff --git a/lib/src/main/resources/img_1.png b/lib/src/main/resources/img_1.png new file mode 100644 index 0000000000000000000000000000000000000000..76f7a1b73941edb3a56e09433bd22bd5d1a49b33 GIT binary patch literal 364 zcmV-y0h9iTP)X1^@s6f?p=d0003qNkl$W^tTh9%ZiXyfeQ4k zeOnDe>oGLR+Ok4;lF;QMZ<}r==rqLmjs~Im-D`*9fU-_3+3T7J9rlm&$}@@vq3^_K z5<1`MG`+W9z0=&rPn5tNEJ&QgF4i=S8&Zt5F0000< KMNUMnLSTXpai3TK literal 0 HcmV?d00001 diff --git a/lib/src/main/resources/readmeIcon.png b/lib/src/main/resources/readmeIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..69b77b743aca1f98a540b032ae48d35981e93d86 GIT binary patch literal 236724 zcmX6^1ymI8*WP7eQCjI%P*S=I*2s>tGQ;$V?KVUdyrQes3TP%~9`%&cda(T=&6!wjy6z;~6H z2Llw}z4_odyLabNRl4myiK|Z~BLfXysNy{kmZX`J1UsPH>gGVTu!wClD8bifB+nkl z{E5LpS<+by3p{TU3RFerC!kQ&+NA+nRl2pbQR)+wX#A6yZ!OPIGix4n<;oVz$((0?iM ziBFf)D!+LBj6n=Yky9gL}t)fd*%jW%PMkr1$Yu>iOuf5bZi6Oz7v* zg6(8W-onoZ>Ws9htf_XpQ9FqT$tLZG)bm-MmX7etKPxLTbzh?e?j=F>Ww6ZJdwfvq zL&JpaZLekVJ>j6`ej`m}U-Z;&UVuRQM&KP!#XPyLwpS5 zl`8&4xQN~7$rT%_j&mfuLm8^p_FWuyxpbZI_vyS&~nkn(sDgJl^2AxRH zIEdWafsP-7{&o2@U1spCU}f>F*hB|V*4RL9It-+pGU^nr;jPImYzq-BEezGs1Rj=6BuTtYC2Rz@(YCcD~`A60ea3<@E zGvnNNo=Hu6Up(l&-fdgm9$u~FP^a1JV1U%-NU^U+q70A*bv%->D1GnS3eESABU ztQ*l)=i#foKR1#sxN9Dv_q8!DZsE3l0CDFH{cp*385wn>7d8E*sQSIxROQmrhTEZ< z;taZaZw}*P%2B&&m-_^{=9A!>j?2T@3%%>JuP$?xUi<$)dE2o8!Rz~B@QZ+wiIRZe zid9Ath<~Z}&>)qkg)|=EwhHLF`GBXf_7sH_bxWv(PJK;pxIX^Q=>V(W>-*VXf@)aX z2~#dDtDirp;mlNeoSWIrbS-B~(+*k+L{qOUJJ&~E2 z51A1!9}?tB&m3(N=97UZZ}Q@~X`WR}s{(vyf!9X=4zV`nq}KJl8rAmxBt#P#4z|^s zJu{kFa0@vnOfoUhNd+OI4BE1Hdz0aLNIM-DZ@NtU0G+CKxKr29q0NH3XRgYSWA%eaBRgb( zt~Rg^a_vb&2b)d{ibO+O=UxlpNu%|nHX$u%zC+9B`{5BGJXhz{w(EOlIoiGxZP$!7 z7;}8C6k>?nqy+dq$wY}h?C-npMpLl`6jYSe{#@Tv%1h=BKxW-e;Gv^~;1YI#bk&BF z-!1*r65NM-b=Qk6#rB6^B{zYaSF*CRK}c5{$0;FIHABvHH1tUK5NGu8f%;P)W}S7m zU6*QCUZihc7@ek@ApiY0otjsrLh;0<$!_%uB|*H-zWLBJPQbk)s2tXRcOjXnuCeD_ z+pOa|54dYN97a?kmFx7SwgUx8lWaj0dw61-{1-F?S*NtL=0l|&%sNYpZnDtfkoAu` z21_p6DPELmNO!NBW-r&Z*$M$22}x};J`b1Wuy3R~y`d%shGn-gMul-|AQ>XaJR#Z^t0FVJ{!)t=Etvg* z0EvUH&utS6s4Ar;@bRIm1=_DJt6m5$I3O_TJ z7F8F|EpILE=IDyTxqSujZs8%$S|UGJ^8Ynacpp=-BTYe0#p62pzu)2cyKLcl*kJJ5 zgumvtq+)$PTB7XsW$!qX6A@s6{p-wOi2Ys>uJxMJ8&%w|6p`oo?!rcv@D6ehk9at@ zBdnNh8Y_!9%C)3qb}1=>uMAdy(jEqBsA}5aaF=d-UE6ZtGH1XYU~TH}y~eSfOc5Bf zd!Kdq0NOLvKUZ>S)Z>w~E4k)L&#Zko4R2rh&+=JGo*3M-Rx8~bSI3Lp3n zQ&c9D*{sKcP=@zL=REQ;=CTT_R+`jxyRsrIY&~Go@%zt0{;t$!d%=Wu15n0XO*UD` zfK_@8HL^4L8OKw~`M#Pnc74{pv~RX%S<4|3)w z?Q;At=OnfChPCaMa7Ks5@!kwhD|1^9B7^ILCQgGJlKYxV(r7>cpU1`L)g*?dNv9Vy z>m|)ybkV^Blrlyd$w|` zZy>gKsH`pi^%*KG_3xz8oq>JQ#H`IJ~-& zo_0S~o8q`qPB0h0G7G%w*zjb;8U6=aB|)TBHLranbX9n=rbcY!Vm0=%M&t!gM^ca_ zF^zDY<8wb2^`9bmD8v4S62vq^fs^I&I^Ame)We&ci=K+GP5AvlLgXE#s=J}PRY%?= zg^{<-hy_96m_Q}UURq>I*bwSAL3I)fJVF+8m5JB3tf%&a7f*|^tPrWpZ+4iiOo_%& z_V)E0^k0qzrh2@a&v9|Kh)n(yovB z>1zt`Qv!-3;EgdFf@6=1$Tib;xm#+OSoFo`lR{lMSB1LF%gDNR#B@rmj8Ig&SX(>- zQUs7WxHMpknHb$@hqX06iUf%B3H`-8g`--2Wfu`vm%`T-%HSz z78pH2LQRbJRBel;RV+g&IF$6x5Js=<4J8^bZ}oxx0A){Fel3;c8nuu;paFO50DatP z2H@81461*xv~le^TbTw@Kxt`YZ$7ZTX=yaEsO1Nn_?Q&&hG!sL79Kw^1kvSh6l2U4 zdvr3gR%(M^fJ3Kd<4*J1gIDFc59GS}`nmj_tc1?Pt?XDo4kSR>b)d8>vpnuzgd=Yt z!w9>S2OpAYjsV*$K*v`KT1ve<4B8^_uvVYFuoP`N{&#Aw9rjq)^+$|T$S+2rb4Acu z$KSCR>$gRmR#8UJYgiGguvd?>Nbv=@g~bl|;R2|phCSPw%MVk85ju&6{?3c*6dQ(S z&WiLQc?#w_VQ*s_6-ZSW8p<12`@rb6hO1XIiezEE1>me4YiE?pkjsIwVFxAPZeoYy zUx3Yc|3j6g65}+*ulpZ>Gfo2ZLJ<(5ceTN``3j-xx%9ZI1;F2iH2$}mbtzWSknOCl z>$GwpcN{|`)3zN4*g6PWYh-qakeUQ;y$1M||BX$X4jrnm1D^H%DO7&1MuQ&hYhLTK z19yQZukvsfv~`WO$&wN!b%*En;ip|lN;jWG!;7w;oO_Oq=6mRj%@4!9<@3IL7vB+k z@{iCOxjV>4WCyaJa_S7DS2qB>@eVijeCHn({!A6=-L3KnNdOH%93kNKA<|u`bHR(_ zZ>m+niLzWNq9lTy*#`6~*Y@%S^tTs7SV-Z^Y(qi7l_0>ih=gzJvZbHFp#k%rGbbey zM#=7>q|8{`(`TQR2Z!r!`?CAXcpB$tWBW^9)dg|MB@7bE_AYujJn?AsqOauaztE#n za4c)Q{Ts!}Bt4KmL&U`5G$dR0unpQ$MZ&q^s&EGr!&QumZ4w6VGPEm?r7^z1Zr%Ln z*-My)v;}}rREy+;v6u}Y{i|KyqZV>OOEM8Ap0GUfBteRAYW!C^VIcr<@6{|EFde6f z86xo$PXH5wn}x{mddmzUm6GDrtpyFcysbjie?l0{bo#q?Ok8*votiEQ@t)P<{A`P8 z@5D)}2w|^n+4+@b$K6`H9#(LJN*13y1)>pfij8|$P!+L6fmp3rx7ZX9>IxrmFIa8y zkaVuUO&MWhAD}1p=Rbd5PzxCHr1uDhyE&>Y6Y%OV4~Ok^C5AzhXI8;e{D~^hpTs2c zJ(2kcw_=ol;KumRkL;EV@41=cdNUyoEYYl|T79zJ1j%YvtT1`r-7o_?l>4VFUvHAm z?{F*DgWI#|3F3=Cg=3t`=0o+RZb<^qLM17Ohz!~NR>o*Eg%L!XLFuc=UF0ccBsWHv zp_-|=kbyT5r&Xbya{a{?{D7jE$K zi@QynRd9A+7`X??C`C<=mbB@aNf%5d*%z3WTzwiAB5-=_&$Bn}BSIn-&Dc13+;V_> zBW0BO-JbZ$_@!I!rWs$)dX!O%wRF<$ZlMPueqjg>-?foTS0V45p9Id7+^2+VMWAbZ zCdz53HnrNZsSSaT@aLdanJwL5EdGc$>`zi#qb2|${qa1{bU99PI-#3S%%Uyj?X%?w z4atr3OD$8j&)L8`M?bf*^^GJ$Ut75%VylR>51}-=1i7?VpAT1VNC2g%Hq_yB0Q@C~ zt!8}5yTiccf120t6sczE|No% zqCE_cos^f63x?YWSFg;s+rI!(mC(%I?Tq4=(S~EmaIIfbK8xAS4&g4;IEp2pz8@rW zgk1{&dWXX(HHIw4cbKX((e7{d$U0V5W+Kz_L$yYkZ!PS6gMdsw69t6Peg1J}E%^93wO|%) zsucrJOjnw@fNUl=D4+VRm#dFSbrtT0`^G?e!G5x+`}wM*0YW%oh|RuaOLt+;C6d%< zp+ZK7HPe2X0IfmL4U!PE#skdytUNEGkq;dwLP2Q-$Pg{#Vo;c1? z@Z1$D-W7;3ocDQ>WgY(nJfd141EeAapqa@WSTbFiOwx^8w*9-P>R&}Hgl;3U8*f;H zbGRjIpE+7|$#3(SvmO|#x-hD+{*{q20hQ;iYDPVZ(Q?N5Ns7g?^r3G9ZQLJd$3md6 zN^#+HYORJa{PD|3VS3&BsmNAc!*k8XTs;Q$0A^7%+pC zc60Ng+cM{RKf&CRuY6Gy{&Kf?ckDwqI)O+N~4Q_qvX+9oB zNs;Z3WlRyW{F@Wo^3yrNGTJIEch<9+e&8CTX&dP7*nMO3Du4mN`5aC3x2JpkJ# zD!N=F|0Aa*rGa~?)1aYgHzumw;zeqoM$_2&*T<`AXMEDRnjk{UuG>OS1iDOuS>i`G z5()MU=6rTC`c(((=l_%>!|iNj07mJ?E(t}lNz^GW6@Q)G=J-bI#}G z@iD-IMtBy@k8fa8?mc@uXH)0r>S=3J+C~h}f_h;MHPnBfBZY48jo6 z+&BZY!I%r&-ESlzLJ>5OAPJoZUvS;biOK$Pph!P@MxX4;%<(>P?{IxQ<&bwT%mIC1 zIlM3+PDY;j6cNSru}BAx2flB}CW*=0p_N#es=>sB-6DT!SnaZyJ`3JU%4ZFT#(FMy zab=zA<^$J(4Q_vb)KvPlp70IX2S*SJ}5+D7V3S;+w>7RRXvUA)rO8pXgAB#l; zwX89P!L|4y;`d6f02`T$NkO7^4^o}&>zr z&7GSz(FC5Ra>^DFU~QCKoRxB|PXKKYjlWtBb}rx@4*f&Jef2~L6qPcHrtRrEA;e4e zA6LorDhj4B^EM;hHB6J^=B6d3dyx}b#v9ju9R1;mW=9y`?fmcjWT6nHqPGz#%*Qzi z$SQnkxPkwa?%hm_UB0TJq}!_)xmF|ZSzyE1NUEw3ZM9>)g4cgvZkfB}aA&pevTe0H z^QlHF#w!KM0_WU=74s&TS;saFsyMaSt2Y3Jpzf<{~Z?;IU~C?wyrS;A3( zKnUa>b+fmBH9bRK*j`>tON+IYf5!l5sB2&|Ma?}-?LYY0hw*`Sc&#hWxrYCkZIXka zO`_IUU*0J5nTLbh3?(F8-Jsn#AZud*d$H#F67F~8T(y(qbTUq~%k+TME=o6+*>*BH z&%Y!+6^+dgMG~1|MXov8_g0&*!FNqm1WrKoVce*-@V#eaa_e|sewh6#BMhJ(*JVYQ zWw2h7Ydrdt7Q$R9fke8_1Z{!Bm94P- zzyNSHw^Rifbs0X2xsCx4hJRLR&j{~s@eF0kAp=%~`50+@i>Fy<)9>y^VQ{}4pBoOQ z_OH%)%*`4`2GOIm%I3pDpusOd7~D~Lgul&A{sw$7*E0PT_X2}&Q)Q7BRa@vqu2|}n z-^A?{X3#xKmWgR$lP{x1WS%-MV!`8eD4%5?#68XEGs(f`PUZ z?VS7Cr>zZbS(vPmUsQx9AUk2WWc;ZWq2jM!#*ZP#d85@IZ*6h6SjiuEEWKFo^Crh# z9JZfOv$UK)IQlxrNar5vC(|dbTxzyV0NDhy#0Rk0lHzkqi?7CZkRI53&Zn!s?+vQ5 zHhAz}S(5`!9=7{5IBkzKVLbsS}08 zGHsK&Ow8HgO$$psC!?I;b zc4yaAL_P!bNMs^E><;X&z9PhO zMSeC)^|;~gYCvk@1Qsu@R@)c9P!Nt%(Uaqr#Z`T&*j(P%5Sykj058knGRu=ae=QX& zg~+%#S-S}P@XXD1JW~}xt`~A3()6NqkF(n=Q|114;a4QW_R6qUL8&iT1=;+43COdRRXEVC^>w(28J`F zerU_MlF)1#c+GNAyz}A{ma?SV^$#FT9CMhZ!W5vXEdywUxbh8{Lb%HxvxT#;?LBXf z!_Nc?YN?x|%QsnsiyG&`x&te9tN2z|78_<8x1W`K>vW{zom++Gf0Fm-`EHm0OhKuG z={71ql}Ij`e|dlxK=Hlpx+CZ*1p+B)?Cqh_7@{Ttk>AiRlB+($-`t;M{10dn6cxEQ zg>4>{uFTsORhd(=8#&cfOB$GbS2h4x>k0z6aJsFs6fXS%DoG5P!Q#`u6lWs&t9`lw z-aekXB~(W*yOGps?%x9WC=VIkwtqj~&c-K&+kX08DZ>%kQMi8{k7~FK#kHP>uaw7i zhuCOmkAF#qW0$`(?En@&_VC@A|NY%=+K#gfR(CrZoBsnl%irmtI~bmjoeOljYOD3s zacUPxOj|T`(Mvv9Y{1hiarR6I3bGcvQG#Lt*j8Q*-QXWyw< z=56TE)hj%gp~uiE=SE3B8$fg2o`pRJ5d){u$s0iG>s=q5y_8$H2qYT z{skjj&H+heW%2iYnZYB~mLnwzE*?_-ogKrAX%4jkKAZ`6;ho<)tKUAvb4GL3?d<*J z94M_DAIO$SGa{kI7{|YIx-b+>#ud9fAd9rjln54wbD`tQQa(L1*>tfi@$$H ze~}|6dwNAX$mQvj1^gljI&;1M+kI;WNX2?ikjg?7X8k7ni#Z73;NZxi|3v*14l>O@ zk}q*k>W}2&2nJK#(}aB%tbMKUtwfTpara~qU=1{F(}t+^hKqwv0$ib4&N72lBli|a_p_bgE(V5A=`kF`qL$UOyRgN$ogP>E_x4J?_D2UOMjBIo9;SD-S})% z*wEa5GJX0&qP`B3=Nb7EMT4x@h2X^8{eP*8kp{ClpIpx>ujosaffw$E(GP-Uf3WI`;F zWz$QENuR#YJ_gz5x@~3kz7c@(t`#58wN-Otx8W=FzwXU`LNDXY18~d>AB=N(PKl~| zj2|NF;Ps~LW4PiJ*66(MAwuv~s$ghXDz6Q6gx`)IVfp*R6OmKXCxl~DVx#$LmES3J z05k#7u4}u;pW(NhkI@YWcsAe(|1_iShsFOuJ^sz58Bt|_D;xXc+-Z@0r8@zXRL$Cu z6--&Yk_90}n;8l4-WMQ5SN{WUy?y>;E5b5NS8ylkY_TSsc9&U!Je`}Tff=-OSEN`g zAhjVZ|7D@;D8^rnip`crdGAjeb>8X|(SRN@TsYxaH$Oukquhcg*_<{-o*6gyK;udK zTh^alYs#mmDC=-Vvq_1g2?}kCgjrtUh570Dpqz^Rxtd^08i+p`@|zs?(1NU_8y8Js z;*_>4wy$D^sLzAL6k?X*=VZQB?@UNN5d<`S-h=zORl?^UZtwzN&k%aE&IA)IqH`yx z{*%J^hgNEgtsUla5&nhzv(4Vgse&Alx7u@vPmF3hl|n$llhP$GjA0mA%C}ZJ$6ns= zoXsjieP_tM3+AuZQsJlWzg0@=_Ii-YYTR{eXW0@d85l5}72x_jOm&3BU0cCz@AXyF zNkOQKxiaHny}%smuE0RkG8g#$CNN;pbTVAmFY2)1sc#*v4LnE&b7a?g2dLiUSdVJ-sU zCI0~0Wf6LN-_Z1nLpr)dQpe1>mr7#5Z!o!mZ$%dR@ATq&ue$I_S8_`-Kcc1Sg?oTQ z^!mj=>~Vm*vxTBPtqe=m=o^#BrD}I(3Pd$v&foI+r-JskeNX6pl3AEleE9PZS8Q;$ zh!mzcmLBwg+ryAk#Z$XtAJ&I#-UjtTg5k!Uo}m7P%p(A7PfS{H*Zhu@0nvq5vonJ#vfqz-;v-ID{k61u4#$E9wO zgY8O{$>M(2EXzswXsnI-Z=9ADo3B5l2Cq=>X>u(VOGS?8>bno3tf@?e37!G%01Z-( zuRNSpY@)7H9o3&K%K=mtliE!HR{n{6()xC1^Idg{;f1}O5NZgw>~|3aI0CqS%@iIO z$X9w?`5t*}6ozngp5bB3>%;-N_9GT&>`a}_zW-Qw?HC}x{$u=c=??Ck%f6QCQWTrL zXrfcQgt)pmdt)&BSVpaJBuk&ur|by89Xohyi{_Q!&%6L<|Hb=R43_($7FK)TaeY6- z6VIxR*ZEymho&56-wI>&u`;q~(;Y)+_Y(~?Tp|L0Rcu|3$4XdaZDZ6W0@2|&)=5)y zJwcmwnY^?WP|Z`DPYGQ85jeHD@eASYVPWiCZYx0!ieJ00>Ca$zjg9u79}5Lpx23QZ z5EK&RS|q-E0_!foZvZw_Z{^gNuj#|XQBt+bsi$^W7+fSl7Sv+lh``>$LQnKmf=uO7 z-e7|Vm*R_!mtrd!Wgg?fm1!P6ehuozzD*0?rTNqb|C=&+DollHtYTZnK>)3d9sMji zh4$UX6O$6cfLXvJ^;aL5!~HU7G=0i>R`U*+X-1KS%@dIovfrcx67)Le<$BS3S%Ds|Af7Uj~M!?=@)MypASpQ?fC5)zLbU-NDZynyE?uSTY) z>7g&nNM2jIxz9{kCQ4{oe*JPc1aC(v2Der9m*0Hc5J!dEP!?EWk8;?ej|nqk{d@h&%$fd=NQfEEKp;UaX!^ZHcM3`9+X*rTct+^yGE|xvr>)|j^hO+p z8*^D{GWhbrUfo{yj5M%<$YmO1UP)XMSiu(qVC|g%vN=Y>te_=Y5nm zn{WHY5A9|Ltzh7|Z!h(Jtki`5ASE?T&SSG`$rDluG%HCOrQ9{9BrJB=JM4X?m@#@W zofkr`hiXr}lBN;>!JUur*~usZwX3LH)sSzJOproOwR4v#5354f`ONGwC_|x^XDZM^ z*bDdX2ee7AXAgyhx}v5FEMYo52&~y@>cdWrTA2NoRAd}uWAo8%_qieP5&sQJrb zy?Xg!jpq*Krsn%@Jks9q$2wfI9#_EVb=2v42g80t|3q!b&PhTGL1~l*-(@M^s5~y^j_=(2K);-Z0*?%3PXLmap zC1TAX4_mQ?iGb<9{MB~%IIqV;dil{Bqyuuv*QyC~ce`umOyxVWR|Y-%#1Y=OHS(q1r{5yGg;kJyqjNcQgZ|EnnV8 z^9AvA-6~#CE$<)^na&7sJkTcDmG1#JERgqS928u}r-Dvq>Iw528@lv{$Wof< zIABqOZ8V;7CDKd7_dgjzRGt&aVkwc(SXd9U3dsg5y{ut1l@gI#5WZM=?M={; zZg5UO?o?B#a{UMpxoZrt;hWo*)k*z!(o??jQJN(@(e1S?ULhYrme~xiRa?r<31*E8 zmuQVZPcFw8O8gkSnX$GeSg~AfERESIw{tvdbITIx*}a^)tY8rRDbA#6K(95Nxy=rz(mp%i)h|0u!(>yxaJPy6CK1QRGW6 z6Lns@S&!`qz1paYcxjes0#VLpKH5MNO3^8nq&o%Fqb!`!RsTe$F|(s5W||R*DL^uv z01gC_I_acaooc{DQQE#d-h_4W3jDfx`nYd{Pb@Z=A}%AEB9av#_#cZ##p4tETHEm| zq%-jC>M}$IH(7r%g`l!!#(8_ShwSu7gr&#nfrk0_o`6htp|jZtRmIUAR!_M#-}crG z(X-w=tgL{LY`wCLfKJHuKMaRvEDwI)JYj#5q$)y*j|b$@PIiC z*1FBfCefr#Ed2}ZnqS=dEWw|p5K|nd7^`Lj7>G1Ce4>4Ce*f;-N8k6ZkUaqc^gWY* zRstAl!zh;*XuJ9ernm5=ARK>(KVo>$ZrOgZjXFkIWw?HA0I57U#A%ujsI`{{>VU}F zLiasS$JMq_wAOLm+T1suOD~f)#+#m7HTc~MyZgmGrVr%InE5gfcbg4%@f$4={+hqv zlp1#$mI`#4ptw1?jNqK+=5*9RR&@rL&sub`7`fZZ$^PlO{&F^IYx^cKw7#|ZU}Zk& zK!$9;`Flp;fUHXcJ7CG7mThi0VcUzG*pEoOPuio+YyHP-*WXgKC+M{?_NQNZS6s=2 zqnk2?_C$3HVi(!uHNHVo758$bl4(e%U4Od^6Of<_c?ZQ_dr?9t8)nYNaM14i$mhRr zUV^rle1K>*T+xT_eU}5B{^MYM7mo>guHoL>J5%4j((|*%b00q?vp=ksM&2&Q1L9KR z`>Ngf3qKZc;ki(#b%jbk`;GIzo+iQ1f=V`Qr5a&)FY8ykgHCZE(vRv3WJ!l)=0rn& z=NjG0_v3T7{3tbTA0!c`0pkUAG%>`#n99o6%)47gKKi2>1j zBu+KO2m5uCf4}1e26Bb6Iah_C8vdRh=~E@?bmrDecfZPapJrY}b*Kr5K15ko zU;0J9ZSpi$vJI2Fx1Z{C*O@<_4U*8;bHxpZJNQ_RmPQldmrR9*&Tnje_0M1M3^3DP zuuqBe(^2rI)_szb-kzfRqGFvb?+HLWO>!q!v@aS9W1ctR(-3MW?tEW-5z;ok3;F6m zzK89-lCVImsJxGXj+1So)}MP%pCU+Ph00_vyr5N`Ycaj&HUEgnc%_q{6~*|*Ya>!( zqk5uL8<4V#h*npLs#;EhU5S|APu1h-$(mN z&ZSMv-U|%#TTR&Cu2|#h!kf>zlp7eMd{FRuE^J2Go4NhgnSx+ilR9;46Ad>+0XVbB1NJuwV zY%o-MSa?no@(yfT^KmH|LfY(Z%`M{6$D#$wKt7 zz$@?QFXA*gU*~S#`i)M{k1Sp89kq7h(k|`ZcBS6t?N)#S1;dDDH{41;J|!mqjK8qt zbHDxQN}uu;5oF5_pSPcEp&$59@h^q~(gIJH1f8Tbqb{EySa1_xFS=I*6M8gNu3StJ zD0*(q~;42)K#^T=$+>Uhk80#_{4nSl(K7xsky`fzmbcDNzdSvX?g5`u(=f|N zDQsFuIPc9Ck>-g5tWjJ~VhZT8827ih5=Q00qXA<_tF@SRNjF!4-Q>M}bF!fi*^9;; zQ`QgSEr%(Q&HaiIxx@U6* zY`>PNs0Sc6gBKRO23vr!6Df?s_5q=UEA!~RiESSIrh}-b2&HFG262zt^b^ z0Qm)O*l__qg*PlblE6n?&=4<$gd@z7B5IPSo$QLx@`JOqp}!fC^zJ735=$JVLt zO5*WsC=94(ac}yHLML>{>_a?cWm-v5kjNFUt8Y{-4y=Ugz|Kf zi&U)s>lG+yN(e~`%{MZ$?jw)s(ARU`zbLd z+wEp5u)8Zb*SwzXfZTrOQq9%l{uD)!`!ACIux|SJi-edyTppJbJdPjMvG7s?cgl#n zg_VRTT|M3W%Ys}Ro#-2&MU&Vbqal}nA>Ti2X?P=Kjv{1O93oH|vjw*H72x-o<2Ebd zvsexeW2UxA?+vu&WT<$Z_nH6_G!lcrCMJM>G4BOW0!T`y-V?FP@7F{gmmW5>_f&Bb zz3&gm6UT<|xF-t&(ixE-qUw41x5IKs!JZeVb-;_Wh~nYFaOw_Qpaxc(pCI-SxV&_N ze1Xz*y=9(=le>(D1Vc*g>k1==4mWUpmXsrGwc2~PN#JS)`_AjGa^%zLuz3i~>V=(N z1FR|HN2g0za>ncvXe+5|jlgmy;BhhN6eh*3tJ+2Q%~|24fs^Ob8V`K!hCx+{R1HgW)ccqduCU`+6Q&fdq0(#T9xy;*fa|dk0-l5P>r{>E z@&TUZl;-}X6wQ^0} zt;mtlpz9mCwO;yLj+3zRS04S$bOWbHdvuw*S^mu%8jnq1TyVKchgDN-YEOx4N(8#b z3v@o_UX$zCM~FO=X+r|7UCIHj{=V8*J2e&E?MEf~>(~esp|pY;+v!{W^=arv=aj%e34XQr zy)BjP1u=Hl4%L7Ur*a;f>7ZWb=(4-mEU=}(=R}-s4`v;qX$hUl6G;*}Rozf3Fit(w z!`+hKq_Cy3Vc`}Lk65j!DWRwTIY+p_AIXs`uI>szZ#g%f zkdfi;itdFlvA?i}(>&Ow_JyIHS2x19V=;Q4YGbN7_T@i~3c5f+_Uwz3 ze8Y3J)z~ejP;c@BPbUcv5v?hAoR9b|VD=}>`Y0yhOIy9a{)ZvMgbG1^COjBRQYycm zEWdm5QntI~C6@K;u(X#<>*1aupC5c?LC);kW0bB$SY4NocQ2|;05ss_wZu6vclm1H zNQUY((#UY2u75ftQ8+*TS9nzKh!FH^>F3NxCvR@;838BE-32)Ij8M+&mhPt7%rymu z)b?UV-JQ+9A4$9UL%ZV({9Ks!xox8AfG@WRAiA;jGdScVdAz#k_kGpNOq79w_139s z{4(IG4lPZ)M-ha{#lhkCqH6_PJ-l`Uy6e-(H4i}~TLC8`Lo=`pgw`~9)v$6B)TZsV z`t=;E1bcapm-DTX)iN@JPG`+BKX-PEO*Ffg?VXtU^7D}2PfS_GOJ*HHVM!L_hwxD^ zAHOEm6SNBGdG3N{O3%;iBq~JzLxqbl%0`o|Lu2F z4Hi?&k!6}Z_clj;d|!X+q6AH6lR7c%OG&_Nt&Qq;Op;5EP75K+*$86AagkxY72z>es_7Dtwi>3uz(6f6JcTn^@@Qt*;(FMSSIL zh$i?3aBLv5d8`(#`Z#y`L|IitG|v^xPQ<&2vN4AOgW9)P#})18#pQ~ zem|^`_uP+P0KZp0CpSx-5~r0pgjrp8oZ>dQhv+@X2ZI13aTBz?&NHj1N7^Ji84RCAlvBkYO_VVDQTWkXKs z2{NpQM!1kR? zqi}jG$keW6B;;d$9yI#?onFT~bg+DU{35QDq?0E{qC$e93K^Q8ioV+l;CY9yBIZx$ zILYxai1GYlUYpvht+r&br_rEI?d#?@4LQG6Zr&vG3YTzHX#ENLPD+$RPaO}%%JlVI zdrcpL?WHBYELa(NGg=`hGC7}(U=oyX0{~(A(MBr+w6~iK<$if8uhxhXz1rTjyMMQ& z0;Hne>G!sd0`BU@mH%0nX--j`i~zFIQgk;Pz2V!SjMTB;R_euiXE1`5DxS6Gd`aVrX4I91Z&j^K0y?W=J98bra7kwWPeQ z$Ehxpi&az`69Zv(DRi|JpO{8jGs{XPPOijQG1?eY&b8yJ}=m7e`b3WSj$n}^%Ho|qVHQ%aM z{;Q`oAN{l{HvT}PoPD*HJ!^!Ey51E}0_MP=HGGuerN)-!6>QtI%;ttHEAhn*7K{9z zwc8n@iB%=d6l}xwzX?A9xo&{Vl#laMdwc_ESX|KVvgM`|3xGzEfS*C*?%Xk9-w=D zv9LTat59BAKs~3H?)sR4^~jX(8Z=O%bFgj;LCrw47JM-x^CFITWH1_w6odXwI)*To ztemz1eQs!6uLb*16c%bIiQ2Sb)A9qEo)rW3Qo0qTGRg+frXn(`i5^pnYLp+n^n06H|bsC|CQOX4OTqMAR;z1QH-+7rOp7ZKw!hiAeD(yrvjnoAhdzz`nQ z0L3SmFQ7`ALdHFI0y2dHwhim9L_WhB!xWD@cXXo8E43KYG_c^S5FZ4f>yH4l zb_yodIo@ogmA5J-O!?6qj@C)XkBOQ_A>T8W{ljV_gy7(;BU*l&kpT9(i+s_8h zL-zN930LeN>$Hd?$;;Vo1p4XFGLBr2;Ae8$+`s#%zmNJBb9CAH;RzWwv5;hJ0pPWI zke!oyq&zpZ$g-TXu4d_4B}?!Ydl`92^Nw&Y4x zoE$2}!GO#RPG01XDL6$PYDDNUN#M*Kv>b6s9+xObu*igP07FnE_a+$snSGYgnO7|uo1iyDhy>H6Z`Yp&= zcSWUV)c!u=>chrBhYbV=4bY8|7yBo~%=w*Wm&W))CrFhDn1_9lQVibi|1g+OiebaJ zVd5+#ywp*rCKbI)CF)3pp7{bqyvD)mm4(=N}KazyB6X`Gz_FQC0!(gp~^-)=%f`9IT1)d zSm@sk)=vdM^EV?2*~3kj*sz`S>VoPa+0~_Zvr_p(7XbZuOOY+`EPG zg~!Nu9s-}7Z#71NAizkTX&e%b5oy~F${gMpNP1#MjDoEvCoT+wjx=7;Ltvebz`F~`JAj^fH$Vf-0h|fi zmPE39%IJ$3?%Q4KN1FvcScIM7G&ZINfW?t<6(vh!lWH-in~}f^q+*~+ zF1$44qiDcf7#P!$-^rO*@eu)>0b*L#?RJjgZ#*vm?V0IJsG|i~F0dWv}x*>xa^YG{8q$ zAbcD^!(IazhiFHk0jqY*9qelK7X?;^vSM^ZPQGdkifP)7R9pd2zdO!+bg5Om=KQb| z3|62;o`tJ+w5toB)CEjAJ3lKeF}YY8HoOeYz-84(e#JiLjTJSL@OSQu9|izjo1GIt z=kJd`1kA-dV~VnYGf}!d>>38p=YzBEynn{k?%LmO4ZF(i5&c~2HK616D5_mx4M;N) zo`NF*IESC{|A0m3`$q-J!OoPUTUc{#CaH{h)d7#`E_fOEcoSb1?$Eu>)h znKVpg$fsed2D)`))sh~u`@70orAV; z#0N)Qf)tT4_)t8oEcPSD=k?m3 zYi3frNZOESsa-m9i9|QGgC=34uXAA?>cV-N@$*SW1WcN^_*5@k$uP zcZPXR&U>Jx>A|+4n(ko?gZtE1Hk{kscW!k@+r`AaA-mW%1kx!kq)9)bNPs`T?3#N9 zIA>n5A(I(n8Jx&!Xt9%<6Fa^zqr4X&-H3cVf)k!{wI5Ltkft0Oba3CJu)_&iJl!<_ z-cx(8#|7(uu8gMIHlp`wUx&n1;0y%>TpR83hbQ*wp5_4Ef1X zo?|hZJW?8iBNuA7l7)%sbY9Xb&!Sb^+tPGu^2)`?14DioE53tIv9Ti7T%-LMfQ~#Z zXpIiJ+uW!z@4i8oy*ucN_lyzWA6LMf^S2DRZwx4^cSfTyrhEI{(dskS+du2-eKQ`c zBfzc$%oer5ITajX%mfqSL$@NS4p=PCcujJ=cuJ2Mcp52a(`dp(mf>-)7_s{WrVI<_ znCTfZz@1W%hI!nvj@O7Ct9{0z`zQ4~d385ti^0^g(kkrkw8OLWlpEgn{!hTPgdQR&h>Z`FvzQx`CVBCn^hEvk>p(eP!@u z0L?{2+d96#tz#~-J=Z+k)$mbf{N=W{RJqS?ZA#t3=U3j}vmL;;=Kqm0LitkI^m0W4 zjX65-cH&g;ofJmMSMqt3j4_ki35<*9YFIS)wq0X;)71>1rR!Z;>O}Cu2%UXu;)r}Q zv>yeg@mpQ04wNz7 z;7m;bFYDCggp@Wk*aWafQBz!7bp_is5{%2zOha368D1-(w)4Amw%75K4pzST$AEM7 zNhy&J0nm@Trb4%IC|0L;eX;OpaY{^je|+4XEG|ygOXaWoYy=)_s^@0 z={d4((TVTIbD1;A(P~j1lOj_gcl<6rqdb};r38#n`LKMd<3mgrp&Ct?{(RuaBWYF} zO(q7yiP1?HGjNSZooVEzLjh61jTsL>Y$teYX2mYDHC6QiXqXA$%>VB947d~9u2S*t z%ygqT7y&fE9G{s~><cBE*Y9w?Oo zTV_!z*mIS4HU!MA-EG&TWt!B`s_)c8f);|`JB=9K5$S_ba8#q>8qFr8II{PL#G=!; z`(5T{pUd9rclkSmV$x#Nw?4~YRE7H$I)b}3(KwqWc=FhLx#**^9=_6avW4p$`_8$w-)%VI-e;EMXUBh}5 zsDmYhNDDh|yDqZ(8!o=NB%nUE9mD?A)G@3_HX~p0(3o_`^MbzdO5dB1BtD; zn>!i-&vvO9I~3poyg^SJSajGLg)bt=*x0JK;sy!^%1W1{;;x;H8>_R3#a#Qy{?4FL zdmi%3q$HRg4M+pB0jN&~M%1N7&JKo*bC@)x(KHT@>Rub3a>?NdcWn1R7#J2aXWK_x zxE#RFJTT!3MiDG!%p*QE;SPod+|k&uJDr_W(U5ImE%KZGC|GoG^y4!Nd1p<~lZ0&;8h)pB#uOvj~vGj7=LGGX{n?Mb!WRzzq!;v`=%Sg_@r=3M%UC3qjXm z5&O+4Z+Mg&9XDVcc8Q*T7rWczi)RDlF5f%i^7n^b?w&E|yPi?!ZVtNQ%@IY(qI|=c z^!3qBly3mgW=#8oH`a!YLL$0d}znS6D=cTB*{<7Vy+cO-#NiU7=oyJHG|MoOsS=D>{Pbuy2d@nf<$ z^1JldBh!-mDeSz5L5G%chTEc?k^hK+EQU}x_8|jIjtslZ=$IBmz#U*$SEPMMGP7FS z=tOpbac0K4m+U-QnRiJ8zQq2TOP_ArCVB5Y0lM}P0Q94M5sw0N@amCC8fnzj1MYbVNW-KH@s3`& z5DDS6QU*0bG6H4;0si|HuhVjX9 z7n>M!XBM%!Dap(zmIXV9H z!^i^QhGvY1hNy;dPhFF*>2?EP&=%7)f*dV3Fs48UO$vcLFCBu_VP8O_(3Zbr&wIVk z0I!Y#s-w`Lf$*gBiS(n zOmazoWprd3g=wXZp@x1XCR&J8Fy)ln2}!lNkO5qJBpFcTgsDu z->9P6NR-y#+?9uPR5+yDQO5t%UI$HzBafRPwx;vJb#Ku%H|_k#)z8k0%;=~V9yHqF zvnM5}I?n83}lB(GXT%ag)xT7K# zsgCX=;m7Fj%k*IAovR%wY6QqN~S2c3>>Ym~u8%6_YiYm(G zjm?TVW6V>D@_GVrX5#H0NH5JoP}|>i<-=X2HhBHySpjGOkfEXRu~D&<aczT< z0tk3qB-z7u=7no{r@ zsgmlLTXyaF71cE4QH^d|8fshYdM7&4z7HDR;LT&OSW@tYF)Iy+7e@rn>3;7QLSu^- zLaq&IpXbG(OHpB#wsSfgD@%T?7X?`4fXr$PU6jgmPx|(8O(LK76W7JaX)vO>$iR;C zoBGFl<)FJ|pPvLkYd}YoR+P^*wJUp#>}FG&NbeRDx!uiXJ(GOlyxscR&jvuBpa1q& zD*GMP#f>=g)XL=Uq9{#!HmkfAp1cMm0E!wtC^AIg6}sVMJ`_Jx(TV8~HH{Wplky^r zPLfI^nrElZzo6Wh_L4dcDvekn|A*UGXP%p#aA>2$i4GG1pt%4@eK2L(?H+2(JpfIP zn~tl}!HzG&9c*jSfTgmPhoL4X#(mKRfVM#0!Fy~*>70)Wpupv~MFGA2Fh3b*ZVx5gCZn-hx44UWk1hFtOP zfGge~bmiV*S2Jc_XOOLd5-N=I`6Z3V;&tO4!g~q3fWou^_Ow0D;_tBj2R6>`h{vi# z*RcFoQim>=IeZsDS2Le1K!>8_yDW^s6C-^2y)BpBUUyl0?d2mQeu?9Ft6caFu$HZIuG{ypT0_jL8kux@&yxH&bw&-oSIgnCHxd z7&U1EKV;TV0+$gJjZy&UfN#%MSK`R58f^%Qf8pouK{tZOOhOO+14gVw zu;zRUOsNt)v>{6m6hx54=xyw;7q=gy<1qC`-G^vh)0e$@XeV4e}qhcXyj>7dJm%ZHYa_{7MtdxPZgx>Hkfl^&ffdf zh%3xcSTj&tB)2}NW}2EY?em3Y-?&1Xwt>I80LkUsYb@R~P`o|h<$-sHq(DK9f+~fg zXZ&W5lqxcLviC*Ky=c3Z!a$VT8J~=t0ll3n?eD331JYRM?B@XHxhVHt=%4c5BKT!b zcz+KqtdT)f!xm@KGUVD>P(u4SgoQecnqTHuM6Lj6Qk0lru>+o)8b12{{b8yK-2>0Av%ox6YM*bEbsLf3Enz!l?rE`NqRa95E( z-g3Fa&1NH){PnM9fc~0xzWC>CwQj2bM2^>hlA|N9xnvjo!m2J}nHjC1t6W!eNfLjC zjde9f&>(@g242ocztA<4Cj~^LxQKi*LWdyW4Di>YVu_DAZSMeD*)0M>Tb>1@xRS#aF+aQ)o%$kIfJd?*19BXnz~}(m)-P5l4>MnBR>w)N9d0a z^4aX9npP+jB7NgzK97usK1j=U{RuX#@3+OH#Z_iSd+Ta*J)9X=o2wR#1>cMSNCVKV zO}~@S&Su^gt2z5zMvOg;FT2KwElz#LmhRcYb>FtjJ@c-3cS<0Qs(_*u7eDNfLaP?W z37_(ivLR_PEHLR1D~dIFUZ0T+4Uma*uzvE|0#)PpWNKmtq8 z(Q&bI02<{)iS!Dxw}}~Pi~;D(%>l2AzB4W*NB+v7D_k|eyNb+iOu&sCk3wPIfU;oV z)*PO2ZF1|p_K^_-eFMZ%zXK>~$iT8lLDJYYX^s4>fty9ifEs{SkWR9lQyuSXn6xb% zSr!umSAdp+X3m~Vij!OY3IX)peq;Xk4WN7Na~N`^0bdBMjZG>__MVIEM8^H1B*u#& z6N(X}bhZNkoB{P*&#$Yw(L6Dc9q1gE1Yh+H_HB)y|QIp$oi|KF>d zyZvmHDNJxehpIemx@8O`uq;g+US4&13)-_a0XhO{8Yl>q8kOxGsVC~uFi~yQ7S=oB z{9v~jq;Z5!Fe>l1#UxF^n0c5mAPf^$s>0Y~b7x2Gtyu#Lwrp(usCL7;gAXqONYniW z^90yn(lCeI)~YvPv7q2dejN5pv=4Tr6-C8?+>O?609^o`_lD??Yk~bBO!~cSSRVyw zw4cfKMR&R|?T&3rduBjM+6B+$G&xmUl+eZIqOQy8?5b-oZn^5jvH+TKQu%um0_94N zvCaDithWs??s{D=MLD+L6u>Hx{uaAU02=j1J(_GyA*#M=EX_U>O~zjKR--JU+KwBz zWbSz2LRrxsm=eRS_KizI2YAaYXp}C)hgf;0#zrF8G8qLYG|_Pbq} zGXr|*uBBKEgAO2F)O^VqNlVY;$IfNrN*SoLl=NLO_O6t~dok$8dGP?wfHaD(j!R+$ z(5NxO3{lmtm?KJ0LgW(?8m7YSM7Uk|k5TVBuxrG#=V}70gB?i)Au_SmrT(^I*;f}p zx7)v?8BhLrd1kS7=*JW#GHi-F$8G@`7E!jtDyuYO@lvlXy4Pr&kb(Zz zj_gAzU}-#3)Y2L{sDgGmgiPAU=Coy|3e94`VgZYmx3E1AY`Ve(jom%hw4XDPF+4ds z03BA>?$YFjsTVNmAT6T#1CS=q%ZUoJR%`4Q;J@zN;l6+zC576*HV>T>VzlidO6_`cd)krZM1iB=WaT}X8t4i33fV~0s&p|k+U2kmDZ)yVtK+UuX& zQhEau{OldO@NeTDH!W~ZUmfo1ynC>XR<1SjLk6G=vkxX&*H#u3^`&{u1?1dlM8^0m zrs?~GE^(vBWp4C}(bn#dx&{p+y<>8p19-hjkl&W88D!jhTL932SAMBVnX~nWMR&UN@CPwq zt}`8F(Svhga$LG-m^0v>8yt42dwl}v%)NfkiUGp@alI!%S!s^@!-~wkL9u6;Gu?=E zDDvLOhv$dv{-jo-v<|EufL6iPt}j$9YAcVl;Q+Kix(lE~P-M*5^8?VGoHaJA!_y)= z0JM$(^os3d$vaQZcR(7`rI>xc=>FL?s#z%&G1snv%#Q+e^$~!6KOY`3=&T3m&SOMH zKJvzZEnTae;~W8HJcEm77yV;F47I%9t<0A|G~TR7jng^raAixdms+urcoc|aMk zZf|XiUCZ^$EQfr`CRK3DULCZf2RIr-P)(5kO!N#&-bIQ-iif$m z&M;PjY1pJY@JH6J$}W6?fi0}C@YJmVm%i5LvKIJh$oR;Blvd~3kT>DyJI0z5K(3B@ z^L3wG;EpU^ga8z*EI~_oPnJ~(ubBg zVz8WZk^eQMhwp_T&C%UP&ig;@vG3XdI)Mg!c|nRHOt@SwE@q1l zb9VvoQGk|o_AySD-BVXerI6jXZQw8@Sa?=eGBpL%FcFA!ZNDhs6QT6iwXnhJyP`Vl^`-(Y~fy zL@5u_9cuVkSrh;R%*s0(b0+wq%gjSx<%T`$q%h!oFMxg$NO#}PI`U=9JKDHxI$hXKWG;X5;Cb~0dW+JNNDKq%QiCPhGDXvWLIjB#bq6dNE{dyF04 zpB6ZmBZf+@H+|dKuWij^#u5Q&7T_?V34jihtO00RiZz?iE_^!k&gTpWrk2&g2O9^N z0qE2__g(IKzt=0w70;Dms_qXLPfdLb8`yF2Exw&-PzL8F*KDbr;7%%~qiKwg?7 zW?|G6DjMxIBY@^R=8@X2 zokq0U#;$8^;9nDa`MlBmeV=-OuB9{Il$@E*CZ~LhFcVD9jGY0lrDb2B6LU4jie%00 z?)mMl6dBR{G-J}F9neRojd?MX4s`{&;h3F}uw2cKoA4UiV3y8_B^D6EE_B!y3IHxz zdFUd`vgbQr*>G)R_gWYtGa1;jy1uAlGva=7UiU`}DlcD5*X@1KmK+9qVj@EW$OARR zx|ANsj+h#v$su-I0BBAUx*!`-j*M;K`vLTmrXmNBu4NxzEDR^T90F}S$YMY)8uq}6 zOtBJwkoeS;SoG1rpgZgzQ0qG^I%XI5+1&w`0yJo@u#1?B9bK_njeESo5S@6rD2K|{ z90naXrU-xl&J11!pm9kfJw%#_oHS_<>8E8(wJIYb`7PrfM^4+=EkK$NsR=u#uJ)_x z1Ro0Ietf@SItn@62Iqop*QJ8E(eHE12EM0n-FETI_r#|0gf19pr?2#QhHk*0y4vfK zSMMpsk-l=z#oxZABN}N^ELnTJg#A5s?VdZiddD4Iz3t8nXlbB9Gn^YkO)=5QIg@&j zrRdO@ih`soSTtbGfH578Y=h4;D9Mi%JtQOu%-20_gEs$=yiPsf*FhQHTTQ zBL%`r1mNr*eExl2gan|+NGf}GSdSY3S`%j?e-A*K=c4tT`HrCpsWXhgK3f(}7EleP zjfMh{pC+xCigrm1TtH(_t%aSWq;}+g&-OZu`O(1<9edK;;{iGZ>J9YN>G-bU{=T2{ zi9C`LWev3ha&hQlBPkc!TY?k=lBGt} z<_nvK)gpC+P3I_rAknh_Wu-nH92R>nMB{-tSpjG@GK|c209x!>7w?QNTKVY?O?IYT zgb_kvj1VV3f#wsl^B`epD_o(s8i6`5-d5-Fi_GRYo1k8NCc?s2F{H~ z7&Ke1;QGckusa9+LBYA(Bm4aTx>-u-ceb14qJzsEg9I?RVdy|&dRic@@5*)&YK6D= z9*deqU{ZX(@{hPPq>CP)p`ALYAK13T*it=1ni)tz&Dr;TE=i;x6dSq>L!$@ZIqa-H zwy3sc07M=XILk${xZ2$br6mCBV zetPY$i?bm0jkrXwf%kwh@Zk{wbCX49dRD+p-Z`i>_4dqKzM!Hec1B= z(tNh^=&;>O7y`iVk8}<>dd^=IEC932|Lqu3Jmjpak(3V=2NJ#TedY1bcq!N3NLq!&o5)gCj^C*+?W z$Dkh_)B$_WCims-kv-j!DnmuVFc@QPNd{$7Q0;lCD-4lL7-yGW8U9406HhSgK1+HgO1t&8eoAj zXz=Nx_s%5;M*|2QgS=)L0vYh}cTybycqk~UO&wq8sGAUbP^#U**)9}RIw=kGpIuSx z3Vwe-fPQGmL6o;6)sfv@Q^7LDPGFJmW<^eAw950nk&iy<9dJnlnt0!k&-X`7ATVVB z2Udt2E{inmc8`i+$)0|{^)?XJq-y(q*>$i(V)sMgkW^AJnyZ(ztv;R<>01U&H!(OG z7JCMi>vxQW-{gFYH`w9^P@mnoCj%#d znMmL5b8d1``DFl@Ji2n?QToPvSC4dzkg~VDe@Nm+pI-z~#TZy>#Fm|lEC>YOgU)3s zHhlVo&PR%)q(@-+XV>qEMN=ST=fh(YH0`kak^eQrb%P(Zkr~Jh*ACYZjX><2m>9z@ z3U73zc%iFPR=oC{oEur&CFf^mw4-D%R8FOG&09L>(S zLt9)>kCEDFADtSh+IRVRyFMrcFBrj^Tz1*vg;wn}`JJCifNoc7f26j#vnFq4yYOIP z{ERw-RE65PBWWApOimfA!aRNe(_S7#msXf)3`h&0BakNFOM5aP&9?d60KY|U6E-{P zbI`QWa`HUNqS^A2kK+U{M8UAl;PMsbz11S0hXCevn1bC-60| zNW&n~_PH|6IceVqwxUcwCpUdYbH><&rf&e$EQly468HO*${@cAILNF>60k9K^GQ{( zu&DF~UL1U%q>>79fAa%X0esQ~6sy>3lR6Pt-7pZbC|w_x`&{9wEe4l*75p548UR1< zH>Q8N*PXt0!yUbR)y3YpWz5$A)EF}<430R|0m6VYfLx~_X3@0Y$pO2O1=kv!)zlSy zqsupjT;=+pqG~^vt_%vO<8R(}$FE&isst8HyE|HICZ?d$$PNrC08E%HV5uVG)RH8E zfHTkIn8p$<1{_&zGWJ|V`r3e)f6|ycX%BM8FkHJFx)}yX*b$rINA>(teMBSEn1<4+p%_4O z-ol*8G1GT|DhjJpLPhw^)lR`4D_vU8 z(%^)o5C~YqnDO68jE{-+v#{WOOu7S*)``S+YqwApn61ArWFM+@; z7&5Ctv&FN{z|H!50CdOqxa@CZwDh<3F<3d~9)QJD8a55YR{;j|{n! zA-hONM|Bah<6)u+J03^c%D@*rZj;=+Ev|G2(lIE5q=f}Fp-^Wyx19*4K@9^eY6i3^ zpI*DK?KY|q^wcOrFf}S%8&*R}{*nRs+kGzcW{-~LtHZ8xZA6VG7#%h4P71J7Z{2tC z*Y6nHy{Cx3dDo>b-BWQ6#>!po(?XDYyVn)%xl5M^ zT;+zAUY+Eh-6ns@z7?#qbJNWMWa$!pxM zC4ASj8jZH-{d_i7J2F>}G>K_I#O{`w#=`9(P+*mJwp@-$LK|xeh|1~2lsiGyG{G=k z3&R#t9htHDN@efl=g(X1-}Pw$=&xxPOMjxgw7wDsvZ}LhW>L)!*mJs_JuhRUgKxvP z6a&ySh#*&kHRmIME=^AhcpVKM1{zfSsr)x7z#&^>h8@*n1400tTrj%kZtn=pV<>K@ z3O(SNMhLaq;>vDvk%MCly~!M1lO%DW(~lS3LyeOs|fg*uEIxidfXp? zr(hUBS|`0-AWTBf?C-dko!rOv`i|{=9WBh+)^ExkPfxnN(Lu@aD2C!2%y+?eOQOT-u%1-RjVRt$>=#KmQC8wm$pzhJR6=Qr$8rcN!q^=JblN(o5?s$O4 zz=#Htf`KET3?P@T3<;cNvTwh0d)7dc`_!-zr8`i!^|;jQcU|nY+b(;#N29KqeGdQ{ zCJTrWB_@LR40#>$ts${(SaRak+b;RaZI^rPzAL=Z>+*KYzusdY%>psx8n&P?->^9_ zqsN1>Qk)|XOw{ar0-#C%(1Zf`lV^r4H~Xest9M4g%r1e6E-WtTw|f)-wZQtFJ~3-R znluO?%0eQa>S*K@={t6Zr&sO>oKc$KAwpV%{4?MTShvR)b^pfToQkW>fjN!%YV_Fo zj$HEIm}&{$`ESS<6uW#tVA5*k?;95|r|dle(!QZ3vQs5Rx#$NjKvZbXq6i1erC`Wz zQ8PJ{C%#C2vfs%S(}BqEn!MB1Xu`D+noyJyA!3K#bKQUy_M4iYk-U$*w^E3k5Bd;7 zAbky8y#b)Z-(c1ln33wM?(JxtSH;fhr0oP^Q@Buip#y9>ZtwNX7WdT5@=85d_!A#T zhvCQ3l=8dVC-GmX&MY@O4I_AA%q&UF&eW1HS|<#o1f)@AklrAV`&fXsRn2Mg#;Tu1 z$LeTbLVj^hlbPqMY4x@*ot2vtq$uk~&zPGV>t2et?5!9>a|>Xusn$`~Axa8F z@#rzhtHG!>JFu%|HZbZifkoy>1s~lLX5J5=0d@cyPuyU;$iUZ9^3-L|j$O)5{P_Ns zJGE`#vAv(e`DwQ|J#LJ8Qd`NQU2N58Mqqw<)=t!McQ!uc+@5_;c7m6d?0Z?5_QPFm z5si(GxPzV^wL{BIjqjpwTue>HJ7bUlQU1Dt@eS|S3^=R(`nm^Y0JwQ`!qu;jxyB6x zoLdHf*Nox4X>9Y=yDszkeT9C0<*rM=c258e=v8hG8(Xz=$F}$s5oLlD0W;}JpN+dydXHhJ5W;sm_K8)K&OsjN!E{17?79Ixz|Z3+ zZ5M(IpWhKoc4T_gP_!EyofiX#jVsMzpwDMd5tckYi-#7dn*$2Kn(rtuY4YtTJ!H;g zU`@J%@E|>=f@N!QS!qYqI_x6Dv>w1Tuw^Mbj(wr766JzPH+1sD(daB$_acjnMyPR= zSG+wA-HH_ZNLN61uuuS?xdyv|m+P`4d$pfst`;$BST?VZ;Q2+CcGtYZ>dZ5LU85_@ z8Va47pL1uE)9!G1)a?xnxx>M+W_EY)7tUL)-~H4B^m(!Lr&>!Jacnu&y%*;B(R485 zjf`lQWDcfq5zXGN3GxZHXu(T#F#OLVLAx8(9x(W&cur-YEkip)Q8Vik7@hX zh~~EevN{Ruq$0FU+VD*)NbMNu1VD4LqFf01YIPsNr0u->xV`xacd~3- zyDfX|2b-?4XD9eJC$?Q2OU5XsM_iLJUbdAjtjyW>Gw+AJauF;0~5Af123p2vQ!+Vk)_ z3YUEm5hm@XmYthh^Gz&_zZx9#)3zByWq^jRnv{qFMtr4T&p|G|dTZRZ?;1IH#{l!v zfPWA6zWCYi9(uLV5d5FC+jgY5NKura*vSq6t5nh)s6~(*l7ogpCvW$OX_Mke-xzdp z+f|T4;c)|%Kw zEeH`X2Otgv3ZU+ez@)hkIcvw_L7tpl?4d0XyXyjNSUN19hB+AZ$<(Afo}6-V!?OI^ zdZJY>|EZ@NpwG{LYh!os4UEXiDi5GG-C1;7mp0Lalk%UTgL9zfo}J3Ta_d@AXbTqGl61xKnQ1OBK0p)J#fPiLUu z2wLC3qJ#X7vf<_h$yns2Vb{27O!6ILeQz3wy0bgzzjEjD0J+cqwIC_c{KzSC*60T2P{%q7o|*&(n1@tOtB#?o0B z_`P&_P$`x48~3#%2q_S9>*TC?9F!%QtM~nTGhoi$>65_{sTFeTFlm$=6v|0gz@E{O zXCkl{lQ)2t>cs#$4BHLzNxmx-D=pj%#}=gSAU)NNY+nIv3YL7gIZUOfKTJ3>|iyvjb4MOQ7oh{ z^#4oq?@E0D%rNSZlNQJW&|F_9TmIbMx7|4Bx-sg~zFpJDUN6nHsp}MiwY8yiNGp|m z({S*&KD_{4sr>QQ@@g@FGn0-{P?Sb|CsKaM=Y;XhfT=3|K&$M8_q%{q1KgxI_uxSP>+ z#gt_{oL4uacH9L;y=(Rz!KzQ8PypT4q(TD>KjRO<>*IgOJ#!LiD}@O@qofvR^d;-d z#{(Lpeoh(|nniDnAJiu>N&r5{?qJiLIGt@)G+b52|8`QdEmY3VfSTMKx$4BuiXvlR zNs&*IrRmVUz-CW3YfS`QhxVJpur@Ku5pL!mIZUNN>61b2nV(<=ZZ20Gl;n zD;UeI-WqYOUJ8I-T9-8dBgFyRP29Tg_TIW8AO)N$4#K>ZHZg!sJ%7`spS|H~Z`kX7 z<-W1#Ue_}A-ncO)7M*&_fb+6}@TFVQM+?yGu}??N#(kwL9%k~=I8r1y=QXsrO06K1<4~tg!rY!`dIOH7yu|T18 ze^?+*ibGPlo-wbjj#MI@=ASDOqrd5M&j7Qgh7b6nI%yK*l=Uw8DY|TQ(Ev0BOLj#u zTNtommBPoaLF^pJL$`K!J%c^i(K${*4|6VD6q!*Ot_7s2?1brg;JT0;7Xq{Ffi}J1 zQzp&5U3Y%CCy@cy4$3h8p9VOtJIQ(1SM@l^USaZdH!^Zgt?1l0N<+$q!JZ2@#|m2N ztwlxUaKqU2jh5|HK^B{kz?v)HOu{`k`g(C6pBt#N$x9H8ghtPNbp z44@~LbYx;gVbH#WK@Ze`bj7|{?<>2|F*>42L<}3dNc7P7To`E8$aUJq4I0*lmVJ>Fr^5IowZ4vy>2b&x1B~Rj$qAD#q1XuUqXLmSgd!eNKYQ-X@VJ5$ z2y(hGoKz<5kuF=gZx##K?v8E4c2-@+Sffr3J%^uE4SUu&smP`@bj}e$)uEj9k{>yQ zi4tGFI-SsM+7?a&SQ95+GaWGyQXy2ugH12hr|Q=O4% zNwd&7;~SksPfSkrwx6gSKtfFJ=9qV!yY6++;7SH|IRn!Y zIbwkA5={4=fwX~`fm!a-eS7Rdbq|ua1xQb5I5F0oyxHqcFWpfi3=2T=6$9m$ZW~D6 zcNJr_Wr~9a{0(F4HT%74Kw7fL=JAWhvI)6esq&LV>aPngQ&K zcUG7$RHw|Q8H{g7w&$VyZ`S|vr9vkT( z3|+bN;RyrE8Q%cYKdw$n@8vNjV3x|m-Zu>@eD(?ZEaUcC)Akup?9{(L0&LB_7 zHj&y=hmz#hZU<~@+gq~yWhN$|jI$+8ZS0Ei`9Q9V(;|v6QlssieStB${Ui!7`#V1G znwvR^z3q%_f_;bMw7hq4-NTqo-O6)|QWi0;j3T~vgooY_x7;(sOG>jVKkVHpT_W3EQ#*+TL_2 z(&!qfpxKRI>GgWuC_NzNjZNrVcDpgl>RbJ;U;tfw#enrS1Ho5rx%7+IUCJVZB+gi@ zXVFtC>d6e#{zjjx8GEkSB2a$qt^w!W z2Ou4(Nm2&F$yX^>+IxAmS1FOwD|UR*7Lk{FT; ze0PmGULWxKa*N!}5t&=1-lqA+*!!FPO1B{CLsExs8a+EYcECA1W;>ecQ5PTV(J)*J z_!^8b?p03YqGLDvIu3?3v?y38qM^d1IcZ3ej^4-)U`?76fQA9n6H(-M3(Seo;AiLj zv?G95=}LfpcF7M3SlMvR^=*x2gAin==XIZ;AVZiCBGj2=X*@Nj*HMeckCEm)UuKTx zwojRE)8Mk@>I~jEh}g3Q;GA7w)HN?4Cv{^)4`RpXksKR4ttOLy;iFP?d_*nX&EH!- z+|*W-H6S z2v{(1CTg~#!Jdl%v;ix!F7&rdx+vOePq!9EHrd`mb+M5%*2L(grA~oP<~+8of;p4l zMn_Fbg#nprkcoT_m?XhK0n!^XKsSKSM~xOzV<4qd7t)+dqGT|zC#3;f2cUUQ`_kOm z(2xpbvL~f`(!iSc7VZa72Y?RMWQvCI`DtwhsUioFJq(+i9H|r)BrU?4OUgDIK;Vd)fGxLVWB?CF@;#AWcfc0W{u%uYXp^^w`N5)L(6Cdb1Kx~4 z?iB+v1K8B_H(cV`YcBc14Fl*qDj1SRQSt8`pL=ch%{n)>DpkbkYq#9VbJtwrxm(IP z=U%=qU?rtdeCe($81t#%h1;SW%!5*81BB)3bFW+_Pm+y%MQ&=p#WMB=z zS`=Qm=SucB9tSo~l;7}+3_0^`6y%*lem!fiC1I>PWv>HPj_QJrNc7dYz9ARC*Q4OP zC!GOslb)a;D6=X1-1&S_Y@m5=+?$qi1m>Kyg$95om2~#@JpnWfoLoB1M%kfZ`HNuw z1cMGde-{b~31I!)SZkU=h=jVxrq0o%3!yY$L6V3 zmXw}FFDjb0x-r-?uwEJ>@H?nrw|IX*tU)cu2Kt=1RF+9mkZ-23qyvU?-hcSqekbb8 z%41y6sP{QQwSgChwr>->c0Zd9z=vfwwfe7X70udf$&e36eufDUZK4(wNn*oZ0}N1; z8c_?Zfi*_}yS=fY*VI`+1p3R$x1sAKFGg{LTsOrGO#??(xE77Jrz0_v6^UU3&@{Sq z%{NqoN(9n4f_0lKJ!(_h4ba`p`B7tZ3YBz)kz%36mgz9D*%@~Tcsx?q5Sb8}=-0h2~~5TjW3hSwyMv&|VG z0;CndlT?BMU_N5Oq&)y|05taEO^X}u?4_FqOn0OZp?y4W;B@@_HFxri0lEQn_N99Q z)!YjPUeDhVsFrzr17?E9FT8YD&zq0(#k>~Sw0wtK8|{Ri zYl6<&MU5Q`u7fHnMvi&G=qVeqYSJE9&z|)R$XJP*1j89fIS|y3O3Q2Vf)7fplmZfPlccbjw%vBZh(=nZ zh~odca?&v9;`XWs=N-?k+Y~)_Hl;9Qd|P~Az-?W*YNTXxr=Cjx>5qa%eZsFo7z%`; zCHzrLcv=QGmu%p%D4#!lFraXk^&Nmdn{WaBARubbG*^85>K{m3MPk%(oEb zoB<08glfLHKj9fNUKnGu%BAv|(_oRlJMchv%XZzEAOL;#>TNMqQV1|zf!>SvjoJ2z z-Esu1E3fsdphud49B}ga+b;RyEeoGxPQT`xPI7GVU%Bsf&M(>hU$*-{Z@)itTTytS z$5mhLca>M{@n7!spbLQ8bLUBayy{sz&yjuQj*5KT4g=@@ygtA=^W1HLKETd>(y!b$ zwtdSbUc2cMZ`_t^9%f95x9+&J*KexH2IU5?kxoWHobE_K99O;c9b4e;+xs^n(6aN6 zw1nEa@B7(?C`PhZd|Q0g7NhoU17`!~#??_*zcQjg+DBd+W=>-a?dzB~;j5y7xpux+ zMrmO;c0SICEh8yIqiVwO3&C_JWfcscq;0=98d;|fOEXQ5n!Gj!TI8p>)@99rb0p~o z?0)SU4U^<^wu=Q90OT;X4*4#=A9SC>$k}xR)?kUoZUs!4@_A?&GXex63sYY7Js7HQ z(NGobYRN%U%#TkqS;!WWS-XC1m!31+DQv9>tgD>oU=S)2181-)IX>!6`}^F%ojY#t z+I3ri`dj(6jbA!%Hh=e59H7su)&I7(y>UW5myo{KJ|GHz0CL8*Jw%odlS=QDFBS!G zW=s`x1o>dOjRGOXIEAffRDtuzbmo1YxXKjlh&fZJpe39w9cc~xb2*_jPY$+r&$q>L zVo+UXn*~T?%_t4@s2`I=GYVFaxoCD9E#ZEPHlo&Dx!(;=c04>_>97h6lY)7FV9b)w zA-^-`3;+{>EeF76l?I>zZEgpoL(x!LX=9;OvN;x|VDY{lJ|FLkj#Qir^WI;YlZwAn z;Gkox68TDt=x6BR4i0Ax;gvF!TY~ zEx&MI0Z0R!1h)gwfHgo0_!6*VB4uD5`{E53fA*$Q2Rlr7{(3&!;%A_liJ99@s`-QUdKzfeIfF7 zq;kF$9S6PS)jKYJ)n3=-o9^t*8}8)Q>uM=SDv0_c_Ojjn>J9s!+w!O`*!iFDv)4O3 z=o)s81ueJ0`IZM~6egrjYFCC_`}&w`zGJ{^fLyyg`jz4Vcd~U1&tdz@`D^COg#iW!Ko*kkBAKIqnYb-F3S+Z@RCpd0bnFIS>TA~FENOM@+it#W{JdU#qZAS)ps8Xr2rmLzO3*y50j;>hm; zX5?{We!??j>oId@8$q!TZw`tRuwA)?-Rkr6+w8=^KY=PdDgX*jj(T4YC7A36p8IM+e1!3&&bEMWmF@83Mf&5#NMywD>8(DlfXcifJKK~ za{%FR8(9gzZWKzY%(i_0&0d8y+)&F_FTe@6*IF1xk6?Wak= zpve`dA09Cj{5sxiCS1q%F#_ihfOCjy)F^}LKMO)FGD=TVh@D@w1!%$@-y3iz1|XSe z(WKk1W*2e!+JMv!xWd5_`QFreY!@9DZLZsOf{)^Jy~cX)EBId? zxAK}TFm}I!J!T%39l$x#MrRFp^Do_0dWF0-;LO4Sdrp1fhS)PKKJmg$y$6b&Vw{l+ zAdEP3-_B_}w==hl(Y|xnx11Zh25952-x5H_-@NGxH|??MSnMA(@EkAz^YaZgOc%>l z+S+fAdWA`(MybEmuV}nAD3GS%h6W#Y5ELJg@CBVU+G_l-8C1YrMuz7S4O;~8FkyHv z-z_^Mnv1yhN*GJo_ssM1oufVPXshq|5ni3H#>si_aYHJRwtw=;h7$~=rMV3|QvkRa zd5&P(M5{W178_*>fAc;d-u6_M4Us!yNM{fLG*-pwXnK8+)=|_)fpNaQC9q~knlyrh zJT!)Vu#rPjh{0orCYsjHmai|_xq4`ze00Zvxu;LNYvh`cxXR|vFMmY=+9>?*FKw)y zO250l>P6)w`wgt}0ib0lGOd*iV5aKYUft661(^%0n{rP}D;jl0V3tIRgJPUqu?(y# z21J0fJRVpDhwa0=<;~KWGsl+?ILj~i_V@#bGrWU)rhL`fP8OyYtGyVH+X#?@uK-03 zjm1J$Lhkd{ptfg$k>e|j(GG3G`1m6H4y(3^1D>hK&2s0*KK@?x?Rq%7=#Xi?~)plCX@L2Z+&ZX!31lai(`UK!f zd%O|={kpN%SNkP<%S8Z<91eg^8}MddxbAY#8-OC4d(oIQujQGWF7~;r2J-h*ggkrU zh8kf2FIZ{=|CbRv9|s1MPGEv~9d|{nT(92OVg^XlaKrKJwQI(7ZyAfW*V)r==dUqn zBQ`Kwa>qFJIs2^6?@vl$!s}wu;5DH_LE2ZpG^F$oIec>5V$rq((Bem-@^YVY-L%KI z4D=m7)HiLRy3wQI1Y3F!D?j#-r<0ejs)|y748|DLBf*g+T&#ABY#J4<;+&jtEd|kjI=K zehfHkpsnFl7~rgKU&AgLI_>Vrwt?L!pe*e)J6Te!(ftU3972SM;mmd}fb;47UQ!z_ zPA$N8^Ncgg4~?I;YPH}06$9vYF84k8rKNLse5@%P<&AYsJZWw0*mhw@6;@iUu^VOT zwr2bpxH8s?ToC@k{4SGz{5T?y3IGoMGOgZ#Gr&wBV?pi%qe}PLNpa)8X4PqBzBcHq z^0qx|+?r6^EK#w*a7WH)_a-_HUXpoX1m8c?G4c;Sxp)+?jdmk;B8;ub4iZzF8Z6MF z5~&RER^UVnu6X!#pg>_&1xNu+j)5fuw1BI_7|L!3phN3&fnlZj>#^Q`MS6BdV_G?E%_Z`J1QX2p> z=k?b*Nu%|vDa>&Z=-L>c6Y{FSx-#K`(F3@Y+gV4ctG0cTNW)NXaI)%{O#$9`z5qB$kWz2HcKaxQnaqWN=jCblSQBXN-}s z9c@OgcG5v=h^PnX6o%)+LoPKsB0mmUj$&as=O-HDgNrvnjxV0-G{!oS^<;?P;Xqd! z%?)aHxa}hwnZYwtk1s#?-s)(;jmuYi2w&iNX-xs<`n_@aE!2(i0oaWJpO=P7^Lnzq z!)kM-A{;5CLQU2N0H`BVyfwfpc46I>blYtWjAp&@l6i{VzG{*mJkNbnbO8fkWqf`r-{2x7%caYPZLpzb4If z>V+#V{o+-h>UiOnOMmgY7&Q6kp@;Yqs;$u7BYGN;1101zB-+ex_Zk|A9{$pnpp{pZ|{H%4!;a;G6-) z3;*JuyMOzmKjKa&?E|;fMZ$6eVStzqCt(*@m_x`|D5(yZbiL*k5W%98+sHSQd_Nl= zbjJgIE;cwIHeH!E(41eAvO;aAHwV0bt!=Anx9q~bVXXG%lzcVk108_*75-5mfn_X&SjP7Q;Z!>x|*F74kT z)zK}R<2D*nFippsS91{~kfi|z%SDtKVVD3wP0m@VlW1f;|05sYSY#P_V2osMPi}>{ z)*apL)d_rhKY!gA?@a^SI|j_Rr4xn-;Idc0Kc({u-84-e$kreQ2hYCkj-PqcC0@AX zaA7?>mHG}a^AiK;cBAq8QY)*kt>3xZTEBbO z9SjY*%fIr=?qB`D_q#jKe8IqYMx8yd;sd+Lm~97BrXBNWWJI|ilI;`~@f0S<6O!+8 zhQs9z`PlTN2kG7(ciP*l{(eOBMPzWw5f6(QS8p0qxHavXH)a%pIhP~5$`C<&OHP{N zp+Nf9aHrEM1gVeCwBx&e(nJ90hFzHC-9kQ1D|p|8R&Guv8e6pWSy<=*bm$laa6>LR zxlo&ruJJ^NNF;)^$2 z`tt_R&%lOn3Y;O?US4WtV*Iip#!!-DT-ye8GEo zP$cBJQI3!bAr)8BRFi&J=pT|yE@!(&7&J_}Ji>JCVR=`p^O1C&fgi=b%!{{N{EOGz z*%#h%@#n6%%q!OoNN*c>+xvR!jtfq@TBi)yVZ>s^kp~F6>YO~T zeY&Rb#-O?tJwQJIX+WOG2B=kZd^@=94f%L`L;%h0`OCiTy?AZFPo#;!nVkF!-6GJv&>jHLf`T8ukoe1J2l=DnAWDqE3Y3Bg`8` z9b)GqMZkJAtXa0Cc3x*!7X;FDDHHPgL8{7*9%31(KWJt_2FM@~lqLfGDL+73mc_=J z)At9J-Tah5cIrUSDyHPfo??!9*2{PHG%e^~%s&*lHr`rY2Got}QT+dt@*Z{Btb z*KfL)|K-1Ozxabc^7rccYG=k_kl)GE zLsKAD1kmJ!0Z|#8M=V+(-PLHq-@-%-8KocYdNmFXd&b6Th@opyE#>=L0)369KiTsR zduNA!2&erYe!rOEsQPV@vfY&4S2Pe-h2ODg43=&Gm{)jq-B|P$W1+@EaA1?>cHFk- za|_NHV=gnr!oKsv>$lv_<*RPNI@O0Q!qpjnQ6pndc2aVav~4brssPUog;r?z$e6$tjG`ie0+v zTcCT#JG1@B9p|H&hWQ(PwuPq&r`P3dw~%_n|39w>$z5oANj!T~%oRUtR1SDIV|7|I zqLUdk&>I~QQ0F3l>T}!Kxd8(z+ZiV8b*ee$h1=Q*WB`6!Tqynl+L@PbxD#XWiZ8z9 z&R%>+y9^4nXs0RWVX%a8Q;J~*m_fbS6Qwe|7CiS_zW^FlisJR2=<~cIPZJ7`K6<78 zAZJYg(1bcQFGXpQD}7?nC^1kiy&a}D#IkgFDhW`fdypPo){1M^BDIbm3HPGhkaI-%BX186)o{o?n1pL_Xdf7ZSAi@)ez z_?e$^U;N1*x8MJ{`;C9{BktO-|Ek;QxhsGU+Cl=YA^yRDbW5w*n%wNL9;Jhi*6p(! zy($i74TQ7r*h>G7vEpm?rCphJm3PLy16nXcr$7>bnfpf*DP$6l1Zn8OP+1BMRDv^1 z_OSb;?W6*vO8|2;ZGwC^lP3UY&2zi&3mC}1NO7>TlAj7iyHM-{gyjVtc|r#@2aK8& zN6>gcblDCWe!bR{l8TkkLyY z@A$L^zYt+(HC$2W}` z-tSkw7R5sw^B?=%(F3Dsh`)NxnBsL8d;Ut)D00Wep1tZ~&s-5e17I-D)6ZX43%H7A zuv-J-xB>OaZ@lGp{`Cv?yD`ttz2S~O_oj=zbj7PpuKPSXsWt83?6r~Wq@e~sY2!n=kdHx&A|Gr6hP^ke8Cq1<6pSs&YpS8C0=;PW#7E*XDQx^ z1|k@vl?OEK^;ZVuy{%yYYEkc1kudv;-Ttb*j@Je{0L@~jZbw_JLb@dQZIiDqyk*zI zTcbJx(E7~YzNZup?3o%4*Bt=Oj)9$%6j=8LB%chU=g1r5$3u5<+&)7mt8Xkb1!q$fHM)tSHoM}Lz0~k^WK%;AxdD-f^ z02-4ef{sL*VagFeyTwg4%M>H^2#Z1>cf6mId$wEb>2)W)eeMKeIWpWX9PIxqu=8UF z=w_+>hYp9w=U1;8D=^9*w{5`U4PzL_kUsa%{+av1Z~A8Ut3UjY-4Fbgzv8~*PycE6 zPyhDccK`YZzu*1KfA~G_XTSUJx?lQ7-{=0xxBgA{zyE{3?=Jn?zjk}01L}FFO9xlD z7LycUYwLbu3M?A0j4DO7kud=@bL;q`WcFeJoxL>dDp&2Ic*nk=x5sqSVWy)g5mEja zkk-j@W55>}2EB5Fm6_Zpav!8VSSMC&`-#!(v60doHhV2n-{|SCByna_dC!W9R zf#!7&I2bBrBU7cbXD%7Yz2lC4^A&gaxz}9EfWKzI&=~S_1sQ~k1xIjbFl7LZi>m%J|6%4HKjNXY%w@~_KK_8F3zzf9~_pBxjsE1 z_DnvSh7P`-HE1GEJ)!T`|T{`8LQTje|Qx*z`~hdzpm zZw^_E=&0BF_C2w70Gh>t6p%(~QQS3xMOq7W5B&#~Xc$Hqmjyr*LB*l5TJqHH*MxJm zpwqtIGcNWFKx@*uW{&z^g-SHO3=)914s{{*$X(Z7r98N1%^k~aqReB`YO3WxYf0Me*E>O=E#XhUOt zKDQ>*74ql2_U>G>>{FMPl@nu@A;pV$|FAo@6_*Lw$kO)j+;=;7?zy7@I~hp{7@%?F zi$?k*=Dg^2nyJ}Y`EuZo3qWQT7i6r&Y(tb70idf-1n=bLl8dbv_}dA}$aw%5 zFl9dUZ1J1d zyx#ls2Be?6rN}>XORAHc9W!>H5+2wuUUTV}uPJ9tia`Jw0UB%*FoU4bghf3CJ^HCF zBp5}}?vB3uQGf=VL;6T5k7%HP3W^5I6c+{7bckNI#qyH9#%n$D3!#62-T}HBX;k66 z)y&UHKdmTbpCM8;+PP#KeE|~l?@2T8p*T&#%Cs&O^ zgdGp{4$>ylMB}@$xZu-)#-`b+hLL&XpS4?8%lA^J#aw#?nt_pw(Uy7whyePhqiT$UG);X zy!;&O%eZ1E$8}#6WYtE_g~pZ>Bzv>RRSk%dEWvchabonsHZngls&;Xlxq|0&#%|Bz zfjc;&wqv%n6f{&2L?(2tPbW304p<2TPGKYGBg@V$Y|D75ygcWM^V7D$x1hrm_>%pA11}L`#ZCg+H}1Qwt2Yh2d)@KikP5J;882kvDMW^QHjUp0?lZ0?qdfxYNtm z-RaxcT=M!IcXaiJi`~61Fg&xx3Wqk#ddkt%6AEXv9oKLsv;QR}0w~rDv@^DK1|Y%+ zEEy0Pu!#8?i!Q&_%BZsnf#_LJfFWMHjR8R`T3gyZy37r z>`WYg?virz-l@XfLBlOmetj@m8++XifXn?_*R zO$rO}cDA7JfkGU84*+kt4rN120iU^ysH^(tTytVwuL~+unD!$ z{F=8IwcwhBD5*HV=82P^VLB_{AR-vNg$Sl^*?}qB(T}+di^AxS-Dv~Qxe=GzoOjCucip~$!Y}{SkGOyEP2b?Y zW09Ha6@%*~+P)0f&@FG)RC>Cl(u4Tyoy^kp+8wlk)04Wk_i>#-2cObY$-V zkUlg(KQX{&j28ndw|e{Cy%%3}LvOz6j*V#_SuoVKes-k%a{h2jp**uOXw(|6m{PG% zVrgO^zu!g|`2sBQdyk!6T>=h|60GyPuSoFyNSLW3lu6BJuY#Y|? zY+GGKvpVQi6S>>>T-COzXP2+q>$>Js7Spo=V}~}~7Ds@(ZRZKyel`0pss=dC;W3|f z>G2D8!vMn#ENb#N4tl8e*Sq9h<4d*-S;`i-~UnXzjzQCn2#;=`XK{`?KGZvKWe zki0WeK(&m2;igM}&fd!xZVRCC3!zm#_646tK|)s}zijL|^VV$vCepUEcWx@*PeGDJ zrD*IwbI%sEJ3cbk?el;Al6_V;v;B2pMh0uFdqAUC+pHjWiUd{rT54=1ZyqIUYHA*#_A4(zeg{ zd|+OmGe!LTO*zC3;V~o!Ph-;9T(oy~BOBl~S3lfzwc`y}JX&^z)0M_~ ztM+vt7eIeaJ)8K+%;ubC&}BAlJH2L%&6wKm)Uc+B{M7e;m;2st{wwZ#{>oo;-}_hp zlKbwz{Ppg0KlM)q1kC-SuRY`;lQ6&GVadw`4i>aYCk;etOyG;7otssbZ4h4&G8|+q z$gwnL-nM5H4zQ{Wg%6ESGN53Kg)102N|+TFASY$bR%aNH^rrTDDl@(b2BUs?Y0N|h zNchBdT=GV=EhC4fZbI6}Va=NIG28*@EJc_*BLaL3om#8-OfUNUxjkpB*SCe9*vX4_ zl42(y9a_)nYQ!*zVj~_Q89NzO&F&kO%O5_sXT#%e*G}@Qzxr?8^FQ-1+~R z_>1n^7oTyxZ@eymKC$OMTb)xH<2)O4u9Q|Iip=%*H&o~&twDMum@>gu@IFDxNZUFZ zQWO`#2PJ2~oR}JQr(=Vf)SZ|ZRznW*BZ@hPwo^H_#bx`A8*cyFUFG30NJlSPpYu+E z8n|l!=Rl-YUq-4Yr5xnTE6_Vl?E+dTo&n9x^jcSB_D^}(tEQ*ytWBiPfg z|H(0o);`yI-;BJXPsuG`?l*uOjZFQ$`hddOxqXim@n~6Akc|8-P4w!+MkeBuK4nWHc>iK)ViH5WWZRdE3 zJbPCFO&dDWLd=jz;WX?zAjeIc_NgsK2XDNiA+k&_$6P7HF2z3I%uNIJ`?kxp9ZT6j zkeoRn3^;QPfDDrdki+fWw><>V_L;KimU;%?1<>q5;DJL4WL)#F+?AL zitQNwpKQC@>82|kuej2gk(F}lC%+UM(l5bQwDtR&siQGg0|2^wuIGj)yehSlH*`Rt}uY6Sm2$s2EFVJvn0FtFqpx=fFYjDW@9KQ z_K4Uq;%iH#oKCg!^rV5+}A*KfG~Udf))i>;{Ez>n`^oS8F86ca_kHdce)u1|fBya7=YHwOe$)+LdD~@o*VL#&Py~z_ zn@`ySN_U}V8E&q4MaH6^0Y@Q_&tHDHcCzAQAWv@Cjj@IiKFXE3lR}dTc@roYY5WdWe0d{2hkP@25BJXp9!)oK9Z5?RKBP;`U#- zA~(3xm#(_KXWkU6bi?zmYP%D8O5FF)4+AGfk-l=rzK0F{eeLrv8n|7T3=n|hHM3pU zByihyBazG7B9pjsTlYJ?d_#&Lw7bordFLk9Gz_5KGvn<&-x*f-9uhxwKcdfmrN_J5 zy?9qS-`b@S0Upvkz&x;L3W25P?kXq<(pUpPBe$dZg?9Avn}e>wxk)gQ~+TJMKLcq-2W5 zpiB0>R_(K?8>x`Fk}YIq+x@ZIIk25c;?8{)LfNU3X5{*Sm~q|1WU+XVzrGdQKRrL&(^LV*k*C%dbVSm1WLw^E{j>jz`?f##rv=WaHjvS|;{)kp z4~7OhTHeFnUN0@PFMyVAfSGMDIVb>v93>1lO`)uRK=~>zL?l;yQLr6~C^DF#L4*QC zlzPbD7!W`wFW(bL%P?kgUMn~`%+~C@9zW!rVd|tE*v?BOV&F5T%QH-2 zwnYHRQ1#YWXCgBIUAt$mp>I|zIV&^;4`hL2xEc3ETGF{M>K4U;WV^aX;~0-{F4rJHFlhlfU;J z?nl1!+ubkx*gtkFgZBl}=$mtU>+W=A)@2N!<#K1=7e&N;G_gfq(sN#?989V>Nhx$F zD&F$Yy=yW_k#H0F13%jq! z|2Z{ww)XNRJr@_`#`Bll!mq#V4qv{m&cm%|-f-)``I6e8DdM@&dF7k|S{8_+v9#(P zWBEg~a*JF0{LAjt81cbtSKRuuuc;G~IfgN8L9cl&wg$CczN7Pvg?#U=Yi{GUOK#(p z%kI#26p2fbo#?A~w#JiH6M0aU;sNgm@_pYJXlD%0j6}Ks>p~Bi=9vGoD;YrA{o3n$LUr9hK>Gb65WqN6}KFsiN~BtQU3{K|bzX3kvgmGvT3 zc>tPOc=#Fv)aq2UEhVx)^2iC>#=#8m4dGZYrb)&Be11vo%Gs!AAN{8!Mz+An@$Hr` zf+97-Lm;cE$kVb)r0q1zr1F^=+mXz=w|@EGxL^9mKjePyAAO(u>F@g<_e0K}f$`|2ssC{n*$UMO!3Wqw_UH+<(3>&6QKzjY=W*tLKKP9~nTOH)?;l5jz~I9c(y) zL5ePZB+mw7YpBotz&CxP`|E%5PuTIVyUV}wuR9Yyw(j3m;{pu}F#}ulftVqY^8}b- z%%~<07s?vVgUm zym?DoJLGlbJQ)Q<7n}5}O!S?YRV=zWzDx7eP1A-mUmyd?GImvL%X$3n0PC5dnPM#aHAh*1!0hVFXaA-Hh z^$&oiD>E@W$kXX|BHXl{qO(yAGv?ZV2hcvl z1l}$fH3`XleNNg{KnP$?VtS@?w@_jH@olplmCWe*L<0SEpS2?NM*^ zALX4Z&lxi{AkTmPx@)}D<8sEXlfQn+CBJZ`(`a)VN%wHOJ9_!5b_<6uU6G$N?~NAg znk`5)-N>kj1`wPEZx5&g5<@0DJQB}eSB^XcsTfi%q+{?p;dff*`5Q;2VVJM@x(8@{ zJ8HHIA-y6E`Ta36XAqHebx4#PNN@7z-=6L6-* zp6?J#U^;^7@Py#NjFo1*fHauSR}5IH~SHI)Ncb-~&2+ zaEYBY7!|OJog=j>SiU^#$9y(`-6xu7lvR*cx_JTU#I_NkgCzlUKC$jf*@KaGv-XES zDuDhi=au4LYsU@}R3uCGgqbx1--9h(3?)T^*;39K<%TRaBfsR3U()0h12YPOd?2JCny4y_p=FI3;f+fRY@F?_ZC}xek|%7C z=IA9SJfx`HVU$vzEE!2X0IbNIU`*&HDJ%*MY$XIJD^e8r6&mL&wmM8Erkjuz8I3c| zF65cLW7mMj4Z(maSBBJf3rNGHX)R}-ohBsP3Xb1IcpiY5rVD=78ckMl zPEy)k3FarN-+yjVz|7?1Q$WfBPm>cyXc<7?dgcr63qSME+%Np_54oTJp&t;7{=UEY zH{1_@$G5qk{O<2`|L~jss{8(L`D^Yc|G{^;ANkI2b${=#|0VYm-}_zejbHya%0a8! zaMK&d$f9&<@quOOvh8;860z?QNgZ~K&K70HjZrKt%}yz|uIjqI-hHNiym?3OKSqby zwQ-kzd&I!azW3MrUHsCZK$@yLOgeXWTqCf!Ktf&`#tAr+4?Mc#Csgd;zAcsvL*Bo8 z$L-v@>Gtm2c1Jz7o9eyqw(s0>ldrwv-un5UbHDK2f6x8MU;C@>AN_^D;QrSC=f8LV z^c%m~z46mO=Vo4d!!5pg$xVFGK>DROl`lWAE$+$Hdv4>Uw{+pVnH7OFyDd^=*qH8q z{%sd~?T#8aw!iSUv0pp)`WN&)hrr4f&`!L0-|ZQgG+wX3dyZ#70vQJ$Gbl;+F*7QF&zj@i#&b!Wf! zri&RU(~=&0_8oWdxwqW*Z@en6Xi_S$e^@lmfsI~oS&D%_Hl%KN*14e>foA?%udbc4 zo%i@=Fkm+wF?0M#Lhw7$nZ-!E`ExfP0%GvW#5{+|TU zkgoqfX@4D`<@t4q;_saK&1dG!Z)RTqTD_D~+#P~La0u=W#ac?K(-sQF-Gjv4-QA6# z0TSZw?zYZe`zE~KnK^Ubkw31_lZ24ud7k@T*V_A9do2Qb73H^Y#>3fO|BQjszs{a}y8knp>n}8BKJDoe+TL4|50n-v`{KX?s>|`!n$`Gx z$ztqYv=C1N{EbOC*+pv8Qh}UoycX8OX}QwKC>??1j-w~1k|D zH3_HBY;210=F3GuQ0AyN-y}{W=}bQl013FBQEDUE8fs~j)=(OVx6zK*JT zd6V$KLszDKbNo8m*&abw_i^>;clhn#=h(j36i2qK$EmLl;OL(1*uHc=wl1EB-76Mj z^TIh;F?|wZJ)HHBkV)WF*zll`0)GB3`h3vayEQ9QrvN!XgyMWuq$Q}#t|$8?dN|U} zzau%T$4|f9whQmkrG0ljB`Mt zATSh#K_S}mB1>+TO^Am@ul@aWr8wFn;QCcv-j^;YFo)kW$Hg!9aImpPtDtaGzYbXFVIq*gSJ`* zI=}AeXs*71-k$&be-6-{9iMcRZ!R{^o`t)| zepS;Creu-KNfYhN^=g%^n$hW&BwW5^y7 zrHk@1n*DH;nZkYT#YM&Iqe}ZYH7UDtWvpT3uR*gZ$+`&AI@ljBkTC>1Xv zLhv*s03kLOII({Z_AFhfdh{Q5Zc)HqGus4f=S&lpy+*-2!P`w;>*$Tk(eXYOO|uvj z76K*3C`(P!$w3t6qAlyWvgiD8VamydPcwV%Y|#*)xiJx-In~+VlZbrl02JDg#RcM- z015S|)%4_!65y4au0}Rko|3cQM>!t*-bG>IDs|#u%qxFiJoa#t${_|BzTUWea6k6U znT2mwufTx?rubp~8ho*I5jM}5iUaaof0XLsOS2`|I!m78@})SuY&mw$oQ1vfO!aa7 zgo!vfe=!a(F~cuwHsZS_EAh>O<@kN=7Wf=Hix+n8I-y$NEPuDXzm)oEdL1F=nKEcP zBNkZu2#`f6VAFE6Oc-__i>~sBKzHOL)#kHu>ZE>GZfFkU^h(K0bqcQoGFJi>Q@IJy z>>F1LxN-QTZz8bbmY4RQ`*hC@mH|2;NT6;N=30JBUKev$w8+K)0oq<+h$JU$DRjtd z%a#Ixn83?+NA7(W6x(>IqaB_9>Rdx~(UFeTAge+;-Idt*YRZfLO&0%ko8zz7Uo+}H z_Dn|2gmQ-UcFH|LQ>A*j2kAp?XtG05BO3?JlX#CZ9j4YR{B3?ATSHW4Xucsy5?83v z>nAW1lxyU^B0#gs;rHvkMa9#b774Zcti+TF#~5u44XO<`S|EL+;(cuqJyZ-eBJtRE zTanN*|O?*^yF=<9W8!2B~0?fdy*E)Pvkl^|MeH#kagk5?{WX) z8RVqPeVp?gZIuP6Eq-MHbXg`1%hGzgyFU3pZ*jD@E~$U@tOX6#!k!D%t3ge1WL-J( zHP%d?gbgOsad4R#%#Z$phBv{f1eMa57+vgb_Gt{Mtz}z-z)W*?mVtaRILb?=!Co;n zsFVP4>WK#$96q2FioG+Ql#?~n1f1oV8kkG@NfUFPUXz*7MzP8%L>D84XzZ@1=w4y^ z=Jo|yYD89m;oCv|w94kBOz#bm-8>*wKv+s9_c&ecve+|hafwEoTeNCETim0SNi#u$ z|Btk3-5&E;SaS$6UHp*g z>aP=rd9q1yw7p0wsgl?j6hubgWl#WeBg2pvC7Wzw9G?68z~;m;{J3E)4zE~-Z`ZBD zhA9)VPd1;O^JZc7q;c3L&-2R_OYwyO^uc9|aAegAWzyfSStWqJ5{Cq=_s(B{FNIle zn=unRXUxITwHvW_#(aFgbPbM~ZNwvMSG;oe#cM}j6bO*Dg`^lQ3jt_u^z1v=da_DO z)khu-lR0;Ur3nMfc&mk{TEIB`ir1wsby0>dgryc>Kj&g>fPmM)3t}d7XhBv&i5_IyAXDDn@ z@(_{dLIi$R*ld4l$TU?PvZ{qy(6Mn}(uwch;p2u^+eIbs~_Q6fy4J}Is~u zq8QmAg|XKr#Gx|U2*}iv@aR-cp`i}EDq zPDTnazwq%xl9Myy9qbS%ixa0-Cy&GJ$_1p#=J7PZ2e*Iv4(p~)#1>)3WXj9Nj8I@c zyxL4bdHY-eT+`XuBhQtK0KInNcsWLC1c>Mg$I)y8Z=(QU2M0|&6E$W_ zwao(B9WGITuMiixwmao|>l6kLVaN0lp+OYALO3W>qQZicject6#FjN6`gd@mlcVjtcex1CY^@a9`6^J8EcLY~%idU{*2w2| z-bOk(?4-p_@vw&{*&iU1hmIQ{9#SGl1^x{r|NW=Rc(%a7kK{>ig)H zR{#7N?i~FId*(01k`beDa`)%hK6@?_oLn?Hg@;=lnIvm243#phxI|AqS-ST=d9LjY zDk-RJpu~n3Fq4Q0&TIh`yZRy5&I@_Ao@&ajrH$Mm1X7lUy51U%N9fYP&Nr)Bm`7jA zvI3}pH+OC|ESHOdjF){t#?Y&Axs&0{#qVW1qr+1+3RiiPo#QpZxy_kOMwp4C{A`B^ zJ*_5-*0zUNoR*1wESjs>&hL#1KrU{VKx60E(kxOL6o(l&ai*!wArX22TIL_1N|lnp zK-KkfvZ^jV0gdz}kPCqmeOv_WZkqJddFwf?JfA6GcQBcTt);YN)CkKqKXw#fuV0OC zH?LQ4CW~f2diAWSSTcDm7L6Z;tpaQZ1mt$jpNoS_7vsCNtMU2#dDv}gil4S@#-+nw zBG%DX*?L}7C`#fY-{_x{RSHS_PnxZ3$U#{0o7~3~HISnJL8$;yA@#!YoIQU#iyWU| z1Yf&}Yx}>zr33qMa@P)gzj2+e@8s{~hOuJuc&wT-9?RtK+ajCD=kmAjSTF~N*R90Q z+cxRv`<5)w$F1`OoEOf;cN^B=TY0X#=FicV$B*(nep)A_VUe)rWlOMk;d}w+X*jsT z42M>)!mfpj1nie%kLh9@Tx^D8n|8wW)FmX`cfeCS50wm3>A@Q~%gRk}TFN%NsB@Q! z>&XpUmCccf@sNtCJetN6^GGLLTi*<@aQKCTIJ~C0A@fXvei+nEw}r;P%?o5Jpwh}) zZBFS^4OVVK3hHc)0T#9p49#(nGHwo%wE0RE8hxXzoPSf@2^G z&1FNp;UIwQgp%vdD7x%`n%lzIZ^N&#sCGZ(?Sk2MpB z+n8l2B;~JxnU7@D1Z@6awmf*BP)kku9b1E~a)0&RYrKD1>1u+wOPKchc__;XP%bKC zDx@-IS`F`es;F3XsBG{JK+|7`=QgasdD!1UgRj^)V-A6zr>3ilhKKNc?a83oyEYQY zW3=KUD6<;kW0PN^eyvtE1GXs@(8;E0jKp)KG$U=i`SEj9$1GypA$&I`K8%?I;$m+GY{MFYeXM#q`LsTXtDLAv|%-(bF zi&kk488YNhK9LPr>@Bn?5l?v0|08tDoj!jX9SQ6yaMpj$peXZ zEpc$x9Guv=1?$F+#f}-%v3=GIY@0Jvz;~`%103G87GDSmvn9i6)J=1zW8<7@*tvKf zwk?>AZKkuZd$Fkk^EVsU;NZ%o*fe_@Hq4xg&lj0u@8bEo*aETs^=DX;&FtTYOHy*4 z*t1jq?|j*i=3<4(Wb9bD5ZmU@$Cs_daT}rUp8!q=ecWm#x{qu&znOeBkzuK zO{xZ14e>yWgn{P$J@JpVy~6<`&CQEVQ|3hgrnhyqZwM-Ug4FA}R9CPr`XRHTLUfvD z1~yBDQDy6mD)zXURcIHAPLCuY;4!+q5_M=L_olNjVP_OSaMwOE!*@C9NC!NQpqB>) z$%YxAEftVmGWlD6o_boFyP@i?{J)!SD7);4#ydW!6OgXECC~G&RC?wCD7)dMiy++> z1fV&J>@&Y>i=*rJ6U30Bx)=y^nbI#1=QT&f4l9YB`udsa0{l+Dqm{M+jryjRQ-TD1-iC!zO&SVwIGr zPACv2TqrMAnUv`S=E9Ir9o;St3DedF!I@Gz0yKSnIW@)u6r?^erv1pE<>F;|!~Px* zVYsuFIWi+C;3JG6Cy^aqcvogDtbBW5d+RQVGq(^08yI74YTqrSQ0VQG4Dc3DGEv zi$ZBsxH4!)GVzp~!!{h7<)8p33CSV}&J|&iDo+v2%cHf1c9Od5?1vrVwqY!SZQA~(Uh z75Xt5_I9ZZzFxBeKMDgs_r*RqUpRx;K~geDhU0~w4>H4okP#e!Aj^BWdF*G|q?Tg0 zY(P7gE)iC~5&M>{z!%F`3!7eo3;U12>&#We-?Bi4l@oHEy;11yj{*Vg688YD>s!JZ_eSK4k zi~#FfeP8?Q#jm%@hSKg3jxMQQdxQ|dBVJi{D^t!Hqa^^%crNz4JH6wT)Q~V~&Z97C z0yK$NrK54bS1<|3)lf)j7%y40tI?8Vi-VHAK7gilm81W>S2!k4KM$HP>7+2dd^n{@ zyMaJs=#0Z_WYAo1C6lsg)Ct3{mA{M3ohmQ_xaOTk85yPJ0ntc(;0V9-*Wq{dHiFD8 z@H8kCcTb+e*W0$?#MfWr(DrROvU?Yf34^|V;so3+?x8AM?oR<@I!l&wVC>Zsuw~41 z9m?QeXBzZ=~=#%Y-OHd z!lHvwU?&%zumHYke8Xx5NUJ%q#AqF7gC#fEM=L0k={DFz>bNYynJh%J=bR$nj;xX` zw+Q>@DRYMxfm=Y_#Q@qqZ72T^0lv*C{ta`EM~47^hcm(2(5JH1!Jc%x%bRgnCnI5k z+UTC36ja#-pwiagc(e~x=@KPR-2K^4=Tui0wU#=y7-_h(2`lEwKLMIP6C7=)DNv2< z)I0!Tm@3sS1Vs+Ps6T;x%$5;Ho=ZZ*km>2A8sV(aV090q3WF+-I5#Jp`{s~J@U|?N zqa*n{mdwZ6=~J+Nrirl8W%8byDnNg}z*OJgy^9xNvw-P|T{{r=;4U%(yp^Gs$p&2# z9$NRYeIu*yaY{vADKg7DJldxF;dd;-S zx;fAqfQyHi{G4qU{@eD2bClteCG+oNn`8B)@z^4N@8OkZ*t=jJ4lY@Qdp~@KXP%xY zii%KYL_U9RSdA%3s-4c2*P<*bUabZqZLRS0&aL=j^-AnBTPEPV90$x+VE+;`?4G|E z`xlwvyOry4aHg>FNz-v{$6lE4JA|?zdCmc0dRUa@>aC0a79%&R+F%BTtBQaW<^{r-(M#GpGuNy z+N`ot)})tERcO@-K(iVpgXXyoCm}gcz&JEcoOaO_Qx*@}l{r)H_{K}8+Ru>c@c_Tt zBm8Y&n*iOU^9)9gsSce9bFO(at=N~=QsftgICCqw{B{oR=dQr~$6pa@ZHJq`9>?C* zYjJqzPVCvR0Y4o)fYaZ9gNr}?fUKlA1!vS%AUio$Z83@;Ch8eckq{{o+4^T00lnSb z|LK1jpo?;IhTC1fki{v%?8F%L+Hk&cRmxE_tTmYmvoT}v)7p(VwP^<)S-Gee18-0y z39}1M6<`&%VH@zar^GaD>LK~WIoZTyXYPOnZa(TFRuvkGOjmb3G-S`G%s)y`)L&S* zBG=AGQ&RW>vMg$nQi{E1wlEs)BGeC;mWzDk=ZpnXr!5D==!NWqkGw!=@WP9Pr51b0 zoao}eYfsrdM%gskaidMR9@OwOy~i^dUGDLENNDt+qu(0cu!Q4+cOrUylhg#KuLLxV za6*(}^8(|jICp-eKGO<Zor}Cae%j|`Pqfr6L=QNq zLZN1q<#dTxknz1vuE=zDRu)x}5QlsLQjTcTEIc28AKj&JjCi&Bm(9ldy2yXv`Zs66*wP=Z+a6OnC}+EnBQI z&8;(MV)xv6*fP}w`xh_9+DQ|zdcp+li62<75L+fs#*Qh|@Xg{Sxbwwf{JwSrg3nx1 znWFY~g|*Y0rPx!zBV69UD0v^*5{i)OB_dqgLR7^>TinOj!k{U|+cIN@k^E!20AI^Z zwr9a295P#pFBUDvb`ujEFD0>!q_Bo?1E};9YyDyP<@r{4Y?QY_@U~iYz$ZB{=MOb8gnlNVTS0I z-}QOxFU^(*$I{Y(tMmh7KDd!qGU`za6{o0rW3&(p((0Ur#EBf_rr6q`?wN_k zJb0ox4o^&J^;apoSFu-^l$RlTvP;QaIZuHd4MXvId&5|U7f-#70nP-_a!X$Y;yRW2 z817qr9}}E4QCUi@zXoWQUD{8ki`Sd%I5n`bF)SRLougD@$C8u1YO;cE?*s+rzV-+o zdBD9vS29G;iXGuz(*Y&^i;-)OwR+vDLm| z#>qD2Msjg$hMesDw6Ww!dy!mx+~jgxg>89wqDt6pMQn_!I|^bVQJfg7fxML8<;zBU z_V5AhksF#VgS9gz>m%C)U#?!Ray3fT7L5?_Hd~4xH?CJk?Q;GsGDCvY^^PFTp`$*N ztG*FfPJ%MYm}W&ZMW=^#QBWjY@VZsy~nkOuFrYZK%n~x*Q%<#jiHTYVpv^^#>abV_LoLRR8wuiq*j9>E$dMGH~JOm#$XT2bBusZQv5TqD&*U1X%X-m%J{Z^u(tH@Vrq!TQM)wPkmB z^(yR?zlUi+hgU4e_v_Z^26br73d|VTUn>vl$QMr7Lua7jD0wLYAAS0yO*0 zEdstwW$DuZxB14Xs~jiw*z%y+5{G;2kNk*}Yu01^h*8)+X)^A9eFSc2&LG3p6NSR^ z81PzP=Zic?AGO&n3y+5H`3v|)nE#iuX&hOx1Ya*Pm3wdleqOl*=Qpp#>9uAs-?0s0 zKc7OzB@0zdH1RN9*lgKtFH{Lj))W@=00CzCxmVp#e8mZsx7^VvgsRgy3|#`M8f;5F z_C0@9lBnIO{l8S1rIhF~O`O?>cim8J=_!Osp1Z4D?~VcJ5>O^M^B!asNqsdB&RGF) zbe`pL{LOCm|7CKs1^ZM`!SC5ct_aNqa(s zUN=0ruZ=Pe>>oS2!tU5_xcb!*-1zP%sm4CXp^cldd-XbOU$GWn?%0E`cI?o(gCtKU z)Mlik@<|FR9;d2Ym8z)xm>9JTu1!ftec~fj#irJ@7M1V(UjpdPj*kEP{-q1owk`lu@jS7D{B_urKc`SDIQ4leZL{YGvs(wU}&bAvmw)C=|#XM5Yqxu z9mO~#K){us+u@lY|4(+-H@1-oNKw|Tk~?RDc(_W8$TW<@6Q(&E09|I|hf)VW)nn6_ zL8lrYKGne-)jp>f=1py!TA~f2Y-%=5jYFgQYAk^6*2k zk3TA;#Bb#=OMI+G?Np>Bq9RSo?SdR+gav9)=2z=iV)e9%SU1N6%LQ!CANc|i_EreC zx`z$ZChJ-{ZWPwfn2L?lr()soA*%4WdiW4hT%A=bP5ESQU=YfqW03CPsEcemKOjhx z*Q+BF@WjC#8M1NwynZuQj-QBG!-m3ax(Ox?{tT0c48%eK=nZq{z;xJf95!2pGjg%p z{csGHhktl`!^IuD5O@8y9t84zBM6jO%Z6#; zq94P?*4wq1vH_RHV)oouK-tzL%THmsD5Z3cc>z6e*gZiLU#qqwtfJ0gENgTi~R zLX^Cbcf%RE0>lLbES!Ceai26{cDTR z)|h-GIJ0%a499W{PYtML|C}J*>=vTEavs35%|~Lwvly~f&Mwf1s=(SCg_d60%3x|s zy$~x_zyxOw=co?b+TQ?byCA)XRH`TU7eTs-WQ{{)so0#RCf>61$}UZytB@U*{ar5Z(gfma z`qk1%oDADFLO*9Zg(0&G)z3NkM__LEH2_+rKF&tj#qZ%TmGp7leYOK99xuAFm$y0c;47NZT$le`|-Z%eFT0qh#f#t4Btz?&HFPf__ z!ima`ayLH|Ie4Jh-pgR!?#3_42bf@on`dAemm8rri zXeavYk+wHp$3-9~Aqvw6_QR?fldyByeC!wI6=M4Ux$#lRiHpP`_Il^dQoC4!^nzi7 zVK#0w%*KquqG3aDZ2MLP>4^LHaAMn599_Fsy)T@9JE4sFwV%IE1JWb+rED5m0U`Kx z%XX}uG8OYijl_~ElQ2&h^vuyCuwdL+0lG<8Ic@@+e?N;v3p-@F`r@gTGvcpX$fob3 z4s@BeUdXcY5LOwWzRpjsTI1k=aE>>`IBNo}YYfqPv zR;4aJT8&WRrsh<#0n)XQ$KM2zefJzo(00VWI6Ur$;P6Hw}Lx<1^&Xnj9j7f^vUZmWWzniwR zWi(Rr4ArSZPA_sIk>Sy-UKwQ25D{)9FvS@@;|$v1rd}u?$J#pJ;-SOXy?8nHuUab` z_e!i1fF_G3Ky#Y6DeD=kpQWK(0Gf&oS~M`Xvzd9EPcqQ`EK_5$isbsJNqijA+uHu| z|1>~nq^FylI(+!$`Z>HHr)w#?b9Ls#3j8k5R7xI7Q#^%8evNE+#$UzOt%UJ%iPpd9; zk#V}aReFc05`r%<88u&Q0(ObLzfnfn`D^(^FzY*PAfQslLl)1Wl@>1nS>JeltaCFo zncAB1h&H4@0>rHzajK;*mEAo5o`9u1zcSef_{i_&;$REqb-d6@tb9~wT_%rgrSPKW zMaG+hY=RO$UQ~25-NpUnx`mx-ZXnUwwcb>MUY-Tc9b&e{Zd}qg`ckgbuf|ztey*NJ-h#)0jz9m3kPDZQ z>)@#yFg>I>aP{2M5fuU9$Z_*Rj+ZZD?%c(laC-Ab+&ypru0Q<*ucOCw zgRl1uRVf~oMO^`MpM^bEH3$_VMK|41c*9Ne5y?tBy?MQdp-whI0xWyRBs_(8Jv4p1 zR6x1<4V&dcpj3bU3Fz|XSx)rZ|66WNH$7LpHg}Y3)OgL=D`sC_Cn!~Z7_KHwWV87X zd)Qeo9apBRQa zQ$m~-5{@s;R$_2Gd3O{j0+oCzlr&uG77|{|L@ZxUUjSz*aYY{zXU^PDPL>xiBnk*AK7vt;PG$c*9hJQPzyuwP1cFT3b{~< ztbE=I=?j@_Cl@78d%GhZ>cIl1p_shPm3QA=FK$h778b!ipdJ#E?aTAmyf-NY%WQqL z46c-Nz0RG7h_b8O%MESghB|qD8iZ*xp2;{IiBy#cMgDv~20(|s0cdXp=;FW-RdR3w zky5*kte41(iO@0d&Vp=YL{1wGHPt?ZJ_SOR;UzWbB`BifuEetD1$q zXo50Z9Sg^e#EymYU^a2Az9t-qSt}L6v5gy%;~#+3yHQUxivLHgV#RSG^-<1}*9Sl^adcryg1@c;EUA0H{ zbq8hG^yR1#qEqAQk0vSm$$saTN&Qi?02(z zWpA4OZwBS^-}v75si?^2dDg_Lm~uVd>%1=r)(zf<)U`$jT|(cEl+&oGmzkkb&2S2l zN*W$0a*~<8G8_|U70SHJMj<;@0>o5Q@qC4rqYSdh5hl&ZxlPk&V)N{ISUYVNmQ9)} z6{#6kO`n5vN50jG>ldMZ%AD(-rlT}5UOjHA8A_d;u33hi!t{I7UZE#313mFCntMvB zcR{fCUj^u%uCD)*5*mJWgX#RN#RA%fCq_651eB=AC~y-d>Jy4DX3WKx^GuOq zYp161nSlYCMMf*j#^iW4>u*eua#Ywgh4@_btY8~}qqKeP0UE8-uY)hTrmOhJEAW=s z`04K^@ba; z<{SZ*J|A+^lhyOWV9!}uwXkji1TS#<>{e;tEx;H`5U|&aoElHLxHwqAfrT2m*y{v* z8=MTonK}oVOTYB99B)4pp1I% z(1Dox$%p#5dg?^|`ht-|v1IH>>|QVrUoKyP3tt?PO3NKvO-(Uh81|wG<1uUaFl~{U zju?RjLxy3`ta*sJZGmT2&ZTd?9OsxH`Y-G3Pb-v`K(lPocbDE8;i#psszX`UZ*lMd22G$;&A}l=$nO5FFdM=W# z+v57>FK~I&UN|2;j-OYpR+R=7A5?NsRkC8zI4l`AN)HKlE;7{tpqT^vYh|&1$|U{1 zR7Xcd-!{i9R}bAZ+3%*@FxlD$k$23cvarYLT|2OQ{v3IIN2?Zm{S4XoCXdy@98O2B zk>9gQHp2Dt+HR0da-F=s%j7*Epp!*!oG<}b_U=Q5y9cs;{4_$1;7pcI$tq3p8QjWg zMKajVkQDTU2k`v?VQzW;SrJm8e;-a;6(MToXQ+Bo+hj|6KCJg}2{flKZUN^sfA zaur&*UA_#trDyqQX*wP<;C zoK%SZh_iFV#%VLKVa9ABF*C4m)HrN5U5M4wXJOyU)p}2{=iK`0g&N0EvC;D68Cuew zYIY&PxijgR5r>wV1;oAR?a0W#+S}RnxBogocXYn{JSHyqaYO`C0s?XK#0i}H{(Eec zUERz?Se5)y2bZqFj+qOQW=GY4T zFOnuJ6M(!Tr34AmM2%Nc0$$#}7ds}I;Mls2!V)H_QlKk43(tMLP!lhpER3!xF%Hd1 z@lpZ^`wsBchJ9ONq9y|~Q;j7Ey$)!W(w7lOUth}kG)bAPRc?5);=c9-L70F{aOT(l z8lVZ@d}J$uJ!*b0nKOI7WXzqKJD7@|*eBZe;;3(_N2pp9E7VV8gMjnFd$SkKK-C`7A zIF`zQ0KT3~VWDw859l~?sQHeM($)AV$;Am@tun)W0Z?k5Cw}sQE&}w8qu(PdI$Slv z&-{E8pqUxT3yVN~e8)EJJFgr!M$3FM>BVD4D03t*FOrX!%g3f8hG0U!_k}4B)Zepk z*a#e1vkq}?Zt{CR!So@66`W^}7>=p^`>VOuD!HilPM?eCHm=CBao7H2u~b)u=5Byr zI=bDGTd_4>8`NmTk`&15KuVls^HvwGZ1a&a)Vk&qU^f6 z{2wXD-J}Y$4$+dHhh8KNY+rQ+rm41`lF(AQ23o_@Q9=^o9-@F3clSQBe0&l2zyd$4 zTBYVo>^Zad%(VK2qlfDN(3ZI~^|jz(BC8Ba^4L@UQHYJjx8JGCdmKvuh(LG0UomMc7LOaD;JkX~WT{qXVENQ>dWgGv>I8YMhpW%XN&#{*YqpcV zSz)F?A9w!&UU~T7v9+U4a}u0cm1>k2d+f9lV5*FOWqVMPPPpnd?i~k&rYrHFx`Kyy z-4Ur$frcU1#swMX*6JlwB*0x@>5kX89q{tH4RUWgYAU)~n-c8hXZ8jrqJt(wJiLDc z&SdRtd+8kyW7J%#qbkc#R5QRp78M=@ z=vM0pbl63s;+Bslnv+>mX+{4KRf9N2sFs>y020IeJ8n`9_~?B|7Tw373DDG|a}I+u z1`Hf#_9f54sPti|Ef4ECYd~A!)@BXU* z-PzJIwB*^-{DP;CQ2P8S`1mX~79RKSz)X&nQXmS2f!=uG;)vItZVJ?$4^z;VoQ(D)BV$k_+~oysd$a3tcP7(CY=#0b z+Y@~ZnkMXhtqd|{KC=DMAsY+$3>Is)%?PKv^j;xi35S&UXkc*2} zn;iLVi%ijA03<$D&jvns4Jwt3TH72v#H1;XT!giEy-|OkJ_!D3whB~P9>xC6Ho=DS zLzs4hY$(l)8nSzn0R4tlvVBzN5{cJ$J=7MHR*4*~X3imHZfaFZlb%OUVSoGt77raJ zCHiz>dDC>%dGXlMF!}ToY@||Q*%CE+c^Mpxmw`dZ2@OLbW9OL)5gmo9v=k&cJ8GYm zAWeW`NlzyEO~M9526107#hg{IO%S#W7nz zdg<74m@{CYl<%h4HfAzjSi2gh>>lA-Sv1Oq)aeqBCM(%^?uY0q6IPskT1xHnuBbN; zM(s_1lwNd4(HUoyU-UrZ4SB6@1)=te4=Toyb zdNE~)>Xi!jK(!a(m~=;QvdUa}Payi)8-fyv?ywX?Sz+&v96MJ%2hj;gxt41MK#Q#1 zg+<%o#WhRh-Eq(fMUJL-`Ng1HDpkI3JXhgiHV@(GH$(8H_jzAXGXamlhsV#1#P;AY z^ipOjzo*FFOE+e=TFTfWz2}7{$7qxZD3_S~>+l&Z7MiT&+P^OhTUa&0njr@j=02*M z=0PSm-Xe1^eayS%f}ESq=#r|Qdh)KIWL1u|ha?)FIB$B-1nBNm`8zcE$T$pSi-Q&h z9181v_|ynR4h~1pi%h)qmun;`8T%J6#r8S#uyN)r%pX1ihu3bvvT+kJt^a2__(UI@ z<`>V^A(EW|=6ljomqE~#K|<=hq~dj_hqy3&Vg1z(5vcV}$nUOe9{R5WbX$G>+Ul&# z?z+Nk)a1TKRZbRq>#EUKUIN#9_poy2Oe`8V0SC?2;NYSa_-5H!gkHWcB|-vWAuI?h?xKQy)x+5 z)Od8H$*!9ijp`_lK8K0vp3C04TYt-c9_8!5c6D_aNd0nJp?3! z&bi~N08GV(_MV*$Zx4R1fbH40qR}FsQ$3;mYyz}gh-xJ&RX}4%yiOPqocTzTak`k% zbCyv~ol)tUSI2&8wz-R3*e-^(qm{oVVbsgw=q)3RT7(6(N~y+0fM%J;0RRPQ8^hj6+TIQ!ckgM;6|!@+jN_PohKHvbUeUxqD<}}L)>g3i^=IsvKToB1 zGX$Ks2@9M%a)`D~=8YMK8N)tPBPO0*%p5vI0DBN-2zw@j=Et)J561ims~*0xB@ zz5BwNy--Cz@em^wM6F(fNEnRpR35;CKIZ~J>;qH(tM^0LLv#llKIdE{CXDvt=BUaj z)!c{Z*$zRPEfTU}2Eg)+q|(6`WpYpRAdphVR-Z`iUDIc}K)%MqdU`hV5TAj%Jk;m? z*v@f(VW$PY{+i0po-P3q&t9tRkQ)fw9AxV3iL>9$5%+2vVbC_=D#6p1hlNy<4}!J5 z!OfPvYPLB_@5rWm$5YR|*y`Zf4}#LvI<>^gID={pNKm&t#^5m#oQ{%IZBVg6KOi2k z7x@P&IMc|Dhk~WSG%Slpb!?)BHYeLV;+9lI->+YoI-6AgrG{1H0xdz~{3T z;_g>JAoh-pFpp@pm87{ynUq_M8nQg{6Amw2h8@#p;p=59v08SUwUegc^M#9bNMWDs zNMEm8FXh5KxLrK2dgzuU0qA78m{OCJHS>{EVR_!(=#m$Vi_QE#zg-<0my(LRV`pHJ z_S-18%aziJr~gINgWh&kQ113i(o&C-Ivz0aP>+v&61{F-#J+|?gTpao27TVq^m!(G zCJ?I$e^{)_0C^J3pjrAX(3GW>LlcbJaW}j=cv{ZkmC|c2sJ|=Vd{4?jDbt$e!eh^r ziwPQi0L_*M+aX4jP`EA?^fP5cSTqkWnfStfxF$nH8Gi$PA@DR)GY;`8SIb~d^Lbys zg0EJrz}!ItVe;`uSSldBX~uL+_{(1qV`Yuk0zx#+r=22=$q7n4xn{ru9SNyrw?u1d9WQGU)%Pu=LK?d4Ds)d(E{iRzcaO}N;> zANh7(s=Q$8O}@L&+XE!N7EZ^GW6kK%_*}>Y&7Kwx9c(b>X_NI3k-ce-r>~SMf@&Dr zq^{HU$1GvjJaiq8bu%YnM@{P9CM8Y&Lz2u;bT)s$$Pz4WD3}t(|2u^ARJ{)tj0r^Yj6!^D){74W6M!pajocC?)lb z(}l2f1+H8xcg-%O!a|vMn9)0K7NF)qV=c{{X!+|ChIS6y$nWh8kV;c3jY2CAyuRnG zxrYR2%GRjPqK64zJ8JV;BTgVPS)Bec)v~!%MJ1}*qA~R`l7#4--m_0l;l5bD0z2j}!19R`b*-2@ z84E^?G=O^82%So``t3KZKJ(bqCOeN$lT9^F$!flnzv0p+%(;>Sak3fFu7ayQJ?G5- z1VC2@Hdhv)rKS*VHASc^%|TJ-W8^+hMMd5Vbk&w4*3T0&a|4Y}AX77SbpFsFrx`rQCt;3G#v$1aC6qw2G!rslE#Y?br!9skqVIwxon2G(% z%@m+_FItRbXBWfxgvs9F@&qEn&=PNWRj~ieC@k*EUGl^U(0#pU&gHj9GCbBuh~T6k zPt=B(U|DC4$G&`a!j!VbGojB|ek5(Qa(gQ%=X!hy4!fAe!qN4U( z2X9S$AV3oksja0@UNZ#+u#2Qx=!_DUkoZiel{mJ_({Gx}aB{1jQX(nO8fi>(-$>D* zL5+GJP`N-2sFjguL4f9TBv+k)bNT%MDgOe5DFo=7%*;F9W+RNRF(_SOnm1ZtQsq2_NXyZ##SDwa#0%BEX{iPv&4n@}y!wc6bbjr$Nea}oP9uD`!r?nmO`+XO7cF zfL=St1lyL((>^rWx8sQu8n>jQ^t{ns0#yIo*IQPG9QzEt5s!4Up@DysH$gy1fK;_4 z9ER;~jF6KQRVX3siMI5z?z$*I^S&XYrP_j-pG^C%mTRZN!%wy3O@2}t2Qkb-KuoG+ zDvlVZl_^y+56L+gLM?KRFk%`L<+*s_rM+D1vgx-brKl2%cA!;}Q5prtYm5DIPRf!1 zvFFW0NwQuYb9a>YO|A!J!uNdMq=$!U+6&KPa;`b3HaqvGld792byYd0RJ?32Qaew{ zCR?k_Cwz3z5!o)jLSC}eSLKzwr_UT*ktaA+9g|?FG{Pg19}t2vxrZwSPpaj0h_|-a z0j62~2VnC1A1aAlJAJxTOEa{M$4S@~6DQ%vt=n+n%de2)=7v0Zj>yeH0sGkk`kbhx zD1z*n>MNQ-RoaJWmB6-TlbuxT_LKoW4gCKG=-%G{*jig!(pp=DnzBsPm1Uu|z8D=1 zrD&|oLv876)R*O=r>PR5ZjP|JbPgP5SvGF0wkLS=(Sr21ox4^4N)!JT<0j~ib7194 zeNqeMdk?N%gB?p2`i#02Zq#x=9{~!ezcRW zWCE+Om(gnkmlh?HBYVO!sCG=W9|7d*)xUwJTNJf?`NxnZ!TE|4?%fR z@^qR8GV~naroi4J?J?Pg$(O$|(?i&yfbZV93$SVGbeMiN7#pWd!}4LHuzuVm9FR(= zHa-=(-U0ghQm#cGV9W5@uknK>1M{`mLW z(wHXfa`HffMH8UOo)--tsqKv|bSSIDHjbTjL5ibte(>ONpFh&plR!$u+40se)Oc{gC^QI|lX3yCG=P|;bM_~EXQCKr; z0@lr$gjF-f>F3L*j?pR4X@mM}8-){%-!ET@Tu*-~%b%(KpE`PVP81TMhtO`}=nYLn zPe_`6+#Qst8t?)E-zqyl9h6e7u|v29QS|yfM7tMf76MeQK?bYd7EufxFoGm_|22j( zwoUF6ri%nb$bBH2nVoEW?jb0!bj6damUw!@TD9dogJ}{HQzlHCQF8P~DVA%A`r^9i zSZ#CAVUAjTvQ~OH(yx~(pN{;Y003HJcnRpUANUl znt}&zIsm7`Ypx;svuOXCD?gW0sOL8A#I-$N!2FAE5O&=ho@Xv%%LJ)lhK$7WLBp|o z+H73hxeq~?uOrt#7_ASVpdl$8IRT+kF(%;pp(EJ0Xo>cm7YeB%DP1~gyjostoHrZm zXHM6aVTzY0P+o@G$B#AKxh>-bO5}NWJbsSWI;+uASAhJibOr2sVb+aB*|58Q z0UJzb>Vy^l(1pS*7L6H&RRY+XOy^cRWU6cejA<%Lj;xJS?TWj`=LB<($@0XSeKzLLaZ0PsE7S<<3`^A;kMcE+ z3p3IBmE7bee|aCK{Re976RVyzqowQ~H6Gh0nCN&l``8SnupSzcJx?6aALIM?Q-G$DV{*TKnDyyrSUO~c zW&tt-kkZ7gTlT1v8?j4RMa>Ntv|9*?a8D3$7Up!>Mg5A~Ye zp&sX01?p}m*=V_e2?*z(vj-gG(R4>P&dX9g(N^=WzvlXB;)iT}9W=}Gk^e)39NBQ` zI@c`Em(!b1Zrs<$I$wEBg{`t5OaE^AY;d#6baF$~wVSwq_y~Spy#@;h4A4VCf;2ZP z_M{h%8K%mNr4vRdNE4hl%$}nCW*!JGnKT>=#}C1x2}9*;qp@=OSOsi;ZQ=M~*t}pC zR+~)1vN0pEa@0utuylo%<4Dia_n9$PZC+7m^N3anCxMDCiU@n8dgn%0_F~@X=j6#NK*?#2D2%aZh9;3o-nzox-(W&U>1NpRLwW#8WlU`9B2cu8x)s&BB;#tFqA6 zT!7Z5d{mXcL|O4O)K$MmQ+*y9s&Y_S`~rC|rG#&z#OG!!@#D6g@HM}WbXQLu zTW$=EdTU0`HU_0RMwVZkmhu#v_ryNZ-Y{qK=}<+zV>!zl3tvrwpxHQYN@`+hV#Lq+ zb$Zp(T)f*o7VXZF>YKsTh!VMAi*LJXdx5Nu2UqNmGHjFaM4h1tD!%756gLMlX!Z7$ zzpt+cPLB;nz_TaJHit5AYOHC>WF^3R+f!Y{2tZ`d^xv(#7o@VHDw}ZaOYxwC&x5^l zg0vw!3RYbxPqnLr5ta&25ujPR(uk^EHjrMjW*YOzML?Zuxt*^b0<}k_q9QN~FI~K0 z`ayrJ8aWmd{^?KHIej)3eKrKEhKF0DUJKK=j${>y(~s$-e`Id^!5Ojl??qUY8(O-XgUI;(MyNH*c4@o zHC};eluehDc#!v!R-mo&9v8`Lm*?c6Oq$@5;p7Utqd)7E-@<{PVXkaQOGge@%{ZL` z*t!`Z?QxfnF>cMX4Kif5y9Y0@_yUKqXey;Er=Llx?!Ox_mRreS%`|G8>(D{SO`!zdWGnKRrY<#^6c^2 z)&muO5h?~?cmpRX8B)NZG7f^2_yr-~$6v1<>cZ*TSHmpMuvk6M;o&9+NebnjV=yn( zPn7zx`s}kA{gqhE-^mm+FvsuUDH=9gTx57E{-fWQ9a>c|6 zy2icxju6Q)+B4?)*9xhI77rbvYxCr3IJ9^Lq8`|yTnM23@8@7PdIHu?o+{V&Kw-^e z^m?8tL}bpWVd~C!V9iQogomQ$*;Bj<3s#A0>x-8vt*cE;Q)wMTk}0dBN{HeFO@ZPd zjt~#3m0GN#(P}B{ek3}xOE>%v0lK5L;bdcNA=+9>QC0a$S7%2Vx;o2IQ=NtC3IcZt z8mn{B*<6a6qD);W;eOb+aRp3<%0+529`h!QhS@9=>|C=Po0cuWLfL87E)X`oe2Kc5 zZCErPJ60|i0H20!i8x#_$Wp-6ixGuIb2#B3AscMeGGLOK+ z2prD)7t=GEz${FR{bu%U3CcX_Wf}yXya>RwO{~1*iIQt>`dEM8@VKp#iXG14ysTC zZD}yMdOC#NGm@Nn_(2IBC%kx~OR;_aJ$F4Y(dT1rc!5wFMg<4sy!copfJI3Wdx|_* z(fBWA7=gOsa6_Vr6NhC=r2;4wFy!3-&3#{E{zn6`Z_Xkt`)mYez55Y1kDZKw^Vbk_ z^S->^(b|V?xDf=m}L_?)?b zJ;KP=$j!^1^ip}g3r86M&9Qm{E)^RDW-{v~zq$p$a4*(nf`owFBR?B%De#fI=mZFAK4FyY7(g|@Ep zde8tWK>N}JMh!CX4ndiJ1WM(#ER>CnXENpfA$aZLq2NqqQHfN=RG_#V`wc%VS_+q+ zkKwVUJ+hsBG+K(WV!U6t=-bRe7WTGzEpj90>tE$2l|xXhvSr?bG!vt@I^*M>+_%BE zGv;Bzdmm!nfY0<=TsnTN%1KvDo{0Ifd2+3pHWlk;OcORd9!rE|&<1z2Y=Rs-Su<%0 zR*j#Cbppt-_KteJa$=BEul)Y`BS&J|paBZZi^h+^iT!(Faqft^22FRz-qhzBf~%@AXXDVH?77)8!T@JkP50%#OU*j@KLF_7f7jVm{2F^VKQ94K6ARZH`Z|?YTjJc%rb)L!HJVd@$$w)FCXpw6bM_(k_)9- zZW?+X^ALhYCp^$0a1}{uMz7nxqz3jC+14ndFM}{Gf-y@7%Br-#D>pNHy#(df2cgPh z>u<`s?W?^`p7595_AyS2)u<)lt@nV@Zt&MqveidQ&DbZJC&x)GvP%Yb(wdNwRg~`V zRK3M7NwwEhkI*xKOjtd1*;^K%;0;tJV8IDOvN-}3%Qf~y$&kvejUFN$!FU4CF=h7J zSdKPoNNu#9#<%+>2}q}*%`;Jfn>SwhRry)xUD03>hB9sfjuCh)H*G^$8jhK5!K%Sy zG4F%V^l{aYF)-i%jas|Dc9t7cm^N<^_VcOtW~IXNHP_V#EeYu=KPnNR&JGH}p=B%7 zo|D?Z4~49R!n4`eE^iQLsIB5-){?MqM@6)BXY* zcVTgsXpoY%=BB;8AI@m82~f!$gH&H%utwfRTXkIPl4^&mm$t8zA2KCGHoYbR^J*qm zGjs2%Bfw!40q&Z6K5C%EGQXI;R$CucHSoP_4;6ruYOFUpNgkQ%9wf{;L_SXtfKF84 z?v-kZyT3BdB2ZjtvH2~1`W(2cq)B0)0{3k0lrfSTjt zsUZB?!&m#ag#zX!5z)w$`!>tlA5Waz^yB7->3Vjt&14Gp&6|V6ix%RS)oav~B+K0g zuN>WxFF;R!6!vPF$V2HHH>C_4N5o&bgF<&-%^=K`?`6A*6j-df#(k&rW3Ps27anGyp+-rkZV#?P@5(sEh7WXPadgRSzTHZYLntM>8Lg# zQ7x2t9z>-O-8P%7LeOjxs&Y{RbCX4++yesCk3*-hq4H^K-5Y?G zRsUu+)*Fl9+)~E_#Mh`V&p|_34qB@VHL*4}z#WTB#$u_kg_&c8HH{gGWiw24tudX0 zb#%p>G6`E2FBCR0Mg4&{&SrYXG&R#XymlRI&z?hsoju$yUxmx*^N2FHzzYF}GCw09 ztSvCn2zr$pM8R0t0oxXgpfZeAj6{?MwJZ$MHUI&N2XFiucPt(lHJi)LNN|1=`BQ(} zU%he}oXHer_O5BEs4Rq|>K4Y7A3>W3h;-8;P?K39PRholbaW;psrwZ(2C1iQaF0A~Ek!+o)Pe2AZ7Y;mxS>G4hXBns16XQ(-3m`4vPFEk#O4@CEnqx2;pQT4}aJmp<;!3dj*tk$%a%YH_$UT zZ$#X)#23rVU^ZbQOr(^YDr?xgf5N08{V=lMUob`hdd`T!nA-1SJq2GfY@|vppGm3w z+}>TwV%n+JItY7q5q2b%O4EHe)XK)iNz3euR>(YOslz?pE>Y-qic}qWgKVN;v{JxB zeJs7^jLcJaQ7IcXyyMU$EV4#6ZmopmHRpK(dzM-j`^(RXOw_hTONd-kVI19$RB*2I zl?q3I8cgB#Wmz4hEf}gkUY)Z+!F6{Xp5PEg4dc{;Qc>X*iPvrcxc~KcSUzYl<_`D- z(?9u07~CK|IGrrXutX2(OJ5$u?L%MTwXYxUef2fIS+W#!KK%r<1`oiDQG+pg z=qFe@b+nYyA7I3v{~jv@*cq1VaruHCI_1$kUalDq)G)w;Q<0kVV;QD0L#Ctf8t-dIcKo?8JZI49S@MR(NR4pg}!y-Z%8w^Iw!ycgZ8CbahdMPenosVk~W8bL=?2UAYPu4(`YA zyLZBD^ay-qwiG9}ZBer)`{T!v9~`1-*_Gk4*~z`0?dgYCp1yGZ{S+?l+KpqY*W%o! ztq3}QNxjBvR27nH*)E7$WNmz$@g?A{A%-BY+7Kr7A~GJ8VlrmxaJG-pzfV5ENbDcj{l1C zI3Y;Sp9@n?)x{tS=7ee_TzV4fQ{z#Y7>mldSPgby&pqG9Fo$BhlSEP1eYyS~$h~06 zdpqfAS7Xb2{~bX0bahNB6dKW9|I8Sje(MKzg^?a+nD~ubgJ0EP9!g8I-JizHAvz z?A?c}Uw@4s*Kfe({a@n2;UAE4#~SIktkuio@kI*-NwT7%JMJiF;;@vx+9t3Hd^?*@ z{iIHeJ@7-ZlyWTRs_q5oG!idjp78epbi9faUmF=zi7>r*B_yXc0C_NB?h*qW$$ z1Kb4bDjQ#&sDgkO5+6yam5O$`v3U`*t)cm9)`q2GiG`u@Wbd<#{<*Tc{bPlxJyMp| z6(&H>!^MCwWLr84Fgv06KH0Okf&>QzSl;mfu$2Zlng+qq^f>hmQA<~0&iOYykblut zWoE^voK$l5>WnQ4@B87oFw>VdKG;2B4$M9qgKbl0W8Ju^81wgkgv0SOcr5r|HPp$gNj zz2~VQOJ>ZTJ$ugVKeOlD>l7{E6RjV!wb3k<5?df;^fi|Ei6GuaV|m*kJ$&R@0s*>@ zJu{Aq2ZGH(QD_tv)+8*gQ^0|&xl10K1gP2KX!DIgk-P`oC^OGnBmauCELT0Gt+WWy z=`YHqUR!%2OPC*lbCs0B8#M`LoT@o^WAi3w{v)R?>2H$YXphhvH&qdn^1w<*%4so5 z<0*dco|TKSe(qG5O&+al$AVcf696GI|9tK|e7D357KeUB=p?YwSh zxsKdZRC?I@KtS47Dz2Np$Ukk5f-?>%zwD_Xo_CfmfHuOW-L*<7xnuY_chO5bAXKsRv018ePHxg^k(&JZ<3{M>Yf1p-wKz*QPnvx(7tMd`Fg$)Zfmioj^mMn+)|1SXj#li^mP)xxFz!ol=?X|z&jA8hAxCLJ?rNGu z6B=qxd6-3~F`B?E2yLLUMe)utF3xgc@#rA&{MC1Am6G6Cm8g2@FZGzoi! zeYC7NR0|8tIbn&Sv$mR2-6|Ut15pbv+6hR-qRAmd6F>5AIU-M1p@q9LXtm&!_lc8s ze68#J^WE?Kt)DO7Cp89M^agQqSKbV`46vNf#*KnNV2lV3uiY(U%!RofMA@G z*PgC*JZzmMY;TcNprhXTM@;JXj+Ek4)ah=8fXv1zQ?cD-I<}8D!H$svMuR88`|t_X z3)4!JY4q6_>{a2?YGrtm^w`B1ho-lLsGM~|&Pk~R&O4#-f}`5BvPHydrAkOdhg+Cd zbX4!Kb-7;Lctt`8hR7hPiT*RMDRe z)YgHSY*-xLp%b8bj>u+3j}dCaS57e80C&upqlC=i#A#fS>SEWtIVxLRJ7uD}{V_0t zUfDFG`en-|JolGPH#reG5n;Ih%TcVOeXVS8OadYhkNMy)28$L@WhF!TjKf^JeMaWGU>qA~8WK znrPG|C2KHtsjz2;RulwEYS%f2mMy!^?Y%t5*EC0R3;e+nc{^t1E3FK>sxu_bm>s^sUBnYN0F8R#~7cA=nFN zkA96Cr+>k*ulM7totv>|{Ysqt?koA)akyICLH46$6g_{0$I)RZc$5ln%X>I^_zQf# zYPn|9t&|sWmz02CtXqw}vU?s`yGB9d!sq+&!;)3F`}tQ$zItB|X?V(O7!euCDO?n` z)5(gX+`SR$K>TyfM?}mM4TYqiFb@#Pw%LcK)|NeJZFdMjvqx8P%aFVg*!sq>IrYar zb<^Ddo&4kH1njb#t|+|jgd%enJ$R!3Fva@>0opB6cwcH~csf@&`s%;ajVpm4H>FJk=?Z%=@l&8~@eP+1so-5pzmErA z$iFF@*L7!<-f%_EX)BaoF+P5J`aZIRJyWz_C_l&J_w(2?c^bz3;m=q)Y7FKN9fHlX zXJOPI{t@ehf!sKB7%%NTbQp)e$r)BInplzP;EB$pCmIG^8=Zt#?tVzMksCcC2HR)O z!QlV=w;27;f5gzg`v=VE*ALS_?1yis}$r4%Dt&w-nNv$w5@7n7o$~H%5c$}&> zDqZD0_6|miAN%{lyq#%W?}6v1&GkLbICWnSma|UVBJZ+`R#_aLP-i^v2n|iha(2hP zgI{9Kd+%Y{u%S{pe5SolvM-j{RJf46-Tvl?_Fj{u@`$~1Q$die0uD)p+`1(Iv{6|m zTN{0ciSvgJ6kwPjfHD;erHtP=b`n0HIvdBAu1AKYn_S~hHOQgKGfou(@J$nt4%R7E zxF(>-F-8weU;J#2j9>3+)D^Ff+PgwT?)#wVn!Or$HQM>9uXCxVKZ<+;RIeO=`>qZy z@netUC$UkknRUZQV#$Edu;9~Av3S@JEE_dKjkNl-;5?Y4R+#tomPMu-6GkcSQYN%d z9)syaKh-364mfcqef~_F=e{6a$Prj8djK0)aguqzCkuA_K(@7;Nvg5w&B2v zrAh{-4d|!9%EeX%4Z99#(6HgN=Q2q4h&@er0u_I1%_(dUQehL;(3Ms6B{BK00J`_zced1D z6L9V%K>sxu|GzDC)B&y4%A&a{vY)}v#R>t=R=9Qc7}hVIhwG<~A^+J!ROP-x@k;^8 z(tOkxWTPT017#Ucke!l%)L?&DUOcNHy^LeAbA>@KS)iGRU#wgqrP6X-|LU*`$^Fk< zM&gYJ8Uw`9O`WZO<5b53GXgU`721N6bb^niIY&F&B8{-x-l#{)tU0X0aM1$Fxx&^1 z4Z@l`gQL+dt4&JJM(T4}qWgrQ)vxxh24>P?g0wqrpvx&i?lPh zk#XBjryFUmQyLzP04cG*UAq>mh3T*t$Nt(U|LH&I#~XM~Fm@us&Ry5|AUfq$3fs(Z z@Bm)rt>nC%(K6+OG+8Q|*Eb;oP4f1X}=;2_6 zy^qSm@~z$Q{HB1tyT7vNmY_&|J?nS~E#TZ05+hYtf>cJXD7xi@?29&oHUik^1$*@Rw6mB4^zss#FErIeA z59Z(pTMHZK&r(}lwi;-_#a08E?D{#=_5U%Uo^1(ketmh7Glvhj}vVxgEcp#2$ z-iR10OB6;$YE)ZcLC306$<0LG<5UzrOGi~^242QT z!{*8b{IF*S4y<2|J+kxBr+DA;#rSUB8d(2!3<;JtI)Y5F;G|Na08?^%M1p=JXf`Rw{3M>!1h^R5&7Bbtgc}S2Ac?Rq_QS zgQmW=MgW=&y3ssP4dR)OTyfLgXm?O^tpM$+y=EP136#IpM}Dr{^aR!>c`S4GK&hiA zYNd44a+?0Qoc))wi0Y3z2EE3`pgxMsIzmCc#!VQ$07&r_YcR8lqm&=f6CFDgEA; zGTH=lh77>SKmDVYeN+GZ4t_FQi^yBTzP$q#j2n|5;km0f_RgG#iU06Nxk-m2*2)ey zj(n&6M$^GV)d-2&(pevVirMe>$J)=v;m(%BNcc^d+gW?0eJ_s!wl&uSQFz)#{W2;q zd!YQH8(Qx8D{C%4XOCxxF5=OF^LYN{RpkA6AH_$lQSiML%71Y{(^+}VPfB%h+!8>-K^2%{E2K4yWO6AzGe-a;6z3mVuWVg{zL+&2Gc)pnN)Th6nQ4NW%8N>@K^ zO;HO?pNS6tDD(!!3OG9;|Ee(b+inVE9Dpgh?5tzwl&aA%zQr+8TWoZ4V=~T*2ewkN zJ;cL%_i=vnHtoyK`s8Cw`S3lAd;c%mlb%0xkPe=FHE%wCU1Ela(-)EN=%FozSJsZ& zMrcoah(srQY@K5Qll~vVbod}F6=33G`A#DyGHJGtwoRRZGuw6};hr5DVjpTk3>h^m zOty|_Rlp>kR?6{Q!?hilbHh;&to3@m@2M;Mx}DmIQcjv{>4L|Wwn(+L(_r20lc%VC zDm_RD%!>yP!a^qFj32AzK3OwI+*P3>kE|57E;Q8~)8(?!ETvcY_>pR&MK;PFFS8xz zjvlH9r)0d#<>zow4o9%wFn5NsXSRo^z#xO(E^L_KPXPUEn_Gf45A+Duv>V_?NY};X z!s>t9xPVRX7U3=jgiUd6rI$omm7)BW7R%U zDEANmlpC>80JtmmF`600o}8wBuFN!Sk5AK4R0{DC`Vb{|sSuX3d3U_)O^U~LPI zkj+HCR~FR*jq+SN1)STWVo~NFfRaFA+KCBhNlQ^P84iaKOsK_W8-zcHKd&W7J`Rmk zfTt`h-`W{3Z&~2Q4GX-!Euc)}8VhInx{D52@C2T=j(DC4JbA7=J*NPxlakac`jL+Z zJg;5C;nilCC1v8o_x`LbZ|}l|nE7#ke7#~d5*=M|_s5^%c>0_spiF)5L(J;;sal58 zbf5as@qc+2bB7GoHT)m`0Mq_Mu;}AqI6Q3`0)M!IfP-i7>ZCmiPq`xhm;>rA`r+~Z z>!|o$SlsWjsr=@Ej++81X9OfpTBH1gEiw*W!NXl=kahSbDvvp#?ss=Io$^A*1=%Q1 z%lr1bu;ZMet>l(qCDbO5hpsbFq4FYu51ABd)8a#v1 z%Cu((Z{**#*Nrawvb9zrRFC9du+_~x^OTh;eW+chzci;%8Nk4)J)S{Ofx~u5fs<4{ zQE~Wo(GrccqM;HilXX(=lL0eDe8b4m_+{~O1?b?Pf5*#vwgT{WcxmN;uuE5w>gsacQQG3DkcziTz@I-m!_^&lDYz#;w?crsLjIlqW>}3rRNP`d zB4hA$fbN!;R_mqLJr#HA9T^rP_jW{TXm@iMG4hN5NwO>-0nvK1o5@^K_IxNyCfqU}T0HOppG=H3^CG;xa1ciGWR( z`VzDYYiJHj)cB$jdEyj;^F`pnOFctqnQfSmj9Mw{If2*}ld2kK&hm5gyOut(w&8DV z3w=;1FBn5&IaJc3>E02lS!S6_K(7&?=AzD;!!I-#rw&^!5}m`;XO{{KsskEBs1OiP z4+ueXV3anI0{Q6s1};FxHHzuPY<)Yq7qOS5QaKG zVORp{-Esq!yL;osJ!?F_ZGmh{2OT6(*RjAz9gU}Vi6(_hdBva0a;qURNiG;E$s;3B zDkWHsY<@-2k*G>b#+K<*b!3?egyDbuXH7xj^i$3#Ipu%?0rzy-5X$bl7>BUoNdnMeYVyOu8&)*jJUJm)>g=tm zm}~(snhNDJNAJFi_MX|^D7@)toH5*R(f69~Ia>v-4v{Fn;jY6_lwC4mgc~DApo?u? zwQW-3;Rml@PvG}Wn=$RZceUJ}E*l_0h9I{@UccqC$vGeS5n0yqI|Nvt-L=+sNAjKf zNE3i&M&$evgVk(l*~HQ6?Lkl@gIp${#BzJfoZ0I2y=MFbY@9S1UoKpV-!^PTid+w= z7WTNa|1j*nJF1%VBy&srx@;}BkDdsRqrb!V_&Ge64XVIHfXT*HO^!Is#4wGB+jsRK zF5bZozwO+vo*+7QE+9xX13{9&Oz`}C=|Tlc9uBfaaPQ}zaQlbvuw%|Ft;V=^o0{sT zzhdlI?3y=E|K^Kdeu-n-x9iubDQ5N~6&PgZWWS6~TP+{27&A)SZcLM5ur7Pvt45Df zZ*EPOk)OXtu33H$KVBywzi0XkO~YYtm};3K!2`A)D#DmJAES>nN22nAgrzPu71<%d z3epT+AY&fY?_Fimvquk6RR??17r*%mp4YD+-OB?Hg>h#G1tHOj3KUmm<`3;15On3L z_MEBcAZxFe`>x{6a94FmtQuWaFyz|GUxRz$nWRQ&&(F#GZyzRAU4#(KN2#544Ws@A zpgY@VwbhkpD>&B{qpm6s^#Zn>DkM<%t+$E}`M9~d01Xwns)y#HnGzXvYel{Sb90^m z^XnI=&3ul!tPC_{zeJO;YJQzxt9_k;nylxjc=bd2r>>C%|a5lhKf!1G7!aIEu&=Z^_|s4 z^)#jyox-%YO-n}_)^Ng+2Y=0S5zuI+IXGN_ne4a0PpScTA8k{x#nCNHpFL=r(=^a` zB-*e}<=>kR3o1r7$@B_8S&`_8Er%{okvmd6wLeGe!;9 zpjVE!PX4sNu6e_aRFvuRy~`#{Py-n*evLi_WB&3UCjQ|)tQ|BS$yaO6kZw5XismzJsQkrBK-U4ym)%kD%RRjQ_7)0`TA=)x zy|U%zv%aYL%>zwm{80L{FvDN%QG7yx`8OL>To!PAz{yZw6q!3|@19aQ9;mYA)a)Oj z0NomF0ClOOm!^Oe345dlnC+n|YcG|XH93c9Vm0Ji;HWhHTk@~DsP`}jpExqkHd(<9 zr?)U_8&^-^plRe}ID$DHXUeR{_Zn0)xYp}Xaj;qW0?K6EH{Oq>ksLq~K%lYS?Z zv2pMs*UJ}=-Cc3^;9dd4VcO#01mdzOWA(6#r96AFOD747d~;w%>sTJtt(`Ct+ony& zH!D`+`_*f(X6!fxZjOsHeSN(!?#+{?;J_SHY@0L{`)16;`R$*>=DS}IdE=gH;a^J? zkQo>R&#RYlWc5nzFVB-rVVxfSP1ntVtpze)ex3hLRvlsa0BQ1flZ}#DhFre^s~>*E zkMg(dGMS;{*GCpFgXQ->38>j3FDO)pot}AmW5={>*%5&ktgnE>3LnvQzjD|BZR z!c-xX|g$NsrFPy$HcZj^hhodd8tQHUmxZ>DkR?<+J(#`rmYSw618YEv@VWXo7QntpIRc;oIrKJ_gO! z2En+cssJ6;MXKUxDa%E3X%5;1oV%-v&{dp^76IhC7f(=|@fZyP#I1Rm=qSuaC&617 zbwkbz)Msa)j!W6~QY8o*bkv*!G@W6ohT#jtYXyv3=y@v}g#prJ z&&B~?wJ_sOVa13})~T)Dm=yKlrTG&*MmWyi8}(49CaDCWN`Ovd+V-G~kF5>?<67Tf zwS(leS8qJ6P16O~Q#G#<83NGHo~X>HQ7VwOhw12i{6vjv=q5&SKYbAxk zqp))0Y&V^cMMFnn&O80Fw%=$(9J`71la?sG>Vxc~)(Wa^=lsxlJ^(!zg3$cC0Nybd zG@KCT`jeF`3$$DiW_;ceWoPAP`}H1PeJ3FOv$Zf~Z#142Ap6AuH3HUoKicTe%=yh$ zWrfs9gEpFh$tbgylHJNld-$9PWFNoL-vDP;7)%PGRPp&W3pJtVI6vih1m;GkV3qwL zKp1#%iZJ8^G&qFl=1kVZA*oiU2nB8Wws7(PFkh13P8R;;;vG#Wp}~~Rk3XsF-^@Xu zD!5Mg@Lf!P_b-?vuiu(ML*ak&j84l@%EsPrrkf{T3zJ(WY?)Fto((WrW$q|p-;>8+ z-k70SFkV1m;wY7^kzunhOWv+(dR=~vJ#V&E*e9kxb)R&NRT96K@Ac<%=HrV6 zOYr@Q_3$}&4H0)Okm~A&yGMV-=ZofRdJfwN++6y4zSI?yNpGD!16zd2-a7KNdW92| z2#}>Ap(qavm-{eXjgjcYm(2}GR#RLGa-_m|>10eG76gXE>f7(};G1s|b>|L3u3b~M z%S=bI-CQ9e*`7X#zjF_VO&4JEm~q%UZ3eban1oa7HtNA@=R+YQNvWCvNw(a>GIasJ>z)rD^}nTZXfCSmXRndpss zipN*1RE1V% zl%Tn>1WgUXl!YymNjKFLC_`?pG+1&+RUvw7N`RVDpsE<%05t(s_0Cjd1VpKAW1DZ3{7zx(p^3(bZR}$mT%n^FlU$hP$cQc)Ks9+sDxgrq z%MoS%J@rh#kVmp>f^&DQ(Hf!6qqa&OryDFReGL2e|4D-s7K|H^tggo@i9 z=yHqJti0X;*%W<3kz?V2a%XQH<0d$BgY1b)($|lsSBQOtN9XS-d(OFTr>c(*H+g*- z1LZH3jAw-UuQYo_0(6QCPF0hC4jIt*B2TKXI{BD-c(Ux0Th5w*Q)}(32enTwn`=** z(lnC~-qV2DHM33h(2MfBsqepw#h(tqHwzad`MxDmEv!{J!PM^shMUPJAJ9i`gScb#gg$OF<+SA3Sop>=g<19 zwDwV&X0&?TWSG3)4+rKi#gFSZ;lPq5TIy3)#$K=j))1+xh7EixIb+{<@51?-X5N?d z!p(*3lxmg|Z-2cOYvlf+(Nj}If_m4`1DY1S`L4dmbZ|%PwL6+(Qs5h)ElCmxUZbV{ z!8-2UH$C{$#a+|OZ|vCzztiW`%Om8%W#kBWbE2@&PnfV&t4M#Uqu*-A6cMB60rc}J zkj+5H!lioU_20*$sbSVBz}Of(_433avt`COx4iyr>F{~7x6Y;B=koe;)1!0Yp_R+E zHPe@z!=xQPZjmZxu~eFCCQnkY4^G;y7tr1^X$F2+xC+s7-7%f1RY*X-mA8U(rC$tU zE?eNSy(eyb`E_fkyT|UH?(Tm}59t5U)!O)@fOB^rKogX?-cAq-YvxEdrv|$!1(*ep z3C1nCuh9JZ1v+vv(J8C#mC*C&Ptfx^Q_6*xXwQ6s&eyNdllvOIdD#lu?E=SP5PN>m521!rQ5FV|Tjhrkb!(|^?9n+%Z zMh_PTam!AJRyd7FpN9qxXm~^!W=D2Gnyr>6rBD7XcO4a{vt0?xZ`w=>up=rBt-{nR zT>P~SQtKmcc8F{s36FrsFEnkYJDmrl>6&Q4wn<;QGH;Yu_HJq*UL>VHeWJ5`gJAdT zDNK9!W6bz?fR5f1lo?lLHg$^TuFdQ}0P_Y6!t9UwW7vQEFPQvKf5y^JhhWE~>6rAl zf566(6EOFkkFlzsu<3v3hu!augwOV$kax-s&DR4^eNq7Iq=1>Q;PRjBfV(khI_HB* z`FhR#_tii0Qt@gW1+#V>9DM%ic44(B0vN6q8}7&FmiMJMQHe>OmXcn0>EE{55t@z^8)vT=$m6BE^J^FWd<6oS2>wivIi4ONo}3*S3$ zu9oyb^R8S?pff8qqFMBIo?gCO^|JP)5aiECPU``=c$Twt(nJO6rNhTy-^>L_xo3}8Hl8SQ4%C$< zc#!Gnix8W@eyG0J=T$70o-L7@C-S1^zeag-oRl5$XnXz?EsxSspOmERnWYR53fqMZ z*U}e)rs@JvYVR5UNFL>H2#wOPN*=iJQG;c<-o*H@|5#>ECpC9{toII6CfXC9t}TL2 zc@C}d(P)ho<}0OJYeb|T`0)U!Gc->7nlvnDq)?uft6Cm{{uN#!+HTM^W0lawDL{9G z3wT5u;M^k&@6}~1 znLcBOI0DY;LbhTU)=4W=x_cqzu|AL55RUAIs-Y9pW|*{+FVCG8kE}d66<95QS8eiR zTt4tMmW>=QKs*R@hYZJ*kNYWGUNUkt<_{c#Ss(Pn+z&p%ym$Ly)n~)8?4yBL@@{`@ z95foMJ|2wYOV+^by#d(z$q4NIa5NlO>{my*($n_H{ox*JPB;VCZ@q0NDZa=z$r2m!p3byz6lr~vXwM^s+-$BW}uczo<0 zGB4R`yQ!U{=~2o09??sq-X{=M!kX&jk<)@~4RLes6o6(WLN~i2OAl>lG&lyKTz)56 zI02d)Hd{&r6>hu?Ri)mU=_NdCXm*Q2k-QgqZ_Y3n`pc=jqRWoRIDQAO&)h{Ro$uuJ zc0YQ|I4ESmt}x$`pS+8SgZk+~)XahXb^Lg$FzjU`hht{HkF@nLchCT3t|kLMQuF_L zqiIb#R2ejfJr)Sa^Wcymyh51yve84aeAG~^5kMzM&llFm!IlHd7736{7v?9xB8+?0 zl(9Pavc_bhro9ks)=Zp;tuqW3y=3qx0qN;dzAsXx2Em7l3kER|ps8d~P4~c0^*`GRzW81c<&U^Xjkve$9 zcr&(j*z(~)DK|MPQ`QTsX6uLN0_<0-b%lU3C#7i)XExmgi^h+W_h~$SUcVKuy%<80 zh=eN-kZJFYe2)-Z+xaCnj+v^J49%JDUA}OmxvBAQ3DE!C(Nt^y*8ojWetQ5|nvJHC zY_t?-qrEr>-9@?Dwy1uXf}*Ghgep4d(M+Ro0;I;h3qWg_tn8{)vP&0olv&Cvp18A5$ugKNgEn~+={enz zm?S%}09R;$F!UgF#73i=;g6BgQZ7fUCb=g}-o!gLI#5Eer{aSBWIBGapG;$S_PYqw z%y(no8{u-{F#FEZ3%OVA^?;HZTP_X{RNV5?NT$qFHkxA1#o-wSozj_1)dY9kbtJdd zEgVfQq00U^?oAt1rYh4vm%3;E9ra8^q4lGe0pQ$($n=Y3r)DyF@=YnJ?L3ezn_K7| zORSqT6BFO>kJ$rG5f<$u=tZfnjyGp*f`A*jJ|LSHv7K8rIlOp z#V8ZpT(c9d2Y*w6Rw))n(c>MCc2*X4vLSgV>-aJQV{^_pYEEIs?^bwu!B*31 zc*C=bXy?WlX2=mc0j0QfVKKlTZhW5uy0o~E>|5;TT9IRkFfsbTkd?XWN- zE0uThLj`CuXJ!PJl0Ewcs3PK#le2nnPcsJD6hlc(C^piL;<>3HU)9}9jFsF?}5XxI=vK%@@Z z4)4t$jwg?wZ7_UZgbn-NvFafhiQI0qX7Y`mrhMON!A2_P% zRkdth_rLfa^FNgLx8Fc{&!%Z0z|V&d+Sk|C{xeYZtZR9}>Z z`n;EVkk|0)nSyh<>@qA1RI0)lb+!S54ew_4ahA^+z(|3%%DTd(j0_J$dAzW`hpE~I zA$w-agYAs=*BJuX&-Af7=M~BlqEY`a8Qm|Qp^bKav2og;qL!CgW_`zk0#`G3dQV3(N36~u%GFU)XfIBW4 zjUjTQd;6+}nWj3t_%g0P(9!EcTQ{9hV}FzC05WmSN7RhOB(?2CR4TfI;xz!^#f1mR zy=^G3_Lr>?GDS|&v1d*RV7GjYBiw9p)C=1Vy=8&j^A=<3#{)6)FCR+jJs8tI z?5}APW`jpy;fDjTZPXNeH)9E|tk@2(L&x##l7RbpOU-cf-2I~-pwSiW$*H?KxmJ3? z0R_h_RfSP?LMnpq@1yuf3jt>jG+*!p?h8O$M++Oa0$hbT`KKZKoRub-cgd#n>cSoL z(sj-&95t5m{k{nT(n$)0+^n9RxQ~p}mM9ibt#%An0~)@!^#a5M)@tuS!+>8{FqxdT zmv{in-ZN931!x&*#!W8kvaPxnQc~A!FRxipGCE}w=J-8RuIcDTHqGj!nN^EtxLUq8 zxP&2JsvoL3`1j<=bIv|(r-Myp*X)sZ)l$zILQh@5)qVT5Tps!1pD}LWM;J5U0}Ort zk9spt{j8rJLQVMK9c?KPm}!g25pf<;vFF>zpsD0wKbi-Ia|i?i?gZMc0t9z0uzJiWSRDNkk3C(G=wOYSv_#~F2jFFpFJ1)t;Pd&jg#nLMZwq>QA6>fOEw}7kUeuTP@^j`RU`9QQL_cgVU_9_ zsjO2{G{K(g6CQSQfQ3$VtxuogsfP!W9Ubt<-A(;JGKF1NJxYW3og1oZVcUn(iyX$G zdmhyvizki4BC2bqO4TuW0?dSYFOZ63fq?Wc+jbyd{>BWc?n1BKQ6#7a>pb*{hs#B6snjDX&S5MHACE%N# zA?uld^AiDnd7)*sy?CU`gL-NdWq0d(^cZ;j3_Ypo%5VvM8aEY{tfSl2F;tEZIKE`Z^L85MoQfWuE=M$o-ot4z<5LT5%W+V)#%?NLLKa4zB!A$ zEB1JG@;-_#I%r}@#bswTHmbQIrM&=j*+l_ud1Q-&i<;ZwTOPv5JoS;8deq=Do{KSE zjdmdl5`}M^_h^+$m%k3LIQ00j@BLJDmt-zv(B)EIy>{|MRYa_s9eugf3@av1!Q2s} zF?qm1%osdWgMHVHn}q!{P4UapbvQc9Om3(kr2lpwuYR{x`^w78Ub@Q9xoL`a%U#1I zu=Ko(>XMtU_~{Ty$**=OJ!X%Whi~B3*Ei8BRYBQVJ2f`yv=3ANjSgn!S$N3CBd^7M zXH;9s-!Fe_hg1cncO1|yuOA}PQETa=tq2a1JU(?B&o4d@Q1H|t6eK$j9AGQ*BqZ7c%$`(yDJSI3@$5=5kHy$)D z7-oe3(hFtx-1#^#e=&aEuo1pjF5#hz1M-4}h^TQ-oKC6sRkj78A#gu?R)BfEdfhPO zxk>;mSMJ%z4la1@?}sO@PB8!a5Oz&7!OkgDaA4*vd_LI(`=?IFuZvc|YX8@$kZXx5 zBTiEyJWXS`c&&H%$0;FUtQdzfINHo$0VDW0SQRjA=M~DvSjm7155Wx25Y2MAW+a3xKHs2k$rP)`}J5Kc}2z81Eg7Z`~0m}uP?s^s6f z_;KIt0EG+B0(@liny!6*dU;b>vwF@{n2t5lt7QY_BfrM>$muWksgDTH z8F(PcK_n`5Oy4L+=mxoa&C1yFl9CVprl-5Be^XuQ^ZM!n)YlfGK>(UEyQZ2vGzw7H z<{L7&CSlG^PadN_DfwT(IoMbY!JO=quldDjd=0&u%Vj5Jx-U~WxYL$L$LN7yr7+_L zg7C}7XwG~rO!}!XXJOA-kJ0h!5jry->5vKAB|M33iiy{h5+3leeb6O4BQprK>42@CA>R;Ry|5RhKTKTuh1;B}~9s%M zLwQgT>Z2mG&CwQ5Z|8VbVo-O?;SjbrD3Qyza1u~<)NyJWoj3bL=>%bml*hc-c)@Dc zo>P!2M97?L?fj8*#ZG~MW8!63UDP+?wXm;()3&I(BtPrCv$9GW^AM0L?)jqpu8-<( zX_rc^b`5mPoE+nIAxx{bh?KMGj<;m;WMr*|;<w==+ zo$>szxd5*t8qWLbsy!nt=d7^VbH-!WO(|_}2)nxIhmPyQN-gBIx#};V?E`qG19p+< zwF_5(ra6B#&GgM3QDg0aHrF88aNJ}A@)c$+o1J$wdgaDvyQAWsE81N{(c>GV6Ms3k z9MnRTv0_xwFawg)qE+%ed@p$O_XH$qP%v{SORh`RanW9r)!8$qO=;%u543O0!@Um2 za0O`gjyuS@^mmv(_)|<9@~Kf4OL;t{-v^lV{yW;&Wlx%NJl^O!B0X`8 zy5UXy=snGTWZ!ebyYHy66}@VXtXzX1)@{U*kDW!#$;%ou*Cnq#<)@6>YIcp#%7UbXDwr0JC>;u`whl6aGww^}Ce>n{WsqLq zoF~w{PPb6)QL7ECY=*@?zR2-%M~=4;l2ExvQ{`TNE@1jh{_aQezL3y8W9u|Ytv&hN zSpBx}J&QAAlzt&3P98ArY%JdWXprS9%0~(&mPN4NBy&hx|(04qf6LykNl3FSC7#t|Hc=JEfe;aHC>uN zM^{Os7^0boUBWDQv)1we#L8L6s&ns432*7FKaZfV@k#7| z^L3C7iXfeG<_2Eh7G@@3^Ww6lG0Y%f#NmdVOSYO4l6T%%rB__F%~5`tPINXXyXmH8 zH;k@g=q(vC6$50PZinvsF{nQ6jj~^ybv2*&18zs5^QzqF#~ktUt2_F&%Hslt-&>>dq^p8- z{RJ=e%;>%+fb9^CZYyD?7Dg5!JYvwx96eh<1>s`(o1gu56O96X2uuNd2RL zwMPQn33||)dCg9<82S9l80#gAT6Bb^YdM{D)efz$k%&EVPG2i#7E&+G&5^74f-tm` z0?@~9Bm0b{h5>YYMk_luq>Q1+6@bmWYp`KD3F_wVA%G$qUcIAiz%*YMaBgu3L%lpF zhDa3Oc0_@>R7y82QOa-vTSx3RnWnjgtIlZVOT1nByh7<4{NLHk45(1nq= z3lnZmidQLWTWX3{>@|EnWD%Sjl9ToE?Z=F=65>`S)ktMh0-~%fV0Y#uem}5BWsKCR zGdYJk@pE4t#CMz5YZM#-n@X68AHS_Zr_=p;CI* z;SsL~SyUZx(=3tK>)N(GSUE__u8;d+!r%XstUqA-pWj#Q^1_b?Vc|!Ev2oZ$95Gph z6Z6+%^&bb}=kZHrLvcm3WfWfiWQE+L_Gr5jin60lDEQU}t!Mm!JMpS-F8;w5&-YzZ zpNy(w0?^-DA@5sD1?$=~!ko@}pzgdo>Mwhs<+cxct-}CEc~17>XcU%Pe%%R$mu*ok z&ne>b<9PP-Ej6s_agGMuWV3XRQV)!h+wMsE`6@ClTBA!CGQu7Tn+rA^4uxsc80f`$ zOC8gvGJ~5~&RJ{i53{vYe#KcUmuvy&GI`ze&)eYHFE{0TtORIf!w5=3Z&;EZZgCTC za1YUQ1gdZd%m_*_)E^EZ`rdFOt$z@xo(ehV?C|{eJ9v5d9-dw@*MXHh4P`pM9*0V9hB*DmTo=jM7nN8qc5Ivl~YkY$0Xx(e-N)bn6CQ`1xpQpB)^iAo0}G z9nPmt;?9vHu>9#K_+7b%hYpS?i;UE^1_8D>AVAHynuR6SMHoz)z7!==&Swb_a*~c? z$gRogI$yxS5_(A#(ydL{B7wcoTgU+AZ=U{0yLk^8=C+7Cbrt^KoX{2%88C@RhfkdL zn#=5c^~{5Q=*15hP~oF2y3sB|NJga5sx$Y}P~=8+p^QY8+(V4hD)A74Ao~NNIB_7qu@hTkPP)c{|BRz*IWj>;lAId$#k#XM@Wu9R=A3^DD zrBv|^!ipQ?W6Cq@xm5Fw@^k7Fi(v>|kwYtVXN@I~~eInk*rgJs={R@Mx7lupO8)Y+X!cb`>f|oDR^YR5+ zA3u^OpP-8-K^+g%&?dVSWpV5q*9RDkoS_;3Mqd%N=+ctz)CmSWhp+FvZy^F z868Qfs(t353cV<*Wiu&u_t66g9^xS;O%E^H2ae{5Wg^M0~FyVymXFLd@tc%lWRB@0gQ8htn7XL4m0%;fTAnvzI@_*foG-mH8 z`Tg=7X^-k0igGJI?Zf5Wc18XjH+4s2xlE?4UJnAo1b}8?XRWSLx>{T!HKmz=&0t(Q z>e1Ji;Q|C8hS4&kjxCXw0(OXfrd}nsU;T)spAN#hvEwnX|EE|rax}J1G{IhzIau?V zTvUJl6wChj8CL#rAUyW{ipOWH5c}O#DNBR#;6%_GhUqPo;Iew?b9(OeWoF9t6-x{;C@? zzQ2x#Utd7cSsQfQgreIr45?pS#G@nEQFz)x9lyG4A~ccn#$w0&t z^oBk}dr-1k&$3cU{_(P!(KEY{DJz*L?`h&n_R0I|8J&095(NUIk&)N|q5pm-dDuwZ7`}ry0 z@?54OT8Kh)nD&;d1ZbGn!NtQemfo)e1-PT5w6#$upj;Cku34E~iOJ}Z`+}jy^^w95 zrAnj)0NWij_2EP*r}n51Ccz=oF7gjV1#L8a1CVNd9}mqRDA{>--$n^fo>Zg{Wdka) z_0(9l+`G=&*5D9V?tK?MFjQwpc|Y1cW8Z|^2B1{l174dkp^p2@dNz{D5I-0HtsblrsDBwsVe5$%Qt;pdtrT{TvJnMl}pB@n?l}&?I!|X>F zc=+ok(-beDo*1X?jfSKc)W!>r#Dz;GA=P@6+>cQaT6xgJnc&|C&}?(?yG>W1lr*pj1+yD060u zgNsTX0`#QMK5or^kul?Mx|^HKdP)j9QC18T7oa;vse69f65E!dK%#6GSCVTu9K(qZ&<`Jm6TQ==gKpvl@yB(!1-A|wC_%TZg?rI9k z@>nlBw@x2^HYbE@(P*NMv?maY(hcoz!>4aTA&L$PAyC`|chDW(7U1FU#&Fs!y6MgfzdrA%iJ zyWoPW=GfI-7glo06%Ci=@3Dh8XgKMC-W&41{pP3!N3~~#;heBX!7mR`c-#uLmt4?&UqIVV zUYE;0cyZ(|UjJZ;*GDaocidL3NxA9;1Q~+LDuI1W#(lMWQJwKv4@BDp3|bt+@%pR) zFacNCbKxa>1>oY#4hq6~=d96W;je)G@;7s3&y@o9OcF1)-uqGeX*axFou783vRUlPX@=qgHxB|M87t=@$eL^3fHZ?yDcx<5ji)|3 zQN7MzK5#~!jkn%cmGrrgO|@Cb5YNJ1SUc-G^*rt z_O#pO-wDuU&}?gP@$cX!Ni`ZD`MU|!g^}TEHr62j=cNF0jJ-92?%#p^g)>O=^~8}4 zYu=8@vj;s*Dki4oP{+<5Gy$3|4yL&iq^At}5EBQyr=Y!W&GOFrigL5RX{)Z<*7`D| z7r8k=b~d_%;Wj_dK>Mpqbi90prZi!?37p=0tQto=%0jyURI8NtP5vd4c9HG;FJ!y|sOin9M20v0%PK`1JF56Vo|&v9D{IX)=lI5J zW5h(FU3Rywyj(n&6RFdHRIiFmK%2ZC0(7Tgsp1()=Nb@ zTHAMppO?SM&^}HYYcl+0=Ky<*IrzfKDLSFFBxj%^JjO7Xa{dxZd_z$zt0yJXE`L`Q zmV}y!6pcVw%&3#|_Si2PH=O)&*F98s{tE9H)%mFOia{-bAv{BiUd>_Y+Tv9gn53N< z%<(a0Qt1|{Lo8fX5>67>nck?C4efzPD2k$!aPX7Om^o~iI;X6jJPE7KCt{tcIlRB$ zj*LHT)ggqMv{!yRgXS}_C>9`v-xZYa4Z`hj?D6;~4>awI#q;B`$(=Zl?!$8KcFT>v zJrwPKgrZ}ofbOr}DEh)qj=re)L4JRGfPi-(%6}0++#81HClgTnr$4H9`J&)AM?Co9 z49fp-1MCyze@TSnA}slcypQePxc{Rg9{=Wsd|9P`2B2IviGrQpXp!Hg14ZExe-+Rt zNHcuy3X_d5@`8?HKRMy^vcEi7?XSp+E9Lw9`yBA(h#OkvvqiAMgtkgHwh{sE8iz2A zJ}PX36l7c76Y(NQKF8=wvM!=qfS-Vk*vp!8CWx~Kn(dEd&UDnSKO3Xcv<>dOmSWU7 zhf!62Kq$5@TY>5Q`zcd4@7D_^z1~oeCO~rvjcRe5Y^K9|*DjRCu(w}A{uqfEC^?WN z#3xhOTas*+0?ZYu!jSXiTF90Ah zXoAj3ln8^SDMt78E1G|9&J)bZ6a2cIrRJ`60@|${l6!Pt*>ls&O@ewpWYJ{L1ZW
pzpa&j#%675U9?`lqSf8XEMdtPuA(k8 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�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 IPaR(W2ab$oc>n+a literal 0 HcmV?d00001 From 023f74c30b8d447ed8b0335deba48db3feeb0aca Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 3 May 2023 01:03:24 +0300 Subject: [PATCH 204/212] feat: Implement centering after find. --- lib/src/main/kotlin/controller/Controller.kt | 13 ++++++- lib/src/main/kotlin/main.kt | 39 ++++++++++++------- lib/src/main/kotlin/ui/ControlFields.kt | 4 +- .../viewPart/nodes/drawableTree/DrawTree.kt | 2 +- .../nodes/drawableTree/DrawableTree.kt | 8 ++-- 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/controller/Controller.kt index ddd39da..49ba36b 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/controller/Controller.kt @@ -1,5 +1,6 @@ package controller +import androidx.compose.ui.window.WindowState import databaseManage.AVLTreeManager import databaseManage.BINTreeManager import databaseManage.RBTreeManager @@ -81,10 +82,18 @@ class Controller { return tree ?: throw NullPointerException() } - fun find(value: String): DrawTree { + fun find(value: String, windowState: WindowState): DrawTree { val key = if (isInt(value.trim())) value.toInt() else value.hashCode() - tree?.find(key) ?: throw NullPointerException() + val offset1 = tree?.find(key) ?: throw NullPointerException() + println(windowState.size.width.value / 2) + if (offset1.first >= 0){ + tree?.addOffset( + windowState.size.width.value / 2 - offset1.first, + windowState.size.height.value / 2 - offset1.second, + ) + } + return tree ?: throw NullPointerException() diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index 35e5cf0..9bf313c 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -38,6 +38,9 @@ fun main() = application { val dragState = remember { mutableStateOf(false) } + val xCenter = remember { mutableStateOf(0f) } + val yCenter = remember { mutableStateOf(0f) } + if (!closeButton.value) { Window( onCloseRequest = ::exitApplication, @@ -51,19 +54,19 @@ fun main() = application { topBar = { WindowDraggableArea { MyTopAppBar( - deleteButton, - saveButton, - openButton, - createButton, - minimizeButton, - maximizeButton, - closeButton, - windowState, - controller, - activeTree, - deleteTreeState, - openTreeState, - createTreeState + clickDeleteButton = deleteButton, + clickSaveButton = saveButton, + clickOpenButton = openButton, + clickCreateButton = createButton, + clickMinimizeButton = minimizeButton, + clickMaximizeButton = maximizeButton, + clickCloseButton = closeButton, + windowState = windowState, + controller = controller, + activeTree = activeTree, + deleteTreeState = deleteTreeState, + openTreeState = openTreeState, + createTreeState = createTreeState ) } }, @@ -83,7 +86,15 @@ fun main() = application { } ) { - ControlFields(controller, activeTree, deleteTreeState, openTreeState, createTreeState, dragState) + ControlFields( + controller = controller, + activeTree = activeTree, + deleteTreeState = deleteTreeState, + openTreeState = openTreeState, + createTreeState = createTreeState, + dragState = dragState, + windowState = windowState + ) } } diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/ui/ControlFields.kt index a778730..d29a246 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/ui/ControlFields.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Popup +import androidx.compose.ui.window.WindowState import controller.Controller import viewPart.nodes.displayTree import viewPart.nodes.drawableTree.DrawTree @@ -35,6 +36,7 @@ fun ControlFields( openTreeState: MutableState, createTreeState: MutableState, dragState: MutableState, + windowState: WindowState, ) { val addFieldState = remember { mutableStateOf(false) } @@ -100,7 +102,7 @@ fun ControlFields( addFieldState.value = false } if (findFieldState.value) { - tree = controller.find(value.value) + tree = controller.find(value.value, windowState) findFieldState.value = false } if (deleteFieldState.value) { diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt index f3d4b7c..65b0db2 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt @@ -37,7 +37,7 @@ interface DrawTree { fun saveTreeToDB() fun insert(item: Container) fun delete(item: Container) - fun find(item: Int) + fun find(item: Int): Pair fun repositionTree(xBase: Float, yBase: Float) fun addOffset(xOffset: Float, yOffset: Float) } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt index a8bd674..ce6609b 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt @@ -71,20 +71,20 @@ abstract class DrawableTree< if (treeStruct.find(item) != null) treeStruct.delete(item) } - override fun find(item: Int) { + override fun find(item: Int): Pair { var currentNode = root if (root == null) - return + return Pair(-1f, -1f) while(true) { if (item == currentNode?.value?.key) { currentNode.clickState.value = true - return + return Pair(currentNode.xState.value, currentNode.yState.value) } currentNode?.let { currentNode = if (item > it.value.key) it.rightChild else it.leftChild } - if (currentNode == null) return + if (currentNode == null) return Pair(-1f, -1f) } } From 86d1fafa6025261846eddd4818be802d8f7cc1a6 Mon Sep 17 00:00:00 2001 From: Artem Date: Wed, 3 May 2023 01:33:48 +0300 Subject: [PATCH 205/212] fix(build.gradle.kts): Add jvmToolchain --- lib/build.gradle.kts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 1c0fc95..816217d 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -5,14 +5,11 @@ plugins { `java-library` `maven-publish` kotlin("plugin.serialization") version "1.5.0" - // checkstyle id("org.jetbrains.compose") version "1.4.0" } -// ? -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } + +kotlin { + jvmToolchain(17) } repositories { @@ -41,11 +38,9 @@ dependencies { implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") - implementation(kotlin("stdlib-jdk8")) implementation(compose.desktop.currentOs) implementation(compose.material3) - 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") testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") @@ -53,12 +48,14 @@ dependencies { } + compose.desktop { application { mainClass = "MainKt" } } + tasks.test { finalizedBy(tasks.jacocoTestReport) useJUnitPlatform() From 218d3f353936bec546b899e1e3c24d14a7665610 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 3 May 2023 01:45:20 +0300 Subject: [PATCH 206/212] feat: Text inside nodes. --- lib/src/main/kotlin/main.kt | 3 --- .../kotlin/viewPart/nodes/TreeDrawingUtils.kt | 7 ++++--- .../viewPart/nodes/drawableAVL/AVLNodeDesign.kt | 9 ++++++++- .../viewPart/nodes/drawableBIN/BINNodeDesign.kt | 15 ++++++++++++--- .../viewPart/nodes/drawableRB/RBNodeDesign.kt | 12 +++++++++++- .../viewPart/nodes/drawableTree/NodeDesign.kt | 2 +- 6 files changed, 36 insertions(+), 12 deletions(-) diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/main.kt index 9bf313c..1f87beb 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/main.kt @@ -38,9 +38,6 @@ fun main() = application { val dragState = remember { mutableStateOf(false) } - val xCenter = remember { mutableStateOf(0f) } - val yCenter = remember { mutableStateOf(0f) } - if (!closeButton.value) { Window( onCloseRequest = ::exitApplication, diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt index 275160f..813eff3 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt @@ -31,18 +31,19 @@ import viewPart.nodes.drawableTree.DrawTree import viewPart.nodes.drawableTree.DrawableNode import viewPart.nodes.drawableTree.NodeDesign import java.util.Locale +import kotlin.math.min import kotlin.math.roundToInt @Composable fun displayTree(tree: DrawTree, state: MutableState){ Box(modifier = Modifier.fillMaxSize() - .pointerInput(state) { + .pointerInput(state, tree) { detectDragGestures { change, dragAmount -> change.consume() tree.addOffset(dragAmount.x, dragAmount.y) state.value = false } - }.zIndex(-1000f) + }.zIndex(-100f) ){ when (tree) { is BINDrawableTree -> displayBIN(tree) @@ -109,7 +110,7 @@ fun , DNode>, NodeD : NodeDesign> di } } - design.infoView(node.modifier) + design.infoView(node.modifier, node.value.key.toString()) node.leftChild?.let { edgeView(node, it, design) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt index b4edcb5..388a012 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt @@ -2,11 +2,13 @@ package viewPart.nodes.drawableAVL import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.style.TextAlign import viewPart.nodes.drawableTree.NodeDesign object AVLNodeDesign : NodeDesign { @@ -17,5 +19,10 @@ object AVLNodeDesign : NodeDesign { override var lineStrokeWidth = 10f @Composable - override fun infoView(modifier: Modifier) = Box(modifier = modifier) + override fun infoView(modifier: Modifier, information: String) = Box(modifier = modifier, contentAlignment = Alignment.Center,) { + Text( + textAlign = TextAlign.Center, + text = if (information.length <=3) information else "..." + ) + } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt index 2c96e81..30ea9c4 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt @@ -2,19 +2,28 @@ package viewPart.nodes.drawableBIN import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.style.TextAlign import viewPart.nodes.drawableTree.NodeDesign +import kotlin.math.min -object BINNodeDesign: NodeDesign { - override var colorNode = Color(208, 223, 252) +object BINNodeDesign : NodeDesign { + override var colorNode = Color(37, 146, 110) override var lineColor = Color(34, 35, 41) override var nodeSize = 60f override var shape: Shape = CircleShape override var lineStrokeWidth = 10f + @Composable - override fun infoView(modifier: Modifier) = Box(modifier = modifier) + override fun infoView(modifier: Modifier, information: String) = Box(modifier = modifier, contentAlignment = Alignment.Center,) { + Text( + textAlign = TextAlign.Center, + text = if (information.length <=3) information else "..." + ) + } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt index 6c253e0..be91e55 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt @@ -1,13 +1,17 @@ package viewPart.nodes.drawableRB +import androidx.compose.animation.VectorConverter import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.style.TextAlign import viewPart.nodes.drawableTree.NodeDesign +import kotlin.math.min object RBNodeDesign: NodeDesign { val redMarker = Color(233, 8, 28) @@ -19,5 +23,11 @@ object RBNodeDesign: NodeDesign { override var lineColor = Color(34, 35, 41) @Composable - override fun infoView(modifier: Modifier) = Box(modifier = modifier) + override fun infoView(modifier: Modifier, information: String) = Box(modifier = modifier, contentAlignment = Alignment.Center,){ + Text( + textAlign = TextAlign.Center, + text = if (information.length <=3) information else "...", + color = Color.White + ) + } } diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt b/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt index f555001..e82bb0f 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt +++ b/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt @@ -13,5 +13,5 @@ interface NodeDesign { var lineColor: Color @Composable - fun infoView(modifier: Modifier) + fun infoView(modifier: Modifier, information: String) } From 0a5c54a4843f46b3d55c63da843626b9020d7da0 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 3 May 2023 02:19:53 +0300 Subject: [PATCH 207/212] refactor: Change the project struct. --- .../{ => treeApp}/controller/Controller.kt | 16 +++++----- .../databaseManage/AVLTreeManager.kt | 6 ++-- .../databaseManage/BINTreeManager.kt | 6 ++-- .../databaseManage/RBTreeManager.kt | 6 ++-- .../controller}/databaseManage/TreeManager.kt | 4 +-- .../databaseSave/DrawableVertex.kt | 2 +- .../jsonFormat/DrawableBINVertex.kt | 4 +-- .../databaseSave/jsonFormat/JsonRepository.kt | 2 +- .../databaseSave/neo4j/DrawableRBVertex.kt | 4 +-- .../databaseSave/neo4j/Neo4jRepository.kt | 2 +- .../databaseSave/sqlite/DrawableAVLVertex.kt | 4 +-- .../sqlite/SQLiteRepositoryExposed.kt | 10 +++---- .../sqlite/SQLiteRepositoryJDBC.kt | 2 +- .../sqlite/treeEntities/TreeTableEntity.kt | 2 +- .../sqlite/treeEntities/TreesTable.kt | 2 +- .../sqlite/vertexEntities/VertexTable.kt | 2 +- .../vertexEntities/VertexTableEntity.kt | 2 +- .../viewPart}/drawableAVL/AVLDrawableNode.kt | 5 ++-- .../viewPart}/drawableAVL/AVLDrawableTree.kt | 9 +++--- .../viewPart}/drawableBIN/BINDrawableNode.kt | 5 ++-- .../viewPart}/drawableBIN/BINDrawableTree.kt | 9 +++--- .../viewPart}/drawableRB/RBDrawableNode.kt | 5 ++-- .../viewPart}/drawableRB/RBDrawableTree.kt | 9 +++--- .../viewPart}/drawableTree/DrawTree.kt | 3 +- .../viewPart}/drawableTree/DrawableNode.kt | 3 +- .../viewPart}/drawableTree/DrawableTree.kt | 6 ++-- lib/src/main/kotlin/{ => treeApp}/main.kt | 8 +++-- .../kotlin/{ => treeApp}/ui/ControlFields.kt | 7 ++--- .../{ => treeApp}/ui/DeleteAlertDialog.kt | 4 +-- .../main/kotlin/{ => treeApp}/ui/Design.kt | 4 +-- .../kotlin/{ => treeApp}/ui/FileDialog.kt | 4 +-- .../kotlin/{ => treeApp}/ui/SaveDialog.kt | 4 +-- .../main/kotlin/{ => treeApp}/ui/Submenu.kt | 6 ++-- .../main/kotlin/{ => treeApp}/ui/TopAppBar.kt | 8 ++--- .../nodes => treeApp/ui}/TreeDrawingUtils.kt | 29 +++++++++---------- .../{ => treeApp}/ui/UserInputValidation.kt | 4 +-- .../ui/nodeDesign}/AVLNodeDesign.kt | 4 +-- .../ui/nodeDesign}/BINNodeDesign.kt | 5 ++-- .../ui/nodeDesign}/NodeDesign.kt | 2 +- .../ui/nodeDesign}/RBNodeDesign.kt | 6 ++-- 40 files changed, 115 insertions(+), 110 deletions(-) rename lib/src/main/kotlin/{ => treeApp}/controller/Controller.kt (89%) rename lib/src/main/kotlin/{ => treeApp/controller}/databaseManage/AVLTreeManager.kt (95%) rename lib/src/main/kotlin/{ => treeApp/controller}/databaseManage/BINTreeManager.kt (94%) rename lib/src/main/kotlin/{ => treeApp/controller}/databaseManage/RBTreeManager.kt (92%) rename lib/src/main/kotlin/{ => treeApp/controller}/databaseManage/TreeManager.kt (89%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/DrawableVertex.kt (66%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/jsonFormat/DrawableBINVertex.kt (61%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/jsonFormat/JsonRepository.kt (94%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/neo4j/DrawableRBVertex.kt (67%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/neo4j/Neo4jRepository.kt (99%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/sqlite/DrawableAVLVertex.kt (60%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/sqlite/SQLiteRepositoryExposed.kt (90%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/sqlite/SQLiteRepositoryJDBC.kt (99%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/sqlite/treeEntities/TreeTableEntity.kt (79%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/sqlite/treeEntities/TreesTable.kt (69%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/sqlite/vertexEntities/VertexTable.kt (78%) rename lib/src/main/kotlin/{ => treeApp/controller/databaseManage}/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt (85%) rename lib/src/main/kotlin/{viewPart/nodes => treeApp/controller/viewPart}/drawableAVL/AVLDrawableNode.kt (77%) rename lib/src/main/kotlin/{viewPart/nodes => treeApp/controller/viewPart}/drawableAVL/AVLDrawableTree.kt (87%) rename lib/src/main/kotlin/{viewPart/nodes => treeApp/controller/viewPart}/drawableBIN/BINDrawableNode.kt (76%) rename lib/src/main/kotlin/{viewPart/nodes => treeApp/controller/viewPart}/drawableBIN/BINDrawableTree.kt (87%) rename lib/src/main/kotlin/{viewPart/nodes => treeApp/controller/viewPart}/drawableRB/RBDrawableNode.kt (80%) rename lib/src/main/kotlin/{viewPart/nodes => treeApp/controller/viewPart}/drawableRB/RBDrawableTree.kt (89%) rename lib/src/main/kotlin/{viewPart/nodes => treeApp/controller/viewPart}/drawableTree/DrawTree.kt (93%) rename lib/src/main/kotlin/{viewPart/nodes => treeApp/controller/viewPart}/drawableTree/DrawableNode.kt (94%) rename lib/src/main/kotlin/{viewPart/nodes => treeApp/controller/viewPart}/drawableTree/DrawableTree.kt (97%) rename lib/src/main/kotlin/{ => treeApp}/main.kt (97%) rename lib/src/main/kotlin/{ => treeApp}/ui/ControlFields.kt (98%) rename lib/src/main/kotlin/{ => treeApp}/ui/DeleteAlertDialog.kt (97%) rename lib/src/main/kotlin/{ => treeApp}/ui/Design.kt (99%) rename lib/src/main/kotlin/{ => treeApp}/ui/FileDialog.kt (99%) rename lib/src/main/kotlin/{ => treeApp}/ui/SaveDialog.kt (99%) rename lib/src/main/kotlin/{ => treeApp}/ui/Submenu.kt (99%) rename lib/src/main/kotlin/{ => treeApp}/ui/TopAppBar.kt (98%) rename lib/src/main/kotlin/{viewPart/nodes => treeApp/ui}/TreeDrawingUtils.kt (85%) rename lib/src/main/kotlin/{ => treeApp}/ui/UserInputValidation.kt (97%) rename lib/src/main/kotlin/{viewPart/nodes/drawableAVL => treeApp/ui/nodeDesign}/AVLNodeDesign.kt (91%) rename lib/src/main/kotlin/{viewPart/nodes/drawableBIN => treeApp/ui/nodeDesign}/BINNodeDesign.kt (89%) rename lib/src/main/kotlin/{viewPart/nodes/drawableTree => treeApp/ui/nodeDesign}/NodeDesign.kt (91%) rename lib/src/main/kotlin/{viewPart/nodes/drawableRB => treeApp/ui/nodeDesign}/RBNodeDesign.kt (87%) diff --git a/lib/src/main/kotlin/controller/Controller.kt b/lib/src/main/kotlin/treeApp/controller/Controller.kt similarity index 89% rename from lib/src/main/kotlin/controller/Controller.kt rename to lib/src/main/kotlin/treeApp/controller/Controller.kt index 49ba36b..9e864a4 100644 --- a/lib/src/main/kotlin/controller/Controller.kt +++ b/lib/src/main/kotlin/treeApp/controller/Controller.kt @@ -1,14 +1,14 @@ -package controller +package treeApp.controller import androidx.compose.ui.window.WindowState -import databaseManage.AVLTreeManager -import databaseManage.BINTreeManager -import databaseManage.RBTreeManager +import treeApp.controller.databaseManage.AVLTreeManager +import treeApp.controller.databaseManage.BINTreeManager +import treeApp.controller.databaseManage.RBTreeManager import treelib.commonObjects.Container -import viewPart.nodes.drawableAVL.AVLDrawableTree -import viewPart.nodes.drawableBIN.BINDrawableTree -import viewPart.nodes.drawableRB.RBDrawableTree -import viewPart.nodes.drawableTree.DrawTree +import treeApp.controller.viewPart.drawableAVL.AVLDrawableTree +import treeApp.controller.viewPart.drawableBIN.BINDrawableTree +import treeApp.controller.viewPart.drawableRB.RBDrawableTree +import treeApp.controller.viewPart.drawableTree.DrawTree import java.io.File import java.lang.Integer.min diff --git a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/AVLTreeManager.kt similarity index 95% rename from lib/src/main/kotlin/databaseManage/AVLTreeManager.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/AVLTreeManager.kt index 5bc3a0e..ab7e55c 100644 --- a/lib/src/main/kotlin/databaseManage/AVLTreeManager.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/AVLTreeManager.kt @@ -1,7 +1,7 @@ -package databaseManage +package treeApp.controller.databaseManage -import databaseSave.sqlite.DrawableAVLVertex -import databaseSave.sqlite.SQLiteRepositoryExposed +import treeApp.controller.databaseManage.databaseSave.sqlite.DrawableAVLVertex +import treeApp.controller.databaseManage.databaseSave.sqlite.SQLiteRepositoryExposed import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json diff --git a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/BINTreeManager.kt similarity index 94% rename from lib/src/main/kotlin/databaseManage/BINTreeManager.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/BINTreeManager.kt index d649ffd..007708f 100644 --- a/lib/src/main/kotlin/databaseManage/BINTreeManager.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/BINTreeManager.kt @@ -1,8 +1,8 @@ -package databaseManage +package treeApp.controller.databaseManage import com.google.gson.reflect.TypeToken -import databaseSave.jsonFormat.DrawableBINVertex -import databaseSave.jsonFormat.JsonRepository +import treeApp.controller.databaseManage.databaseSave.jsonFormat.DrawableBINVertex +import treeApp.controller.databaseManage.databaseSave.jsonFormat.JsonRepository import treelib.binTree.BINNode import treelib.binTree.BINStateContainer import treelib.binTree.BINStruct diff --git a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/RBTreeManager.kt similarity index 92% rename from lib/src/main/kotlin/databaseManage/RBTreeManager.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/RBTreeManager.kt index e24edd2..7484cf8 100644 --- a/lib/src/main/kotlin/databaseManage/RBTreeManager.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/RBTreeManager.kt @@ -1,7 +1,7 @@ -package databaseManage +package treeApp.controller.databaseManage -import databaseSave.neo4j.DrawableRBVertex -import databaseSave.neo4j.Neo4jRepository +import treeApp.controller.databaseManage.databaseSave.neo4j.DrawableRBVertex +import treeApp.controller.databaseManage.databaseSave.neo4j.Neo4jRepository import treelib.commonObjects.Container import treelib.rbTree.RBNode import treelib.rbTree.RBStateContainer diff --git a/lib/src/main/kotlin/databaseManage/TreeManager.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/TreeManager.kt similarity index 89% rename from lib/src/main/kotlin/databaseManage/TreeManager.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/TreeManager.kt index b5bb61d..e8a0be5 100644 --- a/lib/src/main/kotlin/databaseManage/TreeManager.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/TreeManager.kt @@ -1,6 +1,6 @@ -package databaseManage +package treeApp.controller.databaseManage -import databaseSave.DrawableVertex +import treeApp.controller.databaseManage.databaseSave.DrawableVertex import treelib.abstractTree.Node import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct diff --git a/lib/src/main/kotlin/databaseSave/DrawableVertex.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/DrawableVertex.kt similarity index 66% rename from lib/src/main/kotlin/databaseSave/DrawableVertex.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/DrawableVertex.kt index ee98220..9db4ccb 100644 --- a/lib/src/main/kotlin/databaseSave/DrawableVertex.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/DrawableVertex.kt @@ -1,4 +1,4 @@ -package databaseSave +package treeApp.controller.databaseManage.databaseSave interface DrawableVertex> { val value: Pack diff --git a/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/DrawableBINVertex.kt similarity index 61% rename from lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/DrawableBINVertex.kt index cdc42b2..1208448 100644 --- a/lib/src/main/kotlin/databaseSave/jsonFormat/DrawableBINVertex.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/DrawableBINVertex.kt @@ -1,6 +1,6 @@ -package databaseSave.jsonFormat +package treeApp.controller.databaseManage.databaseSave.jsonFormat -import databaseSave.DrawableVertex +import treeApp.controller.databaseManage.databaseSave.DrawableVertex import treelib.binTree.BINVertex class DrawableBINVertex>( diff --git a/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/JsonRepository.kt similarity index 94% rename from lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/JsonRepository.kt index 0db748e..2430e61 100644 --- a/lib/src/main/kotlin/databaseSave/jsonFormat/JsonRepository.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/JsonRepository.kt @@ -1,4 +1,4 @@ -package databaseSave.jsonFormat +package treeApp.controller.databaseManage.databaseSave.jsonFormat import com.google.gson.GsonBuilder import com.google.gson.reflect.TypeToken diff --git a/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/DrawableRBVertex.kt similarity index 67% rename from lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/DrawableRBVertex.kt index 0e32a6d..22a314f 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/DrawableRBVertex.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/DrawableRBVertex.kt @@ -1,6 +1,6 @@ -package databaseSave.neo4j +package treeApp.controller.databaseManage.databaseSave.neo4j -import databaseSave.DrawableVertex +import treeApp.controller.databaseManage.databaseSave.DrawableVertex import treelib.rbTree.Markers import treelib.rbTree.RBVertex diff --git a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/Neo4jRepository.kt similarity index 99% rename from lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/Neo4jRepository.kt index 86ac99e..86c25ea 100644 --- a/lib/src/main/kotlin/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/Neo4jRepository.kt @@ -1,4 +1,4 @@ -package databaseSave.neo4j +package treeApp.controller.databaseManage.databaseSave.neo4j import org.neo4j.driver.AuthTokens diff --git a/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/DrawableAVLVertex.kt similarity index 60% rename from lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/DrawableAVLVertex.kt index 83f10bf..503d896 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/DrawableAVLVertex.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/DrawableAVLVertex.kt @@ -1,6 +1,6 @@ -package databaseSave.sqlite +package treeApp.controller.databaseManage.databaseSave.sqlite -import databaseSave.DrawableVertex +import treeApp.controller.databaseManage.databaseSave.DrawableVertex class DrawableAVLVertex>( override val value: Pack, diff --git a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryExposed.kt similarity index 90% rename from lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryExposed.kt index 3fb8f1f..50ed8d1 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryExposed.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryExposed.kt @@ -1,9 +1,9 @@ -package databaseSave.sqlite +package treeApp.controller.databaseManage.databaseSave.sqlite -import databaseSave.sqlite.treeEntities.TreeTableEntity -import databaseSave.sqlite.treeEntities.TreesTable -import databaseSave.sqlite.vertexEntities.VertexTable -import databaseSave.sqlite.vertexEntities.VertexTableEntity +import treeApp.controller.databaseManage.databaseSave.sqlite.treeEntities.TreeTableEntity +import treeApp.controller.databaseManage.databaseSave.sqlite.treeEntities.TreesTable +import treeApp.controller.databaseManage.databaseSave.sqlite.vertexEntities.VertexTable +import treeApp.controller.databaseManage.databaseSave.sqlite.vertexEntities.VertexTableEntity import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.transactions.transaction diff --git a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryJDBC.kt similarity index 99% rename from lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryJDBC.kt index dc4ac75..d1b76f1 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/SQLiteRepositoryJDBC.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryJDBC.kt @@ -1,4 +1,4 @@ -package databaseSave.sqlite +package treeApp.controller.databaseManage.databaseSave.sqlite import java.io.Closeable import java.sql.DriverManager diff --git a/lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreeTableEntity.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreeTableEntity.kt similarity index 79% rename from lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreeTableEntity.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreeTableEntity.kt index bd3d030..d845b95 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreeTableEntity.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreeTableEntity.kt @@ -1,4 +1,4 @@ -package databaseSave.sqlite.treeEntities +package treeApp.controller.databaseManage.databaseSave.sqlite.treeEntities import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntityClass diff --git a/lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreesTable.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreesTable.kt similarity index 69% rename from lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreesTable.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreesTable.kt index 3bfecf4..df650d2 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/treeEntities/TreesTable.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreesTable.kt @@ -1,4 +1,4 @@ -package databaseSave.sqlite.treeEntities +package treeApp.controller.databaseManage.databaseSave.sqlite.treeEntities import org.jetbrains.exposed.dao.id.IntIdTable diff --git a/lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTable.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTable.kt similarity index 78% rename from lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTable.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTable.kt index 3f878fc..b6ea822 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTable.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTable.kt @@ -1,4 +1,4 @@ -package databaseSave.sqlite.vertexEntities +package treeApp.controller.databaseManage.databaseSave.sqlite.vertexEntities import org.jetbrains.exposed.dao.id.IntIdTable diff --git a/lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt similarity index 85% rename from lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt rename to lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt index 4c9d6ac..d409cec 100644 --- a/lib/src/main/kotlin/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt +++ b/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt @@ -1,4 +1,4 @@ -package databaseSave.sqlite.vertexEntities +package treeApp.controller.databaseManage.databaseSave.sqlite.vertexEntities import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntityClass diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableNode.kt similarity index 77% rename from lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt rename to lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableNode.kt index 34b8d30..e15eb48 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableNode.kt +++ b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableNode.kt @@ -1,8 +1,9 @@ -package viewPart.nodes.drawableAVL +package treeApp.controller.viewPart.drawableAVL import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf -import viewPart.nodes.drawableTree.DrawableNode +import treeApp.controller.viewPart.drawableTree.DrawableNode +import treeApp.ui.nodeDesign.AVLNodeDesign class AVLDrawableNode( override val value: Pack, diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableTree.kt similarity index 87% rename from lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt rename to lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableTree.kt index f0398cc..44de029 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLDrawableTree.kt +++ b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableTree.kt @@ -1,14 +1,15 @@ -package viewPart.nodes.drawableAVL +package treeApp.controller.viewPart.drawableAVL import androidx.compose.runtime.mutableStateOf -import databaseManage.TreeManager -import databaseSave.sqlite.DrawableAVLVertex +import treeApp.controller.databaseManage.TreeManager +import treeApp.controller.databaseManage.databaseSave.sqlite.DrawableAVLVertex import treelib.avlTree.AVLNode import treelib.avlTree.AVLStateContainer import treelib.avlTree.AVLStruct import treelib.avlTree.AVLVertex import treelib.commonObjects.Container -import viewPart.nodes.drawableTree.DrawableTree +import treeApp.controller.viewPart.drawableTree.DrawableTree +import treeApp.ui.nodeDesign.AVLNodeDesign class AVLDrawableTree( override var name: String, diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableNode.kt similarity index 76% rename from lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt rename to lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableNode.kt index e9bfa26..9c1ac9b 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableNode.kt +++ b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableNode.kt @@ -1,8 +1,9 @@ -package viewPart.nodes.drawableBIN +package treeApp.controller.viewPart.drawableBIN import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf -import viewPart.nodes.drawableTree.DrawableNode +import treeApp.controller.viewPart.drawableTree.DrawableNode +import treeApp.ui.nodeDesign.BINNodeDesign class BINDrawableNode( override val value: Pack, diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableTree.kt similarity index 87% rename from lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt rename to lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableTree.kt index c5c9f06..9263999 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINDrawableTree.kt +++ b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableTree.kt @@ -1,14 +1,15 @@ -package viewPart.nodes.drawableBIN +package treeApp.controller.viewPart.drawableBIN import androidx.compose.runtime.mutableStateOf -import databaseManage.BINTreeManager -import databaseSave.jsonFormat.DrawableBINVertex +import treeApp.controller.databaseManage.BINTreeManager +import treeApp.controller.databaseManage.databaseSave.jsonFormat.DrawableBINVertex import treelib.binTree.BINNode import treelib.binTree.BINStateContainer import treelib.binTree.BINStruct import treelib.binTree.BINVertex import treelib.commonObjects.Container -import viewPart.nodes.drawableTree.DrawableTree +import treeApp.controller.viewPart.drawableTree.DrawableTree +import treeApp.ui.nodeDesign.BINNodeDesign class BINDrawableTree( override var name: String, diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableNode.kt similarity index 80% rename from lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt rename to lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableNode.kt index ab44640..57e0298 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableNode.kt +++ b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableNode.kt @@ -1,9 +1,10 @@ -package viewPart.nodes.drawableRB +package treeApp.controller.viewPart.drawableRB import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import treelib.rbTree.Markers -import viewPart.nodes.drawableTree.DrawableNode +import treeApp.controller.viewPart.drawableTree.DrawableNode +import treeApp.ui.nodeDesign.RBNodeDesign class RBDrawableNode( override val value: Pack, diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableTree.kt similarity index 89% rename from lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt rename to lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableTree.kt index d6f4211..f4007d2 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBDrawableTree.kt +++ b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableTree.kt @@ -1,14 +1,15 @@ -package viewPart.nodes.drawableRB +package treeApp.controller.viewPart.drawableRB import androidx.compose.runtime.mutableStateOf -import databaseManage.TreeManager -import databaseSave.neo4j.DrawableRBVertex +import treeApp.controller.databaseManage.TreeManager +import treeApp.controller.databaseManage.databaseSave.neo4j.DrawableRBVertex import treelib.commonObjects.Container import treelib.rbTree.RBNode import treelib.rbTree.RBStateContainer import treelib.rbTree.RBStruct import treelib.rbTree.RBVertex -import viewPart.nodes.drawableTree.DrawableTree +import treeApp.controller.viewPart.drawableTree.DrawableTree +import treeApp.ui.nodeDesign.RBNodeDesign class RBDrawableTree( override var name: String, diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawTree.kt similarity index 93% rename from lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt rename to lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawTree.kt index 65b0db2..b7dba82 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawTree.kt +++ b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawTree.kt @@ -1,5 +1,6 @@ -package viewPart.nodes.drawableTree +package treeApp.controller.viewPart.drawableTree +import treeApp.ui.nodeDesign.NodeDesign import treelib.commonObjects.Container interface DrawTree { diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableNode.kt similarity index 94% rename from lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt rename to lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableNode.kt index 87ce044..c2dfd75 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableNode.kt +++ b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableNode.kt @@ -1,4 +1,4 @@ -package viewPart.nodes.drawableTree +package treeApp.controller.viewPart.drawableTree import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -13,6 +13,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import treeApp.ui.nodeDesign.NodeDesign import kotlin.math.roundToInt abstract class DrawableNode>( diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableTree.kt similarity index 97% rename from lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt rename to lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableTree.kt index ce6609b..9b3b7e1 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/DrawableTree.kt +++ b/lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableTree.kt @@ -1,7 +1,7 @@ -package viewPart.nodes.drawableTree +package treeApp.controller.viewPart.drawableTree -import databaseManage.TreeManager -import databaseSave.DrawableVertex +import treeApp.controller.databaseManage.TreeManager +import treeApp.controller.databaseManage.databaseSave.DrawableVertex import treelib.abstractTree.Node import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct diff --git a/lib/src/main/kotlin/main.kt b/lib/src/main/kotlin/treeApp/main.kt similarity index 97% rename from lib/src/main/kotlin/main.kt rename to lib/src/main/kotlin/treeApp/main.kt index 1f87beb..4e546d8 100644 --- a/lib/src/main/kotlin/main.kt +++ b/lib/src/main/kotlin/treeApp/main.kt @@ -1,4 +1,4 @@ - +package treeApp import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.gestures.onDrag @@ -13,8 +13,10 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.* import androidx.compose.ui.window.* -import controller.Controller -import ui.* +import treeApp.controller.Controller +import treeApp.ui.AppIcon +import treeApp.ui.ControlFields +import treeApp.ui.MyTopAppBar import java.awt.Dimension @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) diff --git a/lib/src/main/kotlin/ui/ControlFields.kt b/lib/src/main/kotlin/treeApp/ui/ControlFields.kt similarity index 98% rename from lib/src/main/kotlin/ui/ControlFields.kt rename to lib/src/main/kotlin/treeApp/ui/ControlFields.kt index d29a246..48d9422 100644 --- a/lib/src/main/kotlin/ui/ControlFields.kt +++ b/lib/src/main/kotlin/treeApp/ui/ControlFields.kt @@ -1,4 +1,4 @@ -package ui +package treeApp.ui import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource @@ -24,9 +24,8 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Popup import androidx.compose.ui.window.WindowState -import controller.Controller -import viewPart.nodes.displayTree -import viewPart.nodes.drawableTree.DrawTree +import treeApp.controller.Controller +import treeApp.controller.viewPart.drawableTree.DrawTree @Composable fun ControlFields( diff --git a/lib/src/main/kotlin/ui/DeleteAlertDialog.kt b/lib/src/main/kotlin/treeApp/ui/DeleteAlertDialog.kt similarity index 97% rename from lib/src/main/kotlin/ui/DeleteAlertDialog.kt rename to lib/src/main/kotlin/treeApp/ui/DeleteAlertDialog.kt index 6e34245..aefdfda 100644 --- a/lib/src/main/kotlin/ui/DeleteAlertDialog.kt +++ b/lib/src/main/kotlin/treeApp/ui/DeleteAlertDialog.kt @@ -1,4 +1,4 @@ -package ui +package treeApp.ui import androidx.compose.foundation.layout.Arrangement @@ -16,7 +16,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.unit.dp -import controller.Controller +import treeApp.controller.Controller @OptIn(ExperimentalMaterialApi::class) diff --git a/lib/src/main/kotlin/ui/Design.kt b/lib/src/main/kotlin/treeApp/ui/Design.kt similarity index 99% rename from lib/src/main/kotlin/ui/Design.kt rename to lib/src/main/kotlin/treeApp/ui/Design.kt index b928e0a..c73f18c 100644 --- a/lib/src/main/kotlin/ui/Design.kt +++ b/lib/src/main/kotlin/treeApp/ui/Design.kt @@ -1,4 +1,4 @@ -package ui +package treeApp.ui import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.size @@ -70,4 +70,4 @@ fun TopAppIconButton( Icon(painter, contentDescription = null) } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/ui/FileDialog.kt b/lib/src/main/kotlin/treeApp/ui/FileDialog.kt similarity index 99% rename from lib/src/main/kotlin/ui/FileDialog.kt rename to lib/src/main/kotlin/treeApp/ui/FileDialog.kt index 1a915f6..65becda 100644 --- a/lib/src/main/kotlin/ui/FileDialog.kt +++ b/lib/src/main/kotlin/treeApp/ui/FileDialog.kt @@ -1,4 +1,4 @@ -package ui +package treeApp.ui import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background @@ -34,7 +34,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.rememberDialogState -import lightColors +import treeApp.lightColors import java.awt.Dimension import java.io.File diff --git a/lib/src/main/kotlin/ui/SaveDialog.kt b/lib/src/main/kotlin/treeApp/ui/SaveDialog.kt similarity index 99% rename from lib/src/main/kotlin/ui/SaveDialog.kt rename to lib/src/main/kotlin/treeApp/ui/SaveDialog.kt index fb660b8..efe8571 100644 --- a/lib/src/main/kotlin/ui/SaveDialog.kt +++ b/lib/src/main/kotlin/treeApp/ui/SaveDialog.kt @@ -1,4 +1,4 @@ -package ui +package treeApp.ui import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.border @@ -21,7 +21,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.WindowPosition import androidx.compose.ui.window.rememberDialogState -import controller.Controller +import treeApp.controller.Controller @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) @Composable diff --git a/lib/src/main/kotlin/ui/Submenu.kt b/lib/src/main/kotlin/treeApp/ui/Submenu.kt similarity index 99% rename from lib/src/main/kotlin/ui/Submenu.kt rename to lib/src/main/kotlin/treeApp/ui/Submenu.kt index 96190d5..86ba633 100644 --- a/lib/src/main/kotlin/ui/Submenu.kt +++ b/lib/src/main/kotlin/treeApp/ui/Submenu.kt @@ -1,4 +1,4 @@ -package ui +package treeApp.ui import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background @@ -16,7 +16,7 @@ import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp -import controller.Controller +import treeApp.controller.Controller @OptIn(ExperimentalComposeUiApi::class) @Composable @@ -168,4 +168,4 @@ fun SelectTree( selectedTreeName.value = "" } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/ui/TopAppBar.kt b/lib/src/main/kotlin/treeApp/ui/TopAppBar.kt similarity index 98% rename from lib/src/main/kotlin/ui/TopAppBar.kt rename to lib/src/main/kotlin/treeApp/ui/TopAppBar.kt index 30c0642..ab6975e 100644 --- a/lib/src/main/kotlin/ui/TopAppBar.kt +++ b/lib/src/main/kotlin/treeApp/ui/TopAppBar.kt @@ -1,4 +1,4 @@ -package ui +package treeApp.ui import androidx.compose.foundation.layout.* import androidx.compose.material.TopAppBar @@ -17,9 +17,9 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.WindowPlacement import androidx.compose.ui.window.WindowState -import controller.Controller -import lightColors -import myTypography +import treeApp.controller.Controller +import treeApp.lightColors +import treeApp.myTypography @Composable fun MyTopAppBar( diff --git a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt b/lib/src/main/kotlin/treeApp/ui/TreeDrawingUtils.kt similarity index 85% rename from lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt rename to lib/src/main/kotlin/treeApp/ui/TreeDrawingUtils.kt index 813eff3..8272297 100644 --- a/lib/src/main/kotlin/viewPart/nodes/TreeDrawingUtils.kt +++ b/lib/src/main/kotlin/treeApp/ui/TreeDrawingUtils.kt @@ -1,4 +1,4 @@ -package viewPart.nodes +package treeApp.ui import androidx.compose.foundation.Canvas import androidx.compose.foundation.background @@ -18,20 +18,19 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import treelib.commonObjects.Container -import viewPart.nodes.drawableAVL.AVLDrawableNode -import viewPart.nodes.drawableAVL.AVLDrawableTree -import viewPart.nodes.drawableAVL.AVLNodeDesign -import viewPart.nodes.drawableBIN.BINDrawableNode -import viewPart.nodes.drawableBIN.BINDrawableTree -import viewPart.nodes.drawableBIN.BINNodeDesign -import viewPart.nodes.drawableRB.RBDrawableNode -import viewPart.nodes.drawableRB.RBDrawableTree -import viewPart.nodes.drawableRB.RBNodeDesign -import viewPart.nodes.drawableTree.DrawTree -import viewPart.nodes.drawableTree.DrawableNode -import viewPart.nodes.drawableTree.NodeDesign +import treeApp.controller.viewPart.drawableAVL.AVLDrawableNode +import treeApp.controller.viewPart.drawableAVL.AVLDrawableTree +import treeApp.ui.nodeDesign.AVLNodeDesign +import treeApp.controller.viewPart.drawableBIN.BINDrawableNode +import treeApp.controller.viewPart.drawableBIN.BINDrawableTree +import treeApp.ui.nodeDesign.BINNodeDesign +import treeApp.controller.viewPart.drawableRB.RBDrawableNode +import treeApp.controller.viewPart.drawableRB.RBDrawableTree +import treeApp.ui.nodeDesign.RBNodeDesign +import treeApp.controller.viewPart.drawableTree.DrawTree +import treeApp.controller.viewPart.drawableTree.DrawableNode +import treeApp.ui.nodeDesign.NodeDesign import java.util.Locale -import kotlin.math.min import kotlin.math.roundToInt @Composable @@ -43,7 +42,7 @@ fun displayTree(tree: DrawTree, state: MutableState){ tree.addOffset(dragAmount.x, dragAmount.y) state.value = false } - }.zIndex(-100f) + }.zIndex(-10000f) ){ when (tree) { is BINDrawableTree -> displayBIN(tree) diff --git a/lib/src/main/kotlin/ui/UserInputValidation.kt b/lib/src/main/kotlin/treeApp/ui/UserInputValidation.kt similarity index 97% rename from lib/src/main/kotlin/ui/UserInputValidation.kt rename to lib/src/main/kotlin/treeApp/ui/UserInputValidation.kt index 673ee5f..358e379 100644 --- a/lib/src/main/kotlin/ui/UserInputValidation.kt +++ b/lib/src/main/kotlin/treeApp/ui/UserInputValidation.kt @@ -1,4 +1,4 @@ -package ui +package treeApp.ui import java.util.* @@ -22,4 +22,4 @@ fun validate(fileName: String): Boolean { else -> true } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt b/lib/src/main/kotlin/treeApp/ui/nodeDesign/AVLNodeDesign.kt similarity index 91% rename from lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt rename to lib/src/main/kotlin/treeApp/ui/nodeDesign/AVLNodeDesign.kt index 388a012..420f682 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableAVL/AVLNodeDesign.kt +++ b/lib/src/main/kotlin/treeApp/ui/nodeDesign/AVLNodeDesign.kt @@ -1,4 +1,4 @@ -package viewPart.nodes.drawableAVL +package treeApp.ui.nodeDesign import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.CircleShape @@ -9,7 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.style.TextAlign -import viewPart.nodes.drawableTree.NodeDesign +import treeApp.ui.nodeDesign.NodeDesign object AVLNodeDesign : NodeDesign { override var colorNode = Color(208, 223, 252) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt b/lib/src/main/kotlin/treeApp/ui/nodeDesign/BINNodeDesign.kt similarity index 89% rename from lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt rename to lib/src/main/kotlin/treeApp/ui/nodeDesign/BINNodeDesign.kt index 30ea9c4..cebe63b 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableBIN/BINNodeDesign.kt +++ b/lib/src/main/kotlin/treeApp/ui/nodeDesign/BINNodeDesign.kt @@ -1,4 +1,4 @@ -package viewPart.nodes.drawableBIN +package treeApp.ui.nodeDesign import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.CircleShape @@ -9,8 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.style.TextAlign -import viewPart.nodes.drawableTree.NodeDesign -import kotlin.math.min +import treeApp.ui.nodeDesign.NodeDesign object BINNodeDesign : NodeDesign { override var colorNode = Color(37, 146, 110) diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt b/lib/src/main/kotlin/treeApp/ui/nodeDesign/NodeDesign.kt similarity index 91% rename from lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt rename to lib/src/main/kotlin/treeApp/ui/nodeDesign/NodeDesign.kt index e82bb0f..8008443 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableTree/NodeDesign.kt +++ b/lib/src/main/kotlin/treeApp/ui/nodeDesign/NodeDesign.kt @@ -1,4 +1,4 @@ -package viewPart.nodes.drawableTree +package treeApp.ui.nodeDesign import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt b/lib/src/main/kotlin/treeApp/ui/nodeDesign/RBNodeDesign.kt similarity index 87% rename from lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt rename to lib/src/main/kotlin/treeApp/ui/nodeDesign/RBNodeDesign.kt index be91e55..cc40291 100644 --- a/lib/src/main/kotlin/viewPart/nodes/drawableRB/RBNodeDesign.kt +++ b/lib/src/main/kotlin/treeApp/ui/nodeDesign/RBNodeDesign.kt @@ -1,6 +1,5 @@ -package viewPart.nodes.drawableRB +package treeApp.ui.nodeDesign -import androidx.compose.animation.VectorConverter import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Text @@ -10,8 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.style.TextAlign -import viewPart.nodes.drawableTree.NodeDesign -import kotlin.math.min +import treeApp.ui.nodeDesign.NodeDesign object RBNodeDesign: NodeDesign { val redMarker = Color(233, 8, 28) From 8174c2555a6aa8842f0c31308e4ede9a7fa4af33 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 3 May 2023 02:32:41 +0300 Subject: [PATCH 208/212] feat: Add examples of the app work. --- lib/src/main/resources/avl_im.png | Bin 0 -> 44809 bytes lib/src/main/resources/rb_im.png | Bin 0 -> 43649 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/src/main/resources/avl_im.png create mode 100644 lib/src/main/resources/rb_im.png diff --git a/lib/src/main/resources/avl_im.png b/lib/src/main/resources/avl_im.png new file mode 100644 index 0000000000000000000000000000000000000000..fa30eb883d6d2ad23b28fea54a55590028400ae7 GIT binary patch literal 44809 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 literal 0 HcmV?d00001 diff --git a/lib/src/main/resources/rb_im.png b/lib/src/main/resources/rb_im.png new file mode 100644 index 0000000000000000000000000000000000000000..e680402538cff9ef3454a7cf3cb0b5cb93367e8f GIT binary patch literal 43649 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 H>(~De>m^^} literal 0 HcmV?d00001 From ba4c655baac5aca6a76973eee0ce623ef1c010ec Mon Sep 17 00:00:00 2001 From: Artem-Rzhankoff <113193908+Artem-Rzhankoff@users.noreply.github.com> Date: Wed, 3 May 2023 02:50:04 +0300 Subject: [PATCH 209/212] feat: Update README.md --- README.md | 64 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ab02c68..ce95799 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -#

logo

+logo @@ -17,41 +17,61 @@ This project will help the user to build an RBT, AVL or binary tree. Add, delete, or search for elements in it. -## Overview +## Features +With **trees-1** you can do the following: +- Create three kinds of binary search trees: **Binary search tree**, **AVL tree**, **Red-black tree** +- Using the GUI, perform the following operations (**insert**, **delete**, **search**) +- Save and load trees into databases (**json files** for binary, **SQLite** for AVL-tree, **neo4j** for RB-tree) +- Use an independent library with the implementation of these trees in your project -- You can create any tree of your choice (AVL, RB, Binary Tree) -- Insert, delete, check for any element -- Support for different types of elements +## Get it! +Important: To build and run the application, you will need the following tools: Gradle, JDK (version 17 and latest) and Neo4j. +### Quick start +```bash +# Clone this repo +git clone https://github.com/spbu-coding-2022/trees-1.it -## Setup +# Build +./gradlew build -To connect a project from Github using Gradle, you need to follow these steps: +# Build Docker (link) -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' -} +# Run +./gradlew run ``` -3. Save the changes in the build file.gradle -4. Synchronize the project using Gradle +If you only want to use the library, you need to follow these steps: +```bash +# Find the "dependencies" block and add the dependency line to it: +# If in your project build.gradle.kts: +implementation("com.github.spbu-coding-2022:trees-1:1.0.0") +# If in your project build.gradle: +implementation 'com.github.spbu-coding-2022:trees-1:1.0.0' + +# Save the changes in the build file +``` +## Neo4j setup +1. Install docker +2. Install docker compose (https://docs.docker.com/compose/install/) +3. Run in project dir: +```bash +docker compose -f "docker-compose.yml" up +``` +4. If you're having trouble getting started, check out this [GUIDE](https://www.baeldung.com/ops/docker-cannot-connect#:~:text=Due%20to%20Inactive%20Docker%20Service) -## Project Status -Project is: _in progress_ +## See examples +logo +logo -## 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. +This project uses the **APACHE LICENSE, VERSION 2.0**. See the [LICENSE](LICENSE.md) for more info.

+ + From 62b08f7f783aba49aeed9911b25dc32c2a1ab14c Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 3 May 2023 04:30:07 +0300 Subject: [PATCH 210/212] fix: Button operation "Back to root" --- lib/src/main/kotlin/treeApp/controller/Controller.kt | 1 - lib/src/main/kotlin/treeApp/ui/ControlFields.kt | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/treeApp/controller/Controller.kt b/lib/src/main/kotlin/treeApp/controller/Controller.kt index 9e864a4..4c6e39a 100644 --- a/lib/src/main/kotlin/treeApp/controller/Controller.kt +++ b/lib/src/main/kotlin/treeApp/controller/Controller.kt @@ -86,7 +86,6 @@ class Controller { val key = if (isInt(value.trim())) value.toInt() else value.hashCode() val offset1 = tree?.find(key) ?: throw NullPointerException() - println(windowState.size.width.value / 2) if (offset1.first >= 0){ tree?.addOffset( windowState.size.width.value / 2 - offset1.first, diff --git a/lib/src/main/kotlin/treeApp/ui/ControlFields.kt b/lib/src/main/kotlin/treeApp/ui/ControlFields.kt index 48d9422..2dffa70 100644 --- a/lib/src/main/kotlin/treeApp/ui/ControlFields.kt +++ b/lib/src/main/kotlin/treeApp/ui/ControlFields.kt @@ -90,6 +90,7 @@ fun ControlFields( tree?.updateTree() tree?.repositionTree(800f, 10f) returnButtonClickState.value = false + dragState.value = false } tree?.let { displayTree(it, dragState) } From 168eac41dd88fdd98a08a850e179cd6c170a53d4 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 3 May 2023 22:09:06 +0300 Subject: [PATCH 211/212] feat: Decompose project into app and library. --- .gitignore | 1 + lib/build.gradle.kts | 13 -- .../databaseSave/DrawableVertex.kt | 2 +- .../jsonFormat/DrawableBINVertex.kt | 4 +- .../databaseSave/jsonFormat/JsonRepository.kt | 2 +- .../databaseSave/neo4j/DrawableRBVertex.kt | 4 +- .../databaseSave/neo4j/Neo4jRepository.kt | 2 +- .../databaseSave/sqlite/DrawableAVLVertex.kt | 4 +- .../sqlite/SQLiteRepositoryExposed.kt | 10 +- .../sqlite/SQLiteRepositoryJDBC.kt | 2 +- .../sqlite/treeEntities/TreeTableEntity.kt | 2 +- .../sqlite/treeEntities/TreesTable.kt | 2 +- .../sqlite/vertexEntities/VertexTable.kt | 2 +- .../vertexEntities/VertexTableEntity.kt | 2 +- settings.gradle.kts | 1 + treeApp/build.gradle.kts | 117 ++++++++++++++++++ .../src/main/kotlin/treeApp/Main.kt | 3 +- .../kotlin/treeApp/controller/Controller.kt | 0 .../databaseManage/AVLTreeManager.kt | 4 +- .../databaseManage/BINTreeManager.kt | 4 +- .../databaseManage/RBTreeManager.kt | 4 +- .../controller/databaseManage/TreeManager.kt | 2 +- .../viewPart/drawableAVL/AVLDrawableNode.kt | 0 .../viewPart/drawableAVL/AVLDrawableTree.kt | 2 +- .../viewPart/drawableBIN/BINDrawableNode.kt | 0 .../viewPart/drawableBIN/BINDrawableTree.kt | 2 +- .../viewPart/drawableRB/RBDrawableNode.kt | 0 .../viewPart/drawableRB/RBDrawableTree.kt | 2 +- .../viewPart/drawableTree/DrawTree.kt | 0 .../viewPart/drawableTree/DrawableNode.kt | 0 .../viewPart/drawableTree/DrawableTree.kt | 2 +- .../main/kotlin/treeApp/ui/ControlFields.kt | 10 +- .../kotlin/treeApp/ui/DeleteAlertDialog.kt | 2 +- .../src/main/kotlin/treeApp/ui/Design.kt | 0 .../src/main/kotlin/treeApp/ui/FileDialog.kt | 0 .../src/main/kotlin/treeApp/ui/SaveDialog.kt | 2 +- .../src/main/kotlin/treeApp/ui/Submenu.kt | 6 +- .../src/main/kotlin/treeApp/ui/TopAppBar.kt | 2 +- .../kotlin/treeApp/ui/TreeDrawingUtils.kt | 0 .../kotlin/treeApp/ui/UserInputValidation.kt | 0 .../treeApp/ui/nodeDesign/AVLNodeDesign.kt | 0 .../treeApp/ui/nodeDesign/BINNodeDesign.kt | 0 .../treeApp/ui/nodeDesign/NodeDesign.kt | 0 .../treeApp/ui/nodeDesign/RBNodeDesign.kt | 0 44 files changed, 162 insertions(+), 55 deletions(-) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/DrawableVertex.kt (66%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/jsonFormat/DrawableBINVertex.kt (61%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/jsonFormat/JsonRepository.kt (94%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/neo4j/DrawableRBVertex.kt (67%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/neo4j/Neo4jRepository.kt (99%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/sqlite/DrawableAVLVertex.kt (60%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/sqlite/SQLiteRepositoryExposed.kt (90%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/sqlite/SQLiteRepositoryJDBC.kt (99%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/sqlite/treeEntities/TreeTableEntity.kt (79%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/sqlite/treeEntities/TreesTable.kt (69%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/sqlite/vertexEntities/VertexTable.kt (78%) rename lib/src/main/kotlin/{treeApp/controller/databaseManage => treelib}/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt (85%) create mode 100644 treeApp/build.gradle.kts rename lib/src/main/kotlin/treeApp/main.kt => treeApp/src/main/kotlin/treeApp/Main.kt (98%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/Controller.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/databaseManage/AVLTreeManager.kt (96%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/databaseManage/BINTreeManager.kt (95%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/databaseManage/RBTreeManager.kt (94%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/databaseManage/TreeManager.kt (93%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableNode.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableTree.kt (96%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableNode.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableTree.kt (96%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableNode.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableTree.kt (97%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawTree.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableNode.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableTree.kt (99%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/ControlFields.kt (96%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/DeleteAlertDialog.kt (98%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/Design.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/FileDialog.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/SaveDialog.kt (99%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/Submenu.kt (97%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/TopAppBar.kt (99%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/TreeDrawingUtils.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/UserInputValidation.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/nodeDesign/AVLNodeDesign.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/nodeDesign/BINNodeDesign.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/nodeDesign/NodeDesign.kt (100%) rename {lib => treeApp}/src/main/kotlin/treeApp/ui/nodeDesign/RBNodeDesign.kt (100%) diff --git a/.gitignore b/.gitignore index 3df0a47..68715a1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /neo4jDB/ /saved-trees/ /sqliteDB/ +/treeApp/build/ diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 816217d..c272ee8 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -5,7 +5,6 @@ plugins { `java-library` `maven-publish` kotlin("plugin.serialization") version "1.5.0" - id("org.jetbrains.compose") version "1.4.0" } kotlin { @@ -14,7 +13,6 @@ kotlin { repositories { mavenLocal() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") mavenCentral() } @@ -38,9 +36,6 @@ dependencies { implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") - implementation(compose.desktop.currentOs) - implementation(compose.material3) - testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") @@ -48,14 +43,6 @@ dependencies { } - -compose.desktop { - application { - mainClass = "MainKt" - } -} - - tasks.test { finalizedBy(tasks.jacocoTestReport) useJUnitPlatform() diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/DrawableVertex.kt b/lib/src/main/kotlin/treelib/databaseSave/DrawableVertex.kt similarity index 66% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/DrawableVertex.kt rename to lib/src/main/kotlin/treelib/databaseSave/DrawableVertex.kt index 9db4ccb..28b2f52 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/DrawableVertex.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/DrawableVertex.kt @@ -1,4 +1,4 @@ -package treeApp.controller.databaseManage.databaseSave +package treelib.databaseSave interface DrawableVertex> { val value: Pack diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/DrawableBINVertex.kt b/lib/src/main/kotlin/treelib/databaseSave/jsonFormat/DrawableBINVertex.kt similarity index 61% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/DrawableBINVertex.kt rename to lib/src/main/kotlin/treelib/databaseSave/jsonFormat/DrawableBINVertex.kt index 1208448..1b4fb04 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/DrawableBINVertex.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/jsonFormat/DrawableBINVertex.kt @@ -1,6 +1,6 @@ -package treeApp.controller.databaseManage.databaseSave.jsonFormat +package treelib.databaseSave.jsonFormat -import treeApp.controller.databaseManage.databaseSave.DrawableVertex +import treelib.databaseSave.DrawableVertex import treelib.binTree.BINVertex class DrawableBINVertex>( diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/treelib/databaseSave/jsonFormat/JsonRepository.kt similarity index 94% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/JsonRepository.kt rename to lib/src/main/kotlin/treelib/databaseSave/jsonFormat/JsonRepository.kt index 2430e61..c602da1 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/jsonFormat/JsonRepository.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/jsonFormat/JsonRepository.kt @@ -1,4 +1,4 @@ -package treeApp.controller.databaseManage.databaseSave.jsonFormat +package treelib.databaseSave.jsonFormat import com.google.gson.GsonBuilder import com.google.gson.reflect.TypeToken diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/DrawableRBVertex.kt b/lib/src/main/kotlin/treelib/databaseSave/neo4j/DrawableRBVertex.kt similarity index 67% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/DrawableRBVertex.kt rename to lib/src/main/kotlin/treelib/databaseSave/neo4j/DrawableRBVertex.kt index 22a314f..285c858 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/DrawableRBVertex.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/neo4j/DrawableRBVertex.kt @@ -1,6 +1,6 @@ -package treeApp.controller.databaseManage.databaseSave.neo4j +package treelib.databaseSave.neo4j -import treeApp.controller.databaseManage.databaseSave.DrawableVertex +import treelib.databaseSave.DrawableVertex import treelib.rbTree.Markers import treelib.rbTree.RBVertex diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/treelib/databaseSave/neo4j/Neo4jRepository.kt similarity index 99% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/Neo4jRepository.kt rename to lib/src/main/kotlin/treelib/databaseSave/neo4j/Neo4jRepository.kt index 86c25ea..4770965 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/neo4j/Neo4jRepository.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/neo4j/Neo4jRepository.kt @@ -1,4 +1,4 @@ -package treeApp.controller.databaseManage.databaseSave.neo4j +package treelib.databaseSave.neo4j import org.neo4j.driver.AuthTokens diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/DrawableAVLVertex.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/DrawableAVLVertex.kt similarity index 60% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/DrawableAVLVertex.kt rename to lib/src/main/kotlin/treelib/databaseSave/sqlite/DrawableAVLVertex.kt index 503d896..ef62b62 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/DrawableAVLVertex.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/DrawableAVLVertex.kt @@ -1,6 +1,6 @@ -package treeApp.controller.databaseManage.databaseSave.sqlite +package treelib.databaseSave.sqlite -import treeApp.controller.databaseManage.databaseSave.DrawableVertex +import treelib.databaseSave.DrawableVertex class DrawableAVLVertex>( override val value: Pack, diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryExposed.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryExposed.kt similarity index 90% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryExposed.kt rename to lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryExposed.kt index 50ed8d1..ad2c32b 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryExposed.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryExposed.kt @@ -1,9 +1,9 @@ -package treeApp.controller.databaseManage.databaseSave.sqlite +package treelib.databaseSave.sqlite -import treeApp.controller.databaseManage.databaseSave.sqlite.treeEntities.TreeTableEntity -import treeApp.controller.databaseManage.databaseSave.sqlite.treeEntities.TreesTable -import treeApp.controller.databaseManage.databaseSave.sqlite.vertexEntities.VertexTable -import treeApp.controller.databaseManage.databaseSave.sqlite.vertexEntities.VertexTableEntity +import treelib.databaseSave.sqlite.treeEntities.TreeTableEntity +import treelib.databaseSave.sqlite.treeEntities.TreesTable +import treelib.databaseSave.sqlite.vertexEntities.VertexTable +import treelib.databaseSave.sqlite.vertexEntities.VertexTableEntity import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.transactions.transaction diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryJDBC.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryJDBC.kt similarity index 99% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryJDBC.kt rename to lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryJDBC.kt index d1b76f1..29d5c9d 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/SQLiteRepositoryJDBC.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryJDBC.kt @@ -1,4 +1,4 @@ -package treeApp.controller.databaseManage.databaseSave.sqlite +package treelib.databaseSave.sqlite import java.io.Closeable import java.sql.DriverManager diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreeTableEntity.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreeTableEntity.kt similarity index 79% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreeTableEntity.kt rename to lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreeTableEntity.kt index d845b95..dcb96b2 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreeTableEntity.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreeTableEntity.kt @@ -1,4 +1,4 @@ -package treeApp.controller.databaseManage.databaseSave.sqlite.treeEntities +package treelib.databaseSave.sqlite.treeEntities import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntityClass diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreesTable.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreesTable.kt similarity index 69% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreesTable.kt rename to lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreesTable.kt index df650d2..32daf7d 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/treeEntities/TreesTable.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreesTable.kt @@ -1,4 +1,4 @@ -package treeApp.controller.databaseManage.databaseSave.sqlite.treeEntities +package treelib.databaseSave.sqlite.treeEntities import org.jetbrains.exposed.dao.id.IntIdTable diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTable.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTable.kt similarity index 78% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTable.kt rename to lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTable.kt index b6ea822..2607412 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTable.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTable.kt @@ -1,4 +1,4 @@ -package treeApp.controller.databaseManage.databaseSave.sqlite.vertexEntities +package treelib.databaseSave.sqlite.vertexEntities import org.jetbrains.exposed.dao.id.IntIdTable diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt similarity index 85% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt rename to lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt index d409cec..3b8461f 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt +++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt @@ -1,4 +1,4 @@ -package treeApp.controller.databaseManage.databaseSave.sqlite.vertexEntities +package treelib.databaseSave.sqlite.vertexEntities import org.jetbrains.exposed.dao.IntEntity import org.jetbrains.exposed.dao.IntEntityClass diff --git a/settings.gradle.kts b/settings.gradle.kts index 26b0762..3c8815c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,3 +9,4 @@ rootProject.name = "treelib" include("lib") +include("treeApp") diff --git a/treeApp/build.gradle.kts b/treeApp/build.gradle.kts new file mode 100644 index 0000000..4e8e8a0 --- /dev/null +++ b/treeApp/build.gradle.kts @@ -0,0 +1,117 @@ +plugins { + java + kotlin("jvm") version "1.8.10" + jacoco + id("org.jetbrains.compose") version "1.4.0" +} + +kotlin { + jvmToolchain(17) +} + +repositories { + mavenLocal() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + mavenCentral() +} + +dependencies { + api("org.apache.commons:commons-math3:3.6.1") + implementation(project(":lib")) + 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.10.1") + + implementation(compose.desktop.currentOs) + implementation(compose.material3) + + implementation(kotlin("stdlib-jdk8")) +} + + +compose.desktop { + application { + mainClass = "Main" + } +} + + +tasks.test { + finalizedBy(tasks.jacocoTestReport) + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + showStandardStreams = true + } + val failedTests = mutableListOf() + val skippedTests = mutableListOf() + + addTestListener (object: TestListener { + override fun beforeSuite(suite: TestDescriptor?) { } + override fun beforeTest(testDescriptor: TestDescriptor?) { } + override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { + when (result.resultType) { + TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor) + TestResult.ResultType.SKIPPED -> skippedTests.add(testDescriptor) + else -> {} + } + } + override fun afterSuite(suite: TestDescriptor, result: TestResult) { + if (suite.parent == null) { // root suite + logger.lifecycle("####################################################################################") + logger.lifecycle("Test result: ${result.resultType}") + logger.lifecycle( + "Test summary: ${result.testCount} tests, " + + "${result.successfulTestCount} succeed, " + + "${result.skippedTestCount} skipped, " + + "${result.failedTestCount} failed." + ) + } + } + }) + +} + +tasks.jacocoTestReport { + dependsOn(tasks.test) + reports { + xml.required.set(true) + xml.outputLocation.set(layout.buildDirectory.file("jacoco/report.xml")) + csv.required.set(true) + csv.outputLocation.set(layout.buildDirectory.file("jacoco/report.csv")) + html.required.set(true) + html.outputLocation.set(layout.buildDirectory.dir("jacocoHtml")) + } +} + +tasks.jacocoTestCoverageVerification { + classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { + include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class") + exclude("**/singleObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class", "**/BINStruct.class", "**/AVLStruct.class", "**/RBStruct.class", "**/TreeStruct.class") + } }) + dependsOn(tasks.jacocoTestReport) + violationRules { + rule { + element = "CLASS" + limit { + counter = "BRANCH" + minimum = 0.5.toBigDecimal() + } + } + rule { + element = "CLASS" + limit { + counter = "LINE" + minimum = 0.6.toBigDecimal() + } + } + rule { + element = "CLASS" + limit { + counter = "METHOD" + minimum = 0.9.toBigDecimal() + } + } + } +} diff --git a/lib/src/main/kotlin/treeApp/main.kt b/treeApp/src/main/kotlin/treeApp/Main.kt similarity index 98% rename from lib/src/main/kotlin/treeApp/main.kt rename to treeApp/src/main/kotlin/treeApp/Main.kt index 4e546d8..654a8a5 100644 --- a/lib/src/main/kotlin/treeApp/main.kt +++ b/treeApp/src/main/kotlin/treeApp/Main.kt @@ -13,7 +13,6 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.* import androidx.compose.ui.window.* -import treeApp.controller.Controller import treeApp.ui.AppIcon import treeApp.ui.ControlFields import treeApp.ui.MyTopAppBar @@ -31,7 +30,7 @@ fun main() = application { val maximizeButton: MutableState = remember { mutableStateOf(false) } val closeButton: MutableState = remember { mutableStateOf(false) } - val controller = Controller() + val controller = treeApp.controller.Controller() val activeTree = remember { mutableStateOf(false) } val windowState = rememberWindowState(placement = WindowPlacement.Maximized) val deleteTreeState = remember { mutableStateOf(false) } diff --git a/lib/src/main/kotlin/treeApp/controller/Controller.kt b/treeApp/src/main/kotlin/treeApp/controller/Controller.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/controller/Controller.kt rename to treeApp/src/main/kotlin/treeApp/controller/Controller.kt diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/AVLTreeManager.kt b/treeApp/src/main/kotlin/treeApp/controller/databaseManage/AVLTreeManager.kt similarity index 96% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/AVLTreeManager.kt rename to treeApp/src/main/kotlin/treeApp/controller/databaseManage/AVLTreeManager.kt index ab7e55c..1d7902f 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/AVLTreeManager.kt +++ b/treeApp/src/main/kotlin/treeApp/controller/databaseManage/AVLTreeManager.kt @@ -1,7 +1,7 @@ package treeApp.controller.databaseManage -import treeApp.controller.databaseManage.databaseSave.sqlite.DrawableAVLVertex -import treeApp.controller.databaseManage.databaseSave.sqlite.SQLiteRepositoryExposed +import treelib.databaseSave.sqlite.DrawableAVLVertex +import treelib.databaseSave.sqlite.SQLiteRepositoryExposed import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/BINTreeManager.kt b/treeApp/src/main/kotlin/treeApp/controller/databaseManage/BINTreeManager.kt similarity index 95% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/BINTreeManager.kt rename to treeApp/src/main/kotlin/treeApp/controller/databaseManage/BINTreeManager.kt index 007708f..583cf14 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/BINTreeManager.kt +++ b/treeApp/src/main/kotlin/treeApp/controller/databaseManage/BINTreeManager.kt @@ -1,8 +1,8 @@ package treeApp.controller.databaseManage import com.google.gson.reflect.TypeToken -import treeApp.controller.databaseManage.databaseSave.jsonFormat.DrawableBINVertex -import treeApp.controller.databaseManage.databaseSave.jsonFormat.JsonRepository +import treelib.databaseSave.jsonFormat.DrawableBINVertex +import treelib.databaseSave.jsonFormat.JsonRepository import treelib.binTree.BINNode import treelib.binTree.BINStateContainer import treelib.binTree.BINStruct diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/RBTreeManager.kt b/treeApp/src/main/kotlin/treeApp/controller/databaseManage/RBTreeManager.kt similarity index 94% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/RBTreeManager.kt rename to treeApp/src/main/kotlin/treeApp/controller/databaseManage/RBTreeManager.kt index 7484cf8..1087744 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/RBTreeManager.kt +++ b/treeApp/src/main/kotlin/treeApp/controller/databaseManage/RBTreeManager.kt @@ -1,7 +1,7 @@ package treeApp.controller.databaseManage -import treeApp.controller.databaseManage.databaseSave.neo4j.DrawableRBVertex -import treeApp.controller.databaseManage.databaseSave.neo4j.Neo4jRepository +import treelib.databaseSave.neo4j.DrawableRBVertex +import treelib.databaseSave.neo4j.Neo4jRepository import treelib.commonObjects.Container import treelib.rbTree.RBNode import treelib.rbTree.RBStateContainer diff --git a/lib/src/main/kotlin/treeApp/controller/databaseManage/TreeManager.kt b/treeApp/src/main/kotlin/treeApp/controller/databaseManage/TreeManager.kt similarity index 93% rename from lib/src/main/kotlin/treeApp/controller/databaseManage/TreeManager.kt rename to treeApp/src/main/kotlin/treeApp/controller/databaseManage/TreeManager.kt index e8a0be5..f598fd7 100644 --- a/lib/src/main/kotlin/treeApp/controller/databaseManage/TreeManager.kt +++ b/treeApp/src/main/kotlin/treeApp/controller/databaseManage/TreeManager.kt @@ -1,6 +1,6 @@ package treeApp.controller.databaseManage -import treeApp.controller.databaseManage.databaseSave.DrawableVertex +import treelib.databaseSave.DrawableVertex import treelib.abstractTree.Node import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct diff --git a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableNode.kt b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableNode.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableNode.kt rename to treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableNode.kt diff --git a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableTree.kt b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableTree.kt similarity index 96% rename from lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableTree.kt rename to treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableTree.kt index 44de029..3b9da5d 100644 --- a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableTree.kt +++ b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableAVL/AVLDrawableTree.kt @@ -2,7 +2,7 @@ package treeApp.controller.viewPart.drawableAVL import androidx.compose.runtime.mutableStateOf import treeApp.controller.databaseManage.TreeManager -import treeApp.controller.databaseManage.databaseSave.sqlite.DrawableAVLVertex +import treelib.databaseSave.sqlite.DrawableAVLVertex import treelib.avlTree.AVLNode import treelib.avlTree.AVLStateContainer import treelib.avlTree.AVLStruct diff --git a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableNode.kt b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableNode.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableNode.kt rename to treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableNode.kt diff --git a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableTree.kt b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableTree.kt similarity index 96% rename from lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableTree.kt rename to treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableTree.kt index 9263999..a2f1142 100644 --- a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableTree.kt +++ b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableBIN/BINDrawableTree.kt @@ -2,7 +2,7 @@ package treeApp.controller.viewPart.drawableBIN import androidx.compose.runtime.mutableStateOf import treeApp.controller.databaseManage.BINTreeManager -import treeApp.controller.databaseManage.databaseSave.jsonFormat.DrawableBINVertex +import treelib.databaseSave.jsonFormat.DrawableBINVertex import treelib.binTree.BINNode import treelib.binTree.BINStateContainer import treelib.binTree.BINStruct diff --git a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableNode.kt b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableNode.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableNode.kt rename to treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableNode.kt diff --git a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableTree.kt b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableTree.kt similarity index 97% rename from lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableTree.kt rename to treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableTree.kt index f4007d2..739444d 100644 --- a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableTree.kt +++ b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableRB/RBDrawableTree.kt @@ -2,7 +2,7 @@ package treeApp.controller.viewPart.drawableRB import androidx.compose.runtime.mutableStateOf import treeApp.controller.databaseManage.TreeManager -import treeApp.controller.databaseManage.databaseSave.neo4j.DrawableRBVertex +import treelib.databaseSave.neo4j.DrawableRBVertex import treelib.commonObjects.Container import treelib.rbTree.RBNode import treelib.rbTree.RBStateContainer diff --git a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawTree.kt b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawTree.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawTree.kt rename to treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawTree.kt diff --git a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableNode.kt b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableNode.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableNode.kt rename to treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableNode.kt diff --git a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableTree.kt b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableTree.kt similarity index 99% rename from lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableTree.kt rename to treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableTree.kt index 9b3b7e1..e744132 100644 --- a/lib/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableTree.kt +++ b/treeApp/src/main/kotlin/treeApp/controller/viewPart/drawableTree/DrawableTree.kt @@ -1,7 +1,7 @@ package treeApp.controller.viewPart.drawableTree import treeApp.controller.databaseManage.TreeManager -import treeApp.controller.databaseManage.databaseSave.DrawableVertex +import treelib.databaseSave.DrawableVertex import treelib.abstractTree.Node import treelib.abstractTree.StateContainer import treelib.abstractTree.TreeStruct diff --git a/lib/src/main/kotlin/treeApp/ui/ControlFields.kt b/treeApp/src/main/kotlin/treeApp/ui/ControlFields.kt similarity index 96% rename from lib/src/main/kotlin/treeApp/ui/ControlFields.kt rename to treeApp/src/main/kotlin/treeApp/ui/ControlFields.kt index 2dffa70..ebafe11 100644 --- a/lib/src/main/kotlin/treeApp/ui/ControlFields.kt +++ b/treeApp/src/main/kotlin/treeApp/ui/ControlFields.kt @@ -29,7 +29,7 @@ import treeApp.controller.viewPart.drawableTree.DrawTree @Composable fun ControlFields( - controller: Controller, + controller: treeApp.controller.Controller, activeTree: MutableState, deleteTreeState: MutableState, openTreeState: MutableState, @@ -87,9 +87,11 @@ fun ControlFields( createTreeState.value = false } if (returnButtonClickState.value) { - tree?.updateTree() - tree?.repositionTree(800f, 10f) - returnButtonClickState.value = false + LaunchedEffect(returnButtonClickState){ + tree?.updateTree() + tree?.repositionTree(800f, 10f) + returnButtonClickState.value = false + } dragState.value = false } tree?.let { displayTree(it, dragState) } diff --git a/lib/src/main/kotlin/treeApp/ui/DeleteAlertDialog.kt b/treeApp/src/main/kotlin/treeApp/ui/DeleteAlertDialog.kt similarity index 98% rename from lib/src/main/kotlin/treeApp/ui/DeleteAlertDialog.kt rename to treeApp/src/main/kotlin/treeApp/ui/DeleteAlertDialog.kt index aefdfda..b2cd8e6 100644 --- a/lib/src/main/kotlin/treeApp/ui/DeleteAlertDialog.kt +++ b/treeApp/src/main/kotlin/treeApp/ui/DeleteAlertDialog.kt @@ -24,7 +24,7 @@ import treeApp.controller.Controller fun DeleteDialog( hoverButtonState: MutableState, clickButtonsState: MutableState, - controller: Controller, + controller: treeApp.controller.Controller, activeTree: MutableState, deleteTreeState: MutableState ) { diff --git a/lib/src/main/kotlin/treeApp/ui/Design.kt b/treeApp/src/main/kotlin/treeApp/ui/Design.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/ui/Design.kt rename to treeApp/src/main/kotlin/treeApp/ui/Design.kt diff --git a/lib/src/main/kotlin/treeApp/ui/FileDialog.kt b/treeApp/src/main/kotlin/treeApp/ui/FileDialog.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/ui/FileDialog.kt rename to treeApp/src/main/kotlin/treeApp/ui/FileDialog.kt diff --git a/lib/src/main/kotlin/treeApp/ui/SaveDialog.kt b/treeApp/src/main/kotlin/treeApp/ui/SaveDialog.kt similarity index 99% rename from lib/src/main/kotlin/treeApp/ui/SaveDialog.kt rename to treeApp/src/main/kotlin/treeApp/ui/SaveDialog.kt index efe8571..874017d 100644 --- a/lib/src/main/kotlin/treeApp/ui/SaveDialog.kt +++ b/treeApp/src/main/kotlin/treeApp/ui/SaveDialog.kt @@ -29,7 +29,7 @@ fun SaveDialog( hoverButtonState: MutableState, clickButtonState: MutableState, selectFileName: MutableState, - controller: Controller, + controller: treeApp.controller.Controller, activeTree: MutableState ) { diff --git a/lib/src/main/kotlin/treeApp/ui/Submenu.kt b/treeApp/src/main/kotlin/treeApp/ui/Submenu.kt similarity index 97% rename from lib/src/main/kotlin/treeApp/ui/Submenu.kt rename to treeApp/src/main/kotlin/treeApp/ui/Submenu.kt index 86ba633..5b5a39a 100644 --- a/lib/src/main/kotlin/treeApp/ui/Submenu.kt +++ b/treeApp/src/main/kotlin/treeApp/ui/Submenu.kt @@ -22,7 +22,7 @@ import treeApp.controller.Controller @Composable fun CreateMenu( expandedNested: MutableState, - controller: Controller, + controller: treeApp.controller.Controller, activeTree: MutableState, createTreeState: MutableState ) { @@ -62,7 +62,7 @@ fun OpenMenu( treesNames: List>, activeTree: MutableState, openTreeState: MutableState, - controller: Controller + controller: treeApp.controller.Controller ) { val trees = listOf("Red black tree", "AVL tree", "Binary tree") @@ -128,7 +128,7 @@ fun SelectTree( selectedTreeName: MutableState, activeTree: MutableState, openTreeState: MutableState, - controller: Controller + controller: treeApp.controller.Controller ) { val dirPath = System.getProperty("user.dir") + "/saved-trees" // !! diff --git a/lib/src/main/kotlin/treeApp/ui/TopAppBar.kt b/treeApp/src/main/kotlin/treeApp/ui/TopAppBar.kt similarity index 99% rename from lib/src/main/kotlin/treeApp/ui/TopAppBar.kt rename to treeApp/src/main/kotlin/treeApp/ui/TopAppBar.kt index ab6975e..57015e6 100644 --- a/lib/src/main/kotlin/treeApp/ui/TopAppBar.kt +++ b/treeApp/src/main/kotlin/treeApp/ui/TopAppBar.kt @@ -31,7 +31,7 @@ fun MyTopAppBar( clickMaximizeButton: MutableState, clickCloseButton: MutableState, windowState: WindowState, - controller: Controller, + controller: treeApp.controller.Controller, activeTree: MutableState, deleteTreeState: MutableState, openTreeState: MutableState, diff --git a/lib/src/main/kotlin/treeApp/ui/TreeDrawingUtils.kt b/treeApp/src/main/kotlin/treeApp/ui/TreeDrawingUtils.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/ui/TreeDrawingUtils.kt rename to treeApp/src/main/kotlin/treeApp/ui/TreeDrawingUtils.kt diff --git a/lib/src/main/kotlin/treeApp/ui/UserInputValidation.kt b/treeApp/src/main/kotlin/treeApp/ui/UserInputValidation.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/ui/UserInputValidation.kt rename to treeApp/src/main/kotlin/treeApp/ui/UserInputValidation.kt diff --git a/lib/src/main/kotlin/treeApp/ui/nodeDesign/AVLNodeDesign.kt b/treeApp/src/main/kotlin/treeApp/ui/nodeDesign/AVLNodeDesign.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/ui/nodeDesign/AVLNodeDesign.kt rename to treeApp/src/main/kotlin/treeApp/ui/nodeDesign/AVLNodeDesign.kt diff --git a/lib/src/main/kotlin/treeApp/ui/nodeDesign/BINNodeDesign.kt b/treeApp/src/main/kotlin/treeApp/ui/nodeDesign/BINNodeDesign.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/ui/nodeDesign/BINNodeDesign.kt rename to treeApp/src/main/kotlin/treeApp/ui/nodeDesign/BINNodeDesign.kt diff --git a/lib/src/main/kotlin/treeApp/ui/nodeDesign/NodeDesign.kt b/treeApp/src/main/kotlin/treeApp/ui/nodeDesign/NodeDesign.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/ui/nodeDesign/NodeDesign.kt rename to treeApp/src/main/kotlin/treeApp/ui/nodeDesign/NodeDesign.kt diff --git a/lib/src/main/kotlin/treeApp/ui/nodeDesign/RBNodeDesign.kt b/treeApp/src/main/kotlin/treeApp/ui/nodeDesign/RBNodeDesign.kt similarity index 100% rename from lib/src/main/kotlin/treeApp/ui/nodeDesign/RBNodeDesign.kt rename to treeApp/src/main/kotlin/treeApp/ui/nodeDesign/RBNodeDesign.kt From dfbcf370311933d4bc7af7ff29579159f1d70370 Mon Sep 17 00:00:00 2001 From: Georgy S Date: Wed, 3 May 2023 23:41:04 +0300 Subject: [PATCH 212/212] feat: Add possibility to run app from gradle. --- lib/build.gradle.kts | 7 --- settings.gradle.kts | 16 +++++++ treeApp/build.gradle.kts | 92 +--------------------------------------- 3 files changed, 17 insertions(+), 98 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index c272ee8..c44d77b 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -11,11 +11,6 @@ kotlin { jvmToolchain(17) } -repositories { - mavenLocal() - mavenCentral() -} - dependencies { api("org.apache.commons:commons-math3:3.6.1") implementation("com.google.guava:guava:31.1-jre") @@ -39,8 +34,6 @@ dependencies { testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2") - implementation(kotlin("stdlib-jdk8")) - } tasks.test { diff --git a/settings.gradle.kts b/settings.gradle.kts index 3c8815c..9e01550 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,3 +10,19 @@ rootProject.name = "treelib" include("lib") include("treeApp") + + +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + } +} + +dependencyResolutionManagement { + repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + google() + } +} diff --git a/treeApp/build.gradle.kts b/treeApp/build.gradle.kts index 4e8e8a0..a44b44d 100644 --- a/treeApp/build.gradle.kts +++ b/treeApp/build.gradle.kts @@ -1,7 +1,6 @@ plugins { java kotlin("jvm") version "1.8.10" - jacoco id("org.jetbrains.compose") version "1.4.0" } @@ -9,12 +8,6 @@ kotlin { jvmToolchain(17) } -repositories { - mavenLocal() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") - mavenCentral() -} - dependencies { api("org.apache.commons:commons-math3:3.6.1") implementation(project(":lib")) @@ -25,93 +18,10 @@ dependencies { implementation(compose.desktop.currentOs) implementation(compose.material3) - - implementation(kotlin("stdlib-jdk8")) } - compose.desktop { application { - mainClass = "Main" - } -} - - -tasks.test { - finalizedBy(tasks.jacocoTestReport) - useJUnitPlatform() - testLogging { - events("passed", "skipped", "failed") - showStandardStreams = true - } - val failedTests = mutableListOf() - val skippedTests = mutableListOf() - - addTestListener (object: TestListener { - override fun beforeSuite(suite: TestDescriptor?) { } - override fun beforeTest(testDescriptor: TestDescriptor?) { } - override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { - when (result.resultType) { - TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor) - TestResult.ResultType.SKIPPED -> skippedTests.add(testDescriptor) - else -> {} - } - } - override fun afterSuite(suite: TestDescriptor, result: TestResult) { - if (suite.parent == null) { // root suite - logger.lifecycle("####################################################################################") - logger.lifecycle("Test result: ${result.resultType}") - logger.lifecycle( - "Test summary: ${result.testCount} tests, " + - "${result.successfulTestCount} succeed, " + - "${result.skippedTestCount} skipped, " + - "${result.failedTestCount} failed." - ) - } - } - }) - -} - -tasks.jacocoTestReport { - dependsOn(tasks.test) - reports { - xml.required.set(true) - xml.outputLocation.set(layout.buildDirectory.file("jacoco/report.xml")) - csv.required.set(true) - csv.outputLocation.set(layout.buildDirectory.file("jacoco/report.csv")) - html.required.set(true) - html.outputLocation.set(layout.buildDirectory.dir("jacocoHtml")) - } -} - -tasks.jacocoTestCoverageVerification { - classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) { - include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class") - exclude("**/singleObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class", "**/BINStruct.class", "**/AVLStruct.class", "**/RBStruct.class", "**/TreeStruct.class") - } }) - dependsOn(tasks.jacocoTestReport) - violationRules { - rule { - element = "CLASS" - limit { - counter = "BRANCH" - minimum = 0.5.toBigDecimal() - } - } - rule { - element = "CLASS" - limit { - counter = "LINE" - minimum = 0.6.toBigDecimal() - } - } - rule { - element = "CLASS" - limit { - counter = "METHOD" - minimum = 0.9.toBigDecimal() - } - } + mainClass = "treeApp.MainKt" } }

i!B-GTA78o5_i-wja0p`9~ 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⁢rD>Ph(-PZ(Dhu#h9|$dTW?&v-3mnQ*LezK9m>ryN{yvhT&V_ZoYc zGM*Dj@0DQgcb-vxiurd+pF@6@>dAdtMN)4luT(GpbbAt<6iOMjKYgF*QED%O3Un*c zE?|OrOa$-s_YcHB`6vI_Uck}I4-I2wi1S>wM4LG*tgE3d$}rfPx~!a( z+dHfb*`$1pG#(AY-6c#f6-kc*ud`i(jD!ZvyV}I8)}y`Fd}Xd}3*RrR zTsSV;S}*#tRks=Q%k#0gvS8+aX>Bo;+Fi>P7_sw*X#+-FXR=7SDG6+kGa;w)UR~ z`PuG`a%9dGnfP9P#(aSBXYZZeow9lljux(Cg_%fA^1+*Y%-mu#Io{n6=9#+81s`~c z0!H)_`Uh`>(hI!qeFDx%b5!lDQ+~LL#pDM80DgfLRunOLUXncHqBn#aJibZFzp-%B zx)xy<*QAifHQ^zc|4V1j*zBiPgT52&?Z*{S$QhWiXRrm_06HH~XkGzG+BDqC;WCX=QZ*@Ii1Z!OAV5OJ zXb_cSl42$S^7q71qcP_=eDqkfNfNJR{<+(UdVbCCo5WiM+Cd<%+>rzjwVC{-HD9gK zQhP;_j6AXFr+XJ?=e#Gt$63Sp3wsLnrIi5X0Ru_i-aI432K3TiYQ~(Tf?Qcf+o`Z) z>hsh>U3E!!yTK`V!Nvclz)zTa=D5@x z;|tUNOR_38CK&6@st;l0`_sgTlCo$mQ`7pGb-K z0NYcF=|^*l@Ic)$v0=PkKmED|5QsDkHJi^M>a$`}Xfh09Te)Q0QM!ZGf)lTwjH{ZU zw;ej-jlhikn0|xVSoX&g3|fEM47@j~A3$h88hNaVu;CMWPQneur9lx_l4Ijy9F+(S zUAKhb)y0(<>>G%A_9Un>Tro-7J3JiyeZ9fa^v&vv4%H768deR)<|bpSDWa=i1ZTKU zecKqDY%UWProU{5uJJ*;LP_%5(43bX_6mX2`67z)izV_;?@kf97NAcQg zCoJrJ``h1+uYTn#F*`FGJ>5MX^A@!Q4!oRn20h;>dd{a?2O~cHp=skG_{ULw&)kL?;T>?w{DpiZ{3Jn6V~nuMxaXCEr0Hh{ zBRHhIZ2lv`%JW!lPt2=Y;`)ifT3nKlRPnafbV{^wjg`WHr;Ei#2{QaeA5&}?vxk>rB^?q@rMzkA6M1) zFgT2>iGRwxh=k(a?+7=(Qb#57Jy-XK`XZS>rKM#0kC<^Ocn&&&7av@8;x&anLy+Pg zmc*Wagd4aLc+WR5<7tdlz6+-wj4OSWV*b5*-3_~GQpmI_Y5O6x2p<{GC!EhSK%I2C z4Y>}9k;_U0mc#4g$dSYG%fI|f@!P-s&*Pzo9n9375Ix z8EE0A81r_$8y~qEJzd>t!>J4&8amr$e5!o&T0;= zti`>z-xVKv@B3q0_gi9|JH)(pS1P8z8=AVEy*(oGZD#%%FN9KVL zL{ZV!(Gjc5>(NnZkE6FAjlFw^J?A;!0Gy4fOk-F zMYnB+1--9!_1F}cv6>eSV3mUtZ^03mT;h!l9E*C8)q_&I^4I*vQ3J{qRth>rPcl