From 4bff01b1488c363b8abea8b80ef0e63657625cc7 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 19:01:55 +0000 Subject: [PATCH 001/122] Setting up GitHub Classroom Feedback From 264f4af687fcbd1231f354092f70cd75bbb11cf3 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 14 Mar 2024 00:21:07 +0300 Subject: [PATCH 002/122] Adding simple gitignore file template --- .gitignore | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39e0dcb --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +### Gradle console-app generated template +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +*/build/* + +### VSCode Template +.vscode/ + +### IntelliJ IDEA Template +.idea/ +*.iml + From 346888b0697c235a608dca449cc33131cc094189 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 14 Mar 2024 00:21:43 +0300 Subject: [PATCH 003/122] Adding gradle project files --- .gitattributes | 9 + gradle.properties | 6 + gradle/libs.versions.toml | 13 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 ++++++++++++++++++ gradlew.bat | 92 +++++++ lib/build.gradle.kts | 45 ++++ lib/src/main/kotlin/org/example/Library.kt | 10 + .../test/kotlin/org/example/LibraryTest.kt | 14 + settings.gradle.kts | 15 ++ 11 files changed, 460 insertions(+) create mode 100644 .gitattributes create mode 100644 gradle.properties 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/main/kotlin/org/example/Library.kt 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.properties b/gradle.properties new file mode 100644 index 0000000..18f452c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,6 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties + +org.gradle.parallel=true +org.gradle.caching=true + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..4f41755 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,13 @@ +# 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" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } + +[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..035fdc2 --- /dev/null +++ b/lib/build.gradle.kts @@ -0,0 +1,45 @@ +/* + * 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. + * This project uses @Incubating APIs which are subject to change. + */ + +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 { + // 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) +} + +testing { + suites { + // Configure the built-in test suite + val test by getting(JvmTestSuite::class) { + // Use Kotlin Test test framework + useKotlinTest("1.9.20") + } + } +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(19) + } +} diff --git a/lib/src/main/kotlin/org/example/Library.kt b/lib/src/main/kotlin/org/example/Library.kt new file mode 100644 index 0000000..628e156 --- /dev/null +++ b/lib/src/main/kotlin/org/example/Library.kt @@ -0,0 +1,10 @@ +/* + * This Kotlin source file was generated by the Gradle 'init' task. + */ +package org.example + +class Library { + fun someLibraryMethod(): Boolean { + return true + } +} 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..7de2ad7 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,15 @@ +/* + * 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. + * This project uses @Incubating APIs which are subject to change. + */ + +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 = "tree-trippers" +include("lib") From 95ef20eb42338218e62b6570f64d50606732c5c1 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 14 Mar 2024 00:23:21 +0300 Subject: [PATCH 004/122] Adding search tree library file structure at project --- lib/src/main/kotlin/org/example/Library.kt | 10 ---------- lib/src/main/kotlin/tree_trippers/SearchTree.kt | 9 +++++++++ .../kotlin/tree_trippers/binary_trees/AVLTree.kt | 6 ++++++ .../tree_trippers/binary_trees/AbstractBSTree.kt | 7 +++++++ .../kotlin/tree_trippers/binary_trees/BSTree.kt | 6 ++++++ .../tree_trippers/binary_trees/RedBlackTree.kt | 6 ++++++ .../kotlin/tree_trippers/nodes/SearchTreeNode.kt | 5 +++++ .../nodes/binary_nodes/AVLTreeNode.kt | 5 +++++ .../nodes/binary_nodes/AbstractBSTreeNode.kt | 7 +++++++ .../tree_trippers/nodes/binary_nodes/BSTreeNode.kt | 5 +++++ .../tree_trippers/nodes/binary_nodes/RBTreeNode.kt | 5 +++++ lib/src/test/kotlin/org/example/LibraryTest.kt | 14 -------------- 12 files changed, 61 insertions(+), 24 deletions(-) delete mode 100644 lib/src/main/kotlin/org/example/Library.kt create mode 100644 lib/src/main/kotlin/tree_trippers/SearchTree.kt create mode 100644 lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt create mode 100644 lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt create mode 100644 lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt create mode 100644 lib/src/main/kotlin/tree_trippers/binary_trees/RedBlackTree.kt create mode 100644 lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt create mode 100644 lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt create mode 100644 lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt create mode 100644 lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt create mode 100644 lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt delete mode 100644 lib/src/test/kotlin/org/example/LibraryTest.kt diff --git a/lib/src/main/kotlin/org/example/Library.kt b/lib/src/main/kotlin/org/example/Library.kt deleted file mode 100644 index 628e156..0000000 --- a/lib/src/main/kotlin/org/example/Library.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * This Kotlin source file was generated by the Gradle 'init' task. - */ -package org.example - -class Library { - fun someLibraryMethod(): Boolean { - return true - } -} diff --git a/lib/src/main/kotlin/tree_trippers/SearchTree.kt b/lib/src/main/kotlin/tree_trippers/SearchTree.kt new file mode 100644 index 0000000..4a27c78 --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/SearchTree.kt @@ -0,0 +1,9 @@ +package tree_trippers + +import tree_trippers.nodes.SearchTreeNode + +public interface SearchTree, V, N: SearchTreeNode> { + + + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt new file mode 100644 index 0000000..2fc8462 --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt @@ -0,0 +1,6 @@ +package tree_trippers.binary_trees + +import tree_trippers.nodes.binary_nodes.AVLTreeNode + +public class AVLTree, V>: AbstractBSTree>() { +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt new file mode 100644 index 0000000..7663372 --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt @@ -0,0 +1,7 @@ +package tree_trippers.binary_trees + +import tree_trippers.SearchTree +import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode + +abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt new file mode 100644 index 0000000..9618d08 --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt @@ -0,0 +1,6 @@ +package tree_trippers.binary_trees + +import tree_trippers.nodes.binary_nodes.BSTreeNode + +public class BSTree, V>: AbstractBSTree>() { +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/RedBlackTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/RedBlackTree.kt new file mode 100644 index 0000000..d6a7dc9 --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/RedBlackTree.kt @@ -0,0 +1,6 @@ +package tree_trippers.binary_trees + +import tree_trippers.nodes.binary_nodes.RBTreeNode + +public class RedBlackTree, V>: AbstractBSTree>() { +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt new file mode 100644 index 0000000..70c596a --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt @@ -0,0 +1,5 @@ +package tree_trippers.nodes + +public interface SearchTreeNode, V, N: SearchTreeNode> { + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt new file mode 100644 index 0000000..c7fffe0 --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt @@ -0,0 +1,5 @@ +package tree_trippers.nodes.binary_nodes + +public class AVLTreeNode, V>: AbstractBSTreeNode>() { + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt new file mode 100644 index 0000000..0d09d2a --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -0,0 +1,7 @@ +package tree_trippers.nodes.binary_nodes + +import tree_trippers.nodes.SearchTreeNode + +public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>: SearchTreeNode { + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt new file mode 100644 index 0000000..92ec05c --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt @@ -0,0 +1,5 @@ +package tree_trippers.nodes.binary_nodes + +public class BSTreeNode, V>: AbstractBSTreeNode>() { + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt new file mode 100644 index 0000000..785409e --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt @@ -0,0 +1,5 @@ +package tree_trippers.nodes.binary_nodes + +public class RBTreeNode, V>: AbstractBSTreeNode>() { + +} \ No newline at end of file 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'") - } -} From 93dd96ebfed7ecd4191d6f062ca9875680b58562 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 14 Mar 2024 01:04:39 +0300 Subject: [PATCH 005/122] Update nodes classes and interfaces with adding properties and update its methods --- .../tree_trippers/nodes/SearchTreeNode.kt | 6 +++++ .../nodes/binary_nodes/AVLTreeNode.kt | 22 ++++++++++++++++++- .../nodes/binary_nodes/AbstractBSTreeNode.kt | 15 ++++++++++++- .../nodes/binary_nodes/BSTreeNode.kt | 2 +- .../nodes/binary_nodes/RBTreeNode.kt | 7 +++++- 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt index 70c596a..9cd7239 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt @@ -1,5 +1,11 @@ package tree_trippers.nodes +// TODO(Adding docs for methods) public interface SearchTreeNode, V, N: SearchTreeNode> { + public fun getKeys(): List + + public fun getValues(): List + + public fun getChildren(): List } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt index c7fffe0..d19210e 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt @@ -1,5 +1,25 @@ package tree_trippers.nodes.binary_nodes -public class AVLTreeNode, V>: AbstractBSTreeNode>() { +import kotlin.math.max +public class AVLTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { + public var height: Int = 1 + + constructor(key: K, value: V, leftChild: AVLTreeNode?, rightChild: AVLTreeNode?) : this(key, value) { + this.leftChild = leftChild + this.rightChild = rightChild + updateHeight() + } + + private fun updateHeight() { + // TODO(Create tests for it method) + var maxChildrenHeight: Int = 0 + val children: List> = getChildren() + children.forEach() { + child -> maxChildrenHeight = max(maxChildrenHeight, child.height) + } + height = 1 + maxChildrenHeight + } + + // TODO(Connect method `updateHeight` to setters of properties: `leftChild` and `rightChild`) } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt index 0d09d2a..381fc91 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -2,6 +2,19 @@ package tree_trippers.nodes.binary_nodes import tree_trippers.nodes.SearchTreeNode -public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>: SearchTreeNode { +public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>(val key: K, var value: V): SearchTreeNode { + public var leftChild: N? = null + public var rightChild: N? = null + override fun getKeys(): List { + return listOf(key) + } + + override fun getValues(): List { + return listOf(value) + } + + override fun getChildren(): List { + return listOfNotNull(leftChild, rightChild) + } } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt index 92ec05c..c7d9cd5 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt @@ -1,5 +1,5 @@ package tree_trippers.nodes.binary_nodes -public class BSTreeNode, V>: AbstractBSTreeNode>() { +public class BSTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt index 785409e..4096a76 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt @@ -1,5 +1,10 @@ package tree_trippers.nodes.binary_nodes -public class RBTreeNode, V>: AbstractBSTreeNode>() { +public class RBTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { + var isRed: Boolean = false + var blackHeight: Int = 1 + // TODO(Create constructors for red-black nodes) + // TODO(Create private method for update black height `updateBlackHeight`) + // TODO(Connect method `updateBlackHeight` to setters of properties: `isRed` and `leftChild` and `rightChild`) } \ No newline at end of file From 6d0184740fd8291e6dad709213b70acf5ab994f7 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 14 Mar 2024 01:09:05 +0300 Subject: [PATCH 006/122] Update search-tree interface and simple init by abstract binary search tree properties as root and tree size --- lib/src/main/kotlin/tree_trippers/SearchTree.kt | 4 +--- .../kotlin/tree_trippers/binary_trees/AbstractBSTree.kt | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/tree_trippers/SearchTree.kt b/lib/src/main/kotlin/tree_trippers/SearchTree.kt index 4a27c78..558f8d5 100644 --- a/lib/src/main/kotlin/tree_trippers/SearchTree.kt +++ b/lib/src/main/kotlin/tree_trippers/SearchTree.kt @@ -3,7 +3,5 @@ package tree_trippers import tree_trippers.nodes.SearchTreeNode public interface SearchTree, V, N: SearchTreeNode> { - - - + public fun size(): Int } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt index 7663372..ea71581 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt @@ -4,4 +4,10 @@ import tree_trippers.SearchTree import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { + protected var root: N? = null + private var size: Int = 0 + + override fun size(): Int { + return size + } } \ No newline at end of file From 9fda59bec0e495be25af8ed0256f634c2e280387 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 14 Mar 2024 09:13:29 +0300 Subject: [PATCH 007/122] Update abstract node class to calling update data method for balanced tree's nodes --- .../nodes/binary_nodes/AVLTreeNode.kt | 19 ++++++++----------- .../nodes/binary_nodes/AbstractBSTreeNode.kt | 16 ++++++++++++++++ .../nodes/binary_nodes/BSTreeNode.kt | 5 +++++ .../nodes/binary_nodes/RBTreeNode.kt | 4 ++++ 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt index d19210e..10c785d 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt @@ -5,21 +5,18 @@ import kotlin.math.max public class AVLTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { public var height: Int = 1 - constructor(key: K, value: V, leftChild: AVLTreeNode?, rightChild: AVLTreeNode?) : this(key, value) { - this.leftChild = leftChild - this.rightChild = rightChild - updateHeight() - } - - private fun updateHeight() { + private fun updateHeight(): Unit{ // TODO(Create tests for it method) - var maxChildrenHeight: Int = 0 + var maxChildHeight: Int = 0 val children: List> = getChildren() children.forEach() { - child -> maxChildrenHeight = max(maxChildrenHeight, child.height) + child -> maxChildHeight = max(maxChildHeight, child.height) } - height = 1 + maxChildrenHeight + height = 1 + maxChildHeight + } + + protected override fun updateNodeData(): Unit { + return updateHeight() } - // TODO(Connect method `updateHeight` to setters of properties: `leftChild` and `rightChild`) } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt index 381fc91..96a80c7 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -4,7 +4,21 @@ import tree_trippers.nodes.SearchTreeNode public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>(val key: K, var value: V): SearchTreeNode { public var leftChild: N? = null + set(value) { + field = value + updateNodeData() + } + public var rightChild: N? = null + set(value) { + field = value + updateNodeData() + } + + constructor(key: K, value: V, leftChild: N?, rightChild: N?) : this(key, value) { + this.leftChild = leftChild + this.rightChild = rightChild + } override fun getKeys(): List { return listOf(key) @@ -17,4 +31,6 @@ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeN override fun getChildren(): List { return listOfNotNull(leftChild, rightChild) } + + protected abstract fun updateNodeData(): Unit } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt index c7d9cd5..b0567cb 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt @@ -1,5 +1,10 @@ package tree_trippers.nodes.binary_nodes public class BSTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { + protected override fun updateNodeData(): Unit { + // Nothing to do + // TODO(Check to valid used) + return Unit + } } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt index 4096a76..6130f85 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt @@ -4,6 +4,10 @@ public class RBTreeNode, V>(key: K, value: V): AbstractBSTreeNo var isRed: Boolean = false var blackHeight: Int = 1 + override fun updateNodeData() { + TODO("Not yet implemented") + } + // TODO(Create constructors for red-black nodes) // TODO(Create private method for update black height `updateBlackHeight`) // TODO(Connect method `updateBlackHeight` to setters of properties: `isRed` and `leftChild` and `rightChild`) From e53ad61768bb586dcb5d3fdd40f4ce612442d396 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 14 Mar 2024 10:39:02 +0300 Subject: [PATCH 008/122] Adding simple ReadMe file with description about library by path ./lib/ReadMe.md --- lib/ReadMe.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 lib/ReadMe.md diff --git a/lib/ReadMe.md b/lib/ReadMe.md new file mode 100644 index 0000000..c3db8ab --- /dev/null +++ b/lib/ReadMe.md @@ -0,0 +1,15 @@ +# Kotlin Library `TreeTrippers` + +*** + +## Description + +Library `TreeTrippers` for working with data in the format of search trees. + +Supported searches trees: + +- [ ] `BinarySearchTree` (BST) +- [ ] `AVLTree` +- [ ] `RedBlackTree` + +The library supports the extension both internally (future library updates) and externally (implemented by the user). From ac6bd18cb93db547dc799f3bb48f3163e762e3b5 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 14 Mar 2024 10:42:20 +0300 Subject: [PATCH 009/122] Adding LICENSE file with MIT-License description --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6d1c1dd --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 salaar + +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 f50ce8f02b34727f5dd7ae530fc8476812be1f5d Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 14 Mar 2024 11:30:20 +0300 Subject: [PATCH 010/122] Update LICENSE file with MIT-License description by fix names of the rights holders --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 6d1c1dd..5a5e946 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 salaar +Copyright (c) 2024 Ilia Suponev, Rodionov Maxim, Vladimir Zaikin 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 d6b363528c0211da8f444d17c66187b6cf46534c Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Wed, 20 Mar 2024 00:10:20 +0300 Subject: [PATCH 011/122] feat(AVLTree): implement balance functions --- .../tree_trippers/binary_trees/AVLTree.kt | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt index 2fc8462..1024e75 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt @@ -3,4 +3,59 @@ package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.AVLTreeNode public class AVLTree, V>: AbstractBSTree>() { + + private fun getBalance(node: AVLTreeNode ): Int { + return (node.rightChild?.height ?: 0) - (node.leftChild?.height ?: 0) + } + + private fun swapNodes(node1: AVLTreeNode, node2: AVLTreeNode) { + val node1Key = node1.key + node1.key = node2.key + node2.key = node1Key + val node1Value = node1.value + node1.value = node2.value + node2.value = node1Value + } + + private fun AVLRightRotation(node: AVLTreeNode ): AVLTreeNode { + node.leftChild?.let { swapNodes(node, it) } + val temp = node.rightChild + node.rightChild = node.leftChild + node.leftChild = node.rightChild?.leftChild + node.rightChild?.leftChild = node.rightChild?.rightChild + node.rightChild?.rightChild = temp + node.rightChild?.updateHeight() + node.updateHeight() + return node.leftChild ?: node + } + + private fun AVLLeftRotation(node: AVLTreeNode ): AVLTreeNode { + node.rightChild?.let { swapNodes(node, it) } + val temp = node.leftChild + node.leftChild = node.rightChild + node.rightChild = node.leftChild?.rightChild + node.leftChild?.rightChild = node.leftChild?.leftChild + node.leftChild?.leftChild = temp + node.leftChild?.updateHeight() + node.updateHeight() + return node.rightChild ?: node + } + + private fun balance(node: AVLTreeNode ): AVLTreeNode { + when (getBalance(node)) { + -2 -> { + if (node.leftChild?.let { getBalance(it) } == 1) { + AVLLeftRotation(node.leftChild!!) + } + AVLRightRotation(node) + } + 2 -> { + if (node.rightChild?.let { getBalance(it) } == -1) { + AVLRightRotation(node.rightChild!!) + } + AVLLeftRotation(node) + } + } + return node + } } \ No newline at end of file From 30b28155cf20499738dcbe975e5d46c712f2f3a3 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Wed, 20 Mar 2024 00:12:25 +0300 Subject: [PATCH 012/122] feat(AVLTree): implement insert --- .../tree_trippers/binary_trees/AVLTree.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt index 1024e75..ac8451d 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt @@ -4,6 +4,26 @@ import tree_trippers.nodes.binary_nodes.AVLTreeNode public class AVLTree, V>: AbstractBSTree>() { + override fun insert (key: K, value: V) { + this.size++ + if (this.root == null) root = AVLTreeNode(key, value) + else this.root?.let { add(it, AVLTreeNode(key, value)) } + } + + private fun add(root: AVLTreeNode, node: AVLTreeNode) { + if (node.key < root.key) { + if (root.leftChild == null) root.leftChild = node + else add(root.leftChild!!, node) + } else if (node.key == root.key) { + root.value = node.value + } else { + if (root.rightChild == null) root.rightChild = node + else add(root.rightChild!!, node) + } + root.updateHeight() + balance(root) + } + private fun getBalance(node: AVLTreeNode ): Int { return (node.rightChild?.height ?: 0) - (node.leftChild?.height ?: 0) } From ca71abfd9151d900ba6f64aef0828ca07ec4cd24 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Wed, 20 Mar 2024 00:14:52 +0300 Subject: [PATCH 013/122] feat(AVLTree): implement remove and removeWithDefault --- .../tree_trippers/binary_trees/AVLTree.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt index ac8451d..2f6c6fe 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt @@ -24,6 +24,48 @@ public class AVLTree, V>: AbstractBSTree?, key: K): AVLTreeNode? { + var tempRoot = root //This is necessary to be able to change the root + if (tempRoot == null) return null + else if (key < tempRoot.key) tempRoot.leftChild = tempRoot.leftChild?.let { delete(it, key) } + else if (key > tempRoot.key) tempRoot.rightChild = tempRoot.rightChild?.let { delete(it, key) } + else { + if (tempRoot.rightChild == null || tempRoot.leftChild == null ) { + tempRoot = if (tempRoot.leftChild == null) tempRoot.rightChild else tempRoot.leftChild + } else { + val maxLeftNode: AVLTreeNode = maxDescendantForAVL(tempRoot.leftChild!!) + tempRoot.key = maxLeftNode.key + tempRoot.value = maxLeftNode.value + tempRoot.leftChild = delete(tempRoot.leftChild, maxLeftNode.key) + } + } + if (tempRoot != null) { + tempRoot.updateHeight() + balance(tempRoot) + } + return tempRoot + } + private fun getBalance(node: AVLTreeNode ): Int { return (node.rightChild?.height ?: 0) - (node.leftChild?.height ?: 0) } From 37609c76300a4cb9439fb9970fe7c0a169614024 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Wed, 20 Mar 2024 00:17:53 +0300 Subject: [PATCH 014/122] refactor(AVLTreeNode): simplify updatingHeight and remove updateNodeData --- .../nodes/binary_nodes/AVLTreeNode.kt | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt index 10c785d..36317f2 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt @@ -5,18 +5,9 @@ import kotlin.math.max public class AVLTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { public var height: Int = 1 - private fun updateHeight(): Unit{ - // TODO(Create tests for it method) - var maxChildHeight: Int = 0 - val children: List> = getChildren() - children.forEach() { - child -> maxChildHeight = max(maxChildHeight, child.height) - } - height = 1 + maxChildHeight + fun updateHeight() { + val leftHeight = this.leftChild?.height ?: 0 + val rightHeight = this.rightChild?.height ?: 0 + height = (max(leftHeight, rightHeight) + 1) } - - protected override fun updateNodeData(): Unit { - return updateHeight() - } - } \ No newline at end of file From ff3f2e015f4912da7f0915e34a45d2d9ab6d5b84 Mon Sep 17 00:00:00 2001 From: Vladimir Zaikin Date: Wed, 20 Mar 2024 13:38:04 +0300 Subject: [PATCH 015/122] feat: add general methods and rename file RedBlackTree --- .../main/kotlin/tree_trippers/SearchTree.kt | 14 +++- .../binary_trees/AbstractBSTree.kt | 82 ++++++++++++++++++- .../{RedBlackTree.kt => RBTree.kt} | 2 + .../tree_trippers/nodes/SearchTreeNode.kt | 3 - .../nodes/binary_nodes/AbstractBSTreeNode.kt | 23 +----- .../nodes/binary_nodes/BSTreeNode.kt | 10 +-- 6 files changed, 101 insertions(+), 33 deletions(-) rename lib/src/main/kotlin/tree_trippers/binary_trees/{RedBlackTree.kt => RBTree.kt} (87%) diff --git a/lib/src/main/kotlin/tree_trippers/SearchTree.kt b/lib/src/main/kotlin/tree_trippers/SearchTree.kt index 558f8d5..ae42fc9 100644 --- a/lib/src/main/kotlin/tree_trippers/SearchTree.kt +++ b/lib/src/main/kotlin/tree_trippers/SearchTree.kt @@ -3,5 +3,17 @@ package tree_trippers import tree_trippers.nodes.SearchTreeNode public interface SearchTree, V, N: SearchTreeNode> { + public fun search(key: K): V? + public fun searchOrDefault(key: K, defaultValue: V): V + public fun isContains(key: K): Boolean + public fun insert(key: K, value: V) + public fun keys(): List + public fun values(): List + public fun maxDescendant(key: K): V? + public fun max(): V? + public fun minDescendant(key: K): V? + public fun min(): V? public fun size(): Int -} \ No newline at end of file + public fun remove(key: K): V? + public fun removeWithDefault(key: K, defaultValue: V): V +} diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt index ea71581..ca50ac3 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt @@ -2,11 +2,89 @@ package tree_trippers.binary_trees import tree_trippers.SearchTree import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode +import tree_trippers.nodes.binary_nodes.BSTreeNode -abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { +public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: + SearchTree { protected var root: N? = null - private var size: Int = 0 + protected var size: Int = 0 + override fun search(key: K): V? { + val resultSearch = searchNode(key) + if (resultSearch != null) + return resultSearch.value + return null + } + override fun searchOrDefault(key: K, defaultValue: V): V { + val resultSearch = search(key) + if (resultSearch != null) + return resultSearch + return defaultValue + } + override fun isContains(key: K): Boolean { + return (search(key) != null) + } + protected fun searchNode(key: K): N? { + var nodeCurrent = this.root + + while (nodeCurrent != null) { + if (key == nodeCurrent.key) { + return nodeCurrent + } else if (key < nodeCurrent.key) { + if (nodeCurrent.leftChild == null) + break + + nodeCurrent = nodeCurrent.leftChild + } else { + if (nodeCurrent.rightChild == null) + break + + nodeCurrent = nodeCurrent.rightChild + } + } + + return null + } + override fun maxDescendant(key: K): V? { + var resultSearch = searchNode(key) + + while (resultSearch != null) { + if (resultSearch.rightChild == null) + return resultSearch.value + + resultSearch = resultSearch.rightChild + } + + return null + } + override fun max(): V? { + if (this.root != null) + return maxDescendant(this.root!!.key) // todo(!!) + return null + } + override fun minDescendant(key: K): V? { + var resultSearch = searchNode(key) + + while (resultSearch != null) { + if (resultSearch.leftChild == null) + return resultSearch.value + + resultSearch = resultSearch.leftChild + } + + return null + } + override fun min(): V? { + if (this.root != null) + return minDescendant(this.root!!.key) // todo(!!) + return null + } + override fun keys(): List { + return listOf() // todo(с помощью стека можно выводить (по росту ключей)) + } + override fun values(): List { + return listOf() // todo(с помощью стека можно выводить (по росту ключей)) + } override fun size(): Int { return size } diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/RedBlackTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt similarity index 87% rename from lib/src/main/kotlin/tree_trippers/binary_trees/RedBlackTree.kt rename to lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt index d6a7dc9..97a2e8d 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/RedBlackTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt @@ -1,3 +1,5 @@ +@file:Suppress("UNUSED") + package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.RBTreeNode diff --git a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt index 9cd7239..c831569 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt @@ -2,10 +2,7 @@ package tree_trippers.nodes // TODO(Adding docs for methods) public interface SearchTreeNode, V, N: SearchTreeNode> { - public fun getKeys(): List - public fun getValues(): List - public fun getChildren(): List } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt index 96a80c7..0f860c5 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -2,35 +2,20 @@ package tree_trippers.nodes.binary_nodes import tree_trippers.nodes.SearchTreeNode -public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>(val key: K, var value: V): SearchTreeNode { +public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>( + public var key: K, // todo(val) + public var value: V +): SearchTreeNode { public var leftChild: N? = null - set(value) { - field = value - updateNodeData() - } - public var rightChild: N? = null - set(value) { - field = value - updateNodeData() - } - - constructor(key: K, value: V, leftChild: N?, rightChild: N?) : this(key, value) { - this.leftChild = leftChild - this.rightChild = rightChild - } override fun getKeys(): List { return listOf(key) } - override fun getValues(): List { return listOf(value) } - override fun getChildren(): List { return listOfNotNull(leftChild, rightChild) } - - protected abstract fun updateNodeData(): Unit } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt index b0567cb..e91978f 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt @@ -1,10 +1,4 @@ package tree_trippers.nodes.binary_nodes -public class BSTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { - protected override fun updateNodeData(): Unit { - // Nothing to do - // TODO(Check to valid used) - return Unit - } - -} \ No newline at end of file +public class BSTreeNode, V>(key: K, value: V): + AbstractBSTreeNode>(key, value) From 977da3cd8fca760683e2d565e77c7d9174d305b0 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Wed, 20 Mar 2024 14:15:34 +0300 Subject: [PATCH 016/122] feat(Refactoring): The implementation of the removeWithDefault(K) method is placed in an abstract class. Clearing some todos from labels. Corrected the AVLTree.kt file by adding the missing maxDescendantForAVL(AVLTreeNode) AVLTree method to the class. Added TODO marks in the BSTree file defining the implementation of the insert & remove methods --- .../main/kotlin/tree_trippers/SearchTree.kt | 6 ++--- .../tree_trippers/binary_trees/AVLTree.kt | 16 +++++-------- .../binary_trees/AbstractBSTree.kt | 24 ++++++++++++------- .../tree_trippers/binary_trees/BSTree.kt | 7 ++++++ .../tree_trippers/binary_trees/RBTree.kt | 9 ++++++- .../tree_trippers/nodes/SearchTreeNode.kt | 2 -- .../nodes/binary_nodes/AbstractBSTreeNode.kt | 6 ----- .../nodes/binary_nodes/BSTreeNode.kt | 2 +- .../nodes/binary_nodes/RBTreeNode.kt | 9 ------- 9 files changed, 40 insertions(+), 41 deletions(-) diff --git a/lib/src/main/kotlin/tree_trippers/SearchTree.kt b/lib/src/main/kotlin/tree_trippers/SearchTree.kt index ae42fc9..6f73c72 100644 --- a/lib/src/main/kotlin/tree_trippers/SearchTree.kt +++ b/lib/src/main/kotlin/tree_trippers/SearchTree.kt @@ -2,11 +2,13 @@ package tree_trippers import tree_trippers.nodes.SearchTreeNode -public interface SearchTree, V, N: SearchTreeNode> { +public interface SearchTree, V, N : SearchTreeNode> { public fun search(key: K): V? public fun searchOrDefault(key: K, defaultValue: V): V public fun isContains(key: K): Boolean public fun insert(key: K, value: V) + public fun remove(key: K): V? + public fun removeWithDefault(key: K, defaultValue: V): V public fun keys(): List public fun values(): List public fun maxDescendant(key: K): V? @@ -14,6 +16,4 @@ public interface SearchTree, V, N: SearchTreeNode> { public fun minDescendant(key: K): V? public fun min(): V? public fun size(): Int - public fun remove(key: K): V? - public fun removeWithDefault(key: K, defaultValue: V): V } diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt index 2f6c6fe..4932736 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt @@ -34,16 +34,6 @@ public class AVLTree, V>: AbstractBSTree?, key: K): AVLTreeNode? { var tempRoot = root //This is necessary to be able to change the root if (tempRoot == null) return null @@ -120,4 +110,10 @@ public class AVLTree, V>: AbstractBSTree): AVLTreeNode { // todo(MAXIM) + if (node.rightChild == null) return node + return maxDescendantForAVL(node.rightChild!!) + } + } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt index ca50ac3..aafd5a3 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt @@ -10,21 +10,20 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< protected var size: Int = 0 override fun search(key: K): V? { - val resultSearch = searchNode(key) - if (resultSearch != null) - return resultSearch.value - return null + return searchNode(key)?.value } override fun searchOrDefault(key: K, defaultValue: V): V { - val resultSearch = search(key) - if (resultSearch != null) - return resultSearch - return defaultValue + return search(key) ?: defaultValue } override fun isContains(key: K): Boolean { return (search(key) != null) } - protected fun searchNode(key: K): N? { + + override fun removeWithDefault(key: K, defaultValue: V): V { + return remove(key) ?: defaultValue + } + + private fun searchNode(key: K): N? { var nodeCurrent = this.root while (nodeCurrent != null) { @@ -45,6 +44,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return null } + override fun maxDescendant(key: K): V? { var resultSearch = searchNode(key) @@ -57,11 +57,13 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return null } + override fun max(): V? { if (this.root != null) return maxDescendant(this.root!!.key) // todo(!!) return null } + override fun minDescendant(key: K): V? { var resultSearch = searchNode(key) @@ -74,17 +76,21 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return null } + override fun min(): V? { if (this.root != null) return minDescendant(this.root!!.key) // todo(!!) return null } + override fun keys(): List { return listOf() // todo(с помощью стека можно выводить (по росту ключей)) } + override fun values(): List { return listOf() // todo(с помощью стека можно выводить (по росту ключей)) } + override fun size(): Int { return size } diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt index 9618d08..f5bc5ef 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt @@ -3,4 +3,11 @@ package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.BSTreeNode public class BSTree, V>: AbstractBSTree>() { + override fun insert(key: K, value: V) { + TODO("Not yet implemented") + } + + override fun remove(key: K): V? { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt index 97a2e8d..e2d47f1 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt @@ -4,5 +4,12 @@ package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.RBTreeNode -public class RedBlackTree, V>: AbstractBSTree>() { +public class RBTree, V>: AbstractBSTree>() { + override fun insert(key: K, value: V) { + TODO("Not yet implemented") + } + + override fun remove(key: K): V? { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt index c831569..603e340 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt @@ -2,7 +2,5 @@ package tree_trippers.nodes // TODO(Adding docs for methods) public interface SearchTreeNode, V, N: SearchTreeNode> { - public fun getKeys(): List - public fun getValues(): List public fun getChildren(): List } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt index 0f860c5..b9abbf3 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -9,12 +9,6 @@ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeN public var leftChild: N? = null public var rightChild: N? = null - override fun getKeys(): List { - return listOf(key) - } - override fun getValues(): List { - return listOf(value) - } override fun getChildren(): List { return listOfNotNull(leftChild, rightChild) } diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt index e91978f..be5e302 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt @@ -1,4 +1,4 @@ package tree_trippers.nodes.binary_nodes -public class BSTreeNode, V>(key: K, value: V): +public class BSTreeNode, V>(key: K, value: V) : AbstractBSTreeNode>(key, value) diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt index 6130f85..d4cba3f 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt @@ -2,13 +2,4 @@ package tree_trippers.nodes.binary_nodes public class RBTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { var isRed: Boolean = false - var blackHeight: Int = 1 - - override fun updateNodeData() { - TODO("Not yet implemented") - } - - // TODO(Create constructors for red-black nodes) - // TODO(Create private method for update black height `updateBlackHeight`) - // TODO(Connect method `updateBlackHeight` to setters of properties: `isRed` and `leftChild` and `rightChild`) } \ No newline at end of file From a07d41c8f6d63484bf07a883fc5824120e588807 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Wed, 20 Mar 2024 14:30:30 +0300 Subject: [PATCH 017/122] feat(Addding iterators): Add subdirrectory with files of intreface SearchTreeIterator and enum of orders to iterate on search trees and class of BinarySearchTreeIterator to iterate on BST and other implementations of it --- .../main/kotlin/tree_trippers/SearchTree.kt | 10 +- .../binary_trees/AbstractBSTree.kt | 47 +++++- .../tree_trippers/binary_trees/RBTree.kt | 2 - .../iterators/BinarySearchTreeIterator.kt | 142 ++++++++++++++++++ .../iterators/IterationOrders.kt | 5 + .../iterators/SearchTreeIterator.kt | 7 + .../tree_trippers/nodes/SearchTreeNode.kt | 2 + .../main/kotlin/tree_trippers/nodes/Utils.kt | 11 ++ .../nodes/binary_nodes/AbstractBSTreeNode.kt | 15 ++ .../nodes/binary_nodes/RBTreeNode.kt | 3 +- 10 files changed, 230 insertions(+), 14 deletions(-) create mode 100755 lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt create mode 100755 lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt create mode 100755 lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt create mode 100755 lib/src/main/kotlin/tree_trippers/nodes/Utils.kt diff --git a/lib/src/main/kotlin/tree_trippers/SearchTree.kt b/lib/src/main/kotlin/tree_trippers/SearchTree.kt index 6f73c72..b88b4d1 100644 --- a/lib/src/main/kotlin/tree_trippers/SearchTree.kt +++ b/lib/src/main/kotlin/tree_trippers/SearchTree.kt @@ -1,19 +1,23 @@ package tree_trippers +import tree_trippers.iterators.IterationOrders +import tree_trippers.iterators.SearchTreeIterator import tree_trippers.nodes.SearchTreeNode -public interface SearchTree, V, N : SearchTreeNode> { +public interface SearchTree, V, N : SearchTreeNode>: Iterable> { public fun search(key: K): V? public fun searchOrDefault(key: K, defaultValue: V): V public fun isContains(key: K): Boolean public fun insert(key: K, value: V) public fun remove(key: K): V? public fun removeWithDefault(key: K, defaultValue: V): V - public fun keys(): List - public fun values(): List public fun maxDescendant(key: K): V? public fun max(): V? public fun minDescendant(key: K): V? public fun min(): V? public fun size(): Int + public fun iterator(order: IterationOrders): SearchTreeIterator + public fun forEach(order: IterationOrders, action: (Pair) -> Unit) + public fun toString(order: IterationOrders): String + public fun toTreeViewString(): String } diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt index aafd5a3..f97e4df 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt @@ -1,20 +1,25 @@ package tree_trippers.binary_trees import tree_trippers.SearchTree +import tree_trippers.iterators.BinarySearchTreeIterator +import tree_trippers.iterators.IterationOrders import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode import tree_trippers.nodes.binary_nodes.BSTreeNode +import tree_trippers.nodes.notNullNodeAction -public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: - SearchTree { +public abstract class AbstractBSTree, V, N : AbstractBSTreeNode> : + SearchTree { protected var root: N? = null protected var size: Int = 0 override fun search(key: K): V? { return searchNode(key)?.value } + override fun searchOrDefault(key: K, defaultValue: V): V { return search(key) ?: defaultValue } + override fun isContains(key: K): Boolean { return (search(key) != null) } @@ -83,15 +88,41 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return null } - override fun keys(): List { - return listOf() // todo(с помощью стека можно выводить (по росту ключей)) + override fun size(): Int { + return size } - override fun values(): List { - return listOf() // todo(с помощью стека можно выводить (по росту ключей)) + override fun iterator(): BinarySearchTreeIterator { + return iterator(IterationOrders.WIDTH_ORDER) } - override fun size(): Int { - return size + override fun iterator(order: IterationOrders): BinarySearchTreeIterator { + return BinarySearchTreeIterator(root, order) + } + + override fun forEach(order: IterationOrders, action: (Pair) -> Unit) { + val treeIterator: BinarySearchTreeIterator = iterator(order) + while (treeIterator.hasNext()) { + action(treeIterator.next()) + } } + + override fun toString(): String { + return toString(IterationOrders.WIDTH_ORDER) + } + + override fun toString(order: IterationOrders): String { + val sb = StringBuilder() + this.forEach(order) { pair: Pair -> + sb.append("${pair.first}: ${pair.second}, ") + } + return "${this.javaClass.simpleName}($sb)" + } + + override fun toTreeViewString(): String { + val sb = StringBuilder() + notNullNodeAction(root, Unit) {node -> node.toTreeViewString(0, sb)} + return "${this.javaClass.simpleName}(\n$sb)" + } + } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt index e2d47f1..aad5c21 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt @@ -1,5 +1,3 @@ -@file:Suppress("UNUSED") - package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.RBTreeNode diff --git a/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt b/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt new file mode 100755 index 0000000..7fe8fcf --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt @@ -0,0 +1,142 @@ +package tree_trippers.iterators + +import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode +import java.util.LinkedList +import java.util.Queue + +class BinarySearchTreeIterator, V, N : AbstractBSTreeNode> : + SearchTreeIterator { + private val iterationState: IterationState + + constructor(root: N?): this(root, IterationOrders.WIDTH_ORDER) + + constructor(root: N?, order: IterationOrders) { + iterationState = when (order) { + IterationOrders.WIDTH_ORDER -> WidthIterationState(root) + IterationOrders.INCREASING_ORDER -> IncreasingIterationState(root) + IterationOrders.DECREASING_ORDER -> DecreasingIterationState(root) + else -> throw Exception("Unsupported iteration order") + } + } + + override fun hasNext(): Boolean { + return iterationState.hasNext() + } + + override fun next(): Pair { + if (!hasNext()) throw Exception("Try get next element from empty iterator") + return iterationState.next() + } + + private interface IterationState, V, N: AbstractBSTreeNode> { + abstract fun hasNext(): Boolean + abstract fun next(): Pair + } + + private class WidthIterationState, V, N: AbstractBSTreeNode>(root: N?): IterationState { + private val queue: Queue = LinkedList() + + init { + if (root != null) queue.add(root) + } + + override fun hasNext(): Boolean { + return queue.size > 0 + } + + override fun next(): Pair { + if (!hasNext()) throw Exception("Try get next element from the end of iterator state") + val currentNode: N = queue.poll() + currentNode.getChildren().forEach() { + child -> queue.add(child) + } + return Pair(currentNode.key, currentNode.value) + } + } + + private class IncreasingIterationState, V, N: AbstractBSTreeNode>(root: N?): IterationState { + private val unprocessedNodesStack: LinkedList = LinkedList() + private val semiProcessedNodesStack: LinkedList = LinkedList() + + init { + if (root != null) unprocessedNodesStack.add(root) + } + + override fun hasNext(): Boolean { + return isHasUnprocessedNodes() || isHasSemiProcessedNodes() + } + + private fun isHasSemiProcessedNodes(): Boolean { + return semiProcessedNodesStack.isNotEmpty() + } + + private fun isHasUnprocessedNodes(): Boolean { + return unprocessedNodesStack.isNotEmpty() + } + + override fun next(): Pair { + if (!hasNext()) throw Exception("Try get next element from the end of iterator state") + var currentNode: N + + while (isHasUnprocessedNodes()) { + currentNode = unprocessedNodesStack.pollFirst() + semiProcessedNodesStack.addFirst(currentNode) + if (currentNode.leftChild != null) { + unprocessedNodesStack.addFirst(currentNode.leftChild) + } else { + semiProcessedNodesStack.pollFirst() + if (currentNode.rightChild != null) unprocessedNodesStack.addFirst(currentNode.rightChild) + return Pair(currentNode.key, currentNode.value) + } + } + + currentNode = semiProcessedNodesStack.pollFirst() + if (currentNode.rightChild != null) unprocessedNodesStack.addFirst(currentNode.rightChild) + return Pair(currentNode.key, currentNode.value) + } + + } + + private class DecreasingIterationState, V, N: AbstractBSTreeNode>(root: N?): IterationState { + private val unprocessedNodesStack: LinkedList = LinkedList() + private val semiProcessedNodesStack: LinkedList = LinkedList() + + init { + if (root != null) unprocessedNodesStack.add(root) + } + + override fun hasNext(): Boolean { + return isHasUnprocessedNodes() || isHasSemiProcessedNodes() + } + + private fun isHasSemiProcessedNodes(): Boolean { + return semiProcessedNodesStack.isNotEmpty() + } + + private fun isHasUnprocessedNodes(): Boolean { + return unprocessedNodesStack.isNotEmpty() + } + + override fun next(): Pair { + if (!hasNext()) throw Exception("Try get next element from the end of iterator state") + var currentNode: N + + while (isHasUnprocessedNodes()) { + currentNode = unprocessedNodesStack.pollFirst() + semiProcessedNodesStack.addFirst(currentNode) + if (currentNode.rightChild != null) { + unprocessedNodesStack.addFirst(currentNode.rightChild) + } else { + semiProcessedNodesStack.pollFirst() + if (currentNode.leftChild != null) unprocessedNodesStack.addFirst(currentNode.leftChild) + return Pair(currentNode.key, currentNode.value) + } + } + + currentNode = semiProcessedNodesStack.pollFirst() + if (currentNode.leftChild != null) unprocessedNodesStack.addFirst(currentNode.leftChild) + return Pair(currentNode.key, currentNode.value) + } + + } +} diff --git a/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt b/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt new file mode 100755 index 0000000..06015e6 --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt @@ -0,0 +1,5 @@ +package tree_trippers.iterators + +enum class IterationOrders { + WIDTH_ORDER, INCREASING_ORDER, DECREASING_ORDER, +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt b/lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt new file mode 100755 index 0000000..7225c4a --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt @@ -0,0 +1,7 @@ +package tree_trippers.iterators + +import tree_trippers.nodes.SearchTreeNode + +interface SearchTreeIterator, V, N: SearchTreeNode>: Iterator> { + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt index 603e340..99de974 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt @@ -3,4 +3,6 @@ package tree_trippers.nodes // TODO(Adding docs for methods) public interface SearchTreeNode, V, N: SearchTreeNode> { public fun getChildren(): List + public fun toSimpleViewString(): String + public fun toTreeViewString(indent: Int, sb: StringBuilder): Unit } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt b/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt new file mode 100755 index 0000000..fd2a31f --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt @@ -0,0 +1,11 @@ +package tree_trippers.nodes + +public fun notNullNodeAction(node: N?, nullNodeResult: R, action: (N) -> (R)): R { + if (node == null) return nullNodeResult + return action(node) +} + +public fun notNullNodeUpdate(node: N?, updateAction: (N) -> (Unit)): Unit { + if (node == null) return + return updateAction(node) +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt index b9abbf3..a245277 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -1,6 +1,7 @@ package tree_trippers.nodes.binary_nodes import tree_trippers.nodes.SearchTreeNode +import tree_trippers.nodes.notNullNodeAction public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>( public var key: K, // todo(val) @@ -12,4 +13,18 @@ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeN override fun getChildren(): List { return listOfNotNull(leftChild, rightChild) } + + override fun toString(): String { + return "${this.javaClass.simpleName}(key=$key, value=$value)" + } + + override fun toSimpleViewString(): String { + return "($key, $value)" + } + + override fun toTreeViewString(indent: Int, sb: StringBuilder) { + notNullNodeAction(this.leftChild, Unit) {node -> node.toTreeViewString(indent + 1, sb)} + sb.append("\t".repeat(indent)).append(this.toSimpleViewString()).append("\n") + notNullNodeAction(this.rightChild, Unit) {node -> node.toTreeViewString(indent + 1, sb)} + } } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt index d4cba3f..cfd3de4 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt @@ -2,4 +2,5 @@ package tree_trippers.nodes.binary_nodes public class RBTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { var isRed: Boolean = false -} \ No newline at end of file +} + From 5e771acd2bb79d7fd7d9cd869136be87e3e3ce21 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Wed, 20 Mar 2024 14:30:30 +0300 Subject: [PATCH 018/122] feat(Adding iterators) --- .../main/kotlin/tree_trippers/SearchTree.kt | 10 +- .../binary_trees/AbstractBSTree.kt | 47 +++++- .../tree_trippers/binary_trees/RBTree.kt | 2 - .../iterators/BinarySearchTreeIterator.kt | 142 ++++++++++++++++++ .../iterators/IterationOrders.kt | 5 + .../iterators/SearchTreeIterator.kt | 7 + .../tree_trippers/nodes/SearchTreeNode.kt | 2 + .../main/kotlin/tree_trippers/nodes/Utils.kt | 11 ++ .../nodes/binary_nodes/AbstractBSTreeNode.kt | 15 ++ .../nodes/binary_nodes/RBTreeNode.kt | 3 +- 10 files changed, 230 insertions(+), 14 deletions(-) create mode 100755 lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt create mode 100755 lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt create mode 100755 lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt create mode 100755 lib/src/main/kotlin/tree_trippers/nodes/Utils.kt diff --git a/lib/src/main/kotlin/tree_trippers/SearchTree.kt b/lib/src/main/kotlin/tree_trippers/SearchTree.kt index 6f73c72..b88b4d1 100644 --- a/lib/src/main/kotlin/tree_trippers/SearchTree.kt +++ b/lib/src/main/kotlin/tree_trippers/SearchTree.kt @@ -1,19 +1,23 @@ package tree_trippers +import tree_trippers.iterators.IterationOrders +import tree_trippers.iterators.SearchTreeIterator import tree_trippers.nodes.SearchTreeNode -public interface SearchTree, V, N : SearchTreeNode> { +public interface SearchTree, V, N : SearchTreeNode>: Iterable> { public fun search(key: K): V? public fun searchOrDefault(key: K, defaultValue: V): V public fun isContains(key: K): Boolean public fun insert(key: K, value: V) public fun remove(key: K): V? public fun removeWithDefault(key: K, defaultValue: V): V - public fun keys(): List - public fun values(): List public fun maxDescendant(key: K): V? public fun max(): V? public fun minDescendant(key: K): V? public fun min(): V? public fun size(): Int + public fun iterator(order: IterationOrders): SearchTreeIterator + public fun forEach(order: IterationOrders, action: (Pair) -> Unit) + public fun toString(order: IterationOrders): String + public fun toTreeViewString(): String } diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt index aafd5a3..f97e4df 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt @@ -1,20 +1,25 @@ package tree_trippers.binary_trees import tree_trippers.SearchTree +import tree_trippers.iterators.BinarySearchTreeIterator +import tree_trippers.iterators.IterationOrders import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode import tree_trippers.nodes.binary_nodes.BSTreeNode +import tree_trippers.nodes.notNullNodeAction -public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: - SearchTree { +public abstract class AbstractBSTree, V, N : AbstractBSTreeNode> : + SearchTree { protected var root: N? = null protected var size: Int = 0 override fun search(key: K): V? { return searchNode(key)?.value } + override fun searchOrDefault(key: K, defaultValue: V): V { return search(key) ?: defaultValue } + override fun isContains(key: K): Boolean { return (search(key) != null) } @@ -83,15 +88,41 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return null } - override fun keys(): List { - return listOf() // todo(с помощью стека можно выводить (по росту ключей)) + override fun size(): Int { + return size } - override fun values(): List { - return listOf() // todo(с помощью стека можно выводить (по росту ключей)) + override fun iterator(): BinarySearchTreeIterator { + return iterator(IterationOrders.WIDTH_ORDER) } - override fun size(): Int { - return size + override fun iterator(order: IterationOrders): BinarySearchTreeIterator { + return BinarySearchTreeIterator(root, order) + } + + override fun forEach(order: IterationOrders, action: (Pair) -> Unit) { + val treeIterator: BinarySearchTreeIterator = iterator(order) + while (treeIterator.hasNext()) { + action(treeIterator.next()) + } } + + override fun toString(): String { + return toString(IterationOrders.WIDTH_ORDER) + } + + override fun toString(order: IterationOrders): String { + val sb = StringBuilder() + this.forEach(order) { pair: Pair -> + sb.append("${pair.first}: ${pair.second}, ") + } + return "${this.javaClass.simpleName}($sb)" + } + + override fun toTreeViewString(): String { + val sb = StringBuilder() + notNullNodeAction(root, Unit) {node -> node.toTreeViewString(0, sb)} + return "${this.javaClass.simpleName}(\n$sb)" + } + } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt index e2d47f1..aad5c21 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt @@ -1,5 +1,3 @@ -@file:Suppress("UNUSED") - package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.RBTreeNode diff --git a/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt b/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt new file mode 100755 index 0000000..7fe8fcf --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt @@ -0,0 +1,142 @@ +package tree_trippers.iterators + +import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode +import java.util.LinkedList +import java.util.Queue + +class BinarySearchTreeIterator, V, N : AbstractBSTreeNode> : + SearchTreeIterator { + private val iterationState: IterationState + + constructor(root: N?): this(root, IterationOrders.WIDTH_ORDER) + + constructor(root: N?, order: IterationOrders) { + iterationState = when (order) { + IterationOrders.WIDTH_ORDER -> WidthIterationState(root) + IterationOrders.INCREASING_ORDER -> IncreasingIterationState(root) + IterationOrders.DECREASING_ORDER -> DecreasingIterationState(root) + else -> throw Exception("Unsupported iteration order") + } + } + + override fun hasNext(): Boolean { + return iterationState.hasNext() + } + + override fun next(): Pair { + if (!hasNext()) throw Exception("Try get next element from empty iterator") + return iterationState.next() + } + + private interface IterationState, V, N: AbstractBSTreeNode> { + abstract fun hasNext(): Boolean + abstract fun next(): Pair + } + + private class WidthIterationState, V, N: AbstractBSTreeNode>(root: N?): IterationState { + private val queue: Queue = LinkedList() + + init { + if (root != null) queue.add(root) + } + + override fun hasNext(): Boolean { + return queue.size > 0 + } + + override fun next(): Pair { + if (!hasNext()) throw Exception("Try get next element from the end of iterator state") + val currentNode: N = queue.poll() + currentNode.getChildren().forEach() { + child -> queue.add(child) + } + return Pair(currentNode.key, currentNode.value) + } + } + + private class IncreasingIterationState, V, N: AbstractBSTreeNode>(root: N?): IterationState { + private val unprocessedNodesStack: LinkedList = LinkedList() + private val semiProcessedNodesStack: LinkedList = LinkedList() + + init { + if (root != null) unprocessedNodesStack.add(root) + } + + override fun hasNext(): Boolean { + return isHasUnprocessedNodes() || isHasSemiProcessedNodes() + } + + private fun isHasSemiProcessedNodes(): Boolean { + return semiProcessedNodesStack.isNotEmpty() + } + + private fun isHasUnprocessedNodes(): Boolean { + return unprocessedNodesStack.isNotEmpty() + } + + override fun next(): Pair { + if (!hasNext()) throw Exception("Try get next element from the end of iterator state") + var currentNode: N + + while (isHasUnprocessedNodes()) { + currentNode = unprocessedNodesStack.pollFirst() + semiProcessedNodesStack.addFirst(currentNode) + if (currentNode.leftChild != null) { + unprocessedNodesStack.addFirst(currentNode.leftChild) + } else { + semiProcessedNodesStack.pollFirst() + if (currentNode.rightChild != null) unprocessedNodesStack.addFirst(currentNode.rightChild) + return Pair(currentNode.key, currentNode.value) + } + } + + currentNode = semiProcessedNodesStack.pollFirst() + if (currentNode.rightChild != null) unprocessedNodesStack.addFirst(currentNode.rightChild) + return Pair(currentNode.key, currentNode.value) + } + + } + + private class DecreasingIterationState, V, N: AbstractBSTreeNode>(root: N?): IterationState { + private val unprocessedNodesStack: LinkedList = LinkedList() + private val semiProcessedNodesStack: LinkedList = LinkedList() + + init { + if (root != null) unprocessedNodesStack.add(root) + } + + override fun hasNext(): Boolean { + return isHasUnprocessedNodes() || isHasSemiProcessedNodes() + } + + private fun isHasSemiProcessedNodes(): Boolean { + return semiProcessedNodesStack.isNotEmpty() + } + + private fun isHasUnprocessedNodes(): Boolean { + return unprocessedNodesStack.isNotEmpty() + } + + override fun next(): Pair { + if (!hasNext()) throw Exception("Try get next element from the end of iterator state") + var currentNode: N + + while (isHasUnprocessedNodes()) { + currentNode = unprocessedNodesStack.pollFirst() + semiProcessedNodesStack.addFirst(currentNode) + if (currentNode.rightChild != null) { + unprocessedNodesStack.addFirst(currentNode.rightChild) + } else { + semiProcessedNodesStack.pollFirst() + if (currentNode.leftChild != null) unprocessedNodesStack.addFirst(currentNode.leftChild) + return Pair(currentNode.key, currentNode.value) + } + } + + currentNode = semiProcessedNodesStack.pollFirst() + if (currentNode.leftChild != null) unprocessedNodesStack.addFirst(currentNode.leftChild) + return Pair(currentNode.key, currentNode.value) + } + + } +} diff --git a/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt b/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt new file mode 100755 index 0000000..06015e6 --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt @@ -0,0 +1,5 @@ +package tree_trippers.iterators + +enum class IterationOrders { + WIDTH_ORDER, INCREASING_ORDER, DECREASING_ORDER, +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt b/lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt new file mode 100755 index 0000000..7225c4a --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt @@ -0,0 +1,7 @@ +package tree_trippers.iterators + +import tree_trippers.nodes.SearchTreeNode + +interface SearchTreeIterator, V, N: SearchTreeNode>: Iterator> { + +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt index 603e340..99de974 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt @@ -3,4 +3,6 @@ package tree_trippers.nodes // TODO(Adding docs for methods) public interface SearchTreeNode, V, N: SearchTreeNode> { public fun getChildren(): List + public fun toSimpleViewString(): String + public fun toTreeViewString(indent: Int, sb: StringBuilder): Unit } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt b/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt new file mode 100755 index 0000000..fd2a31f --- /dev/null +++ b/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt @@ -0,0 +1,11 @@ +package tree_trippers.nodes + +public fun notNullNodeAction(node: N?, nullNodeResult: R, action: (N) -> (R)): R { + if (node == null) return nullNodeResult + return action(node) +} + +public fun notNullNodeUpdate(node: N?, updateAction: (N) -> (Unit)): Unit { + if (node == null) return + return updateAction(node) +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt index b9abbf3..a245277 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -1,6 +1,7 @@ package tree_trippers.nodes.binary_nodes import tree_trippers.nodes.SearchTreeNode +import tree_trippers.nodes.notNullNodeAction public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>( public var key: K, // todo(val) @@ -12,4 +13,18 @@ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeN override fun getChildren(): List { return listOfNotNull(leftChild, rightChild) } + + override fun toString(): String { + return "${this.javaClass.simpleName}(key=$key, value=$value)" + } + + override fun toSimpleViewString(): String { + return "($key, $value)" + } + + override fun toTreeViewString(indent: Int, sb: StringBuilder) { + notNullNodeAction(this.leftChild, Unit) {node -> node.toTreeViewString(indent + 1, sb)} + sb.append("\t".repeat(indent)).append(this.toSimpleViewString()).append("\n") + notNullNodeAction(this.rightChild, Unit) {node -> node.toTreeViewString(indent + 1, sb)} + } } \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt index d4cba3f..cfd3de4 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt @@ -2,4 +2,5 @@ package tree_trippers.nodes.binary_nodes public class RBTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { var isRed: Boolean = false -} \ No newline at end of file +} + From 6e8d900050dcfb4931d59b139c228485bcad6e1b Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Wed, 20 Mar 2024 17:29:06 +0300 Subject: [PATCH 019/122] feat(Adding insert operation at RBTree) Add implementation of insert method of SearchTree at class RBTree and add new method at SearchTree interface with name insertIfAbsent --- .../main/kotlin/tree_trippers/SearchTree.kt | 1 + .../tree_trippers/binary_trees/AVLTree.kt | 6 ++ .../tree_trippers/binary_trees/BSTree.kt | 4 + .../tree_trippers/binary_trees/RBTree.kt | 87 ++++++++++++++++++- 4 files changed, 95 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/tree_trippers/SearchTree.kt b/lib/src/main/kotlin/tree_trippers/SearchTree.kt index b88b4d1..3428f05 100644 --- a/lib/src/main/kotlin/tree_trippers/SearchTree.kt +++ b/lib/src/main/kotlin/tree_trippers/SearchTree.kt @@ -9,6 +9,7 @@ public interface SearchTree, V, N : SearchTreeNode>: public fun searchOrDefault(key: K, defaultValue: V): V public fun isContains(key: K): Boolean public fun insert(key: K, value: V) + public fun insertIfAbsent(key: K, value: V): Boolean public fun remove(key: K): V? public fun removeWithDefault(key: K, defaultValue: V): V public fun maxDescendant(key: K): V? diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt index 4932736..6fff3f0 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt @@ -10,6 +10,10 @@ public class AVLTree, V>: AbstractBSTree, node: AVLTreeNode) { if (node.key < root.key) { if (root.leftChild == null) root.leftChild = node @@ -116,4 +120,6 @@ public class AVLTree, V>: AbstractBSTree, V>: AbstractBSTree> TODO("Not yet implemented") } + override fun insertIfAbsent(key: K, value: V): Boolean { + TODO("Not yet implemented") + } + override fun remove(key: K): V? { TODO("Not yet implemented") } diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt index aad5c21..3b05165 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt @@ -1,13 +1,94 @@ package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.RBTreeNode +import tree_trippers.nodes.notNullNodeUpdate public class RBTree, V>: AbstractBSTree>() { - override fun insert(key: K, value: V) { - TODO("Not yet implemented") - } override fun remove(key: K): V? { TODO("Not yet implemented") } + override fun insert(key: K, value: V): Unit { + insert(key, value, true) + } + + override fun insertIfAbsent(key: K, value: V): Boolean { + return insert(key, value, false) + } + + private fun insert(key: K, value: V, isUpdate: Boolean): Boolean { + val insertResult: Pair, Boolean> = insertNode(root, key, value, isUpdate) + root = insertResult.first + notNullNodeUpdate(root) {node -> node.isRed = false} + return insertResult.second + } + + private fun insertNode(node: RBTreeNode?, key: K, value: V, isUpdate: Boolean): Pair, Boolean> { + if (node == null) return Pair(RBTreeNode(key, value), true) + + val insertResult: Pair, Boolean> + val cmpResult: Int = key.compareTo(node.key) + if (cmpResult > 0) { + insertResult = insertNode(node.rightChild, key, value, isUpdate) + node.rightChild = insertResult.first + } else if (cmpResult < 0) { + insertResult = insertNode(node.leftChild, key, value, isUpdate) + node.leftChild = insertResult.first + } else { + if (isUpdate) node.value = value + return Pair(node, isUpdate) + } + + return Pair(fixNode(node), insertResult.second) + } + + private fun , V> isRedColor(node: RBTreeNode?): Boolean { + if (node == null) return false + return node.isRed + } + + private fun , V> rotateLeft(node: RBTreeNode): RBTreeNode { + val rightChild: RBTreeNode = node.rightChild ?: return node + node.rightChild = rightChild.leftChild + rightChild.leftChild = node + rightChild.isRed = node.isRed + node.isRed = true + + return rightChild + } + + private fun , V> rotateRight(node: RBTreeNode): RBTreeNode { + val leftChild: RBTreeNode = node.leftChild ?: return node + node.leftChild = leftChild.rightChild + leftChild.rightChild = node + leftChild.isRed = node.isRed + node.isRed = true + + return leftChild + } + + private fun , V> flipColors(node: RBTreeNode): Unit { + node.isRed = !node.isRed + notNullNodeUpdate(node.leftChild) { child -> child.isRed = !child.isRed } + notNullNodeUpdate(node.rightChild) { child -> child.isRed = !child.isRed } + } + + private fun , V> fixNode(node: RBTreeNode): RBTreeNode { + var currentNode: RBTreeNode = node + if (isRedColor(currentNode.rightChild)) { + currentNode = rotateLeft(currentNode) + } + if (isRedColor(currentNode.leftChild) && isRedLeftChild(currentNode.leftChild)) { + currentNode = rotateRight(currentNode) + } + if (isRedColor(node.leftChild) && isRedColor(node.rightChild)) { + flipColors(node) + } + return currentNode + } + + private fun , V> isRedLeftChild(node: RBTreeNode?): Boolean { + if (node == null) return false + return isRedColor(node.leftChild) + } } \ No newline at end of file From e4d0a52c5833b4f86eb43d65b71e69825a50e2ee Mon Sep 17 00:00:00 2001 From: Vladimir Zaikin Date: Sun, 24 Mar 2024 13:12:38 +0300 Subject: [PATCH 020/122] refactor: rewrite combine remove and insert for AVLTree and BSTree (not build) BREAKING CHANGE: implement methods for an AbstractBSTree, refactor AbstractBSTree, BSTree, SearchTree, SearchTreeNode, AbstractBSTreeNode, BSTreeNode. Add the pattern of ignoring to .gitignore --- .gitignore | 2 + .../main/kotlin/tree_trippers/SearchTree.kt | 42 ++-- .../binary_trees/AbstractBSTree.kt | 207 ++++++++++++------ .../tree_trippers/binary_trees/BSTree.kt | 13 +- .../tree_trippers/nodes/SearchTreeNode.kt | 12 +- .../nodes/binary_nodes/AbstractBSTreeNode.kt | 16 +- .../nodes/binary_nodes/BSTreeNode.kt | 7 +- 7 files changed, 193 insertions(+), 106 deletions(-) diff --git a/.gitignore b/.gitignore index 39e0dcb..4915a4f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ .idea/ *.iml +### Apple MacOS folder attributes +*.DS_Store \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/SearchTree.kt b/lib/src/main/kotlin/tree_trippers/SearchTree.kt index 3428f05..cd05d9d 100644 --- a/lib/src/main/kotlin/tree_trippers/SearchTree.kt +++ b/lib/src/main/kotlin/tree_trippers/SearchTree.kt @@ -1,24 +1,40 @@ package tree_trippers import tree_trippers.iterators.IterationOrders -import tree_trippers.iterators.SearchTreeIterator -import tree_trippers.nodes.SearchTreeNode -public interface SearchTree, V, N : SearchTreeNode>: Iterable> { - public fun search(key: K): V? - public fun searchOrDefault(key: K, defaultValue: V): V - public fun isContains(key: K): Boolean + +public interface SearchTree, V>: Iterable> { + public fun insert(key: K, value: V) + public fun insertIfAbsent(key: K, value: V): Boolean + public fun remove(key: K): V? + public fun removeWithDefault(key: K, defaultValue: V): V - public fun maxDescendant(key: K): V? - public fun max(): V? - public fun minDescendant(key: K): V? - public fun min(): V? - public fun size(): Int - public fun iterator(order: IterationOrders): SearchTreeIterator + + public fun search(key: K): V? + + public fun searchOrDefault(key: K, defaultValue: V): V + + public fun isContains(key: K): Boolean + + public fun getMaxDescendant(key: K): Pair? + + public fun getMax(): Pair? + + public fun getMinDescendant(key: K): Pair? + + public fun getMin(): Pair? + + public fun getSize(): Int + + public fun iterator(order: IterationOrders): Iterator> + public fun forEach(order: IterationOrders, action: (Pair) -> Unit) + public fun toString(order: IterationOrders): String - public fun toTreeViewString(): String + + public fun toStringWithTreeView(): String + } diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt index f97e4df..47b5ce3 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt @@ -4,91 +4,62 @@ import tree_trippers.SearchTree import tree_trippers.iterators.BinarySearchTreeIterator import tree_trippers.iterators.IterationOrders import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode -import tree_trippers.nodes.binary_nodes.BSTreeNode import tree_trippers.nodes.notNullNodeAction -public abstract class AbstractBSTree, V, N : AbstractBSTreeNode> : - SearchTree { - protected var root: N? = null - protected var size: Int = 0 - override fun search(key: K): V? { - return searchNode(key)?.value +public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { + private var root: N? = null + private var size: Int = 0 + + override fun insert(key: K, value: V) { + insert(key, value, permissionUpdate = true) } - override fun searchOrDefault(key: K, defaultValue: V): V { - return search(key) ?: defaultValue + override fun insertIfAbsent(key: K, value: V): Boolean { + return insert(key, value, permissionUpdate = false) } - override fun isContains(key: K): Boolean { - return (search(key) != null) + override fun remove(key: K): V? { + val resultRemove = removeNode(root, key) + updateRoot(resultRemove.first) + return resultRemove.second } override fun removeWithDefault(key: K, defaultValue: V): V { return remove(key) ?: defaultValue } - private fun searchNode(key: K): N? { - var nodeCurrent = this.root - - while (nodeCurrent != null) { - if (key == nodeCurrent.key) { - return nodeCurrent - } else if (key < nodeCurrent.key) { - if (nodeCurrent.leftChild == null) - break - - nodeCurrent = nodeCurrent.leftChild - } else { - if (nodeCurrent.rightChild == null) - break - - nodeCurrent = nodeCurrent.rightChild - } - } - - return null + override fun search(key: K): V? { + return searchNode(key)?.value } - override fun maxDescendant(key: K): V? { - var resultSearch = searchNode(key) - - while (resultSearch != null) { - if (resultSearch.rightChild == null) - return resultSearch.value - - resultSearch = resultSearch.rightChild - } - - return null + override fun searchOrDefault(key: K, defaultValue: V): V { + return search(key) ?: defaultValue } - override fun max(): V? { - if (this.root != null) - return maxDescendant(this.root!!.key) // todo(!!) - return null + override fun isContains(key: K): Boolean { + return search(key) != null } - override fun minDescendant(key: K): V? { - var resultSearch = searchNode(key) - - while (resultSearch != null) { - if (resultSearch.leftChild == null) - return resultSearch.value + override fun getMaxDescendant(key: K): Pair? { + val resultSearch = getMaxDescendantNode(searchNode(key)) ?: return null + return Pair(resultSearch.key, resultSearch.value) + } - resultSearch = resultSearch.leftChild - } + override fun getMax(): Pair? { + return notNullNodeAction(root, null) {node -> getMaxDescendant(node.key)} + } - return null + override fun getMinDescendant(key: K): Pair? { + val resultSearch = getMinDescendantNode(searchNode(key)) ?: return null + return Pair(resultSearch.key, resultSearch.value) } - override fun min(): V? { - if (this.root != null) - return minDescendant(this.root!!.key) // todo(!!) - return null + override fun getMin(): Pair? { + return notNullNodeAction(root, null) {node -> getMinDescendant(node.key)} } - override fun size(): Int { + override fun getSize(): Int { return size } @@ -102,9 +73,8 @@ public abstract class AbstractBSTree, V, N : AbstractBSTreeNod override fun forEach(order: IterationOrders, action: (Pair) -> Unit) { val treeIterator: BinarySearchTreeIterator = iterator(order) - while (treeIterator.hasNext()) { + while (treeIterator.hasNext()) action(treeIterator.next()) - } } override fun toString(): String { @@ -112,17 +82,112 @@ public abstract class AbstractBSTree, V, N : AbstractBSTreeNod } override fun toString(order: IterationOrders): String { - val sb = StringBuilder() - this.forEach(order) { pair: Pair -> - sb.append("${pair.first}: ${pair.second}, ") + val builder = StringBuilder() + this.forEach(order) { pair: Pair -> builder.append("${pair.first}: ${pair.second}, ") } + return "${this.javaClass.simpleName}($builder)" + } + + override fun toStringWithTreeView(): String { + val builder = StringBuilder() + notNullNodeAction(root, Unit) {node -> node.toStringWithSubtreeView(0, builder)} + return "${this.javaClass.simpleName}(\n$builder)" + } + + protected abstract fun createNode(key: K, value: V): N + + protected open fun updateRoot(node: N?) { + root = node + } + + protected open fun balanceTree(node: N): N { + return node + } + + private fun insert(key: K, value: V, permissionUpdate: Boolean): Boolean { + val insertResult: Pair = insertNode(root, key, value, permissionUpdate) + updateRoot(insertResult.first) + if (insertResult.second) size++ + return insertResult.second + } + + private fun insertNode(node: N?, key: K, value: V, permissionUpdate: Boolean): Pair { + if (node == null) return Pair(createNode(key, value), true) + + val resultInsert: Pair + val resultCompare: Int = key.compareTo(node.key) + if (resultCompare < 0) { + resultInsert = insertNode(node.leftChild, key, value, permissionUpdate) + node.leftChild = resultInsert.first + } else if (resultCompare > 0) { + resultInsert = insertNode(node.rightChild, key, value, permissionUpdate) + node.rightChild = resultInsert.first + } else { + if (permissionUpdate) node.value = value + return Pair(node, false) + } + + return Pair(balanceTree(node), resultInsert.second) + } + + protected open fun removeNode(node: N?, key: K): Pair { + if (node == null) return Pair(null, null) + + val resultRemove: Pair + val resultCompare: Int = key.compareTo(node.key) + if (resultCompare < 0) { + resultRemove = removeNode(node.leftChild, key) + node.leftChild = resultRemove.first + } else if (resultCompare > 0) { + resultRemove = removeNode(node.rightChild, key) + node.rightChild = resultRemove.first + } else { + val nodeSubstitutive: N? + if (node.leftChild == null || node.rightChild == null) { + nodeSubstitutive = node.leftChild ?: node.rightChild + return Pair(nodeSubstitutive, node.value) + } else { + nodeSubstitutive = getMaxDescendantNode(node.leftChild) as N + node.leftChild = removeNode(node.leftChild, nodeSubstitutive.key).first + nodeSubstitutive.rightChild = node.rightChild + nodeSubstitutive.leftChild = node.leftChild + return Pair(balanceTree(nodeSubstitutive), node.value) + } } - return "${this.javaClass.simpleName}($sb)" + + return Pair(balanceTree(node), resultRemove.second) } - override fun toTreeViewString(): String { - val sb = StringBuilder() - notNullNodeAction(root, Unit) {node -> node.toTreeViewString(0, sb)} - return "${this.javaClass.simpleName}(\n$sb)" + private fun searchNode(key: K): N? { + var nodeCurrent: N? = root ?: return null + + while (nodeCurrent != null) { + val resultCompare = key.compareTo(nodeCurrent.key) + if (resultCompare < 0) + nodeCurrent = nodeCurrent.leftChild + else if (resultCompare > 0) + nodeCurrent = nodeCurrent.rightChild + else + return nodeCurrent + } + return null + } + + protected fun getMaxDescendantNode(node: N?): N? { + if (node == null) return null + + var nodeCurrent: N = node + while (true) + nodeCurrent = nodeCurrent.rightChild ?: break + return nodeCurrent + } + + protected fun getMinDescendantNode(node: N?): N? { + if (node == null) return null + + var nodeCurrent: N = node + while (true) + nodeCurrent = nodeCurrent.leftChild ?: break + return nodeCurrent } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt index b9da78b..5ee61f6 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt @@ -2,16 +2,11 @@ package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.BSTreeNode + public class BSTree, V>: AbstractBSTree>() { - override fun insert(key: K, value: V) { - TODO("Not yet implemented") - } - override fun insertIfAbsent(key: K, value: V): Boolean { - TODO("Not yet implemented") + override fun createNode(key: K, value: V): BSTreeNode { + return BSTreeNode(key, value) } - override fun remove(key: K): V? { - TODO("Not yet implemented") - } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt index 99de974..e7b3c02 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt @@ -1,8 +1,12 @@ package tree_trippers.nodes -// TODO(Adding docs for methods) + public interface SearchTreeNode, V, N: SearchTreeNode> { + public fun getChildren(): List - public fun toSimpleViewString(): String - public fun toTreeViewString(indent: Int, sb: StringBuilder): Unit -} \ No newline at end of file + + public fun toStringSimpleView(): String + + public fun toStringWithSubtreeView(indent: Int, builder: StringBuilder): Unit + +} diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt index a245277..fcc9b55 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -3,8 +3,9 @@ package tree_trippers.nodes.binary_nodes import tree_trippers.nodes.SearchTreeNode import tree_trippers.nodes.notNullNodeAction + public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>( - public var key: K, // todo(val) + public val key: K, public var value: V ): SearchTreeNode { public var leftChild: N? = null @@ -18,13 +19,14 @@ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeN return "${this.javaClass.simpleName}(key=$key, value=$value)" } - override fun toSimpleViewString(): String { + override fun toStringSimpleView(): String { return "($key, $value)" } - override fun toTreeViewString(indent: Int, sb: StringBuilder) { - notNullNodeAction(this.leftChild, Unit) {node -> node.toTreeViewString(indent + 1, sb)} - sb.append("\t".repeat(indent)).append(this.toSimpleViewString()).append("\n") - notNullNodeAction(this.rightChild, Unit) {node -> node.toTreeViewString(indent + 1, sb)} + override fun toStringWithSubtreeView(indent: Int, builder: StringBuilder) { + notNullNodeAction(this.rightChild, Unit) {node -> node.toStringWithSubtreeView(indent + 1, builder)} + builder.append("\t".repeat(indent)).append(this.toStringSimpleView()).append("\n") + notNullNodeAction(this.leftChild, Unit) {node -> node.toStringWithSubtreeView(indent + 1, builder)} } -} \ No newline at end of file + +} diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt index be5e302..767fa84 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt @@ -1,4 +1,7 @@ package tree_trippers.nodes.binary_nodes -public class BSTreeNode, V>(key: K, value: V) : - AbstractBSTreeNode>(key, value) + +public class BSTreeNode, V>( + key: K, + value: V +): AbstractBSTreeNode>(key, value) From f049ebd22209e59ecd4a423ae983056775b677c6 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Sun, 24 Mar 2024 13:23:20 +0300 Subject: [PATCH 021/122] refactor(AVLTree & AVLTreeNode): combine code with AbstractBSTree. Add enter to .gitignore --- .gitignore | 2 +- .../tree_trippers/binary_trees/AVLTree.kt | 134 +++++------------- .../nodes/binary_nodes/AVLTreeNode.kt | 11 +- 3 files changed, 42 insertions(+), 105 deletions(-) diff --git a/.gitignore b/.gitignore index 4915a4f..05ede4d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,4 @@ *.iml ### Apple MacOS folder attributes -*.DS_Store \ No newline at end of file +*.DS_Store diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt index 6fff3f0..5d6a9d1 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt @@ -2,124 +2,56 @@ package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.AVLTreeNode -public class AVLTree, V>: AbstractBSTree>() { - - override fun insert (key: K, value: V) { - this.size++ - if (this.root == null) root = AVLTreeNode(key, value) - else this.root?.let { add(it, AVLTreeNode(key, value)) } - } - - override fun insertIfAbsent(key: K, value: V): Boolean { - TODO("Not yet implemented") - } - - private fun add(root: AVLTreeNode, node: AVLTreeNode) { - if (node.key < root.key) { - if (root.leftChild == null) root.leftChild = node - else add(root.leftChild!!, node) - } else if (node.key == root.key) { - root.value = node.value - } else { - if (root.rightChild == null) root.rightChild = node - else add(root.rightChild!!, node) - } - root.updateHeight() - balance(root) - } - - override fun remove(key: K): V? { - val dataToDelete = search(key) - if (dataToDelete != null) { - this.size-- - this.root = delete(this.root, key) - return dataToDelete - } - else return null - } - private fun delete(root: AVLTreeNode?, key: K): AVLTreeNode? { - var tempRoot = root //This is necessary to be able to change the root - if (tempRoot == null) return null - else if (key < tempRoot.key) tempRoot.leftChild = tempRoot.leftChild?.let { delete(it, key) } - else if (key > tempRoot.key) tempRoot.rightChild = tempRoot.rightChild?.let { delete(it, key) } - else { - if (tempRoot.rightChild == null || tempRoot.leftChild == null ) { - tempRoot = if (tempRoot.leftChild == null) tempRoot.rightChild else tempRoot.leftChild - } else { - val maxLeftNode: AVLTreeNode = maxDescendantForAVL(tempRoot.leftChild!!) - tempRoot.key = maxLeftNode.key - tempRoot.value = maxLeftNode.value - tempRoot.leftChild = delete(tempRoot.leftChild, maxLeftNode.key) - } - } - if (tempRoot != null) { - tempRoot.updateHeight() - balance(tempRoot) - } - return tempRoot - } - - private fun getBalance(node: AVLTreeNode ): Int { - return (node.rightChild?.height ?: 0) - (node.leftChild?.height ?: 0) - } +public class AVLTree, V>: AbstractBSTree>() { - private fun swapNodes(node1: AVLTreeNode, node2: AVLTreeNode) { - val node1Key = node1.key - node1.key = node2.key - node2.key = node1Key - val node1Value = node1.value - node1.value = node2.value - node2.value = node1Value + override fun createNode(key: K, value: V): AVLTreeNode { + return AVLTreeNode(key, value) } - private fun AVLRightRotation(node: AVLTreeNode ): AVLTreeNode { - node.leftChild?.let { swapNodes(node, it) } - val temp = node.rightChild - node.rightChild = node.leftChild - node.leftChild = node.rightChild?.leftChild - node.rightChild?.leftChild = node.rightChild?.rightChild - node.rightChild?.rightChild = temp - node.rightChild?.updateHeight() + override fun balanceTree(node: AVLTreeNode): AVLTreeNode { node.updateHeight() - return node.leftChild ?: node + return balance(node) } - private fun AVLLeftRotation(node: AVLTreeNode ): AVLTreeNode { - node.rightChild?.let { swapNodes(node, it) } - val temp = node.leftChild - node.leftChild = node.rightChild - node.rightChild = node.leftChild?.rightChild - node.leftChild?.rightChild = node.leftChild?.leftChild - node.leftChild?.leftChild = temp - node.leftChild?.updateHeight() - node.updateHeight() - return node.rightChild ?: node + private fun balanceFactor(node: AVLTreeNode?): Int { + return (node?.rightChild?.height ?: 0) - (node?.leftChild?.height ?: 0) } - private fun balance(node: AVLTreeNode ): AVLTreeNode { - when (getBalance(node)) { + private fun balance(node: AVLTreeNode): AVLTreeNode { + when (balanceFactor(node)) { -2 -> { - if (node.leftChild?.let { getBalance(it) } == 1) { - AVLLeftRotation(node.leftChild!!) - } - AVLRightRotation(node) + if (balanceFactor(node.leftChild) == 1) + node.leftChild = rotateLeft(node.leftChild as AVLTreeNode) + return rotateRight(node) } 2 -> { - if (node.rightChild?.let { getBalance(it) } == -1) { - AVLRightRotation(node.rightChild!!) - } - AVLLeftRotation(node) + if (balanceFactor(node.rightChild) == -1) + node.rightChild = rotateRight(node.rightChild as AVLTreeNode) + return rotateLeft(node) } + else -> return node } - return node } - private fun maxDescendantForAVL(node: AVLTreeNode): AVLTreeNode { // todo(MAXIM) - if (node.rightChild == null) return node - return maxDescendantForAVL(node.rightChild!!) + private fun rotateLeft(node: AVLTreeNode): AVLTreeNode { + val nodeSwapped = node.rightChild ?: return node + node.rightChild = nodeSwapped.leftChild + nodeSwapped.leftChild = node + + node.updateHeight() + nodeSwapped.updateHeight() + return nodeSwapped } + private fun rotateRight(node: AVLTreeNode): AVLTreeNode { + val nodeSwapped = node.leftChild ?: return node + node.leftChild = nodeSwapped.rightChild + nodeSwapped.rightChild = node + node.updateHeight() + nodeSwapped.updateHeight() + return nodeSwapped + } -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt index 36317f2..c6b14ee 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt @@ -2,12 +2,17 @@ package tree_trippers.nodes.binary_nodes import kotlin.math.max -public class AVLTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { + +public class AVLTreeNode, V>( + key: K, + value: V +): AbstractBSTreeNode>(key, value) { public var height: Int = 1 - fun updateHeight() { + public fun updateHeight() { val leftHeight = this.leftChild?.height ?: 0 val rightHeight = this.rightChild?.height ?: 0 height = (max(leftHeight, rightHeight) + 1) } -} \ No newline at end of file + +} From d4292114d8ae7499678135ca8652e383404a1f63 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 24 Mar 2024 14:30:09 +0300 Subject: [PATCH 022/122] refactor: update RBTree & RBTreeNode by combining the insert method with AbstractBSTree. Add implementation of the remove method at RBTree. Remove unnecessary interface of SearchTreeIterator. Update BinarySearchTreeIterator by implementing Iterator interface. Update ReadMe by adding info about finished trees --- lib/ReadMe.md | 6 +- .../tree_trippers/binary_trees/RBTree.kt | 142 ++++++++++++------ .../iterators/BinarySearchTreeIterator.kt | 119 ++++++++------- .../iterators/IterationOrders.kt | 7 +- .../iterators/SearchTreeIterator.kt | 7 - .../main/kotlin/tree_trippers/nodes/Utils.kt | 7 +- .../nodes/binary_nodes/RBTreeNode.kt | 18 ++- 7 files changed, 189 insertions(+), 117 deletions(-) mode change 100755 => 100644 lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt mode change 100755 => 100644 lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt delete mode 100755 lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt mode change 100755 => 100644 lib/src/main/kotlin/tree_trippers/nodes/Utils.kt diff --git a/lib/ReadMe.md b/lib/ReadMe.md index c3db8ab..a8ba35b 100644 --- a/lib/ReadMe.md +++ b/lib/ReadMe.md @@ -8,8 +8,8 @@ Library `TreeTrippers` for working with data in the format of search trees. Supported searches trees: -- [ ] `BinarySearchTree` (BST) -- [ ] `AVLTree` -- [ ] `RedBlackTree` +- [x] `BinarySearchTree` (BST) +- [x] `AVLTree` +- [x] `RedBlackTree` The library supports the extension both internally (future library updates) and externally (implemented by the user). diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt index 3b05165..8993862 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt @@ -1,94 +1,144 @@ package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.RBTreeNode +import tree_trippers.nodes.notNullNodeAction import tree_trippers.nodes.notNullNodeUpdate + public class RBTree, V>: AbstractBSTree>() { - override fun remove(key: K): V? { - TODO("Not yet implemented") - } - override fun insert(key: K, value: V): Unit { - insert(key, value, true) + override fun createNode(key: K, value: V): RBTreeNode { + return RBTreeNode(key, value) } - override fun insertIfAbsent(key: K, value: V): Boolean { - return insert(key, value, false) + override fun updateRoot(node: RBTreeNode?) { + notNullNodeUpdate(node) { it.isRed = false } + super.updateRoot(node) } - private fun insert(key: K, value: V, isUpdate: Boolean): Boolean { - val insertResult: Pair, Boolean> = insertNode(root, key, value, isUpdate) - root = insertResult.first - notNullNodeUpdate(root) {node -> node.isRed = false} - return insertResult.second + override fun balanceTree(node: RBTreeNode): RBTreeNode { + var nodeCurrent: RBTreeNode = node + if (isRedColor(nodeCurrent.rightChild)) { + nodeCurrent = rotateLeft(nodeCurrent) + } + if (isRedColor(nodeCurrent.leftChild) && isRedLeftChild(nodeCurrent.leftChild)) { + nodeCurrent = rotateRight(nodeCurrent) + } + if (isRedColor(nodeCurrent.leftChild) && isRedColor(nodeCurrent.rightChild)) { + flipColors(nodeCurrent) + } + return nodeCurrent } - private fun insertNode(node: RBTreeNode?, key: K, value: V, isUpdate: Boolean): Pair, Boolean> { - if (node == null) return Pair(RBTreeNode(key, value), true) - - val insertResult: Pair, Boolean> - val cmpResult: Int = key.compareTo(node.key) - if (cmpResult > 0) { - insertResult = insertNode(node.rightChild, key, value, isUpdate) - node.rightChild = insertResult.first - } else if (cmpResult < 0) { - insertResult = insertNode(node.leftChild, key, value, isUpdate) - node.leftChild = insertResult.first + override fun removeNode(node: RBTreeNode?, key: K): Pair?, V?> { + if (node == null) return Pair(null, null) + + val removeResult: Pair?, V?> + val resultCompare: Int = key.compareTo(node.key) + var nodeCurrent: RBTreeNode = node + if (resultCompare < 0) { + if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) + nodeCurrent = moveRedLeft(nodeCurrent) + + removeResult = removeNode(nodeCurrent.leftChild, key) + nodeCurrent.leftChild = removeResult.first } else { - if (isUpdate) node.value = value - return Pair(node, isUpdate) + if (isRedColor(nodeCurrent.leftChild)) + nodeCurrent = rotateRight(nodeCurrent) + if (resultCompare == 0 && nodeCurrent.rightChild == null) + return Pair(null, nodeCurrent.value) + if (!isRedColor(nodeCurrent.rightChild) && !isRedLeftChild(nodeCurrent.rightChild)) + nodeCurrent = moveRedRight(nodeCurrent) + if (resultCompare == 0) { + val nodeWithMinimalKey = getMinDescendantNode(nodeCurrent.rightChild) as RBTreeNode + val nodeSubstitutive: RBTreeNode = createNode(nodeWithMinimalKey.key, nodeWithMinimalKey.value) + nodeSubstitutive.isRed = nodeCurrent.isRed + nodeSubstitutive.leftChild = nodeCurrent.leftChild + nodeSubstitutive.rightChild = removeMinNode(nodeCurrent.rightChild) + return Pair(balanceTree(nodeSubstitutive), nodeCurrent.value) + } else { + removeResult = removeNode(nodeCurrent.rightChild, key) + nodeCurrent.rightChild = removeResult.first + } } - return Pair(fixNode(node), insertResult.second) + return Pair(balanceTree(nodeCurrent), removeResult.second) } - private fun , V> isRedColor(node: RBTreeNode?): Boolean { + private fun isRedColor(node: RBTreeNode?): Boolean { if (node == null) return false return node.isRed } - private fun , V> rotateLeft(node: RBTreeNode): RBTreeNode { + private fun isRedLeftChild(node: RBTreeNode?): Boolean { + if (node == null) return false + return isRedColor(node.leftChild) + } + + private fun rotateLeft(node: RBTreeNode): RBTreeNode { val rightChild: RBTreeNode = node.rightChild ?: return node node.rightChild = rightChild.leftChild rightChild.leftChild = node + rightChild.isRed = node.isRed node.isRed = true - return rightChild } - private fun , V> rotateRight(node: RBTreeNode): RBTreeNode { + private fun rotateRight(node: RBTreeNode): RBTreeNode { val leftChild: RBTreeNode = node.leftChild ?: return node node.leftChild = leftChild.rightChild leftChild.rightChild = node + leftChild.isRed = node.isRed node.isRed = true - return leftChild } - private fun , V> flipColors(node: RBTreeNode): Unit { + private fun flipColors(node: RBTreeNode): Unit { node.isRed = !node.isRed notNullNodeUpdate(node.leftChild) { child -> child.isRed = !child.isRed } notNullNodeUpdate(node.rightChild) { child -> child.isRed = !child.isRed } } - private fun , V> fixNode(node: RBTreeNode): RBTreeNode { - var currentNode: RBTreeNode = node - if (isRedColor(currentNode.rightChild)) { - currentNode = rotateLeft(currentNode) - } - if (isRedColor(currentNode.leftChild) && isRedLeftChild(currentNode.leftChild)) { - currentNode = rotateRight(currentNode) + private fun moveRedRight(node: RBTreeNode): RBTreeNode { + var nodeCurrent: RBTreeNode = node + + flipColors(nodeCurrent) + if (isRedLeftChild(nodeCurrent.leftChild)) { + nodeCurrent = rotateRight(nodeCurrent) + flipColors(nodeCurrent) } - if (isRedColor(node.leftChild) && isRedColor(node.rightChild)) { - flipColors(node) + return nodeCurrent + } + + private fun moveRedLeft(node: RBTreeNode): RBTreeNode { + var nodeCurrent: RBTreeNode = node + + flipColors(nodeCurrent) + if (isRedLeftChild(nodeCurrent.rightChild)) { + nodeCurrent.rightChild = notNullNodeAction( + node.rightChild, null + ) {rightChild -> rotateRight(rightChild)} + nodeCurrent = rotateLeft(nodeCurrent) + flipColors(nodeCurrent) } - return currentNode + return nodeCurrent } - private fun , V> isRedLeftChild(node: RBTreeNode?): Boolean { - if (node == null) return false - return isRedColor(node.leftChild) + private fun removeMinNode(node: RBTreeNode?): RBTreeNode? { + if (node == null) return null + if (node.leftChild == null) return node.rightChild + + var nodeCurrent: RBTreeNode = node + if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) + nodeCurrent = moveRedLeft(nodeCurrent) + + nodeCurrent.leftChild = notNullNodeAction( + nodeCurrent.leftChild, null + ) {child -> removeMinNode(child)} + + return balanceTree(nodeCurrent) } -} \ No newline at end of file + +} diff --git a/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt b/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt old mode 100755 new mode 100644 index 7fe8fcf..0f2d353 --- a/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt +++ b/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt @@ -4,8 +4,8 @@ import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode import java.util.LinkedList import java.util.Queue -class BinarySearchTreeIterator, V, N : AbstractBSTreeNode> : - SearchTreeIterator { + +class BinarySearchTreeIterator, V, N: AbstractBSTreeNode>: Iterator> { private val iterationState: IterationState constructor(root: N?): this(root, IterationOrders.WIDTH_ORDER) @@ -15,7 +15,6 @@ class BinarySearchTreeIterator, V, N : AbstractBSTreeNode WidthIterationState(root) IterationOrders.INCREASING_ORDER -> IncreasingIterationState(root) IterationOrders.DECREASING_ORDER -> DecreasingIterationState(root) - else -> throw Exception("Unsupported iteration order") } } @@ -24,16 +23,20 @@ class BinarySearchTreeIterator, V, N : AbstractBSTreeNode { - if (!hasNext()) throw Exception("Try get next element from empty iterator") return iterationState.next() } private interface IterationState, V, N: AbstractBSTreeNode> { + abstract fun hasNext(): Boolean + abstract fun next(): Pair + } - private class WidthIterationState, V, N: AbstractBSTreeNode>(root: N?): IterationState { + private class WidthIterationState, V, N: AbstractBSTreeNode>( + root: N? + ): IterationState { private val queue: Queue = LinkedList() init { @@ -41,102 +44,112 @@ class BinarySearchTreeIterator, V, N : AbstractBSTreeNode 0 + return (queue.size > 0) } override fun next(): Pair { - if (!hasNext()) throw Exception("Try get next element from the end of iterator state") - val currentNode: N = queue.poll() - currentNode.getChildren().forEach() { - child -> queue.add(child) + if (!hasNext()) throw NoSuchElementException("Try get next element from the end of iterator state") + val nodeCurrent: N = queue.poll() + nodeCurrent.getChildren().forEach() { child -> + queue.add(child) } - return Pair(currentNode.key, currentNode.value) + return Pair(nodeCurrent.key, nodeCurrent.value) } + } - private class IncreasingIterationState, V, N: AbstractBSTreeNode>(root: N?): IterationState { - private val unprocessedNodesStack: LinkedList = LinkedList() - private val semiProcessedNodesStack: LinkedList = LinkedList() + private class IncreasingIterationState, V, N: AbstractBSTreeNode>( + root: N? + ): IterationState { + private val unprocessedNodesStack = LinkedList() + private val semiProcessedNodesStack = LinkedList() init { if (root != null) unprocessedNodesStack.add(root) } override fun hasNext(): Boolean { - return isHasUnprocessedNodes() || isHasSemiProcessedNodes() + return (hasUnprocessedNodes() || hasSemiProcessedNodes()) } - private fun isHasSemiProcessedNodes(): Boolean { - return semiProcessedNodesStack.isNotEmpty() + private fun hasUnprocessedNodes(): Boolean { + return unprocessedNodesStack.isNotEmpty() } - private fun isHasUnprocessedNodes(): Boolean { - return unprocessedNodesStack.isNotEmpty() + private fun hasSemiProcessedNodes(): Boolean { + return semiProcessedNodesStack.isNotEmpty() } override fun next(): Pair { - if (!hasNext()) throw Exception("Try get next element from the end of iterator state") - var currentNode: N - - while (isHasUnprocessedNodes()) { - currentNode = unprocessedNodesStack.pollFirst() - semiProcessedNodesStack.addFirst(currentNode) - if (currentNode.leftChild != null) { - unprocessedNodesStack.addFirst(currentNode.leftChild) - } else { + if (!hasNext()) throw NoSuchElementException("Try get next element from the end of iterator state") + var nodeCurrent: N + + while (hasUnprocessedNodes()) { + nodeCurrent = unprocessedNodesStack.pollFirst() + semiProcessedNodesStack.addFirst(nodeCurrent) + if (nodeCurrent.leftChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.leftChild) + else { semiProcessedNodesStack.pollFirst() - if (currentNode.rightChild != null) unprocessedNodesStack.addFirst(currentNode.rightChild) - return Pair(currentNode.key, currentNode.value) + if (nodeCurrent.rightChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.rightChild) + return Pair(nodeCurrent.key, nodeCurrent.value) } } - currentNode = semiProcessedNodesStack.pollFirst() - if (currentNode.rightChild != null) unprocessedNodesStack.addFirst(currentNode.rightChild) - return Pair(currentNode.key, currentNode.value) + nodeCurrent = semiProcessedNodesStack.pollFirst() + if (nodeCurrent.rightChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.rightChild) + return Pair(nodeCurrent.key, nodeCurrent.value) } } - private class DecreasingIterationState, V, N: AbstractBSTreeNode>(root: N?): IterationState { - private val unprocessedNodesStack: LinkedList = LinkedList() - private val semiProcessedNodesStack: LinkedList = LinkedList() + private class DecreasingIterationState, V, N: AbstractBSTreeNode>( + root: N? + ): IterationState { + private val unprocessedNodesStack = LinkedList() + private val semiProcessedNodesStack = LinkedList() init { if (root != null) unprocessedNodesStack.add(root) } override fun hasNext(): Boolean { - return isHasUnprocessedNodes() || isHasSemiProcessedNodes() + return (hasUnprocessedNodes() || hasSemiProcessedNodes()) } - private fun isHasSemiProcessedNodes(): Boolean { + private fun hasSemiProcessedNodes(): Boolean { return semiProcessedNodesStack.isNotEmpty() } - private fun isHasUnprocessedNodes(): Boolean { + private fun hasUnprocessedNodes(): Boolean { return unprocessedNodesStack.isNotEmpty() } override fun next(): Pair { - if (!hasNext()) throw Exception("Try get next element from the end of iterator state") - var currentNode: N - - while (isHasUnprocessedNodes()) { - currentNode = unprocessedNodesStack.pollFirst() - semiProcessedNodesStack.addFirst(currentNode) - if (currentNode.rightChild != null) { - unprocessedNodesStack.addFirst(currentNode.rightChild) - } else { + if (!hasNext()) throw NoSuchElementException("Try get next element from the end of iterator state") + var nodeCurrent: N + + while (hasUnprocessedNodes()) { + nodeCurrent = unprocessedNodesStack.pollFirst() + semiProcessedNodesStack.addFirst(nodeCurrent) + if (nodeCurrent.rightChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.rightChild) + else { semiProcessedNodesStack.pollFirst() - if (currentNode.leftChild != null) unprocessedNodesStack.addFirst(currentNode.leftChild) - return Pair(currentNode.key, currentNode.value) + if (nodeCurrent.leftChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.leftChild) + return Pair(nodeCurrent.key, nodeCurrent.value) } } - currentNode = semiProcessedNodesStack.pollFirst() - if (currentNode.leftChild != null) unprocessedNodesStack.addFirst(currentNode.leftChild) - return Pair(currentNode.key, currentNode.value) + nodeCurrent = semiProcessedNodesStack.pollFirst() + if (nodeCurrent.leftChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.leftChild) + return Pair(nodeCurrent.key, nodeCurrent.value) } } + } diff --git a/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt b/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt old mode 100755 new mode 100644 index 06015e6..632360b --- a/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt +++ b/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt @@ -1,5 +1,8 @@ package tree_trippers.iterators + enum class IterationOrders { - WIDTH_ORDER, INCREASING_ORDER, DECREASING_ORDER, -} \ No newline at end of file + WIDTH_ORDER, + INCREASING_ORDER, + DECREASING_ORDER, +} diff --git a/lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt b/lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt deleted file mode 100755 index 7225c4a..0000000 --- a/lib/src/main/kotlin/tree_trippers/iterators/SearchTreeIterator.kt +++ /dev/null @@ -1,7 +0,0 @@ -package tree_trippers.iterators - -import tree_trippers.nodes.SearchTreeNode - -interface SearchTreeIterator, V, N: SearchTreeNode>: Iterator> { - -} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt b/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt old mode 100755 new mode 100644 index fd2a31f..6b872c4 --- a/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt @@ -1,11 +1,12 @@ package tree_trippers.nodes -public fun notNullNodeAction(node: N?, nullNodeResult: R, action: (N) -> (R)): R { + +public fun , R> notNullNodeAction(node: N?, nullNodeResult: R, action: (N) -> (R)): R { if (node == null) return nullNodeResult return action(node) } -public fun notNullNodeUpdate(node: N?, updateAction: (N) -> (Unit)): Unit { +public fun > notNullNodeUpdate(node: N?, updateAction: (N) -> (Unit)): Unit { if (node == null) return return updateAction(node) -} \ No newline at end of file +} diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt index cfd3de4..c85a8b9 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt @@ -1,6 +1,18 @@ package tree_trippers.nodes.binary_nodes -public class RBTreeNode, V>(key: K, value: V): AbstractBSTreeNode>(key, value) { - var isRed: Boolean = false -} +public class RBTreeNode, V>( + key: K, + value: V +): AbstractBSTreeNode>(key, value) { + var isRed: Boolean = true + + override fun toStringSimpleView(): String { + return "${super.toStringSimpleView()} - ${colorName()}" + } + + private fun colorName(): String { + return if (isRed) "RED" else "BLACK" + } + +} From 169d9e784d843b5416c83bc65307b30da7276a00 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 24 Mar 2024 17:55:31 +0300 Subject: [PATCH 023/122] feat: Update lib/ReadMe.md by adding description of library and some examples of using it --- lib/ReadMe.md | 130 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 122 insertions(+), 8 deletions(-) diff --git a/lib/ReadMe.md b/lib/ReadMe.md index a8ba35b..f1fe2b6 100644 --- a/lib/ReadMe.md +++ b/lib/ReadMe.md @@ -1,15 +1,129 @@ # Kotlin Library `TreeTrippers` -*** - ## Description -Library `TreeTrippers` for working with data in the format of search trees. +Library `TreeTrippers` - it is providing implementations of binary search trees data structures: +- [x] [`Binary Search Tree`](src/main/kotlin/tree_trippers/binary_trees/BSTree.kt), see more [information](https://en.wikipedia.org/wiki/Binary_search_tree) +- [x] [`AVL tree`](src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt), see more [information](https://en.wikipedia.org/wiki/AVL_tree) +- [x] [`Red-black tree`](src/main/kotlin/tree_trippers/binary_trees/RBTree.kt), see more [information](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) -Supported searches trees: +The library supports the extension both internally (future library updates) and externally (implemented by the user). -- [x] `BinarySearchTree` (BST) -- [x] `AVLTree` -- [x] `RedBlackTree` +## Getting started +To run building library execute command: +```bash +./gradlew build +``` -The library supports the extension both internally (future library updates) and externally (implemented by the user). +## Using library + +### Basic operations ++ `insert` ++ `search` ++ `remove` + +### Examples + +##### Example 1 (importing) +```kotlin +import tree_trippers.binary_trees.BSTree +import tree_trippers.binary_trees.AVLTree +import tree_trippers.binary_trees.RBTree + + +val simpleTree = BSTree() // initialization of empty simple tree +val avlTree = AVLTree() // initialization of empty AVL tree +val rbTree = RBTree>() // initialization of empty red-black tree +``` + +##### Example 2 (inserting) +Code: +```kotlin +import tree_trippers.binary_trees.BSTree + +fun main() { + val tree = BSTree() + + tree.insert(key = 1, value = 1) + tree.insert(key = 2, value = 2) + tree.insert(key = 3, value = 3) + tree.insert(key = 4, value = 4) + tree.insert(key = 5, value = 5) + + println(tree) +} +``` +Output: +```text +BSTree(1: 1, 2: 2, 3: 3, 4: 4, 5: 5, ) +``` + +##### Example 2 (searching) +Code: +```kotlin +import tree_trippers.binary_trees.BSTree + +fun main() { + val tree = BSTree() + /* + ... + inserting from `example 1` + ... + */ + + /* Existing element in tree */ + println(tree.search(key = 1)) + println(tree.search(key = 3)) + println(tree.search(key = 5)) + + /* Unexciting element in tree */ + println(tree.search(key = -2)) + println(tree.search(key = 7)) +} +``` +Output: +```text +1 +3 +5 +null +null +``` + +##### Example 3 (removing) +Code: +```kotlin +import tree_trippers.binary_trees.BSTree + +fun main() { + val tree = BSTree() + /* + ... + inserting from `example 1` + ... + */ + + /* Existing element in tree */ + println(tree.remove(key = 1)) + println(tree.remove(key = 3)) + println(tree.remove(key = 5)) + + /* Unexciting element in tree */ + println(tree.remove(key = -2)) + println(tree.remove(key = 7)) + + println(tree) +} +``` +Output: +```text +1 +3 +5 +null +null +BSTree(2: 2, 4: 4, ) +``` + +## Documentation +See [_**documentation**_](src/main/kotlin/tree_trippers/SearchTree.kt) of library `TreeTrippers` to learn more about it. From 9a33302e709b3d0894463eb3bb827fa9c468c85f Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 24 Mar 2024 19:57:57 +0300 Subject: [PATCH 024/122] feat: add simple root ReadMe for project with simple info upbout it --- ReadMe.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 ReadMe.md diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..fc5dd43 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,37 @@ +[//]: # (Project readme template from https://github.com/othneildrew/Best-README-Template/) + + + +[![Contributors][contributors-shield]][contributors-url] +[![Forks][forks-shield]][forks-url] +[![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] +[![MIT License][license-shield]][license-url] +[![LinkedIn][linkedin-shield]][linkedin-url] + +

TreeTrippers

+ +## Description +The project `TreeTrippers` contains an implementation of a library for working with search trees + +## Roadmap + +- [x] Add library +- [ ] Add documentation of library +- [ ] Add tests to library + +

(back to top)

+ +## Authors + +- [@IliaSuponeff](https://github.com/IliaSuponeff) +- [@RodionovMaxim05](https://github.com/RodionovMaxim05) +- [@Vladimir Zaikin](https://github.com/Friend-zva) + + +## License + + +Distributed under the [MIT License](https://choosealicense.com/licenses/mit/). See [`LICENSE`](LICENSE) for more information. + +

(back to top)

From 6834d74b9973c96832de2e8683b37201490008ef Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 24 Mar 2024 20:09:12 +0300 Subject: [PATCH 025/122] fix: invalid header which do not display information about the project on the github of root readme by removing it --- ReadMe.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index fc5dd43..cd7a016 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,13 +1,7 @@ [//]: # (Project readme template from https://github.com/othneildrew/Best-README-Template/) - -[![Contributors][contributors-shield]][contributors-url] -[![Forks][forks-shield]][forks-url] -[![Stargazers][stars-shield]][stars-url] -[![Issues][issues-shield]][issues-url] -[![MIT License][license-shield]][license-url] -[![LinkedIn][linkedin-shield]][linkedin-url] +[![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/)

TreeTrippers

From 5a2676d19e7e836a74800037522b6d10dd390801 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 25 Mar 2024 22:04:05 +0300 Subject: [PATCH 026/122] feat: Adding doc-string for some source files. Docs integrated on IterationOrders.kt, BinarySearchTreeIterator.kt, RBTree.kt, RBTreeNode.kt and Utils.kt --- .../tree_trippers/binary_trees/RBTree.kt | 57 +++++++++++++++++++ .../iterators/BinarySearchTreeIterator.kt | 37 ++++++++++++ .../iterators/IterationOrders.kt | 7 +++ .../main/kotlin/tree_trippers/nodes/Utils.kt | 8 +++ .../nodes/binary_nodes/RBTreeNode.kt | 9 +++ 5 files changed, 118 insertions(+) diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt index 8993862..e63faef 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt @@ -5,6 +5,12 @@ import tree_trippers.nodes.notNullNodeAction import tree_trippers.nodes.notNullNodeUpdate +/** + * A class that represents a red-black tree data structure. + * + * @param K the type of the keys in the tree + * @param V the type of the values in the tree + */ public class RBTree, V>: AbstractBSTree>() { override fun createNode(key: K, value: V): RBTreeNode { @@ -65,16 +71,35 @@ public class RBTree, V>: AbstractBSTree> return Pair(balanceTree(nodeCurrent), removeResult.second) } + /** + * Returns whether the specified node is red or not. + * + * @param node the node to check + * @return `true` if the node is red, `false` otherwise + */ private fun isRedColor(node: RBTreeNode?): Boolean { if (node == null) return false return node.isRed } + /** + * Returns whether the specified node is red or not. + * + * @param node the node to check color its left child + * @return `true` if left child of `node` is red, `false` otherwise + */ private fun isRedLeftChild(node: RBTreeNode?): Boolean { if (node == null) return false return isRedColor(node.leftChild) } + /** + * Rotates the binary tree node with the given root to the left. + * + * @param node the root of the binary tree node to rotate left + * @return if `node.rightChild` is null, returns `node`, + * otherwise `node` switches places with the right child + */ private fun rotateLeft(node: RBTreeNode): RBTreeNode { val rightChild: RBTreeNode = node.rightChild ?: return node node.rightChild = rightChild.leftChild @@ -85,6 +110,13 @@ public class RBTree, V>: AbstractBSTree> return rightChild } + /** + * Rotates the binary tree node with the given root to the right. + * + * @param node the binary tree node to rotate right + * @return if `node.leftChild` is null, returns `node`, + * otherwise `node` switches places with the left child + */ private fun rotateRight(node: RBTreeNode): RBTreeNode { val leftChild: RBTreeNode = node.leftChild ?: return node node.leftChild = leftChild.rightChild @@ -95,12 +127,24 @@ public class RBTree, V>: AbstractBSTree> return leftChild } + /** + * Flips the colors of the specified node and its children. + * + * @param node needed to flip the colors + */ private fun flipColors(node: RBTreeNode): Unit { node.isRed = !node.isRed notNullNodeUpdate(node.leftChild) { child -> child.isRed = !child.isRed } notNullNodeUpdate(node.rightChild) { child -> child.isRed = !child.isRed } } + /** + * This function is used to move a red node to the right, if it has a red left child of its [node] left child. + * It first flips the colors of the node and its children, then rotates the tree if the left child is also red. + * + * @param node the node to move + * @return the new root of the tree, which is balanced node subtree + */ private fun moveRedRight(node: RBTreeNode): RBTreeNode { var nodeCurrent: RBTreeNode = node @@ -112,6 +156,13 @@ public class RBTree, V>: AbstractBSTree> return nodeCurrent } + /** + * This function is used to move a red node to the left, if it has a red right child of its [node] left child. + * It first flips the colors of the node and its children, then rotates the tree if the left child is also red. + * + * @param node the node to move + * @return the new root of the tree, which is balanced node subtree + */ private fun moveRedLeft(node: RBTreeNode): RBTreeNode { var nodeCurrent: RBTreeNode = node @@ -126,6 +177,12 @@ public class RBTree, V>: AbstractBSTree> return nodeCurrent } + /** + * Removes the node with the minimum key from the binary search tree. + * + * @param node the root of the binary search tree + * @return the root of the binary search tree with the node removed, or `null` if the tree is empty + */ private fun removeMinNode(node: RBTreeNode?): RBTreeNode? { if (node == null) return null if (node.leftChild == null) return node.rightChild diff --git a/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt b/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt index 0f2d353..f91f7db 100644 --- a/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt +++ b/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt @@ -5,11 +5,29 @@ import java.util.LinkedList import java.util.Queue +/** + * A generic binary search tree iterator that can iterate over a binary search tree in different orders. + * + * @param K the type of the keys in the binary search tree + * @param V the type of the values in the binary search tree + * @param N the type of the nodes in the binary search tree + */ class BinarySearchTreeIterator, V, N: AbstractBSTreeNode>: Iterator> { private val iterationState: IterationState + /** + * Constructs a binary search tree iterator with the given root node and the default iteration order, which is [IterationOrders.WIDTH_ORDER]. + * + * @param root the root node of the binary search tree + */ constructor(root: N?): this(root, IterationOrders.WIDTH_ORDER) + /** + * Constructs a binary search tree iterator with the given root node and the given iteration order. + * + * @param root the root node of the binary search tree + * @param order the iteration order + */ constructor(root: N?, order: IterationOrders) { iterationState = when (order) { IterationOrders.WIDTH_ORDER -> WidthIterationState(root) @@ -26,14 +44,27 @@ class BinarySearchTreeIterator, V, N: AbstractBSTreeNode, V, N: AbstractBSTreeNode> { + /** + * Returns `true` if the iteration has more elements. + */ abstract fun hasNext(): Boolean + /** + * Returns `true` if the iteration has more elements. + * @throws: NoSuchElementException - if the iteration state has no next element. + */ abstract fun next(): Pair } + /** + * A concrete iteration state for a binary search tree iterator with the width iteration order. + */ private class WidthIterationState, V, N: AbstractBSTreeNode>( root: N? ): IterationState { @@ -58,6 +89,9 @@ class BinarySearchTreeIterator, V, N: AbstractBSTreeNode, V, N: AbstractBSTreeNode>( root: N? ): IterationState { @@ -105,6 +139,9 @@ class BinarySearchTreeIterator, V, N: AbstractBSTreeNode, V, N: AbstractBSTreeNode>( root: N? ): IterationState { diff --git a/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt b/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt index 632360b..34c75b4 100644 --- a/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt +++ b/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt @@ -1,6 +1,13 @@ package tree_trippers.iterators +/** + * An enumeration of the possible orders in which to iterate over the elements of a tree. + * + * @property WIDTH_ORDER iterate over the elements in order of their width in the tree + * @property INCREASING_ORDER iterate over the elements in order of their increasing depth in the tree + * @property DECREASING_ORDER iterate over the elements in order of their decreasing depth in the tree + */ enum class IterationOrders { WIDTH_ORDER, INCREASING_ORDER, diff --git a/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt b/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt index 6b872c4..4d7b1e7 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt @@ -1,11 +1,19 @@ package tree_trippers.nodes +/** + * Checks the search tree [node] for null and if it is not null then executes the [action] + * + * @return the result of applying the given [action] function to the given [node], or the [nullNodeResult] if the [node] is null. + */ public fun , R> notNullNodeAction(node: N?, nullNodeResult: R, action: (N) -> (R)): R { if (node == null) return nullNodeResult return action(node) } +/** + * Checks the search tree [node] for null and if it is not null then executes the [updateAction] + */ public fun > notNullNodeUpdate(node: N?, updateAction: (N) -> (Unit)): Unit { if (node == null) return return updateAction(node) diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt index c85a8b9..4e72b6d 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt @@ -1,6 +1,12 @@ package tree_trippers.nodes.binary_nodes +/** + * A red-black tree node. + * + * @param K the key type + * @param V the value type + */ public class RBTreeNode, V>( key: K, value: V @@ -11,6 +17,9 @@ public class RBTreeNode, V>( return "${super.toStringSimpleView()} - ${colorName()}" } + /** + * Returns the color name of this node. + */ private fun colorName(): String { return if (isRed) "RED" else "BLACK" } From 742427fbcc337067046e97836a25b703ef99190c Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Mon, 25 Mar 2024 23:02:26 +0300 Subject: [PATCH 027/122] docs(AVLTree & AVLTreeNode): add doc-strings --- .../tree_trippers/binary_trees/AVLTree.kt | 32 +++++++++++++++++++ .../nodes/binary_nodes/AVLTreeNode.kt | 12 ++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt index 5d6a9d1..30f4c3e 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt +++ b/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt @@ -3,6 +3,14 @@ package tree_trippers.binary_trees import tree_trippers.nodes.binary_nodes.AVLTreeNode +/** + * AVLTree class represents a self-balancing binary search tree that maintains + * the property of AVL tree, ensuring that the heights + * of the two child subtrees of any node differ by at most one. + * + * @param K the type of the keys in the tree + * @param V the value type associated with the key + */ public class AVLTree, V>: AbstractBSTree>() { override fun createNode(key: K, value: V): AVLTreeNode { @@ -14,10 +22,20 @@ public class AVLTree, V>: AbstractBSTree?): Int { return (node?.rightChild?.height ?: 0) - (node?.leftChild?.height ?: 0) } + /** + * Defines and calls the method(s) for balancing the current [node]. + * + * @param node the node to balance in the AVL tree + * @return the root of the rebalanced subtree or a [node] if balance is not required + */ private fun balance(node: AVLTreeNode): AVLTreeNode { when (balanceFactor(node)) { -2 -> { @@ -34,6 +52,13 @@ public class AVLTree, V>: AbstractBSTree): AVLTreeNode { val nodeSwapped = node.rightChild ?: return node node.rightChild = nodeSwapped.leftChild @@ -44,6 +69,13 @@ public class AVLTree, V>: AbstractBSTree): AVLTreeNode { val nodeSwapped = node.leftChild ?: return node node.leftChild = nodeSwapped.rightChild diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt index c6b14ee..7363a49 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt +++ b/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt @@ -2,13 +2,23 @@ package tree_trippers.nodes.binary_nodes import kotlin.math.max - +/** + * AVLTreeNode class represents a node in an AVL tree, containing a key-value pair and additional + * information for maintaining the AVL property such as the height of the node. + * + * @param K the key type that implements the Comparable interface + * @param V the value type associated with the key + * @property height the height of the [AVLTreeNode] in the AVL tree + */ public class AVLTreeNode, V>( key: K, value: V ): AbstractBSTreeNode>(key, value) { + + /** The height of the node in the AVL tree, initialized to 1. */ public var height: Int = 1 + /** Updates height of the node in AVL tree based on the heights of its left and right child subtrees. */ public fun updateHeight() { val leftHeight = this.leftChild?.height ?: 0 val rightHeight = this.rightChild?.height ?: 0 From 92b4d211efff510aa679425b4ef30eda0aeb8ccf Mon Sep 17 00:00:00 2001 From: Vladimir Zaikin Date: Tue, 26 Mar 2024 19:37:02 +0300 Subject: [PATCH 028/122] style: rename one method to 'contains' and rename the library to 'TreeTripper' --- ReadMe.md | 4 ++-- lib/ReadMe.md | 24 +++++++++---------- .../SearchTree.kt | 6 ++--- .../binary_trees/AVLTree.kt | 4 ++-- .../binary_trees/AbstractBSTree.kt | 14 +++++------ .../binary_trees/BSTree.kt | 4 ++-- .../binary_trees/RBTree.kt | 8 +++---- .../iterators/BinarySearchTreeIterator.kt | 4 ++-- .../iterators/IterationOrders.kt | 2 +- .../nodes/SearchTreeNode.kt | 2 +- .../nodes/Utils.kt | 2 +- .../nodes/binary_nodes/AVLTreeNode.kt | 2 +- .../nodes/binary_nodes/AbstractBSTreeNode.kt | 6 ++--- .../nodes/binary_nodes/BSTreeNode.kt | 2 +- .../nodes/binary_nodes/RBTreeNode.kt | 2 +- 15 files changed, 43 insertions(+), 43 deletions(-) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/SearchTree.kt (88%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/binary_trees/AVLTree.kt (97%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/binary_trees/AbstractBSTree.kt (95%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/binary_trees/BSTree.kt (70%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/binary_trees/RBTree.kt (97%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/iterators/BinarySearchTreeIterator.kt (98%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/iterators/IterationOrders.kt (93%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/nodes/SearchTreeNode.kt (90%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/nodes/Utils.kt (96%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/nodes/binary_nodes/AVLTreeNode.kt (95%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/nodes/binary_nodes/AbstractBSTreeNode.kt (88%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/nodes/binary_nodes/BSTreeNode.kt (76%) rename lib/src/main/kotlin/{tree_trippers => tree_tripper}/nodes/binary_nodes/RBTreeNode.kt (92%) diff --git a/ReadMe.md b/ReadMe.md index cd7a016..3489b6c 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -3,10 +3,10 @@ [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/) -

TreeTrippers

+

TreeTripper

## Description -The project `TreeTrippers` contains an implementation of a library for working with search trees +The project `TreeTripper` contains an implementation of a library for working with search trees ## Roadmap diff --git a/lib/ReadMe.md b/lib/ReadMe.md index f1fe2b6..0327008 100644 --- a/lib/ReadMe.md +++ b/lib/ReadMe.md @@ -1,11 +1,11 @@ -# Kotlin Library `TreeTrippers` +# Kotlin Library `TreeTripper` ## Description -Library `TreeTrippers` - it is providing implementations of binary search trees data structures: -- [x] [`Binary Search Tree`](src/main/kotlin/tree_trippers/binary_trees/BSTree.kt), see more [information](https://en.wikipedia.org/wiki/Binary_search_tree) -- [x] [`AVL tree`](src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt), see more [information](https://en.wikipedia.org/wiki/AVL_tree) -- [x] [`Red-black tree`](src/main/kotlin/tree_trippers/binary_trees/RBTree.kt), see more [information](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) +Library `TreeTripper` - it is providing implementations of binary search trees data structures: +- [x] [`Binary Search Tree`](src/main/kotlin/tree_tripper/binary_trees/BSTree.kt), see more [information](https://en.wikipedia.org/wiki/Binary_search_tree) +- [x] [`AVL tree`](src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt), see more [information](https://en.wikipedia.org/wiki/AVL_tree) +- [x] [`Red-black tree`](src/main/kotlin/tree_tripper/binary_trees/RBTree.kt), see more [information](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) The library supports the extension both internally (future library updates) and externally (implemented by the user). @@ -26,9 +26,9 @@ To run building library execute command: ##### Example 1 (importing) ```kotlin -import tree_trippers.binary_trees.BSTree -import tree_trippers.binary_trees.AVLTree -import tree_trippers.binary_trees.RBTree +import tree_tripper.binary_trees.BSTree +import tree_tripper.binary_trees.AVLTree +import tree_tripper.binary_trees.RBTree val simpleTree = BSTree() // initialization of empty simple tree @@ -39,7 +39,7 @@ val rbTree = RBTree>() // initialization of empty re ##### Example 2 (inserting) Code: ```kotlin -import tree_trippers.binary_trees.BSTree +import tree_tripper.binary_trees.BSTree fun main() { val tree = BSTree() @@ -61,7 +61,7 @@ BSTree(1: 1, 2: 2, 3: 3, 4: 4, 5: 5, ) ##### Example 2 (searching) Code: ```kotlin -import tree_trippers.binary_trees.BSTree +import tree_tripper.binary_trees.BSTree fun main() { val tree = BSTree() @@ -93,7 +93,7 @@ null ##### Example 3 (removing) Code: ```kotlin -import tree_trippers.binary_trees.BSTree +import tree_tripper.binary_trees.BSTree fun main() { val tree = BSTree() @@ -126,4 +126,4 @@ BSTree(2: 2, 4: 4, ) ``` ## Documentation -See [_**documentation**_](src/main/kotlin/tree_trippers/SearchTree.kt) of library `TreeTrippers` to learn more about it. +See [_**documentation**_](src/main/kotlin/tree_tripper/SearchTree.kt) of library `TreeTripper` to learn more about it. diff --git a/lib/src/main/kotlin/tree_trippers/SearchTree.kt b/lib/src/main/kotlin/tree_tripper/SearchTree.kt similarity index 88% rename from lib/src/main/kotlin/tree_trippers/SearchTree.kt rename to lib/src/main/kotlin/tree_tripper/SearchTree.kt index cd05d9d..99745c5 100644 --- a/lib/src/main/kotlin/tree_trippers/SearchTree.kt +++ b/lib/src/main/kotlin/tree_tripper/SearchTree.kt @@ -1,6 +1,6 @@ -package tree_trippers +package tree_tripper -import tree_trippers.iterators.IterationOrders +import tree_tripper.iterators.IterationOrders public interface SearchTree, V>: Iterable> { @@ -17,7 +17,7 @@ public interface SearchTree, V>: Iterable> { public fun searchOrDefault(key: K, defaultValue: V): V - public fun isContains(key: K): Boolean + public fun contains(key: K): Boolean public fun getMaxDescendant(key: K): Pair? diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt similarity index 97% rename from lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt rename to lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt index 30f4c3e..8d83881 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AVLTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt @@ -1,6 +1,6 @@ -package tree_trippers.binary_trees +package tree_tripper.binary_trees -import tree_trippers.nodes.binary_nodes.AVLTreeNode +import tree_tripper.nodes.binary_nodes.AVLTreeNode /** diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt similarity index 95% rename from lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt rename to lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt index 47b5ce3..d5b4788 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -1,10 +1,10 @@ -package tree_trippers.binary_trees +package tree_tripper.binary_trees -import tree_trippers.SearchTree -import tree_trippers.iterators.BinarySearchTreeIterator -import tree_trippers.iterators.IterationOrders -import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode -import tree_trippers.nodes.notNullNodeAction +import tree_tripper.SearchTree +import tree_tripper.iterators.BinarySearchTreeIterator +import tree_tripper.iterators.IterationOrders +import tree_tripper.nodes.binary_nodes.AbstractBSTreeNode +import tree_tripper.nodes.notNullNodeAction public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { @@ -37,7 +37,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return search(key) ?: defaultValue } - override fun isContains(key: K): Boolean { + override fun contains(key: K): Boolean { return search(key) != null } diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt similarity index 70% rename from lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt rename to lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt index 5ee61f6..b8ae2d1 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/BSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt @@ -1,6 +1,6 @@ -package tree_trippers.binary_trees +package tree_tripper.binary_trees -import tree_trippers.nodes.binary_nodes.BSTreeNode +import tree_tripper.nodes.binary_nodes.BSTreeNode public class BSTree, V>: AbstractBSTree>() { diff --git a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt similarity index 97% rename from lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt rename to lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index e63faef..2f7ee6a 100644 --- a/lib/src/main/kotlin/tree_trippers/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -1,8 +1,8 @@ -package tree_trippers.binary_trees +package tree_tripper.binary_trees -import tree_trippers.nodes.binary_nodes.RBTreeNode -import tree_trippers.nodes.notNullNodeAction -import tree_trippers.nodes.notNullNodeUpdate +import tree_tripper.nodes.binary_nodes.RBTreeNode +import tree_tripper.nodes.notNullNodeAction +import tree_tripper.nodes.notNullNodeUpdate /** diff --git a/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt b/lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt similarity index 98% rename from lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt rename to lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt index f91f7db..ea40851 100644 --- a/lib/src/main/kotlin/tree_trippers/iterators/BinarySearchTreeIterator.kt +++ b/lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt @@ -1,6 +1,6 @@ -package tree_trippers.iterators +package tree_tripper.iterators -import tree_trippers.nodes.binary_nodes.AbstractBSTreeNode +import tree_tripper.nodes.binary_nodes.AbstractBSTreeNode import java.util.LinkedList import java.util.Queue diff --git a/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt b/lib/src/main/kotlin/tree_tripper/iterators/IterationOrders.kt similarity index 93% rename from lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt rename to lib/src/main/kotlin/tree_tripper/iterators/IterationOrders.kt index 34c75b4..7bcec24 100644 --- a/lib/src/main/kotlin/tree_trippers/iterators/IterationOrders.kt +++ b/lib/src/main/kotlin/tree_tripper/iterators/IterationOrders.kt @@ -1,4 +1,4 @@ -package tree_trippers.iterators +package tree_tripper.iterators /** diff --git a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt similarity index 90% rename from lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt rename to lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt index e7b3c02..660abf3 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/SearchTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt @@ -1,4 +1,4 @@ -package tree_trippers.nodes +package tree_tripper.nodes public interface SearchTreeNode, V, N: SearchTreeNode> { diff --git a/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt b/lib/src/main/kotlin/tree_tripper/nodes/Utils.kt similarity index 96% rename from lib/src/main/kotlin/tree_trippers/nodes/Utils.kt rename to lib/src/main/kotlin/tree_tripper/nodes/Utils.kt index 4d7b1e7..2fe421f 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/Utils.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/Utils.kt @@ -1,4 +1,4 @@ -package tree_trippers.nodes +package tree_tripper.nodes /** diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt similarity index 95% rename from lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt rename to lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt index 7363a49..eec6b77 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AVLTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt @@ -1,4 +1,4 @@ -package tree_trippers.nodes.binary_nodes +package tree_tripper.nodes.binary_nodes import kotlin.math.max diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt similarity index 88% rename from lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt rename to lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt index fcc9b55..04bfe55 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -1,7 +1,7 @@ -package tree_trippers.nodes.binary_nodes +package tree_tripper.nodes.binary_nodes -import tree_trippers.nodes.SearchTreeNode -import tree_trippers.nodes.notNullNodeAction +import tree_tripper.nodes.SearchTreeNode +import tree_tripper.nodes.notNullNodeAction public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>( diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt similarity index 76% rename from lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt rename to lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt index 767fa84..7b4485a 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/BSTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt @@ -1,4 +1,4 @@ -package tree_trippers.nodes.binary_nodes +package tree_tripper.nodes.binary_nodes public class BSTreeNode, V>( diff --git a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt similarity index 92% rename from lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt rename to lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt index 4e72b6d..9055646 100644 --- a/lib/src/main/kotlin/tree_trippers/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt @@ -1,4 +1,4 @@ -package tree_trippers.nodes.binary_nodes +package tree_tripper.nodes.binary_nodes /** From d32f2d110d55eede287fa91468c219e47fbb44a4 Mon Sep 17 00:00:00 2001 From: Vladimir Zaikin Date: Tue, 26 Mar 2024 19:45:22 +0300 Subject: [PATCH 029/122] feat: add 'get' and 'set' methods to interact with trees as with a map --- lib/src/main/kotlin/tree_tripper/SearchTree.kt | 4 ++++ .../kotlin/tree_tripper/binary_trees/AbstractBSTree.kt | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/src/main/kotlin/tree_tripper/SearchTree.kt b/lib/src/main/kotlin/tree_tripper/SearchTree.kt index 99745c5..fbd6147 100644 --- a/lib/src/main/kotlin/tree_tripper/SearchTree.kt +++ b/lib/src/main/kotlin/tree_tripper/SearchTree.kt @@ -9,6 +9,8 @@ public interface SearchTree, V>: Iterable> { public fun insertIfAbsent(key: K, value: V): Boolean + public operator fun set(key: K, value: V) + public fun remove(key: K): V? public fun removeWithDefault(key: K, defaultValue: V): V @@ -19,6 +21,8 @@ public interface SearchTree, V>: Iterable> { public fun contains(key: K): Boolean + public operator fun get(key: K): V? + public fun getMaxDescendant(key: K): Pair? public fun getMax(): Pair? diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt index d5b4788..eb6dd74 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -19,6 +19,10 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return insert(key, value, permissionUpdate = false) } + override fun set(key: K, value: V) { + insert(key, value) + } + override fun remove(key: K): V? { val resultRemove = removeNode(root, key) updateRoot(resultRemove.first) @@ -41,6 +45,10 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return search(key) != null } + override fun get(key: K): V? { + return search(key) + } + override fun getMaxDescendant(key: K): Pair? { val resultSearch = getMaxDescendantNode(searchNode(key)) ?: return null return Pair(resultSearch.key, resultSearch.value) From 986e09e746f375973ce97a90bad3ead1f245fc7e Mon Sep 17 00:00:00 2001 From: Vladimir Zaikin Date: Tue, 26 Mar 2024 19:51:04 +0300 Subject: [PATCH 030/122] docs: add doc-strings for the classes and interfaces: SearchTree, SearchTreeNode, AbstractBSTree, AbstractBSTreeNode, BSTree, BSTreeNode --- .../main/kotlin/tree_tripper/SearchTree.kt | 74 ++++++++++++++++++- .../binary_trees/AbstractBSTree.kt | 46 ++++++++++++ .../tree_tripper/binary_trees/BSTree.kt | 6 ++ .../tree_tripper/nodes/SearchTreeNode.kt | 16 ++++ .../nodes/binary_nodes/AbstractBSTreeNode.kt | 8 ++ .../nodes/binary_nodes/BSTreeNode.kt | 6 ++ 6 files changed, 153 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/SearchTree.kt b/lib/src/main/kotlin/tree_tripper/SearchTree.kt index fbd6147..d0e029b 100644 --- a/lib/src/main/kotlin/tree_tripper/SearchTree.kt +++ b/lib/src/main/kotlin/tree_tripper/SearchTree.kt @@ -3,42 +3,110 @@ package tree_tripper import tree_tripper.iterators.IterationOrders +/** + * The interface represents a search tree, that inherits from [Iterable] interface. + * + * @param K the key type in a tree, supporting the [Comparable] interface + * @param V the value type in a tree + */ public interface SearchTree, V>: Iterable> { + /** + * Inserts or updates the specified [value] with the specified [key] in a tree. + */ public fun insert(key: K, value: V) + /** + * Inserts the specified [value] with the specified [key] in a tree. + * @return true if the specified [value] with the specified [key] was inserted in a tree, + * false if a tree was not modified. + */ public fun insertIfAbsent(key: K, value: V): Boolean + /** + * Associates the specified [value] with the specified [key] in a tree. + */ public operator fun set(key: K, value: V) + /** + * Removes a given [key] and its corresponding value from a tree. + * @return the removed value associated with a given [key], or + * null if such a [key] does not present in a tree. + */ public fun remove(key: K): V? + /** + * Removes a given [key] and its corresponding value from a tree. + * @return the removed value associated with a given [key], or + * the [defaultValue] if such a [key] does not present in a tree. + */ public fun removeWithDefault(key: K, defaultValue: V): V + /** + * Searches the value associated with a given [key]. + * @return the value associated with a given [key], or + * null if such a [key] does not present in a tree. + */ public fun search(key: K): V? + /** + * Searches the value associated with a given [key]. + * @return the value associated with a given [key], or + * the [defaultValue] if such a [key] does not present in a tree. + */ public fun searchOrDefault(key: K, defaultValue: V): V + /** + * Checks if a given [key] is contained in a tree. + */ public fun contains(key: K): Boolean + /** + * Returns the value associated with a given [key], or + * null if such a [key] does not present in a tree. + */ public operator fun get(key: K): V? - public fun getMaxDescendant(key: K): Pair? - + /** + * Returns the key/value pair with the max key, or null if a tree is empty. + */ public fun getMax(): Pair? - public fun getMinDescendant(key: K): Pair? + /** + * Returns the key/value pair with the max key after a given [key], or + * null if such a [key] does not present in a tree. + */ + public fun getMaxDescendant(key: K): Pair? + /** + * Returns the key/value pair with the min key, or null if a tree is empty. + */ public fun getMin(): Pair? + /** + * Returns the key/value pair with the min key after a given [key], or + * null if such a [key] does not present in a tree. + */ + public fun getMinDescendant(key: K): Pair? + + /** + * Returns the size of a tree. + */ public fun getSize(): Int + // Iterator public fun iterator(order: IterationOrders): Iterator> public fun forEach(order: IterationOrders, action: (Pair) -> Unit) + /** + * Returns a string with a transformed tree according to the [order]. + */ public fun toString(order: IterationOrders): String + /** + * Returns a string with a transformed tree according to the tree structure. + */ public fun toStringWithTreeView(): String } diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt index eb6dd74..38c36d4 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -7,6 +7,14 @@ import tree_tripper.nodes.binary_nodes.AbstractBSTreeNode import tree_tripper.nodes.notNullNodeAction +/** + * The class represents an abstract binary search tree, + * from which binary search trees are inherited. + * + * @param K the key type in a tree, supporting the [Comparable] interface + * @param V the value type in a tree + * @param N the node type in a tree + */ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { private var root: N? = null private var size: Int = 0 @@ -101,16 +109,31 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return "${this.javaClass.simpleName}(\n$builder)" } + /** + * Returns a new [N] node with the specified [value] with the specified [key]. + */ protected abstract fun createNode(key: K, value: V): N + /** + * Changes the root to a given [node]. + */ protected open fun updateRoot(node: N?) { root = node } + /** + * Balances subtree with a given [node] at the top. + */ protected open fun balanceTree(node: N): N { return node } + /** + * Inserts the specified [value] with the specified [key] in a tree or + * updates [value] if [permissionUpdate] is true + * @return true if the specified [value] with the specified [key] was inserted in a tree, + * false if a tree was not modified. + */ private fun insert(key: K, value: V, permissionUpdate: Boolean): Boolean { val insertResult: Pair = insertNode(root, key, value, permissionUpdate) updateRoot(insertResult.first) @@ -118,6 +141,12 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return insertResult.second } + /** + * Add recursively the node with the specified [value] with the specified [key] in a tree or + * updates [value] if [permissionUpdate] is true and balances tree on every call. + * @return a pair of a new or balanced [N] node, and true if the specified [value] with + * the specified [key] was inserted in a tree, false if not. + */ private fun insertNode(node: N?, key: K, value: V, permissionUpdate: Boolean): Pair { if (node == null) return Pair(createNode(key, value), true) @@ -137,6 +166,11 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return Pair(balanceTree(node), resultInsert.second) } + /** + * Removes recursively the node with a given [key] and balances tree on every call. + * @return a pair of a balanced [N] node or null, and [V] value corresponding the given [key] + * if a node was removed, null if not. + */ protected open fun removeNode(node: N?, key: K): Pair { if (node == null) return Pair(null, null) @@ -165,6 +199,10 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return Pair(balanceTree(node), resultRemove.second) } + /** + * Searches the node with a given key. + * @return the found node or null if the node is not contained in a tree. + */ private fun searchNode(key: K): N? { var nodeCurrent: N? = root ?: return null @@ -180,6 +218,10 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return null } + /** + * Returns the [N] node with the max key after a given [node], or null + * if such a [node] is not contained in a tree or all children of a given [node] are null. + */ protected fun getMaxDescendantNode(node: N?): N? { if (node == null) return null @@ -189,6 +231,10 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return nodeCurrent } + /** + * Returns the [N] node with the min key after a given [node], or null + * if such a [node] is not contained in a tree or all children of a given [node] are null. + */ protected fun getMinDescendantNode(node: N?): N? { if (node == null) return null diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt index b8ae2d1..773e4a3 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt @@ -3,6 +3,12 @@ package tree_tripper.binary_trees import tree_tripper.nodes.binary_nodes.BSTreeNode +/** + * The class represents the binary search tree. + * + * @param K the key type in the tree, supporting the [Comparable] interface + * @param V the value type in the tree + */ public class BSTree, V>: AbstractBSTree>() { override fun createNode(key: K, value: V): BSTreeNode { diff --git a/lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt index 660abf3..78fb845 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt @@ -1,12 +1,28 @@ package tree_tripper.nodes +/** + * The interface represents a node of a search tree. + * + * @param K the key type of node, supporting the [Comparable] interface + * @param V the value type of node + * @param N the node type + */ public interface SearchTreeNode, V, N: SearchTreeNode> { + /** + * Returns a [N] list of not null children of a node. + */ public fun getChildren(): List + /** + * Returns a string with a transformed to the simple view node. + */ public fun toStringSimpleView(): String + /** + * Transforms a node to the [builder] of the subtree structure with a some [indent]. + */ public fun toStringWithSubtreeView(indent: Int, builder: StringBuilder): Unit } diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt index 04bfe55..0a900eb 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -4,6 +4,14 @@ import tree_tripper.nodes.SearchTreeNode import tree_tripper.nodes.notNullNodeAction +/** + * The class represents a node of the abstract binary search tree, + * from which nodes of binary search trees are inherited. + * + * @param K the [key] type of node, supporting the [Comparable] interface + * @param V the [value] type of node + * @param N the node type + */ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>( public val key: K, public var value: V diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt index 7b4485a..4f2cf7c 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt @@ -1,6 +1,12 @@ package tree_tripper.nodes.binary_nodes +/** + * The class represents a node of the binary search tree. + * + * @param K the key type of the node, supporting the [Comparable] interface + * @param V the value type of the node + */ public class BSTreeNode, V>( key: K, value: V From 7ed7d11183ab722d98d5830632b6259504bacb02 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 26 Mar 2024 20:52:47 +0300 Subject: [PATCH 031/122] refactor: add more information to the ReadMe file withsome links to docs and more info about implementation about red-black tree --- lib/ReadMe.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/ReadMe.md b/lib/ReadMe.md index 0327008..289c334 100644 --- a/lib/ReadMe.md +++ b/lib/ReadMe.md @@ -5,7 +5,13 @@ Library `TreeTripper` - it is providing implementations of binary search trees data structures: - [x] [`Binary Search Tree`](src/main/kotlin/tree_tripper/binary_trees/BSTree.kt), see more [information](https://en.wikipedia.org/wiki/Binary_search_tree) - [x] [`AVL tree`](src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt), see more [information](https://en.wikipedia.org/wiki/AVL_tree) -- [x] [`Red-black tree`](src/main/kotlin/tree_tripper/binary_trees/RBTree.kt), see more [information](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) +- [x] [`Red-black tree`](src/main/kotlin/tree_tripper/binary_trees/RBTree.kt), + see more [information](https://en.wikipedia.org/wiki/Left-leaning_red%E2%80%93black_tree) + +> [!IMPORTANT] +> The red-black tree is implemented based on the algorithm +> of left-linear red-black trees described by Robert Sedgewick +> on his [website](https://sedgewick.io/) and [presentation](https://sedgewick.io/wp-content/uploads/2022/03/2008-09LLRB.pdf) about it The library supports the extension both internally (future library updates) and externally (implemented by the user). @@ -18,9 +24,9 @@ To run building library execute command: ## Using library ### Basic operations -+ `insert` -+ `search` -+ `remove` ++ `insert`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L17) ++ `search`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L50) ++ `remove`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L36) ### Examples @@ -31,9 +37,9 @@ import tree_tripper.binary_trees.AVLTree import tree_tripper.binary_trees.RBTree -val simpleTree = BSTree() // initialization of empty simple tree +val simpleTree = BSTree() // initialization of empty simple binary search tree val avlTree = AVLTree() // initialization of empty AVL tree -val rbTree = RBTree>() // initialization of empty red-black tree +val rbTree = RBTree>() // initialization of empty Red-Black tree ``` ##### Example 2 (inserting) @@ -126,4 +132,4 @@ BSTree(2: 2, 4: 4, ) ``` ## Documentation -See [_**documentation**_](src/main/kotlin/tree_tripper/SearchTree.kt) of library `TreeTripper` to learn more about it. +See more [_**documentation**_](src/main/kotlin/tree_tripper/SearchTree.kt) of library `TreeTripper` to learn more about it. From cee9e28dd077b5c2d232e90139d2717d2430a96f Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 26 Mar 2024 21:00:21 +0300 Subject: [PATCH 032/122] refactor: update info at root ReadMe file by updating roadmap and authors informations --- ReadMe.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 3489b6c..563d568 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -11,7 +11,7 @@ The project `TreeTripper` contains an implementation of a library for working wi ## Roadmap - [x] Add library -- [ ] Add documentation of library +- [x] Add documentation of library - [ ] Add tests to library

(back to top)

@@ -20,12 +20,10 @@ The project `TreeTripper` contains an implementation of a library for working wi - [@IliaSuponeff](https://github.com/IliaSuponeff) - [@RodionovMaxim05](https://github.com/RodionovMaxim05) -- [@Vladimir Zaikin](https://github.com/Friend-zva) - +- [@Friend-zva](https://github.com/Friend-zva), sometimes in commits his name is _**Vladimir Zaikin**_ ## License - Distributed under the [MIT License](https://choosealicense.com/licenses/mit/). See [`LICENSE`](LICENSE) for more information.

(back to top)

From bf69065c68cd3446123c731ea425e61eb6feba1a Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 28 Mar 2024 13:09:08 +0300 Subject: [PATCH 033/122] test: add simple tests case for RBTree such as initializing tree, checking methods of node colors, update access params of method and properties from private to protected, add method to comparing by equals for nodes --- lib/build.gradle.kts | 2 + .../binary_trees/AbstractBSTree.kt | 3 +- .../tree_tripper/binary_trees/RBTree.kt | 6 +- .../nodes/binary_nodes/AbstractBSTreeNode.kt | 32 +++++++ .../nodes/binary_nodes/RBTreeNode.kt | 41 ++++++++- .../tree_tripper/binary_trees/RBTreeTest.kt | 84 +++++++++++++++++++ .../assistants/RBTreeTestAssistant.kt | 67 +++++++++++++++ 7 files changed, 229 insertions(+), 6 deletions(-) create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 035fdc2..94ec347 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -22,6 +22,8 @@ repositories { dependencies { // This dependency is exported to consumers, that is to say found on their compile classpath. api(libs.commons.math3) + // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params + testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2") // This dependency is used internally, and not exposed to consumers on their own compile classpath. implementation(libs.guava) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt index 38c36d4..cd6eb70 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -16,7 +16,8 @@ import tree_tripper.nodes.notNullNodeAction * @param N the node type in a tree */ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { - private var root: N? = null + protected var root: N? = null + private set private var size: Int = 0 override fun insert(key: K, value: V) { diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index 2f7ee6a..53ed271 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -11,7 +11,7 @@ import tree_tripper.nodes.notNullNodeUpdate * @param K the type of the keys in the tree * @param V the type of the values in the tree */ -public class RBTree, V>: AbstractBSTree>() { +public open class RBTree, V>: AbstractBSTree>() { override fun createNode(key: K, value: V): RBTreeNode { return RBTreeNode(key, value) @@ -77,7 +77,7 @@ public class RBTree, V>: AbstractBSTree> * @param node the node to check * @return `true` if the node is red, `false` otherwise */ - private fun isRedColor(node: RBTreeNode?): Boolean { + protected fun isRedColor(node: RBTreeNode?): Boolean { if (node == null) return false return node.isRed } @@ -88,7 +88,7 @@ public class RBTree, V>: AbstractBSTree> * @param node the node to check color its left child * @return `true` if left child of `node` is red, `false` otherwise */ - private fun isRedLeftChild(node: RBTreeNode?): Boolean { + protected fun isRedLeftChild(node: RBTreeNode?): Boolean { if (node == null) return false return isRedColor(node.leftChild) } diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt index 0a900eb..aecb28c 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -23,6 +23,38 @@ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeN return listOfNotNull(leftChild, rightChild) } + public open fun dateEqual(other: Any?): Boolean { + if (other == null) return false + if (this == other) return true + if (this.javaClass != other.javaClass) return false + + other as AbstractBSTreeNode<*, *, *> + return this.key == other.key && this.value == other.value + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this === other) return true + if (javaClass != other.javaClass) return false + + other as AbstractBSTreeNode<*, *, *> + + if (key != other.key) return false + if (value != other.value) return false + if (leftChild != other.leftChild) return false + if (rightChild != other.rightChild) return false + + return true + } + + override fun hashCode(): Int { + var result = key.hashCode() + result = 31 * result + (value?.hashCode() ?: 0) + result = 31 * result + (leftChild?.hashCode() ?: 0) + result = 31 * result + (rightChild?.hashCode() ?: 0) + return result + } + override fun toString(): String { return "${this.javaClass.simpleName}(key=$key, value=$value)" } diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt index 9055646..d001a22 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt @@ -7,12 +7,49 @@ package tree_tripper.nodes.binary_nodes * @param K the key type * @param V the value type */ -public class RBTreeNode, V>( +public class RBTreeNode, V>( key: K, value: V -): AbstractBSTreeNode>(key, value) { +) : AbstractBSTreeNode>(key, value) { var isRed: Boolean = true + public constructor(key: K, value: V, isRed: Boolean = true) : this(key, value) { + this.isRed = isRed + } + + public constructor( + key: K, + value: V, + isRed: Boolean = true, + leftChild: RBTreeNode? = null, + rightChild: RBTreeNode? = null + ) : this(key, value, isRed) { + this.leftChild = leftChild + this.rightChild = rightChild + } + + override fun dateEqual(other: Any?): Boolean { + if (other == null) return false + if (this === other) return true + if (javaClass != other.javaClass) return false + + other as RBTreeNode<*, *> + return super.dateEqual(other) && this.isRed == other.isRed + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (this === other) return true + if (javaClass != other.javaClass) return false + + other as RBTreeNode<*, *> + return super.equals(other) && this.isRed == other.isRed + } + + override fun hashCode(): Int { + return 31 * super.hashCode() + isRed.hashCode() + } + override fun toStringSimpleView(): String { return "${super.toStringSimpleView()} - ${colorName()}" } diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt new file mode 100644 index 0000000..db75720 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -0,0 +1,84 @@ +package tree_tripper.binary_trees + +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import tree_tripper.binary_trees.assistants.RBTreeTestAssistant +import tree_tripper.nodes.binary_nodes.RBTreeNode + + +class RBTreeTest { + private lateinit var tree: RBTreeTestAssistant + + @BeforeEach + fun setup() { + tree = RBTreeTestAssistant() + } + + @Test + public fun testTreeInitializing() { + tree = RBTreeTestAssistant() + tree.assertRoot(null) {"Root of RBTree is not null by standard initialize."} + Assertions.assertEquals(0, tree.getSize()) + } + + @ParameterizedTest + @MethodSource("colorizeNodes") + public fun testNodeColorChecker(expected: Boolean, node: RBTreeNode?) { + tree.assertNodeColor(expected, node) + } + + @ParameterizedTest + @MethodSource("colorizeLeftChildNodes") + public fun testNodeLeftChildColorChecker(expected: Boolean, node: RBTreeNode?) { + tree.assertNodeLeftChildColor(expected, node) + } + + companion object { + @JvmStatic + fun colorizeNodes(): List = listOf( + Arguments.of(false, null), + Arguments.of(true, RBTreeNode(0, 0, true)), + Arguments.of(false, RBTreeNode(0, 0, false)), + ) + + @JvmStatic + fun colorizeLeftChildNodes(): List = listOf( + Arguments.of(false, null), + Arguments.of(false, RBTreeNode(0, 0, true)), + Arguments.of(false, RBTreeNode(0, 0, false)), + Arguments.of( + false, + RBTreeNode( + 1, 1, true, + null, RBTreeNode(0, 0, true) + ) + ), + Arguments.of( + false, + RBTreeNode( + 1, 1, true, + null, RBTreeNode(0, 0, false) + ) + ), + Arguments.of( + true, + RBTreeNode( + 1, 1, true, + RBTreeNode(0, 0, true), null + ) + ), + Arguments.of( + false, + RBTreeNode( + 1, 1, true, + RBTreeNode(0, 0, false), null + ) + ) + ) + } + +} \ No newline at end of file diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt new file mode 100644 index 0000000..db12a21 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -0,0 +1,67 @@ +package tree_tripper.binary_trees.assistants + +import org.junit.jupiter.api.Assertions +import tree_tripper.binary_trees.RBTree +import tree_tripper.nodes.binary_nodes.RBTreeNode +import tree_tripper.nodes.notNullNodeAction +import java.util.Queue +import java.util.LinkedList + + +public class RBTreeTestAssistant, V>: RBTree() { + + public fun assertIsRBTree() { + assert(!isRedColor(root)) {"Root of RBTree is red. Must be black."} + val queue: Queue> = LinkedList>(listOf(root)) + + while (queue.isNotEmpty()) { + val node: RBTreeNode = queue.remove() + val leftCompareResult: Int = notNullNodeAction( + node.leftChild, -1 + ) { leftChild -> leftChild.key.compareTo(node.key) } + val rightCompareResult: Int = notNullNodeAction( + node.rightChild, 1 + ) { rightChild -> rightChild.key.compareTo(node.key) } + + assert(leftCompareResult <= -1) { + "Left child of $node is not a BST node: keys compare result: $leftCompareResult" + } + assert(rightCompareResult >= 1) { + "Right child of $node is not a BST node: keys compare result: $rightCompareResult" + } + assert(!isRedColor(node.rightChild)) {"Right child of node at RBTree is red. Its must be black."} + if (isRedColor(node)) { + assert(!isRedLeftChild(node)) {"Left child of red node at RBTree is red. Its must be black."} + } + queue.addAll(node.getChildren()) + } + } + + public fun checkTree(checkAction: (RBTreeNode) -> Boolean): Boolean { + val queue: Queue> = LinkedList>(listOf(root)) + + while (queue.isNotEmpty()) { + val node: RBTreeNode = queue.remove() + if (!checkAction(node)) return false + queue.addAll(node.getChildren()) + } + return true + } + + fun assertRoot(node: RBTreeNode?, lazyMassage: () -> String) { + assert(root?.dateEqual(node) ?: (node == null)) { lazyMassage() } + } + + fun assertNodeColor(expected: Boolean, node: RBTreeNode?) { + Assertions.assertEquals(expected, isRedColor(node)) + } + + fun assertNodeLeftChildColor(expected: Boolean, node: RBTreeNode?) { + Assertions.assertEquals(expected, isRedLeftChild(node)) + } + + fun assertLeftRotation(expected: RBTreeNode, node: RBTreeNode) { + Assertions.assertEquals(expected, rotateLeft(node)) + } + +} From 52efdcdc670eed0d1abede46ef6ff9c13ab350f8 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 28 Mar 2024 13:11:01 +0300 Subject: [PATCH 034/122] fix: remove invalid assertion method for RBTreeTestAssistant which used for check results of left rotation for nodes --- .../binary_trees/assistants/RBTreeTestAssistant.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index db12a21..997cfee 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -60,8 +60,4 @@ public class RBTreeTestAssistant, V>: RBTree() { Assertions.assertEquals(expected, isRedLeftChild(node)) } - fun assertLeftRotation(expected: RBTreeNode, node: RBTreeNode) { - Assertions.assertEquals(expected, rotateLeft(node)) - } - } From a75143742a75bd99661aaa1df9709761930715d4 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 28 Mar 2024 21:08:51 +0300 Subject: [PATCH 035/122] refactor: replace equals nodes methods to assertions utils file(AssertionUtils.kt) --- .../nodes/binary_nodes/AbstractBSTreeNode.kt | 32 ---------------- .../nodes/binary_nodes/RBTreeNode.kt | 18 --------- lib/src/test/kotlin/AssertionUtils.kt | 37 +++++++++++++++++++ .../assistants/RBTreeTestAssistant.kt | 7 +++- 4 files changed, 43 insertions(+), 51 deletions(-) create mode 100644 lib/src/test/kotlin/AssertionUtils.kt diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt index aecb28c..0a900eb 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -23,38 +23,6 @@ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeN return listOfNotNull(leftChild, rightChild) } - public open fun dateEqual(other: Any?): Boolean { - if (other == null) return false - if (this == other) return true - if (this.javaClass != other.javaClass) return false - - other as AbstractBSTreeNode<*, *, *> - return this.key == other.key && this.value == other.value - } - - override fun equals(other: Any?): Boolean { - if (other == null) return false - if (this === other) return true - if (javaClass != other.javaClass) return false - - other as AbstractBSTreeNode<*, *, *> - - if (key != other.key) return false - if (value != other.value) return false - if (leftChild != other.leftChild) return false - if (rightChild != other.rightChild) return false - - return true - } - - override fun hashCode(): Int { - var result = key.hashCode() - result = 31 * result + (value?.hashCode() ?: 0) - result = 31 * result + (leftChild?.hashCode() ?: 0) - result = 31 * result + (rightChild?.hashCode() ?: 0) - return result - } - override fun toString(): String { return "${this.javaClass.simpleName}(key=$key, value=$value)" } diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt index d001a22..002c067 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt @@ -28,24 +28,6 @@ public class RBTreeNode, V>( this.rightChild = rightChild } - override fun dateEqual(other: Any?): Boolean { - if (other == null) return false - if (this === other) return true - if (javaClass != other.javaClass) return false - - other as RBTreeNode<*, *> - return super.dateEqual(other) && this.isRed == other.isRed - } - - override fun equals(other: Any?): Boolean { - if (other == null) return false - if (this === other) return true - if (javaClass != other.javaClass) return false - - other as RBTreeNode<*, *> - return super.equals(other) && this.isRed == other.isRed - } - override fun hashCode(): Int { return 31 * super.hashCode() + isRed.hashCode() } diff --git a/lib/src/test/kotlin/AssertionUtils.kt b/lib/src/test/kotlin/AssertionUtils.kt new file mode 100644 index 0000000..2109199 --- /dev/null +++ b/lib/src/test/kotlin/AssertionUtils.kt @@ -0,0 +1,37 @@ +import org.junit.jupiter.api.Assertions +import tree_tripper.nodes.binary_nodes.AbstractBSTreeNode + + +public fun > assertBinaryNodeDataEquals(nodeFirst: N?, nodeSecond: N?): Unit { + assertBinaryNodeDataEquals(nodeFirst, nodeSecond) { _, _: N -> true } +} + +public fun > assertBinaryNodeDataEquals( + nodeFirst: N?, + nodeSecond: N?, + assertAction: (N, N) -> (Boolean) +): Unit { + if (nodeFirst == null) return Assertions.assertNull(nodeSecond) { "Second node is not null." } + if (nodeSecond == null) return Assertions.assertNull(nodeFirst) { "First node is not null." } + + Assertions.assertEquals(nodeFirst.key, nodeSecond.key) { "Keys are not equal." } + Assertions.assertEquals(nodeFirst.value, nodeSecond.value) { "Values are not equal." } + Assertions.assertTrue(assertAction(nodeFirst, nodeSecond)) { "Action assertion is invalid equals" } +} + +public fun > assertBinaryNodeDeepEquals(nodeFirst: N?, nodeSecond: N?): Unit { + assertBinaryNodeDeepEquals(nodeFirst, nodeSecond) { _, _: N? -> true } +} + +public fun > assertBinaryNodeDeepEquals( + nodeFirst: N?, + nodeSecond: N?, + assertAction: (N, N) -> (Boolean) +): Unit { + if (nodeFirst == null) return Assertions.assertNull(nodeSecond) { "Second node is not null." } + if (nodeSecond == null) return Assertions.assertNull(nodeFirst) { "First node is not null." } + + assertBinaryNodeDataEquals(nodeFirst, nodeSecond, assertAction) + assertBinaryNodeDeepEquals(nodeFirst.leftChild, nodeSecond.leftChild, assertAction) + assertBinaryNodeDeepEquals(nodeFirst.rightChild, nodeSecond.rightChild, assertAction) +} diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index 997cfee..958d278 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -1,5 +1,6 @@ package tree_tripper.binary_trees.assistants +import assertBinaryNodeDataEquals import org.junit.jupiter.api.Assertions import tree_tripper.binary_trees.RBTree import tree_tripper.nodes.binary_nodes.RBTreeNode @@ -49,7 +50,11 @@ public class RBTreeTestAssistant, V>: RBTree() { } fun assertRoot(node: RBTreeNode?, lazyMassage: () -> String) { - assert(root?.dateEqual(node) ?: (node == null)) { lazyMassage() } + try { + assertBinaryNodeDataEquals(root, node) {rootNode, expectedNode -> rootNode.isRed == expectedNode.isRed} + } catch (e: AssertionError) { + throw AssertionError(lazyMassage(), e) + } } fun assertNodeColor(expected: Boolean, node: RBTreeNode?) { From 9e72e4411aecc62c9288ee0fc6a4de77b0b5eb2a Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 28 Mar 2024 21:10:24 +0300 Subject: [PATCH 036/122] test: add tests for utility methods to work with nodes --- .../kotlin/tree_tripper/nodes/UtilsTest.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt diff --git a/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt new file mode 100644 index 0000000..8ae0159 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt @@ -0,0 +1,40 @@ +package tree_tripper.nodes + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import tree_tripper.nodes.binary_nodes.BSTreeNode + + +public class UtilsTest { + + @ParameterizedTest + @MethodSource("testNodeUpdateCases") + public fun testNodeUpdate(expected: Boolean, node: BSTreeNode?) { + var isActivateAction: Boolean = false + notNullNodeUpdate(node) { isActivateAction = true } + Assertions.assertEquals(expected, isActivateAction) + } + + @ParameterizedTest + @MethodSource("testNodeActionCases") + public fun testNodeAction(expected: Boolean, node: BSTreeNode?) { + Assertions.assertEquals(expected, notNullNodeAction(node, false) { expected }) + } + + companion object { + @JvmStatic + fun testNodeUpdateCases(): List = listOf( + Arguments.of(false, null), + Arguments.of(true, BSTreeNode(0, 0)), + ) + + @JvmStatic + fun testNodeActionCases(): List = listOf( + Arguments.of(false, null), + Arguments.of(true, BSTreeNode(0, 0)), + ) + } + +} From 462822596a7c9a9be41da76e64f6f3a147e33d7e Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 28 Mar 2024 21:54:09 +0300 Subject: [PATCH 037/122] test: add tests for red-black tree node to check valid initialize and toString methods --- .../nodes/binary_nodes/RBTreeNode.kt | 14 +-- .../nodes/binary_nodes/RBTreeNodeTest.kt | 88 +++++++++++++++++++ 2 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt index 002c067..47ecd01 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt @@ -13,25 +13,19 @@ public class RBTreeNode, V>( ) : AbstractBSTreeNode>(key, value) { var isRed: Boolean = true - public constructor(key: K, value: V, isRed: Boolean = true) : this(key, value) { + public constructor(key: K, value: V, isRed: Boolean) : this(key, value) { this.isRed = isRed } public constructor( - key: K, - value: V, - isRed: Boolean = true, - leftChild: RBTreeNode? = null, - rightChild: RBTreeNode? = null + key: K, value: V, isRed: Boolean, + leftChild: RBTreeNode?, + rightChild: RBTreeNode? ) : this(key, value, isRed) { this.leftChild = leftChild this.rightChild = rightChild } - override fun hashCode(): Int { - return 31 * super.hashCode() + isRed.hashCode() - } - override fun toStringSimpleView(): String { return "${super.toStringSimpleView()} - ${colorName()}" } diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt new file mode 100644 index 0000000..1ba3db7 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -0,0 +1,88 @@ +package tree_tripper.nodes.binary_nodes + +import assertBinaryNodeDeepEquals +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + + +public class RBTreeNodeTest { + + @ParameterizedTest + @MethodSource("testNodeSimpleInitializeCases") + public fun testNodeSimpleInitialize(key: Int, value: Int?) { + val node = RBTreeNode(key, value) + Assertions.assertEquals(key, node.key) { "Key of node is not equal." } + Assertions.assertEquals(value, node.value) { "Value of node is not equal." } + Assertions.assertTrue(node.isRed) { "Color of node is not red." } + Assertions.assertNull(node.leftChild) { "Left child of node is not null." } + Assertions.assertNull(node.rightChild) { "Right child of node is not null." } + } + + @ParameterizedTest + @MethodSource("testNodeColorTypeInitializeCases") + public fun testNodeColorTypeInitialize(isRed: Boolean) { + val node = RBTreeNode(0, 0, isRed) + Assertions.assertEquals(isRed, node.isRed) { "Color of node is not equal." } + Assertions.assertNull(node.leftChild) { "Left child of node is not null." } + Assertions.assertNull(node.rightChild) { "Right child of node is not null." } + } + + @ParameterizedTest + @MethodSource("testNodeFullInitializeCases") + public fun testNodeFullInitialize(leftChild: RBTreeNode?, rightChild: RBTreeNode?) { + val node = RBTreeNode(0, 0, false, leftChild, rightChild) + assertBinaryNodeDeepEquals(leftChild, node.leftChild) { n1, n2 -> n1.isRed == n2.isRed } + assertBinaryNodeDeepEquals(rightChild, node.rightChild) { n1, n2 -> n1.isRed == n2.isRed } + } + +// @ParameterizedTest +// @MethodSource("testColorNameCases") +// public fun testColorName(expected: String, node: RBTreeNode) { +// Assertions.assertEquals(expected, node()) +// } + + @ParameterizedTest + @MethodSource("testToStringSimpleViewCases") + public fun testToStringSimpleView(expected: String, node: RBTreeNode) { + Assertions.assertEquals(expected, node.toStringSimpleView()) + } + + companion object { + @JvmStatic + fun testNodeSimpleInitializeCases(): List = listOf( + Arguments.of(-1, -1), + Arguments.of(0, 0), + Arguments.of(2, 2), + Arguments.of(3, null), + ) + + @JvmStatic + fun testNodeColorTypeInitializeCases(): List = listOf( + Arguments.of(false), + Arguments.of(true), + ) + + @JvmStatic + fun testNodeFullInitializeCases(): List = listOf( + Arguments.of(null, null), + Arguments.of(RBTreeNode(1, null), null), + Arguments.of(null, RBTreeNode(-1, null)), + Arguments.of(RBTreeNode(-1, null), RBTreeNode(-1, null)), + ) + +// @JvmStatic +// fun testColorNameCases(): List = listOf( +// Arguments.of("RED", RBTreeNode(-1, null)), +// Arguments.of("BLACK", RBTreeNode(0, 0, false)), +// ) + + @JvmStatic + fun testToStringSimpleViewCases(): List = listOf( + Arguments.of("(-1, null) - RED", RBTreeNode(-1, null)), + Arguments.of("(0, 0) - BLACK", RBTreeNode(0, 0, false)), + ) + } + +} From cbd915251c0c7b7a1612091aa0c433333f2abb8f Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 28 Mar 2024 21:55:19 +0300 Subject: [PATCH 038/122] refactor: update method source to template view: 'test%(test-name)Cases' --- .../test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index db75720..42511f7 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -26,27 +26,27 @@ class RBTreeTest { } @ParameterizedTest - @MethodSource("colorizeNodes") + @MethodSource("checkColorNodeCases") public fun testNodeColorChecker(expected: Boolean, node: RBTreeNode?) { tree.assertNodeColor(expected, node) } @ParameterizedTest - @MethodSource("colorizeLeftChildNodes") + @MethodSource("checkColorLeftChildNodesCases") public fun testNodeLeftChildColorChecker(expected: Boolean, node: RBTreeNode?) { tree.assertNodeLeftChildColor(expected, node) } companion object { @JvmStatic - fun colorizeNodes(): List = listOf( + fun checkColorNodeCases(): List = listOf( Arguments.of(false, null), Arguments.of(true, RBTreeNode(0, 0, true)), Arguments.of(false, RBTreeNode(0, 0, false)), ) @JvmStatic - fun colorizeLeftChildNodes(): List = listOf( + fun checkColorLeftChildNodesCases(): List = listOf( Arguments.of(false, null), Arguments.of(false, RBTreeNode(0, 0, true)), Arguments.of(false, RBTreeNode(0, 0, false)), From 42ab01836cf28b2f04b285bc7371dc10007ef93d Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Thu, 28 Mar 2024 22:01:13 +0300 Subject: [PATCH 039/122] refactor: update test methods name to template view: 'test%(test-cause)' at RBTreeTest.kt file --- .../kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 42511f7..cd06415 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -26,27 +26,27 @@ class RBTreeTest { } @ParameterizedTest - @MethodSource("checkColorNodeCases") - public fun testNodeColorChecker(expected: Boolean, node: RBTreeNode?) { + @MethodSource("testNodeColorCases") + public fun testNodeColor(expected: Boolean, node: RBTreeNode?) { tree.assertNodeColor(expected, node) } @ParameterizedTest - @MethodSource("checkColorLeftChildNodesCases") - public fun testNodeLeftChildColorChecker(expected: Boolean, node: RBTreeNode?) { + @MethodSource("testNodeLeftChildColorCases") + public fun testNodeLeftChildColor(expected: Boolean, node: RBTreeNode?) { tree.assertNodeLeftChildColor(expected, node) } companion object { @JvmStatic - fun checkColorNodeCases(): List = listOf( + fun testNodeColorCases(): List = listOf( Arguments.of(false, null), Arguments.of(true, RBTreeNode(0, 0, true)), Arguments.of(false, RBTreeNode(0, 0, false)), ) @JvmStatic - fun checkColorLeftChildNodesCases(): List = listOf( + fun testNodeLeftChildColorCases(): List = listOf( Arguments.of(false, null), Arguments.of(false, RBTreeNode(0, 0, true)), Arguments.of(false, RBTreeNode(0, 0, false)), From e914ddee8afb01f24e09fdae7b6a614faa92bf6b Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Fri, 29 Mar 2024 12:05:58 +0300 Subject: [PATCH 040/122] test: add tests for private left and right node rotations methods --- .../tree_tripper/binary_trees/RBTree.kt | 4 +- .../tree_tripper/binary_trees/RBTreeTest.kt | 100 ++++++++++++++++++ .../assistants/RBTreeTestAssistant.kt | 9 ++ 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index 53ed271..d45b848 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -100,7 +100,7 @@ public open class RBTree, V>: AbstractBSTree): RBTreeNode { + protected fun rotateLeft(node: RBTreeNode): RBTreeNode { val rightChild: RBTreeNode = node.rightChild ?: return node node.rightChild = rightChild.leftChild rightChild.leftChild = node @@ -117,7 +117,7 @@ public open class RBTree, V>: AbstractBSTree): RBTreeNode { + protected fun rotateRight(node: RBTreeNode): RBTreeNode { val leftChild: RBTreeNode = node.leftChild ?: return node node.leftChild = leftChild.rightChild leftChild.rightChild = node diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index cd06415..199fb32 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -37,6 +37,18 @@ class RBTreeTest { tree.assertNodeLeftChildColor(expected, node) } + @ParameterizedTest + @MethodSource("testNodeRotateLeftCases") + public fun testNodeRotateLeft(expected: RBTreeNode, node: RBTreeNode) { + tree.assertNodeLeftRotation(expected, node) + } + + @ParameterizedTest + @MethodSource("testNodeRotateRightCases") + public fun testNodeRotateRight(expected: RBTreeNode, node: RBTreeNode) { + tree.assertNodeRightRotation(expected, node) + } + companion object { @JvmStatic fun testNodeColorCases(): List = listOf( @@ -79,6 +91,94 @@ class RBTreeTest { ) ) ) + + @JvmStatic + fun testNodeRotateLeftCases(): List = listOf( + Arguments.of( + RBTreeNode( + 0, 0, false, + null, null + ), + RBTreeNode( + 0, 0, false, + null, null + ), + ), + Arguments.of( + RBTreeNode( + 1, 1, false, + RBTreeNode(0, 0, true), null + ), + RBTreeNode( + 0, 0, false, + null, RBTreeNode(1, 1, true) + ), + ), + Arguments.of( + RBTreeNode( + 1, 1, true, + RBTreeNode(0, 0, true), null + ), + RBTreeNode( + 0, 0, true, + null, RBTreeNode(1, 1, true) + ) + ), + Arguments.of( + RBTreeNode( + 1, 1, false, + RBTreeNode(0, 0, true), null + ), + RBTreeNode( + 0, 0, false, + null, RBTreeNode(1, 1, false) + ) + ) + ) + + @JvmStatic + fun testNodeRotateRightCases(): List = listOf( + Arguments.of( + RBTreeNode( + 0, 0, false, + null, null + ), + RBTreeNode( + 0, 0, false, + null, null + ), + ), + Arguments.of( + RBTreeNode( + 1, 1, false, + null, RBTreeNode(0, 0, true) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(1, 1, true), null + ), + ), +// Arguments.of( +// RBTreeNode( +// 1, 1, true, +// RBTreeNode(0, 0, true), null +// ), +// RBTreeNode( +// 0, 0, true, +// null, RBTreeNode(1, 1, true) +// ) +// ), +// Arguments.of( +// RBTreeNode( +// 1, 1, false, +// RBTreeNode(0, 0, true), null +// ), +// RBTreeNode( +// 0, 0, false, +// null, RBTreeNode(1, 1, false) +// ) +// ) + ) } } \ No newline at end of file diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index 958d278..1d1ae68 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -1,6 +1,7 @@ package tree_tripper.binary_trees.assistants import assertBinaryNodeDataEquals +import assertBinaryNodeDeepEquals import org.junit.jupiter.api.Assertions import tree_tripper.binary_trees.RBTree import tree_tripper.nodes.binary_nodes.RBTreeNode @@ -65,4 +66,12 @@ public class RBTreeTestAssistant, V>: RBTree() { Assertions.assertEquals(expected, isRedLeftChild(node)) } + fun assertNodeLeftRotation(expected: RBTreeNode, node: RBTreeNode) { + assertBinaryNodeDeepEquals(expected, rotateLeft(node)) {n1, n2 -> n1.isRed == n2.isRed} + } + + fun assertNodeRightRotation(expected: RBTreeNode, node: RBTreeNode) { + assertBinaryNodeDeepEquals(expected, rotateRight(node)) {n1, n2 -> n1.isRed == n2.isRed} + } + } From cc88eda9ee9c5d6e870e91cb2f2bfa4a19c3f647 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Fri, 29 Mar 2024 12:22:48 +0300 Subject: [PATCH 041/122] test: add test for private method to flip color of node and its childs colors --- .../tree_tripper/binary_trees/RBTree.kt | 2 +- .../tree_tripper/binary_trees/RBTreeTest.kt | 60 +++++++++++++++++++ .../assistants/RBTreeTestAssistant.kt | 5 ++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index d45b848..2bc8ddf 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -132,7 +132,7 @@ public open class RBTree, V>: AbstractBSTree): Unit { + protected fun flipColors(node: RBTreeNode): Unit { node.isRed = !node.isRed notNullNodeUpdate(node.leftChild) { child -> child.isRed = !child.isRed } notNullNodeUpdate(node.rightChild) { child -> child.isRed = !child.isRed } diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 199fb32..5b7f8f0 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -49,6 +49,12 @@ class RBTreeTest { tree.assertNodeRightRotation(expected, node) } + @ParameterizedTest + @MethodSource("testNodeColorFlipCases") + public fun testNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { + tree.assertNodeColorFlip(expected, node) + } + companion object { @JvmStatic fun testNodeColorCases(): List = listOf( @@ -179,6 +185,60 @@ class RBTreeTest { // ) // ) ) + + @JvmStatic + fun testNodeColorFlipCases(): List = listOf( + Arguments.of( + RBTreeNode( + 0, 0, false, + null, null + ), + RBTreeNode( + 0, 0, true, + null, null + ), + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + null, null + ), + RBTreeNode( + 0, 0, true, + null, null + ), + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + RBTreeNode(1, 1, false), null + ), + RBTreeNode( + 0, 0, true, + RBTreeNode(1, 1), null + ), + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + null, RBTreeNode(1, 1, true) + ), + RBTreeNode( + 0, 0, true, + null, RBTreeNode(1, 1, false) + ), + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + RBTreeNode(2, 2, false), RBTreeNode(1, 1, true) + ), + RBTreeNode( + 0, 0, true, + RBTreeNode(2, 2), RBTreeNode(1, 1, false) + ), + ), + ) } } \ No newline at end of file diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index 1d1ae68..4d952dc 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -74,4 +74,9 @@ public class RBTreeTestAssistant, V>: RBTree() { assertBinaryNodeDeepEquals(expected, rotateRight(node)) {n1, n2 -> n1.isRed == n2.isRed} } + fun assertNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { + flipColors(node) + assertBinaryNodeDeepEquals(expected, node) {n1, n2 -> n1.isRed == n2.isRed} + } + } From 3deb3c63b2e1289d1294d98c413d94899b812769 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sat, 30 Mar 2024 12:02:32 +0300 Subject: [PATCH 042/122] test: add tests for private methods of creation new tree node, updating root and add empty test for balance tree method --- .../tree_tripper/binary_trees/RBTreeTest.kt | 57 ++++++++++++------- .../assistants/RBTreeTestAssistant.kt | 13 +++++ 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 5b7f8f0..1551531 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -55,6 +55,24 @@ class RBTreeTest { tree.assertNodeColorFlip(expected, node) } + @ParameterizedTest + @MethodSource("testNodeCreationCases") + public fun testNodeCreation(key: Int, value: Int) { + tree.assertNodeCreation(key, value) + } + + @ParameterizedTest + @MethodSource("testUpdateRootCases") + public fun testUpdateRoot(node: RBTreeNode?) { + tree.assertUpdateRoot(node) + } + + @ParameterizedTest + @MethodSource("testBalanceTreeCases") + public fun testBalanceTree(expectedNodeTreeView: RBTreeNode, nodeTreeView: RBTreeNode) { + tree.assertBalanceTree(expectedNodeTreeView, nodeTreeView) + } + companion object { @JvmStatic fun testNodeColorCases(): List = listOf( @@ -164,26 +182,6 @@ class RBTreeTest { RBTreeNode(1, 1, true), null ), ), -// Arguments.of( -// RBTreeNode( -// 1, 1, true, -// RBTreeNode(0, 0, true), null -// ), -// RBTreeNode( -// 0, 0, true, -// null, RBTreeNode(1, 1, true) -// ) -// ), -// Arguments.of( -// RBTreeNode( -// 1, 1, false, -// RBTreeNode(0, 0, true), null -// ), -// RBTreeNode( -// 0, 0, false, -// null, RBTreeNode(1, 1, false) -// ) -// ) ) @JvmStatic @@ -239,6 +237,25 @@ class RBTreeTest { ), ), ) + + @JvmStatic + fun testNodeCreationCases(): List = listOf( + Arguments.of(0, 0), + Arguments.of(1, 1), + Arguments.of(-1, -1), + ) + + @JvmStatic + fun testUpdateRootCases(): List = listOf( + Arguments.of(null), + Arguments.of(RBTreeNode(0, 0, true)), + Arguments.of(RBTreeNode(0, 0, false)), + ) + + @JvmStatic + fun testBalanceTreeCases(): List = listOf( + + ) } } \ No newline at end of file diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index 4d952dc..d67bcd9 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -79,4 +79,17 @@ public class RBTreeTestAssistant, V>: RBTree() { assertBinaryNodeDeepEquals(expected, node) {n1, n2 -> n1.isRed == n2.isRed} } + fun assertNodeCreation(key: K, value: V) { + assertBinaryNodeDeepEquals(createNode(key, value), RBTreeNode(key, value)) { n1, n2 -> n1.isRed == n2.isRed} + } + + fun assertUpdateRoot(node: RBTreeNode?) { + updateRoot(node) + assertBinaryNodeDataEquals(root, if (node != null) RBTreeNode(node.key, node.value, false) else null) + } + + fun assertBalanceTree(expectedNodeTreeView: RBTreeNode, nodeTreeView: RBTreeNode) { + assertBinaryNodeDeepEquals(expectedNodeTreeView, balanceTree(nodeTreeView)) {n1, n2 -> n1.isRed == n2.isRed} + } + } From 4d504e29e211499dbe1e9c99b38809608b3c01ac Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Sat, 30 Mar 2024 12:33:15 +0300 Subject: [PATCH 043/122] test(AVLTreeNode): add constructor for node, add tests for updateHeight --- .../nodes/binary_nodes/AVLTreeNode.kt | 10 ++ .../nodes/binary_nodes/AVLTreeNodeTest.kt | 94 +++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt index eec6b77..0e8e42a 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt @@ -15,6 +15,16 @@ public class AVLTreeNode, V>( value: V ): AbstractBSTreeNode>(key, value) { + public constructor( + key: K, value: V, height: Int, + leftChild: AVLTreeNode?, + rightChild: AVLTreeNode? + ) : this(key, value) { + this.height = height + this.leftChild = leftChild + this.rightChild = rightChild + } + /** The height of the node in the AVL tree, initialized to 1. */ public var height: Int = 1 diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt new file mode 100644 index 0000000..7c3a65b --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt @@ -0,0 +1,94 @@ +package tree_tripper.nodes.binary_nodes + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + + +class AVLTreeNodeTest { + + @ParameterizedTest + @MethodSource("testUpdateHeight") + public fun testUpdateHeight(expected: Int, node: AVLTreeNode) { + node.updateHeight() + Assertions.assertEquals(expected, node.height) {"The height does not match the expected."} + } + + companion object { + @JvmStatic + fun testUpdateHeight(): List = listOf( + Arguments.of(1, + AVLTreeNode( + 0, 0, 0, + null, null + ) + ), + + Arguments.of(2, + AVLTreeNode( + 0, 0, 0, + AVLTreeNode( + 1, 1, 1, + null, null + ), + null + ) + ), + + Arguments.of(2, + AVLTreeNode( + 0, 0, 0, + null, + AVLTreeNode( + 1, 1, 1, + null, null + ) + ) + ), + + Arguments.of(2, + AVLTreeNode( + 0, 0, 0, + AVLTreeNode( + 1, 1, 1, + null, null + ), + AVLTreeNode( + 2, 2, 1, + null, null + ) + ) + ), + + Arguments.of(3, + AVLTreeNode( + 0, 0, 0, + AVLTreeNode( + 1, 1, 2, + null, null + ), + AVLTreeNode( + 2, 2, 1, + null, null + ) + ) + ), + + Arguments.of(3, + AVLTreeNode( + 0, 0, 0, + AVLTreeNode( + 1, 1, 1, + null, null + ), + AVLTreeNode( + 2, 2, 2, + null, null + ) + ) + ) + + ) + } +} \ No newline at end of file From 3106128be02261228f70450849cd7c158bdd7dc4 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Sat, 30 Mar 2024 12:47:11 +0300 Subject: [PATCH 044/122] test(AVLTree): update access params of method from private to protected, add tests for balanceFactor, rotateLeft, rotateRight --- .../tree_tripper/binary_trees/AVLTree.kt | 10 +- .../tree_tripper/binary_trees/AVLTreeTest.kt | 256 ++++++++++++++++++ .../assistants/AVLTreeTestAssistant.kt | 34 +++ 3 files changed, 295 insertions(+), 5 deletions(-) create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt index 8d83881..0ea16fe 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt @@ -11,7 +11,7 @@ import tree_tripper.nodes.binary_nodes.AVLTreeNode * @param K the type of the keys in the tree * @param V the value type associated with the key */ -public class AVLTree, V>: AbstractBSTree>() { +public open class AVLTree, V>: AbstractBSTree>() { override fun createNode(key: K, value: V): AVLTreeNode { return AVLTreeNode(key, value) @@ -26,7 +26,7 @@ public class AVLTree, V>: AbstractBSTree?): Int { + protected fun balanceFactor(node: AVLTreeNode?): Int { return (node?.rightChild?.height ?: 0) - (node?.leftChild?.height ?: 0) } @@ -36,7 +36,7 @@ public class AVLTree, V>: AbstractBSTree): AVLTreeNode { + protected fun balance(node: AVLTreeNode): AVLTreeNode { when (balanceFactor(node)) { -2 -> { if (balanceFactor(node.leftChild) == 1) @@ -59,7 +59,7 @@ public class AVLTree, V>: AbstractBSTree): AVLTreeNode { + protected fun rotateLeft(node: AVLTreeNode): AVLTreeNode { val nodeSwapped = node.rightChild ?: return node node.rightChild = nodeSwapped.leftChild nodeSwapped.leftChild = node @@ -76,7 +76,7 @@ public class AVLTree, V>: AbstractBSTree): AVLTreeNode { + protected fun rotateRight(node: AVLTreeNode): AVLTreeNode { val nodeSwapped = node.leftChild ?: return node node.leftChild = nodeSwapped.rightChild nodeSwapped.rightChild = node diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt new file mode 100644 index 0000000..b12c79d --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt @@ -0,0 +1,256 @@ +package tree_tripper.binary_trees + +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import tree_tripper.binary_trees.assistants.AVLTreeTestAssistant +import tree_tripper.nodes.binary_nodes.AVLTreeNode + + +class AVLTreeTest { + private lateinit var tree: AVLTreeTestAssistant + + @BeforeEach + fun setup() { + tree = AVLTreeTestAssistant() + } + + @Test + public fun testTreeInitializing() { + tree.assertRoot(null) { "Root of AVLTree is not null by standard initialize." } + Assertions.assertEquals(0, tree.getSize()) + } + + @ParameterizedTest + @MethodSource("checkBalanceFactor") + public fun checkBalanceFactor(node: AVLTreeNode?) { + tree.assertBalanceFactor(node) + } + + @ParameterizedTest + @MethodSource("testNodeRotateRightCases") + public fun testNodeRotateRightCases(expected: AVLTreeNode, node: AVLTreeNode) { + tree.assertNodeRightRotation(expected, node) + } + + @ParameterizedTest + @MethodSource("testNodeRotateLeftCases") + public fun testNodeRotateLeftCases(expected: AVLTreeNode, node: AVLTreeNode) { + tree.assertNodeLeftRotation(expected, node) + } + + companion object { + + @JvmStatic + fun checkBalanceFactor(): List = listOf( + + Arguments.of( + AVLTreeNode( + 0, 0, 1, + null, + null + ) + ), + + Arguments.of( + AVLTreeNode( + 0, 0, 2, + null, + AVLTreeNode(0, 0, 1, null, null) + ) + ), + + Arguments.of( + AVLTreeNode( + 0, 0, 2, + AVLTreeNode(0, 0, 1,null, null), + null + ) + ), + + Arguments.of( + AVLTreeNode(0, 0, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(0, 0, 1, null, null) + ) + ), + ) + + @JvmStatic + fun testNodeRotateLeftCases(): List = listOf( + + //Null check + Arguments.of( + AVLTreeNode( + 0, 0, 1, + null, null + ), + AVLTreeNode( + 0, 0, 1, + null, null + ) + ), + + //Simple left rotation + Arguments.of( + AVLTreeNode( + 1, 1, 2, + AVLTreeNode( + 0, 0, 1, null, null), + AVLTreeNode( + 2, 2, 1, null, null), + ), + AVLTreeNode( + 0, 0, 3, + null, + AVLTreeNode( + 1, 1, 2, + null, + AVLTreeNode( + 2, 2, 1, + null, null + ), + ), + ), + ), + + //Left rotation with children + Arguments.of( + AVLTreeNode( + 3, 3, 3, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode( + 0, 0, 1, + null, null + ), + AVLTreeNode( + 2, 2, 1, + null, null + ) + ), + AVLTreeNode( + 4, 4, 2, + null, + AVLTreeNode( + 5, 5, 1, + null, null + ) + ) + ), + AVLTreeNode( + 1, 1, 4, + AVLTreeNode( + 0, 0, 1, + null, null + ), + AVLTreeNode( + 3, 3, 3, + AVLTreeNode( + 2, 2, 1, + null, null + ), + AVLTreeNode( + 4, 4, 2, + null, + AVLTreeNode( + 5, 5, 1, + null, null + ) + ) + ) + ) + ) + ) + + @JvmStatic + fun testNodeRotateRightCases(): List = listOf( + + //Null check + Arguments.of( + AVLTreeNode( + 0, 0, 1, + null, null + ), + AVLTreeNode( + 0, 0, 1, + null, null + ) + ), + + //Right rotation with children + Arguments.of( + AVLTreeNode( + 1, 1, 2, + AVLTreeNode( + 0, 0, 1, null, null), + AVLTreeNode( + 2, 2, 1, null, null), + ), + AVLTreeNode( + 2, 2, 3, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode( + 0, 0, 1, + null, null + ), + null, + ), + null + ), + ), + + //Right rotation with children + Arguments.of( + AVLTreeNode( + 2, 2, 3, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode( + 0, 0, 1, + null, null + ), + null + ), + AVLTreeNode( + 4, 4, 2, + AVLTreeNode( + 3, 3, 1, + null, null + ), + AVLTreeNode( + 5, 5, 1, + null, null + ), + ), + ), + AVLTreeNode( + 4, 4, 4, + AVLTreeNode( + 2, 2, 3, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode( + 0, 0, 1, + null, null + ), + null + ), + AVLTreeNode( + 3, 3, 1, + null, null + ) + ), + AVLTreeNode( + 5, 5, 1, + null, null), + ) + ) + ) + + } +} diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt new file mode 100644 index 0000000..c1bbcf4 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt @@ -0,0 +1,34 @@ +package tree_tripper.binary_trees.assistants + +import assertBinaryNodeDataEquals +import assertBinaryNodeDeepEquals +import tree_tripper.binary_trees.AVLTree +import tree_tripper.nodes.binary_nodes.AVLTreeNode +import kotlin.math.abs + + +public class AVLTreeTestAssistant, V> : AVLTree() { + + fun assertRoot(node: AVLTreeNode?, lazyMassage: () -> String) { + try { + assertBinaryNodeDataEquals(root, node) {rootNode, expectedNode -> rootNode.height == expectedNode.height} + } catch (e: AssertionError) { + throw AssertionError(lazyMassage(), e) + } + } + + fun assertBalanceFactor(node: AVLTreeNode?) { + assert(abs(balanceFactor(node)) <= 1) { + "Invalid height balance of $node, balance factor: ${balanceFactor(node)}." + } + } + + fun assertNodeRightRotation(expected: AVLTreeNode, node: AVLTreeNode) { + assertBinaryNodeDeepEquals(expected, rotateRight(node)) {node1, node2 -> node1.height == node2.height} + } + + fun assertNodeLeftRotation(expected: AVLTreeNode, node: AVLTreeNode) { + assertBinaryNodeDeepEquals(expected, rotateLeft(node)) {node1, node2 -> node1.height == node2.height} + } + +} From 1458b9d2feecf38fbf98ed023d3a72c2be5f7a45 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sat, 30 Mar 2024 14:32:38 +0300 Subject: [PATCH 045/122] test: update test for balance tree method by adding cases to test it --- .../tree_tripper/binary_trees/RBTreeTest.kt | 211 ++++++++++++------ 1 file changed, 139 insertions(+), 72 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 1551531..5c68bf0 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -21,7 +21,7 @@ class RBTreeTest { @Test public fun testTreeInitializing() { tree = RBTreeTestAssistant() - tree.assertRoot(null) {"Root of RBTree is not null by standard initialize."} + tree.assertRoot(null) { "Root of RBTree is not null by standard initialize." } Assertions.assertEquals(0, tree.getSize()) } @@ -87,31 +87,23 @@ class RBTreeTest { Arguments.of(false, RBTreeNode(0, 0, true)), Arguments.of(false, RBTreeNode(0, 0, false)), Arguments.of( - false, - RBTreeNode( - 1, 1, true, - null, RBTreeNode(0, 0, true) + false, RBTreeNode( + 1, 1, true, null, RBTreeNode(0, 0, true) ) ), Arguments.of( - false, - RBTreeNode( - 1, 1, true, - null, RBTreeNode(0, 0, false) + false, RBTreeNode( + 1, 1, true, null, RBTreeNode(0, 0, false) ) ), Arguments.of( - true, - RBTreeNode( - 1, 1, true, - RBTreeNode(0, 0, true), null + true, RBTreeNode( + 1, 1, true, RBTreeNode(0, 0, true), null ) ), Arguments.of( - false, - RBTreeNode( - 1, 1, true, - RBTreeNode(0, 0, false), null + false, RBTreeNode( + 1, 1, true, RBTreeNode(0, 0, false), null ) ) ) @@ -120,42 +112,29 @@ class RBTreeTest { fun testNodeRotateLeftCases(): List = listOf( Arguments.of( RBTreeNode( - 0, 0, false, - null, null + 0, 0, false, null, null ), RBTreeNode( - 0, 0, false, - null, null + 0, 0, false, null, null ), - ), - Arguments.of( + ), Arguments.of( RBTreeNode( - 1, 1, false, - RBTreeNode(0, 0, true), null + 1, 1, false, RBTreeNode(0, 0, true), null ), RBTreeNode( - 0, 0, false, - null, RBTreeNode(1, 1, true) + 0, 0, false, null, RBTreeNode(1, 1, true) ), - ), - Arguments.of( + ), Arguments.of( RBTreeNode( - 1, 1, true, - RBTreeNode(0, 0, true), null - ), - RBTreeNode( - 0, 0, true, - null, RBTreeNode(1, 1, true) + 1, 1, true, RBTreeNode(0, 0, true), null + ), RBTreeNode( + 0, 0, true, null, RBTreeNode(1, 1, true) ) - ), - Arguments.of( + ), Arguments.of( RBTreeNode( - 1, 1, false, - RBTreeNode(0, 0, true), null - ), - RBTreeNode( - 0, 0, false, - null, RBTreeNode(1, 1, false) + 1, 1, false, RBTreeNode(0, 0, true), null + ), RBTreeNode( + 0, 0, false, null, RBTreeNode(1, 1, false) ) ) ) @@ -164,22 +143,18 @@ class RBTreeTest { fun testNodeRotateRightCases(): List = listOf( Arguments.of( RBTreeNode( - 0, 0, false, - null, null + 0, 0, false, null, null ), RBTreeNode( - 0, 0, false, - null, null + 0, 0, false, null, null ), ), Arguments.of( RBTreeNode( - 1, 1, false, - null, RBTreeNode(0, 0, true) + 1, 1, false, null, RBTreeNode(0, 0, true) ), RBTreeNode( - 0, 0, false, - RBTreeNode(1, 1, true), null + 0, 0, false, RBTreeNode(1, 1, true), null ), ), ) @@ -188,52 +163,42 @@ class RBTreeTest { fun testNodeColorFlipCases(): List = listOf( Arguments.of( RBTreeNode( - 0, 0, false, - null, null + 0, 0, false, null, null ), RBTreeNode( - 0, 0, true, - null, null + 0, 0, true, null, null ), ), Arguments.of( RBTreeNode( - 0, 0, false, - null, null + 0, 0, false, null, null ), RBTreeNode( - 0, 0, true, - null, null + 0, 0, true, null, null ), ), Arguments.of( RBTreeNode( - 0, 0, false, - RBTreeNode(1, 1, false), null + 0, 0, false, RBTreeNode(1, 1, false), null ), RBTreeNode( - 0, 0, true, - RBTreeNode(1, 1), null + 0, 0, true, RBTreeNode(1, 1), null ), ), Arguments.of( RBTreeNode( - 0, 0, false, - null, RBTreeNode(1, 1, true) + 0, 0, false, null, RBTreeNode(1, 1, true) ), RBTreeNode( - 0, 0, true, - null, RBTreeNode(1, 1, false) + 0, 0, true, null, RBTreeNode(1, 1, false) ), ), Arguments.of( RBTreeNode( - 0, 0, false, - RBTreeNode(2, 2, false), RBTreeNode(1, 1, true) + 0, 0, false, RBTreeNode(2, 2, false), RBTreeNode(1, 1, true) ), RBTreeNode( - 0, 0, true, - RBTreeNode(2, 2), RBTreeNode(1, 1, false) + 0, 0, true, RBTreeNode(2, 2), RBTreeNode(1, 1, false) ), ), ) @@ -254,7 +219,109 @@ class RBTreeTest { @JvmStatic fun testBalanceTreeCases(): List = listOf( - + Arguments.of( + RBTreeNode(0, 0, false), RBTreeNode(0, 0, false) + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), null + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), null + ) + ), + Arguments.of( + RBTreeNode( + 1, 1, false, + RBTreeNode(0, 0, true), null + ), + RBTreeNode( + 0, 0, false, + null, RBTreeNode(1, 1, true) + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, true, + null, RBTreeNode(1, 1, false) + ), + RBTreeNode( + 0, 0, true, + null, RBTreeNode(1, 1, false) + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, false), RBTreeNode(1, 1, false) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, false), RBTreeNode(1, 1, false) + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), RBTreeNode(1, 1, false) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), RBTreeNode(1, 1, false) + ) + ), + Arguments.of( + RBTreeNode( + 1, 1, false, + RBTreeNode( + 0, 0, true, + RBTreeNode(-1, -1, false), null + ), + null + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, false), RBTreeNode(1, 1, true) + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, true, + RBTreeNode(-1, -1, false), RBTreeNode(1, 1, false) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), RBTreeNode(1, 1, true) + ) + ), + Arguments.of( + RBTreeNode(-1, -1, true, + RBTreeNode(-2, -2, false), RBTreeNode(0, 0, false) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode( + -1, -1, true, RBTreeNode(-2, -2), null + ), null + ) + ), + Arguments.of( + RBTreeNode(0, 0, true, + RBTreeNode( + -1, -1, false, + RBTreeNode(-2, -2), null + ), RBTreeNode(1, 1, false) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode( + -1, -1, true, RBTreeNode(-2, -2), null + ), + RBTreeNode(1, 1) + ) + ), ) } From 35b1ef6f5eb618af1a022206c2ce4d836fd0afab Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sat, 30 Mar 2024 14:39:06 +0300 Subject: [PATCH 046/122] refactor: remove redundant information at file RBTreeTest.kt during object initialization, and remove unnecessary tree creation in the initialization test, remove commented-out code in file RBTreeNodeTest.kt --- .../tree_tripper/binary_trees/RBTreeTest.kt | 27 +++++++++---------- .../nodes/binary_nodes/RBTreeNodeTest.kt | 12 --------- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 5c68bf0..657e73e 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -15,12 +15,11 @@ class RBTreeTest { @BeforeEach fun setup() { - tree = RBTreeTestAssistant() + tree = RBTreeTestAssistant() } @Test public fun testTreeInitializing() { - tree = RBTreeTestAssistant() tree.assertRoot(null) { "Root of RBTree is not null by standard initialize." } Assertions.assertEquals(0, tree.getSize()) } @@ -77,33 +76,33 @@ class RBTreeTest { @JvmStatic fun testNodeColorCases(): List = listOf( Arguments.of(false, null), - Arguments.of(true, RBTreeNode(0, 0, true)), - Arguments.of(false, RBTreeNode(0, 0, false)), + Arguments.of(true, RBTreeNode(0, 0, true)), + Arguments.of(false, RBTreeNode(0, 0, false)), ) @JvmStatic fun testNodeLeftChildColorCases(): List = listOf( Arguments.of(false, null), - Arguments.of(false, RBTreeNode(0, 0, true)), - Arguments.of(false, RBTreeNode(0, 0, false)), + Arguments.of(false, RBTreeNode(0, 0, true)), + Arguments.of(false, RBTreeNode(0, 0, false)), Arguments.of( - false, RBTreeNode( - 1, 1, true, null, RBTreeNode(0, 0, true) + false, RBTreeNode( + 1, 1, true, null, RBTreeNode(0, 0, true) ) ), Arguments.of( - false, RBTreeNode( - 1, 1, true, null, RBTreeNode(0, 0, false) + false, RBTreeNode( + 1, 1, true, null, RBTreeNode(0, 0, false) ) ), Arguments.of( - true, RBTreeNode( - 1, 1, true, RBTreeNode(0, 0, true), null + true, RBTreeNode( + 1, 1, true, RBTreeNode(0, 0, true), null ) ), Arguments.of( - false, RBTreeNode( - 1, 1, true, RBTreeNode(0, 0, false), null + false, RBTreeNode( + 1, 1, true, RBTreeNode(0, 0, false), null ) ) ) diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt index 1ba3db7..8945751 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -37,12 +37,6 @@ public class RBTreeNodeTest { assertBinaryNodeDeepEquals(rightChild, node.rightChild) { n1, n2 -> n1.isRed == n2.isRed } } -// @ParameterizedTest -// @MethodSource("testColorNameCases") -// public fun testColorName(expected: String, node: RBTreeNode) { -// Assertions.assertEquals(expected, node()) -// } - @ParameterizedTest @MethodSource("testToStringSimpleViewCases") public fun testToStringSimpleView(expected: String, node: RBTreeNode) { @@ -72,12 +66,6 @@ public class RBTreeNodeTest { Arguments.of(RBTreeNode(-1, null), RBTreeNode(-1, null)), ) -// @JvmStatic -// fun testColorNameCases(): List = listOf( -// Arguments.of("RED", RBTreeNode(-1, null)), -// Arguments.of("BLACK", RBTreeNode(0, 0, false)), -// ) - @JvmStatic fun testToStringSimpleViewCases(): List = listOf( Arguments.of("(-1, null) - RED", RBTreeNode(-1, null)), From 7a46637d9605727405e8868ffd20dba3707cb454 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Sat, 30 Mar 2024 16:06:22 +0300 Subject: [PATCH 047/122] test(AVLTree): add tests for balance, add test for balanceFactor, changed how checkBalanceFactor works, refactor tests --- .../tree_tripper/binary_trees/AVLTreeTest.kt | 254 +++++++++++------- .../assistants/AVLTreeTestAssistant.kt | 14 +- .../nodes/binary_nodes/AVLTreeNodeTest.kt | 47 +--- 3 files changed, 178 insertions(+), 137 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt index b12c79d..53d3aff 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt @@ -26,8 +26,14 @@ class AVLTreeTest { @ParameterizedTest @MethodSource("checkBalanceFactor") - public fun checkBalanceFactor(node: AVLTreeNode?) { - tree.assertBalanceFactor(node) + public fun checkBalanceFactor(expected: Int, node: AVLTreeNode?) { + tree.assertBalanceFactor(expected, node) + } + + @ParameterizedTest + @MethodSource("testBalanceCases") + public fun testBalanceCases(expected: AVLTreeNode, node: AVLTreeNode) { + tree.assertBalance(expected, node) } @ParameterizedTest @@ -47,15 +53,13 @@ class AVLTreeTest { @JvmStatic fun checkBalanceFactor(): List = listOf( - Arguments.of( - AVLTreeNode( - 0, 0, 1, - null, - null - ) + Arguments.of(0, null), + + Arguments.of(0, + AVLTreeNode(0, 0, 1, null, null) ), - Arguments.of( + Arguments.of(1, AVLTreeNode( 0, 0, 2, null, @@ -63,107 +67,188 @@ class AVLTreeTest { ) ), - Arguments.of( + Arguments.of(-1, AVLTreeNode( 0, 0, 2, - AVLTreeNode(0, 0, 1,null, null), + AVLTreeNode(0, 0, 1, null, null), null ) ), - Arguments.of( - AVLTreeNode(0, 0, 2, + Arguments.of(0, + AVLTreeNode( + 0, 0, 2, AVLTreeNode(0, 0, 1, null, null), AVLTreeNode(0, 0, 1, null, null) ) - ), + ) + ) @JvmStatic - fun testNodeRotateLeftCases(): List = listOf( + fun testBalanceCases(): List = listOf( - //Null check + //Does not require balance Arguments.of( + //Expected AVLTreeNode( - 0, 0, 1, - null, null + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) ), + //Testing AVLTreeNode( - 0, 0, 1, - null, null + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) ) ), //Simple left rotation Arguments.of( + //Expected AVLTreeNode( 1, 1, 2, - AVLTreeNode( - 0, 0, 1, null, null), - AVLTreeNode( - 2, 2, 1, null, null), + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) ), + //Testing AVLTreeNode( 0, 0, 3, null, AVLTreeNode( 1, 1, 2, null, - AVLTreeNode( - 2, 2, 1, - null, null - ), + AVLTreeNode(2, 2, 1, null, null) + ) + ) + ), + + //Simple right rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 2, 2, 3, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + null ), + null + ) + ), + + //Simple left right rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) ), + //Testing + AVLTreeNode( + 2, 2, 3, + AVLTreeNode( + 0, 0, 2, + null, + AVLTreeNode(1, 1, 1, null, null) + ), + null + ) + ), + + //Simple right left rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 0, 0, 3, + null, + AVLTreeNode( + 2, 2, 2, + AVLTreeNode(1, 1, 1, null, null), + null + ) + ) + ) + + ) + + @JvmStatic + fun testNodeRotateLeftCases(): List = listOf( + + //Null check + Arguments.of( + //Expected + AVLTreeNode(0, 0, 1, null, null), + //Testing + AVLTreeNode(0, 0, 1, null, null) + ), + + //Simple left rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 0, 0, 3, + null, + AVLTreeNode( + 1, 1, 2, + null, + AVLTreeNode(2, 2, 1, null, null) + ) + ) ), //Left rotation with children Arguments.of( + //Expected AVLTreeNode( 3, 3, 3, AVLTreeNode( 1, 1, 2, - AVLTreeNode( - 0, 0, 1, - null, null - ), - AVLTreeNode( - 2, 2, 1, - null, null - ) + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) ), AVLTreeNode( 4, 4, 2, null, - AVLTreeNode( - 5, 5, 1, - null, null - ) + AVLTreeNode(5, 5, 1, null, null) ) ), + //Testing AVLTreeNode( 1, 1, 4, - AVLTreeNode( - 0, 0, 1, - null, null - ), + AVLTreeNode(0, 0, 1, null, null), AVLTreeNode( 3, 3, 3, - AVLTreeNode( - 2, 2, 1, - null, null - ), + AVLTreeNode(2, 2, 1, null, null), AVLTreeNode( 4, 4, 2, null, - AVLTreeNode( - 5, 5, 1, - null, null - ) + AVLTreeNode(5, 5, 1, null, null) ) ) ) ) + ) @JvmStatic @@ -171,85 +256,64 @@ class AVLTreeTest { //Null check Arguments.of( - AVLTreeNode( - 0, 0, 1, - null, null - ), - AVLTreeNode( - 0, 0, 1, - null, null - ) + //Expected + AVLTreeNode(0, 0, 1, null, null), + //Testing + AVLTreeNode(0, 0, 1, null, null) ), - //Right rotation with children + //Simple right rotation Arguments.of( + //Expected AVLTreeNode( 1, 1, 2, - AVLTreeNode( - 0, 0, 1, null, null), - AVLTreeNode( - 2, 2, 1, null, null), + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) ), + //Testing AVLTreeNode( 2, 2, 3, AVLTreeNode( 1, 1, 2, - AVLTreeNode( - 0, 0, 1, - null, null - ), - null, + AVLTreeNode(0, 0, 1, null, null), + null ), null - ), + ) ), //Right rotation with children Arguments.of( + //Expected AVLTreeNode( 2, 2, 3, AVLTreeNode( 1, 1, 2, - AVLTreeNode( - 0, 0, 1, - null, null - ), + AVLTreeNode(0, 0, 1, null, null), null ), AVLTreeNode( 4, 4, 2, - AVLTreeNode( - 3, 3, 1, - null, null - ), - AVLTreeNode( - 5, 5, 1, - null, null - ), - ), + AVLTreeNode(3, 3, 1, null, null), + AVLTreeNode(5, 5, 1, null, null) + ) ), + //Testing AVLTreeNode( 4, 4, 4, AVLTreeNode( 2, 2, 3, AVLTreeNode( 1, 1, 2, - AVLTreeNode( - 0, 0, 1, - null, null - ), + AVLTreeNode(0, 0, 1, null, null), null ), - AVLTreeNode( - 3, 3, 1, - null, null - ) + AVLTreeNode(3, 3, 1, null, null) ), - AVLTreeNode( - 5, 5, 1, - null, null), + AVLTreeNode(5, 5, 1, null, null) ) ) + ) } diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt index c1bbcf4..22ef1c8 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt @@ -4,25 +4,29 @@ import assertBinaryNodeDataEquals import assertBinaryNodeDeepEquals import tree_tripper.binary_trees.AVLTree import tree_tripper.nodes.binary_nodes.AVLTreeNode -import kotlin.math.abs public class AVLTreeTestAssistant, V> : AVLTree() { fun assertRoot(node: AVLTreeNode?, lazyMassage: () -> String) { try { - assertBinaryNodeDataEquals(root, node) {rootNode, expectedNode -> rootNode.height == expectedNode.height} + assertBinaryNodeDataEquals(root, node) {root, expected -> root.height == expected.height} } catch (e: AssertionError) { throw AssertionError(lazyMassage(), e) } } - fun assertBalanceFactor(node: AVLTreeNode?) { - assert(abs(balanceFactor(node)) <= 1) { - "Invalid height balance of $node, balance factor: ${balanceFactor(node)}." + fun assertBalanceFactor(expected: Int, node: AVLTreeNode?) { + val factor = balanceFactor(node) + assert(factor == expected) { + "Invalid height balance of $node, balance factor: $factor, expected: $expected." } } + fun assertBalance(expected: AVLTreeNode, node: AVLTreeNode) { + assertBinaryNodeDeepEquals(expected, balance(node)) {node1, node2 -> node1.height == node2.height} + } + fun assertNodeRightRotation(expected: AVLTreeNode, node: AVLTreeNode) { assertBinaryNodeDeepEquals(expected, rotateRight(node)) {node1, node2 -> node1.height == node2.height} } diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt index 7c3a65b..d5e4725 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt @@ -19,19 +19,13 @@ class AVLTreeNodeTest { @JvmStatic fun testUpdateHeight(): List = listOf( Arguments.of(1, - AVLTreeNode( - 0, 0, 0, - null, null - ) + AVLTreeNode(0, 0, 0, null, null) ), Arguments.of(2, AVLTreeNode( 0, 0, 0, - AVLTreeNode( - 1, 1, 1, - null, null - ), + AVLTreeNode(1, 1, 1, null, null), null ) ), @@ -40,55 +34,34 @@ class AVLTreeNodeTest { AVLTreeNode( 0, 0, 0, null, - AVLTreeNode( - 1, 1, 1, - null, null - ) + AVLTreeNode(1, 1, 1, null, null) ) ), Arguments.of(2, AVLTreeNode( 0, 0, 0, - AVLTreeNode( - 1, 1, 1, - null, null - ), - AVLTreeNode( - 2, 2, 1, - null, null - ) + AVLTreeNode(1, 1, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) ) ), Arguments.of(3, AVLTreeNode( 0, 0, 0, - AVLTreeNode( - 1, 1, 2, - null, null - ), - AVLTreeNode( - 2, 2, 1, - null, null - ) + AVLTreeNode(1, 1, 2, null, null), + AVLTreeNode(2, 2, 1, null, null) ) ), Arguments.of(3, AVLTreeNode( 0, 0, 0, - AVLTreeNode( - 1, 1, 1, - null, null - ), - AVLTreeNode( - 2, 2, 2, - null, null - ) + AVLTreeNode(1, 1, 1, null, null), + AVLTreeNode(2, 2, 2, null, null) ) ) ) } -} \ No newline at end of file +} From e480d9d7bb5dc40f76bad629d864fd6d6bd73a6f Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sat, 30 Mar 2024 16:52:42 +0300 Subject: [PATCH 048/122] test: add tests for insert methods such as insert 1 element, insert double unequals values by 1 key, insert sorted elements insert reversed of sorted elements and unsorted elements --- .../tree_tripper/binary_trees/RBTreeTest.kt | 168 +++++++++++++++++- 1 file changed, 165 insertions(+), 3 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 657e73e..229b4b4 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -24,6 +24,61 @@ class RBTreeTest { Assertions.assertEquals(0, tree.getSize()) } + @Test + public fun testSimpleInsert() { + tree.insert(0, 0) + tree.assertRoot(RBTreeNode(0, 0, false)) { "Root of RBTree is not equal to inserted node." } + tree.assertIsRBTree() + Assertions.assertEquals(1, tree.getSize()) + } + + @Test + public fun testValueChangeInsert() { + tree.insert(0, 0) + tree.insert(0, 1) + tree.assertRoot(RBTreeNode(0, 1, false)) { "Root of RBTree is not equal to inserted node." } + } + + @ParameterizedTest + @MethodSource("testSortedInsertElementsCases") + public fun testSortedInsert(elements: List, expectedTreeView: RBTreeNode) { + val elementsSet = elements.toSet() + insert(elements.sorted()) + + tree.assertIsRBTree() + Assertions.assertEquals(elementsSet.size, tree.getSize()) + tree.assertRoot(expectedTreeView) { + "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}" + } + } + + @ParameterizedTest + @MethodSource("testReverseSortedInsertElementsCases") + public fun testReverseSortedInsert(elements: List, expectedTreeView: RBTreeNode) { + val elementsSet = elements.toSet() + insert(elements.sorted().reversed()) + + tree.assertIsRBTree() + Assertions.assertEquals(elementsSet.size, tree.getSize()) + tree.assertRoot(expectedTreeView) { + "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}" + } + } + + @ParameterizedTest + @MethodSource("testUnsortedInsertElementsCases") + public fun testUnsortedInsert(elements: List, expectedTreeView: RBTreeNode) { + val elementsSet = elements.toSet() + insert(elements) + + tree.assertIsRBTree() + Assertions.assertEquals(elementsSet.size, tree.getSize()) + tree.assertRoot(expectedTreeView) { + "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}\n" + "Tree view: ${tree.toStringWithTreeView()}" + } + } + @ParameterizedTest @MethodSource("testNodeColorCases") public fun testNodeColor(expected: Boolean, node: RBTreeNode?) { @@ -73,6 +128,98 @@ class RBTreeTest { } companion object { + + @JvmStatic + fun testSortedInsertElementsCases(): List = listOf( + Arguments.of( + listOf(10, 20, -10), + RBTreeNode(10,10, false, + RBTreeNode(-10, -10, false), + RBTreeNode(20, 20, false) + ) + ), + Arguments.of( + listOf(-10, -20, 10), + RBTreeNode(-10, -10, false, + RBTreeNode(-20, -20, false), + RBTreeNode(10, 10, false), + ) + ), + Arguments.of( + listOf(15, 34, -23, 20, 10, -100), + RBTreeNode(15, 15, false, + RBTreeNode(-23, -23, true, + RBTreeNode(-100, -100, false), + RBTreeNode(10, 10, false) + ), + RBTreeNode(34, 34, false, + RBTreeNode(20, 20, true), null + ) + ) + ), + ) + + @JvmStatic + fun testReverseSortedInsertElementsCases(): List = listOf( + Arguments.of( + listOf(10, 20, -10), + RBTreeNode(10,10, false, + RBTreeNode(-10, -10, false), + RBTreeNode(20, 20, false) + ) + ), + Arguments.of( + listOf(-10, -20, 10), + RBTreeNode(-10, -10, false, + RBTreeNode(-20, -20, false), + RBTreeNode(10, 10, false), + ) + ), + Arguments.of( + listOf(15, 34, -23, 20, 10, -100), + RBTreeNode(20, 20, false, + RBTreeNode(10, 10, true, + RBTreeNode(-23, -23, false, + RBTreeNode(-100, -100), + null + ), + RBTreeNode(15, 15, false) + ), + RBTreeNode(34, 34, false) + ) + ), + ) + + @JvmStatic + fun testUnsortedInsertElementsCases(): List = listOf( + Arguments.of( + listOf(10, 20, -10), + RBTreeNode(10,10, false, + RBTreeNode(-10, -10, false), + RBTreeNode(20, 20, false) + ) + ), + Arguments.of( + listOf(-10, -20, 10), + RBTreeNode(-10, -10, false, + RBTreeNode(-20, -20, false), + RBTreeNode(10, 10, false), + ) + ), + Arguments.of( + listOf(15, 34, -23, 20, 10, -100), + RBTreeNode(15, 15, false, + RBTreeNode(-23, -23, true, + RBTreeNode(-100, -100, false), + RBTreeNode(10, 10, false) + ), + RBTreeNode(34, 34, false, + RBTreeNode(20, 20, true), null + ) + ) + ), + ) + @JvmStatic fun testNodeColorCases(): List = listOf( Arguments.of(false, null), @@ -296,7 +443,8 @@ class RBTreeTest { ) ), Arguments.of( - RBTreeNode(-1, -1, true, + RBTreeNode( + -1, -1, true, RBTreeNode(-2, -2, false), RBTreeNode(0, 0, false) ), RBTreeNode( @@ -307,7 +455,8 @@ class RBTreeTest { ) ), Arguments.of( - RBTreeNode(0, 0, true, + RBTreeNode( + 0, 0, true, RBTreeNode( -1, -1, false, RBTreeNode(-2, -2), null @@ -324,4 +473,17 @@ class RBTreeTest { ) } -} \ No newline at end of file + private fun insert(elements: List) { + println("Elements: ${elements}") + for (element in elements) { + tree.insert(element, element) + } + } + + private fun nodeToStringTreeView(node: RBTreeNode): String { + val builder = StringBuilder() + node.toStringWithSubtreeView(0, builder) + return builder.toString() + } + +} From 2da470ef8b8479501b522c4438adb80afa9abb82 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sat, 30 Mar 2024 16:54:30 +0300 Subject: [PATCH 049/122] refactor: update initialize test by adding assertion 'is tree with root equals null is RBTree' --- lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 1 + .../tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 229b4b4..fc6ebf4 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -21,6 +21,7 @@ class RBTreeTest { @Test public fun testTreeInitializing() { tree.assertRoot(null) { "Root of RBTree is not null by standard initialize." } + tree.assertIsRBTree() Assertions.assertEquals(0, tree.getSize()) } diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index d67bcd9..567b106 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -14,7 +14,7 @@ public class RBTreeTestAssistant, V>: RBTree() { public fun assertIsRBTree() { assert(!isRedColor(root)) {"Root of RBTree is red. Must be black."} - val queue: Queue> = LinkedList>(listOf(root)) + val queue: Queue> = LinkedList>(listOfNotNull(root)) while (queue.isNotEmpty()) { val node: RBTreeNode = queue.remove() From 5f9b8ba6264006330a7cab5c6f22ef3355595eec Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sat, 30 Mar 2024 16:57:20 +0300 Subject: [PATCH 050/122] refactor: remove output printing log information of insert tests --- lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index fc6ebf4..1155a93 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -475,7 +475,6 @@ class RBTreeTest { } private fun insert(elements: List) { - println("Elements: ${elements}") for (element in elements) { tree.insert(element, element) } From 8c43815d315b18382a7c1ba32323142ce9eaff5a Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sat, 30 Mar 2024 17:07:02 +0300 Subject: [PATCH 051/122] refactor: update private insert of test to test public tree method 'set' together with 'insert' --- lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 1155a93..953e458 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -476,7 +476,7 @@ class RBTreeTest { private fun insert(elements: List) { for (element in elements) { - tree.insert(element, element) + tree[element] = element } } From d507e83309b2705ebb7405efe71eee143c284114 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Sat, 30 Mar 2024 17:39:36 +0300 Subject: [PATCH 052/122] test(AVLTree): add tests for createNode, balanceTree --- .../tree_tripper/binary_trees/AVLTreeTest.kt | 80 +++++++++++++++++++ .../assistants/AVLTreeTestAssistant.kt | 8 ++ 2 files changed, 88 insertions(+) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt index 53d3aff..7257a27 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt @@ -24,6 +24,18 @@ class AVLTreeTest { Assertions.assertEquals(0, tree.getSize()) } + @ParameterizedTest + @MethodSource("testNodeCreationCases") + public fun testNodeCreation(key: Int, value: Int) { + tree.assertNodeCreation(key, value) + } + + @ParameterizedTest + @MethodSource("testBalanceTreeCases") + public fun testBalanceTree(expected: AVLTreeNode, node: AVLTreeNode) { + tree.assertBalanceTree(expected, node) + } + @ParameterizedTest @MethodSource("checkBalanceFactor") public fun checkBalanceFactor(expected: Int, node: AVLTreeNode?) { @@ -50,6 +62,74 @@ class AVLTreeTest { companion object { + @JvmStatic + fun testNodeCreationCases(): List = listOf( + Arguments.of(0, 0), + Arguments.of(1, 1), + Arguments.of(-1, -1) + ) + + @JvmStatic + fun testBalanceTreeCases(): List = listOf( + + //Does not require balance + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 1, 1, 1, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ) + ), + + //Simple left rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 0, 0, 1, + null, + AVLTreeNode( + 1, 1, 2, + null, + AVLTreeNode(2, 2, 1, null, null) + ) + ) + ), + + //Simple right rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 2, 2, 1, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + null + ), + null + ) + ) + + ) + @JvmStatic fun checkBalanceFactor(): List = listOf( diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt index 22ef1c8..7e2cd8c 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt @@ -16,6 +16,14 @@ public class AVLTreeTestAssistant, V> : AVLTree() { } } + fun assertNodeCreation(key: K, value: V) { + assertBinaryNodeDeepEquals(createNode(key, value), AVLTreeNode(key, value)) {node1, node2 -> node1.height == node2.height} + } + + fun assertBalanceTree(expected: AVLTreeNode, node: AVLTreeNode) { + assertBinaryNodeDeepEquals(expected, balanceTree(node)) {node1, node2 -> node1.height == node2.height} + } + fun assertBalanceFactor(expected: Int, node: AVLTreeNode?) { val factor = balanceFactor(node) assert(factor == expected) { From 8a26f0b848d58a4413fcf36f6f39061329cfef11 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Sun, 31 Mar 2024 13:09:41 +0300 Subject: [PATCH 053/122] test(AVLTreeNode): add test for class field --- .../tree_tripper/nodes/binary_nodes/AVLTreeNode.kt | 1 + .../tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt index 0e8e42a..7e70f93 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt @@ -27,6 +27,7 @@ public class AVLTreeNode, V>( /** The height of the node in the AVL tree, initialized to 1. */ public var height: Int = 1 + private set /** Updates height of the node in AVL tree based on the heights of its left and right child subtrees. */ public fun updateHeight() { diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt index d5e4725..8cb5a98 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt @@ -1,6 +1,7 @@ package tree_tripper.nodes.binary_nodes import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -8,6 +9,12 @@ import org.junit.jupiter.params.provider.MethodSource class AVLTreeNodeTest { + @Test + public fun nodeInitializing() { + val node = AVLTreeNode(0, 0) + Assertions.assertEquals(1, node.height) {"The height is not 1 by standard initialize."} + } + @ParameterizedTest @MethodSource("testUpdateHeight") public fun testUpdateHeight(expected: Int, node: AVLTreeNode) { @@ -16,8 +23,10 @@ class AVLTreeNodeTest { } companion object { + @JvmStatic fun testUpdateHeight(): List = listOf( + Arguments.of(1, AVLTreeNode(0, 0, 0, null, null) ), @@ -63,5 +72,6 @@ class AVLTreeNodeTest { ) ) + } } From a02f815bde2cb8f0f35a8f898ca4bc741b2e2b42 Mon Sep 17 00:00:00 2001 From: Vladimir Zaikin Date: Sun, 31 Mar 2024 14:21:20 +0300 Subject: [PATCH 054/122] test(BSTree): BSTreeTest and BSTreeTestAssistant classes are created. BSTree class is opened for BSTreeTestAssistant inheritance. Add tests for the check tree initialization, node creation and insert methods --- .../tree_tripper/binary_trees/BSTree.kt | 2 +- .../tree_tripper/binary_trees/BSTreeTest.kt | 90 +++++++++++++++++++ .../assistants/BSTreeTestAssistant.kt | 50 +++++++++++ 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt index 773e4a3..5f790c6 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt @@ -9,7 +9,7 @@ import tree_tripper.nodes.binary_nodes.BSTreeNode * @param K the key type in the tree, supporting the [Comparable] interface * @param V the value type in the tree */ -public class BSTree, V>: AbstractBSTree>() { +public open class BSTree, V>: AbstractBSTree>() { override fun createNode(key: K, value: V): BSTreeNode { return BSTreeNode(key, value) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt new file mode 100644 index 0000000..48256d5 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -0,0 +1,90 @@ +package tree_tripper.binary_trees + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.assertTimeout +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import tree_tripper.binary_trees.assistants.BSTreeTestAssistant +import java.time.Duration +import kotlin.random.Random +import kotlin.test.Test + + +class BSTreeTest { + private lateinit var tree: BSTreeTestAssistant + + @BeforeEach + fun setup() { + tree = BSTreeTestAssistant() + } + + @Test + public fun `tree initialization`() { + tree.assertRootInitialization() + assertEquals(tree.getSize(), 0, "Incorrect a tree initialization") + } + + @Test + public fun `create node`() { + tree.assertWasCreatedNode(5, 10) + } + + @Test + public fun `insert root`() { + tree.insert(0, 0) + tree.assertIsBSTree() + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + } + + @Test + public fun `insert children root`() { + tree.insert(0, 0) + tree.insert(-1, -1) + tree.insert(1, 1) + tree.assertIsBSTree() + assertEquals(tree.getSize(), 3, "Incorrect resizing tree size") + } + + @Test + public fun `insert 5 elements`() { + tree.insert(0, 0) + tree.insert(2, 2) + tree.insert(-1, -1) + tree.insert(1, 1) + tree.insert(3, 3) + tree.assertIsBSTree() + assertEquals(tree.getSize(), 5, "Incorrect resizing tree size") + } + + @ParameterizedTest + @MethodSource("sizeAndTime") + public fun `insert with size and time`(size: Int, seconds: Long) { + assertTimeout(Duration.ofSeconds(seconds)) { + repeat(size) { + val keyRandom = Random.nextInt(-1000, 1000) + tree.insert(keyRandom, keyRandom) + } + } + + tree.assertIsBSTree() + } + + @Test + public fun `insert if absent root`() { + assertEquals(tree.insertIfAbsent(0, 0), true) + tree.assertIsBSTree() + assertEquals(tree.insertIfAbsent(0, 10), false) + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + } + + companion object { + @JvmStatic + fun sizeAndTime() = listOf( + Arguments.of(100, 1L), + Arguments.of(10000, 1L) + ) + } + +} diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt new file mode 100644 index 0000000..2a6d9a5 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt @@ -0,0 +1,50 @@ +package tree_tripper.binary_trees.assistants + +import org.junit.jupiter.api.Assertions.assertEquals +import tree_tripper.binary_trees.BSTree +import tree_tripper.nodes.binary_nodes.BSTreeNode +import java.util.* + + +class BSTreeTestAssistant, V>: BSTree() { + + public fun assertRootInitialization() { + assertEquals(root, null, "Incorrect a root initialization") + } + + public fun assertWasCreatedNode(key: K, value: V) { + val node = createNode(key, value) + assertEquals(node.key, key, "Incorrect a key assignment") + assertEquals(node.value, value, "Incorrect a value assignment") + assertEquals(node.leftChild, null, "Incorrect a left child assignment") + assertEquals(node.rightChild, null, "Incorrect a right child assignment") + } + + public fun assertWasUpdatedRoot(node: BSTreeNode) { + updateRoot(node) + assertEquals(root, node, "Incorrect a root update") + } + + public fun assertWasBalancedTree(node: BSTreeNode) { + assertEquals(balanceTree(node), node, "Incorrect a tree balance") + } + + public fun assertIsBSTree() { + val queue: Queue> = LinkedList(listOfNotNull(root)) + + while (queue.isNotEmpty()) { + val node = queue.remove() + val nodeLeft = node.leftChild + if (nodeLeft != null) + assert(nodeLeft.key < node.key) + { "Incorrect the binary search tree structure: a left child key is no less than the parent key" } + val nodeRight = node.rightChild + if (nodeRight != null) + assert(nodeRight.key > node.key) + { "Incorrect the binary search tree structure: a left child key is no more than the parent key" } + + queue.addAll(node.getChildren()) + } + } + +} From 86dca1e629bb4d470e6c4c9d2de9e8ec898c74c1 Mon Sep 17 00:00:00 2001 From: Vladimir Zaikin Date: Sun, 31 Mar 2024 15:22:55 +0300 Subject: [PATCH 055/122] test: add autotests that build the project and run tests with 'jacoco'. Add 'jacoco' in build.gradle.kts. Add '@DisplayName' for correct output of the test name to the console --- .github/workflows/build_and_test.yml | 19 ++++++++++ lib/build.gradle.kts | 36 +++++++++++++++---- .../tree_tripper/binary_trees/BSTreeTest.kt | 22 ++++++++---- 3 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/build_and_test.yml diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml new file mode 100644 index 0000000..a982c9f --- /dev/null +++ b/.github/workflows/build_and_test.yml @@ -0,0 +1,19 @@ +name: Build and test + +on: + workflow_dispatch: + + push: + branches: [ "dev", "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Build source code and run test with jacoco + run: ./gradlew :test diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 94ec347..02cd19e 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -12,6 +12,11 @@ plugins { // Apply the java-library plugin for API and implementation separation. `java-library` + + // Code coverage plugin + jacoco + + id("org.barfuin.gradle.jacocolog") version "3.1.0" } repositories { @@ -24,24 +29,43 @@ dependencies { api(libs.commons.math3) // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2") - // 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(19) + } +} + testing { suites { // Configure the built-in test suite val test by getting(JvmTestSuite::class) { - // Use Kotlin Test test framework + // Use Kotlin Test framework useKotlinTest("1.9.20") } } } -// Apply a specific Java toolchain to ease working on different environments. -java { - toolchain { - languageVersion = JavaLanguageVersion.of(19) +tasks.named("test") { + useJUnitPlatform() + finalizedBy(tasks.jacocoTestReport) +} + +tasks.withType { + testLogging { + events("PASSED", "SKIPPED", "FAILED") + } +} + +tasks.named("jacocoTestReport") { + dependsOn(tasks.test) + reports { + csv.required = false + xml.required = false + html.outputLocation = layout.buildDirectory.dir("jacocoHtml") } } diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index 48256d5..3e7aaec 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -2,6 +2,7 @@ package tree_tripper.binary_trees import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertTimeout import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -21,25 +22,29 @@ class BSTreeTest { } @Test - public fun `tree initialization`() { + @DisplayName("tree initialization") + public fun treeInitialization() { tree.assertRootInitialization() assertEquals(tree.getSize(), 0, "Incorrect a tree initialization") } @Test - public fun `create node`() { + @DisplayName("create node") + public fun createNode() { tree.assertWasCreatedNode(5, 10) } @Test - public fun `insert root`() { + @DisplayName("insert root") + public fun insertRoot() { tree.insert(0, 0) tree.assertIsBSTree() assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") } @Test - public fun `insert children root`() { + @DisplayName("insert children root") + public fun insertChildrenRoot() { tree.insert(0, 0) tree.insert(-1, -1) tree.insert(1, 1) @@ -48,7 +53,8 @@ class BSTreeTest { } @Test - public fun `insert 5 elements`() { + @DisplayName("insert 5 elements") + public fun insertElements() { tree.insert(0, 0) tree.insert(2, 2) tree.insert(-1, -1) @@ -60,7 +66,8 @@ class BSTreeTest { @ParameterizedTest @MethodSource("sizeAndTime") - public fun `insert with size and time`(size: Int, seconds: Long) { + @DisplayName("insert with size and time") + public fun insertWithSizeAndTime(size: Int, seconds: Long) { assertTimeout(Duration.ofSeconds(seconds)) { repeat(size) { val keyRandom = Random.nextInt(-1000, 1000) @@ -72,7 +79,8 @@ class BSTreeTest { } @Test - public fun `insert if absent root`() { + @DisplayName("insert if absent root") + public fun insertIfAbsentRoot() { assertEquals(tree.insertIfAbsent(0, 0), true) tree.assertIsBSTree() assertEquals(tree.insertIfAbsent(0, 10), false) From 7064395df0c447e088cb6d702eed4827888df9c2 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Sun, 31 Mar 2024 18:00:05 +0300 Subject: [PATCH 056/122] docs: update ReadMe, delete ReadMe from lib/ --- ReadMe.md | 244 ++++++++++++++++++++++++++++++++++++++++++++++++-- lib/ReadMe.md | 135 ---------------------------- 2 files changed, 238 insertions(+), 141 deletions(-) delete mode 100644 lib/ReadMe.md diff --git a/ReadMe.md b/ReadMe.md index 563d568..56401d9 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -3,18 +3,250 @@ [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/) +

TreeTripper

## Description -The project `TreeTripper` contains an implementation of a library for working with search trees -## Roadmap +Library `TreeTripper`: provides implementations of the following binary search tree data structures: +- [x] [`Binary Search Tree`](src/main/kotlin/tree_tripper/binary_trees/BSTree.kt), see more [information](https://en.wikipedia.org/wiki/Binary_search_tree) +- [x] [`AVL tree`](src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt), see more [information](https://en.wikipedia.org/wiki/AVL_tree) +- [x] [`Red-black tree`](src/main/kotlin/tree_tripper/binary_trees/RBTree.kt), + see more [information](https://en.wikipedia.org/wiki/Left-leaning_red%E2%80%93black_tree) -- [x] Add library -- [x] Add documentation of library -- [ ] Add tests to library +> [!IMPORTANT] +> The red-black tree is implemented based on the algorithm +> of left-linear red-black trees described by Robert Sedgewick +> on his [website](https://sedgewick.io/) and [presentation](https://sedgewick.io/wp-content/uploads/2022/03/2008-09LLRB.pdf) about it -

(back to top)

+The library supports the extension both internally (future library updates) and externally (implemented by the user). + +## Getting started +To run building library execute command: +```bash +./gradlew build +``` + +## Using library + +### Basic operations ++ `insert`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L17) ++ `search`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L50) ++ `remove`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L36) + +### Examples + +##### Example 1 (importing) +```kotlin +import tree_tripper.binary_trees.BSTree +import tree_tripper.binary_trees.AVLTree +import tree_tripper.binary_trees.RBTree + + +val simpleTree = BSTree() // initialization of empty simple binary search tree +val avlTree = AVLTree() // initialization of empty AVL tree +val rbTree = RBTree>() // initialization of empty Red-Black tree +``` + +##### Example 2 (inserting) +Code: +```kotlin +import tree_tripper.binary_trees.BSTree + +fun main() { + val tree = BSTree() + + tree.insert(key = 1, value = 1) + tree.insert(key = 2, value = 2) + tree.insert(key = 3, value = 3) + tree.insert(key = 4, value = 4) + tree.insert(key = 5, value = 5) + + println(tree) +} +``` +Output: +```text +BSTree(1: 1, 2: 2, 3: 3, 4: 4, 5: 5, ) +``` + +##### Example 3 (searching) +Code: +```kotlin +import tree_tripper.binary_trees.BSTree + +fun main() { + val tree = BSTree() + /* + ... + inserting from `example 2` + ... + */ + + /* Existing element in tree */ + println(tree.search(key = 1)) + println(tree.search(key = 3)) + println(tree.search(key = 5)) + + /* Unexciting element in tree */ + println(tree.search(key = -2)) + println(tree.search(key = 7)) + + /* Alternative search method */ + println(tree[2]) + println(tree[0]) +} +``` +Output: +```text +1 +3 +5 +null +null +2 +null +``` + +##### Example 3 (removing) +Code: +```kotlin +import tree_tripper.binary_trees.BSTree + +fun main() { + val tree = BSTree() + /* + ... + inserting from `example 2` + ... + */ + + /* Existing element in tree */ + println(tree.remove(key = 1)) + println(tree.remove(key = 3)) + println(tree.remove(key = 5)) + + /* Unexciting element in tree */ + println(tree.remove(key = -2)) + println(tree.removeWithDefault(key = 7, "Element not found")) + + println(tree) +} +``` +Output: +```text +1 +3 +5 +null +Element not found +BSTree(2: 2, 4: 4, ) +``` + +##### Example 4 (tree-like printing) +Code: +```kotlin +import tree_tripper.binary_trees.BSTree +import tree_tripper.binary_trees.AVLTree +import tree_tripper.binary_trees.RBTree + +fun main() { + val simpleTree = BSTree() + val avlTree = AVLTree() + val rbTree = RBTree() + /* + ... + inserting similar to `example 2` for each tree + ... + */ + + println("${simpleTree.toStringWithTreeView()}\n") + println("${avlTree.toStringWithTreeView()}\n") + println("${rbTree.toStringWithTreeView()}\n") +} +``` +Output: +```text +BSTree( + (5, 5) + (4, 4) + (3, 3) + (2, 2) +(1, 1) +) + +AVLTree( + (5, 5) + (4, 4) + (3, 3) +(2, 2) + (1, 1) +) + +RBTree( + (5, 5) - BLACK +(4, 4) - BLACK + (3, 3) - BLACK + (2, 2) - RED + (1, 1) - BLACK +) +``` + +##### Example 5 (iterators) +Code: +```kotlin +import tree_tripper.binary_trees.AVLTree + +fun main() { + val tree = AVLTree() + /* + ... + inserting from `example 2` + ... + */ + + println("WIDTH ORDER:") + tree.forEach(IterationOrders.WIDTH_ORDER) { + println(it) + } + println() + println("INCREASING ORDER:") + tree.forEach(IterationOrders.INCREASING_ORDER) { + println(it) + } + println() + println("DECREASING ORDER:") + tree.forEach(IterationOrders.DECREASING_ORDER) { + println(it) + } +} +``` +Output: +```text +WIDTH ORDER: +(2, 2) +(1, 1) +(4, 4) +(3, 3) +(5, 5) + +INCREASING ORDER: +(1, 1) +(2, 2) +(3, 3) +(4, 4) +(5, 5) + +DECREASING ORDER: +(5, 5) +(4, 4) +(3, 3) +(2, 2) +(1, 1) +``` + +## Documentation +See more [_**documentation**_](src/main/kotlin/tree_tripper/SearchTree.kt) of library `TreeTripper` to learn more about it. ## Authors diff --git a/lib/ReadMe.md b/lib/ReadMe.md deleted file mode 100644 index 289c334..0000000 --- a/lib/ReadMe.md +++ /dev/null @@ -1,135 +0,0 @@ -# Kotlin Library `TreeTripper` - -## Description - -Library `TreeTripper` - it is providing implementations of binary search trees data structures: -- [x] [`Binary Search Tree`](src/main/kotlin/tree_tripper/binary_trees/BSTree.kt), see more [information](https://en.wikipedia.org/wiki/Binary_search_tree) -- [x] [`AVL tree`](src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt), see more [information](https://en.wikipedia.org/wiki/AVL_tree) -- [x] [`Red-black tree`](src/main/kotlin/tree_tripper/binary_trees/RBTree.kt), - see more [information](https://en.wikipedia.org/wiki/Left-leaning_red%E2%80%93black_tree) - -> [!IMPORTANT] -> The red-black tree is implemented based on the algorithm -> of left-linear red-black trees described by Robert Sedgewick -> on his [website](https://sedgewick.io/) and [presentation](https://sedgewick.io/wp-content/uploads/2022/03/2008-09LLRB.pdf) about it - -The library supports the extension both internally (future library updates) and externally (implemented by the user). - -## Getting started -To run building library execute command: -```bash -./gradlew build -``` - -## Using library - -### Basic operations -+ `insert`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L17) -+ `search`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L50) -+ `remove`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L36) - -### Examples - -##### Example 1 (importing) -```kotlin -import tree_tripper.binary_trees.BSTree -import tree_tripper.binary_trees.AVLTree -import tree_tripper.binary_trees.RBTree - - -val simpleTree = BSTree() // initialization of empty simple binary search tree -val avlTree = AVLTree() // initialization of empty AVL tree -val rbTree = RBTree>() // initialization of empty Red-Black tree -``` - -##### Example 2 (inserting) -Code: -```kotlin -import tree_tripper.binary_trees.BSTree - -fun main() { - val tree = BSTree() - - tree.insert(key = 1, value = 1) - tree.insert(key = 2, value = 2) - tree.insert(key = 3, value = 3) - tree.insert(key = 4, value = 4) - tree.insert(key = 5, value = 5) - - println(tree) -} -``` -Output: -```text -BSTree(1: 1, 2: 2, 3: 3, 4: 4, 5: 5, ) -``` - -##### Example 2 (searching) -Code: -```kotlin -import tree_tripper.binary_trees.BSTree - -fun main() { - val tree = BSTree() - /* - ... - inserting from `example 1` - ... - */ - - /* Existing element in tree */ - println(tree.search(key = 1)) - println(tree.search(key = 3)) - println(tree.search(key = 5)) - - /* Unexciting element in tree */ - println(tree.search(key = -2)) - println(tree.search(key = 7)) -} -``` -Output: -```text -1 -3 -5 -null -null -``` - -##### Example 3 (removing) -Code: -```kotlin -import tree_tripper.binary_trees.BSTree - -fun main() { - val tree = BSTree() - /* - ... - inserting from `example 1` - ... - */ - - /* Existing element in tree */ - println(tree.remove(key = 1)) - println(tree.remove(key = 3)) - println(tree.remove(key = 5)) - - /* Unexciting element in tree */ - println(tree.remove(key = -2)) - println(tree.remove(key = 7)) - - println(tree) -} -``` -Output: -```text -1 -3 -5 -null -null -BSTree(2: 2, 4: 4, ) -``` - -## Documentation -See more [_**documentation**_](src/main/kotlin/tree_tripper/SearchTree.kt) of library `TreeTripper` to learn more about it. From fbee719825e9c3405471fdc3506063f8478477bd Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Sun, 31 Mar 2024 18:16:08 +0300 Subject: [PATCH 057/122] docs: update links in ReadMe --- ReadMe.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 56401d9..31a3279 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -9,9 +9,9 @@ ## Description Library `TreeTripper`: provides implementations of the following binary search tree data structures: -- [x] [`Binary Search Tree`](src/main/kotlin/tree_tripper/binary_trees/BSTree.kt), see more [information](https://en.wikipedia.org/wiki/Binary_search_tree) -- [x] [`AVL tree`](src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt), see more [information](https://en.wikipedia.org/wiki/AVL_tree) -- [x] [`Red-black tree`](src/main/kotlin/tree_tripper/binary_trees/RBTree.kt), +- [x] [`Binary Search Tree`](lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt), see more [information](https://en.wikipedia.org/wiki/Binary_search_tree) +- [x] [`AVL tree`](lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt), see more [information](https://en.wikipedia.org/wiki/AVL_tree) +- [x] [`Red-black tree`](lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt), see more [information](https://en.wikipedia.org/wiki/Left-leaning_red%E2%80%93black_tree) > [!IMPORTANT] @@ -30,9 +30,9 @@ To run building library execute command: ## Using library ### Basic operations -+ `insert`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L17) -+ `search`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L50) -+ `remove`, see [docs](src/main/kotlin/tree_tripper/SearchTree.kt#L36) ++ `insert`, see [docs](lib/src/main/kotlin/tree_tripper/SearchTree.kt#L17) ++ `search`, see [docs](lib/src/main/kotlin/tree_tripper/SearchTree.kt#L50) ++ `remove`, see [docs](lib/src/main/kotlin/tree_tripper/SearchTree.kt#L36) ### Examples @@ -246,7 +246,7 @@ DECREASING ORDER: ``` ## Documentation -See more [_**documentation**_](src/main/kotlin/tree_tripper/SearchTree.kt) of library `TreeTripper` to learn more about it. +See more [_**documentation**_](lib/src/main/kotlin/tree_tripper/SearchTree.kt) of library `TreeTripper` to learn more about it. ## Authors From de325f149fa597d21c1fffefb49d2bd860c8a3b0 Mon Sep 17 00:00:00 2001 From: Vladimir Zaikin Date: Sun, 31 Mar 2024 19:19:58 +0300 Subject: [PATCH 058/122] refactor: rename several methods according to their invariant. Renumber examples in ReadMe --- ReadMe.md | 6 ++-- .../main/kotlin/tree_tripper/SearchTree.kt | 10 +++---- .../binary_trees/AbstractBSTree.kt | 28 +++++++++---------- .../tree_tripper/binary_trees/RBTree.kt | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 31a3279..41aac85 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -108,7 +108,7 @@ null null ``` -##### Example 3 (removing) +##### Example 4 (removing) Code: ```kotlin import tree_tripper.binary_trees.BSTree @@ -143,7 +143,7 @@ Element not found BSTree(2: 2, 4: 4, ) ``` -##### Example 4 (tree-like printing) +##### Example 5 (tree-like printing) Code: ```kotlin import tree_tripper.binary_trees.BSTree @@ -192,7 +192,7 @@ RBTree( ) ``` -##### Example 5 (iterators) +##### Example 6 (iterators) Code: ```kotlin import tree_tripper.binary_trees.AVLTree diff --git a/lib/src/main/kotlin/tree_tripper/SearchTree.kt b/lib/src/main/kotlin/tree_tripper/SearchTree.kt index d0e029b..29a3e28 100644 --- a/lib/src/main/kotlin/tree_tripper/SearchTree.kt +++ b/lib/src/main/kotlin/tree_tripper/SearchTree.kt @@ -40,7 +40,7 @@ public interface SearchTree, V>: Iterable> { * @return the removed value associated with a given [key], or * the [defaultValue] if such a [key] does not present in a tree. */ - public fun removeWithDefault(key: K, defaultValue: V): V + public fun removeOrDefault(key: K, defaultValue: V): V /** * Searches the value associated with a given [key]. @@ -73,10 +73,10 @@ public interface SearchTree, V>: Iterable> { public fun getMax(): Pair? /** - * Returns the key/value pair with the max key after a given [key], or + * Returns the key/value pair with the max key in subtree with a given [key] in root, or * null if such a [key] does not present in a tree. */ - public fun getMaxDescendant(key: K): Pair? + public fun getMaxInSubtree(key: K): Pair? /** * Returns the key/value pair with the min key, or null if a tree is empty. @@ -84,10 +84,10 @@ public interface SearchTree, V>: Iterable> { public fun getMin(): Pair? /** - * Returns the key/value pair with the min key after a given [key], or + * Returns the key/value pair with the min key in subtree with a given [key] in root, or * null if such a [key] does not present in a tree. */ - public fun getMinDescendant(key: K): Pair? + public fun getMinInSubtree(key: K): Pair? /** * Returns the size of a tree. diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt index cd6eb70..efa095e 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -38,7 +38,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return resultRemove.second } - override fun removeWithDefault(key: K, defaultValue: V): V { + override fun removeOrDefault(key: K, defaultValue: V): V { return remove(key) ?: defaultValue } @@ -58,22 +58,22 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return search(key) } - override fun getMaxDescendant(key: K): Pair? { - val resultSearch = getMaxDescendantNode(searchNode(key)) ?: return null + override fun getMaxInSubtree(key: K): Pair? { + val resultSearch = getMaxNodeInSubtree(searchNode(key)) ?: return null return Pair(resultSearch.key, resultSearch.value) } override fun getMax(): Pair? { - return notNullNodeAction(root, null) {node -> getMaxDescendant(node.key)} + return notNullNodeAction(root, null) {node -> getMaxInSubtree(node.key)} } - override fun getMinDescendant(key: K): Pair? { - val resultSearch = getMinDescendantNode(searchNode(key)) ?: return null + override fun getMinInSubtree(key: K): Pair? { + val resultSearch = getMinNodeInSubtree(searchNode(key)) ?: return null return Pair(resultSearch.key, resultSearch.value) } override fun getMin(): Pair? { - return notNullNodeAction(root, null) {node -> getMinDescendant(node.key)} + return notNullNodeAction(root, null) {node -> getMinInSubtree(node.key)} } override fun getSize(): Int { @@ -189,7 +189,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< nodeSubstitutive = node.leftChild ?: node.rightChild return Pair(nodeSubstitutive, node.value) } else { - nodeSubstitutive = getMaxDescendantNode(node.leftChild) as N + nodeSubstitutive = getMaxNodeInSubtree(node.leftChild) as N node.leftChild = removeNode(node.leftChild, nodeSubstitutive.key).first nodeSubstitutive.rightChild = node.rightChild nodeSubstitutive.leftChild = node.leftChild @@ -220,10 +220,10 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< } /** - * Returns the [N] node with the max key after a given [node], or null - * if such a [node] is not contained in a tree or all children of a given [node] are null. + * Returns the [N] node with the max key in subtree with a given [node] in root, or null + * if such a [node] is not contained in a tree. */ - protected fun getMaxDescendantNode(node: N?): N? { + protected fun getMaxNodeInSubtree(node: N?): N? { if (node == null) return null var nodeCurrent: N = node @@ -233,10 +233,10 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< } /** - * Returns the [N] node with the min key after a given [node], or null - * if such a [node] is not contained in a tree or all children of a given [node] are null. + * Returns the [N] node with the min key in subtree with a given [node] in root, or null + * if such a [node] is not contained in a tree. */ - protected fun getMinDescendantNode(node: N?): N? { + protected fun getMinNodeInSubtree(node: N?): N? { if (node == null) return null var nodeCurrent: N = node diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index 2bc8ddf..acfe53e 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -56,7 +56,7 @@ public open class RBTree, V>: AbstractBSTree = createNode(nodeWithMinimalKey.key, nodeWithMinimalKey.value) nodeSubstitutive.isRed = nodeCurrent.isRed nodeSubstitutive.leftChild = nodeCurrent.leftChild From 8e50a01deedebb42fa1d253221fbdaf3bacec788 Mon Sep 17 00:00:00 2001 From: Vladimir Zaikin Date: Sun, 31 Mar 2024 19:20:22 +0300 Subject: [PATCH 059/122] test: add tests of basic methods for AbstractBSTree --- .../tree_tripper/binary_trees/BSTreeTest.kt | 212 ++++++++++++++++-- 1 file changed, 199 insertions(+), 13 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index 3e7aaec..bfb05af 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -13,11 +13,11 @@ import kotlin.random.Random import kotlin.test.Test -class BSTreeTest { +public class BSTreeTest { private lateinit var tree: BSTreeTestAssistant @BeforeEach - fun setup() { + public fun setup() { tree = BSTreeTestAssistant() } @@ -37,7 +37,10 @@ class BSTreeTest { @Test @DisplayName("insert root") public fun insertRoot() { - tree.insert(0, 0) + tree.insert(1, -1) + tree.assertIsBSTree() + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + tree.insert(1, 0) tree.assertIsBSTree() assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") } @@ -45,9 +48,9 @@ class BSTreeTest { @Test @DisplayName("insert children root") public fun insertChildrenRoot() { - tree.insert(0, 0) - tree.insert(-1, -1) - tree.insert(1, 1) + tree.insert(2, -2) + tree.insert(1, -1) + tree.insert(3, -3) tree.assertIsBSTree() assertEquals(tree.getSize(), 3, "Incorrect resizing tree size") } @@ -55,11 +58,11 @@ class BSTreeTest { @Test @DisplayName("insert 5 elements") public fun insertElements() { - tree.insert(0, 0) - tree.insert(2, 2) - tree.insert(-1, -1) - tree.insert(1, 1) - tree.insert(3, 3) + tree.insert(4, -4) + tree.insert(5, -5) + tree.insert(3, -3) + tree.insert(2, -2) + tree.insert(6, -6) tree.assertIsBSTree() assertEquals(tree.getSize(), 5, "Incorrect resizing tree size") } @@ -81,12 +84,195 @@ class BSTreeTest { @Test @DisplayName("insert if absent root") public fun insertIfAbsentRoot() { - assertEquals(tree.insertIfAbsent(0, 0), true) + assertEquals(tree.insertIfAbsent(1, -1), true) tree.assertIsBSTree() - assertEquals(tree.insertIfAbsent(0, 10), false) + assertEquals(tree.insertIfAbsent(1, 1), false) assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") } + @Test + @DisplayName("set with brackets") + public fun set() { + assertEquals(tree.search(1), null) + tree[1] = -1 + assertEquals(tree.search(1), -1) + } + + @Test + @DisplayName("search node") + public fun searchNode() { + tree.insert(1, -1) + assertEquals(tree.search(1), -1) + tree.assertIsBSTree() + assertEquals(tree.search(0), null) + tree.assertIsBSTree() + } + + @Test + @DisplayName("search children node") + public fun searchChildrenNode() { + tree.insert(2, -2) + tree.insert(1, -1) + tree.insert(3, -3) + assertEquals(tree.search(2), -2) + assertEquals(tree.search(1), -1) + assertEquals(tree.search(3), -3) + tree.assertIsBSTree() + } + + @ParameterizedTest + @MethodSource("sizeAndTime") + @DisplayName("search with size and time") + public fun searchWithSizeAndTime(size: Int, seconds: Long) { + val array = IntArray(size) + var index = 0 + repeat(size) { + val keyRandom = Random.nextInt(-1000, 1000) + array[index++] = keyRandom + tree.insert(keyRandom, keyRandom * (-1)) + } + + assertTimeout(Duration.ofSeconds(seconds)) { + repeat(10) { + val keyRandom = Random.nextInt(-1000, 1000) + if (keyRandom in array) + assertEquals(tree.search(keyRandom), (-1) * keyRandom) + else + assertEquals(tree.search(keyRandom), null) + } + } + + tree.assertIsBSTree() + } + + @Test + @DisplayName("search of default") + public fun searchOrDefault() { + assertEquals(tree.searchOrDefault(1, 0), 0) + tree.insert(1, -1) + assertEquals(tree.searchOrDefault(1, 0), -1) + } + + @Test + @DisplayName("contains") + public fun contains() { + assertEquals(tree.contains(1), false) + tree.insert(1, -1) + assertEquals(tree.contains(1), true) + } + + @Test + @DisplayName("get with brackets") + public fun get() { + assertEquals(tree[1], null) + tree[1] = -1 + assertEquals(tree[1], -1) + } + + @Test + @DisplayName("get maximum in subtree") + public fun getMaxInSubtree() { + assertEquals(tree.getMaxInSubtree(0), null) + tree.assertIsBSTree() + tree[2] = -2 + assertEquals(tree.getMaxInSubtree(2), Pair(2, -2)) + tree.assertIsBSTree() + tree[3] = -3 + tree[1] = -1 + assertEquals(tree.getMaxInSubtree(2), Pair(3, -3)) + tree.assertIsBSTree() + } + + @Test + @DisplayName("get maximum") + public fun getMax() { + assertEquals(tree.getMax(), null) + tree[2] = -2 + assertEquals(tree.getMax(), Pair(2, -2)) + tree[3] = -3 + tree[1] = -1 + assertEquals(tree.getMax(), Pair(3, -3)) + } + + @Test + @DisplayName("get minimum in subtree") + public fun getMinInSubtree() { + assertEquals(tree.getMinInSubtree(0), null) + tree.assertIsBSTree() + tree[2] = -2 + assertEquals(tree.getMinInSubtree(2), Pair(2, -2)) + tree.assertIsBSTree() + tree[1] = -1 + tree[3] = -3 + assertEquals(tree.getMinInSubtree(2), Pair(1, -1)) + tree.assertIsBSTree() + } + + @Test + @DisplayName("get minimum") + public fun getMin() { + assertEquals(tree.getMin(), null) + tree[2] = -2 + assertEquals(tree.getMin(), Pair(2, -2)) + tree[1] = -1 + tree[3] = -3 + assertEquals(tree.getMin(), Pair(1, -1)) + } + + @Test + @DisplayName("remove") + public fun remove() { + assertEquals(tree.remove(0), null) + tree.assertIsBSTree() + tree[2] = -2 + assertEquals(tree.remove(2), -2) + tree.assertIsBSTree() + tree[2] = -2 + tree[1] = -1 + assertEquals(tree.remove(2), -2) + tree.assertIsBSTree() + tree[2] = -2 + tree[3] = -3 + assertEquals(tree.remove(2), -2) + tree.assertIsBSTree() + assertEquals(tree.remove(3), -3) + tree.assertIsBSTree() + } + + @ParameterizedTest + @MethodSource("sizeAndTime") + @DisplayName("remove with size and time") + public fun removeWithSizeAndTime(size: Int, seconds: Long) { + val array = IntArray(size) + var index = 0 + repeat(size) { + val keyRandom = Random.nextInt(-1000, 1000) + array[index++] = keyRandom + tree[keyRandom] = (-1) * keyRandom + } + + assertTimeout(Duration.ofSeconds(seconds)) { + repeat(10) { + val keyRandom = Random.nextInt(-1000, 1000) + if (keyRandom in array) + assertEquals(tree.remove(keyRandom), (-1) * keyRandom) + else + assertEquals(tree.remove(keyRandom), null) + } + } + + tree.assertIsBSTree() + } + + @Test + @DisplayName("remove or default") + public fun removeWithDefault() { + assertEquals(tree.removeOrDefault(1, 0), 0) + tree.insert(1, -1) + assertEquals(tree.removeOrDefault(1, 0), -1) + } + + companion object { @JvmStatic fun sizeAndTime() = listOf( From 42a4484b4d24afa64cdad7537b27164767c2d080 Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Sun, 31 Mar 2024 20:05:25 +0300 Subject: [PATCH 060/122] docs: fix the typo in 'removeOrDefault' method --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 41aac85..e01a32a 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -128,7 +128,7 @@ fun main() { /* Unexciting element in tree */ println(tree.remove(key = -2)) - println(tree.removeWithDefault(key = 7, "Element not found")) + println(tree.removeOrDefault(key = 7, "Element not found")) println(tree) } From 8f69a4b251c023b4e5690a0cb872f4c6a17ccf24 Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Sun, 31 Mar 2024 20:42:10 +0300 Subject: [PATCH 061/122] test(BSTree): add tests for 'updateRoot' and 'balanceTree' methods --- .../tree_tripper/binary_trees/BSTreeTest.kt | 17 ++++++++++++++--- .../assistants/BSTreeTestAssistant.kt | 6 ++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index bfb05af..23694db 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -31,7 +31,19 @@ public class BSTreeTest { @Test @DisplayName("create node") public fun createNode() { - tree.assertWasCreatedNode(5, 10) + tree.assertWasCreatedNode(1, -1) + } + + @Test + @DisplayName("update root") + public fun updateRoot() { + tree.assertWasUpdatedRoot(1, -1) + } + + @Test + @DisplayName("balance tree") + public fun balanceTree() { + tree.assertWasBalancedTree(1, -1) } @Test @@ -266,13 +278,12 @@ public class BSTreeTest { @Test @DisplayName("remove or default") - public fun removeWithDefault() { + public fun removeOrDefault() { assertEquals(tree.removeOrDefault(1, 0), 0) tree.insert(1, -1) assertEquals(tree.removeOrDefault(1, 0), -1) } - companion object { @JvmStatic fun sizeAndTime() = listOf( diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt index 2a6d9a5..2eb4b9d 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt @@ -20,12 +20,14 @@ class BSTreeTestAssistant, V>: BSTree() { assertEquals(node.rightChild, null, "Incorrect a right child assignment") } - public fun assertWasUpdatedRoot(node: BSTreeNode) { + public fun assertWasUpdatedRoot(key: K, value: V) { + val node = createNode(key, value) updateRoot(node) assertEquals(root, node, "Incorrect a root update") } - public fun assertWasBalancedTree(node: BSTreeNode) { + public fun assertWasBalancedTree(key: K, value: V) { + val node = createNode(key, value) assertEquals(balanceTree(node), node, "Incorrect a tree balance") } From 7975b787a681f7cd0a7b8aba0210fe994cb7cf0b Mon Sep 17 00:00:00 2001 From: Vladimir Zaikin <144177450+Friend-zva@users.noreply.github.com> Date: Sun, 31 Mar 2024 23:58:20 +0300 Subject: [PATCH 062/122] Merge test and main (#2) * Adding simple gitignore file template * Adding gradle project files * Adding search tree library file structure at project * Update nodes classes and interfaces with adding properties and update its methods * Update search-tree interface and simple init by abstract binary search tree properties as root and tree size * Update abstract node class to calling update data method for balanced tree's nodes * Adding simple ReadMe file with description about library by path ./lib/ReadMe.md * Adding LICENSE file with MIT-License description * Update LICENSE file with MIT-License description by fix names of the rights holders * feat(AVLTree): implement balance functions * feat(AVLTree): implement insert * feat(AVLTree): implement remove and removeWithDefault * refactor(AVLTreeNode): simplify updatingHeight and remove updateNodeData * feat: add general methods and rename file RedBlackTree * feat(Refactoring): The implementation of the removeWithDefault(K) method is placed in an abstract class. Clearing some todos from labels. Corrected the AVLTree.kt file by adding the missing maxDescendantForAVL(AVLTreeNode) AVLTree method to the class. Added TODO marks in the BSTree file defining the implementation of the insert & remove methods * feat(Addding iterators): Add subdirrectory with files of intreface SearchTreeIterator and enum of orders to iterate on search trees and class of BinarySearchTreeIterator to iterate on BST and other implementations of it * feat(Adding iterators) * feat(Adding insert operation at RBTree) Add implementation of insert method of SearchTree at class RBTree and add new method at SearchTree interface with name insertIfAbsent * refactor: rewrite combine remove and insert for AVLTree and BSTree (not build) BREAKING CHANGE: implement methods for an AbstractBSTree, refactor AbstractBSTree, BSTree, SearchTree, SearchTreeNode, AbstractBSTreeNode, BSTreeNode. Add the pattern of ignoring to .gitignore * refactor(AVLTree & AVLTreeNode): combine code with AbstractBSTree. Add enter to .gitignore * refactor: update RBTree & RBTreeNode by combining the insert method with AbstractBSTree. Add implementation of the remove method at RBTree. Remove unnecessary interface of SearchTreeIterator. Update BinarySearchTreeIterator by implementing Iterator interface. Update ReadMe by adding info about finished trees * feat: Update lib/ReadMe.md by adding description of library and some examples of using it * feat: add simple root ReadMe for project with simple info upbout it * fix: invalid header which do not display information about the project on the github of root readme by removing it * feat: Adding doc-string for some source files. Docs integrated on IterationOrders.kt, BinarySearchTreeIterator.kt, RBTree.kt, RBTreeNode.kt and Utils.kt * docs(AVLTree & AVLTreeNode): add doc-strings * style: rename one method to 'contains' and rename the library to 'TreeTripper' * feat: add 'get' and 'set' methods to interact with trees as with a map * docs: add doc-strings for the classes and interfaces: SearchTree, SearchTreeNode, AbstractBSTree, AbstractBSTreeNode, BSTree, BSTreeNode * refactor: add more information to the ReadMe file withsome links to docs and more info about implementation about red-black tree * refactor: update info at root ReadMe file by updating roadmap and authors informations * test: add simple tests case for RBTree such as initializing tree, checking methods of node colors, update access params of method and properties from private to protected, add method to comparing by equals for nodes * fix: remove invalid assertion method for RBTreeTestAssistant which used for check results of left rotation for nodes * refactor: replace equals nodes methods to assertions utils file(AssertionUtils.kt) * test: add tests for utility methods to work with nodes * test: add tests for red-black tree node to check valid initialize and toString methods * refactor: update method source to template view: 'test%(test-name)Cases' * refactor: update test methods name to template view: 'test%(test-cause)' at RBTreeTest.kt file * test: add tests for private left and right node rotations methods * test: add test for private method to flip color of node and its childs colors * test: add tests for private methods of creation new tree node, updating root and add empty test for balance tree method * test(AVLTreeNode): add constructor for node, add tests for updateHeight * test(AVLTree): update access params of method from private to protected, add tests for balanceFactor, rotateLeft, rotateRight * test: update test for balance tree method by adding cases to test it * refactor: remove redundant information at file RBTreeTest.kt during object initialization, and remove unnecessary tree creation in the initialization test, remove commented-out code in file RBTreeNodeTest.kt * test(AVLTree): add tests for balance, add test for balanceFactor, changed how checkBalanceFactor works, refactor tests * test: add tests for insert methods such as insert 1 element, insert double unequals values by 1 key, insert sorted elements insert reversed of sorted elements and unsorted elements * refactor: update initialize test by adding assertion 'is tree with root equals null is RBTree' * refactor: remove output printing log information of insert tests * refactor: update private insert of test to test public tree method 'set' together with 'insert' * test(AVLTree): add tests for createNode, balanceTree * test(AVLTreeNode): add test for class field * test(BSTree): BSTreeTest and BSTreeTestAssistant classes are created. BSTree class is opened for BSTreeTestAssistant inheritance. Add tests for the check tree initialization, node creation and insert methods * test: add autotests that build the project and run tests with 'jacoco'. Add 'jacoco' in build.gradle.kts. Add '@DisplayName' for correct output of the test name to the console * docs: update ReadMe, delete ReadMe from lib/ * docs: update links in ReadMe * refactor: rename several methods according to their invariant. Renumber examples in ReadMe * test: add tests of basic methods for AbstractBSTree * docs: fix the typo in 'removeOrDefault' method * test(BSTree): add tests for 'updateRoot' and 'balanceTree' methods --------- Co-authored-by: IliaSuponeff Co-authored-by: Maxim Rodionov Co-authored-by: Friend-zva --- .gitattributes | 9 + .github/workflows/build_and_test.yml | 19 + .gitignore | 16 + LICENSE | 21 + ReadMe.md | 261 ++++++++++ gradle.properties | 6 + gradle/libs.versions.toml | 13 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 +++++++++ gradlew.bat | 92 ++++ lib/build.gradle.kts | 71 +++ .../main/kotlin/tree_tripper/SearchTree.kt | 112 ++++ .../tree_tripper/binary_trees/AVLTree.kt | 89 ++++ .../binary_trees/AbstractBSTree.kt | 248 +++++++++ .../tree_tripper/binary_trees/BSTree.kt | 18 + .../tree_tripper/binary_trees/RBTree.kt | 201 +++++++ .../iterators/BinarySearchTreeIterator.kt | 192 +++++++ .../tree_tripper/iterators/IterationOrders.kt | 15 + .../tree_tripper/nodes/SearchTreeNode.kt | 28 + .../main/kotlin/tree_tripper/nodes/Utils.kt | 20 + .../nodes/binary_nodes/AVLTreeNode.kt | 39 ++ .../nodes/binary_nodes/AbstractBSTreeNode.kt | 40 ++ .../nodes/binary_nodes/BSTreeNode.kt | 13 + .../nodes/binary_nodes/RBTreeNode.kt | 40 ++ lib/src/test/kotlin/AssertionUtils.kt | 37 ++ .../tree_tripper/binary_trees/AVLTreeTest.kt | 400 ++++++++++++++ .../tree_tripper/binary_trees/BSTreeTest.kt | 295 +++++++++++ .../tree_tripper/binary_trees/RBTreeTest.kt | 489 ++++++++++++++++++ .../assistants/AVLTreeTestAssistant.kt | 46 ++ .../assistants/BSTreeTestAssistant.kt | 52 ++ .../assistants/RBTreeTestAssistant.kt | 95 ++++ .../kotlin/tree_tripper/nodes/UtilsTest.kt | 40 ++ .../nodes/binary_nodes/AVLTreeNodeTest.kt | 77 +++ .../nodes/binary_nodes/RBTreeNodeTest.kt | 76 +++ settings.gradle.kts | 15 + 36 files changed, 3441 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/workflows/build_and_test.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 ReadMe.md create mode 100644 gradle.properties 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/main/kotlin/tree_tripper/SearchTree.kt create mode 100644 lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt create mode 100644 lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt create mode 100644 lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt create mode 100644 lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt create mode 100644 lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt create mode 100644 lib/src/main/kotlin/tree_tripper/iterators/IterationOrders.kt create mode 100644 lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt create mode 100644 lib/src/main/kotlin/tree_tripper/nodes/Utils.kt create mode 100644 lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt create mode 100644 lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt create mode 100644 lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt create mode 100644 lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt create mode 100644 lib/src/test/kotlin/AssertionUtils.kt create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt create mode 100644 lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt create mode 100644 lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt create mode 100644 lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt create mode 100644 lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.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/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml new file mode 100644 index 0000000..a982c9f --- /dev/null +++ b/.github/workflows/build_and_test.yml @@ -0,0 +1,19 @@ +name: Build and test + +on: + workflow_dispatch: + + push: + branches: [ "dev", "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Build source code and run test with jacoco + run: ./gradlew :test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..05ede4d --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +### Gradle console-app generated template +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +*/build/* + +### VSCode Template +.vscode/ + +### IntelliJ IDEA Template +.idea/ +*.iml + +### Apple MacOS folder attributes +*.DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5a5e946 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Ilia Suponev, Rodionov Maxim, Vladimir Zaikin + +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. diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..e01a32a --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,261 @@ +[//]: # (Project readme template from https://github.com/othneildrew/Best-README-Template/) + + +[![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/) + + +

TreeTripper

+ +## Description + +Library `TreeTripper`: provides implementations of the following binary search tree data structures: +- [x] [`Binary Search Tree`](lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt), see more [information](https://en.wikipedia.org/wiki/Binary_search_tree) +- [x] [`AVL tree`](lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt), see more [information](https://en.wikipedia.org/wiki/AVL_tree) +- [x] [`Red-black tree`](lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt), + see more [information](https://en.wikipedia.org/wiki/Left-leaning_red%E2%80%93black_tree) + +> [!IMPORTANT] +> The red-black tree is implemented based on the algorithm +> of left-linear red-black trees described by Robert Sedgewick +> on his [website](https://sedgewick.io/) and [presentation](https://sedgewick.io/wp-content/uploads/2022/03/2008-09LLRB.pdf) about it + +The library supports the extension both internally (future library updates) and externally (implemented by the user). + +## Getting started +To run building library execute command: +```bash +./gradlew build +``` + +## Using library + +### Basic operations ++ `insert`, see [docs](lib/src/main/kotlin/tree_tripper/SearchTree.kt#L17) ++ `search`, see [docs](lib/src/main/kotlin/tree_tripper/SearchTree.kt#L50) ++ `remove`, see [docs](lib/src/main/kotlin/tree_tripper/SearchTree.kt#L36) + +### Examples + +##### Example 1 (importing) +```kotlin +import tree_tripper.binary_trees.BSTree +import tree_tripper.binary_trees.AVLTree +import tree_tripper.binary_trees.RBTree + + +val simpleTree = BSTree() // initialization of empty simple binary search tree +val avlTree = AVLTree() // initialization of empty AVL tree +val rbTree = RBTree>() // initialization of empty Red-Black tree +``` + +##### Example 2 (inserting) +Code: +```kotlin +import tree_tripper.binary_trees.BSTree + +fun main() { + val tree = BSTree() + + tree.insert(key = 1, value = 1) + tree.insert(key = 2, value = 2) + tree.insert(key = 3, value = 3) + tree.insert(key = 4, value = 4) + tree.insert(key = 5, value = 5) + + println(tree) +} +``` +Output: +```text +BSTree(1: 1, 2: 2, 3: 3, 4: 4, 5: 5, ) +``` + +##### Example 3 (searching) +Code: +```kotlin +import tree_tripper.binary_trees.BSTree + +fun main() { + val tree = BSTree() + /* + ... + inserting from `example 2` + ... + */ + + /* Existing element in tree */ + println(tree.search(key = 1)) + println(tree.search(key = 3)) + println(tree.search(key = 5)) + + /* Unexciting element in tree */ + println(tree.search(key = -2)) + println(tree.search(key = 7)) + + /* Alternative search method */ + println(tree[2]) + println(tree[0]) +} +``` +Output: +```text +1 +3 +5 +null +null +2 +null +``` + +##### Example 4 (removing) +Code: +```kotlin +import tree_tripper.binary_trees.BSTree + +fun main() { + val tree = BSTree() + /* + ... + inserting from `example 2` + ... + */ + + /* Existing element in tree */ + println(tree.remove(key = 1)) + println(tree.remove(key = 3)) + println(tree.remove(key = 5)) + + /* Unexciting element in tree */ + println(tree.remove(key = -2)) + println(tree.removeOrDefault(key = 7, "Element not found")) + + println(tree) +} +``` +Output: +```text +1 +3 +5 +null +Element not found +BSTree(2: 2, 4: 4, ) +``` + +##### Example 5 (tree-like printing) +Code: +```kotlin +import tree_tripper.binary_trees.BSTree +import tree_tripper.binary_trees.AVLTree +import tree_tripper.binary_trees.RBTree + +fun main() { + val simpleTree = BSTree() + val avlTree = AVLTree() + val rbTree = RBTree() + /* + ... + inserting similar to `example 2` for each tree + ... + */ + + println("${simpleTree.toStringWithTreeView()}\n") + println("${avlTree.toStringWithTreeView()}\n") + println("${rbTree.toStringWithTreeView()}\n") +} +``` +Output: +```text +BSTree( + (5, 5) + (4, 4) + (3, 3) + (2, 2) +(1, 1) +) + +AVLTree( + (5, 5) + (4, 4) + (3, 3) +(2, 2) + (1, 1) +) + +RBTree( + (5, 5) - BLACK +(4, 4) - BLACK + (3, 3) - BLACK + (2, 2) - RED + (1, 1) - BLACK +) +``` + +##### Example 6 (iterators) +Code: +```kotlin +import tree_tripper.binary_trees.AVLTree + +fun main() { + val tree = AVLTree() + /* + ... + inserting from `example 2` + ... + */ + + println("WIDTH ORDER:") + tree.forEach(IterationOrders.WIDTH_ORDER) { + println(it) + } + println() + println("INCREASING ORDER:") + tree.forEach(IterationOrders.INCREASING_ORDER) { + println(it) + } + println() + println("DECREASING ORDER:") + tree.forEach(IterationOrders.DECREASING_ORDER) { + println(it) + } +} +``` +Output: +```text +WIDTH ORDER: +(2, 2) +(1, 1) +(4, 4) +(3, 3) +(5, 5) + +INCREASING ORDER: +(1, 1) +(2, 2) +(3, 3) +(4, 4) +(5, 5) + +DECREASING ORDER: +(5, 5) +(4, 4) +(3, 3) +(2, 2) +(1, 1) +``` + +## Documentation +See more [_**documentation**_](lib/src/main/kotlin/tree_tripper/SearchTree.kt) of library `TreeTripper` to learn more about it. + +## Authors + +- [@IliaSuponeff](https://github.com/IliaSuponeff) +- [@RodionovMaxim05](https://github.com/RodionovMaxim05) +- [@Friend-zva](https://github.com/Friend-zva), sometimes in commits his name is _**Vladimir Zaikin**_ + +## License + +Distributed under the [MIT License](https://choosealicense.com/licenses/mit/). See [`LICENSE`](LICENSE) for more information. + +

(back to top)

diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..18f452c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,6 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties + +org.gradle.parallel=true +org.gradle.caching=true + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..4f41755 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,13 @@ +# 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" + +[libraries] +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } + +[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..02cd19e --- /dev/null +++ b/lib/build.gradle.kts @@ -0,0 +1,71 @@ +/* + * 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. + * This project uses @Incubating APIs which are subject to change. + */ + +plugins { + // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. + alias(libs.plugins.jvm) + + // Apply the java-library plugin for API and implementation separation. + `java-library` + + // Code coverage plugin + jacoco + + id("org.barfuin.gradle.jacocolog") version "3.1.0" +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // This dependency is exported to consumers, that is to say found on their compile classpath. + api(libs.commons.math3) + // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params + testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2") + // 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(19) + } +} + +testing { + suites { + // Configure the built-in test suite + val test by getting(JvmTestSuite::class) { + // Use Kotlin Test framework + useKotlinTest("1.9.20") + } + } +} + +tasks.named("test") { + useJUnitPlatform() + finalizedBy(tasks.jacocoTestReport) +} + +tasks.withType { + testLogging { + events("PASSED", "SKIPPED", "FAILED") + } +} + +tasks.named("jacocoTestReport") { + dependsOn(tasks.test) + reports { + csv.required = false + xml.required = false + html.outputLocation = layout.buildDirectory.dir("jacocoHtml") + } +} diff --git a/lib/src/main/kotlin/tree_tripper/SearchTree.kt b/lib/src/main/kotlin/tree_tripper/SearchTree.kt new file mode 100644 index 0000000..29a3e28 --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/SearchTree.kt @@ -0,0 +1,112 @@ +package tree_tripper + +import tree_tripper.iterators.IterationOrders + + +/** + * The interface represents a search tree, that inherits from [Iterable] interface. + * + * @param K the key type in a tree, supporting the [Comparable] interface + * @param V the value type in a tree + */ +public interface SearchTree, V>: Iterable> { + + /** + * Inserts or updates the specified [value] with the specified [key] in a tree. + */ + public fun insert(key: K, value: V) + + /** + * Inserts the specified [value] with the specified [key] in a tree. + * @return true if the specified [value] with the specified [key] was inserted in a tree, + * false if a tree was not modified. + */ + public fun insertIfAbsent(key: K, value: V): Boolean + + /** + * Associates the specified [value] with the specified [key] in a tree. + */ + public operator fun set(key: K, value: V) + + /** + * Removes a given [key] and its corresponding value from a tree. + * @return the removed value associated with a given [key], or + * null if such a [key] does not present in a tree. + */ + public fun remove(key: K): V? + + /** + * Removes a given [key] and its corresponding value from a tree. + * @return the removed value associated with a given [key], or + * the [defaultValue] if such a [key] does not present in a tree. + */ + public fun removeOrDefault(key: K, defaultValue: V): V + + /** + * Searches the value associated with a given [key]. + * @return the value associated with a given [key], or + * null if such a [key] does not present in a tree. + */ + public fun search(key: K): V? + + /** + * Searches the value associated with a given [key]. + * @return the value associated with a given [key], or + * the [defaultValue] if such a [key] does not present in a tree. + */ + public fun searchOrDefault(key: K, defaultValue: V): V + + /** + * Checks if a given [key] is contained in a tree. + */ + public fun contains(key: K): Boolean + + /** + * Returns the value associated with a given [key], or + * null if such a [key] does not present in a tree. + */ + public operator fun get(key: K): V? + + /** + * Returns the key/value pair with the max key, or null if a tree is empty. + */ + public fun getMax(): Pair? + + /** + * Returns the key/value pair with the max key in subtree with a given [key] in root, or + * null if such a [key] does not present in a tree. + */ + public fun getMaxInSubtree(key: K): Pair? + + /** + * Returns the key/value pair with the min key, or null if a tree is empty. + */ + public fun getMin(): Pair? + + /** + * Returns the key/value pair with the min key in subtree with a given [key] in root, or + * null if such a [key] does not present in a tree. + */ + public fun getMinInSubtree(key: K): Pair? + + /** + * Returns the size of a tree. + */ + public fun getSize(): Int + + // Iterator + public fun iterator(order: IterationOrders): Iterator> + + public fun forEach(order: IterationOrders, action: (Pair) -> Unit) + + /** + * Returns a string with a transformed tree according to the [order]. + */ + public fun toString(order: IterationOrders): String + + /** + * Returns a string with a transformed tree according to the tree structure. + */ + public fun toStringWithTreeView(): String + +} diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt new file mode 100644 index 0000000..0ea16fe --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AVLTree.kt @@ -0,0 +1,89 @@ +package tree_tripper.binary_trees + +import tree_tripper.nodes.binary_nodes.AVLTreeNode + + +/** + * AVLTree class represents a self-balancing binary search tree that maintains + * the property of AVL tree, ensuring that the heights + * of the two child subtrees of any node differ by at most one. + * + * @param K the type of the keys in the tree + * @param V the value type associated with the key + */ +public open class AVLTree, V>: AbstractBSTree>() { + + override fun createNode(key: K, value: V): AVLTreeNode { + return AVLTreeNode(key, value) + } + + override fun balanceTree(node: AVLTreeNode): AVLTreeNode { + node.updateHeight() + return balance(node) + } + + /** + * @param node the AVLTreeNode for which to calculate the balance factor + * @return balance factor (height difference of his children) of the [node] + */ + protected fun balanceFactor(node: AVLTreeNode?): Int { + return (node?.rightChild?.height ?: 0) - (node?.leftChild?.height ?: 0) + } + + /** + * Defines and calls the method(s) for balancing the current [node]. + * + * @param node the node to balance in the AVL tree + * @return the root of the rebalanced subtree or a [node] if balance is not required + */ + protected fun balance(node: AVLTreeNode): AVLTreeNode { + when (balanceFactor(node)) { + -2 -> { + if (balanceFactor(node.leftChild) == 1) + node.leftChild = rotateLeft(node.leftChild as AVLTreeNode) + return rotateRight(node) + } + 2 -> { + if (balanceFactor(node.rightChild) == -1) + node.rightChild = rotateRight(node.rightChild as AVLTreeNode) + return rotateLeft(node) + } + else -> return node + } + } + + /** + * Performs a left rotation (counterclockwise) of the tree with the [node] as the root. + * Updates the heights of the affected nodes. + * + * @param node the node to perform a left rotation on + * @returns the root of the rotated subtree + */ + protected fun rotateLeft(node: AVLTreeNode): AVLTreeNode { + val nodeSwapped = node.rightChild ?: return node + node.rightChild = nodeSwapped.leftChild + nodeSwapped.leftChild = node + + node.updateHeight() + nodeSwapped.updateHeight() + return nodeSwapped + } + + /** + * Performs a right rotation (clockwise) of the tree with the [node] as the root. + * Updates the heights of the affected nodes. + * + * @param node the node to perform a right rotation on + * @returns the root of the rotated subtree + */ + protected fun rotateRight(node: AVLTreeNode): AVLTreeNode { + val nodeSwapped = node.leftChild ?: return node + node.leftChild = nodeSwapped.rightChild + nodeSwapped.rightChild = node + + node.updateHeight() + nodeSwapped.updateHeight() + return nodeSwapped + } + +} diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt new file mode 100644 index 0000000..efa095e --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -0,0 +1,248 @@ +package tree_tripper.binary_trees + +import tree_tripper.SearchTree +import tree_tripper.iterators.BinarySearchTreeIterator +import tree_tripper.iterators.IterationOrders +import tree_tripper.nodes.binary_nodes.AbstractBSTreeNode +import tree_tripper.nodes.notNullNodeAction + + +/** + * The class represents an abstract binary search tree, + * from which binary search trees are inherited. + * + * @param K the key type in a tree, supporting the [Comparable] interface + * @param V the value type in a tree + * @param N the node type in a tree + */ +public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { + protected var root: N? = null + private set + private var size: Int = 0 + + override fun insert(key: K, value: V) { + insert(key, value, permissionUpdate = true) + } + + override fun insertIfAbsent(key: K, value: V): Boolean { + return insert(key, value, permissionUpdate = false) + } + + override fun set(key: K, value: V) { + insert(key, value) + } + + override fun remove(key: K): V? { + val resultRemove = removeNode(root, key) + updateRoot(resultRemove.first) + return resultRemove.second + } + + override fun removeOrDefault(key: K, defaultValue: V): V { + return remove(key) ?: defaultValue + } + + override fun search(key: K): V? { + return searchNode(key)?.value + } + + override fun searchOrDefault(key: K, defaultValue: V): V { + return search(key) ?: defaultValue + } + + override fun contains(key: K): Boolean { + return search(key) != null + } + + override fun get(key: K): V? { + return search(key) + } + + override fun getMaxInSubtree(key: K): Pair? { + val resultSearch = getMaxNodeInSubtree(searchNode(key)) ?: return null + return Pair(resultSearch.key, resultSearch.value) + } + + override fun getMax(): Pair? { + return notNullNodeAction(root, null) {node -> getMaxInSubtree(node.key)} + } + + override fun getMinInSubtree(key: K): Pair? { + val resultSearch = getMinNodeInSubtree(searchNode(key)) ?: return null + return Pair(resultSearch.key, resultSearch.value) + } + + override fun getMin(): Pair? { + return notNullNodeAction(root, null) {node -> getMinInSubtree(node.key)} + } + + override fun getSize(): Int { + return size + } + + override fun iterator(): BinarySearchTreeIterator { + return iterator(IterationOrders.WIDTH_ORDER) + } + + override fun iterator(order: IterationOrders): BinarySearchTreeIterator { + return BinarySearchTreeIterator(root, order) + } + + override fun forEach(order: IterationOrders, action: (Pair) -> Unit) { + val treeIterator: BinarySearchTreeIterator = iterator(order) + while (treeIterator.hasNext()) + action(treeIterator.next()) + } + + override fun toString(): String { + return toString(IterationOrders.WIDTH_ORDER) + } + + override fun toString(order: IterationOrders): String { + val builder = StringBuilder() + this.forEach(order) { pair: Pair -> builder.append("${pair.first}: ${pair.second}, ") } + return "${this.javaClass.simpleName}($builder)" + } + + override fun toStringWithTreeView(): String { + val builder = StringBuilder() + notNullNodeAction(root, Unit) {node -> node.toStringWithSubtreeView(0, builder)} + return "${this.javaClass.simpleName}(\n$builder)" + } + + /** + * Returns a new [N] node with the specified [value] with the specified [key]. + */ + protected abstract fun createNode(key: K, value: V): N + + /** + * Changes the root to a given [node]. + */ + protected open fun updateRoot(node: N?) { + root = node + } + + /** + * Balances subtree with a given [node] at the top. + */ + protected open fun balanceTree(node: N): N { + return node + } + + /** + * Inserts the specified [value] with the specified [key] in a tree or + * updates [value] if [permissionUpdate] is true + * @return true if the specified [value] with the specified [key] was inserted in a tree, + * false if a tree was not modified. + */ + private fun insert(key: K, value: V, permissionUpdate: Boolean): Boolean { + val insertResult: Pair = insertNode(root, key, value, permissionUpdate) + updateRoot(insertResult.first) + if (insertResult.second) size++ + return insertResult.second + } + + /** + * Add recursively the node with the specified [value] with the specified [key] in a tree or + * updates [value] if [permissionUpdate] is true and balances tree on every call. + * @return a pair of a new or balanced [N] node, and true if the specified [value] with + * the specified [key] was inserted in a tree, false if not. + */ + private fun insertNode(node: N?, key: K, value: V, permissionUpdate: Boolean): Pair { + if (node == null) return Pair(createNode(key, value), true) + + val resultInsert: Pair + val resultCompare: Int = key.compareTo(node.key) + if (resultCompare < 0) { + resultInsert = insertNode(node.leftChild, key, value, permissionUpdate) + node.leftChild = resultInsert.first + } else if (resultCompare > 0) { + resultInsert = insertNode(node.rightChild, key, value, permissionUpdate) + node.rightChild = resultInsert.first + } else { + if (permissionUpdate) node.value = value + return Pair(node, false) + } + + return Pair(balanceTree(node), resultInsert.second) + } + + /** + * Removes recursively the node with a given [key] and balances tree on every call. + * @return a pair of a balanced [N] node or null, and [V] value corresponding the given [key] + * if a node was removed, null if not. + */ + protected open fun removeNode(node: N?, key: K): Pair { + if (node == null) return Pair(null, null) + + val resultRemove: Pair + val resultCompare: Int = key.compareTo(node.key) + if (resultCompare < 0) { + resultRemove = removeNode(node.leftChild, key) + node.leftChild = resultRemove.first + } else if (resultCompare > 0) { + resultRemove = removeNode(node.rightChild, key) + node.rightChild = resultRemove.first + } else { + val nodeSubstitutive: N? + if (node.leftChild == null || node.rightChild == null) { + nodeSubstitutive = node.leftChild ?: node.rightChild + return Pair(nodeSubstitutive, node.value) + } else { + nodeSubstitutive = getMaxNodeInSubtree(node.leftChild) as N + node.leftChild = removeNode(node.leftChild, nodeSubstitutive.key).first + nodeSubstitutive.rightChild = node.rightChild + nodeSubstitutive.leftChild = node.leftChild + return Pair(balanceTree(nodeSubstitutive), node.value) + } + } + + return Pair(balanceTree(node), resultRemove.second) + } + + /** + * Searches the node with a given key. + * @return the found node or null if the node is not contained in a tree. + */ + private fun searchNode(key: K): N? { + var nodeCurrent: N? = root ?: return null + + while (nodeCurrent != null) { + val resultCompare = key.compareTo(nodeCurrent.key) + if (resultCompare < 0) + nodeCurrent = nodeCurrent.leftChild + else if (resultCompare > 0) + nodeCurrent = nodeCurrent.rightChild + else + return nodeCurrent + } + return null + } + + /** + * Returns the [N] node with the max key in subtree with a given [node] in root, or null + * if such a [node] is not contained in a tree. + */ + protected fun getMaxNodeInSubtree(node: N?): N? { + if (node == null) return null + + var nodeCurrent: N = node + while (true) + nodeCurrent = nodeCurrent.rightChild ?: break + return nodeCurrent + } + + /** + * Returns the [N] node with the min key in subtree with a given [node] in root, or null + * if such a [node] is not contained in a tree. + */ + protected fun getMinNodeInSubtree(node: N?): N? { + if (node == null) return null + + var nodeCurrent: N = node + while (true) + nodeCurrent = nodeCurrent.leftChild ?: break + return nodeCurrent + } + +} diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt new file mode 100644 index 0000000..5f790c6 --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/BSTree.kt @@ -0,0 +1,18 @@ +package tree_tripper.binary_trees + +import tree_tripper.nodes.binary_nodes.BSTreeNode + + +/** + * The class represents the binary search tree. + * + * @param K the key type in the tree, supporting the [Comparable] interface + * @param V the value type in the tree + */ +public open class BSTree, V>: AbstractBSTree>() { + + override fun createNode(key: K, value: V): BSTreeNode { + return BSTreeNode(key, value) + } + +} diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt new file mode 100644 index 0000000..acfe53e --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -0,0 +1,201 @@ +package tree_tripper.binary_trees + +import tree_tripper.nodes.binary_nodes.RBTreeNode +import tree_tripper.nodes.notNullNodeAction +import tree_tripper.nodes.notNullNodeUpdate + + +/** + * A class that represents a red-black tree data structure. + * + * @param K the type of the keys in the tree + * @param V the type of the values in the tree + */ +public open class RBTree, V>: AbstractBSTree>() { + + override fun createNode(key: K, value: V): RBTreeNode { + return RBTreeNode(key, value) + } + + override fun updateRoot(node: RBTreeNode?) { + notNullNodeUpdate(node) { it.isRed = false } + super.updateRoot(node) + } + + override fun balanceTree(node: RBTreeNode): RBTreeNode { + var nodeCurrent: RBTreeNode = node + if (isRedColor(nodeCurrent.rightChild)) { + nodeCurrent = rotateLeft(nodeCurrent) + } + if (isRedColor(nodeCurrent.leftChild) && isRedLeftChild(nodeCurrent.leftChild)) { + nodeCurrent = rotateRight(nodeCurrent) + } + if (isRedColor(nodeCurrent.leftChild) && isRedColor(nodeCurrent.rightChild)) { + flipColors(nodeCurrent) + } + return nodeCurrent + } + + override fun removeNode(node: RBTreeNode?, key: K): Pair?, V?> { + if (node == null) return Pair(null, null) + + val removeResult: Pair?, V?> + val resultCompare: Int = key.compareTo(node.key) + var nodeCurrent: RBTreeNode = node + if (resultCompare < 0) { + if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) + nodeCurrent = moveRedLeft(nodeCurrent) + + removeResult = removeNode(nodeCurrent.leftChild, key) + nodeCurrent.leftChild = removeResult.first + } else { + if (isRedColor(nodeCurrent.leftChild)) + nodeCurrent = rotateRight(nodeCurrent) + if (resultCompare == 0 && nodeCurrent.rightChild == null) + return Pair(null, nodeCurrent.value) + if (!isRedColor(nodeCurrent.rightChild) && !isRedLeftChild(nodeCurrent.rightChild)) + nodeCurrent = moveRedRight(nodeCurrent) + if (resultCompare == 0) { + val nodeWithMinimalKey = getMinNodeInSubtree(nodeCurrent.rightChild) as RBTreeNode + val nodeSubstitutive: RBTreeNode = createNode(nodeWithMinimalKey.key, nodeWithMinimalKey.value) + nodeSubstitutive.isRed = nodeCurrent.isRed + nodeSubstitutive.leftChild = nodeCurrent.leftChild + nodeSubstitutive.rightChild = removeMinNode(nodeCurrent.rightChild) + return Pair(balanceTree(nodeSubstitutive), nodeCurrent.value) + } else { + removeResult = removeNode(nodeCurrent.rightChild, key) + nodeCurrent.rightChild = removeResult.first + } + } + + return Pair(balanceTree(nodeCurrent), removeResult.second) + } + + /** + * Returns whether the specified node is red or not. + * + * @param node the node to check + * @return `true` if the node is red, `false` otherwise + */ + protected fun isRedColor(node: RBTreeNode?): Boolean { + if (node == null) return false + return node.isRed + } + + /** + * Returns whether the specified node is red or not. + * + * @param node the node to check color its left child + * @return `true` if left child of `node` is red, `false` otherwise + */ + protected fun isRedLeftChild(node: RBTreeNode?): Boolean { + if (node == null) return false + return isRedColor(node.leftChild) + } + + /** + * Rotates the binary tree node with the given root to the left. + * + * @param node the root of the binary tree node to rotate left + * @return if `node.rightChild` is null, returns `node`, + * otherwise `node` switches places with the right child + */ + protected fun rotateLeft(node: RBTreeNode): RBTreeNode { + val rightChild: RBTreeNode = node.rightChild ?: return node + node.rightChild = rightChild.leftChild + rightChild.leftChild = node + + rightChild.isRed = node.isRed + node.isRed = true + return rightChild + } + + /** + * Rotates the binary tree node with the given root to the right. + * + * @param node the binary tree node to rotate right + * @return if `node.leftChild` is null, returns `node`, + * otherwise `node` switches places with the left child + */ + protected fun rotateRight(node: RBTreeNode): RBTreeNode { + val leftChild: RBTreeNode = node.leftChild ?: return node + node.leftChild = leftChild.rightChild + leftChild.rightChild = node + + leftChild.isRed = node.isRed + node.isRed = true + return leftChild + } + + /** + * Flips the colors of the specified node and its children. + * + * @param node needed to flip the colors + */ + protected fun flipColors(node: RBTreeNode): Unit { + node.isRed = !node.isRed + notNullNodeUpdate(node.leftChild) { child -> child.isRed = !child.isRed } + notNullNodeUpdate(node.rightChild) { child -> child.isRed = !child.isRed } + } + + /** + * This function is used to move a red node to the right, if it has a red left child of its [node] left child. + * It first flips the colors of the node and its children, then rotates the tree if the left child is also red. + * + * @param node the node to move + * @return the new root of the tree, which is balanced node subtree + */ + private fun moveRedRight(node: RBTreeNode): RBTreeNode { + var nodeCurrent: RBTreeNode = node + + flipColors(nodeCurrent) + if (isRedLeftChild(nodeCurrent.leftChild)) { + nodeCurrent = rotateRight(nodeCurrent) + flipColors(nodeCurrent) + } + return nodeCurrent + } + + /** + * This function is used to move a red node to the left, if it has a red right child of its [node] left child. + * It first flips the colors of the node and its children, then rotates the tree if the left child is also red. + * + * @param node the node to move + * @return the new root of the tree, which is balanced node subtree + */ + private fun moveRedLeft(node: RBTreeNode): RBTreeNode { + var nodeCurrent: RBTreeNode = node + + flipColors(nodeCurrent) + if (isRedLeftChild(nodeCurrent.rightChild)) { + nodeCurrent.rightChild = notNullNodeAction( + node.rightChild, null + ) {rightChild -> rotateRight(rightChild)} + nodeCurrent = rotateLeft(nodeCurrent) + flipColors(nodeCurrent) + } + return nodeCurrent + } + + /** + * Removes the node with the minimum key from the binary search tree. + * + * @param node the root of the binary search tree + * @return the root of the binary search tree with the node removed, or `null` if the tree is empty + */ + private fun removeMinNode(node: RBTreeNode?): RBTreeNode? { + if (node == null) return null + if (node.leftChild == null) return node.rightChild + + var nodeCurrent: RBTreeNode = node + if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) + nodeCurrent = moveRedLeft(nodeCurrent) + + nodeCurrent.leftChild = notNullNodeAction( + nodeCurrent.leftChild, null + ) {child -> removeMinNode(child)} + + return balanceTree(nodeCurrent) + } + +} diff --git a/lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt b/lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt new file mode 100644 index 0000000..ea40851 --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt @@ -0,0 +1,192 @@ +package tree_tripper.iterators + +import tree_tripper.nodes.binary_nodes.AbstractBSTreeNode +import java.util.LinkedList +import java.util.Queue + + +/** + * A generic binary search tree iterator that can iterate over a binary search tree in different orders. + * + * @param K the type of the keys in the binary search tree + * @param V the type of the values in the binary search tree + * @param N the type of the nodes in the binary search tree + */ +class BinarySearchTreeIterator, V, N: AbstractBSTreeNode>: Iterator> { + private val iterationState: IterationState + + /** + * Constructs a binary search tree iterator with the given root node and the default iteration order, which is [IterationOrders.WIDTH_ORDER]. + * + * @param root the root node of the binary search tree + */ + constructor(root: N?): this(root, IterationOrders.WIDTH_ORDER) + + /** + * Constructs a binary search tree iterator with the given root node and the given iteration order. + * + * @param root the root node of the binary search tree + * @param order the iteration order + */ + constructor(root: N?, order: IterationOrders) { + iterationState = when (order) { + IterationOrders.WIDTH_ORDER -> WidthIterationState(root) + IterationOrders.INCREASING_ORDER -> IncreasingIterationState(root) + IterationOrders.DECREASING_ORDER -> DecreasingIterationState(root) + } + } + + override fun hasNext(): Boolean { + return iterationState.hasNext() + } + + override fun next(): Pair { + return iterationState.next() + } + + /** + * An interface for the different iteration states of a binary search tree iterator. + */ + private interface IterationState, V, N: AbstractBSTreeNode> { + + /** + * Returns `true` if the iteration has more elements. + */ + abstract fun hasNext(): Boolean + + /** + * Returns `true` if the iteration has more elements. + * @throws: NoSuchElementException - if the iteration state has no next element. + */ + abstract fun next(): Pair + + } + + /** + * A concrete iteration state for a binary search tree iterator with the width iteration order. + */ + private class WidthIterationState, V, N: AbstractBSTreeNode>( + root: N? + ): IterationState { + private val queue: Queue = LinkedList() + + init { + if (root != null) queue.add(root) + } + + override fun hasNext(): Boolean { + return (queue.size > 0) + } + + override fun next(): Pair { + if (!hasNext()) throw NoSuchElementException("Try get next element from the end of iterator state") + val nodeCurrent: N = queue.poll() + nodeCurrent.getChildren().forEach() { child -> + queue.add(child) + } + return Pair(nodeCurrent.key, nodeCurrent.value) + } + + } + + /** + * A concrete iteration state for a binary search tree iterator with the increasing iteration order. + */ + private class IncreasingIterationState, V, N: AbstractBSTreeNode>( + root: N? + ): IterationState { + private val unprocessedNodesStack = LinkedList() + private val semiProcessedNodesStack = LinkedList() + + init { + if (root != null) unprocessedNodesStack.add(root) + } + + override fun hasNext(): Boolean { + return (hasUnprocessedNodes() || hasSemiProcessedNodes()) + } + + private fun hasUnprocessedNodes(): Boolean { + return unprocessedNodesStack.isNotEmpty() + } + + private fun hasSemiProcessedNodes(): Boolean { + return semiProcessedNodesStack.isNotEmpty() + } + + override fun next(): Pair { + if (!hasNext()) throw NoSuchElementException("Try get next element from the end of iterator state") + var nodeCurrent: N + + while (hasUnprocessedNodes()) { + nodeCurrent = unprocessedNodesStack.pollFirst() + semiProcessedNodesStack.addFirst(nodeCurrent) + if (nodeCurrent.leftChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.leftChild) + else { + semiProcessedNodesStack.pollFirst() + if (nodeCurrent.rightChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.rightChild) + return Pair(nodeCurrent.key, nodeCurrent.value) + } + } + + nodeCurrent = semiProcessedNodesStack.pollFirst() + if (nodeCurrent.rightChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.rightChild) + return Pair(nodeCurrent.key, nodeCurrent.value) + } + + } + + /** + * A concrete iteration state for a binary search tree iterator with the decreasing iteration order. + */ + private class DecreasingIterationState, V, N: AbstractBSTreeNode>( + root: N? + ): IterationState { + private val unprocessedNodesStack = LinkedList() + private val semiProcessedNodesStack = LinkedList() + + init { + if (root != null) unprocessedNodesStack.add(root) + } + + override fun hasNext(): Boolean { + return (hasUnprocessedNodes() || hasSemiProcessedNodes()) + } + + private fun hasSemiProcessedNodes(): Boolean { + return semiProcessedNodesStack.isNotEmpty() + } + + private fun hasUnprocessedNodes(): Boolean { + return unprocessedNodesStack.isNotEmpty() + } + + override fun next(): Pair { + if (!hasNext()) throw NoSuchElementException("Try get next element from the end of iterator state") + var nodeCurrent: N + + while (hasUnprocessedNodes()) { + nodeCurrent = unprocessedNodesStack.pollFirst() + semiProcessedNodesStack.addFirst(nodeCurrent) + if (nodeCurrent.rightChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.rightChild) + else { + semiProcessedNodesStack.pollFirst() + if (nodeCurrent.leftChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.leftChild) + return Pair(nodeCurrent.key, nodeCurrent.value) + } + } + + nodeCurrent = semiProcessedNodesStack.pollFirst() + if (nodeCurrent.leftChild != null) + unprocessedNodesStack.addFirst(nodeCurrent.leftChild) + return Pair(nodeCurrent.key, nodeCurrent.value) + } + + } + +} diff --git a/lib/src/main/kotlin/tree_tripper/iterators/IterationOrders.kt b/lib/src/main/kotlin/tree_tripper/iterators/IterationOrders.kt new file mode 100644 index 0000000..7bcec24 --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/iterators/IterationOrders.kt @@ -0,0 +1,15 @@ +package tree_tripper.iterators + + +/** + * An enumeration of the possible orders in which to iterate over the elements of a tree. + * + * @property WIDTH_ORDER iterate over the elements in order of their width in the tree + * @property INCREASING_ORDER iterate over the elements in order of their increasing depth in the tree + * @property DECREASING_ORDER iterate over the elements in order of their decreasing depth in the tree + */ +enum class IterationOrders { + WIDTH_ORDER, + INCREASING_ORDER, + DECREASING_ORDER, +} diff --git a/lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt new file mode 100644 index 0000000..78fb845 --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/nodes/SearchTreeNode.kt @@ -0,0 +1,28 @@ +package tree_tripper.nodes + + +/** + * The interface represents a node of a search tree. + * + * @param K the key type of node, supporting the [Comparable] interface + * @param V the value type of node + * @param N the node type + */ +public interface SearchTreeNode, V, N: SearchTreeNode> { + + /** + * Returns a [N] list of not null children of a node. + */ + public fun getChildren(): List + + /** + * Returns a string with a transformed to the simple view node. + */ + public fun toStringSimpleView(): String + + /** + * Transforms a node to the [builder] of the subtree structure with a some [indent]. + */ + public fun toStringWithSubtreeView(indent: Int, builder: StringBuilder): Unit + +} diff --git a/lib/src/main/kotlin/tree_tripper/nodes/Utils.kt b/lib/src/main/kotlin/tree_tripper/nodes/Utils.kt new file mode 100644 index 0000000..2fe421f --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/nodes/Utils.kt @@ -0,0 +1,20 @@ +package tree_tripper.nodes + + +/** + * Checks the search tree [node] for null and if it is not null then executes the [action] + * + * @return the result of applying the given [action] function to the given [node], or the [nullNodeResult] if the [node] is null. + */ +public fun , R> notNullNodeAction(node: N?, nullNodeResult: R, action: (N) -> (R)): R { + if (node == null) return nullNodeResult + return action(node) +} + +/** + * Checks the search tree [node] for null and if it is not null then executes the [updateAction] + */ +public fun > notNullNodeUpdate(node: N?, updateAction: (N) -> (Unit)): Unit { + if (node == null) return + return updateAction(node) +} diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt new file mode 100644 index 0000000..7e70f93 --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNode.kt @@ -0,0 +1,39 @@ +package tree_tripper.nodes.binary_nodes + +import kotlin.math.max + +/** + * AVLTreeNode class represents a node in an AVL tree, containing a key-value pair and additional + * information for maintaining the AVL property such as the height of the node. + * + * @param K the key type that implements the Comparable interface + * @param V the value type associated with the key + * @property height the height of the [AVLTreeNode] in the AVL tree + */ +public class AVLTreeNode, V>( + key: K, + value: V +): AbstractBSTreeNode>(key, value) { + + public constructor( + key: K, value: V, height: Int, + leftChild: AVLTreeNode?, + rightChild: AVLTreeNode? + ) : this(key, value) { + this.height = height + this.leftChild = leftChild + this.rightChild = rightChild + } + + /** The height of the node in the AVL tree, initialized to 1. */ + public var height: Int = 1 + private set + + /** Updates height of the node in AVL tree based on the heights of its left and right child subtrees. */ + public fun updateHeight() { + val leftHeight = this.leftChild?.height ?: 0 + val rightHeight = this.rightChild?.height ?: 0 + height = (max(leftHeight, rightHeight) + 1) + } + +} diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt new file mode 100644 index 0000000..0a900eb --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -0,0 +1,40 @@ +package tree_tripper.nodes.binary_nodes + +import tree_tripper.nodes.SearchTreeNode +import tree_tripper.nodes.notNullNodeAction + + +/** + * The class represents a node of the abstract binary search tree, + * from which nodes of binary search trees are inherited. + * + * @param K the [key] type of node, supporting the [Comparable] interface + * @param V the [value] type of node + * @param N the node type + */ +public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeNode>( + public val key: K, + public var value: V +): SearchTreeNode { + public var leftChild: N? = null + public var rightChild: N? = null + + override fun getChildren(): List { + return listOfNotNull(leftChild, rightChild) + } + + override fun toString(): String { + return "${this.javaClass.simpleName}(key=$key, value=$value)" + } + + override fun toStringSimpleView(): String { + return "($key, $value)" + } + + override fun toStringWithSubtreeView(indent: Int, builder: StringBuilder) { + notNullNodeAction(this.rightChild, Unit) {node -> node.toStringWithSubtreeView(indent + 1, builder)} + builder.append("\t".repeat(indent)).append(this.toStringSimpleView()).append("\n") + notNullNodeAction(this.leftChild, Unit) {node -> node.toStringWithSubtreeView(indent + 1, builder)} + } + +} diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt new file mode 100644 index 0000000..4f2cf7c --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt @@ -0,0 +1,13 @@ +package tree_tripper.nodes.binary_nodes + + +/** + * The class represents a node of the binary search tree. + * + * @param K the key type of the node, supporting the [Comparable] interface + * @param V the value type of the node + */ +public class BSTreeNode, V>( + key: K, + value: V +): AbstractBSTreeNode>(key, value) diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt new file mode 100644 index 0000000..47ecd01 --- /dev/null +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt @@ -0,0 +1,40 @@ +package tree_tripper.nodes.binary_nodes + + +/** + * A red-black tree node. + * + * @param K the key type + * @param V the value type + */ +public class RBTreeNode, V>( + key: K, + value: V +) : AbstractBSTreeNode>(key, value) { + var isRed: Boolean = true + + public constructor(key: K, value: V, isRed: Boolean) : this(key, value) { + this.isRed = isRed + } + + public constructor( + key: K, value: V, isRed: Boolean, + leftChild: RBTreeNode?, + rightChild: RBTreeNode? + ) : this(key, value, isRed) { + this.leftChild = leftChild + this.rightChild = rightChild + } + + override fun toStringSimpleView(): String { + return "${super.toStringSimpleView()} - ${colorName()}" + } + + /** + * Returns the color name of this node. + */ + private fun colorName(): String { + return if (isRed) "RED" else "BLACK" + } + +} diff --git a/lib/src/test/kotlin/AssertionUtils.kt b/lib/src/test/kotlin/AssertionUtils.kt new file mode 100644 index 0000000..2109199 --- /dev/null +++ b/lib/src/test/kotlin/AssertionUtils.kt @@ -0,0 +1,37 @@ +import org.junit.jupiter.api.Assertions +import tree_tripper.nodes.binary_nodes.AbstractBSTreeNode + + +public fun > assertBinaryNodeDataEquals(nodeFirst: N?, nodeSecond: N?): Unit { + assertBinaryNodeDataEquals(nodeFirst, nodeSecond) { _, _: N -> true } +} + +public fun > assertBinaryNodeDataEquals( + nodeFirst: N?, + nodeSecond: N?, + assertAction: (N, N) -> (Boolean) +): Unit { + if (nodeFirst == null) return Assertions.assertNull(nodeSecond) { "Second node is not null." } + if (nodeSecond == null) return Assertions.assertNull(nodeFirst) { "First node is not null." } + + Assertions.assertEquals(nodeFirst.key, nodeSecond.key) { "Keys are not equal." } + Assertions.assertEquals(nodeFirst.value, nodeSecond.value) { "Values are not equal." } + Assertions.assertTrue(assertAction(nodeFirst, nodeSecond)) { "Action assertion is invalid equals" } +} + +public fun > assertBinaryNodeDeepEquals(nodeFirst: N?, nodeSecond: N?): Unit { + assertBinaryNodeDeepEquals(nodeFirst, nodeSecond) { _, _: N? -> true } +} + +public fun > assertBinaryNodeDeepEquals( + nodeFirst: N?, + nodeSecond: N?, + assertAction: (N, N) -> (Boolean) +): Unit { + if (nodeFirst == null) return Assertions.assertNull(nodeSecond) { "Second node is not null." } + if (nodeSecond == null) return Assertions.assertNull(nodeFirst) { "First node is not null." } + + assertBinaryNodeDataEquals(nodeFirst, nodeSecond, assertAction) + assertBinaryNodeDeepEquals(nodeFirst.leftChild, nodeSecond.leftChild, assertAction) + assertBinaryNodeDeepEquals(nodeFirst.rightChild, nodeSecond.rightChild, assertAction) +} diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt new file mode 100644 index 0000000..7257a27 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt @@ -0,0 +1,400 @@ +package tree_tripper.binary_trees + +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import tree_tripper.binary_trees.assistants.AVLTreeTestAssistant +import tree_tripper.nodes.binary_nodes.AVLTreeNode + + +class AVLTreeTest { + private lateinit var tree: AVLTreeTestAssistant + + @BeforeEach + fun setup() { + tree = AVLTreeTestAssistant() + } + + @Test + public fun testTreeInitializing() { + tree.assertRoot(null) { "Root of AVLTree is not null by standard initialize." } + Assertions.assertEquals(0, tree.getSize()) + } + + @ParameterizedTest + @MethodSource("testNodeCreationCases") + public fun testNodeCreation(key: Int, value: Int) { + tree.assertNodeCreation(key, value) + } + + @ParameterizedTest + @MethodSource("testBalanceTreeCases") + public fun testBalanceTree(expected: AVLTreeNode, node: AVLTreeNode) { + tree.assertBalanceTree(expected, node) + } + + @ParameterizedTest + @MethodSource("checkBalanceFactor") + public fun checkBalanceFactor(expected: Int, node: AVLTreeNode?) { + tree.assertBalanceFactor(expected, node) + } + + @ParameterizedTest + @MethodSource("testBalanceCases") + public fun testBalanceCases(expected: AVLTreeNode, node: AVLTreeNode) { + tree.assertBalance(expected, node) + } + + @ParameterizedTest + @MethodSource("testNodeRotateRightCases") + public fun testNodeRotateRightCases(expected: AVLTreeNode, node: AVLTreeNode) { + tree.assertNodeRightRotation(expected, node) + } + + @ParameterizedTest + @MethodSource("testNodeRotateLeftCases") + public fun testNodeRotateLeftCases(expected: AVLTreeNode, node: AVLTreeNode) { + tree.assertNodeLeftRotation(expected, node) + } + + companion object { + + @JvmStatic + fun testNodeCreationCases(): List = listOf( + Arguments.of(0, 0), + Arguments.of(1, 1), + Arguments.of(-1, -1) + ) + + @JvmStatic + fun testBalanceTreeCases(): List = listOf( + + //Does not require balance + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 1, 1, 1, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ) + ), + + //Simple left rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 0, 0, 1, + null, + AVLTreeNode( + 1, 1, 2, + null, + AVLTreeNode(2, 2, 1, null, null) + ) + ) + ), + + //Simple right rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 2, 2, 1, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + null + ), + null + ) + ) + + ) + + @JvmStatic + fun checkBalanceFactor(): List = listOf( + + Arguments.of(0, null), + + Arguments.of(0, + AVLTreeNode(0, 0, 1, null, null) + ), + + Arguments.of(1, + AVLTreeNode( + 0, 0, 2, + null, + AVLTreeNode(0, 0, 1, null, null) + ) + ), + + Arguments.of(-1, + AVLTreeNode( + 0, 0, 2, + AVLTreeNode(0, 0, 1, null, null), + null + ) + ), + + Arguments.of(0, + AVLTreeNode( + 0, 0, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(0, 0, 1, null, null) + ) + ) + + ) + + @JvmStatic + fun testBalanceCases(): List = listOf( + + //Does not require balance + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ) + ), + + //Simple left rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 0, 0, 3, + null, + AVLTreeNode( + 1, 1, 2, + null, + AVLTreeNode(2, 2, 1, null, null) + ) + ) + ), + + //Simple right rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 2, 2, 3, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + null + ), + null + ) + ), + + //Simple left right rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 2, 2, 3, + AVLTreeNode( + 0, 0, 2, + null, + AVLTreeNode(1, 1, 1, null, null) + ), + null + ) + ), + + //Simple right left rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 0, 0, 3, + null, + AVLTreeNode( + 2, 2, 2, + AVLTreeNode(1, 1, 1, null, null), + null + ) + ) + ) + + ) + + @JvmStatic + fun testNodeRotateLeftCases(): List = listOf( + + //Null check + Arguments.of( + //Expected + AVLTreeNode(0, 0, 1, null, null), + //Testing + AVLTreeNode(0, 0, 1, null, null) + ), + + //Simple left rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 0, 0, 3, + null, + AVLTreeNode( + 1, 1, 2, + null, + AVLTreeNode(2, 2, 1, null, null) + ) + ) + ), + + //Left rotation with children + Arguments.of( + //Expected + AVLTreeNode( + 3, 3, 3, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + AVLTreeNode( + 4, 4, 2, + null, + AVLTreeNode(5, 5, 1, null, null) + ) + ), + //Testing + AVLTreeNode( + 1, 1, 4, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode( + 3, 3, 3, + AVLTreeNode(2, 2, 1, null, null), + AVLTreeNode( + 4, 4, 2, + null, + AVLTreeNode(5, 5, 1, null, null) + ) + ) + ) + ) + + ) + + @JvmStatic + fun testNodeRotateRightCases(): List = listOf( + + //Null check + Arguments.of( + //Expected + AVLTreeNode(0, 0, 1, null, null), + //Testing + AVLTreeNode(0, 0, 1, null, null) + ), + + //Simple right rotation + Arguments.of( + //Expected + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ), + //Testing + AVLTreeNode( + 2, 2, 3, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + null + ), + null + ) + ), + + //Right rotation with children + Arguments.of( + //Expected + AVLTreeNode( + 2, 2, 3, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + null + ), + AVLTreeNode( + 4, 4, 2, + AVLTreeNode(3, 3, 1, null, null), + AVLTreeNode(5, 5, 1, null, null) + ) + ), + //Testing + AVLTreeNode( + 4, 4, 4, + AVLTreeNode( + 2, 2, 3, + AVLTreeNode( + 1, 1, 2, + AVLTreeNode(0, 0, 1, null, null), + null + ), + AVLTreeNode(3, 3, 1, null, null) + ), + AVLTreeNode(5, 5, 1, null, null) + ) + ) + + ) + + } +} diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt new file mode 100644 index 0000000..23694db --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -0,0 +1,295 @@ +package tree_tripper.binary_trees + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.assertTimeout +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import tree_tripper.binary_trees.assistants.BSTreeTestAssistant +import java.time.Duration +import kotlin.random.Random +import kotlin.test.Test + + +public class BSTreeTest { + private lateinit var tree: BSTreeTestAssistant + + @BeforeEach + public fun setup() { + tree = BSTreeTestAssistant() + } + + @Test + @DisplayName("tree initialization") + public fun treeInitialization() { + tree.assertRootInitialization() + assertEquals(tree.getSize(), 0, "Incorrect a tree initialization") + } + + @Test + @DisplayName("create node") + public fun createNode() { + tree.assertWasCreatedNode(1, -1) + } + + @Test + @DisplayName("update root") + public fun updateRoot() { + tree.assertWasUpdatedRoot(1, -1) + } + + @Test + @DisplayName("balance tree") + public fun balanceTree() { + tree.assertWasBalancedTree(1, -1) + } + + @Test + @DisplayName("insert root") + public fun insertRoot() { + tree.insert(1, -1) + tree.assertIsBSTree() + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + tree.insert(1, 0) + tree.assertIsBSTree() + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + } + + @Test + @DisplayName("insert children root") + public fun insertChildrenRoot() { + tree.insert(2, -2) + tree.insert(1, -1) + tree.insert(3, -3) + tree.assertIsBSTree() + assertEquals(tree.getSize(), 3, "Incorrect resizing tree size") + } + + @Test + @DisplayName("insert 5 elements") + public fun insertElements() { + tree.insert(4, -4) + tree.insert(5, -5) + tree.insert(3, -3) + tree.insert(2, -2) + tree.insert(6, -6) + tree.assertIsBSTree() + assertEquals(tree.getSize(), 5, "Incorrect resizing tree size") + } + + @ParameterizedTest + @MethodSource("sizeAndTime") + @DisplayName("insert with size and time") + public fun insertWithSizeAndTime(size: Int, seconds: Long) { + assertTimeout(Duration.ofSeconds(seconds)) { + repeat(size) { + val keyRandom = Random.nextInt(-1000, 1000) + tree.insert(keyRandom, keyRandom) + } + } + + tree.assertIsBSTree() + } + + @Test + @DisplayName("insert if absent root") + public fun insertIfAbsentRoot() { + assertEquals(tree.insertIfAbsent(1, -1), true) + tree.assertIsBSTree() + assertEquals(tree.insertIfAbsent(1, 1), false) + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + } + + @Test + @DisplayName("set with brackets") + public fun set() { + assertEquals(tree.search(1), null) + tree[1] = -1 + assertEquals(tree.search(1), -1) + } + + @Test + @DisplayName("search node") + public fun searchNode() { + tree.insert(1, -1) + assertEquals(tree.search(1), -1) + tree.assertIsBSTree() + assertEquals(tree.search(0), null) + tree.assertIsBSTree() + } + + @Test + @DisplayName("search children node") + public fun searchChildrenNode() { + tree.insert(2, -2) + tree.insert(1, -1) + tree.insert(3, -3) + assertEquals(tree.search(2), -2) + assertEquals(tree.search(1), -1) + assertEquals(tree.search(3), -3) + tree.assertIsBSTree() + } + + @ParameterizedTest + @MethodSource("sizeAndTime") + @DisplayName("search with size and time") + public fun searchWithSizeAndTime(size: Int, seconds: Long) { + val array = IntArray(size) + var index = 0 + repeat(size) { + val keyRandom = Random.nextInt(-1000, 1000) + array[index++] = keyRandom + tree.insert(keyRandom, keyRandom * (-1)) + } + + assertTimeout(Duration.ofSeconds(seconds)) { + repeat(10) { + val keyRandom = Random.nextInt(-1000, 1000) + if (keyRandom in array) + assertEquals(tree.search(keyRandom), (-1) * keyRandom) + else + assertEquals(tree.search(keyRandom), null) + } + } + + tree.assertIsBSTree() + } + + @Test + @DisplayName("search of default") + public fun searchOrDefault() { + assertEquals(tree.searchOrDefault(1, 0), 0) + tree.insert(1, -1) + assertEquals(tree.searchOrDefault(1, 0), -1) + } + + @Test + @DisplayName("contains") + public fun contains() { + assertEquals(tree.contains(1), false) + tree.insert(1, -1) + assertEquals(tree.contains(1), true) + } + + @Test + @DisplayName("get with brackets") + public fun get() { + assertEquals(tree[1], null) + tree[1] = -1 + assertEquals(tree[1], -1) + } + + @Test + @DisplayName("get maximum in subtree") + public fun getMaxInSubtree() { + assertEquals(tree.getMaxInSubtree(0), null) + tree.assertIsBSTree() + tree[2] = -2 + assertEquals(tree.getMaxInSubtree(2), Pair(2, -2)) + tree.assertIsBSTree() + tree[3] = -3 + tree[1] = -1 + assertEquals(tree.getMaxInSubtree(2), Pair(3, -3)) + tree.assertIsBSTree() + } + + @Test + @DisplayName("get maximum") + public fun getMax() { + assertEquals(tree.getMax(), null) + tree[2] = -2 + assertEquals(tree.getMax(), Pair(2, -2)) + tree[3] = -3 + tree[1] = -1 + assertEquals(tree.getMax(), Pair(3, -3)) + } + + @Test + @DisplayName("get minimum in subtree") + public fun getMinInSubtree() { + assertEquals(tree.getMinInSubtree(0), null) + tree.assertIsBSTree() + tree[2] = -2 + assertEquals(tree.getMinInSubtree(2), Pair(2, -2)) + tree.assertIsBSTree() + tree[1] = -1 + tree[3] = -3 + assertEquals(tree.getMinInSubtree(2), Pair(1, -1)) + tree.assertIsBSTree() + } + + @Test + @DisplayName("get minimum") + public fun getMin() { + assertEquals(tree.getMin(), null) + tree[2] = -2 + assertEquals(tree.getMin(), Pair(2, -2)) + tree[1] = -1 + tree[3] = -3 + assertEquals(tree.getMin(), Pair(1, -1)) + } + + @Test + @DisplayName("remove") + public fun remove() { + assertEquals(tree.remove(0), null) + tree.assertIsBSTree() + tree[2] = -2 + assertEquals(tree.remove(2), -2) + tree.assertIsBSTree() + tree[2] = -2 + tree[1] = -1 + assertEquals(tree.remove(2), -2) + tree.assertIsBSTree() + tree[2] = -2 + tree[3] = -3 + assertEquals(tree.remove(2), -2) + tree.assertIsBSTree() + assertEquals(tree.remove(3), -3) + tree.assertIsBSTree() + } + + @ParameterizedTest + @MethodSource("sizeAndTime") + @DisplayName("remove with size and time") + public fun removeWithSizeAndTime(size: Int, seconds: Long) { + val array = IntArray(size) + var index = 0 + repeat(size) { + val keyRandom = Random.nextInt(-1000, 1000) + array[index++] = keyRandom + tree[keyRandom] = (-1) * keyRandom + } + + assertTimeout(Duration.ofSeconds(seconds)) { + repeat(10) { + val keyRandom = Random.nextInt(-1000, 1000) + if (keyRandom in array) + assertEquals(tree.remove(keyRandom), (-1) * keyRandom) + else + assertEquals(tree.remove(keyRandom), null) + } + } + + tree.assertIsBSTree() + } + + @Test + @DisplayName("remove or default") + public fun removeOrDefault() { + assertEquals(tree.removeOrDefault(1, 0), 0) + tree.insert(1, -1) + assertEquals(tree.removeOrDefault(1, 0), -1) + } + + companion object { + @JvmStatic + fun sizeAndTime() = listOf( + Arguments.of(100, 1L), + Arguments.of(10000, 1L) + ) + } + +} diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt new file mode 100644 index 0000000..953e458 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -0,0 +1,489 @@ +package tree_tripper.binary_trees + +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import tree_tripper.binary_trees.assistants.RBTreeTestAssistant +import tree_tripper.nodes.binary_nodes.RBTreeNode + + +class RBTreeTest { + private lateinit var tree: RBTreeTestAssistant + + @BeforeEach + fun setup() { + tree = RBTreeTestAssistant() + } + + @Test + public fun testTreeInitializing() { + tree.assertRoot(null) { "Root of RBTree is not null by standard initialize." } + tree.assertIsRBTree() + Assertions.assertEquals(0, tree.getSize()) + } + + @Test + public fun testSimpleInsert() { + tree.insert(0, 0) + tree.assertRoot(RBTreeNode(0, 0, false)) { "Root of RBTree is not equal to inserted node." } + tree.assertIsRBTree() + Assertions.assertEquals(1, tree.getSize()) + } + + @Test + public fun testValueChangeInsert() { + tree.insert(0, 0) + tree.insert(0, 1) + tree.assertRoot(RBTreeNode(0, 1, false)) { "Root of RBTree is not equal to inserted node." } + } + + @ParameterizedTest + @MethodSource("testSortedInsertElementsCases") + public fun testSortedInsert(elements: List, expectedTreeView: RBTreeNode) { + val elementsSet = elements.toSet() + insert(elements.sorted()) + + tree.assertIsRBTree() + Assertions.assertEquals(elementsSet.size, tree.getSize()) + tree.assertRoot(expectedTreeView) { + "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}" + } + } + + @ParameterizedTest + @MethodSource("testReverseSortedInsertElementsCases") + public fun testReverseSortedInsert(elements: List, expectedTreeView: RBTreeNode) { + val elementsSet = elements.toSet() + insert(elements.sorted().reversed()) + + tree.assertIsRBTree() + Assertions.assertEquals(elementsSet.size, tree.getSize()) + tree.assertRoot(expectedTreeView) { + "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}" + } + } + + @ParameterizedTest + @MethodSource("testUnsortedInsertElementsCases") + public fun testUnsortedInsert(elements: List, expectedTreeView: RBTreeNode) { + val elementsSet = elements.toSet() + insert(elements) + + tree.assertIsRBTree() + Assertions.assertEquals(elementsSet.size, tree.getSize()) + tree.assertRoot(expectedTreeView) { + "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}\n" + "Tree view: ${tree.toStringWithTreeView()}" + } + } + + @ParameterizedTest + @MethodSource("testNodeColorCases") + public fun testNodeColor(expected: Boolean, node: RBTreeNode?) { + tree.assertNodeColor(expected, node) + } + + @ParameterizedTest + @MethodSource("testNodeLeftChildColorCases") + public fun testNodeLeftChildColor(expected: Boolean, node: RBTreeNode?) { + tree.assertNodeLeftChildColor(expected, node) + } + + @ParameterizedTest + @MethodSource("testNodeRotateLeftCases") + public fun testNodeRotateLeft(expected: RBTreeNode, node: RBTreeNode) { + tree.assertNodeLeftRotation(expected, node) + } + + @ParameterizedTest + @MethodSource("testNodeRotateRightCases") + public fun testNodeRotateRight(expected: RBTreeNode, node: RBTreeNode) { + tree.assertNodeRightRotation(expected, node) + } + + @ParameterizedTest + @MethodSource("testNodeColorFlipCases") + public fun testNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { + tree.assertNodeColorFlip(expected, node) + } + + @ParameterizedTest + @MethodSource("testNodeCreationCases") + public fun testNodeCreation(key: Int, value: Int) { + tree.assertNodeCreation(key, value) + } + + @ParameterizedTest + @MethodSource("testUpdateRootCases") + public fun testUpdateRoot(node: RBTreeNode?) { + tree.assertUpdateRoot(node) + } + + @ParameterizedTest + @MethodSource("testBalanceTreeCases") + public fun testBalanceTree(expectedNodeTreeView: RBTreeNode, nodeTreeView: RBTreeNode) { + tree.assertBalanceTree(expectedNodeTreeView, nodeTreeView) + } + + companion object { + + @JvmStatic + fun testSortedInsertElementsCases(): List = listOf( + Arguments.of( + listOf(10, 20, -10), + RBTreeNode(10,10, false, + RBTreeNode(-10, -10, false), + RBTreeNode(20, 20, false) + ) + ), + Arguments.of( + listOf(-10, -20, 10), + RBTreeNode(-10, -10, false, + RBTreeNode(-20, -20, false), + RBTreeNode(10, 10, false), + ) + ), + Arguments.of( + listOf(15, 34, -23, 20, 10, -100), + RBTreeNode(15, 15, false, + RBTreeNode(-23, -23, true, + RBTreeNode(-100, -100, false), + RBTreeNode(10, 10, false) + ), + RBTreeNode(34, 34, false, + RBTreeNode(20, 20, true), null + ) + ) + ), + ) + + @JvmStatic + fun testReverseSortedInsertElementsCases(): List = listOf( + Arguments.of( + listOf(10, 20, -10), + RBTreeNode(10,10, false, + RBTreeNode(-10, -10, false), + RBTreeNode(20, 20, false) + ) + ), + Arguments.of( + listOf(-10, -20, 10), + RBTreeNode(-10, -10, false, + RBTreeNode(-20, -20, false), + RBTreeNode(10, 10, false), + ) + ), + Arguments.of( + listOf(15, 34, -23, 20, 10, -100), + RBTreeNode(20, 20, false, + RBTreeNode(10, 10, true, + RBTreeNode(-23, -23, false, + RBTreeNode(-100, -100), + null + ), + RBTreeNode(15, 15, false) + ), + RBTreeNode(34, 34, false) + ) + ), + ) + + @JvmStatic + fun testUnsortedInsertElementsCases(): List = listOf( + Arguments.of( + listOf(10, 20, -10), + RBTreeNode(10,10, false, + RBTreeNode(-10, -10, false), + RBTreeNode(20, 20, false) + ) + ), + Arguments.of( + listOf(-10, -20, 10), + RBTreeNode(-10, -10, false, + RBTreeNode(-20, -20, false), + RBTreeNode(10, 10, false), + ) + ), + Arguments.of( + listOf(15, 34, -23, 20, 10, -100), + RBTreeNode(15, 15, false, + RBTreeNode(-23, -23, true, + RBTreeNode(-100, -100, false), + RBTreeNode(10, 10, false) + ), + RBTreeNode(34, 34, false, + RBTreeNode(20, 20, true), null + ) + ) + ), + ) + + @JvmStatic + fun testNodeColorCases(): List = listOf( + Arguments.of(false, null), + Arguments.of(true, RBTreeNode(0, 0, true)), + Arguments.of(false, RBTreeNode(0, 0, false)), + ) + + @JvmStatic + fun testNodeLeftChildColorCases(): List = listOf( + Arguments.of(false, null), + Arguments.of(false, RBTreeNode(0, 0, true)), + Arguments.of(false, RBTreeNode(0, 0, false)), + Arguments.of( + false, RBTreeNode( + 1, 1, true, null, RBTreeNode(0, 0, true) + ) + ), + Arguments.of( + false, RBTreeNode( + 1, 1, true, null, RBTreeNode(0, 0, false) + ) + ), + Arguments.of( + true, RBTreeNode( + 1, 1, true, RBTreeNode(0, 0, true), null + ) + ), + Arguments.of( + false, RBTreeNode( + 1, 1, true, RBTreeNode(0, 0, false), null + ) + ) + ) + + @JvmStatic + fun testNodeRotateLeftCases(): List = listOf( + Arguments.of( + RBTreeNode( + 0, 0, false, null, null + ), + RBTreeNode( + 0, 0, false, null, null + ), + ), Arguments.of( + RBTreeNode( + 1, 1, false, RBTreeNode(0, 0, true), null + ), + RBTreeNode( + 0, 0, false, null, RBTreeNode(1, 1, true) + ), + ), Arguments.of( + RBTreeNode( + 1, 1, true, RBTreeNode(0, 0, true), null + ), RBTreeNode( + 0, 0, true, null, RBTreeNode(1, 1, true) + ) + ), Arguments.of( + RBTreeNode( + 1, 1, false, RBTreeNode(0, 0, true), null + ), RBTreeNode( + 0, 0, false, null, RBTreeNode(1, 1, false) + ) + ) + ) + + @JvmStatic + fun testNodeRotateRightCases(): List = listOf( + Arguments.of( + RBTreeNode( + 0, 0, false, null, null + ), + RBTreeNode( + 0, 0, false, null, null + ), + ), + Arguments.of( + RBTreeNode( + 1, 1, false, null, RBTreeNode(0, 0, true) + ), + RBTreeNode( + 0, 0, false, RBTreeNode(1, 1, true), null + ), + ), + ) + + @JvmStatic + fun testNodeColorFlipCases(): List = listOf( + Arguments.of( + RBTreeNode( + 0, 0, false, null, null + ), + RBTreeNode( + 0, 0, true, null, null + ), + ), + Arguments.of( + RBTreeNode( + 0, 0, false, null, null + ), + RBTreeNode( + 0, 0, true, null, null + ), + ), + Arguments.of( + RBTreeNode( + 0, 0, false, RBTreeNode(1, 1, false), null + ), + RBTreeNode( + 0, 0, true, RBTreeNode(1, 1), null + ), + ), + Arguments.of( + RBTreeNode( + 0, 0, false, null, RBTreeNode(1, 1, true) + ), + RBTreeNode( + 0, 0, true, null, RBTreeNode(1, 1, false) + ), + ), + Arguments.of( + RBTreeNode( + 0, 0, false, RBTreeNode(2, 2, false), RBTreeNode(1, 1, true) + ), + RBTreeNode( + 0, 0, true, RBTreeNode(2, 2), RBTreeNode(1, 1, false) + ), + ), + ) + + @JvmStatic + fun testNodeCreationCases(): List = listOf( + Arguments.of(0, 0), + Arguments.of(1, 1), + Arguments.of(-1, -1), + ) + + @JvmStatic + fun testUpdateRootCases(): List = listOf( + Arguments.of(null), + Arguments.of(RBTreeNode(0, 0, true)), + Arguments.of(RBTreeNode(0, 0, false)), + ) + + @JvmStatic + fun testBalanceTreeCases(): List = listOf( + Arguments.of( + RBTreeNode(0, 0, false), RBTreeNode(0, 0, false) + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), null + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), null + ) + ), + Arguments.of( + RBTreeNode( + 1, 1, false, + RBTreeNode(0, 0, true), null + ), + RBTreeNode( + 0, 0, false, + null, RBTreeNode(1, 1, true) + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, true, + null, RBTreeNode(1, 1, false) + ), + RBTreeNode( + 0, 0, true, + null, RBTreeNode(1, 1, false) + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, false), RBTreeNode(1, 1, false) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, false), RBTreeNode(1, 1, false) + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), RBTreeNode(1, 1, false) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), RBTreeNode(1, 1, false) + ) + ), + Arguments.of( + RBTreeNode( + 1, 1, false, + RBTreeNode( + 0, 0, true, + RBTreeNode(-1, -1, false), null + ), + null + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, false), RBTreeNode(1, 1, true) + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, true, + RBTreeNode(-1, -1, false), RBTreeNode(1, 1, false) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), RBTreeNode(1, 1, true) + ) + ), + Arguments.of( + RBTreeNode( + -1, -1, true, + RBTreeNode(-2, -2, false), RBTreeNode(0, 0, false) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode( + -1, -1, true, RBTreeNode(-2, -2), null + ), null + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, true, + RBTreeNode( + -1, -1, false, + RBTreeNode(-2, -2), null + ), RBTreeNode(1, 1, false) + ), + RBTreeNode( + 0, 0, false, + RBTreeNode( + -1, -1, true, RBTreeNode(-2, -2), null + ), + RBTreeNode(1, 1) + ) + ), + ) + } + + private fun insert(elements: List) { + for (element in elements) { + tree[element] = element + } + } + + private fun nodeToStringTreeView(node: RBTreeNode): String { + val builder = StringBuilder() + node.toStringWithSubtreeView(0, builder) + return builder.toString() + } + +} diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt new file mode 100644 index 0000000..7e2cd8c --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/AVLTreeTestAssistant.kt @@ -0,0 +1,46 @@ +package tree_tripper.binary_trees.assistants + +import assertBinaryNodeDataEquals +import assertBinaryNodeDeepEquals +import tree_tripper.binary_trees.AVLTree +import tree_tripper.nodes.binary_nodes.AVLTreeNode + + +public class AVLTreeTestAssistant, V> : AVLTree() { + + fun assertRoot(node: AVLTreeNode?, lazyMassage: () -> String) { + try { + assertBinaryNodeDataEquals(root, node) {root, expected -> root.height == expected.height} + } catch (e: AssertionError) { + throw AssertionError(lazyMassage(), e) + } + } + + fun assertNodeCreation(key: K, value: V) { + assertBinaryNodeDeepEquals(createNode(key, value), AVLTreeNode(key, value)) {node1, node2 -> node1.height == node2.height} + } + + fun assertBalanceTree(expected: AVLTreeNode, node: AVLTreeNode) { + assertBinaryNodeDeepEquals(expected, balanceTree(node)) {node1, node2 -> node1.height == node2.height} + } + + fun assertBalanceFactor(expected: Int, node: AVLTreeNode?) { + val factor = balanceFactor(node) + assert(factor == expected) { + "Invalid height balance of $node, balance factor: $factor, expected: $expected." + } + } + + fun assertBalance(expected: AVLTreeNode, node: AVLTreeNode) { + assertBinaryNodeDeepEquals(expected, balance(node)) {node1, node2 -> node1.height == node2.height} + } + + fun assertNodeRightRotation(expected: AVLTreeNode, node: AVLTreeNode) { + assertBinaryNodeDeepEquals(expected, rotateRight(node)) {node1, node2 -> node1.height == node2.height} + } + + fun assertNodeLeftRotation(expected: AVLTreeNode, node: AVLTreeNode) { + assertBinaryNodeDeepEquals(expected, rotateLeft(node)) {node1, node2 -> node1.height == node2.height} + } + +} diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt new file mode 100644 index 0000000..2eb4b9d --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt @@ -0,0 +1,52 @@ +package tree_tripper.binary_trees.assistants + +import org.junit.jupiter.api.Assertions.assertEquals +import tree_tripper.binary_trees.BSTree +import tree_tripper.nodes.binary_nodes.BSTreeNode +import java.util.* + + +class BSTreeTestAssistant, V>: BSTree() { + + public fun assertRootInitialization() { + assertEquals(root, null, "Incorrect a root initialization") + } + + public fun assertWasCreatedNode(key: K, value: V) { + val node = createNode(key, value) + assertEquals(node.key, key, "Incorrect a key assignment") + assertEquals(node.value, value, "Incorrect a value assignment") + assertEquals(node.leftChild, null, "Incorrect a left child assignment") + assertEquals(node.rightChild, null, "Incorrect a right child assignment") + } + + public fun assertWasUpdatedRoot(key: K, value: V) { + val node = createNode(key, value) + updateRoot(node) + assertEquals(root, node, "Incorrect a root update") + } + + public fun assertWasBalancedTree(key: K, value: V) { + val node = createNode(key, value) + assertEquals(balanceTree(node), node, "Incorrect a tree balance") + } + + public fun assertIsBSTree() { + val queue: Queue> = LinkedList(listOfNotNull(root)) + + while (queue.isNotEmpty()) { + val node = queue.remove() + val nodeLeft = node.leftChild + if (nodeLeft != null) + assert(nodeLeft.key < node.key) + { "Incorrect the binary search tree structure: a left child key is no less than the parent key" } + val nodeRight = node.rightChild + if (nodeRight != null) + assert(nodeRight.key > node.key) + { "Incorrect the binary search tree structure: a left child key is no more than the parent key" } + + queue.addAll(node.getChildren()) + } + } + +} diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt new file mode 100644 index 0000000..567b106 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -0,0 +1,95 @@ +package tree_tripper.binary_trees.assistants + +import assertBinaryNodeDataEquals +import assertBinaryNodeDeepEquals +import org.junit.jupiter.api.Assertions +import tree_tripper.binary_trees.RBTree +import tree_tripper.nodes.binary_nodes.RBTreeNode +import tree_tripper.nodes.notNullNodeAction +import java.util.Queue +import java.util.LinkedList + + +public class RBTreeTestAssistant, V>: RBTree() { + + public fun assertIsRBTree() { + assert(!isRedColor(root)) {"Root of RBTree is red. Must be black."} + val queue: Queue> = LinkedList>(listOfNotNull(root)) + + while (queue.isNotEmpty()) { + val node: RBTreeNode = queue.remove() + val leftCompareResult: Int = notNullNodeAction( + node.leftChild, -1 + ) { leftChild -> leftChild.key.compareTo(node.key) } + val rightCompareResult: Int = notNullNodeAction( + node.rightChild, 1 + ) { rightChild -> rightChild.key.compareTo(node.key) } + + assert(leftCompareResult <= -1) { + "Left child of $node is not a BST node: keys compare result: $leftCompareResult" + } + assert(rightCompareResult >= 1) { + "Right child of $node is not a BST node: keys compare result: $rightCompareResult" + } + assert(!isRedColor(node.rightChild)) {"Right child of node at RBTree is red. Its must be black."} + if (isRedColor(node)) { + assert(!isRedLeftChild(node)) {"Left child of red node at RBTree is red. Its must be black."} + } + queue.addAll(node.getChildren()) + } + } + + public fun checkTree(checkAction: (RBTreeNode) -> Boolean): Boolean { + val queue: Queue> = LinkedList>(listOf(root)) + + while (queue.isNotEmpty()) { + val node: RBTreeNode = queue.remove() + if (!checkAction(node)) return false + queue.addAll(node.getChildren()) + } + return true + } + + fun assertRoot(node: RBTreeNode?, lazyMassage: () -> String) { + try { + assertBinaryNodeDataEquals(root, node) {rootNode, expectedNode -> rootNode.isRed == expectedNode.isRed} + } catch (e: AssertionError) { + throw AssertionError(lazyMassage(), e) + } + } + + fun assertNodeColor(expected: Boolean, node: RBTreeNode?) { + Assertions.assertEquals(expected, isRedColor(node)) + } + + fun assertNodeLeftChildColor(expected: Boolean, node: RBTreeNode?) { + Assertions.assertEquals(expected, isRedLeftChild(node)) + } + + fun assertNodeLeftRotation(expected: RBTreeNode, node: RBTreeNode) { + assertBinaryNodeDeepEquals(expected, rotateLeft(node)) {n1, n2 -> n1.isRed == n2.isRed} + } + + fun assertNodeRightRotation(expected: RBTreeNode, node: RBTreeNode) { + assertBinaryNodeDeepEquals(expected, rotateRight(node)) {n1, n2 -> n1.isRed == n2.isRed} + } + + fun assertNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { + flipColors(node) + assertBinaryNodeDeepEquals(expected, node) {n1, n2 -> n1.isRed == n2.isRed} + } + + fun assertNodeCreation(key: K, value: V) { + assertBinaryNodeDeepEquals(createNode(key, value), RBTreeNode(key, value)) { n1, n2 -> n1.isRed == n2.isRed} + } + + fun assertUpdateRoot(node: RBTreeNode?) { + updateRoot(node) + assertBinaryNodeDataEquals(root, if (node != null) RBTreeNode(node.key, node.value, false) else null) + } + + fun assertBalanceTree(expectedNodeTreeView: RBTreeNode, nodeTreeView: RBTreeNode) { + assertBinaryNodeDeepEquals(expectedNodeTreeView, balanceTree(nodeTreeView)) {n1, n2 -> n1.isRed == n2.isRed} + } + +} diff --git a/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt new file mode 100644 index 0000000..8ae0159 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt @@ -0,0 +1,40 @@ +package tree_tripper.nodes + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import tree_tripper.nodes.binary_nodes.BSTreeNode + + +public class UtilsTest { + + @ParameterizedTest + @MethodSource("testNodeUpdateCases") + public fun testNodeUpdate(expected: Boolean, node: BSTreeNode?) { + var isActivateAction: Boolean = false + notNullNodeUpdate(node) { isActivateAction = true } + Assertions.assertEquals(expected, isActivateAction) + } + + @ParameterizedTest + @MethodSource("testNodeActionCases") + public fun testNodeAction(expected: Boolean, node: BSTreeNode?) { + Assertions.assertEquals(expected, notNullNodeAction(node, false) { expected }) + } + + companion object { + @JvmStatic + fun testNodeUpdateCases(): List = listOf( + Arguments.of(false, null), + Arguments.of(true, BSTreeNode(0, 0)), + ) + + @JvmStatic + fun testNodeActionCases(): List = listOf( + Arguments.of(false, null), + Arguments.of(true, BSTreeNode(0, 0)), + ) + } + +} diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt new file mode 100644 index 0000000..8cb5a98 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt @@ -0,0 +1,77 @@ +package tree_tripper.nodes.binary_nodes + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + + +class AVLTreeNodeTest { + + @Test + public fun nodeInitializing() { + val node = AVLTreeNode(0, 0) + Assertions.assertEquals(1, node.height) {"The height is not 1 by standard initialize."} + } + + @ParameterizedTest + @MethodSource("testUpdateHeight") + public fun testUpdateHeight(expected: Int, node: AVLTreeNode) { + node.updateHeight() + Assertions.assertEquals(expected, node.height) {"The height does not match the expected."} + } + + companion object { + + @JvmStatic + fun testUpdateHeight(): List = listOf( + + Arguments.of(1, + AVLTreeNode(0, 0, 0, null, null) + ), + + Arguments.of(2, + AVLTreeNode( + 0, 0, 0, + AVLTreeNode(1, 1, 1, null, null), + null + ) + ), + + Arguments.of(2, + AVLTreeNode( + 0, 0, 0, + null, + AVLTreeNode(1, 1, 1, null, null) + ) + ), + + Arguments.of(2, + AVLTreeNode( + 0, 0, 0, + AVLTreeNode(1, 1, 1, null, null), + AVLTreeNode(2, 2, 1, null, null) + ) + ), + + Arguments.of(3, + AVLTreeNode( + 0, 0, 0, + AVLTreeNode(1, 1, 2, null, null), + AVLTreeNode(2, 2, 1, null, null) + ) + ), + + Arguments.of(3, + AVLTreeNode( + 0, 0, 0, + AVLTreeNode(1, 1, 1, null, null), + AVLTreeNode(2, 2, 2, null, null) + ) + ) + + ) + + } +} diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt new file mode 100644 index 0000000..8945751 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -0,0 +1,76 @@ +package tree_tripper.nodes.binary_nodes + +import assertBinaryNodeDeepEquals +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + + +public class RBTreeNodeTest { + + @ParameterizedTest + @MethodSource("testNodeSimpleInitializeCases") + public fun testNodeSimpleInitialize(key: Int, value: Int?) { + val node = RBTreeNode(key, value) + Assertions.assertEquals(key, node.key) { "Key of node is not equal." } + Assertions.assertEquals(value, node.value) { "Value of node is not equal." } + Assertions.assertTrue(node.isRed) { "Color of node is not red." } + Assertions.assertNull(node.leftChild) { "Left child of node is not null." } + Assertions.assertNull(node.rightChild) { "Right child of node is not null." } + } + + @ParameterizedTest + @MethodSource("testNodeColorTypeInitializeCases") + public fun testNodeColorTypeInitialize(isRed: Boolean) { + val node = RBTreeNode(0, 0, isRed) + Assertions.assertEquals(isRed, node.isRed) { "Color of node is not equal." } + Assertions.assertNull(node.leftChild) { "Left child of node is not null." } + Assertions.assertNull(node.rightChild) { "Right child of node is not null." } + } + + @ParameterizedTest + @MethodSource("testNodeFullInitializeCases") + public fun testNodeFullInitialize(leftChild: RBTreeNode?, rightChild: RBTreeNode?) { + val node = RBTreeNode(0, 0, false, leftChild, rightChild) + assertBinaryNodeDeepEquals(leftChild, node.leftChild) { n1, n2 -> n1.isRed == n2.isRed } + assertBinaryNodeDeepEquals(rightChild, node.rightChild) { n1, n2 -> n1.isRed == n2.isRed } + } + + @ParameterizedTest + @MethodSource("testToStringSimpleViewCases") + public fun testToStringSimpleView(expected: String, node: RBTreeNode) { + Assertions.assertEquals(expected, node.toStringSimpleView()) + } + + companion object { + @JvmStatic + fun testNodeSimpleInitializeCases(): List = listOf( + Arguments.of(-1, -1), + Arguments.of(0, 0), + Arguments.of(2, 2), + Arguments.of(3, null), + ) + + @JvmStatic + fun testNodeColorTypeInitializeCases(): List = listOf( + Arguments.of(false), + Arguments.of(true), + ) + + @JvmStatic + fun testNodeFullInitializeCases(): List = listOf( + Arguments.of(null, null), + Arguments.of(RBTreeNode(1, null), null), + Arguments.of(null, RBTreeNode(-1, null)), + Arguments.of(RBTreeNode(-1, null), RBTreeNode(-1, null)), + ) + + @JvmStatic + fun testToStringSimpleViewCases(): List = listOf( + Arguments.of("(-1, null) - RED", RBTreeNode(-1, null)), + Arguments.of("(0, 0) - BLACK", RBTreeNode(0, 0, false)), + ) + } + +} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..7de2ad7 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,15 @@ +/* + * 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. + * This project uses @Incubating APIs which are subject to change. + */ + +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 = "tree-trippers" +include("lib") From 967e7fdef1f83cf732365ce092b411bdbd7d498d Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 01:02:13 +0300 Subject: [PATCH 063/122] fix: rewrite remove node method at red-black tree. Add perent property to RBTreeNode class and update it by updating left and right children --- .../tree_tripper/binary_trees/RBTree.kt | 144 ++++++++---------- .../nodes/binary_nodes/AbstractBSTreeNode.kt | 4 +- .../nodes/binary_nodes/RBTreeNode.kt | 26 ++++ 3 files changed, 90 insertions(+), 84 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index acfe53e..b760fe2 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -41,36 +41,75 @@ public open class RBTree, V>: AbstractBSTree?, V?> val resultCompare: Int = key.compareTo(node.key) - var nodeCurrent: RBTreeNode = node + val nodeCurrent: RBTreeNode = node if (resultCompare < 0) { - if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) - nodeCurrent = moveRedLeft(nodeCurrent) - removeResult = removeNode(nodeCurrent.leftChild, key) nodeCurrent.leftChild = removeResult.first + } else if (resultCompare > 0) { + removeResult = removeNode(nodeCurrent.rightChild, key) + nodeCurrent.rightChild = removeResult.first } else { - if (isRedColor(nodeCurrent.leftChild)) - nodeCurrent = rotateRight(nodeCurrent) - if (resultCompare == 0 && nodeCurrent.rightChild == null) + val leftChild = nodeCurrent.leftChild + val rightChild = nodeCurrent.rightChild + if (leftChild == null && rightChild == null) { + if (isRedColor(nodeCurrent)) return Pair(null, nodeCurrent.value) + if (isRedColor(nodeCurrent.parent)) { + flipColors(nodeCurrent.parent) + } else { + val uncle = nodeCurrent.getUncle() + if (isRedColor(uncle)) { + flipColors(uncle) + nodeCurrent.parent?.flipColor() + } + uncle?.flipColor() + } return Pair(null, nodeCurrent.value) - if (!isRedColor(nodeCurrent.rightChild) && !isRedLeftChild(nodeCurrent.rightChild)) - nodeCurrent = moveRedRight(nodeCurrent) - if (resultCompare == 0) { - val nodeWithMinimalKey = getMinNodeInSubtree(nodeCurrent.rightChild) as RBTreeNode - val nodeSubstitutive: RBTreeNode = createNode(nodeWithMinimalKey.key, nodeWithMinimalKey.value) - nodeSubstitutive.isRed = nodeCurrent.isRed - nodeSubstitutive.leftChild = nodeCurrent.leftChild - nodeSubstitutive.rightChild = removeMinNode(nodeCurrent.rightChild) - return Pair(balanceTree(nodeSubstitutive), nodeCurrent.value) + } else if (leftChild == null) { + throw IllegalArgumentException( + "Invalid RedBlackTree state, node with one child as right is not valid rb-tree" + ) + } else if (rightChild == null) { + if (isRedColor(leftChild)) { + flipColors(nodeCurrent) + return Pair(leftChild, nodeCurrent.value) + } + throw IllegalArgumentException( + "Invalid RedBlackTree state, node with one child as left with black color is not valid rb-tree" + ) } else { - removeResult = removeNode(nodeCurrent.rightChild, key) - nodeCurrent.rightChild = removeResult.first + val newNode: RBTreeNode + val nodeCached: RBTreeNode + if (isRedColor(nodeCurrent)) { + nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode + newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCached.isRed) + newNode.leftChild = leftChild + newNode.rightChild = removeNode(rightChild, nodeCached.key).first + if (!isRedColor(nodeCached)) leftChild.flipColor() + } else { + if (isRedColor(leftChild)) { + nodeCached = getMaxNodeInSubtree(leftChild) as RBTreeNode + newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCurrent.isRed) + newNode.rightChild = rightChild + newNode.leftChild = removeNode(leftChild, nodeCached.key).first + return Pair(balanceTree(newNode), nodeCurrent.value) + } + nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode + newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCurrent.isRed) + newNode.leftChild = leftChild + newNode.rightChild = removeNode(rightChild, nodeCached.key).first + if (!isRedColor(nodeCached)) { + leftChild.isRed = true + newNode.isRed = true + } + } + return Pair(balanceTree(newNode), nodeCurrent.value) } } return Pair(balanceTree(nodeCurrent), removeResult.second) } + /** * Returns whether the specified node is red or not. * @@ -132,70 +171,11 @@ public open class RBTree, V>: AbstractBSTree): Unit { - node.isRed = !node.isRed - notNullNodeUpdate(node.leftChild) { child -> child.isRed = !child.isRed } - notNullNodeUpdate(node.rightChild) { child -> child.isRed = !child.isRed } - } - - /** - * This function is used to move a red node to the right, if it has a red left child of its [node] left child. - * It first flips the colors of the node and its children, then rotates the tree if the left child is also red. - * - * @param node the node to move - * @return the new root of the tree, which is balanced node subtree - */ - private fun moveRedRight(node: RBTreeNode): RBTreeNode { - var nodeCurrent: RBTreeNode = node - - flipColors(nodeCurrent) - if (isRedLeftChild(nodeCurrent.leftChild)) { - nodeCurrent = rotateRight(nodeCurrent) - flipColors(nodeCurrent) - } - return nodeCurrent - } - - /** - * This function is used to move a red node to the left, if it has a red right child of its [node] left child. - * It first flips the colors of the node and its children, then rotates the tree if the left child is also red. - * - * @param node the node to move - * @return the new root of the tree, which is balanced node subtree - */ - private fun moveRedLeft(node: RBTreeNode): RBTreeNode { - var nodeCurrent: RBTreeNode = node - - flipColors(nodeCurrent) - if (isRedLeftChild(nodeCurrent.rightChild)) { - nodeCurrent.rightChild = notNullNodeAction( - node.rightChild, null - ) {rightChild -> rotateRight(rightChild)} - nodeCurrent = rotateLeft(nodeCurrent) - flipColors(nodeCurrent) - } - return nodeCurrent - } - - /** - * Removes the node with the minimum key from the binary search tree. - * - * @param node the root of the binary search tree - * @return the root of the binary search tree with the node removed, or `null` if the tree is empty - */ - private fun removeMinNode(node: RBTreeNode?): RBTreeNode? { - if (node == null) return null - if (node.leftChild == null) return node.rightChild - - var nodeCurrent: RBTreeNode = node - if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) - nodeCurrent = moveRedLeft(nodeCurrent) - - nodeCurrent.leftChild = notNullNodeAction( - nodeCurrent.leftChild, null - ) {child -> removeMinNode(child)} - - return balanceTree(nodeCurrent) + protected fun flipColors(node: RBTreeNode?): Unit { + if (node == null) return + node.flipColor() + notNullNodeUpdate(node.leftChild) { child -> child.flipColor() } + notNullNodeUpdate(node.rightChild) { child -> child.flipColor() } } } diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt index 0a900eb..9803ff8 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -16,8 +16,8 @@ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeN public val key: K, public var value: V ): SearchTreeNode { - public var leftChild: N? = null - public var rightChild: N? = null + public open var leftChild: N? = null + public open var rightChild: N? = null override fun getChildren(): List { return listOfNotNull(leftChild, rightChild) diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt index 47ecd01..aef89d1 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt @@ -11,8 +11,23 @@ public class RBTreeNode, V>( key: K, value: V ) : AbstractBSTreeNode>(key, value) { + var parent: RBTreeNode? = null var isRed: Boolean = true + override var leftChild: RBTreeNode? + get() = super.leftChild + set(value) { + if (value != null) value.parent = this + super.leftChild = value + } + + override var rightChild: RBTreeNode? + get() = super.rightChild + set(value) { + if (value != null) value.parent = this + super.rightChild = value + } + public constructor(key: K, value: V, isRed: Boolean) : this(key, value) { this.isRed = isRed } @@ -26,6 +41,17 @@ public class RBTreeNode, V>( this.rightChild = rightChild } + public fun getUncle(): RBTreeNode? { + val parent = this.parent + if (parent == null) return null + if (parent.leftChild === this) return parent.rightChild + return parent.leftChild + } + + public fun flipColor(): Unit { + isRed = !isRed + } + override fun toStringSimpleView(): String { return "${super.toStringSimpleView()} - ${colorName()}" } From 0cf42f4cdb69ac3c451c7f03c416c6bb771b3a8f Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 01:07:12 +0300 Subject: [PATCH 064/122] fix: update CI script to valid run of gradle build and run test tasks --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index a982c9f..a216aff 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -16,4 +16,4 @@ jobs: - uses: actions/checkout@v3 - name: Build source code and run test with jacoco - run: ./gradlew :test + run: ./gradlew test From 67a0c619c837677ed7487dca547804432fae2af4 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 01:11:35 +0300 Subject: [PATCH 065/122] fix: remove unnecessary insertion tests at red-black tree --- .../tree_tripper/binary_trees/RBTreeTest.kt | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 953e458..4781a23 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -25,61 +25,6 @@ class RBTreeTest { Assertions.assertEquals(0, tree.getSize()) } - @Test - public fun testSimpleInsert() { - tree.insert(0, 0) - tree.assertRoot(RBTreeNode(0, 0, false)) { "Root of RBTree is not equal to inserted node." } - tree.assertIsRBTree() - Assertions.assertEquals(1, tree.getSize()) - } - - @Test - public fun testValueChangeInsert() { - tree.insert(0, 0) - tree.insert(0, 1) - tree.assertRoot(RBTreeNode(0, 1, false)) { "Root of RBTree is not equal to inserted node." } - } - - @ParameterizedTest - @MethodSource("testSortedInsertElementsCases") - public fun testSortedInsert(elements: List, expectedTreeView: RBTreeNode) { - val elementsSet = elements.toSet() - insert(elements.sorted()) - - tree.assertIsRBTree() - Assertions.assertEquals(elementsSet.size, tree.getSize()) - tree.assertRoot(expectedTreeView) { - "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}" - } - } - - @ParameterizedTest - @MethodSource("testReverseSortedInsertElementsCases") - public fun testReverseSortedInsert(elements: List, expectedTreeView: RBTreeNode) { - val elementsSet = elements.toSet() - insert(elements.sorted().reversed()) - - tree.assertIsRBTree() - Assertions.assertEquals(elementsSet.size, tree.getSize()) - tree.assertRoot(expectedTreeView) { - "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}" - } - } - - @ParameterizedTest - @MethodSource("testUnsortedInsertElementsCases") - public fun testUnsortedInsert(elements: List, expectedTreeView: RBTreeNode) { - val elementsSet = elements.toSet() - insert(elements) - - tree.assertIsRBTree() - Assertions.assertEquals(elementsSet.size, tree.getSize()) - tree.assertRoot(expectedTreeView) { - "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}\n" - "Tree view: ${tree.toStringWithTreeView()}" - } - } - @ParameterizedTest @MethodSource("testNodeColorCases") public fun testNodeColor(expected: Boolean, node: RBTreeNode?) { From e43ea99fd31a7ed0ce62dceb60292760f55b17f6 Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Mon, 1 Apr 2024 11:50:28 +0300 Subject: [PATCH 066/122] test(BSTree): add 'test' prefix to names of BSTree test methods. Add test methods for BSTreeNode in BSTreeNodeTest --- .../tree_tripper/binary_trees/BSTreeTest.kt | 74 ++++++++++++------- .../assistants/BSTreeTestAssistant.kt | 2 +- .../nodes/binary_nodes/BSTreeNodeTest.kt | 74 +++++++++++++++++++ 3 files changed, 122 insertions(+), 28 deletions(-) create mode 100644 lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index 23694db..bfc22b9 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -23,35 +23,36 @@ public class BSTreeTest { @Test @DisplayName("tree initialization") - public fun treeInitialization() { + public fun testTreeInitialization() { tree.assertRootInitialization() assertEquals(tree.getSize(), 0, "Incorrect a tree initialization") } @Test @DisplayName("create node") - public fun createNode() { + public fun testCreateNode() { tree.assertWasCreatedNode(1, -1) } @Test @DisplayName("update root") - public fun updateRoot() { + public fun testUpdateRoot() { tree.assertWasUpdatedRoot(1, -1) } @Test @DisplayName("balance tree") - public fun balanceTree() { + public fun testBalanceTree() { tree.assertWasBalancedTree(1, -1) } @Test @DisplayName("insert root") - public fun insertRoot() { + public fun testInsertRoot() { tree.insert(1, -1) tree.assertIsBSTree() assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + tree.insert(1, 0) tree.assertIsBSTree() assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") @@ -59,7 +60,7 @@ public class BSTreeTest { @Test @DisplayName("insert children root") - public fun insertChildrenRoot() { + public fun testInsertChildrenRoot() { tree.insert(2, -2) tree.insert(1, -1) tree.insert(3, -3) @@ -69,7 +70,7 @@ public class BSTreeTest { @Test @DisplayName("insert 5 elements") - public fun insertElements() { + public fun testInsertElements() { tree.insert(4, -4) tree.insert(5, -5) tree.insert(3, -3) @@ -80,9 +81,9 @@ public class BSTreeTest { } @ParameterizedTest - @MethodSource("sizeAndTime") + @MethodSource("getSizeAndTimeArguments") @DisplayName("insert with size and time") - public fun insertWithSizeAndTime(size: Int, seconds: Long) { + public fun testInsertWithSizeAndTime(size: Int, seconds: Long) { assertTimeout(Duration.ofSeconds(seconds)) { repeat(size) { val keyRandom = Random.nextInt(-1000, 1000) @@ -95,34 +96,39 @@ public class BSTreeTest { @Test @DisplayName("insert if absent root") - public fun insertIfAbsentRoot() { + public fun testInsertIfAbsentRoot() { assertEquals(tree.insertIfAbsent(1, -1), true) tree.assertIsBSTree() + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + assertEquals(tree.insertIfAbsent(1, 1), false) + tree.assertIsBSTree() assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") } @Test @DisplayName("set with brackets") - public fun set() { + public fun testSet() { assertEquals(tree.search(1), null) + tree[1] = -1 assertEquals(tree.search(1), -1) } @Test @DisplayName("search node") - public fun searchNode() { + public fun testSearchNode() { tree.insert(1, -1) assertEquals(tree.search(1), -1) tree.assertIsBSTree() + assertEquals(tree.search(0), null) tree.assertIsBSTree() } @Test @DisplayName("search children node") - public fun searchChildrenNode() { + public fun testSearchChildrenNode() { tree.insert(2, -2) tree.insert(1, -1) tree.insert(3, -3) @@ -133,9 +139,9 @@ public class BSTreeTest { } @ParameterizedTest - @MethodSource("sizeAndTime") + @MethodSource("getSizeAndTimeArguments") @DisplayName("search with size and time") - public fun searchWithSizeAndTime(size: Int, seconds: Long) { + public fun testSearchWithSizeAndTime(size: Int, seconds: Long) { val array = IntArray(size) var index = 0 repeat(size) { @@ -159,36 +165,41 @@ public class BSTreeTest { @Test @DisplayName("search of default") - public fun searchOrDefault() { + public fun testSearchOrDefault() { assertEquals(tree.searchOrDefault(1, 0), 0) + tree.insert(1, -1) assertEquals(tree.searchOrDefault(1, 0), -1) } @Test @DisplayName("contains") - public fun contains() { + public fun testContains() { assertEquals(tree.contains(1), false) + tree.insert(1, -1) assertEquals(tree.contains(1), true) } @Test @DisplayName("get with brackets") - public fun get() { + public fun testGet() { assertEquals(tree[1], null) + tree[1] = -1 assertEquals(tree[1], -1) } @Test @DisplayName("get maximum in subtree") - public fun getMaxInSubtree() { + public fun testGetMaxInSubtree() { assertEquals(tree.getMaxInSubtree(0), null) tree.assertIsBSTree() + tree[2] = -2 assertEquals(tree.getMaxInSubtree(2), Pair(2, -2)) tree.assertIsBSTree() + tree[3] = -3 tree[1] = -1 assertEquals(tree.getMaxInSubtree(2), Pair(3, -3)) @@ -197,10 +208,12 @@ public class BSTreeTest { @Test @DisplayName("get maximum") - public fun getMax() { + public fun testGetMax() { assertEquals(tree.getMax(), null) + tree[2] = -2 assertEquals(tree.getMax(), Pair(2, -2)) + tree[3] = -3 tree[1] = -1 assertEquals(tree.getMax(), Pair(3, -3)) @@ -208,12 +221,14 @@ public class BSTreeTest { @Test @DisplayName("get minimum in subtree") - public fun getMinInSubtree() { + public fun testGetMinInSubtree() { assertEquals(tree.getMinInSubtree(0), null) tree.assertIsBSTree() + tree[2] = -2 assertEquals(tree.getMinInSubtree(2), Pair(2, -2)) tree.assertIsBSTree() + tree[1] = -1 tree[3] = -3 assertEquals(tree.getMinInSubtree(2), Pair(1, -1)) @@ -222,10 +237,12 @@ public class BSTreeTest { @Test @DisplayName("get minimum") - public fun getMin() { + public fun testGetMin() { assertEquals(tree.getMin(), null) + tree[2] = -2 assertEquals(tree.getMin(), Pair(2, -2)) + tree[1] = -1 tree[3] = -3 assertEquals(tree.getMin(), Pair(1, -1)) @@ -233,16 +250,19 @@ public class BSTreeTest { @Test @DisplayName("remove") - public fun remove() { + public fun testRemove() { assertEquals(tree.remove(0), null) tree.assertIsBSTree() + tree[2] = -2 assertEquals(tree.remove(2), -2) tree.assertIsBSTree() + tree[2] = -2 tree[1] = -1 assertEquals(tree.remove(2), -2) tree.assertIsBSTree() + tree[2] = -2 tree[3] = -3 assertEquals(tree.remove(2), -2) @@ -252,9 +272,9 @@ public class BSTreeTest { } @ParameterizedTest - @MethodSource("sizeAndTime") + @MethodSource("getSizeAndTimeArguments") @DisplayName("remove with size and time") - public fun removeWithSizeAndTime(size: Int, seconds: Long) { + public fun testRemoveWithSizeAndTime(size: Int, seconds: Long) { val array = IntArray(size) var index = 0 repeat(size) { @@ -278,7 +298,7 @@ public class BSTreeTest { @Test @DisplayName("remove or default") - public fun removeOrDefault() { + public fun testRemoveOrDefault() { assertEquals(tree.removeOrDefault(1, 0), 0) tree.insert(1, -1) assertEquals(tree.removeOrDefault(1, 0), -1) @@ -286,7 +306,7 @@ public class BSTreeTest { companion object { @JvmStatic - fun sizeAndTime() = listOf( + fun getSizeAndTimeArguments() = listOf( Arguments.of(100, 1L), Arguments.of(10000, 1L) ) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt index 2eb4b9d..2e575d2 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt @@ -6,7 +6,7 @@ import tree_tripper.nodes.binary_nodes.BSTreeNode import java.util.* -class BSTreeTestAssistant, V>: BSTree() { +public class BSTreeTestAssistant, V>: BSTree() { public fun assertRootInitialization() { assertEquals(root, null, "Incorrect a root initialization") diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt new file mode 100644 index 0000000..52c8193 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt @@ -0,0 +1,74 @@ +package tree_tripper.nodes.binary_nodes + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import tree_tripper.nodes.notNullNodeAction +import kotlin.test.assertContains +import kotlin.test.assertEquals + + +public class BSTreeNodeTest { + + @Test + @DisplayName("node initialization") + public fun testNodeInitialization() { + val node = BSTreeNode(1, -1) + assertEquals(node.key, 1, "Incorrect a key assignment") + assertEquals(node.value, -1, "Incorrect a value assignment") + assertEquals(node.leftChild, null, "Incorrect a left child assignment") + assertEquals(node.rightChild, null, "Incorrect a right child assignment") + } + + @Test + @DisplayName("get children") + public fun testGetChildren() { + val node = BSTreeNode(2, -2) + assertEquals(node.getChildren(), listOf()) + val nodeLeft = BSTreeNode(1, -1) + node.leftChild = nodeLeft + assertEquals(node.getChildren(), listOf(nodeLeft)) + val nodeRight = BSTreeNode(3, -3) + node.rightChild = nodeRight + assertEquals(node.getChildren(), listOf(nodeLeft, nodeRight)) + } + + @Test + @DisplayName("to string") + public fun testToString() { + val node = BSTreeNode(1, -1) + assertEquals(node.toString(), "BSTreeNode(key=1, value=-1)") + } + + @Test + @DisplayName("to string simple view") + public fun testToStringSimpleView() { + val node = BSTreeNode(1, -1) + assertEquals(node.toStringSimpleView(), "(1, -1)") + } + + @Test + @DisplayName("node to string with subtree view") + public fun testNodeToStringWithSubtreeView() { + val builder = StringBuilder() + val node = BSTreeNode(1, -1) + node.toStringWithSubtreeView(0, builder) + assertEquals(builder.toString(), "${node.toStringSimpleView()}\n") + } + + @Test + @DisplayName("node with children to string with subtree view") + public fun testNodeWithChildrenToStringWithSubtreeView() { + val builder = StringBuilder() + val node = BSTreeNode(2, -2) + val nodeLeft = BSTreeNode(1, -1) + node.leftChild = nodeLeft + val nodeRight = BSTreeNode(3, -3) + node.rightChild = nodeRight + node.toStringWithSubtreeView(0, builder) + assertEquals(builder.toString(), + "\t${nodeRight.toStringSimpleView()}\n" + + "${node.toStringSimpleView()}\n" + + "\t${nodeLeft.toStringSimpleView()}\n") + } + +} \ No newline at end of file From 9cf737ff8f63dd7182fa354e23b1315a197f4a99 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 13:47:42 +0300 Subject: [PATCH 067/122] refactor: update null parent value check to getting uncle at RBTreeNode --- .../kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt index 6586e86..2115276 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt @@ -42,8 +42,7 @@ public class RBTreeNode, V>( } public fun getUncle(): RBTreeNode? { - val parent = this.parent - if (parent == null) return null + val parent = this.parent ?: return null if (parent.leftChild === this) return parent.rightChild return parent.leftChild } @@ -51,7 +50,7 @@ public class RBTreeNode, V>( public fun flipColor(): Unit { isRed = !isRed } - + override fun toStringSimpleView(): String { return "${super.toStringSimpleView()} - ${colorName()}" } From c49bd8b7032e097645f1f981d832eb9bd00ad0b0 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 13:50:43 +0300 Subject: [PATCH 068/122] test: update initialized tests to assert parent as null value and assert at full init node test check updated parent of left and right child --- .../nodes/binary_nodes/RBTreeNodeTest.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt index 8945751..98f481e 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -2,9 +2,12 @@ package tree_tripper.nodes.binary_nodes import assertBinaryNodeDeepEquals import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource +import tree_tripper.nodes.notNullNodeUpdate +import kotlin.test.assertEquals public class RBTreeNodeTest { @@ -16,6 +19,7 @@ public class RBTreeNodeTest { Assertions.assertEquals(key, node.key) { "Key of node is not equal." } Assertions.assertEquals(value, node.value) { "Value of node is not equal." } Assertions.assertTrue(node.isRed) { "Color of node is not red." } + Assertions.assertNull(node.parent) { "Parent of node is not null." } Assertions.assertNull(node.leftChild) { "Left child of node is not null." } Assertions.assertNull(node.rightChild) { "Right child of node is not null." } } @@ -25,6 +29,7 @@ public class RBTreeNodeTest { public fun testNodeColorTypeInitialize(isRed: Boolean) { val node = RBTreeNode(0, 0, isRed) Assertions.assertEquals(isRed, node.isRed) { "Color of node is not equal." } + Assertions.assertNull(node.parent) { "Parent of node is not null." } Assertions.assertNull(node.leftChild) { "Left child of node is not null." } Assertions.assertNull(node.rightChild) { "Right child of node is not null." } } @@ -33,8 +38,13 @@ public class RBTreeNodeTest { @MethodSource("testNodeFullInitializeCases") public fun testNodeFullInitialize(leftChild: RBTreeNode?, rightChild: RBTreeNode?) { val node = RBTreeNode(0, 0, false, leftChild, rightChild) - assertBinaryNodeDeepEquals(leftChild, node.leftChild) { n1, n2 -> n1.isRed == n2.isRed } - assertBinaryNodeDeepEquals(rightChild, node.rightChild) { n1, n2 -> n1.isRed == n2.isRed } + Assertions.assertNull(node.parent) { "Parent of node is not null." } + assertBinaryNodeDeepEquals(leftChild, node.leftChild) { n1, n2 -> + n1.isRed == n2.isRed && n2.parent === node + } + assertBinaryNodeDeepEquals(rightChild, node.rightChild) { n1, n2 -> + n1.isRed == n2.isRed && n2.parent === node + } } @ParameterizedTest From 23c344fee91391643b523a590095b2a9a1fb8474 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 13:52:49 +0300 Subject: [PATCH 069/122] test: add tests to assert parent setter and tests of uncle getter --- .../nodes/binary_nodes/RBTreeNodeTest.kt | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt index 98f481e..0bbc784 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -47,12 +47,45 @@ public class RBTreeNodeTest { } } + @Test + public fun testParentSetter() { + val node = RBTreeNode(0, 0) + Assertions.assertNull(node.parent) + + val parent = RBTreeNode(1, 1, false) + node.parent = parent + assertBinaryNodeDeepEquals(parent, node.parent) + } + @ParameterizedTest @MethodSource("testToStringSimpleViewCases") public fun testToStringSimpleView(expected: String, node: RBTreeNode) { Assertions.assertEquals(expected, node.toStringSimpleView()) } + @ParameterizedTest + @MethodSource("testGetUncleCases") + public fun testGetUncle(node: RBTreeNode) { + Assertions.assertNull(node.getUncle()) + notNullNodeUpdate(node.leftChild) { leftChild -> + Assertions.assertEquals( + node.rightChild, leftChild.getUncle() + ) + } + notNullNodeUpdate(node.rightChild) { rightChild -> + Assertions.assertEquals( + node.leftChild, rightChild.getUncle() + ) + } + } + + @ParameterizedTest + @MethodSource("testFlipColorCases") + public fun testFlipColor(node: RBTreeNode, expected: Boolean) { + node.flipColor() + assertEquals(expected, node.isRed) + } + companion object { @JvmStatic fun testNodeSimpleInitializeCases(): List = listOf( @@ -81,6 +114,46 @@ public class RBTreeNodeTest { Arguments.of("(-1, null) - RED", RBTreeNode(-1, null)), Arguments.of("(0, 0) - BLACK", RBTreeNode(0, 0, false)), ) + + @JvmStatic + fun testGetUncleCases(): List = listOf( + Arguments.of( + RBTreeNode(0, 0) + ), + Arguments.of( + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), + null + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, true, + null, + RBTreeNode(1, 1, false), + ) + ), + Arguments.of( + RBTreeNode( + 0, 0, true, + RBTreeNode(-1, -1, false), + RBTreeNode(1, 1, false), + ) + ) + ) + + @JvmStatic + fun testFlipColorCases(): List = listOf( + Arguments.of( + RBTreeNode(0, 0, true), + false + ), + Arguments.of( + RBTreeNode(0, 0, false), + true + ) + ) } } From 0d56925dd79736293325f13ca1325f1fdfebe1b1 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 13:54:32 +0300 Subject: [PATCH 070/122] refactor: add new constructor of simple BSTreeNode by adding params as left and right child --- .../tree_tripper/nodes/binary_nodes/BSTreeNode.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt index 4f2cf7c..601102b 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNode.kt @@ -7,7 +7,16 @@ package tree_tripper.nodes.binary_nodes * @param K the key type of the node, supporting the [Comparable] interface * @param V the value type of the node */ -public class BSTreeNode, V>( +public class BSTreeNode, V>( key: K, value: V -): AbstractBSTreeNode>(key, value) +) : AbstractBSTreeNode>(key, value) { + + public constructor(key: K, value: V, leftChild: BSTreeNode?, rightChild: BSTreeNode?) : this( + key, + value + ) { + this.leftChild = leftChild + this.rightChild = rightChild + } +} From 09a742389534f72598555f9f8e17689a388ff361 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 13:55:37 +0300 Subject: [PATCH 071/122] refactor: union two secondary constructor of BinarySearchTreeIterator to primary --- .../iterators/BinarySearchTreeIterator.kt | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt b/lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt index ea40851..8fc15da 100644 --- a/lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt +++ b/lib/src/main/kotlin/tree_tripper/iterators/BinarySearchTreeIterator.kt @@ -12,23 +12,13 @@ import java.util.Queue * @param V the type of the values in the binary search tree * @param N the type of the nodes in the binary search tree */ -class BinarySearchTreeIterator, V, N: AbstractBSTreeNode>: Iterator> { +class BinarySearchTreeIterator, V, N : AbstractBSTreeNode>( + root: N?, + order: IterationOrders = IterationOrders.WIDTH_ORDER +) : Iterator> { private val iterationState: IterationState - /** - * Constructs a binary search tree iterator with the given root node and the default iteration order, which is [IterationOrders.WIDTH_ORDER]. - * - * @param root the root node of the binary search tree - */ - constructor(root: N?): this(root, IterationOrders.WIDTH_ORDER) - - /** - * Constructs a binary search tree iterator with the given root node and the given iteration order. - * - * @param root the root node of the binary search tree - * @param order the iteration order - */ - constructor(root: N?, order: IterationOrders) { + init { iterationState = when (order) { IterationOrders.WIDTH_ORDER -> WidthIterationState(root) IterationOrders.INCREASING_ORDER -> IncreasingIterationState(root) @@ -47,7 +37,7 @@ class BinarySearchTreeIterator, V, N: AbstractBSTreeNode, V, N: AbstractBSTreeNode> { + private interface IterationState, V, N : AbstractBSTreeNode> { /** * Returns `true` if the iteration has more elements. @@ -65,9 +55,9 @@ class BinarySearchTreeIterator, V, N: AbstractBSTreeNode, V, N: AbstractBSTreeNode>( + private class WidthIterationState, V, N : AbstractBSTreeNode>( root: N? - ): IterationState { + ) : IterationState { private val queue: Queue = LinkedList() init { @@ -92,9 +82,9 @@ class BinarySearchTreeIterator, V, N: AbstractBSTreeNode, V, N: AbstractBSTreeNode>( + private class IncreasingIterationState, V, N : AbstractBSTreeNode>( root: N? - ): IterationState { + ) : IterationState { private val unprocessedNodesStack = LinkedList() private val semiProcessedNodesStack = LinkedList() @@ -142,9 +132,9 @@ class BinarySearchTreeIterator, V, N: AbstractBSTreeNode, V, N: AbstractBSTreeNode>( + private class DecreasingIterationState, V, N : AbstractBSTreeNode>( root: N? - ): IterationState { + ) : IterationState { private val unprocessedNodesStack = LinkedList() private val semiProcessedNodesStack = LinkedList() From 5ccfab63c63df86f6a759da22a626632bbdb8710 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 13:56:57 +0300 Subject: [PATCH 072/122] test: add tests to iterators and all orders --- .../iterators/BinarySearchTreeIteratorTest.kt | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt diff --git a/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt b/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt new file mode 100644 index 0000000..2480d99 --- /dev/null +++ b/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt @@ -0,0 +1,131 @@ +package tree_tripper.iterators + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import tree_tripper.nodes.binary_nodes.BSTreeNode + + +class BinarySearchTreeIteratorTest { + lateinit var iterator: BinarySearchTreeIterator> + + @ParameterizedTest + @MethodSource("testIteratorCases") + public fun testWidthOrderIterator(expected: List, root: BSTreeNode) { + iterator = BinarySearchTreeIterator(root) + var index: Int = 0 + + while (iterator.hasNext()) { + Assertions.assertEquals(iterator.next(), Pair(expected[index], expected[index])) + index++ + } + } + + @ParameterizedTest + @MethodSource("testGetALotOfElementsCases") + public fun testGetALotOfElements(order: IterationOrders) { + iterator = BinarySearchTreeIterator(null, order) + Assertions.assertFalse(iterator.hasNext()) + Assertions.assertThrows(NoSuchElementException::class.java) { iterator.next() } + } + + @ParameterizedTest + @MethodSource("testIteratorCases") + public fun testIncreasingOrderIterator(expected: List, root: BSTreeNode) { + iterator = BinarySearchTreeIterator(root, IterationOrders.INCREASING_ORDER) + val sortedElements = expected.sorted() + var index: Int = 0 + + while (iterator.hasNext()) { + Assertions.assertEquals(iterator.next(), Pair(sortedElements[index], sortedElements[index])) + index++ + } + } + + @ParameterizedTest + @MethodSource("testIteratorCases") + public fun testDecreasingOrderIterator(expected: List, root: BSTreeNode) { + iterator = BinarySearchTreeIterator(root, IterationOrders.DECREASING_ORDER) + val sortedElements = expected.sorted().reversed() + var index: Int = 0 + + while (iterator.hasNext()) { + Assertions.assertEquals(iterator.next(), Pair(sortedElements[index], sortedElements[index])) + index++ + } + } + + companion object { + + @JvmStatic + fun testGetALotOfElementsCases(): List = listOf( + Arguments.of(IterationOrders.WIDTH_ORDER), + Arguments.of(IterationOrders.INCREASING_ORDER), + Arguments.of(IterationOrders.DECREASING_ORDER), + ) + + @JvmStatic + fun testIteratorCases(): List = listOf( + Arguments.of(listOf(0), BSTreeNode(0, 0)), + Arguments.of( + listOf(0, -5, 5), + BSTreeNode( + 0, 0, + BSTreeNode(-5, -5), + BSTreeNode(5, 5), + ) + ), + Arguments.of( + listOf(0, -5, 5, -10), + BSTreeNode( + 0, 0, + BSTreeNode( + -5, -5, + BSTreeNode(-10, -10), + null, + ), + BSTreeNode(5, 5), + ) + ), + Arguments.of( + listOf(0, -5, 5, -10, 10), + BSTreeNode( + 0, 0, + BSTreeNode( + -5, -5, + BSTreeNode(-10, -10), + null, + ), + BSTreeNode( + 5, 5, + null, + BSTreeNode(10, 10), + ) + ) + ), + Arguments.of( + listOf(0, -5, 5, -10, -3, 3, 10, 4), + BSTreeNode( + 0, 0, + BSTreeNode( + -5, -5, + BSTreeNode(-10, -10), + BSTreeNode(-3, -3), + ), + BSTreeNode( + 5, 5, + BSTreeNode( + 3, 3, + null, + BSTreeNode(4, 4) + ), + BSTreeNode(10, 10), + ) + ) + ) + ) + + } + +} From 26cb79a9deaca5e379760db19e99c696b269ef36 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Mon, 1 Apr 2024 14:30:33 +0300 Subject: [PATCH 073/122] refactor: add @DisplayName to tests --- .../tree_tripper/binary_trees/AVLTreeTest.kt | 36 +++++++++++-------- .../nodes/binary_nodes/AVLTreeNodeTest.kt | 7 ++-- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt index 7257a27..e855a8b 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource import tree_tripper.binary_trees.assistants.AVLTreeTestAssistant @@ -19,58 +20,65 @@ class AVLTreeTest { } @Test - public fun testTreeInitializing() { + @DisplayName("tree initialization") + public fun testTreeInitialization() { tree.assertRoot(null) { "Root of AVLTree is not null by standard initialize." } Assertions.assertEquals(0, tree.getSize()) } @ParameterizedTest @MethodSource("testNodeCreationCases") + @DisplayName("node creation") public fun testNodeCreation(key: Int, value: Int) { tree.assertNodeCreation(key, value) } @ParameterizedTest @MethodSource("testBalanceTreeCases") + @DisplayName("check balance tree") public fun testBalanceTree(expected: AVLTreeNode, node: AVLTreeNode) { tree.assertBalanceTree(expected, node) } @ParameterizedTest @MethodSource("checkBalanceFactor") + @DisplayName("balance factor") public fun checkBalanceFactor(expected: Int, node: AVLTreeNode?) { tree.assertBalanceFactor(expected, node) } @ParameterizedTest @MethodSource("testBalanceCases") - public fun testBalanceCases(expected: AVLTreeNode, node: AVLTreeNode) { + @DisplayName("balance case") + public fun testBalanceCase(expected: AVLTreeNode, node: AVLTreeNode) { tree.assertBalance(expected, node) } - @ParameterizedTest - @MethodSource("testNodeRotateRightCases") - public fun testNodeRotateRightCases(expected: AVLTreeNode, node: AVLTreeNode) { - tree.assertNodeRightRotation(expected, node) - } - @ParameterizedTest @MethodSource("testNodeRotateLeftCases") + @DisplayName("node rotate left case") public fun testNodeRotateLeftCases(expected: AVLTreeNode, node: AVLTreeNode) { tree.assertNodeLeftRotation(expected, node) } + @ParameterizedTest + @MethodSource("testNodeRotateRightCases") + @DisplayName("node rotate right case") + public fun testNodeRotateRightCase(expected: AVLTreeNode, node: AVLTreeNode) { + tree.assertNodeRightRotation(expected, node) + } + companion object { @JvmStatic - fun testNodeCreationCases(): List = listOf( + public fun testNodeCreationCases(): List = listOf( Arguments.of(0, 0), Arguments.of(1, 1), Arguments.of(-1, -1) ) @JvmStatic - fun testBalanceTreeCases(): List = listOf( + public fun testBalanceTreeCases(): List = listOf( //Does not require balance Arguments.of( @@ -131,7 +139,7 @@ class AVLTreeTest { ) @JvmStatic - fun checkBalanceFactor(): List = listOf( + public fun checkBalanceFactor(): List = listOf( Arguments.of(0, null), @@ -166,7 +174,7 @@ class AVLTreeTest { ) @JvmStatic - fun testBalanceCases(): List = listOf( + public fun testBalanceCases(): List = listOf( //Does not require balance Arguments.of( @@ -267,7 +275,7 @@ class AVLTreeTest { ) @JvmStatic - fun testNodeRotateLeftCases(): List = listOf( + public fun testNodeRotateLeftCases(): List = listOf( //Null check Arguments.of( @@ -332,7 +340,7 @@ class AVLTreeTest { ) @JvmStatic - fun testNodeRotateRightCases(): List = listOf( + public fun testNodeRotateRightCases(): List = listOf( //Null check Arguments.of( diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt index 8cb5a98..cad70a1 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt @@ -1,6 +1,7 @@ package tree_tripper.nodes.binary_nodes import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -10,13 +11,15 @@ import org.junit.jupiter.params.provider.MethodSource class AVLTreeNodeTest { @Test - public fun nodeInitializing() { + @DisplayName("node initialization") + public fun nodeInitialization() { val node = AVLTreeNode(0, 0) Assertions.assertEquals(1, node.height) {"The height is not 1 by standard initialize."} } @ParameterizedTest @MethodSource("testUpdateHeight") + @DisplayName("update height") public fun testUpdateHeight(expected: Int, node: AVLTreeNode) { node.updateHeight() Assertions.assertEquals(expected, node.height) {"The height does not match the expected."} @@ -25,7 +28,7 @@ class AVLTreeNodeTest { companion object { @JvmStatic - fun testUpdateHeight(): List = listOf( + public fun testUpdateHeight(): List = listOf( Arguments.of(1, AVLTreeNode(0, 0, 0, null, null) From 66b193247bca2a09213b20a56c328a41705a4142 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 14:30:23 +0300 Subject: [PATCH 074/122] refactor: update displayed names of tests names --- .../kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 10 ++++++++++ .../iterators/BinarySearchTreeIteratorTest.kt | 5 +++++ lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt | 3 +++ .../tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt | 8 ++++++++ 4 files changed, 26 insertions(+) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 4781a23..fc51cd7 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource import tree_tripper.binary_trees.assistants.RBTreeTestAssistant @@ -19,6 +20,7 @@ class RBTreeTest { } @Test + @DisplayName("tree initialization") public fun testTreeInitializing() { tree.assertRoot(null) { "Root of RBTree is not null by standard initialize." } tree.assertIsRBTree() @@ -27,48 +29,56 @@ class RBTreeTest { @ParameterizedTest @MethodSource("testNodeColorCases") + @DisplayName("color of node") public fun testNodeColor(expected: Boolean, node: RBTreeNode?) { tree.assertNodeColor(expected, node) } @ParameterizedTest @MethodSource("testNodeLeftChildColorCases") + @DisplayName("color of left child of node") public fun testNodeLeftChildColor(expected: Boolean, node: RBTreeNode?) { tree.assertNodeLeftChildColor(expected, node) } @ParameterizedTest @MethodSource("testNodeRotateLeftCases") + @DisplayName("rotate node left") public fun testNodeRotateLeft(expected: RBTreeNode, node: RBTreeNode) { tree.assertNodeLeftRotation(expected, node) } @ParameterizedTest @MethodSource("testNodeRotateRightCases") + @DisplayName("rotate node right") public fun testNodeRotateRight(expected: RBTreeNode, node: RBTreeNode) { tree.assertNodeRightRotation(expected, node) } @ParameterizedTest @MethodSource("testNodeColorFlipCases") + @DisplayName("flip colors of node") public fun testNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { tree.assertNodeColorFlip(expected, node) } @ParameterizedTest @MethodSource("testNodeCreationCases") + @DisplayName("create node") public fun testNodeCreation(key: Int, value: Int) { tree.assertNodeCreation(key, value) } @ParameterizedTest @MethodSource("testUpdateRootCases") + @DisplayName("update root") public fun testUpdateRoot(node: RBTreeNode?) { tree.assertUpdateRoot(node) } @ParameterizedTest @MethodSource("testBalanceTreeCases") + @DisplayName("balance tree") public fun testBalanceTree(expectedNodeTreeView: RBTreeNode, nodeTreeView: RBTreeNode) { tree.assertBalanceTree(expectedNodeTreeView, nodeTreeView) } diff --git a/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt b/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt index 2480d99..0cbf597 100644 --- a/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt +++ b/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt @@ -1,6 +1,7 @@ package tree_tripper.iterators import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -12,6 +13,7 @@ class BinarySearchTreeIteratorTest { @ParameterizedTest @MethodSource("testIteratorCases") + @DisplayName("test iterator at width order") public fun testWidthOrderIterator(expected: List, root: BSTreeNode) { iterator = BinarySearchTreeIterator(root) var index: Int = 0 @@ -24,6 +26,7 @@ class BinarySearchTreeIteratorTest { @ParameterizedTest @MethodSource("testGetALotOfElementsCases") + @DisplayName("try get more elements than iterator has") public fun testGetALotOfElements(order: IterationOrders) { iterator = BinarySearchTreeIterator(null, order) Assertions.assertFalse(iterator.hasNext()) @@ -32,6 +35,7 @@ class BinarySearchTreeIteratorTest { @ParameterizedTest @MethodSource("testIteratorCases") + @DisplayName("test iterator at increase order") public fun testIncreasingOrderIterator(expected: List, root: BSTreeNode) { iterator = BinarySearchTreeIterator(root, IterationOrders.INCREASING_ORDER) val sortedElements = expected.sorted() @@ -45,6 +49,7 @@ class BinarySearchTreeIteratorTest { @ParameterizedTest @MethodSource("testIteratorCases") + @DisplayName("test iterator at decrease order") public fun testDecreasingOrderIterator(expected: List, root: BSTreeNode) { iterator = BinarySearchTreeIterator(root, IterationOrders.DECREASING_ORDER) val sortedElements = expected.sorted().reversed() diff --git a/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt index 8ae0159..cc2fa1a 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt @@ -1,6 +1,7 @@ package tree_tripper.nodes import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -11,6 +12,7 @@ public class UtilsTest { @ParameterizedTest @MethodSource("testNodeUpdateCases") + @DisplayName("util of node update") public fun testNodeUpdate(expected: Boolean, node: BSTreeNode?) { var isActivateAction: Boolean = false notNullNodeUpdate(node) { isActivateAction = true } @@ -19,6 +21,7 @@ public class UtilsTest { @ParameterizedTest @MethodSource("testNodeActionCases") + @DisplayName("util of action on node") public fun testNodeAction(expected: Boolean, node: BSTreeNode?) { Assertions.assertEquals(expected, notNullNodeAction(node, false) { expected }) } diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt index 0bbc784..5d69aae 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -2,6 +2,7 @@ package tree_tripper.nodes.binary_nodes import assertBinaryNodeDeepEquals import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -14,6 +15,7 @@ public class RBTreeNodeTest { @ParameterizedTest @MethodSource("testNodeSimpleInitializeCases") + @DisplayName("node simple initialization") public fun testNodeSimpleInitialize(key: Int, value: Int?) { val node = RBTreeNode(key, value) Assertions.assertEquals(key, node.key) { "Key of node is not equal." } @@ -26,6 +28,7 @@ public class RBTreeNodeTest { @ParameterizedTest @MethodSource("testNodeColorTypeInitializeCases") + @DisplayName("node initialization with color") public fun testNodeColorTypeInitialize(isRed: Boolean) { val node = RBTreeNode(0, 0, isRed) Assertions.assertEquals(isRed, node.isRed) { "Color of node is not equal." } @@ -36,6 +39,7 @@ public class RBTreeNodeTest { @ParameterizedTest @MethodSource("testNodeFullInitializeCases") + @DisplayName("node initialization with color and children") public fun testNodeFullInitialize(leftChild: RBTreeNode?, rightChild: RBTreeNode?) { val node = RBTreeNode(0, 0, false, leftChild, rightChild) Assertions.assertNull(node.parent) { "Parent of node is not null." } @@ -48,6 +52,7 @@ public class RBTreeNodeTest { } @Test + @DisplayName("setter of node parent property") public fun testParentSetter() { val node = RBTreeNode(0, 0) Assertions.assertNull(node.parent) @@ -59,12 +64,14 @@ public class RBTreeNodeTest { @ParameterizedTest @MethodSource("testToStringSimpleViewCases") + @DisplayName("to string simple view") public fun testToStringSimpleView(expected: String, node: RBTreeNode) { Assertions.assertEquals(expected, node.toStringSimpleView()) } @ParameterizedTest @MethodSource("testGetUncleCases") + @DisplayName("getter of node uncle") public fun testGetUncle(node: RBTreeNode) { Assertions.assertNull(node.getUncle()) notNullNodeUpdate(node.leftChild) { leftChild -> @@ -81,6 +88,7 @@ public class RBTreeNodeTest { @ParameterizedTest @MethodSource("testFlipColorCases") + @DisplayName("flip color of node") public fun testFlipColor(node: RBTreeNode, expected: Boolean) { node.flipColor() assertEquals(expected, node.isRed) From 098564835104c0f23bd4b66e91707cb80e665ce8 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 14:37:50 +0300 Subject: [PATCH 075/122] test: add new test case of `flip colors of node` test at red-black tree --- lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 3 ++- .../binary_trees/assistants/RBTreeTestAssistant.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index fc51cd7..26c7a66 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -58,7 +58,7 @@ class RBTreeTest { @ParameterizedTest @MethodSource("testNodeColorFlipCases") @DisplayName("flip colors of node") - public fun testNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { + public fun testNodeColorFlip(expected: RBTreeNode?, node: RBTreeNode?) { tree.assertNodeColorFlip(expected, node) } @@ -263,6 +263,7 @@ class RBTreeTest { @JvmStatic fun testNodeColorFlipCases(): List = listOf( + Arguments.of(null, null), Arguments.of( RBTreeNode( 0, 0, false, null, null diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index 567b106..4485dcb 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -74,7 +74,7 @@ public class RBTreeTestAssistant, V>: RBTree() { assertBinaryNodeDeepEquals(expected, rotateRight(node)) {n1, n2 -> n1.isRed == n2.isRed} } - fun assertNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { + fun assertNodeColorFlip(expected: RBTreeNode?, node: RBTreeNode?) { flipColors(node) assertBinaryNodeDeepEquals(expected, node) {n1, n2 -> n1.isRed == n2.isRed} } From e34d02c847223b1b6da2ab3fa95112f7ef424b2f Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 15:44:26 +0300 Subject: [PATCH 076/122] refactor: update viability of companion objects at tests --- lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt | 2 +- lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 2 +- .../kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index bfc22b9..d4bd4eb 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -304,7 +304,7 @@ public class BSTreeTest { assertEquals(tree.removeOrDefault(1, 0), -1) } - companion object { + public companion object { @JvmStatic fun getSizeAndTimeArguments() = listOf( Arguments.of(100, 1L), diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 26c7a66..c2865fd 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -83,7 +83,7 @@ class RBTreeTest { tree.assertBalanceTree(expectedNodeTreeView, nodeTreeView) } - companion object { + public companion object { @JvmStatic fun testSortedInsertElementsCases(): List = listOf( diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt index 5d69aae..33e3c9e 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -94,7 +94,7 @@ public class RBTreeNodeTest { assertEquals(expected, node.isRed) } - companion object { + public companion object { @JvmStatic fun testNodeSimpleInitializeCases(): List = listOf( Arguments.of(-1, -1), From aafb36db5bd1148ba143131c0340768a91445160 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 15:46:14 +0300 Subject: [PATCH 077/122] refactor: update viability some methods at tests --- lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt | 2 +- lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt index e855a8b..7c678df 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt @@ -15,7 +15,7 @@ class AVLTreeTest { private lateinit var tree: AVLTreeTestAssistant @BeforeEach - fun setup() { + public fun setup() { tree = AVLTreeTestAssistant() } diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index c2865fd..f32baa1 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -15,7 +15,7 @@ class RBTreeTest { private lateinit var tree: RBTreeTestAssistant @BeforeEach - fun setup() { + public fun setup() { tree = RBTreeTestAssistant() } From f86f54823b7882ffa0c935e7315e1d63491f32be Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 1 Apr 2024 16:01:57 +0300 Subject: [PATCH 078/122] refactor: union methods removeNode at AbstractBSTree and removeNode at RBTree --- .../binary_trees/AbstractBSTree.kt | 35 +++--- .../tree_tripper/binary_trees/RBTree.kt | 118 ++++++++---------- 2 files changed, 70 insertions(+), 83 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt index efa095e..a9a705e 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -15,7 +15,7 @@ import tree_tripper.nodes.notNullNodeAction * @param V the value type in a tree * @param N the node type in a tree */ -public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { +public abstract class AbstractBSTree, V, N : AbstractBSTreeNode> : SearchTree { protected var root: N? = null private set private var size: Int = 0 @@ -64,7 +64,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< } override fun getMax(): Pair? { - return notNullNodeAction(root, null) {node -> getMaxInSubtree(node.key)} + return notNullNodeAction(root, null) { node -> getMaxInSubtree(node.key) } } override fun getMinInSubtree(key: K): Pair? { @@ -73,7 +73,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< } override fun getMin(): Pair? { - return notNullNodeAction(root, null) {node -> getMinInSubtree(node.key)} + return notNullNodeAction(root, null) { node -> getMinInSubtree(node.key) } } override fun getSize(): Int { @@ -106,7 +106,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< override fun toStringWithTreeView(): String { val builder = StringBuilder() - notNullNodeAction(root, Unit) {node -> node.toStringWithSubtreeView(0, builder)} + notNullNodeAction(root, Unit) { node -> node.toStringWithSubtreeView(0, builder) } return "${this.javaClass.simpleName}(\n$builder)" } @@ -172,7 +172,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< * @return a pair of a balanced [N] node or null, and [V] value corresponding the given [key] * if a node was removed, null if not. */ - protected open fun removeNode(node: N?, key: K): Pair { + protected fun removeNode(node: N?, key: K): Pair { if (node == null) return Pair(null, null) val resultRemove: Pair @@ -184,22 +184,25 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< resultRemove = removeNode(node.rightChild, key) node.rightChild = resultRemove.first } else { - val nodeSubstitutive: N? - if (node.leftChild == null || node.rightChild == null) { - nodeSubstitutive = node.leftChild ?: node.rightChild - return Pair(nodeSubstitutive, node.value) - } else { - nodeSubstitutive = getMaxNodeInSubtree(node.leftChild) as N - node.leftChild = removeNode(node.leftChild, nodeSubstitutive.key).first - nodeSubstitutive.rightChild = node.rightChild - nodeSubstitutive.leftChild = node.leftChild - return Pair(balanceTree(nodeSubstitutive), node.value) - } + return removeCurrentNode(node) } return Pair(balanceTree(node), resultRemove.second) } + protected open fun removeCurrentNode(nodeCurrent: N): Pair { + val nodeSubstitutive: N? + if (nodeCurrent.leftChild == null || nodeCurrent.rightChild == null) { + nodeSubstitutive = nodeCurrent.leftChild ?: nodeCurrent.rightChild + return Pair(nodeSubstitutive, nodeCurrent.value) + } + nodeSubstitutive = getMaxNodeInSubtree(nodeCurrent.leftChild) as N + nodeCurrent.leftChild = removeNode(nodeCurrent.leftChild, nodeSubstitutive.key).first + nodeSubstitutive.rightChild = nodeCurrent.rightChild + nodeSubstitutive.leftChild = nodeCurrent.leftChild + return Pair(balanceTree(nodeSubstitutive), nodeCurrent.value) + } + /** * Searches the node with a given key. * @return the found node or null if the node is not contained in a tree. diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index d4dd36e..414e0f0 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -1,7 +1,6 @@ package tree_tripper.binary_trees import tree_tripper.nodes.binary_nodes.RBTreeNode -import tree_tripper.nodes.notNullNodeAction import tree_tripper.nodes.notNullNodeUpdate @@ -36,79 +35,64 @@ public open class RBTree, V>: AbstractBSTree?, key: K): Pair?, V?> { - if (node == null) return Pair(null, null) - - val removeResult: Pair?, V?> - val resultCompare: Int = key.compareTo(node.key) - val nodeCurrent: RBTreeNode = node - if (resultCompare < 0) { - removeResult = removeNode(nodeCurrent.leftChild, key) - nodeCurrent.leftChild = removeResult.first - } else if (resultCompare > 0) { - removeResult = removeNode(nodeCurrent.rightChild, key) - nodeCurrent.rightChild = removeResult.first - } else { - val leftChild = nodeCurrent.leftChild - val rightChild = nodeCurrent.rightChild - if (leftChild == null && rightChild == null) { - if (isRedColor(nodeCurrent)) return Pair(null, nodeCurrent.value) - if (isRedColor(nodeCurrent.parent)) { - flipColors(nodeCurrent.parent) - } else { - val uncle = nodeCurrent.getUncle() - if (isRedColor(uncle)) { - flipColors(uncle) - nodeCurrent.parent?.flipColor() - } - uncle?.flipColor() - } - return Pair(null, nodeCurrent.value) - } else if (leftChild == null) { - throw IllegalArgumentException( - "Invalid RedBlackTree state, node with one child as right is not valid rb-tree" - ) - } else if (rightChild == null) { - if (isRedColor(leftChild)) { - flipColors(nodeCurrent) - return Pair(leftChild, nodeCurrent.value) + override fun removeCurrentNode(nodeCurrent: RBTreeNode): Pair?, V?> { + val leftChild = nodeCurrent.leftChild + val rightChild = nodeCurrent.rightChild + if (leftChild == null && rightChild == null) { + if (isRedColor(nodeCurrent)) return Pair(null, nodeCurrent.value) + if (isRedColor(nodeCurrent.parent)) { + flipColors(nodeCurrent.parent) + } else { + val uncle = nodeCurrent.getUncle() + if (isRedColor(uncle)) { + flipColors(uncle) + nodeCurrent.parent?.flipColor() } - throw IllegalArgumentException( - "Invalid RedBlackTree state, node with one child as left with black color is not valid rb-tree" - ) + uncle?.flipColor() + } + return Pair(null, nodeCurrent.value) + } else if (leftChild == null) { + throw IllegalArgumentException( + "Invalid RedBlackTree state, node with one child as right is not valid rb-tree" + ) + } else if (rightChild == null) { + if (isRedColor(leftChild)) { + flipColors(nodeCurrent) + return Pair(leftChild, nodeCurrent.value) + } + throw IllegalArgumentException( + "Invalid RedBlackTree state, node with one child as left with black color is not valid rb-tree" + ) + } else { + val newNode: RBTreeNode + val nodeCached: RBTreeNode + if (isRedColor(nodeCurrent)) { + nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode + newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCached.isRed) + newNode.leftChild = leftChild + newNode.rightChild = removeNode(rightChild, nodeCached.key).first + if (!isRedColor(nodeCached)) leftChild.flipColor() } else { - val newNode: RBTreeNode - val nodeCached: RBTreeNode - if (isRedColor(nodeCurrent)) { - nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode - newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCached.isRed) - newNode.leftChild = leftChild - newNode.rightChild = removeNode(rightChild, nodeCached.key).first - if (!isRedColor(nodeCached)) leftChild.flipColor() - } else { - if (isRedColor(leftChild)) { - nodeCached = getMaxNodeInSubtree(leftChild) as RBTreeNode - newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCurrent.isRed) - newNode.rightChild = rightChild - newNode.leftChild = removeNode(leftChild, nodeCached.key).first - return Pair(balanceTree(newNode), nodeCurrent.value) - } - nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode + if (isRedColor(leftChild)) { + nodeCached = getMaxNodeInSubtree(leftChild) as RBTreeNode newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCurrent.isRed) - newNode.leftChild = leftChild - newNode.rightChild = removeNode(rightChild, nodeCached.key).first - if (!isRedColor(nodeCached)) { - leftChild.isRed = true - newNode.isRed = true - } + newNode.rightChild = rightChild + newNode.leftChild = removeNode(leftChild, nodeCached.key).first + return Pair(balanceTree(newNode), nodeCurrent.value) + } + nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode + newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCurrent.isRed) + newNode.leftChild = leftChild + newNode.rightChild = removeNode(rightChild, nodeCached.key).first + if (!isRedColor(nodeCached)) { + leftChild.isRed = true + newNode.isRed = true } - return Pair(balanceTree(newNode), nodeCurrent.value) } + return Pair(balanceTree(newNode), nodeCurrent.value) } - - return Pair(balanceTree(nodeCurrent), removeResult.second) } - + /** * Returns whether the specified node is red or not. * From eee5fbf28f629fd1b7a7fed29e85fd6838b5e604 Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Wed, 3 Apr 2024 15:16:32 +0300 Subject: [PATCH 079/122] test(BSTree): add a test for iterator. Add missing exceptions to all BSTree test methods --- .../tree_tripper/binary_trees/BSTreeTest.kt | 168 ++++++++++-------- .../assistants/BSTreeTestAssistant.kt | 18 +- 2 files changed, 98 insertions(+), 88 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index d4bd4eb..25b6f76 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -1,6 +1,6 @@ package tree_tripper.binary_trees -import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertTimeout @@ -25,7 +25,7 @@ public class BSTreeTest { @DisplayName("tree initialization") public fun testTreeInitialization() { tree.assertRootInitialization() - assertEquals(tree.getSize(), 0, "Incorrect a tree initialization") + assertEquals(tree.getSize(), 0, "Incorrect a tree initialization.") } @Test @@ -51,11 +51,11 @@ public class BSTreeTest { public fun testInsertRoot() { tree.insert(1, -1) tree.assertIsBSTree() - assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size.") tree.insert(1, 0) tree.assertIsBSTree() - assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size.") } @Test @@ -65,19 +65,7 @@ public class BSTreeTest { tree.insert(1, -1) tree.insert(3, -3) tree.assertIsBSTree() - assertEquals(tree.getSize(), 3, "Incorrect resizing tree size") - } - - @Test - @DisplayName("insert 5 elements") - public fun testInsertElements() { - tree.insert(4, -4) - tree.insert(5, -5) - tree.insert(3, -3) - tree.insert(2, -2) - tree.insert(6, -6) - tree.assertIsBSTree() - assertEquals(tree.getSize(), 5, "Incorrect resizing tree size") + assertEquals(tree.getSize(), 3, "Incorrect resizing tree size.") } @ParameterizedTest @@ -95,47 +83,34 @@ public class BSTreeTest { } @Test - @DisplayName("insert if absent root") + @DisplayName("if absent insert root") public fun testInsertIfAbsentRoot() { assertEquals(tree.insertIfAbsent(1, -1), true) tree.assertIsBSTree() - assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size.") assertEquals(tree.insertIfAbsent(1, 1), false) tree.assertIsBSTree() - assertEquals(tree.getSize(), 1, "Incorrect resizing tree size") - } - - @Test - @DisplayName("set with brackets") - public fun testSet() { - assertEquals(tree.search(1), null) - - tree[1] = -1 - assertEquals(tree.search(1), -1) + assertEquals(tree.getSize(), 1, "Incorrect resizing tree size.") } @Test - @DisplayName("search node") + @DisplayName("search root") public fun testSearchNode() { - tree.insert(1, -1) - assertEquals(tree.search(1), -1) - tree.assertIsBSTree() + assertEquals(tree.search(0), null, "Incorrect search in a empty tree.") - assertEquals(tree.search(0), null) - tree.assertIsBSTree() + tree.insert(1, -1) + assertEquals(tree.search(1), -1, "Incorrect search an existent root.") } @Test - @DisplayName("search children node") + @DisplayName("search children root") public fun testSearchChildrenNode() { tree.insert(2, -2) tree.insert(1, -1) tree.insert(3, -3) - assertEquals(tree.search(2), -2) - assertEquals(tree.search(1), -1) - assertEquals(tree.search(3), -3) - tree.assertIsBSTree() + assertEquals(tree.search(1), -1, "Incorrect search existent children root.") + assertEquals(tree.search(3), -3, "Incorrect search existent children root.") } @ParameterizedTest @@ -154,9 +129,11 @@ public class BSTreeTest { repeat(10) { val keyRandom = Random.nextInt(-1000, 1000) if (keyRandom in array) - assertEquals(tree.search(keyRandom), (-1) * keyRandom) + assertEquals(tree.search(keyRandom), (-1) * keyRandom, + "Incorrect search an existent node.") else - assertEquals(tree.search(keyRandom), null) + assertEquals(tree.search(keyRandom), null, + "Incorrect search a non-existent node.") } } @@ -166,109 +143,131 @@ public class BSTreeTest { @Test @DisplayName("search of default") public fun testSearchOrDefault() { - assertEquals(tree.searchOrDefault(1, 0), 0) + assertEquals(tree.searchOrDefault(1, 0), 0, + "Incorrect return of search a non-existent node.") tree.insert(1, -1) - assertEquals(tree.searchOrDefault(1, 0), -1) + assertEquals(tree.searchOrDefault(1, 0), -1, + "Incorrect return of search an existent node.") } @Test @DisplayName("contains") public fun testContains() { - assertEquals(tree.contains(1), false) + assertEquals(tree.contains(1), false, "Incorrect return of search a non-existent node.") tree.insert(1, -1) - assertEquals(tree.contains(1), true) + assertEquals(tree.contains(1), true, "Incorrect return of search an existent node.") + } + + @Test + @DisplayName("set with brackets") + public fun testSet() { + tree[1] = -1 + assertEquals(tree.search(1), -1, "Incorrect set.") + + tree[1] = 1 + assertEquals(tree.search(1), 1, "Incorrect change of the value.") } @Test @DisplayName("get with brackets") public fun testGet() { - assertEquals(tree[1], null) + assertEquals(tree[1], null, "Incorrect get a non-existent node.") tree[1] = -1 - assertEquals(tree[1], -1) + assertEquals(tree[1], -1, "Incorrect get an existent node.") } @Test @DisplayName("get maximum in subtree") public fun testGetMaxInSubtree() { - assertEquals(tree.getMaxInSubtree(0), null) + assertEquals(tree.getMaxInSubtree(0), null, + "Incorrect search a maximum key in a empty tree.") tree.assertIsBSTree() tree[2] = -2 - assertEquals(tree.getMaxInSubtree(2), Pair(2, -2)) + assertEquals(tree.getMaxInSubtree(2), Pair(2, -2), + "Incorrect search a maximum key in subtree without children.") tree.assertIsBSTree() + } + @Test + @DisplayName("get maximum in subtree with children") + public fun testGetMaxInSubtreeWithChildren() { + tree[2] = -2 tree[3] = -3 tree[1] = -1 - assertEquals(tree.getMaxInSubtree(2), Pair(3, -3)) + assertEquals(tree.getMaxInSubtree(2), Pair(3, -3), "Incorrect search a maximum key in a subtree.") tree.assertIsBSTree() } @Test @DisplayName("get maximum") public fun testGetMax() { - assertEquals(tree.getMax(), null) + assertEquals(tree.getMax(), null, "Incorrect search a maximum key in a empty tree.") tree[2] = -2 - assertEquals(tree.getMax(), Pair(2, -2)) - tree[3] = -3 tree[1] = -1 - assertEquals(tree.getMax(), Pair(3, -3)) + assertEquals(tree.getMax(), Pair(3, -3), "Incorrect search a maximum key in a tree.") } @Test @DisplayName("get minimum in subtree") public fun testGetMinInSubtree() { - assertEquals(tree.getMinInSubtree(0), null) + assertEquals(tree.getMinInSubtree(0), null, + "Incorrect search a minimum key in a empty tree.") tree.assertIsBSTree() tree[2] = -2 - assertEquals(tree.getMinInSubtree(2), Pair(2, -2)) + assertEquals(tree.getMinInSubtree(2), Pair(2, -2), + "Incorrect search a minimum key in subtree without children.") tree.assertIsBSTree() + } - tree[1] = -1 + @Test + @DisplayName("get minimum in subtree with children") + public fun testGetMinInSubtreeWithChildren() { + tree[2] = -2 tree[3] = -3 - assertEquals(tree.getMinInSubtree(2), Pair(1, -1)) + tree[1] = -1 + assertEquals(tree.getMinInSubtree(2), Pair(1, -1), "Incorrect search a minimum key in a subtree.") tree.assertIsBSTree() } @Test @DisplayName("get minimum") public fun testGetMin() { - assertEquals(tree.getMin(), null) + assertEquals(tree.getMin(), null, "Incorrect search a minimum key in a empty tree.") tree[2] = -2 - assertEquals(tree.getMin(), Pair(2, -2)) - - tree[1] = -1 tree[3] = -3 - assertEquals(tree.getMin(), Pair(1, -1)) + tree[1] = -1 + assertEquals(tree.getMin(), Pair(1, -1), "Incorrect search a minimum key in a tree.") } @Test - @DisplayName("remove") + @DisplayName("remove root") public fun testRemove() { - assertEquals(tree.remove(0), null) + tree[1] = -1 + assertEquals(tree.remove(1), -1, "Incorrect remove root.") tree.assertIsBSTree() - tree[2] = -2 - assertEquals(tree.remove(2), -2) + assertEquals(tree.remove(1), null, "Incorrect remove a non-existent root.") tree.assertIsBSTree() + } + @Test + @DisplayName("remove root with children") + public fun testRemoveWithChildren() { tree[2] = -2 tree[1] = -1 - assertEquals(tree.remove(2), -2) - tree.assertIsBSTree() - - tree[2] = -2 tree[3] = -3 - assertEquals(tree.remove(2), -2) - tree.assertIsBSTree() - assertEquals(tree.remove(3), -3) + assertEquals(tree.remove(2), -2, "Incorrect remove a root.") tree.assertIsBSTree() + assertEquals(tree.search(1), -1, "Incorrect remove a root and lose the left child.") + assertEquals(tree.search(3), -3, "Incorrect remove a root and lose the right child.") } @ParameterizedTest @@ -287,9 +286,11 @@ public class BSTreeTest { repeat(10) { val keyRandom = Random.nextInt(-1000, 1000) if (keyRandom in array) - assertEquals(tree.remove(keyRandom), (-1) * keyRandom) + assertEquals(tree.remove(keyRandom), (-1) * keyRandom, + "Incorrect return of remove an existent node.") else - assertEquals(tree.remove(keyRandom), null) + assertEquals(tree.remove(keyRandom), null, + "Incorrect return of remove a non-existent node.") } } @@ -299,9 +300,18 @@ public class BSTreeTest { @Test @DisplayName("remove or default") public fun testRemoveOrDefault() { - assertEquals(tree.removeOrDefault(1, 0), 0) + assertEquals(tree.removeOrDefault(1, 0), 0, + "Incorrect return of remove a non-existent node.") + tree.insert(1, -1) - assertEquals(tree.removeOrDefault(1, 0), -1) + assertEquals(tree.removeOrDefault(1, 0), -1, + "Incorrect return of remove an existent node.") + } + + @Test + @DisplayName("iterator") + public fun testIterator() { + assertFalse(tree.iterator().hasNext(), "Incorrect check next.") } public companion object { diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt index 2e575d2..5f544a0 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt @@ -9,26 +9,26 @@ import java.util.* public class BSTreeTestAssistant, V>: BSTree() { public fun assertRootInitialization() { - assertEquals(root, null, "Incorrect a root initialization") + assertEquals(root, null, "Incorrect a root initialization.") } public fun assertWasCreatedNode(key: K, value: V) { val node = createNode(key, value) - assertEquals(node.key, key, "Incorrect a key assignment") - assertEquals(node.value, value, "Incorrect a value assignment") - assertEquals(node.leftChild, null, "Incorrect a left child assignment") - assertEquals(node.rightChild, null, "Incorrect a right child assignment") + assertEquals(node.key, key, "Incorrect a key assignment.") + assertEquals(node.value, value, "Incorrect a value assignment.") + assertEquals(node.leftChild, null, "Incorrect a left child assignment.") + assertEquals(node.rightChild, null, "Incorrect a right child assignment.") } public fun assertWasUpdatedRoot(key: K, value: V) { val node = createNode(key, value) updateRoot(node) - assertEquals(root, node, "Incorrect a root update") + assertEquals(root, node, "Incorrect a root update.") } public fun assertWasBalancedTree(key: K, value: V) { val node = createNode(key, value) - assertEquals(balanceTree(node), node, "Incorrect a tree balance") + assertEquals(balanceTree(node), node, "Incorrect a tree balance.") } public fun assertIsBSTree() { @@ -39,11 +39,11 @@ public class BSTreeTestAssistant, V>: BSTree() { val nodeLeft = node.leftChild if (nodeLeft != null) assert(nodeLeft.key < node.key) - { "Incorrect the binary search tree structure: a left child key is no less than the parent key" } + { "Incorrect the binary search tree structure: a left child key is no less than the parent key." } val nodeRight = node.rightChild if (nodeRight != null) assert(nodeRight.key > node.key) - { "Incorrect the binary search tree structure: a left child key is no more than the parent key" } + { "Incorrect the binary search tree structure: a left child key is no more than the parent key." } queue.addAll(node.getChildren()) } From 5a4a51c6545ac77709aeea3f9d40895c09cf6f6b Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Wed, 3 Apr 2024 16:39:24 +0300 Subject: [PATCH 080/122] refactor: change the output a key/value pair of a node and tests for this method --- .../tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt | 2 +- .../kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt | 2 +- .../kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt index 9803ff8..32ed285 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -28,7 +28,7 @@ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeN } override fun toStringSimpleView(): String { - return "($key, $value)" + return "($key: $value)" } override fun toStringWithSubtreeView(indent: Int, builder: StringBuilder) { diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt index 52c8193..45cda2a 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt @@ -43,7 +43,7 @@ public class BSTreeNodeTest { @DisplayName("to string simple view") public fun testToStringSimpleView() { val node = BSTreeNode(1, -1) - assertEquals(node.toStringSimpleView(), "(1, -1)") + assertEquals(node.toStringSimpleView(), "(1: -1)") } @Test diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt index 33e3c9e..07261a4 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -119,8 +119,8 @@ public class RBTreeNodeTest { @JvmStatic fun testToStringSimpleViewCases(): List = listOf( - Arguments.of("(-1, null) - RED", RBTreeNode(-1, null)), - Arguments.of("(0, 0) - BLACK", RBTreeNode(0, 0, false)), + Arguments.of("(-1: null) - RED", RBTreeNode(-1, null)), + Arguments.of("(0: 0) - BLACK", RBTreeNode(0, 0, false)), ) @JvmStatic From dd4afdc001bbb43ef68a37ef05a9f1addc9869a0 Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Wed, 3 Apr 2024 16:41:16 +0300 Subject: [PATCH 081/122] test: add tests for the iteration with 'forEach' and output a tree to the string --- .../tree_tripper/binary_trees/BSTreeTest.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index 25b6f76..f53c8b5 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -8,6 +8,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource import tree_tripper.binary_trees.assistants.BSTreeTestAssistant +import tree_tripper.iterators.IterationOrders import java.time.Duration import kotlin.random.Random import kotlin.test.Test @@ -314,6 +315,45 @@ public class BSTreeTest { assertFalse(tree.iterator().hasNext(), "Incorrect check next.") } + @Test + @DisplayName("for each") + public fun testForEach() { + tree[2] = -2 + tree[1] = -1 + tree[3] = -3 + tree[4] = -4 + val arrayPair = arrayOf(Pair(2, -2), Pair(1, -1), Pair(3, -3), Pair(4, -4)) + var index = 0 + tree.forEach(IterationOrders.WIDTH_ORDER) { + assertEquals(arrayPair[index++], it, "Incorrect iteration.") + } + } + + @Test + @DisplayName("tree to string") + public fun testToString() { + tree[2] = -2 + tree[1] = -1 + tree[3] = -3 + tree[4] = -4 + val builder = StringBuilder("BSTreeTestAssistant(") + tree.forEach { builder.append("${it.first}: ${it.second}, ") } + builder.append(')') + assertEquals(tree.toString(), builder.toString(), "Incorrect construction string.") + } + + @Test + @DisplayName("tree to string with tree view") + public fun testToStringWithTreeView() { + tree[2] = -2 + tree[1] = -1 + tree[3] = -3 + tree[4] = -4 + val string = "BSTreeTestAssistant(\n\t\t(4: -4)\n\t(3: -3)\n(2: -2)\n\t(1: -1)\n)" + assertEquals(tree.toStringWithTreeView(), string, "Incorrect construction string.") + } + + public companion object { @JvmStatic fun getSizeAndTimeArguments() = listOf( From dfc8d2df12e5a622ba06e965773b8de7a10f7f6b Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Wed, 3 Apr 2024 19:06:38 +0300 Subject: [PATCH 082/122] refactor: change output a tree to the string. Rename some variables and methods according invariant --- .../binary_trees/AbstractBSTree.kt | 29 +++++++---- .../tree_tripper/binary_trees/RBTree.kt | 50 +++++++++---------- .../tree_tripper/binary_trees/BSTreeTest.kt | 2 +- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt index a9a705e..359c383 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -15,7 +15,7 @@ import tree_tripper.nodes.notNullNodeAction * @param V the value type in a tree * @param N the node type in a tree */ -public abstract class AbstractBSTree, V, N : AbstractBSTreeNode> : SearchTree { +public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { protected var root: N? = null private set private var size: Int = 0 @@ -101,6 +101,8 @@ public abstract class AbstractBSTree, V, N : AbstractBSTreeNod override fun toString(order: IterationOrders): String { val builder = StringBuilder() this.forEach(order) { pair: Pair -> builder.append("${pair.first}: ${pair.second}, ") } + if (builder.isNotEmpty()) + repeat(2) { builder.deleteCharAt(builder.length - 1) } // remove ", " in the last pair return "${this.javaClass.simpleName}($builder)" } @@ -184,23 +186,28 @@ public abstract class AbstractBSTree, V, N : AbstractBSTreeNod resultRemove = removeNode(node.rightChild, key) node.rightChild = resultRemove.first } else { - return removeCurrentNode(node) + return substituteNode(node) } return Pair(balanceTree(node), resultRemove.second) } - protected open fun removeCurrentNode(nodeCurrent: N): Pair { + /** + * Substitutes the node on the node with the max key. + * @return a pair of a balanced [N] node or null, and [V] value removed node + * if a node was removed, null if not. + */ + protected open fun substituteNode(node: N): Pair { val nodeSubstitutive: N? - if (nodeCurrent.leftChild == null || nodeCurrent.rightChild == null) { - nodeSubstitutive = nodeCurrent.leftChild ?: nodeCurrent.rightChild - return Pair(nodeSubstitutive, nodeCurrent.value) + if (node.leftChild == null || node.rightChild == null) { + nodeSubstitutive = node.leftChild ?: node.rightChild + return Pair(nodeSubstitutive, node.value) } - nodeSubstitutive = getMaxNodeInSubtree(nodeCurrent.leftChild) as N - nodeCurrent.leftChild = removeNode(nodeCurrent.leftChild, nodeSubstitutive.key).first - nodeSubstitutive.rightChild = nodeCurrent.rightChild - nodeSubstitutive.leftChild = nodeCurrent.leftChild - return Pair(balanceTree(nodeSubstitutive), nodeCurrent.value) + nodeSubstitutive = getMaxNodeInSubtree(node.leftChild) as N + node.leftChild = removeNode(node.leftChild, nodeSubstitutive.key).first + nodeSubstitutive.rightChild = node.rightChild + nodeSubstitutive.leftChild = node.leftChild + return Pair(balanceTree(nodeSubstitutive), node.value) } /** diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index 414e0f0..9604794 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -35,61 +35,61 @@ public open class RBTree, V>: AbstractBSTree): Pair?, V?> { - val leftChild = nodeCurrent.leftChild - val rightChild = nodeCurrent.rightChild + override fun substituteNode(node: RBTreeNode): Pair?, V?> { + val leftChild = node.leftChild + val rightChild = node.rightChild if (leftChild == null && rightChild == null) { - if (isRedColor(nodeCurrent)) return Pair(null, nodeCurrent.value) - if (isRedColor(nodeCurrent.parent)) { - flipColors(nodeCurrent.parent) + if (isRedColor(node)) return Pair(null, node.value) + if (isRedColor(node.parent)) { + flipColors(node.parent) } else { - val uncle = nodeCurrent.getUncle() + val uncle = node.getUncle() if (isRedColor(uncle)) { flipColors(uncle) - nodeCurrent.parent?.flipColor() + node.parent?.flipColor() } uncle?.flipColor() } - return Pair(null, nodeCurrent.value) + return Pair(null, node.value) } else if (leftChild == null) { throw IllegalArgumentException( "Invalid RedBlackTree state, node with one child as right is not valid rb-tree" ) } else if (rightChild == null) { if (isRedColor(leftChild)) { - flipColors(nodeCurrent) - return Pair(leftChild, nodeCurrent.value) + flipColors(node) + return Pair(leftChild, node.value) } throw IllegalArgumentException( "Invalid RedBlackTree state, node with one child as left with black color is not valid rb-tree" ) } else { - val newNode: RBTreeNode + val nodeNew: RBTreeNode val nodeCached: RBTreeNode - if (isRedColor(nodeCurrent)) { + if (isRedColor(node)) { nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode - newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCached.isRed) - newNode.leftChild = leftChild - newNode.rightChild = removeNode(rightChild, nodeCached.key).first + nodeNew = RBTreeNode(nodeCached.key, nodeCached.value, nodeCached.isRed) + nodeNew.leftChild = leftChild + nodeNew.rightChild = removeNode(rightChild, nodeCached.key).first if (!isRedColor(nodeCached)) leftChild.flipColor() } else { if (isRedColor(leftChild)) { nodeCached = getMaxNodeInSubtree(leftChild) as RBTreeNode - newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCurrent.isRed) - newNode.rightChild = rightChild - newNode.leftChild = removeNode(leftChild, nodeCached.key).first - return Pair(balanceTree(newNode), nodeCurrent.value) + nodeNew = RBTreeNode(nodeCached.key, nodeCached.value, node.isRed) + nodeNew.rightChild = rightChild + nodeNew.leftChild = removeNode(leftChild, nodeCached.key).first + return Pair(balanceTree(nodeNew), node.value) } nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode - newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCurrent.isRed) - newNode.leftChild = leftChild - newNode.rightChild = removeNode(rightChild, nodeCached.key).first + nodeNew = RBTreeNode(nodeCached.key, nodeCached.value, node.isRed) + nodeNew.leftChild = leftChild + nodeNew.rightChild = removeNode(rightChild, nodeCached.key).first if (!isRedColor(nodeCached)) { leftChild.isRed = true - newNode.isRed = true + nodeNew.isRed = true } } - return Pair(balanceTree(newNode), nodeCurrent.value) + return Pair(balanceTree(nodeNew), node.value) } } diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index f53c8b5..fe8fd05 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -338,6 +338,7 @@ public class BSTreeTest { tree[4] = -4 val builder = StringBuilder("BSTreeTestAssistant(") tree.forEach { builder.append("${it.first}: ${it.second}, ") } + repeat(2) { builder.deleteCharAt(builder.length - 1) } builder.append(')') assertEquals(tree.toString(), builder.toString(), "Incorrect construction string.") } @@ -353,7 +354,6 @@ public class BSTreeTest { assertEquals(tree.toStringWithTreeView(), string, "Incorrect construction string.") } - public companion object { @JvmStatic fun getSizeAndTimeArguments() = listOf( From 316a638b08a7df986be8b24353f3270d0abe35a8 Mon Sep 17 00:00:00 2001 From: Maxim Rodionov Date: Wed, 3 Apr 2024 19:13:45 +0300 Subject: [PATCH 083/122] docs: add new methods to examples in ReadMe, change the output according to the code. Correct the LICENSE --- LICENSE | 2 +- ReadMe.md | 80 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/LICENSE b/LICENSE index 5a5e946..22f3e1d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Ilia Suponev, Rodionov Maxim, Vladimir Zaikin +Copyright (c) 2024 Ilia Suponev, Maxim Rodionov, Vladimir Zaikin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ReadMe.md b/ReadMe.md index e01a32a..c88124c 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -59,15 +59,19 @@ fun main() { tree.insert(key = 1, value = 1) tree.insert(key = 2, value = 2) tree.insert(key = 3, value = 3) - tree.insert(key = 4, value = 4) - tree.insert(key = 5, value = 5) - + + /* The words `key` and `value` are optional */ + tree.insert(4, 4) + + /* Alternative insert method */ + tree[5] = 5 + println(tree) } ``` Output: ```text -BSTree(1: 1, 2: 2, 3: 3, 4: 4, 5: 5, ) +BSTree(1: 1, 2: 2, 3: 3, 4: 4, 5: 5) ``` ##### Example 3 (searching) @@ -76,7 +80,7 @@ Code: import tree_tripper.binary_trees.BSTree fun main() { - val tree = BSTree() + val tree = BSTree() /* ... inserting from `example 2` @@ -90,7 +94,7 @@ fun main() { /* Unexciting element in tree */ println(tree.search(key = -2)) - println(tree.search(key = 7)) + println(tree.searchOrDefault(7, "Element not found")) /* Alternative search method */ println(tree[2]) @@ -103,7 +107,7 @@ Output: 3 5 null -null +Element not found 2 null ``` @@ -140,7 +144,7 @@ Output: 5 null Element not found -BSTree(2: 2, 4: 4, ) +BSTree(2: 2, 4: 4) ``` ##### Example 5 (tree-like printing) @@ -168,27 +172,27 @@ fun main() { Output: ```text BSTree( - (5, 5) - (4, 4) - (3, 3) - (2, 2) -(1, 1) + (5: 5) + (4: 4) + (3: 3) + (2: 2) +(1: 1) ) AVLTree( - (5, 5) - (4, 4) - (3, 3) -(2, 2) - (1, 1) + (5: 5) + (4: 4) + (3: 3) +(2: 2) + (1: 1) ) RBTree( - (5, 5) - BLACK -(4, 4) - BLACK - (3, 3) - BLACK - (2, 2) - RED - (1, 1) - BLACK + (5: 5) - BLACK +(4: 4) - BLACK + (3: 3) - BLACK + (2: 2) - RED + (1: 1) - BLACK ) ``` @@ -224,25 +228,25 @@ fun main() { Output: ```text WIDTH ORDER: -(2, 2) -(1, 1) -(4, 4) -(3, 3) -(5, 5) +(2: 2) +(1: 1) +(4: 4) +(3: 3) +(5: 5) INCREASING ORDER: -(1, 1) -(2, 2) -(3, 3) -(4, 4) -(5, 5) +(1: 1) +(2: 2) +(3: 3) +(4: 4) +(5: 5) DECREASING ORDER: -(5, 5) -(4, 4) -(3, 3) -(2, 2) -(1, 1) +(5: 5) +(4: 4) +(3: 3) +(2: 2) +(1: 1) ``` ## Documentation From a24770b2a068421e6c57a2f854f2d0800bb8d39e Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Wed, 3 Apr 2024 20:32:25 +0300 Subject: [PATCH 084/122] refactor(RBTree): backup to version on commit with ID 8c43815 --- .../binary_trees/AbstractBSTree.kt | 31 ++-- .../tree_tripper/binary_trees/RBTree.kt | 153 +++++++++++------- .../nodes/binary_nodes/RBTreeNode.kt | 27 +--- .../tree_tripper/binary_trees/RBTreeTest.kt | 74 +++++++-- .../assistants/RBTreeTestAssistant.kt | 4 +- .../nodes/binary_nodes/RBTreeNodeTest.kt | 99 +----------- 6 files changed, 172 insertions(+), 216 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt index 359c383..97b360d 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -174,7 +174,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< * @return a pair of a balanced [N] node or null, and [V] value corresponding the given [key] * if a node was removed, null if not. */ - protected fun removeNode(node: N?, key: K): Pair { + protected open fun removeNode(node: N?, key: K): Pair { if (node == null) return Pair(null, null) val resultRemove: Pair @@ -186,30 +186,21 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< resultRemove = removeNode(node.rightChild, key) node.rightChild = resultRemove.first } else { - return substituteNode(node) + val nodeSubstitutive: N? + if (node.leftChild == null || node.rightChild == null) { + nodeSubstitutive = node.leftChild ?: node.rightChild + return Pair(nodeSubstitutive, node.value) + } + nodeSubstitutive = getMaxNodeInSubtree(node.leftChild) as N + node.leftChild = removeNode(node.leftChild, nodeSubstitutive.key).first + nodeSubstitutive.rightChild = node.rightChild + nodeSubstitutive.leftChild = node.leftChild + return Pair(balanceTree(nodeSubstitutive), node.value) } return Pair(balanceTree(node), resultRemove.second) } - /** - * Substitutes the node on the node with the max key. - * @return a pair of a balanced [N] node or null, and [V] value removed node - * if a node was removed, null if not. - */ - protected open fun substituteNode(node: N): Pair { - val nodeSubstitutive: N? - if (node.leftChild == null || node.rightChild == null) { - nodeSubstitutive = node.leftChild ?: node.rightChild - return Pair(nodeSubstitutive, node.value) - } - nodeSubstitutive = getMaxNodeInSubtree(node.leftChild) as N - node.leftChild = removeNode(node.leftChild, nodeSubstitutive.key).first - nodeSubstitutive.rightChild = node.rightChild - nodeSubstitutive.leftChild = node.leftChild - return Pair(balanceTree(nodeSubstitutive), node.value) - } - /** * Searches the node with a given key. * @return the found node or null if the node is not contained in a tree. diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index 9604794..cadcf0b 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -1,6 +1,7 @@ package tree_tripper.binary_trees import tree_tripper.nodes.binary_nodes.RBTreeNode +import tree_tripper.nodes.notNullNodeAction import tree_tripper.nodes.notNullNodeUpdate @@ -35,62 +36,39 @@ public open class RBTree, V>: AbstractBSTree): Pair?, V?> { - val leftChild = node.leftChild - val rightChild = node.rightChild - if (leftChild == null && rightChild == null) { - if (isRedColor(node)) return Pair(null, node.value) - if (isRedColor(node.parent)) { - flipColors(node.parent) - } else { - val uncle = node.getUncle() - if (isRedColor(uncle)) { - flipColors(uncle) - node.parent?.flipColor() - } - uncle?.flipColor() - } - return Pair(null, node.value) - } else if (leftChild == null) { - throw IllegalArgumentException( - "Invalid RedBlackTree state, node with one child as right is not valid rb-tree" - ) - } else if (rightChild == null) { - if (isRedColor(leftChild)) { - flipColors(node) - return Pair(leftChild, node.value) - } - throw IllegalArgumentException( - "Invalid RedBlackTree state, node with one child as left with black color is not valid rb-tree" - ) + override fun removeNode(node: RBTreeNode?, key: K): Pair?, V?> { + if (node == null) return Pair(null, null) + + val removeResult: Pair?, V?> + val resultCompare: Int = key.compareTo(node.key) + var nodeCurrent: RBTreeNode = node + if (resultCompare < 0) { + if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) + nodeCurrent = moveRedLeft(nodeCurrent) + + removeResult = removeNode(nodeCurrent.leftChild, key) + nodeCurrent.leftChild = removeResult.first } else { - val nodeNew: RBTreeNode - val nodeCached: RBTreeNode - if (isRedColor(node)) { - nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode - nodeNew = RBTreeNode(nodeCached.key, nodeCached.value, nodeCached.isRed) - nodeNew.leftChild = leftChild - nodeNew.rightChild = removeNode(rightChild, nodeCached.key).first - if (!isRedColor(nodeCached)) leftChild.flipColor() + if (isRedColor(nodeCurrent.leftChild)) + nodeCurrent = rotateRight(nodeCurrent) + if (resultCompare == 0 && nodeCurrent.rightChild == null) + return Pair(null, nodeCurrent.value) + if (!isRedColor(nodeCurrent.rightChild) && !isRedLeftChild(nodeCurrent.rightChild)) + nodeCurrent = moveRedRight(nodeCurrent) + if (resultCompare == 0) { + val nodeWithMinimalKey = getMinNodeInSubtree(nodeCurrent.rightChild) as RBTreeNode + val nodeSubstitutive: RBTreeNode = createNode(nodeWithMinimalKey.key, nodeWithMinimalKey.value) + nodeSubstitutive.isRed = nodeCurrent.isRed + nodeSubstitutive.leftChild = nodeCurrent.leftChild + nodeSubstitutive.rightChild = removeMinNode(nodeCurrent.rightChild) + return Pair(balanceTree(nodeSubstitutive), nodeCurrent.value) } else { - if (isRedColor(leftChild)) { - nodeCached = getMaxNodeInSubtree(leftChild) as RBTreeNode - nodeNew = RBTreeNode(nodeCached.key, nodeCached.value, node.isRed) - nodeNew.rightChild = rightChild - nodeNew.leftChild = removeNode(leftChild, nodeCached.key).first - return Pair(balanceTree(nodeNew), node.value) - } - nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode - nodeNew = RBTreeNode(nodeCached.key, nodeCached.value, node.isRed) - nodeNew.leftChild = leftChild - nodeNew.rightChild = removeNode(rightChild, nodeCached.key).first - if (!isRedColor(nodeCached)) { - leftChild.isRed = true - nodeNew.isRed = true - } + removeResult = removeNode(nodeCurrent.rightChild, key) + nodeCurrent.rightChild = removeResult.first } - return Pair(balanceTree(nodeNew), node.value) } + + return Pair(balanceTree(nodeCurrent), removeResult.second) } /** @@ -154,11 +132,70 @@ public open class RBTree, V>: AbstractBSTree?): Unit { - if (node == null) return - node.flipColor() - notNullNodeUpdate(node.leftChild) { child -> child.flipColor() } - notNullNodeUpdate(node.rightChild) { child -> child.flipColor() } + protected fun flipColors(node: RBTreeNode): Unit { + node.isRed = !node.isRed + notNullNodeUpdate(node.leftChild) { child -> child.isRed = !child.isRed } + notNullNodeUpdate(node.rightChild) { child -> child.isRed = !child.isRed } + } + + /** + * This function is used to move a red node to the right, if it has a red left child of its [node] left child. + * It first flips the colors of the node and its children, then rotates the tree if the left child is also red. + * + * @param node the node to move + * @return the new root of the tree, which is balanced node subtree + */ + private fun moveRedRight(node: RBTreeNode): RBTreeNode { + var nodeCurrent: RBTreeNode = node + + flipColors(nodeCurrent) + if (isRedLeftChild(nodeCurrent.leftChild)) { + nodeCurrent = rotateRight(nodeCurrent) + flipColors(nodeCurrent) + } + return nodeCurrent + } + + /** + * This function is used to move a red node to the left, if it has a red right child of its [node] left child. + * It first flips the colors of the node and its children, then rotates the tree if the left child is also red. + * + * @param node the node to move + * @return the new root of the tree, which is balanced node subtree + */ + private fun moveRedLeft(node: RBTreeNode): RBTreeNode { + var nodeCurrent: RBTreeNode = node + + flipColors(nodeCurrent) + if (isRedLeftChild(nodeCurrent.rightChild)) { + nodeCurrent.rightChild = notNullNodeAction( + node.rightChild, null + ) {rightChild -> rotateRight(rightChild)} + nodeCurrent = rotateLeft(nodeCurrent) + flipColors(nodeCurrent) + } + return nodeCurrent + } + + /** + * Removes the node with the minimum key from the binary search tree. + * + * @param node the root of the binary search tree + * @return the root of the binary search tree with the node removed, or `null` if the tree is empty + */ + private fun removeMinNode(node: RBTreeNode?): RBTreeNode? { + if (node == null) return null + if (node.leftChild == null) return node.rightChild + + var nodeCurrent: RBTreeNode = node + if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) + nodeCurrent = moveRedLeft(nodeCurrent) + + nodeCurrent.leftChild = notNullNodeAction( + nodeCurrent.leftChild, null + ) {child -> removeMinNode(child)} + + return balanceTree(nodeCurrent) } -} +} \ No newline at end of file diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt index 2115276..f76dba0 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt @@ -11,23 +11,8 @@ public class RBTreeNode, V>( key: K, value: V ) : AbstractBSTreeNode>(key, value) { - var parent: RBTreeNode? = null var isRed: Boolean = true - override var leftChild: RBTreeNode? - get() = super.leftChild - set(value) { - if (value != null) value.parent = this - super.leftChild = value - } - - override var rightChild: RBTreeNode? - get() = super.rightChild - set(value) { - if (value != null) value.parent = this - super.rightChild = value - } - public constructor(key: K, value: V, isRed: Boolean) : this(key, value) { this.isRed = isRed } @@ -41,16 +26,6 @@ public class RBTreeNode, V>( this.rightChild = rightChild } - public fun getUncle(): RBTreeNode? { - val parent = this.parent ?: return null - if (parent.leftChild === this) return parent.rightChild - return parent.leftChild - } - - public fun flipColor(): Unit { - isRed = !isRed - } - override fun toStringSimpleView(): String { return "${super.toStringSimpleView()} - ${colorName()}" } @@ -62,4 +37,4 @@ public class RBTreeNode, V>( return if (isRed) "RED" else "BLACK" } -} +} \ No newline at end of file diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index f32baa1..51359dc 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource import tree_tripper.binary_trees.assistants.RBTreeTestAssistant @@ -15,75 +14,121 @@ class RBTreeTest { private lateinit var tree: RBTreeTestAssistant @BeforeEach - public fun setup() { + fun setup() { tree = RBTreeTestAssistant() } @Test - @DisplayName("tree initialization") public fun testTreeInitializing() { tree.assertRoot(null) { "Root of RBTree is not null by standard initialize." } tree.assertIsRBTree() Assertions.assertEquals(0, tree.getSize()) } + @Test + public fun testSimpleInsert() { + tree.insert(0, 0) + tree.assertRoot(RBTreeNode(0, 0, false)) { "Root of RBTree is not equal to inserted node." } + tree.assertIsRBTree() + Assertions.assertEquals(1, tree.getSize()) + } + + @Test + public fun testValueChangeInsert() { + tree.insert(0, 0) + tree.insert(0, 1) + tree.assertRoot(RBTreeNode(0, 1, false)) { "Root of RBTree is not equal to inserted node." } + } + + @ParameterizedTest + @MethodSource("testSortedInsertElementsCases") + public fun testSortedInsert(elements: List, expectedTreeView: RBTreeNode) { + val elementsSet = elements.toSet() + insert(elements.sorted()) + + tree.assertIsRBTree() + Assertions.assertEquals(elementsSet.size, tree.getSize()) + tree.assertRoot(expectedTreeView) { + "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}" + } + } + + @ParameterizedTest + @MethodSource("testReverseSortedInsertElementsCases") + public fun testReverseSortedInsert(elements: List, expectedTreeView: RBTreeNode) { + val elementsSet = elements.toSet() + insert(elements.sorted().reversed()) + + tree.assertIsRBTree() + Assertions.assertEquals(elementsSet.size, tree.getSize()) + tree.assertRoot(expectedTreeView) { + "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}" + } + } + + @ParameterizedTest + @MethodSource("testUnsortedInsertElementsCases") + public fun testUnsortedInsert(elements: List, expectedTreeView: RBTreeNode) { + val elementsSet = elements.toSet() + insert(elements) + + tree.assertIsRBTree() + Assertions.assertEquals(elementsSet.size, tree.getSize()) + tree.assertRoot(expectedTreeView) { + "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}\n" + "Tree view: ${tree.toStringWithTreeView()}" + } + } + @ParameterizedTest @MethodSource("testNodeColorCases") - @DisplayName("color of node") public fun testNodeColor(expected: Boolean, node: RBTreeNode?) { tree.assertNodeColor(expected, node) } @ParameterizedTest @MethodSource("testNodeLeftChildColorCases") - @DisplayName("color of left child of node") public fun testNodeLeftChildColor(expected: Boolean, node: RBTreeNode?) { tree.assertNodeLeftChildColor(expected, node) } @ParameterizedTest @MethodSource("testNodeRotateLeftCases") - @DisplayName("rotate node left") public fun testNodeRotateLeft(expected: RBTreeNode, node: RBTreeNode) { tree.assertNodeLeftRotation(expected, node) } @ParameterizedTest @MethodSource("testNodeRotateRightCases") - @DisplayName("rotate node right") public fun testNodeRotateRight(expected: RBTreeNode, node: RBTreeNode) { tree.assertNodeRightRotation(expected, node) } @ParameterizedTest @MethodSource("testNodeColorFlipCases") - @DisplayName("flip colors of node") - public fun testNodeColorFlip(expected: RBTreeNode?, node: RBTreeNode?) { + public fun testNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { tree.assertNodeColorFlip(expected, node) } @ParameterizedTest @MethodSource("testNodeCreationCases") - @DisplayName("create node") public fun testNodeCreation(key: Int, value: Int) { tree.assertNodeCreation(key, value) } @ParameterizedTest @MethodSource("testUpdateRootCases") - @DisplayName("update root") public fun testUpdateRoot(node: RBTreeNode?) { tree.assertUpdateRoot(node) } @ParameterizedTest @MethodSource("testBalanceTreeCases") - @DisplayName("balance tree") public fun testBalanceTree(expectedNodeTreeView: RBTreeNode, nodeTreeView: RBTreeNode) { tree.assertBalanceTree(expectedNodeTreeView, nodeTreeView) } - public companion object { + companion object { @JvmStatic fun testSortedInsertElementsCases(): List = listOf( @@ -263,7 +308,6 @@ class RBTreeTest { @JvmStatic fun testNodeColorFlipCases(): List = listOf( - Arguments.of(null, null), Arguments.of( RBTreeNode( 0, 0, false, null, null @@ -442,4 +486,4 @@ class RBTreeTest { return builder.toString() } -} +} \ No newline at end of file diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index 4485dcb..b051d27 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -74,7 +74,7 @@ public class RBTreeTestAssistant, V>: RBTree() { assertBinaryNodeDeepEquals(expected, rotateRight(node)) {n1, n2 -> n1.isRed == n2.isRed} } - fun assertNodeColorFlip(expected: RBTreeNode?, node: RBTreeNode?) { + fun assertNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { flipColors(node) assertBinaryNodeDeepEquals(expected, node) {n1, n2 -> n1.isRed == n2.isRed} } @@ -92,4 +92,4 @@ public class RBTreeTestAssistant, V>: RBTree() { assertBinaryNodeDeepEquals(expectedNodeTreeView, balanceTree(nodeTreeView)) {n1, n2 -> n1.isRed == n2.isRed} } -} +} \ No newline at end of file diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt index 07261a4..82ee58d 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -2,99 +2,48 @@ package tree_tripper.nodes.binary_nodes import assertBinaryNodeDeepEquals import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource -import tree_tripper.nodes.notNullNodeUpdate -import kotlin.test.assertEquals public class RBTreeNodeTest { @ParameterizedTest @MethodSource("testNodeSimpleInitializeCases") - @DisplayName("node simple initialization") public fun testNodeSimpleInitialize(key: Int, value: Int?) { val node = RBTreeNode(key, value) Assertions.assertEquals(key, node.key) { "Key of node is not equal." } Assertions.assertEquals(value, node.value) { "Value of node is not equal." } Assertions.assertTrue(node.isRed) { "Color of node is not red." } - Assertions.assertNull(node.parent) { "Parent of node is not null." } Assertions.assertNull(node.leftChild) { "Left child of node is not null." } Assertions.assertNull(node.rightChild) { "Right child of node is not null." } } @ParameterizedTest @MethodSource("testNodeColorTypeInitializeCases") - @DisplayName("node initialization with color") public fun testNodeColorTypeInitialize(isRed: Boolean) { val node = RBTreeNode(0, 0, isRed) Assertions.assertEquals(isRed, node.isRed) { "Color of node is not equal." } - Assertions.assertNull(node.parent) { "Parent of node is not null." } Assertions.assertNull(node.leftChild) { "Left child of node is not null." } Assertions.assertNull(node.rightChild) { "Right child of node is not null." } } @ParameterizedTest @MethodSource("testNodeFullInitializeCases") - @DisplayName("node initialization with color and children") public fun testNodeFullInitialize(leftChild: RBTreeNode?, rightChild: RBTreeNode?) { val node = RBTreeNode(0, 0, false, leftChild, rightChild) - Assertions.assertNull(node.parent) { "Parent of node is not null." } - assertBinaryNodeDeepEquals(leftChild, node.leftChild) { n1, n2 -> - n1.isRed == n2.isRed && n2.parent === node - } - assertBinaryNodeDeepEquals(rightChild, node.rightChild) { n1, n2 -> - n1.isRed == n2.isRed && n2.parent === node - } - } - - @Test - @DisplayName("setter of node parent property") - public fun testParentSetter() { - val node = RBTreeNode(0, 0) - Assertions.assertNull(node.parent) - - val parent = RBTreeNode(1, 1, false) - node.parent = parent - assertBinaryNodeDeepEquals(parent, node.parent) + assertBinaryNodeDeepEquals(leftChild, node.leftChild) { n1, n2 -> n1.isRed == n2.isRed } + assertBinaryNodeDeepEquals(rightChild, node.rightChild) { n1, n2 -> n1.isRed == n2.isRed } } @ParameterizedTest @MethodSource("testToStringSimpleViewCases") - @DisplayName("to string simple view") public fun testToStringSimpleView(expected: String, node: RBTreeNode) { Assertions.assertEquals(expected, node.toStringSimpleView()) } - @ParameterizedTest - @MethodSource("testGetUncleCases") - @DisplayName("getter of node uncle") - public fun testGetUncle(node: RBTreeNode) { - Assertions.assertNull(node.getUncle()) - notNullNodeUpdate(node.leftChild) { leftChild -> - Assertions.assertEquals( - node.rightChild, leftChild.getUncle() - ) - } - notNullNodeUpdate(node.rightChild) { rightChild -> - Assertions.assertEquals( - node.leftChild, rightChild.getUncle() - ) - } - } - - @ParameterizedTest - @MethodSource("testFlipColorCases") - @DisplayName("flip color of node") - public fun testFlipColor(node: RBTreeNode, expected: Boolean) { - node.flipColor() - assertEquals(expected, node.isRed) - } - - public companion object { + companion object { @JvmStatic fun testNodeSimpleInitializeCases(): List = listOf( Arguments.of(-1, -1), @@ -122,46 +71,6 @@ public class RBTreeNodeTest { Arguments.of("(-1: null) - RED", RBTreeNode(-1, null)), Arguments.of("(0: 0) - BLACK", RBTreeNode(0, 0, false)), ) - - @JvmStatic - fun testGetUncleCases(): List = listOf( - Arguments.of( - RBTreeNode(0, 0) - ), - Arguments.of( - RBTreeNode( - 0, 0, false, - RBTreeNode(-1, -1, true), - null - ) - ), - Arguments.of( - RBTreeNode( - 0, 0, true, - null, - RBTreeNode(1, 1, false), - ) - ), - Arguments.of( - RBTreeNode( - 0, 0, true, - RBTreeNode(-1, -1, false), - RBTreeNode(1, 1, false), - ) - ) - ) - - @JvmStatic - fun testFlipColorCases(): List = listOf( - Arguments.of( - RBTreeNode(0, 0, true), - false - ), - Arguments.of( - RBTreeNode(0, 0, false), - true - ) - ) } -} +} \ No newline at end of file From 25e69758f3fce65b1095395b170f0c2cdc5cda9a Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Wed, 3 Apr 2024 20:39:34 +0300 Subject: [PATCH 085/122] docs: add CAUTION information about red-black tree --- ReadMe.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ReadMe.md b/ReadMe.md index c88124c..bef03ce 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -19,6 +19,10 @@ Library `TreeTripper`: provides implementations of the following binary search t > of left-linear red-black trees described by Robert Sedgewick > on his [website](https://sedgewick.io/) and [presentation](https://sedgewick.io/wp-content/uploads/2022/03/2008-09LLRB.pdf) about it +> [!CAUTION] +> The red-black tree implementation of remove is not tests. +> Problems may arise at the stage of working with this method, currently in development + The library supports the extension both internally (future library updates) and externally (implemented by the user). ## Getting started From 5f054f37e755de1dc2990d8ea8fce1ea4eb26891 Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Wed, 3 Apr 2024 20:47:30 +0300 Subject: [PATCH 086/122] chore: change the build instruction and worlflow yml-script --- .../{build_and_test.yml => build-test.yml} | 7 +++++-- lib/build.gradle.kts | 19 +------------------ 2 files changed, 6 insertions(+), 20 deletions(-) rename .github/workflows/{build_and_test.yml => build-test.yml} (62%) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build-test.yml similarity index 62% rename from .github/workflows/build_and_test.yml rename to .github/workflows/build-test.yml index a216aff..99b5319 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build-test.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: push: - branches: [ "dev", "main" ] + branches: [ "main", "dev"] pull_request: branches: [ "main" ] @@ -15,5 +15,8 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Build source code and run test with jacoco + - name: Build source code + run: ./gradlew build + + - name: Run tests with jacoco run: ./gradlew test diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 02cd19e..29ad893 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -1,21 +1,11 @@ -/* - * 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. - * This project uses @Incubating APIs which are subject to change. - */ - plugins { // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. alias(libs.plugins.jvm) - // Apply the java-library plugin for API and implementation separation. - `java-library` - // Code coverage plugin jacoco + // Output project coverage id("org.barfuin.gradle.jacocolog") version "3.1.0" } @@ -33,13 +23,6 @@ dependencies { implementation(libs.guava) } -// Apply a specific Java toolchain to ease working on different environments. -java { - toolchain { - languageVersion = JavaLanguageVersion.of(19) - } -} - testing { suites { // Configure the built-in test suite From a3745b91ae2856d831a74e096cb5cd1347f35a6b Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Wed, 3 Apr 2024 21:04:32 +0300 Subject: [PATCH 087/122] fix: fix double run tests and incorrect work tests --- .github/workflows/build-test.yml | 2 +- .../kotlin/tree_tripper/binary_trees/BSTreeTest.kt | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 99b5319..86f35b1 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v3 - name: Build source code - run: ./gradlew build + run: ./gradlew build -x test - name: Run tests with jacoco run: ./gradlew test diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index fe8fd05..d7529b8 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -275,21 +275,22 @@ public class BSTreeTest { @MethodSource("getSizeAndTimeArguments") @DisplayName("remove with size and time") public fun testRemoveWithSizeAndTime(size: Int, seconds: Long) { - val array = IntArray(size) - var index = 0 + val setKeys: MutableSet = mutableSetOf() repeat(size) { val keyRandom = Random.nextInt(-1000, 1000) - array[index++] = keyRandom + setKeys.add(keyRandom) tree[keyRandom] = (-1) * keyRandom } assertTimeout(Duration.ofSeconds(seconds)) { repeat(10) { val keyRandom = Random.nextInt(-1000, 1000) - if (keyRandom in array) + if (keyRandom in setKeys) { assertEquals(tree.remove(keyRandom), (-1) * keyRandom, - "Incorrect return of remove an existent node.") - else + "Incorrect return of remove an existent node." + ) + setKeys.remove(keyRandom) + } else assertEquals(tree.remove(keyRandom), null, "Incorrect return of remove a non-existent node.") } From d3a5802044d91f593b3dfce2b3628f8b16e883e2 Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Wed, 3 Apr 2024 21:30:14 +0300 Subject: [PATCH 088/122] add '@DisplayName' to all RBTreeTest methods. Add a case of remove in BSTreeTest --- .../tree_tripper/binary_trees/BSTreeTest.kt | 11 ++++++++++- .../tree_tripper/binary_trees/RBTreeTest.kt | 15 +++++++++++++++ .../nodes/binary_nodes/RBTreeNodeTest.kt | 5 +++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index d7529b8..d639785 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -269,6 +269,10 @@ public class BSTreeTest { tree.assertIsBSTree() assertEquals(tree.search(1), -1, "Incorrect remove a root and lose the left child.") assertEquals(tree.search(3), -3, "Incorrect remove a root and lose the right child.") + + assertEquals(tree.remove(1), -1, "Incorrect remove a root.") + tree.assertIsBSTree() + assertEquals(tree.search(3), -3, "Incorrect remove a root and lose the right child.") } @ParameterizedTest @@ -333,11 +337,16 @@ public class BSTreeTest { @Test @DisplayName("tree to string") public fun testToString() { + var builder = StringBuilder("BSTreeTestAssistant(") + tree.forEach { builder.append("${it.first}: ${it.second}, ") } + builder.append(')') + assertEquals(tree.toString(), builder.toString(), "Incorrect construction string.") + + builder = StringBuilder("BSTreeTestAssistant(") tree[2] = -2 tree[1] = -1 tree[3] = -3 tree[4] = -4 - val builder = StringBuilder("BSTreeTestAssistant(") tree.forEach { builder.append("${it.first}: ${it.second}, ") } repeat(2) { builder.deleteCharAt(builder.length - 1) } builder.append(')') diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 51359dc..dc6f31b 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource import tree_tripper.binary_trees.assistants.RBTreeTestAssistant @@ -19,6 +20,7 @@ class RBTreeTest { } @Test + @DisplayName("tree initialization") public fun testTreeInitializing() { tree.assertRoot(null) { "Root of RBTree is not null by standard initialize." } tree.assertIsRBTree() @@ -26,6 +28,7 @@ class RBTreeTest { } @Test + @DisplayName("insert") public fun testSimpleInsert() { tree.insert(0, 0) tree.assertRoot(RBTreeNode(0, 0, false)) { "Root of RBTree is not equal to inserted node." } @@ -34,6 +37,7 @@ class RBTreeTest { } @Test + @DisplayName("double insert") public fun testValueChangeInsert() { tree.insert(0, 0) tree.insert(0, 1) @@ -42,6 +46,7 @@ class RBTreeTest { @ParameterizedTest @MethodSource("testSortedInsertElementsCases") + @DisplayName("insert sorted elements") public fun testSortedInsert(elements: List, expectedTreeView: RBTreeNode) { val elementsSet = elements.toSet() insert(elements.sorted()) @@ -55,6 +60,7 @@ class RBTreeTest { @ParameterizedTest @MethodSource("testReverseSortedInsertElementsCases") + @DisplayName("insert reverse sorted elements") public fun testReverseSortedInsert(elements: List, expectedTreeView: RBTreeNode) { val elementsSet = elements.toSet() insert(elements.sorted().reversed()) @@ -68,6 +74,7 @@ class RBTreeTest { @ParameterizedTest @MethodSource("testUnsortedInsertElementsCases") + @DisplayName("insert unsorted elements") public fun testUnsortedInsert(elements: List, expectedTreeView: RBTreeNode) { val elementsSet = elements.toSet() insert(elements) @@ -82,48 +89,56 @@ class RBTreeTest { @ParameterizedTest @MethodSource("testNodeColorCases") + @DisplayName("color of node") public fun testNodeColor(expected: Boolean, node: RBTreeNode?) { tree.assertNodeColor(expected, node) } @ParameterizedTest @MethodSource("testNodeLeftChildColorCases") + @DisplayName("color of left child of node") public fun testNodeLeftChildColor(expected: Boolean, node: RBTreeNode?) { tree.assertNodeLeftChildColor(expected, node) } @ParameterizedTest @MethodSource("testNodeRotateLeftCases") + @DisplayName("rotate node left") public fun testNodeRotateLeft(expected: RBTreeNode, node: RBTreeNode) { tree.assertNodeLeftRotation(expected, node) } @ParameterizedTest @MethodSource("testNodeRotateRightCases") + @DisplayName("rotate node right") public fun testNodeRotateRight(expected: RBTreeNode, node: RBTreeNode) { tree.assertNodeRightRotation(expected, node) } @ParameterizedTest @MethodSource("testNodeColorFlipCases") + @DisplayName("flip colors of node") public fun testNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { tree.assertNodeColorFlip(expected, node) } @ParameterizedTest @MethodSource("testNodeCreationCases") + @DisplayName("create node") public fun testNodeCreation(key: Int, value: Int) { tree.assertNodeCreation(key, value) } @ParameterizedTest @MethodSource("testUpdateRootCases") + @DisplayName("update root") public fun testUpdateRoot(node: RBTreeNode?) { tree.assertUpdateRoot(node) } @ParameterizedTest @MethodSource("testBalanceTreeCases") + @DisplayName("balance tree") public fun testBalanceTree(expectedNodeTreeView: RBTreeNode, nodeTreeView: RBTreeNode) { tree.assertBalanceTree(expectedNodeTreeView, nodeTreeView) } diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt index 82ee58d..5a71782 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -2,6 +2,7 @@ package tree_tripper.nodes.binary_nodes import assertBinaryNodeDeepEquals import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -11,6 +12,7 @@ public class RBTreeNodeTest { @ParameterizedTest @MethodSource("testNodeSimpleInitializeCases") + @DisplayName("node simple initialization") public fun testNodeSimpleInitialize(key: Int, value: Int?) { val node = RBTreeNode(key, value) Assertions.assertEquals(key, node.key) { "Key of node is not equal." } @@ -22,6 +24,7 @@ public class RBTreeNodeTest { @ParameterizedTest @MethodSource("testNodeColorTypeInitializeCases") + @DisplayName("node initialization with color") public fun testNodeColorTypeInitialize(isRed: Boolean) { val node = RBTreeNode(0, 0, isRed) Assertions.assertEquals(isRed, node.isRed) { "Color of node is not equal." } @@ -31,6 +34,7 @@ public class RBTreeNodeTest { @ParameterizedTest @MethodSource("testNodeFullInitializeCases") + @DisplayName("node initialization with color and children") public fun testNodeFullInitialize(leftChild: RBTreeNode?, rightChild: RBTreeNode?) { val node = RBTreeNode(0, 0, false, leftChild, rightChild) assertBinaryNodeDeepEquals(leftChild, node.leftChild) { n1, n2 -> n1.isRed == n2.isRed } @@ -39,6 +43,7 @@ public class RBTreeNodeTest { @ParameterizedTest @MethodSource("testToStringSimpleViewCases") + @DisplayName("to string simple view") public fun testToStringSimpleView(expected: String, node: RBTreeNode) { Assertions.assertEquals(expected, node.toStringSimpleView()) } From 80c6352706b3d272d373d972e4a7676d223c0a28 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Fri, 5 Apr 2024 12:02:28 +0300 Subject: [PATCH 089/122] fix: add updating size after remove --- lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt index 97b360d..6c2e216 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -35,6 +35,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< override fun remove(key: K): V? { val resultRemove = removeNode(root, key) updateRoot(resultRemove.first) + if (resultRemove.second != null) size-- return resultRemove.second } From 6112520944f869b4216c5eeecdc8eb06c166c064 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Fri, 5 Apr 2024 12:05:20 +0300 Subject: [PATCH 090/122] fix: update balanceTree and removeNode and removeMinNode methods at RBTree --- .../tree_tripper/binary_trees/RBTree.kt | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index cadcf0b..eebe016 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -24,7 +24,7 @@ public open class RBTree, V>: AbstractBSTree): RBTreeNode { var nodeCurrent: RBTreeNode = node - if (isRedColor(nodeCurrent.rightChild)) { + if (isRedColor(nodeCurrent.rightChild) && !isRedColor(nodeCurrent.leftChild)) { nodeCurrent = rotateLeft(nodeCurrent) } if (isRedColor(nodeCurrent.leftChild) && isRedLeftChild(nodeCurrent.leftChild)) { @@ -40,21 +40,25 @@ public open class RBTree, V>: AbstractBSTree?, V?> - val resultCompare: Int = key.compareTo(node.key) + var resultCompare: Int = key.compareTo(node.key) var nodeCurrent: RBTreeNode = node if (resultCompare < 0) { - if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) + if (nodeCurrent.leftChild != null && !isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) nodeCurrent = moveRedLeft(nodeCurrent) removeResult = removeNode(nodeCurrent.leftChild, key) nodeCurrent.leftChild = removeResult.first } else { - if (isRedColor(nodeCurrent.leftChild)) + if (isRedColor(nodeCurrent.leftChild)) { nodeCurrent = rotateRight(nodeCurrent) + resultCompare = key.compareTo(nodeCurrent.key) + } if (resultCompare == 0 && nodeCurrent.rightChild == null) return Pair(null, nodeCurrent.value) - if (!isRedColor(nodeCurrent.rightChild) && !isRedLeftChild(nodeCurrent.rightChild)) + if (nodeCurrent.rightChild != null && !isRedColor(nodeCurrent.rightChild) && !isRedLeftChild(nodeCurrent.rightChild)) { nodeCurrent = moveRedRight(nodeCurrent) + resultCompare = key.compareTo(nodeCurrent.key) + } if (resultCompare == 0) { val nodeWithMinimalKey = getMinNodeInSubtree(nodeCurrent.rightChild) as RBTreeNode val nodeSubstitutive: RBTreeNode = createNode(nodeWithMinimalKey.key, nodeWithMinimalKey.value) @@ -185,15 +189,14 @@ public open class RBTree, V>: AbstractBSTree?): RBTreeNode? { if (node == null) return null - if (node.leftChild == null) return node.rightChild + + val leftChild = node.leftChild ?: return node.rightChild var nodeCurrent: RBTreeNode = node - if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) + if (!isRedColor(leftChild) && !isRedLeftChild(leftChild)) nodeCurrent = moveRedLeft(nodeCurrent) - nodeCurrent.leftChild = notNullNodeAction( - nodeCurrent.leftChild, null - ) {child -> removeMinNode(child)} + nodeCurrent.leftChild = removeMinNode(leftChild) return balanceTree(nodeCurrent) } From fea92ad6b5bbe8c95e4b24d2a18fe6c06537e886 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Fri, 5 Apr 2024 12:08:39 +0300 Subject: [PATCH 091/122] test: update test cases of insert, rotations and add tests for remove method at RBTree. Add assertion by black height of tree to check valid tree state --- .../tree_tripper/binary_trees/RBTreeTest.kt | 655 +++++++----------- .../assistants/RBTreeTestAssistant.kt | 27 +- 2 files changed, 247 insertions(+), 435 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index dc6f31b..f6fceee 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -1,504 +1,313 @@ package tree_tripper.binary_trees import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.MethodSource import tree_tripper.binary_trees.assistants.RBTreeTestAssistant import tree_tripper.nodes.binary_nodes.RBTreeNode +import kotlin.random.Random +import kotlin.random.nextInt class RBTreeTest { private lateinit var tree: RBTreeTestAssistant + private val randomizer = Random(42) @BeforeEach - fun setup() { + public fun setup() { tree = RBTreeTestAssistant() } @Test - @DisplayName("tree initialization") - public fun testTreeInitializing() { - tree.assertRoot(null) { "Root of RBTree is not null by standard initialize." } + @DisplayName("is root after initializing equals null") + public fun testInitializing() { + tree.assertRoot(null) { "Root is not null after init" } tree.assertIsRBTree() Assertions.assertEquals(0, tree.getSize()) } @Test - @DisplayName("insert") - public fun testSimpleInsert() { - tree.insert(0, 0) - tree.assertRoot(RBTreeNode(0, 0, false)) { "Root of RBTree is not equal to inserted node." } - tree.assertIsRBTree() - Assertions.assertEquals(1, tree.getSize()) + @DisplayName("insert sorted elements") + public fun testInsertSortedElements() { + for (i in 0..20) { + tree.insert(i, i) + tree.assertIsRBTree() + Assertions.assertEquals(i + 1, tree.getSize()) + } + } @Test - @DisplayName("double insert") - public fun testValueChangeInsert() { - tree.insert(0, 0) - tree.insert(0, 1) - tree.assertRoot(RBTreeNode(0, 1, false)) { "Root of RBTree is not equal to inserted node." } - } + @DisplayName("insert reversed sorted elements") + public fun testInsertReverseSortedElements() { + for (i in 20 downTo 0) { + tree.insert(i, i) + tree.assertIsRBTree() + Assertions.assertEquals((20 - i) + 1, tree.getSize()) + } - @ParameterizedTest - @MethodSource("testSortedInsertElementsCases") - @DisplayName("insert sorted elements") - public fun testSortedInsert(elements: List, expectedTreeView: RBTreeNode) { - val elementsSet = elements.toSet() - insert(elements.sorted()) + } - tree.assertIsRBTree() - Assertions.assertEquals(elementsSet.size, tree.getSize()) - tree.assertRoot(expectedTreeView) { - "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}" + @Test + @DisplayName("insert a lot of unsorted elements") + public fun testInsertUnsortedElements() { + val elements: MutableSet = mutableSetOf() + for (i in 0..256) { + val value = randomizer.nextInt() + tree.insert(value, value) + elements.add(value) + tree.assertIsRBTree() + Assertions.assertEquals(elements.size, tree.getSize()) } } - @ParameterizedTest - @MethodSource("testReverseSortedInsertElementsCases") - @DisplayName("insert reverse sorted elements") - public fun testReverseSortedInsert(elements: List, expectedTreeView: RBTreeNode) { - val elementsSet = elements.toSet() - insert(elements.sorted().reversed()) - + @Test + @DisplayName("remove root with children") + public fun testRemoveRootWithChildren() { + for (i in 0..20) tree.insert(i, i) + val root: Pair = tree.getRoot() + Assertions.assertEquals(root.second, tree.remove(root.first)) tree.assertIsRBTree() - Assertions.assertEquals(elementsSet.size, tree.getSize()) - tree.assertRoot(expectedTreeView) { - "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}" - } + Assertions.assertEquals(20, tree.getSize()) } - @ParameterizedTest - @MethodSource("testUnsortedInsertElementsCases") - @DisplayName("insert unsorted elements") - public fun testUnsortedInsert(elements: List, expectedTreeView: RBTreeNode) { - val elementsSet = elements.toSet() - insert(elements) - + @Test + @DisplayName("remove black node with children") + public fun testRemoveBlackNodeWithChildren() { + for (i in 0..20) tree.insert(i, i) + Assertions.assertEquals(15, tree.remove(15)) tree.assertIsRBTree() - Assertions.assertEquals(elementsSet.size, tree.getSize()) - tree.assertRoot(expectedTreeView) { - "Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}\n" - "Tree view: ${tree.toStringWithTreeView()}" - } + Assertions.assertEquals(20, tree.getSize()) } - - @ParameterizedTest - @MethodSource("testNodeColorCases") - @DisplayName("color of node") - public fun testNodeColor(expected: Boolean, node: RBTreeNode?) { - tree.assertNodeColor(expected, node) + @Test + @DisplayName("remove red node with children") + public fun testRemoveRedNodeWithChildren() { + for (i in 0..20) tree.insert(i, i) + Assertions.assertEquals(1, tree.remove(1)) + tree.assertIsRBTree() + Assertions.assertEquals(20, tree.getSize()) } - @ParameterizedTest - @MethodSource("testNodeLeftChildColorCases") - @DisplayName("color of left child of node") - public fun testNodeLeftChildColor(expected: Boolean, node: RBTreeNode?) { - tree.assertNodeLeftChildColor(expected, node) + @Test + @DisplayName("remove random node") + public fun testRemoveRandomNodeChildren() { + for (i in 0..20) tree.insert(i, i) + val key = randomizer.nextInt(0..20) + try { + Assertions.assertEquals(key, tree.remove(key)) + tree.assertIsRBTree() + Assertions.assertEquals(20, tree.getSize()) + } catch (e: AssertionError) { + throw AssertionError( + "Try remove node with key $key from tree: ${tree.toStringWithTreeView()}", + e + ) + } } - @ParameterizedTest - @MethodSource("testNodeRotateLeftCases") - @DisplayName("rotate node left") - public fun testNodeRotateLeft(expected: RBTreeNode, node: RBTreeNode) { - tree.assertNodeLeftRotation(expected, node) - } + @Test + @DisplayName("remove root without children") + public fun testRemoveRootWithoutChildren() { + tree.insert(0, 0) + Assertions.assertEquals(0, tree.remove(0)) + Assertions.assertEquals(0, tree.getSize()) - @ParameterizedTest - @MethodSource("testNodeRotateRightCases") - @DisplayName("rotate node right") - public fun testNodeRotateRight(expected: RBTreeNode, node: RBTreeNode) { - tree.assertNodeRightRotation(expected, node) } - @ParameterizedTest - @MethodSource("testNodeColorFlipCases") - @DisplayName("flip colors of node") - public fun testNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { - tree.assertNodeColorFlip(expected, node) + @Test + @DisplayName("remove black node without children") + public fun testRemoveBlackNodeWithoutChildren() { + for (i in 0..20) tree.insert(i, i) + Assertions.assertEquals(6, tree.remove(6)) + tree.assertIsRBTree() + Assertions.assertEquals(20, tree.getSize()) } - - @ParameterizedTest - @MethodSource("testNodeCreationCases") - @DisplayName("create node") - public fun testNodeCreation(key: Int, value: Int) { - tree.assertNodeCreation(key, value) + @Test + @DisplayName("remove red node without children") + public fun testRemoveRedNodeWithoutChildren() { + for (i in 0..21) tree.insert(i, i) + Assertions.assertEquals(20, tree.remove(20)) + tree.assertIsRBTree() + Assertions.assertEquals(21, tree.getSize()) } - @ParameterizedTest - @MethodSource("testUpdateRootCases") - @DisplayName("update root") - public fun testUpdateRoot(node: RBTreeNode?) { - tree.assertUpdateRoot(node) + @Test + @DisplayName("remove root with one left child") + public fun testRemoveRootWithOneLeftChild() { + tree.insert(0, 0) + tree.insert(-1, -1) + Assertions.assertEquals(0, tree.remove(0)) + Assertions.assertEquals(1, tree.getSize()) + Assertions.assertEquals(Pair(-1, -1), tree.getRoot()) } - @ParameterizedTest - @MethodSource("testBalanceTreeCases") - @DisplayName("balance tree") - public fun testBalanceTree(expectedNodeTreeView: RBTreeNode, nodeTreeView: RBTreeNode) { - tree.assertBalanceTree(expectedNodeTreeView, nodeTreeView) + @Test + @DisplayName("left rotate node without right") + public fun testLeftRotateNodeWithoutRightChild() { + val node = RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), + null, + ) + tree.assertNodeLeftRotation( + node, node + ) } - companion object { - - @JvmStatic - fun testSortedInsertElementsCases(): List = listOf( - Arguments.of( - listOf(10, 20, -10), - RBTreeNode(10,10, false, - RBTreeNode(-10, -10, false), - RBTreeNode(20, 20, false) - ) - ), - Arguments.of( - listOf(-10, -20, 10), - RBTreeNode(-10, -10, false, - RBTreeNode(-20, -20, false), - RBTreeNode(10, 10, false), - ) + @Test + @DisplayName("left rotate subtree") + public fun testLeftRotateOf() { + tree.assertNodeLeftRotation( + RBTreeNode( + 2, 2, false, + RBTreeNode( + 0, 0, true, + RBTreeNode(-1, -1, false), + RBTreeNode(1, 1, false) + ), + RBTreeNode(3, 3, false) ), - Arguments.of( - listOf(15, 34, -23, 20, 10, -100), - RBTreeNode(15, 15, false, - RBTreeNode(-23, -23, true, - RBTreeNode(-100, -100, false), - RBTreeNode(10, 10, false) - ), - RBTreeNode(34, 34, false, - RBTreeNode(20, 20, true), null - ) + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, false), + RBTreeNode( + 2, 2, true, + RBTreeNode(1, 1, false), + RBTreeNode(3, 3, false), ) ), ) + } - @JvmStatic - fun testReverseSortedInsertElementsCases(): List = listOf( - Arguments.of( - listOf(10, 20, -10), - RBTreeNode(10,10, false, - RBTreeNode(-10, -10, false), - RBTreeNode(20, 20, false) - ) - ), - Arguments.of( - listOf(-10, -20, 10), - RBTreeNode(-10, -10, false, - RBTreeNode(-20, -20, false), - RBTreeNode(10, 10, false), - ) - ), - Arguments.of( - listOf(15, 34, -23, 20, 10, -100), - RBTreeNode(20, 20, false, - RBTreeNode(10, 10, true, - RBTreeNode(-23, -23, false, - RBTreeNode(-100, -100), - null - ), - RBTreeNode(15, 15, false) - ), - RBTreeNode(34, 34, false) - ) - ), + @Test + @DisplayName("right rotate node without left") + public fun testRightRotateNodeWithoutLeftChild() { + val node = RBTreeNode( + 0, 0, false, + null, + RBTreeNode(1, 1, true) ) + tree.assertNodeRightRotation( + node, node + ) + } - @JvmStatic - fun testUnsortedInsertElementsCases(): List = listOf( - Arguments.of( - listOf(10, 20, -10), - RBTreeNode(10,10, false, - RBTreeNode(-10, -10, false), - RBTreeNode(20, 20, false) - ) - ), - Arguments.of( - listOf(-10, -20, 10), - RBTreeNode(-10, -10, false, - RBTreeNode(-20, -20, false), - RBTreeNode(10, 10, false), + @Test + @DisplayName("right rotate of subtree") + public fun testRightRotate() { + tree.assertNodeRightRotation( + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, false), + RBTreeNode( + 2, 2, true, + RBTreeNode(1, 1, false), + RBTreeNode(3, 3, false), ) ), - Arguments.of( - listOf(15, 34, -23, 20, 10, -100), - RBTreeNode(15, 15, false, - RBTreeNode(-23, -23, true, - RBTreeNode(-100, -100, false), - RBTreeNode(10, 10, false) - ), - RBTreeNode(34, 34, false, - RBTreeNode(20, 20, true), null - ) - ) + RBTreeNode( + 2, 2, false, + RBTreeNode( + 0, 0, true, + RBTreeNode(-1, -1, false), + RBTreeNode(1, 1, false) + ), + RBTreeNode(3, 3, false) ), ) + } - @JvmStatic - fun testNodeColorCases(): List = listOf( - Arguments.of(false, null), - Arguments.of(true, RBTreeNode(0, 0, true)), - Arguments.of(false, RBTreeNode(0, 0, false)), + @Test + @DisplayName("flip colors of node and repeat it") + public fun testFlipColors() { + val node = RBTreeNode(0, 0, false, RBTreeNode(-1, -1), RBTreeNode(1, 1)) + tree.assertNodeColorFlip( + RBTreeNode( + 0, 0, true, + RBTreeNode(-1, -1, false), + RBTreeNode(1, 1, false) + ), node + ) + tree.assertNodeColorFlip( + RBTreeNode(0, 0, false, RBTreeNode(-1, -1), RBTreeNode(1, 1)), + node ) + } - @JvmStatic - fun testNodeLeftChildColorCases(): List = listOf( - Arguments.of(false, null), - Arguments.of(false, RBTreeNode(0, 0, true)), - Arguments.of(false, RBTreeNode(0, 0, false)), - Arguments.of( - false, RBTreeNode( - 1, 1, true, null, RBTreeNode(0, 0, true) - ) - ), - Arguments.of( - false, RBTreeNode( - 1, 1, true, null, RBTreeNode(0, 0, false) - ) - ), - Arguments.of( - true, RBTreeNode( - 1, 1, true, RBTreeNode(0, 0, true), null - ) - ), - Arguments.of( - false, RBTreeNode( - 1, 1, true, RBTreeNode(0, 0, false), null - ) + @Test + @DisplayName("is left child of red null node") + public fun testIsRedLeftChildOfNullNode() { + tree.assertNodeLeftChildColor(false, null) + } + + @Test + @DisplayName("is red left child of node which property isRed equals true") + public fun testIsRedLeftChildOfNodeWhichPropertyEqualsRed() { + tree.assertNodeLeftChildColor( + true, + RBTreeNode( + 0, 0, false, + RBTreeNode(-1, -1, true), + null ) ) + } - @JvmStatic - fun testNodeRotateLeftCases(): List = listOf( - Arguments.of( - RBTreeNode( - 0, 0, false, null, null - ), - RBTreeNode( - 0, 0, false, null, null - ), - ), Arguments.of( - RBTreeNode( - 1, 1, false, RBTreeNode(0, 0, true), null - ), - RBTreeNode( - 0, 0, false, null, RBTreeNode(1, 1, true) - ), - ), Arguments.of( - RBTreeNode( - 1, 1, true, RBTreeNode(0, 0, true), null - ), RBTreeNode( - 0, 0, true, null, RBTreeNode(1, 1, true) - ) - ), Arguments.of( - RBTreeNode( - 1, 1, false, RBTreeNode(0, 0, true), null - ), RBTreeNode( - 0, 0, false, null, RBTreeNode(1, 1, false) - ) + @Test + @DisplayName("is red left child of node with property isRed equals false") + public fun testIsRedLeftChildOfNodeWhichPropertyEqualsBlack() { + tree.assertNodeLeftChildColor( + false, + RBTreeNode( + 0, 0, true, + RBTreeNode(-1, -1, false), + null ) ) + } - @JvmStatic - fun testNodeRotateRightCases(): List = listOf( - Arguments.of( - RBTreeNode( - 0, 0, false, null, null - ), - RBTreeNode( - 0, 0, false, null, null - ), - ), - Arguments.of( - RBTreeNode( - 1, 1, false, null, RBTreeNode(0, 0, true) - ), - RBTreeNode( - 0, 0, false, RBTreeNode(1, 1, true), null - ), - ), - ) + @Test + @DisplayName("is red null node") + public fun testIsRedNullNode() { + tree.assertNodeColor(false, null) + } - @JvmStatic - fun testNodeColorFlipCases(): List = listOf( - Arguments.of( - RBTreeNode( - 0, 0, false, null, null - ), - RBTreeNode( - 0, 0, true, null, null - ), - ), - Arguments.of( - RBTreeNode( - 0, 0, false, null, null - ), - RBTreeNode( - 0, 0, true, null, null - ), - ), - Arguments.of( - RBTreeNode( - 0, 0, false, RBTreeNode(1, 1, false), null - ), - RBTreeNode( - 0, 0, true, RBTreeNode(1, 1), null - ), - ), - Arguments.of( - RBTreeNode( - 0, 0, false, null, RBTreeNode(1, 1, true) - ), - RBTreeNode( - 0, 0, true, null, RBTreeNode(1, 1, false) - ), - ), - Arguments.of( - RBTreeNode( - 0, 0, false, RBTreeNode(2, 2, false), RBTreeNode(1, 1, true) - ), - RBTreeNode( - 0, 0, true, RBTreeNode(2, 2), RBTreeNode(1, 1, false) - ), - ), - ) + @Test + @DisplayName("is red node with property isRed equals true") + public fun testIsRedNodeWithPropertyEqualsRed() { + tree.assertNodeColor(true, RBTreeNode(0, 0, true)) + } - @JvmStatic - fun testNodeCreationCases(): List = listOf( - Arguments.of(0, 0), - Arguments.of(1, 1), - Arguments.of(-1, -1), - ) + @Test + @DisplayName("is red node with property isRed equals false") + public fun testIsRedNodeWithPropertyEqualsBlack() { + tree.assertNodeColor(false, RBTreeNode(0, 0, false)) + } - @JvmStatic - fun testUpdateRootCases(): List = listOf( - Arguments.of(null), - Arguments.of(RBTreeNode(0, 0, true)), - Arguments.of(RBTreeNode(0, 0, false)), - ) - @JvmStatic - fun testBalanceTreeCases(): List = listOf( - Arguments.of( - RBTreeNode(0, 0, false), RBTreeNode(0, 0, false) - ), - Arguments.of( - RBTreeNode( - 0, 0, false, - RBTreeNode(-1, -1, true), null - ), - RBTreeNode( - 0, 0, false, - RBTreeNode(-1, -1, true), null - ) - ), - Arguments.of( - RBTreeNode( - 1, 1, false, - RBTreeNode(0, 0, true), null - ), - RBTreeNode( - 0, 0, false, - null, RBTreeNode(1, 1, true) - ) - ), - Arguments.of( - RBTreeNode( - 0, 0, true, - null, RBTreeNode(1, 1, false) - ), - RBTreeNode( - 0, 0, true, - null, RBTreeNode(1, 1, false) - ) - ), - Arguments.of( - RBTreeNode( - 0, 0, false, - RBTreeNode(-1, -1, false), RBTreeNode(1, 1, false) - ), - RBTreeNode( - 0, 0, false, - RBTreeNode(-1, -1, false), RBTreeNode(1, 1, false) - ) - ), - Arguments.of( - RBTreeNode( - 0, 0, false, - RBTreeNode(-1, -1, true), RBTreeNode(1, 1, false) - ), - RBTreeNode( - 0, 0, false, - RBTreeNode(-1, -1, true), RBTreeNode(1, 1, false) - ) - ), - Arguments.of( - RBTreeNode( - 1, 1, false, - RBTreeNode( - 0, 0, true, - RBTreeNode(-1, -1, false), null - ), - null - ), - RBTreeNode( - 0, 0, false, - RBTreeNode(-1, -1, false), RBTreeNode(1, 1, true) - ) - ), - Arguments.of( - RBTreeNode( - 0, 0, true, - RBTreeNode(-1, -1, false), RBTreeNode(1, 1, false) - ), - RBTreeNode( - 0, 0, false, - RBTreeNode(-1, -1, true), RBTreeNode(1, 1, true) - ) - ), - Arguments.of( - RBTreeNode( - -1, -1, true, - RBTreeNode(-2, -2, false), RBTreeNode(0, 0, false) - ), - RBTreeNode( - 0, 0, false, - RBTreeNode( - -1, -1, true, RBTreeNode(-2, -2), null - ), null - ) - ), - Arguments.of( - RBTreeNode( - 0, 0, true, - RBTreeNode( - -1, -1, false, - RBTreeNode(-2, -2), null - ), RBTreeNode(1, 1, false) - ), - RBTreeNode( - 0, 0, false, - RBTreeNode( - -1, -1, true, RBTreeNode(-2, -2), null - ), - RBTreeNode(1, 1) - ) - ), - ) + @Test + @DisplayName("create node") + public fun testCreateNode() { + tree.assertNodeCreation(0, 0) } - private fun insert(elements: List) { - for (element in elements) { - tree[element] = element - } + @Test + @DisplayName("update root as null") + public fun testUpdateRootAsNull() { + tree.assertUpdateRoot(null) + } + + @Test + @DisplayName("update root as red node") + public fun testUpdateRootAsRedNode() { + tree.assertUpdateRoot(RBTreeNode(0, 0, true)) } - private fun nodeToStringTreeView(node: RBTreeNode): String { - val builder = StringBuilder() - node.toStringWithSubtreeView(0, builder) - return builder.toString() + @Test + @DisplayName("update root as black node") + public fun testUpdateRootAsBlackNode() { + tree.assertUpdateRoot(RBTreeNode(0, 0, false)) } } \ No newline at end of file diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index b051d27..94c309b 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -37,17 +37,15 @@ public class RBTreeTestAssistant, V>: RBTree() { } queue.addAll(node.getChildren()) } + assertBlackHeight(root) } - public fun checkTree(checkAction: (RBTreeNode) -> Boolean): Boolean { - val queue: Queue> = LinkedList>(listOf(root)) - - while (queue.isNotEmpty()) { - val node: RBTreeNode = queue.remove() - if (!checkAction(node)) return false - queue.addAll(node.getChildren()) - } - return true + private fun assertBlackHeight(node: RBTreeNode?): Int { + if (node == null) return 1 + val left = assertBlackHeight(node.leftChild) + val right = assertBlackHeight(node.rightChild) + Assertions.assertEquals(left, right) + return (if (node.isRed) 0 else 1) + left } fun assertRoot(node: RBTreeNode?, lazyMassage: () -> String) { @@ -85,11 +83,16 @@ public class RBTreeTestAssistant, V>: RBTree() { fun assertUpdateRoot(node: RBTreeNode?) { updateRoot(node) - assertBinaryNodeDataEquals(root, if (node != null) RBTreeNode(node.key, node.value, false) else null) + assertBinaryNodeDataEquals( + root, + if (node != null) RBTreeNode(node.key, node.value, false) else null + ) } - fun assertBalanceTree(expectedNodeTreeView: RBTreeNode, nodeTreeView: RBTreeNode) { - assertBinaryNodeDeepEquals(expectedNodeTreeView, balanceTree(nodeTreeView)) {n1, n2 -> n1.isRed == n2.isRed} + fun getRoot(): Pair { + val root = this.root + if (root == null) throw NullPointerException("Tree is empty can't get root pair") + return Pair(root.key, root.value) } } \ No newline at end of file From 5a7869bcbe63e99e36ed04e41f02bae8f7a93c57 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Fri, 5 Apr 2024 12:14:02 +0300 Subject: [PATCH 092/122] test: update test of remove for BSTree to assertion size changed after remove some nodes --- .../test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index d639785..633c842 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -253,9 +253,11 @@ public class BSTreeTest { public fun testRemove() { tree[1] = -1 assertEquals(tree.remove(1), -1, "Incorrect remove root.") + assertEquals(0, tree.getSize()) tree.assertIsBSTree() assertEquals(tree.remove(1), null, "Incorrect remove a non-existent root.") + assertEquals(0, tree.getSize()) tree.assertIsBSTree() } @@ -266,12 +268,14 @@ public class BSTreeTest { tree[1] = -1 tree[3] = -3 assertEquals(tree.remove(2), -2, "Incorrect remove a root.") + assertEquals(2, tree.getSize()) tree.assertIsBSTree() assertEquals(tree.search(1), -1, "Incorrect remove a root and lose the left child.") assertEquals(tree.search(3), -3, "Incorrect remove a root and lose the right child.") assertEquals(tree.remove(1), -1, "Incorrect remove a root.") tree.assertIsBSTree() + assertEquals(1, tree.getSize()) assertEquals(tree.search(3), -3, "Incorrect remove a root and lose the right child.") } @@ -285,7 +289,6 @@ public class BSTreeTest { setKeys.add(keyRandom) tree[keyRandom] = (-1) * keyRandom } - assertTimeout(Duration.ofSeconds(seconds)) { repeat(10) { val keyRandom = Random.nextInt(-1000, 1000) @@ -297,6 +300,7 @@ public class BSTreeTest { } else assertEquals(tree.remove(keyRandom), null, "Incorrect return of remove a non-existent node.") + assertEquals(setKeys.size, tree.getSize()) } } @@ -308,10 +312,12 @@ public class BSTreeTest { public fun testRemoveOrDefault() { assertEquals(tree.removeOrDefault(1, 0), 0, "Incorrect return of remove a non-existent node.") + assertEquals(0, tree.getSize()) tree.insert(1, -1) assertEquals(tree.removeOrDefault(1, 0), -1, "Incorrect return of remove an existent node.") + assertEquals(0, tree.getSize()) } @Test From b97dbbd20366c88a58fc4eaf2370033964406a5c Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Fri, 5 Apr 2024 12:28:31 +0300 Subject: [PATCH 093/122] feat: java language type of as 19 version is being standardized --- lib/build.gradle.kts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 29ad893..48a49ca 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -23,6 +23,13 @@ dependencies { implementation(libs.guava) } +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(19) + } +} + testing { suites { // Configure the built-in test suite From 853e8e01cc730a3327e10583289c971e787fc3f5 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Fri, 5 Apr 2024 12:30:02 +0300 Subject: [PATCH 094/122] refactor: clear readme by removing caution information. Update get started info as build project script and run tests script --- ReadMe.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index bef03ce..406ed99 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -19,16 +19,17 @@ Library `TreeTripper`: provides implementations of the following binary search t > of left-linear red-black trees described by Robert Sedgewick > on his [website](https://sedgewick.io/) and [presentation](https://sedgewick.io/wp-content/uploads/2022/03/2008-09LLRB.pdf) about it -> [!CAUTION] -> The red-black tree implementation of remove is not tests. -> Problems may arise at the stage of working with this method, currently in development - The library supports the extension both internally (future library updates) and externally (implemented by the user). ## Getting started To run building library execute command: ```bash -./gradlew build +./gradlew build -x test +``` + +To run tests of library execute command: +```bash +./gradlew test ``` ## Using library From 091dab9c68e5327f2e3177cf071f09c6b10e30b7 Mon Sep 17 00:00:00 2001 From: Friend-zva Date: Sun, 7 Apr 2024 02:23:01 +0300 Subject: [PATCH 095/122] refactor: delete 'getSize' method and make getter of 'size' property as public. A child property is closed. Modify tests after feedback. Add whitespace in yml-script --- .github/workflows/build-test.yml | 2 +- .../main/kotlin/tree_tripper/SearchTree.kt | 5 - .../binary_trees/AbstractBSTree.kt | 9 +- .../nodes/binary_nodes/AbstractBSTreeNode.kt | 4 +- .../tree_tripper/binary_trees/BSTreeTest.kt | 190 +++++++++--------- .../assistants/BSTreeTestAssistant.kt | 23 ++- .../nodes/binary_nodes/BSTreeNodeTest.kt | 2 - 7 files changed, 117 insertions(+), 118 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 86f35b1..35a2c6a 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: push: - branches: [ "main", "dev"] + branches: [ "main", "dev" ] pull_request: branches: [ "main" ] diff --git a/lib/src/main/kotlin/tree_tripper/SearchTree.kt b/lib/src/main/kotlin/tree_tripper/SearchTree.kt index 29a3e28..e7773ee 100644 --- a/lib/src/main/kotlin/tree_tripper/SearchTree.kt +++ b/lib/src/main/kotlin/tree_tripper/SearchTree.kt @@ -89,11 +89,6 @@ public interface SearchTree, V>: Iterable> { */ public fun getMinInSubtree(key: K): Pair? - /** - * Returns the size of a tree. - */ - public fun getSize(): Int - // Iterator public fun iterator(order: IterationOrders): Iterator> diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt index 6c2e216..e507d30 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/AbstractBSTree.kt @@ -18,7 +18,8 @@ import tree_tripper.nodes.notNullNodeAction public abstract class AbstractBSTree, V, N: AbstractBSTreeNode>: SearchTree { protected var root: N? = null private set - private var size: Int = 0 + public var size: Int = 0 + private set override fun insert(key: K, value: V) { insert(key, value, permissionUpdate = true) @@ -77,10 +78,6 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< return notNullNodeAction(root, null) { node -> getMinInSubtree(node.key) } } - override fun getSize(): Int { - return size - } - override fun iterator(): BinarySearchTreeIterator { return iterator(IterationOrders.WIDTH_ORDER) } @@ -210,7 +207,7 @@ public abstract class AbstractBSTree, V, N: AbstractBSTreeNode< var nodeCurrent: N? = root ?: return null while (nodeCurrent != null) { - val resultCompare = key.compareTo(nodeCurrent.key) + val resultCompare: Int = key.compareTo(nodeCurrent.key) if (resultCompare < 0) nodeCurrent = nodeCurrent.leftChild else if (resultCompare > 0) diff --git a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt index 32ed285..8707bef 100644 --- a/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt +++ b/lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/AbstractBSTreeNode.kt @@ -16,8 +16,8 @@ public abstract class AbstractBSTreeNode, V, N: AbstractBSTreeN public val key: K, public var value: V ): SearchTreeNode { - public open var leftChild: N? = null - public open var rightChild: N? = null + public var leftChild: N? = null + public var rightChild: N? = null override fun getChildren(): List { return listOfNotNull(leftChild, rightChild) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index 633c842..13da642 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -1,6 +1,6 @@ package tree_tripper.binary_trees -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertTimeout @@ -25,8 +25,8 @@ public class BSTreeTest { @Test @DisplayName("tree initialization") public fun testTreeInitialization() { - tree.assertRootInitialization() - assertEquals(tree.getSize(), 0, "Incorrect a tree initialization.") + tree.assertNullRoot() + Assertions.assertEquals(tree.size, 0, "Incorrect a tree initialization.") } @Test @@ -51,12 +51,12 @@ public class BSTreeTest { @DisplayName("insert root") public fun testInsertRoot() { tree.insert(1, -1) - tree.assertIsBSTree() - assertEquals(tree.getSize(), 1, "Incorrect resizing tree size.") + Assertions.assertEquals(tree.size, 1, "Incorrect resizing tree size.") + Assertions.assertEquals(tree.getRoot(), Pair(1, -1), "Incorrect insert root") tree.insert(1, 0) - tree.assertIsBSTree() - assertEquals(tree.getSize(), 1, "Incorrect resizing tree size.") + Assertions.assertEquals(tree.size, 1, "Incorrect resizing tree size.") + Assertions.assertEquals(tree.getRoot(), Pair(1, 0), "Incorrect change root") } @Test @@ -66,7 +66,7 @@ public class BSTreeTest { tree.insert(1, -1) tree.insert(3, -3) tree.assertIsBSTree() - assertEquals(tree.getSize(), 3, "Incorrect resizing tree size.") + Assertions.assertEquals(tree.size, 3, "Incorrect resizing tree size.") } @ParameterizedTest @@ -86,22 +86,22 @@ public class BSTreeTest { @Test @DisplayName("if absent insert root") public fun testInsertIfAbsentRoot() { - assertEquals(tree.insertIfAbsent(1, -1), true) - tree.assertIsBSTree() - assertEquals(tree.getSize(), 1, "Incorrect resizing tree size.") + Assertions.assertEquals(tree.insertIfAbsent(1, -1), true) + Assertions.assertEquals(tree.size, 1, "Incorrect resizing tree size.") + Assertions.assertEquals(tree.getRoot(), Pair(1, -1), "Incorrect insert root") - assertEquals(tree.insertIfAbsent(1, 1), false) - tree.assertIsBSTree() - assertEquals(tree.getSize(), 1, "Incorrect resizing tree size.") + Assertions.assertEquals(tree.insertIfAbsent(1, 1), false) + Assertions.assertEquals(tree.size, 1, "Incorrect resizing tree size.") + Assertions.assertEquals(tree.getRoot(), Pair(1, -1), "Incorrect change root") } @Test @DisplayName("search root") public fun testSearchNode() { - assertEquals(tree.search(0), null, "Incorrect search in a empty tree.") + Assertions.assertEquals(tree.search(0), null, "Incorrect search in a empty tree.") tree.insert(1, -1) - assertEquals(tree.search(1), -1, "Incorrect search an existent root.") + Assertions.assertEquals(tree.search(1), -1, "Incorrect search an existent root.") } @Test @@ -110,30 +110,31 @@ public class BSTreeTest { tree.insert(2, -2) tree.insert(1, -1) tree.insert(3, -3) - assertEquals(tree.search(1), -1, "Incorrect search existent children root.") - assertEquals(tree.search(3), -3, "Incorrect search existent children root.") + Assertions.assertEquals(tree.search(1), -1, "Incorrect search an existent child root.") + Assertions.assertEquals(tree.search(3), -3, "Incorrect search an existent child root.") + Assertions.assertEquals(tree.search(0), null, "Incorrect search a non-existent child root.") } @ParameterizedTest @MethodSource("getSizeAndTimeArguments") @DisplayName("search with size and time") public fun testSearchWithSizeAndTime(size: Int, seconds: Long) { - val array = IntArray(size) + val arrayKeys = IntArray(size) var index = 0 repeat(size) { val keyRandom = Random.nextInt(-1000, 1000) - array[index++] = keyRandom + arrayKeys[index++] = keyRandom tree.insert(keyRandom, keyRandom * (-1)) } assertTimeout(Duration.ofSeconds(seconds)) { repeat(10) { - val keyRandom = Random.nextInt(-1000, 1000) - if (keyRandom in array) - assertEquals(tree.search(keyRandom), (-1) * keyRandom, - "Incorrect search an existent node.") - else - assertEquals(tree.search(keyRandom), null, + val keyRandom = arrayKeys[Random.nextInt(0, size - 1)] + Assertions.assertEquals(tree.search(keyRandom), (-1) * keyRandom, + "Incorrect search an existent node.") + + if ((keyRandom - 10) !in arrayKeys) + Assertions.assertEquals(tree.search(keyRandom - 10), null, "Incorrect search a non-existent node.") } } @@ -142,123 +143,126 @@ public class BSTreeTest { } @Test - @DisplayName("search of default") + @DisplayName("search of default root") public fun testSearchOrDefault() { - assertEquals(tree.searchOrDefault(1, 0), 0, - "Incorrect return of search a non-existent node.") + Assertions.assertEquals(tree.searchOrDefault(1, 0), 0, + "Incorrect return of search a non-existent child root.") tree.insert(1, -1) - assertEquals(tree.searchOrDefault(1, 0), -1, - "Incorrect return of search an existent node.") + Assertions.assertEquals(tree.searchOrDefault(1, 0), -1, + "Incorrect return of search an existent child root.") } @Test @DisplayName("contains") public fun testContains() { - assertEquals(tree.contains(1), false, "Incorrect return of search a non-existent node.") + Assertions.assertEquals(tree.contains(1), false, "Incorrect return of search a non-existent node.") tree.insert(1, -1) - assertEquals(tree.contains(1), true, "Incorrect return of search an existent node.") + Assertions.assertEquals(tree.contains(1), true, "Incorrect return of search an existent node.") } @Test @DisplayName("set with brackets") public fun testSet() { tree[1] = -1 - assertEquals(tree.search(1), -1, "Incorrect set.") + Assertions.assertEquals(tree.getRoot(), Pair(1, -1), "Incorrect set.") - tree[1] = 1 - assertEquals(tree.search(1), 1, "Incorrect change of the value.") + tree[1] = 0 + Assertions.assertEquals(tree.getRoot(), Pair(1, 0), "Incorrect change of the value.") } @Test @DisplayName("get with brackets") public fun testGet() { - assertEquals(tree[1], null, "Incorrect get a non-existent node.") + Assertions.assertEquals(tree[1], null, "Incorrect get a non-existent node.") tree[1] = -1 - assertEquals(tree[1], -1, "Incorrect get an existent node.") + Assertions.assertEquals(tree[1], -1, "Incorrect get an existent node.") } @Test - @DisplayName("get maximum in subtree") + @DisplayName("get maximum in subtree without children") public fun testGetMaxInSubtree() { - assertEquals(tree.getMaxInSubtree(0), null, + Assertions.assertEquals(tree.getMaxInSubtree(0), null, "Incorrect search a maximum key in a empty tree.") - tree.assertIsBSTree() - tree[2] = -2 - assertEquals(tree.getMaxInSubtree(2), Pair(2, -2), + tree[1] = -1 + Assertions.assertEquals(tree.getMaxInSubtree(1), Pair(1, -1), "Incorrect search a maximum key in subtree without children.") - tree.assertIsBSTree() + Assertions.assertEquals(tree.getRoot(), Pair(1, -1), "A tree is damaged.") } @Test @DisplayName("get maximum in subtree with children") public fun testGetMaxInSubtreeWithChildren() { - tree[2] = -2 - tree[3] = -3 + tree[5] = -5 tree[1] = -1 - assertEquals(tree.getMaxInSubtree(2), Pair(3, -3), "Incorrect search a maximum key in a subtree.") + tree[3] = -3 + tree[2] = -2 + Assertions.assertEquals(tree.getMaxInSubtree(1), Pair(3, -3), + "Incorrect search a maximum key in subtree with children.") tree.assertIsBSTree() } @Test @DisplayName("get maximum") public fun testGetMax() { - assertEquals(tree.getMax(), null, "Incorrect search a maximum key in a empty tree.") + Assertions.assertEquals(tree.getMax(), null, "Incorrect search a maximum key in a empty tree.") tree[2] = -2 tree[3] = -3 tree[1] = -1 - assertEquals(tree.getMax(), Pair(3, -3), "Incorrect search a maximum key in a tree.") + tree[4] = -4 + Assertions.assertEquals(tree.getMax(), Pair(4, -4), "Incorrect search a maximum key in a tree.") } @Test - @DisplayName("get minimum in subtree") + @DisplayName("get minimum in subtree without children") public fun testGetMinInSubtree() { - assertEquals(tree.getMinInSubtree(0), null, + Assertions.assertEquals(tree.getMinInSubtree(0), null, "Incorrect search a minimum key in a empty tree.") - tree.assertIsBSTree() - tree[2] = -2 - assertEquals(tree.getMinInSubtree(2), Pair(2, -2), + tree[1] = -1 + Assertions.assertEquals(tree.getMinInSubtree(1), Pair(1, -1), "Incorrect search a minimum key in subtree without children.") - tree.assertIsBSTree() + Assertions.assertEquals(tree.getRoot(), Pair(1, -1), "A tree is damaged.") } @Test @DisplayName("get minimum in subtree with children") public fun testGetMinInSubtreeWithChildren() { - tree[2] = -2 - tree[3] = -3 + tree[5] = -5 tree[1] = -1 - assertEquals(tree.getMinInSubtree(2), Pair(1, -1), "Incorrect search a minimum key in a subtree.") + tree[3] = -3 + tree[2] = -2 + Assertions.assertEquals(tree.getMinInSubtree(3), Pair(2, -2), + "Incorrect search a minimum key in a subtree.") tree.assertIsBSTree() } @Test @DisplayName("get minimum") public fun testGetMin() { - assertEquals(tree.getMin(), null, "Incorrect search a minimum key in a empty tree.") + Assertions.assertEquals(tree.getMin(), null, "Incorrect search a minimum key in a empty tree.") tree[2] = -2 tree[3] = -3 tree[1] = -1 - assertEquals(tree.getMin(), Pair(1, -1), "Incorrect search a minimum key in a tree.") + tree[4] = -4 + Assertions.assertEquals(tree.getMin(), Pair(1, -1), "Incorrect search a minimum key in a tree.") } @Test - @DisplayName("remove root") + @DisplayName("remove root without children") public fun testRemove() { tree[1] = -1 - assertEquals(tree.remove(1), -1, "Incorrect remove root.") - assertEquals(0, tree.getSize()) - tree.assertIsBSTree() + Assertions.assertEquals(tree.remove(1), -1, "Incorrect remove root.") + Assertions.assertEquals(0, tree.size) + tree.assertNullRoot() - assertEquals(tree.remove(1), null, "Incorrect remove a non-existent root.") - assertEquals(0, tree.getSize()) - tree.assertIsBSTree() + Assertions.assertEquals(tree.remove(1), null, "Incorrect remove a non-existent root.") + Assertions.assertEquals(0, tree.size) } @Test @@ -267,16 +271,18 @@ public class BSTreeTest { tree[2] = -2 tree[1] = -1 tree[3] = -3 - assertEquals(tree.remove(2), -2, "Incorrect remove a root.") - assertEquals(2, tree.getSize()) + Assertions.assertEquals(tree.remove(2), -2, "Incorrect remove a root.") + Assertions.assertEquals(2, tree.size) tree.assertIsBSTree() - assertEquals(tree.search(1), -1, "Incorrect remove a root and lose the left child.") - assertEquals(tree.search(3), -3, "Incorrect remove a root and lose the right child.") - assertEquals(tree.remove(1), -1, "Incorrect remove a root.") + Assertions.assertEquals(tree.search(1), -1, "Incorrect remove a root and lose the left child.") + Assertions.assertEquals(tree.search(3), -3, "Incorrect remove a root and lose the right child.") + + Assertions.assertEquals(tree.remove(1), -1, "Incorrect remove a root.") tree.assertIsBSTree() - assertEquals(1, tree.getSize()) - assertEquals(tree.search(3), -3, "Incorrect remove a root and lose the right child.") + Assertions.assertEquals(1, tree.size) + + Assertions.assertEquals(tree.search(3), -3, "Incorrect remove a root and lose the right child.") } @ParameterizedTest @@ -289,41 +295,41 @@ public class BSTreeTest { setKeys.add(keyRandom) tree[keyRandom] = (-1) * keyRandom } + assertTimeout(Duration.ofSeconds(seconds)) { repeat(10) { - val keyRandom = Random.nextInt(-1000, 1000) - if (keyRandom in setKeys) { - assertEquals(tree.remove(keyRandom), (-1) * keyRandom, - "Incorrect return of remove an existent node." - ) - setKeys.remove(keyRandom) - } else - assertEquals(tree.remove(keyRandom), null, + val keyRandom = setKeys.elementAt(Random.nextInt(0, setKeys.size - 1)) + Assertions.assertEquals(tree.remove(keyRandom), (-1) * keyRandom, + "Incorrect return of remove an existent node.") + setKeys.remove(keyRandom) + + if ((keyRandom - 10) !in setKeys) + Assertions.assertEquals(tree.remove(keyRandom - 10), null, "Incorrect return of remove a non-existent node.") - assertEquals(setKeys.size, tree.getSize()) } } + Assertions.assertEquals(tree.size, setKeys.size) tree.assertIsBSTree() } @Test @DisplayName("remove or default") public fun testRemoveOrDefault() { - assertEquals(tree.removeOrDefault(1, 0), 0, + Assertions.assertEquals(tree.removeOrDefault(1, 0), 0, "Incorrect return of remove a non-existent node.") - assertEquals(0, tree.getSize()) + Assertions.assertEquals(0, tree.size) tree.insert(1, -1) - assertEquals(tree.removeOrDefault(1, 0), -1, + Assertions.assertEquals(tree.removeOrDefault(1, 0), -1, "Incorrect return of remove an existent node.") - assertEquals(0, tree.getSize()) + Assertions.assertEquals(0, tree.size) } @Test @DisplayName("iterator") public fun testIterator() { - assertFalse(tree.iterator().hasNext(), "Incorrect check next.") + Assertions.assertFalse(tree.iterator().hasNext(), "Incorrect check next.") } @Test @@ -336,7 +342,7 @@ public class BSTreeTest { val arrayPair = arrayOf(Pair(2, -2), Pair(1, -1), Pair(3, -3), Pair(4, -4)) var index = 0 tree.forEach(IterationOrders.WIDTH_ORDER) { - assertEquals(arrayPair[index++], it, "Incorrect iteration.") + Assertions.assertEquals(arrayPair[index++], it, "Incorrect iteration.") } } @@ -346,7 +352,7 @@ public class BSTreeTest { var builder = StringBuilder("BSTreeTestAssistant(") tree.forEach { builder.append("${it.first}: ${it.second}, ") } builder.append(')') - assertEquals(tree.toString(), builder.toString(), "Incorrect construction string.") + Assertions.assertEquals(tree.toString(), builder.toString(), "Incorrect construction string.") builder = StringBuilder("BSTreeTestAssistant(") tree[2] = -2 @@ -356,7 +362,7 @@ public class BSTreeTest { tree.forEach { builder.append("${it.first}: ${it.second}, ") } repeat(2) { builder.deleteCharAt(builder.length - 1) } builder.append(')') - assertEquals(tree.toString(), builder.toString(), "Incorrect construction string.") + Assertions.assertEquals(tree.toString(), builder.toString(), "Incorrect construction string.") } @Test @@ -367,7 +373,7 @@ public class BSTreeTest { tree[3] = -3 tree[4] = -4 val string = "BSTreeTestAssistant(\n\t\t(4: -4)\n\t(3: -3)\n(2: -2)\n\t(1: -1)\n)" - assertEquals(tree.toStringWithTreeView(), string, "Incorrect construction string.") + Assertions.assertEquals(tree.toStringWithTreeView(), string, "Incorrect construction string.") } public companion object { diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt index 5f544a0..875aba5 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/BSTreeTestAssistant.kt @@ -1,6 +1,7 @@ package tree_tripper.binary_trees.assistants -import org.junit.jupiter.api.Assertions.assertEquals +import assertBinaryNodeDeepEquals +import org.junit.jupiter.api.Assertions import tree_tripper.binary_trees.BSTree import tree_tripper.nodes.binary_nodes.BSTreeNode import java.util.* @@ -8,30 +9,27 @@ import java.util.* public class BSTreeTestAssistant, V>: BSTree() { - public fun assertRootInitialization() { - assertEquals(root, null, "Incorrect a root initialization.") + public fun assertNullRoot() { + Assertions.assertEquals(root, null, "Incorrect a root initialization.") } public fun assertWasCreatedNode(key: K, value: V) { - val node = createNode(key, value) - assertEquals(node.key, key, "Incorrect a key assignment.") - assertEquals(node.value, value, "Incorrect a value assignment.") - assertEquals(node.leftChild, null, "Incorrect a left child assignment.") - assertEquals(node.rightChild, null, "Incorrect a right child assignment.") + assertBinaryNodeDeepEquals(createNode(key, value), BSTreeNode(key, value)) } public fun assertWasUpdatedRoot(key: K, value: V) { val node = createNode(key, value) updateRoot(node) - assertEquals(root, node, "Incorrect a root update.") + Assertions.assertEquals(root, node, "Incorrect a root update.") } public fun assertWasBalancedTree(key: K, value: V) { val node = createNode(key, value) - assertEquals(balanceTree(node), node, "Incorrect a tree balance.") + Assertions.assertEquals(balanceTree(node), node, "Incorrect a tree balance.") } public fun assertIsBSTree() { + if (root == null) throw NullPointerException("Root is null") val queue: Queue> = LinkedList(listOfNotNull(root)) while (queue.isNotEmpty()) { @@ -49,4 +47,9 @@ public class BSTreeTestAssistant, V>: BSTree() { } } + public fun getRoot(): Pair { + val root = this.root ?: throw NullPointerException("Root is null") + return Pair(root.key, root.value) + } + } diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt index 45cda2a..612d22e 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/BSTreeNodeTest.kt @@ -2,8 +2,6 @@ package tree_tripper.nodes.binary_nodes import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import tree_tripper.nodes.notNullNodeAction -import kotlin.test.assertContains import kotlin.test.assertEquals From 5c676d6e8d16349a78087414e6846a9a0d237948 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 7 Apr 2024 12:40:26 +0300 Subject: [PATCH 096/122] refactor: update usage of tree size getter at AVLTreeTest and RBTreeTest --- .../tree_tripper/binary_trees/AVLTreeTest.kt | 2 +- .../tree_tripper/binary_trees/RBTreeTest.kt | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt index 7c678df..124e62e 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt @@ -23,7 +23,7 @@ class AVLTreeTest { @DisplayName("tree initialization") public fun testTreeInitialization() { tree.assertRoot(null) { "Root of AVLTree is not null by standard initialize." } - Assertions.assertEquals(0, tree.getSize()) + Assertions.assertEquals(0, tree.size) } @ParameterizedTest diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index f6fceee..7b12296 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -24,7 +24,7 @@ class RBTreeTest { public fun testInitializing() { tree.assertRoot(null) { "Root is not null after init" } tree.assertIsRBTree() - Assertions.assertEquals(0, tree.getSize()) + Assertions.assertEquals(0, tree.size) } @Test @@ -33,7 +33,7 @@ class RBTreeTest { for (i in 0..20) { tree.insert(i, i) tree.assertIsRBTree() - Assertions.assertEquals(i + 1, tree.getSize()) + Assertions.assertEquals(i + 1, tree.size) } } @@ -44,7 +44,7 @@ class RBTreeTest { for (i in 20 downTo 0) { tree.insert(i, i) tree.assertIsRBTree() - Assertions.assertEquals((20 - i) + 1, tree.getSize()) + Assertions.assertEquals((20 - i) + 1, tree.size) } } @@ -58,7 +58,7 @@ class RBTreeTest { tree.insert(value, value) elements.add(value) tree.assertIsRBTree() - Assertions.assertEquals(elements.size, tree.getSize()) + Assertions.assertEquals(elements.size, tree.size) } } @@ -69,7 +69,7 @@ class RBTreeTest { val root: Pair = tree.getRoot() Assertions.assertEquals(root.second, tree.remove(root.first)) tree.assertIsRBTree() - Assertions.assertEquals(20, tree.getSize()) + Assertions.assertEquals(20, tree.size) } @Test @@ -78,7 +78,7 @@ class RBTreeTest { for (i in 0..20) tree.insert(i, i) Assertions.assertEquals(15, tree.remove(15)) tree.assertIsRBTree() - Assertions.assertEquals(20, tree.getSize()) + Assertions.assertEquals(20, tree.size) } @Test @DisplayName("remove red node with children") @@ -86,7 +86,7 @@ class RBTreeTest { for (i in 0..20) tree.insert(i, i) Assertions.assertEquals(1, tree.remove(1)) tree.assertIsRBTree() - Assertions.assertEquals(20, tree.getSize()) + Assertions.assertEquals(20, tree.size) } @Test @@ -97,7 +97,7 @@ class RBTreeTest { try { Assertions.assertEquals(key, tree.remove(key)) tree.assertIsRBTree() - Assertions.assertEquals(20, tree.getSize()) + Assertions.assertEquals(20, tree.size) } catch (e: AssertionError) { throw AssertionError( "Try remove node with key $key from tree: ${tree.toStringWithTreeView()}", @@ -111,7 +111,7 @@ class RBTreeTest { public fun testRemoveRootWithoutChildren() { tree.insert(0, 0) Assertions.assertEquals(0, tree.remove(0)) - Assertions.assertEquals(0, tree.getSize()) + Assertions.assertEquals(0, tree.size) } @@ -121,7 +121,7 @@ class RBTreeTest { for (i in 0..20) tree.insert(i, i) Assertions.assertEquals(6, tree.remove(6)) tree.assertIsRBTree() - Assertions.assertEquals(20, tree.getSize()) + Assertions.assertEquals(20, tree.size) } @Test @DisplayName("remove red node without children") @@ -129,7 +129,7 @@ class RBTreeTest { for (i in 0..21) tree.insert(i, i) Assertions.assertEquals(20, tree.remove(20)) tree.assertIsRBTree() - Assertions.assertEquals(21, tree.getSize()) + Assertions.assertEquals(21, tree.size) } @Test @@ -138,7 +138,7 @@ class RBTreeTest { tree.insert(0, 0) tree.insert(-1, -1) Assertions.assertEquals(0, tree.remove(0)) - Assertions.assertEquals(1, tree.getSize()) + Assertions.assertEquals(1, tree.size) Assertions.assertEquals(Pair(-1, -1), tree.getRoot()) } From e8660d81485d0af1aca9f5418bfdcc753887a8df Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 7 Apr 2024 12:41:37 +0300 Subject: [PATCH 097/122] refactor: update null checking at remove node at RBTree --- lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index eebe016..78cc207 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -23,7 +23,7 @@ public open class RBTree, V>: AbstractBSTree): RBTreeNode { - var nodeCurrent: RBTreeNode = node + var nodeCurrent = node if (isRedColor(nodeCurrent.rightChild) && !isRedColor(nodeCurrent.leftChild)) { nodeCurrent = rotateLeft(nodeCurrent) } @@ -43,7 +43,7 @@ public open class RBTree, V>: AbstractBSTree = node if (resultCompare < 0) { - if (nodeCurrent.leftChild != null && !isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) + if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) nodeCurrent = moveRedLeft(nodeCurrent) removeResult = removeNode(nodeCurrent.leftChild, key) @@ -55,7 +55,7 @@ public open class RBTree, V>: AbstractBSTree, V>: AbstractBSTree): RBTreeNode { + if (node.rightChild == null) return node var nodeCurrent: RBTreeNode = node flipColors(nodeCurrent) @@ -168,6 +169,7 @@ public open class RBTree, V>: AbstractBSTree): RBTreeNode { + if (node.leftChild == null) return node var nodeCurrent: RBTreeNode = node flipColors(nodeCurrent) From ca3db51c26d18d5bdf7ce945a214684c67589a07 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 7 Apr 2024 13:26:25 +0300 Subject: [PATCH 098/122] refactor: update createNode method at RBTree to always valid init of node with red color type and null children --- lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index 78cc207..92ac7c5 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -14,7 +14,7 @@ import tree_tripper.nodes.notNullNodeUpdate public open class RBTree, V>: AbstractBSTree>() { override fun createNode(key: K, value: V): RBTreeNode { - return RBTreeNode(key, value) + return RBTreeNode(key, value, isRed = true, leftChild = null, rightChild = null) } override fun updateRoot(node: RBTreeNode?) { From f4945f2e7a8dd1fa52be56eaba71c17402f334bd Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 7 Apr 2024 13:30:01 +0300 Subject: [PATCH 099/122] test: add tests of removeMinNode method at RBTree --- .../tree_tripper/binary_trees/RBTree.kt | 3 +- .../tree_tripper/binary_trees/RBTreeTest.kt | 87 +++++++++++++++++++ .../assistants/RBTreeTestAssistant.kt | 5 ++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index 92ac7c5..cf18418 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -189,9 +189,8 @@ public open class RBTree, V>: AbstractBSTree?): RBTreeNode? { + protected fun removeMinNode(node: RBTreeNode?): RBTreeNode? { if (node == null) return null - val leftChild = node.leftChild ?: return node.rightChild var nodeCurrent: RBTreeNode = node diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 7b12296..d30eb68 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -80,6 +80,7 @@ class RBTreeTest { tree.assertIsRBTree() Assertions.assertEquals(20, tree.size) } + @Test @DisplayName("remove red node with children") public fun testRemoveRedNodeWithChildren() { @@ -123,6 +124,7 @@ class RBTreeTest { tree.assertIsRBTree() Assertions.assertEquals(20, tree.size) } + @Test @DisplayName("remove red node without children") public fun testRemoveRedNodeWithoutChildren() { @@ -142,6 +144,91 @@ class RBTreeTest { Assertions.assertEquals(Pair(-1, -1), tree.getRoot()) } + @Test + @DisplayName("remove min node at empty tree") + public fun testRemoveMinNodeAtEmptyTree() { + tree.assertRemoveMinNode( + tree_view = null, + expected = null + ) + } + + @Test + @DisplayName("remove min node without children") + public fun testRemoveMinNodeWithoutChildren() { + tree.assertRemoveMinNode( + tree_view = RBTreeNode(1, 1, isRed = false), + expected = null + ) + tree.assertRemoveMinNode( + tree_view = RBTreeNode(1, 1, isRed = true), + expected = null + ) + } + + @Test + @DisplayName("remove min node with red child") + public fun testRemoveMinNodeWithRedChild() { + tree.assertRemoveMinNode( + tree_view = RBTreeNode( + 1, 1, isRed = false, + leftChild = RBTreeNode(0, 0, isRed = true), + rightChild = null + ), + expected = RBTreeNode(1, 1, isRed = false) + ) + } + + @Test + @DisplayName("remove min node with two child") + public fun testRemoveMinNodeWithTwoChild() { + tree.assertRemoveMinNode( + tree_view = RBTreeNode( + 1, 1, isRed = false, + leftChild = RBTreeNode(0, 0, isRed = false), + rightChild = RBTreeNode(2, 2, isRed = false), + ), + expected = RBTreeNode( + 2, 2, isRed = true, + leftChild = RBTreeNode(1, 1, isRed = true), + rightChild = null + ) + ) + } + + @Test + @DisplayName("remove min node with big subtree") + public fun testRemoveMinNodeWithBigSubtree() { + tree.assertRemoveMinNode( + tree_view = RBTreeNode( + 2, 2, isRed = false, + leftChild = RBTreeNode( + 0, 0, isRed = false, + leftChild = RBTreeNode(-1, -1, isRed = false), + rightChild = RBTreeNode(1, 1, isRed = false) + ), + rightChild = RBTreeNode( + 4, 4, isRed = false, + leftChild = RBTreeNode(3, 3, isRed = false), + rightChild = RBTreeNode(5, 5, isRed = false) + ), + ), + expected = RBTreeNode( + 4, 4, isRed = true, + leftChild = RBTreeNode( + 2, 2, isRed = true, + leftChild = RBTreeNode( + 1, 1, isRed = false, + leftChild = RBTreeNode(0, 0, isRed = true), + rightChild = null + ), + rightChild = RBTreeNode(3, 3, isRed = false) + ), + rightChild = RBTreeNode(5, 5, isRed = false) + ) + ) + } + @Test @DisplayName("left rotate node without right") public fun testLeftRotateNodeWithoutRightChild() { diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index 94c309b..ba6e4c0 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -95,4 +95,9 @@ public class RBTreeTestAssistant, V>: RBTree() { return Pair(root.key, root.value) } + fun assertRemoveMinNode(tree_view: RBTreeNode?, expected: RBTreeNode?) { + val result = removeMinNode(tree_view) + assertBinaryNodeDeepEquals(expected, result) {n1, n2 -> n1.isRed == n2.isRed} + } + } \ No newline at end of file From 94be012294dea23ea504f0ec83377d4d412d4d0e Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 7 Apr 2024 13:31:34 +0300 Subject: [PATCH 100/122] refactor: update get root method at RBTreeTestAssistant --- .../binary_trees/assistants/RBTreeTestAssistant.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index ba6e4c0..e286924 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -90,8 +90,7 @@ public class RBTreeTestAssistant, V>: RBTree() { } fun getRoot(): Pair { - val root = this.root - if (root == null) throw NullPointerException("Tree is empty can't get root pair") + val root = this.root ?: throw NullPointerException("Tree is empty can't get root pair") return Pair(root.key, root.value) } From 121dc52a61e2eda3fff06d5d4c50b3689f784620 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 7 Apr 2024 13:32:38 +0300 Subject: [PATCH 101/122] test: add test of removing not contains elements at tree --- .../kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index d30eb68..5537634 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -62,6 +62,19 @@ class RBTreeTest { } } + @Test + @DisplayName("remove not contains element") + public fun testRemoveNotContainsElement() { + for (i in 0..20) tree.insert(i, i) + Assertions.assertEquals(null, tree.remove(25)) + tree.assertIsRBTree() + Assertions.assertEquals(21, tree.size) + + Assertions.assertEquals(null, tree.remove(-100)) + tree.assertIsRBTree() + Assertions.assertEquals(21, tree.size) + } + @Test @DisplayName("remove root with children") public fun testRemoveRootWithChildren() { From 863b66db0860cc7f29827ad504d99de7f775cf5b Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 7 Apr 2024 13:56:01 +0300 Subject: [PATCH 102/122] refactor: change name of assertion param from 'tree_view' to 'treeView' --- .../test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt | 8 ++++---- .../binary_trees/assistants/RBTreeTestAssistant.kt | 9 +++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 5537634..69cbc75 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -161,7 +161,7 @@ class RBTreeTest { @DisplayName("remove min node at empty tree") public fun testRemoveMinNodeAtEmptyTree() { tree.assertRemoveMinNode( - tree_view = null, + treeView = null, expected = null ) } @@ -170,11 +170,11 @@ class RBTreeTest { @DisplayName("remove min node without children") public fun testRemoveMinNodeWithoutChildren() { tree.assertRemoveMinNode( - tree_view = RBTreeNode(1, 1, isRed = false), + treeView = RBTreeNode(1, 1, isRed = false), expected = null ) tree.assertRemoveMinNode( - tree_view = RBTreeNode(1, 1, isRed = true), + treeView = RBTreeNode(1, 1, isRed = true), expected = null ) } @@ -183,7 +183,7 @@ class RBTreeTest { @DisplayName("remove min node with red child") public fun testRemoveMinNodeWithRedChild() { tree.assertRemoveMinNode( - tree_view = RBTreeNode( + treeView = RBTreeNode( 1, 1, isRed = false, leftChild = RBTreeNode(0, 0, isRed = true), rightChild = null diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index e286924..cafc87e 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -94,8 +94,13 @@ public class RBTreeTestAssistant, V>: RBTree() { return Pair(root.key, root.value) } - fun assertRemoveMinNode(tree_view: RBTreeNode?, expected: RBTreeNode?) { - val result = removeMinNode(tree_view) + fun assertRemoveMinNode(treeView: RBTreeNode?, expected: RBTreeNode?) { + val result = removeMinNode(treeView) + assertBinaryNodeDeepEquals(expected, result) {n1, n2 -> n1.isRed == n2.isRed} + } + + fun assertMoveRightNode(treeView: RBTreeNode, expected: RBTreeNode) { + val result = moveRedRight(treeView) assertBinaryNodeDeepEquals(expected, result) {n1, n2 -> n1.isRed == n2.isRed} } From 8280df1f698f06f81660a8e164aad5e73ea9d3db Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 7 Apr 2024 13:56:32 +0300 Subject: [PATCH 103/122] test: add tests of moveRedRight of RBTree --- .../tree_tripper/binary_trees/RBTree.kt | 2 +- .../tree_tripper/binary_trees/RBTreeTest.kt | 74 ++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index cf18418..6cf81ca 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -149,7 +149,7 @@ public open class RBTree, V>: AbstractBSTree): RBTreeNode { + protected fun moveRedRight(node: RBTreeNode): RBTreeNode { if (node.rightChild == null) return node var nodeCurrent: RBTreeNode = node diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index 69cbc75..f553378 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt @@ -192,11 +192,81 @@ class RBTreeTest { ) } + @Test + @DisplayName("move right node without children") + public fun testModeRightNodeWithoutChildren() { + tree.assertMoveRightNode( + treeView = RBTreeNode(0, 0, isRed = false), + expected = RBTreeNode(0, 0, isRed = false), + ) + } + + @Test + @DisplayName("move right node with red child") + public fun testModeRightNodeWithRedChild() { + tree.assertMoveRightNode( + treeView = RBTreeNode( + 0, 0, isRed = false, + leftChild = RBTreeNode(-1, -1, isRed = true), + rightChild = null + ), + expected = RBTreeNode( + 0, 0, isRed = false, + leftChild = RBTreeNode(-1, -1, isRed = true), + rightChild = null + ), + ) + } + + @Test + @DisplayName("move right node with children") + public fun testModeRightNodeWithChildren() { + tree.assertMoveRightNode( + treeView = RBTreeNode( + 0, 0, isRed = false, + leftChild = RBTreeNode(-1, -1, isRed = false), + rightChild = RBTreeNode(1, 1, isRed = false) + ), + expected = RBTreeNode( + 0, 0, isRed = true, + leftChild = RBTreeNode(-1, -1, isRed = true), + rightChild = RBTreeNode(1, 1, isRed = true) + ), + ) + } + + @Test + @DisplayName("move right node with children and left child of left child is red") + public fun testModeRightNodeWithChildrenAndLeftChildOfLeftChildIsRed() { + tree.assertMoveRightNode( + treeView = RBTreeNode( + 0, 0, isRed = false, + leftChild = RBTreeNode( + -1, -1, isRed = false, + leftChild = RBTreeNode(-2, -2, isRed = true), + rightChild = null + ), + rightChild = RBTreeNode(1, 1, isRed = false) + ), + expected = RBTreeNode( + -1, -1, isRed = false, + leftChild = RBTreeNode( + -2, -2, isRed = false + ), + rightChild = RBTreeNode( + 0, 0, isRed = false, + leftChild = null, + rightChild = RBTreeNode(1, 1, isRed = true) + ) + ), + ) + } + @Test @DisplayName("remove min node with two child") public fun testRemoveMinNodeWithTwoChild() { tree.assertRemoveMinNode( - tree_view = RBTreeNode( + treeView = RBTreeNode( 1, 1, isRed = false, leftChild = RBTreeNode(0, 0, isRed = false), rightChild = RBTreeNode(2, 2, isRed = false), @@ -213,7 +283,7 @@ class RBTreeTest { @DisplayName("remove min node with big subtree") public fun testRemoveMinNodeWithBigSubtree() { tree.assertRemoveMinNode( - tree_view = RBTreeNode( + treeView = RBTreeNode( 2, 2, isRed = false, leftChild = RBTreeNode( 0, 0, isRed = false, From fd9761be1ed60ae40a44319893cac8533a737a74 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Sun, 7 Apr 2024 13:57:53 +0300 Subject: [PATCH 104/122] refactor: add public modifier to methods of RBTreeTestAssistant --- .../assistants/RBTreeTestAssistant.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index cafc87e..ac601d7 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt @@ -48,7 +48,7 @@ public class RBTreeTestAssistant, V>: RBTree() { return (if (node.isRed) 0 else 1) + left } - fun assertRoot(node: RBTreeNode?, lazyMassage: () -> String) { + public fun assertRoot(node: RBTreeNode?, lazyMassage: () -> String) { try { assertBinaryNodeDataEquals(root, node) {rootNode, expectedNode -> rootNode.isRed == expectedNode.isRed} } catch (e: AssertionError) { @@ -56,32 +56,32 @@ public class RBTreeTestAssistant, V>: RBTree() { } } - fun assertNodeColor(expected: Boolean, node: RBTreeNode?) { + public fun assertNodeColor(expected: Boolean, node: RBTreeNode?) { Assertions.assertEquals(expected, isRedColor(node)) } - fun assertNodeLeftChildColor(expected: Boolean, node: RBTreeNode?) { + public fun assertNodeLeftChildColor(expected: Boolean, node: RBTreeNode?) { Assertions.assertEquals(expected, isRedLeftChild(node)) } - fun assertNodeLeftRotation(expected: RBTreeNode, node: RBTreeNode) { + public fun assertNodeLeftRotation(expected: RBTreeNode, node: RBTreeNode) { assertBinaryNodeDeepEquals(expected, rotateLeft(node)) {n1, n2 -> n1.isRed == n2.isRed} } - fun assertNodeRightRotation(expected: RBTreeNode, node: RBTreeNode) { + public fun assertNodeRightRotation(expected: RBTreeNode, node: RBTreeNode) { assertBinaryNodeDeepEquals(expected, rotateRight(node)) {n1, n2 -> n1.isRed == n2.isRed} } - fun assertNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { + public fun assertNodeColorFlip(expected: RBTreeNode, node: RBTreeNode) { flipColors(node) assertBinaryNodeDeepEquals(expected, node) {n1, n2 -> n1.isRed == n2.isRed} } - fun assertNodeCreation(key: K, value: V) { + public fun assertNodeCreation(key: K, value: V) { assertBinaryNodeDeepEquals(createNode(key, value), RBTreeNode(key, value)) { n1, n2 -> n1.isRed == n2.isRed} } - fun assertUpdateRoot(node: RBTreeNode?) { + public fun assertUpdateRoot(node: RBTreeNode?) { updateRoot(node) assertBinaryNodeDataEquals( root, @@ -89,17 +89,17 @@ public class RBTreeTestAssistant, V>: RBTree() { ) } - fun getRoot(): Pair { + public fun getRoot(): Pair { val root = this.root ?: throw NullPointerException("Tree is empty can't get root pair") return Pair(root.key, root.value) } - fun assertRemoveMinNode(treeView: RBTreeNode?, expected: RBTreeNode?) { + public fun assertRemoveMinNode(treeView: RBTreeNode?, expected: RBTreeNode?) { val result = removeMinNode(treeView) assertBinaryNodeDeepEquals(expected, result) {n1, n2 -> n1.isRed == n2.isRed} } - fun assertMoveRightNode(treeView: RBTreeNode, expected: RBTreeNode) { + public fun assertMoveRightNode(treeView: RBTreeNode, expected: RBTreeNode) { val result = moveRedRight(treeView) assertBinaryNodeDeepEquals(expected, result) {n1, n2 -> n1.isRed == n2.isRed} } From bce53e6416e88f0652af85fb16f76ae708d5e657 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 8 Apr 2024 19:41:51 +0300 Subject: [PATCH 105/122] test: add table information about coverage of each classes at library with python-script and update yml-script for it --- .github/workflows/build-test.yml | 3 + lib/build.gradle.kts | 23 ++-- scripts/csv-reports-printer.py | 183 +++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 15 deletions(-) create mode 100644 scripts/csv-reports-printer.py diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 35a2c6a..c98aaf8 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -20,3 +20,6 @@ jobs: - name: Run tests with jacoco run: ./gradlew test + + - name: Display info about coverage + run: python3 ./scripts/csv-reports-printer.py -i ./lib/build/reports/jacoco/info.csv -l lib diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 48a49ca..47511d0 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -4,9 +4,6 @@ plugins { // Code coverage plugin jacoco - - // Output project coverage - id("org.barfuin.gradle.jacocolog") version "3.1.0" } repositories { @@ -17,6 +14,8 @@ repositories { dependencies { // This dependency is exported to consumers, that is to say found on their compile classpath. api(libs.commons.math3) + // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2") // This dependency is used internally, and not exposed to consumers on their own compile classpath. @@ -30,16 +29,6 @@ java { } } -testing { - suites { - // Configure the built-in test suite - val test by getting(JvmTestSuite::class) { - // Use Kotlin Test framework - useKotlinTest("1.9.20") - } - } -} - tasks.named("test") { useJUnitPlatform() finalizedBy(tasks.jacocoTestReport) @@ -53,9 +42,13 @@ tasks.withType { tasks.named("jacocoTestReport") { dependsOn(tasks.test) + val sep = File.separator + val jacocoReportsDirName = "reports${sep}jacoco" reports { - csv.required = false + csv.required = true xml.required = false - html.outputLocation = layout.buildDirectory.dir("jacocoHtml") + html.required = true + csv.outputLocation = layout.buildDirectory.file("${jacocoReportsDirName}${sep}info.csv") + html.outputLocation = layout.buildDirectory.dir("${jacocoReportsDirName}${sep}html") } } diff --git a/scripts/csv-reports-printer.py b/scripts/csv-reports-printer.py new file mode 100644 index 0000000..4ed3c55 --- /dev/null +++ b/scripts/csv-reports-printer.py @@ -0,0 +1,183 @@ +import argparse +import csv +import sys +import typing + +COLUMNS_TYPES = [ + '_MISSED', + '_COVERED', +] + +CSV_COLUMNS = [ + 'PACKAGES', + 'CLASS', + 'INSTRUCTION_MISSED', + 'INSTRUCTION_COVERED', + 'BRANCH_MISSED', + 'BRANCH_COVERED', + 'LINE_MISSED', + 'LINE_COVERED', + 'COMPLEXITY_MISSED', + 'COMPLEXITY_COVERED', + 'METHOD_MISSED', + 'METHOD_COVERED', +] +DISPLAY_COLUMNS = [ + 'PACKAGES', + 'CLASS', + 'INSTRUCTION', + 'BRANCH', + 'LINE', + 'COMPLEXITY', + 'METHOD', +] +DEFAULT_LABEL_SIZE = 15 + + +def create_row_info() -> dict: + return { + key: 0 for key in CSV_COLUMNS + } + + +def is_valid_lib(group: str, lib_name: str) -> bool: + if len(lib_name) == 0: + return True + return group == lib_name + + +def parse_args(args: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser( + prog="CSV Jacoco Test Reports Printer", + description="Program read csv file with jacoco report and print it at terminal at stdin", + ) + + parser.add_argument( + "-i", "--input", required=True, + help="setup path to CSV file with jacoco report information", + metavar="" + ) + parser.add_argument( + "-l", "--lib", + help="setup library name to remove from package path", + default="", + metavar="" + ) + parser.add_argument( + "-p", "--package-print", + help="setup flag to 'ON' to print packages of files at report (default 'OFF')", + action='store_true', + default=False + ) + + return parser.parse_args(args) + + +def read_csv(namespace: argparse.Namespace) -> typing.Optional[dict]: + table = [] + max_packages_name_length = 20 + max_classes_name_length = 20 + + with open(getattr(namespace, "input"), 'r') as file: + reader = csv.reader(file) + for row in reader: + if len(row) == 0: + break + if (row[0] == "GROUP") or not is_valid_lib(row[0], getattr(namespace, "lib", "")): + continue + + row_info = create_row_info() + is_skipped = False + for key in row_info.keys(): + if key not in CSV_COLUMNS: + row_info.pop(key) + + index = CSV_COLUMNS.index(key) + 1 + row_info[key] = row[index] + + if key == "PACKAGES": + max_packages_name_length = max(max_packages_name_length, len(row_info[key])) + elif key == "CLASS": + if '(' in row_info[key] or ')' in row_info[key] or ' ' in row_info[key]: + is_skipped = True + break + max_classes_name_length = max(max_classes_name_length, len(row_info[key])) + + if not is_skipped: + table.append(row_info) + return { + "table": table, + "max_packages_name_length": max_packages_name_length, + "max_classes_name_length": max_classes_name_length + } + + +def create_label(text: str, lbl_size: int): + if len(text) >= lbl_size: + return '| ' + text[:lbl_size] + ' |' + + return f'| {{:^{lbl_size}}} |'.format(text) + + +def display_columns(max_packages_name_length: int, max_classes_name_length: int) -> int: + global DISPLAY_COLUMNS, DEFAULT_LABEL_SIZE + result_length = 0 + for column in DISPLAY_COLUMNS: + lbl_size = DEFAULT_LABEL_SIZE + if column == "CLASS": + lbl_size = max_classes_name_length + elif column == "PACKAGES": + lbl_size = max_packages_name_length + + lbl = create_label(column, lbl_size) + result_length += lbl_size + print(lbl, end="") + print() + return result_length + + +def display_csv_data(namespace: argparse.Namespace, csv_data_dict: dict) -> None: + global DISPLAY_COLUMNS, COLUMNS_TYPES + if not getattr(namespace, "package_print", False): + DISPLAY_COLUMNS.remove("PACKAGES") + + if getattr(namespace, 'lib'): + print(f"Jacoco Covered Report Info for module named '{getattr(namespace, 'lib')}':") + + max_packages_name_length = csv_data_dict.get("max_packages_name_length", 20) + max_classes_name_length = csv_data_dict.get("max_classes_name_length", 20) + table: list[dict] = csv_data_dict.get("table", []) + result_length: int = display_columns(max_packages_name_length, max_classes_name_length) + + for row in table: + for column in DISPLAY_COLUMNS: + lbl = "" + if column in ["PACKAGES", "CLASS"]: + lbl = create_label( + row[column], + max_packages_name_length if column == "PACKAGES" else max_classes_name_length + ) + else: + vals = [int(row[column + _type]) for _type in COLUMNS_TYPES] + lbl = create_label( + f"{int(round((vals[1] - vals[0]) / vals[1], 2) * 100)}%" if vals[1] != 0 else "100%", + DEFAULT_LABEL_SIZE + ) + + print(lbl, end="") + print() + + +if __name__ == "__main__": + ns = parse_args(sys.argv[1:]) + + try: + csv_data = read_csv(ns) + except Exception as e: + print( + f"Can't read csv file: '{getattr(ns, 'input')}', get exception: '{e}'", + file=sys.stderr + ) + sys.exit(-1) + + display_csv_data(ns, csv_data) From 052e2bca289a61491ea39cfac202415df565d7ba Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Mon, 8 Apr 2024 20:04:39 +0300 Subject: [PATCH 106/122] refactor: update format of coverage information --- scripts/csv-reports-printer.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/csv-reports-printer.py b/scripts/csv-reports-printer.py index 4ed3c55..9898919 100644 --- a/scripts/csv-reports-printer.py +++ b/scripts/csv-reports-printer.py @@ -11,27 +11,21 @@ CSV_COLUMNS = [ 'PACKAGES', 'CLASS', - 'INSTRUCTION_MISSED', - 'INSTRUCTION_COVERED', 'BRANCH_MISSED', 'BRANCH_COVERED', 'LINE_MISSED', 'LINE_COVERED', - 'COMPLEXITY_MISSED', - 'COMPLEXITY_COVERED', 'METHOD_MISSED', 'METHOD_COVERED', ] DISPLAY_COLUMNS = [ 'PACKAGES', 'CLASS', - 'INSTRUCTION', 'BRANCH', 'LINE', - 'COMPLEXITY', 'METHOD', ] -DEFAULT_LABEL_SIZE = 15 +DEFAULT_LABEL_SIZE = 8 def create_row_info() -> dict: @@ -81,7 +75,7 @@ def read_csv(namespace: argparse.Namespace) -> typing.Optional[dict]: with open(getattr(namespace, "input"), 'r') as file: reader = csv.reader(file) for row in reader: - if len(row) == 0: + if len(row) == 0: break if (row[0] == "GROUP") or not is_valid_lib(row[0], getattr(namespace, "lib", "")): continue @@ -101,6 +95,9 @@ def read_csv(namespace: argparse.Namespace) -> typing.Optional[dict]: if '(' in row_info[key] or ')' in row_info[key] or ' ' in row_info[key]: is_skipped = True break + elif '.' in row_info[key]: + row_info[key] = row_info[key].split('.')[-1] + max_classes_name_length = max(max_classes_name_length, len(row_info[key])) if not is_skipped: @@ -116,6 +113,9 @@ def create_label(text: str, lbl_size: int): if len(text) >= lbl_size: return '| ' + text[:lbl_size] + ' |' + if len(text) % 2 != 0: + text = ' ' + text + return f'| {{:^{lbl_size}}} |'.format(text) From 68ece3f4b09be12c44f92cc77bd255a03b86d80e Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 01:10:39 +0300 Subject: [PATCH 107/122] refactor: update test gradle task and change logging of it. Update information of ParametrizedTests by changing display name --- lib/build.gradle.kts | 10 ++++------ .../tree_tripper/binary_trees/AVLTreeTest.kt | 12 ++++++------ .../kotlin/tree_tripper/binary_trees/BSTreeTest.kt | 14 ++++++++++---- .../iterators/BinarySearchTreeIteratorTest.kt | 8 ++++---- .../test/kotlin/tree_tripper/nodes/UtilsTest.kt | 4 ++-- .../nodes/binary_nodes/AVLTreeNodeTest.kt | 2 +- .../nodes/binary_nodes/RBTreeNodeTest.kt | 8 ++++---- 7 files changed, 31 insertions(+), 27 deletions(-) diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 47511d0..d8e9d90 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -31,12 +31,10 @@ java { tasks.named("test") { useJUnitPlatform() - finalizedBy(tasks.jacocoTestReport) -} - -tasks.withType { testLogging { - events("PASSED", "SKIPPED", "FAILED") + showCauses = false + showStackTraces = false + showExceptions = false } } @@ -51,4 +49,4 @@ tasks.named("jacocoTestReport") { csv.outputLocation = layout.buildDirectory.file("${jacocoReportsDirName}${sep}info.csv") html.outputLocation = layout.buildDirectory.dir("${jacocoReportsDirName}${sep}html") } -} +} \ No newline at end of file diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt index 124e62e..5296d7c 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/AVLTreeTest.kt @@ -26,42 +26,42 @@ class AVLTreeTest { Assertions.assertEquals(0, tree.size) } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testNodeCreationCases") @DisplayName("node creation") public fun testNodeCreation(key: Int, value: Int) { tree.assertNodeCreation(key, value) } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testBalanceTreeCases") @DisplayName("check balance tree") public fun testBalanceTree(expected: AVLTreeNode, node: AVLTreeNode) { tree.assertBalanceTree(expected, node) } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("checkBalanceFactor") @DisplayName("balance factor") public fun checkBalanceFactor(expected: Int, node: AVLTreeNode?) { tree.assertBalanceFactor(expected, node) } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testBalanceCases") @DisplayName("balance case") public fun testBalanceCase(expected: AVLTreeNode, node: AVLTreeNode) { tree.assertBalance(expected, node) } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testNodeRotateLeftCases") @DisplayName("node rotate left case") public fun testNodeRotateLeftCases(expected: AVLTreeNode, node: AVLTreeNode) { tree.assertNodeLeftRotation(expected, node) } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testNodeRotateRightCases") @DisplayName("node rotate right case") public fun testNodeRotateRightCase(expected: AVLTreeNode, node: AVLTreeNode) { diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index 13da642..f3a0a24 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -12,6 +12,7 @@ import tree_tripper.iterators.IterationOrders import java.time.Duration import kotlin.random.Random import kotlin.test.Test +import kotlin.test.assertEquals public class BSTreeTest { @@ -22,6 +23,11 @@ public class BSTreeTest { tree = BSTreeTestAssistant() } + @Test + public fun maximkaTop() { + assertEquals("a", "asdf") + } + @Test @DisplayName("tree initialization") public fun testTreeInitialization() { @@ -59,7 +65,7 @@ public class BSTreeTest { Assertions.assertEquals(tree.getRoot(), Pair(1, 0), "Incorrect change root") } - @Test + @Test() @DisplayName("insert children root") public fun testInsertChildrenRoot() { tree.insert(2, -2) @@ -69,7 +75,7 @@ public class BSTreeTest { Assertions.assertEquals(tree.size, 3, "Incorrect resizing tree size.") } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("getSizeAndTimeArguments") @DisplayName("insert with size and time") public fun testInsertWithSizeAndTime(size: Int, seconds: Long) { @@ -115,7 +121,7 @@ public class BSTreeTest { Assertions.assertEquals(tree.search(0), null, "Incorrect search a non-existent child root.") } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("getSizeAndTimeArguments") @DisplayName("search with size and time") public fun testSearchWithSizeAndTime(size: Int, seconds: Long) { @@ -285,7 +291,7 @@ public class BSTreeTest { Assertions.assertEquals(tree.search(3), -3, "Incorrect remove a root and lose the right child.") } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("getSizeAndTimeArguments") @DisplayName("remove with size and time") public fun testRemoveWithSizeAndTime(size: Int, seconds: Long) { diff --git a/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt b/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt index 0cbf597..3ac0388 100644 --- a/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt +++ b/lib/src/test/kotlin/tree_tripper/iterators/BinarySearchTreeIteratorTest.kt @@ -11,7 +11,7 @@ import tree_tripper.nodes.binary_nodes.BSTreeNode class BinarySearchTreeIteratorTest { lateinit var iterator: BinarySearchTreeIterator> - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testIteratorCases") @DisplayName("test iterator at width order") public fun testWidthOrderIterator(expected: List, root: BSTreeNode) { @@ -24,7 +24,7 @@ class BinarySearchTreeIteratorTest { } } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testGetALotOfElementsCases") @DisplayName("try get more elements than iterator has") public fun testGetALotOfElements(order: IterationOrders) { @@ -33,7 +33,7 @@ class BinarySearchTreeIteratorTest { Assertions.assertThrows(NoSuchElementException::class.java) { iterator.next() } } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testIteratorCases") @DisplayName("test iterator at increase order") public fun testIncreasingOrderIterator(expected: List, root: BSTreeNode) { @@ -47,7 +47,7 @@ class BinarySearchTreeIteratorTest { } } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testIteratorCases") @DisplayName("test iterator at decrease order") public fun testDecreasingOrderIterator(expected: List, root: BSTreeNode) { diff --git a/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt index cc2fa1a..c21815d 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/UtilsTest.kt @@ -10,7 +10,7 @@ import tree_tripper.nodes.binary_nodes.BSTreeNode public class UtilsTest { - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testNodeUpdateCases") @DisplayName("util of node update") public fun testNodeUpdate(expected: Boolean, node: BSTreeNode?) { @@ -19,7 +19,7 @@ public class UtilsTest { Assertions.assertEquals(expected, isActivateAction) } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testNodeActionCases") @DisplayName("util of action on node") public fun testNodeAction(expected: Boolean, node: BSTreeNode?) { diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt index cad70a1..ca1a2b2 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/AVLTreeNodeTest.kt @@ -17,7 +17,7 @@ class AVLTreeNodeTest { Assertions.assertEquals(1, node.height) {"The height is not 1 by standard initialize."} } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testUpdateHeight") @DisplayName("update height") public fun testUpdateHeight(expected: Int, node: AVLTreeNode) { diff --git a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt index 5a71782..ac95991 100644 --- a/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNodeTest.kt @@ -10,7 +10,7 @@ import org.junit.jupiter.params.provider.MethodSource public class RBTreeNodeTest { - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testNodeSimpleInitializeCases") @DisplayName("node simple initialization") public fun testNodeSimpleInitialize(key: Int, value: Int?) { @@ -22,7 +22,7 @@ public class RBTreeNodeTest { Assertions.assertNull(node.rightChild) { "Right child of node is not null." } } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testNodeColorTypeInitializeCases") @DisplayName("node initialization with color") public fun testNodeColorTypeInitialize(isRed: Boolean) { @@ -32,7 +32,7 @@ public class RBTreeNodeTest { Assertions.assertNull(node.rightChild) { "Right child of node is not null." } } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testNodeFullInitializeCases") @DisplayName("node initialization with color and children") public fun testNodeFullInitialize(leftChild: RBTreeNode?, rightChild: RBTreeNode?) { @@ -41,7 +41,7 @@ public class RBTreeNodeTest { assertBinaryNodeDeepEquals(rightChild, node.rightChild) { n1, n2 -> n1.isRed == n2.isRed } } - @ParameterizedTest + @ParameterizedTest(name = "{displayName}[{index}] {argumentsWithNames}") @MethodSource("testToStringSimpleViewCases") @DisplayName("to string simple view") public fun testToStringSimpleView(expected: String, node: RBTreeNode) { From 1a493ec49801e32ac7e0117413cb523d7fb77879 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 13:28:22 +0300 Subject: [PATCH 108/122] test: add test result printer to terminal and add run cmd at action workflow yml-script --- .github/workflows/build-test.yml | 8 ++- scripts/test-result-printer.py | 105 +++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 scripts/test-result-printer.py diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index c98aaf8..f66d304 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -19,7 +19,13 @@ jobs: run: ./gradlew build -x test - name: Run tests with jacoco - run: ./gradlew test + run: ./gradlew test >./test-res-out.log 2>./test-res-err.log + + - name: Display test results + run: python ./scripts/test-result-printer.py -d ./lib/build/test-results/test - name: Display info about coverage run: python3 ./scripts/csv-reports-printer.py -i ./lib/build/reports/jacoco/info.csv -l lib + + - name: Clearing tmpfiles of test runnig + run: rm ./test-res-out.log && rm ./test-res-err.log diff --git a/scripts/test-result-printer.py b/scripts/test-result-printer.py new file mode 100644 index 0000000..cf7db31 --- /dev/null +++ b/scripts/test-result-printer.py @@ -0,0 +1,105 @@ +import argparse +import sys, os, pprint +import typing +import xml.etree.ElementTree as ET + + +class TestCase: + + def __init__(self, name: str, is_passed: bool) -> None: + self.name = str(name) + self.is_passed = bool(is_passed) + + def toString(self, indent: int = 0): + return "\t" * indent + f"{self.name} -> {'PASSED' if self.is_passed else 'FAILURE'}" + + def __bool__(self): + return self.is_passed + + +class ParametrisedTestCase(TestCase): + + def __init__(self, name: str, cases: list[TestCase]) -> None: + super().__init__(name, all(cases)) + self.cases = cases + + def add_case(self, case: TestCase): + self.is_passed = self.is_passed and case.is_passed + self.cases.append(case) + + def toString(self, indent: int = 0): + inline_cases = [] + for case in self.cases: + inline_cases.append(case.toString(indent + 1)) + + inline_cases = '\n'.join(inline_cases).rstrip() + return "\t" * indent + f"{self.name} -> {'PASSED' if self.is_passed else 'FAILURE'}\n{inline_cases}" + + +def parse_args(args: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser( + prog="Test Result Printer", + description="Program read xml files with tests results and print it at terminal at stdin", + ) + parser.add_argument( + "-d", "--dir", required=True, + help="setup path to directory with xml files with tests information", + metavar="" + ) + return parser.parse_args(args) + + +def display_test(test_path: str): + tree_root = ET.parse(test_path).getroot() + print( + "Tests of ", + tree_root.attrib.get("name", "UncnownTestSuite").split('.')[-1].replace("Test", ":"), + sep=" " + ) + cases = dict() + for child in tree_root: + if child.tag != "testcase": + continue + + name = child.get("name", "uncknown test cases") + is_passed = child.find("failure") is None + if '[' in name: + primary_name = name.split('[')[0] + args = '[' + name.split('[')[1] + + case: ParametrisedTestCase = cases.get(primary_name, ParametrisedTestCase(primary_name, [])) + case.add_case(TestCase(args, is_passed)) + cases[primary_name] = case + else: + cases[name] = TestCase(name, is_passed) + + for name in sorted(cases.keys()): + print(cases[name].toString(indent=1)) + + print( + f"Passed: {int(tree_root.attrib.get("tests", 0)) - int(tree_root.attrib.get("failures", 0))}", + f"Failures: {tree_root.attrib.get("failures", 0)}", + f"Time: {tree_root.attrib.get("time", 0.0)}", + sep=" ", + end=os.linesep * 2 + ) + return [] + + +if __name__ == "__main__": + ns = parse_args(sys.argv[1:]) + + tests_result_dir = getattr(ns, "dir") + childs = os.listdir(tests_result_dir) + for child in sorted(childs): + child_path = os.path.join(tests_result_dir, child) + if not os.path.isfile(child_path): + continue + + if not (child.startswith("TEST") and child.endswith(".xml")): + continue + + try: + display_test(child_path) + except Exception as e: + print(f"Can't display ttest information at file '{child}': {e}", file=sys.stderr) From 1a1a6c42e64eefaf09faf04c242e49609a4c98e4 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 13:37:23 +0300 Subject: [PATCH 109/122] fix: update yml-script to continue execution on error at execution tests --- .github/workflows/build-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index f66d304..87ed2f3 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -23,6 +23,7 @@ jobs: - name: Display test results run: python ./scripts/test-result-printer.py -d ./lib/build/test-results/test + continue-on-error: true - name: Display info about coverage run: python3 ./scripts/csv-reports-printer.py -i ./lib/build/reports/jacoco/info.csv -l lib From a541b276eb95268d51a0b02dceb19920d54e0577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=20=D0=A1=D1=83=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=B2?= <90147482+IliaSuponeff@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:50:57 +0300 Subject: [PATCH 110/122] Update build-test.yml by change tasks line init --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 87ed2f3..1abc77a 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -22,8 +22,8 @@ jobs: run: ./gradlew test >./test-res-out.log 2>./test-res-err.log - name: Display test results - run: python ./scripts/test-result-printer.py -d ./lib/build/test-results/test continue-on-error: true + run: python ./scripts/test-result-printer.py -d ./lib/build/test-results/test - name: Display info about coverage run: python3 ./scripts/csv-reports-printer.py -i ./lib/build/reports/jacoco/info.csv -l lib From e299d152379b3b8f07eeb139669360678bfd80de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=20=D0=A1=D1=83=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=B2?= <90147482+IliaSuponeff@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:54:24 +0300 Subject: [PATCH 111/122] fix: update build-test.yml to contiue on error at tests runtime(test fail)) --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 1abc77a..e73c003 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -20,9 +20,9 @@ jobs: - name: Run tests with jacoco run: ./gradlew test >./test-res-out.log 2>./test-res-err.log + continue-on-error: true - name: Display test results - continue-on-error: true run: python ./scripts/test-result-printer.py -d ./lib/build/test-results/test - name: Display info about coverage From 3c5420b27ae6f144e2cd04cc44eb0055ab0117fb Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 14:02:55 +0300 Subject: [PATCH 112/122] refactor: update test result printer test --- scripts/test-result-printer.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/test-result-printer.py b/scripts/test-result-printer.py index cf7db31..6aa84c7 100644 --- a/scripts/test-result-printer.py +++ b/scripts/test-result-printer.py @@ -65,7 +65,7 @@ def display_test(test_path: str): is_passed = child.find("failure") is None if '[' in name: primary_name = name.split('[')[0] - args = '[' + name.split('[')[1] + args = '[' + "[".join(name.split('[')[1:]) case: ParametrisedTestCase = cases.get(primary_name, ParametrisedTestCase(primary_name, [])) case.add_case(TestCase(args, is_passed)) @@ -76,10 +76,11 @@ def display_test(test_path: str): for name in sorted(cases.keys()): print(cases[name].toString(indent=1)) + passed_test_count = int(tree_root.attrib.get("tests", 0)) - int(tree_root.attrib.get("failures", 0)) print( - f"Passed: {int(tree_root.attrib.get("tests", 0)) - int(tree_root.attrib.get("failures", 0))}", - f"Failures: {tree_root.attrib.get("failures", 0)}", - f"Time: {tree_root.attrib.get("time", 0.0)}", + f"Passed: {passed_test_count}", + f"Failures: {tree_root.attrib.get('failures', 0)}", + f"Time: {tree_root.attrib.get('time', 0.0)}", sep=" ", end=os.linesep * 2 ) From 87b88fa08e53c2367f02e6218b598753772e35b0 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 14:07:03 +0300 Subject: [PATCH 113/122] refactor: update yml-script to more-continue on error at same steps of displaying test result information --- .github/workflows/build-test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index e73c003..a0a4ffd 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -24,9 +24,11 @@ jobs: - name: Display test results run: python ./scripts/test-result-printer.py -d ./lib/build/test-results/test + continue-on-error: true - name: Display info about coverage run: python3 ./scripts/csv-reports-printer.py -i ./lib/build/reports/jacoco/info.csv -l lib + continue-on-error: true - name: Clearing tmpfiles of test runnig run: rm ./test-res-out.log && rm ./test-res-err.log From 410fdc355945c26aab7ba069a7bc8d6af28cb04b Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 14:45:34 +0300 Subject: [PATCH 114/122] test: add colorize output of tests result printer --- scripts/test-result-printer.py | 23 +++++++++++++++-------- scripts/text_colorize.py | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 scripts/text_colorize.py diff --git a/scripts/test-result-printer.py b/scripts/test-result-printer.py index 6aa84c7..c2c4f96 100644 --- a/scripts/test-result-printer.py +++ b/scripts/test-result-printer.py @@ -1,7 +1,7 @@ import argparse -import sys, os, pprint -import typing +import sys, os import xml.etree.ElementTree as ET +from text_colorize import ANSIColors, TextStyle, colorize class TestCase: @@ -11,8 +11,15 @@ def __init__(self, name: str, is_passed: bool) -> None: self.is_passed = bool(is_passed) def toString(self, indent: int = 0): - return "\t" * indent + f"{self.name} -> {'PASSED' if self.is_passed else 'FAILURE'}" - + return "\t" * indent + f"{self.name} -> {self.result_type()}" + + def result_type(self) -> str: + return colorize( + 'PASSED' if self.is_passed else 'FAILURE', + ANSIColors.GREEN if self.is_passed else ANSIColors.RED, + TextStyle.ITALIC + ) + def __bool__(self): return self.is_passed @@ -33,7 +40,7 @@ def toString(self, indent: int = 0): inline_cases.append(case.toString(indent + 1)) inline_cases = '\n'.join(inline_cases).rstrip() - return "\t" * indent + f"{self.name} -> {'PASSED' if self.is_passed else 'FAILURE'}\n{inline_cases}" + return super().toString(indent) + f"\n{inline_cases}" def parse_args(args: list[str]) -> argparse.Namespace: @@ -52,7 +59,7 @@ def parse_args(args: list[str]) -> argparse.Namespace: def display_test(test_path: str): tree_root = ET.parse(test_path).getroot() print( - "Tests of ", + "Tests of", tree_root.attrib.get("name", "UncnownTestSuite").split('.')[-1].replace("Test", ":"), sep=" " ) @@ -78,8 +85,8 @@ def display_test(test_path: str): passed_test_count = int(tree_root.attrib.get("tests", 0)) - int(tree_root.attrib.get("failures", 0)) print( - f"Passed: {passed_test_count}", - f"Failures: {tree_root.attrib.get('failures', 0)}", + colorize(f"Passed: {passed_test_count}", ANSIColors.GREEN, TextStyle.BOLD), + colorize(f"Failures: {tree_root.attrib.get('failures', 0)}", ANSIColors.RED, TextStyle.BOLD), f"Time: {tree_root.attrib.get('time', 0.0)}", sep=" ", end=os.linesep * 2 diff --git a/scripts/text_colorize.py b/scripts/text_colorize.py new file mode 100644 index 0000000..a25a857 --- /dev/null +++ b/scripts/text_colorize.py @@ -0,0 +1,20 @@ +import enum + +class ANSIColors(enum.IntEnum): + PURPLE = 35 + BLUE = 34 + GREEN = 32 + YELLOW = 33 + RED = 31 + BLACK = 30 + WHITE = 37 + + +class TextStyle(enum.IntEnum): + SIMPLE = 0 + BOLD = 1 + ITALIC = 3 + + +def colorize(text: str, color: ANSIColors, style: TextStyle = TextStyle.SIMPLE) -> str: + return f"\033[{style};{color}m{text}\033[0m" \ No newline at end of file From cc7f891e65d0e841db41a630081ce9138bb1ce2f Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 15:45:53 +0300 Subject: [PATCH 115/122] test: add colorize output of tests coverage printer --- scripts/csv-reports-printer.py | 44 ++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/scripts/csv-reports-printer.py b/scripts/csv-reports-printer.py index 9898919..5a71cf8 100644 --- a/scripts/csv-reports-printer.py +++ b/scripts/csv-reports-printer.py @@ -2,6 +2,8 @@ import csv import sys import typing +from text_colorize import ANSIColors, TextStyle, colorize + COLUMNS_TYPES = [ '_MISSED', @@ -109,19 +111,38 @@ def read_csv(namespace: argparse.Namespace) -> typing.Optional[dict]: } -def create_label(text: str, lbl_size: int): +def create_label(text: str, lbl_size: int, color: ANSIColors = ANSIColors.BLACK): if len(text) >= lbl_size: - return '| ' + text[:lbl_size] + ' |' + text = text[:lbl_size] if len(text) % 2 != 0: text = ' ' + text - return f'| {{:^{lbl_size}}} |'.format(text) + color_text = colorize( + f"{{:^{lbl_size}}}".format(text), + color, + TextStyle.BOLD + ) + return f'| {color_text} |' + + +def colorize_percent_label(percent: int) -> str: + color = ANSIColors.RED + if 50 <= percent < 75: + color = ANSIColors.YELLOW + elif 75 <= percent <= 100: + color = ANSIColors.GREEN + + color_text = colorize( + f"{{:^{DEFAULT_LABEL_SIZE}}}".format(f"{percent}%"), + color, + TextStyle.BOLD + ) + return create_label(f"{percent}%", DEFAULT_LABEL_SIZE, color) def display_columns(max_packages_name_length: int, max_classes_name_length: int) -> int: global DISPLAY_COLUMNS, DEFAULT_LABEL_SIZE - result_length = 0 for column in DISPLAY_COLUMNS: lbl_size = DEFAULT_LABEL_SIZE if column == "CLASS": @@ -129,11 +150,9 @@ def display_columns(max_packages_name_length: int, max_classes_name_length: int) elif column == "PACKAGES": lbl_size = max_packages_name_length - lbl = create_label(column, lbl_size) - result_length += lbl_size + lbl = create_label(column, lbl_size, ANSIColors.PURPLE) print(lbl, end="") print() - return result_length def display_csv_data(namespace: argparse.Namespace, csv_data_dict: dict) -> None: @@ -147,7 +166,7 @@ def display_csv_data(namespace: argparse.Namespace, csv_data_dict: dict) -> None max_packages_name_length = csv_data_dict.get("max_packages_name_length", 20) max_classes_name_length = csv_data_dict.get("max_classes_name_length", 20) table: list[dict] = csv_data_dict.get("table", []) - result_length: int = display_columns(max_packages_name_length, max_classes_name_length) + display_columns(max_packages_name_length, max_classes_name_length) for row in table: for column in DISPLAY_COLUMNS: @@ -155,13 +174,14 @@ def display_csv_data(namespace: argparse.Namespace, csv_data_dict: dict) -> None if column in ["PACKAGES", "CLASS"]: lbl = create_label( row[column], - max_packages_name_length if column == "PACKAGES" else max_classes_name_length + max_packages_name_length if column == "PACKAGES" else max_classes_name_length, + ANSIColors.RED ) else: vals = [int(row[column + _type]) for _type in COLUMNS_TYPES] - lbl = create_label( - f"{int(round((vals[1] - vals[0]) / vals[1], 2) * 100)}%" if vals[1] != 0 else "100%", - DEFAULT_LABEL_SIZE + percent = int(round((vals[1] - vals[0]) / vals[1], 2) * 100) if vals[1] != 0 else 100 + lbl = colorize_percent_label( + percent, ) print(lbl, end="") From 2b6c419dc1b71c71b55e527375f6c252621914e7 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 15:47:01 +0300 Subject: [PATCH 116/122] refactor: set yml-script result checking of execution test task --- .github/workflows/build-test.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index a0a4ffd..d4a4a00 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -18,15 +18,22 @@ jobs: - name: Build source code run: ./gradlew build -x test - - name: Run tests with jacoco + - name: Run tests + id: run-tests run: ./gradlew test >./test-res-out.log 2>./test-res-err.log continue-on-error: true - name: Display test results run: python ./scripts/test-result-printer.py -d ./lib/build/test-results/test continue-on-error: true + + - name: Run jacoco tests coverage report creating + if: ${{ steps.run-tests.outcome == 'success' }} + run: ./gradlew jacocoTestReport >./test-res-out.log 2>./test-res-err.log + continue-on-error: true - name: Display info about coverage + if: ${{ steps.run-tests.outcome == 'success' }} run: python3 ./scripts/csv-reports-printer.py -i ./lib/build/reports/jacoco/info.csv -l lib continue-on-error: true From 9f52cf356d41a1dfbab292a62b898a9ddc969b55 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 15:51:11 +0300 Subject: [PATCH 117/122] refactor: delete test that always crashes, used to check the output of test results --- lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt index f3a0a24..226b109 100644 --- a/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt +++ b/lib/src/test/kotlin/tree_tripper/binary_trees/BSTreeTest.kt @@ -23,11 +23,6 @@ public class BSTreeTest { tree = BSTreeTestAssistant() } - @Test - public fun maximkaTop() { - assertEquals("a", "asdf") - } - @Test @DisplayName("tree initialization") public fun testTreeInitialization() { From e1956c7413e7f8c5e1c39d27514ef701ca6c0a1d Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 18:16:46 +0300 Subject: [PATCH 118/122] refactor: update yml-script to minimaize checking of tests execution. Disable depentancy task jacocoTestReport on task test. Change colorize of test coverage information and add endline at file text_colorize.py --- .github/workflows/build-test.yml | 8 +------- lib/build.gradle.kts | 1 - scripts/csv-reports-printer.py | 9 ++------- scripts/text_colorize.py | 2 +- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index d4a4a00..b67c263 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -19,23 +19,17 @@ jobs: run: ./gradlew build -x test - name: Run tests - id: run-tests run: ./gradlew test >./test-res-out.log 2>./test-res-err.log continue-on-error: true - name: Display test results - run: python ./scripts/test-result-printer.py -d ./lib/build/test-results/test - continue-on-error: true + run: python3 ./scripts/test-result-printer.py -d ./lib/build/test-results/test - name: Run jacoco tests coverage report creating - if: ${{ steps.run-tests.outcome == 'success' }} run: ./gradlew jacocoTestReport >./test-res-out.log 2>./test-res-err.log - continue-on-error: true - name: Display info about coverage - if: ${{ steps.run-tests.outcome == 'success' }} run: python3 ./scripts/csv-reports-printer.py -i ./lib/build/reports/jacoco/info.csv -l lib - continue-on-error: true - name: Clearing tmpfiles of test runnig run: rm ./test-res-out.log && rm ./test-res-err.log diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index d8e9d90..62c75da 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -39,7 +39,6 @@ tasks.named("test") { } tasks.named("jacocoTestReport") { - dependsOn(tasks.test) val sep = File.separator val jacocoReportsDirName = "reports${sep}jacoco" reports { diff --git a/scripts/csv-reports-printer.py b/scripts/csv-reports-printer.py index 5a71cf8..4ee0bbe 100644 --- a/scripts/csv-reports-printer.py +++ b/scripts/csv-reports-printer.py @@ -132,12 +132,7 @@ def colorize_percent_label(percent: int) -> str: color = ANSIColors.YELLOW elif 75 <= percent <= 100: color = ANSIColors.GREEN - - color_text = colorize( - f"{{:^{DEFAULT_LABEL_SIZE}}}".format(f"{percent}%"), - color, - TextStyle.BOLD - ) + return create_label(f"{percent}%", DEFAULT_LABEL_SIZE, color) @@ -175,7 +170,7 @@ def display_csv_data(namespace: argparse.Namespace, csv_data_dict: dict) -> None lbl = create_label( row[column], max_packages_name_length if column == "PACKAGES" else max_classes_name_length, - ANSIColors.RED + ANSIColors.YELLOW ) else: vals = [int(row[column + _type]) for _type in COLUMNS_TYPES] diff --git a/scripts/text_colorize.py b/scripts/text_colorize.py index a25a857..ce38225 100644 --- a/scripts/text_colorize.py +++ b/scripts/text_colorize.py @@ -17,4 +17,4 @@ class TextStyle(enum.IntEnum): def colorize(text: str, color: ANSIColors, style: TextStyle = TextStyle.SIMPLE) -> str: - return f"\033[{style};{color}m{text}\033[0m" \ No newline at end of file + return f"\033[{style};{color}m{text}\033[0m" From 092b89d4a62991125867c7fc90abd8daf6084050 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 18:52:29 +0300 Subject: [PATCH 119/122] refactor: split display_test function into two parse_test_result and display_test_result --- scripts/test-result-printer.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/scripts/test-result-printer.py b/scripts/test-result-printer.py index c2c4f96..b46c3ec 100644 --- a/scripts/test-result-printer.py +++ b/scripts/test-result-printer.py @@ -56,13 +56,8 @@ def parse_args(args: list[str]) -> argparse.Namespace: return parser.parse_args(args) -def display_test(test_path: str): +def parse_test_result(test_path: str) -> tuple[ET.Element, dict[str, TestCase]]: tree_root = ET.parse(test_path).getroot() - print( - "Tests of", - tree_root.attrib.get("name", "UncnownTestSuite").split('.')[-1].replace("Test", ":"), - sep=" " - ) cases = dict() for child in tree_root: if child.tag != "testcase": @@ -79,7 +74,16 @@ def display_test(test_path: str): cases[primary_name] = case else: cases[name] = TestCase(name, is_passed) + + return (tree_root, cases,) + +def display_test_result(tree_root: ET.Element, cases: dict[str, TestCase]): + print( + "Tests of", + tree_root.attrib.get("name", "UncnownTestSuite").split('.')[-1].replace("Test", ":"), + sep=" " + ) for name in sorted(cases.keys()): print(cases[name].toString(indent=1)) @@ -91,7 +95,6 @@ def display_test(test_path: str): sep=" ", end=os.linesep * 2 ) - return [] if __name__ == "__main__": @@ -99,6 +102,7 @@ def display_test(test_path: str): tests_result_dir = getattr(ns, "dir") childs = os.listdir(tests_result_dir) + tests_results: list[tuple[ET.Element, dict[str, TestCase]]] = [] for child in sorted(childs): child_path = os.path.join(tests_result_dir, child) if not os.path.isfile(child_path): @@ -108,6 +112,9 @@ def display_test(test_path: str): continue try: - display_test(child_path) + tests_results += parse_test_result(child_path) except Exception as e: print(f"Can't display ttest information at file '{child}': {e}", file=sys.stderr) + + for test_result in tests_results: + display_test_result(tests_results[0], tests_results[1]) From 2185545f8d61ee1717ba97082e4c246177411057 Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 18:53:13 +0300 Subject: [PATCH 120/122] refactor: update names of steps at yml-script --- .github/workflows/build-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index b67c263..6e1952d 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -25,11 +25,11 @@ jobs: - name: Display test results run: python3 ./scripts/test-result-printer.py -d ./lib/build/test-results/test - - name: Run jacoco tests coverage report creating + - name: Run jacoco coverage report run: ./gradlew jacocoTestReport >./test-res-out.log 2>./test-res-err.log - name: Display info about coverage run: python3 ./scripts/csv-reports-printer.py -i ./lib/build/reports/jacoco/info.csv -l lib - - name: Clearing tmpfiles of test runnig + - name: Clear tmpfiles of runnig tests run: rm ./test-res-out.log && rm ./test-res-err.log From 054d6f85d0913b15255ee4c170f607d84866e44d Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 19:40:49 +0300 Subject: [PATCH 121/122] test: update python-script for displaying test results and update it accordingly yml-script to run updated python-script correctly --- .github/workflows/build-test.yml | 4 +- scripts/test-result-printer.py | 73 ++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 6e1952d..44b952f 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -23,13 +23,13 @@ jobs: continue-on-error: true - name: Display test results - run: python3 ./scripts/test-result-printer.py -d ./lib/build/test-results/test + run: python3 ./scripts/test-result-printer.py --dir ./lib/build/test-results/test --all-failures - name: Run jacoco coverage report run: ./gradlew jacocoTestReport >./test-res-out.log 2>./test-res-err.log - name: Display info about coverage - run: python3 ./scripts/csv-reports-printer.py -i ./lib/build/reports/jacoco/info.csv -l lib + run: python3 ./scripts/csv-reports-printer.py --input ./lib/build/reports/jacoco/info.csv --lib lib - name: Clear tmpfiles of runnig tests run: rm ./test-res-out.log && rm ./test-res-err.log diff --git a/scripts/test-result-printer.py b/scripts/test-result-printer.py index b46c3ec..5f9e987 100644 --- a/scripts/test-result-printer.py +++ b/scripts/test-result-printer.py @@ -34,9 +34,12 @@ def add_case(self, case: TestCase): self.is_passed = self.is_passed and case.is_passed self.cases.append(case) - def toString(self, indent: int = 0): + def toString(self, indent: int = 0, also_failed: bool = False): inline_cases = [] for case in self.cases: + if (also_failed and case.is_passed): + continue + inline_cases.append(case.toString(indent + 1)) inline_cases = '\n'.join(inline_cases).rstrip() @@ -53,6 +56,16 @@ def parse_args(args: list[str]) -> argparse.Namespace: help="setup path to directory with xml files with tests information", metavar="" ) + parser.add_argument( + "-a", "--all", + action="store_true", + help="setup mode of diplay type to print all test information to ON (default OFF)", + ) + parser.add_argument( + "-f", "--all-failures", + action="store_true", + help="setup mode of diplay type to print also test information which failed to ON (default OFF)", + ) return parser.parse_args(args) @@ -78,7 +91,7 @@ def parse_test_result(test_path: str) -> tuple[ET.Element, dict[str, TestCase]]: return (tree_root, cases,) -def display_test_result(tree_root: ET.Element, cases: dict[str, TestCase]): +def display_all_test_result(tree_root: ET.Element, cases: dict[str, TestCase]): print( "Tests of", tree_root.attrib.get("name", "UncnownTestSuite").split('.')[-1].replace("Test", ":"), @@ -96,6 +109,36 @@ def display_test_result(tree_root: ET.Element, cases: dict[str, TestCase]): end=os.linesep * 2 ) + +def display_failures_test_result(tree_root: ET.Element, cases: dict[str, TestCase]): + failed_tests = [] + for name in sorted(cases.keys()): + if not cases[name].is_passed: + failed_tests.append(cases[name]) + + if len(failed_tests) == 0: + return + + print( + "Failed tests of", + tree_root.attrib.get("name", "UncnownTestSuite").split('.')[-1].replace("Test", ":"), + sep=" " + ) + for case in failed_tests: + if isinstance(case, ParametrisedTestCase): + print(case.toString(indent=1, also_failed=True)) + elif isinstance(case, TestCase): + print(case.toString(indent=1)) + + passed_test_count = int(tree_root.attrib.get("tests", 0)) - int(tree_root.attrib.get("failures", 0)) + print( + colorize(f"Passed: {passed_test_count}", ANSIColors.GREEN, TextStyle.BOLD), + colorize(f"Failures: {tree_root.attrib.get('failures', 0)}", ANSIColors.RED, TextStyle.BOLD), + f"Time: {tree_root.attrib.get('time', 0.0)}", + sep=" ", + end=os.linesep * 2 + ) + if __name__ == "__main__": ns = parse_args(sys.argv[1:]) @@ -112,9 +155,31 @@ def display_test_result(tree_root: ET.Element, cases: dict[str, TestCase]): continue try: - tests_results += parse_test_result(child_path) + tests_results.append(parse_test_result(child_path)) except Exception as e: print(f"Can't display ttest information at file '{child}': {e}", file=sys.stderr) + tests_count = 0 + tests_failed_count = 0 + time_of_all_tests = 0 + for test_result in tests_results: + tree_root: ET.Element = test_result[0] + tests_count += int(tree_root.attrib.get("tests", 0)) + tests_failed_count += int(tree_root.attrib.get("failures", 0)) + time_of_all_tests += float(tree_root.attrib.get('time', 0.0)) + + + print( + colorize(f"Count of tests: {tests_count}", ANSIColors.YELLOW, TextStyle.BOLD), + colorize(f"Count of passed tests: {tests_count - tests_failed_count}", ANSIColors.GREEN, TextStyle.BOLD), + colorize(f"Count of failured tests: {tests_failed_count}", ANSIColors.RED, TextStyle.BOLD), + colorize(f"Time: {time_of_all_tests}", ANSIColors.BLUE, TextStyle.BOLD), + sep=os.linesep, + end=os.linesep * 2 + ) + for test_result in tests_results: - display_test_result(tests_results[0], tests_results[1]) + if getattr(ns, "all", False): + display_all_test_result(test_result[0], test_result[1]) + elif getattr(ns, "all_failures", False): + display_failures_test_result(test_result[0], test_result[1]) From 8a0b4edf582c5bc9d98fa2b5e8ee34745f064f4e Mon Sep 17 00:00:00 2001 From: IliaSuponeff Date: Tue, 9 Apr 2024 19:46:33 +0300 Subject: [PATCH 122/122] refactor: remove unnecessary variable type declarations --- .../tree_tripper/binary_trees/RBTree.kt | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index 6cf81ca..12aa1ac 100644 --- a/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt +++ b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt @@ -41,7 +41,7 @@ public open class RBTree, V>: AbstractBSTree?, V?> var resultCompare: Int = key.compareTo(node.key) - var nodeCurrent: RBTreeNode = node + var nodeCurrent = node if (resultCompare < 0) { if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild)) nodeCurrent = moveRedLeft(nodeCurrent) @@ -105,13 +105,13 @@ public open class RBTree, V>: AbstractBSTree): RBTreeNode { - val rightChild: RBTreeNode = node.rightChild ?: return node - node.rightChild = rightChild.leftChild - rightChild.leftChild = node + val nodeSwapped: RBTreeNode = node.rightChild ?: return node + node.rightChild = nodeSwapped.leftChild + nodeSwapped.leftChild = node - rightChild.isRed = node.isRed + nodeSwapped.isRed = node.isRed node.isRed = true - return rightChild + return nodeSwapped } /** @@ -122,13 +122,13 @@ public open class RBTree, V>: AbstractBSTree): RBTreeNode { - val leftChild: RBTreeNode = node.leftChild ?: return node - node.leftChild = leftChild.rightChild - leftChild.rightChild = node + val nodeSwapped: RBTreeNode = node.leftChild ?: return node + node.leftChild = nodeSwapped.rightChild + nodeSwapped.rightChild = node - leftChild.isRed = node.isRed + nodeSwapped.isRed = node.isRed node.isRed = true - return leftChild + return nodeSwapped } /** @@ -151,7 +151,7 @@ public open class RBTree, V>: AbstractBSTree): RBTreeNode { if (node.rightChild == null) return node - var nodeCurrent: RBTreeNode = node + var nodeCurrent = node flipColors(nodeCurrent) if (isRedLeftChild(nodeCurrent.leftChild)) { @@ -170,13 +170,13 @@ public open class RBTree, V>: AbstractBSTree): RBTreeNode { if (node.leftChild == null) return node - var nodeCurrent: RBTreeNode = node + var nodeCurrent = node flipColors(nodeCurrent) if (isRedLeftChild(nodeCurrent.rightChild)) { nodeCurrent.rightChild = notNullNodeAction( node.rightChild, null - ) {rightChild -> rotateRight(rightChild)} + ) { rightChild -> rotateRight(rightChild) } nodeCurrent = rotateLeft(nodeCurrent) flipColors(nodeCurrent) } @@ -193,7 +193,7 @@ public open class RBTree, V>: AbstractBSTree = node + var nodeCurrent = node if (!isRedColor(leftChild) && !isRedLeftChild(leftChild)) nodeCurrent = moveRedLeft(nodeCurrent)