275 lines
8.3 KiB
C
275 lines
8.3 KiB
C
/* Include statements. */
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include "../trees/binary_tree/binary_tree.c"
|
|
|
|
/* Define constants. */
|
|
#define PRINT_INFO_TESTING_START "Starting tests.\n"
|
|
#define PRINT_INFO_TESTING_END "Done testing.\n"
|
|
#define ERROR_TEXT_RANDOM_LIST_FAIL_ALLOCATION "Long list test failed because of allocation errors!\n"
|
|
#define SOME_TEST_FAILED "Recheck the tests, one of them failed!\n"
|
|
#define TEST_INITIALIZE_TREE_NODE_SUCCESS "initialize_tree_node() passed tests!\n"
|
|
#define TEST_INITIALIZE_TREE_NODE_STORE_SUCCESS "initialized_tree_node_store() passed tests!\n"
|
|
#define TEST_TREE_NODE_LINK_SUCCESS "Tree node linking functions (left and right) passed tests!\n"
|
|
#define TEST_FREE_TREE_SUCCESS "free_tree() passed tests!\n"
|
|
#define ARRAY_START_POINT 0
|
|
#define ARRAY_LENGTH_TREE_NODES 30
|
|
|
|
/* Define functions. */
|
|
tree_node_t* generate_random_tree_node();
|
|
int comparison_function_int(const void *first, const void *second);
|
|
tree_node_t* insert_sorted_array_into_tree(int *array, int start, int end);
|
|
tree_node_t* build_tree(int **arr);
|
|
int check_tree_bst(tree_node_t *root, int (*comparison_function)());
|
|
int check_tree_build_data_free();
|
|
int test_trees();
|
|
|
|
/* Starting point. */
|
|
int main() {
|
|
printf(PRINT_INFO_TESTING_START);
|
|
if (test_trees()) {
|
|
printf(SOME_TEST_FAILED);
|
|
}
|
|
printf(PRINT_INFO_TESTING_END);
|
|
}
|
|
|
|
/* This function generates a random number and puts it in a newly allocated node. */
|
|
/* Caller has to free memory afterwards, returns NULL on failure. */
|
|
tree_node_t* generate_random_tree_node() {
|
|
/* The node to return. */
|
|
tree_node_t* result;
|
|
int *value;
|
|
|
|
/* Attempt to allocate the node. */
|
|
result = (tree_node_t*) malloc(sizeof(tree_node_t));
|
|
if (result == NULL)
|
|
return NULL;
|
|
value = (int*) malloc(sizeof(int));
|
|
if (value == NULL) {
|
|
free(result);
|
|
return NULL;
|
|
}
|
|
*value = random();
|
|
|
|
/* Store and return. */
|
|
result->right = NULL;
|
|
result->left = NULL;
|
|
result->data = value;
|
|
return result;
|
|
}
|
|
|
|
/* This function is used to compare two integers (given their pointers), exhibits undefined behaviour on NULL pointers. */
|
|
int comparison_function_int(const void *first, const void *second) {
|
|
return *((int*)first) == *((int*)second) ? TREE_COMPARISON_EQUAL_TO : ((*((int*)first) < *((int*)second)) ? TREE_COMPARISON_SMALLER_THAN : TREE_COMPARISON_LARGER_THAN);
|
|
}
|
|
|
|
/* This function will generate a balanced tree from the given sorted array, returns the root. */
|
|
tree_node_t* insert_sorted_array_into_tree(int *array, int start, int end) {
|
|
/* Stop if start is larger than end. */
|
|
if (start > end)
|
|
return NULL;
|
|
/* Find the middle point. */
|
|
int middle = start + (end - start) / 2;
|
|
tree_node_t *root = initialize_tree_node_store(array + middle);
|
|
if (root == NULL)
|
|
return NULL;
|
|
root->left = insert_sorted_array_into_tree(array, start, middle - 1);
|
|
root->right = insert_sorted_array_into_tree(array, middle + 1, end);
|
|
return root;
|
|
}
|
|
|
|
/* This function attempts to build a tree that is "sorted". */
|
|
/* Will return NULL on failure. */
|
|
tree_node_t* build_tree(int **arr) {
|
|
/* Local variables. */
|
|
int *array;
|
|
int array_length = ARRAY_LENGTH_TREE_NODES - ARRAY_START_POINT;
|
|
|
|
/* Allocate and populate array. */
|
|
array = (int*) malloc(array_length * sizeof(int));
|
|
if (array == NULL) {
|
|
*arr = NULL;
|
|
return NULL;
|
|
}
|
|
for (int i = ARRAY_START_POINT; i < ARRAY_LENGTH_TREE_NODES; i++)
|
|
array[i - ARRAY_START_POINT] = i;
|
|
/* Set the pointer back to the caller. */
|
|
*arr = array;
|
|
|
|
/* Return the resulting tree - END IS AN INDEX! */
|
|
return insert_sorted_array_into_tree(array, 0, array_length - 1);
|
|
}
|
|
|
|
/* This function checks that the given tree is a BST. */
|
|
int check_tree_bst(tree_node_t *root, int (*comparison_function)()) {
|
|
/* Base case. */
|
|
if (root == NULL || (root->right == NULL && root->left == NULL))
|
|
return 1;
|
|
/* Only need to check the right side. */
|
|
if (root->left == NULL)
|
|
return (((*comparison_function)(root, root->right) == TREE_COMPARISON_LARGER_THAN) && check_tree_bst(root->right, comparison_function));
|
|
/* Only need to check the left side. */
|
|
if (root->right == NULL)
|
|
return (((*comparison_function)(root, root->left) == TREE_COMPARISON_SMALLER_THAN) && check_tree_bst(root->left, comparison_function));
|
|
/* Check both sides. */
|
|
return (((*comparison_function)(root, root->right) == TREE_COMPARISON_LARGER_THAN) && ((*comparison_function)(root, root->left) == TREE_COMPARISON_SMALLER_THAN) && check_tree_bst(root->right, comparison_function) && check_tree_bst(root->left, comparison_function));
|
|
}
|
|
|
|
/* This function builds a tree then attempts to free it with the data thereof. */
|
|
int check_tree_build_data_free(){
|
|
/* Build a simple tree and try to free with data. */
|
|
int *root_data, *left_data, *right_data;
|
|
tree_node_t *root, *left, *right;
|
|
|
|
/* Allocate and fill the data. */
|
|
root_data = (int*) malloc(sizeof(int));
|
|
if (root_data == NULL)
|
|
return 1;
|
|
*root_data = 1;
|
|
left_data = (int*) malloc(sizeof(int));
|
|
if (left_data == NULL) {
|
|
free(root_data);
|
|
return 1;
|
|
}
|
|
*left_data = 0;
|
|
right_data = (int*) malloc(sizeof(int));
|
|
if (right_data == NULL) {
|
|
free(root_data);
|
|
free(left_data);
|
|
return 1;
|
|
}
|
|
*right_data = 2;
|
|
|
|
/* Allocate and fill the nodes. */
|
|
root = (tree_node_t*) malloc(sizeof(tree_node_t));
|
|
if (root == NULL) {
|
|
free(root_data);
|
|
free(left_data);
|
|
free(right_data);
|
|
return 1;
|
|
}
|
|
root->data = root_data;
|
|
left = (tree_node_t*) malloc(sizeof(tree_node_t));
|
|
if (left == NULL) {
|
|
free(root_data);
|
|
free(left_data);
|
|
free(right_data);
|
|
free(root);
|
|
return 1;
|
|
}
|
|
left->data = left_data;
|
|
right = (tree_node_t*) malloc(sizeof(tree_node_t));
|
|
if (right == NULL) {
|
|
free(root_data);
|
|
free(left_data);
|
|
free(right_data);
|
|
free(root);
|
|
free(left);
|
|
return 1;
|
|
}
|
|
right->data = right_data;
|
|
|
|
/* Link. */
|
|
root->right = right;
|
|
root->left = left;
|
|
right->right = NULL;
|
|
right->left = NULL;
|
|
left->right = NULL;
|
|
left->left = NULL;
|
|
|
|
/* Try to free with data. */
|
|
free_tree_data(root, NULL);
|
|
return 0;
|
|
}
|
|
|
|
/* This function does the testing. */
|
|
int test_trees() {
|
|
/* Local variables. */
|
|
tree_node_t *root, *right, *left;
|
|
int *first_value, *second_value, *array;
|
|
|
|
/* Allocate the root. */
|
|
root = initialize_tree_node();
|
|
if (root == NULL)
|
|
return 1;
|
|
/* Assert that the left and right pointer are NULL, and the data is NULL too. */
|
|
assert(root->right == NULL);
|
|
assert(root->left == NULL);
|
|
assert(root->data == NULL);
|
|
/* Print result. */
|
|
printf(TEST_INITIALIZE_TREE_NODE_SUCCESS);
|
|
|
|
/* Allocate two nodes with random values. */
|
|
first_value = (int*) malloc(sizeof(int));
|
|
second_value = (int*) malloc(sizeof(int));
|
|
if (first_value == NULL || second_value == NULL) {
|
|
free(first_value);
|
|
free(second_value);
|
|
free(root);
|
|
return 1;
|
|
}
|
|
/* Generate right and left nodes. */
|
|
right = initialize_tree_node_store(first_value);
|
|
left = initialize_tree_node_store(second_value);
|
|
if (right == NULL || left == NULL) {
|
|
free(first_value);
|
|
free(second_value);
|
|
free(right);
|
|
free(left);
|
|
free(root);
|
|
return 1;
|
|
}
|
|
assert(*((int*)right->data) == *first_value && *((int*)left->data) == *second_value);
|
|
/* Print result. */
|
|
printf(TEST_INITIALIZE_TREE_NODE_STORE_SUCCESS);
|
|
/* Link the left and right nodes. */
|
|
link_tree_node_right(root, right);
|
|
link_tree_node_left(root, left);
|
|
assert(root->right == right);
|
|
assert(root->left == left);
|
|
/* Print result. */
|
|
printf(TEST_TREE_NODE_LINK_SUCCESS);
|
|
/* Free tree, but not data. */
|
|
free_tree(root);
|
|
assert(first_value != NULL && second_value != NULL);
|
|
/* Print the result. */
|
|
printf(TEST_FREE_TREE_SUCCESS);
|
|
/* Free the data. */
|
|
free(first_value);
|
|
first_value = NULL;
|
|
/* Free the nodes. */
|
|
right = NULL;
|
|
left = NULL;
|
|
|
|
/* Build the bigger tree. */
|
|
root = build_tree(&array);
|
|
if (root == NULL) {
|
|
free(second_value);
|
|
return 1;
|
|
}
|
|
/* Verify that it is indeed a BST. */
|
|
//assert(check_tree_bst(root, comparison_function_int) == 1);
|
|
/* Attempt to insert an extra node, check where it ends up. */
|
|
*second_value = ARRAY_LENGTH_TREE_NODES;
|
|
right = initialize_tree_node_store(second_value);
|
|
if (right == NULL) {
|
|
free_tree_data(root, NULL);
|
|
free(second_value);
|
|
return 1;
|
|
}
|
|
/* Get the max node. */
|
|
left = root;
|
|
while (left->right != NULL)
|
|
left = left->right;
|
|
/* Attach the node. */
|
|
insert_node_into_tree(root, right, comparison_function_int);
|
|
assert(left->right == right);
|
|
/* Cleanup. */
|
|
free_tree(root);
|
|
free(array);
|
|
free(second_value);
|
|
|
|
/* Ran successfully so far, return last test. */
|
|
return check_tree_build_data_free();
|
|
}
|