Задача «Префиксное дерево (бор)»

Реализовать бор, он же префиксное дерево. Ключи — С-строки.

Интерфейс модуля:

typedef void (* Destructor)(Pointer data);
typedef void (* Enumerator)(const char * key, Pointer data, void * user);
typedef Pointer (* Updater)(Pointer old, Pointer next);


typedef struct _Trie { 
  /* ... */
} Trie;


/* Создать и инициализировать префиксное дерево
 * dtor - деструктор. Этой функции будут передаваться data удаляемых элементов.
 * Если dtor = NULL, деструктор отсутствует (не будет вызываться). */
Trie * trie_create(Destructor dtor);


/* Уничтожить префиксное дерево (dtor) */
void trie_free(Trie * tr);


/* Проверка существования ключа key в дереве. 1 - есть, 0 - нет. */
int trie_has(const Trie * tr, const char * key);


/* Получить значение по ключу. Если ключа нет, вернуть NULL. */
Pointer trie_get(const Trie * tr, const char * key);


/* Записать соответствие key -> data в префиксное дерево. 
 * Если key уже существовал, соответствующее поле data будет удалено (dtor) 
 * и перезаписано. */
int trie_set(Trie * tr, const char * key, Pointer data);


/* Обновить в префиксном дереве соответствие key -> data (без вызова dtor). 
 * 
 * При обновлении вызовется ф-я up, которой передается старое значение data и
 * параметр next. Функция возвращает новое значение data. 
 * 
 * Если элемента с ключом key нет, то достраивается новая ветка дерева, 
 * а в ф-ю up вместо старого значения data передается NULL. 
 */
int trie_update(Trie * tr, const char * key, Updater up, Pointer next);


/* Удалить элемент с ключом key (dtor) из префиксного дерева (если он есть). 
 * NB!! При этом может быть удалено несколько узлов дерева. */
int trie_delete(Trie * tr, const char * key);


/* Обход дерева в глубину с посещением всех элементов. Функция en будет 
 * вызвана для всех пар (key, data). 
 * 
 * user - дополнительный пользовательский параметр для ф-ии en. 
 */
void trie_traverse(const Trie * tr, Enumerator en, void * user);

Пример использования:

void print(const char * key, Pointer data, void * user) {
  if (user) {
    FILE * out = (FILE *) user;
    fprintf(out, "%s  %d\n", key, data);
  }
}

Pointer update(Pointer old, Pointer next) {
  return (Pointer)((int)old + (int)next);
}

void main(void)
{
  Trie * T = trie_create(NULL);

  trie_set(T, "Vasya", (Pointer)16);
  trie_set(T, "Kolya", (Pointer)10);
  trie_set(T, "Petya", (Pointer)18);

  trie_update(T, "Vasya", update, (Pointer)-3);
  trie_update(T, "", Update, (Pointer)100);

  trie_delete(T, "Kolya");

  trie_traverse(T, print, stdout);

  trie_free(T);
}

Работа всех функций должна быть протестирована.

Пример визуализации.