/* __ __ __ __ __ ___ * \ \ / / \ \ / / __/ * \ \/ / /\ \ \/ / / * \____/__/ \__\____/__/.ɪᴏ * ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ */ package io.vavr.collection; import org.junit.Test; import java.util.NoSuchElementException; import static org.assertj.core.api.Assertions.assertThat; public class RedBlackTreeTest { // Rudimentary tests // empty tree @Test public void shouldCreateEmptyTree() { final RedBlackTree<Integer> tree = RedBlackTree.empty(); assertThat(tree.isEmpty()).isTrue(); assertThat(tree.size()).isEqualTo(0); assertThat(tree.color()).isEqualTo(RedBlackTree.Color.BLACK); } @Test(expected = UnsupportedOperationException.class) public void shouldFailLeftOfEmpty() { RedBlackTree.empty().left(); } @Test(expected = UnsupportedOperationException.class) public void shouldFailRightOfEmpty() { RedBlackTree.empty().right(); } @Test(expected = NoSuchElementException.class) public void shouldFailValueOfEmpty() { RedBlackTree.empty().value(); } // isEmpty @Test public void shouldRecognizeEmptyTree() { assertThat(RedBlackTree.empty().isEmpty()).isTrue(); } @Test public void shouldRecognizeNonEmptyTree() { assertThat(RedBlackTree.of(1).isEmpty()).isFalse(); } // contains @Test public void shouldRecognizeContainedElement() { assertThat(RedBlackTree.of(1, 2, 3).contains(2)).isTrue(); } @Test public void shouldRecognizeNonContainedElementOfEmptyTree() { assertThat(RedBlackTree.<Integer> empty().contains(1)).isFalse(); } @Test public void shouldRecognizeNonContainedElementOfNonEmptyTree() { assertThat(RedBlackTree.of(1, 2, 3).contains(0)).isFalse(); } // insert @Test public void shouldInsert_2_1_4_5_9_3_6_7() { RedBlackTree<Integer> tree = RedBlackTree.empty(); assertThat(tree.toString()).isEqualTo("()"); assertThat(tree.size()).isEqualTo(0); tree = tree.insert(2); assertThat(tree.toString()).isEqualTo("(B:2)"); assertThat(tree.size()).isEqualTo(1); tree = tree.insert(1); assertThat(tree.toString()).isEqualTo("(B:2 R:1)"); assertThat(tree.size()).isEqualTo(2); tree = tree.insert(4); assertThat(tree.toString()).isEqualTo("(B:2 R:1 R:4)"); assertThat(tree.size()).isEqualTo(3); tree = tree.insert(5); assertThat(tree.toString()).isEqualTo("(B:4 (B:2 R:1) B:5)"); assertThat(tree.size()).isEqualTo(4); tree = tree.insert(9); assertThat(tree.toString()).isEqualTo("(B:4 (B:2 R:1) (B:5 R:9))"); assertThat(tree.size()).isEqualTo(5); tree = tree.insert(3); assertThat(tree.toString()).isEqualTo("(B:4 (B:2 R:1 R:3) (B:5 R:9))"); assertThat(tree.size()).isEqualTo(6); tree = tree.insert(6); assertThat(tree.toString()).isEqualTo("(B:4 (B:2 R:1 R:3) (R:6 B:5 B:9))"); assertThat(tree.size()).isEqualTo(7); tree = tree.insert(7); assertThat(tree.toString()).isEqualTo("(B:4 (B:2 R:1 R:3) (R:6 B:5 (B:9 R:7)))"); assertThat(tree.size()).isEqualTo(8); } @Test public void shouldInsertNullIntoEmptyTreeBecauseComparatorNotCalled() { final RedBlackTree<Integer> actual = RedBlackTree.<Integer> empty().insert(null); final RedBlackTree<Integer> expected = RedBlackTree.of((Integer) null); assertThat(actual).isEqualTo(expected); } @Test(expected = NullPointerException.class) public void shouldNotInsertNullTwoTimesIntoEmptyTreeBecauseComparatorCalled() { RedBlackTree.<Integer> empty().insert(null).insert(null); } @Test public void shouldInsertNonNullIntoEmptyTree() { final RedBlackTree<Integer> actual = RedBlackTree.<Integer> empty().insert(2); final RedBlackTree<Integer> expected = RedBlackTree.of(2); assertThat(actual).isEqualTo(expected); } @Test public void shouldReturnTheSameInstanceWhenInsertingAnAlreadyContainedELement() { final RedBlackTree<Integer> testee = RedBlackTree.of(1, 2, 3); final RedBlackTree<Integer> actual = testee.insert(2); assertThat(actual).isEqualTo(testee); } // delete @Test public void shouldDelete_2_from_2_1_4_5_9_3_6_7() { final RedBlackTree<Integer> testee = RedBlackTree.of(2, 1, 4, 5, 9, 3, 6, 7); final RedBlackTree<Integer> actual = testee.delete(2); assertThat(actual.toString()).isEqualTo("(B:4 (B:3 R:1) (R:6 B:5 (B:9 R:7)))"); assertThat(actual.size()).isEqualTo(7); } // difference() @Test public void shouldSubtractEmptyFromNonEmpty() { final RedBlackTree<Integer> t1 = RedBlackTree.of(3, 5); final RedBlackTree<Integer> t2 = RedBlackTree.empty(); final RedBlackTree<Integer> actual = t1.difference(t2); assertThat(actual).isEqualTo(t1); } @Test public void shouldSubtractNonEmptyFromEmpty() { final RedBlackTree<Integer> t1 = RedBlackTree.empty(); final RedBlackTree<Integer> t2 = RedBlackTree.of(5, 7); final RedBlackTree<Integer> actual = t1.difference(t2); assertThat(actual).isEqualTo(t1); } @Test public void shouldSubtractNonEmptyFromNonEmpty() { final RedBlackTree<Integer> t1 = RedBlackTree.of(3, 5); final RedBlackTree<Integer> t2 = RedBlackTree.of(5, 7); final RedBlackTree<Integer> actual = t1.difference(t2); final RedBlackTree<Integer> expected = RedBlackTree.of(3); assertThat(actual).isEqualTo(expected); } // intersection() @Test public void shouldIntersectOnNonEmptyGivenEmpty() { final RedBlackTree<Integer> t1 = RedBlackTree.of(3, 5); final RedBlackTree<Integer> t2 = RedBlackTree.empty(); final RedBlackTree<Integer> actual = t1.intersection(t2); final RedBlackTree<Integer> expected = RedBlackTree.empty(); assertThat(actual).isEqualTo(expected); } @Test public void shouldIntersectOnEmptyGivenNonEmpty() { final RedBlackTree<Integer> t1 = RedBlackTree.empty(); final RedBlackTree<Integer> t2 = RedBlackTree.of(5, 7); final RedBlackTree<Integer> actual = t1.intersection(t2); final RedBlackTree<Integer> expected = RedBlackTree.empty(); assertThat(actual).isEqualTo(expected); } @Test public void shouldIntersectOnNonEmptyGivenNonEmpty() { final RedBlackTree<Integer> t1 = RedBlackTree.of(3, 5); final RedBlackTree<Integer> t2 = RedBlackTree.of(5, 7); final RedBlackTree<Integer> actual = t1.intersection(t2); final RedBlackTree<Integer> expected = RedBlackTree.of(5); assertThat(actual).isEqualTo(expected); } @Test public void shouldIntersectOnNonEmptyGivenNonEmptyUnbalancedHeightLeft() { // Node::mergeGT // // Trees have // - different values // - similar to each other left children // - and unlike each other right children final RedBlackTree<Integer> t1 = RedBlackTree.of(1, 2, 3, 4, 5, 6, 7, 8, 60, 66, 67); final RedBlackTree<Integer> t2 = RedBlackTree.of(1, 2, 3, 10, 11, 12, 13, 14, 60, 76, 77); final RedBlackTree<Integer> actual = t1.intersection(t2); final RedBlackTree<Integer> expected = RedBlackTree.of(1, 2, 3, 60); assertThat(actual).isEqualTo(expected); } @Test public void shouldIntersectOnNonEmptyGivenNonEmptyUnbalancedHeightRight() { // Node::mergeLT // // Trees have // - different values // - unlike each other left children // - and similar to each other right children final RedBlackTree<Integer> t1 = RedBlackTree.of(1, 2, 3, 4, 40, 61, 62, 63, 64, 65); final RedBlackTree<Integer> t2 = RedBlackTree.of(2, 7, 8, 9, 50, 61, 62, 63, 64, 65); final RedBlackTree<Integer> actual = t1.intersection(t2); final RedBlackTree<Integer> expected = RedBlackTree.of(2, 61, 62, 63, 64, 65); assertThat(actual).isEqualTo(expected); } @Test public void shouldIntersectOnNonEmptyGivenNonEmptyBalancedHeightRight() { // Node::mergeEQ && isRed(n1.right) // final RedBlackTree<Integer> t1 = RedBlackTree.of(-10, -20, -30, -40, -50, 1, 10, 20, 30); final RedBlackTree<Integer> t2 = RedBlackTree.of(-10, -20, -30, -40, -50, 2, 10, 20, 30); assertThat(t1.intersection(t2)).isEqualTo(t1.delete(1)); } // union() @Test public void shouldUnionOnNonEmptyGivenEmpty() { final RedBlackTree<Integer> t1 = RedBlackTree.of(3, 5); final RedBlackTree<Integer> t2 = RedBlackTree.empty(); final RedBlackTree<Integer> actual = t1.union(t2); final RedBlackTree<Integer> expected = RedBlackTree.of(3, 5); assertThat(actual).isEqualTo(expected); } @Test public void shouldUnionOnEmptyGivenNonEmpty() { final RedBlackTree<Integer> t1 = RedBlackTree.empty(); final RedBlackTree<Integer> t2 = RedBlackTree.of(5, 7); final RedBlackTree<Integer> actual = t1.union(t2); final RedBlackTree<Integer> expected = RedBlackTree.of(5, 7); assertThat(actual).isEqualTo(expected); } @Test public void shouldUnionOnNonEmptyGivenNonEmpty() { final RedBlackTree<Integer> t1 = RedBlackTree.of(3, 5); final RedBlackTree<Integer> t2 = RedBlackTree.of(5, 7); final RedBlackTree<Integer> actual = t1.union(t2); final RedBlackTree<Integer> expected = RedBlackTree.of(3, 5, 7); assertThat(actual).isEqualTo(expected); } @Test public void shouldComputeUnionAndEqualTreesOfDifferentShapeButSameElements() { final RedBlackTree<Integer> t1 = RedBlackTree.of(-1, -1, 0, 1); final RedBlackTree<Integer> t2 = RedBlackTree.of(-2, -1, 0, 1); final RedBlackTree<Integer> actual = t1.union(t2); final RedBlackTree<Integer> expected = RedBlackTree.of(-2, -1, 0, 1); assertThat(actual).isEqualTo(expected); } // iterator() @Test public void shouldIterateEmptyTree() { assertThat(RedBlackTree.empty().iterator().hasNext()).isFalse(); } @Test public void shouldIterateNonEmptyTree() { final RedBlackTree<Integer> testee = RedBlackTree.of(7, 1, 6, 2, 5, 3, 4); final List<Integer> actual = testee.iterator().toList(); assertThat(actual.toString()).isEqualTo("List(1, 2, 3, 4, 5, 6, 7)"); } }