diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..c2df060
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,37 @@
+name: Build
+
+on:
+ pull_request:
+ branches:
+ - main
+ - develop
+ - fixes-after-1st-review
+ push:
+ branches:
+ - main
+ - develop
+ - fixes-after-1st-review
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Java JDK
+ uses: actions/setup-java@v4
+ with:
+ java-version: 17
+ cache: gradle
+ distribution: "temurin"
+
+ - name: Set up Gradle
+ uses: gradle/actions/setup-gradle@v3
+
+ - name: Build with Gradle
+ run: ./gradlew build
+
+ - name: Run tests
+ run: ./gradlew test
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
new file mode 100644
index 0000000..8e40a86
--- /dev/null
+++ b/.github/workflows/coverage.yml
@@ -0,0 +1,56 @@
+name: Jacoco coverage
+
+on:
+ pull_request:
+ branches:
+ - main
+ - develop
+ - fixes-after-1st-review
+ push:
+ branches:
+ - main
+ - develop
+ - fixes-after-1st-review
+ workflow_dispatch:
+
+jobs:
+ coverage:
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Java JDK
+ uses: actions/setup-java@v4
+ with:
+ java-version: 17
+ distribution: "temurin"
+
+ - name: Set up Gradle
+ uses: gradle/actions/setup-gradle@v3
+
+ - name: Run Coverage
+ run: ./gradlew jacocoTestReport
+
+ - name: Generate JaCoCo Report
+ uses: cicirello/jacoco-badge-generator@v2
+ with:
+ generate-branches-badge: true
+ jacoco-csv-file: lib/build/reports/jacoco/test/jacocoTestReport.csv
+
+ - name: Add coverage to PR
+ id: jacoco
+ uses: madrapps/jacoco-report@v1.6.1
+ with:
+ paths: |
+ ${{ github.workspace }}/lib/build/reports/jacoco/test/jacocoTestReport.xml,
+ ${{ github.workspace }}/**/build/reports/jacoco/**/debugCoverage.xml
+ token: ${{ secrets.GITHUB_TOKEN }}
+ title: '# 🇷🇺 Coverage Report'
+ update-comment: true
+ min-coverage-overall: 40
+ min-coverage-changed-files: 60
+ pass-emoji: '🥳'
+ fail-emoji: '🤡'
diff --git a/.github/workflows/ktlint.yml b/.github/workflows/ktlint.yml
new file mode 100644
index 0000000..f58f9f6
--- /dev/null
+++ b/.github/workflows/ktlint.yml
@@ -0,0 +1,25 @@
+name: ktlint
+
+on:
+ pull_request:
+ branches:
+ - main
+ - develop
+ - fixes-after-1st-review
+ push:
+ branches:
+ - main
+ - develop
+ - fixes-after-1st-review
+ workflow_dispatch:
+
+jobs:
+ ktlint:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: "checkout"
+ uses: actions/checkout@v2
+
+ - name: "ktlint"
+ uses: "block42-blockchain-company/ktlint-action@master"
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8d55f14
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,282 @@
+# Created by https://www.toptal.com/developers/gitignore/api/linux,windows,java,kotlin,gradle,intellij+all,intellij
+# Edit at https://www.toptal.com/developers/gitignore?templates=linux,windows,java,kotlin,gradle,intellij+all,intellij
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Intellij Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+# Sonarlint plugin
+# https://plugins.jetbrains.com/plugin/7973-sonarlint
+.idea/**/sonarlint/
+
+# SonarQube Plugin
+# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
+.idea/**/sonarIssues.xml
+
+# Markdown Navigator plugin
+# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
+.idea/**/markdown-navigator.xml
+.idea/**/markdown-navigator-enh.xml
+.idea/**/markdown-navigator/
+
+# Cache file creation bug
+# See https://youtrack.jetbrains.com/issue/JBR-2257
+.idea/$CACHE_FILE$
+
+# CodeStream plugin
+# https://plugins.jetbrains.com/plugin/12206-codestream
+.idea/codestream.xml
+
+# Azure Toolkit for IntelliJ plugin
+# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
+.idea/**/azureSettings.xml
+
+### Intellij+all ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+
+# AWS User-specific
+
+# Generated files
+
+# Sensitive or high-churn files
+
+# Gradle
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+
+# Mongo Explorer plugin
+
+# File-based project format
+
+# IntelliJ
+
+# mpeltonen/sbt-idea plugin
+
+# JIRA plugin
+
+# Cursive Clojure plugin
+
+# SonarLint plugin
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+
+# Editor-based Rest Client
+
+# Android studio 3.1+ serialized cache file
+
+### Intellij+all Patch ###
+# Ignore everything but code style settings and run configurations
+# that are supposed to be shared within teams.
+
+.idea/*
+
+!.idea/codeStyles
+!.idea/runConfigurations
+
+### Java ###
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+replay_pid*
+
+### Kotlin ###
+# Compiled class file
+
+# Log file
+
+# BlueJ files
+
+# Mobile Tools for Java (J2ME)
+
+# Package Files #
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+### Gradle ###
+.gradle
+**/build/
+!src/**/build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Avoid ignore Gradle wrappper properties
+!gradle-wrapper.properties
+
+# Cache of project
+.gradletasknamecache
+
+# Eclipse Gradle plugin generated files
+# Eclipse Core
+.project
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+### Gradle Patch ###
+# Java heap dump
+*.hprof
+
+# End of https://www.toptal.com/developers/gitignore/api/linux,windows,java,kotlin,gradle,intellij+all,intellij
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c2d2824
--- /dev/null
+++ b/README.md
@@ -0,0 +1,168 @@
+
+
+
+ BinTreeKit
+
+## About Project
+`BinTreeKit` is a library that provides implementations for three types of trees: `SimpleBinarySearchTree`, `AVLSearchTree` and `RBSearchTree`. The library is designed to simplify the process of managing hierarchical data, allowing Kotlin developers to focus on building robust and scalable applications.
+
+
+#### Table of Contents
+- [About Project](#about-project)
+- [Usage](#usage)
+- [Library Features](#library-features)
+ - [Constructors](#constructors)
+ - [Methods](#methods)
+ - [Tree Properties](#tree-properties)
+ - [Iterator](#iterator)
+ - [Constructors](#constructors-1)
+ - [Methods](#methods-1)
+- [Developers](#developers)
+- [Contributing](#contributing)
+- [License](#license)
+
+
+## Usage
+1. **Importing Classes:**
+```kotlin
+import main.trees.SimpleBinarySearchTree
+import main.trees.AVLSearchTree
+import main.trees.rbTree
+```
+
+2. **Instantiate Trees:**
+```kotlin
+val map = mapOf(Pair(1, "cat"), Pair(2, "dog"))
+
+// create a Simple Binary Search Tree
+val emptyBstTree = SimpleBinarySearchTree()
+val bstTree = SimpleBinarySearchTree(map)
+
+// create an AVL Search Tree
+val emptyAvlTree = AVLSearchTree()
+val avlTree = AVLSearchTree(map)
+
+// create a Red-Black Search Tree
+val emptyRbTree = RBSearchTree()
+val rbTree = RBSearchTree(map)
+```
+
+
+3. **Use Tree Methods:**
+```kotlin
+// put key-value pairs with different values of replaceIfExists perematers
+bstTree.putAll(map, true)
+rbTree.put(4, "horse", false)
+
+// remove key-value pair from tree and return value
+println(rbTree.remove(4)) // output: horse
+bstTree.remove(1)
+
+//get key-value pair from tree
+println(avlTree.getMin()) //output: cat
+
+// pairwise iteration
+for (pair in avlTree) {
+ print("${pair.second}, ")
+} // output: cat, dog
+```
+
+
+## Library Features
+
+
+### Constructors
+- `constructor(comparator: Comparator? = null)` - constructs a new AbstractBinarySearchTree instance with the given comparator.
+
+
+- `constructor(map: Map, replaceIfExists: Boolean = true, comparator: Comparator? = null)` - constructs a new AbstractBinarySearchTree instance initialized with the contents of the given map.
+
+ > The `comparator` to be used optional for ordering keys in the tree.
+
+
+### Methods
+
+
+- `put(key: K, value: V, replaceIfExists : Boolean = true)` - inserts the specified key-value pair into the tree.
+
+- `putAll(map: Map, replaceIfExists: Boolean = true)` - inserts all key-value pairs from the given map into the tree.
+
+ > `replaceIfExists` is an optional (default is true) Boolean flag indicating whether to replace the value if the key already exists in the tree.
+
+
+- `remove(key: K): V?` - removes the key-value pair with the specified key from the tree and returns value associated with the removed key, or `null` if the key was not found in the tree.
+
+
+- `get(key: K): V?` - retrieves the value associated with the specified key from the tree and returns value associated with the specified key, or `null` if the key was not found in the tree.
+
+- `getPair(key: K): Pair?` - retrieves the key-value pair associated with the specified key from the tree and returns the key-value pair associated with the specified key, or `null` if the key was not found in the tree.
+
+- `getMin(): V?` - retrieves the value associated with the minimum key in the tree and returns the value associated with the minimum key, or `null` if the tree is empty.
+
+- `getMax(): V?` - retrieves the value associated with the maximum key in the tree and returns the value associated with the maximum key, or `null` if the tree is empty.
+
+- `getMinKeyPair(): Pair?` - retrieves the key-value pair associated with the minimum key in the tree and returns the key-value pair associated with the minimum key, or `null` if the tree is empty.
+
+- `getMaxKeyPair(): Pair?` - retrieves the key-value pair associated with the maximum key in the tree and returns the key-value pair associated with the maximum key, or `null` if the tree is empty.
+
+
+- `size(): Long` - returns the number of key-value pairs in the tree.
+
+- `isEmpty(): Boolean` checks whether the tree is empty and returns `true` if the tree is empty, `false` otherwise.
+
+
+### Tree Properties
+
+- `size: Long` - the number of key-value pairs currently stored in the tree.
+
+
+- `isEmpty: Boolean` - indicates whether the tree is empty.
+
+
+### Iterator
+###### Constructors
+- `constructor(vertex: N?)` - constructs a new TreeIterator instance with the specified starting vertex.
+
+ > `vertex` is the starting vertex of the iterator.
+
+###### Methods
+
+- `hasNext(): Boolean` - checks if there are more elements in the iteration and returns `true` if there are more elements, `false` otherwise.
+
+
+- `next(): Pair` - retrieves the next key-value pair in the iteration and returns the next key-value pair in the iteration.
+
+
+## Developers
+- [vicitori](https://github.com/vicitori) - Victoria Lutsyuk (Simple Binary Search Tree)
+- [Szczucki](https://github.com/Szczucki) - Nikita Shchutskii (AVL Search Tree)
+- [TerrMen](https://github.com/TerrMen) - Kostya Oreshin (RB Search Tree)
+
+
+## Contributing
+
+**Quick start**
+
+1. Create new branch branching off `develop`, name it as feature you want to implement
+```bash
+git checkout develop
+git switch -c my_feature
+```
+
+2. Commit the changes and write messages according to [commits convention](https://www.conventionalcommits.org/en/v1.0.0/)
+```bash
+git commit -m "feat: implement new feature"
+```
+
+3. Push changes to a remote repository
+```bash
+git push origin my_feature
+```
+
+4. Open pull request to `develop`
+
+
+## License
+This project uses the **APACHE LICENSE, VERSION 2.0**. See the [LICENSE](LICENSE.md) for more info.
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..d4d7e1f
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,15 @@
+# This file was generated by the Gradle 'init' task.
+# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
+
+[versions]
+commons-math3 = "3.6.1"
+guava = "32.1.3-jre"
+junit-jupiter-engine = "5.10.0"
+
+[libraries]
+commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" }
+guava = { module = "com.google.guava:guava", version.ref = "guava" }
+junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter-engine" }
+
+[plugins]
+jvm = { id = "org.jetbrains.kotlin.jvm", version = "1.9.20" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..d64cd49
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a80b22c
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..1aa94a4
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,249 @@
+#!/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##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# 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
+ if ! command -v java >/dev/null 2>&1
+ then
+ 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
+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=SC2039,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=SC2039,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
+
+
+# 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"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+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..25da30d
--- /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. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+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
new file mode 100644
index 0000000..50adebb
--- /dev/null
+++ b/lib/build.gradle.kts
@@ -0,0 +1,80 @@
+/*
+ * 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 on building Java & JVM projects, please refer to https://docs.gradle.org/8.6/userguide/building_java_projects.html in the Gradle documentation.
+ */
+
+plugins {
+ // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
+ alias(libs.plugins.jvm)
+
+ // Apply the java-library plugin for API and implementation separation.
+ `java-library`
+
+ // Code coverage plugin
+ jacoco
+
+ // Documentation generation
+ id("org.jetbrains.dokka") version "1.9.20"
+}
+
+repositories {
+ // Use Maven Central for resolving dependencies.
+ mavenCentral()
+}
+
+dependencies {
+ // Use the Kotlin JUnit 5 integration.
+ testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
+ //
+ testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2")
+
+ // Use the JUnit 5 integration.
+ testImplementation(libs.junit.jupiter.engine)
+
+ testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+
+ compileOnly("org.jetbrains.dokka:dokka-core:1.9.20")
+}
+
+// Apply a specific Java toolchain to ease working on different environments.
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(17)
+ }
+}
+
+tasks.named("test") {
+ // Use JUnit Platform for unit tests.
+ useJUnitPlatform()
+}
+
+tasks.named("jacocoTestReport") {
+ dependsOn(tasks.test)
+ reports {
+ csv.required = true
+ xml.required = true
+ html.outputLocation = layout.buildDirectory.dir("jacocoHtml")
+ }
+}
+
+tasks.dokkaHtml {
+ outputDirectory.set(layout.buildDirectory.dir("documentation/html"))
+}
+
+tasks.dokkaGfm {
+ outputDirectory.set(layout.buildDirectory.dir("documentation/markdown"))
+}
+
+tasks.register("dokkaHtmlJar") {
+ dependsOn(tasks.dokkaHtml)
+ from(tasks.dokkaHtml.flatMap { it.outputDirectory })
+ archiveClassifier.set("html-docs")
+}
+
+tasks.register("dokkaJavadocJar") {
+ dependsOn(tasks.dokkaJavadoc)
+ from(tasks.dokkaJavadoc.flatMap { it.outputDirectory })
+ archiveClassifier.set("javadoc")
+}
diff --git a/lib/src/main/kotlin/iterator/TreeIterator.kt b/lib/src/main/kotlin/iterator/TreeIterator.kt
new file mode 100644
index 0000000..1365945
--- /dev/null
+++ b/lib/src/main/kotlin/iterator/TreeIterator.kt
@@ -0,0 +1,49 @@
+package iterator
+
+import vertexes.InterfaceBSTVertex
+import java.util.Stack
+
+/**
+ * Iterator iterates over the vertices of the tree, visiting each vertex in the order of a depth-first traversal.
+ *
+ * [Iterator] interface implementation.
+ *
+ * @param K the type of keys in the tree
+ * @param V the type of values associated with the keys
+ * @param N the type of tree vertices implementing InterfaceBSTVertex
+ */
+open class TreeIterator>(
+ vertex: N?,
+) : Iterator> {
+ protected val stack = Stack()
+
+ init {
+ // Initialize the iterator with the given vertex by adding it to the stack
+ vertex?.let { stack.add(it) }
+ }
+
+ /**
+ * Returns true if the iterator has more elements.
+ *
+ * This method checks if there are more vertices to traverse in the tree.
+ *
+ * @return true if the iterator has more elements, otherwise false
+ */
+ override fun hasNext(): Boolean {
+ return stack.isNotEmpty()
+ }
+
+ /**
+ * Returns the next element in the iteration.
+ *
+ * This method returns the next vertex in the depth-first traversal of the tree.
+ *
+ * @return the next element in the iteration as a Pair containing the key and value of the vertex
+ */
+ override fun next(): Pair {
+ val nextVertex: N = stack.pop()
+ nextVertex.leftSon?.let { stack.add(it) }
+ nextVertex.rightSon?.let { stack.add(it) }
+ return Pair(nextVertex.key, nextVertex.value)
+ }
+}
diff --git a/lib/src/main/kotlin/trees/AVLSearchTree.kt b/lib/src/main/kotlin/trees/AVLSearchTree.kt
new file mode 100644
index 0000000..0fb1771
--- /dev/null
+++ b/lib/src/main/kotlin/trees/AVLSearchTree.kt
@@ -0,0 +1,466 @@
+package trees
+
+import vertexes.AVLVertex
+
+/**
+ * An implementation of a binary search tree that automatically balances itself using AVL rotations.
+ *
+ * It extends [AbstractBinarySearchTree] and uses [AVLVertex] as vertices.
+ *
+ * If the tree has an incomparable key type and comparator is `null`, if the tree
+ * is non-empty, when trying to call the search, insert and delete methods, the tree
+ * will remain unchanged, the operation throws an exception with the message "Key's
+ * type is incomparable and comparator was not given".
+ *
+ * @param K key type
+ * @param V value type
+ * @property comparator `Comparator?` type; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ * @property size `Long` type; number of key-value pairs in this tree
+ * @property root `AVLVertex?` type, `null` by default
+ */
+open class AVLSearchTree : AbstractBinarySearchTree> {
+ /**
+ * Puts a new vertex of a certain type into the tree with a given key and value
+ *
+ * @param key `K` type
+ * @param value `V` type, associated with this key
+ * @param replaceIfExists `Boolean` type; If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ */
+ override fun put(
+ key: K,
+ value: V,
+ replaceIfExists: Boolean,
+ ) {
+ if (isNotEmpty()) {
+ when (val putRecReturned = putRec(key, value, replaceIfExists, root as AVLVertex)) {
+ null, root -> {}
+ else -> root = putRecReturned
+ }
+ return
+ }
+ root = AVLVertex(key, value)
+ size++
+ }
+
+ /**
+ * Associates the specified value with the specified key in this tree
+ *
+ * @param key `K` type
+ * @param value `V` type
+ * @param replaceIfExists `Boolean` type; If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ * @param vertex `AVLVertex` type; current vertex in the recursion
+ * @return root vertex of the tree after the operation
+ */
+ private fun putRec(
+ key: K,
+ value: V,
+ replaceIfExists: Boolean,
+ vertex: AVLVertex,
+ ): AVLVertex? {
+ fun putRecShort(vertex: AVLVertex): AVLVertex? {
+ return putRec(key, value, replaceIfExists, vertex)
+ }
+
+ val nextCallReturned: AVLVertex?
+ when (compareKeys(key, vertex.key)) {
+ -1 -> {
+ if (vertex.leftSon == null) {
+ vertex.leftSon = AVLVertex(key, value)
+ vertex.sonsHeightDiff++
+ size++
+ return vertex
+ }
+ nextCallReturned = putRecShort(vertex.leftSon as AVLVertex)
+ }
+
+ 0 -> {
+ if (replaceIfExists) {
+ vertex.key = key
+ vertex.value = value
+ }
+ return null
+ }
+
+ else -> {
+ if (vertex.rightSon == null) {
+ vertex.rightSon = AVLVertex(key, value)
+ vertex.sonsHeightDiff--
+ size++
+ return vertex
+ }
+ nextCallReturned = putRecShort(vertex.rightSon as AVLVertex)
+ }
+ }
+ if (nextCallReturned == null) return null
+
+ fun doBalanceChoreWhenLeftSubTreeChanged(): AVLVertex? {
+ if (nextCallReturned.sonsHeightDiff == 0) return null
+ if (++vertex.sonsHeightDiff == 2) return balance(vertex)
+ return vertex
+ }
+
+ fun doBalanceChoreWhenRightSubTreeChanged(): AVLVertex? {
+ if (nextCallReturned.sonsHeightDiff == 0) return null
+ if (--vertex.sonsHeightDiff == -2) return balance(vertex)
+ return vertex
+ }
+ when (nextCallReturned) {
+ vertex.leftSon -> return doBalanceChoreWhenLeftSubTreeChanged()
+ vertex.rightSon -> return doBalanceChoreWhenRightSubTreeChanged()
+ else -> {
+ if (compareKeys(nextCallReturned.key, vertex.key) == -1) {
+ vertex.leftSon = nextCallReturned
+ return doBalanceChoreWhenLeftSubTreeChanged()
+ }
+ vertex.rightSon = nextCallReturned
+ return doBalanceChoreWhenRightSubTreeChanged()
+ }
+ }
+ }
+
+ /**
+ * Removes the mapping for a key from this tree if it is present
+ *
+ * @param key `K` type
+ * @return the previous value associated with key, or `null` if there was no mapping for key
+ */
+ override fun remove(key: K): V? {
+ if (isNotEmpty()) {
+ val removeRecReturned = removeRec(key, root as AVLVertex)
+ when (removeRecReturned.first) {
+ RemovalStage.B -> {
+ if (removeRecReturned.component2() != root) {
+ root = removeRecReturned.component2()
+ }
+ }
+
+ RemovalStage.C -> root = null
+ else -> {}
+ }
+ return removeRecReturned.component3()
+ }
+ return null
+ }
+
+ /**
+ * An enumeration representing different stages of removal during the removal process.
+ */
+ private enum class RemovalStage {
+ /**
+ * Don't need tree changes anymore
+ */
+ A,
+
+ /**
+ * Probably need some tree changes, but not make the son of the vertex `null`
+ */
+ B,
+
+ /**
+ * Need to `null` due "Son" property of (if exists) the parent of removed vertex + b
+ */
+ C,
+ }
+
+ /**
+ * Recursively removes a key-value pair from the subtree rooted at the given vertex
+ *
+ * @param key `K` type
+ * @param vertex the root of the subtree to remove from
+ * @return Triple that consists of:
+ *
+ * 1) removal stage;
+ *
+ * 2) if RemovalStage == a : just a vertex (don't need it later);
+ *
+ * if RemovalStage == b : the root of the changed subtree;
+ *
+ * if RemovalStage == c : the removed vertex;
+ *
+ * 3) a value of the removed vertex (or `null` if key not exists). */
+ private fun removeRec(
+ key: K,
+ vertex: AVLVertex,
+ ): Triple, V?> {
+ /**
+ * Triple consists of:
+ *
+ * 1) removal stage;
+ *
+ * 2) if RemovalStage == a : just a vertex (don't need it later);
+ * if RemovalStage == b : the root of the changed subtree;
+ * if RemovalStage == c : the removed vertex;
+ *
+ * 3) a value of the removed vertex (or `null` if key not exists).
+ */
+ val nextCallReturned: Triple?, V?>
+ when (compareKeys(key, vertex.key)) {
+ -1 -> {
+ if (vertex.leftSon == null) return Triple(RemovalStage.A, vertex, null)
+ nextCallReturned = removeRec(key, vertex.leftSon as AVLVertex)
+ }
+
+ 1 -> {
+ if (vertex.rightSon == null) return Triple(RemovalStage.A, vertex, null)
+ nextCallReturned = removeRec(key, vertex.rightSon as AVLVertex)
+ }
+
+ else -> {
+ return when ((vertex.leftSon == null) to (vertex.rightSon == null)) {
+ true to true -> {
+ size--
+ Triple(RemovalStage.C, vertex, vertex.value)
+ }
+
+ true to false -> {
+ size--
+ Triple(RemovalStage.B, vertex.rightSon as AVLVertex, vertex.value)
+ }
+
+ false to true -> {
+ size--
+ Triple(RemovalStage.B, vertex.leftSon as AVLVertex, vertex.value)
+ }
+
+ else -> {
+ val valueOfVertex = vertex.value
+ Triple(
+ RemovalStage.B,
+ replaceSubtreeSRootByLargestInItsLeftSubtree(vertex),
+ valueOfVertex,
+ )
+ }
+ }
+ }
+ }
+
+ fun doBalanceChoreWhenLeftSubTreeChanged(): Triple, V?> {
+ if (nextCallReturned.component2().sonsHeightDiff in listOf(-1, 1)) {
+ return Triple(RemovalStage.A, vertex, nextCallReturned.component3())
+ }
+ if (--vertex.sonsHeightDiff == -2) {
+ return Triple(RemovalStage.B, balance(vertex), nextCallReturned.component3())
+ }
+ return Triple(RemovalStage.B, vertex, nextCallReturned.third)
+ }
+
+ fun doBalanceChoreWhenRightSubTreeChanged(): Triple, V?> {
+ if (nextCallReturned.component2().sonsHeightDiff in listOf(-1, 1)) {
+ return Triple(RemovalStage.A, vertex, nextCallReturned.component3())
+ }
+ if (++vertex.sonsHeightDiff == 2) {
+ return Triple(RemovalStage.B, balance(vertex), nextCallReturned.component3())
+ }
+ return Triple(RemovalStage.B, vertex, nextCallReturned.component3())
+ }
+ when (nextCallReturned.component1()) {
+ RemovalStage.A -> return nextCallReturned
+ RemovalStage.B ->
+ when (nextCallReturned.component2()) {
+ vertex.leftSon -> return doBalanceChoreWhenLeftSubTreeChanged()
+ vertex.rightSon -> return doBalanceChoreWhenRightSubTreeChanged()
+ else ->
+ when (compareKeys(nextCallReturned.component2().key, vertex.key)) {
+ -1 -> {
+ vertex.leftSon = nextCallReturned.component2()
+ return doBalanceChoreWhenLeftSubTreeChanged()
+ }
+
+ else -> {
+ vertex.rightSon = nextCallReturned.component2()
+ return doBalanceChoreWhenRightSubTreeChanged()
+ }
+ }
+ }
+
+ RemovalStage.C ->
+ when (compareKeys(nextCallReturned.component2().key, vertex.key)) {
+ -1 -> {
+ vertex.leftSon = null
+ return doBalanceChoreWhenLeftSubTreeChanged()
+ }
+
+ else -> {
+ vertex.rightSon = null
+ return doBalanceChoreWhenRightSubTreeChanged()
+ }
+ }
+ }
+ }
+
+ /**
+ * Replaces the initially subtree's root by the its left subtree's vertex with largest key,
+ * having previously removed that vertex
+ *
+ * @param subtreeSInitiallyRoot `AVLVertex` type; initially root of the subtree
+ * @return vertex that is the subtree's root after function was executed
+ */
+ private fun replaceSubtreeSRootByLargestInItsLeftSubtree(subtreeSInitiallyRoot: AVLVertex): AVLVertex {
+ val substitute = getMaxKeyNodeRec(subtreeSInitiallyRoot.leftSon) as AVLVertex
+ val removeRecReturned = removeRec(substitute.key, subtreeSInitiallyRoot)
+ subtreeSInitiallyRoot.key = substitute.key
+ subtreeSInitiallyRoot.value = substitute.value
+ return if (removeRecReturned.component1() == RemovalStage.A) {
+ subtreeSInitiallyRoot
+ } else {
+ removeRecReturned.component2()
+ }
+ }
+
+ /**
+ * Balances the subtree
+ *
+ * @param curVertex `AVLVertex` type; the root vertex of subtree to be balanced
+ * @return root vertex of the subtree after balancing
+ */
+ private fun balance(curVertex: AVLVertex): AVLVertex {
+ var (rightSon, leftSon) = List?>(2) { null }
+
+ fun setSonSHeightDiffsOfTwoVertices(values: Pair) {
+ curVertex.sonsHeightDiff = values.component1()
+ if (rightSon != null) {
+ (rightSon as AVLVertex).sonsHeightDiff = values.component2()
+ return
+ }
+ (leftSon as AVLVertex).sonsHeightDiff = values.component2()
+ }
+
+ if (curVertex.sonsHeightDiff == -2) {
+ rightSon = curVertex.rightSon as AVLVertex
+ return if (rightSon.sonsHeightDiff == 1) {
+ val rightSonSLeftSon = rightSon.leftSon as AVLVertex
+ val subtreeRoot = bigRotateLeft(curVertex, rightSon)
+ setSonSHeightDiffsOfTwoVertices(
+ when (rightSonSLeftSon.sonsHeightDiff) {
+ 1 -> 0 to -1
+ -1 -> 1 to 0
+ else -> 0 to 0
+ },
+ )
+ rightSonSLeftSon.sonsHeightDiff = 0
+ subtreeRoot
+ } else {
+ val subtreeRoot = rotateLeft(curVertex, rightSon)
+ setSonSHeightDiffsOfTwoVertices(
+ if (rightSon.sonsHeightDiff == 0) {
+ -1 to 1
+ } else {
+ 0 to 0
+ },
+ )
+ subtreeRoot
+ }
+ }
+ leftSon = curVertex.leftSon as AVLVertex
+ return if (leftSon.sonsHeightDiff == -1) {
+ val leftSonSRightSon = leftSon.rightSon as AVLVertex
+ val subtreeRoot = bigRotateRight(curVertex, leftSon)
+ setSonSHeightDiffsOfTwoVertices(
+ when (leftSonSRightSon.sonsHeightDiff) {
+ -1 -> 0 to 1
+ 1 -> -1 to 0
+ else -> 0 to 0
+ },
+ )
+ leftSonSRightSon.sonsHeightDiff = 0
+ subtreeRoot
+ } else {
+ val subtreeRoot = rotateRight(curVertex, leftSon)
+ setSonSHeightDiffsOfTwoVertices(
+ if (leftSon.sonsHeightDiff == 0) {
+ 1 to -1
+ } else {
+ 0 to 0
+ },
+ )
+ subtreeRoot
+ }
+ }
+
+ /**
+ * Performs a single left rotation of the subtree
+ *
+ * @param curVertex `AVLVertex` type; the current vertex to rotate around (the subtree's root)
+ * @param rightSon `AVLVertex` type; the right son of the subtree's root
+ * @return the new root of the subtree after rotation
+ */
+ private fun rotateLeft(
+ curVertex: AVLVertex,
+ rightSon: AVLVertex,
+ ): AVLVertex {
+ curVertex.rightSon = rightSon.leftSon
+ rightSon.leftSon = curVertex
+ return rightSon
+ }
+
+ /**
+ * Performs a single right rotation of the subtree
+ *
+ * @param curVertex `AVLVertex` type; the current vertex to rotate around (the subtree's root)
+ * @param leftSon `AVLVertex` type; the left son of the subtree's root
+ * @return the new root of the subtree after rotation
+ */
+ private fun rotateRight(
+ curVertex: AVLVertex,
+ leftSon: AVLVertex,
+ ): AVLVertex {
+ curVertex.leftSon = leftSon.rightSon
+ leftSon.rightSon = curVertex
+ return leftSon
+ }
+
+ /**
+ * Performs a big left rotation of the subtree
+ *
+ * @param curVertex `AVLVertex` type; the current vertex to rotate around (the subtree's root)
+ * @param rightSon `AVLVertex` type; the right son of the subtree's root
+ * @return the new root of the subtree after rotation
+ */
+ private fun bigRotateLeft(
+ curVertex: AVLVertex,
+ rightSon: AVLVertex,
+ ): AVLVertex {
+ val curRightSon = rotateRight(rightSon, rightSon.leftSon as AVLVertex)
+ curVertex.rightSon = curRightSon
+ return rotateLeft(curVertex, curRightSon)
+ }
+
+ /**
+ * Performs a big right rotation of the subtree
+ *
+ * @param curVertex `AVLVertex` type; the current vertex to rotate around (the subtree's root)
+ * @param leftSon `AVLVertex` type; the left son vertex of the subtree's root
+ * @return the new root of the subtree after rotation
+ */
+ private fun bigRotateRight(
+ curVertex: AVLVertex,
+ leftSon: AVLVertex,
+ ): AVLVertex {
+ val curLeftSon = rotateLeft(leftSon, leftSon.rightSon as AVLVertex)
+ curVertex.leftSon = curLeftSon
+ return rotateRight(curVertex, curLeftSon)
+ }
+
+ /**
+ * Constructs a new binary search tree with the specified comparator
+ *
+ * @param comparator `Comparator?` type, `null `by default; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ */
+ constructor (comparator: Comparator? = null) : super(comparator)
+
+ /**
+ * Constructs a new binary search tree and puts all key-value pairs from the specified map to this tree
+ *
+ * @param map `Map` type
+ * @param replaceIfExists `Boolean` type.
+ * If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ * Supplied only if a [comparator] is present. If comparator is `null`, the value is replaced
+ * by the last value from the key-value pair in the map, where the key is the one already existing in the tree.
+ * @param comparator `Comparator?` type, `null `by default; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ */
+ constructor(map: Map, replaceIfExists: Boolean = true, comparator: Comparator? = null) : super(
+ map,
+ replaceIfExists,
+ comparator,
+ )
+}
diff --git a/lib/src/main/kotlin/trees/AbstractBinarySearchTree.kt b/lib/src/main/kotlin/trees/AbstractBinarySearchTree.kt
new file mode 100644
index 0000000..c16753c
--- /dev/null
+++ b/lib/src/main/kotlin/trees/AbstractBinarySearchTree.kt
@@ -0,0 +1,281 @@
+package trees
+
+import iterator.TreeIterator
+import vertexes.InterfaceBSTVertex
+
+/**
+ * An abstract class representing a binary search tree.
+ *
+ * If the tree has an incomparable key type and comparator is `null`, if the tree
+ * is non-empty, when trying to call the search, insert and delete methods, the tree
+ * will remain unchanged, the operation throws an exception with the message "Key's
+ * type is incomparable and comparator was not given".
+ *
+ * @param K key type
+ * @param V value type
+ * @param N vertex type implementing the [InterfaceBSTVertex] interface
+ * @property comparator `Comparator?` type; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ * @property size `Long` type; number of key-value pairs in this tree
+ * @property root `N?` type, `null` by default
+ */
+abstract class AbstractBinarySearchTree> {
+ protected var comparator: Comparator? = null
+ protected var size: Long = 0L
+ protected var root: N? = null
+
+ /**
+ * Returns an iterator over the elements of this tree in a certain sequence
+ */
+ operator fun iterator(): Iterator> {
+ return TreeIterator(root)
+ }
+
+ /**
+ * Returns the number of key-value pairs in this tree
+ */
+ fun size(): Long {
+ return size
+ }
+
+ /**
+ * Returns `true` if this tree contains no key-value pairs, and `false` otherwise
+ */
+ fun isEmpty(): Boolean {
+ return size == 0L
+ }
+
+ /**
+ * Returns `true` if this tree contains at least one key-value pair, and `false` otherwise
+ */
+ fun isNotEmpty(): Boolean {
+ return size != 0L
+ }
+
+ /**
+ * Returns the value associated with the specified key in this tree
+ *
+ * @param key `K` type
+ * @return If the key exists - corresponding value
+ * If the key does not exist - `null`
+ */
+ fun get(key: K): V? {
+ return getRec(key)
+ }
+
+ /**
+ * Returns a pair containing the specified key-value mapping
+ *
+ * @param key `K` type
+ * @return If the key exists - pair. If the key does not exist - `null`.
+ */
+ fun getPair(key: K): Pair? {
+ val value = get(key)
+ return if (value == null) null else Pair(key, value)
+ }
+
+ /**
+ * Returns the minimum key in the tree
+ *
+ * @return If the tree is not empty - minimum key, and `null` otherwise
+ */
+ fun getMin(): V? {
+ val minKeyNode = getMinKeyNodeRec()
+ return if (minKeyNode == null) null else minKeyNode.value
+ }
+
+ /**
+ * Returns the maximum key in the tree
+ *
+ * @return If the tree is not empty - maximum key, and `null` otherwise
+ */
+ fun getMax(): V? {
+ val maxKeyNode = getMaxKeyNodeRec()
+ return if (maxKeyNode == null) null else maxKeyNode.value
+ }
+
+ /**
+ * Returns key-value pair with the minimum key in the tree
+ *
+ * @return If the tree is not empty - pair with minimum key, and `null` otherwise
+ */
+ fun getMinKeyPair(): Pair? {
+ val minKeyNode = getMinKeyNodeRec()
+ return if (minKeyNode == null) null else Pair(minKeyNode.key, minKeyNode.value)
+ }
+
+ /**
+ * Returns key-value pair with the maximum key in the tree
+ *
+ * @return If the tree is not empty - pair with maximum key, and `null` otherwise
+ */
+ fun getMaxKeyPair(): Pair? {
+ val maxKeyNode = getMaxKeyNodeRec()
+ return if (maxKeyNode == null) null else Pair(maxKeyNode.key, maxKeyNode.value)
+ }
+
+ /**
+ * Puts a new vertex of a certain type into the tree with a given key and value
+ *
+ * @param key `K` type
+ * @param value `V` type, associated with this key
+ * @param replaceIfExists `Boolean` type,
+ *
+ * If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ */
+ abstract fun put(
+ key: K,
+ value: V,
+ replaceIfExists: Boolean = true,
+ )
+
+ /**
+ * Puts all key-value pairs from the specified map to this tree
+ *
+ * @param map `Map` type
+ * @param replaceIfExists `Boolean` type.
+ * If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ * Supplied only if a [comparator] is present. If [comparator] is `null`, the value is replaced
+ * by the last value from the key-value pair in the map, where the key is the one already existing in the tree.
+ */
+ fun putAll(
+ map: Map,
+ replaceIfExists: Boolean = true,
+ ) {
+ for (pair in map) put(pair.key, pair.value, replaceIfExists)
+ }
+
+ /**
+ * Deletes a vertex by the entered key and returns the value stored in it
+ *
+ * @param key `K` type
+ * @return If a vertex with this key exists in the tree - the value stored in this vertex, otherwise `null`
+ */
+ abstract fun remove(key: K): V?
+
+ /**
+ * Deletes a vertex by the entered key and returns it as a key-value pair
+ *
+ * @param key `K` type
+ * @return If a vertex with such a key exists in the tree - the key-value pair corresponding to this vertex, otherwise `null`
+ */
+ fun removeAndReturnPair(key: K): Pair? {
+ val value = remove(key)
+ return if (value == null) null else Pair(key, value)
+ }
+
+ /**
+ * Recursively searches for the value associated with the entered key
+ *
+ * @param key `K` type
+ * @param vertex `N?` type, `root` by default; current vertex being examined
+ * @return If a vertex with this key exists in the tree - the value stored in this vertex, otherwise `null`
+ */
+ private fun getRec(
+ key: K,
+ vertex: N? = root,
+ ): V? {
+ if (vertex == null) return null
+ return when (compareKeys(key, vertex.key)) {
+ 0 -> vertex.value
+ -1 -> getRec(key, vertex.leftSon)
+ else -> getRec(key, vertex.rightSon)
+ }
+ }
+
+ /**
+ * Recursively searches for the value associated with the minimum key in the tree
+ *
+ * @param vertex `N?` type, `root` by default; current vertex being examined
+ * @return If the tree is not empty - the vertex with the minimum key in the tree, otherwise `null`
+ */
+ protected fun getMinKeyNodeRec(vertex: N? = root): N? {
+ if (vertex == null) {
+ return null
+ } else {
+ return if (vertex.leftSon == null) {
+ vertex
+ } else {
+ getMinKeyNodeRec(vertex.leftSon)
+ }
+ }
+ }
+
+ /**
+ * Recursively searches for the value associated with the maximum key in the tree
+ *
+ * @param vertex `N?` type, `root` by default; current vertex being examined
+ * @return If the tree is not empty - the vertex with the maximum key in the tree, otherwise `null`
+ */
+ protected fun getMaxKeyNodeRec(vertex: N? = root): N? {
+ if (vertex == null) {
+ return null
+ } else {
+ return if (vertex.rightSon == null) {
+ vertex
+ } else {
+ getMaxKeyNodeRec(vertex.rightSon)
+ }
+ }
+ }
+
+ /**
+ * Compares two keys
+ *
+ * Comparing with a comparator if it is not `null`, or without one if the comparator is null and
+ * the keys are of comparable type
+ *
+ * @param firstKey `K` type
+ * @param secondKey `K` type
+ * @return
+ * -1 if the first key is less than the second key;
+ * 0 if they are equal;
+ * 1 if the first key is greater than the second key.
+ */
+ protected fun compareKeys(
+ firstKey: K,
+ secondKey: K,
+ ): Int {
+ val cpr = comparator
+ return if (cpr != null) {
+ when (cpr.compare(firstKey, secondKey)) {
+ in Int.MIN_VALUE..-1 -> -1
+ 0 -> 0
+ else -> 1
+ }
+ } else {
+ val comparableKey =
+ firstKey as? Comparable
+ ?: throw Exception("Key's type is incomparable and comparator wasn't given")
+ when (comparableKey.compareTo(secondKey)) {
+ in Int.MIN_VALUE..-1 -> -1
+ 0 -> 0
+ else -> 1
+ }
+ }
+ }
+
+ /**
+ * Constructs a new binary search tree with the specified comparator
+ *
+ * @param comparator `Comparator?` type, `null `by default; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ */
+ constructor(comparator: Comparator? = null) {
+ this.comparator = comparator
+ }
+
+ /**
+ * Constructs a new binary search tree and puts all key-value pairs from the specified map to this tree
+ *
+ * @param map `Map` type
+ * @param replaceIfExists `Boolean` type.
+ * If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ * Supplied only if a [comparator] is present. If comparator is `null`, the value is replaced
+ * by the last value from the key-value pair in the map, where the key is the one already existing in the tree.
+ *
+ * @param comparator `Comparator?` type, `null `by default; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ */
+ constructor(map: Map, replaceIfExists: Boolean = true, comparator: Comparator? = null) {
+ this.comparator = comparator
+ putAll(map, replaceIfExists)
+ }
+}
diff --git a/lib/src/main/kotlin/trees/RBSearchTree.kt b/lib/src/main/kotlin/trees/RBSearchTree.kt
new file mode 100644
index 0000000..96e16d1
--- /dev/null
+++ b/lib/src/main/kotlin/trees/RBSearchTree.kt
@@ -0,0 +1,426 @@
+package trees
+
+import vertexes.RBVertex
+
+/**
+ * Red-Black Tree implementation.
+ *
+ * It extends [AbstractBinarySearchTree] and uses [RBVertex] as vertices.
+ * Red-Black Tree is a balanced binary search tree, where each vertex is colored either red or black.
+ * This implementation ensures the following properties:
+ *
+ * - Every vertex is either red or black.
+ * - The root is black.
+ * - Every leaf is black.
+ * - If a vertex is red, then both its children are black.
+ * - Every simple path from a vertex to a descendant leaf has the same number of black vertexes.
+ *
+ * If the tree has an incomparable key type and comparator is `null`, if the tree
+ * is non-empty, when trying to call the search, insert and delete methods, the tree
+ * will remain unchanged, the operation throws an exception with the message "Key's
+ * type is incomparable and comparator was not given".
+ *
+ * @param K key type
+ * @param V value type
+ * @property comparator `Comparator?` type; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ * @property size `Long` type; number of key-value pairs in this tree
+ * @property root `RBVertex?` type, `null` by default
+ */
+class RBSearchTree : AbstractBinarySearchTree> {
+ private val red = RBVertex.Color.RED
+ private val black = RBVertex.Color.BLACK
+
+ /**
+ * This method removes the vertex with the given key from the tree and returns its associated value,
+ * maintaining the properties of the red-black tree.
+ *
+ * 4 cases we need to look at:
+ *
+ * 1) remove red vertex with 0 children -> just remove vertex
+ *
+ * 2) remove red or black vertex with 2 children ->
+ * find min vertex on the right subtree and swap it's key and value with
+ * key and value of vertex that we need to remove.
+ * Now we can work with vertex which has 1 or 0 children
+ *
+ * 3) remove black vertex with 1 child -> child can be only red,
+ * so we just swap child's key and value with key and value that we need to remove
+ * and look at case 1)
+ *
+ * 4) remove black vertex with 0 children -> just remove vertex
+ *
+ * @param key `K` type
+ * @return value associated with the removed vertex, or `null` if the key is not found
+ */
+ override fun remove(key: K): V? {
+ val vertex: RBVertex = getVertex(key) ?: return null
+ --size
+ val value = vertex.value
+
+ if (vertex == root && size == 0L) {
+ root = null
+ } else if (needToBalance(vertex)) {
+ balanceAfterRemove(vertex)
+ }
+
+ return value
+ }
+
+ /**
+ * Determines whether balancing is required after removing a vertex from the Red-Black Search Tree.
+ *
+ * @param vertex `RBVertex` type; The vertex to be checked for balancing.
+ * @return true if further balancing is required, false otherwise.
+ */
+ private fun needToBalance(vertex: RBVertex): Boolean {
+ when (countChildren(vertex)) {
+ 0 -> {
+ if (vertex.color == red) {
+ replaceVertexBy(vertex, null)
+ return false
+ }
+ return true
+ }
+
+ 1 -> {
+ replaceVertexBy(vertex, getChild(vertex))
+ return false
+ }
+
+ 2 -> {
+ val vertexForSwap = getMinKeyNodeRec(vertex.rightSon)
+ vertexForSwap?.let {
+ val key = vertex.key
+ vertex.key = it.key
+ it.key = key
+
+ val value = vertex.value
+ vertex.value = it.value
+ it.value = value
+
+ needToBalance(vertexForSwap)
+ }
+ }
+ }
+ return false
+ }
+
+ /**
+ * We need to balance tree after removal black vertex with 0 children.
+ *
+ * In this fun we need to look at vertex's parent and brother:
+ * 1) brother is black and brother's rightSon is red -> we paint
+ * brother in parent's color, parent and brother's rightSon in black
+ * then rotate left
+ *
+ * 2) brother is black and brother's leftSon is red (rightSon - black) ->
+ * we swap colors of brother and brother's leftSon and rotate right
+ * then look at case 1
+ *
+ * 3) brother is black and both sons are black -> we make brother red
+ * then we need to launch algorithm from the parent because of it
+ * can be red, so we have red parent and red son or black so
+ * the black height of all subtree decreased
+ *
+ * 4) brother is red -> make brother black, parent red and
+ * rotate left. We move conflict on level below, then we look at the previous cases
+ *
+ * @param vertex `RBVertex` type; The child vertex of the removed vertex or `null` if the removed vertex had no children.
+ */
+ private fun balanceAfterRemove(vertex: RBVertex?) {
+ var currentVertex = vertex
+ while (currentVertex != root && (currentVertex?.color == black || currentVertex == null)) {
+ val isBrotherRightSon = (currentVertex == currentVertex?.parent?.leftSon)
+ var brother: RBVertex? = if (isBrotherRightSon) currentVertex?.parent?.rightSon else currentVertex?.parent?.leftSon
+
+ if (brother?.color == red) {
+ brother.color = black
+ currentVertex?.parent?.color = red
+ val vertexForRotate = currentVertex?.parent
+
+ when (isBrotherRightSon) {
+ true -> {
+ vertexForRotate?.let { rotateLeft(vertexForRotate) }
+ brother = currentVertex?.parent?.rightSon
+ } else -> {
+ vertexForRotate?.let { rotateRight(vertexForRotate) }
+ brother = currentVertex?.parent?.leftSon
+ }
+ }
+ }
+
+ if ((brother?.leftSon?.color == black || brother?.leftSon == null) &&
+ (brother?.rightSon?.color == black || brother?.rightSon == null)
+ ) {
+ brother?.color = red
+ currentVertex = currentVertex?.parent
+
+ when (vertex) {
+ currentVertex?.leftSon -> currentVertex?.leftSon = null
+ currentVertex?.rightSon -> currentVertex?.rightSon = null
+ }
+ } else {
+ if ((isBrotherRightSon) && (brother.rightSon?.color == black || brother.rightSon == null)) {
+ brother.leftSon?.color = black
+ brother.color = red
+ rotateRight(brother)
+ brother = currentVertex?.parent?.rightSon
+ } else if ((!isBrotherRightSon) && (brother.leftSon?.color == black || brother.leftSon == null)) {
+ brother.rightSon?.color = black
+ brother.color = red
+ rotateLeft(brother)
+ brother = currentVertex?.parent?.leftSon
+ }
+
+ val parentColor = currentVertex?.parent?.color
+ parentColor?.let { brother?.color = parentColor }
+ currentVertex?.parent?.color = black
+ val vertexForRotate = currentVertex?.parent
+
+ when (isBrotherRightSon) {
+ true -> {
+ brother?.rightSon?.color = black
+ vertexForRotate?.let { rotateLeft(vertexForRotate) }
+ if (currentVertex == vertex) currentVertex?.parent?.leftSon = null
+ } else -> {
+ brother?.leftSon?.color = black
+ vertexForRotate?.let { rotateRight(vertexForRotate) }
+ if (currentVertex == vertex) currentVertex?.parent?.rightSon = null
+ }
+ }
+ currentVertex = root
+ }
+ }
+ currentVertex?.color = black
+ }
+
+ /**
+ * Finds a vertex by corresponding key. If such vertex doesn't exist returns `null`
+ *
+ * @param key 'K` type
+ * @return vertex with the corresponding key, or `null` if such vertex doesn't exist
+ */
+ private fun getVertex(key: K): RBVertex? {
+ var currentVertex: RBVertex? = root
+
+ while (currentVertex?.key != key) {
+ if (currentVertex == null) return null
+ when (compareKeys(key, currentVertex.key)) {
+ -1 -> {
+ currentVertex = currentVertex.leftSon
+ }
+
+ 1 -> {
+ currentVertex = currentVertex.rightSon
+ }
+ }
+ }
+ return currentVertex
+ }
+
+ /**
+ * Finds free place and inserts newVertex, colors it in red.
+ *
+ * @param key 'K` type
+ * @param value `V` type
+ * @param replaceIfExists `Boolean` type; If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ */
+ override fun put(
+ key: K,
+ value: V,
+ replaceIfExists: Boolean,
+ ) {
+ var currentVertex: RBVertex? = root
+ var parent: RBVertex? = null
+ var isLeft: Boolean = false
+ ++size
+
+ while (currentVertex != null) {
+ when (compareKeys(key, currentVertex.key)) {
+ -1 -> {
+ parent = currentVertex
+ currentVertex = currentVertex.leftSon
+ isLeft = true
+ }
+
+ 0 -> {
+ if (replaceIfExists) currentVertex.value = value
+ --size
+ break
+ }
+
+ 1 -> {
+ parent = currentVertex
+ currentVertex = currentVertex.rightSon
+ isLeft = false
+ }
+ }
+ }
+
+ if (currentVertex == null) {
+ currentVertex = RBVertex(key, value, null, null, red, parent)
+ if (root == null) {
+ root = currentVertex
+ } else if (isLeft) {
+ parent?.let { parent.leftSon = currentVertex }
+ } else {
+ parent?.let { parent.rightSon = currentVertex }
+ }
+ }
+
+ balanceAfterPut(currentVertex)
+ }
+
+ /**
+ * Balances the tree after inserting a new vertex.
+ *
+ * We need to balance tree in two cases:
+ * 1) when newVertex is root, so our root is red
+ * 2) when parent of our newVertex is red(because newVertex is also red)
+ *
+ * in first case we just make the root black
+ * in second case we need to look at the newVertex's uncle
+ * if uncle is red, we make it black and newVertex's parent black and grandparent red
+ * launch algorithm to grandfather because now it's color changed to red
+ * if uncle is black we also make newVertex's parent black, grandparent red
+ * and rotate it right
+ *
+ * @param vertex `RBVertex` type; The newly inserted vertex.
+ */
+ private fun balanceAfterPut(vertex: RBVertex) {
+ var currentVertex = vertex
+
+ while (currentVertex.parent?.color == red) {
+ val grandparent = currentVertex.parent?.parent
+ val isUncleRightSon = (currentVertex.parent == grandparent?.leftSon)
+ val uncle = if (isUncleRightSon) grandparent?.rightSon else grandparent?.leftSon
+
+ if (uncle?.color == red) {
+ currentVertex.parent?.color = black
+ uncle.color = black
+ grandparent?.color = red
+ currentVertex = grandparent ?: currentVertex
+ } else {
+ if ((isUncleRightSon) && (currentVertex == currentVertex.parent?.rightSon)) {
+ currentVertex = currentVertex.parent ?: currentVertex
+ rotateLeft(currentVertex)
+ } else if ((!isUncleRightSon) && (currentVertex == currentVertex.parent?.leftSon)) {
+ currentVertex = currentVertex.parent ?: currentVertex
+ rotateRight(currentVertex)
+ }
+
+ currentVertex.parent?.color = black
+ currentVertex.parent?.parent?.color = red
+ val vertexForRotate = currentVertex.parent?.parent
+ vertexForRotate?.let { if (isUncleRightSon) rotateRight(vertexForRotate) else rotateLeft(vertexForRotate) }
+ }
+ }
+ root?.color = black
+ }
+
+ /**
+ * Counts the number of children of the given vertex
+ *
+ * @param vertex `RBVertex` type; The vertex whose children count is to be determined.
+ * @return The number of children of the given vertex.
+ */
+ private fun countChildren(vertex: RBVertex): Int {
+ var numOfChild = 0
+ if (vertex.leftSon != null) ++numOfChild
+ if (vertex.rightSon != null) ++numOfChild
+ return numOfChild
+ }
+
+ /**
+ * Retrieves the child vertex of the given vertex
+ *
+ * @param vertex `RBVertex` type; vertex The vertex whose child is to be retrieved.
+ * @return The child vertex of the given vertex.
+ */
+ private fun getChild(vertex: RBVertex) = if (vertex.leftSon != null) vertex.leftSon else vertex.rightSon
+
+ /**
+ * Replaces the old vertex with the new vertex in the tree structure
+ *
+ * @param oldVertex `RBVertex` type; The old vertex to be replaced.
+ * @param newVertex `RBVertex` type; The new vertex that replaces the old vertex.
+ */
+ private fun replaceVertexBy(
+ oldVertex: RBVertex,
+ newVertex: RBVertex?,
+ ) {
+ if (root == oldVertex) {
+ root = newVertex
+ } else if (oldVertex == oldVertex.parent?.leftSon) {
+ oldVertex.parent?.leftSon = newVertex
+ } else {
+ oldVertex.parent?.rightSon = newVertex
+ }
+ newVertex?.parent = oldVertex.parent
+ }
+
+ /**
+ * Performs a left rotation on the given vertex.
+ *
+ * Suppose that vertex has a rightSon. Swap parent and rightSon, rightSon's leftSon becomes parent's rightSon.
+ *
+ * @param vertex `RBVertex` type; The vertex on which the left rotation is to be performed.
+ */
+ private fun rotateLeft(vertex: RBVertex) {
+ val rightVertex: RBVertex? = vertex.rightSon
+ vertex.rightSon = rightVertex?.leftSon
+ rightVertex?.leftSon.let { rightVertex?.leftSon?.parent = vertex }
+ rightVertex?.parent = vertex.parent
+ when {
+ vertex.parent == null -> root = rightVertex
+ vertex == vertex.parent?.rightSon -> vertex.parent?.rightSon = rightVertex
+ else -> vertex.parent?.leftSon = rightVertex
+ }
+ vertex.parent = rightVertex
+ rightVertex?.leftSon = vertex
+ }
+
+ /**
+ * Performs a right rotation on the given vertex.
+ *
+ * Suppose that vertex has a leftSon. Swap parent and leftSon, leftSon's rightSon becomes parent's leftSon.
+ * @param vertex `RBVertex` type; The vertex on which the right rotation is to be performed.
+ */
+ private fun rotateRight(vertex: RBVertex) {
+ val leftVertex: RBVertex? = vertex.leftSon
+ vertex.leftSon = leftVertex?.rightSon
+ leftVertex?.rightSon.let { leftVertex?.rightSon?.parent = vertex }
+ leftVertex?.parent = vertex.parent
+ when {
+ vertex.parent == null -> root = leftVertex
+ vertex == vertex.parent?.leftSon -> vertex.parent?.leftSon = leftVertex
+ else -> vertex.parent?.rightSon = leftVertex
+ }
+ vertex.parent = leftVertex
+ leftVertex?.rightSon = vertex
+ }
+
+ /**
+ * Constructs a new binary search tree with the specified comparator
+ *
+ * @param comparator `Comparator?` type, `null `by default; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ */
+ constructor(comparator: Comparator? = null) {
+ this.comparator = comparator
+ }
+
+ /**
+ * Constructs a new binary search tree and puts all key-value pairs from the specified map to this tree
+ *
+ * @param map `Map` type
+ * @param replaceIfExists `Boolean` type.
+ * If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ * Supplied only if a [comparator] is present. If comparator is `null`, the value is replaced
+ * by the last value from the key-value pair in the map, where the key is the one already existing in the tree.
+ * @param comparator `Comparator?` type, `null `by default; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ */
+ constructor(map: Map, replaceIfExists: Boolean = true, comparator: Comparator? = null) {
+ this.comparator = comparator
+ putAll(map, replaceIfExists)
+ }
+}
diff --git a/lib/src/main/kotlin/trees/SimpleBinarySearchTree.kt b/lib/src/main/kotlin/trees/SimpleBinarySearchTree.kt
new file mode 100644
index 0000000..b821a80
--- /dev/null
+++ b/lib/src/main/kotlin/trees/SimpleBinarySearchTree.kt
@@ -0,0 +1,169 @@
+package trees
+
+import vertexes.SimpleBSTVertex
+
+/**
+ * Simple implementation of a binary search tree.
+ *
+ * It extends [AbstractBinarySearchTree] and uses [SimpleBSTVertex] as vertices.
+ *
+ * If the tree has an incomparable key type and comparator is `null`, if the tree
+ * is non-empty, when trying to call the search, insert and delete methods, the tree
+ * will remain unchanged, the operation throws an exception with the message "Key's
+ * type is incomparable and comparator was not given".
+ *
+ * @param K key type
+ * @param V value type
+ * @property comparator `Comparator?` type; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ * @property size `Long` type; number of key-value pairs in this tree
+ * @property root `SimpleBSTVertex?` type, `null` by default
+ */
+open class SimpleBinarySearchTree : AbstractBinarySearchTree> {
+ /**
+ * Puts a new vertex of a certain type into the tree with a given key and value
+ *
+ * @param key `K` type
+ * @param value `V` type
+ * @param replaceIfExists `Boolean` type,
+ *
+ * If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ */
+ override fun put(
+ key: K,
+ value: V,
+ replaceIfExists: Boolean,
+ ) {
+ putRec(key, value, replaceIfExists)
+ }
+
+ /**
+ * Recursively inserts a key-value pair into the tree.
+ *
+ * @param key `K` type
+ * @param value `V` type
+ * @param replaceIfExists `Boolean` type,
+ * If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ * @param vertex `SimpleBSTVertex?` type, `root` by default; The current vertex in the recursion.
+ */
+ private fun putRec(
+ key: K,
+ value: V,
+ replaceIfExists: Boolean,
+ vertex: SimpleBSTVertex? = root,
+ ) {
+ if (vertex == null) {
+ root = SimpleBSTVertex(key, value)
+ size++
+ return
+ }
+ when (compareKeys(key, vertex.key)) {
+ 0 -> if (replaceIfExists) vertex.value = value
+ -1 -> {
+ if (vertex.leftSon == null) {
+ vertex.leftSon = SimpleBSTVertex(key, value)
+ size++
+ } else {
+ putRec(key, value, replaceIfExists, vertex.leftSon)
+ }
+ }
+
+ 1 -> {
+ if (vertex.rightSon == null) {
+ vertex.rightSon = SimpleBSTVertex(key, value)
+ size++
+ } else {
+ putRec(key, value, replaceIfExists, vertex.rightSon)
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes the key-value pair associated with the specified key from the tree.
+ *
+ * @param key `K` type
+ * @return The value associated with the removed key, or null if the key is not found.
+ */
+ override fun remove(key: K): V? {
+ val (_, deletedValue, isRemoved) = removeRec(key)
+ if (isRemoved) size--
+ return deletedValue
+ }
+
+ /**
+ * Recursively removes the key-value pair associated with the specified key from the tree.
+ *
+ * This method traverses the tree recursively to find the node with the given key and removes it.
+ * If the key is found and the corresponding node has both left and right children,
+ * the method replaces the node's key and value with those of the minimum key node in its right subtree,
+ * and then removes the minimum key node from the right subtree.
+ * If the key is not found, it returns a pair containing the vertex and null value.
+ *
+ * @param key `K` type
+ * @param vertex `SimpleBSTVertex?` type, `root` by default; The current vertex being examined in the recursion.
+ * @return A pair containing the updated vertex and the value associated with the removed key, or null if the key is not found.
+ */
+ private fun removeRec(
+ key: K,
+ vertex: SimpleBSTVertex? = root,
+ ): Triple?, V?, Boolean> {
+ if (vertex == null) return Triple(null, null, false)
+
+ when (compareKeys(key, vertex.key)) {
+ -1 -> {
+ val (updatedLeftSon, deletedValue, isRemoved) = removeRec(key, vertex.leftSon)
+ vertex.leftSon = updatedLeftSon
+ return Triple(vertex, deletedValue, isRemoved)
+ }
+
+ 1 -> {
+ val (updatedRightSon, deletedValue, isRemoved) = removeRec(key, vertex.rightSon)
+ vertex.rightSon = updatedRightSon
+ return Triple(vertex, deletedValue, isRemoved)
+ }
+
+ else -> {
+ val deletedValue: V = vertex.value
+ if (vertex.leftSon == null || vertex.rightSon == null) {
+ if (vertex.leftSon == null) {
+ if (vertex == root) root = root?.rightSon
+ return Triple(vertex.rightSon, deletedValue, true)
+ }
+ if (vertex == root) root = root?.leftSon
+ return Triple(vertex.leftSon, deletedValue, true)
+ }
+ val minKeyRightSubtreeNode: SimpleBSTVertex? = getMinKeyNodeRec(vertex.rightSon)
+ minKeyRightSubtreeNode?.let {
+ vertex.key = it.key
+ vertex.value = it.value
+ val (updatedRightSon, _, _) = removeRec(it.key, vertex.rightSon)
+ vertex.rightSon = updatedRightSon
+ }
+ return Triple(vertex, deletedValue, true)
+ }
+ }
+ }
+
+ /**
+ * Constructs a new binary search tree with the specified comparator
+ *
+ * @param comparator `Comparator?` type, `null `by default; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ */
+ constructor(comparator: Comparator? = null) : super(comparator)
+
+ /**
+ * Constructs a new binary search tree and puts all key-value pairs from the specified map to this tree
+ *
+ * @param map `Map` type
+ * @param replaceIfExists `Boolean` type.
+ * If `true` - replaces the value if the key already exists. If `false` - ignores it.
+ * Supplied only if a [comparator] is present. If comparator is `null`, the value is replaced
+ * by the last value from the key-value pair in the map, where the key is the one already existing in the tree.
+ * @param comparator `Comparator?` type, `null `by default; used optionally to compare keys in a tree. If `null`, it is expected that keys of comparable type.
+ */
+ constructor(map: Map, replaceIfExists: Boolean = true, comparator: Comparator? = null) : super(
+ map,
+ replaceIfExists,
+ comparator,
+ )
+}
diff --git a/lib/src/main/kotlin/vertexes/AVLVertex.kt b/lib/src/main/kotlin/vertexes/AVLVertex.kt
new file mode 100644
index 0000000..05c298a
--- /dev/null
+++ b/lib/src/main/kotlin/vertexes/AVLVertex.kt
@@ -0,0 +1,47 @@
+package vertexes
+
+/**
+ * Represents a vertex in an AVL tree
+ *
+ * @param K key type
+ * @param V value type
+ * @property key
+ * @property value
+ * @property leftSon `AVLVertex?` type,
+ * @property rightSon `AVLVertex?` type
+ * @property sonsHeightDiff 'Int' type, difference in height between the left and right subtrees
+ */
+
+class AVLVertex(
+ override var key: K,
+ override var value: V,
+) : InterfaceBSTVertex> {
+ override var leftSon: AVLVertex? = null
+ override var rightSon: AVLVertex? = null
+
+ /**
+ * The difference in height between the left and right subtrees.
+ */
+ var sonsHeightDiff: Int = 0
+
+ /**
+ * Constructs vertex for AVL tree with the specified key and value
+ *
+ * @param key `K` type
+ * @param value `V` type
+ * @param leftSon `AVLVertex?` type
+ * @param rightSon `AVLVertex?` type
+ * @param sonsHeightDiff 'Int' type, 0 by default; difference in height between the left and right subtrees
+ */
+ constructor(
+ key: K,
+ value: V,
+ leftSon: AVLVertex?,
+ rightSon: AVLVertex?,
+ sonsHeightDiff: Int = 0,
+ ) : this(key, value) {
+ this.leftSon = leftSon
+ this.rightSon = rightSon
+ this.sonsHeightDiff = sonsHeightDiff
+ }
+}
diff --git a/lib/src/main/kotlin/vertexes/InterfaceBSTVertex.kt b/lib/src/main/kotlin/vertexes/InterfaceBSTVertex.kt
new file mode 100644
index 0000000..bce3946
--- /dev/null
+++ b/lib/src/main/kotlin/vertexes/InterfaceBSTVertex.kt
@@ -0,0 +1,19 @@
+package vertexes
+
+/**
+ * Represents a generic vertex in a binary search tree
+ *
+ * @param K key type
+ * @param V value type
+ * @param N child vertices type
+ * @property key
+ * @property value
+ * @property leftSon `N?` type
+ * @property rightSon `N?` type
+ */
+interface InterfaceBSTVertex {
+ var key: K
+ var value: V
+ var leftSon: N?
+ var rightSon: N?
+}
diff --git a/lib/src/main/kotlin/vertexes/RBVertex.kt b/lib/src/main/kotlin/vertexes/RBVertex.kt
new file mode 100644
index 0000000..af5602e
--- /dev/null
+++ b/lib/src/main/kotlin/vertexes/RBVertex.kt
@@ -0,0 +1,50 @@
+package vertexes
+
+/**
+ * Represents a vertex in a Red-Black Tree.
+ * @param K Type of keys.
+ * @param V Type of values.
+ * @property key The key associated with this vertex.
+ * @property value The value associated with this vertex.
+ * @property color The color of this vertex (red or black).
+ * @property parent The parent vertex of this vertex.
+ * @property leftSon The left child vertex of this vertex.
+ * @property rightSon The right child vertex of this vertex.
+ */
+class RBVertex(
+ override var key: K,
+ override var value: V,
+) : InterfaceBSTVertex> {
+ enum class Color {
+ RED,
+ BLACK,
+ }
+
+ var color: Color = Color.RED
+ var parent: RBVertex? = null
+ override var leftSon: RBVertex? = null
+ override var rightSon: RBVertex? = null
+
+ /**
+ * Creates a new RBVertex with the specified parameters.
+ * @param key The key associated with this vertex.
+ * @param value The value associated with this vertex.
+ * @param leftSon The left child vertex of this vertex.
+ * @param rightSon The right child vertex of this vertex.
+ * @param color The color of this vertex (red or black).
+ * @param parent The parent vertex of this vertex.
+ */
+ constructor(
+ key: K,
+ value: V,
+ leftSon: RBVertex?,
+ rightSon: RBVertex?,
+ color: Color,
+ parent: RBVertex?,
+ ) : this(key, value) {
+ this.leftSon = leftSon
+ this.rightSon = rightSon
+ this.parent = parent
+ this.color = color
+ }
+}
diff --git a/lib/src/main/kotlin/vertexes/SimpleBSTVertex.kt b/lib/src/main/kotlin/vertexes/SimpleBSTVertex.kt
new file mode 100644
index 0000000..2107434
--- /dev/null
+++ b/lib/src/main/kotlin/vertexes/SimpleBSTVertex.kt
@@ -0,0 +1,37 @@
+package vertexes
+
+/**
+ * Represents a simple vertex in a binary search tree
+ *
+ * @param K key type
+ * @param V value type
+ * @property key
+ * @property value
+ * @property leftSon `SimpleBSTVertex?` type, `null` by default
+ * @property rightSon `SimpleBSTVertex?` type, `null` by default
+ */
+class SimpleBSTVertex(
+ override var key: K,
+ override var value: V,
+) : InterfaceBSTVertex> {
+ override var leftSon: SimpleBSTVertex? = null
+ override var rightSon: SimpleBSTVertex? = null
+
+ /**
+ * Constructs a simple vertex with the specified key and value
+ *
+ * @param key `K` type
+ * @param value `V` type
+ * @param leftSon `SimpleBSTVertex?` type
+ * @param rightSon `SimpleBSTVertex?` type
+ */
+ constructor(
+ key: K,
+ value: V,
+ leftSon: SimpleBSTVertex?,
+ rightSon: SimpleBSTVertex?,
+ ) : this(key, value) {
+ this.leftSon = leftSon
+ this.rightSon = rightSon
+ }
+}
diff --git a/lib/src/test/kotlin/iterator/IteratorTests.kt b/lib/src/test/kotlin/iterator/IteratorTests.kt
new file mode 100644
index 0000000..508d05b
--- /dev/null
+++ b/lib/src/test/kotlin/iterator/IteratorTests.kt
@@ -0,0 +1,48 @@
+package iterator
+
+import org.junit.jupiter.api.Assertions.assertEquals
+import trees.AVLSearchTree
+import vertexes.SimpleBSTVertex
+import kotlin.test.Test
+
+class IteratorTests {
+ @Test
+ fun `add in stack`() {
+ val vertex = SimpleBSTVertex(1, "one")
+ val iterator = TestIterator(vertex)
+ assertEquals(1, iterator.getTreeStack().removeLast().key)
+ }
+
+ @Test
+ fun `hasNext if stack is not empty`() {
+ val vertex = SimpleBSTVertex(1, "one")
+ val iterator = TestIterator(vertex)
+ assertEquals(true, iterator.hasNext())
+ }
+
+ @Test
+ fun `check if method next() works correctly`() {
+ val vertex = SimpleBSTVertex(1, "one")
+ val iterator = TestIterator(vertex)
+ val deletedVertex = iterator.next()
+ assertEquals(Pair(1, "one"), deletedVertex)
+ }
+
+ @Test
+ fun `hasNext if stack is empty`() {
+ val vertex = SimpleBSTVertex(1, "one")
+ val iterator = TestIterator(vertex)
+ iterator.next()
+ assertEquals(false, iterator.hasNext())
+ }
+
+ @Test
+ fun `check if method iterator() works correctly`() {
+ val tree = AVLSearchTree(mapOf(Pair(3, "three"), Pair(1, "one"), Pair(2, "two"), Pair(150, "one-five-zero")))
+ val list: MutableList> = mutableListOf()
+ for (pair in tree) {
+ list.add(pair)
+ }
+ assertEquals(mutableListOf(Pair(2, "two"), Pair(3, "three"), Pair(150, "one-five-zero"), Pair(1, "one")), list)
+ }
+}
diff --git a/lib/src/test/kotlin/iterator/TestIterator.kt b/lib/src/test/kotlin/iterator/TestIterator.kt
new file mode 100644
index 0000000..d42d08d
--- /dev/null
+++ b/lib/src/test/kotlin/iterator/TestIterator.kt
@@ -0,0 +1,10 @@
+package iterator
+
+import vertexes.InterfaceBSTVertex
+import java.util.Stack
+
+internal class TestIterator>(vertex: N?) : TreeIterator(vertex) {
+ fun getTreeStack(): Stack {
+ return stack
+ }
+}
diff --git a/lib/src/test/kotlin/trees/abstractTree/AbstractTreeTest.kt b/lib/src/test/kotlin/trees/abstractTree/AbstractTreeTest.kt
new file mode 100644
index 0000000..02e80d2
--- /dev/null
+++ b/lib/src/test/kotlin/trees/abstractTree/AbstractTreeTest.kt
@@ -0,0 +1,260 @@
+package trees.abstractTree
+
+import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertNotNull
+import org.junit.jupiter.api.Assertions.assertNull
+import org.junit.jupiter.api.Test
+import vertexes.SimpleBSTVertex
+
+class AbstractTreeTest {
+ private fun makeEmptyTree(): TestTree {
+ return TestTree()
+ }
+
+ @Test
+ fun `isEmpty() returns true if tree is empty `() {
+ val tree = makeEmptyTree()
+ assert(tree.isEmpty())
+ }
+
+ @Test
+ fun `isNotEmpty() returns false if tree is empty `() {
+ val tree = makeEmptyTree()
+ assertFalse(tree.isNotEmpty())
+ }
+
+ @Test
+ fun `size() returns 0 if tree is empty`() {
+ val tree = makeEmptyTree()
+ assert(tree.size() == 0L)
+ }
+
+ @Test
+ fun `get() returns null if tree is empty`() {
+ val tree = makeEmptyTree()
+ assertNull(tree.get(intArrayOf(0)))
+ }
+
+ @Test
+ fun `getMax() returns null if tree is empty`() {
+ val tree = makeEmptyTree()
+ assertNull(tree.getMax())
+ }
+
+ @Test
+ fun `getMin() returns null if tree is empty`() {
+ val tree = makeEmptyTree()
+ assertNull(tree.getMin())
+ }
+
+ @Test
+ fun `getMaxKeyPair() returns null if tree is empty`() {
+ val tree = makeEmptyTree()
+ assertNull(tree.getMaxKeyPair())
+ }
+
+ @Test
+ fun `getMinKeyPair() returns null if tree is empty`() {
+ val tree = makeEmptyTree()
+ assertNull(tree.getMinKeyPair())
+ }
+
+ private fun makeTreeWithBothRootSSons(): TestTree {
+ val leftSon = SimpleBSTVertex('1', "!", SimpleBSTVertex('0', ")"), null)
+ val rightSon = SimpleBSTVertex('4', "$", SimpleBSTVertex('3', "#"), null)
+ return TestTree(SimpleBSTVertex('2', "@", leftSon, rightSon), 5L)
+ }
+
+ @Test
+ fun `isEmpty() returns false if tree is not empty `() {
+ val tree = makeTreeWithBothRootSSons()
+ assertFalse(tree.isEmpty())
+ }
+
+ @Test
+ fun `isNotEmpty() returns true if tree is not empty `() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.isNotEmpty())
+ }
+
+ @Test
+ fun `size() returns not 0 if tree is not empty`() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.size() != 0L)
+ }
+
+ @Test
+ fun `get() returns null when tree doesn't contains given key`() {
+ val tree = makeTreeWithBothRootSSons()
+ assertNull(tree.get('z'))
+ }
+
+ @Test
+ fun `getPair() returns null when tree doesn't contains given key`() {
+ val tree = makeTreeWithBothRootSSons()
+ assertNull(tree.getPair('z'))
+ }
+
+ @Test
+ fun `get() returns correct value when tree contains given key in left subtree`() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.get('1') == "!")
+ }
+
+ @Test
+ fun `getPair() returns correct value when tree contains given key in left subtree`() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.getPair('1') == ('1' to "!"))
+ }
+
+ @Test
+ fun `get() returns correct value when tree contains given key in right subtree`() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.get('4') == "$")
+ }
+
+ @Test
+ fun `get() returns correct value when root's key was given`() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.get('2') == "@")
+ }
+
+ @Test
+ fun `get() returns correct value when leaf's key was given`() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.get('3') == "#")
+ }
+
+ @Test
+ fun `getMin() returns correct value when root has two sons`() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.getMin() == ")")
+ }
+
+ @Test
+ fun `getMax() returns correct value when root has two sons`() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.getMax() == "$")
+ }
+
+ @Test
+ fun `getMinKeyPair() returns correct value when root has two sons`() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.getMinKeyPair() == ('0' to ")"))
+ }
+
+ @Test
+ fun `getMaxKeyPair() returns correct value when root has two sons`() {
+ val tree = makeTreeWithBothRootSSons()
+ assert(tree.getMaxKeyPair() == ('4' to "$"))
+ }
+
+ private fun makeTreeWithOnlyLeftRootSSon(): TestTree {
+ val leftSon = SimpleBSTVertex('1', "!", SimpleBSTVertex('0', ")"), SimpleBSTVertex('2', "@"))
+ return TestTree(SimpleBSTVertex('3', "#", leftSon, null), 4L)
+ }
+
+ @Test
+ fun `getMax() returns correct value when root has only left son`() {
+ val tree = makeTreeWithOnlyLeftRootSSon()
+ assert(tree.getMax() == "#")
+ }
+
+ private fun makeTreeWithOnlyRightRootSSon(): TestTree {
+ val rightSon = SimpleBSTVertex('6', "^", SimpleBSTVertex('5', "%"), SimpleBSTVertex('8', "*"))
+ return TestTree(SimpleBSTVertex('3', "#", null, rightSon), 4L)
+ }
+
+ @Test
+ fun `getMin() returns correct value when root has only right son`() {
+ val tree = makeTreeWithOnlyRightRootSSon()
+ assert(tree.getMin() == "#")
+ }
+
+ @Test
+ fun `removeAndReturnPair() returns null when remove() returned null`() {
+ val tree = TestTree(removeShouldReturns = null)
+ assertNull(tree.removeAndReturnPair(1))
+ }
+
+ @Test
+ fun `removeAndReturnPair() returns (given key) to (value that remove() returned) pair`() {
+ val tree = TestTree(removeShouldReturns = '1')
+ assert(tree.removeAndReturnPair(3) == (3 to '1'))
+ }
+
+ @Test
+ fun `putAll() do correct put() call for each map element (1)`() {
+ val tree = TestTree()
+ val map = hashMapOf('a' to 'A', 'b' to 'B', 'c' to 'C', 'a' to 'A')
+ tree.putAll(map)
+ for (pair in map)
+ assertNotNull(tree.putWasCalledWithParams.remove(Triple(pair.component1(), pair.component2(), true)))
+ }
+
+ @Test
+ fun `putAll() do correct put() call for each map element (2)`() {
+ val tree = TestTree()
+ val map = hashMapOf('a' to 'A', 'b' to 'B', 'c' to 'C', 'a' to 'A')
+ tree.putAll(map, false)
+ for (pair in map)
+ assertNotNull(tree.putWasCalledWithParams.remove(Triple(pair.component1(), pair.component2(), false)))
+ }
+
+ @Test
+ fun `compareKeys return 1 when key1 larger than key2 (keys are comparable)`() {
+ val tree = TestTree()
+ assert(tree.compareKeysT(18, 14) == 1)
+ }
+
+ @Test
+ fun `compareKeys return 0 when key1 equals key2 (keys are comparable)`() {
+ val tree = TestTree()
+ assert(tree.compareKeysT(18, 18) == 0)
+ }
+
+ @Test
+ fun `compareKeys return -1 when key1 lesser than key2 (keys are comparable)`() {
+ val tree = TestTree()
+ assert(tree.compareKeysT(14, 18) == -1)
+ }
+
+ class CMP : Comparator {
+ override fun compare(
+ o1: IntArray,
+ o2: IntArray,
+ ): Int {
+ return o1.sum() - o2.sum()
+ }
+ }
+
+ @Test
+ fun `compareKeys return 1 when key1 larger than key2 (comparator was given)`() {
+ val tree = TestTree(CMP())
+ assert(tree.compareKeysT(intArrayOf(-18, 18), intArrayOf(-1)) == 1)
+ }
+
+ @Test
+ fun `compareKeys return 0 when key1 equals key2 (comparator was given)`() {
+ val tree = TestTree(CMP())
+ assert(tree.compareKeysT(intArrayOf(-18, 18), intArrayOf(0)) == 0)
+ }
+
+ @Test
+ fun `compareKeys return -1 when key1 lesser than key2 (comparator was given)`() {
+ val tree = TestTree(CMP())
+ assert(tree.compareKeysT(intArrayOf(-18, 18), intArrayOf(1)) == -1)
+ }
+
+ @Test
+ fun `compareKeys fall when keys type doesn't implement comparable && comparator wasn't given`() {
+ var didItFall = false
+ val tree = TestTree()
+ try {
+ tree.compareKeysT(intArrayOf(-18, 18), intArrayOf(1))
+ } catch (e: Exception) {
+ didItFall = true
+ }
+ assert(didItFall)
+ }
+}
diff --git a/lib/src/test/kotlin/trees/abstractTree/TestTree.kt b/lib/src/test/kotlin/trees/abstractTree/TestTree.kt
new file mode 100644
index 0000000..9974358
--- /dev/null
+++ b/lib/src/test/kotlin/trees/abstractTree/TestTree.kt
@@ -0,0 +1,47 @@
+package trees.abstractTree
+
+import trees.AbstractBinarySearchTree
+import vertexes.SimpleBSTVertex
+
+class TestTree : AbstractBinarySearchTree> {
+ var removeShouldReturns: V? = null
+ var getShouldReturns: V? = null
+ val putWasCalledWithParams: MutableList> = mutableListOf()
+
+ override fun put(
+ key: K,
+ value: V,
+ replaceIfExists: Boolean,
+ ) {
+ putWasCalledWithParams.add(Triple(key, value, replaceIfExists))
+ }
+
+ override fun remove(key: K): V? {
+ return removeShouldReturns
+ }
+
+ fun compareKeysT(
+ firstKey: K,
+ secondKey: K,
+ ): Int {
+ return super.compareKeys(firstKey, secondKey)
+ }
+
+ constructor (root: SimpleBSTVertex, size: Long, comparator: Comparator? = null) :
+ super(comparator) {
+ this.root = root
+ this.size = size
+ }
+
+ constructor (removeShouldReturns: V?) : super() {
+ this.removeShouldReturns = removeShouldReturns
+ }
+
+ constructor (comparator: Comparator? = null) : super(comparator)
+
+ constructor (
+ map: Map,
+ replaceIfExists: Boolean = true,
+ comparator: Comparator? = null,
+ ) : super(map, replaceIfExists, comparator)
+}
diff --git a/lib/src/test/kotlin/trees/avlTree/AVLTreeForTest.kt b/lib/src/test/kotlin/trees/avlTree/AVLTreeForTest.kt
new file mode 100644
index 0000000..7266073
--- /dev/null
+++ b/lib/src/test/kotlin/trees/avlTree/AVLTreeForTest.kt
@@ -0,0 +1,79 @@
+package trees.avlTree
+
+import trees.AVLSearchTree
+import vertexes.AVLVertex
+import kotlin.math.max
+
+class AVLTreeForTest : AVLSearchTree {
+ fun getRootT(): AVLVertex? {
+ return root
+ }
+
+ fun getVertexesInDFSOrder(): MutableList> {
+ val vertexes: MutableList> = mutableListOf()
+ getVertexesRec(root, vertexes)
+ return vertexes
+ }
+
+ private fun getVertexesRec(
+ curVertex: AVLVertex?,
+ vrtList: MutableList>,
+ ) {
+ if (curVertex == null) return
+ vrtList.add(curVertex)
+ getVertexesRec(curVertex.leftSon, vrtList)
+ getVertexesRec(curVertex.rightSon, vrtList)
+ }
+
+ fun getHeights(): MutableMap {
+ val heights: MutableMap = mutableMapOf()
+ if (root == null) return heights
+ getHeightsRec(root as AVLVertex, heights)
+ return heights
+ }
+
+ private fun getHeightsRec(
+ rootOfSubtree: AVLVertex,
+ heights: MutableMap,
+ ): Int {
+ return when ((rootOfSubtree.leftSon == null) to (rootOfSubtree.rightSon == null)) {
+ true to true -> {
+ heights.put(rootOfSubtree.key, 0)
+ 0
+ }
+
+ true to false -> {
+ val height = getHeightsRec(rootOfSubtree.rightSon as AVLVertex, heights) + 1
+ heights.put(rootOfSubtree.key, height)
+ height
+ }
+
+ false to true -> {
+ val height = getHeightsRec(rootOfSubtree.leftSon as AVLVertex, heights) + 1
+ heights.put(rootOfSubtree.key, height)
+ height
+ }
+
+ else -> {
+ val heightOfLeftSubtree = getHeightsRec(rootOfSubtree.leftSon as AVLVertex, heights)
+ val heightOfRightSubtree = getHeightsRec(rootOfSubtree.rightSon as AVLVertex, heights)
+ val height = max(heightOfLeftSubtree, heightOfRightSubtree) + 1
+ heights.put(rootOfSubtree.key, height)
+ max(heightOfLeftSubtree, heightOfRightSubtree) + 1
+ }
+ }
+ }
+
+ constructor (root: AVLVertex, size: Long, comparator: Comparator? = null) : super(comparator) {
+ this.root = root
+ this.size = size
+ }
+
+ constructor (comparator: Comparator? = null) : super(comparator)
+
+ constructor(
+ map: Map,
+ replaceIfExists: Boolean = true,
+ comparator: Comparator? = null,
+ ) : super(map, replaceIfExists, comparator)
+}
diff --git a/lib/src/test/kotlin/trees/avlTree/AuxiliaryFunctions.kt b/lib/src/test/kotlin/trees/avlTree/AuxiliaryFunctions.kt
new file mode 100644
index 0000000..3e932ea
--- /dev/null
+++ b/lib/src/test/kotlin/trees/avlTree/AuxiliaryFunctions.kt
@@ -0,0 +1,60 @@
+package trees.avlTree
+
+import vertexes.AVLVertex
+
+object AuxiliaryFunctions {
+ fun isTreeConsistsOf(
+ expectedContent: Set>,
+ tree: AVLTreeForTest,
+ ): Boolean {
+ val vertexes = tree.getVertexesInDFSOrder()
+ val pairsFromVertexes = (Array(vertexes.size) { i -> (vertexes[i].key to vertexes[i].value) }).toSet()
+ return pairsFromVertexes == expectedContent
+ }
+
+ fun isTreeSStructureThat(
+ tree: AVLTreeForTest,
+ order: Array,
+ deps: List>,
+ ): Boolean {
+ // Triple consists of indexes in order of (1)vertex, one's (2)leftSon and (3)RightSon
+ val vertexes = tree.getVertexesInDFSOrder()
+ if (vertexes.size != order.size) return false
+ for (i in order.indices)
+ if (order[i] != vertexes[i].key) return false
+ for (dep in deps) {
+ if (dep.component2() != null) {
+ if (vertexes[dep.component1()].leftSon != vertexes[dep.component2() as Int]) {
+ return false
+ }
+ }
+ if (dep.component3() != null) {
+ if (vertexes[dep.component1()].rightSon != vertexes[dep.component3() as Int]) {
+ return false
+ }
+ }
+ }
+ return true
+ }
+
+ fun isSonsHeightDiffCorrect(tree: AVLTreeForTest): Boolean {
+ val vertexes = tree.getVertexesInDFSOrder()
+ val heights = tree.getHeights()
+ if (vertexes.size != heights.size) return false
+ for (vertex in vertexes) {
+ val expectedSonsHeightDiff =
+ when ((vertex.leftSon == null) to (vertex.rightSon == null)) {
+ true to true -> 0
+ true to false -> -1 - (heights[(vertex.rightSon as AVLVertex).key] ?: return false)
+ false to true -> 1 + (heights[(vertex.leftSon as AVLVertex).key] ?: return false)
+ else -> {
+ val heightOfLeftSubtree = heights[(vertex.leftSon as AVLVertex).key] ?: return false
+ val heightOfRightSubtree = heights[(vertex.rightSon as AVLVertex).key] ?: return false
+ heightOfLeftSubtree - heightOfRightSubtree
+ }
+ }
+ if (expectedSonsHeightDiff != vertex.sonsHeightDiff) return false
+ }
+ return true
+ }
+}
diff --git a/lib/src/test/kotlin/trees/avlTree/putTest/HaveToDoBigRotation.kt b/lib/src/test/kotlin/trees/avlTree/putTest/HaveToDoBigRotation.kt
new file mode 100644
index 0000000..7beabd4
--- /dev/null
+++ b/lib/src/test/kotlin/trees/avlTree/putTest/HaveToDoBigRotation.kt
@@ -0,0 +1,222 @@
+package trees.avlTree.putTest
+
+import org.junit.jupiter.api.Test
+import trees.avlTree.AVLTreeForTest
+import trees.avlTree.AuxiliaryFunctions.isSonsHeightDiffCorrect
+import trees.avlTree.AuxiliaryFunctions.isTreeConsistsOf
+import trees.avlTree.AuxiliaryFunctions.isTreeSStructureThat
+import vertexes.AVLVertex
+
+class HaveToDoBigRotation {
+ private fun makeTreeForHaveToDoBigLeftRotationPutTest(): AVLTreeForTest {
+ val rightSonSRightSon = AVLVertex('i', 'I', AVLVertex('g', 'G'), AVLVertex('j', 'J'))
+ val rightSon = AVLVertex('e', 'E', AVLVertex('d', 'D'), rightSonSRightSon, -1)
+ val root = AVLVertex('c', 'C', AVLVertex('b', 'B', AVLVertex('a', 'A'), null, 1), rightSon, -1)
+ return AVLTreeForTest(root, 8L)
+ }
+
+ // (1) - add grandson's right son, (2) add grandson's left son
+ @Test
+ fun `content is correct after entry was added (big left rotation) (1)`() {
+ val tree = makeTreeForHaveToDoBigLeftRotationPutTest()
+ tree.put('f', 'F')
+ val expectedContent =
+ setOf(
+ 'a' to 'A',
+ 'b' to 'B',
+ 'c' to 'C',
+ 'd' to 'D',
+ 'e' to 'E',
+ 'g' to 'G',
+ 'f' to 'F',
+ 'i' to 'I',
+ 'j' to 'J',
+ )
+ assert(isTreeConsistsOf(expectedContent, tree))
+ }
+
+ @Test
+ fun `content is correct after entry was added (big left rotation) (2)`() {
+ val tree = makeTreeForHaveToDoBigLeftRotationPutTest()
+ tree.put('h', 'H')
+ val expectedContent =
+ setOf(
+ 'a' to 'A',
+ 'b' to 'B',
+ 'c' to 'C',
+ 'd' to 'D',
+ 'e' to 'E',
+ 'h' to 'H',
+ 'g' to 'G',
+ 'i' to 'I',
+ 'j' to 'J',
+ )
+ assert(isTreeConsistsOf(expectedContent, tree))
+ }
+
+ @Test
+ fun `size increased by 1 after entry was added (big left rotation) (1)`() {
+ val tree = makeTreeForHaveToDoBigLeftRotationPutTest()
+ tree.put('f', 'F')
+ assert(tree.size() == 9L)
+ }
+
+ @Test
+ fun `size increased by 1 after entry was added (big left rotation) (2)`() {
+ val tree = makeTreeForHaveToDoBigLeftRotationPutTest()
+ tree.put('h', 'H')
+ assert(tree.size() == 9L)
+ }
+
+ @Test
+ fun `structure is correct after entry was added (big left rotation) (1)`() {
+ val tree = makeTreeForHaveToDoBigLeftRotationPutTest()
+ tree.put('f', 'F')
+ val expectedDependencies =
+ listOf(
+ Triple(0, 1, 3),
+ Triple(1, 2, null),
+ Triple(3, 4, 7),
+ Triple(4, 5, 6),
+ Triple(7, null, 8),
+ )
+ val expectedOrder = arrayOf('c', 'b', 'a', 'g', 'e', 'd', 'f', 'i', 'j')
+ assert(isTreeSStructureThat(tree, expectedOrder, expectedDependencies))
+ }
+
+ @Test
+ fun `structure is correct after entry was added (big left rotation) (2)`() {
+ val tree = makeTreeForHaveToDoBigLeftRotationPutTest()
+ tree.put('h', 'H')
+ val expectedDependencies =
+ listOf(
+ Triple(0, 1, 3),
+ Triple(1, 2, null),
+ Triple(3, 4, 6),
+ Triple(4, 5, null),
+ Triple(6, 7, 8),
+ )
+ val expectedOrder = arrayOf('c', 'b', 'a', 'g', 'e', 'd', 'i', 'h', 'j')
+ assert(isTreeSStructureThat(tree, expectedOrder, expectedDependencies))
+ }
+
+ @Test
+ fun `sonsHeightDiff values are correct after entry was added (big left rotation) (1)`() {
+ val tree = makeTreeForHaveToDoBigLeftRotationPutTest()
+ tree.put('f', 'F')
+ assert(isSonsHeightDiffCorrect(tree))
+ }
+
+ @Test
+ fun `sonsHeightDiff values are correct after entry was added (big left rotation) (2)`() {
+ val tree = makeTreeForHaveToDoBigLeftRotationPutTest()
+ tree.put('h', 'H')
+ assert(isSonsHeightDiffCorrect(tree))
+ }
+
+ private fun makeTreeForHaveToDoBigRightRotationPutTest(): AVLTreeForTest {
+ val leftSonSLeftSon = AVLVertex('b', 'B', AVLVertex('a', 'A'), AVLVertex('d', 'D'))
+ val leftSon = AVLVertex('f', 'F', leftSonSLeftSon, AVLVertex('g', 'G'), 1)
+ val root = AVLVertex('h', 'H', leftSon, AVLVertex('i', 'I', null, AVLVertex('j', 'J'), -1), 1)
+ return AVLTreeForTest(root, 8L)
+ }
+
+ // (1) - add grandson's left son, (2) add grandson's right son
+ @Test
+ fun `content is correct after entry was added (big right rotation) (1)`() {
+ val tree = makeTreeForHaveToDoBigRightRotationPutTest()
+ tree.put('c', 'C')
+ val expectedContent =
+ setOf(
+ 'a' to 'A',
+ 'b' to 'B',
+ 'c' to 'C',
+ 'd' to 'D',
+ 'h' to 'H',
+ 'g' to 'G',
+ 'f' to 'F',
+ 'i' to 'I',
+ 'j' to 'J',
+ )
+ assert(isTreeConsistsOf(expectedContent, tree))
+ }
+
+ @Test
+ fun `content is correct after entry was added (big right rotation) (2)`() {
+ val tree = makeTreeForHaveToDoBigRightRotationPutTest()
+ tree.put('e', 'E')
+ val expectedContent =
+ setOf(
+ 'a' to 'A',
+ 'b' to 'B',
+ 'f' to 'F',
+ 'd' to 'D',
+ 'e' to 'E',
+ 'h' to 'H',
+ 'g' to 'G',
+ 'i' to 'I',
+ 'j' to 'J',
+ )
+ assert(isTreeConsistsOf(expectedContent, tree))
+ }
+
+ @Test
+ fun `size increased by 1 after entry was added (big right rotation) (1)`() {
+ val tree = makeTreeForHaveToDoBigRightRotationPutTest()
+ tree.put('c', 'C')
+ assert(tree.size() == 9L)
+ }
+
+ @Test
+ fun `size increased by 1 after entry was added (big right rotation) (2)`() {
+ val tree = makeTreeForHaveToDoBigRightRotationPutTest()
+ tree.put('e', 'E')
+ assert(tree.size() == 9L)
+ }
+
+ @Test
+ fun `structure is correct after entry was added (big right rotation) (1)`() {
+ val tree = makeTreeForHaveToDoBigRightRotationPutTest()
+ tree.put('c', 'C')
+ val expectedDependencies =
+ listOf(
+ Triple(0, 1, 7),
+ Triple(7, null, 8),
+ Triple(1, 2, 5),
+ Triple(5, null, 6),
+ Triple(2, 3, 4),
+ )
+ val expectedOrder = arrayOf('h', 'd', 'b', 'a', 'c', 'f', 'g', 'i', 'j')
+ assert(isTreeSStructureThat(tree, expectedOrder, expectedDependencies))
+ }
+
+ @Test
+ fun `structure is correct after entry was added (big right rotation) (2)`() {
+ val tree = makeTreeForHaveToDoBigRightRotationPutTest()
+ tree.put('e', 'E')
+ val expectedDependencies =
+ listOf(
+ Triple(0, 1, 7),
+ Triple(7, null, 8),
+ Triple(1, 2, 4),
+ Triple(2, 3, null),
+ Triple(4, 5, 6),
+ )
+ val expectedOrder = arrayOf('h', 'd', 'b', 'a', 'f', 'e', 'g', 'i', 'j')
+ assert(isTreeSStructureThat(tree, expectedOrder, expectedDependencies))
+ }
+
+ @Test
+ fun `sonsHeightDiff values are correct after entry was added (big right rotation) (1)`() {
+ val tree = makeTreeForHaveToDoBigRightRotationPutTest()
+ tree.put('c', 'C')
+ assert(isSonsHeightDiffCorrect(tree))
+ }
+
+ @Test
+ fun `sonsHeightDiff values are correct after entry was added (big right rotation) (2)`() {
+ val tree = makeTreeForHaveToDoBigLeftRotationPutTest()
+ tree.put('e', 'E')
+ assert(isSonsHeightDiffCorrect(tree))
+ }
+}
diff --git a/lib/src/test/kotlin/trees/avlTree/putTest/HaveToRotateLeft.kt b/lib/src/test/kotlin/trees/avlTree/putTest/HaveToRotateLeft.kt
new file mode 100644
index 0000000..afc2ac4
--- /dev/null
+++ b/lib/src/test/kotlin/trees/avlTree/putTest/HaveToRotateLeft.kt
@@ -0,0 +1,103 @@
+package trees.avlTree.putTest
+
+import org.junit.jupiter.api.Test
+import trees.avlTree.AVLTreeForTest
+import trees.avlTree.AuxiliaryFunctions.isSonsHeightDiffCorrect
+import trees.avlTree.AuxiliaryFunctions.isTreeConsistsOf
+import trees.avlTree.AuxiliaryFunctions.isTreeSStructureThat
+import vertexes.AVLVertex
+
+class HaveToRotateLeft {
+ private fun makeTreeForHaveToRotateLeftPutTest(): AVLTreeForTest {
+ val rightSonSRightSon = AVLVertex('h', 'H', AVLVertex('f', 'F'), AVLVertex('i', 'I'))
+ val rightSon = AVLVertex('e', 'E', AVLVertex('d', 'D'), rightSonSRightSon, -1)
+ val root = AVLVertex('c', 'C', AVLVertex('b', 'B', AVLVertex('a', 'A'), null, 1), rightSon, -1)
+ return AVLTreeForTest(root, 8L)
+ }
+
+ @Test
+ fun `content is correct after entry was added`() {
+ val tree = makeTreeForHaveToRotateLeftPutTest()
+ tree.put('j', 'J')
+ val expectedContent =
+ setOf(
+ 'a' to 'A',
+ 'b' to 'B',
+ 'c' to 'C',
+ 'd' to 'D',
+ 'e' to 'E',
+ 'h' to 'H',
+ 'f' to 'F',
+ 'i' to 'I',
+ 'j' to 'J',
+ )
+ assert(isTreeConsistsOf(expectedContent, tree))
+ }
+
+ @Test
+ fun `size increased by 1 after entry was added`() {
+ val tree = makeTreeForHaveToRotateLeftPutTest()
+ tree.put('j', 'J')
+ assert(tree.size() == 9L)
+ }
+
+ @Test
+ fun `structure is correct after entry was added`() {
+ val tree = makeTreeForHaveToRotateLeftPutTest()
+ tree.put('j', 'J')
+ val expectedDependencies =
+ listOf(
+ Triple(0, 1, 3),
+ Triple(1, 2, null),
+ Triple(3, 4, 7),
+ Triple(4, 5, 6),
+ Triple(7, null, 8),
+ )
+ val expectedOrder = arrayOf('c', 'b', 'a', 'h', 'e', 'd', 'f', 'i', 'j')
+ assert(isTreeSStructureThat(tree, expectedOrder, expectedDependencies))
+ }
+
+ @Test
+ fun `sonsHeightDiff values are correct after entry was added`() {
+ val tree = makeTreeForHaveToRotateLeftPutTest()
+ tree.put('j', 'J')
+ assert(isSonsHeightDiffCorrect(tree))
+ }
+
+ private fun makeTreeForHaveToChangeRootByRotateLeftPutTest(): AVLTreeForTest {
+ val rightSon = AVLVertex('h', 'H', AVLVertex('f', 'F'), AVLVertex('i', 'I'))
+ val root = AVLVertex('e', 'E', AVLVertex('d', 'D'), rightSon, -1)
+ return AVLTreeForTest(root, 5L)
+ }
+
+ @Test
+ fun `content is correct after entry was added (rotateLeft changes root)`() {
+ val tree = makeTreeForHaveToChangeRootByRotateLeftPutTest()
+ tree.put('j', 'J')
+ val expectedContent = setOf('d' to 'D', 'e' to 'E', 'h' to 'H', 'f' to 'F', 'i' to 'I', 'j' to 'J')
+ assert(isTreeConsistsOf(expectedContent, tree))
+ }
+
+ @Test
+ fun `size increased by 1 after entry was added (rotateLeft changes root)`() {
+ val tree = makeTreeForHaveToChangeRootByRotateLeftPutTest()
+ tree.put('j', 'J')
+ assert(tree.size() == 6L)
+ }
+
+ @Test
+ fun `structure is correct after entry was added (rotateLeft changes root)`() {
+ val tree = makeTreeForHaveToChangeRootByRotateLeftPutTest()
+ tree.put('j', 'J')
+ val expectedDependencies = listOf(Triple(0, 1, 4), Triple(1, 2, 3), Triple(4, null, 5))
+ val expectedOrder = arrayOf('h', 'e', 'd', 'f', 'i', 'j')
+ assert(isTreeSStructureThat(tree, expectedOrder, expectedDependencies))
+ }
+
+ @Test
+ fun `sonsHeightDiff values are correct after entry was added (rotateLeft changes root)`() {
+ val tree = makeTreeForHaveToChangeRootByRotateLeftPutTest()
+ tree.put('j', 'J')
+ assert(isSonsHeightDiffCorrect(tree))
+ }
+}
diff --git a/lib/src/test/kotlin/trees/avlTree/putTest/HaveToRotateRight.kt b/lib/src/test/kotlin/trees/avlTree/putTest/HaveToRotateRight.kt
new file mode 100644
index 0000000..a03fb71
--- /dev/null
+++ b/lib/src/test/kotlin/trees/avlTree/putTest/HaveToRotateRight.kt
@@ -0,0 +1,103 @@
+package trees.avlTree.putTest
+
+import org.junit.jupiter.api.Test
+import trees.avlTree.AVLTreeForTest
+import trees.avlTree.AuxiliaryFunctions.isSonsHeightDiffCorrect
+import trees.avlTree.AuxiliaryFunctions.isTreeConsistsOf
+import trees.avlTree.AuxiliaryFunctions.isTreeSStructureThat
+import vertexes.AVLVertex
+
+class HaveToRotateRight {
+ private fun makeTreeForHaveToRotateRightPutTest(): AVLTreeForTest {
+ val leftSonSLeftSon = AVLVertex('c', 'C', AVLVertex('b', 'B'), AVLVertex('d', 'D'))
+ val leftSon = AVLVertex('f', 'F', leftSonSLeftSon, AVLVertex('e', 'E'), 1)
+ val root = AVLVertex('h', 'H', leftSon, AVLVertex('i', 'I', null, AVLVertex('j', 'J'), -1), 1)
+ return AVLTreeForTest(root, 8L)
+ }
+
+ @Test
+ fun `content is correct after entry was added `() {
+ val tree = makeTreeForHaveToRotateRightPutTest()
+ tree.put('a', 'A')
+ val expectedContent =
+ setOf(
+ 'a' to 'A',
+ 'b' to 'B',
+ 'c' to 'C',
+ 'd' to 'D',
+ 'e' to 'E',
+ 'h' to 'H',
+ 'f' to 'F',
+ 'i' to 'I',
+ 'j' to 'J',
+ )
+ assert(isTreeConsistsOf(expectedContent, tree))
+ }
+
+ @Test
+ fun `size increased by 1 after entry was added`() {
+ val tree = makeTreeForHaveToRotateRightPutTest()
+ tree.put('a', 'A')
+ assert(tree.size() == 9L)
+ }
+
+ @Test
+ fun `structure is correct after entry was added`() {
+ val tree = makeTreeForHaveToRotateRightPutTest()
+ tree.put('a', 'A')
+ val expectedDependencies =
+ listOf(
+ Triple(0, 1, 7),
+ Triple(1, 2, 4),
+ Triple(2, 3, null),
+ Triple(4, 5, 6),
+ Triple(7, null, 8),
+ )
+ val expectedOrder = arrayOf('h', 'c', 'b', 'a', 'f', 'd', 'e', 'i', 'j')
+ assert(isTreeSStructureThat(tree, expectedOrder, expectedDependencies))
+ }
+
+ @Test
+ fun `sonsHeightDiff values are correct after entry was added`() {
+ val tree = makeTreeForHaveToRotateRightPutTest()
+ tree.put('a', 'A')
+ assert(isSonsHeightDiffCorrect(tree))
+ }
+
+ private fun makeTreeForHaveToChangeRootByRotateRightPutTest(): AVLTreeForTest {
+ val leftSon = AVLVertex('c', 'C', AVLVertex('b', 'B'), AVLVertex('d', 'D'))
+ val root = AVLVertex('f', 'F', leftSon, AVLVertex('e', 'E'), 1)
+ return AVLTreeForTest(root, 5L)
+ }
+
+ @Test
+ fun `content is correct after entry was added (rotateRight changes root)`() {
+ val tree = makeTreeForHaveToChangeRootByRotateRightPutTest()
+ tree.put('a', 'A')
+ val expectedContent = setOf('a' to 'A', 'b' to 'B', 'c' to 'C', 'd' to 'D', 'e' to 'E', 'f' to 'F')
+ assert(isTreeConsistsOf(expectedContent, tree))
+ }
+
+ @Test
+ fun `size increased by 1 after entry was added (rotateRight changes root)`() {
+ val tree = makeTreeForHaveToChangeRootByRotateRightPutTest()
+ tree.put('a', 'A')
+ assert(tree.size() == 6L)
+ }
+
+ @Test
+ fun `structure is correct after entry was added (rotateRight changes root)`() {
+ val tree = makeTreeForHaveToChangeRootByRotateRightPutTest()
+ tree.put('a', 'A')
+ val expectedDependencies = listOf(Triple(0, 1, 3), Triple(1, 2, null), Triple(3, 4, 5))
+ val expectedOrder = arrayOf('c', 'b', 'a', 'f', 'd', 'e')
+ assert(isTreeSStructureThat(tree, expectedOrder, expectedDependencies))
+ }
+
+ @Test
+ fun `sonsHeightDiff values are correct after entry was added (rotateRight changes root)`() {
+ val tree = makeTreeForHaveToChangeRootByRotateRightPutTest()
+ tree.put('a', 'A')
+ assert(isSonsHeightDiffCorrect(tree))
+ }
+}
diff --git a/lib/src/test/kotlin/trees/avlTree/putTest/Other.kt b/lib/src/test/kotlin/trees/avlTree/putTest/Other.kt
new file mode 100644
index 0000000..2793296
--- /dev/null
+++ b/lib/src/test/kotlin/trees/avlTree/putTest/Other.kt
@@ -0,0 +1,27 @@
+package trees.avlTree.putTest
+
+import org.junit.jupiter.api.Test
+import trees.avlTree.AVLTreeForTest
+import trees.avlTree.AuxiliaryFunctions.isTreeConsistsOf
+import vertexes.AVLVertex
+
+class Other {
+ private fun makeSize1Tree(): AVLTreeForTest {
+ return AVLTreeForTest(AVLVertex('b', '+'), 1L)
+ }
+
+ @Test
+ fun `tree has no changes after entry with existed key and param replaceIfExists = false was added`() {
+ val tree = makeSize1Tree()
+ tree.put('b', '-', false)
+ assert(tree.size() == 1L)
+ assert(tree.getRootT()?.value == '+')
+ }
+
+ @Test
+ fun `content is correct after init by map`() {
+ val tree = AVLTreeForTest(hashMapOf('a' to 'A', 'b' to 'B'))
+ val expectedContent = setOf('a' to 'A', 'b' to 'B')
+ assert(isTreeConsistsOf(expectedContent, tree))
+ }
+}
diff --git a/lib/src/test/kotlin/trees/avlTree/putTest/WithoutBalancing.kt b/lib/src/test/kotlin/trees/avlTree/putTest/WithoutBalancing.kt
new file mode 100644
index 0000000..d8cbb0c
--- /dev/null
+++ b/lib/src/test/kotlin/trees/avlTree/putTest/WithoutBalancing.kt
@@ -0,0 +1,112 @@
+package trees.avlTree.putTest
+
+import org.junit.jupiter.api.Test
+import trees.avlTree.AVLTreeForTest
+import trees.avlTree.AuxiliaryFunctions.isSonsHeightDiffCorrect
+import trees.avlTree.AuxiliaryFunctions.isTreeConsistsOf
+import trees.avlTree.AuxiliaryFunctions.isTreeSStructureThat
+import vertexes.AVLVertex
+
+class WithoutBalancing {
+ private fun makeEmptyTree(): AVLTreeForTest {
+ return AVLTreeForTest