From fa96eaea704d760e433c29bf983681b20a035f2d Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 17:35:28 +0000 Subject: [PATCH 001/164] Setting up GitHub Classroom Feedback From a7712c858590323b88727f113744cff6a8cc66b1 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Wed, 13 Mar 2024 21:08:36 +0300 Subject: [PATCH 002/164] chore: add gitignore file for different OS's and projects languages --- .gitignore | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ab266e --- /dev/null +++ b/.gitignore @@ -0,0 +1,219 @@ +# Created by https://www.toptal.com/developers/gitignore/api/intellij+all,macos,windows,linux,gradle,kotlin +# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all,macos,windows,linux,gradle,kotlin + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Kotlin ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### Gradle Patch ### +# Java heap dump +*.hprof + +# End of https://www.toptal.com/developers/gitignore/api/intellij+all,macos,windows,linux,gradle,kotlin From ef7b5055b8dc10218eb6bdd33cb70de5060512e2 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Wed, 13 Mar 2024 21:59:10 +0300 Subject: [PATCH 003/164] chore: add MIT authored license --- LICENSE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..de42fde --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) 2023-present Gavrilenko Mike, Shakirov Karim, Vlasenko Danil + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. From 7d734b9755f4bc8586361f76fd539a7e0d7b8640 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Wed, 13 Mar 2024 22:05:10 +0300 Subject: [PATCH 004/164] feat: set up the lib structure and gradle project --- .gitattributes | 9 + gradle/libs.versions.toml | 15 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 ++++++++++++++++++ gradlew.bat | 92 +++++++ lib/build.gradle.kts | 47 ++++ .../test/kotlin/org/example/LibraryTest.kt | 14 + settings.gradle.kts | 14 + 9 files changed, 447 insertions(+) create mode 100644 .gitattributes create mode 100644 gradle/libs.versions.toml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 lib/build.gradle.kts create mode 100644 lib/src/test/kotlin/org/example/LibraryTest.kt create mode 100644 settings.gradle.kts diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..097f9f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..d4d7e1f --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,15 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +commons-math3 = "3.6.1" +guava = "32.1.3-jre" +junit-jupiter-engine = "5.10.0" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit-jupiter-engine" } + +[plugins] +jvm = { id = "org.jetbrains.kotlin.jvm", version = "1.9.20" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a80b22c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..1aa94a4 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..25da30d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts new file mode 100644 index 0000000..f6cad6d --- /dev/null +++ b/lib/build.gradle.kts @@ -0,0 +1,47 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Kotlin library project to get you started. + * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/8.6/userguide/building_java_projects.html in the Gradle documentation. + */ + +plugins { + // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. + alias(libs.plugins.jvm) + + // Apply the java-library plugin for API and implementation separation. + `java-library` +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use the Kotlin JUnit 5 integration. + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + + // Use the JUnit 5 integration. + testImplementation(libs.junit.jupiter.engine) + + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + + // This dependency is exported to consumers, that is to say found on their compile classpath. + api(libs.commons.math3) + + // This dependency is used internally, and not exposed to consumers on their own compile classpath. + implementation(libs.guava) +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +tasks.named("test") { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/lib/src/test/kotlin/org/example/LibraryTest.kt b/lib/src/test/kotlin/org/example/LibraryTest.kt new file mode 100644 index 0000000..f6fa692 --- /dev/null +++ b/lib/src/test/kotlin/org/example/LibraryTest.kt @@ -0,0 +1,14 @@ +/* + * This Kotlin source file was generated by the Gradle 'init' task. + */ +package org.example + +import kotlin.test.Test +import kotlin.test.assertTrue + +class LibraryTest { + @Test fun someLibraryMethodReturnsTrue() { + val classUnderTest = Library() + assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'") + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..f12bfbf --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,14 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.6/userguide/multi_project_builds.html in the Gradle documentation. + */ + +plugins { + // Apply the foojay-resolver plugin to allow automatic download of JDKs + id("org.gradle.toolchains.foojay-resolver-convention") version "0.7.0" +} + +rootProject.name = "TSL" +include("lib") From 8a6e58d4651c63e2208ae13c6ec4cdf515818d97 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:33:42 +0300 Subject: [PATCH 005/164] feat: add initial rb node implementation --- lib/src/main/kotlin/tsl/nodes/RBNode.kt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 lib/src/main/kotlin/tsl/nodes/RBNode.kt diff --git a/lib/src/main/kotlin/tsl/nodes/RBNode.kt b/lib/src/main/kotlin/tsl/nodes/RBNode.kt new file mode 100644 index 0000000..620daad --- /dev/null +++ b/lib/src/main/kotlin/tsl/nodes/RBNode.kt @@ -0,0 +1,21 @@ +package nodes + +class RBNode, V>(key: K, value: V) : AbstractNode>(key, value) { + internal var parent: RBNode? = null + + enum class Color { + Black, + Red, + } + + internal var color = Color.Black + + internal fun switchColor() { + if (this.color == Color.Black) { + this.color = Color.Red + } else { + this.color = Color.Black + } + } +} + From f282f409663b6e83b409b4d36890b88d7e259766 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:36:40 +0300 Subject: [PATCH 006/164] feat: add draft of rb-tree implementation --- lib/src/main/kotlin/tsl/RBTree.kt | 221 ++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 lib/src/main/kotlin/tsl/RBTree.kt diff --git a/lib/src/main/kotlin/tsl/RBTree.kt b/lib/src/main/kotlin/tsl/RBTree.kt new file mode 100644 index 0000000..0fd892d --- /dev/null +++ b/lib/src/main/kotlin/tsl/RBTree.kt @@ -0,0 +1,221 @@ +import nodes.RBNode + +class ExitLoopException : Exception() + +class RBTree, V> : AbstractBinaryTree>() { + + override fun insert( + key: K, + value: V, + ) { // идем по дереву и вставляем + insertNode(root, key, value) + } + + private fun insertNode( + node: RBNode?, + key: K, + value: V, + ): RBNode? { + if (root == null) { + val newRoot = RBNode(key, value) + root = newRoot + return newRoot + } + if (node == null) { + return RBNode(key, value).apply { color = RBNode.Color.Red } + } + + var insertedNode: RBNode? = null + if (key < node.key) { + insertedNode = insertNode(node.leftChild, key, value) + node.leftChild = insertedNode + insertedNode?.parent = node + balanceNode(insertedNode) + throw ExitLoopException() + + } else if (key > node.key) { + insertedNode = insertNode(node.rightChild, key, value) + node.rightChild = insertedNode + insertedNode?.parent = node + balanceNode(insertedNode) + throw ExitLoopException() + + } else { + println("Duplicate keys are not allowed") // throw expression + } + return null + } + private fun balanceNode(node: RBNode?) { + var newNode = node + var uncle: RBNode? + while (newNode?.parent?.color == RBNode.Color.Red) { + if (newNode.parent == newNode.parent?.parent?.leftChild) { + uncle = newNode.parent?.parent?.rightChild + if (uncle?.color == RBNode.Color.Red) { + newNode.parent?.color = RBNode.Color.Black + uncle.color = RBNode.Color.Black + newNode.parent?.parent?.color = RBNode.Color.Red + newNode = newNode.parent?.parent + } else { + if (newNode == newNode.parent?.rightChild) { + newNode = newNode.parent + newNode?.let { rotateLeft(it) } + } + newNode?.parent?.color = RBNode.Color.Black + newNode?.parent?.parent?.color = RBNode.Color.Red + newNode?.parent?.parent?.let { rotateRight(it) } + } + } else { + uncle = newNode.parent?.parent?.leftChild + if (uncle?.color == RBNode.Color.Red) { + newNode.parent?.color = RBNode.Color.Black + uncle.color = RBNode.Color.Black + newNode.parent?.parent?.color = RBNode.Color.Red + newNode = newNode.parent?.parent + } else { + if (newNode == newNode.parent?.leftChild) { + newNode = newNode.parent + newNode?.let { rotateRight(it) } + } + newNode?.parent?.color = RBNode.Color.Black + newNode?.parent?.parent?.color = RBNode.Color.Red + newNode?.parent?.parent?.let { rotateLeft(it) } + } + } + } + root?.color = RBNode.Color.Black + } + + private fun rotateLeft(node: RBNode): RBNode? { + val temp = node.rightChild + node.rightChild = temp?.leftChild + temp?.rightChild = node + temp?.color = node.color + node.color = RBNode.Color.Red + return temp + } + + private fun rotateRight(node: RBNode): RBNode? { + val temp = node.leftChild + node.leftChild = temp?.rightChild + temp?.rightChild = node + temp?.color = node.color + node.color = RBNode.Color.Red + return temp + } + + override fun delete(key: K) { + root = deleteNodeRecursive(root, key) + } + + private fun deleteNodeRecursive( + node: RBNode?, + key: K, + ): RBNode? { + val currentNode = node ?: return null + + when { + key < currentNode.key -> currentNode.leftChild = deleteNodeRecursive(currentNode.leftChild, key) + key > currentNode.key -> currentNode.rightChild = deleteNodeRecursive(currentNode.rightChild, key) + else -> { + if (currentNode.leftChild == null || currentNode.rightChild == null) { + val temp = if (currentNode.leftChild != null) currentNode.leftChild else currentNode.rightChild + if (temp == null) { + if (currentNode.color == RBNode.Color.Black) { + fixDoubleBlack(currentNode) + } + return null + } else { + return temp + } + } else { + var successor = currentNode.rightChild + while (successor?.leftChild != null) { + successor = successor.leftChild + } + if (successor != null) { + currentNode.key = successor.key + currentNode.value = successor.value + currentNode.rightChild = deleteNodeRecursive(currentNode.rightChild, successor.key) + } + } + } + } + return currentNode + } + + private fun fixDoubleBlack(node: RBNode) { + var currentNode = node + while (currentNode != root && currentNode.color == RBNode.Color.Black) { + currentNode = fixDoubleBlackHelper(currentNode) ?: return + } + currentNode.color = RBNode.Color.Black + } + + private fun fixDoubleBlackHelper(node: RBNode): RBNode? { + val sibling = + when { + node == node.parent?.leftChild -> node.parent?.rightChild + else -> node.parent?.leftChild + } + + sibling?.takeIf { it.color == RBNode.Color.Red }?.run { + color = RBNode.Color.Black + node.parent?.color = RBNode.Color.Red + if (this == node.parent?.leftChild) node.parent?.let { rotateRight(it) } else node.parent?.let { rotateLeft(it) } + return null + } + + if ((sibling?.leftChild?.color ?: RBNode.Color.Black) == RBNode.Color.Black && + (sibling?.rightChild?.color ?: RBNode.Color.Black) == RBNode.Color.Black + ) { + sibling?.color = RBNode.Color.Red + return node.parent + } + + sibling?.let { + if (it.color == RBNode.Color.Black) { + if (node == node.parent?.leftChild) { + it.rightChild?.let { right -> + right.color = RBNode.Color.Black + it.color = RBNode.Color.Red + rotateLeft(it) + } + } else { + it.leftChild?.let { left -> + left.color = RBNode.Color.Black + it.color = RBNode.Color.Red + rotateRight(it) + } + } + return null + } + } + + node.parent?.let { + sibling?.color = it.color + it.color = RBNode.Color.Black + } + return if (node == node.parent?.leftChild) node.parent?.let { rotateRight(it) } else node.parent?.let { rotateLeft(it) } + } + + fun printTree() { + printTree(root, "", true) + } + + private fun printTree(node: RBNode?, prefix: String, isTail: Boolean) { + if (node != null) { + printTree(node.rightChild, "$prefix${if (isTail) "│ " else " "}", false) + println("$prefix${if (isTail) "└── " else "┌── "}${if (node.color == RBNode.Color.Red) "\u001b[31m" else "\u001b[30;1m"}(${node.key},${node.value})\u001b[0m") + printTree(node.leftChild, "$prefix${if (isTail) " " else "│ "}", true) + } + } + + fun rightSubbfsPrint(node: RBNode?) { + if (node != null) { + println("${node.key}") + rightSubbfsPrint(node.rightChild) + } + } + +} From 667da6c5c9f4faced17f4cf2b1fa11f08b646897 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:51:25 +0300 Subject: [PATCH 007/164] feat: add different type of nodes and abstract class for inheritance --- lib/src/main/kotlin/tsl/nodes/AVLNode.kt | 5 +++++ lib/src/main/kotlin/tsl/nodes/AbstractNode.kt | 6 ++++++ lib/src/main/kotlin/tsl/nodes/BSTNode.kt | 4 ++++ 3 files changed, 15 insertions(+) create mode 100644 lib/src/main/kotlin/tsl/nodes/AVLNode.kt create mode 100644 lib/src/main/kotlin/tsl/nodes/AbstractNode.kt create mode 100644 lib/src/main/kotlin/tsl/nodes/BSTNode.kt diff --git a/lib/src/main/kotlin/tsl/nodes/AVLNode.kt b/lib/src/main/kotlin/tsl/nodes/AVLNode.kt new file mode 100644 index 0000000..2542b30 --- /dev/null +++ b/lib/src/main/kotlin/tsl/nodes/AVLNode.kt @@ -0,0 +1,5 @@ +package nodes + +class AVLNode, V>(key: K, value: V) : AbstractNode>(key, value) { + var height = 0 +} diff --git a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt new file mode 100644 index 0000000..1afc7f6 --- /dev/null +++ b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt @@ -0,0 +1,6 @@ +package nodes + +abstract class AbstractNode, V, N : AbstractNode>(var key: K, var value: V) { + internal var leftChild: N? = null + internal var rightChild: N? = null +} diff --git a/lib/src/main/kotlin/tsl/nodes/BSTNode.kt b/lib/src/main/kotlin/tsl/nodes/BSTNode.kt new file mode 100644 index 0000000..2e7b473 --- /dev/null +++ b/lib/src/main/kotlin/tsl/nodes/BSTNode.kt @@ -0,0 +1,4 @@ +package nodes + +class BSTNode, V>(key: K, value: V) : AbstractNode>(key, value) + From 710c192ba61c5f18c5540e1f6ce7312d3929305b Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sat, 16 Mar 2024 10:34:28 +0300 Subject: [PATCH 008/164] docs: correct initials in LICENSE.md --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index de42fde..01c67d0 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License - Copyright (c) 2023-present Gavrilenko Mike, Shakirov Karim, Vlasenko Danil + Copyright (c) 2023-present Gavrilenko Mike, Shakirov Karim, Vlasenco Daniel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 8878ce820ad9e0e23f5b433970153f481e786e7d Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 11:46:16 +0300 Subject: [PATCH 009/164] ci: create CI workflow for code coverage Adding code coverage for GH actions workflow, using Kover. For this moment it's only a workflow-dispatch, meaning it can be used only by manual execution. --- .github/workflows/codecoverage.yml | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/codecoverage.yml diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml new file mode 100644 index 0000000..4e20a2f --- /dev/null +++ b/.github/workflows/codecoverage.yml @@ -0,0 +1,35 @@ +name: Measure coverage + +on: + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: 17 + + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + + - name: Generate kover coverage report + run: ./gradlew koverXmlReport + + - name: Add coverage report to PR + id: kover + uses: mi-kas/kover-report@v1 + with: + path: | + ${{ github.workspace }}/project1/build/reports/kover/report.xml + ${{ github.workspace }}/project2/build/reports/kover/report.xml + title: Code Coverage + update-comment: true + min-coverage-overall: 80 + min-coverage-changed-files: 80 + coverage-counter-type: LINE + From ffcc02f171dcc3fc9ad3aafe41a1d8a93d4c6cbb Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Fri, 15 Mar 2024 22:01:29 +0300 Subject: [PATCH 010/164] fix: issue with all-black tree is resolved --- lib/src/main/kotlin/tsl/RBTree.kt | 120 ++++++++++++++++-------------- 1 file changed, 65 insertions(+), 55 deletions(-) diff --git a/lib/src/main/kotlin/tsl/RBTree.kt b/lib/src/main/kotlin/tsl/RBTree.kt index 0fd892d..a2e3636 100644 --- a/lib/src/main/kotlin/tsl/RBTree.kt +++ b/lib/src/main/kotlin/tsl/RBTree.kt @@ -1,85 +1,95 @@ import nodes.RBNode -class ExitLoopException : Exception() - class RBTree, V> : AbstractBinaryTree>() { override fun insert( key: K, value: V, - ) { // идем по дереву и вставляем - insertNode(root, key, value) - } - - private fun insertNode( - node: RBNode?, - key: K, - value: V, - ): RBNode? { + ) { // не нужна рекурсия if (root == null) { val newRoot = RBNode(key, value) root = newRoot - return newRoot + return } - if (node == null) { - return RBNode(key, value).apply { color = RBNode.Color.Red } + + var currentNode: RBNode? = root + var currentParent: RBNode? = null + while (currentNode != null) { + if (key < currentNode.key) { + currentParent = currentNode + currentNode = currentNode.leftChild + } + else if (key > currentNode.key) { + currentParent = currentNode + currentNode = currentNode.rightChild + } + else { + println("Duplicate keys are not allowed") // throw expression? + } } - var insertedNode: RBNode? = null - if (key < node.key) { - insertedNode = insertNode(node.leftChild, key, value) - node.leftChild = insertedNode - insertedNode?.parent = node - balanceNode(insertedNode) - throw ExitLoopException() - - } else if (key > node.key) { - insertedNode = insertNode(node.rightChild, key, value) - node.rightChild = insertedNode - insertedNode?.parent = node - balanceNode(insertedNode) - throw ExitLoopException() - - } else { - println("Duplicate keys are not allowed") // throw expression + //currentNode = RBNode(key, value).apply { color = RBNode.Color.Red } + if (currentParent == null) { + return + } + if (key < currentParent.key) { + currentParent.leftChild = RBNode(key, value) + balanceNode(currentParent.leftChild) + } + if (key > currentParent.key) { + currentParent.rightChild = RBNode(key, value) + balanceNode(currentParent.rightChild) } - return null } + private fun balanceNode(node: RBNode?) { var newNode = node var uncle: RBNode? while (newNode?.parent?.color == RBNode.Color.Red) { if (newNode.parent == newNode.parent?.parent?.leftChild) { uncle = newNode.parent?.parent?.rightChild - if (uncle?.color == RBNode.Color.Red) { - newNode.parent?.color = RBNode.Color.Black - uncle.color = RBNode.Color.Black - newNode.parent?.parent?.color = RBNode.Color.Red - newNode = newNode.parent?.parent - } else { - if (newNode == newNode.parent?.rightChild) { + + when { + uncle?.color == RBNode.Color.Red -> { + newNode.parent?.color = RBNode.Color.Black + uncle.color = RBNode.Color.Black + newNode.parent?.parent?.color = RBNode.Color.Red + newNode = newNode.parent?.parent + } + + newNode == newNode.parent?.rightChild -> { newNode = newNode.parent - newNode?.let { rotateLeft(it) } + if (newNode!!.parent?.parent == null) root = newNode.parent + rotateLeft(newNode) + } + + newNode == newNode.parent?.leftChild -> { + if (newNode.parent?.parent?.parent == null) root = newNode.parent + newNode.parent?.parent?.let { rotateRight(it) } } - newNode?.parent?.color = RBNode.Color.Black - newNode?.parent?.parent?.color = RBNode.Color.Red - newNode?.parent?.parent?.let { rotateRight(it) } } - } else { + } + else { uncle = newNode.parent?.parent?.leftChild - if (uncle?.color == RBNode.Color.Red) { - newNode.parent?.color = RBNode.Color.Black - uncle.color = RBNode.Color.Black - newNode.parent?.parent?.color = RBNode.Color.Red - newNode = newNode.parent?.parent - } else { - if (newNode == newNode.parent?.leftChild) { + + when { + uncle?.color == RBNode.Color.Red -> { + newNode.parent?.color = RBNode.Color.Black + uncle.color = RBNode.Color.Black + newNode.parent?.parent?.color = RBNode.Color.Red + newNode = newNode.parent?.parent + } + + newNode == newNode.parent?.leftChild -> { newNode = newNode.parent - newNode?.let { rotateRight(it) } + if (newNode!!.parent?.parent == null) root = newNode.parent + rotateRight(newNode) + } + + newNode == newNode.parent?.rightChild -> { + if (newNode.parent?.parent?.parent == null) root = newNode.parent + newNode.parent?.parent?.let { rotateLeft(it) } } - newNode?.parent?.color = RBNode.Color.Black - newNode?.parent?.parent?.color = RBNode.Color.Red - newNode?.parent?.parent?.let { rotateLeft(it) } } } } From 3d0be4652bdbb6abc3ebb386c9b094a234a9c02f Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Fri, 15 Mar 2024 22:57:03 +0300 Subject: [PATCH 011/164] refactor: insert method modify. remove unnecessay statements, switch to when statement. clarifying comments added --- lib/src/main/kotlin/tsl/RBTree.kt | 42 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/src/main/kotlin/tsl/RBTree.kt b/lib/src/main/kotlin/tsl/RBTree.kt index a2e3636..2345be7 100644 --- a/lib/src/main/kotlin/tsl/RBTree.kt +++ b/lib/src/main/kotlin/tsl/RBTree.kt @@ -5,38 +5,39 @@ class RBTree, V> : AbstractBinaryTree>() { override fun insert( key: K, value: V, - ) { // не нужна рекурсия + ) { + // check if the tree is empty if (root == null) { - val newRoot = RBNode(key, value) - root = newRoot + root = RBNode(key, value) return } var currentNode: RBNode? = root var currentParent: RBNode? = null + + // traverse the tree to find the insertion point while (currentNode != null) { - if (key < currentNode.key) { - currentParent = currentNode - currentNode = currentNode.leftChild - } - else if (key > currentNode.key) { - currentParent = currentNode - currentNode = currentNode.rightChild - } - else { - println("Duplicate keys are not allowed") // throw expression? + when { + key < currentNode.key -> { + currentParent = currentNode + currentNode = currentNode.leftChild + } + key > currentNode.key -> { + currentParent = currentNode + currentNode = currentNode.rightChild + } + else -> { + println("Duplicate keys are not allowed") + return + } } } - //currentNode = RBNode(key, value).apply { color = RBNode.Color.Red } - if (currentParent == null) { - return - } - if (key < currentParent.key) { + // insert the new node based on comparison with its parent + if (key < currentParent!!.key) { currentParent.leftChild = RBNode(key, value) balanceNode(currentParent.leftChild) - } - if (key > currentParent.key) { + } else { currentParent.rightChild = RBNode(key, value) balanceNode(currentParent.rightChild) } @@ -227,5 +228,4 @@ class RBTree, V> : AbstractBinaryTree>() { rightSubbfsPrint(node.rightChild) } } - } From 3385f7190e06706652a43ff29d8c9e1badb954d4 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:30:49 +0300 Subject: [PATCH 012/164] fix: correct imports and packages for nodes --- lib/src/main/kotlin/tsl/nodes/AVLNode.kt | 2 +- lib/src/main/kotlin/tsl/nodes/AbstractNode.kt | 2 +- lib/src/main/kotlin/tsl/nodes/BSTNode.kt | 2 +- lib/src/main/kotlin/tsl/nodes/RBNode.kt | 2 +- lib/src/main/kotlin/tsl/{ => trees}/RBTree.kt | 58 ++++++++----------- 5 files changed, 28 insertions(+), 38 deletions(-) rename lib/src/main/kotlin/tsl/{ => trees}/RBTree.kt (85%) diff --git a/lib/src/main/kotlin/tsl/nodes/AVLNode.kt b/lib/src/main/kotlin/tsl/nodes/AVLNode.kt index 2542b30..0337c2c 100644 --- a/lib/src/main/kotlin/tsl/nodes/AVLNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/AVLNode.kt @@ -1,4 +1,4 @@ -package nodes +package tsl.nodes class AVLNode, V>(key: K, value: V) : AbstractNode>(key, value) { var height = 0 diff --git a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt index 1afc7f6..a21d32b 100644 --- a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt @@ -1,4 +1,4 @@ -package nodes +package tsl.nodes abstract class AbstractNode, V, N : AbstractNode>(var key: K, var value: V) { internal var leftChild: N? = null diff --git a/lib/src/main/kotlin/tsl/nodes/BSTNode.kt b/lib/src/main/kotlin/tsl/nodes/BSTNode.kt index 2e7b473..378b2bf 100644 --- a/lib/src/main/kotlin/tsl/nodes/BSTNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/BSTNode.kt @@ -1,4 +1,4 @@ -package nodes +package tsl.nodes class BSTNode, V>(key: K, value: V) : AbstractNode>(key, value) diff --git a/lib/src/main/kotlin/tsl/nodes/RBNode.kt b/lib/src/main/kotlin/tsl/nodes/RBNode.kt index 620daad..ade8226 100644 --- a/lib/src/main/kotlin/tsl/nodes/RBNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/RBNode.kt @@ -1,4 +1,4 @@ -package nodes +package tsl.nodes class RBNode, V>(key: K, value: V) : AbstractNode>(key, value) { internal var parent: RBNode? = null diff --git a/lib/src/main/kotlin/tsl/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt similarity index 85% rename from lib/src/main/kotlin/tsl/RBTree.kt rename to lib/src/main/kotlin/tsl/trees/RBTree.kt index 2345be7..b5852c1 100644 --- a/lib/src/main/kotlin/tsl/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -1,4 +1,5 @@ import nodes.RBNode +import tsl.AbstractBinaryTree class RBTree, V> : AbstractBinaryTree>() { @@ -6,41 +7,37 @@ class RBTree, V> : AbstractBinaryTree>() { key: K, value: V, ) { + + val newNode = RBNode(key, value) // check if the tree is empty if (root == null) { root = RBNode(key, value) - return + return balanceNode(root) } var currentNode: RBNode? = root - var currentParent: RBNode? = null // traverse the tree to find the insertion point while (currentNode != null) { - when { - key < currentNode.key -> { - currentParent = currentNode - currentNode = currentNode.leftChild - } - key > currentNode.key -> { - currentParent = currentNode - currentNode = currentNode.rightChild - } - else -> { - println("Duplicate keys are not allowed") - return + if (currentNode.key > newNode.key) { + if (currentNode.leftChild == null) { + currentNode.leftChild = newNode + newNode.parent = currentNode + return balanceNode(newNode) } + currentNode = currentNode.leftChild } - } + else { + if (currentNode.rightChild == null) { - // insert the new node based on comparison with its parent - if (key < currentParent!!.key) { - currentParent.leftChild = RBNode(key, value) - balanceNode(currentParent.leftChild) - } else { - currentParent.rightChild = RBNode(key, value) - balanceNode(currentParent.rightChild) + currentNode.rightChild = newNode + newNode.parent = currentNode + return balanceNode(newNode) + } + currentNode = currentNode.rightChild + } } + return balanceNode(newNode) } private fun balanceNode(node: RBNode?) { @@ -116,18 +113,18 @@ class RBTree, V> : AbstractBinaryTree>() { } override fun delete(key: K) { - root = deleteNodeRecursive(root, key) + root = deleteNodeRecursively(root, key) } - private fun deleteNodeRecursive( + private fun deleteNodeRecursively( node: RBNode?, key: K, ): RBNode? { val currentNode = node ?: return null when { - key < currentNode.key -> currentNode.leftChild = deleteNodeRecursive(currentNode.leftChild, key) - key > currentNode.key -> currentNode.rightChild = deleteNodeRecursive(currentNode.rightChild, key) + key < currentNode.key -> currentNode.leftChild = deleteNodeRecursively(currentNode.leftChild, key) + key > currentNode.key -> currentNode.rightChild = deleteNodeRecursively(currentNode.rightChild, key) else -> { if (currentNode.leftChild == null || currentNode.rightChild == null) { val temp = if (currentNode.leftChild != null) currentNode.leftChild else currentNode.rightChild @@ -147,7 +144,7 @@ class RBTree, V> : AbstractBinaryTree>() { if (successor != null) { currentNode.key = successor.key currentNode.value = successor.value - currentNode.rightChild = deleteNodeRecursive(currentNode.rightChild, successor.key) + currentNode.rightChild = deleteNodeRecursively(currentNode.rightChild, successor.key) } } } @@ -221,11 +218,4 @@ class RBTree, V> : AbstractBinaryTree>() { printTree(node.leftChild, "$prefix${if (isTail) " " else "│ "}", true) } } - - fun rightSubbfsPrint(node: RBNode?) { - if (node != null) { - println("${node.key}") - rightSubbfsPrint(node.rightChild) - } - } } From 21ba7d6e46e48557f159259cb3f7c9fc5e5383bf Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:32:28 +0300 Subject: [PATCH 013/164] feat: add abstract binary tree for future inheritance --- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt new file mode 100644 index 0000000..af987b8 --- /dev/null +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -0,0 +1,35 @@ +package tsl.trees + +import tsl.nodes.AbstractNode + +abstract class AbstractBinaryTree, V, N : AbstractNode> { + internal var root: N? = null + + fun search(key: K): V? { + return searchNode(root, key) + } + + private fun searchNode( + node: AbstractNode?, + key: K, + ): V? { + return if (node?.key == key) { + node.value + } else if (node == null) { + return null + } else { + if (key < (node.key)) { + searchNode(node.leftChild, key) + } else { + searchNode(node.rightChild, key) + } + } + } + + abstract fun delete(key: K) + + abstract fun insert( + key: K, + value: V, + ) +} \ No newline at end of file From 596af3ce4f42a253c09a6563b603da50d47b33bd Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:36:13 +0300 Subject: [PATCH 014/164] fix: get rid of rotations in tree-class, balance after insertion added --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 39 +++++++------------------ 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index b5852c1..aa777af 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -1,5 +1,6 @@ -import nodes.RBNode -import tsl.AbstractBinaryTree +package tsl.trees + +import tsl.nodes.RBNode class RBTree, V> : AbstractBinaryTree>() { @@ -58,12 +59,12 @@ class RBTree, V> : AbstractBinaryTree>() { newNode == newNode.parent?.rightChild -> { newNode = newNode.parent if (newNode!!.parent?.parent == null) root = newNode.parent - rotateLeft(newNode) + newNode.rotateLeft() } newNode == newNode.parent?.leftChild -> { if (newNode.parent?.parent?.parent == null) root = newNode.parent - newNode.parent?.parent?.let { rotateRight(it) } + newNode.parent?.parent?.rotateRight() } } } @@ -81,12 +82,12 @@ class RBTree, V> : AbstractBinaryTree>() { newNode == newNode.parent?.leftChild -> { newNode = newNode.parent if (newNode!!.parent?.parent == null) root = newNode.parent - rotateRight(newNode) + newNode.rotateRight() } newNode == newNode.parent?.rightChild -> { if (newNode.parent?.parent?.parent == null) root = newNode.parent - newNode.parent?.parent?.let { rotateLeft(it) } + newNode.parent?.parent?.rotateLeft() } } } @@ -94,24 +95,6 @@ class RBTree, V> : AbstractBinaryTree>() { root?.color = RBNode.Color.Black } - private fun rotateLeft(node: RBNode): RBNode? { - val temp = node.rightChild - node.rightChild = temp?.leftChild - temp?.rightChild = node - temp?.color = node.color - node.color = RBNode.Color.Red - return temp - } - - private fun rotateRight(node: RBNode): RBNode? { - val temp = node.leftChild - node.leftChild = temp?.rightChild - temp?.rightChild = node - temp?.color = node.color - node.color = RBNode.Color.Red - return temp - } - override fun delete(key: K) { root = deleteNodeRecursively(root, key) } @@ -170,7 +153,7 @@ class RBTree, V> : AbstractBinaryTree>() { sibling?.takeIf { it.color == RBNode.Color.Red }?.run { color = RBNode.Color.Black node.parent?.color = RBNode.Color.Red - if (this == node.parent?.leftChild) node.parent?.let { rotateRight(it) } else node.parent?.let { rotateLeft(it) } + if (this == node.parent?.leftChild) node.parent?.rotateRight() else node.parent?.rotateLeft() return null } @@ -187,13 +170,13 @@ class RBTree, V> : AbstractBinaryTree>() { it.rightChild?.let { right -> right.color = RBNode.Color.Black it.color = RBNode.Color.Red - rotateLeft(it) + it.rotateLeft() } } else { it.leftChild?.let { left -> left.color = RBNode.Color.Black it.color = RBNode.Color.Red - rotateRight(it) + it.rotateRight() } } return null @@ -204,7 +187,7 @@ class RBTree, V> : AbstractBinaryTree>() { sibling?.color = it.color it.color = RBNode.Color.Black } - return if (node == node.parent?.leftChild) node.parent?.let { rotateRight(it) } else node.parent?.let { rotateLeft(it) } + return if (node == node.parent?.leftChild) node.parent?.rotateRight() else node.parent?.rotateLeft() } fun printTree() { From d4ee8f21196a136cd0c6704beec6efcc8fca01b2 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:37:48 +0300 Subject: [PATCH 015/164] fix: transfer rotate feature from tree class to node one --- lib/src/main/kotlin/tsl/nodes/RBNode.kt | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/src/main/kotlin/tsl/nodes/RBNode.kt b/lib/src/main/kotlin/tsl/nodes/RBNode.kt index ade8226..052f9d8 100644 --- a/lib/src/main/kotlin/tsl/nodes/RBNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/RBNode.kt @@ -17,5 +17,41 @@ class RBNode, V>(key: K, value: V) : AbstractNode dad.leftChild = rightChild + this == dad?.rightChild -> dad.rightChild = rightChild + } + + this.parent = rightChild + rightChild.parent = dad + } + + internal fun rotateRight() { + val leftChild = this.leftChild ?: return + val dad = this.parent + + this.switchColor(leftChild) + leftChild.rightChild?.parent = this + this.leftChild = leftChild.rightChild + leftChild.rightChild = this + + when { + this == dad?.leftChild -> dad.leftChild = leftChild + this == dad?.rightChild -> dad.rightChild = leftChild + } + + this.parent = leftChild + leftChild.parent = dad + } } From e70a0617747320bd88a7eb0f3e759e261396e105 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:39:32 +0300 Subject: [PATCH 016/164] fix: remade switchcolor method, so it requires the second node to switch. changed default color to red --- lib/src/main/kotlin/tsl/nodes/RBNode.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/src/main/kotlin/tsl/nodes/RBNode.kt b/lib/src/main/kotlin/tsl/nodes/RBNode.kt index 052f9d8..c840fad 100644 --- a/lib/src/main/kotlin/tsl/nodes/RBNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/RBNode.kt @@ -8,13 +8,14 @@ class RBNode, V>(key: K, value: V) : AbstractNode?) { + val node1color = this.color + + if (node2 != null) { + this.color = node2.color + node2.color = node1color } } From 0857a8260d6be931f3d11999248e5442ef0ea95f Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:46:46 +0300 Subject: [PATCH 017/164] fix: correct method name (to printTreeRecursively) --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index aa777af..8ff63d3 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -191,10 +191,10 @@ class RBTree, V> : AbstractBinaryTree>() { } fun printTree() { - printTree(root, "", true) + printTreeRecursively(root, "", true) } - private fun printTree(node: RBNode?, prefix: String, isTail: Boolean) { + private fun printTreeRecursively(node: RBNode?, prefix: String, isTail: Boolean) { if (node != null) { printTree(node.rightChild, "$prefix${if (isTail) "│ " else " "}", false) println("$prefix${if (isTail) "└── " else "┌── "}${if (node.color == RBNode.Color.Red) "\u001b[31m" else "\u001b[30;1m"}(${node.key},${node.value})\u001b[0m") From 2d5870a96f22c963924efe0b8f91f71ca832c301 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 14:47:21 +0300 Subject: [PATCH 018/164] feat: add tree files for future implemetation --- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 0 lib/src/main/kotlin/tsl/trees/BSTree.kt | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/src/main/kotlin/tsl/trees/AVLTree.kt create mode 100644 lib/src/main/kotlin/tsl/trees/BSTree.kt diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt new file mode 100644 index 0000000..e69de29 diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt new file mode 100644 index 0000000..e69de29 From 07a81da7dfadd7cbf75094eadc045275ce6a27b4 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 16:03:11 +0300 Subject: [PATCH 019/164] fix: correct function names --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 8ff63d3..5573860 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -196,9 +196,9 @@ class RBTree, V> : AbstractBinaryTree>() { private fun printTreeRecursively(node: RBNode?, prefix: String, isTail: Boolean) { if (node != null) { - printTree(node.rightChild, "$prefix${if (isTail) "│ " else " "}", false) + printTreeRecursively(node.rightChild, "$prefix${if (isTail) "│ " else " "}", false) println("$prefix${if (isTail) "└── " else "┌── "}${if (node.color == RBNode.Color.Red) "\u001b[31m" else "\u001b[30;1m"}(${node.key},${node.value})\u001b[0m") - printTree(node.leftChild, "$prefix${if (isTail) " " else "│ "}", true) + printTreeRecursively(node.leftChild, "$prefix${if (isTail) " " else "│ "}", true) } } } From 35d4b1e5f07353cb59ffeedeb926d8f98757e3eb Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sun, 17 Mar 2024 18:51:32 +0300 Subject: [PATCH 020/164] feat: add insertion method to AVL tree --- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 84 ++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index e69de29..5e33e58 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -0,0 +1,84 @@ +package tsl.trees + +import tsl.nodes.AVLNode + +import kotlin.math.max + +class AVLTree, V> : AbstractBinaryTree>() { + + // TODO: add return of replaced value or null if key was not present in the tree + override fun insert(key: K, value: V) { + insertAndBalanceRecursively(root, key, value) + } + + private fun insertAndBalanceRecursively(node: AVLNode?, key: K, value: V): AVLNode? { + if (root == null) { + root = AVLNode(key, value) + return root + } + if (node == null) return AVLNode(key, value) + + if (key == node.key) return node + else if (key < node.key) { + node.leftChild = insertAndBalanceRecursively(node.leftChild, key, value) + } else if (key > node.key) { + node.rightChild = insertAndBalanceRecursively(node.rightChild, key, value) + } + + updateHeight(node) + + val balanceFactor = getHeight(node.rightChild) - getHeight(node.leftChild) + + // rotate if node is not balanced + if (balanceFactor < -1) { // *-right cases + node.leftChild?.let { + if (key > it.key) node.leftChild = rotateLeft(it) // left-right case + return rotateRight(node) + } + } else if (balanceFactor > 1) { // *-left cases + node.rightChild?.let { + if (key < it.key) node.rightChild = rotateRight(it) // right-left case + return rotateLeft(node) + } + } + + return node + } + + private fun rotateRight(oldUpperNode: AVLNode): AVLNode { + val newUpperNode = oldUpperNode.leftChild ?: return oldUpperNode + oldUpperNode.leftChild = newUpperNode.rightChild + newUpperNode.rightChild = oldUpperNode + + if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root + + updateHeight(oldUpperNode) + updateHeight(newUpperNode) + + return newUpperNode + } + + private fun rotateLeft(oldUpperNode: AVLNode): AVLNode? { + val newUpperNode = oldUpperNode.rightChild ?: return oldUpperNode + oldUpperNode.rightChild = newUpperNode.leftChild + newUpperNode.leftChild = oldUpperNode + + if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root + + updateHeight(oldUpperNode) + updateHeight(newUpperNode) + + return newUpperNode + } + + private fun updateHeight(node: AVLNode): Int { + node.height = max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1 + return node.height + } + + private fun getHeight(node: AVLNode?) = node?.height ?: -1 + + override fun delete(key: K) { + TODO("implement node deletion method") + } +} \ No newline at end of file From 58e87db888937aa4408b1548a48729a7459716cc Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:36:32 +0300 Subject: [PATCH 021/164] fix: get rid of non-null asserted calls --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 100 ++---------------------- 1 file changed, 5 insertions(+), 95 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 5573860..9acbee5 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -1,6 +1,7 @@ package tsl.trees import tsl.nodes.RBNode +import javax.crypto.KEM class RBTree, V> : AbstractBinaryTree>() { @@ -58,8 +59,8 @@ class RBTree, V> : AbstractBinaryTree>() { newNode == newNode.parent?.rightChild -> { newNode = newNode.parent - if (newNode!!.parent?.parent == null) root = newNode.parent - newNode.rotateLeft() + if (newNode?.parent?.parent == null) root = newNode?.parent + newNode?.rotateLeft() } newNode == newNode.parent?.leftChild -> { @@ -81,8 +82,8 @@ class RBTree, V> : AbstractBinaryTree>() { newNode == newNode.parent?.leftChild -> { newNode = newNode.parent - if (newNode!!.parent?.parent == null) root = newNode.parent - newNode.rotateRight() + if (newNode?.parent?.parent == null) root = newNode?.parent + newNode?.rotateRight() } newNode == newNode.parent?.rightChild -> { @@ -96,98 +97,7 @@ class RBTree, V> : AbstractBinaryTree>() { } override fun delete(key: K) { - root = deleteNodeRecursively(root, key) - } - - private fun deleteNodeRecursively( - node: RBNode?, - key: K, - ): RBNode? { - val currentNode = node ?: return null - - when { - key < currentNode.key -> currentNode.leftChild = deleteNodeRecursively(currentNode.leftChild, key) - key > currentNode.key -> currentNode.rightChild = deleteNodeRecursively(currentNode.rightChild, key) - else -> { - if (currentNode.leftChild == null || currentNode.rightChild == null) { - val temp = if (currentNode.leftChild != null) currentNode.leftChild else currentNode.rightChild - if (temp == null) { - if (currentNode.color == RBNode.Color.Black) { - fixDoubleBlack(currentNode) - } - return null - } else { - return temp - } - } else { - var successor = currentNode.rightChild - while (successor?.leftChild != null) { - successor = successor.leftChild - } - if (successor != null) { - currentNode.key = successor.key - currentNode.value = successor.value - currentNode.rightChild = deleteNodeRecursively(currentNode.rightChild, successor.key) - } - } - } - } - return currentNode - } - - private fun fixDoubleBlack(node: RBNode) { - var currentNode = node - while (currentNode != root && currentNode.color == RBNode.Color.Black) { - currentNode = fixDoubleBlackHelper(currentNode) ?: return - } - currentNode.color = RBNode.Color.Black - } - private fun fixDoubleBlackHelper(node: RBNode): RBNode? { - val sibling = - when { - node == node.parent?.leftChild -> node.parent?.rightChild - else -> node.parent?.leftChild - } - - sibling?.takeIf { it.color == RBNode.Color.Red }?.run { - color = RBNode.Color.Black - node.parent?.color = RBNode.Color.Red - if (this == node.parent?.leftChild) node.parent?.rotateRight() else node.parent?.rotateLeft() - return null - } - - if ((sibling?.leftChild?.color ?: RBNode.Color.Black) == RBNode.Color.Black && - (sibling?.rightChild?.color ?: RBNode.Color.Black) == RBNode.Color.Black - ) { - sibling?.color = RBNode.Color.Red - return node.parent - } - - sibling?.let { - if (it.color == RBNode.Color.Black) { - if (node == node.parent?.leftChild) { - it.rightChild?.let { right -> - right.color = RBNode.Color.Black - it.color = RBNode.Color.Red - it.rotateLeft() - } - } else { - it.leftChild?.let { left -> - left.color = RBNode.Color.Black - it.color = RBNode.Color.Red - it.rotateRight() - } - } - return null - } - } - - node.parent?.let { - sibling?.color = it.color - it.color = RBNode.Color.Black - } - return if (node == node.parent?.leftChild) node.parent?.rotateRight() else node.parent?.rotateLeft() } fun printTree() { From 33da688fe348276e1b928df510cd5cff20a4355e Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:41:44 +0300 Subject: [PATCH 022/164] feat: add delete by key method, add signatures of auxiliary methods --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 9acbee5..a8006c2 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -97,7 +97,41 @@ class RBTree, V> : AbstractBinaryTree>() { } override fun delete(key: K) { + val deleteNode: RBNode? = searchNodeF(key) // track node that will replace other one + var colorOfTransferingNode = deleteNode?.color + val childNode: RBNode? + if (getChildrenCount(deleteNode) < 2) { + childNode = if (deleteNode?.leftChild != null) deleteNode.leftChild else deleteNode?.rightChild + transplantTwoNodes(deleteNode, childNode) + } + else { + val minNode = getMin(deleteNode?.rightChild) + if (minNode != null) { + deleteNode?.key = minNode.key + deleteNode?.value = minNode.value + colorOfTransferingNode = minNode.color + } + childNode = if (minNode?.leftChild != null) minNode.leftChild else minNode?.rightChild + transplantTwoNodes(minNode, childNode) + } + if (colorOfTransferingNode == RBNode.Color.Black) fixAfterDelete(childNode) + // TODO: return values + return + } + + private fun searchNodeF(key: K): RBNode? { + } + + private fun transplantTwoNodes(firstNode: RBNode? , secondNode: RBNode?) { + } + + private fun getChildrenCount(node: RBNode?): Int { + } + + private fun getMin(node: RBNode?): RBNode? { + } + private fun fixAfterDelete(node: RBNode?) { } fun printTree() { From dbe5780da5c70d302276f11ee36f6f799474bf36 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:45:28 +0300 Subject: [PATCH 023/164] feat: add balancing method after node delete. method cover all 4 cases of sibling positioning --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index a8006c2..f5953b2 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -132,6 +132,62 @@ class RBTree, V> : AbstractBinaryTree>() { } private fun fixAfterDelete(node: RBNode?) { + var fixNode: RBNode? = node + var fixNodeBrother: RBNode? + while (fixNode != root && fixNode?.color == RBNode.Color.Black) { + if (fixNode == fixNode.parent?.leftChild) { + val parent = fixNode.parent + fixNodeBrother = parent?.rightChild + if (fixNodeBrother?.leftChild?.color == RBNode.Color.Black && fixNodeBrother.rightChild?.color == RBNode.Color.Black) { + fixNodeBrother.color = RBNode.Color.Red + fixNode = fixNode.parent + } + else { + if (fixNodeBrother?.rightChild?.color == RBNode.Color.Black) { + fixNodeBrother.leftChild?.color = RBNode.Color.Black + fixNodeBrother.color = RBNode.Color.Red + fixNodeBrother.rotateRight() + fixNodeBrother = fixNode.parent?.rightChild + } + if (fixNodeBrother != null && fixNode.parent != null) { + fixNode.parent?.color.also { if (it != null) fixNodeBrother?.color = it } + // omg do not touch TODO: good coomment + } + fixNode.parent?.color = RBNode.Color.Black + fixNodeBrother?.rightChild?.color = RBNode.Color.Black + fixNode.parent?.rotateLeft() + fixNode = root + } + } + else { + fixNodeBrother = fixNode.parent?.leftChild + if(fixNodeBrother?.color == RBNode.Color.Red) { + fixNodeBrother.color = RBNode.Color.Black + fixNode.parent?.color = RBNode.Color.Red + fixNode.parent?.rotateRight() + fixNodeBrother = fixNode.parent?.leftChild + } + if(fixNodeBrother?.leftChild?.color == RBNode.Color.Black && fixNodeBrother.rightChild?.color == RBNode.Color.Black) { + fixNode.parent?.rotateRight() + fixNodeBrother = fixNode.parent?.leftChild + } + else { + if(fixNodeBrother?.leftChild?.color == RBNode.Color.Black) { + fixNodeBrother.rightChild?.color = RBNode.Color.Black + fixNodeBrother.color = RBNode.Color.Red + fixNodeBrother.rotateLeft() + fixNodeBrother = fixNode.parent?.leftChild + } + fixNode.parent?.color.also { if (it != null) fixNodeBrother?.color = it } + // TODO: good coomment + fixNode.parent?.color = RBNode.Color.Black + fixNodeBrother?. leftChild?.color = RBNode.Color.Black + fixNode.parent?.rotateRight() + fixNode = root + } + } + } + fixNode?.color = RBNode.Color.Black } fun printTree() { From dd05d087f63105bc74d1c97bd0e4bcc4ef93800a Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:50:21 +0300 Subject: [PATCH 024/164] feat: add all auxiliary methods for delete, it include: get min of subtree, count children, transplant of two nodes and search node by key --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 49 +++++++++++++++++++------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index f5953b2..7abb6a5 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -119,18 +119,6 @@ class RBTree, V> : AbstractBinaryTree>() { return } - private fun searchNodeF(key: K): RBNode? { - } - - private fun transplantTwoNodes(firstNode: RBNode? , secondNode: RBNode?) { - } - - private fun getChildrenCount(node: RBNode?): Int { - } - - private fun getMin(node: RBNode?): RBNode? { - } - private fun fixAfterDelete(node: RBNode?) { var fixNode: RBNode? = node var fixNodeBrother: RBNode? @@ -190,6 +178,43 @@ class RBTree, V> : AbstractBinaryTree>() { fixNode?.color = RBNode.Color.Black } + private fun searchNodeF(key: K): RBNode? { + var currentNode = root + while (currentNode != null) { + if (key == currentNode.key) { + return currentNode + } + currentNode = if (key < currentNode.key) currentNode.leftChild else currentNode.rightChild + } + return null + } + + private fun transplantTwoNodes(firstNode: RBNode? , secondNode: RBNode?) { + if (firstNode == root) root = secondNode + else if (firstNode?.parent?.leftChild == firstNode) firstNode?.parent?.leftChild = secondNode + else firstNode?.parent?.rightChild = secondNode + secondNode?.parent = firstNode?.parent + } + + private fun getChildrenCount(node: RBNode?): Int { + if (node == null) return 0 + var count = 0 + + if (node.leftChild != null) count++ + if (node.rightChild != null) count++ + return count + } + + private fun getMin(node: RBNode?): RBNode? { + if (node == null) return null + var current: RBNode = node + + while (current.leftChild != null) { + current.leftChild.also { if (it != null) current = it } + } + return current + } + fun printTree() { printTreeRecursively(root, "", true) } From dba1ac7a4e7fbdddcd5b7cccb66af726e317cd09 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Tue, 19 Mar 2024 23:09:09 +0300 Subject: [PATCH 025/164] fix: add return types for delete and insert methods --- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index af987b8..fdcd241 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -26,10 +26,10 @@ abstract class AbstractBinaryTree, V, N : AbstractNode Date: Tue, 19 Mar 2024 23:12:01 +0300 Subject: [PATCH 026/164] refactor: correct insert method, add return values in case of duplicate, add todo's and some comments for better code readability --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 44 +++++++++++-------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 7abb6a5..b62bd11 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -1,45 +1,47 @@ package tsl.trees import tsl.nodes.RBNode -import javax.crypto.KEM class RBTree, V> : AbstractBinaryTree>() { - override fun insert( key: K, value: V, - ) { + ): V? { // in case we inserted successfully - > return null -> else -> return value, so user could have another try to insert it val newNode = RBNode(key, value) - // check if the tree is empty if (root == null) { root = RBNode(key, value) - return balanceNode(root) + balanceNode(newNode) + return null } var currentNode: RBNode? = root - // traverse the tree to find the insertion point + // traverse the tree to find the insertion point(node) while (currentNode != null) { if (currentNode.key > newNode.key) { if (currentNode.leftChild == null) { currentNode.leftChild = newNode newNode.parent = currentNode - return balanceNode(newNode) + balanceNode(newNode) + return null } currentNode = currentNode.leftChild - } - else { + } else if (currentNode.key < newNode.key) + { if (currentNode.rightChild == null) { - currentNode.rightChild = newNode newNode.parent = currentNode - return balanceNode(newNode) + balanceNode(newNode) + return null } currentNode = currentNode.rightChild + } else { + return value } } - return balanceNode(newNode) + balanceNode(newNode) + return null } private fun balanceNode(node: RBNode?) { @@ -119,6 +121,8 @@ class RBTree, V> : AbstractBinaryTree>() { return } + // TODO: fix case of deleting the root + private fun fixAfterDelete(node: RBNode?) { var fixNode: RBNode? = node var fixNodeBrother: RBNode? @@ -189,6 +193,10 @@ class RBTree, V> : AbstractBinaryTree>() { return null } + // TODO: move this type of methods to abstract mb + + + // this method movest the parent of 2nd node -> to 1st node private fun transplantTwoNodes(firstNode: RBNode? , secondNode: RBNode?) { if (firstNode == root) root = secondNode else if (firstNode?.parent?.leftChild == firstNode) firstNode?.parent?.leftChild = secondNode @@ -214,16 +222,4 @@ class RBTree, V> : AbstractBinaryTree>() { } return current } - - fun printTree() { - printTreeRecursively(root, "", true) - } - - private fun printTreeRecursively(node: RBNode?, prefix: String, isTail: Boolean) { - if (node != null) { - printTreeRecursively(node.rightChild, "$prefix${if (isTail) "│ " else " "}", false) - println("$prefix${if (isTail) "└── " else "┌── "}${if (node.color == RBNode.Color.Red) "\u001b[31m" else "\u001b[30;1m"}(${node.key},${node.value})\u001b[0m") - printTreeRecursively(node.leftChild, "$prefix${if (isTail) " " else "│ "}", true) - } - } } From 12d51b12442ac42352c65da58b5e2b904b57ca69 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Tue, 19 Mar 2024 23:16:45 +0300 Subject: [PATCH 027/164] refactor: reorganize delete method and its dependent methods, add description of null-pointer magic, rename searchNode method --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 56 ++++++++++++------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index b62bd11..882572d 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -70,8 +70,7 @@ class RBTree, V> : AbstractBinaryTree>() { newNode.parent?.parent?.rotateRight() } } - } - else { + } else { uncle = newNode.parent?.parent?.leftChild when { @@ -98,27 +97,29 @@ class RBTree, V> : AbstractBinaryTree>() { root?.color = RBNode.Color.Black } - override fun delete(key: K) { - val deleteNode: RBNode? = searchNodeF(key) // track node that will replace other one - var colorOfTransferingNode = deleteNode?.color - val childNode: RBNode? + override fun delete(key: K): V? { + val deleteNode: RBNode = searchNode(key) ?: return null // track node that will replace other one + val deleteNodeValue = deleteNode.value + var colorOfTransferringNode = deleteNode.color + val childNode: RBNode? // node that will be on the place of deleteNode + if (getChildrenCount(deleteNode) < 2) { - childNode = if (deleteNode?.leftChild != null) deleteNode.leftChild else deleteNode?.rightChild + childNode = if (deleteNode.leftChild != null) deleteNode.leftChild else deleteNode.rightChild transplantTwoNodes(deleteNode, childNode) - } - else { - val minNode = getMin(deleteNode?.rightChild) + } else { + val minNode = getMin(deleteNode.rightChild) if (minNode != null) { - deleteNode?.key = minNode.key - deleteNode?.value = minNode.value - colorOfTransferingNode = minNode.color + deleteNode.key = minNode.key + deleteNode.value = minNode.value + colorOfTransferringNode = minNode.color } childNode = if (minNode?.leftChild != null) minNode.leftChild else minNode?.rightChild transplantTwoNodes(minNode, childNode) } - if (colorOfTransferingNode == RBNode.Color.Black) fixAfterDelete(childNode) - // TODO: return values - return + + if (colorOfTransferringNode == RBNode.Color.Black) fixAfterDelete(childNode) + + return deleteNodeValue // in case the deleting process was successful - we return value of deleted node. in other case - null } // TODO: fix case of deleting the root @@ -133,8 +134,7 @@ class RBTree, V> : AbstractBinaryTree>() { if (fixNodeBrother?.leftChild?.color == RBNode.Color.Black && fixNodeBrother.rightChild?.color == RBNode.Color.Black) { fixNodeBrother.color = RBNode.Color.Red fixNode = fixNode.parent - } - else { + } else { if (fixNodeBrother?.rightChild?.color == RBNode.Color.Black) { fixNodeBrother.leftChild?.color = RBNode.Color.Black fixNodeBrother.color = RBNode.Color.Red @@ -143,35 +143,35 @@ class RBTree, V> : AbstractBinaryTree>() { } if (fixNodeBrother != null && fixNode.parent != null) { fixNode.parent?.color.also { if (it != null) fixNodeBrother?.color = it } - // omg do not touch TODO: good coomment + /*this line of code checks if the color property of the parent of fixNode is not null, + * and if it isn't, it assigns that color to the color property of fixNodeBrother. + * safe call operator ensures that the code doesn't throw a NullPointerException + * if any of the properties are null. */ } fixNode.parent?.color = RBNode.Color.Black fixNodeBrother?.rightChild?.color = RBNode.Color.Black fixNode.parent?.rotateLeft() fixNode = root } - } - else { + } else { fixNodeBrother = fixNode.parent?.leftChild - if(fixNodeBrother?.color == RBNode.Color.Red) { + if (fixNodeBrother?.color == RBNode.Color.Red) { fixNodeBrother.color = RBNode.Color.Black fixNode.parent?.color = RBNode.Color.Red fixNode.parent?.rotateRight() fixNodeBrother = fixNode.parent?.leftChild } - if(fixNodeBrother?.leftChild?.color == RBNode.Color.Black && fixNodeBrother.rightChild?.color == RBNode.Color.Black) { + if (fixNodeBrother?.leftChild?.color == RBNode.Color.Black && fixNodeBrother.rightChild?.color == RBNode.Color.Black) { fixNode.parent?.rotateRight() fixNodeBrother = fixNode.parent?.leftChild - } - else { - if(fixNodeBrother?.leftChild?.color == RBNode.Color.Black) { + } else { + if (fixNodeBrother?.leftChild?.color == RBNode.Color.Black) { fixNodeBrother.rightChild?.color = RBNode.Color.Black fixNodeBrother.color = RBNode.Color.Red fixNodeBrother.rotateLeft() fixNodeBrother = fixNode.parent?.leftChild } fixNode.parent?.color.also { if (it != null) fixNodeBrother?.color = it } - // TODO: good coomment fixNode.parent?.color = RBNode.Color.Black fixNodeBrother?. leftChild?.color = RBNode.Color.Black fixNode.parent?.rotateRight() @@ -182,7 +182,7 @@ class RBTree, V> : AbstractBinaryTree>() { fixNode?.color = RBNode.Color.Black } - private fun searchNodeF(key: K): RBNode? { + private fun searchNode(key: K): RBNode? { var currentNode = root while (currentNode != null) { if (key == currentNode.key) { From 773ad29e47343982dee6cd756e9feb7854828c9f Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 20 Mar 2024 09:50:24 +0300 Subject: [PATCH 028/164] feat: add deletion method to AVL tree, add auxiliary method to find balance factor of a node --- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 71 ++++++++++++++++++++---- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index 5e33e58..1c4f038 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -1,14 +1,16 @@ package tsl.trees import tsl.nodes.AVLNode +import tsl.nodes.RBNode import kotlin.math.max class AVLTree, V> : AbstractBinaryTree>() { // TODO: add return of replaced value or null if key was not present in the tree - override fun insert(key: K, value: V) { + override fun insert(key: K, value: V): V? { insertAndBalanceRecursively(root, key, value) + return value // TODO: DELETE } private fun insertAndBalanceRecursively(node: AVLNode?, key: K, value: V): AVLNode? { @@ -18,24 +20,22 @@ class AVLTree, V> : AbstractBinaryTree>() } if (node == null) return AVLNode(key, value) - if (key == node.key) return node - else if (key < node.key) { + if (key < node.key) { node.leftChild = insertAndBalanceRecursively(node.leftChild, key, value) } else if (key > node.key) { node.rightChild = insertAndBalanceRecursively(node.rightChild, key, value) - } + } else return node updateHeight(node) - val balanceFactor = getHeight(node.rightChild) - getHeight(node.leftChild) + val balanceFactor = getBalanceFactor(node) - // rotate if node is not balanced if (balanceFactor < -1) { // *-right cases node.leftChild?.let { if (key > it.key) node.leftChild = rotateLeft(it) // left-right case return rotateRight(node) } - } else if (balanceFactor > 1) { // *-left cases + } else if (balanceFactor > 1) { // *-left cases node.rightChild?.let { if (key < it.key) node.rightChild = rotateRight(it) // right-left case return rotateLeft(node) @@ -58,7 +58,7 @@ class AVLTree, V> : AbstractBinaryTree>() return newUpperNode } - private fun rotateLeft(oldUpperNode: AVLNode): AVLNode? { + private fun rotateLeft(oldUpperNode: AVLNode): AVLNode { val newUpperNode = oldUpperNode.rightChild ?: return oldUpperNode oldUpperNode.rightChild = newUpperNode.leftChild newUpperNode.leftChild = oldUpperNode @@ -76,9 +76,58 @@ class AVLTree, V> : AbstractBinaryTree>() return node.height } + private fun getBalanceFactor(node: AVLNode): Int { + return getHeight(node.rightChild) - getHeight(node.leftChild) + } + private fun getHeight(node: AVLNode?) = node?.height ?: -1 - override fun delete(key: K) { - TODO("implement node deletion method") + override fun delete(key: K): V? { + deleteAndBalanceRecursively(root, key) + return null // TODO: DELETE + } + + private fun deleteAndBalanceRecursively(node: AVLNode?, key: K): AVLNode? { + if (node == null) return null + + if (key < node.key) { + node.leftChild = deleteAndBalanceRecursively(node.leftChild, key) + } else if (key > node.key) { + node.rightChild = deleteAndBalanceRecursively(node.rightChild, key) + } else { + if (node.leftChild == null || node.rightChild == null) { // case of 0 or 1 child + val temp = node.leftChild ?: node.rightChild + return temp + } else { // case of 2 children + var successor = node.rightChild + while (successor?.leftChild != null) { + successor = successor.leftChild + } + if (successor != null) { + node.key = successor.key + node.value = successor.value + val temp = deleteAndBalanceRecursively(node.rightChild, successor.key) + if (temp != null) node.rightChild = temp + } + } + } + + updateHeight(node) + + val balanceFactor = getBalanceFactor(node) + + if (balanceFactor < -1) { // *-right cases + node.leftChild?.let { + if (getBalanceFactor(it) > 0) node.leftChild = rotateLeft(it) // left-right case + return rotateRight(node) + } + } else if (balanceFactor > 1) { // *-left cases + node.rightChild?.let { + if (getBalanceFactor(it) < 0) node.rightChild = rotateRight(it) // right-left case + return rotateLeft(node) + } + } + + return node } -} \ No newline at end of file +} From f132809c4ff479dfdc1d6305422820993bad9ce9 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 20 Mar 2024 10:03:54 +0300 Subject: [PATCH 029/164] fix: delete excess import --- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index 1c4f038..e1e1554 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -1,7 +1,6 @@ package tsl.trees import tsl.nodes.AVLNode -import tsl.nodes.RBNode import kotlin.math.max From 64b011df4a6bd6f2a966638de16356c839f35ca5 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 20 Mar 2024 10:05:54 +0300 Subject: [PATCH 030/164] fix: return value of AVL tree insertion method --- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index e1e1554..d393867 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -5,11 +5,11 @@ import tsl.nodes.AVLNode import kotlin.math.max class AVLTree, V> : AbstractBinaryTree>() { - - // TODO: add return of replaced value or null if key was not present in the tree override fun insert(key: K, value: V): V? { + val prevValue = search(key) // null if key wasn't in the tree insertAndBalanceRecursively(root, key, value) - return value // TODO: DELETE + + return prevValue } private fun insertAndBalanceRecursively(node: AVLNode?, key: K, value: V): AVLNode? { From 36d6b517b43727ed3dbb681c928b227fdc06c6a4 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 20 Mar 2024 10:17:58 +0300 Subject: [PATCH 031/164] fix: return value of AVL tree deletion method --- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index d393867..f7fa610 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -82,8 +82,10 @@ class AVLTree, V> : AbstractBinaryTree>() private fun getHeight(node: AVLNode?) = node?.height ?: -1 override fun delete(key: K): V? { + val prevValue: V = search(key) ?: return null // if key was not in the tree, nothing to delete deleteAndBalanceRecursively(root, key) - return null // TODO: DELETE + + return prevValue } private fun deleteAndBalanceRecursively(node: AVLNode?, key: K): AVLNode? { From b6c9364dbc9d6a73a1d8d6a0f08ca4399cd37774 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 20 Mar 2024 10:45:53 +0300 Subject: [PATCH 032/164] fix: change visibility modifier of root from internal to protected --- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index fdcd241..0f2c745 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -3,7 +3,7 @@ package tsl.trees import tsl.nodes.AbstractNode abstract class AbstractBinaryTree, V, N : AbstractNode> { - internal var root: N? = null + protected var root: N? = null fun search(key: K): V? { return searchNode(root, key) @@ -16,9 +16,9 @@ abstract class AbstractBinaryTree, V, N : AbstractNode, V, N : AbstractNode Date: Wed, 20 Mar 2024 20:59:44 +0300 Subject: [PATCH 033/164] feat: add insertion method into BST tree --- lib/src/main/kotlin/tsl/trees/BSTree.kt | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index e69de29..29cf25c 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -0,0 +1,38 @@ +package tsl.trees + +import tsl.nodes.BSTNode + +class BSTree, V> : AbstractBinaryTree>() { + + override fun insert(key: K, value: V): V? { + val nodeToInsert = BSTNode(key, value) + + if (root == null) { + root = nodeToInsert + return null + } + + var currentNode: BSTNode? = root + while (true) { + if (nodeToInsert.key == currentNode.key) { + val oldValue = currentNode.value + currentNode.value = nodeToInsert.value + return oldValue + } + else if (nodeToInsert.key < currentNode.key) { + if (currentNode.leftChild == null) { + currentNode.leftChild = nodeToInsert + return null + } + currentNode = currentNode.leftChid + } + else if (nodeToInsert.key > currentNode.key) { + if (currentNode.rightChild == null) { + currentNode.rightChild = nodeToInsert + return null + } + currentNode = currentNode.rightChild + } + } + } + From 8d72621b2fff00fed36b79295332394c455466eb Mon Sep 17 00:00:00 2001 From: Karim Shakirov Date: Thu, 21 Mar 2024 14:07:37 +0300 Subject: [PATCH 034/164] feat: add methods that find min and max keys and values in abstract tree class --- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 0f2c745..59c2dce 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -8,6 +8,12 @@ abstract class AbstractBinaryTree, V, N : AbstractNode?, @@ -26,10 +32,35 @@ abstract class AbstractBinaryTree, V, N : AbstractNode): AbstractNode? { + if (node == null) return null + if (node.leftChild == null) return node + return getMinNode(node.leftChild) + } + + protected fun getMaxNode(node: AbstractNode): AbstractNode? { + if (node == null) return null + if (node.leftChild == null) return node + return getMinNode(node.leftChild) + } } From f2baa248d6b89dfd07ab1cfa1cb19a47a236a863 Mon Sep 17 00:00:00 2001 From: Karim Shakirov Date: Thu, 21 Mar 2024 14:22:46 +0300 Subject: [PATCH 035/164] feat: add delete method in BSTree --- lib/src/main/kotlin/tsl/trees/BSTree.kt | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index 29cf25c..db7b453 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -12,7 +12,7 @@ class BSTree, V> : AbstractBinaryTree>() { return null } - var currentNode: BSTNode? = root + var currentNode: BSTNode = root while (true) { if (nodeToInsert.key == currentNode.key) { val oldValue = currentNode.value @@ -36,3 +36,24 @@ class BSTree, V> : AbstractBinaryTree>() { } } + override fun delete(key: K): V? { + val deletedValue? = search(key) ?: return null + deleteRecursively(root, key) + return deletedValue + } + + private fun deleteRecursively(node: BSTNode, key): BSTNode { + if (node == null) return null + if (key < node.key) node.leftChild = deleteRecursively(node.leftChild, key) + else if (key > node.key) node.rightChild = deleteRecursively(node.rightChild, key) + else if (key == node.key) { + if (node.leftChild == null || node.rightChild == null) + return node.leftChild ?: node.rightChild + else { + val minRightNode = getMinNode(node.rightChild) + node.key = minRightNode.key + node.value = minRightNode.value + node.rightChild = deleteRecursively(node.rightChild, minRightNode.key) + } + } + } From 8e0167ef929a447d301e9a3ebe973da71ae7285d Mon Sep 17 00:00:00 2001 From: Karim Shakirov Date: Thu, 21 Mar 2024 14:31:11 +0300 Subject: [PATCH 036/164] fix: add missing arguments to getMaxNode and getMinNode method calls --- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 59c2dce..562c25b 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -34,22 +34,22 @@ abstract class AbstractBinaryTree, V, N : AbstractNode): AbstractNode? { From 3ea48dddc1cd7bd5f040b2173d0a177b38497375 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Fri, 22 Mar 2024 00:57:50 +0300 Subject: [PATCH 037/164] fix: change some method arguments to nullable, add safe-call operators and fix syntactic errors --- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 14 +++++----- lib/src/main/kotlin/tsl/trees/BSTree.kt | 28 ++++++++++--------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 562c25b..24fd3df 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -34,31 +34,31 @@ abstract class AbstractBinaryTree, V, N : AbstractNode): AbstractNode? { + protected fun getMinNode(node: AbstractNode?): AbstractNode? { if (node == null) return null if (node.leftChild == null) return node return getMinNode(node.leftChild) } - protected fun getMaxNode(node: AbstractNode): AbstractNode? { + protected fun getMaxNode(node: AbstractNode?): AbstractNode? { if (node == null) return null if (node.leftChild == null) return node return getMinNode(node.leftChild) diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index db7b453..9986a78 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -11,22 +11,19 @@ class BSTree, V> : AbstractBinaryTree>() { root = nodeToInsert return null } - - var currentNode: BSTNode = root - while (true) { + var currentNode: BSTNode? = root + while (currentNode != null) { if (nodeToInsert.key == currentNode.key) { val oldValue = currentNode.value currentNode.value = nodeToInsert.value return oldValue - } - else if (nodeToInsert.key < currentNode.key) { + } else if (nodeToInsert.key < currentNode.key) { if (currentNode.leftChild == null) { currentNode.leftChild = nodeToInsert return null } - currentNode = currentNode.leftChid - } - else if (nodeToInsert.key > currentNode.key) { + currentNode = currentNode.leftChild + } else if (nodeToInsert.key > currentNode.key) { if (currentNode.rightChild == null) { currentNode.rightChild = nodeToInsert return null @@ -34,15 +31,16 @@ class BSTree, V> : AbstractBinaryTree>() { currentNode = currentNode.rightChild } } + return currentNode?.value } override fun delete(key: K): V? { - val deletedValue? = search(key) ?: return null + val deletedValue: V? = search(key) ?: return null deleteRecursively(root, key) return deletedValue } - private fun deleteRecursively(node: BSTNode, key): BSTNode { + private fun deleteRecursively(node: BSTNode?, key: K): BSTNode? { if (node == null) return null if (key < node.key) node.leftChild = deleteRecursively(node.leftChild, key) else if (key > node.key) node.rightChild = deleteRecursively(node.rightChild, key) @@ -51,9 +49,13 @@ class BSTree, V> : AbstractBinaryTree>() { return node.leftChild ?: node.rightChild else { val minRightNode = getMinNode(node.rightChild) - node.key = minRightNode.key - node.value = minRightNode.value - node.rightChild = deleteRecursively(node.rightChild, minRightNode.key) + if (minRightNode != null) { + node.key = minRightNode.key + node.value = minRightNode.value + node.rightChild = deleteRecursively(node.rightChild, minRightNode.key) + } } } + return node } +} From b40dfb5050d248bd55fd030d6b013b6c9f376794 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Fri, 22 Mar 2024 19:13:24 +0300 Subject: [PATCH 038/164] refactor: delete getMinValue and getMaxValue methods --- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 24fd3df..42c47b9 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -37,21 +37,11 @@ abstract class AbstractBinaryTree, V, N : AbstractNode?): AbstractNode? { if (node == null) return null if (node.leftChild == null) return node From 439aff4f4307870b6136127d55ab93dc26c3fa39 Mon Sep 17 00:00:00 2001 From: Daniel Vlasenco Date: Fri, 22 Mar 2024 19:19:22 +0300 Subject: [PATCH 039/164] Create README.md --- README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..129c616 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# forest-group + +[![Kotlin][kotlin_img]][kotlin_releases_url] +[![License][license_img]][repo_license_url] + +`forest-group` is a library that lets you easily create and use [Binary search trees](https://en.wikipedia.org/wiki/Binary_search_tree), [AVL trees](https://en.wikipedia.org/wiki/AVL_tree) and [Red-black trees](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) in your applications! + + + +## Table of contents +- [About the project](#about-the-project) +- [Usage](#usage) +- [Contributing](#contributing) +- [License](#license) + + + +## About The Project +Tree data structure is well-known for its average logarithmic operations time. However, there are a lot of tree implementations that are strict to data types that can be stored. +There is no problem - *just use keys to store anything you want!* + + + +## Usage +To create a tree, pass the key type and your stored data type to a generic. *Note that your key should implement Comparable type.* + +``` +val myTree = AVLTree() +``` + +You now can simply insert and replace values in your tree: + +``` +myTree.insert(keyA, "Something important") +val oldValue = myTree.insert(keyA, "Something more important") +``` + +You can also search for values and delete values from tree by keys: + +``` +val myValue = myTree.search(myKey) +myTree.delete(myKey) +``` + + + +## Contributing +If you have found a bug, or want to propose some useful feature for our project, please do the following: +1. Fork the Project +2. Create your Feature Branch (git checkout -b feature/MyFeature) +3. Commit your Changes (git commit -m 'add some Feature') +4. Push to the Branch (git push origin feature/MyFeature) +5. Open a Pull Request + + + +## License +Distributed under the MIT License. See LICENSE.md for more information. + + + + + +[license_img]: https://img.shields.io/badge/license-MIT-green +[kotlin_img]: https://img.shields.io/badge/kotlin-magenta + + + +[repo_license_url]: https://github.com/spbu-coding-2023/trees-2/blob/main/LICENSE.md + + + +[kotlin_releases_url]: https://kotlinlang.org/docs/releases.html#release-details From a43da1d824e53a81c394574587eff40ffdf9e623 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Fri, 22 Mar 2024 19:41:18 +0300 Subject: [PATCH 040/164] feat: add method that clears the tree --- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 42c47b9..1dce9ca 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -5,9 +5,6 @@ import tsl.nodes.AbstractNode abstract class AbstractBinaryTree, V, N : AbstractNode> { protected var root: N? = null - fun search(key: K): V? { - return searchNode(root, key) - } abstract fun delete(key: K): V? abstract fun insert( @@ -15,6 +12,10 @@ abstract class AbstractBinaryTree, V, N : AbstractNode?, key: K, @@ -32,6 +33,10 @@ abstract class AbstractBinaryTree, V, N : AbstractNode Date: Fri, 22 Mar 2024 20:01:39 +0300 Subject: [PATCH 041/164] refactor: change serchNode method's return value to node --- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 1dce9ca..d192a74 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -13,23 +13,15 @@ abstract class AbstractBinaryTree, V, N : AbstractNode?, - key: K, - ): V? { - return if (node?.key == key) { - node.value - } else if (node == null) { - null - } else { - if (key < node.key) { - searchNode(node.leftChild, key) - } else { - searchNode(node.rightChild, key) - } + protected fun searchNode(node: AbstractNode?, key: K): AbstractNode? { + return if (node?.key == key) node + else if (node == null) null + else { + if (key < node.key) searchNode(node.leftChild, key) + else searchNode(node.rightChild, key) } } From 029cf32aa01c4ccbb825f95dd62c9ceaba5626ee Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Fri, 22 Mar 2024 23:11:50 +0300 Subject: [PATCH 042/164] feat: make abstract tree class iterable by key/value pairs, add draft of iterator --- .../kotlin/tsl/iterator/BinaryTreeIterator.kt | 16 ++++++++++++++++ .../main/kotlin/tsl/trees/AbstractBinaryTree.kt | 7 ++++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt diff --git a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt new file mode 100644 index 0000000..0303e2c --- /dev/null +++ b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt @@ -0,0 +1,16 @@ +package tsl.iterator + +import tsl.nodes.AbstractNode +import tsl.trees.AbstractBinaryTree + +internal class BinaryTreeIterator, V, N: AbstractNode> + (tree: AbstractBinaryTree): Iterator> { + + override fun hasNext(): Boolean { + TODO("Not yet implemented") + } + + override fun next(): Pair { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index d192a74..f6d90e0 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -1,8 +1,9 @@ package tsl.trees import tsl.nodes.AbstractNode +import tsl.iterator.BinaryTreeIterator -abstract class AbstractBinaryTree, V, N : AbstractNode> { +abstract class AbstractBinaryTree, V, N : AbstractNode>: Iterable> { protected var root: N? = null abstract fun delete(key: K): V? @@ -50,4 +51,8 @@ abstract class AbstractBinaryTree, V, N : AbstractNode> { + return BinaryTreeIterator(this) + } } From ddfd6ae8d605238b5500559f8f9cc984bdd90c67 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Fri, 22 Mar 2024 19:55:23 +0300 Subject: [PATCH 043/164] refactor: AVL tree and AVL node classes --- lib/src/main/kotlin/tsl/nodes/AVLNode.kt | 2 +- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 124 ++++++++++++----------- 2 files changed, 65 insertions(+), 61 deletions(-) diff --git a/lib/src/main/kotlin/tsl/nodes/AVLNode.kt b/lib/src/main/kotlin/tsl/nodes/AVLNode.kt index 0337c2c..0eb8926 100644 --- a/lib/src/main/kotlin/tsl/nodes/AVLNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/AVLNode.kt @@ -1,5 +1,5 @@ package tsl.nodes class AVLNode, V>(key: K, value: V) : AbstractNode>(key, value) { - var height = 0 + internal var height = 0 } diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index f7fa610..f7eb33c 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -6,10 +6,9 @@ import kotlin.math.max class AVLTree, V> : AbstractBinaryTree>() { override fun insert(key: K, value: V): V? { - val prevValue = search(key) // null if key wasn't in the tree + val replacedValue = search(key) // null if key isn't in the tree insertAndBalanceRecursively(root, key, value) - - return prevValue + return replacedValue } private fun insertAndBalanceRecursively(node: AVLNode?, key: K, value: V): AVLNode? { @@ -29,14 +28,68 @@ class AVLTree, V> : AbstractBinaryTree>() val balanceFactor = getBalanceFactor(node) - if (balanceFactor < -1) { // *-right cases + if (balanceFactor < -1) { // right cases node.leftChild?.let { - if (key > it.key) node.leftChild = rotateLeft(it) // left-right case + if (key > it.key) { + node.leftChild = rotateLeft(it) // left-right case + } return rotateRight(node) } - } else if (balanceFactor > 1) { // *-left cases + } else if (balanceFactor > 1) { // left cases node.rightChild?.let { - if (key < it.key) node.rightChild = rotateRight(it) // right-left case + if (key < it.key) { + node.rightChild = rotateRight(it) // right-left case + } + return rotateLeft(node) + } + } + + return node + } + + override fun delete(key: K): V? { + val deletedValue: V = search(key) ?: return null // if key isn't in the tree, there's nothing to delete + deleteAndBalanceRecursively(root, key) + return deletedValue + } + + private fun deleteAndBalanceRecursively(node: AVLNode?, key: K): AVLNode? { + if (node == null) return null // node to be deleted was not found + + if (key < node.key) { + node.leftChild = deleteAndBalanceRecursively(node.leftChild, key) + } else if (key > node.key) { + node.rightChild = deleteAndBalanceRecursively(node.rightChild, key) + } else if (node.leftChild == null || node.rightChild == null) { // if node to be deleted has 0 or 1 child + return node.leftChild ?: node.rightChild + } else { // if node to be deleted has 2 children + val successor = getMinNode(node.rightChild) + if (successor != null) { + node.key = successor.key + node.value = successor.value + + // delete successor node from tree + val temp = deleteAndBalanceRecursively(node.rightChild, successor.key) + if (temp != null) node.rightChild = temp + } + } + + updateHeight(node) + + val balanceFactor = getBalanceFactor(node) + + if (balanceFactor < -1) { // right rotation cases + node.leftChild?.let { + if (getBalanceFactor(it) > 0) { // left-right case + node.leftChild = rotateLeft(it) + } + return rotateRight(node) + } + } else if (balanceFactor > 1) { // left rotation cases + node.rightChild?.let { + if (getBalanceFactor(it) < 0) { // right-left case + node.rightChild = rotateRight(it) + } return rotateLeft(node) } } @@ -49,7 +102,7 @@ class AVLTree, V> : AbstractBinaryTree>() oldUpperNode.leftChild = newUpperNode.rightChild newUpperNode.rightChild = oldUpperNode - if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root + if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root updateHeight(oldUpperNode) updateHeight(newUpperNode) @@ -62,7 +115,7 @@ class AVLTree, V> : AbstractBinaryTree>() oldUpperNode.rightChild = newUpperNode.leftChild newUpperNode.leftChild = oldUpperNode - if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root + if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root updateHeight(oldUpperNode) updateHeight(newUpperNode) @@ -79,56 +132,7 @@ class AVLTree, V> : AbstractBinaryTree>() return getHeight(node.rightChild) - getHeight(node.leftChild) } - private fun getHeight(node: AVLNode?) = node?.height ?: -1 - - override fun delete(key: K): V? { - val prevValue: V = search(key) ?: return null // if key was not in the tree, nothing to delete - deleteAndBalanceRecursively(root, key) - - return prevValue - } - - private fun deleteAndBalanceRecursively(node: AVLNode?, key: K): AVLNode? { - if (node == null) return null - - if (key < node.key) { - node.leftChild = deleteAndBalanceRecursively(node.leftChild, key) - } else if (key > node.key) { - node.rightChild = deleteAndBalanceRecursively(node.rightChild, key) - } else { - if (node.leftChild == null || node.rightChild == null) { // case of 0 or 1 child - val temp = node.leftChild ?: node.rightChild - return temp - } else { // case of 2 children - var successor = node.rightChild - while (successor?.leftChild != null) { - successor = successor.leftChild - } - if (successor != null) { - node.key = successor.key - node.value = successor.value - val temp = deleteAndBalanceRecursively(node.rightChild, successor.key) - if (temp != null) node.rightChild = temp - } - } - } - - updateHeight(node) - - val balanceFactor = getBalanceFactor(node) - - if (balanceFactor < -1) { // *-right cases - node.leftChild?.let { - if (getBalanceFactor(it) > 0) node.leftChild = rotateLeft(it) // left-right case - return rotateRight(node) - } - } else if (balanceFactor > 1) { // *-left cases - node.rightChild?.let { - if (getBalanceFactor(it) < 0) node.rightChild = rotateRight(it) // right-left case - return rotateLeft(node) - } - } - - return node + private fun getHeight(node: AVLNode?): Int { + return node?.height ?: -1 } } From f9d40daca1ef424cb797f814fe9c0ac51120918c Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sat, 23 Mar 2024 11:54:38 +0300 Subject: [PATCH 044/164] refactor: Binary search tree and Abstract tree classes --- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 32 ++++++++----------- lib/src/main/kotlin/tsl/trees/BSTree.kt | 4 ++- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index f6d90e0..7e1b6c7 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -3,7 +3,7 @@ package tsl.trees import tsl.nodes.AbstractNode import tsl.iterator.BinaryTreeIterator -abstract class AbstractBinaryTree, V, N : AbstractNode>: Iterable> { +abstract class AbstractBinaryTree, V, N : AbstractNode> : Iterable> { protected var root: N? = null abstract fun delete(key: K): V? @@ -17,13 +17,11 @@ abstract class AbstractBinaryTree, V, N : AbstractNode?, key: K): AbstractNode? { - return if (node?.key == key) node - else if (node == null) null - else { - if (key < node.key) searchNode(node.leftChild, key) - else searchNode(node.rightChild, key) - } + private fun searchNode(node: AbstractNode?, key: K): AbstractNode? { + return if (node == null) null + else if (node.key == key) node + else if (key < node.key) searchNode(node.leftChild, key) + else searchNode(node.rightChild, key) } fun clear() { @@ -31,25 +29,23 @@ abstract class AbstractBinaryTree, V, N : AbstractNode?): AbstractNode? { - if (node == null) return null - if (node.leftChild == null) return node - return getMinNode(node.leftChild) + protected fun getMinNode(node: N?): N? { + return if (node == null) null + else if (node.leftChild == null) node + else getMinNode(node.leftChild) } - protected fun getMaxNode(node: AbstractNode?): AbstractNode? { - if (node == null) return null - if (node.leftChild == null) return node - return getMinNode(node.leftChild) + private fun getMaxNode(node: N?): N? { + return if (node == null) null + else if (node.leftChild == null) node + else getMaxNode(node.leftChild) } override fun iterator(): Iterator> { diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index 9986a78..c5d1fb8 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -11,7 +11,9 @@ class BSTree, V> : AbstractBinaryTree>() { root = nodeToInsert return null } - var currentNode: BSTNode? = root + + var currentNode = root + while (currentNode != null) { if (nodeToInsert.key == currentNode.key) { val oldValue = currentNode.value From 73193896e33c68cf7309f78bab71e41fbb33d1e0 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sat, 23 Mar 2024 14:33:07 +0300 Subject: [PATCH 045/164] feat: add next and hasNext methods to iterator --- .../kotlin/tsl/iterator/BinaryTreeIterator.kt | 20 +++++++++++++++---- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 6 +++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt index 0303e2c..cccd2fa 100644 --- a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt +++ b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt @@ -4,13 +4,25 @@ import tsl.nodes.AbstractNode import tsl.trees.AbstractBinaryTree internal class BinaryTreeIterator, V, N: AbstractNode> - (tree: AbstractBinaryTree): Iterator> { + (tree: AbstractBinaryTree): Iterator> { + + private var stack = ArrayDeque() + private var currentNode: N? = tree.root override fun hasNext(): Boolean { - TODO("Not yet implemented") + return (!stack.isEmpty() || currentNode != null) } - override fun next(): Pair { - TODO("Not yet implemented") + override fun next(): Pair { + while (currentNode != null) { + currentNode?.let { stack.addLast(it) } + currentNode = currentNode?.leftChild + } + + currentNode = stack.removeLast() + val nextNode = currentNode + currentNode = currentNode?.rightChild + + return Pair(nextNode?.key, nextNode?.value) } } \ No newline at end of file diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 7e1b6c7..64143b0 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -3,8 +3,8 @@ package tsl.trees import tsl.nodes.AbstractNode import tsl.iterator.BinaryTreeIterator -abstract class AbstractBinaryTree, V, N : AbstractNode> : Iterable> { - protected var root: N? = null +abstract class AbstractBinaryTree, V, N : AbstractNode>: Iterable> { + internal var root: N? = null abstract fun delete(key: K): V? @@ -48,7 +48,7 @@ abstract class AbstractBinaryTree, V, N : AbstractNode> { + override fun iterator(): Iterator> { return BinaryTreeIterator(this) } } From aa8c49c256056ef1297d1fe3a0a59907380609ee Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 24 Mar 2024 13:41:07 +0300 Subject: [PATCH 046/164] refactor: change types in searchNode method from AbsctractNode? to N? --- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 64143b0..5ec3f1a 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -17,7 +17,7 @@ abstract class AbstractBinaryTree, V, N : AbstractNode?, key: K): AbstractNode? { + private fun searchNode(node: N?, key: K): N? { return if (node == null) null else if (node.key == key) node else if (key < node.key) searchNode(node.leftChild, key) From 5fd7c5998560e4d1b1de0af7e3fbcf98a8327491 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:57:55 +0300 Subject: [PATCH 047/164] fix: resolve conflict which cause the signature difference --- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 5ec3f1a..2edaca2 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -17,7 +17,7 @@ abstract class AbstractBinaryTree, V, N : AbstractNode?, key: K): AbstractNode? { return if (node == null) null else if (node.key == key) node else if (key < node.key) searchNode(node.leftChild, key) @@ -42,10 +42,10 @@ abstract class AbstractBinaryTree, V, N : AbstractNode> { From 630ec1b624905d61ba3086d7c843ef7e61cac361 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 24 Mar 2024 10:45:00 +0300 Subject: [PATCH 048/164] fix: rewrote the 'delete' method for RB tree --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 159 ++++++------------------ 1 file changed, 41 insertions(+), 118 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 882572d..bc93ffd 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -6,7 +6,7 @@ class RBTree, V> : AbstractBinaryTree>() { override fun insert( key: K, value: V, - ): V? { // in case we inserted successfully - > return null -> else -> return value, so user could have another try to insert it + ): V? { // in case key isnt in the tree/ inserts successfully-> return null -> else -> return value val newNode = RBNode(key, value) if (root == null) { @@ -27,8 +27,7 @@ class RBTree, V> : AbstractBinaryTree>() { return null } currentNode = currentNode.leftChild - } else if (currentNode.key < newNode.key) - { + } else if (currentNode.key < newNode.key) { if (currentNode.rightChild == null) { currentNode.rightChild = newNode newNode.parent = currentNode @@ -37,6 +36,7 @@ class RBTree, V> : AbstractBinaryTree>() { } currentNode = currentNode.rightChild } else { + balanceNode(newNode) return value } } @@ -98,128 +98,51 @@ class RBTree, V> : AbstractBinaryTree>() { } override fun delete(key: K): V? { - val deleteNode: RBNode = searchNode(key) ?: return null // track node that will replace other one - val deleteNodeValue = deleteNode.value - var colorOfTransferringNode = deleteNode.color - val childNode: RBNode? // node that will be on the place of deleteNode - - if (getChildrenCount(deleteNode) < 2) { - childNode = if (deleteNode.leftChild != null) deleteNode.leftChild else deleteNode.rightChild - transplantTwoNodes(deleteNode, childNode) - } else { - val minNode = getMin(deleteNode.rightChild) - if (minNode != null) { - deleteNode.key = minNode.key - deleteNode.value = minNode.value - colorOfTransferringNode = minNode.color - } - childNode = if (minNode?.leftChild != null) minNode.leftChild else minNode?.rightChild - transplantTwoNodes(minNode, childNode) - } - - if (colorOfTransferringNode == RBNode.Color.Black) fixAfterDelete(childNode) - - return deleteNodeValue // in case the deleting process was successful - we return value of deleted node. in other case - null + root = deleteNode(root, key) + root?.color = RBNode.Color.Black // Ensure the root is black after deletion + return null } - // TODO: fix case of deleting the root - - private fun fixAfterDelete(node: RBNode?) { - var fixNode: RBNode? = node - var fixNodeBrother: RBNode? - while (fixNode != root && fixNode?.color == RBNode.Color.Black) { - if (fixNode == fixNode.parent?.leftChild) { - val parent = fixNode.parent - fixNodeBrother = parent?.rightChild - if (fixNodeBrother?.leftChild?.color == RBNode.Color.Black && fixNodeBrother.rightChild?.color == RBNode.Color.Black) { - fixNodeBrother.color = RBNode.Color.Red - fixNode = fixNode.parent - } else { - if (fixNodeBrother?.rightChild?.color == RBNode.Color.Black) { - fixNodeBrother.leftChild?.color = RBNode.Color.Black - fixNodeBrother.color = RBNode.Color.Red - fixNodeBrother.rotateRight() - fixNodeBrother = fixNode.parent?.rightChild - } - if (fixNodeBrother != null && fixNode.parent != null) { - fixNode.parent?.color.also { if (it != null) fixNodeBrother?.color = it } - /*this line of code checks if the color property of the parent of fixNode is not null, - * and if it isn't, it assigns that color to the color property of fixNodeBrother. - * safe call operator ensures that the code doesn't throw a NullPointerException - * if any of the properties are null. */ - } - fixNode.parent?.color = RBNode.Color.Black - fixNodeBrother?.rightChild?.color = RBNode.Color.Black - fixNode.parent?.rotateLeft() - fixNode = root - } - } else { - fixNodeBrother = fixNode.parent?.leftChild - if (fixNodeBrother?.color == RBNode.Color.Red) { - fixNodeBrother.color = RBNode.Color.Black - fixNode.parent?.color = RBNode.Color.Red - fixNode.parent?.rotateRight() - fixNodeBrother = fixNode.parent?.leftChild - } - if (fixNodeBrother?.leftChild?.color == RBNode.Color.Black && fixNodeBrother.rightChild?.color == RBNode.Color.Black) { - fixNode.parent?.rotateRight() - fixNodeBrother = fixNode.parent?.leftChild - } else { - if (fixNodeBrother?.leftChild?.color == RBNode.Color.Black) { - fixNodeBrother.rightChild?.color = RBNode.Color.Black - fixNodeBrother.color = RBNode.Color.Red - fixNodeBrother.rotateLeft() - fixNodeBrother = fixNode.parent?.leftChild - } - fixNode.parent?.color.also { if (it != null) fixNodeBrother?.color = it } - fixNode.parent?.color = RBNode.Color.Black - fixNodeBrother?. leftChild?.color = RBNode.Color.Black - fixNode.parent?.rotateRight() - fixNode = root - } - } - } - fixNode?.color = RBNode.Color.Black - } + private fun deleteNode(node: RBNode?, key: K): RBNode? { + var current: RBNode? = searchNode(root, key) as RBNode? + if (current == null) return root + var newRoot = root - private fun searchNode(key: K): RBNode? { - var currentNode = root - while (currentNode != null) { - if (key == currentNode.key) { - return currentNode + if (current.leftChild != null && current.rightChild != null) { + val successor = getMaxNode(current.leftChild) + if (successor != null) { + current.value = successor.value + current.key = successor.key } - currentNode = if (key < currentNode.key) currentNode.leftChild else currentNode.rightChild + current = successor } - return null - } - - // TODO: move this type of methods to abstract mb - - // this method movest the parent of 2nd node -> to 1st node - private fun transplantTwoNodes(firstNode: RBNode? , secondNode: RBNode?) { - if (firstNode == root) root = secondNode - else if (firstNode?.parent?.leftChild == firstNode) firstNode?.parent?.leftChild = secondNode - else firstNode?.parent?.rightChild = secondNode - secondNode?.parent = firstNode?.parent - } - - private fun getChildrenCount(node: RBNode?): Int { - if (node == null) return 0 - var count = 0 - - if (node.leftChild != null) count++ - if (node.rightChild != null) count++ - return count - } - private fun getMin(node: RBNode?): RBNode? { - if (node == null) return null - var current: RBNode = node + val child = if (current?.leftChild != null) current.leftChild else current?.rightChild + if (child != null) { + child.parent = current?.parent + if (current?.parent == null) return child + if (current == current.parent?.leftChild) { + current.parent?.leftChild = child + } else { + current.parent?.rightChild = child + } - while (current.leftChild != null) { - current.leftChild.also { if (it != null) current = it } + if (current.color == RBNode.Color.Black) { + newRoot = fixAfterDeletion(child) + } + } else if (current?.parent == null) { + return null + } else { + if (current.color == RBNode.Color.Black) { + newRoot = fixAfterDeletion(current) + } + if (current.parent?.leftChild == current) { + current.parent?.leftChild = null + } else { + current.parent?.rightChild = null + } } - return current + return newRoot } -} +} \ No newline at end of file From ff71aecc3f943d03ba6d50fb1e4194b88a0cb13d Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 24 Mar 2024 10:56:46 +0300 Subject: [PATCH 049/164] feat: add 'fixAfterDeletion' method that help to rebalance the tree after delete --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 63 +++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index bc93ffd..2493a4f 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -145,4 +145,67 @@ class RBTree, V> : AbstractBinaryTree>() { } return newRoot } + + private fun fixAfterDeletion(currentNode: RBNode?): RBNode? { + var fixedRoot = root + var node = currentNode + + while (node != root && node?.color == RBNode.Color.Black) { + if (node == node.parent?.leftChild) { + var currentSibling: RBNode? = node.parent?.rightChild + if (currentSibling?.color == RBNode.Color.Red) { // 1st case "currentSibling is red" + currentSibling.color = RBNode.Color.Black + node.parent?.color = RBNode.Color.Red + fixedRoot = rotateLeft(node.parent, fixedRoot) + currentSibling = node.parent?.rightChild // transfer to other cases + } + else if (currentSibling?.leftChild?.color == RBNode.Color.Black && currentSibling.rightChild?.color == RBNode.Color.Black) { + // 2nd case "currentSibling and its children are black" + currentSibling.color = RBNode.Color.Red + node = node.parent + } else { + if (currentSibling?.rightChild?.color == RBNode.Color.Black) { + // 3rd case "like 2nd but the left child is red" + (currentSibling.leftChild )?.color = RBNode.Color.Black + currentSibling.color = RBNode.Color.Red + fixedRoot = rotateRight(currentSibling, fixedRoot) + currentSibling = node.parent?.rightChild + } + currentSibling?.color = node.parent?.color!! // 4th case "black currentSibling, right child is black' + node.parent?.color = RBNode.Color.Black + currentSibling?.rightChild?.color = RBNode.Color.Black + fixedRoot = rotateLeft(node.parent, fixedRoot) + node = fixedRoot + } + } else { + var currentSibling: RBNode? = node.parent?.leftChild + if (currentSibling?.color == RBNode.Color.Red) { + // 1st case "red currentSibling" + currentSibling.color = RBNode.Color.Black + node.parent?.color = RBNode.Color.Red + fixedRoot = rotateRight(node.parent, fixedRoot) + currentSibling = node.parent?.leftChild // transfer to other cases + } + // 2nd case "currentSibling and its children are black" + if (currentSibling?.leftChild?.color == RBNode.Color.Black && currentSibling.rightChild?.color == RBNode.Color.Black) { + currentSibling.color = RBNode.Color.Red + node = node.parent + } else { + if (currentSibling?.leftChild?.color == RBNode.Color.Black) { // 3rd case "like 2nd but the right child is red" + currentSibling.rightChild?.color = RBNode.Color.Black + currentSibling.color = RBNode.Color.Red + fixedRoot = rotateLeft(currentSibling, fixedRoot) + currentSibling = node.parent?.leftChild + } + currentSibling?.color = node.parent?.color!! // 4th case "black currentSibling, left child is red' + (node.parent)?.color = RBNode.Color.Black + (currentSibling?.leftChild)?.color = RBNode.Color.Black + fixedRoot = rotateRight(node.parent, fixedRoot) + node = fixedRoot + } + } + } + node?.color = RBNode.Color.Black + return fixedRoot + } } \ No newline at end of file From f1af74a32f6095b95bc04f7e9db6c7e3af433a38 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 24 Mar 2024 11:07:53 +0300 Subject: [PATCH 050/164] refactor: better comments, get rif of 'git add RBTree.kt', switch to more understandable variable names in 'fixAfterDelete' --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 56 +++++++++++++------------ 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 2493a4f..04f930e 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -146,66 +146,70 @@ class RBTree, V> : AbstractBinaryTree>() { return newRoot } - private fun fixAfterDeletion(currentNode: RBNode?): RBNode? { + private fun fixAfterDeletion(currentDeletingNode: RBNode?): RBNode? { var fixedRoot = root - var node = currentNode + var currentNode = currentDeletingNode + // currentNode - variable that is used to perform various operations and control the loop of fixing the tree. - while (node != root && node?.color == RBNode.Color.Black) { - if (node == node.parent?.leftChild) { - var currentSibling: RBNode? = node.parent?.rightChild + while (currentNode != root && currentNode?.color == RBNode.Color.Black) { + if (currentNode == currentNode.parent?.leftChild) { + var currentSibling: RBNode? = currentNode.parent?.rightChild if (currentSibling?.color == RBNode.Color.Red) { // 1st case "currentSibling is red" currentSibling.color = RBNode.Color.Black - node.parent?.color = RBNode.Color.Red - fixedRoot = rotateLeft(node.parent, fixedRoot) - currentSibling = node.parent?.rightChild // transfer to other cases + currentNode.parent?.color = RBNode.Color.Red + fixedRoot = rotateLeft(currentNode.parent, fixedRoot) + currentSibling = currentNode.parent?.rightChild // transfer to other cases } else if (currentSibling?.leftChild?.color == RBNode.Color.Black && currentSibling.rightChild?.color == RBNode.Color.Black) { // 2nd case "currentSibling and its children are black" currentSibling.color = RBNode.Color.Red - node = node.parent + currentNode = currentNode.parent } else { if (currentSibling?.rightChild?.color == RBNode.Color.Black) { // 3rd case "like 2nd but the left child is red" (currentSibling.leftChild )?.color = RBNode.Color.Black currentSibling.color = RBNode.Color.Red fixedRoot = rotateRight(currentSibling, fixedRoot) - currentSibling = node.parent?.rightChild + currentSibling = currentNode.parent?.rightChild } - currentSibling?.color = node.parent?.color!! // 4th case "black currentSibling, right child is black' - node.parent?.color = RBNode.Color.Black + // 4th case "black currentSibling, right child is black' + currentSibling?.color = currentNode.parent?.color ?: RBNode.Color.Black + currentNode.parent?.color = RBNode.Color.Black currentSibling?.rightChild?.color = RBNode.Color.Black - fixedRoot = rotateLeft(node.parent, fixedRoot) - node = fixedRoot + fixedRoot = rotateLeft(currentNode.parent, fixedRoot) + currentNode = fixedRoot } } else { - var currentSibling: RBNode? = node.parent?.leftChild + var currentSibling: RBNode? = currentNode.parent?.leftChild if (currentSibling?.color == RBNode.Color.Red) { // 1st case "red currentSibling" currentSibling.color = RBNode.Color.Black - node.parent?.color = RBNode.Color.Red - fixedRoot = rotateRight(node.parent, fixedRoot) - currentSibling = node.parent?.leftChild // transfer to other cases + currentNode.parent?.color = RBNode.Color.Red + fixedRoot = rotateRight(currentNode.parent, fixedRoot) + currentSibling = currentNode.parent?.leftChild // transfer to other cases } // 2nd case "currentSibling and its children are black" if (currentSibling?.leftChild?.color == RBNode.Color.Black && currentSibling.rightChild?.color == RBNode.Color.Black) { currentSibling.color = RBNode.Color.Red - node = node.parent + currentNode = currentNode.parent } else { - if (currentSibling?.leftChild?.color == RBNode.Color.Black) { // 3rd case "like 2nd but the right child is red" + // 3rd case "like 2nd but the right child is red" + if (currentSibling?.leftChild?.color == RBNode.Color.Black) { currentSibling.rightChild?.color = RBNode.Color.Black currentSibling.color = RBNode.Color.Red fixedRoot = rotateLeft(currentSibling, fixedRoot) - currentSibling = node.parent?.leftChild + currentSibling = currentNode.parent?.leftChild } - currentSibling?.color = node.parent?.color!! // 4th case "black currentSibling, left child is red' - (node.parent)?.color = RBNode.Color.Black + // 4th case "black currentSibling, left child is red' + currentSibling?.color = currentNode.parent?.color ?: RBNode.Color.Black + (currentNode.parent)?.color = RBNode.Color.Black (currentSibling?.leftChild)?.color = RBNode.Color.Black - fixedRoot = rotateRight(node.parent, fixedRoot) - node = fixedRoot + fixedRoot = rotateRight(currentNode.parent, fixedRoot) + currentNode = fixedRoot } } } - node?.color = RBNode.Color.Black + currentNode?.color = RBNode.Color.Black return fixedRoot } } \ No newline at end of file From e8a07571adfe009ca8125c4d5100915205ed973c Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 24 Mar 2024 12:51:20 +0300 Subject: [PATCH 051/164] fix: move rotation methods to the tree file --- lib/src/main/kotlin/tsl/nodes/RBNode.kt | 36 ------------------------- 1 file changed, 36 deletions(-) diff --git a/lib/src/main/kotlin/tsl/nodes/RBNode.kt b/lib/src/main/kotlin/tsl/nodes/RBNode.kt index c840fad..6427626 100644 --- a/lib/src/main/kotlin/tsl/nodes/RBNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/RBNode.kt @@ -18,41 +18,5 @@ class RBNode, V>(key: K, value: V) : AbstractNode dad.leftChild = rightChild - this == dad?.rightChild -> dad.rightChild = rightChild - } - - this.parent = rightChild - rightChild.parent = dad - } - - internal fun rotateRight() { - val leftChild = this.leftChild ?: return - val dad = this.parent - - this.switchColor(leftChild) - leftChild.rightChild?.parent = this - this.leftChild = leftChild.rightChild - leftChild.rightChild = this - - when { - this == dad?.leftChild -> dad.leftChild = leftChild - this == dad?.rightChild -> dad.rightChild = leftChild - } - - this.parent = leftChild - leftChild.parent = dad - } } From b45b7f85ca5d8226afdf3ce592a1e39881be6c0a Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 24 Mar 2024 12:54:53 +0300 Subject: [PATCH 052/164] feat: add rotate methods to tree class --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 52 ++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 04f930e..ee8954d 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -99,7 +99,7 @@ class RBTree, V> : AbstractBinaryTree>() { override fun delete(key: K): V? { root = deleteNode(root, key) - root?.color = RBNode.Color.Black // Ensure the root is black after deletion + root?.color = RBNode.Color.Black // ensure the root is black after deletion return null } @@ -212,4 +212,54 @@ class RBTree, V> : AbstractBinaryTree>() { currentNode?.color = RBNode.Color.Black return fixedRoot } + private fun rotateRight(node: RBNode?, currentRoot: RBNode?): RBNode? { + val temp = node?.leftChild ?: return node // if left child is null, no rotation needed + var newRoot = currentRoot + temp.parent = node.parent + + if (temp.leftChild != null) { + temp.leftChild?.parent = node + } + temp.rightChild = node + node.parent = temp + + if (temp.parent != null) { + if (node == temp.parent?.leftChild) { + temp.parent?.leftChild = temp + } else { + temp.parent?.rightChild = temp + } + } else { + root = temp + } + + return newRoot + } + + private fun rotateLeft(node: RBNode?, currentRoot: RBNode?): RBNode? { + val temp = node?.rightChild ?: return node // if right child is null, no rotation needed + var newRoot = currentRoot + temp.parent = node.parent + + node.rightChild = temp.leftChild; + if (node.rightChild != null) { + node.rightChild?.parent = node; + } + + temp.leftChild = node; + node.parent = temp; + + // temp took over node's place so now its parent should point to temp + if (temp.parent != null) { + if (node == temp.parent?.leftChild) { + temp.parent?.leftChild = temp; + } else { + temp.parent?.rightChild = temp; + } + } else { + root = temp; + newRoot = temp + } + return newRoot + } } \ No newline at end of file From cb08ceb2ad624c3250f7f60fdaf794da5cb3d9eb Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:05:06 +0300 Subject: [PATCH 053/164] fix: correct work of balance after insert, get rid of unnecessary cases --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 72 ++++++++++++------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index ee8954d..50f4cf5 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -47,54 +47,48 @@ class RBTree, V> : AbstractBinaryTree>() { private fun balanceNode(node: RBNode?) { var newNode = node var uncle: RBNode? - while (newNode?.parent?.color == RBNode.Color.Red) { + var newRoot = root + while (newNode?.parent?.color == RBNode.Color.Red && newNode.parent != newRoot) { + var currentParent: RBNode? = newNode.parent + val currentGrandParent: RBNode? = currentParent?.parent if (newNode.parent == newNode.parent?.parent?.leftChild) { - uncle = newNode.parent?.parent?.rightChild - - when { - uncle?.color == RBNode.Color.Red -> { - newNode.parent?.color = RBNode.Color.Black - uncle.color = RBNode.Color.Black - newNode.parent?.parent?.color = RBNode.Color.Red - newNode = newNode.parent?.parent - } + uncle = currentParent?.parent?.rightChild - newNode == newNode.parent?.rightChild -> { - newNode = newNode.parent - if (newNode?.parent?.parent == null) root = newNode?.parent - newNode?.rotateLeft() - } - - newNode == newNode.parent?.leftChild -> { - if (newNode.parent?.parent?.parent == null) root = newNode.parent - newNode.parent?.parent?.rotateRight() + if (uncle?.color == RBNode.Color.Red) { + currentParent?.color = RBNode.Color.Black + uncle.color = RBNode.Color.Black + currentGrandParent?.color = RBNode.Color.Red + newNode = currentGrandParent + } else { + if (newNode == newNode.parent?.rightChild) { + newNode = currentParent + newRoot = rotateLeft(newNode, newRoot) + currentParent = newNode?.parent } + currentParent?.color = RBNode.Color.Black + currentGrandParent?.color = RBNode.Color.Red + newRoot = rotateRight(currentGrandParent, newRoot) } - } else { + } else if (currentParent == currentGrandParent?.rightChild) { uncle = newNode.parent?.parent?.leftChild - - when { - uncle?.color == RBNode.Color.Red -> { - newNode.parent?.color = RBNode.Color.Black - uncle.color = RBNode.Color.Black - newNode.parent?.parent?.color = RBNode.Color.Red - newNode = newNode.parent?.parent - } - - newNode == newNode.parent?.leftChild -> { - newNode = newNode.parent - if (newNode?.parent?.parent == null) root = newNode?.parent - newNode?.rotateRight() - } - - newNode == newNode.parent?.rightChild -> { - if (newNode.parent?.parent?.parent == null) root = newNode.parent - newNode.parent?.parent?.rotateLeft() + if (uncle?.color == RBNode.Color.Red) { + currentParent?.color = RBNode.Color.Black + uncle.color = RBNode.Color.Black + currentGrandParent?.color = RBNode.Color.Red + newNode = currentGrandParent + } else { + if (newNode == currentParent?.leftChild) { + newNode = currentParent + newRoot = rotateRight(newNode, newRoot) + currentParent = newNode.parent } + currentParent?.color = RBNode.Color.Black + currentGrandParent?.color = RBNode.Color.Red + newRoot = rotateLeft(currentGrandParent, newRoot) } } } - root?.color = RBNode.Color.Black + root = newRoot } override fun delete(key: K): V? { From 7e1f77a96b7e28e4450e72f2922dcd335a84e580 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:19:44 +0300 Subject: [PATCH 054/164] refactor: add docs-typed comments for stressed methods --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 50f4cf5..a1a50e2 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -44,6 +44,14 @@ class RBTree, V> : AbstractBinaryTree>() { return null } + /** + * This function is used to balance the red-black tree node after insertion. + * It checks the colors of the parent and grandparent nodes to determine the rotations needed for balancing. + * The function alternates between left and right rotations to maintain the balance of the tree. + * The root variable is updated to maintain the correct root node after balancing. + * @param RBNode-typed node + */ + private fun balanceNode(node: RBNode?) { var newNode = node var uncle: RBNode? @@ -97,6 +105,15 @@ class RBTree, V> : AbstractBinaryTree>() { return null } + /** + * Deletes a node with the specified key from the Red\-Black Tree\. + * If the node with the key is found, it is removed from the tree and the tree is rebalanced if necessary\. + * + * @param node The root node of the Red\-Black Tree\. + * @param key The key of the node to be deleted\. + * @return The new root node of the Red\-Black Tree after deletion\. + */ + private fun deleteNode(node: RBNode?, key: K): RBNode? { var current: RBNode? = searchNode(root, key) as RBNode? if (current == null) return root @@ -140,6 +157,16 @@ class RBTree, V> : AbstractBinaryTree>() { return newRoot } + /** + * This function is used to fix the red-black tree structure after a node deletion to ensure that the red-black + * properties of the tree are maintained. + * It iterates through the nodes and performs rotations and recoloring based on the cases derived from the sibling + * and its children's colors to balance the tree. + * + * @param currentNode The node that needs fixing after deletion + * @return The root of the tree after fixing the structure to maintain the red-black properties + */ + private fun fixAfterDeletion(currentDeletingNode: RBNode?): RBNode? { var fixedRoot = root var currentNode = currentDeletingNode From 688c5d640621b5ab6da27e94bb3e9f75869f68f5 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:31:44 +0300 Subject: [PATCH 055/164] refactor: linter corrections, edit variables names in rotation methods --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 98 ++++++++++++++----------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index a1a50e2..d4d47af 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -61,7 +61,6 @@ class RBTree, V> : AbstractBinaryTree>() { val currentGrandParent: RBNode? = currentParent?.parent if (newNode.parent == newNode.parent?.parent?.leftChild) { uncle = currentParent?.parent?.rightChild - if (uncle?.color == RBNode.Color.Red) { currentParent?.color = RBNode.Color.Black uncle.color = RBNode.Color.Black @@ -106,15 +105,18 @@ class RBTree, V> : AbstractBinaryTree>() { } /** - * Deletes a node with the specified key from the Red\-Black Tree\. - * If the node with the key is found, it is removed from the tree and the tree is rebalanced if necessary\. - * - * @param node The root node of the Red\-Black Tree\. - * @param key The key of the node to be deleted\. - * @return The new root node of the Red\-Black Tree after deletion\. - */ + * Deletes a node with the specified key from the Red\-Black Tree\. + * If the node with the key is found, it is removed from the tree and the tree is rebalanced if necessary\. + * + * @param node The root node of the Red\-Black Tree\. + * @param key The key of the node to be deleted\. + * @return The new root node of the Red\-Black Tree after deletion\. + */ - private fun deleteNode(node: RBNode?, key: K): RBNode? { + private fun deleteNode( + node: RBNode?, + key: K, + ): RBNode? { var current: RBNode? = searchNode(root, key) as RBNode? if (current == null) return root var newRoot = root @@ -128,7 +130,6 @@ class RBTree, V> : AbstractBinaryTree>() { current = successor } - val child = if (current?.leftChild != null) current.leftChild else current?.rightChild if (child != null) { child.parent = current?.parent @@ -179,16 +180,16 @@ class RBTree, V> : AbstractBinaryTree>() { currentSibling.color = RBNode.Color.Black currentNode.parent?.color = RBNode.Color.Red fixedRoot = rotateLeft(currentNode.parent, fixedRoot) - currentSibling = currentNode.parent?.rightChild // transfer to other cases - } - else if (currentSibling?.leftChild?.color == RBNode.Color.Black && currentSibling.rightChild?.color == RBNode.Color.Black) { + currentSibling = currentNode.parent?.rightChild // transfer to other cases + } else if (currentSibling?.leftChild?.color == RBNode.Color.Black + && currentSibling.rightChild?.color == RBNode.Color.Black) { // 2nd case "currentSibling and its children are black" currentSibling.color = RBNode.Color.Red currentNode = currentNode.parent } else { if (currentSibling?.rightChild?.color == RBNode.Color.Black) { // 3rd case "like 2nd but the left child is red" - (currentSibling.leftChild )?.color = RBNode.Color.Black + (currentSibling.leftChild)?.color = RBNode.Color.Black currentSibling.color = RBNode.Color.Red fixedRoot = rotateRight(currentSibling, fixedRoot) currentSibling = currentNode.parent?.rightChild @@ -207,10 +208,11 @@ class RBTree, V> : AbstractBinaryTree>() { currentSibling.color = RBNode.Color.Black currentNode.parent?.color = RBNode.Color.Red fixedRoot = rotateRight(currentNode.parent, fixedRoot) - currentSibling = currentNode.parent?.leftChild // transfer to other cases + currentSibling = currentNode.parent?.leftChild // transfer to other cases } // 2nd case "currentSibling and its children are black" - if (currentSibling?.leftChild?.color == RBNode.Color.Black && currentSibling.rightChild?.color == RBNode.Color.Black) { + if (currentSibling?.leftChild?.color == RBNode.Color.Black + && currentSibling.rightChild?.color == RBNode.Color.Black) { currentSibling.color = RBNode.Color.Red currentNode = currentNode.parent } else { @@ -233,54 +235,62 @@ class RBTree, V> : AbstractBinaryTree>() { currentNode?.color = RBNode.Color.Black return fixedRoot } - private fun rotateRight(node: RBNode?, currentRoot: RBNode?): RBNode? { - val temp = node?.leftChild ?: return node // if left child is null, no rotation needed + + private fun rotateRight( + nodeToRotate: RBNode?, + currentRoot: RBNode?, + ): RBNode? { + val rightChild = nodeToRotate?.leftChild ?: return nodeToRotate // if left child is null, no rotation needed var newRoot = currentRoot - temp.parent = node.parent + rightChild.parent = nodeToRotate.parent - if (temp.leftChild != null) { - temp.leftChild?.parent = node + if (rightChild.leftChild != null) { + rightChild.leftChild?.parent = nodeToRotate } - temp.rightChild = node - node.parent = temp + rightChild.rightChild = nodeToRotate + nodeToRotate.parent = rightChild - if (temp.parent != null) { - if (node == temp.parent?.leftChild) { - temp.parent?.leftChild = temp + if (rightChild.parent != null) { + if (nodeToRotate == rightChild.parent?.leftChild) { + rightChild.parent?.leftChild = rightChild } else { - temp.parent?.rightChild = temp + rightChild.parent?.rightChild = rightChild } } else { - root = temp + root = rightChild + newRoot = rightChild } return newRoot } - private fun rotateLeft(node: RBNode?, currentRoot: RBNode?): RBNode? { - val temp = node?.rightChild ?: return node // if right child is null, no rotation needed + private fun rotateLeft( + nodeToRotate: RBNode?, + currentRoot: RBNode?, + ): RBNode? { + val rightChild = nodeToRotate?.rightChild ?: return nodeToRotate // if right child is null, no rotation needed var newRoot = currentRoot - temp.parent = node.parent + rightChild.parent = nodeToRotate.parent - node.rightChild = temp.leftChild; - if (node.rightChild != null) { - node.rightChild?.parent = node; + nodeToRotate.rightChild = rightChild.leftChild + if (nodeToRotate.rightChild != null) { + nodeToRotate.rightChild?.parent = nodeToRotate } - temp.leftChild = node; - node.parent = temp; + rightChild.leftChild = nodeToRotate + nodeToRotate.parent = rightChild - // temp took over node's place so now its parent should point to temp - if (temp.parent != null) { - if (node == temp.parent?.leftChild) { - temp.parent?.leftChild = temp; + // rightChild took over nodeToRotate's place so now its parent should point to rightChild + if (rightChild.parent != null) { + if (nodeToRotate == rightChild.parent?.leftChild) { + rightChild.parent?.leftChild = rightChild } else { - temp.parent?.rightChild = temp; + rightChild.parent?.rightChild = rightChild } } else { - root = temp; - newRoot = temp + root = rightChild + newRoot = rightChild } return newRoot } -} \ No newline at end of file +} From bbbec30bf7b8f464f7073b6b4206df5d8fa2d45c Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:47:57 +0300 Subject: [PATCH 056/164] refactor: remove unnecessary parametere from 'deleteNode' method signature --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index d4d47af..a392152 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -99,7 +99,7 @@ class RBTree, V> : AbstractBinaryTree>() { } override fun delete(key: K): V? { - root = deleteNode(root, key) + root = deleteNode(key) root?.color = RBNode.Color.Black // ensure the root is black after deletion return null } @@ -108,13 +108,11 @@ class RBTree, V> : AbstractBinaryTree>() { * Deletes a node with the specified key from the Red\-Black Tree\. * If the node with the key is found, it is removed from the tree and the tree is rebalanced if necessary\. * - * @param node The root node of the Red\-Black Tree\. * @param key The key of the node to be deleted\. * @return The new root node of the Red\-Black Tree after deletion\. */ private fun deleteNode( - node: RBNode?, key: K, ): RBNode? { var current: RBNode? = searchNode(root, key) as RBNode? From 997edc8005f54bac0edd4e1995a79259a7801c0e Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sun, 24 Mar 2024 20:54:17 +0300 Subject: [PATCH 057/164] refactor: AVL tree class, explicitly mark public methods --- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 188 +++++++++++++---------- 1 file changed, 104 insertions(+), 84 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index f7eb33c..0e7d3bb 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -5,96 +5,60 @@ import tsl.nodes.AVLNode import kotlin.math.max class AVLTree, V> : AbstractBinaryTree>() { - override fun insert(key: K, value: V): V? { - val replacedValue = search(key) // null if key isn't in the tree - insertAndBalanceRecursively(root, key, value) - return replacedValue - } - - private fun insertAndBalanceRecursively(node: AVLNode?, key: K, value: V): AVLNode? { - if (root == null) { - root = AVLNode(key, value) - return root - } - if (node == null) return AVLNode(key, value) + public override fun insert(key: K, value: V): V? { + val oldValueByKey = search(key) // null if key isn't in the tree - if (key < node.key) { - node.leftChild = insertAndBalanceRecursively(node.leftChild, key, value) - } else if (key > node.key) { - node.rightChild = insertAndBalanceRecursively(node.rightChild, key, value) - } else return node + insertNodeAndBalanceRecursively(root, key, value) - updateHeight(node) + return oldValueByKey + } - val balanceFactor = getBalanceFactor(node) + private fun insertNodeAndBalanceRecursively( + currNode: AVLNode?, + keyToInsert: K, + valueToInsert: V + ): AVLNode { - if (balanceFactor < -1) { // right cases - node.leftChild?.let { - if (key > it.key) { - node.leftChild = rotateLeft(it) // left-right case - } - return rotateRight(node) - } - } else if (balanceFactor > 1) { // left cases - node.rightChild?.let { - if (key < it.key) { - node.rightChild = rotateRight(it) // right-left case - } - return rotateLeft(node) - } + if (currNode == null) { + val newNode = AVLNode(keyToInsert, valueToInsert) + if (root == null) root = newNode + return newNode } - return node - } + when { + keyToInsert < currNode.key -> + currNode.leftChild = insertNodeAndBalanceRecursively(currNode.leftChild, keyToInsert, valueToInsert) - override fun delete(key: K): V? { - val deletedValue: V = search(key) ?: return null // if key isn't in the tree, there's nothing to delete - deleteAndBalanceRecursively(root, key) - return deletedValue - } + keyToInsert > currNode.key -> + currNode.rightChild = insertNodeAndBalanceRecursively(currNode.rightChild, keyToInsert, valueToInsert) - private fun deleteAndBalanceRecursively(node: AVLNode?, key: K): AVLNode? { - if (node == null) return null // node to be deleted was not found - - if (key < node.key) { - node.leftChild = deleteAndBalanceRecursively(node.leftChild, key) - } else if (key > node.key) { - node.rightChild = deleteAndBalanceRecursively(node.rightChild, key) - } else if (node.leftChild == null || node.rightChild == null) { // if node to be deleted has 0 or 1 child - return node.leftChild ?: node.rightChild - } else { // if node to be deleted has 2 children - val successor = getMinNode(node.rightChild) - if (successor != null) { - node.key = successor.key - node.value = successor.value - - // delete successor node from tree - val temp = deleteAndBalanceRecursively(node.rightChild, successor.key) - if (temp != null) node.rightChild = temp - } + else -> return currNode } - updateHeight(node) + val balanceFactor = getBalanceFactor(currNode) + var balancedNode: AVLNode = currNode - val balanceFactor = getBalanceFactor(node) - - if (balanceFactor < -1) { // right rotation cases - node.leftChild?.let { - if (getBalanceFactor(it) > 0) { // left-right case - node.leftChild = rotateLeft(it) + if (balanceFactor < -1) { // if left subtree got bigger + currNode.leftChild?.let { + if (keyToInsert > it.key) { // if inserted node has greater key + currNode.leftChild = rotateLeft(it) // perform left rotation first } - return rotateRight(node) + balancedNode = rotateRight(currNode) // anyway, perform right rotation } - } else if (balanceFactor > 1) { // left rotation cases - node.rightChild?.let { - if (getBalanceFactor(it) < 0) { // right-left case - node.rightChild = rotateRight(it) + } else if (balanceFactor > 1) { // if right subtree got bigger + currNode.rightChild?.let { + if (keyToInsert < it.key) { // if inserted node has lesser key + currNode.rightChild = rotateRight(it) // perform right rotation first } - return rotateLeft(node) + balancedNode = rotateLeft(currNode) // anyway, perform left rotation } } - return node + updateHeight(balancedNode.leftChild) + updateHeight(balancedNode.rightChild) + updateHeight(balancedNode) + + return balancedNode } private fun rotateRight(oldUpperNode: AVLNode): AVLNode { @@ -104,9 +68,6 @@ class AVLTree, V> : AbstractBinaryTree>() if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root - updateHeight(oldUpperNode) - updateHeight(newUpperNode) - return newUpperNode } @@ -117,22 +78,81 @@ class AVLTree, V> : AbstractBinaryTree>() if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root - updateHeight(oldUpperNode) - updateHeight(newUpperNode) - return newUpperNode } - private fun updateHeight(node: AVLNode): Int { + private fun updateHeight(node: AVLNode?): Int { + if (node == null) return -1 node.height = max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1 return node.height } - private fun getBalanceFactor(node: AVLNode): Int { - return getHeight(node.rightChild) - getHeight(node.leftChild) + private fun getBalanceFactor(node: AVLNode): Int = + getHeight(node.rightChild) - getHeight(node.leftChild) + + private fun getHeight(node: AVLNode?): Int = node?.height ?: -1 + + public override fun delete(key: K): V? { + val deletedValue: V = search(key) ?: return null // if key isn't in the tree, there's nothing to delete + + deleteAndBalanceRecursively(root, key) + + return deletedValue } - private fun getHeight(node: AVLNode?): Int { - return node?.height ?: -1 + private fun deleteAndBalanceRecursively( + currNode: AVLNode?, + keyToDelete: K + ): AVLNode? { + + when { + currNode == null -> return null // node to be deleted was not found + + keyToDelete < currNode.key -> + currNode.leftChild = deleteAndBalanceRecursively(currNode.leftChild, keyToDelete) + + keyToDelete > currNode.key -> + currNode.rightChild = deleteAndBalanceRecursively(currNode.rightChild, keyToDelete) + + (currNode.leftChild == null || currNode.rightChild == null) -> // if node has 0 or 1 child + return currNode.leftChild ?: currNode.rightChild // replace it with its child + + else -> { // if node has 2 children + val successor = getMinNodeRecursively(currNode.rightChild) + if (successor != null) { + currNode.key = successor.key + currNode.value = successor.value + + // delete successor node from tree + val newSubtree = deleteAndBalanceRecursively(currNode.rightChild, successor.key) + if (newSubtree != null) currNode.rightChild = newSubtree + } + } + } + + val balanceFactor = getBalanceFactor(currNode) + var balancedNode: AVLNode = currNode + + if (balanceFactor < -1) { // if left subtree got bigger + currNode.leftChild?.let { + if (getBalanceFactor(it) > 0) { // if inserted node has greater key + currNode.leftChild = rotateLeft(it) // perform left rotation first + } + balancedNode = rotateRight(currNode) // anyway, perform left rotation + } + } else if (balanceFactor > 1) { // if right subtree got bigger + currNode.rightChild?.let { + if (getBalanceFactor(it) < 0) { // if inserted node has greater key + currNode.rightChild = rotateRight(it) // perform right rotation first + } + balancedNode = rotateLeft(currNode) // anyway, perform left rotation + } + } + + updateHeight(balancedNode.leftChild) + updateHeight(balancedNode.rightChild) + updateHeight(balancedNode) + + return balancedNode } } From 06fb05c812a344143ae0584b5983ca9fa7ea27fa Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sun, 24 Mar 2024 20:58:06 +0300 Subject: [PATCH 058/164] refactor: Abstract and Binary search tree classes, explicitly mark public methods --- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 61 +++++----- lib/src/main/kotlin/tsl/trees/BSTree.kt | 114 +++++++++--------- lib/src/main/kotlin/tsl/trees/RBTree.kt | 9 +- 3 files changed, 93 insertions(+), 91 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 2edaca2..1cc6de8 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -6,49 +6,50 @@ import tsl.iterator.BinaryTreeIterator abstract class AbstractBinaryTree, V, N : AbstractNode>: Iterable> { internal var root: N? = null - abstract fun delete(key: K): V? + public abstract fun delete(key: K): V? - abstract fun insert( - key: K, - value: V, - ): V? + public abstract fun insert(key: K, value: V): V? - fun search(key: K): V? { - return searchNode(root, key)?.value + public fun search(key: K): V? { + return searchNodeRecursively(root, key)?.value } - protected fun searchNode(node: AbstractNode?, key: K): AbstractNode? { - return if (node == null) null - else if (node.key == key) node - else if (key < node.key) searchNode(node.leftChild, key) - else searchNode(node.rightChild, key) + protected fun searchNodeRecursively(currNode: AbstractNode?, keyToSearch: K): AbstractNode? { + when { + currNode == null -> return null + keyToSearch > currNode.key -> return currNode + keyToSearch < currNode.key -> return searchNodeRecursively(currNode.leftChild, keyToSearch) + else -> return searchNodeRecursively(currNode.rightChild, keyToSearch) + } } - fun clear() { + public fun clear(): V? { + val rootValueBeforeClear = root?.value + root = null - } - fun getMinKey(): K? { - return getMinNode(root)?.key + return rootValueBeforeClear } - fun getMaxKey(): K? { - return getMaxNode(root)?.key - } + public fun getMinKey(): K? = getMinNodeRecursively(root)?.key - protected fun getMinNode(node: N?): N? { - return if (node == null) null - else if (node.leftChild == null) node - else getMinNode(node.leftChild) - } + public fun getMaxKey(): K? = getMaxNodeRecursively(root)?.key - protected fun getMaxNode(node: N?): N? { - return if (node == null) null - else if (node.rightChild == null) node - else getMaxNode(node.rightChild) + protected fun getMinNodeRecursively(node: N?): N? { + when { + node == null -> return null + node.leftChild == null -> return node + else -> return getMinNodeRecursively(node.leftChild) + } } - override fun iterator(): Iterator> { - return BinaryTreeIterator(this) + protected fun getMaxNodeRecursively(node: N?): N? { + when { + node == null -> return null + node.rightChild == null -> return node + else -> return getMinNodeRecursively(node.rightChild) + } } + + public override fun iterator(): Iterator> = BinaryTreeIterator(this) } diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index c5d1fb8..9a6fdea 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -4,60 +4,62 @@ import tsl.nodes.BSTNode class BSTree, V> : AbstractBinaryTree>() { - override fun insert(key: K, value: V): V? { - val nodeToInsert = BSTNode(key, value) - - if (root == null) { - root = nodeToInsert - return null - } - - var currentNode = root - - while (currentNode != null) { - if (nodeToInsert.key == currentNode.key) { - val oldValue = currentNode.value - currentNode.value = nodeToInsert.value - return oldValue - } else if (nodeToInsert.key < currentNode.key) { - if (currentNode.leftChild == null) { - currentNode.leftChild = nodeToInsert - return null - } - currentNode = currentNode.leftChild - } else if (nodeToInsert.key > currentNode.key) { - if (currentNode.rightChild == null) { - currentNode.rightChild = nodeToInsert - return null - } - currentNode = currentNode.rightChild - } - } - return currentNode?.value - } - - override fun delete(key: K): V? { - val deletedValue: V? = search(key) ?: return null - deleteRecursively(root, key) - return deletedValue - } - - private fun deleteRecursively(node: BSTNode?, key: K): BSTNode? { - if (node == null) return null - if (key < node.key) node.leftChild = deleteRecursively(node.leftChild, key) - else if (key > node.key) node.rightChild = deleteRecursively(node.rightChild, key) - else if (key == node.key) { - if (node.leftChild == null || node.rightChild == null) - return node.leftChild ?: node.rightChild - else { - val minRightNode = getMinNode(node.rightChild) - if (minRightNode != null) { - node.key = minRightNode.key - node.value = minRightNode.value - node.rightChild = deleteRecursively(node.rightChild, minRightNode.key) - } - } - } - return node - } + public override fun insert(key: K, value: V): V? { + val nodeToInsert = BSTNode(key, value) + + if (root == null) { + root = nodeToInsert + return null + } + + var currentNode = root + + while (currentNode != null) { + if (nodeToInsert.key == currentNode.key) { + val oldValue = currentNode.value + currentNode.value = nodeToInsert.value + return oldValue + } else if (nodeToInsert.key < currentNode.key) { + if (currentNode.leftChild == null) { + currentNode.leftChild = nodeToInsert + return null + } + currentNode = currentNode.leftChild + } else if (nodeToInsert.key > currentNode.key) { + if (currentNode.rightChild == null) { + currentNode.rightChild = nodeToInsert + return null + } + currentNode = currentNode.rightChild + } + } + return currentNode?.value + } + + public override fun delete(key: K): V? { + val deletedValue: V? = search(key) ?: return null + + deleteRecursively(root, key) + + return deletedValue + } + + private fun deleteRecursively(node: BSTNode?, key: K): BSTNode? { + if (node == null) return null + if (key < node.key) node.leftChild = deleteRecursively(node.leftChild, key) + else if (key > node.key) node.rightChild = deleteRecursively(node.rightChild, key) + else if (key == node.key) { + if (node.leftChild == null || node.rightChild == null) + return node.leftChild ?: node.rightChild + else { + val minRightNode = getMinNodeRecursively(node.rightChild) + if (minRightNode != null) { + node.key = minRightNode.key + node.value = minRightNode.value + node.rightChild = deleteRecursively(node.rightChild, minRightNode.key) + } + } + } + return node + } } diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index a392152..fef2d34 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -3,8 +3,7 @@ package tsl.trees import tsl.nodes.RBNode class RBTree, V> : AbstractBinaryTree>() { - override fun insert( - key: K, + public override fun insert(key: K, value: V, ): V? { // in case key isnt in the tree/ inserts successfully-> return null -> else -> return value @@ -98,7 +97,7 @@ class RBTree, V> : AbstractBinaryTree>() { root = newRoot } - override fun delete(key: K): V? { + public override fun delete(key: K): V? { root = deleteNode(key) root?.color = RBNode.Color.Black // ensure the root is black after deletion return null @@ -115,12 +114,12 @@ class RBTree, V> : AbstractBinaryTree>() { private fun deleteNode( key: K, ): RBNode? { - var current: RBNode? = searchNode(root, key) as RBNode? + var current: RBNode? = searchNodeRecursively(root, key) as RBNode? if (current == null) return root var newRoot = root if (current.leftChild != null && current.rightChild != null) { - val successor = getMaxNode(current.leftChild) + val successor = getMaxNodeRecursively(current.leftChild) if (successor != null) { current.value = successor.value current.key = successor.key From b667cd9f049d9c7e1e57d8de11a561dab7a914ec Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sun, 24 Mar 2024 21:07:15 +0300 Subject: [PATCH 059/164] refactor: if conditions, undo last signature refactor in RB tree class --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index fef2d34..4445d49 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -3,8 +3,9 @@ package tsl.trees import tsl.nodes.RBNode class RBTree, V> : AbstractBinaryTree>() { - public override fun insert(key: K, - value: V, + public override fun insert( + key: K, + value: V ): V? { // in case key isnt in the tree/ inserts successfully-> return null -> else -> return value val newNode = RBNode(key, value) @@ -179,7 +180,8 @@ class RBTree, V> : AbstractBinaryTree>() { fixedRoot = rotateLeft(currentNode.parent, fixedRoot) currentSibling = currentNode.parent?.rightChild // transfer to other cases } else if (currentSibling?.leftChild?.color == RBNode.Color.Black - && currentSibling.rightChild?.color == RBNode.Color.Black) { + && currentSibling.rightChild?.color == RBNode.Color.Black + ) { // 2nd case "currentSibling and its children are black" currentSibling.color = RBNode.Color.Red currentNode = currentNode.parent @@ -209,7 +211,8 @@ class RBTree, V> : AbstractBinaryTree>() { } // 2nd case "currentSibling and its children are black" if (currentSibling?.leftChild?.color == RBNode.Color.Black - && currentSibling.rightChild?.color == RBNode.Color.Black) { + && currentSibling.rightChild?.color == RBNode.Color.Black + ) { currentSibling.color = RBNode.Color.Red currentNode = currentNode.parent } else { From 11f8c82ef7ef2893c4cb158ce07b4f0d6be8839f Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Mon, 25 Mar 2024 21:18:08 +0300 Subject: [PATCH 060/164] fix: logical error in searchNodeRecursively method --- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 1cc6de8..049e921 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -14,10 +14,10 @@ abstract class AbstractBinaryTree, V, N : AbstractNode?, keyToSearch: K): AbstractNode? { + protected fun searchNodeRecursively(currNode: N?, keyToSearch: K): N? { when { currNode == null -> return null - keyToSearch > currNode.key -> return currNode + keyToSearch == currNode.key -> return currNode keyToSearch < currNode.key -> return searchNodeRecursively(currNode.leftChild, keyToSearch) else -> return searchNodeRecursively(currNode.rightChild, keyToSearch) } From 2717b663e59a556107a49a262bb37e6c758745b2 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Mon, 25 Mar 2024 21:55:18 +0300 Subject: [PATCH 061/164] feat: add test drafts for all trees --- lib/src/test/kotlin/org/example/LibraryTest.kt | 14 -------------- lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt | 18 ++++++++++++++++++ lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 18 ++++++++++++++++++ lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 18 ++++++++++++++++++ 4 files changed, 54 insertions(+), 14 deletions(-) delete mode 100644 lib/src/test/kotlin/org/example/LibraryTest.kt create mode 100644 lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt create mode 100644 lib/src/test/kotlin/tsl/trees/BSTreeTest.kt create mode 100644 lib/src/test/kotlin/tsl/trees/RBTreeTest.kt diff --git a/lib/src/test/kotlin/org/example/LibraryTest.kt b/lib/src/test/kotlin/org/example/LibraryTest.kt deleted file mode 100644 index f6fa692..0000000 --- a/lib/src/test/kotlin/org/example/LibraryTest.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * This Kotlin source file was generated by the Gradle 'init' task. - */ -package org.example - -import kotlin.test.Test -import kotlin.test.assertTrue - -class LibraryTest { - @Test fun someLibraryMethodReturnsTrue() { - val classUnderTest = Library() - assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'") - } -} diff --git a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt new file mode 100644 index 0000000..91eeff4 --- /dev/null +++ b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt @@ -0,0 +1,18 @@ +package tsl.trees + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class AVLTreeTest { + private lateinit var tree: AVLTree + + @BeforeEach + fun setup() { + tree = AVLTree() + } + + @Test + fun test() {} + +} \ No newline at end of file diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt new file mode 100644 index 0000000..5c9d367 --- /dev/null +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -0,0 +1,18 @@ +package tsl.trees + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class BSTreeTest { + private lateinit var tree: BSTree + + @BeforeEach + fun setup() { + tree = BSTree() + } + + @Test + fun test() {} + +} \ No newline at end of file diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt new file mode 100644 index 0000000..d2de872 --- /dev/null +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -0,0 +1,18 @@ +package tsl.trees + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class RBTreeTest{ + private lateinit var tree: RBTree + + @BeforeEach + fun setup() { + tree = RBTree() + } + + @Test + fun test() {} + +} \ No newline at end of file From 17174d947106b7bd0842c84e2e5166e2925120f1 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 25 Mar 2024 11:39:33 +0300 Subject: [PATCH 062/164] refactor: remove unused method 'switchColor' --- lib/src/main/kotlin/tsl/nodes/RBNode.kt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/src/main/kotlin/tsl/nodes/RBNode.kt b/lib/src/main/kotlin/tsl/nodes/RBNode.kt index 6427626..905778c 100644 --- a/lib/src/main/kotlin/tsl/nodes/RBNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/RBNode.kt @@ -9,14 +9,5 @@ class RBNode, V>(key: K, value: V) : AbstractNode?) { - val node1color = this.color - - if (node2 != null) { - this.color = node2.color - node2.color = node1color - } - } } From 6636cea0d08bc930c1a55d427232760c6f0eb1b4 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:16:48 +0300 Subject: [PATCH 063/164] fix: correct the return values of 'delete' and 'insert' methods --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 37 +++++++++++++++++-------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 4445d49..c06e668 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -3,10 +3,17 @@ package tsl.trees import tsl.nodes.RBNode class RBTree, V> : AbstractBinaryTree>() { + + /** + * Inserts a node with provided value and key.\. + * @ + * @param key The key of the node to be deleted\. + * @return Null if 'insert' method haven't replaced any value. Return value if there was some replace.\. + */ public override fun insert( key: K, value: V - ): V? { // in case key isnt in the tree/ inserts successfully-> return null -> else -> return value + ): V? { val newNode = RBNode(key, value) if (root == null) { @@ -16,15 +23,16 @@ class RBTree, V> : AbstractBinaryTree>() { } var currentNode: RBNode? = root + val returnValue: V? = search(key) ?: return null - // traverse the tree to find the insertion point(node) + // traverse the tree to find the insertion point (node) while (currentNode != null) { if (currentNode.key > newNode.key) { if (currentNode.leftChild == null) { currentNode.leftChild = newNode newNode.parent = currentNode balanceNode(newNode) - return null + return returnValue } currentNode = currentNode.leftChild } else if (currentNode.key < newNode.key) { @@ -32,16 +40,15 @@ class RBTree, V> : AbstractBinaryTree>() { currentNode.rightChild = newNode newNode.parent = currentNode balanceNode(newNode) - return null + return returnValue } currentNode = currentNode.rightChild } else { - balanceNode(newNode) - return value + return returnValue } } balanceNode(newNode) - return null + return returnValue } /** @@ -98,8 +105,16 @@ class RBTree, V> : AbstractBinaryTree>() { root = newRoot } + /** + * Returns something and sets up the root depending on the result of deleteNode\. + * + * @param key The key of the node to be deleted\. + * @return Value if delete was successful and null if it isn't.\. + */ + public override fun delete(key: K): V? { root = deleteNode(key) + if (root == null) return search(key) root?.color = RBNode.Color.Black // ensure the root is black after deletion return null } @@ -116,7 +131,7 @@ class RBTree, V> : AbstractBinaryTree>() { key: K, ): RBNode? { var current: RBNode? = searchNodeRecursively(root, key) as RBNode? - if (current == null) return root + if (current == null) return null var newRoot = root if (current.leftChild != null && current.rightChild != null) { @@ -193,7 +208,7 @@ class RBTree, V> : AbstractBinaryTree>() { fixedRoot = rotateRight(currentSibling, fixedRoot) currentSibling = currentNode.parent?.rightChild } - // 4th case "black currentSibling, right child is black' + // 4th case "black currentSibling, right child is black" currentSibling?.color = currentNode.parent?.color ?: RBNode.Color.Black currentNode.parent?.color = RBNode.Color.Black currentSibling?.rightChild?.color = RBNode.Color.Black @@ -223,7 +238,7 @@ class RBTree, V> : AbstractBinaryTree>() { fixedRoot = rotateLeft(currentSibling, fixedRoot) currentSibling = currentNode.parent?.leftChild } - // 4th case "black currentSibling, left child is red' + // 4th case "black currentSibling, left child is red" currentSibling?.color = currentNode.parent?.color ?: RBNode.Color.Black (currentNode.parent)?.color = RBNode.Color.Black (currentSibling?.leftChild)?.color = RBNode.Color.Black @@ -280,7 +295,7 @@ class RBTree, V> : AbstractBinaryTree>() { rightChild.leftChild = nodeToRotate nodeToRotate.parent = rightChild - // rightChild took over nodeToRotate's place so now its parent should point to rightChild + // rightChild took over "nodeToRotate" place so now its parent should point to rightChild if (rightChild.parent != null) { if (nodeToRotate == rightChild.parent?.leftChild) { rightChild.parent?.leftChild = rightChild From d30235e2375e363a1ddecd16fef8bf2e142b44b7 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:02:53 +0300 Subject: [PATCH 064/164] test: add tests for 'insert' and 'search' methods --- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 61 +++++++++++++++++++-- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index d2de872..206669e 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -1,18 +1,67 @@ package tsl.trees +import org.checkerframework.checker.units.qual.N import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test class RBTreeTest{ - private lateinit var tree: RBTree + @Nested + inner class SearchTests() { + @Test + fun `search node that isn't in the tree`() { + val rbtree = RBTree() + rbtree.insert(15, "hihi") + rbtree.insert(10, "haha") + rbtree.insert(5, "bugaga") + val returnValue = rbtree.search(7) + assertEquals(null, returnValue) + } - @BeforeEach - fun setup() { - tree = RBTree() +// @Test +// fun `search node that is in tree`() { +// val rbtree = RBTree() +// rbtree.insert(15, "hihi") +// rbtree.insert(10, "haha") +// rbtree.insert(5, "bugaga") +// val returnValue = rbtree.search(5) +// assertEquals("bugaga", returnValue) +// } } - @Test - fun test() {} + @Nested + inner class InsertTests() { + fun `insert if the node with this key doesn't exist`() { + val rbtree = RBTree() + rbtree.insert(15, "i") + rbtree.insert(10, "am") + rbtree.insert(5, "really") + val returnValue = rbtree.insert(7, "wordless") + assertEquals(null, returnValue) + } + fun `insert if the node with this key exists`() { + val rbtree = RBTree() + rbtree.insert(15, "i") + rbtree.insert(10, "am") + rbtree.insert(5, "really") + val returnValue = rbtree.insert(5, "wordless") + assertEquals("really", returnValue) + } + + fun `insert left child`() { + val rbtree = RBTree() + rbtree.insert(17, "legend") + rbtree.insert(27, "club") + rbtree.insert(49, "poliklinika") + } + + fun `insert right child`() { + val rbtree = RBTree() + rbtree.insert(17, "legend") + rbtree.insert(27, "club") + rbtree.insert(49, "poliklinika") + } + } } \ No newline at end of file From 1db2780e14b07c5421cf7e48ab09e09d62db06e1 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:05:22 +0300 Subject: [PATCH 065/164] test: tweaked initial structure for tests, divide them to classes --- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index 206669e..23417ea 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -64,4 +64,18 @@ class RBTreeTest{ rbtree.insert(49, "poliklinika") } } + @Nested + inner class DeleteTests() { + + } + + @Nested + inner class IteratorTests() { + + } + + @Nested + inner class AuxiliaryTests() { + + } } \ No newline at end of file From 0d14d7601820bc773a03ecf505de5203efa775b7 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:17:39 +0300 Subject: [PATCH 066/164] test: implement test for search of existing node --- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index 23417ea..3e0ce4b 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -1,6 +1,5 @@ package tsl.trees -import org.checkerframework.checker.units.qual.N import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -19,15 +18,15 @@ class RBTreeTest{ assertEquals(null, returnValue) } -// @Test -// fun `search node that is in tree`() { -// val rbtree = RBTree() -// rbtree.insert(15, "hihi") -// rbtree.insert(10, "haha") -// rbtree.insert(5, "bugaga") -// val returnValue = rbtree.search(5) -// assertEquals("bugaga", returnValue) -// } + @Test + fun `search node that is in tree`() { + val rbtree = RBTree() + rbtree.insert(15, "hihi") + rbtree.insert(10, "haha") + rbtree.insert(5, "bugaga") + val returnValue = rbtree.search(5) + assertEquals("bugaga", returnValue) + } } @Nested From 79e0f56fa352dde12b4cf1fab4126fcf7980958e Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:19:37 +0300 Subject: [PATCH 067/164] fix: remove unnecessary return, that cause issue with tree build --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index c06e668..e4aa9c9 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -23,7 +23,7 @@ class RBTree, V> : AbstractBinaryTree>() { } var currentNode: RBNode? = root - val returnValue: V? = search(key) ?: return null + val returnValue: V? = search(key) // traverse the tree to find the insertion point (node) while (currentNode != null) { From 5b2b1e58d350d8162f43eef8fd6cc856fc929166 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Tue, 26 Mar 2024 20:53:27 +0300 Subject: [PATCH 068/164] chore: add jacoco test coverage --- lib/build.gradle.kts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index f6cad6d..e993082 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -11,6 +11,7 @@ plugins { // Apply the java-library plugin for API and implementation separation. `java-library` + jacoco } repositories { @@ -44,4 +45,23 @@ java { tasks.named("test") { // Use JUnit Platform for unit tests. useJUnitPlatform() + finalizedBy(tasks.jacocoTestReport) // report generates after run } + +// Configuring JaCoCo plugin settings +jacoco { + toolVersion = "0.8.11" + reportsDirectory.set(layout.buildDirectory.dir("coverage")) // directory for reports +} + +tasks.jacocoTestReport { + dependsOn(tasks.test) // tests are required to run before generating the report + + reports { + xml.required = true + html.required = true + csv.required = false + } +} + + From b1b654e76e8d691dcab12ed7c1af4737ca9b4ce4 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Wed, 27 Mar 2024 09:45:00 +0300 Subject: [PATCH 069/164] refactor: apply new formatter 'ktfmt' to the project --- .../kotlin/tsl/iterator/BinaryTreeIterator.kt | 7 +- lib/src/main/kotlin/tsl/nodes/AVLNode.kt | 3 +- lib/src/main/kotlin/tsl/nodes/AbstractNode.kt | 5 +- lib/src/main/kotlin/tsl/nodes/BSTNode.kt | 4 +- lib/src/main/kotlin/tsl/nodes/RBNode.kt | 4 +- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 65 +++++++++---------- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 31 ++++----- lib/src/main/kotlin/tsl/trees/RBTree.kt | 64 +++++++++--------- lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt | 8 +-- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 8 +-- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 31 ++++----- 11 files changed, 111 insertions(+), 119 deletions(-) diff --git a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt index cccd2fa..8a3c8e7 100644 --- a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt +++ b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt @@ -3,8 +3,9 @@ package tsl.iterator import tsl.nodes.AbstractNode import tsl.trees.AbstractBinaryTree -internal class BinaryTreeIterator, V, N: AbstractNode> - (tree: AbstractBinaryTree): Iterator> { +internal class BinaryTreeIterator, V, N : AbstractNode>( + tree: AbstractBinaryTree +) : Iterator> { private var stack = ArrayDeque() private var currentNode: N? = tree.root @@ -25,4 +26,4 @@ internal class BinaryTreeIterator, V, N: AbstractNode return Pair(nextNode?.key, nextNode?.value) } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/tsl/nodes/AVLNode.kt b/lib/src/main/kotlin/tsl/nodes/AVLNode.kt index 0eb8926..e571847 100644 --- a/lib/src/main/kotlin/tsl/nodes/AVLNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/AVLNode.kt @@ -1,5 +1,6 @@ package tsl.nodes -class AVLNode, V>(key: K, value: V) : AbstractNode>(key, value) { +class AVLNode, V>(key: K, value: V) : + AbstractNode>(key, value) { internal var height = 0 } diff --git a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt index a21d32b..19a9062 100644 --- a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt @@ -1,6 +1,9 @@ package tsl.nodes -abstract class AbstractNode, V, N : AbstractNode>(var key: K, var value: V) { +abstract class AbstractNode, V, N : AbstractNode>( + var key: K, + var value: V +) { internal var leftChild: N? = null internal var rightChild: N? = null } diff --git a/lib/src/main/kotlin/tsl/nodes/BSTNode.kt b/lib/src/main/kotlin/tsl/nodes/BSTNode.kt index 378b2bf..1c8529b 100644 --- a/lib/src/main/kotlin/tsl/nodes/BSTNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/BSTNode.kt @@ -1,4 +1,4 @@ package tsl.nodes -class BSTNode, V>(key: K, value: V) : AbstractNode>(key, value) - +class BSTNode, V>(key: K, value: V) : + AbstractNode>(key, value) diff --git a/lib/src/main/kotlin/tsl/nodes/RBNode.kt b/lib/src/main/kotlin/tsl/nodes/RBNode.kt index 905778c..50ee49f 100644 --- a/lib/src/main/kotlin/tsl/nodes/RBNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/RBNode.kt @@ -1,6 +1,7 @@ package tsl.nodes -class RBNode, V>(key: K, value: V) : AbstractNode>(key, value) { +class RBNode, V>(key: K, value: V) : + AbstractNode>(key, value) { internal var parent: RBNode? = null enum class Color { @@ -10,4 +11,3 @@ class RBNode, V>(key: K, value: V) : AbstractNode, V> : AbstractBinaryTree>() { public override fun insert(key: K, value: V): V? { - val oldValueByKey = search(key) // null if key isn't in the tree + val oldValueByKey = search(key) // null if key isn't in the tree insertNodeAndBalanceRecursively(root, key, value) @@ -27,30 +26,30 @@ class AVLTree, V> : AbstractBinaryTree>() when { keyToInsert < currNode.key -> - currNode.leftChild = insertNodeAndBalanceRecursively(currNode.leftChild, keyToInsert, valueToInsert) - + currNode.leftChild = + insertNodeAndBalanceRecursively(currNode.leftChild, keyToInsert, valueToInsert) keyToInsert > currNode.key -> - currNode.rightChild = insertNodeAndBalanceRecursively(currNode.rightChild, keyToInsert, valueToInsert) - + currNode.rightChild = + insertNodeAndBalanceRecursively(currNode.rightChild, keyToInsert, valueToInsert) else -> return currNode } val balanceFactor = getBalanceFactor(currNode) var balancedNode: AVLNode = currNode - if (balanceFactor < -1) { // if left subtree got bigger + if (balanceFactor < -1) { // if left subtree got bigger currNode.leftChild?.let { - if (keyToInsert > it.key) { // if inserted node has greater key - currNode.leftChild = rotateLeft(it) // perform left rotation first + if (keyToInsert > it.key) { // if inserted node has greater key + currNode.leftChild = rotateLeft(it) // perform left rotation first } - balancedNode = rotateRight(currNode) // anyway, perform right rotation + balancedNode = rotateRight(currNode) // anyway, perform right rotation } - } else if (balanceFactor > 1) { // if right subtree got bigger + } else if (balanceFactor > 1) { // if right subtree got bigger currNode.rightChild?.let { - if (keyToInsert < it.key) { // if inserted node has lesser key - currNode.rightChild = rotateRight(it) // perform right rotation first + if (keyToInsert < it.key) { // if inserted node has lesser key + currNode.rightChild = rotateRight(it) // perform right rotation first } - balancedNode = rotateLeft(currNode) // anyway, perform left rotation + balancedNode = rotateLeft(currNode) // anyway, perform left rotation } } @@ -66,7 +65,7 @@ class AVLTree, V> : AbstractBinaryTree>() oldUpperNode.leftChild = newUpperNode.rightChild newUpperNode.rightChild = oldUpperNode - if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root + if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root return newUpperNode } @@ -76,7 +75,7 @@ class AVLTree, V> : AbstractBinaryTree>() oldUpperNode.rightChild = newUpperNode.leftChild newUpperNode.leftChild = oldUpperNode - if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root + if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root return newUpperNode } @@ -93,7 +92,8 @@ class AVLTree, V> : AbstractBinaryTree>() private fun getHeight(node: AVLNode?): Int = node?.height ?: -1 public override fun delete(key: K): V? { - val deletedValue: V = search(key) ?: return null // if key isn't in the tree, there's nothing to delete + val deletedValue: V = + search(key) ?: return null // if key isn't in the tree, there's nothing to delete deleteAndBalanceRecursively(root, key) @@ -106,18 +106,15 @@ class AVLTree, V> : AbstractBinaryTree>() ): AVLNode? { when { - currNode == null -> return null // node to be deleted was not found - + currNode == null -> return null // node to be deleted was not found keyToDelete < currNode.key -> currNode.leftChild = deleteAndBalanceRecursively(currNode.leftChild, keyToDelete) - keyToDelete > currNode.key -> currNode.rightChild = deleteAndBalanceRecursively(currNode.rightChild, keyToDelete) - - (currNode.leftChild == null || currNode.rightChild == null) -> // if node has 0 or 1 child - return currNode.leftChild ?: currNode.rightChild // replace it with its child - - else -> { // if node has 2 children + (currNode.leftChild == null || + currNode.rightChild == null) -> // if node has 0 or 1 child + return currNode.leftChild ?: currNode.rightChild // replace it with its child + else -> { // if node has 2 children val successor = getMinNodeRecursively(currNode.rightChild) if (successor != null) { currNode.key = successor.key @@ -133,19 +130,19 @@ class AVLTree, V> : AbstractBinaryTree>() val balanceFactor = getBalanceFactor(currNode) var balancedNode: AVLNode = currNode - if (balanceFactor < -1) { // if left subtree got bigger + if (balanceFactor < -1) { // if left subtree got bigger currNode.leftChild?.let { - if (getBalanceFactor(it) > 0) { // if inserted node has greater key - currNode.leftChild = rotateLeft(it) // perform left rotation first + if (getBalanceFactor(it) > 0) { // if inserted node has greater key + currNode.leftChild = rotateLeft(it) // perform left rotation first } - balancedNode = rotateRight(currNode) // anyway, perform left rotation + balancedNode = rotateRight(currNode) // anyway, perform left rotation } - } else if (balanceFactor > 1) { // if right subtree got bigger + } else if (balanceFactor > 1) { // if right subtree got bigger currNode.rightChild?.let { - if (getBalanceFactor(it) < 0) { // if inserted node has greater key - currNode.rightChild = rotateRight(it) // perform right rotation first + if (getBalanceFactor(it) < 0) { // if inserted node has greater key + currNode.rightChild = rotateRight(it) // perform right rotation first } - balancedNode = rotateLeft(currNode) // anyway, perform left rotation + balancedNode = rotateLeft(currNode) // anyway, perform left rotation } } diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 049e921..aec9db5 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -1,9 +1,10 @@ package tsl.trees -import tsl.nodes.AbstractNode import tsl.iterator.BinaryTreeIterator +import tsl.nodes.AbstractNode -abstract class AbstractBinaryTree, V, N : AbstractNode>: Iterable> { +abstract class AbstractBinaryTree, V, N : AbstractNode> : + Iterable> { internal var root: N? = null public abstract fun delete(key: K): V? @@ -15,11 +16,11 @@ abstract class AbstractBinaryTree, V, N : AbstractNode return null - keyToSearch == currNode.key -> return currNode - keyToSearch < currNode.key -> return searchNodeRecursively(currNode.leftChild, keyToSearch) - else -> return searchNodeRecursively(currNode.rightChild, keyToSearch) + return when { + currNode == null -> null + keyToSearch == currNode.key -> currNode + keyToSearch < currNode.key -> searchNodeRecursively(currNode.leftChild, keyToSearch) + else -> searchNodeRecursively(currNode.rightChild, keyToSearch) } } @@ -36,18 +37,18 @@ abstract class AbstractBinaryTree, V, N : AbstractNode return null - node.leftChild == null -> return node - else -> return getMinNodeRecursively(node.leftChild) + return when { + node == null -> null + node.leftChild == null -> node + else -> getMinNodeRecursively(node.leftChild) } } protected fun getMaxNodeRecursively(node: N?): N? { - when { - node == null -> return null - node.rightChild == null -> return node - else -> return getMinNodeRecursively(node.rightChild) + return when { + node == null -> null + node.rightChild == null -> node + else -> getMinNodeRecursively(node.rightChild) } } diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index e4aa9c9..54dc33e 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -5,15 +5,13 @@ import tsl.nodes.RBNode class RBTree, V> : AbstractBinaryTree>() { /** - * Inserts a node with provided value and key.\. - * @ - * @param key The key of the node to be deleted\. - * @return Null if 'insert' method haven't replaced any value. Return value if there was some replace.\. + * Inserts a node with provided value and key.\. + * + * @param key The key of the node to be deleted.\. + * @return Null if 'insert' method haven't replaced any value. Return value if there was some + * replace.\. */ - public override fun insert( - key: K, - value: V - ): V? { + public override fun insert(key: K, value: V): V? { val newNode = RBNode(key, value) if (root == null) { @@ -52,13 +50,13 @@ class RBTree, V> : AbstractBinaryTree>() { } /** - * This function is used to balance the red-black tree node after insertion. - * It checks the colors of the parent and grandparent nodes to determine the rotations needed for balancing. + * This function is used to balance the red-black tree node after insertion. It checks the + * colors of the parent and grandparent nodes to determine the rotations needed for balancing. * The function alternates between left and right rotations to maintain the balance of the tree. * The root variable is updated to maintain the correct root node after balancing. - * @param RBNode-typed node + * + * @param RBNode-typed node. */ - private fun balanceNode(node: RBNode?) { var newNode = node var uncle: RBNode? @@ -106,12 +104,11 @@ class RBTree, V> : AbstractBinaryTree>() { } /** - * Returns something and sets up the root depending on the result of deleteNode\. + * Returns something and sets up the root depending on the result of deleteNode.\. * - * @param key The key of the node to be deleted\. - * @return Value if delete was successful and null if it isn't.\. + * @param key The key of the node to be deleted\. + * @return Value if delete was successful and null if it isn't.\. */ - public override fun delete(key: K): V? { root = deleteNode(key) if (root == null) return search(key) @@ -120,13 +117,12 @@ class RBTree, V> : AbstractBinaryTree>() { } /** - * Deletes a node with the specified key from the Red\-Black Tree\. - * If the node with the key is found, it is removed from the tree and the tree is rebalanced if necessary\. + * Deletes a node with the specified key from the Red\-Black Tree\. If the node with the key is + * found, it is removed from the tree and the tree is rebalanced if necessary\. * * @param key The key of the node to be deleted\. * @return The new root node of the Red\-Black Tree after deletion\. */ - private fun deleteNode( key: K, ): RBNode? { @@ -172,19 +168,19 @@ class RBTree, V> : AbstractBinaryTree>() { } /** - * This function is used to fix the red-black tree structure after a node deletion to ensure that the red-black - * properties of the tree are maintained. - * It iterates through the nodes and performs rotations and recoloring based on the cases derived from the sibling - * and its children's colors to balance the tree. + * This function is used to fix the red-black tree structure after a node deletion to ensure + * that the red-black properties of the tree are maintained. It iterates through the nodes and + * performs rotations and recoloring based on the cases derived from the sibling and its + * children's colors to balance the tree. * * @param currentNode The node that needs fixing after deletion * @return The root of the tree after fixing the structure to maintain the red-black properties */ - private fun fixAfterDeletion(currentDeletingNode: RBNode?): RBNode? { var fixedRoot = root var currentNode = currentDeletingNode - // currentNode - variable that is used to perform various operations and control the loop of fixing the tree. + // currentNode - variable that is used to perform various operations and control the loop of + // fixing the tree. while (currentNode != root && currentNode?.color == RBNode.Color.Black) { if (currentNode == currentNode.parent?.leftChild) { @@ -194,8 +190,9 @@ class RBTree, V> : AbstractBinaryTree>() { currentNode.parent?.color = RBNode.Color.Red fixedRoot = rotateLeft(currentNode.parent, fixedRoot) currentSibling = currentNode.parent?.rightChild // transfer to other cases - } else if (currentSibling?.leftChild?.color == RBNode.Color.Black - && currentSibling.rightChild?.color == RBNode.Color.Black + } else if ( + currentSibling?.leftChild?.color == RBNode.Color.Black && + currentSibling.rightChild?.color == RBNode.Color.Black ) { // 2nd case "currentSibling and its children are black" currentSibling.color = RBNode.Color.Red @@ -225,8 +222,9 @@ class RBTree, V> : AbstractBinaryTree>() { currentSibling = currentNode.parent?.leftChild // transfer to other cases } // 2nd case "currentSibling and its children are black" - if (currentSibling?.leftChild?.color == RBNode.Color.Black - && currentSibling.rightChild?.color == RBNode.Color.Black + if ( + currentSibling?.leftChild?.color == RBNode.Color.Black && + currentSibling.rightChild?.color == RBNode.Color.Black ) { currentSibling.color = RBNode.Color.Red currentNode = currentNode.parent @@ -255,7 +253,9 @@ class RBTree, V> : AbstractBinaryTree>() { nodeToRotate: RBNode?, currentRoot: RBNode?, ): RBNode? { - val rightChild = nodeToRotate?.leftChild ?: return nodeToRotate // if left child is null, no rotation needed + val rightChild = + nodeToRotate?.leftChild + ?: return nodeToRotate // if left child is null, no rotation needed var newRoot = currentRoot rightChild.parent = nodeToRotate.parent @@ -283,7 +283,9 @@ class RBTree, V> : AbstractBinaryTree>() { nodeToRotate: RBNode?, currentRoot: RBNode?, ): RBNode? { - val rightChild = nodeToRotate?.rightChild ?: return nodeToRotate // if right child is null, no rotation needed + val rightChild = + nodeToRotate?.rightChild + ?: return nodeToRotate // if right child is null, no rotation needed var newRoot = currentRoot rightChild.parent = nodeToRotate.parent diff --git a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt index 91eeff4..394135d 100644 --- a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt @@ -9,10 +9,8 @@ class AVLTreeTest { @BeforeEach fun setup() { - tree = AVLTree() + tree = AVLTree() } - @Test - fun test() {} - -} \ No newline at end of file + @Test fun test() {} +} diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index 5c9d367..f352fe9 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -9,10 +9,8 @@ class BSTreeTest { @BeforeEach fun setup() { - tree = BSTree() + tree = BSTree() } - @Test - fun test() {} - -} \ No newline at end of file + @Test fun test() {} +} diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index 3e0ce4b..a176301 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -1,16 +1,15 @@ package tsl.trees import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test -class RBTreeTest{ +class RBTreeTest { @Nested inner class SearchTests() { @Test fun `search node that isn't in the tree`() { - val rbtree = RBTree() + val rbtree = RBTree() rbtree.insert(15, "hihi") rbtree.insert(10, "haha") rbtree.insert(5, "bugaga") @@ -20,7 +19,7 @@ class RBTreeTest{ @Test fun `search node that is in tree`() { - val rbtree = RBTree() + val rbtree = RBTree() rbtree.insert(15, "hihi") rbtree.insert(10, "haha") rbtree.insert(5, "bugaga") @@ -32,7 +31,7 @@ class RBTreeTest{ @Nested inner class InsertTests() { fun `insert if the node with this key doesn't exist`() { - val rbtree = RBTree() + val rbtree = RBTree() rbtree.insert(15, "i") rbtree.insert(10, "am") rbtree.insert(5, "really") @@ -41,7 +40,7 @@ class RBTreeTest{ } fun `insert if the node with this key exists`() { - val rbtree = RBTree() + val rbtree = RBTree() rbtree.insert(15, "i") rbtree.insert(10, "am") rbtree.insert(5, "really") @@ -50,31 +49,23 @@ class RBTreeTest{ } fun `insert left child`() { - val rbtree = RBTree() + val rbtree = RBTree() rbtree.insert(17, "legend") rbtree.insert(27, "club") rbtree.insert(49, "poliklinika") } fun `insert right child`() { - val rbtree = RBTree() + val rbtree = RBTree() rbtree.insert(17, "legend") rbtree.insert(27, "club") rbtree.insert(49, "poliklinika") } } - @Nested - inner class DeleteTests() { - - } - - @Nested - inner class IteratorTests() { - } + @Nested inner class DeleteTests() {} - @Nested - inner class AuxiliaryTests() { + @Nested inner class IteratorTests() {} - } -} \ No newline at end of file + @Nested inner class AuxiliaryTests() {} +} From 01767be1694d2cff2009ad5fe4874d905c11c6fd Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Wed, 27 Mar 2024 09:48:17 +0300 Subject: [PATCH 070/164] build: add new formatter 'ktfmt', update dependecies, set up formatter config and create relevant tasks --- lib/build.gradle.kts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index e993082..b585d23 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -7,11 +7,16 @@ plugins { // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. - alias(libs.plugins.jvm) +// alias(libs.plugins.jvm) // Apply the java-library plugin for API and implementation separation. `java-library` + + kotlin("jvm") version "1.9.23" + jacoco + + id("com.ncorti.ktfmt.gradle") version "0.17.0" } repositories { @@ -64,4 +69,20 @@ tasks.jacocoTestReport { } } +ktfmt { + // KotlinLang style - 4 space indentation - From kotlinlang.org/docs/coding-conventions.html + kotlinLangStyle() + // blockIndent is the indent size used when a new block is opened, in spaces. + blockIndent.set(4) + // continuationIndent is the indent size used when a line is broken because it's too + continuationIndent.set(4) + // Whether ktfmt should automatically add/remove trailing commas. + //manageTrailingCommas.set(true) current release doesn't support this feature + + removeUnusedImports.set(true) +} + +tasks.register("checkFormat") { dependsOn(tasks.ktfmtCheck) } + +tasks.register("format") { dependsOn(tasks.ktfmtFormat) } From 8b77a85e36879b619895d0e290a2d029cbfe24d4 Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 12:10:32 +0300 Subject: [PATCH 071/164] ci: switch to jacoco test-coverage system Switched to the Jacoco Test coverage system, due to better community support. Add distribution to JDK set up, that caused error in the latest CI PR. --- .github/workflows/codecoverage.yml | 45 +++++++++++++++++------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 4e20a2f..6f0ee4e 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -2,34 +2,41 @@ name: Measure coverage on: workflow_dispatch: + push: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ list.os }} + + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macox-latest] + + permissions: + pull-requests: write + steps: - uses: actions/checkout@v3 - - name: Set up JDK + - name: Set up JDK 17 uses: actions/setup-java@v3 with: - java-version: 17 - - - name: Set up Gradle - uses: gradle/gradle-build-action@v2 + java-version: '17' + distribution: zulu - - name: Generate kover coverage report - run: ./gradlew koverXmlReport + - name: Run Coverage + run: | + chmod +x gradlew + ./gradlew testCoverage - - name: Add coverage report to PR - id: kover - uses: mi-kas/kover-report@v1 + - name: Add coverage to PR + id: jacoco + uses: madrapps/jacoco-report@v1.6.1 with: - path: | - ${{ github.workspace }}/project1/build/reports/kover/report.xml - ${{ github.workspace }}/project2/build/reports/kover/report.xml - title: Code Coverage - update-comment: true - min-coverage-overall: 80 - min-coverage-changed-files: 80 - coverage-counter-type: LINE + paths: | + ${{ github.workspace }}/**/build/reports/jacoco/prodNormalDebugCoverage/prodNormalDebugCoverage.xml, + ${{ github.workspace }}/**/build/reports/jacoco/**/debugCoverage.xml + token: ${{ secrets.GITHUB_TOKEN }} + min-coverage-overall: 40 + min-coverage-changed-files: 60 From 3e6b889d4bbfdc57a6f4b5c787d4c5742f5f0a4e Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Sun, 17 Mar 2024 12:12:39 +0300 Subject: [PATCH 072/164] fix: removed strategy error Fixed strategy name (from list to matrix). --- .github/workflows/codecoverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 6f0ee4e..af42208 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -6,7 +6,7 @@ on: jobs: build: - runs-on: ${{ list.os }} + runs-on: ${{ matrix.os }} strategy: matrix: From 748e51803446c560cf2b432f219e0a3d3ccba070 Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Tue, 19 Mar 2024 22:43:46 +0300 Subject: [PATCH 073/164] fix: remove windows from workflow list, correct macos name 1. removed windows os from workflow list, due to incompatibility with common steps for executing the test coverage 2. corrected macos name (from macox) --- .github/workflows/codecoverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index af42208..5606e5d 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest, windows-latest, macox-latest] + os: [ubuntu-latest, macos-latest] permissions: pull-requests: write From f49f25d27c22db2cf73b5047efe1fe47607ab78f Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:17:13 +0300 Subject: [PATCH 074/164] ci: add merge rule and greeting for PR Added merge rule for that doesn't let you merge PR when you have an empty description. + Greeting for new contributor. --- .github/mergeable.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/mergeable.yml diff --git a/.github/mergeable.yml b/.github/mergeable.yml new file mode 100644 index 0000000..58e342a --- /dev/null +++ b/.github/mergeable.yml @@ -0,0 +1,18 @@ +version: 2 +mergeable: + - when: pull_request.* + name: "Description check" + validate: + - 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. + + - when: pull_request.opened + name: "Greet a contributor" + validate: [] + pass: + - do: comment + payload: + body: > + Thanks for creating a pull request! A maintainer will review your changes shortly. Please don't be discouraged if it takes a while. From b407cb8523f33431535f92f6d0a4e6cbdf501622 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:26:08 +0300 Subject: [PATCH 075/164] ci: reduce percent of coverage for test in main branch --- .github/workflows/codecoverage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 5606e5d..66620e1 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -37,6 +37,6 @@ jobs: ${{ github.workspace }}/**/build/reports/jacoco/prodNormalDebugCoverage/prodNormalDebugCoverage.xml, ${{ github.workspace }}/**/build/reports/jacoco/**/debugCoverage.xml token: ${{ secrets.GITHUB_TOKEN }} - min-coverage-overall: 40 - min-coverage-changed-files: 60 + min-coverage-overall: 10 + min-coverage-changed-files: 10 // its temporary, for test only From 43fc964a2727a0edae92cf854d6a7f6c7ae98d85 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:49:19 +0300 Subject: [PATCH 076/164] build: require code coverage report in csv, set specific directory for report file --- lib/build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index b585d23..15df3d3 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -63,9 +63,10 @@ tasks.jacocoTestReport { dependsOn(tasks.test) // tests are required to run before generating the report reports { - xml.required = true + xml.required = false html.required = true - csv.required = false + csv.required = true + csv.outputLocation.set(layout.buildDirectory.file("jacoco/report.csv")) } } @@ -85,4 +86,3 @@ ktfmt { tasks.register("checkFormat") { dependsOn(tasks.ktfmtCheck) } tasks.register("format") { dependsOn(tasks.ktfmtFormat) } - From de2de4a92fcea18510b68fcd9fa52b333a9246a1 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:04:01 +0300 Subject: [PATCH 077/164] ci: edit the task name, to execute for coverage report --- .github/workflows/codecoverage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 66620e1..34abedf 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -24,10 +24,10 @@ jobs: java-version: '17' distribution: zulu - - name: Run Coverage + - name: Create Coverage Report run: | chmod +x gradlew - ./gradlew testCoverage + ./gradlew jacocoTestReport - name: Add coverage to PR id: jacoco From 0661c49c3d54044989aafb3116c62889302e6dd2 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:17:21 +0300 Subject: [PATCH 078/164] ci: set task to run on PR, instead of push --- .github/workflows/codecoverage.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 34abedf..63aaf90 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -2,7 +2,7 @@ name: Measure coverage on: workflow_dispatch: - push: + pull_request: jobs: build: @@ -39,4 +39,3 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 10 min-coverage-changed-files: 10 // its temporary, for test only - From bd884b65143bd7caabf4aa36dfaf373212e35431 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Tue, 26 Mar 2024 10:29:29 +0300 Subject: [PATCH 079/164] refactor: replace variables with method calls and solve merge conflict --- lib/build.gradle.kts | 3 +- .../kotlin/tsl/iterator/BinaryTreeIterator.kt | 1 - lib/src/main/kotlin/tsl/trees/AVLTree.kt | 33 +++++++++---------- settings.gradle.kts | 5 +++ 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index b585d23..296f67f 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { // This dependency is used internally, and not exposed to consumers on their own compile classpath. implementation(libs.guava) + implementation(kotlin("stdlib-jdk8")) } // Apply a specific Java toolchain to ease working on different environments. @@ -77,7 +78,7 @@ ktfmt { // continuationIndent is the indent size used when a line is broken because it's too continuationIndent.set(4) // Whether ktfmt should automatically add/remove trailing commas. - //manageTrailingCommas.set(true) current release doesn't support this feature + // manageTrailingCommas.set(true) current release doesn't support this feature removeUnusedImports.set(true) } diff --git a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt index 8a3c8e7..df26199 100644 --- a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt +++ b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt @@ -2,7 +2,6 @@ package tsl.iterator import tsl.nodes.AbstractNode import tsl.trees.AbstractBinaryTree - internal class BinaryTreeIterator, V, N : AbstractNode>( tree: AbstractBinaryTree ) : Iterator> { diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index 3e649f9..275282d 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -26,25 +26,24 @@ class AVLTree, V> : AbstractBinaryTree>() when { keyToInsert < currNode.key -> - currNode.leftChild = - insertNodeAndBalanceRecursively(currNode.leftChild, keyToInsert, valueToInsert) + currNode.leftChild = insertNodeAndBalanceRecursively(currNode.leftChild, keyToInsert, valueToInsert) + keyToInsert > currNode.key -> - currNode.rightChild = - insertNodeAndBalanceRecursively(currNode.rightChild, keyToInsert, valueToInsert) + currNode.rightChild = insertNodeAndBalanceRecursively(currNode.rightChild, keyToInsert, valueToInsert) + else -> return currNode } - val balanceFactor = getBalanceFactor(currNode) var balancedNode: AVLNode = currNode - if (balanceFactor < -1) { // if left subtree got bigger + if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger currNode.leftChild?.let { if (keyToInsert > it.key) { // if inserted node has greater key currNode.leftChild = rotateLeft(it) // perform left rotation first } balancedNode = rotateRight(currNode) // anyway, perform right rotation } - } else if (balanceFactor > 1) { // if right subtree got bigger + } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger currNode.rightChild?.let { if (keyToInsert < it.key) { // if inserted node has lesser key currNode.rightChild = rotateRight(it) // perform right rotation first @@ -92,8 +91,7 @@ class AVLTree, V> : AbstractBinaryTree>() private fun getHeight(node: AVLNode?): Int = node?.height ?: -1 public override fun delete(key: K): V? { - val deletedValue: V = - search(key) ?: return null // if key isn't in the tree, there's nothing to delete + val deletedValue: V = search(key) ?: return null // if key isn't in the tree, there's nothing to delete deleteAndBalanceRecursively(root, key) @@ -109,35 +107,36 @@ class AVLTree, V> : AbstractBinaryTree>() currNode == null -> return null // node to be deleted was not found keyToDelete < currNode.key -> currNode.leftChild = deleteAndBalanceRecursively(currNode.leftChild, keyToDelete) + keyToDelete > currNode.key -> currNode.rightChild = deleteAndBalanceRecursively(currNode.rightChild, keyToDelete) - (currNode.leftChild == null || - currNode.rightChild == null) -> // if node has 0 or 1 child - return currNode.leftChild ?: currNode.rightChild // replace it with its child + + (currNode.leftChild == null || currNode.rightChild == null) -> // if node has 0 or 1 child + return currNode.leftChild ?: currNode.rightChild // replace it with its child + else -> { // if node has 2 children val successor = getMinNodeRecursively(currNode.rightChild) - if (successor != null) { + if (successor != null) { // replace it with its successor currNode.key = successor.key currNode.value = successor.value - // delete successor node from tree + // delete original successor from tree val newSubtree = deleteAndBalanceRecursively(currNode.rightChild, successor.key) if (newSubtree != null) currNode.rightChild = newSubtree } } } - val balanceFactor = getBalanceFactor(currNode) var balancedNode: AVLNode = currNode - if (balanceFactor < -1) { // if left subtree got bigger + if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger currNode.leftChild?.let { if (getBalanceFactor(it) > 0) { // if inserted node has greater key currNode.leftChild = rotateLeft(it) // perform left rotation first } balancedNode = rotateRight(currNode) // anyway, perform left rotation } - } else if (balanceFactor > 1) { // if right subtree got bigger + } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger currNode.rightChild?.let { if (getBalanceFactor(it) < 0) { // if inserted node has greater key currNode.rightChild = rotateRight(it) // perform right rotation first diff --git a/settings.gradle.kts b/settings.gradle.kts index f12bfbf..fa97dad 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,8 @@ +pluginManagement { + plugins { + kotlin("jvm") version "1.9.23" + } +} /* * This file was generated by the Gradle 'init' task. * From d5f4ce626e4a11cad5b82a0dc0880fd817f2a163 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 27 Mar 2024 21:51:51 +0300 Subject: [PATCH 080/164] ci: add generation of jacoco coverage badges --- .github/workflows/codecoverage.yml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 63aaf90..3f40866 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -2,33 +2,42 @@ name: Measure coverage on: workflow_dispatch: - pull_request: + pull_request: jobs: build: runs-on: ${{ matrix.os }} - + strategy: matrix: - os: [ubuntu-latest, macos-latest] + os: [ ubuntu-latest, macos-latest ] permissions: pull-requests: write steps: - uses: actions/checkout@v3 - + - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: zulu - + - name: Create Coverage Report run: | chmod +x gradlew ./gradlew jacocoTestReport + - if: matrix.os == 'ubuntu-latest' + name: Generate Jacoco Badge + uses: cicirello/jacoco-badge-generator@2.11.0 + with: + jacoco-csv-file: lib/build/jacoco/report.csv + badges-directory: .github/badges + generate-coverage-badge: true + coverage-badge-filename: jacoco.svg + - name: Add coverage to PR id: jacoco uses: madrapps/jacoco-report@v1.6.1 @@ -38,4 +47,4 @@ jobs: ${{ github.workspace }}/**/build/reports/jacoco/**/debugCoverage.xml token: ${{ secrets.GITHUB_TOKEN }} min-coverage-overall: 10 - min-coverage-changed-files: 10 // its temporary, for test only + min-coverage-changed-files: 10 // its temporary, for test only \ No newline at end of file From f14327c0ca7c216bcf882fb2bfc531fb022e6f87 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 27 Mar 2024 22:12:50 +0300 Subject: [PATCH 081/164] ci: fix badge generator version in codecoverage.yml --- .github/workflows/codecoverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 3f40866..70b642a 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -31,7 +31,7 @@ jobs: - if: matrix.os == 'ubuntu-latest' name: Generate Jacoco Badge - uses: cicirello/jacoco-badge-generator@2.11.0 + uses: cicirello/jacoco-badge-generator@v2.11.0 with: jacoco-csv-file: lib/build/jacoco/report.csv badges-directory: .github/badges From 59bec93d28acfe67a63ceeed06f5d3d37f35f141 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Fri, 29 Mar 2024 19:29:21 +0300 Subject: [PATCH 082/164] ci(refactor): remove usage of troublesome plugin (jacoco-report), due to issues with set up gradle tasks for it --- .github/workflows/codecoverage.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 70b642a..0745d34 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -36,15 +36,4 @@ jobs: jacoco-csv-file: lib/build/jacoco/report.csv badges-directory: .github/badges generate-coverage-badge: true - coverage-badge-filename: jacoco.svg - - - name: Add coverage to PR - id: jacoco - uses: madrapps/jacoco-report@v1.6.1 - with: - paths: | - ${{ github.workspace }}/**/build/reports/jacoco/prodNormalDebugCoverage/prodNormalDebugCoverage.xml, - ${{ github.workspace }}/**/build/reports/jacoco/**/debugCoverage.xml - token: ${{ secrets.GITHUB_TOKEN }} - min-coverage-overall: 10 - min-coverage-changed-files: 10 // its temporary, for test only \ No newline at end of file + coverage-badge-filename: jacoco.svg \ No newline at end of file From ebfcf80261da45527bb809d3ab6f208661fd37e5 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Thu, 28 Mar 2024 12:19:52 +0300 Subject: [PATCH 083/164] fix: make trees iterable by Pair --- lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt | 9 ++++----- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt index df26199..ec9f4d7 100644 --- a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt +++ b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt @@ -4,7 +4,7 @@ import tsl.nodes.AbstractNode import tsl.trees.AbstractBinaryTree internal class BinaryTreeIterator, V, N : AbstractNode>( tree: AbstractBinaryTree -) : Iterator> { +) : Iterator> { private var stack = ArrayDeque() private var currentNode: N? = tree.root @@ -13,16 +13,15 @@ internal class BinaryTreeIterator, V, N : AbstractNode { + override fun next(): Pair { while (currentNode != null) { currentNode?.let { stack.addLast(it) } currentNode = currentNode?.leftChild } - currentNode = stack.removeLast() - val nextNode = currentNode + val nextNode = stack.removeLast() currentNode = currentNode?.rightChild - return Pair(nextNode?.key, nextNode?.value) + return Pair(nextNode.key, nextNode.value) } } diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index aec9db5..9f871ad 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -4,7 +4,7 @@ import tsl.iterator.BinaryTreeIterator import tsl.nodes.AbstractNode abstract class AbstractBinaryTree, V, N : AbstractNode> : - Iterable> { + Iterable> { internal var root: N? = null public abstract fun delete(key: K): V? @@ -52,5 +52,5 @@ abstract class AbstractBinaryTree, V, N : AbstractNode> = BinaryTreeIterator(this) + public override fun iterator(): Iterator> = BinaryTreeIterator(this) } From b772c85da1d8c04ddceecb39d2aa5bf5b613ea41 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Thu, 28 Mar 2024 17:40:57 +0300 Subject: [PATCH 084/164] fix: next method in iterator --- lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt index ec9f4d7..4980f56 100644 --- a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt +++ b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt @@ -20,7 +20,7 @@ internal class BinaryTreeIterator, V, N : AbstractNode Date: Fri, 22 Mar 2024 19:19:22 +0300 Subject: [PATCH 085/164] Create README.md --- README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..129c616 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# forest-group + +[![Kotlin][kotlin_img]][kotlin_releases_url] +[![License][license_img]][repo_license_url] + +`forest-group` is a library that lets you easily create and use [Binary search trees](https://en.wikipedia.org/wiki/Binary_search_tree), [AVL trees](https://en.wikipedia.org/wiki/AVL_tree) and [Red-black trees](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) in your applications! + + + +## Table of contents +- [About the project](#about-the-project) +- [Usage](#usage) +- [Contributing](#contributing) +- [License](#license) + + + +## About The Project +Tree data structure is well-known for its average logarithmic operations time. However, there are a lot of tree implementations that are strict to data types that can be stored. +There is no problem - *just use keys to store anything you want!* + + + +## Usage +To create a tree, pass the key type and your stored data type to a generic. *Note that your key should implement Comparable type.* + +``` +val myTree = AVLTree() +``` + +You now can simply insert and replace values in your tree: + +``` +myTree.insert(keyA, "Something important") +val oldValue = myTree.insert(keyA, "Something more important") +``` + +You can also search for values and delete values from tree by keys: + +``` +val myValue = myTree.search(myKey) +myTree.delete(myKey) +``` + + + +## Contributing +If you have found a bug, or want to propose some useful feature for our project, please do the following: +1. Fork the Project +2. Create your Feature Branch (git checkout -b feature/MyFeature) +3. Commit your Changes (git commit -m 'add some Feature') +4. Push to the Branch (git push origin feature/MyFeature) +5. Open a Pull Request + + + +## License +Distributed under the MIT License. See LICENSE.md for more information. + + + + + +[license_img]: https://img.shields.io/badge/license-MIT-green +[kotlin_img]: https://img.shields.io/badge/kotlin-magenta + + + +[repo_license_url]: https://github.com/spbu-coding-2023/trees-2/blob/main/LICENSE.md + + + +[kotlin_releases_url]: https://kotlinlang.org/docs/releases.html#release-details From 85c4735457dd8526410eb9704ff25da0153e76b9 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Thu, 28 Mar 2024 23:54:16 +0300 Subject: [PATCH 086/164] refactor: remove unnecessary returns in rotation methods and variables, correct delete return, refactored a little --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 78 +++++++-------------- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 71 ------------------- 2 files changed, 26 insertions(+), 123 deletions(-) delete mode 100644 lib/src/test/kotlin/tsl/trees/RBTreeTest.kt diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 54dc33e..717e621 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -30,7 +30,7 @@ class RBTree, V> : AbstractBinaryTree>() { currentNode.leftChild = newNode newNode.parent = currentNode balanceNode(newNode) - return returnValue + break } currentNode = currentNode.leftChild } else if (currentNode.key < newNode.key) { @@ -38,14 +38,15 @@ class RBTree, V> : AbstractBinaryTree>() { currentNode.rightChild = newNode newNode.parent = currentNode balanceNode(newNode) - return returnValue + break } currentNode = currentNode.rightChild } else { - return returnValue + currentNode.key = key + balanceNode(newNode) + break } } - balanceNode(newNode) return returnValue } @@ -58,11 +59,13 @@ class RBTree, V> : AbstractBinaryTree>() { * @param RBNode-typed node. */ private fun balanceNode(node: RBNode?) { + if (node?.parent == null) root.also { it?.color = RBNode.Color.Black } var newNode = node var uncle: RBNode? var newRoot = root - while (newNode?.parent?.color == RBNode.Color.Red && newNode.parent != newRoot) { - var currentParent: RBNode? = newNode.parent + + while (newNode?.parent?.color == RBNode.Color.Red) { + val currentParent: RBNode? = newNode.parent val currentGrandParent: RBNode? = currentParent?.parent if (newNode.parent == newNode.parent?.parent?.leftChild) { uncle = currentParent?.parent?.rightChild @@ -72,11 +75,6 @@ class RBTree, V> : AbstractBinaryTree>() { currentGrandParent?.color = RBNode.Color.Red newNode = currentGrandParent } else { - if (newNode == newNode.parent?.rightChild) { - newNode = currentParent - newRoot = rotateLeft(newNode, newRoot) - currentParent = newNode?.parent - } currentParent?.color = RBNode.Color.Black currentGrandParent?.color = RBNode.Color.Red newRoot = rotateRight(currentGrandParent, newRoot) @@ -89,17 +87,13 @@ class RBTree, V> : AbstractBinaryTree>() { currentGrandParent?.color = RBNode.Color.Red newNode = currentGrandParent } else { - if (newNode == currentParent?.leftChild) { - newNode = currentParent - newRoot = rotateRight(newNode, newRoot) - currentParent = newNode.parent - } currentParent?.color = RBNode.Color.Black currentGrandParent?.color = RBNode.Color.Red newRoot = rotateLeft(currentGrandParent, newRoot) } } } + newRoot?.color = RBNode.Color.Black root = newRoot } @@ -110,8 +104,9 @@ class RBTree, V> : AbstractBinaryTree>() { * @return Value if delete was successful and null if it isn't.\. */ public override fun delete(key: K): V? { + val deletingNodeValue = search(key) root = deleteNode(key) - if (root == null) return search(key) + if (root != null) return deletingNodeValue root?.color = RBNode.Color.Black // ensure the root is black after deletion return null } @@ -126,7 +121,7 @@ class RBTree, V> : AbstractBinaryTree>() { private fun deleteNode( key: K, ): RBNode? { - var current: RBNode? = searchNodeRecursively(root, key) as RBNode? + var current: RBNode? = searchNodeRecursively(root, key) if (current == null) return null var newRoot = root @@ -189,14 +184,6 @@ class RBTree, V> : AbstractBinaryTree>() { currentSibling.color = RBNode.Color.Black currentNode.parent?.color = RBNode.Color.Red fixedRoot = rotateLeft(currentNode.parent, fixedRoot) - currentSibling = currentNode.parent?.rightChild // transfer to other cases - } else if ( - currentSibling?.leftChild?.color == RBNode.Color.Black && - currentSibling.rightChild?.color == RBNode.Color.Black - ) { - // 2nd case "currentSibling and its children are black" - currentSibling.color = RBNode.Color.Red - currentNode = currentNode.parent } else { if (currentSibling?.rightChild?.color == RBNode.Color.Black) { // 3rd case "like 2nd but the left child is red" @@ -219,15 +206,6 @@ class RBTree, V> : AbstractBinaryTree>() { currentSibling.color = RBNode.Color.Black currentNode.parent?.color = RBNode.Color.Red fixedRoot = rotateRight(currentNode.parent, fixedRoot) - currentSibling = currentNode.parent?.leftChild // transfer to other cases - } - // 2nd case "currentSibling and its children are black" - if ( - currentSibling?.leftChild?.color == RBNode.Color.Black && - currentSibling.rightChild?.color == RBNode.Color.Black - ) { - currentSibling.color = RBNode.Color.Red - currentNode = currentNode.parent } else { // 3rd case "like 2nd but the right child is red" if (currentSibling?.leftChild?.color == RBNode.Color.Black) { @@ -253,19 +231,17 @@ class RBTree, V> : AbstractBinaryTree>() { nodeToRotate: RBNode?, currentRoot: RBNode?, ): RBNode? { - val rightChild = - nodeToRotate?.leftChild - ?: return nodeToRotate // if left child is null, no rotation needed + val rightChild = nodeToRotate?.leftChild var newRoot = currentRoot - rightChild.parent = nodeToRotate.parent + rightChild?.parent = nodeToRotate?.parent - if (rightChild.leftChild != null) { + if (rightChild?.leftChild != null) { rightChild.leftChild?.parent = nodeToRotate } - rightChild.rightChild = nodeToRotate - nodeToRotate.parent = rightChild + rightChild?.rightChild = nodeToRotate + nodeToRotate?.parent = rightChild - if (rightChild.parent != null) { + if (rightChild?.parent != null) { if (nodeToRotate == rightChild.parent?.leftChild) { rightChild.parent?.leftChild = rightChild } else { @@ -283,22 +259,20 @@ class RBTree, V> : AbstractBinaryTree>() { nodeToRotate: RBNode?, currentRoot: RBNode?, ): RBNode? { - val rightChild = - nodeToRotate?.rightChild - ?: return nodeToRotate // if right child is null, no rotation needed + val rightChild = nodeToRotate?.rightChild var newRoot = currentRoot - rightChild.parent = nodeToRotate.parent + rightChild?.parent = nodeToRotate?.parent - nodeToRotate.rightChild = rightChild.leftChild - if (nodeToRotate.rightChild != null) { + nodeToRotate?.rightChild = rightChild?.leftChild + if (nodeToRotate?.rightChild != null) { nodeToRotate.rightChild?.parent = nodeToRotate } - rightChild.leftChild = nodeToRotate - nodeToRotate.parent = rightChild + rightChild?.leftChild = nodeToRotate + nodeToRotate?.parent = rightChild // rightChild took over "nodeToRotate" place so now its parent should point to rightChild - if (rightChild.parent != null) { + if (rightChild?.parent != null) { if (nodeToRotate == rightChild.parent?.leftChild) { rightChild.parent?.leftChild = rightChild } else { diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt deleted file mode 100644 index a176301..0000000 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ /dev/null @@ -1,71 +0,0 @@ -package tsl.trees - -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test - -class RBTreeTest { - @Nested - inner class SearchTests() { - @Test - fun `search node that isn't in the tree`() { - val rbtree = RBTree() - rbtree.insert(15, "hihi") - rbtree.insert(10, "haha") - rbtree.insert(5, "bugaga") - val returnValue = rbtree.search(7) - assertEquals(null, returnValue) - } - - @Test - fun `search node that is in tree`() { - val rbtree = RBTree() - rbtree.insert(15, "hihi") - rbtree.insert(10, "haha") - rbtree.insert(5, "bugaga") - val returnValue = rbtree.search(5) - assertEquals("bugaga", returnValue) - } - } - - @Nested - inner class InsertTests() { - fun `insert if the node with this key doesn't exist`() { - val rbtree = RBTree() - rbtree.insert(15, "i") - rbtree.insert(10, "am") - rbtree.insert(5, "really") - val returnValue = rbtree.insert(7, "wordless") - assertEquals(null, returnValue) - } - - fun `insert if the node with this key exists`() { - val rbtree = RBTree() - rbtree.insert(15, "i") - rbtree.insert(10, "am") - rbtree.insert(5, "really") - val returnValue = rbtree.insert(5, "wordless") - assertEquals("really", returnValue) - } - - fun `insert left child`() { - val rbtree = RBTree() - rbtree.insert(17, "legend") - rbtree.insert(27, "club") - rbtree.insert(49, "poliklinika") - } - - fun `insert right child`() { - val rbtree = RBTree() - rbtree.insert(17, "legend") - rbtree.insert(27, "club") - rbtree.insert(49, "poliklinika") - } - } - - @Nested inner class DeleteTests() {} - - @Nested inner class IteratorTests() {} - - @Nested inner class AuxiliaryTests() {} -} From a5ed79110f5edff50278eb71920444194520b4a5 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sat, 30 Mar 2024 17:41:22 +0300 Subject: [PATCH 087/164] feat: add isEmpty method to Abstract binary tree class --- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 9f871ad..03f77fd 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -32,6 +32,9 @@ abstract class AbstractBinaryTree, V, N : AbstractNode Date: Sat, 30 Mar 2024 14:53:19 +0300 Subject: [PATCH 088/164] test: implement tests for search, insert and clear methods of AVL tree --- lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt | 382 ++++++++++++++++++- 1 file changed, 379 insertions(+), 3 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt index 394135d..e35a0fc 100644 --- a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt @@ -2,15 +2,391 @@ package tsl.trees import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test class AVLTreeTest { - private lateinit var tree: AVLTree + private lateinit var avlTree: AVLTree @BeforeEach fun setup() { - tree = AVLTree() + avlTree = AVLTree() } - @Test fun test() {} + @Nested + inner class SearchTests { + @Test + fun `search of non-existing key should return null`() { + avlTree.insert(1, "Put") + avlTree.insert(2, "The") + avlTree.insert(3, "Lights") + avlTree.insert(4, "On") + + val expectedValue = null + val actualValue = avlTree.search(100) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `search of existing key should return associated value`() { + avlTree.insert(1, "This") + avlTree.insert(2, "Is") + avlTree.insert(3, "My") + avlTree.insert(4, "Home") + + val expectedValue = "Home" + val actualValue = avlTree.search(4) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `empty tree should not change after search`() { + avlTree.search(1) + + val expectedStructure = listOf>() + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + + @Test + fun `non-empty tree should not change after search`() { + avlTree.insert(1, "Losing") + avlTree.insert(2, "My") + avlTree.insert(3, "Favourite") + avlTree.insert(4, "Game") + + val expectedStructure = + listOf(Pair(1, "Losing"), Pair(2, "My"), Pair(3, "Favourite"), Pair(4, "Game")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + } + + @Nested + inner class InsertTests { + // Common tests + + @Test + fun `insert of new key should return null`() { + val expectedValue = null + val actualValue = avlTree.insert(1, "I am just a freak") + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `insert of existing key should replace old value with new value and return old value`() { + avlTree.insert(1, "But I love you so") + + val expectedReturnValue = "But I love you so" + val actualReturnValue = avlTree.insert(1, "Please let me go") + + val expectedNewValue = "Please let me go" + val actualNewValue = avlTree.search(1) + + assertEquals(expectedReturnValue, actualReturnValue, "Returned wrong value") + assertEquals(expectedNewValue, actualNewValue, "Value wasn't replaced with new value") + } + + // Small tree tests + + @Test + fun `insertion of left child triggering right rotation should balance small tree`() { + avlTree.insert(20, "Take") + avlTree.insert(15, "To church") + avlTree.insert(4, "Me") + + val expectedStructure = + listOf(Pair(4, "Me"), Pair(15, "To church"), Pair(20, "Take")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 15) + } + + @Test + fun `insertion of right child triggering left rotation should balance small tree`() { + avlTree.insert(4, "A") + avlTree.insert(15, "B") + avlTree.insert(20, "C") + + val expectedStructure = + listOf(Pair(4, "A"), Pair(15, "B"), Pair(20, "C")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 15) + } + + // Medium tree tests + + @Test + fun `insertion of right child triggering left-right rotation should balance medium tree`() { + avlTree.insert(20, "I") + avlTree.insert(4, "Swear") + avlTree.insert(26, "I'll only") + avlTree.insert(9, "Make") + avlTree.insert(3, "You") + + avlTree.insert(15, "Cry") + + val expectedStructure = + listOf( + Pair(3, "You"), Pair(4, "Swear"), Pair(9, "Make"), + Pair(15, "Cry"), Pair(20, "I"), Pair(26, "I'll only") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 9) + } + + @Test + fun `insertion of left child triggering left-right rotation should balance medium tree`() { + avlTree.insert(20, "E") + avlTree.insert(4, "B") + avlTree.insert(26, "F") + avlTree.insert(3, "A") + avlTree.insert(9, "D") + + avlTree.insert(8, "C") + + val expectedStructure = + listOf( + Pair(3, "A"), Pair(4, "B"), Pair(8, "C"), + Pair(9, "D"), Pair(20, "E"), Pair(26, "F") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 9) + } + + @Test + fun `insertion of right child triggering right-left rotation should balance medium tree`() { + avlTree.insert(20, "B") + avlTree.insert(4, "A") + avlTree.insert(26, "E") + avlTree.insert(24, "C") + avlTree.insert(30, "F") + + avlTree.insert(25, "D") + + val expectedStructure = + listOf( + Pair(4, "A"), Pair(20, "B"), Pair(24, "C"), + Pair(25, "D"), Pair(26, "E"), Pair(30, "F") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 24) + } + + @Test + fun `insertion of left child triggering right-left rotation should balance medium tree`() { + avlTree.insert(20, "B") + avlTree.insert(4, "A") + avlTree.insert(26, "E") + avlTree.insert(24, "D") + avlTree.insert(30, "F") + + avlTree.insert(21, "C") + + val expectedStructure = + listOf( + Pair(4, "A"), Pair(20, "B"), Pair(21, "C"), + Pair(24, "D"), Pair(26, "E"), Pair(30, "F") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 24) + } + + // Large tree tests + + @Test + fun `insertion of right child triggering left-right rotation should balance large tree`() { + avlTree.insert(20, "Master") + avlTree.insert(4, "Of puppets") + avlTree.insert(26, "I'm pulling") + avlTree.insert(3, "Your") + avlTree.insert(9, "Strings") + avlTree.insert(21, "Twisting") + avlTree.insert(30, "Your mind") + avlTree.insert(2, "And") + avlTree.insert(7, "Smashing") + avlTree.insert(11, "Your") + + avlTree.insert(15, "Dreams") + + val expectedStructure = + listOf( + Pair(2, "And"), Pair(3, "Your"), Pair(4, "Of puppets"), + Pair(7, "Smashing"), Pair(9, "Strings"), Pair(11, "Your"), + Pair(15, "Dreams"), Pair(20, "Master"), Pair(21, "Twisting"), + Pair(26, "I'm pulling"), Pair(30, "Your mind") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 9) + } + + @Test + fun `insertion of left child triggering left-right rotation should balance large tree`() { + avlTree.insert(20, "H") + avlTree.insert(4, "C") + avlTree.insert(26, "J") + avlTree.insert(3, "B") + avlTree.insert(9, "F") + avlTree.insert(21, "I") + avlTree.insert(30, "K") + avlTree.insert(2, "A") + avlTree.insert(7, "E") + avlTree.insert(11, "G") + + avlTree.insert(6, "D") + + val expectedStructure = + listOf( + Pair(2, "A"), Pair(3, "B"), Pair(4, "C"), + Pair(6, "D"), Pair(7, "E"), Pair(9, "F"), + Pair(11, "G"), Pair(20, "H"), Pair(21, "I"), + Pair(26, "J"), Pair(30, "K") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 9) + } + + @Test + fun `insertion of right child triggering right-left rotation should balance large tree`() { + avlTree.insert(20, "D") + avlTree.insert(10, "B") + avlTree.insert(30, "I") + avlTree.insert(3, "A") + avlTree.insert(15, "C") + avlTree.insert(25, "F") + avlTree.insert(35, "J") + avlTree.insert(22, "E") + avlTree.insert(27, "G") + avlTree.insert(40, "K") + + avlTree.insert(28, "H") + + val expectedStructure = + listOf( + Pair(3, "A"), Pair(10, "B"), Pair(15, "C"), + Pair(20, "D"), Pair(22, "E"), Pair(25, "F"), + Pair(27, "G"), Pair(28, "H"), Pair(30, "I"), + Pair(35, "J"), Pair(40, "K") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 25) + } + + @Test + fun `insertion of left child triggering right-left rotation should balance large tree`() { + avlTree.insert(20, "D") + avlTree.insert(10, "B") + avlTree.insert(30, "I") + avlTree.insert(3, "A") + avlTree.insert(15, "C") + avlTree.insert(25, "F") + avlTree.insert(35, "J") + avlTree.insert(22, "E") + avlTree.insert(27, "H") + avlTree.insert(40, "K") + + avlTree.insert(26, "G") + + val expectedStructure = + listOf( + Pair(3, "A"), Pair(10, "B"), Pair(15, "C"), + Pair(20, "D"), Pair(22, "E"), Pair(25, "F"), + Pair(26, "G"), Pair(27, "H"), Pair(30, "I"), + Pair(35, "J"), Pair(40, "K") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 25) + } + } + + @Nested + inner class ClearTests { + @Test + fun `empty tree should be empty after clear`() { + avlTree.clear() + + val expectedStructure = listOf>() + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + + @Test + fun `not empty tree should be empty after clear`() { + avlTree.insert(1, "Take") + avlTree.insert(2, "Me") + avlTree.insert(3, "To") + avlTree.insert(4, "Church") + + avlTree.clear() + + val expectedStructure = listOf>() + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + } } From c1180c5b6ae61909f167897fa0f050c201f297f8 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sat, 30 Mar 2024 10:58:08 +0300 Subject: [PATCH 089/164] fix: insertion in AVL tree --- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 72 +++++++++++++----------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index 275282d..20baa56 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -5,7 +5,7 @@ import tsl.nodes.AVLNode class AVLTree, V> : AbstractBinaryTree>() { public override fun insert(key: K, value: V): V? { - val oldValueByKey = search(key) // null if key isn't in the tree + val oldValueByKey = search(key) // null if key isn't in the tree insertNodeAndBalanceRecursively(root, key, value) @@ -31,24 +31,27 @@ class AVLTree, V> : AbstractBinaryTree>() keyToInsert > currNode.key -> currNode.rightChild = insertNodeAndBalanceRecursively(currNode.rightChild, keyToInsert, valueToInsert) - else -> return currNode + else -> { + currNode.value = valueToInsert + return currNode + } } var balancedNode: AVLNode = currNode - if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger + if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger currNode.leftChild?.let { - if (keyToInsert > it.key) { // if inserted node has greater key - currNode.leftChild = rotateLeft(it) // perform left rotation first + if (keyToInsert > it.key) { // if inserted node has greater key + currNode.leftChild = rotateLeft(it) // perform left rotation first } - balancedNode = rotateRight(currNode) // anyway, perform right rotation + balancedNode = rotateRight(currNode) // anyway, perform right rotation } - } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger + } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger currNode.rightChild?.let { - if (keyToInsert < it.key) { // if inserted node has lesser key - currNode.rightChild = rotateRight(it) // perform right rotation first + if (keyToInsert < it.key) { // if inserted node has lesser key + currNode.rightChild = rotateRight(it) // perform right rotation first } - balancedNode = rotateLeft(currNode) // anyway, perform left rotation + balancedNode = rotateLeft(currNode) // anyway, perform left rotation } } @@ -104,44 +107,47 @@ class AVLTree, V> : AbstractBinaryTree>() ): AVLNode? { when { - currNode == null -> return null // node to be deleted was not found - keyToDelete < currNode.key -> + currNode == null -> return null // node to be deleted was not found + + keyToDelete < currNode.key -> // node to be deleted is in the left subtree currNode.leftChild = deleteAndBalanceRecursively(currNode.leftChild, keyToDelete) - keyToDelete > currNode.key -> + keyToDelete > currNode.key -> // node to be deleted is in the right subtree currNode.rightChild = deleteAndBalanceRecursively(currNode.rightChild, keyToDelete) - (currNode.leftChild == null || currNode.rightChild == null) -> // if node has 0 or 1 child - return currNode.leftChild ?: currNode.rightChild // replace it with its child - - else -> { // if node has 2 children - val successor = getMinNodeRecursively(currNode.rightChild) - if (successor != null) { // replace it with its successor - currNode.key = successor.key - currNode.value = successor.value - - // delete original successor from tree - val newSubtree = deleteAndBalanceRecursively(currNode.rightChild, successor.key) - if (newSubtree != null) currNode.rightChild = newSubtree + else -> { + if (currNode.leftChild == null ||currNode.rightChild == null) { + return currNode.leftChild ?: currNode.rightChild + } else { + val successor = getMinNodeRecursively(currNode.rightChild) + if (successor != null) { + // copy its successor + currNode.key = successor.key + currNode.value = successor.value + + // delete original successor node from tree + val newSubtree = deleteAndBalanceRecursively(currNode.rightChild, successor.key) + if (newSubtree != null) currNode.rightChild = newSubtree + } } } } var balancedNode: AVLNode = currNode - if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger + if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger currNode.leftChild?.let { - if (getBalanceFactor(it) > 0) { // if inserted node has greater key - currNode.leftChild = rotateLeft(it) // perform left rotation first + if (getBalanceFactor(it) > 0) { // if inserted node has greater key + currNode.leftChild = rotateLeft(it) // perform left rotation first } - balancedNode = rotateRight(currNode) // anyway, perform left rotation + balancedNode = rotateRight(currNode) // anyway, perform left rotation } - } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger + } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger currNode.rightChild?.let { - if (getBalanceFactor(it) < 0) { // if inserted node has greater key - currNode.rightChild = rotateRight(it) // perform right rotation first + if (getBalanceFactor(it) < 0) { // if inserted node has greater key + currNode.rightChild = rotateRight(it) // perform right rotation first } - balancedNode = rotateLeft(currNode) // anyway, perform left rotation + balancedNode = rotateLeft(currNode) // anyway, perform left rotation } } From dead3576d647f96516fe0987ce8a1641657d7fc1 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sat, 30 Mar 2024 16:59:40 +0300 Subject: [PATCH 090/164] test: implement tests for delete method of AVL tree --- lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt | 418 +++++++++++++++++++ 1 file changed, 418 insertions(+) diff --git a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt index e35a0fc..a292f0d 100644 --- a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt @@ -358,6 +358,424 @@ class AVLTreeTest { } } + @Nested + inner class DeleteTests { + // Common tests + + @Test + fun `if key doesn't exist deletion should not change tree and return null`() { + avlTree.insert(10, "A") + avlTree.insert(20, "B") + avlTree.insert(30, "C") + + val expectedReturn = null + val actualReturn = avlTree.delete(40) + + val expectedStructure = + listOf(Pair(10, "A"), Pair(20, "B"), Pair(30, "C")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedReturn, actualReturn, "Returned wrong value") + assertEquals(expectedStructure, actualStructure, "The tree changed after no deletion") + assertEquals(avlTree.root?.key, 20) + } + + @Test + fun `if key exists deletion should return associated value and delete node`() { + avlTree.insert(10, "A") + avlTree.insert(20, "B") + avlTree.insert(30, "C") + + val expectedReturn = "A" + val actualReturn = avlTree.delete(10) + + val expectedStructure = + listOf(Pair(20, "B"), Pair(30, "C")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedReturn, actualReturn, "Returned wrong value") + assertEquals(expectedStructure, actualStructure, "The tree has incorrect structure after deletion") + assertEquals(avlTree.root?.key, 20) + } + + @Test + fun `deletion of node with one child should replace it with its child`() { + avlTree.insert(20, "B") + avlTree.insert(10, "A") + avlTree.insert(30, "C") + avlTree.insert(40, "D") + + avlTree.delete(30) + + val expectedStructure = + listOf(Pair(10, "A"), Pair(20, "B"), Pair(40, "D")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 20) + } + + // Small tree tests + + @Test + fun `deletion of right child triggering right rotation should balance small tree`() { + avlTree.insert(40, "D") + avlTree.insert(30, "C") + avlTree.insert(20, "B") + avlTree.insert(10, "A") + + avlTree.delete(40) + + val expectedStructure = + listOf(Pair(10, "A"), Pair(20, "B"), Pair(30, "C")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 20) + } + + @Test + fun `deletion of left child triggering left rotation should balance small tree`() { + avlTree.insert(10, "A") + avlTree.insert(20, "B") + avlTree.insert(30, "C") + avlTree.insert(40, "D") + + avlTree.delete(10) + + val expectedStructure = + listOf(Pair(20, "B"), Pair(30, "C"), Pair(40, "D")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 30) + } + + @Test + fun `deletion of right child triggering left-right rotation should balance small tree`() { + avlTree.insert(30, "C") + avlTree.insert(10, "A") + avlTree.insert(40, "D") + avlTree.insert(20, "B") + + avlTree.delete(40) + + val expectedStructure = + listOf(Pair(10, "A"), Pair(20, "B"), Pair(30, "C")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 20) + } + + @Test + fun `deletion of left child triggering right-left rotation should balance small tree`() { + avlTree.insert(20, "B") + avlTree.insert(10, "A") + avlTree.insert(40, "D") + avlTree.insert(30, "C") + + avlTree.delete(10) + + val expectedStructure = + listOf(Pair(20, "B"), Pair(30, "C"), Pair(40, "D")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 30) + } + + @Test + fun `deletion of node with two children should replace it with its successor in small tree`() { + avlTree.insert(20, "B") + avlTree.insert(10, "A") + avlTree.insert(30, "C") + avlTree.insert(40, "D") + + avlTree.delete(20) + + val expectedStructure = + listOf(Pair(10, "A"), Pair(30, "C"), Pair(40, "D")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 30) + } + + // Medium tree tests + + @Test + fun `deletion of right child triggering right rotation should balance medium tree`() { + avlTree.insert(50, "E") + avlTree.insert(30, "C") + avlTree.insert(60, "F") + avlTree.insert(20, "B") + avlTree.insert(40, "D") + avlTree.insert(70, "G") + avlTree.insert(10, "A") + + avlTree.delete(70) + + val expectedStructure = + listOf( + Pair(10, "A"), Pair(20, "B"), Pair(30, "C"), + Pair(40, "D"), Pair(50, "E"), Pair(60, "F") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 30) + } + + @Test + fun `deletion of left child triggering left rotation should balance medium tree`() { + avlTree.insert(30, "C") + avlTree.insert(20, "B") + avlTree.insert(50, "E") + avlTree.insert(10, "A") + avlTree.insert(40, "D") + avlTree.insert(60, "F") + avlTree.insert(70, "G") + + avlTree.delete(10) + + val expectedStructure = + listOf( + Pair(20, "B"), Pair(30, "C"), Pair(40, "D"), + Pair(50, "E"), Pair(60, "F"), Pair(70, "G") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 50) + } + + @Test + fun `deletion of node with two children should replace it with its successor in medium tree`() { + avlTree.insert(30, "C") + avlTree.insert(20, "B") + avlTree.insert(50, "E") + avlTree.insert(10, "A") + avlTree.insert(40, "D") + avlTree.insert(60, "F") + avlTree.insert(70, "G") + + avlTree.delete(50) + + val expectedStructure = + listOf( + Pair(10, "A"), Pair(20, "B"), Pair(30, "C"), + Pair(40, "D"), Pair(60, "F"), Pair(70, "G") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 30) + } + + // Large tree tests + @Test + fun `deletion of right child triggering right rotation should balance large tree`() { + avlTree.insert(80, "H") + avlTree.insert(50, "E") + avlTree.insert(120, "L") + avlTree.insert(30, "C") + avlTree.insert(60, "F") + avlTree.insert(100, "J") + avlTree.insert(130, "M") + avlTree.insert(20, "B") + avlTree.insert(40, "D") + avlTree.insert(70, "G") + avlTree.insert(90, "I") + avlTree.insert(110, "K") + avlTree.insert(10, "A") + + avlTree.delete(130) + + val expectedStructure = + listOf( + Pair(10, "A"), Pair(20, "B"), Pair(30, "C"), + Pair(40, "D"), Pair(50, "E"), Pair(60, "F"), + Pair(70, "G"), Pair(80, "H"), Pair(90, "I"), + Pair(100, "J"), Pair(110, "K"), Pair(120, "L") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 80) + } + + @Test + fun `deletion of left child triggering left rotation should balance large tree`() { + avlTree.insert(60, "F") + avlTree.insert(20, "B") + avlTree.insert(90, "I") + avlTree.insert(10, "A") + avlTree.insert(40, "D") + avlTree.insert(80, "H") + avlTree.insert(110, "K") + avlTree.insert(30, "C") + avlTree.insert(50, "E") + avlTree.insert(70, "G") + avlTree.insert(100, "J") + avlTree.insert(120, "L") + avlTree.insert(130, "M") + + avlTree.delete(10) + + val expectedStructure = + listOf( + Pair(20, "B"), Pair(30, "C"), Pair(40, "D"), + Pair(50, "E"), Pair(60, "F"), Pair(70, "G"), + Pair(80, "H"), Pair(90, "I"), Pair(100, "J"), + Pair(110, "K"), Pair(120, "L"), Pair(130, "M") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 60) + } + + @Test + fun `deletion of right child triggering double right rotation should balance large tree`() { + avlTree.insert(80, "H") + avlTree.insert(50, "E") + avlTree.insert(110, "K") + avlTree.insert(30, "C") + avlTree.insert(60, "F") + avlTree.insert(100, "J") + avlTree.insert(120, "L") + avlTree.insert(20, "B") + avlTree.insert(40, "D") + avlTree.insert(70, "G") + avlTree.insert(90, "I") + avlTree.insert(10, "A") + + avlTree.delete(120) + + val expectedStructure = + listOf( + Pair(10, "A"), Pair(20, "B"), Pair(30, "C"), + Pair(40, "D"), Pair(50, "E"), Pair(60, "F"), + Pair(70, "G"), Pair(80, "H"), Pair(90, "I"), + Pair(100, "J"), Pair(110, "K") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 50) + } + + @Test + fun `deletion of left child triggering double left rotation should balance large tree`() { + avlTree.insert(50, "E") + avlTree.insert(20, "B") + avlTree.insert(80, "H") + avlTree.insert(10, "A") + avlTree.insert(30, "C") + avlTree.insert(70, "G") + avlTree.insert(100, "J") + avlTree.insert(40, "D") + avlTree.insert(60, "F") + avlTree.insert(90, "I") + avlTree.insert(110, "K") + avlTree.insert(120, "L") + + avlTree.delete(10) + + val expectedStructure = + listOf( + Pair(20, "B"), Pair(30, "C"), Pair(40, "D"), + Pair(50, "E"), Pair(60, "F"), Pair(70, "G"), + Pair(80, "H"), Pair(90, "I"), Pair(100, "J"), + Pair(110, "K"), Pair(120, "L") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 80) + } + + @Test + fun `deletion of node with two children should replace it with its successor in large tree`() { + avlTree.insert(80, "H") + avlTree.insert(50, "E") + avlTree.insert(120, "L") + avlTree.insert(30, "C") + avlTree.insert(60, "F") + avlTree.insert(100, "J") + avlTree.insert(130, "M") + avlTree.insert(20, "B") + avlTree.insert(40, "D") + avlTree.insert(70, "G") + avlTree.insert(90, "I") + avlTree.insert(110, "K") + avlTree.insert(10, "A") + + avlTree.delete(80) + + val expectedStructure = + listOf( + Pair(10, "A"), Pair(20, "B"), Pair(30, "C"), + Pair(40, "D"), Pair(50, "E"), Pair(60, "F"), + Pair(70, "G"), Pair(90, "I"), Pair(100, "J"), + Pair(110, "K"), Pair(120, "L"), Pair(130, "M") + ) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 90) + } + } + @Nested inner class ClearTests { @Test From f798a1f66da77b7f71da25bce7d99a2033ca6574 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sat, 30 Mar 2024 14:02:25 +0300 Subject: [PATCH 091/164] docs: correct copyright date in LICENSE.md --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index 01c67d0..34355b9 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License - Copyright (c) 2023-present Gavrilenko Mike, Shakirov Karim, Vlasenco Daniel + Copyright (c) 2024-present Gavrilenko Mike, Shakirov Karim, Vlasenco Daniel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From fbf988b34353ee91c5bb2bdf2933058f7221103d Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Fri, 29 Mar 2024 19:26:11 +0300 Subject: [PATCH 092/164] test: add initial test-base for rb-tree, include delete, insert and search methods tests --- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 433 ++++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 lib/src/test/kotlin/tsl/trees/RBTreeTest.kt diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt new file mode 100644 index 0000000..b6a68c0 --- /dev/null +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -0,0 +1,433 @@ +package tsl.trees + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import tsl.nodes.RBNode +import kotlin.test.assertEquals + + +class RBTreeTest { + + @Nested + inner class InsertTests() { + + @Test + fun `insert if the node with this key doesn't exist`() { + val rbtree = RBTree() + rbtree.insert(15, "i") + rbtree.insert(10, "am") + rbtree.insert(5, "really") + val returnValue = rbtree.insert(7, "wordless") + Assertions.assertEquals(null, returnValue) + } + + @Test + fun `insert if the node with this key exists`() { + val rbtree = RBTree() + rbtree.insert(15, "i") + rbtree.insert(10, "am") + rbtree.insert(5, "really") + val returnValue = rbtree.insert(5, "wordless") + Assertions.assertEquals("really", returnValue) + } + + @Test + fun `insert left child`() { + val rbtree = RBTree() + rbtree.insert(17, "legend") + rbtree.insert(27, "club") + rbtree.insert(49, "poliklinika") + } + + @Test + fun `insert right child`() { + val rbtree = RBTree() + rbtree.insert(17, "legend") + rbtree.insert(27, "club") + rbtree.insert(49, "poliklinika") + } + } + + @Nested + inner class SearchTests() { + + @Test + fun `search node that isn't in the rbtree`() { + val rbtree = RBTree() + rbtree.insert(15, "hihi") + rbtree.insert(10, "haha") + rbtree.insert(5, "bugaga") + val returnValue = rbtree.search(7) + Assertions.assertEquals(null, returnValue) + } + + @Test + fun `search node that is in rbtree`() { + val rbtree = RBTree() + rbtree.insert(15, "hihi") + rbtree.insert(10, "haha") + rbtree.insert(5, "bugaga") + val returnValue = rbtree.search(5) + Assertions.assertEquals("bugaga", returnValue) + } + } + + @Nested + inner class DeleteTests() { + + @Test + fun `successful delete (return value)`() { + val rbtree = RBTree() + rbtree.insert(30, "pink") + rbtree.insert(20, "white") + rbtree.insert(40, "red") + val returnValue = rbtree.delete(40) + Assertions.assertEquals("red", returnValue) + } + + @Test + fun `unsuccessful delete (return value)`() { + val rbtree = RBTree() + rbtree.insert(30, "pink") + rbtree.insert(20, "white") + rbtree.insert(40, "red") + val returnValue = rbtree.delete(50) + Assertions.assertEquals(null, returnValue) + } + + @Test + fun `delete if sibling and its left-child are black`() { + val rbtree = RBTree() + rbtree.insert(30, "pink") + rbtree.insert(20, "white") + rbtree.insert(40, "red") + rbtree.insert(50, "yellow") + val returnValue = rbtree.delete(20) + Assertions.assertEquals("white", returnValue) + } + +// @Test +// fun `right-right case`() { +// // s is right child of its parent and both children of s are red +// val rbtree = RBTree() +// rbtree.insert(30, "pink") +// rbtree.insert(18, "white") +// rbtree.insert(40, "red") +// rbtree.insert(45, "yellow") +// val returnValue = rbtree.delete(40) +// var inorderedTraversalTree: List> = listOf() +// for ((key, value) in rbtree) { +// inorderedTraversalTree += Pair(key, value) +// } +// } + + @Test + fun `left-left case`() { + // s is left child of its parent and both children of s are red + val rbtree = RBTree() + rbtree.insert(30, "pink") + rbtree.insert(40, "white") + rbtree.insert(18, "red") + rbtree.insert(10, "yellow") + val returnValue = rbtree.delete(18) + var inorderedTraversalTree: List> = listOf() + for ((key, value) in rbtree) { + inorderedTraversalTree += Pair(key, value) + println(key) + } + Assertions.assertEquals(listOf(Pair(10, "yellow"), Pair(30, "pink"), Pair(40, "white")), inorderedTraversalTree) + Assertions.assertEquals("red", returnValue) + } + + @Test + fun `right-left case`() { + // s is right child of its parent and r is left child of s + val rbtree = RBTree() + rbtree.insert(30, "pink") + rbtree.insert(40, "white") + rbtree.insert(18, "red") + rbtree.insert(35, "yellow") + val returnValue = rbtree.delete(40) + var inorderedTraversalTree: List> = listOf() + for ((key, value) in rbtree) { + inorderedTraversalTree += Pair(key, value) + println(key) + } + Assertions.assertEquals(listOf(Pair(18, "red"), Pair(30, "pink"), Pair(35, "yellow")), inorderedTraversalTree) + Assertions.assertEquals("white", returnValue) + } + + @Test + fun `left-right case`() { + // s is left child of its parent and r is right child + val rbtree = RBTree() + rbtree.insert(30, "pink") + rbtree.insert(40, "white") + rbtree.insert(18, "red") + rbtree.insert(20, "yellow") + val returnValue = rbtree.delete(18) + var inorderedTraversalTree: List> = listOf() + for ((key, value) in rbtree) { + inorderedTraversalTree += Pair(key, value) + println(key) + } + Assertions.assertEquals(listOf(Pair(20, "yellow"), Pair(30, "pink"), Pair(40, "white")), inorderedTraversalTree) + Assertions.assertEquals("red", returnValue) + + } + // fun `sibling is black and both children are black`() { +// val rbtree = RBTree() +// rbtree.insert(30, "pink") +// rbtree.insert(40, "white") +// rbtree.insert(18, "red") +// rbtree.delete(18) +// // TODO +// } +// + @Test + fun `left-red sibling`() { + // s is left child of its parent + val rbtree = RBTree() + rbtree.insert(20, "pink") + rbtree.insert(30, "white") + rbtree.insert(10, "red") + rbtree.insert(25, "brown") + rbtree.insert(35, "gold") + val returnValue = rbtree.delete(10) + var inorderedTraversalTree: List> = listOf() + for ((key, value) in rbtree) { + inorderedTraversalTree += Pair(key, value) + println(key) + } + Assertions.assertEquals(listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), inorderedTraversalTree) + Assertions.assertEquals("red", returnValue) + } + + @Test + fun `remove root`(){ + val rbtree = RBTree() + rbtree.insert(10, "hihi haha") + val returnValue = rbtree.delete(10) + assertEquals(null, returnValue) + } + + @Test + fun `delete node with two children`() { + // s is left child of its parent + val rbtree = RBTree() + rbtree.insert(20, "pink") + rbtree.insert(30, "white") + rbtree.insert(10, "red") + rbtree.insert(25, "brown") + rbtree.insert(35, "gold") + val returnValue = rbtree.delete(30) + var inorderedTraversalTree: List> = listOf() + for ((key, value) in rbtree) { + inorderedTraversalTree += Pair(key, value) + println(key) + } + Assertions.assertEquals(listOf( Pair(10, "red"), Pair(20, "pink"), Pair(25, "brown"), Pair(35, "gold")), inorderedTraversalTree) + Assertions.assertEquals("white", returnValue) + } + + @Test + fun testFixAfterDeletionCase3LeftChildRed() { + val rbTree = RBTree() + rbTree.insert(15, "C") + rbTree.insert(10, "A") + rbTree.insert(20, "E") + rbTree.insert(18, "D") + + // for case 3 - insert sibling and ensure its child is red + rbTree.insert(17, "Z") + + // deletion to trigger fixAfterDeletion with Case 3 conditions + rbTree.delete(10) + + val currentSibling = rbTree.root?.rightChild + + assertEquals(RBNode.Color.Black, currentSibling?.leftChild?.color) + assertEquals(RBNode.Color.Black, currentSibling?.color) + } + + @Test + fun `fix after deletion case 3 right child red`() { + val rbTree = RBTree() + rbTree.insert(15, "C") + rbTree.insert(10, "A") + rbTree.insert(20, "E") + rbTree.insert(22, "F") + + // for case 3 - insert sibling and ensure its child is red + rbTree.insert(23, "K") + + // deletion to trigger fixAfterDeletion with Case 3 conditions + rbTree.delete(10) + + val currentSibling = rbTree.root?.rightChild + + assertEquals(null, currentSibling?.rightChild?.color) + assertEquals(RBNode.Color.Black, currentSibling?.color) + } + +// @Test +// fun testFixAfterDeletionSecondCase() { +// val rbtree = RBTree() +// rbtree.insert(15, "c") +// rbtree.insert(10, "a") +// rbtree.insert(20, "e") +// rbtree.insert(5, "b") +// rbtree.insert(6, "d") +// +// val returnValue = rbtree.delete(6) +// var inorderedTraversalTree: List> = listOf() +// for ((key, value) in rbtree) { +// inorderedTraversalTree += Pair(key, value) +// println(key) +// } +// Assertions.assertEquals(listOf(Pair(5, "b"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), inorderedTraversalTree) +// Assertions.assertEquals("d", returnValue) +// } + + + +// @Test +// fun testFixAfterDeletionFourthCase() { +// val rbtree = RBTree() +// rbtree.insert(15, "c") +// rbtree.insert(10, "a") +// rbtree.insert(20, "e") +// rbtree.insert(5, "b") +// rbtree.insert(6, "d") +// rbtree.insert(4, "f") +// +// val returnValue = rbtree.delete(4) +// var inorderedTraversalTree: List> = listOf() +// for ((key, value) in rbtree) { +// inorderedTraversalTree += Pair(key, value) +// println(key) +// } +// Assertions.assertEquals(listOf(Pair(5, "b"), Pair(6, "d"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), inorderedTraversalTree) +// Assertions.assertEquals("f", returnValue) +// } + + + + // fix +// @Test +// fun `right-red sibling`() { +// // s is right child of its parent +// val rbtree = RBTree() +// rbtree.insert(20, "pink") +// rbtree.insert(30, "white") +// rbtree.insert(15, "red") +// rbtree.insert(10, "brown") +// rbtree.insert(5, "gold") +// val returnValue = rbtree.delete(30) +// var inorderedTraversalTree: List> = listOf() +// for ((key, value) in rbtree) { +// inorderedTraversalTree += Pair(key, value) +// } +// Assertions.assertEquals(listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), inorderedTraversalTree) +// Assertions.assertEquals("red", returnValue) +// } + +// // fix +// @Test +// fun `root left rotate`() { +// val rbtree = RBTree() +// rbtree.insert(20, "pink") +// rbtree.insert(30, "white") +// rbtree.insert(15, "red") +// rbtree.insert(10, "brown") +// rbtree.insert(5, "gold") +// val returnValue = rbtree.delete(20) +// var inorderedTraversalTree: List> = listOf() +// for ((key, value) in rbtree) { +// inorderedTraversalTree += Pair(key, value) +// println(key) +// } +// Assertions.assertEquals(listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), inorderedTraversalTree) +// Assertions.assertEquals("red", returnValue) +// } + +// // fix +// @Test +// fun `root right rotate`() { +// val rbtree = RBTree() +// rbtree.insert(20, "pink") +// rbtree.insert(30, "white") +// rbtree.insert(15, "red") +// rbtree.insert(10, "brown") +// rbtree.insert(5, "gold") +// val returnValue = rbtree.delete(30) +// var inorderedTraversalTree: List> = listOf() +// for ((key, value) in rbtree) { +// inorderedTraversalTree += Pair(key, value) +// println(key) +// } +// Assertions.assertEquals(listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), inorderedTraversalTree) +// Assertions.assertEquals("red", returnValue) +// } + } + + @Nested + inner class BalancerTests() { + + } + + @Nested + inner class AuxiliaryTests() { + @Test + fun testInsertAndBalance() { + val rbtree = RBTree() + rbtree.insert(10, "A") + rbtree.insert(5, "B") + rbtree.insert(15, "C") + rbtree.insert(3, "D") + rbtree.insert(7, "E") + + assertEquals(RBNode.Color.Black, rbtree.root?.color) + assertEquals(RBNode.Color.Black, rbtree.root?.leftChild?.color) + assertEquals(RBNode.Color.Black, rbtree.root?.rightChild?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.leftChild?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.rightChild?.color) + } + + @Test + fun testRBTreeBalancing() { + val rbtree = RBTree() + rbtree.insert(10, "A") + rbtree.insert(5, "B") + rbtree.insert(15, "C") + rbtree.insert(3, "D") + rbtree.insert(7, "E") + rbtree.insert(13, "F") + rbtree.insert(17, "G") + rbtree.insert(2, "H") + rbtree.insert(4, "I") + rbtree.insert(6, "J") + rbtree.insert(8, "K") + rbtree.insert(14, "L") + rbtree.insert(16, "M") + rbtree.insert(18, "N") + + assertEquals(RBNode.Color.Black, rbtree.root?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.rightChild?.color) + assertEquals(RBNode.Color.Black, rbtree.root?.leftChild?.leftChild?.color) + assertEquals(RBNode.Color.Black, rbtree.root?.leftChild?.rightChild?.color) + assertEquals(RBNode.Color.Black, rbtree.root?.rightChild?.leftChild?.color) + assertEquals(RBNode.Color.Black, rbtree.root?.rightChild?.rightChild?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.leftChild?.leftChild?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.leftChild?.rightChild?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.rightChild?.leftChild?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.rightChild?.rightChild?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.rightChild?.leftChild?.rightChild?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.rightChild?.rightChild?.leftChild?.color) + assertEquals(RBNode.Color.Red, rbtree.root?.rightChild?.rightChild?.rightChild?.color) + } + + } +} \ No newline at end of file From 964dc6418b8c2b4974feea6df55b91f4ee2e2a12 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sat, 30 Mar 2024 18:28:38 +0300 Subject: [PATCH 093/164] fix: add node transfer to rotation methods, update parent-child relationships, resolve issues with coloring and balancing while inserting. --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 84 +++++++++++++++++-------- 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 717e621..e25dd5c 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -11,6 +11,7 @@ class RBTree, V> : AbstractBinaryTree>() { * @return Null if 'insert' method haven't replaced any value. Return value if there was some * replace.\. */ + public override fun insert(key: K, value: V): V? { val newNode = RBNode(key, value) @@ -58,6 +59,7 @@ class RBTree, V> : AbstractBinaryTree>() { * * @param RBNode-typed node. */ + private fun balanceNode(node: RBNode?) { if (node?.parent == null) root.also { it?.color = RBNode.Color.Black } var newNode = node @@ -75,6 +77,14 @@ class RBTree, V> : AbstractBinaryTree>() { currentGrandParent?.color = RBNode.Color.Red newNode = currentGrandParent } else { + if (currentParent?.color == RBNode.Color.Red && newNode.color == RBNode.Color.Red && newNode == currentParent.rightChild) { + newRoot = rotateLeft(currentParent, newRoot) + newNode.color = RBNode.Color.Black + newNode.leftChild?.color = RBNode.Color.Red + newRoot = rotateRight(currentGrandParent, newRoot) + newNode.rightChild?.color = RBNode.Color.Red + break + } currentParent?.color = RBNode.Color.Black currentGrandParent?.color = RBNode.Color.Red newRoot = rotateRight(currentGrandParent, newRoot) @@ -87,6 +97,10 @@ class RBTree, V> : AbstractBinaryTree>() { currentGrandParent?.color = RBNode.Color.Red newNode = currentGrandParent } else { + if (currentParent?.color == RBNode.Color.Red && newNode.color == RBNode.Color.Red && newNode == currentParent.leftChild) { + currentParent.leftChild?.color = RBNode.Color.Black + newRoot = rotateRight(currentParent, newRoot) + } currentParent?.color = RBNode.Color.Black currentGrandParent?.color = RBNode.Color.Red newRoot = rotateLeft(currentGrandParent, newRoot) @@ -103,6 +117,7 @@ class RBTree, V> : AbstractBinaryTree>() { * @param key The key of the node to be deleted\. * @return Value if delete was successful and null if it isn't.\. */ + public override fun delete(key: K): V? { val deletingNodeValue = search(key) root = deleteNode(key) @@ -118,6 +133,7 @@ class RBTree, V> : AbstractBinaryTree>() { * @param key The key of the node to be deleted\. * @return The new root node of the Red\-Black Tree after deletion\. */ + private fun deleteNode( key: K, ): RBNode? { @@ -127,11 +143,16 @@ class RBTree, V> : AbstractBinaryTree>() { if (current.leftChild != null && current.rightChild != null) { val successor = getMaxNodeRecursively(current.leftChild) + if (successor?.parent?.leftChild == successor) successor?.parent?.leftChild = null + else successor?.parent?.rightChild = null if (successor != null) { current.value = successor.value current.key = successor.key } current = successor + if (successor?.color == RBNode.Color.Red) { + return newRoot + } } val child = if (current?.leftChild != null) current.leftChild else current?.rightChild @@ -216,13 +237,14 @@ class RBTree, V> : AbstractBinaryTree>() { } // 4th case "black currentSibling, left child is red" currentSibling?.color = currentNode.parent?.color ?: RBNode.Color.Black - (currentNode.parent)?.color = RBNode.Color.Black - (currentSibling?.leftChild)?.color = RBNode.Color.Black + currentNode.parent?.color = RBNode.Color.Black + currentSibling?.leftChild?.color = RBNode.Color.Black fixedRoot = rotateRight(currentNode.parent, fixedRoot) currentNode = fixedRoot } } } + balanceNode(fixedRoot) currentNode?.color = RBNode.Color.Black return fixedRoot } @@ -231,27 +253,34 @@ class RBTree, V> : AbstractBinaryTree>() { nodeToRotate: RBNode?, currentRoot: RBNode?, ): RBNode? { - val rightChild = nodeToRotate?.leftChild + val leftChild = nodeToRotate?.leftChild var newRoot = currentRoot - rightChild?.parent = nodeToRotate?.parent - if (rightChild?.leftChild != null) { - rightChild.leftChild?.parent = nodeToRotate + leftChild?.parent = nodeToRotate?.parent + nodeToRotate?.parent = leftChild + + if (leftChild?.rightChild != null) { + nodeToRotate.leftChild = leftChild.rightChild + leftChild.rightChild?.parent = nodeToRotate + leftChild.rightChild = nodeToRotate + } + else { + leftChild?.rightChild = nodeToRotate + nodeToRotate?.leftChild = null } - rightChild?.rightChild = nodeToRotate - nodeToRotate?.parent = rightChild - if (rightChild?.parent != null) { - if (nodeToRotate == rightChild.parent?.leftChild) { - rightChild.parent?.leftChild = rightChild - } else { - rightChild.parent?.rightChild = rightChild + if (leftChild?.parent != null) { + if (leftChild.parent?.leftChild == nodeToRotate || leftChild.parent?.leftChild == null) { + leftChild.parent?.leftChild?.leftChild = null + leftChild.parent?.leftChild = leftChild + } + else { + leftChild.parent?.rightChild = leftChild } } else { - root = rightChild - newRoot = rightChild + root = leftChild + newRoot = leftChild } - return newRoot } @@ -261,23 +290,28 @@ class RBTree, V> : AbstractBinaryTree>() { ): RBNode? { val rightChild = nodeToRotate?.rightChild var newRoot = currentRoot + rightChild?.parent = nodeToRotate?.parent + nodeToRotate?.parent = rightChild - nodeToRotate?.rightChild = rightChild?.leftChild - if (nodeToRotate?.rightChild != null) { - nodeToRotate.rightChild?.parent = nodeToRotate + if (rightChild?.leftChild != null) { + nodeToRotate.rightChild = rightChild.leftChild + rightChild.leftChild?.parent = nodeToRotate.rightChild + rightChild.leftChild = nodeToRotate + } + else { + rightChild?.leftChild = nodeToRotate + nodeToRotate?.rightChild = null } - - rightChild?.leftChild = nodeToRotate - nodeToRotate?.parent = rightChild // rightChild took over "nodeToRotate" place so now its parent should point to rightChild if (rightChild?.parent != null) { - if (nodeToRotate == rightChild.parent?.leftChild) { - rightChild.parent?.leftChild = rightChild - } else { + if (rightChild.parent?.rightChild == nodeToRotate) { rightChild.parent?.rightChild = rightChild } + else { + rightChild.parent?.leftChild = rightChild + } } else { root = rightChild newRoot = rightChild From c978eb63f3044702da2495ab10be0810f2c22252 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sat, 30 Mar 2024 18:30:56 +0300 Subject: [PATCH 094/164] test: add 77% coverage tests (insert, search, delete and some auxiliar ones), try to match all type of situations --- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 578 +++++++++----------- 1 file changed, 271 insertions(+), 307 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index b6a68c0..79ee7a3 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -1,6 +1,6 @@ package tsl.trees -import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import tsl.nodes.RBNode @@ -8,426 +8,390 @@ import kotlin.test.assertEquals class RBTreeTest { + private lateinit var rbTree: RBTree + @BeforeEach + fun setup() { + rbTree = RBTree() + } + @Nested - inner class InsertTests() { + inner class InsertTests { @Test fun `insert if the node with this key doesn't exist`() { - val rbtree = RBTree() - rbtree.insert(15, "i") - rbtree.insert(10, "am") - rbtree.insert(5, "really") - val returnValue = rbtree.insert(7, "wordless") - Assertions.assertEquals(null, returnValue) + rbTree.insert(15, "i") + rbTree.insert(10, "am") + rbTree.insert(5, "really") + val returnValue = rbTree.insert(7, "wordless") + assertEquals(null, returnValue) } @Test fun `insert if the node with this key exists`() { - val rbtree = RBTree() - rbtree.insert(15, "i") - rbtree.insert(10, "am") - rbtree.insert(5, "really") - val returnValue = rbtree.insert(5, "wordless") - Assertions.assertEquals("really", returnValue) + rbTree.insert(15, "i") + rbTree.insert(10, "am") + rbTree.insert(5, "really") + val returnValue = rbTree.insert(5, "wordless") + assertEquals("really", returnValue) } @Test fun `insert left child`() { - val rbtree = RBTree() - rbtree.insert(17, "legend") - rbtree.insert(27, "club") - rbtree.insert(49, "poliklinika") + rbTree.insert(17, "legend") + rbTree.insert(27, "club") + rbTree.insert(49, "poliklinika") + val returnValue = rbTree.insert(16, "18+") + assertEquals(null, returnValue) + assertEquals(16, rbTree.root?.leftChild?.leftChild?.key) } @Test fun `insert right child`() { - val rbtree = RBTree() - rbtree.insert(17, "legend") - rbtree.insert(27, "club") - rbtree.insert(49, "poliklinika") + rbTree.insert(17, "legend") + rbTree.insert(27, "club") + rbTree.insert(49, "poliklinika") + val returnValue = rbTree.insert(20, "wordless") + assertEquals(null, returnValue) + assertEquals(20, rbTree.root?.leftChild?.rightChild?.key) } } @Nested - inner class SearchTests() { + inner class SearchTests { @Test - fun `search node that isn't in the rbtree`() { - val rbtree = RBTree() - rbtree.insert(15, "hihi") - rbtree.insert(10, "haha") - rbtree.insert(5, "bugaga") - val returnValue = rbtree.search(7) - Assertions.assertEquals(null, returnValue) + fun `search node that isn't in the rbTree`() { + rbTree.insert(15, "hihi") + rbTree.insert(10, "haha") + rbTree.insert(5, "bugaga") + + val returnValue = rbTree.search(7) + assertEquals(null, returnValue) } @Test - fun `search node that is in rbtree`() { - val rbtree = RBTree() - rbtree.insert(15, "hihi") - rbtree.insert(10, "haha") - rbtree.insert(5, "bugaga") - val returnValue = rbtree.search(5) - Assertions.assertEquals("bugaga", returnValue) + fun `search node that is in rbTree`() { + rbTree.insert(15, "hihi") + rbTree.insert(10, "haha") + rbTree.insert(5, "bugaga") + + val returnValue = rbTree.search(5) + assertEquals("bugaga", returnValue) } } @Nested - inner class DeleteTests() { + inner class DeleteTests { @Test fun `successful delete (return value)`() { - val rbtree = RBTree() - rbtree.insert(30, "pink") - rbtree.insert(20, "white") - rbtree.insert(40, "red") - val returnValue = rbtree.delete(40) - Assertions.assertEquals("red", returnValue) + rbTree.insert(30, "pink") + rbTree.insert(20, "white") + rbTree.insert(40, "red") + + val returnValue = rbTree.delete(40) + assertEquals("red", returnValue) } @Test fun `unsuccessful delete (return value)`() { - val rbtree = RBTree() - rbtree.insert(30, "pink") - rbtree.insert(20, "white") - rbtree.insert(40, "red") - val returnValue = rbtree.delete(50) - Assertions.assertEquals(null, returnValue) + rbTree.insert(30, "pink") + rbTree.insert(20, "white") + rbTree.insert(40, "red") + + val returnValue = rbTree.delete(50) + assertEquals(null, returnValue) } @Test fun `delete if sibling and its left-child are black`() { - val rbtree = RBTree() - rbtree.insert(30, "pink") - rbtree.insert(20, "white") - rbtree.insert(40, "red") - rbtree.insert(50, "yellow") - val returnValue = rbtree.delete(20) - Assertions.assertEquals("white", returnValue) + rbTree.insert(30, "pink") + rbTree.insert(20, "white") + rbTree.insert(40, "red") + rbTree.insert(50, "yellow") + + val returnValue = rbTree.delete(20) + assertEquals("white", returnValue) } -// @Test -// fun `right-right case`() { -// // s is right child of its parent and both children of s are red -// val rbtree = RBTree() -// rbtree.insert(30, "pink") -// rbtree.insert(18, "white") -// rbtree.insert(40, "red") -// rbtree.insert(45, "yellow") -// val returnValue = rbtree.delete(40) -// var inorderedTraversalTree: List> = listOf() -// for ((key, value) in rbtree) { -// inorderedTraversalTree += Pair(key, value) -// } -// } + @Test + fun `right-right case`() { + // s is right child of its parent and both children of s are red + rbTree.insert(30, "pink") + rbTree.insert(18, "white") + rbTree.insert(40, "red") + rbTree.insert(45, "yellow") + + val returnValue = rbTree.delete(40) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals("red", returnValue) + assertEquals(listOf(Pair(18, "white"), Pair(30, "pink"), Pair(45, "yellow")), inorderedTraversalTree) + } @Test fun `left-left case`() { // s is left child of its parent and both children of s are red - val rbtree = RBTree() - rbtree.insert(30, "pink") - rbtree.insert(40, "white") - rbtree.insert(18, "red") - rbtree.insert(10, "yellow") - val returnValue = rbtree.delete(18) - var inorderedTraversalTree: List> = listOf() - for ((key, value) in rbtree) { + rbTree.insert(30, "pink") + rbTree.insert(40, "white") + rbTree.insert(18, "red") + rbTree.insert(10, "yellow") + + val returnValue = rbTree.delete(18) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) - println(key) } - Assertions.assertEquals(listOf(Pair(10, "yellow"), Pair(30, "pink"), Pair(40, "white")), inorderedTraversalTree) - Assertions.assertEquals("red", returnValue) + assertEquals(listOf(Pair(10, "yellow"), Pair(30, "pink"), Pair(40, "white")), inorderedTraversalTree) + assertEquals("red", returnValue) } @Test fun `right-left case`() { // s is right child of its parent and r is left child of s - val rbtree = RBTree() - rbtree.insert(30, "pink") - rbtree.insert(40, "white") - rbtree.insert(18, "red") - rbtree.insert(35, "yellow") - val returnValue = rbtree.delete(40) - var inorderedTraversalTree: List> = listOf() - for ((key, value) in rbtree) { + rbTree.insert(30, "pink") + rbTree.insert(40, "white") + rbTree.insert(18, "red") + rbTree.insert(35, "yellow") + + val returnValue = rbTree.delete(40) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) - println(key) } - Assertions.assertEquals(listOf(Pair(18, "red"), Pair(30, "pink"), Pair(35, "yellow")), inorderedTraversalTree) - Assertions.assertEquals("white", returnValue) + assertEquals(listOf(Pair(18, "red"), Pair(30, "pink"), Pair(35, "yellow")), inorderedTraversalTree) + assertEquals("white", returnValue) } @Test fun `left-right case`() { - // s is left child of its parent and r is right child - val rbtree = RBTree() - rbtree.insert(30, "pink") - rbtree.insert(40, "white") - rbtree.insert(18, "red") - rbtree.insert(20, "yellow") - val returnValue = rbtree.delete(18) - var inorderedTraversalTree: List> = listOf() - for ((key, value) in rbtree) { + rbTree.insert(30, "pink") + rbTree.insert(40, "white") + rbTree.insert(18, "red") + rbTree.insert(20, "yellow") + + val returnValue = rbTree.delete(18) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) - println(key) } - Assertions.assertEquals(listOf(Pair(20, "yellow"), Pair(30, "pink"), Pair(40, "white")), inorderedTraversalTree) - Assertions.assertEquals("red", returnValue) + assertEquals(listOf(Pair(20, "yellow"), Pair(30, "pink"), Pair(40, "white")), inorderedTraversalTree) + assertEquals("red", returnValue) } - // fun `sibling is black and both children are black`() { -// val rbtree = RBTree() -// rbtree.insert(30, "pink") -// rbtree.insert(40, "white") -// rbtree.insert(18, "red") -// rbtree.delete(18) -// // TODO -// } -// + @Test fun `left-red sibling`() { - // s is left child of its parent - val rbtree = RBTree() - rbtree.insert(20, "pink") - rbtree.insert(30, "white") - rbtree.insert(10, "red") - rbtree.insert(25, "brown") - rbtree.insert(35, "gold") - val returnValue = rbtree.delete(10) - var inorderedTraversalTree: List> = listOf() - for ((key, value) in rbtree) { + rbTree.insert(20, "pink") + rbTree.insert(30, "white") + rbTree.insert(10, "red") + rbTree.insert(25, "brown") + rbTree.insert(35, "gold") + + val returnValue = rbTree.delete(10) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) - println(key) } - Assertions.assertEquals(listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), inorderedTraversalTree) - Assertions.assertEquals("red", returnValue) + assertEquals(listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), inorderedTraversalTree) + assertEquals("red", returnValue) } @Test - fun `remove root`(){ - val rbtree = RBTree() - rbtree.insert(10, "hihi haha") - val returnValue = rbtree.delete(10) + fun `remove root - sole node in tree`(){ + rbTree.insert(10, "hihi haha") + val returnValue = rbTree.delete(10) assertEquals(null, returnValue) } @Test fun `delete node with two children`() { - // s is left child of its parent - val rbtree = RBTree() - rbtree.insert(20, "pink") - rbtree.insert(30, "white") - rbtree.insert(10, "red") - rbtree.insert(25, "brown") - rbtree.insert(35, "gold") - val returnValue = rbtree.delete(30) - var inorderedTraversalTree: List> = listOf() - for ((key, value) in rbtree) { + rbTree.insert(20, "pink") + rbTree.insert(30, "white") + rbTree.insert(10, "red") + rbTree.insert(25, "brown") + rbTree.insert(35, "gold") + + val returnValue = rbTree.delete(30) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) - println(key) } - Assertions.assertEquals(listOf( Pair(10, "red"), Pair(20, "pink"), Pair(25, "brown"), Pair(35, "gold")), inorderedTraversalTree) - Assertions.assertEquals("white", returnValue) + assertEquals(listOf( Pair(10, "red"), Pair(20, "pink"), Pair(25, "brown"), Pair(35, "gold")), inorderedTraversalTree) + assertEquals("white", returnValue) } @Test - fun testFixAfterDeletionCase3LeftChildRed() { - val rbTree = RBTree() + fun `fix after deletion second case`() { + rbTree.insert(15, "c") + rbTree.insert(10, "a") + rbTree.insert(20, "e") + rbTree.insert(5, "b") + rbTree.insert(6, "d") + + val returnValue = rbTree.delete(6) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals(listOf(Pair(5, "b"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), inorderedTraversalTree) + assertEquals("d", returnValue) + } + + @Test + fun `fix after deletion case 3 right child red`() { rbTree.insert(15, "C") rbTree.insert(10, "A") rbTree.insert(20, "E") - rbTree.insert(18, "D") + rbTree.insert(22, "F") // for case 3 - insert sibling and ensure its child is red - rbTree.insert(17, "Z") + rbTree.insert(23, "K") // deletion to trigger fixAfterDeletion with Case 3 conditions rbTree.delete(10) - val currentSibling = rbTree.root?.rightChild - - assertEquals(RBNode.Color.Black, currentSibling?.leftChild?.color) + assertEquals(null, currentSibling?.rightChild?.color) assertEquals(RBNode.Color.Black, currentSibling?.color) } @Test - fun `fix after deletion case 3 right child red`() { - val rbTree = RBTree() + fun `fix after deletion case 3 left child red`() { rbTree.insert(15, "C") rbTree.insert(10, "A") rbTree.insert(20, "E") - rbTree.insert(22, "F") + rbTree.insert(18, "D") // for case 3 - insert sibling and ensure its child is red - rbTree.insert(23, "K") - + rbTree.insert(17, "Z") // deletion to trigger fixAfterDeletion with Case 3 conditions rbTree.delete(10) - val currentSibling = rbTree.root?.rightChild - - assertEquals(null, currentSibling?.rightChild?.color) assertEquals(RBNode.Color.Black, currentSibling?.color) } -// @Test -// fun testFixAfterDeletionSecondCase() { -// val rbtree = RBTree() -// rbtree.insert(15, "c") -// rbtree.insert(10, "a") -// rbtree.insert(20, "e") -// rbtree.insert(5, "b") -// rbtree.insert(6, "d") -// -// val returnValue = rbtree.delete(6) -// var inorderedTraversalTree: List> = listOf() -// for ((key, value) in rbtree) { -// inorderedTraversalTree += Pair(key, value) -// println(key) -// } -// Assertions.assertEquals(listOf(Pair(5, "b"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), inorderedTraversalTree) -// Assertions.assertEquals("d", returnValue) -// } - - - -// @Test -// fun testFixAfterDeletionFourthCase() { -// val rbtree = RBTree() -// rbtree.insert(15, "c") -// rbtree.insert(10, "a") -// rbtree.insert(20, "e") -// rbtree.insert(5, "b") -// rbtree.insert(6, "d") -// rbtree.insert(4, "f") -// -// val returnValue = rbtree.delete(4) -// var inorderedTraversalTree: List> = listOf() -// for ((key, value) in rbtree) { -// inorderedTraversalTree += Pair(key, value) -// println(key) -// } -// Assertions.assertEquals(listOf(Pair(5, "b"), Pair(6, "d"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), inorderedTraversalTree) -// Assertions.assertEquals("f", returnValue) -// } - - - - // fix -// @Test -// fun `right-red sibling`() { -// // s is right child of its parent -// val rbtree = RBTree() -// rbtree.insert(20, "pink") -// rbtree.insert(30, "white") -// rbtree.insert(15, "red") -// rbtree.insert(10, "brown") -// rbtree.insert(5, "gold") -// val returnValue = rbtree.delete(30) -// var inorderedTraversalTree: List> = listOf() -// for ((key, value) in rbtree) { -// inorderedTraversalTree += Pair(key, value) -// } -// Assertions.assertEquals(listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), inorderedTraversalTree) -// Assertions.assertEquals("red", returnValue) -// } - -// // fix -// @Test -// fun `root left rotate`() { -// val rbtree = RBTree() -// rbtree.insert(20, "pink") -// rbtree.insert(30, "white") -// rbtree.insert(15, "red") -// rbtree.insert(10, "brown") -// rbtree.insert(5, "gold") -// val returnValue = rbtree.delete(20) -// var inorderedTraversalTree: List> = listOf() -// for ((key, value) in rbtree) { -// inorderedTraversalTree += Pair(key, value) -// println(key) -// } -// Assertions.assertEquals(listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), inorderedTraversalTree) -// Assertions.assertEquals("red", returnValue) -// } - -// // fix -// @Test -// fun `root right rotate`() { -// val rbtree = RBTree() -// rbtree.insert(20, "pink") -// rbtree.insert(30, "white") -// rbtree.insert(15, "red") -// rbtree.insert(10, "brown") -// rbtree.insert(5, "gold") -// val returnValue = rbtree.delete(30) -// var inorderedTraversalTree: List> = listOf() -// for ((key, value) in rbtree) { -// inorderedTraversalTree += Pair(key, value) -// println(key) -// } -// Assertions.assertEquals(listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), inorderedTraversalTree) -// Assertions.assertEquals("red", returnValue) -// } - } + @Test + fun `fix after deletion fourth case`() { + rbTree.insert(15, "c") + rbTree.insert(10, "a") + rbTree.insert(20, "e") + rbTree.insert(5, "b") + rbTree.insert(4, "f") + + val returnValue = rbTree.delete(4) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals(listOf(Pair(5, "b"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), inorderedTraversalTree) + assertEquals("f", returnValue) + } - @Nested - inner class BalancerTests() { + @Test + fun `right-red sibling`() { + rbTree.insert(20, "pink") + rbTree.insert(30, "white") + rbTree.insert(15, "red") + rbTree.insert(10, "brown") + rbTree.insert(5, "gold") + + val returnValue = rbTree.delete(30) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals(listOf(Pair(5, "gold"), Pair(10, "brown"), Pair(15, "red"), Pair(20, "pink")), inorderedTraversalTree) + assertEquals("white", returnValue) + } + + @Test + fun `root left rotate`() { + rbTree.insert(20, "pink") + rbTree.insert(30, "white") + rbTree.insert(15, "red") + rbTree.insert(10, "brown") + rbTree.insert(5, "gold") + + val returnValue = rbTree.delete(20) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals(listOf(Pair(5, "gold"), Pair(10, "brown"), Pair(15, "red"), Pair(30, "white")), inorderedTraversalTree) + assertEquals("pink", returnValue) + } + + @Test + fun `root right rotate`() { + rbTree.insert(20, "pink") + rbTree.insert(30, "white") + rbTree.insert(15, "red") + rbTree.insert(10, "brown") + rbTree.insert(5, "gold") + + val returnValue = rbTree.delete(30) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + + } + assertEquals(listOf(Pair(5, "gold"), Pair(10, "brown"), Pair(15, "red"), Pair(20, "pink")), inorderedTraversalTree) + assertEquals("white", returnValue) + } } @Nested - inner class AuxiliaryTests() { + inner class AuxiliaryTests { @Test - fun testInsertAndBalance() { - val rbtree = RBTree() - rbtree.insert(10, "A") - rbtree.insert(5, "B") - rbtree.insert(15, "C") - rbtree.insert(3, "D") - rbtree.insert(7, "E") - - assertEquals(RBNode.Color.Black, rbtree.root?.color) - assertEquals(RBNode.Color.Black, rbtree.root?.leftChild?.color) - assertEquals(RBNode.Color.Black, rbtree.root?.rightChild?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.leftChild?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.rightChild?.color) + fun `insert and balance, color check 1`() { + rbTree.insert(10, "A") + rbTree.insert(5, "B") + rbTree.insert(15, "C") + rbTree.insert(3, "D") + rbTree.insert(7, "E") + + assertEquals(RBNode.Color.Black, rbTree.root?.color) + assertEquals(RBNode.Color.Black, rbTree.root?.leftChild?.color) + assertEquals(RBNode.Color.Black, rbTree.root?.rightChild?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.leftChild?.leftChild?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.leftChild?.rightChild?.color) } @Test - fun testRBTreeBalancing() { - val rbtree = RBTree() - rbtree.insert(10, "A") - rbtree.insert(5, "B") - rbtree.insert(15, "C") - rbtree.insert(3, "D") - rbtree.insert(7, "E") - rbtree.insert(13, "F") - rbtree.insert(17, "G") - rbtree.insert(2, "H") - rbtree.insert(4, "I") - rbtree.insert(6, "J") - rbtree.insert(8, "K") - rbtree.insert(14, "L") - rbtree.insert(16, "M") - rbtree.insert(18, "N") - - assertEquals(RBNode.Color.Black, rbtree.root?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.rightChild?.color) - assertEquals(RBNode.Color.Black, rbtree.root?.leftChild?.leftChild?.color) - assertEquals(RBNode.Color.Black, rbtree.root?.leftChild?.rightChild?.color) - assertEquals(RBNode.Color.Black, rbtree.root?.rightChild?.leftChild?.color) - assertEquals(RBNode.Color.Black, rbtree.root?.rightChild?.rightChild?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.leftChild?.leftChild?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.leftChild?.rightChild?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.rightChild?.leftChild?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.leftChild?.rightChild?.rightChild?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.rightChild?.leftChild?.rightChild?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.rightChild?.rightChild?.leftChild?.color) - assertEquals(RBNode.Color.Red, rbtree.root?.rightChild?.rightChild?.rightChild?.color) + fun `insert and balance, color check 2`() { + rbTree.insert(10, "A") + rbTree.insert(5, "B") + rbTree.insert(15, "C") + rbTree.insert(3, "D") + rbTree.insert(7, "E") + rbTree.insert(13, "F") + rbTree.insert(17, "G") + rbTree.insert(2, "H") + rbTree.insert(4, "I") + rbTree.insert(6, "J") + rbTree.insert(8, "K") + rbTree.insert(14, "L") + rbTree.insert(16, "M") + rbTree.insert(18, "N") + + assertEquals(RBNode.Color.Black, rbTree.root?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.leftChild?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.rightChild?.color) + assertEquals(RBNode.Color.Black, rbTree.root?.leftChild?.leftChild?.color) + assertEquals(RBNode.Color.Black, rbTree.root?.leftChild?.rightChild?.color) + assertEquals(RBNode.Color.Black, rbTree.root?.rightChild?.leftChild?.color) + assertEquals(RBNode.Color.Black, rbTree.root?.rightChild?.rightChild?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.leftChild?.leftChild?.leftChild?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.leftChild?.leftChild?.rightChild?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.leftChild?.rightChild?.leftChild?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.leftChild?.rightChild?.rightChild?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.rightChild?.leftChild?.rightChild?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.rightChild?.rightChild?.leftChild?.color) + assertEquals(RBNode.Color.Red, rbTree.root?.rightChild?.rightChild?.rightChild?.color) } - } } \ No newline at end of file From 5a9167e17940f07d5ae8968a2080957e06ce9d0d Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Sun, 31 Mar 2024 16:12:34 +0300 Subject: [PATCH 095/164] ci: add badge update for jacoco coverage --- .github/workflows/codecoverage.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 0745d34..0637fa9 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -36,4 +36,15 @@ jobs: jacoco-csv-file: lib/build/jacoco/report.csv badges-directory: .github/badges generate-coverage-badge: true - coverage-badge-filename: jacoco.svg \ No newline at end of file + coverage-badge-filename: jacoco.svg + + - name: Commit the badges (if they changed) + run: | + if [[ `git diff --exit-code .github/badges` ]]; then + git config --global user.name 'qrutyy' + git config --global user.email 'qrutyq@gmail.com' + git add .github/badges/* + git commit -m "ci: autogenerate JaCoCo coverage badge" + git push || true + fi + shell: bash From 1c5ba44b0ca3eff2f78aa56bd84b804740a531f7 Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Sun, 31 Mar 2024 16:27:33 +0300 Subject: [PATCH 096/164] ci: add formatting verifier --- .github/workflows/formatter.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/formatter.yml diff --git a/.github/workflows/formatter.yml b/.github/workflows/formatter.yml new file mode 100644 index 0000000..380b2c0 --- /dev/null +++ b/.github/workflows/formatter.yml @@ -0,0 +1,21 @@ +name: Verify formatting + +on: + workflow_dispatch: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: zulu + + - name: Run formatting test + run: ./gradlew checkFormat From c9d9daff0705437736ff25c46ecdb7a1222c5a58 Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Sun, 31 Mar 2024 16:29:09 +0300 Subject: [PATCH 097/164] docs: add coverage badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 129c616..b6056a6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![Kotlin][kotlin_img]][kotlin_releases_url] [![License][license_img]][repo_license_url] +![Coverage](.github/badges/jacoco.svg) + `forest-group` is a library that lets you easily create and use [Binary search trees](https://en.wikipedia.org/wiki/Binary_search_tree), [AVL trees](https://en.wikipedia.org/wiki/AVL_tree) and [Red-black trees](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) in your applications! From 02ad8fe554665f6ec0cbb2c3980b4b949fe8bb6d Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Sun, 31 Mar 2024 16:54:07 +0300 Subject: [PATCH 098/164] ci: create tests workflow, use JUnit5 --- .github/workflows/test.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..eef21c0 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,24 @@ +name: Build & Run JUnit5 tests + +on: + workflow_dispatch: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: zulu + + - name: Build by Gradle + run: ./gradlew build + + - name: Run JUnit5 tests + run: ./gradlew test From ae1280d0a75a3fcae57c7fa572f571af0faf49c9 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sat, 30 Mar 2024 21:51:29 +0300 Subject: [PATCH 099/164] test: add tests of insertion into BS tree, drafts of tests for other methods --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 95 ++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index f352fe9..982934f 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -3,6 +3,7 @@ package tsl.trees import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Nested class BSTreeTest { private lateinit var tree: BSTree @@ -12,5 +13,97 @@ class BSTreeTest { tree = BSTree() } - @Test fun test() {} + fun defaultFill(tree: BSTree) { + tree.insert(30, "java") + tree.insert(40, "gnomik") + tree.insert(20, "kotlin") + tree.insert(10, "kotik") + } + + @Nested + inner class InsertTests { + + @Test + fun `insert of the key that isn't in the tree should return null`() { + defaultFill(tree) + + val expectedValue = null + val actualValue = tree.insert(50, "pesik") + + assertEquals(expectedValue, actualValue) + } + @Test + fun `insert of existing key should replace old value and return it`() { + defaultFill(tree) + + val expectedReturnValue = "kotik" + val actualReturnValue = tree.insert(10, "pesik") + + val expectedNewValue = "pesik" + val actualNewValue = tree.search(10) + + + assertEquals(expectedReturnValue, actualReturnValue, "wrong value returned") + assertEquals(expectedNewValue, actualNewValue, "value didn't change") + } + + @Test + fun `node with the smallest key should be inserted at the most left position`() { + defaultFill(tree) + + tree.insert(5, "pesik") + + val expectedStructure = listOf(Pair(5, "pesik"), Pair(10, "kotik"), Pair(20, "kotlin"), + Pair(30, "java"), Pair(40, "gnomik")) // in the sequence of inorder traversal + + val actualStructure: MutableList> = mutableListOf() + for ((key, value) in tree) actualStructure.add(Pair(key, value)) + + assertEquals(expectedStructure, actualStructure) + } + + @Test + fun `node with the biggest key should be inserted at the most right position`() { + defaultFill(tree) + + tree.insert(50, "pesik") + + val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), Pair(30, "java"), + Pair(40, "gnomik"), Pair(5, "pesik")) // in the sequence of inorder traversal + + val actualStructure: MutableList> = mutableListOf() + for ((key, value) in tree) actualStructure.add(Pair(key, value)) + + assertEquals(expectedStructure, actualStructure) + } + + @Test + fun `node with random key should be inserted at correct position`() { + defaultFill(tree) + + tree.insert(25, "pesik") + + val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), Pair(25, "pesik"), + Pair(30, "java"), Pair(40, "gnomik")) // in the sequence of inorder traversal + + val actualStructure: MutableList> = mutableListOf() + for ((key, value) in tree) actualStructure.add(Pair(key, value)) + + assertEquals(expectedStructure, actualStructure) + } + } + @Nested + inner class SearchTests { + + } + + @Nested + inner class DeleteTests { + + } + + @Nested + inner class Iteratortests { + + } } From 8ca04ce704a317a8b044ba04312b26a855f1f20c Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sat, 30 Mar 2024 22:43:10 +0300 Subject: [PATCH 100/164] test: add tests of search method of the BS tree --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 65 +++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index 982934f..b5d88fa 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -95,6 +95,71 @@ class BSTreeTest { @Nested inner class SearchTests { + @Test + fun `search of non existing key should return null`() { + defaultFill(tree) + + val expectedValue = null + val actualValue = tree.search(100) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `search of existing key that is in the root node should return the corresponding value`() { + defaultFill(tree) + + val expectedValue = "java" + val actualValue = tree.search(30) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `search of existing key that is to the left of the root node should return the corresponding value`() { + defaultFill(tree) + + val expectedValue = "kotlin" + val actualValue = tree.search(20) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `search of existing key that is to the right of the root node should return the corresponding value`() { + defaultFill(tree) + + val expectedValue = "gnomik" + val actualValue = tree.search(40) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `search should not change an empty tree`() { + tree.search(42069) + + val expectedStructure: List> = listOf() + val actualStructure: MutableList> = mutableListOf() + for ((key, value) in tree) actualStructure.add(Pair(key, value)) + + assertEquals(expectedStructure, actualStructure) + } + + @Test + fun `search should not change a non-empty tree`() { + defaultFill(tree) + + tree.search(40) + + val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), + Pair(30, "java"), Pair(40, "gnomik")) + + val actualStructure: MutableList> = mutableListOf() + for ((key, value) in tree) actualStructure.add(Pair(key, value)) + + assertEquals(expectedStructure, actualStructure) + } } @Nested From aa8b1804ba39949241c2b3c3145e2427a0558103 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sat, 30 Mar 2024 22:44:16 +0300 Subject: [PATCH 101/164] test: fix search test of AVL tree --- lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt index a292f0d..4816389 100644 --- a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt @@ -60,6 +60,8 @@ class AVLTreeTest { avlTree.insert(3, "Favourite") avlTree.insert(4, "Game") + avlTree.search(4) + val expectedStructure = listOf(Pair(1, "Losing"), Pair(2, "My"), Pair(3, "Favourite"), Pair(4, "Game")) From f2e238fbc97626fd0087ecc5d30eac1882154781 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sat, 30 Mar 2024 22:48:51 +0300 Subject: [PATCH 102/164] test: put the content of the defaultFill method to setup method --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 30 +++------------------ 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index b5d88fa..8236d01 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -11,9 +11,6 @@ class BSTreeTest { @BeforeEach fun setup() { tree = BSTree() - } - - fun defaultFill(tree: BSTree) { tree.insert(30, "java") tree.insert(40, "gnomik") tree.insert(20, "kotlin") @@ -25,8 +22,6 @@ class BSTreeTest { @Test fun `insert of the key that isn't in the tree should return null`() { - defaultFill(tree) - val expectedValue = null val actualValue = tree.insert(50, "pesik") @@ -34,8 +29,6 @@ class BSTreeTest { } @Test fun `insert of existing key should replace old value and return it`() { - defaultFill(tree) - val expectedReturnValue = "kotik" val actualReturnValue = tree.insert(10, "pesik") @@ -49,12 +42,10 @@ class BSTreeTest { @Test fun `node with the smallest key should be inserted at the most left position`() { - defaultFill(tree) - tree.insert(5, "pesik") val expectedStructure = listOf(Pair(5, "pesik"), Pair(10, "kotik"), Pair(20, "kotlin"), - Pair(30, "java"), Pair(40, "gnomik")) // in the sequence of inorder traversal + Pair(30, "java"), Pair(40, "gnomik")) val actualStructure: MutableList> = mutableListOf() for ((key, value) in tree) actualStructure.add(Pair(key, value)) @@ -64,12 +55,10 @@ class BSTreeTest { @Test fun `node with the biggest key should be inserted at the most right position`() { - defaultFill(tree) - tree.insert(50, "pesik") val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), Pair(30, "java"), - Pair(40, "gnomik"), Pair(5, "pesik")) // in the sequence of inorder traversal + Pair(40, "gnomik"), Pair(5, "pesik")) val actualStructure: MutableList> = mutableListOf() for ((key, value) in tree) actualStructure.add(Pair(key, value)) @@ -79,12 +68,10 @@ class BSTreeTest { @Test fun `node with random key should be inserted at correct position`() { - defaultFill(tree) - tree.insert(25, "pesik") val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), Pair(25, "pesik"), - Pair(30, "java"), Pair(40, "gnomik")) // in the sequence of inorder traversal + Pair(30, "java"), Pair(40, "gnomik")) val actualStructure: MutableList> = mutableListOf() for ((key, value) in tree) actualStructure.add(Pair(key, value)) @@ -97,8 +84,6 @@ class BSTreeTest { @Test fun `search of non existing key should return null`() { - defaultFill(tree) - val expectedValue = null val actualValue = tree.search(100) @@ -107,8 +92,6 @@ class BSTreeTest { @Test fun `search of existing key that is in the root node should return the corresponding value`() { - defaultFill(tree) - val expectedValue = "java" val actualValue = tree.search(30) @@ -117,8 +100,6 @@ class BSTreeTest { @Test fun `search of existing key that is to the left of the root node should return the corresponding value`() { - defaultFill(tree) - val expectedValue = "kotlin" val actualValue = tree.search(20) @@ -127,8 +108,6 @@ class BSTreeTest { @Test fun `search of existing key that is to the right of the root node should return the corresponding value`() { - defaultFill(tree) - val expectedValue = "gnomik" val actualValue = tree.search(40) @@ -137,6 +116,7 @@ class BSTreeTest { @Test fun `search should not change an empty tree`() { + tree.clear() tree.search(42069) val expectedStructure: List> = listOf() @@ -148,8 +128,6 @@ class BSTreeTest { @Test fun `search should not change a non-empty tree`() { - defaultFill(tree) - tree.search(40) val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), From fbb332907cf9d9c8186b320c0aefb340f6994a32 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sat, 30 Mar 2024 22:57:37 +0300 Subject: [PATCH 103/164] refactor: change (key, value) pairs to one var in for loops in all BS tree tests --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index 8236d01..b4741d1 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -48,7 +48,7 @@ class BSTreeTest { Pair(30, "java"), Pair(40, "gnomik")) val actualStructure: MutableList> = mutableListOf() - for ((key, value) in tree) actualStructure.add(Pair(key, value)) + for (pair in tree) actualStructure.add(pair) assertEquals(expectedStructure, actualStructure) } @@ -61,7 +61,7 @@ class BSTreeTest { Pair(40, "gnomik"), Pair(5, "pesik")) val actualStructure: MutableList> = mutableListOf() - for ((key, value) in tree) actualStructure.add(Pair(key, value)) + for (pair in tree) actualStructure.add(pair) assertEquals(expectedStructure, actualStructure) } @@ -74,7 +74,7 @@ class BSTreeTest { Pair(30, "java"), Pair(40, "gnomik")) val actualStructure: MutableList> = mutableListOf() - for ((key, value) in tree) actualStructure.add(Pair(key, value)) + for (pair in tree) actualStructure.add(pair) assertEquals(expectedStructure, actualStructure) } @@ -121,7 +121,7 @@ class BSTreeTest { val expectedStructure: List> = listOf() val actualStructure: MutableList> = mutableListOf() - for ((key, value) in tree) actualStructure.add(Pair(key, value)) + for (pair in tree) actualStructure.add(pair) assertEquals(expectedStructure, actualStructure) } @@ -134,7 +134,7 @@ class BSTreeTest { Pair(30, "java"), Pair(40, "gnomik")) val actualStructure: MutableList> = mutableListOf() - for ((key, value) in tree) actualStructure.add(Pair(key, value)) + for (pair in tree) actualStructure.add(pair) assertEquals(expectedStructure, actualStructure) } From 2b5fa3560a95928c7bb5a77a32d5868a52802546 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sat, 30 Mar 2024 23:36:26 +0300 Subject: [PATCH 104/164] test: add tests for delete method of BS tree --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 75 +++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index b4741d1..6178391 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -143,6 +143,81 @@ class BSTreeTest { @Nested inner class DeleteTests { + @Test + fun `delete of the non-existing key should return null`() { + val expectedValue = null + val actualValue = tree.delete(42069) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `delete of existing key should return the corresponding deleted value`() { + val expectedValue = "gnomik" + val actualValue = tree.delete(40) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `a key should be deleted after deletion`() { + tree.delete(30) + + val expectedValue = null + val actualValue = tree.search(30) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `delete of a node without children shouldn't change tree structure`() { + tree. delete(10) + + val expectedStructure = listOf(Pair(20, "kotlin"), Pair(30, "java"), Pair(40, "gnomik")) + val actualStructure: MutableList> = mutableListOf() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + + @Test + fun `delete of a node with a left child should replace it with it's child`() { + tree. delete(20) + + val expectedStructure = listOf(Pair(10, "kotlin"), Pair(30, "java"), Pair(40, "gnomik")) + val actualStructure: MutableList> = mutableListOf() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + + @Test + fun `delete of a node with a right child should replace it with it's child`() { + tree.insert(50, "pesik") + tree. delete(40) + + val expectedStructure = listOf(Pair(10, "kotlin"), Pair(20, "kotlin"), + Pair(30, "java"), Pair(50, "pesik")) + + val actualStructure: MutableList> = mutableListOf() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + + @Test + fun `delete of a node with two children should replace it with smallest node in the right subtree`() { + tree.insert(35, "pesik") + tree. delete(30) + + val expectedStructure = listOf(Pair(10, "kotlin"), Pair(20, "kotlin"), + Pair(35, "pesik"), Pair(40, "gnomik")) + + val actualStructure: MutableList> = mutableListOf() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } } @Nested From 5591d3150fbf41c14e87679361fe53866bb3b15f Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 00:05:17 +0300 Subject: [PATCH 105/164] test: add tests for clear and isEmpty methods of BS tree --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 97 ++++++++++++++++++++- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index 6178391..ba0cb2e 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -119,11 +119,10 @@ class BSTreeTest { tree.clear() tree.search(42069) - val expectedStructure: List> = listOf() - val actualStructure: MutableList> = mutableListOf() - for (pair in tree) actualStructure.add(pair) + val expectedRoot = null + val actualRoot = tree.root - assertEquals(expectedStructure, actualStructure) + assertEquals(expectedRoot, actualRoot) } @Test @@ -220,6 +219,96 @@ class BSTreeTest { } } + @Nested + inner class ClearTests { + + @Test + fun `clear of a non-empty tree should return value of its root node`() { + val expectedValue = "java" + val actualValue = tree.clear() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `clear of an empty tree should return null`() { + tree.clear() + + val expectedValue = null + val actualValue = tree.clear() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `clear of an empty tree should remain it empty`() { + val emptyTree = BSTree() + emptyTree.clear() + + val expectedValue = null + val actualValue = emptyTree.root + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `clear of a non-empty tree should make it empty`() { + tree.clear() + + val expectedValue = null + val actualValue = tree.root + + assertEquals(expectedValue, actualValue) + } + } + + @Nested + inner class IsEmptyTests { + + @Test + fun `isEmpty should return true if the tree is empty`() { + tree.clear() + + val expectedValue = true + val actualValue = tree.isEmpty() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `isEmpty should return false if the tree is not empty`() { + val expectedValue = false + val actualValue = tree.isEmpty() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `isEmpty should not change an empty tree`() { + tree.clear() + tree.isEmpty() + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } + + @Test + fun `isEmpty should not change a non-empty tree`() { + tree.isEmpty() + + val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), + Pair(30, "java"), Pair(40, "gnomik")) + + val actualStructure: MutableList> = mutableListOf() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + + } + @Nested inner class Iteratortests { From 5a46fe29c95a6e44d4ab597cefb383e60873ceb8 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 00:12:48 +0300 Subject: [PATCH 106/164] refactor: format in abstract binary tree class --- .../main/kotlin/tsl/trees/AbstractBinaryTree.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 03f77fd..a3ad11d 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -11,9 +11,8 @@ abstract class AbstractBinaryTree, V, N : AbstractNode, V, N : AbstractNode, V, N : AbstractNode> = BinaryTreeIterator(this) + public override fun iterator(): Iterator> = + BinaryTreeIterator(this) } From 32789396a584ba5f2d00ad87c61dd400a51b22ac Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 00:35:39 +0300 Subject: [PATCH 107/164] test: add tests for getMinKey and getMaxKey methods of BS tree --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 91 +++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index ba0cb2e..1aeecd8 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -306,7 +306,98 @@ class BSTreeTest { assertEquals(expectedStructure, actualStructure) } + } + + @Nested + inner class GetMinKeyTests { + + @Test + fun `getMinKey should return null if the tree is empty`() { + tree.clear() + + val expectedValue = null + val actualValue = tree.getMinKey() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `getMinKey should return min key if the tree is not empty`() { + val expectedValue = 10 + val actualValue = tree.getMinKey() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `getMinKey should not change an empty tree`() { + tree.clear() + tree.getMinKey() + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } + + @Test + fun `getMinKey should not change a non-empty tree`() { + tree.getMinKey() + + val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), + Pair(30, "java"), Pair(40, "gnomik")) + + val actualStructure: MutableList> = mutableListOf() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + } + + @Nested + inner class GetMaxKeyTests { + + @Test + fun `getMaxKey should return null if the tree is empty`() { + tree.clear() + + val expectedValue = null + val actualValue = tree.getMaxKey() + assertEquals(expectedValue, actualValue) + } + + @Test + fun `getMaxKey should return min key if the tree is not empty`() { + val expectedValue = 10 + val actualValue = tree.getMaxKey() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `getMaxKey should not change an empty tree`() { + tree.clear() + tree.getMaxKey() + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } + + @Test + fun `getMaxKey should not change a non-empty tree`() { + tree.getMaxKey() + + val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), + Pair(30, "java"), Pair(40, "gnomik")) + + val actualStructure: MutableList> = mutableListOf() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } } @Nested From e784d3e764c1aaa296902bfbce5d7a49496fe08e Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 01:16:15 +0300 Subject: [PATCH 108/164] test: add tests for hasNext and next methods iterator of BS tree --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 59 ++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index 1aeecd8..10fe7ba 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -391,7 +391,7 @@ class BSTreeTest { tree.getMaxKey() val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), - Pair(30, "java"), Pair(40, "gnomik")) + Pair(30, "java"), Pair(40, "gnomik")) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -403,5 +403,62 @@ class BSTreeTest { @Nested inner class Iteratortests { + @Test + fun `hasNext should return false right after it traversed all nodes in a non-empty tree`() { + val treeIterator = tree.iterator() + + var count = 0 + while (treeIterator.hasNext()) { + treeIterator.next() + count++ + } + + val expectedQuantity = 4 + val actualQuantity = count + + assertEquals(expectedQuantity, actualQuantity) + } + + @Test + fun `hasNext should always return false in an empty tree`() { + tree.clear() + val treeIterator = tree.iterator() + + var count = 0 + while (treeIterator.hasNext()) { + treeIterator.next() + count++ + } + + val expectedQuantity = 0 + val actualQuantity = count + + assertEquals(expectedQuantity, actualQuantity) + } + + @Test + fun `next method should return pairs with bigger key that it has returned before`() { + val treeIterator = tree.iterator() + + var everyNextKeyIsBigger = true + + var currentKey = 0 + var previousKey = 0 + var iteration = 0 + while (treeIterator.hasNext()) { + iteration++ + currentKey = treeIterator.next().first + + if (iteration > 1) { + if (currentKey <= previousKey) { + everyNextKeyIsBigger = false + break + } + } + previousKey = currentKey + } + + assertEquals(true, everyNextKeyIsBigger) + } } } From 9e11146520f5db177b79368c50faa25cfccb0300 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 01:35:13 +0300 Subject: [PATCH 109/164] test: fix logical errors in BS tree tests --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index 10fe7ba..1a9a922 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -58,7 +58,7 @@ class BSTreeTest { tree.insert(50, "pesik") val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), Pair(30, "java"), - Pair(40, "gnomik"), Pair(5, "pesik")) + Pair(40, "gnomik"), Pair(50, "pesik")) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -170,7 +170,7 @@ class BSTreeTest { @Test fun `delete of a node without children shouldn't change tree structure`() { - tree. delete(10) + tree.delete(10) val expectedStructure = listOf(Pair(20, "kotlin"), Pair(30, "java"), Pair(40, "gnomik")) val actualStructure: MutableList> = mutableListOf() @@ -181,7 +181,7 @@ class BSTreeTest { @Test fun `delete of a node with a left child should replace it with it's child`() { - tree. delete(20) + tree.delete(20) val expectedStructure = listOf(Pair(10, "kotlin"), Pair(30, "java"), Pair(40, "gnomik")) val actualStructure: MutableList> = mutableListOf() @@ -193,7 +193,7 @@ class BSTreeTest { @Test fun `delete of a node with a right child should replace it with it's child`() { tree.insert(50, "pesik") - tree. delete(40) + tree.delete(40) val expectedStructure = listOf(Pair(10, "kotlin"), Pair(20, "kotlin"), Pair(30, "java"), Pair(50, "pesik")) @@ -207,7 +207,7 @@ class BSTreeTest { @Test fun `delete of a node with two children should replace it with smallest node in the right subtree`() { tree.insert(35, "pesik") - tree. delete(30) + tree.delete(30) val expectedStructure = listOf(Pair(10, "kotlin"), Pair(20, "kotlin"), Pair(35, "pesik"), Pair(40, "gnomik")) @@ -368,8 +368,8 @@ class BSTreeTest { } @Test - fun `getMaxKey should return min key if the tree is not empty`() { - val expectedValue = 10 + fun `getMaxKey should return max key if the tree is not empty`() { + val expectedValue = 40 val actualValue = tree.getMaxKey() assertEquals(expectedValue, actualValue) From ec6f888a2cf4a2d439f378ecad01555d58bef2aa Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sun, 31 Mar 2024 10:24:40 +0300 Subject: [PATCH 110/164] test: remove tests for methods implemented in Abstract tree class from tests for AVL tree class --- lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt | 93 +------------------- 1 file changed, 1 insertion(+), 92 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt index 4816389..d981e4f 100644 --- a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt @@ -13,66 +13,6 @@ class AVLTreeTest { avlTree = AVLTree() } - @Nested - inner class SearchTests { - @Test - fun `search of non-existing key should return null`() { - avlTree.insert(1, "Put") - avlTree.insert(2, "The") - avlTree.insert(3, "Lights") - avlTree.insert(4, "On") - - val expectedValue = null - val actualValue = avlTree.search(100) - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `search of existing key should return associated value`() { - avlTree.insert(1, "This") - avlTree.insert(2, "Is") - avlTree.insert(3, "My") - avlTree.insert(4, "Home") - - val expectedValue = "Home" - val actualValue = avlTree.search(4) - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `empty tree should not change after search`() { - avlTree.search(1) - - val expectedStructure = listOf>() - val actualStructure = mutableListOf>() - - for (pair in avlTree) actualStructure.add(pair) - - assertEquals(expectedStructure, actualStructure) - } - - @Test - fun `non-empty tree should not change after search`() { - avlTree.insert(1, "Losing") - avlTree.insert(2, "My") - avlTree.insert(3, "Favourite") - avlTree.insert(4, "Game") - - avlTree.search(4) - - val expectedStructure = - listOf(Pair(1, "Losing"), Pair(2, "My"), Pair(3, "Favourite"), Pair(4, "Game")) - - val actualStructure = mutableListOf>() - - for (pair in avlTree) actualStructure.add(pair) - - assertEquals(expectedStructure, actualStructure) - } - } - @Nested inner class InsertTests { // Common tests @@ -609,6 +549,7 @@ class AVLTreeTest { } // Large tree tests + @Test fun `deletion of right child triggering right rotation should balance large tree`() { avlTree.insert(80, "H") @@ -777,36 +718,4 @@ class AVLTreeTest { assertEquals(avlTree.root?.key, 90) } } - - @Nested - inner class ClearTests { - @Test - fun `empty tree should be empty after clear`() { - avlTree.clear() - - val expectedStructure = listOf>() - val actualStructure = mutableListOf>() - - for (pair in avlTree) actualStructure.add(pair) - - assertEquals(expectedStructure, actualStructure) - } - - @Test - fun `not empty tree should be empty after clear`() { - avlTree.insert(1, "Take") - avlTree.insert(2, "Me") - avlTree.insert(3, "To") - avlTree.insert(4, "Church") - - avlTree.clear() - - val expectedStructure = listOf>() - val actualStructure = mutableListOf>() - - for (pair in avlTree) actualStructure.add(pair) - - assertEquals(expectedStructure, actualStructure) - } - } } From 5243d286b6fcea8b2ba52ed44d8d1dfbeb911b5c Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 13:08:14 +0300 Subject: [PATCH 111/164] test: move all tests for methods implemented in abstract binary tree class to separate test class --- .../tsl/trees/AbstractBinaryTreeTest.kt | 369 ++++++++++++++++++ lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 333 +--------------- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 26 +- 3 files changed, 391 insertions(+), 337 deletions(-) create mode 100644 lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt diff --git a/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt new file mode 100644 index 0000000..5d60bec --- /dev/null +++ b/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt @@ -0,0 +1,369 @@ +package tsl.trees + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +/** + * This class tests methods implemented in the AbstractTreeClass + * that are common for all its inheritors (BSTree, AVLTree, RBTree). + * BSTree is used in tests as an instance of the AbstractTreeClass. + */ + +class AbstractBinaryTreeTest { + private lateinit var tree: BSTree + + @BeforeEach + fun setup() { + tree = BSTree() + tree.insert(30, "Danya") + tree.insert(20, "Misha") + tree.insert(40, "Karim") + tree.insert(10, "Moldova") + tree.insert(50, "Tatarstan") + } + + @Nested + inner class SearchTests { + @Test + fun `search of non-existing key should return null`() { + val expectedValue = null + val actualValue = tree.search(100) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `search of existing key should return associated value`() { + val expectedValue = "Danya" + val actualValue = tree.search(30) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `search of existing key that is to the left of the root node should return the corresponding value`() { + val expectedValue = "Moldova" + val actualValue = tree.search(10) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `search of existing key that is to the right of the root node should return the corresponding value`() { + val expectedValue = "Tatarstan" + val actualValue = tree.search(50) + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `empty tree should not change after search`() { + tree.clear() + tree.search(1) + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } + + @Test + fun `non-empty tree should not change after search`() { + tree.search(30) + + val expectedStructure = + listOf( + Pair(10, "Moldova"), Pair(20, "Misha"), Pair(30, "Danya"), + Pair(40, "Karim"), Pair(50, "Tatarstan") + ) + + val actualStructure = mutableListOf>() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + } + + @Nested + inner class ClearTests { + + @Test + fun `clear of an empty tree should return null`() { + val emptyTree = BSTree() + + val expectedValue = null + val actualValue = emptyTree.clear() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `clear of a non-empty tree should return value of its root node`() { + val expectedValue = "Danya" + val actualValue = tree.clear() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `empty tree should be empty after clear`() { + val emptyTree = BSTree() + emptyTree.clear() + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } + + @Test + fun `not empty tree should be empty after clear`() { + tree.clear() + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } + } + + + @Nested + inner class IsEmptyTests { + + @Test + fun `isEmpty should return true if the tree is empty`() { + tree.clear() + + val expectedValue = true + val actualValue = tree.isEmpty() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `isEmpty should return false if the tree is not empty`() { + val expectedValue = false + val actualValue = tree.isEmpty() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `isEmpty should not change an empty tree`() { + tree.clear() + tree.isEmpty() + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } + + @Test + fun `isEmpty should not change a non-empty tree`() { + tree.isEmpty() + + val expectedStructure = + listOf( + Pair(10, "Moldova"), Pair(20, "Misha"), Pair(30, "Danya"), + Pair(40, "Karim"), Pair(50, "Tatarstan") + ) + + val actualStructure = mutableListOf>() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + } + + @Nested + inner class GetMinKeyTests { + + @Test + fun `getMinKey should return null if the tree is empty`() { + tree.clear() + + val expectedValue = null + val actualValue = tree.getMinKey() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `getMinKey should return min key if the tree is not empty`() { + val expectedValue = 10 + val actualValue = tree.getMinKey() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `getMinKey should not change an empty tree`() { + tree.clear() + tree.getMinKey() + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } + + @Test + fun `getMinKey should not change a non-empty tree`() { + tree.getMinKey() + + val expectedStructure = + listOf( + Pair(10, "Moldova"), Pair(20, "Misha"), Pair(30, "Danya"), + Pair(40, "Karim"), Pair(50, "Tatarstan") + ) + + val actualStructure = mutableListOf>() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + } + + @Nested + inner class GetMaxKeyTests { + + @Test + fun `getMaxKey should return null if the tree is empty`() { + tree.clear() + + val expectedValue = null + val actualValue = tree.getMaxKey() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `getMaxKey should return max key if the tree is not empty`() { + val expectedValue = 50 + val actualValue = tree.getMaxKey() + + assertEquals(expectedValue, actualValue) + } + + @Test + fun `getMaxKey should not change an empty tree`() { + tree.clear() + tree.getMaxKey() + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } + + @Test + fun `getMaxKey should not change a non-empty tree`() { + tree.getMaxKey() + + val expectedStructure = + listOf( + Pair(10, "Moldova"), Pair(20, "Misha"), Pair(30, "Danya"), + Pair(40, "Karim"), Pair(50, "Tatarstan") + ) + + val actualStructure = mutableListOf>() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + } + + @Nested + inner class IteratorTests { + + @Test + fun `hasNext should return false right after it traversed all nodes in a non-empty tree`() { + val treeIterator = tree.iterator() + + var count = 0 + while (treeIterator.hasNext()) { + treeIterator.next() + count++ + } + + val expectedQuantity = 5 + val actualQuantity = count + + assertEquals(expectedQuantity, actualQuantity) + } + + @Test + fun `hasNext should always return false in an empty tree`() { + tree.clear() + val treeIterator = tree.iterator() + + var count = 0 + while (treeIterator.hasNext()) { + treeIterator.next() + count++ + } + + val expectedQuantity = 0 + val actualQuantity = count + + assertEquals(expectedQuantity, actualQuantity) + } + + @Test + fun `next method should return pairs with bigger keys than it has returned before`() { + val treeIterator = tree.iterator() + + var everyNextKeyIsBigger = true + + var currentKey = treeIterator.next().first + var previousKey = currentKey + var iteration = 0 + while (treeIterator.hasNext()) { + iteration++ + currentKey = treeIterator.next().first + if (currentKey <= previousKey) { + everyNextKeyIsBigger = false + break + } + previousKey = currentKey + } + + assertEquals(true, everyNextKeyIsBigger) + } + + @Test + fun `iterator should not change an empty tree`() { + tree.clear() + val treeIterator = tree.iterator() + + while (treeIterator.hasNext()) treeIterator.next() + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } + + @Test + fun `iterator should not change a not empty tree`() { + val treeIterator = tree.iterator() + + while (treeIterator.hasNext()) treeIterator.next() + + val expectedStructure = + listOf( + Pair(10, "Moldova"), Pair(20, "Misha"), Pair(30, "Danya"), + Pair(40, "Karim"), Pair(50, "Tatarstan") + ) + + val actualStructure = mutableListOf>() + for (pair in tree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + } + } +} diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index 1a9a922..f6ed992 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -27,6 +27,7 @@ class BSTreeTest { assertEquals(expectedValue, actualValue) } + @Test fun `insert of existing key should replace old value and return it`() { val expectedReturnValue = "kotik" @@ -44,8 +45,10 @@ class BSTreeTest { fun `node with the smallest key should be inserted at the most left position`() { tree.insert(5, "pesik") - val expectedStructure = listOf(Pair(5, "pesik"), Pair(10, "kotik"), Pair(20, "kotlin"), - Pair(30, "java"), Pair(40, "gnomik")) + val expectedStructure = listOf( + Pair(5, "pesik"), Pair(10, "kotik"), Pair(20, "kotlin"), + Pair(30, "java"), Pair(40, "gnomik") + ) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -57,8 +60,10 @@ class BSTreeTest { fun `node with the biggest key should be inserted at the most right position`() { tree.insert(50, "pesik") - val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), Pair(30, "java"), - Pair(40, "gnomik"), Pair(50, "pesik")) + val expectedStructure = listOf( + Pair(10, "kotik"), Pair(20, "kotlin"), Pair(30, "java"), + Pair(40, "gnomik"), Pair(50, "pesik") + ) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -70,67 +75,10 @@ class BSTreeTest { fun `node with random key should be inserted at correct position`() { tree.insert(25, "pesik") - val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), Pair(25, "pesik"), - Pair(30, "java"), Pair(40, "gnomik")) - - val actualStructure: MutableList> = mutableListOf() - for (pair in tree) actualStructure.add(pair) - - assertEquals(expectedStructure, actualStructure) - } - } - @Nested - inner class SearchTests { - - @Test - fun `search of non existing key should return null`() { - val expectedValue = null - val actualValue = tree.search(100) - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `search of existing key that is in the root node should return the corresponding value`() { - val expectedValue = "java" - val actualValue = tree.search(30) - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `search of existing key that is to the left of the root node should return the corresponding value`() { - val expectedValue = "kotlin" - val actualValue = tree.search(20) - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `search of existing key that is to the right of the root node should return the corresponding value`() { - val expectedValue = "gnomik" - val actualValue = tree.search(40) - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `search should not change an empty tree`() { - tree.clear() - tree.search(42069) - - val expectedRoot = null - val actualRoot = tree.root - - assertEquals(expectedRoot, actualRoot) - } - - @Test - fun `search should not change a non-empty tree`() { - tree.search(40) - - val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), - Pair(30, "java"), Pair(40, "gnomik")) + val expectedStructure = listOf( + Pair(10, "kotik"), Pair(20, "kotlin"), Pair(25, "pesik"), + Pair(30, "java"), Pair(40, "gnomik") + ) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -195,8 +143,10 @@ class BSTreeTest { tree.insert(50, "pesik") tree.delete(40) - val expectedStructure = listOf(Pair(10, "kotlin"), Pair(20, "kotlin"), - Pair(30, "java"), Pair(50, "pesik")) + val expectedStructure = listOf( + Pair(10, "kotlin"), Pair(20, "kotlin"), + Pair(30, "java"), Pair(50, "pesik") + ) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -209,8 +159,10 @@ class BSTreeTest { tree.insert(35, "pesik") tree.delete(30) - val expectedStructure = listOf(Pair(10, "kotlin"), Pair(20, "kotlin"), - Pair(35, "pesik"), Pair(40, "gnomik")) + val expectedStructure = listOf( + Pair(10, "kotlin"), Pair(20, "kotlin"), + Pair(35, "pesik"), Pair(40, "gnomik") + ) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -218,247 +170,4 @@ class BSTreeTest { assertEquals(expectedStructure, actualStructure) } } - - @Nested - inner class ClearTests { - - @Test - fun `clear of a non-empty tree should return value of its root node`() { - val expectedValue = "java" - val actualValue = tree.clear() - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `clear of an empty tree should return null`() { - tree.clear() - - val expectedValue = null - val actualValue = tree.clear() - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `clear of an empty tree should remain it empty`() { - val emptyTree = BSTree() - emptyTree.clear() - - val expectedValue = null - val actualValue = emptyTree.root - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `clear of a non-empty tree should make it empty`() { - tree.clear() - - val expectedValue = null - val actualValue = tree.root - - assertEquals(expectedValue, actualValue) - } - } - - @Nested - inner class IsEmptyTests { - - @Test - fun `isEmpty should return true if the tree is empty`() { - tree.clear() - - val expectedValue = true - val actualValue = tree.isEmpty() - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `isEmpty should return false if the tree is not empty`() { - val expectedValue = false - val actualValue = tree.isEmpty() - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `isEmpty should not change an empty tree`() { - tree.clear() - tree.isEmpty() - - val expectedRoot = null - val actualRoot = tree.root - - assertEquals(expectedRoot, actualRoot) - } - - @Test - fun `isEmpty should not change a non-empty tree`() { - tree.isEmpty() - - val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), - Pair(30, "java"), Pair(40, "gnomik")) - - val actualStructure: MutableList> = mutableListOf() - for (pair in tree) actualStructure.add(pair) - - assertEquals(expectedStructure, actualStructure) - } - } - - @Nested - inner class GetMinKeyTests { - - @Test - fun `getMinKey should return null if the tree is empty`() { - tree.clear() - - val expectedValue = null - val actualValue = tree.getMinKey() - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `getMinKey should return min key if the tree is not empty`() { - val expectedValue = 10 - val actualValue = tree.getMinKey() - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `getMinKey should not change an empty tree`() { - tree.clear() - tree.getMinKey() - - val expectedRoot = null - val actualRoot = tree.root - - assertEquals(expectedRoot, actualRoot) - } - - @Test - fun `getMinKey should not change a non-empty tree`() { - tree.getMinKey() - - val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), - Pair(30, "java"), Pair(40, "gnomik")) - - val actualStructure: MutableList> = mutableListOf() - for (pair in tree) actualStructure.add(pair) - - assertEquals(expectedStructure, actualStructure) - } - } - - @Nested - inner class GetMaxKeyTests { - - @Test - fun `getMaxKey should return null if the tree is empty`() { - tree.clear() - - val expectedValue = null - val actualValue = tree.getMaxKey() - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `getMaxKey should return max key if the tree is not empty`() { - val expectedValue = 40 - val actualValue = tree.getMaxKey() - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `getMaxKey should not change an empty tree`() { - tree.clear() - tree.getMaxKey() - - val expectedRoot = null - val actualRoot = tree.root - - assertEquals(expectedRoot, actualRoot) - } - - @Test - fun `getMaxKey should not change a non-empty tree`() { - tree.getMaxKey() - - val expectedStructure = listOf(Pair(10, "kotik"), Pair(20, "kotlin"), - Pair(30, "java"), Pair(40, "gnomik")) - - val actualStructure: MutableList> = mutableListOf() - for (pair in tree) actualStructure.add(pair) - - assertEquals(expectedStructure, actualStructure) - } - } - - @Nested - inner class Iteratortests { - - @Test - fun `hasNext should return false right after it traversed all nodes in a non-empty tree`() { - val treeIterator = tree.iterator() - - var count = 0 - while (treeIterator.hasNext()) { - treeIterator.next() - count++ - } - - val expectedQuantity = 4 - val actualQuantity = count - - assertEquals(expectedQuantity, actualQuantity) - } - - @Test - fun `hasNext should always return false in an empty tree`() { - tree.clear() - val treeIterator = tree.iterator() - - var count = 0 - while (treeIterator.hasNext()) { - treeIterator.next() - count++ - } - - val expectedQuantity = 0 - val actualQuantity = count - - assertEquals(expectedQuantity, actualQuantity) - } - - @Test - fun `next method should return pairs with bigger key that it has returned before`() { - val treeIterator = tree.iterator() - - var everyNextKeyIsBigger = true - - var currentKey = 0 - var previousKey = 0 - var iteration = 0 - while (treeIterator.hasNext()) { - iteration++ - currentKey = treeIterator.next().first - - if (iteration > 1) { - if (currentKey <= previousKey) { - everyNextKeyIsBigger = false - break - } - } - previousKey = currentKey - } - - assertEquals(true, everyNextKeyIsBigger) - } - } } diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index 79ee7a3..6e29dc3 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -57,30 +57,6 @@ class RBTreeTest { } } - @Nested - inner class SearchTests { - - @Test - fun `search node that isn't in the rbTree`() { - rbTree.insert(15, "hihi") - rbTree.insert(10, "haha") - rbTree.insert(5, "bugaga") - - val returnValue = rbTree.search(7) - assertEquals(null, returnValue) - } - - @Test - fun `search node that is in rbTree`() { - rbTree.insert(15, "hihi") - rbTree.insert(10, "haha") - rbTree.insert(5, "bugaga") - - val returnValue = rbTree.search(5) - assertEquals("bugaga", returnValue) - } - } - @Nested inner class DeleteTests { @@ -394,4 +370,4 @@ class RBTreeTest { assertEquals(RBNode.Color.Red, rbTree.root?.rightChild?.rightChild?.rightChild?.color) } } -} \ No newline at end of file +} From 613471ffdfe827a663e4411b020f41081245905f Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 14:36:39 +0300 Subject: [PATCH 112/164] refactor: rename variables and change tree structure in AbstractBinaryTreeTest class --- .../tsl/trees/AbstractBinaryTreeTest.kt | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt index 5d60bec..c6321ea 100644 --- a/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt @@ -18,14 +18,15 @@ class AbstractBinaryTreeTest { fun setup() { tree = BSTree() tree.insert(30, "Danya") - tree.insert(20, "Misha") - tree.insert(40, "Karim") - tree.insert(10, "Moldova") - tree.insert(50, "Tatarstan") + tree.insert(10, "Misha") + tree.insert(50, "Karim") + tree.insert(20, "Moldova") + tree.insert(40, "Tatarstan") } @Nested inner class SearchTests { + @Test fun `search of non-existing key should return null`() { val expectedValue = null @@ -45,7 +46,7 @@ class AbstractBinaryTreeTest { @Test fun `search of existing key that is to the left of the root node should return the corresponding value`() { val expectedValue = "Moldova" - val actualValue = tree.search(10) + val actualValue = tree.search(20) assertEquals(expectedValue, actualValue) } @@ -53,7 +54,7 @@ class AbstractBinaryTreeTest { @Test fun `search of existing key that is to the right of the root node should return the corresponding value`() { val expectedValue = "Tatarstan" - val actualValue = tree.search(50) + val actualValue = tree.search(40) assertEquals(expectedValue, actualValue) } @@ -71,12 +72,12 @@ class AbstractBinaryTreeTest { @Test fun `non-empty tree should not change after search`() { - tree.search(30) + tree.search(40) val expectedStructure = listOf( - Pair(10, "Moldova"), Pair(20, "Misha"), Pair(30, "Danya"), - Pair(40, "Karim"), Pair(50, "Tatarstan") + Pair(10, "Misha"), Pair(20, "Moldova"), Pair(30, "Danya"), + Pair(40, "Tatarstan"), Pair(50, "Karim") ) val actualStructure = mutableListOf>() @@ -113,7 +114,7 @@ class AbstractBinaryTreeTest { emptyTree.clear() val expectedRoot = null - val actualRoot = tree.root + val actualRoot = emptyTree.root assertEquals(expectedRoot, actualRoot) } @@ -168,8 +169,8 @@ class AbstractBinaryTreeTest { val expectedStructure = listOf( - Pair(10, "Moldova"), Pair(20, "Misha"), Pair(30, "Danya"), - Pair(40, "Karim"), Pair(50, "Tatarstan") + Pair(10, "Misha"), Pair(20, "Moldova"), Pair(30, "Danya"), + Pair(40, "Tatarstan"), Pair(50, "Karim") ) val actualStructure = mutableListOf>() @@ -186,18 +187,18 @@ class AbstractBinaryTreeTest { fun `getMinKey should return null if the tree is empty`() { tree.clear() - val expectedValue = null - val actualValue = tree.getMinKey() + val expectedKey = null + val actualKey = tree.getMinKey() - assertEquals(expectedValue, actualValue) + assertEquals(expectedKey, actualKey) } @Test fun `getMinKey should return min key if the tree is not empty`() { - val expectedValue = 10 - val actualValue = tree.getMinKey() + val expectedKey = 10 + val actualKey = tree.getMinKey() - assertEquals(expectedValue, actualValue) + assertEquals(expectedKey, actualKey) } @Test @@ -217,8 +218,8 @@ class AbstractBinaryTreeTest { val expectedStructure = listOf( - Pair(10, "Moldova"), Pair(20, "Misha"), Pair(30, "Danya"), - Pair(40, "Karim"), Pair(50, "Tatarstan") + Pair(10, "Misha"), Pair(20, "Moldova"), Pair(30, "Danya"), + Pair(40, "Tatarstan"), Pair(50, "Karim") ) val actualStructure = mutableListOf>() @@ -235,18 +236,18 @@ class AbstractBinaryTreeTest { fun `getMaxKey should return null if the tree is empty`() { tree.clear() - val expectedValue = null - val actualValue = tree.getMaxKey() + val expectedKey = null + val actualKey = tree.getMaxKey() - assertEquals(expectedValue, actualValue) + assertEquals(expectedKey, actualKey) } @Test fun `getMaxKey should return max key if the tree is not empty`() { - val expectedValue = 50 - val actualValue = tree.getMaxKey() + val expectedKey = 50 + val actualKey = tree.getMaxKey() - assertEquals(expectedValue, actualValue) + assertEquals(expectedKey, actualKey) } @Test @@ -266,8 +267,8 @@ class AbstractBinaryTreeTest { val expectedStructure = listOf( - Pair(10, "Moldova"), Pair(20, "Misha"), Pair(30, "Danya"), - Pair(40, "Karim"), Pair(50, "Tatarstan") + Pair(10, "Misha"), Pair(20, "Moldova"), Pair(30, "Danya"), + Pair(40, "Tatarstan"), Pair(50, "Karim") ) val actualStructure = mutableListOf>() @@ -356,8 +357,8 @@ class AbstractBinaryTreeTest { val expectedStructure = listOf( - Pair(10, "Moldova"), Pair(20, "Misha"), Pair(30, "Danya"), - Pair(40, "Karim"), Pair(50, "Tatarstan") + Pair(10, "Misha"), Pair(20, "Moldova"), Pair(30, "Danya"), + Pair(40, "Tatarstan"), Pair(50, "Karim") ) val actualStructure = mutableListOf>() From 2be222304684ac1b50015c53e3ddb5f9e66bef7c Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 15:04:08 +0300 Subject: [PATCH 113/164] fix: logical error in GetMaxNodeRecursively method --- lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index a3ad11d..20945c6 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -52,7 +52,7 @@ abstract class AbstractBinaryTree, V, N : AbstractNode null node.rightChild == null -> node - else -> getMinNodeRecursively(node.rightChild) + else -> getMaxNodeRecursively(node.rightChild) } } From 6903edb363b2f1e230da69878881282126831a58 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 15:06:26 +0300 Subject: [PATCH 114/164] fix: wrong expected values in deletion method tests of BS tree --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index f6ed992..0059054 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -131,7 +131,7 @@ class BSTreeTest { fun `delete of a node with a left child should replace it with it's child`() { tree.delete(20) - val expectedStructure = listOf(Pair(10, "kotlin"), Pair(30, "java"), Pair(40, "gnomik")) + val expectedStructure = listOf(Pair(10, "kotik"), Pair(30, "java"), Pair(40, "gnomik")) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -144,7 +144,7 @@ class BSTreeTest { tree.delete(40) val expectedStructure = listOf( - Pair(10, "kotlin"), Pair(20, "kotlin"), + Pair(10, "kotik"), Pair(20, "kotlin"), Pair(30, "java"), Pair(50, "pesik") ) @@ -160,7 +160,7 @@ class BSTreeTest { tree.delete(30) val expectedStructure = listOf( - Pair(10, "kotlin"), Pair(20, "kotlin"), + Pair(10, "kotik"), Pair(20, "kotlin"), Pair(35, "pesik"), Pair(40, "gnomik") ) From 693350917f86b80e24a8fe121e9daccd743f37b5 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 18:19:31 +0300 Subject: [PATCH 115/164] refactor: change variables' names and replace if/else with when in BSTree class methods --- lib/src/main/kotlin/tsl/trees/BSTree.kt | 66 +++++++++++++++---------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index 9a6fdea..177e3b4 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -15,22 +15,28 @@ class BSTree, V> : AbstractBinaryTree>() { var currentNode = root while (currentNode != null) { - if (nodeToInsert.key == currentNode.key) { - val oldValue = currentNode.value - currentNode.value = nodeToInsert.value - return oldValue - } else if (nodeToInsert.key < currentNode.key) { - if (currentNode.leftChild == null) { - currentNode.leftChild = nodeToInsert - return null + when { + nodeToInsert.key == currentNode.key -> { + val oldValue = currentNode.value + currentNode.value = nodeToInsert.value + return oldValue } - currentNode = currentNode.leftChild - } else if (nodeToInsert.key > currentNode.key) { - if (currentNode.rightChild == null) { - currentNode.rightChild = nodeToInsert - return null + + nodeToInsert.key < currentNode.key -> { + if (currentNode.leftChild == null) { + currentNode.leftChild = nodeToInsert + return null + } + currentNode = currentNode.leftChild + } + + nodeToInsert.key > currentNode.key -> { + if (currentNode.rightChild == null) { + currentNode.rightChild = nodeToInsert + return null + } + currentNode = currentNode.rightChild } - currentNode = currentNode.rightChild } } return currentNode?.value @@ -44,22 +50,28 @@ class BSTree, V> : AbstractBinaryTree>() { return deletedValue } - private fun deleteRecursively(node: BSTNode?, key: K): BSTNode? { - if (node == null) return null - if (key < node.key) node.leftChild = deleteRecursively(node.leftChild, key) - else if (key > node.key) node.rightChild = deleteRecursively(node.rightChild, key) - else if (key == node.key) { - if (node.leftChild == null || node.rightChild == null) - return node.leftChild ?: node.rightChild - else { - val minRightNode = getMinNodeRecursively(node.rightChild) + private fun deleteRecursively(currentNode: BSTNode?, keyToDelete: K): BSTNode? { + when { + currentNode == null -> return null + + keyToDelete < currentNode.key -> + currentNode.leftChild = deleteRecursively(currentNode.leftChild, keyToDelete) + + keyToDelete > currentNode.key -> + currentNode.rightChild = deleteRecursively(currentNode.rightChild, keyToDelete) + + keyToDelete == currentNode.key -> { + if (currentNode.leftChild == null || currentNode.rightChild == null) + return currentNode.leftChild ?: currentNode.rightChild + + val minRightNode = getMinNodeRecursively(currentNode.rightChild) if (minRightNode != null) { - node.key = minRightNode.key - node.value = minRightNode.value - node.rightChild = deleteRecursively(node.rightChild, minRightNode.key) + currentNode.key = minRightNode.key + currentNode.value = minRightNode.value + currentNode.rightChild = deleteRecursively(currentNode.rightChild, minRightNode.key) } } } - return node + return currentNode } } From 4f8028325b065e3614f4ed0b37a7e2f6cdd49058 Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Sun, 31 Mar 2024 21:43:08 +0300 Subject: [PATCH 116/164] docs: create new issue template for bug report --- .github/ISSUE_TEMPLATE/bug_report.md | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..d1ba2bc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. From 6cb438ca349585c9978fe70103459ebaf606487e Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Sun, 31 Mar 2024 21:51:07 +0300 Subject: [PATCH 117/164] docs: prescribe rules for contribution --- CONTRIBUTING.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..787043c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,53 @@ +# 💪 Interested in contributing? + +Good boy! Please read the few sections below to understand how to implement new features. + +> 😅 Be positive! Even if your changes don't get merged in forest-group, please don't be too sad, you will always be able to run workflows directly from your fork! +> Meow + +## 🤝 Accepted contributions + +The following contributions are accepted: + + + + + + + + + + + + + + + + + + + + + + + + + +
SectionChangesAdditionsNotes
🧩 Features✔️✔️ +
    +
  • New features are allowed, but must be optional and backward compatible
  • +
+
🧪 Tests✔️✔️ +
    +
  • Everything that makes forest-group more stable is welcomed!
  • +
+
🗃️ Repository +
    +
  • Workflows, license, readmes, etc. usually don't need to be edited
  • +
+
+ +**Legend** +* ✔️: Contributions welcomed! +* ✓: Contributions are welcomed, but must be discussed first +* ❌: Only maintainers can manage these files From 691569a77eaa30d0799c1c0d550a8aca87e3833a Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Sun, 31 Mar 2024 22:09:12 +0300 Subject: [PATCH 118/164] docs: get rid of unnecessary statements --- .github/ISSUE_TEMPLATE/bug_report.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d1ba2bc..ae1749f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,9 +1,7 @@ --- name: Bug report about: Create a report to help us improve -title: '' labels: bug -assignees: '' --- @@ -24,8 +22,7 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] + - OS: [e.g. macOS] - Version [e.g. 22] **Additional context** From bcc79b2980f1f0ffe0da4a76571342b989845fd9 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 31 Mar 2024 22:09:12 +0300 Subject: [PATCH 119/164] fix: remove return value of clear method and tests for it --- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 6 +----- .../kotlin/tsl/trees/AbstractBinaryTreeTest.kt | 18 ------------------ 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 20945c6..413bdd6 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -23,12 +23,8 @@ abstract class AbstractBinaryTree, V, N : AbstractNode() - - val expectedValue = null - val actualValue = emptyTree.clear() - - assertEquals(expectedValue, actualValue) - } - - @Test - fun `clear of a non-empty tree should return value of its root node`() { - val expectedValue = "Danya" - val actualValue = tree.clear() - - assertEquals(expectedValue, actualValue) - } - @Test fun `empty tree should be empty after clear`() { val emptyTree = BSTree() From f5782f02a157d0a30d7b0b3e8d6dc57a1ba8b90b Mon Sep 17 00:00:00 2001 From: Karim Shakirov Date: Mon, 1 Apr 2024 00:33:29 +0300 Subject: [PATCH 120/164] Update README.md Extend usage description and add authors. --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b6056a6..4373c24 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # forest-group -[![Kotlin][kotlin_img]][kotlin_releases_url] +[![Kotlin 1.9.23][kotlin_img]][kotlin_releases_url] [![License][license_img]][repo_license_url] ![Coverage](.github/badges/jacoco.svg) + `forest-group` is a library that lets you easily create and use [Binary search trees](https://en.wikipedia.org/wiki/Binary_search_tree), [AVL trees](https://en.wikipedia.org/wiki/AVL_tree) and [Red-black trees](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) in your applications! @@ -14,8 +15,9 @@ - [Usage](#usage) - [Contributing](#contributing) - [License](#license) +- [Authors](#authors) - + ## About The Project Tree data structure is well-known for its average logarithmic operations time. However, there are a lot of tree implementations that are strict to data types that can be stored. @@ -34,15 +36,46 @@ You now can simply insert and replace values in your tree: ``` myTree.insert(keyA, "Something important") + val oldValue = myTree.insert(keyA, "Something more important") ``` You can also search for values and delete values from tree by keys: ``` -val myValue = myTree.search(myKey) -myTree.delete(myKey) +val myValue1 = myTree.search(myKey) + +val myValue2 = myTree.delete(myKey) + +println(myValue1 == myValue2) // true +``` + +All trees are iterable by Pair(key, value), so they can be used in a for loop: + ``` +for ((key, value) in myTree) { + keysList.add(key) + valuesList.add(value) +} + +myTree.forEach { println(it) } // prints every pair +``` + +You can create your own iterator manually: + +``` +val treeIterator = myTree.iterator() + +while (treeIterator.hasNext()) + println(treeIterator.next()) +``` + +There are also other methods: + +- `clear()` +- `isEmpty()` +- `getMinKey()` +- `getMaxKey()` @@ -60,11 +93,17 @@ If you have found a bug, or want to propose some useful feature for our project, Distributed under the MIT License. See LICENSE.md for more information. +## Authors + +- [Shakirov Karim](https://github.com/kar1mgh) +- [Vlasenco Daniel](https://github.com/spisladqo) +- [Gavrilenko Mike](https://github.com/qrutyy) + [license_img]: https://img.shields.io/badge/license-MIT-green -[kotlin_img]: https://img.shields.io/badge/kotlin-magenta +[kotlin_img]: https://img.shields.io/badge/kotlin%201.9.23-magenta From 2893836ab114cf0a06514d9617a52697e11a2d81 Mon Sep 17 00:00:00 2001 From: Daniel Vlasenco Date: Mon, 1 Apr 2024 11:06:29 +0300 Subject: [PATCH 121/164] Update README.md remove coverage badge, add small enhancements --- README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4373c24..f0e4463 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,6 @@ [![Kotlin 1.9.23][kotlin_img]][kotlin_releases_url] [![License][license_img]][repo_license_url] -![Coverage](.github/badges/jacoco.svg) - - `forest-group` is a library that lets you easily create and use [Binary search trees](https://en.wikipedia.org/wiki/Binary_search_tree), [AVL trees](https://en.wikipedia.org/wiki/AVL_tree) and [Red-black trees](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) in your applications! @@ -17,7 +14,7 @@ - [License](#license) - [Authors](#authors) - + ## About The Project Tree data structure is well-known for its average logarithmic operations time. However, there are a lot of tree implementations that are strict to data types that can be stored. @@ -70,7 +67,7 @@ while (treeIterator.hasNext()) println(treeIterator.next()) ``` -There are also other methods: +There are also other helpful methods: - `clear()` - `isEmpty()` @@ -82,9 +79,9 @@ There are also other methods: ## Contributing If you have found a bug, or want to propose some useful feature for our project, please do the following: 1. Fork the Project -2. Create your Feature Branch (git checkout -b feature/MyFeature) -3. Commit your Changes (git commit -m 'add some Feature') -4. Push to the Branch (git push origin feature/MyFeature) +2. Create your Feature Branch (git checkout -b feature/myFeature) +3. Commit your Changes (git commit -m 'add some feature') +4. Push to the Branch (git push origin feature/myFeature) 5. Open a Pull Request @@ -93,13 +90,14 @@ If you have found a bug, or want to propose some useful feature for our project, Distributed under the MIT License. See LICENSE.md for more information. -## Authors +## Authors - [Shakirov Karim](https://github.com/kar1mgh) - [Vlasenco Daniel](https://github.com/spisladqo) - [Gavrilenko Mike](https://github.com/qrutyy) + [license_img]: https://img.shields.io/badge/license-MIT-green From 292886c1ad8a9a3485a9138730e6330f1627bc77 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 1 Apr 2024 11:23:14 +0300 Subject: [PATCH 122/164] refactor: apply formatter for single code style --- .../kotlin/tsl/iterator/BinaryTreeIterator.kt | 1 + lib/src/main/kotlin/tsl/trees/AVLTree.kt | 63 +++-- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 15 +- lib/src/main/kotlin/tsl/trees/BSTree.kt | 8 +- lib/src/main/kotlin/tsl/trees/RBTree.kt | 32 +-- lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt | 230 ++++++++++++------ .../tsl/trees/AbstractBinaryTreeTest.kt | 43 ++-- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 51 ++-- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 65 +++-- 9 files changed, 319 insertions(+), 189 deletions(-) diff --git a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt index 4980f56..c5b83de 100644 --- a/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt +++ b/lib/src/main/kotlin/tsl/iterator/BinaryTreeIterator.kt @@ -2,6 +2,7 @@ package tsl.iterator import tsl.nodes.AbstractNode import tsl.trees.AbstractBinaryTree + internal class BinaryTreeIterator, V, N : AbstractNode>( tree: AbstractBinaryTree ) : Iterator> { diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index 20baa56..36cb8b4 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -5,7 +5,7 @@ import tsl.nodes.AVLNode class AVLTree, V> : AbstractBinaryTree>() { public override fun insert(key: K, value: V): V? { - val oldValueByKey = search(key) // null if key isn't in the tree + val oldValueByKey = search(key) // null if key isn't in the tree insertNodeAndBalanceRecursively(root, key, value) @@ -26,11 +26,11 @@ class AVLTree, V> : AbstractBinaryTree>() when { keyToInsert < currNode.key -> - currNode.leftChild = insertNodeAndBalanceRecursively(currNode.leftChild, keyToInsert, valueToInsert) - + currNode.leftChild = + insertNodeAndBalanceRecursively(currNode.leftChild, keyToInsert, valueToInsert) keyToInsert > currNode.key -> - currNode.rightChild = insertNodeAndBalanceRecursively(currNode.rightChild, keyToInsert, valueToInsert) - + currNode.rightChild = + insertNodeAndBalanceRecursively(currNode.rightChild, keyToInsert, valueToInsert) else -> { currNode.value = valueToInsert return currNode @@ -39,19 +39,19 @@ class AVLTree, V> : AbstractBinaryTree>() var balancedNode: AVLNode = currNode - if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger + if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger currNode.leftChild?.let { - if (keyToInsert > it.key) { // if inserted node has greater key - currNode.leftChild = rotateLeft(it) // perform left rotation first + if (keyToInsert > it.key) { // if inserted node has greater key + currNode.leftChild = rotateLeft(it) // perform left rotation first } - balancedNode = rotateRight(currNode) // anyway, perform right rotation + balancedNode = rotateRight(currNode) // anyway, perform right rotation } - } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger + } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger currNode.rightChild?.let { - if (keyToInsert < it.key) { // if inserted node has lesser key - currNode.rightChild = rotateRight(it) // perform right rotation first + if (keyToInsert < it.key) { // if inserted node has lesser key + currNode.rightChild = rotateRight(it) // perform right rotation first } - balancedNode = rotateLeft(currNode) // anyway, perform left rotation + balancedNode = rotateLeft(currNode) // anyway, perform left rotation } } @@ -94,7 +94,8 @@ class AVLTree, V> : AbstractBinaryTree>() private fun getHeight(node: AVLNode?): Int = node?.height ?: -1 public override fun delete(key: K): V? { - val deletedValue: V = search(key) ?: return null // if key isn't in the tree, there's nothing to delete + val deletedValue: V = + search(key) ?: return null // if key isn't in the tree, there's nothing to delete deleteAndBalanceRecursively(root, key) @@ -107,16 +108,13 @@ class AVLTree, V> : AbstractBinaryTree>() ): AVLNode? { when { - currNode == null -> return null // node to be deleted was not found - - keyToDelete < currNode.key -> // node to be deleted is in the left subtree - currNode.leftChild = deleteAndBalanceRecursively(currNode.leftChild, keyToDelete) - - keyToDelete > currNode.key -> // node to be deleted is in the right subtree - currNode.rightChild = deleteAndBalanceRecursively(currNode.rightChild, keyToDelete) - + currNode == null -> return null // node to be deleted was not found + keyToDelete < currNode.key -> // node to be deleted is in the left subtree + currNode.leftChild = deleteAndBalanceRecursively(currNode.leftChild, keyToDelete) + keyToDelete > currNode.key -> // node to be deleted is in the right subtree + currNode.rightChild = deleteAndBalanceRecursively(currNode.rightChild, keyToDelete) else -> { - if (currNode.leftChild == null ||currNode.rightChild == null) { + if (currNode.leftChild == null || currNode.rightChild == null) { return currNode.leftChild ?: currNode.rightChild } else { val successor = getMinNodeRecursively(currNode.rightChild) @@ -126,7 +124,8 @@ class AVLTree, V> : AbstractBinaryTree>() currNode.value = successor.value // delete original successor node from tree - val newSubtree = deleteAndBalanceRecursively(currNode.rightChild, successor.key) + val newSubtree = + deleteAndBalanceRecursively(currNode.rightChild, successor.key) if (newSubtree != null) currNode.rightChild = newSubtree } } @@ -135,19 +134,19 @@ class AVLTree, V> : AbstractBinaryTree>() var balancedNode: AVLNode = currNode - if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger + if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger currNode.leftChild?.let { - if (getBalanceFactor(it) > 0) { // if inserted node has greater key - currNode.leftChild = rotateLeft(it) // perform left rotation first + if (getBalanceFactor(it) > 0) { // if inserted node has greater key + currNode.leftChild = rotateLeft(it) // perform left rotation first } - balancedNode = rotateRight(currNode) // anyway, perform left rotation + balancedNode = rotateRight(currNode) // anyway, perform left rotation } - } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger + } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger currNode.rightChild?.let { - if (getBalanceFactor(it) < 0) { // if inserted node has greater key - currNode.rightChild = rotateRight(it) // perform right rotation first + if (getBalanceFactor(it) < 0) { // if inserted node has greater key + currNode.rightChild = rotateRight(it) // perform right rotation first } - balancedNode = rotateLeft(currNode) // anyway, perform left rotation + balancedNode = rotateLeft(currNode) // anyway, perform left rotation } } diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 413bdd6..4629f59 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -11,8 +11,7 @@ abstract class AbstractBinaryTree, V, N : AbstractNode, V, N : AbstractNode, V, N : AbstractNode> = - BinaryTreeIterator(this) + public override fun iterator(): Iterator> = BinaryTreeIterator(this) } diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index 177e3b4..540c1ca 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -21,7 +21,6 @@ class BSTree, V> : AbstractBinaryTree>() { currentNode.value = nodeToInsert.value return oldValue } - nodeToInsert.key < currentNode.key -> { if (currentNode.leftChild == null) { currentNode.leftChild = nodeToInsert @@ -29,7 +28,6 @@ class BSTree, V> : AbstractBinaryTree>() { } currentNode = currentNode.leftChild } - nodeToInsert.key > currentNode.key -> { if (currentNode.rightChild == null) { currentNode.rightChild = nodeToInsert @@ -53,13 +51,10 @@ class BSTree, V> : AbstractBinaryTree>() { private fun deleteRecursively(currentNode: BSTNode?, keyToDelete: K): BSTNode? { when { currentNode == null -> return null - keyToDelete < currentNode.key -> currentNode.leftChild = deleteRecursively(currentNode.leftChild, keyToDelete) - keyToDelete > currentNode.key -> currentNode.rightChild = deleteRecursively(currentNode.rightChild, keyToDelete) - keyToDelete == currentNode.key -> { if (currentNode.leftChild == null || currentNode.rightChild == null) return currentNode.leftChild ?: currentNode.rightChild @@ -68,7 +63,8 @@ class BSTree, V> : AbstractBinaryTree>() { if (minRightNode != null) { currentNode.key = minRightNode.key currentNode.value = minRightNode.value - currentNode.rightChild = deleteRecursively(currentNode.rightChild, minRightNode.key) + currentNode.rightChild = + deleteRecursively(currentNode.rightChild, minRightNode.key) } } } diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index e25dd5c..a4f09d0 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -11,7 +11,6 @@ class RBTree, V> : AbstractBinaryTree>() { * @return Null if 'insert' method haven't replaced any value. Return value if there was some * replace.\. */ - public override fun insert(key: K, value: V): V? { val newNode = RBNode(key, value) @@ -59,7 +58,6 @@ class RBTree, V> : AbstractBinaryTree>() { * * @param RBNode-typed node. */ - private fun balanceNode(node: RBNode?) { if (node?.parent == null) root.also { it?.color = RBNode.Color.Black } var newNode = node @@ -77,7 +75,11 @@ class RBTree, V> : AbstractBinaryTree>() { currentGrandParent?.color = RBNode.Color.Red newNode = currentGrandParent } else { - if (currentParent?.color == RBNode.Color.Red && newNode.color == RBNode.Color.Red && newNode == currentParent.rightChild) { + if ( + currentParent?.color == RBNode.Color.Red && + newNode.color == RBNode.Color.Red && + newNode == currentParent.rightChild + ) { newRoot = rotateLeft(currentParent, newRoot) newNode.color = RBNode.Color.Black newNode.leftChild?.color = RBNode.Color.Red @@ -97,7 +99,11 @@ class RBTree, V> : AbstractBinaryTree>() { currentGrandParent?.color = RBNode.Color.Red newNode = currentGrandParent } else { - if (currentParent?.color == RBNode.Color.Red && newNode.color == RBNode.Color.Red && newNode == currentParent.leftChild) { + if ( + currentParent?.color == RBNode.Color.Red && + newNode.color == RBNode.Color.Red && + newNode == currentParent.leftChild + ) { currentParent.leftChild?.color = RBNode.Color.Black newRoot = rotateRight(currentParent, newRoot) } @@ -117,7 +123,6 @@ class RBTree, V> : AbstractBinaryTree>() { * @param key The key of the node to be deleted\. * @return Value if delete was successful and null if it isn't.\. */ - public override fun delete(key: K): V? { val deletingNodeValue = search(key) root = deleteNode(key) @@ -133,7 +138,6 @@ class RBTree, V> : AbstractBinaryTree>() { * @param key The key of the node to be deleted\. * @return The new root node of the Red\-Black Tree after deletion\. */ - private fun deleteNode( key: K, ): RBNode? { @@ -263,18 +267,18 @@ class RBTree, V> : AbstractBinaryTree>() { nodeToRotate.leftChild = leftChild.rightChild leftChild.rightChild?.parent = nodeToRotate leftChild.rightChild = nodeToRotate - } - else { + } else { leftChild?.rightChild = nodeToRotate nodeToRotate?.leftChild = null } if (leftChild?.parent != null) { - if (leftChild.parent?.leftChild == nodeToRotate || leftChild.parent?.leftChild == null) { + if ( + leftChild.parent?.leftChild == nodeToRotate || leftChild.parent?.leftChild == null + ) { leftChild.parent?.leftChild?.leftChild = null leftChild.parent?.leftChild = leftChild - } - else { + } else { leftChild.parent?.rightChild = leftChild } } else { @@ -298,8 +302,7 @@ class RBTree, V> : AbstractBinaryTree>() { nodeToRotate.rightChild = rightChild.leftChild rightChild.leftChild?.parent = nodeToRotate.rightChild rightChild.leftChild = nodeToRotate - } - else { + } else { rightChild?.leftChild = nodeToRotate nodeToRotate?.rightChild = null } @@ -308,8 +311,7 @@ class RBTree, V> : AbstractBinaryTree>() { if (rightChild?.parent != null) { if (rightChild.parent?.rightChild == nodeToRotate) { rightChild.parent?.rightChild = rightChild - } - else { + } else { rightChild.parent?.leftChild = rightChild } } else { diff --git a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt index d981e4f..012a240 100644 --- a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt @@ -47,8 +47,7 @@ class AVLTreeTest { avlTree.insert(15, "To church") avlTree.insert(4, "Me") - val expectedStructure = - listOf(Pair(4, "Me"), Pair(15, "To church"), Pair(20, "Take")) + val expectedStructure = listOf(Pair(4, "Me"), Pair(15, "To church"), Pair(20, "Take")) val actualStructure = mutableListOf>() @@ -64,8 +63,7 @@ class AVLTreeTest { avlTree.insert(15, "B") avlTree.insert(20, "C") - val expectedStructure = - listOf(Pair(4, "A"), Pair(15, "B"), Pair(20, "C")) + val expectedStructure = listOf(Pair(4, "A"), Pair(15, "B"), Pair(20, "C")) val actualStructure = mutableListOf>() @@ -89,8 +87,12 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(3, "You"), Pair(4, "Swear"), Pair(9, "Make"), - Pair(15, "Cry"), Pair(20, "I"), Pair(26, "I'll only") + Pair(3, "You"), + Pair(4, "Swear"), + Pair(9, "Make"), + Pair(15, "Cry"), + Pair(20, "I"), + Pair(26, "I'll only") ) val actualStructure = mutableListOf>() @@ -113,8 +115,12 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(3, "A"), Pair(4, "B"), Pair(8, "C"), - Pair(9, "D"), Pair(20, "E"), Pair(26, "F") + Pair(3, "A"), + Pair(4, "B"), + Pair(8, "C"), + Pair(9, "D"), + Pair(20, "E"), + Pair(26, "F") ) val actualStructure = mutableListOf>() @@ -137,8 +143,12 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(4, "A"), Pair(20, "B"), Pair(24, "C"), - Pair(25, "D"), Pair(26, "E"), Pair(30, "F") + Pair(4, "A"), + Pair(20, "B"), + Pair(24, "C"), + Pair(25, "D"), + Pair(26, "E"), + Pair(30, "F") ) val actualStructure = mutableListOf>() @@ -161,8 +171,12 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(4, "A"), Pair(20, "B"), Pair(21, "C"), - Pair(24, "D"), Pair(26, "E"), Pair(30, "F") + Pair(4, "A"), + Pair(20, "B"), + Pair(21, "C"), + Pair(24, "D"), + Pair(26, "E"), + Pair(30, "F") ) val actualStructure = mutableListOf>() @@ -192,10 +206,17 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(2, "And"), Pair(3, "Your"), Pair(4, "Of puppets"), - Pair(7, "Smashing"), Pair(9, "Strings"), Pair(11, "Your"), - Pair(15, "Dreams"), Pair(20, "Master"), Pair(21, "Twisting"), - Pair(26, "I'm pulling"), Pair(30, "Your mind") + Pair(2, "And"), + Pair(3, "Your"), + Pair(4, "Of puppets"), + Pair(7, "Smashing"), + Pair(9, "Strings"), + Pair(11, "Your"), + Pair(15, "Dreams"), + Pair(20, "Master"), + Pair(21, "Twisting"), + Pair(26, "I'm pulling"), + Pair(30, "Your mind") ) val actualStructure = mutableListOf>() @@ -223,10 +244,17 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(2, "A"), Pair(3, "B"), Pair(4, "C"), - Pair(6, "D"), Pair(7, "E"), Pair(9, "F"), - Pair(11, "G"), Pair(20, "H"), Pair(21, "I"), - Pair(26, "J"), Pair(30, "K") + Pair(2, "A"), + Pair(3, "B"), + Pair(4, "C"), + Pair(6, "D"), + Pair(7, "E"), + Pair(9, "F"), + Pair(11, "G"), + Pair(20, "H"), + Pair(21, "I"), + Pair(26, "J"), + Pair(30, "K") ) val actualStructure = mutableListOf>() @@ -254,10 +282,17 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(3, "A"), Pair(10, "B"), Pair(15, "C"), - Pair(20, "D"), Pair(22, "E"), Pair(25, "F"), - Pair(27, "G"), Pair(28, "H"), Pair(30, "I"), - Pair(35, "J"), Pair(40, "K") + Pair(3, "A"), + Pair(10, "B"), + Pair(15, "C"), + Pair(20, "D"), + Pair(22, "E"), + Pair(25, "F"), + Pair(27, "G"), + Pair(28, "H"), + Pair(30, "I"), + Pair(35, "J"), + Pair(40, "K") ) val actualStructure = mutableListOf>() @@ -285,10 +320,17 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(3, "A"), Pair(10, "B"), Pair(15, "C"), - Pair(20, "D"), Pair(22, "E"), Pair(25, "F"), - Pair(26, "G"), Pair(27, "H"), Pair(30, "I"), - Pair(35, "J"), Pair(40, "K") + Pair(3, "A"), + Pair(10, "B"), + Pair(15, "C"), + Pair(20, "D"), + Pair(22, "E"), + Pair(25, "F"), + Pair(26, "G"), + Pair(27, "H"), + Pair(30, "I"), + Pair(35, "J"), + Pair(40, "K") ) val actualStructure = mutableListOf>() @@ -313,8 +355,7 @@ class AVLTreeTest { val expectedReturn = null val actualReturn = avlTree.delete(40) - val expectedStructure = - listOf(Pair(10, "A"), Pair(20, "B"), Pair(30, "C")) + val expectedStructure = listOf(Pair(10, "A"), Pair(20, "B"), Pair(30, "C")) val actualStructure = mutableListOf>() @@ -334,15 +375,18 @@ class AVLTreeTest { val expectedReturn = "A" val actualReturn = avlTree.delete(10) - val expectedStructure = - listOf(Pair(20, "B"), Pair(30, "C")) + val expectedStructure = listOf(Pair(20, "B"), Pair(30, "C")) val actualStructure = mutableListOf>() for (pair in avlTree) actualStructure.add(pair) assertEquals(expectedReturn, actualReturn, "Returned wrong value") - assertEquals(expectedStructure, actualStructure, "The tree has incorrect structure after deletion") + assertEquals( + expectedStructure, + actualStructure, + "The tree has incorrect structure after deletion" + ) assertEquals(avlTree.root?.key, 20) } @@ -355,8 +399,7 @@ class AVLTreeTest { avlTree.delete(30) - val expectedStructure = - listOf(Pair(10, "A"), Pair(20, "B"), Pair(40, "D")) + val expectedStructure = listOf(Pair(10, "A"), Pair(20, "B"), Pair(40, "D")) val actualStructure = mutableListOf>() @@ -377,8 +420,7 @@ class AVLTreeTest { avlTree.delete(40) - val expectedStructure = - listOf(Pair(10, "A"), Pair(20, "B"), Pair(30, "C")) + val expectedStructure = listOf(Pair(10, "A"), Pair(20, "B"), Pair(30, "C")) val actualStructure = mutableListOf>() @@ -397,8 +439,7 @@ class AVLTreeTest { avlTree.delete(10) - val expectedStructure = - listOf(Pair(20, "B"), Pair(30, "C"), Pair(40, "D")) + val expectedStructure = listOf(Pair(20, "B"), Pair(30, "C"), Pair(40, "D")) val actualStructure = mutableListOf>() @@ -417,8 +458,7 @@ class AVLTreeTest { avlTree.delete(40) - val expectedStructure = - listOf(Pair(10, "A"), Pair(20, "B"), Pair(30, "C")) + val expectedStructure = listOf(Pair(10, "A"), Pair(20, "B"), Pair(30, "C")) val actualStructure = mutableListOf>() @@ -437,8 +477,7 @@ class AVLTreeTest { avlTree.delete(10) - val expectedStructure = - listOf(Pair(20, "B"), Pair(30, "C"), Pair(40, "D")) + val expectedStructure = listOf(Pair(20, "B"), Pair(30, "C"), Pair(40, "D")) val actualStructure = mutableListOf>() @@ -457,8 +496,7 @@ class AVLTreeTest { avlTree.delete(20) - val expectedStructure = - listOf(Pair(10, "A"), Pair(30, "C"), Pair(40, "D")) + val expectedStructure = listOf(Pair(10, "A"), Pair(30, "C"), Pair(40, "D")) val actualStructure = mutableListOf>() @@ -484,8 +522,12 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(10, "A"), Pair(20, "B"), Pair(30, "C"), - Pair(40, "D"), Pair(50, "E"), Pair(60, "F") + Pair(10, "A"), + Pair(20, "B"), + Pair(30, "C"), + Pair(40, "D"), + Pair(50, "E"), + Pair(60, "F") ) val actualStructure = mutableListOf>() @@ -510,8 +552,12 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(20, "B"), Pair(30, "C"), Pair(40, "D"), - Pair(50, "E"), Pair(60, "F"), Pair(70, "G") + Pair(20, "B"), + Pair(30, "C"), + Pair(40, "D"), + Pair(50, "E"), + Pair(60, "F"), + Pair(70, "G") ) val actualStructure = mutableListOf>() @@ -536,8 +582,12 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(10, "A"), Pair(20, "B"), Pair(30, "C"), - Pair(40, "D"), Pair(60, "F"), Pair(70, "G") + Pair(10, "A"), + Pair(20, "B"), + Pair(30, "C"), + Pair(40, "D"), + Pair(60, "F"), + Pair(70, "G") ) val actualStructure = mutableListOf>() @@ -570,10 +620,18 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(10, "A"), Pair(20, "B"), Pair(30, "C"), - Pair(40, "D"), Pair(50, "E"), Pair(60, "F"), - Pair(70, "G"), Pair(80, "H"), Pair(90, "I"), - Pair(100, "J"), Pair(110, "K"), Pair(120, "L") + Pair(10, "A"), + Pair(20, "B"), + Pair(30, "C"), + Pair(40, "D"), + Pair(50, "E"), + Pair(60, "F"), + Pair(70, "G"), + Pair(80, "H"), + Pair(90, "I"), + Pair(100, "J"), + Pair(110, "K"), + Pair(120, "L") ) val actualStructure = mutableListOf>() @@ -604,10 +662,18 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(20, "B"), Pair(30, "C"), Pair(40, "D"), - Pair(50, "E"), Pair(60, "F"), Pair(70, "G"), - Pair(80, "H"), Pair(90, "I"), Pair(100, "J"), - Pair(110, "K"), Pair(120, "L"), Pair(130, "M") + Pair(20, "B"), + Pair(30, "C"), + Pair(40, "D"), + Pair(50, "E"), + Pair(60, "F"), + Pair(70, "G"), + Pair(80, "H"), + Pair(90, "I"), + Pair(100, "J"), + Pair(110, "K"), + Pair(120, "L"), + Pair(130, "M") ) val actualStructure = mutableListOf>() @@ -637,10 +703,17 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(10, "A"), Pair(20, "B"), Pair(30, "C"), - Pair(40, "D"), Pair(50, "E"), Pair(60, "F"), - Pair(70, "G"), Pair(80, "H"), Pair(90, "I"), - Pair(100, "J"), Pair(110, "K") + Pair(10, "A"), + Pair(20, "B"), + Pair(30, "C"), + Pair(40, "D"), + Pair(50, "E"), + Pair(60, "F"), + Pair(70, "G"), + Pair(80, "H"), + Pair(90, "I"), + Pair(100, "J"), + Pair(110, "K") ) val actualStructure = mutableListOf>() @@ -670,10 +743,17 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(20, "B"), Pair(30, "C"), Pair(40, "D"), - Pair(50, "E"), Pair(60, "F"), Pair(70, "G"), - Pair(80, "H"), Pair(90, "I"), Pair(100, "J"), - Pair(110, "K"), Pair(120, "L") + Pair(20, "B"), + Pair(30, "C"), + Pair(40, "D"), + Pair(50, "E"), + Pair(60, "F"), + Pair(70, "G"), + Pair(80, "H"), + Pair(90, "I"), + Pair(100, "J"), + Pair(110, "K"), + Pair(120, "L") ) val actualStructure = mutableListOf>() @@ -704,10 +784,18 @@ class AVLTreeTest { val expectedStructure = listOf( - Pair(10, "A"), Pair(20, "B"), Pair(30, "C"), - Pair(40, "D"), Pair(50, "E"), Pair(60, "F"), - Pair(70, "G"), Pair(90, "I"), Pair(100, "J"), - Pair(110, "K"), Pair(120, "L"), Pair(130, "M") + Pair(10, "A"), + Pair(20, "B"), + Pair(30, "C"), + Pair(40, "D"), + Pair(50, "E"), + Pair(60, "F"), + Pair(70, "G"), + Pair(90, "I"), + Pair(100, "J"), + Pair(110, "K"), + Pair(120, "L"), + Pair(130, "M") ) val actualStructure = mutableListOf>() diff --git a/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt index 7488e93..a51af98 100644 --- a/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/AbstractBinaryTreeTest.kt @@ -6,11 +6,10 @@ import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test /** - * This class tests methods implemented in the AbstractTreeClass - * that are common for all its inheritors (BSTree, AVLTree, RBTree). - * BSTree is used in tests as an instance of the AbstractTreeClass. + * This class tests methods implemented in the AbstractTreeClass that are common for all its + * inheritors (BSTree, AVLTree, RBTree). BSTree is used in tests as an instance of the + * AbstractTreeClass. */ - class AbstractBinaryTreeTest { private lateinit var tree: BSTree @@ -76,8 +75,11 @@ class AbstractBinaryTreeTest { val expectedStructure = listOf( - Pair(10, "Misha"), Pair(20, "Moldova"), Pair(30, "Danya"), - Pair(40, "Tatarstan"), Pair(50, "Karim") + Pair(10, "Misha"), + Pair(20, "Moldova"), + Pair(30, "Danya"), + Pair(40, "Tatarstan"), + Pair(50, "Karim") ) val actualStructure = mutableListOf>() @@ -112,7 +114,6 @@ class AbstractBinaryTreeTest { } } - @Nested inner class IsEmptyTests { @@ -151,8 +152,11 @@ class AbstractBinaryTreeTest { val expectedStructure = listOf( - Pair(10, "Misha"), Pair(20, "Moldova"), Pair(30, "Danya"), - Pair(40, "Tatarstan"), Pair(50, "Karim") + Pair(10, "Misha"), + Pair(20, "Moldova"), + Pair(30, "Danya"), + Pair(40, "Tatarstan"), + Pair(50, "Karim") ) val actualStructure = mutableListOf>() @@ -200,8 +204,11 @@ class AbstractBinaryTreeTest { val expectedStructure = listOf( - Pair(10, "Misha"), Pair(20, "Moldova"), Pair(30, "Danya"), - Pair(40, "Tatarstan"), Pair(50, "Karim") + Pair(10, "Misha"), + Pair(20, "Moldova"), + Pair(30, "Danya"), + Pair(40, "Tatarstan"), + Pair(50, "Karim") ) val actualStructure = mutableListOf>() @@ -249,8 +256,11 @@ class AbstractBinaryTreeTest { val expectedStructure = listOf( - Pair(10, "Misha"), Pair(20, "Moldova"), Pair(30, "Danya"), - Pair(40, "Tatarstan"), Pair(50, "Karim") + Pair(10, "Misha"), + Pair(20, "Moldova"), + Pair(30, "Danya"), + Pair(40, "Tatarstan"), + Pair(50, "Karim") ) val actualStructure = mutableListOf>() @@ -339,8 +349,11 @@ class AbstractBinaryTreeTest { val expectedStructure = listOf( - Pair(10, "Misha"), Pair(20, "Moldova"), Pair(30, "Danya"), - Pair(40, "Tatarstan"), Pair(50, "Karim") + Pair(10, "Misha"), + Pair(20, "Moldova"), + Pair(30, "Danya"), + Pair(40, "Tatarstan"), + Pair(50, "Karim") ) val actualStructure = mutableListOf>() diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index 0059054..58969bf 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -2,8 +2,8 @@ package tsl.trees import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test class BSTreeTest { private lateinit var tree: BSTree @@ -36,7 +36,6 @@ class BSTreeTest { val expectedNewValue = "pesik" val actualNewValue = tree.search(10) - assertEquals(expectedReturnValue, actualReturnValue, "wrong value returned") assertEquals(expectedNewValue, actualNewValue, "value didn't change") } @@ -45,10 +44,14 @@ class BSTreeTest { fun `node with the smallest key should be inserted at the most left position`() { tree.insert(5, "pesik") - val expectedStructure = listOf( - Pair(5, "pesik"), Pair(10, "kotik"), Pair(20, "kotlin"), - Pair(30, "java"), Pair(40, "gnomik") - ) + val expectedStructure = + listOf( + Pair(5, "pesik"), + Pair(10, "kotik"), + Pair(20, "kotlin"), + Pair(30, "java"), + Pair(40, "gnomik") + ) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -60,10 +63,14 @@ class BSTreeTest { fun `node with the biggest key should be inserted at the most right position`() { tree.insert(50, "pesik") - val expectedStructure = listOf( - Pair(10, "kotik"), Pair(20, "kotlin"), Pair(30, "java"), - Pair(40, "gnomik"), Pair(50, "pesik") - ) + val expectedStructure = + listOf( + Pair(10, "kotik"), + Pair(20, "kotlin"), + Pair(30, "java"), + Pair(40, "gnomik"), + Pair(50, "pesik") + ) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -75,10 +82,14 @@ class BSTreeTest { fun `node with random key should be inserted at correct position`() { tree.insert(25, "pesik") - val expectedStructure = listOf( - Pair(10, "kotik"), Pair(20, "kotlin"), Pair(25, "pesik"), - Pair(30, "java"), Pair(40, "gnomik") - ) + val expectedStructure = + listOf( + Pair(10, "kotik"), + Pair(20, "kotlin"), + Pair(25, "pesik"), + Pair(30, "java"), + Pair(40, "gnomik") + ) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -143,10 +154,8 @@ class BSTreeTest { tree.insert(50, "pesik") tree.delete(40) - val expectedStructure = listOf( - Pair(10, "kotik"), Pair(20, "kotlin"), - Pair(30, "java"), Pair(50, "pesik") - ) + val expectedStructure = + listOf(Pair(10, "kotik"), Pair(20, "kotlin"), Pair(30, "java"), Pair(50, "pesik")) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) @@ -159,10 +168,8 @@ class BSTreeTest { tree.insert(35, "pesik") tree.delete(30) - val expectedStructure = listOf( - Pair(10, "kotik"), Pair(20, "kotlin"), - Pair(35, "pesik"), Pair(40, "gnomik") - ) + val expectedStructure = + listOf(Pair(10, "kotik"), Pair(20, "kotlin"), Pair(35, "pesik"), Pair(40, "gnomik")) val actualStructure: MutableList> = mutableListOf() for (pair in tree) actualStructure.add(pair) diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index 6e29dc3..ea583b9 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -1,11 +1,10 @@ package tsl.trees +import kotlin.test.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import tsl.nodes.RBNode -import kotlin.test.assertEquals - class RBTreeTest { private lateinit var rbTree: RBTree @@ -14,7 +13,7 @@ class RBTreeTest { fun setup() { rbTree = RBTree() } - + @Nested inner class InsertTests { @@ -105,7 +104,10 @@ class RBTreeTest { inorderedTraversalTree += Pair(key, value) } assertEquals("red", returnValue) - assertEquals(listOf(Pair(18, "white"), Pair(30, "pink"), Pair(45, "yellow")), inorderedTraversalTree) + assertEquals( + listOf(Pair(18, "white"), Pair(30, "pink"), Pair(45, "yellow")), + inorderedTraversalTree + ) } @Test @@ -121,7 +123,10 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals(listOf(Pair(10, "yellow"), Pair(30, "pink"), Pair(40, "white")), inorderedTraversalTree) + assertEquals( + listOf(Pair(10, "yellow"), Pair(30, "pink"), Pair(40, "white")), + inorderedTraversalTree + ) assertEquals("red", returnValue) } @@ -138,7 +143,10 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals(listOf(Pair(18, "red"), Pair(30, "pink"), Pair(35, "yellow")), inorderedTraversalTree) + assertEquals( + listOf(Pair(18, "red"), Pair(30, "pink"), Pair(35, "yellow")), + inorderedTraversalTree + ) assertEquals("white", returnValue) } @@ -154,9 +162,11 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals(listOf(Pair(20, "yellow"), Pair(30, "pink"), Pair(40, "white")), inorderedTraversalTree) + assertEquals( + listOf(Pair(20, "yellow"), Pair(30, "pink"), Pair(40, "white")), + inorderedTraversalTree + ) assertEquals("red", returnValue) - } @Test @@ -172,12 +182,15 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals(listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), inorderedTraversalTree) + assertEquals( + listOf(Pair(20, "pink"), Pair(25, "brown"), Pair(30, "white"), Pair(35, "gold")), + inorderedTraversalTree + ) assertEquals("red", returnValue) } @Test - fun `remove root - sole node in tree`(){ + fun `remove root - sole node in tree`() { rbTree.insert(10, "hihi haha") val returnValue = rbTree.delete(10) assertEquals(null, returnValue) @@ -196,7 +209,10 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals(listOf( Pair(10, "red"), Pair(20, "pink"), Pair(25, "brown"), Pair(35, "gold")), inorderedTraversalTree) + assertEquals( + listOf(Pair(10, "red"), Pair(20, "pink"), Pair(25, "brown"), Pair(35, "gold")), + inorderedTraversalTree + ) assertEquals("white", returnValue) } @@ -213,7 +229,10 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals(listOf(Pair(5, "b"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), inorderedTraversalTree) + assertEquals( + listOf(Pair(5, "b"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), + inorderedTraversalTree + ) assertEquals("d", returnValue) } @@ -262,11 +281,13 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals(listOf(Pair(5, "b"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), inorderedTraversalTree) + assertEquals( + listOf(Pair(5, "b"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), + inorderedTraversalTree + ) assertEquals("f", returnValue) } - @Test fun `right-red sibling`() { rbTree.insert(20, "pink") @@ -280,7 +301,10 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals(listOf(Pair(5, "gold"), Pair(10, "brown"), Pair(15, "red"), Pair(20, "pink")), inorderedTraversalTree) + assertEquals( + listOf(Pair(5, "gold"), Pair(10, "brown"), Pair(15, "red"), Pair(20, "pink")), + inorderedTraversalTree + ) assertEquals("white", returnValue) } @@ -297,7 +321,10 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals(listOf(Pair(5, "gold"), Pair(10, "brown"), Pair(15, "red"), Pair(30, "white")), inorderedTraversalTree) + assertEquals( + listOf(Pair(5, "gold"), Pair(10, "brown"), Pair(15, "red"), Pair(30, "white")), + inorderedTraversalTree + ) assertEquals("pink", returnValue) } @@ -313,9 +340,11 @@ class RBTreeTest { val inorderedTraversalTree: MutableList> = mutableListOf() for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) - } - assertEquals(listOf(Pair(5, "gold"), Pair(10, "brown"), Pair(15, "red"), Pair(20, "pink")), inorderedTraversalTree) + assertEquals( + listOf(Pair(5, "gold"), Pair(10, "brown"), Pair(15, "red"), Pair(20, "pink")), + inorderedTraversalTree + ) assertEquals("white", returnValue) } } From 212a7031fb7f2e6fe0a35de674ebfbd670149eb3 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 1 Apr 2024 12:02:44 +0300 Subject: [PATCH 123/164] test: increase coverage up to 80%, cover more test-cases --- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 107 ++++++++++++++++++-- 1 file changed, 96 insertions(+), 11 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index ea583b9..1f3fd6e 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -1,5 +1,6 @@ package tsl.trees +import com.google.errorprone.annotations.CanIgnoreReturnValue import kotlin.test.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -8,10 +9,12 @@ import tsl.nodes.RBNode class RBTreeTest { private lateinit var rbTree: RBTree + private lateinit var inorderedTraversalTree: MutableList> @BeforeEach fun setup() { rbTree = RBTree() + inorderedTraversalTree = mutableListOf() } @Nested @@ -99,7 +102,7 @@ class RBTreeTest { rbTree.insert(45, "yellow") val returnValue = rbTree.delete(40) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -119,7 +122,7 @@ class RBTreeTest { rbTree.insert(10, "yellow") val returnValue = rbTree.delete(18) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -139,7 +142,7 @@ class RBTreeTest { rbTree.insert(35, "yellow") val returnValue = rbTree.delete(40) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -158,7 +161,7 @@ class RBTreeTest { rbTree.insert(20, "yellow") val returnValue = rbTree.delete(18) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -178,7 +181,7 @@ class RBTreeTest { rbTree.insert(35, "gold") val returnValue = rbTree.delete(10) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -205,7 +208,7 @@ class RBTreeTest { rbTree.insert(35, "gold") val returnValue = rbTree.delete(30) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -216,6 +219,88 @@ class RBTreeTest { assertEquals("white", returnValue) } + @Test + fun `delete lonely black node with red sibling`() { + rbTree.insert(20, "hi") + rbTree.insert(10, "hihi") + rbTree.insert(40, "what") + rbTree.insert(30, "omg") + rbTree.insert(45, "damn") + rbTree.insert(33, "!") + val returnValue = rbTree.delete(10) + + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals( + listOf(Pair(20, "hi"), Pair(30, "omg"), Pair(33, "!"), Pair(40, "what"), Pair(45, "damn")), + inorderedTraversalTree + ) + assertEquals("hihi", returnValue) + } + + + @Test + fun `left-black node with red-right child`() { + rbTree.insert(100, "huh") + rbTree.insert(80, "?") + rbTree.insert(200, "you") + rbTree.insert(60, "meow") + rbTree.insert(90, ".") + rbTree.insert(88, "cat") + val returnValue = rbTree.delete(60) + + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals( + listOf(Pair(80, "?"), Pair(88, "cat"), Pair(90, "."), Pair(100, "huh"), Pair(200, "you")), + inorderedTraversalTree + ) + assertEquals("meow", returnValue) + } + + @Test + fun `black node with zero children and black sibling`() { + rbTree.insert(110, "walking") + rbTree.insert(238, "in") + rbTree.insert(88, "of") + rbTree.insert(233, "phone") + val returnValue = rbTree.delete(233) + rbTree.delete(88) + + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals( + listOf(Pair(110, "walking"), Pair(238, "in")), + inorderedTraversalTree + ) + assertEquals("phone", returnValue) + } + + @Test + fun `left-red node with no children`() { + val rbTree = RBTree() + rbTree.insert(6, "monkey") + rbTree.insert(4, "you") + rbTree.insert(7, "dog") + rbTree.insert(2, "cat") + rbTree.insert(5, "dog") + rbTree.insert(1, "cat") + + val returnValue = rbTree.delete(7) + + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals( + listOf(Pair(1, "cat"), Pair(2, "cat"), Pair(4, "you"), Pair(5, "dog"), Pair(6, "monkey")), + inorderedTraversalTree + ) + assertEquals("dog", returnValue) + } + @Test fun `fix after deletion second case`() { rbTree.insert(15, "c") @@ -225,7 +310,7 @@ class RBTreeTest { rbTree.insert(6, "d") val returnValue = rbTree.delete(6) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -277,7 +362,7 @@ class RBTreeTest { rbTree.insert(4, "f") val returnValue = rbTree.delete(4) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -297,7 +382,7 @@ class RBTreeTest { rbTree.insert(5, "gold") val returnValue = rbTree.delete(30) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -317,7 +402,7 @@ class RBTreeTest { rbTree.insert(5, "gold") val returnValue = rbTree.delete(20) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -337,7 +422,7 @@ class RBTreeTest { rbTree.insert(5, "gold") val returnValue = rbTree.delete(30) - val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } From 33799ee0998b7ae0c451aac0033411a982497f5b Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 1 Apr 2024 12:05:00 +0300 Subject: [PATCH 124/164] refactor: apply formatter to new tests --- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 31 +++++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index 1f3fd6e..fe9e8f2 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -1,6 +1,5 @@ package tsl.trees -import com.google.errorprone.annotations.CanIgnoreReturnValue import kotlin.test.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -233,13 +232,18 @@ class RBTreeTest { inorderedTraversalTree += Pair(key, value) } assertEquals( - listOf(Pair(20, "hi"), Pair(30, "omg"), Pair(33, "!"), Pair(40, "what"), Pair(45, "damn")), + listOf( + Pair(20, "hi"), + Pair(30, "omg"), + Pair(33, "!"), + Pair(40, "what"), + Pair(45, "damn") + ), inorderedTraversalTree ) assertEquals("hihi", returnValue) } - @Test fun `left-black node with red-right child`() { rbTree.insert(100, "huh") @@ -254,7 +258,13 @@ class RBTreeTest { inorderedTraversalTree += Pair(key, value) } assertEquals( - listOf(Pair(80, "?"), Pair(88, "cat"), Pair(90, "."), Pair(100, "huh"), Pair(200, "you")), + listOf( + Pair(80, "?"), + Pair(88, "cat"), + Pair(90, "."), + Pair(100, "huh"), + Pair(200, "you") + ), inorderedTraversalTree ) assertEquals("meow", returnValue) @@ -272,10 +282,7 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals( - listOf(Pair(110, "walking"), Pair(238, "in")), - inorderedTraversalTree - ) + assertEquals(listOf(Pair(110, "walking"), Pair(238, "in")), inorderedTraversalTree) assertEquals("phone", returnValue) } @@ -295,7 +302,13 @@ class RBTreeTest { inorderedTraversalTree += Pair(key, value) } assertEquals( - listOf(Pair(1, "cat"), Pair(2, "cat"), Pair(4, "you"), Pair(5, "dog"), Pair(6, "monkey")), + listOf( + Pair(1, "cat"), + Pair(2, "cat"), + Pair(4, "you"), + Pair(5, "dog"), + Pair(6, "monkey") + ), inorderedTraversalTree ) assertEquals("dog", returnValue) From b546f37fa06899a8f42ae58f6fe075be523647f9 Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Mon, 1 Apr 2024 12:25:08 +0300 Subject: [PATCH 125/164] docs: add link to contribution rules --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f0e4463..c2417c0 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,8 @@ There are also other helpful methods: ## Contributing -If you have found a bug, or want to propose some useful feature for our project, please do the following: +If you have found a bug, or want to propose some useful feature for our project, please firstly read our [Contribution Rules](https://github.com/spbu-coding-2023/trees-2/blob/main/CONTRIBUTING.md) and +do the following: 1. Fork the Project 2. Create your Feature Branch (git checkout -b feature/myFeature) 3. Commit your Changes (git commit -m 'add some feature') From 3ece688c639a6ffddb983643c4ae4a808b7927d1 Mon Sep 17 00:00:00 2001 From: qrutyy <64466788+qrutyy@users.noreply.github.com> Date: Mon, 1 Apr 2024 13:47:47 +0300 Subject: [PATCH 126/164] docs: add link for license file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c2417c0..43f9818 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ do the following: ## License -Distributed under the MIT License. See LICENSE.md for more information. +Distributed under the [MIT License](https://github.com/spbu-coding-2023/trees-2/blob/main/LICENSE.md). From f9643bdcf1f1dd41d9b8466ea0a1bf2110f42e44 Mon Sep 17 00:00:00 2001 From: Karim Shakirov Date: Mon, 1 Apr 2024 17:49:58 +0300 Subject: [PATCH 127/164] docs: add information about traversal order of iterator in readme Add information that iterator implements inorder traversal, delete example of creation of iterator manually. --- README.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 43f9818..4091404 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,9 @@ val myValue2 = myTree.delete(myKey) println(myValue1 == myValue2) // true ``` -All trees are iterable by Pair(key, value), so they can be used in a for loop: +All trees are iterable by Pair(key, value), so they can be used in a for loop. + +Iterator implements inorder traversal (every next key is greater than the previous). ``` for ((key, value) in myTree) { @@ -58,15 +60,6 @@ for ((key, value) in myTree) { myTree.forEach { println(it) } // prints every pair ``` -You can create your own iterator manually: - -``` -val treeIterator = myTree.iterator() - -while (treeIterator.hasNext()) - println(treeIterator.next()) -``` - There are also other helpful methods: - `clear()` From 804f4d943e53ab4f6e68af04286f48589760ae57 Mon Sep 17 00:00:00 2001 From: Daniel Vlasenco Date: Mon, 1 Apr 2024 21:31:52 +0300 Subject: [PATCH 128/164] docs(readme): change About the Project, add more badges, make refactor --- README.md | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 4091404..c08049e 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,30 @@ # forest-group [![Kotlin 1.9.23][kotlin_img]][kotlin_releases_url] +[![Tests passing][tests_passing_img]][tests_workflow_url] [![License][license_img]][repo_license_url] +[![CodeFactor][codefactor_img]][codefactor_url] `forest-group` is a library that lets you easily create and use [Binary search trees](https://en.wikipedia.org/wiki/Binary_search_tree), [AVL trees](https://en.wikipedia.org/wiki/AVL_tree) and [Red-black trees](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) in your applications! - - ## Table of contents + - [About the project](#about-the-project) - [Usage](#usage) - [Contributing](#contributing) - [License](#license) - [Authors](#authors) - - ## About The Project -Tree data structure is well-known for its average logarithmic operations time. However, there are a lot of tree implementations that are strict to data types that can be stored. -There is no problem - *just use keys to store anything you want!* +Trees are well-known for their speed and efficiency. + +But there are lots of tree implementations that are strict to data types that can be stored. +The solution is easy - *just use keys to store anything you want!* ## Usage + To create a tree, pass the key type and your stored data type to a generic. *Note that your key should implement Comparable type.* ``` @@ -67,10 +69,9 @@ There are also other helpful methods: - `getMinKey()` - `getMaxKey()` - - ## Contributing -If you have found a bug, or want to propose some useful feature for our project, please firstly read our [Contribution Rules](https://github.com/spbu-coding-2023/trees-2/blob/main/CONTRIBUTING.md) and + +If you have found a bug, or want to propose some useful feature for our project, please firstly read our [Contribution Rules][contribute_rules_url] and do the following: 1. Fork the Project 2. Create your Feature Branch (git checkout -b feature/myFeature) @@ -78,29 +79,30 @@ do the following: 4. Push to the Branch (git push origin feature/myFeature) 5. Open a Pull Request - - ## License -Distributed under the [MIT License](https://github.com/spbu-coding-2023/trees-2/blob/main/LICENSE.md). - +Distributed under the [MIT License][repo_license_url]. ## Authors + - [Shakirov Karim](https://github.com/kar1mgh) - [Vlasenco Daniel](https://github.com/spisladqo) - [Gavrilenko Mike](https://github.com/qrutyy) - - -[license_img]: https://img.shields.io/badge/license-MIT-green -[kotlin_img]: https://img.shields.io/badge/kotlin%201.9.23-magenta +[kotlin_img]: https://img.shields.io/badge/Kotlin-%201.9.23-magenta +[tests_passing_img]: https://img.shields.io/badge/tests-Passing-green +[license_img]: https://img.shields.io/badge/license-MIT-blue +[codefactor_img]: https://www.codefactor.io/repository/github/spbu-coding-2023/trees-2/badge - + +[tests_workflow_url]: https://github.com/spbu-coding-2023/trees-2/actions/workflows/test.yml [repo_license_url]: https://github.com/spbu-coding-2023/trees-2/blob/main/LICENSE.md +[contribute_rules_url]: https://github.com/spbu-coding-2023/trees-2/blob/main/CONTRIBUTING.md - + [kotlin_releases_url]: https://kotlinlang.org/docs/releases.html#release-details +[codefactor_url]: https://www.codefactor.io/repository/github/spbu-coding-2023/trees-2 From ac891e271ab4ca7c5c4c033cbb0ab0401d0db162 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 1 Apr 2024 21:58:58 +0300 Subject: [PATCH 129/164] fix: remove problematic line that get rid of left-side node --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index a4f09d0..c96e28e 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -276,7 +276,6 @@ class RBTree, V> : AbstractBinaryTree>() { if ( leftChild.parent?.leftChild == nodeToRotate || leftChild.parent?.leftChild == null ) { - leftChild.parent?.leftChild?.leftChild = null leftChild.parent?.leftChild = leftChild } else { leftChild.parent?.rightChild = leftChild From 4b805c8288c4260295ce00b84d682f7704a190ea Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:01:28 +0300 Subject: [PATCH 130/164] test(refactor): add another test case for delete black node with two children, refactor a little --- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 80 ++++++++++++++------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index fe9e8f2..469d057 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -6,6 +6,13 @@ import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import tsl.nodes.RBNode +/* + * Some standarts and "variables" that were used for decreasing the amount of words) + * S - sibling of deleting node + * V - deleting node + * U - node that will take place of deleting one + */ + class RBTreeTest { private lateinit var rbTree: RBTree private lateinit var inorderedTraversalTree: MutableList> @@ -62,7 +69,7 @@ class RBTreeTest { inner class DeleteTests { @Test - fun `successful delete (return value)`() { + fun `successful delete (return value check)`() { rbTree.insert(30, "pink") rbTree.insert(20, "white") rbTree.insert(40, "red") @@ -72,7 +79,7 @@ class RBTreeTest { } @Test - fun `unsuccessful delete (return value)`() { + fun `unsuccessful delete (return value check)`() { rbTree.insert(30, "pink") rbTree.insert(20, "white") rbTree.insert(40, "red") @@ -93,7 +100,7 @@ class RBTreeTest { } @Test - fun `right-right case`() { + fun `delete right-right case`() { // s is right child of its parent and both children of s are red rbTree.insert(30, "pink") rbTree.insert(18, "white") @@ -101,7 +108,6 @@ class RBTreeTest { rbTree.insert(45, "yellow") val returnValue = rbTree.delete(40) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -113,7 +119,7 @@ class RBTreeTest { } @Test - fun `left-left case`() { + fun `delete left-left case`() { // s is left child of its parent and both children of s are red rbTree.insert(30, "pink") rbTree.insert(40, "white") @@ -121,7 +127,6 @@ class RBTreeTest { rbTree.insert(10, "yellow") val returnValue = rbTree.delete(18) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -133,7 +138,7 @@ class RBTreeTest { } @Test - fun `right-left case`() { + fun `delete right-left case`() { // s is right child of its parent and r is left child of s rbTree.insert(30, "pink") rbTree.insert(40, "white") @@ -141,7 +146,6 @@ class RBTreeTest { rbTree.insert(35, "yellow") val returnValue = rbTree.delete(40) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -153,14 +157,14 @@ class RBTreeTest { } @Test - fun `left-right case`() { + fun `delete left-right case`() { + // s is left child of its parent and r is right child rbTree.insert(30, "pink") rbTree.insert(40, "white") rbTree.insert(18, "red") rbTree.insert(20, "yellow") val returnValue = rbTree.delete(18) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -172,7 +176,7 @@ class RBTreeTest { } @Test - fun `left-red sibling`() { + fun `delete left-red sibling`() { rbTree.insert(20, "pink") rbTree.insert(30, "white") rbTree.insert(10, "red") @@ -180,7 +184,6 @@ class RBTreeTest { rbTree.insert(35, "gold") val returnValue = rbTree.delete(10) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -192,7 +195,7 @@ class RBTreeTest { } @Test - fun `remove root - sole node in tree`() { + fun `delete root - sole node in tree`() { rbTree.insert(10, "hihi haha") val returnValue = rbTree.delete(10) assertEquals(null, returnValue) @@ -207,7 +210,6 @@ class RBTreeTest { rbTree.insert(35, "gold") val returnValue = rbTree.delete(30) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -226,8 +228,8 @@ class RBTreeTest { rbTree.insert(30, "omg") rbTree.insert(45, "damn") rbTree.insert(33, "!") - val returnValue = rbTree.delete(10) + val returnValue = rbTree.delete(10) for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -245,15 +247,15 @@ class RBTreeTest { } @Test - fun `left-black node with red-right child`() { + fun `delete left-black node with red-right child`() { rbTree.insert(100, "huh") rbTree.insert(80, "?") rbTree.insert(200, "you") rbTree.insert(60, "meow") rbTree.insert(90, ".") rbTree.insert(88, "cat") - val returnValue = rbTree.delete(60) + val returnValue = rbTree.delete(60) for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -271,14 +273,14 @@ class RBTreeTest { } @Test - fun `black node with zero children and black sibling`() { + fun `delete black node with zero children and black sibling`() { rbTree.insert(110, "walking") rbTree.insert(238, "in") rbTree.insert(88, "of") rbTree.insert(233, "phone") + val returnValue = rbTree.delete(233) rbTree.delete(88) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -287,7 +289,7 @@ class RBTreeTest { } @Test - fun `left-red node with no children`() { + fun `delete left-red node with no children`() { val rbTree = RBTree() rbTree.insert(6, "monkey") rbTree.insert(4, "you") @@ -297,7 +299,6 @@ class RBTreeTest { rbTree.insert(1, "cat") val returnValue = rbTree.delete(7) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -314,6 +315,34 @@ class RBTreeTest { assertEquals("dog", returnValue) } + @Test + fun `delete black node with two children (left subtree & right leaf)`() { + val rbTree = RBTree() + rbTree.insert(200, "Im") + rbTree.insert(160, "gonna make") + rbTree.insert(400, "him an") + rbTree.insert(120, "offer") + rbTree.insert(180, "he") + rbTree.insert(130, "cant refuse") + + val returnValue = rbTree.delete(200) + val inorderedTraversalTree: MutableList> = mutableListOf() + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals( + listOf( + Pair(120, "offer"), + Pair(130, "cant refuse"), + Pair(160, "gonna make"), + Pair(180, "he"), + Pair(400, "him an") + ), + inorderedTraversalTree + ) + assertEquals("Im", returnValue) + } + @Test fun `fix after deletion second case`() { rbTree.insert(15, "c") @@ -323,7 +352,6 @@ class RBTreeTest { rbTree.insert(6, "d") val returnValue = rbTree.delete(6) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -346,6 +374,7 @@ class RBTreeTest { // deletion to trigger fixAfterDeletion with Case 3 conditions rbTree.delete(10) + val currentSibling = rbTree.root?.rightChild assertEquals(null, currentSibling?.rightChild?.color) assertEquals(RBNode.Color.Black, currentSibling?.color) @@ -362,6 +391,7 @@ class RBTreeTest { rbTree.insert(17, "Z") // deletion to trigger fixAfterDeletion with Case 3 conditions rbTree.delete(10) + val currentSibling = rbTree.root?.rightChild assertEquals(RBNode.Color.Black, currentSibling?.color) } @@ -375,7 +405,6 @@ class RBTreeTest { rbTree.insert(4, "f") val returnValue = rbTree.delete(4) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -387,7 +416,7 @@ class RBTreeTest { } @Test - fun `right-red sibling`() { + fun `delete right-red sibling`() { rbTree.insert(20, "pink") rbTree.insert(30, "white") rbTree.insert(15, "red") @@ -395,7 +424,6 @@ class RBTreeTest { rbTree.insert(5, "gold") val returnValue = rbTree.delete(30) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -415,7 +443,6 @@ class RBTreeTest { rbTree.insert(5, "gold") val returnValue = rbTree.delete(20) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } @@ -435,7 +462,6 @@ class RBTreeTest { rbTree.insert(5, "gold") val returnValue = rbTree.delete(30) - for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } From af95a83abdd4e7f99da5de8c21a546d03c8013d7 Mon Sep 17 00:00:00 2001 From: Daniel Vlasenco Date: Mon, 1 Apr 2024 23:21:05 +0300 Subject: [PATCH 131/164] docs: correct copyright date in LICENSE.md --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index 34355b9..5d7d8c4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License - Copyright (c) 2024-present Gavrilenko Mike, Shakirov Karim, Vlasenco Daniel + Copyright (c) 2024 Gavrilenko Mike, Shakirov Karim, Vlasenco Daniel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 9169723cfc7cff43ef5f04494fc79575f1202d55 Mon Sep 17 00:00:00 2001 From: Karim Shakirov Date: Tue, 2 Apr 2024 21:17:07 +0300 Subject: [PATCH 132/164] docs: add code highlighting in README.md --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c08049e..86798f3 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,13 @@ The solution is easy - *just use keys to store anything you want!* To create a tree, pass the key type and your stored data type to a generic. *Note that your key should implement Comparable type.* -``` +```kotlin val myTree = AVLTree() ``` You now can simply insert and replace values in your tree: -``` +```kotlin myTree.insert(keyA, "Something important") val oldValue = myTree.insert(keyA, "Something more important") @@ -41,7 +41,7 @@ val oldValue = myTree.insert(keyA, "Something more important") You can also search for values and delete values from tree by keys: -``` +```kotlin val myValue1 = myTree.search(myKey) val myValue2 = myTree.delete(myKey) @@ -53,7 +53,7 @@ All trees are iterable by Pair(key, value), so they can be used in a for loop. Iterator implements inorder traversal (every next key is greater than the previous). -``` +```kotlin for ((key, value) in myTree) { keysList.add(key) valuesList.add(value) @@ -89,6 +89,10 @@ Distributed under the [MIT License][repo_license_url]. - [Vlasenco Daniel](https://github.com/spisladqo) - [Gavrilenko Mike](https://github.com/qrutyy) +_______________________________ + +[*Java gnomik*](https://ibb.co/54hJVd2) + [kotlin_img]: https://img.shields.io/badge/Kotlin-%201.9.23-magenta From 6fca42edf8a405ab08988db1e22796b347e3e711 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 3 Apr 2024 01:52:38 +0300 Subject: [PATCH 133/164] gradle: update kotlin formatter settings and tasks, remove unnecessary comments --- lib/build.gradle.kts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 15df3d3..f83af36 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -6,9 +6,6 @@ */ plugins { - // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. -// alias(libs.plugins.jvm) - // Apply the java-library plugin for API and implementation separation. `java-library` @@ -71,18 +68,13 @@ tasks.jacocoTestReport { } ktfmt { - // KotlinLang style - 4 space indentation - From kotlinlang.org/docs/coding-conventions.html - kotlinLangStyle() - // blockIndent is the indent size used when a new block is opened, in spaces. + googleStyle() + blockIndent.set(4) - // continuationIndent is the indent size used when a line is broken because it's too + continuationIndent.set(4) - // Whether ktfmt should automatically add/remove trailing commas. - //manageTrailingCommas.set(true) current release doesn't support this feature removeUnusedImports.set(true) } -tasks.register("checkFormat") { dependsOn(tasks.ktfmtCheck) } - tasks.register("format") { dependsOn(tasks.ktfmtFormat) } From 44bb72e36be3979d2be88a62d024ea157d17b40e Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 3 Apr 2024 02:19:07 +0300 Subject: [PATCH 134/164] ci: change formatter workflow to reformat and push modified files --- .github/workflows/formatter.yml | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/formatter.yml b/.github/workflows/formatter.yml index 380b2c0..0642d04 100644 --- a/.github/workflows/formatter.yml +++ b/.github/workflows/formatter.yml @@ -1,15 +1,21 @@ -name: Verify formatting +name: Formatter on: workflow_dispatch: + push: + branches: [ "main" ] pull_request: + branches: [ "main" ] + jobs: build: runs-on: ubuntu-latest - + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} - name: Set up JDK 17 uses: actions/setup-java@v3 @@ -17,5 +23,19 @@ jobs: java-version: '17' distribution: zulu - - name: Run formatting test - run: ./gradlew checkFormat + - name: Run kotlin formatter + run: ./gradlew format + + - name: Check for modified files + id: git-check + run: echo "modified=$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT + + - name: Push changes + if: steps.git-check.outputs.modified == 'true' + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' + git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} + git commit -am "Refactor changes" + + git push \ No newline at end of file From 5bca40cc860f37b75c9e2bd2bb80824226da81d2 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 3 Apr 2024 02:36:25 +0300 Subject: [PATCH 135/164] ci: change the message of automatic formatter' commits --- .github/workflows/formatter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/formatter.yml b/.github/workflows/formatter.yml index 0642d04..3e2bdd4 100644 --- a/.github/workflows/formatter.yml +++ b/.github/workflows/formatter.yml @@ -36,6 +36,6 @@ jobs: git config --global user.name 'github-actions[bot]' git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} - git commit -am "Refactor changes" + git commit -am "Reformat changes" git push \ No newline at end of file From 28b08dccaa6ba11c16d1553a41e81e44c82c5806 Mon Sep 17 00:00:00 2001 From: Karim Shakirov Date: Wed, 3 Apr 2024 11:34:07 +0300 Subject: [PATCH 136/164] docs: add BSTree an RBTree signatures in README.md Added signatures of BSTree and RBTree? changed names in the usage examples. --- README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 86798f3..d21e156 100644 --- a/README.md +++ b/README.md @@ -28,23 +28,27 @@ The solution is easy - *just use keys to store anything you want!* To create a tree, pass the key type and your stored data type to a generic. *Note that your key should implement Comparable type.* ```kotlin -val myTree = AVLTree() +val myBSTree = BSTree() + +val myAVLTree = AVLTree() + +val myRBTree = RBTree() ``` You now can simply insert and replace values in your tree: ```kotlin -myTree.insert(keyA, "Something important") +myBSTree.insert(keyA, "Something important") -val oldValue = myTree.insert(keyA, "Something more important") +val myBSTree = myTree.insert(keyA, "Something more important") ``` You can also search for values and delete values from tree by keys: ```kotlin -val myValue1 = myTree.search(myKey) +val myValue1 = myAVLTree.search(myKey) -val myValue2 = myTree.delete(myKey) +val myValue2 = myAVLTree.delete(myKey) println(myValue1 == myValue2) // true ``` @@ -54,12 +58,12 @@ All trees are iterable by Pair(key, value), so they can be used in a for loop. Iterator implements inorder traversal (every next key is greater than the previous). ```kotlin -for ((key, value) in myTree) { +for ((key, value) in myRBTree) { keysList.add(key) valuesList.add(value) } -myTree.forEach { println(it) } // prints every pair +myRBTree.forEach { println(it) } // prints every pair ``` There are also other helpful methods: From b0a98719f50e4015907758c371212b2d4de0aee7 Mon Sep 17 00:00:00 2001 From: Karim Shakirov Date: Wed, 3 Apr 2024 11:40:03 +0300 Subject: [PATCH 137/164] docs: fix tree name in README.md Fixed unchanged tree name rom "myTree" to "myBSTree" in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d21e156..51c9a58 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ You now can simply insert and replace values in your tree: ```kotlin myBSTree.insert(keyA, "Something important") -val myBSTree = myTree.insert(keyA, "Something more important") +val myBSTree = myBSTree.insert(keyA, "Something more important") ``` You can also search for values and delete values from tree by keys: From a9aa19d55c1b44a74e7d436e939b30cf36f81bba Mon Sep 17 00:00:00 2001 From: Daniel Vlasenco Date: Thu, 4 Apr 2024 20:11:02 +0300 Subject: [PATCH 138/164] docs: update usage and contributing blocks in README.md --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 51c9a58..9684001 100644 --- a/README.md +++ b/README.md @@ -38,17 +38,19 @@ val myRBTree = RBTree() You now can simply insert and replace values in your tree: ```kotlin -myBSTree.insert(keyA, "Something important") +val myKey = 10 -val myBSTree = myBSTree.insert(keyA, "Something more important") +myBSTree.insert(myKey, "Something important") + +val replacedValue = myBSTree.insert(myKey, "Something more important") ``` You can also search for values and delete values from tree by keys: ```kotlin -val myValue1 = myAVLTree.search(myKey) +val myValue1 = myBSTree.search(myKey) -val myValue2 = myAVLTree.delete(myKey) +val myValue2 = myBSTree.delete(myKey) println(myValue1 == myValue2) // true ``` @@ -78,9 +80,9 @@ There are also other helpful methods: If you have found a bug, or want to propose some useful feature for our project, please firstly read our [Contribution Rules][contribute_rules_url] and do the following: 1. Fork the Project -2. Create your Feature Branch (git checkout -b feature/myFeature) +2. Create your Feature Branch (git checkout -b feature/my-feature) 3. Commit your Changes (git commit -m 'add some feature') -4. Push to the Branch (git push origin feature/myFeature) +4. Push to the Branch (git push origin feature/my-feature) 5. Open a Pull Request ## License @@ -95,7 +97,7 @@ Distributed under the [MIT License][repo_license_url]. _______________________________ -[*Java gnomik*](https://ibb.co/54hJVd2) +[*Java gnomik*][java_gnomik_url] @@ -114,3 +116,4 @@ _______________________________ [kotlin_releases_url]: https://kotlinlang.org/docs/releases.html#release-details [codefactor_url]: https://www.codefactor.io/repository/github/spbu-coding-2023/trees-2 +[java_gnomik_url]: https://ibb.co/54hJVd2 From 1b28b625b0a43a8229b1507859e235e3abfac487 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Fri, 5 Apr 2024 20:32:39 +0300 Subject: [PATCH 139/164] fix: remove unnecessary check, switch some lines, dlete methods documentation and refactor a lil --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 89 ++++++------------------- 1 file changed, 20 insertions(+), 69 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index c96e28e..e4649d4 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -4,19 +4,14 @@ import tsl.nodes.RBNode class RBTree, V> : AbstractBinaryTree>() { - /** - * Inserts a node with provided value and key.\. - * - * @param key The key of the node to be deleted.\. - * @return Null if 'insert' method haven't replaced any value. Return value if there was some - * replace.\. - */ + // @return Null if 'insert' method haven't replaced any value. Return value if there was some replace + public override fun insert(key: K, value: V): V? { val newNode = RBNode(key, value) if (root == null) { root = RBNode(key, value) - balanceNode(newNode) + fixAfterInsertion(newNode) return null } @@ -25,11 +20,11 @@ class RBTree, V> : AbstractBinaryTree>() { // traverse the tree to find the insertion point (node) while (currentNode != null) { - if (currentNode.key > newNode.key) { + if (currentNode.key >= newNode.key) { if (currentNode.leftChild == null) { currentNode.leftChild = newNode newNode.parent = currentNode - balanceNode(newNode) + fixAfterInsertion(newNode) break } currentNode = currentNode.leftChild @@ -37,28 +32,16 @@ class RBTree, V> : AbstractBinaryTree>() { if (currentNode.rightChild == null) { currentNode.rightChild = newNode newNode.parent = currentNode - balanceNode(newNode) + fixAfterInsertion(newNode) break } currentNode = currentNode.rightChild - } else { - currentNode.key = key - balanceNode(newNode) - break } } return returnValue } - /** - * This function is used to balance the red-black tree node after insertion. It checks the - * colors of the parent and grandparent nodes to determine the rotations needed for balancing. - * The function alternates between left and right rotations to maintain the balance of the tree. - * The root variable is updated to maintain the correct root node after balancing. - * - * @param RBNode-typed node. - */ - private fun balanceNode(node: RBNode?) { + private fun fixAfterInsertion(node: RBNode?) { if (node?.parent == null) root.also { it?.color = RBNode.Color.Black } var newNode = node var uncle: RBNode? @@ -67,7 +50,7 @@ class RBTree, V> : AbstractBinaryTree>() { while (newNode?.parent?.color == RBNode.Color.Red) { val currentParent: RBNode? = newNode.parent val currentGrandParent: RBNode? = currentParent?.parent - if (newNode.parent == newNode.parent?.parent?.leftChild) { + if (currentParent == currentGrandParent?.leftChild) { uncle = currentParent?.parent?.rightChild if (uncle?.color == RBNode.Color.Red) { currentParent?.color = RBNode.Color.Black @@ -117,27 +100,18 @@ class RBTree, V> : AbstractBinaryTree>() { root = newRoot } - /** - * Returns something and sets up the root depending on the result of deleteNode.\. - * - * @param key The key of the node to be deleted\. - * @return Value if delete was successful and null if it isn't.\. - */ + // @return Value if delete was successful and null if it isn't. + public override fun delete(key: K): V? { val deletingNodeValue = search(key) + if (deletingNodeValue == null) return null // in case we do not found the deletinnode root = deleteNode(key) - if (root != null) return deletingNodeValue root?.color = RBNode.Color.Black // ensure the root is black after deletion - return null + return deletingNodeValue } - /** - * Deletes a node with the specified key from the Red\-Black Tree\. If the node with the key is - * found, it is removed from the tree and the tree is rebalanced if necessary\. - * - * @param key The key of the node to be deleted\. - * @return The new root node of the Red\-Black Tree after deletion\. - */ + // @return The new root node of the Red\-Black Tree after deletion\. + private fun deleteNode( key: K, ): RBNode? { @@ -187,15 +161,8 @@ class RBTree, V> : AbstractBinaryTree>() { return newRoot } - /** - * This function is used to fix the red-black tree structure after a node deletion to ensure - * that the red-black properties of the tree are maintained. It iterates through the nodes and - * performs rotations and recoloring based on the cases derived from the sibling and its - * children's colors to balance the tree. - * - * @param currentNode The node that needs fixing after deletion - * @return The root of the tree after fixing the structure to maintain the red-black properties - */ + // @return The root of the tree after fixing the structure to maintain the red-black properties + private fun fixAfterDeletion(currentDeletingNode: RBNode?): RBNode? { var fixedRoot = root var currentNode = currentDeletingNode @@ -210,14 +177,7 @@ class RBTree, V> : AbstractBinaryTree>() { currentNode.parent?.color = RBNode.Color.Red fixedRoot = rotateLeft(currentNode.parent, fixedRoot) } else { - if (currentSibling?.rightChild?.color == RBNode.Color.Black) { - // 3rd case "like 2nd but the left child is red" - (currentSibling.leftChild)?.color = RBNode.Color.Black - currentSibling.color = RBNode.Color.Red - fixedRoot = rotateRight(currentSibling, fixedRoot) - currentSibling = currentNode.parent?.rightChild - } - // 4th case "black currentSibling, right child is black" + // 2nd case "black currentSibling, right child is black" currentSibling?.color = currentNode.parent?.color ?: RBNode.Color.Black currentNode.parent?.color = RBNode.Color.Black currentSibling?.rightChild?.color = RBNode.Color.Black @@ -232,14 +192,7 @@ class RBTree, V> : AbstractBinaryTree>() { currentNode.parent?.color = RBNode.Color.Red fixedRoot = rotateRight(currentNode.parent, fixedRoot) } else { - // 3rd case "like 2nd but the right child is red" - if (currentSibling?.leftChild?.color == RBNode.Color.Black) { - currentSibling.rightChild?.color = RBNode.Color.Black - currentSibling.color = RBNode.Color.Red - fixedRoot = rotateLeft(currentSibling, fixedRoot) - currentSibling = currentNode.parent?.leftChild - } - // 4th case "black currentSibling, left child is red" + // 2nd case "black currentSibling, left child is red" currentSibling?.color = currentNode.parent?.color ?: RBNode.Color.Black currentNode.parent?.color = RBNode.Color.Black currentSibling?.leftChild?.color = RBNode.Color.Black @@ -248,7 +201,7 @@ class RBTree, V> : AbstractBinaryTree>() { } } } - balanceNode(fixedRoot) + fixAfterInsertion(fixedRoot) currentNode?.color = RBNode.Color.Black return fixedRoot } @@ -273,9 +226,7 @@ class RBTree, V> : AbstractBinaryTree>() { } if (leftChild?.parent != null) { - if ( - leftChild.parent?.leftChild == nodeToRotate || leftChild.parent?.leftChild == null - ) { + if (leftChild.parent?.leftChild == nodeToRotate) { leftChild.parent?.leftChild = leftChild } else { leftChild.parent?.rightChild = leftChild From 7f8f2818d5a67d637fc50301c613968d5b3ccca7 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Fri, 5 Apr 2024 20:41:04 +0300 Subject: [PATCH 140/164] test: add new RB tree tests (for negative numbers, duplicates and random tests) --- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 97 ++++++++++++++++----- 1 file changed, 77 insertions(+), 20 deletions(-) diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index 469d057..1ef9e9f 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -5,6 +5,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import tsl.nodes.RBNode +import kotlin.random.Random /* * Some standarts and "variables" that were used for decreasing the amount of words) @@ -63,6 +64,18 @@ class RBTreeTest { assertEquals(null, returnValue) assertEquals(20, rbTree.root?.leftChild?.rightChild?.key) } + + @Test + fun `balance negative numbers`() { + val rbTree = RBTree() + rbTree.insert(-1, "a") + rbTree.insert(1, "a") + rbTree.insert(0, "a") + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals(listOf(Pair(-1, "a"), Pair(0, "a"), Pair(1, "a")), inorderedTraversalTree) + } } @Nested @@ -198,7 +211,7 @@ class RBTreeTest { fun `delete root - sole node in tree`() { rbTree.insert(10, "hihi haha") val returnValue = rbTree.delete(10) - assertEquals(null, returnValue) + assertEquals("hihi haha", returnValue) } @Test @@ -343,25 +356,6 @@ class RBTreeTest { assertEquals("Im", returnValue) } - @Test - fun `fix after deletion second case`() { - rbTree.insert(15, "c") - rbTree.insert(10, "a") - rbTree.insert(20, "e") - rbTree.insert(5, "b") - rbTree.insert(6, "d") - - val returnValue = rbTree.delete(6) - for ((key, value) in rbTree) { - inorderedTraversalTree += Pair(key, value) - } - assertEquals( - listOf(Pair(5, "b"), Pair(10, "a"), Pair(15, "c"), Pair(20, "e")), - inorderedTraversalTree - ) - assertEquals("d", returnValue) - } - @Test fun `fix after deletion case 3 right child red`() { rbTree.insert(15, "C") @@ -471,6 +465,69 @@ class RBTreeTest { ) assertEquals("white", returnValue) } + + @Test + fun `zero root deletion`() { + rbTree.insert(0, "a") + + val returnValue = rbTree.delete(0) + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals(listOf(), inorderedTraversalTree) + assertEquals("a", returnValue) + } + + @Test + fun `duplicates test`() { + rbTree.insert(-1, "a") + rbTree.insert(1, "a") + rbTree.insert(0, "a") + rbTree.insert(0, "b") + + val returnValue = rbTree.delete(0) + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals(listOf(Pair(-1, "a"), Pair(0, "a"), Pair(1, "a")), inorderedTraversalTree) + assertEquals("b", returnValue) + } + + @Test + fun `zero delete case`() { + rbTree.insert(-1, "a") + rbTree.insert(1, "a") + + val returnValue = rbTree.delete(0) + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals(listOf(Pair(-1, "a"), Pair(1, "a")), inorderedTraversalTree) + assertEquals(null, returnValue) + } + + @Test + fun `random tests`() { + val generator = Random(12) + + val randomKeys = mutableListOf() + for (i in 1..70) { + val randomValue = generator.nextInt() + randomKeys.add(randomValue) + rbTree.insert(randomValue, "") // this sets up key and default value + } + + val len = randomKeys.size + for (index in 1..len) { + val value: Int = randomKeys.removeLast() + rbTree.delete(value) + } + + for ((key, value) in rbTree) { + inorderedTraversalTree += Pair(key, value) + } + assertEquals(listOf(), inorderedTraversalTree) + } } @Nested From 13c70499136287a4b26587ff0bab41a038872600 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sat, 6 Apr 2024 10:56:15 +0300 Subject: [PATCH 141/164] fix: deletion of root node in AVL tree and BS tree classes --- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 3 ++- lib/src/main/kotlin/tsl/trees/BSTree.kt | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index 36cb8b4..ee6f741 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -115,6 +115,7 @@ class AVLTree, V> : AbstractBinaryTree>() currNode.rightChild = deleteAndBalanceRecursively(currNode.rightChild, keyToDelete) else -> { if (currNode.leftChild == null || currNode.rightChild == null) { + if (currNode == root) root = null return currNode.leftChild ?: currNode.rightChild } else { val successor = getMinNodeRecursively(currNode.rightChild) @@ -126,7 +127,7 @@ class AVLTree, V> : AbstractBinaryTree>() // delete original successor node from tree val newSubtree = deleteAndBalanceRecursively(currNode.rightChild, successor.key) - if (newSubtree != null) currNode.rightChild = newSubtree + currNode.rightChild = newSubtree } } } diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index 540c1ca..ab26545 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -56,8 +56,10 @@ class BSTree, V> : AbstractBinaryTree>() { keyToDelete > currentNode.key -> currentNode.rightChild = deleteRecursively(currentNode.rightChild, keyToDelete) keyToDelete == currentNode.key -> { - if (currentNode.leftChild == null || currentNode.rightChild == null) + if (currentNode.leftChild == null || currentNode.rightChild == null) { + if (currentNode == root) root = null return currentNode.leftChild ?: currentNode.rightChild + } val minRightNode = getMinNodeRecursively(currentNode.rightChild) if (minRightNode != null) { From f203b908dca275d1ff49e0a055214c60f60a2ffd Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sat, 6 Apr 2024 15:25:51 +0300 Subject: [PATCH 142/164] refactor: shorten recursive methods names, add empty lines after classes' signatures, refactor AVL tree class --- lib/src/main/kotlin/tsl/nodes/AVLNode.kt | 1 + lib/src/main/kotlin/tsl/nodes/RBNode.kt | 1 + lib/src/main/kotlin/tsl/trees/AVLTree.kt | 85 +++++++++---------- .../kotlin/tsl/trees/AbstractBinaryTree.kt | 23 ++--- lib/src/main/kotlin/tsl/trees/BSTree.kt | 2 +- lib/src/main/kotlin/tsl/trees/RBTree.kt | 13 +-- 6 files changed, 60 insertions(+), 65 deletions(-) diff --git a/lib/src/main/kotlin/tsl/nodes/AVLNode.kt b/lib/src/main/kotlin/tsl/nodes/AVLNode.kt index e571847..b980b81 100644 --- a/lib/src/main/kotlin/tsl/nodes/AVLNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/AVLNode.kt @@ -2,5 +2,6 @@ package tsl.nodes class AVLNode, V>(key: K, value: V) : AbstractNode>(key, value) { + internal var height = 0 } diff --git a/lib/src/main/kotlin/tsl/nodes/RBNode.kt b/lib/src/main/kotlin/tsl/nodes/RBNode.kt index 50ee49f..92ebc63 100644 --- a/lib/src/main/kotlin/tsl/nodes/RBNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/RBNode.kt @@ -2,6 +2,7 @@ package tsl.nodes class RBNode, V>(key: K, value: V) : AbstractNode>(key, value) { + internal var parent: RBNode? = null enum class Color { diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index ee6f741..f3d28d9 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -4,15 +4,16 @@ import kotlin.math.max import tsl.nodes.AVLNode class AVLTree, V> : AbstractBinaryTree>() { + public override fun insert(key: K, value: V): V? { - val oldValueByKey = search(key) // null if key isn't in the tree + val oldValueByKey = search(key) - insertNodeAndBalanceRecursively(root, key, value) + insertNodeAndBalanceRec(root, key, value) return oldValueByKey } - private fun insertNodeAndBalanceRecursively( + private fun insertNodeAndBalanceRec( currNode: AVLNode?, keyToInsert: K, valueToInsert: V @@ -27,10 +28,10 @@ class AVLTree, V> : AbstractBinaryTree>() when { keyToInsert < currNode.key -> currNode.leftChild = - insertNodeAndBalanceRecursively(currNode.leftChild, keyToInsert, valueToInsert) + insertNodeAndBalanceRec(currNode.leftChild, keyToInsert, valueToInsert) keyToInsert > currNode.key -> currNode.rightChild = - insertNodeAndBalanceRecursively(currNode.rightChild, keyToInsert, valueToInsert) + insertNodeAndBalanceRec(currNode.rightChild, keyToInsert, valueToInsert) else -> { currNode.value = valueToInsert return currNode @@ -39,19 +40,17 @@ class AVLTree, V> : AbstractBinaryTree>() var balancedNode: AVLNode = currNode - if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger + if (getBalanceFactor(currNode) < -1) { currNode.leftChild?.let { - if (keyToInsert > it.key) { // if inserted node has greater key - currNode.leftChild = rotateLeft(it) // perform left rotation first - } - balancedNode = rotateRight(currNode) // anyway, perform right rotation + if (keyToInsert > it.key) currNode.leftChild = rotateLeft(it) + + balancedNode = rotateRight(currNode) } - } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger + } else if (getBalanceFactor(currNode) > 1) { currNode.rightChild?.let { - if (keyToInsert < it.key) { // if inserted node has lesser key - currNode.rightChild = rotateRight(it) // perform right rotation first - } - balancedNode = rotateLeft(currNode) // anyway, perform left rotation + if (keyToInsert < it.key) currNode.rightChild = rotateRight(it) + + balancedNode = rotateLeft(currNode) } } @@ -94,60 +93,52 @@ class AVLTree, V> : AbstractBinaryTree>() private fun getHeight(node: AVLNode?): Int = node?.height ?: -1 public override fun delete(key: K): V? { - val deletedValue: V = - search(key) ?: return null // if key isn't in the tree, there's nothing to delete + val deletedValue = search(key) ?: return null - deleteAndBalanceRecursively(root, key) + deleteNodeAndBalanceRec(root, key) return deletedValue } - private fun deleteAndBalanceRecursively( - currNode: AVLNode?, - keyToDelete: K - ): AVLNode? { + private fun deleteNodeAndBalanceRec(currNode: AVLNode?, keyToDelete: K): AVLNode? { + + if (currNode == null) return null when { - currNode == null -> return null // node to be deleted was not found - keyToDelete < currNode.key -> // node to be deleted is in the left subtree - currNode.leftChild = deleteAndBalanceRecursively(currNode.leftChild, keyToDelete) - keyToDelete > currNode.key -> // node to be deleted is in the right subtree - currNode.rightChild = deleteAndBalanceRecursively(currNode.rightChild, keyToDelete) - else -> { - if (currNode.leftChild == null || currNode.rightChild == null) { - if (currNode == root) root = null - return currNode.leftChild ?: currNode.rightChild - } else { - val successor = getMinNodeRecursively(currNode.rightChild) + keyToDelete < currNode.key -> + currNode.leftChild = deleteNodeAndBalanceRec(currNode.leftChild, keyToDelete) + keyToDelete > currNode.key -> + currNode.rightChild = deleteNodeAndBalanceRec(currNode.rightChild, keyToDelete) + keyToDelete == currNode.key -> { + if (currNode.leftChild != null && currNode.rightChild != null) { + val successor = getMinNodeRec(currNode.rightChild) if (successor != null) { - // copy its successor currNode.key = successor.key currNode.value = successor.value - // delete original successor node from tree - val newSubtree = - deleteAndBalanceRecursively(currNode.rightChild, successor.key) + val newSubtree = deleteNodeAndBalanceRec(currNode.rightChild, successor.key) currNode.rightChild = newSubtree } + } else { + if (currNode == root) root = null + return currNode.leftChild ?: currNode.rightChild } } } var balancedNode: AVLNode = currNode - if (getBalanceFactor(currNode) < -1) { // if left subtree got bigger + if (getBalanceFactor(currNode) < -1) { currNode.leftChild?.let { - if (getBalanceFactor(it) > 0) { // if inserted node has greater key - currNode.leftChild = rotateLeft(it) // perform left rotation first - } - balancedNode = rotateRight(currNode) // anyway, perform left rotation + if (getBalanceFactor(it) > 0) currNode.leftChild = rotateLeft(it) + + balancedNode = rotateRight(currNode) } - } else if (getBalanceFactor(currNode) > 1) { // if right subtree got bigger + } else if (getBalanceFactor(currNode) > 1) { currNode.rightChild?.let { - if (getBalanceFactor(it) < 0) { // if inserted node has greater key - currNode.rightChild = rotateRight(it) // perform right rotation first - } - balancedNode = rotateLeft(currNode) // anyway, perform left rotation + if (getBalanceFactor(it) < 0) currNode.rightChild = rotateRight(it) + + balancedNode = rotateLeft(currNode) } } diff --git a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt index 4629f59..a2379c7 100644 --- a/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AbstractBinaryTree.kt @@ -5,20 +5,21 @@ import tsl.nodes.AbstractNode abstract class AbstractBinaryTree, V, N : AbstractNode> : Iterable> { + internal var root: N? = null public abstract fun delete(key: K): V? public abstract fun insert(key: K, value: V): V? - public fun search(key: K): V? = searchNodeRecursively(root, key)?.value + public fun search(key: K): V? = searchNodeRec(root, key)?.value - protected fun searchNodeRecursively(currNode: N?, keyToSearch: K): N? { + protected fun searchNodeRec(currNode: N?, keyToSearch: K): N? { return when { currNode == null -> null keyToSearch == currNode.key -> currNode - keyToSearch < currNode.key -> searchNodeRecursively(currNode.leftChild, keyToSearch) - else -> searchNodeRecursively(currNode.rightChild, keyToSearch) + keyToSearch < currNode.key -> searchNodeRec(currNode.leftChild, keyToSearch) + else -> searchNodeRec(currNode.rightChild, keyToSearch) } } @@ -28,23 +29,23 @@ abstract class AbstractBinaryTree, V, N : AbstractNode null node.leftChild == null -> node - else -> getMinNodeRecursively(node.leftChild) + else -> getMinNodeRec(node.leftChild) } } - protected fun getMaxNodeRecursively(node: N?): N? { + public fun getMaxKey(): K? = getMaxNodeRec(root)?.key + + protected fun getMaxNodeRec(node: N?): N? { return when { node == null -> null node.rightChild == null -> node - else -> getMaxNodeRecursively(node.rightChild) + else -> getMaxNodeRec(node.rightChild) } } diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index ab26545..7680a97 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -61,7 +61,7 @@ class BSTree, V> : AbstractBinaryTree>() { return currentNode.leftChild ?: currentNode.rightChild } - val minRightNode = getMinNodeRecursively(currentNode.rightChild) + val minRightNode = getMinNodeRec(currentNode.rightChild) if (minRightNode != null) { currentNode.key = minRightNode.key currentNode.value = minRightNode.value diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index e4649d4..eae016f 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -4,7 +4,8 @@ import tsl.nodes.RBNode class RBTree, V> : AbstractBinaryTree>() { - // @return Null if 'insert' method haven't replaced any value. Return value if there was some replace + // @return Null if 'insert' method haven't replaced any value. Return value if there was some + // replace public override fun insert(key: K, value: V): V? { @@ -100,7 +101,7 @@ class RBTree, V> : AbstractBinaryTree>() { root = newRoot } - // @return Value if delete was successful and null if it isn't. + // @return Value if delete was successful and null if it isn't. public override fun delete(key: K): V? { val deletingNodeValue = search(key) @@ -110,17 +111,17 @@ class RBTree, V> : AbstractBinaryTree>() { return deletingNodeValue } - // @return The new root node of the Red\-Black Tree after deletion\. + // @return The new root node of the Red\-Black Tree after deletion\. private fun deleteNode( key: K, ): RBNode? { - var current: RBNode? = searchNodeRecursively(root, key) + var current: RBNode? = searchNodeRec(root, key) if (current == null) return null var newRoot = root if (current.leftChild != null && current.rightChild != null) { - val successor = getMaxNodeRecursively(current.leftChild) + val successor = getMaxNodeRec(current.leftChild) if (successor?.parent?.leftChild == successor) successor?.parent?.leftChild = null else successor?.parent?.rightChild = null if (successor != null) { @@ -161,7 +162,7 @@ class RBTree, V> : AbstractBinaryTree>() { return newRoot } - // @return The root of the tree after fixing the structure to maintain the red-black properties + // @return The root of the tree after fixing the structure to maintain the red-black properties private fun fixAfterDeletion(currentDeletingNode: RBNode?): RBNode? { var fixedRoot = root From 1c396efcfe2cb32382dc92b4b0176204f98f580f Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sat, 6 Apr 2024 16:18:26 +0300 Subject: [PATCH 143/164] feat: add hasTwoChildren() method to Abstract node class --- lib/src/main/kotlin/tsl/nodes/AbstractNode.kt | 3 +++ lib/src/main/kotlin/tsl/trees/AVLTree.kt | 2 +- lib/src/main/kotlin/tsl/trees/BSTree.kt | 24 +++++++++++-------- lib/src/main/kotlin/tsl/trees/RBTree.kt | 2 +- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt index 19a9062..3607340 100644 --- a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt @@ -6,4 +6,7 @@ abstract class AbstractNode, V, N : AbstractNode>( ) { internal var leftChild: N? = null internal var rightChild: N? = null + + internal fun hasTwoChildren(): Boolean = + (this.leftChild != null && this.rightChild != null) } diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index f3d28d9..808824a 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -110,7 +110,7 @@ class AVLTree, V> : AbstractBinaryTree>() keyToDelete > currNode.key -> currNode.rightChild = deleteNodeAndBalanceRec(currNode.rightChild, keyToDelete) keyToDelete == currNode.key -> { - if (currNode.leftChild != null && currNode.rightChild != null) { + if (currNode.hasTwoChildren()) { val successor = getMinNodeRec(currNode.rightChild) if (successor != null) { currNode.key = successor.key diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index 7680a97..4a033d4 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -21,6 +21,7 @@ class BSTree, V> : AbstractBinaryTree>() { currentNode.value = nodeToInsert.value return oldValue } + nodeToInsert.key < currentNode.key -> { if (currentNode.leftChild == null) { currentNode.leftChild = nodeToInsert @@ -28,6 +29,7 @@ class BSTree, V> : AbstractBinaryTree>() { } currentNode = currentNode.leftChild } + nodeToInsert.key > currentNode.key -> { if (currentNode.rightChild == null) { currentNode.rightChild = nodeToInsert @@ -49,27 +51,29 @@ class BSTree, V> : AbstractBinaryTree>() { } private fun deleteRecursively(currentNode: BSTNode?, keyToDelete: K): BSTNode? { + if (currentNode == null) return null + when { - currentNode == null -> return null keyToDelete < currentNode.key -> currentNode.leftChild = deleteRecursively(currentNode.leftChild, keyToDelete) keyToDelete > currentNode.key -> currentNode.rightChild = deleteRecursively(currentNode.rightChild, keyToDelete) keyToDelete == currentNode.key -> { - if (currentNode.leftChild == null || currentNode.rightChild == null) { + if (currentNode.hasTwoChildren()) { + val minRightNode = getMinNodeRec(currentNode.rightChild) + if (minRightNode != null) { + currentNode.key = minRightNode.key + currentNode.value = minRightNode.value + currentNode.rightChild = + deleteRecursively(currentNode.rightChild, minRightNode.key) + } + } else { if (currentNode == root) root = null return currentNode.leftChild ?: currentNode.rightChild } - - val minRightNode = getMinNodeRec(currentNode.rightChild) - if (minRightNode != null) { - currentNode.key = minRightNode.key - currentNode.value = minRightNode.value - currentNode.rightChild = - deleteRecursively(currentNode.rightChild, minRightNode.key) - } } } + return currentNode } } diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index eae016f..8fb868e 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -120,7 +120,7 @@ class RBTree, V> : AbstractBinaryTree>() { if (current == null) return null var newRoot = root - if (current.leftChild != null && current.rightChild != null) { + if (current.hasTwoChildren()) { val successor = getMaxNodeRec(current.leftChild) if (successor?.parent?.leftChild == successor) successor?.parent?.leftChild = null else successor?.parent?.rightChild = null From f24d5cc55f113f05fc71587f298fa5491383cf59 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sun, 7 Apr 2024 10:56:38 +0300 Subject: [PATCH 144/164] test: add more tests for deletion in AVL tree --- lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt index 012a240..4572784 100644 --- a/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/AVLTreeTest.kt @@ -390,6 +390,21 @@ class AVLTreeTest { assertEquals(avlTree.root?.key, 20) } + @Test + fun `deletion of last node should make tree empty`() { + avlTree.insert(1, "A") + avlTree.delete(1) + + val expectedStructure = listOf>() + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(avlTree.isEmpty(), true) + assertEquals(expectedStructure, actualStructure) + } + @Test fun `deletion of node with one child should replace it with its child`() { avlTree.insert(20, "B") @@ -411,6 +426,24 @@ class AVLTreeTest { // Small tree tests + @Test + fun `deletion of root node should replace it with its successor`() { + avlTree.insert(10, "A") + avlTree.insert(20, "B") + avlTree.insert(30, "C") + + avlTree.delete(20) + + val expectedStructure = listOf(Pair(10, "A"), Pair(30, "C")) + + val actualStructure = mutableListOf>() + + for (pair in avlTree) actualStructure.add(pair) + + assertEquals(expectedStructure, actualStructure) + assertEquals(avlTree.root?.key, 30) + } + @Test fun `deletion of right child triggering right rotation should balance small tree`() { avlTree.insert(40, "D") From 8196aec684d4912d10f3179ecb6f5d5192d09a1b Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 7 Apr 2024 11:29:24 +0300 Subject: [PATCH 145/164] fix: return to case when we don't support duplicates (i forgot:) ) --- lib/src/main/kotlin/tsl/trees/RBTree.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tsl/trees/RBTree.kt b/lib/src/main/kotlin/tsl/trees/RBTree.kt index 8fb868e..69ecc25 100644 --- a/lib/src/main/kotlin/tsl/trees/RBTree.kt +++ b/lib/src/main/kotlin/tsl/trees/RBTree.kt @@ -21,7 +21,7 @@ class RBTree, V> : AbstractBinaryTree>() { // traverse the tree to find the insertion point (node) while (currentNode != null) { - if (currentNode.key >= newNode.key) { + if (currentNode.key > newNode.key) { if (currentNode.leftChild == null) { currentNode.leftChild = newNode newNode.parent = currentNode @@ -37,6 +37,10 @@ class RBTree, V> : AbstractBinaryTree>() { break } currentNode = currentNode.rightChild + } else { + currentNode.value = value + fixAfterInsertion(newNode) + break } } return returnValue From 891d37aabbf3d0edd6f83299e23a62c4eccf9b3d Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 7 Apr 2024 11:30:24 +0300 Subject: [PATCH 146/164] test: correct duplicate test --- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index 1ef9e9f..aac2d09 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -489,7 +489,7 @@ class RBTreeTest { for ((key, value) in rbTree) { inorderedTraversalTree += Pair(key, value) } - assertEquals(listOf(Pair(-1, "a"), Pair(0, "a"), Pair(1, "a")), inorderedTraversalTree) + assertEquals(listOf(Pair(-1, "a"), Pair(1, "a")), inorderedTraversalTree) assertEquals("b", returnValue) } From d3200313110248c9f2748c4d59bdb84c3cdac27c Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Sun, 7 Apr 2024 16:32:47 +0300 Subject: [PATCH 147/164] refactor: delete some repeating code in AVL tree class --- lib/src/main/kotlin/tsl/trees/AVLTree.kt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/src/main/kotlin/tsl/trees/AVLTree.kt b/lib/src/main/kotlin/tsl/trees/AVLTree.kt index 808824a..a9c82bc 100644 --- a/lib/src/main/kotlin/tsl/trees/AVLTree.kt +++ b/lib/src/main/kotlin/tsl/trees/AVLTree.kt @@ -8,7 +8,7 @@ class AVLTree, V> : AbstractBinaryTree>() public override fun insert(key: K, value: V): V? { val oldValueByKey = search(key) - insertNodeAndBalanceRec(root, key, value) + root = insertNodeAndBalanceRec(root, key, value) return oldValueByKey } @@ -21,7 +21,6 @@ class AVLTree, V> : AbstractBinaryTree>() if (currNode == null) { val newNode = AVLNode(keyToInsert, valueToInsert) - if (root == null) root = newNode return newNode } @@ -63,21 +62,19 @@ class AVLTree, V> : AbstractBinaryTree>() private fun rotateRight(oldUpperNode: AVLNode): AVLNode { val newUpperNode = oldUpperNode.leftChild ?: return oldUpperNode + oldUpperNode.leftChild = newUpperNode.rightChild newUpperNode.rightChild = oldUpperNode - if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root - return newUpperNode } private fun rotateLeft(oldUpperNode: AVLNode): AVLNode { val newUpperNode = oldUpperNode.rightChild ?: return oldUpperNode + oldUpperNode.rightChild = newUpperNode.leftChild newUpperNode.leftChild = oldUpperNode - if (root == oldUpperNode) root = newUpperNode // if root was rotated, set new root - return newUpperNode } @@ -95,7 +92,7 @@ class AVLTree, V> : AbstractBinaryTree>() public override fun delete(key: K): V? { val deletedValue = search(key) ?: return null - deleteNodeAndBalanceRec(root, key) + root = deleteNodeAndBalanceRec(root, key) return deletedValue } @@ -120,7 +117,6 @@ class AVLTree, V> : AbstractBinaryTree>() currNode.rightChild = newSubtree } } else { - if (currNode == root) root = null return currNode.leftChild ?: currNode.rightChild } } From 18341fab492cbeee30517022635a07a8d6188af5 Mon Sep 17 00:00:00 2001 From: Shakirov Karim Date: Sun, 7 Apr 2024 23:53:37 +0300 Subject: [PATCH 148/164] test: add test for deletion of the last node in BS tree --- lib/src/test/kotlin/tsl/trees/BSTreeTest.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt index 58969bf..c3838bb 100644 --- a/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/BSTreeTest.kt @@ -176,5 +176,17 @@ class BSTreeTest { assertEquals(expectedStructure, actualStructure) } + + @Test + fun `deletion of last node should make tree empty`() { + tree.clear() + tree.insert(1, "A") + tree.delete(1) + + val expectedRoot = null + val actualRoot = tree.root + + assertEquals(expectedRoot, actualRoot) + } } } From 752c62cece8120562c5cf2f3bb6dff14921f898a Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 8 Apr 2024 09:56:13 +0300 Subject: [PATCH 149/164] ci(fix): specify permissions for commit formatter changes --- .github/workflows/formatter.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/formatter.yml b/.github/workflows/formatter.yml index 3e2bdd4..2ed41d5 100644 --- a/.github/workflows/formatter.yml +++ b/.github/workflows/formatter.yml @@ -7,6 +7,7 @@ on: pull_request: branches: [ "main" ] +permissions: write-all jobs: build: @@ -38,4 +39,4 @@ jobs: git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} git commit -am "Reformat changes" - git push \ No newline at end of file + git push From 1ef3e9e9d03d4117e18fe55d8fa515a90d27a2a0 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Mon, 8 Apr 2024 09:59:58 +0300 Subject: [PATCH 150/164] ci: edit commit message --- .github/workflows/formatter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/formatter.yml b/.github/workflows/formatter.yml index 2ed41d5..9c5127b 100644 --- a/.github/workflows/formatter.yml +++ b/.github/workflows/formatter.yml @@ -37,6 +37,6 @@ jobs: git config --global user.name 'github-actions[bot]' git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} - git commit -am "Reformat changes" + git commit -am "ci(formatter): reformat changes" git push From f2003647ce188b99e852acf467b1f5fa4a65a5ff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 07:05:35 +0000 Subject: [PATCH 151/164] ci(formatter): reformat changes --- lib/src/main/kotlin/tsl/nodes/AbstractNode.kt | 3 +-- lib/src/main/kotlin/tsl/trees/BSTree.kt | 2 -- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt index 3607340..fadc6c5 100644 --- a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt @@ -7,6 +7,5 @@ abstract class AbstractNode, V, N : AbstractNode>( internal var leftChild: N? = null internal var rightChild: N? = null - internal fun hasTwoChildren(): Boolean = - (this.leftChild != null && this.rightChild != null) + internal fun hasTwoChildren(): Boolean = (this.leftChild != null && this.rightChild != null) } diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index 4a033d4..5834465 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -21,7 +21,6 @@ class BSTree, V> : AbstractBinaryTree>() { currentNode.value = nodeToInsert.value return oldValue } - nodeToInsert.key < currentNode.key -> { if (currentNode.leftChild == null) { currentNode.leftChild = nodeToInsert @@ -29,7 +28,6 @@ class BSTree, V> : AbstractBinaryTree>() { } currentNode = currentNode.leftChild } - nodeToInsert.key > currentNode.key -> { if (currentNode.rightChild == null) { currentNode.rightChild = nodeToInsert diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index aac2d09..6f40fec 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -1,11 +1,11 @@ package tsl.trees +import kotlin.random.Random import kotlin.test.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import tsl.nodes.RBNode -import kotlin.random.Random /* * Some standarts and "variables" that were used for decreasing the amount of words) From 32150f7075c31a3ca871cfa2d0043d4486e187bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 07:05:47 +0000 Subject: [PATCH 152/164] ci(formatter): reformat changes --- lib/src/main/kotlin/tsl/nodes/AbstractNode.kt | 3 +-- lib/src/main/kotlin/tsl/trees/BSTree.kt | 2 -- lib/src/test/kotlin/tsl/trees/RBTreeTest.kt | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt index 3607340..fadc6c5 100644 --- a/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt +++ b/lib/src/main/kotlin/tsl/nodes/AbstractNode.kt @@ -7,6 +7,5 @@ abstract class AbstractNode, V, N : AbstractNode>( internal var leftChild: N? = null internal var rightChild: N? = null - internal fun hasTwoChildren(): Boolean = - (this.leftChild != null && this.rightChild != null) + internal fun hasTwoChildren(): Boolean = (this.leftChild != null && this.rightChild != null) } diff --git a/lib/src/main/kotlin/tsl/trees/BSTree.kt b/lib/src/main/kotlin/tsl/trees/BSTree.kt index 4a033d4..5834465 100644 --- a/lib/src/main/kotlin/tsl/trees/BSTree.kt +++ b/lib/src/main/kotlin/tsl/trees/BSTree.kt @@ -21,7 +21,6 @@ class BSTree, V> : AbstractBinaryTree>() { currentNode.value = nodeToInsert.value return oldValue } - nodeToInsert.key < currentNode.key -> { if (currentNode.leftChild == null) { currentNode.leftChild = nodeToInsert @@ -29,7 +28,6 @@ class BSTree, V> : AbstractBinaryTree>() { } currentNode = currentNode.leftChild } - nodeToInsert.key > currentNode.key -> { if (currentNode.rightChild == null) { currentNode.rightChild = nodeToInsert diff --git a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt index aac2d09..6f40fec 100644 --- a/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tsl/trees/RBTreeTest.kt @@ -1,11 +1,11 @@ package tsl.trees +import kotlin.random.Random import kotlin.test.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import tsl.nodes.RBNode -import kotlin.random.Random /* * Some standarts and "variables" that were used for decreasing the amount of words) From 14695125c49076b500f11709190bd61f7c0497b7 Mon Sep 17 00:00:00 2001 From: Vlasenco Daniel Date: Wed, 10 Apr 2024 20:58:02 +0300 Subject: [PATCH 153/164] ci: delete formatter from GitHub Actions workflows --- .github/workflows/formatter.yml | 42 --------------------------------- 1 file changed, 42 deletions(-) delete mode 100644 .github/workflows/formatter.yml diff --git a/.github/workflows/formatter.yml b/.github/workflows/formatter.yml deleted file mode 100644 index 9c5127b..0000000 --- a/.github/workflows/formatter.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Formatter - -on: - workflow_dispatch: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -permissions: write-all - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: zulu - - - name: Run kotlin formatter - run: ./gradlew format - - - name: Check for modified files - id: git-check - run: echo "modified=$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT - - - name: Push changes - if: steps.git-check.outputs.modified == 'true' - run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' - git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} - git commit -am "ci(formatter): reformat changes" - - git push From 5d2e2f0e9b308aef29ddc095b43061b3a69129c3 Mon Sep 17 00:00:00 2001 From: Mike Gavrilenko <64466788+qrutyy@users.noreply.github.com> Date: Sun, 14 Apr 2024 11:32:35 +0300 Subject: [PATCH 154/164] ci: correct badge's commit process --- .github/workflows/codecoverage.yml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 0637fa9..c314c0e 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -2,7 +2,9 @@ name: Measure coverage on: workflow_dispatch: - pull_request: + push: + +permissions: write-all jobs: build: @@ -11,9 +13,6 @@ jobs: strategy: matrix: os: [ ubuntu-latest, macos-latest ] - - permissions: - pull-requests: write steps: - uses: actions/checkout@v3 @@ -38,13 +37,19 @@ jobs: generate-coverage-badge: true coverage-badge-filename: jacoco.svg + - name: Check for modified files + id: git-check + run: echo "modified=$(if git diff-index --quiet HEAD -- .github/badges ; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT + + - name: Commit the badges (if they changed) + if: steps.git-check.outputs.modified == 'true' run: | - if [[ `git diff --exit-code .github/badges` ]]; then - git config --global user.name 'qrutyy' - git config --global user.email 'qrutyq@gmail.com' + git config --global user.name 'github-actions[bot]' + git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' + git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} git add .github/badges/* - git commit -m "ci: autogenerate JaCoCo coverage badge" - git push || true + git commit -am "ci: autogenerate JaCoCo coverage badge" + git push fi shell: bash From 1c98faffe5846f45384cefc929ce602ee8e386ca Mon Sep 17 00:00:00 2001 From: Mike Gavrilenko <64466788+qrutyy@users.noreply.github.com> Date: Sun, 14 Apr 2024 11:37:17 +0300 Subject: [PATCH 155/164] ci(fix): remove folder specifying --- .github/workflows/codecoverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index c314c0e..65c9ff4 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -39,7 +39,7 @@ jobs: - name: Check for modified files id: git-check - run: echo "modified=$(if git diff-index --quiet HEAD -- .github/badges ; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT + run: echo "modified=$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT - name: Commit the badges (if they changed) From ff0a3397335a919cf17249d03adfcd15e14f0586 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 14 Apr 2024 11:45:45 +0300 Subject: [PATCH 156/164] ci(fix): remove line that cause errors, switch to PR --- .github/workflows/codecoverage.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 65c9ff4..d8d3830 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -2,7 +2,7 @@ name: Measure coverage on: workflow_dispatch: - push: + pull-request: permissions: write-all @@ -51,5 +51,4 @@ jobs: git add .github/badges/* git commit -am "ci: autogenerate JaCoCo coverage badge" git push - fi shell: bash From 2f32a328a44de6c865de014d7012428344b31d99 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 14 Apr 2024 11:54:13 +0300 Subject: [PATCH 157/164] docs: add code coverage badge, remove superfluous doc. lines --- README.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9684001..c3a4ecd 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Kotlin 1.9.23][kotlin_img]][kotlin_releases_url] [![Tests passing][tests_passing_img]][tests_workflow_url] +[![Code Coverage][code_coverage_badge_img]][code_coverage_workflow_url] [![License][license_img]][repo_license_url] [![CodeFactor][codefactor_img]][codefactor_url] @@ -68,13 +69,6 @@ for ((key, value) in myRBTree) { myRBTree.forEach { println(it) } // prints every pair ``` -There are also other helpful methods: - -- `clear()` -- `isEmpty()` -- `getMinKey()` -- `getMaxKey()` - ## Contributing If you have found a bug, or want to propose some useful feature for our project, please firstly read our [Contribution Rules][contribute_rules_url] and @@ -105,12 +99,14 @@ _______________________________ [tests_passing_img]: https://img.shields.io/badge/tests-Passing-green [license_img]: https://img.shields.io/badge/license-MIT-blue [codefactor_img]: https://www.codefactor.io/repository/github/spbu-coding-2023/trees-2/badge +[code_coverage_badge_img]: https://github.com/spbu-coding-2023/trees-2/.github/badges/jacoco.svg [tests_workflow_url]: https://github.com/spbu-coding-2023/trees-2/actions/workflows/test.yml [repo_license_url]: https://github.com/spbu-coding-2023/trees-2/blob/main/LICENSE.md [contribute_rules_url]: https://github.com/spbu-coding-2023/trees-2/blob/main/CONTRIBUTING.md +[code_coverage_workflow_url]: https://github.com/spbu-coding-2023/trees-2/actions/workflows/codecoverage.yml From 65750a15b414c98a10459d2c7f00ff9ca0b3b3a9 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 14 Apr 2024 12:04:08 +0300 Subject: [PATCH 158/164] ci(fix): correct misprint --- .github/workflows/codecoverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index d8d3830..5d75601 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -2,7 +2,7 @@ name: Measure coverage on: workflow_dispatch: - pull-request: + pull_request: permissions: write-all From 9bcf2ad431e8f942a3d45bf76db4a2299b9e91bd Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 14 Apr 2024 12:41:15 +0300 Subject: [PATCH 159/164] ci(fix): edit checkout setting --- .github/workflows/codecoverage.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 5d75601..69a2ebf 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -15,7 +15,10 @@ jobs: os: [ ubuntu-latest, macos-latest ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + - name: Set up JDK 17 uses: actions/setup-java@v3 From 2a7c713d862d5fbb98554d16593f41524b0a82e3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Apr 2024 09:42:47 +0000 Subject: [PATCH 160/164] ci: autogenerate JaCoCo coverage badge --- .github/badges/jacoco.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/badges/jacoco.svg diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg new file mode 100644 index 0000000..2b337d7 --- /dev/null +++ b/.github/badges/jacoco.svg @@ -0,0 +1 @@ +coverage91.9% \ No newline at end of file From f89e7f1693dac59b85901943fbcf2830a857108a Mon Sep 17 00:00:00 2001 From: Mike Gavrilenko <64466788+qrutyy@users.noreply.github.com> Date: Sun, 14 Apr 2024 13:01:20 +0300 Subject: [PATCH 161/164] ci: set up new check approach --- .github/workflows/codecoverage.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codecoverage.yml b/.github/workflows/codecoverage.yml index 69a2ebf..969b9e3 100644 --- a/.github/workflows/codecoverage.yml +++ b/.github/workflows/codecoverage.yml @@ -19,7 +19,6 @@ jobs: with: ref: ${{ github.head_ref }} - - name: Set up JDK 17 uses: actions/setup-java@v3 with: @@ -42,11 +41,17 @@ jobs: - name: Check for modified files id: git-check - run: echo "modified=$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)" >> $GITHUB_OUTPUT - + run: | + modified=$(git status --porcelain) + if [ -n "$modified" ]; then + echo "modified=true" >> $GITHUB_ENV + else + echo "modified=false" >> $GITHUB_ENV + fi + shell: bash - name: Commit the badges (if they changed) - if: steps.git-check.outputs.modified == 'true' + if: env.git-check == 'true' run: | git config --global user.name 'github-actions[bot]' git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' @@ -55,3 +60,4 @@ jobs: git commit -am "ci: autogenerate JaCoCo coverage badge" git push shell: bash + From 50fda8617364ce89bbd32c5c2c3eabf7034aedf9 Mon Sep 17 00:00:00 2001 From: Mike Gavrilenko <64466788+qrutyy@users.noreply.github.com> Date: Sun, 14 Apr 2024 13:02:51 +0300 Subject: [PATCH 162/164] docs: edit badge's liink --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3a4ecd..746068b 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ _______________________________ [tests_passing_img]: https://img.shields.io/badge/tests-Passing-green [license_img]: https://img.shields.io/badge/license-MIT-blue [codefactor_img]: https://www.codefactor.io/repository/github/spbu-coding-2023/trees-2/badge -[code_coverage_badge_img]: https://github.com/spbu-coding-2023/trees-2/.github/badges/jacoco.svg +[code_coverage_badge_img]: https://raw.githubusercontent.com/spbu-coding-2023/trees-2/main/.github/badges/jacoco.svg From a7a5ccdff7921797abe309a1f591caf0ef05c495 Mon Sep 17 00:00:00 2001 From: qruty <64466788+qrutyy@users.noreply.github.com> Date: Sun, 14 Apr 2024 13:07:47 +0300 Subject: [PATCH 163/164] ci(fix): remove duplicate of test execution --- .github/workflows/test.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eef21c0..ed46039 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,8 +17,5 @@ jobs: java-version: '17' distribution: zulu - - name: Build by Gradle + - name: Build & Test by Gradle run: ./gradlew build - - - name: Run JUnit5 tests - run: ./gradlew test From aab170e0f73d06f1e4fd7284758db29e0800daa9 Mon Sep 17 00:00:00 2001 From: Daniel Vlasenco Date: Sun, 5 May 2024 17:46:28 +0300 Subject: [PATCH 164/164] docs: remove broken link from README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 746068b..3acbef0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![Tests passing][tests_passing_img]][tests_workflow_url] [![Code Coverage][code_coverage_badge_img]][code_coverage_workflow_url] [![License][license_img]][repo_license_url] -[![CodeFactor][codefactor_img]][codefactor_url] `forest-group` is a library that lets you easily create and use [Binary search trees](https://en.wikipedia.org/wiki/Binary_search_tree), [AVL trees](https://en.wikipedia.org/wiki/AVL_tree) and [Red-black trees](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) in your applications! @@ -98,7 +97,6 @@ _______________________________ [kotlin_img]: https://img.shields.io/badge/Kotlin-%201.9.23-magenta [tests_passing_img]: https://img.shields.io/badge/tests-Passing-green [license_img]: https://img.shields.io/badge/license-MIT-blue -[codefactor_img]: https://www.codefactor.io/repository/github/spbu-coding-2023/trees-2/badge [code_coverage_badge_img]: https://raw.githubusercontent.com/spbu-coding-2023/trees-2/main/.github/badges/jacoco.svg @@ -111,5 +109,4 @@ _______________________________ [kotlin_releases_url]: https://kotlinlang.org/docs/releases.html#release-details -[codefactor_url]: https://www.codefactor.io/repository/github/spbu-coding-2023/trees-2 [java_gnomik_url]: https://ibb.co/54hJVd2