diff --git a/.github/mergeable.yml b/.github/mergeable.yml
new file mode 100644
index 0000000..2201f84
--- /dev/null
+++ b/.github/mergeable.yml
@@ -0,0 +1,20 @@
+version: 2
+mergeable:
+ - when: pull_request.*
+ name: 'Check decoration'
+ validate:
+ - do: approvals
+ min:
+ count: 2
+ limit:
+ users: ['Artem-Rzhankoff', 'ItIsMrLaG', 'RozhkovAleksandr']
+ - do: description
+ no_empty:
+ enabled: true
+ message: Description matter and should not be empty. Provide detail with **what** was changed, **why** it was changed, and **how** it was changed.
+ - do: title
+ must_exclude:
+ regex: ^\[WIP\]
+ - do: label
+ must_exclude:
+ regex: 'wip'
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
new file mode 100644
index 0000000..cbb1d05
--- /dev/null
+++ b/.github/workflows/CI.yml
@@ -0,0 +1,38 @@
+name: build project
+
+on: [push, pull_request]
+
+jobs:
+ run:
+ strategy:
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v3
+ with:
+ distribution: temurin
+ java-version: 17
+ cache: gradle
+
+ - name: Run Tests
+ run: ./gradlew clean test
+
+ - if: matrix.os == 'ubuntu-latest' # Container action is only supported on Linux
+ name: Run Test Coverage
+ run: ./gradlew jacocoTestReport
+
+ - if: matrix.os == 'ubuntu-latest'
+ name: Jacoco Code Coverage Report
+ uses: cicirello/jacoco-badge-generator@v2.8.0
+ with:
+ generate-branches-badge: true
+ jacoco-csv-file: lib/build/jacoco/report.csv
+
+ - if: ${{ github.event_name == 'pull_request' }}
+ name: Jacoco Test Coverage Verification
+ run: ./gradlew jacocoTestCoverageVerification
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..68715a1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+/.gradle/
+/.idea/
+/build/
+/lib/build/
+/lib/TEST_TEST/
+/gradle/
+/neo4jDB/
+/saved-trees/
+/sqliteDB/
+/treeApp/build/
diff --git a/LICENCE b/LICENCE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENCE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ce95799
--- /dev/null
+++ b/README.md
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+## Tree Structure
+
+
+
+This project will help the user to build an RBT, AVL or binary tree. Add, delete, or search for elements in it.
+
+
+## Features
+With **trees-1** you can do the following:
+- Create three kinds of binary search trees: **Binary search tree**, **AVL tree**, **Red-black tree**
+- Using the GUI, perform the following operations (**insert**, **delete**, **search**)
+- Save and load trees into databases (**json files** for binary, **SQLite** for AVL-tree, **neo4j** for RB-tree)
+- Use an independent library with the implementation of these trees in your project
+
+## Get it!
+Important: To build and run the application, you will need the following tools: Gradle, JDK (version 17 and latest) and Neo4j.
+### Quick start
+```bash
+# Clone this repo
+git clone https://github.com/spbu-coding-2022/trees-1.it
+
+# Build
+./gradlew build
+
+# Build Docker (link)
+
+# Run
+./gradlew run
+```
+If you only want to use the library, you need to follow these steps:
+```bash
+# Find the "dependencies" block and add the dependency line to it:
+
+# If in your project build.gradle.kts:
+implementation("com.github.spbu-coding-2022:trees-1:1.0.0")
+# If in your project build.gradle:
+implementation 'com.github.spbu-coding-2022:trees-1:1.0.0'
+
+# Save the changes in the build file
+```
+## Neo4j setup
+1. Install docker
+2. Install docker compose (https://docs.docker.com/compose/install/)
+3. Run in project dir:
+```bash
+docker compose -f "docker-compose.yml" up
+```
+4. If you're having trouble getting started, check out this [GUIDE](https://www.baeldung.com/ops/docker-cannot-connect#:~:text=Due%20to%20Inactive%20Docker%20Service)
+
+
+## See examples
+
+
+
+
+## Contacts
+For all questions: [Click](http://telegram.me/LesokSupportbot)
+
+## License
+This project uses the **APACHE LICENSE, VERSION 2.0**. See the [LICENSE](LICENSE.md) for more info.
+
+
+
+
+
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..5fce390
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,13 @@
+version: '3.9'
+
+services:
+ neo4j:
+ image: neo4j:latest
+ volumes:
+ - ./neo4jDB:/data
+ container_name: neo4j-db
+ ports:
+ - "7474:7474"
+ - "7687:7687"
+ environment:
+ - NEO4J_AUTH=neo4j/password
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..f1a74ac
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,3 @@
+kotlinVersion=1.8.10
+sqliteJdbcVersion=3.41.2.1
+exposedVersion=0.40.1
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..ccebba7
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..42defcc
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+networkTimeout=10000
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..a9bd892
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,243 @@
+#!/bin/sh
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..93e3f59
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts
new file mode 100644
index 0000000..c44d77b
--- /dev/null
+++ b/lib/build.gradle.kts
@@ -0,0 +1,127 @@
+plugins {
+ java
+ kotlin("jvm") version "1.8.10"
+ jacoco
+ `java-library`
+ `maven-publish`
+ kotlin("plugin.serialization") version "1.5.0"
+}
+
+kotlin {
+ jvmToolchain(17)
+}
+
+dependencies {
+ api("org.apache.commons:commons-math3:3.6.1")
+ implementation("com.google.guava:guava:31.1-jre")
+ implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
+
+ implementation("com.google.code.gson:gson:2.10.1")
+
+ val neo4jCore = "4.0.5"
+ implementation("org.neo4j", "neo4j-ogm-core", neo4jCore)
+ implementation("org.neo4j", "neo4j-ogm-bolt-driver", neo4jCore)
+
+ // JDBC Sqlite
+ val sqliteJdbcVersion: String by project
+ implementation("org.xerial", "sqlite-jdbc", sqliteJdbcVersion)
+
+ val exposedVersion: String by project
+ implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
+ implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")
+ implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
+
+ testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:1.8.10")
+ testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2")
+ testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.2")
+}
+
+tasks.test {
+ finalizedBy(tasks.jacocoTestReport)
+ useJUnitPlatform()
+ testLogging {
+ events("passed", "skipped", "failed")
+ showStandardStreams = true
+ }
+ val failedTests = mutableListOf()
+ val skippedTests = mutableListOf()
+
+ addTestListener (object: TestListener {
+ override fun beforeSuite(suite: TestDescriptor?) { }
+ override fun beforeTest(testDescriptor: TestDescriptor?) { }
+ override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {
+ when (result.resultType) {
+ TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor)
+ TestResult.ResultType.SKIPPED -> skippedTests.add(testDescriptor)
+ else -> {}
+ }
+ }
+ override fun afterSuite(suite: TestDescriptor, result: TestResult) {
+ if (suite.parent == null) { // root suite
+ logger.lifecycle("####################################################################################")
+ logger.lifecycle("Test result: ${result.resultType}")
+ logger.lifecycle(
+ "Test summary: ${result.testCount} tests, " +
+ "${result.successfulTestCount} succeed, " +
+ "${result.skippedTestCount} skipped, " +
+ "${result.failedTestCount} failed."
+ )
+ }
+ }
+ })
+
+}
+
+tasks.jacocoTestReport {
+ dependsOn(tasks.test)
+ reports {
+ xml.required.set(true)
+ xml.outputLocation.set(layout.buildDirectory.file("jacoco/report.xml"))
+ csv.required.set(true)
+ csv.outputLocation.set(layout.buildDirectory.file("jacoco/report.csv"))
+ html.required.set(true)
+ html.outputLocation.set(layout.buildDirectory.dir("jacocoHtml"))
+ }
+}
+
+tasks.jacocoTestCoverageVerification {
+ classDirectories.setFrom( classDirectories.files.flatMap { fileTree(it) {
+ include("**/RBBalancer.class", "**/AVLBalancer.class", "**/BINStruct", "**/AVLStruct.class", "**/BINStruct.class")
+ exclude("**/singleObjects/**", "**/RBVertex.class", "**/AVLVertex.class", "**/BINVertex.class", "**/Vertex.class", "**/BINStruct.class", "**/AVLStruct.class", "**/RBStruct.class", "**/TreeStruct.class")
+ } })
+ dependsOn(tasks.jacocoTestReport)
+ violationRules {
+ rule {
+ element = "CLASS"
+ limit {
+ counter = "BRANCH"
+ minimum = 0.5.toBigDecimal()
+ }
+ }
+ rule {
+ element = "CLASS"
+ limit {
+ counter = "LINE"
+ minimum = 0.6.toBigDecimal()
+ }
+ }
+ rule {
+ element = "CLASS"
+ limit {
+ counter = "METHOD"
+ minimum = 0.9.toBigDecimal()
+ }
+ }
+ }
+}
+
+publishing {
+ publications {
+ create("maven") {
+ groupId = "Tree"
+ artifactId = "lib"
+ version = "1.1"
+ from(components["java"])
+ }
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/abstractTree/Node.kt b/lib/src/main/kotlin/treelib/abstractTree/Node.kt
new file mode 100644
index 0000000..849e46e
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/abstractTree/Node.kt
@@ -0,0 +1,7 @@
+package treelib.abstractTree
+
+interface Node, SubNode : Node> {
+ var value: Pack
+ var left: SubNode?
+ var right: SubNode?
+}
diff --git a/lib/src/main/kotlin/treelib/abstractTree/NodeParent.kt b/lib/src/main/kotlin/treelib/abstractTree/NodeParent.kt
new file mode 100644
index 0000000..4d8c046
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/abstractTree/NodeParent.kt
@@ -0,0 +1,7 @@
+package treelib.abstractTree
+
+interface NodeParent, SubNode : NodeParent> : Node {
+ var parent: SubNode?
+ override var right: SubNode?
+ override var left: SubNode?
+}
diff --git a/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt b/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt
new file mode 100644
index 0000000..b835879
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/abstractTree/StateContainer.kt
@@ -0,0 +1,5 @@
+package treelib.abstractTree
+
+interface StateContainer, NodeType : Node> {
+ val contentNode: NodeType?
+}
diff --git a/lib/src/main/kotlin/treelib/abstractTree/Tree.kt b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt
new file mode 100644
index 0000000..01bfc48
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/abstractTree/Tree.kt
@@ -0,0 +1,52 @@
+package treelib.abstractTree
+
+import treelib.commonObjects.Container
+import treelib.commonObjects.exceptions.VauleNotFound
+
+abstract class Tree<
+ K : Comparable,
+ V,
+ NodeType : Node, NodeType>,
+ State : StateContainer, NodeType>,
+ VertexType : Vertex>
+ > {
+
+ protected abstract val treeStruct: TreeStruct, NodeType, State, VertexType>
+
+ private fun wrapForFind(key: K) = Container(key to null)
+
+ fun putItem(item: Pair) {
+ treeStruct.insert(Container(item))
+ }
+
+ fun putItem(vararg items: Pair) {
+ for (element in items) putItem(element)
+ }
+
+ fun putItem(items: Iterable>) {
+ for (element in items) putItem(element)
+ }
+
+ fun getItem(key: K): V? = treeStruct.find(wrapForFind(key))?.value
+
+ open operator fun get(key: K): V? = treeStruct.find(wrapForFind(key))?.value
+
+ fun deleteItem(key: K) {
+ if (treeStruct.find(wrapForFind(key)) == null) throw VauleNotFound()
+ treeStruct.delete(wrapForFind(key))
+ }
+
+ private fun createPoorList(info: List): List> {
+ val returnInfo = mutableListOf>()
+ for (element in info) {
+ returnInfo.add(element.value.pair)
+ }
+ return returnInfo
+ }
+
+ fun inOrder(): List> = createPoorList(treeStruct.inOrder())
+
+ fun preOrder(): List> = createPoorList(treeStruct.preOrder())
+
+ fun postOrder(): List> = createPoorList(treeStruct.postOrder())
+}
diff --git a/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt
new file mode 100644
index 0000000..272eed4
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/abstractTree/TreeStruct.kt
@@ -0,0 +1,362 @@
+package treelib.abstractTree
+
+import treelib.commonObjects.exceptions.ImpossibleCaseException
+import treelib.commonObjects.exceptions.VauleNotFound
+
+
+abstract class TreeStruct<
+ Pack : Comparable,
+ NodeType : Node,
+ State : StateContainer,
+ VertexType : Vertex
+ > {
+
+ protected abstract var root: NodeType?
+
+ protected fun getLeafForInsert(item: Pack): NodeType? {
+ var currentNode: NodeType? = root ?: return null
+
+ while (true) {
+ currentNode?.let {
+ when {
+ item > it.value -> {
+ if (it.right == null) return@getLeafForInsert currentNode
+ currentNode = it.right
+ }
+
+ item < it.value -> {
+ if (it.left == null) return@getLeafForInsert currentNode
+ currentNode = it.left
+ }
+
+ else -> throw InternalError("getLeafForInsert shouldn't be used with a value exists in Struct")
+ }
+ } ?: throw ImpossibleCaseException()
+ }
+ }
+
+ private fun getParentByValue(item: Pack): NodeType? {
+ var currentNode = root
+
+ if ((currentNode != null) && (currentNode.value == item)) return null
+
+ while (true) {
+ currentNode?.let {
+ when {
+ item inRightOf it -> return@getParentByValue currentNode
+ item inLeftOf it -> return@getParentByValue currentNode
+ else -> {
+ if (item > it.value) currentNode = it.right
+ else currentNode = it.left
+ }
+ }
+ }
+ ?: throw InternalError("getParentByValue shouldn't be used with value doesn't exist in tree")// (1)l ->
+ }
+ }
+
+ private infix fun Pack.inRightOf(node: NodeType?): Boolean {
+ if (node == null) return false
+ node.right?.let {
+ if (this == it.value) return@inRightOf true
+ }
+ return false
+ }
+
+ private infix fun Pack.inLeftOf(node: NodeType?): Boolean {
+ if (node == null) return false
+ node.left?.let {
+ if (it.value == this) return@inLeftOf true
+ }
+ return false
+ }
+
+ private fun getRightMinNode(localRoot: NodeType): NodeType {
+ var currentNode: NodeType?
+
+ localRoot.right
+ ?: throw InternalError("Incorrect usage of the getRightMinNode: right node doesn't exist")
+
+ currentNode = localRoot.right
+
+ while (true) {
+ currentNode?.let { curNode ->
+ if (curNode.left == null) return@getRightMinNode curNode
+ else currentNode = curNode.left
+ } ?: throw ImpossibleCaseException()
+ }
+ }
+
+ private fun unLink(
+ node: NodeType,
+ parent: NodeType?,
+ ): NodeType {
+ val unLinkedNode: NodeType = node
+ val childForLink: NodeType?
+
+ when {
+ (node.right != null) && (node.left != null) -> throw InternalError("unLink - method Shouldn't be used with node with both children")
+ node.right != null -> childForLink = node.right
+ node.left != null -> childForLink = node.left
+ else -> childForLink = null
+ }
+ unLinkedNode.left = null
+ unLinkedNode.right = null
+
+ if (parent == null) return unLinkedNode
+ connectUnlinkedSubTreeWithParent(node, parent, childForLink)
+
+ return unLinkedNode
+ }
+
+ private fun rebaseNode(
+ node: NodeType,
+ parent: NodeType?,
+ replaceNode: NodeType?,
+ ) {
+ when {
+ (parent == null) && (replaceNode == null) -> root = null
+ (parent != null) && (replaceNode == null) -> {
+ when {
+ node.value inLeftOf parent.left -> parent.left = null
+ node.value inRightOf parent.right -> parent.right = null
+ }
+ }
+
+ replaceNode != null -> node.value = replaceNode.value
+ }
+ }
+
+ protected abstract fun generateStateFind(
+ findNode: NodeType?,
+ contentNode: NodeType? = null,
+ ): State
+
+ protected fun findItem(obj: Pack): State {
+ var currentNode = root
+ if (root == null) return generateStateFind(null, null)
+
+ while (true) {
+ if (obj == currentNode?.value) return generateStateFind(currentNode, currentNode)
+ else {
+ currentNode?.let {
+ if (obj > it.value) currentNode = it.right
+ else currentNode = it.left
+ }
+ if (currentNode == null) return generateStateFind(null, null)
+ }
+ }
+ }
+
+ protected abstract fun generateStateInsert(
+ insertNode: NodeType?,
+ contentNode: NodeType? = null,
+ ): State
+
+ protected fun insertItem(item: Pack): State {
+ val parentNode: NodeType?
+ val currentNode: NodeType
+ val updateNode: NodeType? = findItem(item).contentNode
+
+ if (updateNode == null) {
+ parentNode = getLeafForInsert(item)
+ currentNode = createNode(item)
+
+ linkNewNode(currentNode, parentNode)
+
+ if (parentNode != null) return generateStateInsert(currentNode, parentNode)
+ else return generateStateInsert(currentNode, currentNode)
+ }
+
+ updateNode.value = item
+ return generateStateInsert(null, null)
+ }
+
+ protected abstract fun generateStateDelete(
+ deletedNode: NodeType?,
+ contentNode: NodeType? = null,
+ ): State
+
+ protected fun deleteItem(item: Pack): State {
+ val parentDeleteNode: NodeType?
+ val deleteNode: NodeType?
+
+ if (findItem(item).contentNode == null) throw VauleNotFound()
+
+ parentDeleteNode = getParentByValue(item)
+ if (parentDeleteNode != null) {
+ deleteNode = when {
+ item inRightOf parentDeleteNode -> parentDeleteNode.right
+ item inLeftOf parentDeleteNode -> parentDeleteNode.left
+ else -> throw ImpossibleCaseException()
+ }
+ } else deleteNode = root
+
+ if (deleteNode == null) throw ImpossibleCaseException()
+
+ val nodeForReplace: NodeType?
+ val parentNodeForReplace: NodeType?
+ val deletedNodeWithoutLinks = getNodeKernel(deleteNode)
+
+ when {
+ (deleteNode.left != null) && (deleteNode.right != null) -> {
+ val rightMinNode = getRightMinNode(deleteNode)
+
+ parentNodeForReplace = getParentByValue(rightMinNode.value)
+ nodeForReplace = unLink(rightMinNode, parentNodeForReplace)
+
+ rebaseNode(deleteNode, parentDeleteNode, nodeForReplace)
+
+ return generateStateDelete(deletedNodeWithoutLinks, parentNodeForReplace)
+ }
+
+ (deleteNode.left == null) && (deleteNode.right == null) -> {
+ if (parentDeleteNode == null) {
+ root = null
+ return generateStateDelete(deletedNodeWithoutLinks, null)
+ } else {
+ when {
+ item inRightOf parentDeleteNode -> parentDeleteNode.right = null
+ item inLeftOf parentDeleteNode -> parentDeleteNode.left = null
+ }
+ return generateStateDelete(deletedNodeWithoutLinks, parentDeleteNode)
+ }
+ }
+
+ else -> {
+ for (child in listOf(deleteNode.right, deleteNode.left))
+ child?.let {
+ connectUnlinkedSubTreeWithParent(deleteNode, parentDeleteNode, it)
+ if (parentDeleteNode != null) {
+ return@deleteItem generateStateDelete(deletedNodeWithoutLinks, parentDeleteNode)
+ } else {
+ return@deleteItem generateStateDelete(deletedNodeWithoutLinks, root)
+ }
+ }
+ }
+ }
+ throw ImpossibleCaseException()
+ }
+
+ protected abstract fun connectUnlinkedSubTreeWithParent(
+ node: NodeType,
+ parent: NodeType?,
+ childForLink: NodeType?
+ ) /* Behaviour: link rebased node */
+
+ /* Return node with fields: right == left == {parent} == null */
+ protected abstract fun getNodeKernel(node: NodeType): NodeType
+
+ protected abstract fun linkNewNode(
+ node: NodeType,
+ parent: NodeType?,
+ ): NodeType
+
+ protected abstract fun createNode(item: Pack): NodeType
+
+ abstract fun insert(item: Pack)
+
+ /* Behaviour: null - means value not in tree; Pack - value was successfully deleted */
+ abstract fun delete(item: Pack)
+
+ fun find(obj: Pack): Pack? = findItem(obj).contentNode?.value
+
+ fun inOrder(): List {
+ val arrayNodes = mutableListOf()
+ var flagVisited = 0
+ var current = root
+ val parents = ArrayDeque()
+
+ while (current != null) {
+ if (flagVisited == 0) {
+ while (true) {
+ current?.let {
+ if (it.left == null) return@let null
+ parents.add(it)
+ current = it.left
+ return@let current
+ } ?: break
+ }
+ }
+ current?.let {
+ arrayNodes.add(it)
+ if (it.right != null) {
+ flagVisited = 0
+ current = it.right
+ } else {
+ if (parents.isEmpty())
+ return@inOrder arrayNodes.map { toVertex(it) }
+ flagVisited = 1
+ current = parents.removeLast()
+ }
+ }
+ }
+ return arrayNodes.map { toVertex(it) }
+ }
+
+ abstract fun toVertex(node: NodeType): VertexType
+
+ fun postOrder(): List {
+ val parents = ArrayDeque()
+ val arrayNodes = mutableListOf()
+ var flagVisited = 0
+ var current = root
+
+ while (current != null) {
+ if (flagVisited == 0) {
+ while (true) {
+ current?.let {
+ if (it.left == null) return@let null
+ parents.add(it)
+ current = it.left
+ return@let current
+ } ?: break
+ }
+ }
+ current?.let {
+ if (it.right != null && flagVisited != 2) {
+ parents.add(it)
+ current = it.right
+ flagVisited = 0
+ } else {
+ arrayNodes.add(it)
+ if (parents.isEmpty())
+ return@postOrder arrayNodes.map { toVertex(it) }
+ val parent = parents.removeLast()
+ if (parent.right == it) {
+ flagVisited = 2
+ }
+ else {
+ flagVisited = 1
+ }
+ current = parent
+ }
+ } ?: throw ImpossibleCaseException()
+ }
+ return arrayNodes.map { toVertex(it) }
+ }
+
+ fun preOrder(): List {
+ val arrayNodes = mutableListOf()
+ var current: NodeType
+ val queue = ArrayDeque()
+
+ root?.let { root ->
+ queue.add(root)
+ while (queue.isNotEmpty()) {
+ current = queue.removeLast()
+ arrayNodes.add(current)
+ if (current.right != null)
+ current.right?.let {
+ queue.add(it)
+ } ?: throw ImpossibleCaseException()
+
+ if (current.left != null)
+ current.left?.let {
+ queue.add(it)
+ } ?: throw ImpossibleCaseException()
+ }
+ }
+ return arrayNodes.map { toVertex(it) }
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt b/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt
new file mode 100644
index 0000000..68f3726
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/abstractTree/Vertex.kt
@@ -0,0 +1,5 @@
+package treelib.abstractTree
+
+abstract class Vertex> {
+ abstract val value: Pack
+}
\ No newline at end of file
diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt
new file mode 100644
index 0000000..5172f71
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancedTreeStruct.kt
@@ -0,0 +1,35 @@
+package treelib.abstractTree.balanced
+
+import treelib.abstractTree.Node
+import treelib.abstractTree.StateContainer
+import treelib.abstractTree.TreeStruct
+import treelib.abstractTree.Vertex
+
+abstract class BalancedTreeStruct<
+ Pack : Comparable,
+ NodeType : Node,
+ State : StateContainer,
+ VertexType : Vertex,
+ BalancerType : Balancer,
+ > : TreeStruct() {
+
+ protected abstract val balancer: BalancerType
+
+ override fun insert(item: Pack) {
+ val currentState = insertItem(item)
+ if (currentState.contentNode != null) {
+ root = balancer.balance(currentState)
+ }
+ }
+
+ override fun delete(item: Pack) {
+ val currentState = deleteItem(item)
+ if (root == null) {
+ return
+ }
+ if (currentState.contentNode != null) {
+ root = balancer.balance(currentState)
+ return
+ }
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt
new file mode 100644
index 0000000..6931a5b
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/Balancer.kt
@@ -0,0 +1,13 @@
+package treelib.abstractTree.balanced
+
+import treelib.abstractTree.Node
+import treelib.abstractTree.StateContainer
+
+interface Balancer<
+ Pack : Comparable,
+ NodeType : Node,
+ State : StateContainer,
+ > {
+
+ fun balance(state: State): NodeType
+}
diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt
new file mode 100644
index 0000000..c479e2b
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerNoParent.kt
@@ -0,0 +1,21 @@
+package treelib.abstractTree.balanced
+
+import treelib.abstractTree.Node
+import treelib.abstractTree.StateContainer
+
+abstract class BalancerNoParent, NodeType : Node, StateContainerType : StateContainer> :
+ Balancer {
+ fun rightRotate(currentNode: NodeType): NodeType {
+ val leftSon = currentNode.left ?: throw InternalError()
+ currentNode.left = leftSon.right
+ leftSon.right = currentNode
+ return leftSon
+ }
+
+ fun leftRotate(currentNode: NodeType): NodeType {
+ val rightSon = currentNode.right ?: throw InternalError()
+ currentNode.right = rightSon.left
+ rightSon.left = currentNode
+ return rightSon
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt
new file mode 100644
index 0000000..e72e763
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/abstractTree/balanced/BalancerParent.kt
@@ -0,0 +1,41 @@
+package treelib.abstractTree.balanced
+
+import treelib.abstractTree.NodeParent
+import treelib.abstractTree.StateContainer
+
+abstract class BalancerParent, NodeType : NodeParent, StateContainerType : StateContainer> :
+ Balancer {
+ fun rightRotate(currentNode: NodeType): NodeType {
+ val leftChild = currentNode.left ?: throw InternalError()
+
+ val parent = currentNode.parent
+ leftChild.right?.parent = currentNode
+ currentNode.left = leftChild.right
+ leftChild.right = currentNode
+
+ when {
+ parent?.left == currentNode -> parent.left = leftChild
+ parent?.right == currentNode -> parent.right = leftChild
+ }
+ currentNode.parent = leftChild
+ leftChild.parent = parent
+ return leftChild
+ }
+
+ fun leftRotate(currentNode: NodeType): NodeType {
+ val rightChild = currentNode.right ?: throw InternalError()
+ val parent = currentNode.parent
+
+ rightChild.left?.parent = currentNode
+ currentNode.right = rightChild.left
+ rightChild.left = currentNode
+ when {
+ parent?.left == currentNode -> parent.left = rightChild
+ parent?.right == currentNode -> parent.right = rightChild
+ }
+ currentNode.parent = rightChild
+ rightChild.parent = parent
+ return rightChild
+ }
+
+}
diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt
new file mode 100644
index 0000000..65157da
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/avlTree/AVLBalancer.kt
@@ -0,0 +1,63 @@
+package treelib.avlTree
+
+import treelib.abstractTree.balanced.BalancerNoParent
+
+class AVLBalancer>(private var root: AVLNode?) :
+ BalancerNoParent, AVLStateContainer>() {
+ private fun updateBalance(node: AVLNode?): Int {
+ return (getHeight(node?.left) - getHeight(node?.right)).toInt()
+ }
+
+ private fun getHeight(currentNode: AVLNode?): UInt {
+ return currentNode?.height ?: 0u
+ }
+
+ private fun updateHeight(currentNode: AVLNode?) {
+ if (currentNode != null)
+ currentNode.height = maxOf(getHeight(currentNode.left), getHeight(currentNode.right)) + 1u
+ }
+
+ override fun balance(state: AVLStateContainer): AVLNode {
+ val node = state.contentNode
+ ?: throw IllegalStateException("") // IllegalBaseNodeException("A non-existent node (null) was passed to the method")
+ root = state.root
+ return balance(root, node.value)
+ }
+
+ /*** In the method we pass the parent of the removed/inserted node ***/
+ private fun balance(currentNode: AVLNode?, value: Pack): AVLNode {
+ if (currentNode == null) {
+ throw NullPointerException()
+ }
+ when {
+ currentNode.value < value -> currentNode.right = balance(currentNode.right, value)
+ currentNode.value > value -> currentNode.left = balance(currentNode.left, value)
+ }
+ updateHeight(currentNode)
+ val balance = updateBalance(currentNode)
+ if (balance == -2) {
+ if (updateBalance(currentNode.right) == 1) {
+ currentNode.right = currentNode.right?.let { rightRotate(it) }
+ ?: throw NullPointerException() // IllegalNodeStateException()
+ updateHeight(currentNode.right?.right)
+ }
+ val balancedNode = leftRotate(currentNode)
+ updateHeight(balancedNode.left)
+ updateHeight(balancedNode)
+ return balancedNode
+ }
+ if (balance == 2) {
+ if (updateBalance(currentNode.left) == -1) {
+ currentNode.left = currentNode.left?.let { leftRotate(it) }
+ ?: throw NullPointerException() // IllegalNodeStateException("There is no node required by the condition of the algorithm")
+ updateHeight(currentNode.left?.left)
+ }
+ val balanceNode = rightRotate(currentNode)
+ updateHeight(balanceNode.right)
+ updateHeight(balanceNode)
+ return balanceNode
+ }
+ return currentNode
+ }
+
+}
diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt b/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt
new file mode 100644
index 0000000..bcbffc1
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/avlTree/AVLNode.kt
@@ -0,0 +1,10 @@
+package treelib.avlTree
+
+import treelib.abstractTree.Node
+
+class AVLNode>(
+ override var value: Pack,
+ override var left: AVLNode? = null,
+ override var right: AVLNode? = null,
+ var height: UInt = 1U,
+) : Node>
diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt
new file mode 100644
index 0000000..121a614
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/avlTree/AVLStateContainer.kt
@@ -0,0 +1,8 @@
+package treelib.avlTree
+
+import treelib.abstractTree.StateContainer
+
+class AVLStateContainer>(
+ override val contentNode: AVLNode?,
+ val root: AVLNode?,
+) : StateContainer>
diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt
new file mode 100644
index 0000000..d977ba0
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/avlTree/AVLStruct.kt
@@ -0,0 +1,69 @@
+package treelib.avlTree
+
+import treelib.abstractTree.balanced.BalancedTreeStruct
+import treelib.commonObjects.exceptions.IncorrectUsage
+
+class AVLStruct> :
+ BalancedTreeStruct, AVLStateContainer, AVLVertex, AVLBalancer>() {
+
+ override var root: AVLNode? = null
+ override val balancer = AVLBalancer(root)
+
+ override fun generateStateDelete(
+ deletedNode: AVLNode?,
+ contentNode: AVLNode?,
+ ): AVLStateContainer = AVLStateContainer(contentNode, root)
+
+ override fun generateStateInsert(
+ insertNode: AVLNode?,
+ contentNode: AVLNode?
+ ): AVLStateContainer = AVLStateContainer(contentNode, root)
+
+ override fun generateStateFind(
+ findNode: AVLNode?,
+ contentNode: AVLNode?,
+ ): AVLStateContainer = AVLStateContainer(contentNode, root)
+
+ override fun connectUnlinkedSubTreeWithParent(
+ node: AVLNode,
+ parent: AVLNode?,
+ childForLink: AVLNode?
+ ) {
+ if (root == null) return
+
+ if (parent != null) {
+ when {
+ (node.value < parent.value) -> parent.left = childForLink
+ (node.value > parent.value) -> parent.right = childForLink
+ }
+ } else root?.let {
+ root = childForLink
+ }
+ }
+
+ override fun toVertex(node: AVLNode): AVLVertex = AVLVertex(node.value, node.height)
+
+ fun toNode(vertex: AVLVertex): AVLNode = AVLNode(value = vertex.value, height = vertex.height)
+
+ override fun createNode(item: Pack): AVLNode = AVLNode(item)
+
+ override fun getNodeKernel(node: AVLNode): AVLNode = AVLNode(node.value, height = node.height)
+
+ override fun linkNewNode(node: AVLNode, parent: AVLNode?): AVLNode {
+ if (parent == null) root = node
+ else {
+ if (node.value > parent.value) parent.right = node
+ else parent.left = node
+ }
+ return node
+ }
+
+ fun > restoreStruct(preOrder: MutableList) {
+ if (root != null) throw IncorrectUsage("The tree already exists")
+ for (vertex in preOrder) {
+ val currentNode = toNode(vertex)
+ val leaf = getLeafForInsert(currentNode.value)
+ linkNewNode(currentNode, leaf)
+ }
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt
new file mode 100644
index 0000000..a5f1af3
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/avlTree/AVLTree.kt
@@ -0,0 +1,15 @@
+package treelib.avlTree
+
+import treelib.abstractTree.Tree
+
+import treelib.commonObjects.Container
+
+class AVLTree, V> :
+ Tree>, AVLStateContainer>, AVLVertex>>() {
+
+ override val treeStruct = AVLStruct>()
+
+ operator fun set(key: K, value: V) = putItem(key to value)
+
+ override operator fun get(key: K) = getItem(key)
+}
diff --git a/lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt b/lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt
new file mode 100644
index 0000000..774d064
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/avlTree/AVLVertex.kt
@@ -0,0 +1,8 @@
+package treelib.avlTree
+
+import treelib.abstractTree.Vertex
+
+class AVLVertex>(
+ override val value: Pack,
+ val height: UInt,
+) : Vertex()
diff --git a/lib/src/main/kotlin/treelib/binTree/BINNode.kt b/lib/src/main/kotlin/treelib/binTree/BINNode.kt
new file mode 100644
index 0000000..5b764ff
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/binTree/BINNode.kt
@@ -0,0 +1,9 @@
+package treelib.binTree
+
+import treelib.abstractTree.Node
+
+class BINNode>(
+ override var value: Pack,
+ override var left: BINNode? = null,
+ override var right: BINNode? = null,
+) : Node>
diff --git a/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt
new file mode 100644
index 0000000..4a1266e
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/binTree/BINStateContainer.kt
@@ -0,0 +1,7 @@
+package treelib.binTree
+
+import treelib.abstractTree.StateContainer
+
+class BINStateContainer>(
+ override val contentNode: BINNode?,
+) : StateContainer>
diff --git a/lib/src/main/kotlin/treelib/binTree/BINStruct.kt b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt
new file mode 100644
index 0000000..b66ca85
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/binTree/BINStruct.kt
@@ -0,0 +1,81 @@
+package treelib.binTree
+
+import treelib.abstractTree.TreeStruct
+import treelib.commonObjects.exceptions.IncorrectUsage
+
+class BINStruct> :
+ TreeStruct, BINStateContainer, BINVertex>() {
+
+ override var root: BINNode? = null
+
+ override fun generateStateDelete(
+ deletedNode: BINNode?,
+ contentNode: BINNode?
+ ): BINStateContainer = BINStateContainer(deletedNode)
+
+ override fun generateStateInsert(
+ insertNode: BINNode?,
+ contentNode: BINNode?,
+ ): BINStateContainer = BINStateContainer(insertNode)
+
+ override fun generateStateFind(
+ findNode: BINNode?,
+ contentNode: BINNode?,
+ ): BINStateContainer = BINStateContainer(findNode)
+
+ override fun getNodeKernel(node: BINNode) = BINNode(node.value)
+
+ override fun connectUnlinkedSubTreeWithParent(
+ node: BINNode,
+ parent: BINNode?,
+ childForLink: BINNode?,
+ ) {
+ if (root == null) return
+
+ if (parent != null) {
+ when {
+ (node.value < parent.value) -> parent.left = childForLink
+ (node.value > parent.value) -> parent.right = childForLink
+ }
+ } else root?.let {
+ root = childForLink
+ }
+ }
+
+ override fun linkNewNode(
+ node: BINNode,
+ parent: BINNode?,
+ ): BINNode {
+ if (parent == null) root = node
+ else {
+ if (node.value > parent.value) parent.right = node
+ else parent.left = node
+ }
+ return node
+ }
+
+ override fun toVertex(node: BINNode): BINVertex {
+ return BINVertex(node.value)
+ }
+
+ override fun createNode(item: Pack) = BINNode(item)
+
+ override fun delete(item: Pack) {
+ deleteItem(item).contentNode
+ }
+
+ override fun insert(item: Pack) {
+ insertItem(item).contentNode
+ }
+
+ private fun toNode(vertex: BINVertex): BINNode = BINNode(value = vertex.value)
+
+ fun > restoreStruct(preOrder: List) {
+ if (root != null) throw IncorrectUsage("The tree already exists")
+ for (vertex in preOrder) {
+ val currentNode = toNode(vertex)
+ val leaf = getLeafForInsert(currentNode.value)
+ linkNewNode(currentNode, leaf)
+ }
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/binTree/BINTree.kt b/lib/src/main/kotlin/treelib/binTree/BINTree.kt
new file mode 100644
index 0000000..ebc792c
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/binTree/BINTree.kt
@@ -0,0 +1,14 @@
+package treelib.binTree
+
+import treelib.abstractTree.Tree
+import treelib.commonObjects.Container
+
+class BINTree, V>
+ : Tree>, BINStateContainer>, BINVertex>>() {
+
+ override val treeStruct = BINStruct>()
+
+ operator fun set(key: K, value: V) = putItem(key to value)
+
+ override operator fun get(key: K) = getItem(key)
+}
diff --git a/lib/src/main/kotlin/treelib/binTree/BINVertex.kt b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt
new file mode 100644
index 0000000..cb489a0
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/binTree/BINVertex.kt
@@ -0,0 +1,5 @@
+package treelib.binTree
+
+import treelib.abstractTree.Vertex
+
+open class BINVertex>(override val value: Pack) : Vertex()
\ No newline at end of file
diff --git a/lib/src/main/kotlin/treelib/commonObjects/Container.kt b/lib/src/main/kotlin/treelib/commonObjects/Container.kt
new file mode 100644
index 0000000..20530cf
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/commonObjects/Container.kt
@@ -0,0 +1,30 @@
+package treelib.commonObjects
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class Container, V>(val pair: Pair) : Comparable> {
+
+ val key = pair.first
+ val value = pair.second
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other)
+ return true
+
+ other as Container<*, *>
+ return this.pair.first == other.pair.first
+ }
+
+ override fun hashCode(): Int {
+ return pair.hashCode()
+ }
+
+ override fun compareTo(other: Container): Int {
+ return compareValuesBy(this, other) { it.pair.first }
+ }
+
+ override fun toString(): String {
+ return "$key"
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/commonObjects/exceptions/ImpossibleCaseException.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/ImpossibleCaseException.kt
new file mode 100644
index 0000000..568cf04
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/ImpossibleCaseException.kt
@@ -0,0 +1,18 @@
+package treelib.commonObjects.exceptions
+
+open class ImpossibleCaseException : Exception {
+ constructor() : super(
+ "This case is impossible, otherwise there is an error in the program code",
+ )
+
+ constructor(message: String) : super(
+ "This case is impossible, otherwise there is an error in the program code -> $message",
+ )
+
+ constructor(message: String, cause: Throwable) : super(
+ "This case is impossible, otherwise there is an error in the program code -> $message",
+ cause,
+ )
+
+ constructor(cause: Throwable) : super(cause)
+}
diff --git a/lib/src/main/kotlin/treelib/commonObjects/exceptions/IncorrectUsage.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/IncorrectUsage.kt
new file mode 100644
index 0000000..1bcd994
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/IncorrectUsage.kt
@@ -0,0 +1,18 @@
+package treelib.commonObjects.exceptions
+
+class IncorrectUsage : Exception {
+ constructor() : super(
+ "Incorrect use of the tree"
+ )
+
+ constructor(message: String) : super(
+ "$message",
+ )
+
+ constructor(message: String, cause: Throwable) : super(
+ "$message",
+ cause,
+ )
+
+ constructor(cause: Throwable) : super(cause)
+}
diff --git a/lib/src/main/kotlin/treelib/commonObjects/exceptions/VauleNotFound.kt b/lib/src/main/kotlin/treelib/commonObjects/exceptions/VauleNotFound.kt
new file mode 100644
index 0000000..00efb04
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/commonObjects/exceptions/VauleNotFound.kt
@@ -0,0 +1,18 @@
+package treelib.commonObjects.exceptions
+
+open class VauleNotFound : Exception {
+ constructor() : super(
+ "Value doesn't exist in the tree"
+ )
+
+ constructor(message: String) : super(
+ "Value doesn't exist in the tree $message",
+ )
+
+ constructor(message: String, cause: Throwable) : super(
+ "Value doesn't exist in the tree -> $message",
+ cause,
+ )
+
+ constructor(cause: Throwable) : super(cause)
+}
diff --git a/lib/src/main/kotlin/treelib/databaseSave/DrawableVertex.kt b/lib/src/main/kotlin/treelib/databaseSave/DrawableVertex.kt
new file mode 100644
index 0000000..28b2f52
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/DrawableVertex.kt
@@ -0,0 +1,7 @@
+package treelib.databaseSave
+
+interface DrawableVertex> {
+ val value: Pack
+ val x: Double
+ val y: Double
+}
diff --git a/lib/src/main/kotlin/treelib/databaseSave/jsonFormat/DrawableBINVertex.kt b/lib/src/main/kotlin/treelib/databaseSave/jsonFormat/DrawableBINVertex.kt
new file mode 100644
index 0000000..1b4fb04
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/jsonFormat/DrawableBINVertex.kt
@@ -0,0 +1,11 @@
+package treelib.databaseSave.jsonFormat
+
+import treelib.databaseSave.DrawableVertex
+import treelib.binTree.BINVertex
+
+class DrawableBINVertex>(
+ value: Pack,
+ override val x: Double = 0.0,
+ override val y: Double = 0.0
+) : BINVertex(value), DrawableVertex
+
diff --git a/lib/src/main/kotlin/treelib/databaseSave/jsonFormat/JsonRepository.kt b/lib/src/main/kotlin/treelib/databaseSave/jsonFormat/JsonRepository.kt
new file mode 100644
index 0000000..c602da1
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/jsonFormat/JsonRepository.kt
@@ -0,0 +1,50 @@
+package treelib.databaseSave.jsonFormat
+
+import com.google.gson.GsonBuilder
+import com.google.gson.reflect.TypeToken
+import java.io.File
+
+class JsonRepository(private val dirPath: String) {
+
+ init {
+ File(dirPath).mkdirs()
+ }
+
+ fun > saveChanges(
+ preOrder: Array>,
+ fileName: String
+ ) {
+
+ val gson = GsonBuilder().setPrettyPrinting().create()
+ val json = gson.toJson(preOrder)
+
+ File(dirPath, fileName).run {
+ createNewFile()
+ writeText(json)
+ }
+
+ }
+
+ fun > exportTree(
+ fileName: String,
+ typeToken: TypeToken>>
+ ): Array> {
+ val gson = GsonBuilder().setPrettyPrinting().create()
+ val json = File(dirPath, fileName).readText()
+
+ val preOrder = gson.fromJson>>(json, typeToken.type)
+
+ return preOrder
+ }
+
+ fun removeTree(treeName: String) {
+ File(dirPath, treeName).delete()
+ }
+
+ fun clean() {
+
+ File(dirPath).deleteRecursively()
+ }
+
+
+}
diff --git a/lib/src/main/kotlin/treelib/databaseSave/neo4j/DrawableRBVertex.kt b/lib/src/main/kotlin/treelib/databaseSave/neo4j/DrawableRBVertex.kt
new file mode 100644
index 0000000..285c858
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/neo4j/DrawableRBVertex.kt
@@ -0,0 +1,12 @@
+package treelib.databaseSave.neo4j
+
+import treelib.databaseSave.DrawableVertex
+import treelib.rbTree.Markers
+import treelib.rbTree.RBVertex
+
+class DrawableRBVertex>(
+ value: Pack,
+ color: Markers,
+ override val x: Double = 0.0,
+ override val y: Double = 0.0
+) : RBVertex(value, color), DrawableVertex
diff --git a/lib/src/main/kotlin/treelib/databaseSave/neo4j/Neo4jRepository.kt b/lib/src/main/kotlin/treelib/databaseSave/neo4j/Neo4jRepository.kt
new file mode 100644
index 0000000..4770965
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/neo4j/Neo4jRepository.kt
@@ -0,0 +1,265 @@
+package treelib.databaseSave.neo4j
+
+
+import org.neo4j.driver.AuthTokens
+import org.neo4j.driver.Driver
+import org.neo4j.driver.GraphDatabase
+import org.neo4j.driver.TransactionContext
+import org.neo4j.driver.exceptions.SessionExpiredException
+import treelib.commonObjects.Container
+import treelib.rbTree.Markers
+import java.io.Closeable
+import java.io.IOException
+import java.util.LinkedList
+import kotlin.collections.HashSet
+
+
+class Neo4jRepository : Closeable {
+
+ private var driver: Driver? = null
+
+ fun open(uri: String, username: String, password: String) {
+ try {
+ driver = GraphDatabase.driver(uri, AuthTokens.basic(username, password))
+ } catch (ex: IllegalArgumentException) {
+ throw IOException()
+ } catch (ex: SessionExpiredException) {
+ throw IOException()
+ }
+ }
+
+ fun > saveChanges(
+ preOrder: Array>,
+ inOrder: Array>,
+ treeName: String
+ ) {
+
+ val session = driver?.session() ?: throw IOException()
+
+ var inOrderIndex = 0
+ var preOrderIndex = 0
+ val set = HashSet>()
+ val stack = LinkedList>()
+ var id = 0
+
+ while (preOrderIndex in preOrder.indices) {
+ do {
+ val currentNode = preOrder[preOrderIndex]
+ if (preOrderIndex == 0) {
+ session.executeWrite { tx ->
+ //cleanDB(tx)
+ createRoot(tx, currentNode, id, treeName)
+ }
+ ++id
+ }
+ if (!stack.isEmpty()) {
+ if (set.contains(stack.peek())) {
+ set.remove(stack.peek())
+ val parentNode = stack.pop()
+ session.executeWrite { tx ->
+ createRightSon(tx, parentNode, currentNode, id, treeName)
+ }
+ ++id
+ } else {
+ val parentNode = stack.peek()
+ parentNode.value as Container<*, *>
+ session.executeWrite { tx ->
+ createLeftSon(tx, parentNode, currentNode, id, treeName)
+ }
+ ++id
+ }
+ }
+ stack.push(currentNode)
+ } while (preOrder[preOrderIndex++].value != inOrder[inOrderIndex].value && preOrderIndex < preOrder.size)
+
+ var currentNode: DrawableRBVertex? = null
+
+ while (!stack.isEmpty() && inOrderIndex < inOrder.size && stack.peek().value == inOrder[inOrderIndex].value) {
+ currentNode = stack.pop()
+ ++inOrderIndex
+ }
+
+ if (currentNode != null) {
+ set.add(currentNode)
+ stack.push(currentNode)
+ }
+
+ }
+
+ session.close()
+ }
+
+ fun exportRBtree(treeName: String): Pair>>, List>>> {
+
+ val session = driver?.session() ?: throw IOException()
+ var preOrder: List>> = listOf()
+ var inOrder: List>> = listOf()
+
+ session.executeRead { tx ->
+ preOrder = tx.run(
+ "MATCH (node: Node {treeName: \$name}) " +
+ "RETURN node.value, node.key, node.color, node.x, node.y " +
+ "ORDER BY node.id",
+ mutableMapOf("name" to treeName) as Map?
+ ).list()
+ .map {
+ DrawableRBVertex(
+ value = Container(Pair(it.values()[1].toString().toInt(), it.values()[0].toString().replace("\"", ""))),
+ color = if (it.values()[2].toString().replace("\"", "") == "RED") Markers.RED else Markers.BLACK,
+ x = it.values()[3].toString().toDouble(),
+ y = it.values()[4].toString().toDouble()
+ )
+ }
+
+ inOrder = tx.run(
+ "MATCH (node: Node {treeName: \$name}) " +
+ "RETURN node.value, node.key, node.color, node.x, node.y " +
+ "ORDER BY node.key",
+ mutableMapOf("name" to treeName) as Map?
+ ).list()
+ .map {
+ DrawableRBVertex(
+ value = Container(Pair(it.values()[1].toString().toInt(), it.values()[0].toString())),
+ color = if (it.values()[2].toString() == """RED""") Markers.RED else Markers.BLACK,
+ x = it.values()[3].toString().toDouble(),
+ y = it.values()[4].toString().toDouble()
+ )
+ }
+ }
+
+ session.close()
+
+ return Pair(preOrder, inOrder)
+
+ }
+
+ fun removeTree(treeName: String) {
+
+ val session = driver?.session() ?: throw IOException()
+
+ session.executeWrite { tx ->
+ tx.run(
+ "MATCH (n: Node {treeName: \$name}) DETACH DELETE n",
+ mutableMapOf("name" to treeName) as Map?
+ )
+ }
+
+ }
+
+ fun findNamesTrees(): List {
+ val session = driver?.session() ?: throw IOException()
+ var treesNames: List = listOf()
+ session.executeRead { tx ->
+ treesNames = tx.run("MATCH (n: Node) WHERE NOT(:Node)-->(n) RETURN n.treeName")
+ .list().map { it.values()[0].toString().replace("\"", "") }.filter { it != "null" }
+ }
+
+ return treesNames
+ }
+
+ fun clean() {
+ val session = driver?.session() ?: throw IOException()
+
+ session.executeWrite { tx ->
+ tx.run("MATCH (n: Node) DETACH DELETE n")
+ }
+ }
+
+ private fun > createRoot(
+ tx: TransactionContext,
+ rootNode: DrawableRBVertex,
+ id: Int,
+ treeName: String
+ ) {
+ rootNode.value as Container<*, *>
+ tx.run(
+ "MERGE(:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY, id: \$nodeID, treeName: \$name})",
+ mutableMapOf(
+ "nodeValue" to rootNode.value.pair.second,
+ "nodeKey" to rootNode.value.pair.first,
+ "nodeColor" to rootNode.color.toString(),
+ "nodeX" to rootNode.x,
+ "nodeY" to rootNode.y,
+ "nodeID" to id,
+ "name" to treeName
+ )
+ )
+ }
+
+ private fun > createRightSon(
+ tx: TransactionContext, parentNode: DrawableRBVertex,
+ currentNode: DrawableRBVertex, id: Int, treeName: String
+ ) {
+ parentNode.value as Container<*, *>
+ currentNode.value as Container<*, *>
+ tx.run(
+ "MERGE(parent:Node {value: \$parentNodeValue, key: \$parentNodeKey, " +
+ "color: \$parentNodeColor, x: \$parentNodeX, y: \$parentNodeY, treeName: \$name}) " +
+ "MERGE(son:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY, id: \$nodeID, treeName: \$name}) " +
+ "MERGE (parent)-[:RIGHT_SON]->(son)",
+ mutableMapOf(
+ "parentNodeValue" to parentNode.value.pair.second,
+ "parentNodeKey" to parentNode.value.pair.first,
+ "parentNodeColor" to parentNode.color.toString(),
+ "parentNodeX" to parentNode.x,
+ "parentNodeY" to parentNode.y,
+ "nodeValue" to currentNode.value.pair.second,
+ "nodeKey" to currentNode.value.pair.first,
+ "nodeColor" to currentNode.color.toString(),
+ "nodeX" to currentNode.x,
+ "nodeY" to currentNode.y,
+ "nodeID" to id,
+ "name" to treeName
+ )
+ )
+ }
+
+ private fun > createLeftSon(
+ tx: TransactionContext, parentNode: DrawableRBVertex,
+ currentNode: DrawableRBVertex, id: Int, treeName: String
+ ) {
+ parentNode.value as Container<*, *>
+ currentNode.value as Container<*, *>
+ tx.run(
+ "MERGE(parent:Node {value: \$parentNodeValue, key: \$parentNodeKey, " +
+ "color: \$parentNodeColor, x: \$parentNodeX, y: \$parentNodeY, treeName: \$name}) " +
+ "MERGE(son:Node {value: \$nodeValue, key: \$nodeKey, color: \$nodeColor, x: \$nodeX, y: \$nodeY, id: \$nodeID, treeName: \$name}) " +
+ "MERGE (parent)-[:LEFT_SON]->(son)",
+ mutableMapOf(
+ "parentNodeValue" to parentNode.value.pair.second,
+ "parentNodeKey" to parentNode.value.pair.first,
+ "parentNodeColor" to parentNode.color.toString(),
+ "parentNodeX" to parentNode.x,
+ "parentNodeY" to parentNode.y,
+ "nodeValue" to currentNode.value.pair.second,
+ "nodeKey" to currentNode.value.pair.first,
+ "nodeColor" to currentNode.color.toString(),
+ "nodeX" to currentNode.x,
+ "nodeY" to currentNode.y,
+ "nodeID" to id,
+ "name" to treeName
+ )
+ )
+ }
+
+ fun findTree(treeName: String): Boolean {
+ val session = driver?.session() ?: throw IOException()
+ var name = ""
+
+ session.executeRead { tx ->
+ name = tx.run(
+ "MATCH (n: Node {treeName: \$treeName}) WHERE NOT (:Node)-->(n) RETURN n.treeName",
+ mutableMapOf(
+ "treeName" to treeName
+ ) as Map?
+ ).list().singleOrNull()?.values()?.get(0)?.toString()?.replace("\"", "") ?: ""
+
+ }
+ return name == treeName
+
+ }
+
+ override fun close() {
+ driver?.close()
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/databaseSave/sqlite/DrawableAVLVertex.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/DrawableAVLVertex.kt
new file mode 100644
index 0000000..ef62b62
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/DrawableAVLVertex.kt
@@ -0,0 +1,10 @@
+package treelib.databaseSave.sqlite
+
+import treelib.databaseSave.DrawableVertex
+
+class DrawableAVLVertex>(
+ override val value: Pack,
+ override val x: Double = 0.0,
+ override val y: Double = 0.0,
+ val height: UInt,
+) : DrawableVertex
diff --git a/lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryExposed.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryExposed.kt
new file mode 100644
index 0000000..ad2c32b
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryExposed.kt
@@ -0,0 +1,121 @@
+package treelib.databaseSave.sqlite
+
+import treelib.databaseSave.sqlite.treeEntities.TreeTableEntity
+import treelib.databaseSave.sqlite.treeEntities.TreesTable
+import treelib.databaseSave.sqlite.vertexEntities.VertexTable
+import treelib.databaseSave.sqlite.vertexEntities.VertexTableEntity
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
+import org.jetbrains.exposed.sql.transactions.transaction
+import java.io.File
+import java.sql.SQLException
+
+class SQLiteRepositoryExposed {
+ private var db: Database? = null
+ var dbName: String? = null
+ private set
+
+ fun initDataBase(name: String) {
+ if ((dbName != null) && (dbName == name)) return
+
+ File(System.getProperty("user.dir") + "/sqliteDB").mkdirs()
+
+ db = Database.connect("jdbc:sqlite:sqliteDB/$name", driver = "org.sqlite.JDBC")
+
+ if (!isEmptyDB()) createTables()
+ }
+
+
+ private fun createTables() {
+ transaction(db) {
+ when {
+ !TreesTable.exists() && !VertexTable.exists() -> {
+ SchemaUtils.create(TreesTable)
+ SchemaUtils.create(VertexTable)
+ }
+
+ !TreesTable.exists() -> SchemaUtils.create(TreesTable)
+ !VertexTable.exists() -> SchemaUtils.create(VertexTable)
+ else -> {}
+ }
+ }
+ }
+
+ private fun interIsTreeExist(name: String): Int? {
+ val id = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return null
+ return id.value
+ }
+
+ private fun interDelete(name: String) {
+ val treeId = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return
+ TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.delete()
+
+ VertexTableEntity.find(VertexTable.tree eq treeId.value).firstOrNull() ?: return
+ VertexTable.deleteWhere { tree eq treeId.value }
+ return
+ }
+
+ fun > saveTree(
+ treeName: String,
+ vertexes: MutableList>,
+ serializeData: (input: Pack) -> String,
+ ): Unit = transaction(db) {
+ interDelete(treeName)
+
+ val id = TreeTableEntity.new {
+ name = treeName
+ }
+
+ for (index in vertexes.indices) VertexTableEntity.new {
+ height = vertexes[index].height.toInt()
+ value = serializeData(vertexes[index].value)
+ order = index
+ x = vertexes[index].x
+ y = vertexes[index].y
+ tree = id.id.value
+ }
+ }
+
+ fun getAllSavedTrees(): List = transaction(db) {
+ return@transaction TreeTableEntity.all().map { el -> el.name }
+ }
+
+ fun > getTree(
+ name: String,
+ deSerializeData: (input: String) -> Pack,
+ ): MutableList> = transaction(db) {
+ val ans = mutableListOf>()
+ val treeId = interIsTreeExist(name) ?: throw SQLException("Tree doesn't exist")
+
+ for (el in VertexTableEntity.find(VertexTable.tree eq treeId).orderBy(VertexTable.order to SortOrder.ASC)){
+ ans.add(
+ DrawableAVLVertex(
+ value = deSerializeData(el.value),
+ height = el.height.toUInt(),
+ x = el.x,
+ y = el.y
+ )
+ )
+ }
+ return@transaction ans
+ }
+
+ fun deleteTree(name: String): Boolean =
+ transaction(db) {
+ val treeId = TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.id ?: return@transaction false
+ TreeTableEntity.find(TreesTable.name eq name).firstOrNull()?.delete()
+
+ VertexTableEntity.find(VertexTable.tree eq treeId.value).firstOrNull() ?: return@transaction false
+ VertexTable.deleteWhere { tree eq treeId.value }
+ return@transaction true
+ }
+
+ fun isEmptyDB(): Boolean = transaction(db) {
+ if (TreesTable.exists() && VertexTable.exists()) return@transaction true
+ else false
+ }
+
+ fun isTreeExist(name: String): Boolean = transaction(db) {
+ return@transaction TreeTableEntity.find(TreesTable.name eq name).firstOrNull() != null
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryJDBC.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryJDBC.kt
new file mode 100644
index 0000000..29d5c9d
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/SQLiteRepositoryJDBC.kt
@@ -0,0 +1,227 @@
+package treelib.databaseSave.sqlite
+
+import java.io.Closeable
+import java.sql.DriverManager
+import java.sql.SQLException
+
+class SQLiteRepositoryJDBC>(
+ private val dbPath: String,
+ private val serializeData: (input: Pack) -> String,
+ private val deSerializeData: (input: String) -> Pack,
+ private val logErrorMethod: (input: Exception) -> Unit = { throw it },
+ private val logInfoMethod: (input: String) -> Unit = { /* Nothing to do */ },
+) : Closeable {
+
+ private val treeTable = "AVLTreesTable"
+ private val avlTreeName = "name"
+
+ private val value = "value"
+ private val height = "height"
+ private val xCord = "x"
+ private val yCord = "y"
+
+ private val dbDriver = "jdbc:sqlite"
+ private val connection = DriverManager.getConnection("$dbDriver:$dbPath")
+ ?: throw SQLException("Cannot connect to database")
+
+ init {
+ createTreeTable()
+ }
+
+ fun createTreeTable() {
+ connection.createStatement().also { stmt ->
+ try {
+ stmt.execute("CREATE TABLE if not exists $treeTable(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name text);")
+ logInfoMethod("Table with trees created or already exists")
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ } finally {
+ stmt.close()
+ }
+ }
+ }
+
+ fun clearTreeTable() {
+ connection.createStatement().also { stmt ->
+ try {
+ stmt.execute("DELETE FROM $treeTable;")
+ logInfoMethod("TreeTable: $treeTable has been deleted")
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ } finally {
+ stmt.close()
+ }
+ }
+ }
+
+ fun addTree(treeName: String) {
+ val isInDB = isNameInDB(treeName, avlTreeName, treeTable)
+
+ if (isInDB) {
+ logInfoMethod("Tree - $treeName, have been exist yet in treeTable - $treeTable")
+ return
+ }
+
+ connection.createStatement().also { stmt ->
+ try {
+ stmt.execute("CREATE TABLE if not exists $treeName(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, $value text, $height INT, $xCord DOUBLE, $yCord DOUBLE);")
+ stmt.execute("INSERT INTO $treeTable ($avlTreeName) VALUES ('$treeName');")
+ logInfoMethod("Was created Tree: $treeName in table: $treeTable")
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ } finally {
+ stmt.close()
+ }
+ }
+ }
+
+ fun getTreeNames(): MutableList {
+ val info = mutableListOf()
+ connection.createStatement().also { stmt ->
+ try {
+ val result = stmt.executeQuery("SELECT $treeTable.$avlTreeName as $avlTreeName FROM $treeTable;")
+ while (result.next()) {
+ info.add(result.getString(avlTreeName))
+ }
+ logInfoMethod("Available tree is given")
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ } finally {
+ stmt.close()
+ }
+ return info
+ }
+ }
+
+ fun deleteTree(treeName: String) {
+ val deleteId = getTreeId(treeName)
+ if (deleteId == 0) return
+
+ connection.createStatement().also { stmt ->
+ try {
+ stmt.execute("DROP TABLE $treeName;")
+ stmt.execute("DELETE FROM $treeTable WHERE id=$deleteId;")
+ logInfoMethod("Tree: $treeName has been deleted")
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ } finally {
+ stmt.close()
+ }
+ }
+ }
+
+ fun getTreeId(treeName: String): Int {
+ var id: Int? = null
+ try {
+ val statement = connection.prepareStatement("SELECT id FROM $treeTable WHERE name=?;")
+ statement.setString(1, treeName)
+ id = statement.executeQuery().getInt(1)
+ statement.close()
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ }
+ if (id != null) return id
+ else throw SQLException("Impossible case")
+ }
+
+ fun addVertex(avlDVertex: DrawableAVLVertex, treeName: String) {
+ val isInDB = getVertexId(avlDVertex, treeName)
+ if (isInDB != 0) {
+ deleteVertex(isInDB, treeName)
+ logInfoMethod("Attempt write duplicate of the vertex: value = ${avlDVertex.value}; hieght = ${avlDVertex.height}; x = ${avlDVertex.x}; y = ${avlDVertex.y}")
+ }
+
+ try {
+ val stmt = connection.prepareStatement("INSERT INTO $treeName (value, height, x, y) VALUES (?, ?, ?, ?);")
+ val info = serializeData(avlDVertex.value)
+ stmt.setString(1, info)
+ stmt.setInt(2, avlDVertex.height.toInt())
+ stmt.setDouble(3, avlDVertex.x)
+ stmt.setDouble(4, avlDVertex.y)
+ stmt.execute()
+ stmt.close()
+ logInfoMethod("Vertex: value = $info has been saved")
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ }
+
+ }
+
+ fun addVertexes(list: MutableList>, treeName: String) {
+ for (el in list) addVertex(el, treeName)
+ }
+
+ fun deleteVertex(id: Int, treeName: String) {
+ try {
+ val stmt = connection.prepareStatement("DELETE FROM $treeName WHERE id=?;")
+ stmt.setInt(1, id)
+ stmt.execute()
+ stmt.close()
+ logInfoMethod("Element: id = $id has been deleted in table: $treeName")
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ }
+ }
+
+ fun getAllVertexes(treeName: String): MutableList> {
+ val info = mutableListOf>()
+ connection.createStatement().also { stmt ->
+ try {
+ val result =
+ stmt.executeQuery("SELECT $treeName.$value as $value, $treeName.$height as $height, $treeName.$xCord as $xCord, $treeName.$yCord as $yCord FROM $treeName;")
+ while (result.next()) {
+ info.add(
+ DrawableAVLVertex(
+ value = deSerializeData(result.getString(value)),
+ height = result.getInt(height).toUInt(),
+ x = result.getDouble(xCord),
+ y = result.getDouble(yCord),
+ )
+ )
+ }
+ logInfoMethod("Vertexes from $treeName have been received")
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ } finally {
+ stmt.close()
+ }
+ return info
+ }
+ }
+
+ private fun getVertexId(vertex: DrawableAVLVertex, tableName: String): Int {
+ var id: Int? = null
+ try {
+ val stmt =
+ connection.prepareStatement("SELECT id FROM $tableName WHERE value=? AND height=? AND x=? AND y=?;")
+ stmt.setString(1, serializeData(vertex.value))
+ stmt.setInt(2, vertex.height.toInt())
+ stmt.setDouble(3, vertex.x)
+ stmt.setDouble(4, vertex.y)
+ id = stmt.executeQuery().getInt(1)
+ stmt.close()
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ }
+ if (id != null) return id
+ else throw SQLException("Impossible case")
+ }
+
+ private fun isNameInDB(rowName: String, columnName: String, tableName: String): Boolean {
+ var isInDB: Boolean? = null
+ try {
+ val statement = connection.prepareStatement("SELECT EXISTS(SELECT 1 FROM $tableName WHERE $columnName=?);")
+ statement.setString(1, rowName)
+ isInDB = statement.executeQuery().getBoolean(1)
+ statement.close()
+ } catch (ex: SQLException) {
+ logErrorMethod(ex)
+ }
+ if (isInDB != null) return isInDB
+ else throw SQLException("Impossible case")
+ }
+
+ override fun close() {
+ connection.close()
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreeTableEntity.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreeTableEntity.kt
new file mode 100644
index 0000000..dcb96b2
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreeTableEntity.kt
@@ -0,0 +1,11 @@
+package treelib.databaseSave.sqlite.treeEntities
+
+import org.jetbrains.exposed.dao.IntEntity
+import org.jetbrains.exposed.dao.IntEntityClass
+import org.jetbrains.exposed.dao.id.EntityID
+
+class TreeTableEntity(id: EntityID) : IntEntity(id) {
+ companion object : IntEntityClass(TreesTable)
+
+ var name by TreesTable.name
+}
diff --git a/lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreesTable.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreesTable.kt
new file mode 100644
index 0000000..32daf7d
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/treeEntities/TreesTable.kt
@@ -0,0 +1,13 @@
+package treelib.databaseSave.sqlite.treeEntities
+
+import org.jetbrains.exposed.dao.id.IntIdTable
+
+
+object TreesTable : IntIdTable("trees") {
+
+ var name = text("name")
+
+ init {
+ uniqueIndex(name)
+ }
+}
diff --git a/lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTable.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTable.kt
new file mode 100644
index 0000000..2607412
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTable.kt
@@ -0,0 +1,13 @@
+package treelib.databaseSave.sqlite.vertexEntities
+
+import org.jetbrains.exposed.dao.id.IntIdTable
+
+
+object VertexTable : IntIdTable("vertex") {
+ var height = integer("height")
+ var value = text("data")
+ var order = integer("orderId")
+ var x = double("xCord")
+ var y = double("yCord")
+ var tree = integer("treeId")
+}
diff --git a/lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt b/lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt
new file mode 100644
index 0000000..3b8461f
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/databaseSave/sqlite/vertexEntities/VertexTableEntity.kt
@@ -0,0 +1,16 @@
+package treelib.databaseSave.sqlite.vertexEntities
+
+import org.jetbrains.exposed.dao.IntEntity
+import org.jetbrains.exposed.dao.IntEntityClass
+import org.jetbrains.exposed.dao.id.EntityID
+
+class VertexTableEntity(id: EntityID) : IntEntity(id) {
+ companion object : IntEntityClass(VertexTable)
+
+ var height by VertexTable.height
+ var value by VertexTable.value
+ var order by VertexTable.order
+ var x by VertexTable.x
+ var y by VertexTable.y
+ var tree by VertexTable.tree
+}
diff --git a/lib/src/main/kotlin/treelib/rbTree/Markers.kt b/lib/src/main/kotlin/treelib/rbTree/Markers.kt
new file mode 100644
index 0000000..7ae37fc
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/rbTree/Markers.kt
@@ -0,0 +1,5 @@
+package treelib.rbTree
+
+enum class Markers {
+ RED, BLACK
+}
diff --git a/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt
new file mode 100644
index 0000000..6c20f2f
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/rbTree/RBBalancer.kt
@@ -0,0 +1,344 @@
+package treelib.rbTree
+
+import treelib.abstractTree.balanced.BalancerParent
+
+
+class RBBalancer>(var root: RBNode?) :
+ BalancerParent, RBStateContainer>() {
+
+ init {
+ root?.color = Markers.BLACK
+ }
+
+ private fun getUncle(node: RBNode): RBNode? {
+ val parent = node.parent
+ return when (parent?.parent?.left) {
+ parent -> parent?.parent?.right
+ else -> parent?.parent?.left
+ }
+ }
+
+ private fun getBrother(parent: RBNode?, node: RBNode?): RBNode? {
+ return if (parent?.right == node)
+ parent?.left
+ else
+ parent?.right
+ }
+
+ private fun getRoot(node: RBNode): RBNode {
+ var currentNode = node
+ while (currentNode.parent != null)
+ currentNode = currentNode.parent ?: throw NullPointerException()
+ root = currentNode
+ root?.color = Markers.BLACK
+ return currentNode
+ }
+
+ private fun nodeIsLeaf(node: RBNode?): Boolean {
+ if (node == null)
+ throw NullPointerException()
+ return node.right == null && node.left == null
+ }
+
+ override fun balance(state: RBStateContainer): RBNode {
+ val node = state.contentNode
+ ?: throw InternalError()
+ val uncle = getUncle(node)
+ when {
+ /** node insertion case **/
+ node.color == Markers.RED && node.right == null && node.left == null -> {
+ var currentNode = node
+
+ if (currentNode.parent?.color == Markers.RED && uncle?.color == Markers.RED) {
+ currentNode = afterInsert(node)
+ }
+ if (currentNode.parent?.color != Markers.RED) {
+ return getRoot(currentNode)
+ }
+
+ var parent =
+ currentNode.parent ?: throw IllegalStateException()
+ when (parent) {
+ parent.parent?.left -> {
+ if (currentNode == parent.right) {
+ leftRotate(parent)
+ currentNode = parent
+ }
+ parent =
+ currentNode.parent?.parent ?: throw InternalError()
+ currentNode = rightRotate(parent)
+ currentNode.color = Markers.BLACK
+ currentNode.right?.color = Markers.RED
+ currentNode.left?.color = Markers.RED
+ }
+
+ parent.parent?.right -> {
+ if (currentNode == parent.left) {
+ rightRotate(parent)
+ currentNode = parent
+ }
+ parent =
+ currentNode.parent?.parent ?: throw InternalError()
+ currentNode = leftRotate(parent)
+ currentNode.color = Markers.BLACK
+ currentNode.right?.color = Markers.RED
+ currentNode.left?.color = Markers.RED
+ }
+
+ else -> throw IllegalStateException()
+ }
+ if (currentNode.parent == null)
+ root = currentNode
+ return root ?: throw NullPointerException()
+ }
+ /** node removal cases **/
+ node.color == Markers.RED && (node.right != null || node.left != null) -> {
+ when {
+ /** black leaf removal case **/
+ node.right?.color == Markers.BLACK -> {
+ return firstCase(node, node.left)
+ }
+
+ node.left?.color == Markers.BLACK -> {
+ return firstCase(node, node.right)
+ }
+ }
+
+ }
+
+ node.color == Markers.BLACK -> {
+ return when {
+ /** red leaf removal case **/
+ (node.left == null && node.right == null) ||
+ (node.left == null && node.right?.color == Markers.RED && nodeIsLeaf(node.right)) ||
+ (node.right == null && node.left?.color == Markers.RED && nodeIsLeaf(node.left)) -> {
+ getRoot(node)
+ }
+ /** black leaf removal case **/
+ node.left == null || node.right == null -> {
+ firstCase(node, null)
+
+ }
+
+ node.left?.color == Markers.RED && node.right?.color == Markers.RED -> {
+ if (nodeIsLeaf(node.right)) {
+ node.right?.color = Markers.BLACK
+ }
+ else {
+ node.left?.color = Markers.BLACK
+ }
+ getRoot(node)
+ }
+
+ node.left?.color == Markers.RED -> {
+ if (node.right?.right?.color != Markers.BLACK && node.right?.left?.color != Markers.BLACK) {
+ node.left?.color = Markers.BLACK
+ }
+ getRoot(node)
+ }
+
+ node.right?.color == Markers.RED -> {
+ if (node.left?.right?.color != Markers.BLACK && node.left?.left?.color != Markers.BLACK) {
+ node.right?.color = Markers.BLACK
+ }
+ getRoot(node)
+ }
+
+ else -> throw IllegalStateException()
+ }
+ }
+ }
+ throw IllegalStateException()
+ }
+
+ private fun afterInsert(node: RBNode): RBNode {
+ var currentNode = node
+ while (currentNode.parent?.color == Markers.RED) {
+ val uncle = getUncle(currentNode)
+ if (uncle?.color == Markers.RED) {
+ currentNode.parent?.color = Markers.BLACK
+ currentNode = currentNode.parent?.parent ?: throw InternalError()
+ currentNode.color = Markers.RED
+ uncle.color = Markers.BLACK
+ } else if (uncle != null) {
+ return currentNode
+ }
+ }
+ root?.color = Markers.BLACK
+ return currentNode
+ }
+
+ /** black node removal case **/
+ private fun firstCase(parent: RBNode?, node: RBNode?): RBNode {
+ return when {
+ parent == null && node == null -> throw NullPointerException()
+ parent != null -> {
+ when (parent.color) {
+ Markers.RED -> secondCase(parent, node)
+ Markers.BLACK -> thirdCase(parent, node)
+ }
+ getRoot(parent)
+ }
+
+ else -> getRoot(node ?: throw InternalError())
+ }
+ }
+
+ /** parent is red **/
+ private fun secondCase(parent: RBNode, node: RBNode?) {
+ var brother = getBrother(parent, node) ?: throw InternalError()
+ if (brother.color == Markers.RED)
+ throw NullPointerException()
+
+ if (brother.right?.color != Markers.RED && brother.left?.color != Markers.RED) {
+ brother.color = Markers.RED
+ parent.color = Markers.BLACK
+ return
+ }
+
+ when (node) {
+ parent.left -> {
+ if (brother.right?.color == Markers.RED) {
+ leftRotate(parent)
+ brother.left?.color = Markers.RED
+ brother.right?.color = Markers.RED
+ brother.color = Markers.BLACK
+ } else if (brother.left?.color == Markers.RED) {
+ brother = rightRotate(brother)
+ leftRotate(parent)
+ brother.left?.color = Markers.BLACK
+ brother.left?.color = Markers.BLACK
+ brother.color = Markers.RED
+ } else {
+ throw IllegalStateException()
+ }
+ }
+
+ parent.right -> {
+ if (brother.left?.color == Markers.RED) {
+ rightRotate(parent)
+ brother.color = Markers.BLACK
+ brother.left?.color = Markers.RED
+ brother.right?.color = Markers.RED
+ } else if (brother.right?.color == Markers.RED) {
+ brother = leftRotate(brother)
+ rightRotate(parent)
+ brother.color = Markers.RED
+ brother.left?.color = Markers.BLACK
+ brother.right?.color = Markers.BLACK
+ } else {
+ throw IllegalStateException()
+ }
+
+ }
+
+ else -> throw IllegalStateException()
+ }
+ }
+
+ /** parent is black **/
+ private fun thirdCase(parent: RBNode, node: RBNode?) {
+ val brother = getBrother(parent, node) ?: throw InternalError()
+ when (brother.color) {
+ Markers.RED -> thirdCaseSubFirst(brother, parent)
+ Markers.BLACK -> thirdCaseSubSecond(brother, parent)
+ }
+ }
+
+ /** black parent and red brother **/
+ private fun thirdCaseSubFirst(brother: RBNode, parent: RBNode) {
+ when (brother) {
+ brother.parent?.left -> {
+ var rightBrotherSon = brother.right ?: throw InternalError()
+
+ if (rightBrotherSon.right?.color != Markers.RED && rightBrotherSon.left?.color != Markers.RED) {
+ rightBrotherSon.color = Markers.RED
+ brother.color = Markers.BLACK
+ rightRotate(parent)
+ return
+ }
+ /** if the right son of the right son of the brother is red, then we make it the right son
+ * of the brother, and the right son of the brother is the left son of the new right son
+ * of the brother and repaint it red so that the following condition works **/
+
+ if (rightBrotherSon.right?.color == Markers.RED) {
+ rightBrotherSon.color = Markers.RED
+ leftRotate(rightBrotherSon)
+
+ rightBrotherSon =
+ rightBrotherSon.parent ?: throw InternalError()
+ rightBrotherSon.color = Markers.BLACK
+ }
+
+ if (rightBrotherSon.left?.color == Markers.RED) {
+ rightBrotherSon.left?.color = Markers.BLACK
+ leftRotate(brother)
+ rightRotate(parent)
+ }
+ }
+
+ brother.parent?.right -> {
+ var leftBrotherSon = brother.left ?: throw NullPointerException()
+ if (leftBrotherSon.right?.color != Markers.RED && leftBrotherSon.left?.color != Markers.RED) {
+ leftBrotherSon.color = Markers.RED
+ brother.color = Markers.BLACK
+ leftRotate(brother.parent ?: throw InternalError())
+ return
+ }
+
+ if (leftBrotherSon.left?.color == Markers.RED) {
+ rightRotate(leftBrotherSon)
+ leftBrotherSon.color = Markers.RED
+ leftBrotherSon =
+ leftBrotherSon.parent ?: throw InternalError()
+ leftBrotherSon.color = Markers.BLACK
+ }
+
+ if (leftBrotherSon.right?.color == Markers.RED) {
+ leftBrotherSon.right?.color = Markers.BLACK
+ rightRotate(brother)
+ leftRotate(parent)
+ }
+ }
+
+ else -> throw IllegalStateException()
+ }
+ }
+
+ /** black parent and black brother **/
+ private fun thirdCaseSubSecond(brother: RBNode, parent: RBNode) {
+ /** if the brother hasn't read children, restart
+ * from the parent (the height in the subtree has decreased by 1) **/
+
+ if (brother.left?.color != Markers.RED && brother.right?.color != Markers.RED) {
+ brother.color = Markers.RED
+ firstCase(parent.parent, parent)
+ return
+ }
+ when {
+ brother.left?.color == Markers.RED -> {
+ brother.left?.color = Markers.BLACK
+ if (brother == parent.left) {
+ rightRotate(parent)
+ } else {
+ rightRotate(brother)
+ leftRotate(parent)
+ }
+ }
+
+ brother.right?.color == Markers.RED -> {
+ brother.right?.color = Markers.BLACK
+ if (brother == parent.right) {
+ leftRotate(parent)
+ } else {
+ leftRotate(brother)
+ rightRotate(parent)
+ }
+
+ }
+
+ else -> throw IllegalStateException()
+ }
+ }
+
+}
diff --git a/lib/src/main/kotlin/treelib/rbTree/RBNode.kt b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt
new file mode 100644
index 0000000..f62abe2
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/rbTree/RBNode.kt
@@ -0,0 +1,11 @@
+package treelib.rbTree
+
+import treelib.abstractTree.NodeParent
+
+class RBNode>(
+ override var value: Pack,
+ override var left: RBNode? = null,
+ override var right: RBNode? = null,
+ override var parent: RBNode? = null,
+ var color: Markers = Markers.RED,
+) : NodeParent>
diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt
new file mode 100644
index 0000000..18e01e4
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/rbTree/RBStateContainer.kt
@@ -0,0 +1,7 @@
+package treelib.rbTree
+
+import treelib.abstractTree.StateContainer
+
+class RBStateContainer>(
+ override val contentNode: RBNode?,
+) : StateContainer>
diff --git a/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt
new file mode 100644
index 0000000..090b5d1
--- /dev/null
+++ b/lib/src/main/kotlin/treelib/rbTree/RBStruct.kt
@@ -0,0 +1,126 @@
+package treelib.rbTree
+
+import treelib.abstractTree.balanced.BalancedTreeStruct
+import treelib.commonObjects.exceptions.ImpossibleCaseException
+import java.util.*
+
+class RBStruct> :
+ BalancedTreeStruct, RBStateContainer, RBVertex, RBBalancer