207 lines
5.7 KiB
C
207 lines
5.7 KiB
C
/* This software is licensed by the MIT License, see LICENSE file */
|
|
/* Copyright © 2022 Gregory Lirent */
|
|
|
|
#include "__internal/rbtree.h"
|
|
|
|
/*#####################################################################################################################*/
|
|
|
|
static rbnode_t* rbnode_duplicate(const rbnode_t* s, rbnode_t* p, const vtype* info) {
|
|
return rbnode_create(vnode_duplicate(&s->value, (info) ? *info : VTYPE_POINTER), p, s->colored);
|
|
}
|
|
|
|
static int rbnode_compare(const rbnode_t* s0, const rbnode_t* s1, vtype* tp) {
|
|
vtype t = !is_null(tp) ? *tp : VTYPE_POINTER;
|
|
return vnode_compare(s0->value, t, s1->value, t);
|
|
}
|
|
|
|
static hash_t rbnode_hash(const rbnode_t* s, vtype* tp) {
|
|
vtype t = (!is_null(tp)) ? *tp : VTYPE_POINTER;
|
|
return vnode_hash(s->value, t);
|
|
}
|
|
|
|
static void rbnode_free(rbnode_t* x, vtype *t) {
|
|
if (!is_null(t)) vnode_free(&x->value, *t);
|
|
}
|
|
|
|
/*#####################################################################################################################*/
|
|
|
|
hash_t libcdsb_rbtree_hash(const void* s, hash_t (*node_hash)(const void* s, void* info), void* info) {
|
|
|
|
rbnode_t *c0, *c1;
|
|
hash_t hash, v;
|
|
stack_t z;
|
|
|
|
if (rbnode_is_empty(s)) return 0;
|
|
if (is_null(node_hash)) node_hash = (void*)rbnode_hash;
|
|
|
|
z.prev = 0;
|
|
hash = 1;
|
|
c1 = (void*)s;
|
|
|
|
if (!rbnode_is_empty(z.value = c1->left)) do {
|
|
c0 = stack_pop(&z);
|
|
++hash;
|
|
if (!rbnode_is_empty(c0->left))
|
|
stack_push(&z, c1 = c0->left);
|
|
if (!rbnode_is_empty(c0->right))
|
|
stack_push(&z, c0->right);
|
|
} while (!is_null(z.value));
|
|
|
|
v = node_hash(c1, info);
|
|
c1 = (void*)s;
|
|
|
|
if (!rbnode_is_empty(z.value = c1->right)) do {
|
|
c0 = stack_pop(&z);
|
|
++hash;
|
|
if (!rbnode_is_empty(c0->left))
|
|
stack_push(&z, c1 = c0->left);
|
|
if (!rbnode_is_empty(c0->right))
|
|
stack_push(&z, c0->right);
|
|
} while (!is_null(z.value));
|
|
|
|
v += node_hash(c1, info);
|
|
|
|
return hash ^ v;
|
|
}
|
|
|
|
/*#####################################################################################################################*/
|
|
|
|
void* libcdsb_rbtree_duplicate(const rbnode_t* s, void* (*node_duplicate)(void* src, void* parent, void* info), void* info) {
|
|
rbnode_t *p0, *p1, *x;
|
|
stack_t z;
|
|
|
|
z.prev = 0;
|
|
z.value = (void*)s;
|
|
|
|
if (is_null(node_duplicate)) {
|
|
node_duplicate = (void*)rbnode_duplicate;
|
|
}
|
|
|
|
if (!rbnode_is_empty(s)) {
|
|
x = node_duplicate(z.value, rbnode_empty, info);
|
|
stack_push(&z, x);
|
|
|
|
do {
|
|
p0 = stack_pop(&z);
|
|
p1 = stack_pop(&z);
|
|
|
|
if (!rbnode_is_empty(p1->left)) {
|
|
p0->left = node_duplicate(p1->left, p0, info);
|
|
|
|
stack_push(&z, p1->left);
|
|
stack_push(&z, p0->left);
|
|
}
|
|
|
|
if (!rbnode_is_empty(p1->right)) {
|
|
p0->right = node_duplicate(p1->right, p0, info);
|
|
|
|
stack_push(&z, p1->right);
|
|
stack_push(&z, p0->right);
|
|
}
|
|
|
|
} while (!is_null(z.value));
|
|
|
|
} else x = rbnode_empty;
|
|
|
|
return x;
|
|
}
|
|
|
|
/*#####################################################################################################################*/
|
|
|
|
int libcdsb_rbtree_compare(const rbnode_t* s0, const rbnode_t* s1, int (*node_compare)(const rbnode_t* s0, const rbnode_t* s1, void* info), void* info) {
|
|
|
|
rbnode_t *c0, *c1;
|
|
stack_t z;
|
|
int c = 0;
|
|
|
|
if (s0 == s1) return 0;
|
|
if (is_null(node_compare)) node_compare = (void*)rbnode_compare;
|
|
|
|
z.prev = 0;
|
|
z.value = 0;
|
|
|
|
stack_push(&z, (void*)s1);
|
|
stack_push(&z, (void*)s0);
|
|
|
|
do {
|
|
c0 = stack_pop(&z);
|
|
c1 = stack_pop(&z);
|
|
|
|
if (rbnode_is_empty(c0) || rbnode_is_empty(c1)) {
|
|
if (c0 != c1) {
|
|
stack_flush(&z);
|
|
return rbnode_is_empty(c0) ? -1 : 1;
|
|
}
|
|
} else if ((c = node_compare(c0, c1, info))) {
|
|
if (c0->left == c1->right) { // <-- rbnode_empty
|
|
c = node_compare(c0->right, c1, info);
|
|
if (!c) c = node_compare(c0, c1->left, info);
|
|
} else if (c0->right == c1->left) { // <-- rbnode_empty
|
|
c = node_compare(c0, c1->right, info);
|
|
if (!c) c = node_compare(c0->left, c1, info);
|
|
}
|
|
|
|
if (c) {
|
|
stack_flush(&z);
|
|
return c;
|
|
}
|
|
} else {
|
|
stack_push(&z, c1->right);
|
|
stack_push(&z, c0->right);
|
|
stack_push(&z, c1->left);
|
|
stack_push(&z, c0->left);
|
|
}
|
|
|
|
} while (!is_null(z.value));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
size_t libcdsb_rbtree_size(const void* s) {
|
|
stack_t z = { .prev = 0, .value = (void*)s };
|
|
size_t n = 0;
|
|
rbnode_t* c;
|
|
|
|
if (!rbnode_is_empty(s)) {
|
|
while ((c = stack_pop(&z))) {
|
|
++n;
|
|
if (!rbnode_is_empty(c->left))
|
|
stack_push(&z, c->left);
|
|
if (!rbnode_is_empty(c->right))
|
|
stack_push(&z, c->right);
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
void libcdsb_rbtree_free(void* x, void (*node_free)(void* x, void* info), void* info) {
|
|
rbnode_t *t, *c;
|
|
|
|
c = x;
|
|
if (is_null(node_free)) node_free = (void*)rbnode_free;
|
|
|
|
while (!rbnode_is_empty(x)) {
|
|
if (!rbnode_is_empty(c->left)) {
|
|
c = c->left;
|
|
} else if (!rbnode_is_empty(c->right)) {
|
|
c = c->right;
|
|
} else if (!rbnode_is_root(c)) {
|
|
node_free(c, info);
|
|
|
|
t = c;
|
|
c = c->parent;
|
|
|
|
if (t == c->left) c->left = rbnode_empty;
|
|
else c->right = rbnode_empty;
|
|
|
|
free(t);
|
|
} else {
|
|
node_free(c, info);
|
|
x = rbnode_empty;
|
|
}
|
|
}
|
|
}
|