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/binary_trees/RBTree.kt b/lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt index eebe016..6cf81ca 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?) { @@ -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 { + protected fun moveRedRight(node: RBTreeNode): 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) @@ -187,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/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/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/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/RBTreeTest.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt index f6fceee..f553378 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,10 +58,23 @@ class RBTreeTest { tree.insert(value, value) elements.add(value) tree.assertIsRBTree() - Assertions.assertEquals(elements.size, tree.getSize()) + Assertions.assertEquals(elements.size, tree.size) } } + @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() { @@ -69,7 +82,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,15 +91,16 @@ 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") 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()) + Assertions.assertEquals(20, tree.size) } @Test @@ -97,7 +111,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 +125,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,15 +135,16 @@ 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") 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()) + Assertions.assertEquals(21, tree.size) } @Test @@ -138,10 +153,165 @@ 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()) } + @Test + @DisplayName("remove min node at empty tree") + public fun testRemoveMinNodeAtEmptyTree() { + tree.assertRemoveMinNode( + treeView = null, + expected = null + ) + } + + @Test + @DisplayName("remove min node without children") + public fun testRemoveMinNodeWithoutChildren() { + tree.assertRemoveMinNode( + treeView = RBTreeNode(1, 1, isRed = false), + expected = null + ) + tree.assertRemoveMinNode( + treeView = RBTreeNode(1, 1, isRed = true), + expected = null + ) + } + + @Test + @DisplayName("remove min node with red child") + public fun testRemoveMinNodeWithRedChild() { + tree.assertRemoveMinNode( + treeView = RBTreeNode( + 1, 1, isRed = false, + leftChild = RBTreeNode(0, 0, isRed = true), + rightChild = null + ), + expected = RBTreeNode(1, 1, isRed = false) + ) + } + + @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( + treeView = 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( + treeView = 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/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/binary_trees/assistants/RBTreeTestAssistant.kt b/lib/src/test/kotlin/tree_tripper/binary_trees/assistants/RBTreeTestAssistant.kt index 94c309b..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,10 +89,19 @@ 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") + public fun getRoot(): Pair { + val root = this.root ?: throw NullPointerException("Tree is empty can't get root pair") return Pair(root.key, root.value) } + public fun assertRemoveMinNode(treeView: RBTreeNode?, expected: RBTreeNode?) { + val result = removeMinNode(treeView) + assertBinaryNodeDeepEquals(expected, result) {n1, n2 -> n1.isRed == n2.isRed} + } + + public fun assertMoveRightNode(treeView: RBTreeNode, expected: RBTreeNode) { + val result = moveRedRight(treeView) + assertBinaryNodeDeepEquals(expected, result) {n1, n2 -> n1.isRed == n2.isRed} + } + } \ No newline at end of file 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