public class IntBinarySearchTreeTest {

    static int passed = 0;
    static int failed = 0;

    public static void main(String[] args) {
        testEmptyContains();
        testSingleAdd();
        testStandardTree();
        testDuplicate();
        testLeftAndRightExtremes();
        testStrictlyAscending();
        testStrictlyDescending();

        System.out.println();
        System.out.println("Passed: " + passed + " / " + (passed + failed));
    }

    static void testEmptyContains() {
        IntBinarySearchTree t = new IntBinarySearchTree();
        checkBool("empty contains(5)", t.contains(5), false);
    }

    static void testSingleAdd() {
        IntBinarySearchTree t = new IntBinarySearchTree();
        t.add(42);
        checkBool("single contains(42)", t.contains(42), true);
        checkBool("single contains(7)", t.contains(7), false);
    }

    static void testStandardTree() {
        IntBinarySearchTree t = new IntBinarySearchTree();
        int[] vals = {5, 3, 8, 1, 4, 7, 9, 5};
        for (int v : vals) t.add(v);
        checkBool("contains(7)", t.contains(7), true);
        checkBool("contains(6)", t.contains(6), false);
        checkBool("contains(1)", t.contains(1), true);
        checkBool("contains(9)", t.contains(9), true);
        checkBool("contains(5)", t.contains(5), true);
        checkBool("contains(4)", t.contains(4), true);
        checkBool("contains(3)", t.contains(3), true);
        checkBool("contains(0)", t.contains(0), false);
        checkBool("contains(10)", t.contains(10), false);
    }

    static void testDuplicate() {
        IntBinarySearchTree t = new IntBinarySearchTree();
        t.add(10);
        t.add(10);
        t.add(10);
        checkBool("duplicates contains(10)", t.contains(10), true);
        // Tree should still work after duplicates
        t.add(5);
        t.add(15);
        checkBool("after dup contains(5)", t.contains(5), true);
        checkBool("after dup contains(15)", t.contains(15), true);
    }

    static void testLeftAndRightExtremes() {
        IntBinarySearchTree t = new IntBinarySearchTree();
        int[] vals = {50, 25, 75, 10, 35, 60, 90, 5, 15, 30, 40};
        for (int v : vals) t.add(v);
        for (int v : vals) checkBool("extremes contains(" + v + ")", t.contains(v), true);
        checkBool("extremes contains(-1)", t.contains(-1), false);
        checkBool("extremes contains(100)", t.contains(100), false);
        checkBool("extremes contains(45)", t.contains(45), false);
    }

    static void testStrictlyAscending() {
        // Degenerates into a right-only chain - good edge case
        IntBinarySearchTree t = new IntBinarySearchTree();
        for (int i = 1; i <= 10; i++) t.add(i);
        for (int i = 1; i <= 10; i++) checkBool("asc contains(" + i + ")", t.contains(i), true);
        checkBool("asc contains(0)", t.contains(0), false);
        checkBool("asc contains(11)", t.contains(11), false);
    }

    static void testStrictlyDescending() {
        // Degenerates into a left-only chain
        IntBinarySearchTree t = new IntBinarySearchTree();
        for (int i = 10; i >= 1; i--) t.add(i);
        for (int i = 1; i <= 10; i++) checkBool("desc contains(" + i + ")", t.contains(i), true);
        checkBool("desc contains(0)", t.contains(0), false);
        checkBool("desc contains(11)", t.contains(11), false);
    }

    static void checkBool(String label, boolean actual, boolean expected) {
        if (actual == expected) {
            System.out.println("PASS " + label + " = " + actual);
            passed++;
        } else {
            System.out.println("FAIL " + label + " expected=" + expected + " actual=" + actual);
            failed++;
        }
    }
}
