refactor: decouple trie into static library
This commit is contained in:
parent
17dcc1db06
commit
fb4a9a3b2e
12 changed files with 1967 additions and 7 deletions
|
|
@ -1,205 +0,0 @@
|
|||
#ifndef AD3_FUZZYTEST
|
||||
#define AD3_FUZZYTEST
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "trie.h"
|
||||
|
||||
typedef struct fuzzyconfig {
|
||||
int seed;
|
||||
int word_length;
|
||||
int word_count;
|
||||
} FuzzyConfig;
|
||||
|
||||
void random_clean_string(char* s, int len) {
|
||||
char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,?";
|
||||
int charset_len = strlen(charset);
|
||||
|
||||
// len - 1 ensures that we can still set the null byte for the final byte
|
||||
int actual_len = rand() % (len - 1);
|
||||
int key;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < actual_len; i++) {
|
||||
key = rand() % charset_len;
|
||||
s[i] = charset[key];
|
||||
}
|
||||
|
||||
s[i] = '\0';
|
||||
}
|
||||
|
||||
void random_string(char* s, int len) {
|
||||
int val = rand();
|
||||
|
||||
// String can't be an empty string as they aren't supported
|
||||
s[0] = (char)(val % 255 + 1);
|
||||
|
||||
for (int i = 1; i < len - 1; i++) {
|
||||
val = rand();
|
||||
s[i] = (char)(val % 256);
|
||||
}
|
||||
|
||||
// Just in case no null characters were created
|
||||
s[len - 1] = '\0';
|
||||
}
|
||||
|
||||
void random_string_matrix(char** s, int count, int len) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
random_string(s[i], len);
|
||||
}
|
||||
}
|
||||
|
||||
char** init_string_matrix(int count, int len) {
|
||||
char** matrix = malloc(count * sizeof(char*));
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
matrix[i] = calloc(len, sizeof(char));
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a given trie implementation using randomly generated strings generated
|
||||
* using a given seed.
|
||||
*
|
||||
* @param seed seed to use for generating random strings
|
||||
* @param count how many strings to test with
|
||||
* @param len maximum length of each string
|
||||
* @param init_func function to creat a new trie of the wanted type
|
||||
* @param free_func function to free the given trie
|
||||
* @param add_func function to add a string to the given trie
|
||||
* @param remove_func function to remove a string from the given trie
|
||||
* @param size_func function to get the size of the given trie
|
||||
* @return exit code describing failures, if any
|
||||
*/
|
||||
int fuzzy_test_trie_seed(FuzzyConfig conf) {
|
||||
srand(conf.seed);
|
||||
|
||||
char** matrix = init_string_matrix(conf.word_count, conf.word_length);
|
||||
random_string_matrix(matrix, conf.word_count, conf.word_length);
|
||||
bool* contains = calloc(conf.word_count, sizeof(bool));
|
||||
|
||||
// It's possible that the string matrix contains duplicate strings
|
||||
bool** contains_dedupped = calloc(conf.word_count, sizeof(bool*));
|
||||
|
||||
for (int i = 0; i < conf.word_count; i++) {
|
||||
if (contains_dedupped[i] == NULL) {
|
||||
contains_dedupped[i] = contains + i;
|
||||
|
||||
for (int j = i + 1; j < conf.word_count; j++) {
|
||||
if (strcmp(matrix[i], matrix[j]) == 0) {
|
||||
contains_dedupped[j] = contains + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We keep track of the size as well so that we can check whether this is
|
||||
// also correct
|
||||
size_t size = 0;
|
||||
|
||||
Trie *ct;
|
||||
trie_init(&ct, NULL);
|
||||
|
||||
bool changed;
|
||||
TrieExitCode status;
|
||||
|
||||
// 0: success
|
||||
// 1: invalid add
|
||||
// 2: invalid remove
|
||||
// 3: bad size after adds
|
||||
// 4: bad size after removes
|
||||
int exit_code = 0;
|
||||
|
||||
// Add all strings to trie, checking for duplicates
|
||||
for (int i = 0; i < conf.word_count; i++) {
|
||||
status = trie_add(ct, matrix[i], NULL);
|
||||
|
||||
// if changed is false, *contains_dedupped[i] should be true, as changed
|
||||
// can only be false if the string is already contained in the trie. if
|
||||
// changed is true, *contains_dedupped[i] should be false, as the string
|
||||
// cannot be in the trie yet.
|
||||
if (status == Ok && *contains_dedupped[i]) {
|
||||
exit_code = 1;
|
||||
goto END;
|
||||
}
|
||||
|
||||
if (!*contains_dedupped[i]) {
|
||||
*contains_dedupped[i] = true;
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure size is correct
|
||||
if (trie_size(ct) != size) {
|
||||
printf("%i %i\n", trie_size(ct), size);
|
||||
exit_code = 3;
|
||||
goto END;
|
||||
}
|
||||
|
||||
// Remove all strings again, again taking duplicates into consideration
|
||||
/* for (int i = 0; i < conf.word_count; i++) { */
|
||||
/* changed = remove_func(ct, matrix[i]); */
|
||||
|
||||
/* // The string shouldn't be in the trie, yet another add operation */
|
||||
/* // says it added it as well */
|
||||
/* if (changed != *contains_dedupped[i]) { */
|
||||
/* exit_code = 2; */
|
||||
/* goto END; */
|
||||
/* } */
|
||||
|
||||
/* if (*contains_dedupped[i]) { */
|
||||
/* *contains_dedupped[i] = false; */
|
||||
/* size--; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
// Finally, check that the trie is completely empty
|
||||
/* if (size_func(ct) != 0) { */
|
||||
/* exit_code = 4; */
|
||||
/* } */
|
||||
|
||||
END:
|
||||
trie_free(ct);
|
||||
|
||||
// Even testing functions should properly free memory
|
||||
free(contains);
|
||||
free(contains_dedupped);
|
||||
|
||||
for (int i = 0; i < conf.word_count; i++) {
|
||||
free(matrix[i]);
|
||||
}
|
||||
|
||||
free(matrix);
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as fuzzy_test_trie_seed, except that the seed is randomly generated.
|
||||
*
|
||||
* @param count how many strings to test with
|
||||
* @param len maximum length of each string
|
||||
* @param init_func function to creat a new trie of the wanted type
|
||||
* @param free_func function to free the given trie
|
||||
* @param add_func function to add a string to the given trie
|
||||
* @param remove_func function to remove a string from the given trie
|
||||
* @param size_func function to get the size of the given trie
|
||||
* @return the generated seed if the test wasn't successful, -1 otherwise.
|
||||
*/
|
||||
/* int fuzzy_test_trie(int count, int len, void* (*init_func) (), void (*free_func) (void*), bool (*add_func) (void*, char*), bool (*remove_func) (void*, char*), int (*size_func) (void*)) { */
|
||||
/* int seed = rand(); */
|
||||
/* bool succeeded = fuzzy_test_trie_seed(seed, count, len, init_func, free_func, add_func, remove_func, size_func); */
|
||||
|
||||
/* if (!succeeded) { */
|
||||
/* return seed; */
|
||||
/* } */
|
||||
|
||||
/* return -1; */
|
||||
/* } */
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
#include "test.h"
|
||||
#include "trie.h"
|
||||
#include "fuzzy.h"
|
||||
|
||||
#define TEST_SIZE(ct, size) \
|
||||
TEST_CHECK(trie_size(ct) == size); \
|
||||
TEST_MSG("Size: %zu", trie_size(ct))
|
||||
|
||||
# define TRIE_INIT() \
|
||||
Trie *ct; \
|
||||
trie_init(&ct, NULL); \
|
||||
TEST_CHECK(ct != NULL)
|
||||
|
||||
void test_init() {
|
||||
TRIE_INIT();
|
||||
TEST_SIZE(ct, 0);
|
||||
trie_free(ct);
|
||||
}
|
||||
|
||||
void test_add_one() {
|
||||
TRIE_INIT();
|
||||
|
||||
Entry *entry = entry_new(Redirect, "");
|
||||
const char* string = "this is a test";
|
||||
|
||||
TEST_CHECK(trie_add(ct, string, entry) == Ok);
|
||||
Entry *entry2;
|
||||
TEST_CHECK(trie_search(ct, &entry2, string) == Ok);
|
||||
TEST_CHECK(entry == entry2);
|
||||
TEST_SIZE(ct, 1);
|
||||
trie_free(ct);
|
||||
}
|
||||
|
||||
void test_add_prefix() {
|
||||
TRIE_INIT();
|
||||
|
||||
const char *s1 = "halloween-2022";
|
||||
const char *s2 = "halloween-202";
|
||||
|
||||
Entry *entry1 = entry_new(Redirect, "");
|
||||
Entry *entry2 = entry_new(Redirect, "");
|
||||
|
||||
TEST_CHECK(trie_add(ct, s1, entry1) == Ok);
|
||||
TEST_CHECK(trie_add(ct, s2, entry2) == Ok);
|
||||
|
||||
Entry *entry3;
|
||||
|
||||
TEST_CHECK(trie_search(ct, &entry3, s1) == Ok);
|
||||
TEST_CHECK(entry3 == entry1);
|
||||
entry2 = NULL;
|
||||
|
||||
TEST_CHECK(trie_search(ct, &entry3, s2) == Ok);
|
||||
TEST_CHECK(entry3 == entry2);
|
||||
|
||||
trie_free(ct);
|
||||
}
|
||||
|
||||
void test_search_not_present() {
|
||||
TRIE_INIT();
|
||||
|
||||
TEST_CHECK(trie_add(ct, "this string exists", NULL) == Ok);
|
||||
Entry *entry;
|
||||
TEST_CHECK(trie_search(ct, &entry, "this string does not exist") == NotFound);
|
||||
|
||||
trie_free(ct);
|
||||
}
|
||||
|
||||
void test_add_more() {
|
||||
TRIE_INIT();
|
||||
|
||||
const char* one = "one";
|
||||
const char* two = "two";
|
||||
const char* twenty = "twenty";
|
||||
const char* twentytwo = "twentytwo";
|
||||
|
||||
Entry *entry = entry_new(Redirect, "");
|
||||
|
||||
TEST_CHECK(trie_add(ct, one, entry) == Ok);
|
||||
TEST_CHECK(trie_add(ct, two, entry) == Ok);
|
||||
TEST_CHECK(trie_add(ct, twenty, entry) == Ok);
|
||||
TEST_CHECK(trie_add(ct, twentytwo, entry) == Ok);
|
||||
|
||||
TEST_SIZE(ct, 4);
|
||||
|
||||
Entry *entry2;
|
||||
TEST_CHECK(trie_search(ct, &entry2, one) == Ok);
|
||||
TEST_CHECK(entry2 == entry);
|
||||
entry2 = NULL;
|
||||
|
||||
TEST_CHECK(trie_search(ct, &entry2, two) == Ok);
|
||||
TEST_CHECK(entry2 == entry);
|
||||
entry2 = NULL;
|
||||
|
||||
TEST_CHECK(trie_search(ct, &entry2, twenty) == Ok);
|
||||
TEST_CHECK(entry2 == entry);
|
||||
entry2 = NULL;
|
||||
|
||||
TEST_CHECK(trie_search(ct, &entry2, twentytwo) == Ok);
|
||||
TEST_CHECK(entry2 == entry);
|
||||
entry2 = NULL;
|
||||
|
||||
TEST_CHECK(trie_add(ct, one, NULL) == AlreadyPresent);
|
||||
TEST_CHECK(trie_add(ct, two, NULL) == AlreadyPresent);
|
||||
TEST_CHECK(trie_add(ct, twenty, NULL) == AlreadyPresent);
|
||||
TEST_CHECK(trie_add(ct, twentytwo, NULL) == AlreadyPresent);
|
||||
|
||||
trie_free(ct);
|
||||
}
|
||||
|
||||
/* void test_remove_one() { */
|
||||
/* Trie* ct = trie_init(); */
|
||||
/* TEST_CHECK(ct != NULL); */
|
||||
|
||||
/* const char* string = "this is a test"; */
|
||||
/* TEST_CHECK(trie_add(ct, string, NULL)); */
|
||||
/* TEST_SIZE(ct, 1); */
|
||||
|
||||
/* TEST_CHECK(trie_remove(ct, string)); */
|
||||
/* TEST_SIZE(ct, 0); */
|
||||
|
||||
/* trie_free(ct); */
|
||||
/* } */
|
||||
|
||||
/* void test_remove_more() { */
|
||||
/* Trie* ct = trie_init(); */
|
||||
/* TEST_CHECK(ct != NULL); */
|
||||
|
||||
/* const char* one = "one"; */
|
||||
/* const char* two = "two"; */
|
||||
/* const char* twenty = "twenty"; */
|
||||
/* const char* twentytwo = "twentytwo"; */
|
||||
/* TEST_CHECK(trie_add(ct, one, NULL)); */
|
||||
/* TEST_CHECK(trie_add(ct, two, NULL)); */
|
||||
/* TEST_CHECK(trie_add(ct, twenty, NULL)); */
|
||||
/* TEST_CHECK(trie_add(ct, twentytwo, NULL)); */
|
||||
|
||||
/* TEST_SIZE(ct, 4); */
|
||||
|
||||
/* TEST_CHECK(trie_remove(ct, one)); */
|
||||
/* TEST_CHECK(trie_remove(ct, two)); */
|
||||
/* TEST_CHECK(trie_remove(ct, twenty)); */
|
||||
/* TEST_CHECK(trie_remove(ct, twentytwo)); */
|
||||
|
||||
/* TEST_SIZE(ct, 0); */
|
||||
|
||||
/* trie_free(ct); */
|
||||
/* } */
|
||||
|
||||
/* void test_remove_not_present() { */
|
||||
/* Trie* ct = trie_init(); */
|
||||
/* TEST_CHECK(ct != NULL); */
|
||||
|
||||
/* TEST_CHECK(trie_add(ct, "this string exists", NULL)); */
|
||||
/* TEST_CHECK(!trie_remove(ct, "this string does not exist")); */
|
||||
|
||||
/* trie_free(ct); */
|
||||
/* } */
|
||||
|
||||
// Test seeds that are known to fail so we don't get regressions
|
||||
void test_fuzzy_set() {
|
||||
FuzzyConfig configs[] = {
|
||||
{ 403318210, 5, 500},
|
||||
{ 588218406, 16, 460},
|
||||
{ 297512224, 21, 500},
|
||||
{ 403318210, 5, 500}
|
||||
};
|
||||
|
||||
int count = sizeof(configs) / sizeof(FuzzyConfig);
|
||||
int res;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
res = fuzzy_test_trie_seed(configs[i]);
|
||||
TEST_CHECK_(res == 0,
|
||||
"Failed config, seed = %i, len = %i, count = %i, code=%i", configs[i].seed, configs[i].word_length, configs[i].word_count, res);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{"trie init",test_init },
|
||||
{ "trie add one",test_add_one },
|
||||
{ "trie add more",test_add_more },
|
||||
{ "trie search not present",test_search_not_present},
|
||||
|
||||
/* { "trie remove one",test_remove_one }, */
|
||||
/* { "trie remove more",test_remove_more }, */
|
||||
/* { "trie remove not present",test_remove_not_present}, */
|
||||
{ "trie fuzzy set", test_fuzzy_set },
|
||||
{ NULL, NULL}
|
||||
};
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
#include "test.h"
|
||||
#include "trie.h"
|
||||
#include "fuzzy.h"
|
||||
|
||||
void test_fuzzy() {
|
||||
// Randomize seed
|
||||
srand(time(NULL));
|
||||
|
||||
FuzzyConfig config;
|
||||
int counter = 0;
|
||||
int res;
|
||||
|
||||
for (int len = 1; len < 25; len += 5) {
|
||||
for (int count = 10; count <= 500; count += 10) {
|
||||
for (int i = 0; i < 50; i++) {
|
||||
counter++;
|
||||
|
||||
config.seed = rand();
|
||||
config.word_length = len;
|
||||
config.word_count = count;
|
||||
|
||||
res = fuzzy_test_trie_seed(config);
|
||||
TEST_CHECK_(res == 0,
|
||||
"Failed config, seed = %i, len = %i, count = %i, code = %i", config.seed, config.word_length, config.word_count, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
TEST_MSG("fuzzy tests done = %i", counter);
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{ "customtrie fuzzy", test_fuzzy },
|
||||
{ NULL, NULL}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue