diff --git a/GNUmakefile b/GNUmakefile index 60913b30..4027454b 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -144,7 +144,9 @@ TESTS = \ ifneq ($(DEBUG),) TESTS += \ - tests/func/test_map \ + tests/func/test_btree_set \ + tests/func/test_btree_map \ + tests/func/test_map \ tests/func/test_unordered_map endif @@ -186,6 +188,7 @@ perf: $(PERFS_C) $(PERFS_CC) tests/perf/arr/perf_arr_generate $(wildcard tests/perf/lst/perf*.cc?) : $(COMMON_H) ctl/list.h $(wildcard tests/perf/set/perf*.cc?) : $(COMMON_H) ctl/set.h +$(wildcard tests/perf/btset/perf*.cc?) : $(COMMON_H) ctl/btree_set.h $(wildcard tests/perf/deq/perf*.cc?) : $(COMMON_H) ctl/deque.h $(wildcard tests/perf/pqu/perf*.cc?) : $(COMMON_H) ctl/priority_queue.h $(wildcard tests/perf/vec/perf*.cc?) : $(COMMON_H) ctl/vector.h @@ -292,6 +295,8 @@ ctl/string.i: $(call expand,$(subst .i,,$@)) ctl/map.i: $(call expand,$(subst .i,,$@),-DT=strint -DPOD) +ctl/btree_map.i: + $(call expand,$(subst .i,,$@),-DT=strint -DPOD) ctl/unordered_map.i: $(call expand,$(subst .i,,$@),-DT=strint -DPOD) ctl/array.i: @@ -328,7 +333,11 @@ tests/func/test_queue: .cflags $(COMMON_H) tests/test.h tests/func/digi.hh ct tests/func/test_set: .cflags $(COMMON_H) tests/test.h tests/func/digi.hh ctl/set.h \ tests/func/test_set.cc $(CXX) $(CXXFLAGS) -o $@ $@.cc -tests/func/test_map: .cflags $(H) tests/test.h tests/func/strint.hh ctl/set.h ctl/map.h tests/func/test_map.cc +tests/func/test_btree_set: .cflags $(COMMON_H) tests/test.h tests/func/digi.hh ctl/btree_set.h \ + tests/func/test_btree_set.cc + $(CXX) $(CXXFLAGS) -o $@ $@.cc +tests/func/test_map: .cflags $(H) tests/test.h tests/func/strint.hh \ + tests/func/test_map.cc $(CXX) $(CXXFLAGS) -o $@ $@.cc tests/func/test_unordered_set: .cflags $(COMMON_H) ctl/unordered_set.h \ tests/func/test_unordered_set.cc diff --git a/README.md b/README.md index aaf23c59..311ec5b8 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ all containers in ISO C99/C11: | [ctl/map.h](docs/map.md) | std::map | map | | [ctl/unordered_map.h](docs/unordered_map.md) | std::unordered_map | umap | | [ctl/unordered_set.h](docs/unordered_set.md) | std::unordered_set | uset | +| [ctl/btree_set.h](docs/btree_set.md) | absl::btree_set | btset | +| [ctl/btree_map.h](docs/btree_map.md) | absl::btree_map | btmap | |------------------------------------------------|----------------------| | [ctl/algorithm.h](docs/algorithm.md) | `` | | [ctl/numeric.h](docs/numeric.md) | `` | @@ -359,7 +361,6 @@ And in its grandiosity (esp. not header-only): ## Base Implementation Details - array.h: stack/heap allocated vector.h: realloc string.h: vector.h @@ -371,6 +372,8 @@ And in its grandiosity (esp. not header-only): forward_list.h: single linked list set.h: red black tree map.h: set.h + btree_set.h: b-tree + btree_map.h: - " - unordered_set.h: hashed forward linked lists unordered_map.h: unordered_set.h (pair in work) hashmap.h: stanford hash for integer keys, intel only. diff --git a/ctl/btree_set.h b/ctl/btree_set.h new file mode 100644 index 00000000..88193255 --- /dev/null +++ b/ctl/btree_set.h @@ -0,0 +1,794 @@ +/* B-Tree with node size 256. + SPDX-License-Identifier: MIT */ + +#ifndef T +# error "Template type T undefined for " +#endif + +#include + +// TODO emplace, lower_bound, upper_bound, equal_range + +#define CTL_BTSET +#define A JOIN(set, T) +#define B JOIN(A, node) +#define I JOIN(A, it) +// usually called M. max 31 (i.e. 4 bits) +#define NS ((256 / MAX(sizeof(T),sizeof(void*))) - 1) + +//struct B {}; +typedef struct B +{ +#if 1 + struct B* l; + struct B* r; + struct B* p; + T key; + int color; // Red = 0, Black = 1 +#endif + struct B* parent; + unsigned position:4; + //unsigned start:4; // always 0 for now + unsigned finish:4; + unsigned maxcount:4; // => is_leaf + // either internal or leaves, no mix. + union { + T values[NS - 1]; + struct B* children[NS]; + // all keys in children[i] are less than key[i], + // all keys in children[i+1] are greater than key[i], + // 0 keys for leaf nodes, 7 for internal nodes. + } u; +} B; + +typedef struct A +{ + B* root; + size_t size; + void (*free)(T*); + T (*copy)(T*); + int (*compare)(T*, T*); + int (*equal)(T*, T*); +} A; + +typedef struct I +{ + CTL_B_ITER_FIELDS; +} I; + +static inline B* +JOIN(A, begin)(A* self) +{ + return self->root; +} + +static inline B* +JOIN(A, end)(A* self) +{ + (void) self; + return NULL; +} + +static inline B* +JOIN(B, next)(B* node) +{ + for (int i = 0; i < node->maxcount; i++) + JOIN(B, next)(node->u.children[i]); + return node; +} + +static inline void +JOIN(I, step)(I* iter) +{ + if(iter->next == iter->end) + iter->done = 1; + else + { + iter->node = iter->next; + if (iter->node) + { + iter->ref = &iter->node->key; + iter->next = JOIN(B, next)(iter->node); + } + else + { + iter->done = 1; + iter->ref = NULL; + iter->next = NULL; + } + } +} + +static inline I +JOIN(I, range)(A* container, B* begin, B* end) +{ + static I zero; + I iter = zero; + iter.container = container; + if(begin) + { + iter.step = JOIN(I, step); + iter.node = begin; + iter.ref = JOIN(B, ref)(iter.node); + iter.next = JOIN(B, next)(iter.node); + iter.end = end; + } + else + iter.done = 1; + return iter; +} + +// FIXME: disable algos until we have iters +#define __CTL_ALGORITHM_H__ +#include + +static inline A +JOIN(A, init)(int _compare(T*, T*)) +{ + static A zero; + A self = zero; + self.compare = _compare; +#ifdef POD + self.copy = JOIN(A, implicit_copy); + _JOIN(A, _set_default_methods)(&self); +#else + self.free = JOIN(T, free); + self.copy = JOIN(T, copy); +#endif + return self; +} + +static inline A +JOIN(A, init_from)(A* from) +{ + static A zero; + A self = zero; + self.equal = from->equal; + self.compare = from->compare; + self.copy = from->copy; + self.free = from->free; + return self; +} + +static inline void +JOIN(A, free_node)(A* self, B* node) +{ +#ifndef POD + if(self->free) + self->free(&node->key); +#else + (void) self; +#endif + free(node); +} + +static inline B* +JOIN(B, init)() +{ + B* node = (B*) calloc(1, sizeof(B)); + return node; +} + +static inline B* +JOIN(A, find)(A* self, T key) +{ + B* node = self->root; + while(node) + { + int diff = self->compare(&key, &node->key); + if(diff == 0) + return node; + else + if(diff < 0) + node = node->l; + else + node = node->r; + } + return NULL; +} + +static inline int +JOIN(A, count)(A* self, T key) +{ + int result = JOIN(A, find)(self, key) ? 1 : 0; +#ifndef POD + if(self->free) + self->free(&key); +#endif + return result; +} + +static inline int +JOIN(A, contains)(A* self, T key) +{ + return JOIN(A, count)(self, key) == 1; +} + +static inline void +JOIN(B, replace)(A* self, B* a, B* b) +{ + if(a->p) + { + if(a == a->p->l) + a->p->l = b; + else + a->p->r = b; + } + else + self->root = b; + if(b) + b->p = a->p; +} + +#ifdef USE_INTERNAL_VERIFY + +#include + +static inline void +JOIN(B, verify_property_1)(B* node) +{ + assert(JOIN(B, is_red)(node) || JOIN(B, is_black)(node)); + if(node) + { + JOIN(B, verify_property_1)(node->l); + JOIN(B, verify_property_1)(node->r); + } +} + +static inline void +JOIN(B, verify_property_2)(B* node) +{ + assert(JOIN(B, is_black)(node)); +} + +static inline void +JOIN(B, verify_property_4)(B* node) +{ + if(JOIN(B, is_red)(node)) + { + assert(JOIN(B, is_black)(node->l)); + assert(JOIN(B, is_black)(node->r)); + assert(JOIN(B, is_black)(node->p)); + } + if(node) + { + JOIN(B, verify_property_4)(node->l); + JOIN(B, verify_property_4)(node->r); + } +} + +static inline void +JOIN(B, count_black)(B* node, int nodes, int* in_path) +{ + if(JOIN(B, is_black)(node)) + nodes++; + if(node) + { + JOIN(B, count_black)(node->l, nodes, in_path); + JOIN(B, count_black)(node->r, nodes, in_path); + } + else + { + if(*in_path == -1) + *in_path = nodes; + else + assert(nodes == *in_path); + } +} + +static inline void +JOIN(B, verify_property_5)(B* node) +{ + int in_path = -1; + JOIN(B, count_black)(node, 0, &in_path); +} + +static inline void +JOIN(A, verify)(A* node) +{ + JOIN(B, verify_property_1)(node->root); // Property 1: Each node is either red or black. + JOIN(B, verify_property_2)(node->root); // Property 2: The root node is black. + /* Implicit */ // Property 3: Leaves are colored black + JOIN(B, verify_property_4)(node->root); // Property 4: Every red node has two black nodes. + JOIN(B, verify_property_5)(node->root); // Property 5: All paths from a node have the same number of black nodes. +} + +#endif + +static inline void +JOIN(A, rotate_l)(A* self, B* node) +{ + B* r = node->r; + JOIN(B, replace)(self, node, r); + node->r = r->l; + if(r->l) + r->l->p = node; + r->l = node; + node->p = r; +} + +static inline void +JOIN(A, rotate_r)(A* self, B* node) +{ + B* l = node->l; + JOIN(B, replace)(self, node, l); + node->l = l->r; + if(l->r) + l->r->p = node; + l->r = node; + node->p = l; +} + +static inline B* +JOIN(A, insert)(A* self, T key) +{ + B* insert = JOIN(B, init)(key, 0); + if(self->root) + { + B* node = self->root; + while(1) + { + int diff = self->compare(&key, &node->key); + if(diff == 0) + { + JOIN(A, free_node)(self, insert); + return node; + } + else + if(diff < 0) + { + if(node->l) + node = node->l; + else + { + node->l = insert; + break; + } + } + else + { + if(node->r) + node = node->r; + else + { + node->r = insert; + break; + } + } + } + insert->p = node; + } + else + self->root = insert; + JOIN(A, insert_1)(self, insert); + self->size++; +#ifdef USE_INTERNAL_VERIFY + JOIN(A, verify)(self); +#endif + return insert; +} + +static inline void +JOIN(A, insert_1)(A* self, B* node) +{ + if(node->p) + JOIN(A, insert_2)(self, node); + else + node->color = 1; +} + +static inline void +JOIN(A, insert_2)(A* self, B* node) +{ + if(JOIN(B, is_black)(node->p)) + return; + else + JOIN(A, insert_3)(self, node); +} + +static inline void +JOIN(A, insert_3)(A* self, B* node) +{ + if(JOIN(B, is_red)(JOIN(B, uncle)(node))) + { + node->p->color = 1; + JOIN(B, uncle)(node)->color = 1; + JOIN(B, grandfather)(node)->color = 0; + JOIN(A, insert_1)(self, JOIN(B, grandfather)(node)); + } + else + JOIN(A, insert_4)(self, node); +} + +static inline void +JOIN(A, insert_4)(A* self, B* node) +{ + if(node == node->p->r && node->p == JOIN(B, grandfather)(node)->l) + { + JOIN(A, rotate_l)(self, node->p); + node = node->l; + } + else + if(node == node->p->l && node->p == JOIN(B, grandfather)(node)->r) + { + JOIN(A, rotate_r)(self, node->p); + node = node->r; + } + JOIN(A, insert_5)(self, node); +} + +static inline void +JOIN(A, insert_5)(A* self, B* node) +{ + node->p->color = 1; + JOIN(B, grandfather)(node)->color = 0; + if(node == node->p->l && node->p == JOIN(B, grandfather)(node)->l) + JOIN(A, rotate_r)(self, JOIN(B, grandfather)(node)); + else + JOIN(A, rotate_l)(self, JOIN(B, grandfather)(node)); +} + +static inline void +JOIN(A, erase_1)(A*, B*), +JOIN(A, erase_2)(A*, B*), +JOIN(A, erase_3)(A*, B*), +JOIN(A, erase_4)(A*, B*), +JOIN(A, erase_5)(A*, B*), +JOIN(A, erase_6)(A*, B*); + +static inline void +JOIN(A, erase_node)(A* self, B* node) +{ + if(node->l && node->r) + { + B* pred = JOIN(B, max)(node->l); + SWAP(T, &node->key, &pred->key); + node = pred; + } + B* child = node->r ? node->r : node->l; + if(JOIN(B, is_black)(node)) + { + node->color = JOIN(B, color)(child); + JOIN(A, erase_1)(self, node); + } + JOIN(B, replace)(self, node, child); + if(node->p == NULL && child) + child->color = 1; + JOIN(A, free_node)(self, node); + self->size--; +#ifdef USE_INTERNAL_VERIFY + JOIN(A, verify)(self); +#endif +} + +static inline I +JOIN(I, iter)(A* self, B *node) +{ + I it = JOIN(I, each)(self); + it.node = node; + it.ref = &node->key; + it.next = JOIN(B, next)(it.node); + return it; +} + +static inline void +JOIN(A, erase_it)(A* self, I* pos) +{ + B* node = pos->node; + if(node) + JOIN(A, erase_node)(self, node); +} + +#ifdef DEBUG +static inline void +JOIN(A, erase_range)(A* self, I* from, I* to) +{ + B* node = from->node; + if(node) + { + // TODO: check if clear would be faster (from==begin && to==end) + JOIN(A, it) it = JOIN(I, range)(self, from->node, to->node); + for(; !it.done; it.step(&it)) + JOIN(A, erase_node)(self, it.node); + } +} +#endif + +static inline void +JOIN(A, erase)(A* self, T key) +{ + B* node = JOIN(A, find)(self, key); + if(node) + JOIN(A, erase_node)(self, node); +} + +static inline void +JOIN(A, erase_1)(A* self, B* node) +{ + if(node->p) + JOIN(A, erase_2)(self, node); +} + +static inline void +JOIN(A, erase_2)(A* self, B* node) +{ + if(JOIN(B, is_red)(JOIN(B, sibling)(node))) + { + node->p->color = 0; + JOIN(B, sibling)(node)->color = 1; + if(node == node->p->l) + JOIN(A, rotate_l)(self, node->p); + else + JOIN(A, rotate_r)(self, node->p); + } + JOIN(A, erase_3)(self, node); +} + +static inline void +JOIN(A, erase_3)(A* self, B* node) +{ + if(JOIN(B, is_black)(node->p) + && JOIN(B, is_black)(JOIN(B, sibling)(node)) + && JOIN(B, is_black)(JOIN(B, sibling)(node)->l) + && JOIN(B, is_black)(JOIN(B, sibling)(node)->r)) + { + JOIN(B, sibling)(node)->color = 0; + JOIN(A, erase_1)(self, node->p); + } + else + JOIN(A, erase_4)(self, node); +} + +static inline void +JOIN(A, erase_4)(A* self, B* node) +{ + if(JOIN(B, is_red)(node->p) + && JOIN(B, is_black)(JOIN(B, sibling)(node)) + && JOIN(B, is_black)(JOIN(B, sibling)(node)->l) + && JOIN(B, is_black)(JOIN(B, sibling)(node)->r)) + { + JOIN(B, sibling)(node)->color = 0; + node->p->color = 1; + } + else + JOIN(A, erase_5)(self, node); +} + +static inline void +JOIN(A, erase_5)(A* self, B* node) +{ + if(node == node->p->l + && JOIN(B, is_black)(JOIN(B, sibling)(node)) + && JOIN(B, is_red)(JOIN(B, sibling)(node)->l) + && JOIN(B, is_black)(JOIN(B, sibling)(node)->r)) + { + JOIN(B, sibling)(node)->color = 0; + JOIN(B, sibling)(node)->l->color = 1; + JOIN(A, rotate_r)(self, JOIN(B, sibling)(node)); + } + else + if(node == node->p->r + && JOIN(B, is_black)(JOIN(B, sibling)(node)) + && JOIN(B, is_red)(JOIN(B, sibling)(node)->r) + && JOIN(B, is_black)(JOIN(B, sibling)(node)->l)) + { + JOIN(B, sibling)(node)->color = 0; + JOIN(B, sibling)(node)->r->color = 1; + JOIN(A, rotate_l)(self, JOIN(B, sibling)(node)); + } + JOIN(A, erase_6)(self, node); +} + +static inline void +JOIN(A, erase_6)(A* self, B* node) +{ + JOIN(B, sibling)(node)->color = JOIN(B, color)(node->p); + node->p->color = 1; + if(node == node->p->l) + { + JOIN(B, sibling)(node)->r->color = 1; + JOIN(A, rotate_l)(self, node->p); + } + else + { + JOIN(B, sibling)(node)->l->color = 1; + JOIN(A, rotate_r)(self, node->p); + } +} + +// erase without rebalancing. e.g. for clear +static inline void +JOIN(B, erase_fast)(A* self, B* node) +{ + while(node) + { + JOIN(B, erase_fast)(self, node->r); + B* left = node->l; + JOIN(A, free_node)(self, node); + node = left; + self->size--; + } +} + +static inline void +JOIN(A, clear)(A* self) +{ + while(!JOIN(A, empty)(self)) + JOIN(B, erase_fast)(self, self->root); +} + +static inline void +JOIN(A, free)(A* self) +{ + JOIN(A, clear)(self); + *self = JOIN(A, init)(self->compare); +} + +static inline A +JOIN(A, copy)(A* self) +{ + I it = JOIN(I, each)(self); + A copy = JOIN(A, init)(self->compare); + while(!it.done) + { + JOIN(A, insert)(©, self->copy(&it.node->key)); + it.step(&it); + } + return copy; +} + +static inline void +JOIN(A, swap)(A* self, A* other) +{ + A temp = *self; + *self = *other; + *other = temp; +} + +static inline size_t +JOIN(A, remove_if)(A* self, int (*_match)(T*)) +{ + size_t erases = 0; + foreach(A, self, it) + if(_match(&it.node->key)) + { + JOIN(A, erase_node)(self, it.node); + erases++; + } + return erases; +} + +static inline size_t +JOIN(A, erase_if)(A* self, int (*_match)(T*)) +{ + return JOIN(A, remove_if)(self, _match); +} + +// join and split for fast bulk insert, bulk erase and the algos below. +// also needed for pctl. +static inline size_t +JOIN(B, rank)(B* node) +{ + size_t count = 0; + while (node) { + if (!node->r) { + count++; + } + node = node->l; + } + return count; +} + +/* +static inline A* +JOIN(A, join_right)(A* left, T key, B* right) +{ + // same black height + if(JOIN(B, rank)(left->root) == (JOIN(B, rank)(right->root)/2) * 2) + { + B* old = left->root; + int color = JOIN(B, is_black)(left->root) & JOIN(B, is_black)(right->root) ? 0 : 1; + left->root = JOIN(B, init)(key, color); + left->root->l = old; + left->root->r = right->root; + left->size += right->size + 1; + return left; + } + else + { + B* l1 = left->root->l; + B* r1 = left->root->r; + T k1 = left->root->key; + int c1 = left->root->color; + A new_t = JOIN(A, init_from)(left); + new_t->root = r1; + new_t->root->r = join_right(&new_r, key, right); + new_t->root->l = l1; + new_t->key = k1; + new_t->color = c1; + if(c == black) and (c(R(T′)) == c(R(R(T′))) == red) + { + c(R(R(T′))) = black; + JOIN(A, rotate_l)(new_t); + } + return new_t; + } +} + +static inline A* +JOIN(A, join_left)(A* left, T key, B* right) +{ + if r(TL)/2 > r(TR)/2 + T′ = join_right(TL, k, TR); + if(c(T′) == red) and (c(R(T′))=red) then + Node(L(T′),〈k(T′),black〉, R(T′)) + else T′ + else if r(TR)/2c>br(TL)/2 then + T′ = join_eft(TL, k, TR); + if (c(T′)==red) and (c(L(T′)) == red) + Node(L(T′),〈k(T′),black〉, R(T′)); + else + T′ + else if (c(TL) == black and c(TR) == black) + Node(TL,〈k,red〉, TR) + else + Node(TL,〈k,black〉, TR) +} +*/ + +static inline A +JOIN(A, intersection)(A* a, A* b) +{ + A self = JOIN(A, init)(a->compare); + foreach(A, a, i) + if(JOIN(A, find)(b, *i.ref)) + JOIN(A, insert)(&self, self.copy(i.ref)); + return self; +} + +static inline A +JOIN(A, union)(A* a, A* b) +{ + A self = JOIN(A, init)(a->compare); + foreach(A, a, i) JOIN(A, insert)(&self, self.copy(i.ref)); + foreach(A, b, i) JOIN(A, insert)(&self, self.copy(i.ref)); + return self; +} + +static inline A +JOIN(A, difference)(A* a, A* b) +{ + A self = JOIN(A, copy)(a); + foreach(A, b, i) JOIN(A, erase)(&self, *i.ref); + return self; +} + +static inline A +JOIN(A, symmetric_difference)(A* a, A* b) +{ + A self = JOIN(A, union)(a, b); + A intersection = JOIN(A, intersection)(a, b); + foreach(A, &intersection, i) JOIN(A, erase)(&self, *i.ref); + JOIN(A, free)(&intersection); + return self; +} + +#if defined(CTL_MAP) +# include +#endif + +#ifndef HOLD +#undef POD +#undef NOT_INTEGRAL +#undef T +#undef A +#undef B +#undef I +#else +#undef HOLD +#endif +#undef CTL_SET + +#ifdef USE_INTERNAL_VERIFY +#undef USE_INTERNAL_VERIFY +#endif diff --git a/ctl/set.h b/ctl/set.h index ef22260b..c3f1eb58 100644 --- a/ctl/set.h +++ b/ctl/set.h @@ -2,7 +2,7 @@ SPDX-License-Identifier: MIT */ #ifndef T -#error "Template type T undefined for " +# error "Template type T undefined for " #endif // TODO emplace, extract, extract_it diff --git a/docs/index.md b/docs/index.md index 14d5c613..a5043cf4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -61,6 +61,8 @@ all containers in ISO C99/C11: | [ctl/map.h](map.md) | std::map | map | | [ctl/unordered_map.h](unordered_map.md) | std::unordered_map | umap | | [ctl/unordered_set.h](unordered_set.md) | std::unordered_set | uset | +| [ctl/btree_set.h](btree_set.md) | absl::btree_set | btset | +| [ctl/btree_map.h](btree_map.md) | absl::btree_map | btmap | |------------------------------------------------|----------------------| | [ctl/algorithm.h](algorithm.md) | `` | | [ctl/numeric.h](numeric.md) | `` | @@ -359,7 +361,6 @@ And in its grandiosity (esp. not header-only): ## Base Implementation Details - array.h: stack/heap allocated vector.h: realloc string.h: vector.h @@ -371,6 +372,8 @@ And in its grandiosity (esp. not header-only): forward_list.h: single linked list set.h: red black tree map.h: set.h + btree_set.h: b-tree + btree_map.h: - " - unordered_set.h: hashed forward linked lists unordered_map.h: unordered_set.h (pair in work) hashmap.h: stanford hash for integer keys, intel only. diff --git a/makefile b/makefile index 922cd031..680c39c6 100644 --- a/makefile +++ b/makefile @@ -165,12 +165,13 @@ tests/perf/arr/perf_arr_generate: tests/perf/arr/perf_arr_generate.c ${wildcard tests/perf/lst/perf*.cc?} : ${COMMON_H} ctl/list.h ${wildcard tests/perf/set/perf*.cc?} : ${COMMON_H} ctl/set.h +${wildcard tests/perf/btset/perf*.cc?}:${COMMON_H} ctl/btset.h ${wildcard tests/perf/deq/perf*.cc?} : ${COMMON_H} ctl/deque.h ${wildcard tests/perf/pqu/perf*.cc?} : ${COMMON_H} ctl/priority_queue.h ${wildcard tests/perf/vec/perf*.cc?} : ${COMMON_H} ctl/vector.h ${wildcard tests/perf/uset/perf*.cc?}: ${COMMON_H} ctl/unordered_set.h -${wildcard tests/perf/arr/gen*.cc?}: ${COMMON_H} ctl/array.h ${wildcard tests/perf/str/perf*.cc?} : ${COMMON_H} ctl/vector.h ctl/string.h +${wildcard tests/perf/arr/gen*.cc?}: ${COMMON_H} ctl/array.h examples: ${EXAMPLES} @@ -275,29 +276,23 @@ cppcheck: #tests/func/test_generic_iter: .cflags ${H} \ # tests/func/test_generic_iter.cc # ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_deque: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/deque.h \ - tests/func/test_deque.cc +tests/func/test_deque: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/deque.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_list: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/list.h \ - tests/func/test_list.cc +tests/func/test_list: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/list.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_forward_list: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/forward_list.h \ - tests/func/test_forward_list.cc +tests/func/test_forward_list: .cflags ${H} tests/test.h tests/func/digi.hh $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_priority_queue: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/priority_queue.h ctl/vector.h \ - tests/func/test_priority_queue.cc +tests/func/test_priority_queue: .cflags ${H} tests/test.h tests/func/digi.hh $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_queue: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/queue.h ctl/deque.h \ - tests/func/test_queue.cc +tests/func/test_queue: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/queue.h ctl/deque.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_set: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/set.h \ - tests/func/test_set.cc +tests/func/test_set: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/set.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_map: .cflags ${COMMON_H} tests/test.h tests/func/strint.hh ctl/map.h ctl/set.h \ - tests/func/test_map.cc +tests/func/test_map: .cflags ${H} tests/test.h tests/func/strint.hh $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_unordered_set: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/unordered_set.h \ - tests/func/test_unordered_set.cc +tests/func/test_btree_set: .cflags ${COMMON_H} tests/test.h ctl/set.h $@.cc + ${CXX} ${CXXFLAGS} -o $@ $@.cc +tests/func/test_unordered_set: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/unordered_set.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc tests/func/test_unordered_set_power2: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/unordered_set.h \ tests/func/test_unordered_set.cc @@ -308,32 +303,23 @@ tests/func/test_unordered_set_cached: .cflags ${COMMON_H} tests/test.h tests/fun tests/func/test_unordered_set_sleep: .cflags ${COMMON_H} tests/test.h ctl/unordered_set.h \ tests/func/test_unordered_set_sleep.c ${CC} ${CFLAGS} -O3 -finline tests/func/test_unordered_set_sleep.c -o $@ -tests/func/test_unordered_map: .cflags ${COMMON_H} tests/test.h tests/func/strint.hh ctl/unordered_map.h ctl/unordered_set.h \ - tests/func/test_unordered_map.cc +tests/func/test_unordered_map: .cflags ${COMMON_H} tests/test.h tests/func/strint.hh ctl/unordered_map.h ctl/unordered_set.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_stack: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/stack.h ctl/deque.h \ - tests/func/test_stack.cc +tests/func/test_stack: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/stack.h ctl/deque.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_string: .cflags ${COMMON_H} tests/test.h ctl/string.h ctl/vector.h \ - tests/func/test_string.cc +tests/func/test_string: .cflags ${COMMON_H} tests/test.h ctl/string.h ctl/vector.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_str_capacity: .cflags ${COMMON_H} tests/test.h ctl/string.h ctl/vector.h \ - tests/func/test_str_capacity.cc +tests/func/test_str_capacity: .cflags ${COMMON_H} tests/test.h ctl/string.h ctl/vector.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_vec_capacity: .cflags ${COMMON_H} tests/test.h ctl/vector.h \ - tests/func/test_vec_capacity.cc +tests/func/test_vec_capacity: .cflags ${COMMON_H} tests/test.h ctl/vector.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_vector: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/vector.h \ - tests/func/test_vector.cc +tests/func/test_vector: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/vector.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_array: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/array.h \ - tests/func/test_array.cc +tests/func/test_array: .cflags ${COMMON_H} tests/test.h tests/func/digi.hh ctl/array.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_double_array: .cflags ${COMMON_H} tests/test.h ctl/array.h \ - tests/func/test_double_array.cc +tests/func/test_double_array: .cflags ${COMMON_H} tests/test.h ctl/array.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc -tests/func/test_int_vector: .cflags ${COMMON_H} tests/test.h ctl/vector.h \ - tests/func/test_int_vector.cc +tests/func/test_int_vector: .cflags ${COMMON_H} tests/test.h ctl/vector.h $@.cc ${CXX} ${CXXFLAGS} -o $@ $@.cc compile_commands.json : $(H) makefile diff --git a/tests/func/test_btree_set.cc b/tests/func/test_btree_set.cc new file mode 100644 index 00000000..76bb785c --- /dev/null +++ b/tests/func/test_btree_set.cc @@ -0,0 +1,672 @@ +#include "../test.h" +#include "digi.hh" + +static inline int +digi_key_compare(digi* a, digi* b) +{ + return (*a->value == *b->value) ? 0 : (*a->value < *b->value) ? -1 : 1; +} + +#define USE_INTERNAL_VERIFY +#define T digi +#include + +#include +#include +#include + +#define TEST_MAX_VALUE INT_MAX +#ifndef DEBUG +#define print_set(a) +#define print_setpp(a) +#else +//#undef TEST_MAX_SIZE +//#define TEST_MAX_SIZE 15 +//#define TEST_MAX_VALUE 50 +void print_set(set_digi* a) { + int i = 0; + foreach(set_digi, a, it) + printf("[%d] %d\n", i++, *it.ref->value); + printf("--\n"); +} +void print_setpp(absl::btree_set& b) { + int i = 0; + for(auto& d : b) + printf("[%d] %d\n", i++, *d.value); + printf("--\n"); +} +#endif + +#define CHECK(_x, _y) { \ + assert(_x.size == _y.size()); \ + absl::btree_set::iterator _iter = _y.begin(); \ + foreach(set_digi, &_x, _it) { \ + assert(*_it.ref->value == *_iter->value); \ + _iter++; \ + } \ + set_digi_it _it = set_digi_it_each(&_x); \ + for(auto& _d : _y) { \ + assert(*_it.ref->value == *_d.value); \ + _it.step(&_it); \ + } \ +} + +#define CHECK_ITER(_node, b, _iter) \ + if (_node != NULL) \ + { \ + assert (_iter != b.end()); \ + assert(*_node->key.value == *(*_iter).value); \ + } else \ + assert (_iter == b.end()) + +int random_element(set_digi* a) +{ + const size_t index = TEST_RAND(a->size); + int test_value = 0; + size_t current = 0; + foreach(set_digi, a, it) + { + if(current == index) + { + test_value = *it.ref->value; + break; + } + current++; + } + return test_value; +} + +static void +get_random_iters (set_digi *a, set_digi_it *first_a, set_digi_it *last_a, + absl::btree_set& b, absl::btree_set::iterator& first_b, + absl::btree_set::iterator& last_b) +{ + size_t r1 = TEST_RAND(a->size / 2); + const size_t rnd = TEST_RAND(a->size / 2); + size_t r2 = MIN(r1 + rnd, a->size); + LOG("iters [%zu, %zu) of %zu\n", r1, r2, a->size); + if (a->size) + { + set_digi_it it1 = set_digi_it_range(a, set_digi_begin(a), NULL); + first_b = b.begin(); + for(size_t i = 0; i < r1; i++) + { + it1.step(&it1); + first_b++; + } + *first_a = it1; + if (r1 == r2) + { + *last_a = it1; + last_b = first_b; + } + else if (r2 == a->size) + { + *last_a = set_digi_it_range(a, NULL, NULL); + last_b = b.end(); + } + else + { + set_digi_it it2 = set_digi_it_range(a, set_digi_begin(a), NULL); + last_b = b.begin(); + for(size_t i = 0; i < r2; i++) + { + it2.step(&it2); + last_b++; + } + *last_a = it2; + } + first_a->end = last_a->node; + } + else + { + set_digi_it end = set_digi_it_range(a, NULL, NULL); + *first_a = end; + *last_a = end; + first_b = b.begin(); + last_b = b.end(); + } +} + +static void +setup_sets(set_digi* a, absl::btree_set& b) +{ + size_t iters = TEST_RAND(TEST_MAX_SIZE); + *a = set_digi_init(digi_key_compare); + for(size_t inserts = 0; inserts < iters; inserts++) + { + const int vb = TEST_RAND(TEST_MAX_SIZE); + set_digi_insert(a, digi_init(vb)); + b.insert(DIGI{vb}); + } +} + +int +main(void) +{ + int errors = 0; + INIT_SRAND; + INIT_TEST_LOOPS(10); + for(size_t loop = 0; loop < loops; loop++) + { + set_digi a; + absl::btree_set b; + setup_sets(&a, b); +#define FOREACH_METH(TEST) \ + TEST(SELF) \ + TEST(INSERT) \ + TEST(ERASE) \ + TEST(REMOVE_IF) \ + TEST(ERASE_IF) \ + TEST(ERASE_IT) \ + TEST(CLEAR) \ + TEST(SWAP) \ + TEST(COUNT) \ + TEST(CONTAINS) \ + TEST(FIND) \ + TEST(COPY) \ + TEST(EQUAL) \ + TEST(UNION) \ + +#define FOREACH_DEBUG(TEST) \ + TEST(ERASE_RANGE) /* broken */ \ + /* TEST(EMPLACE) */ \ + /* TEST(EXTRACT) */ \ + /* TEST(MERGE) */ \ + TEST(EQUAL_RANGE) \ + TEST(INTERSECTION) \ + TEST(SYMMETRIC_DIFFERENCE) \ + TEST(DIFFERENCE) \ + TEST(FIND_IF) \ + TEST(FIND_IF_NOT) \ + TEST(COUNT_RANGE) \ + TEST(COUNT_IF) \ + TEST(COUNT_IF_RANGE) \ + TEST(ALL_OF) \ + TEST(ALL_OF_RANGE) \ + TEST(ANY_OF) \ + TEST(ANY_OF_RANGE) \ + TEST(NONE_OF) \ + TEST(NONE_OF_RANGE) \ + TEST(FIND_RANGE) \ + TEST(FIND_IF_RANGE) \ + TEST(FIND_IF_NOT_RANGE) \ + +#define GENERATE_ENUM(x) TEST_##x, +#define GENERATE_NAME(x) #x, + + enum { + FOREACH_METH(GENERATE_ENUM) +#ifdef DEBUG + FOREACH_DEBUG(GENERATE_ENUM) +#endif + TEST_TOTAL + }; +#ifdef DEBUG + static const char *test_names[] = { + FOREACH_METH(GENERATE_NAME) + FOREACH_DEBUG(GENERATE_NAME) + "" + }; +#endif + int which = TEST_RAND(TEST_TOTAL); + if (test >= 0 && test < (int)TEST_TOTAL) + which = test; + LOG ("TEST %s %d (size %zu)\n", test_names[which], which, a.size); + switch(which) + { + case TEST_SELF: + { + set_digi aa = set_digi_copy(&a); + foreach(set_digi, &aa, it) + assert(set_digi_find(&a, *it.ref)); + foreach(set_digi, &a, it) + set_digi_erase(&aa, *it.ref); + assert(set_digi_empty(&aa)); + set_digi_free(&aa); + break; + } + case TEST_INSERT: + { + const int vb = TEST_RAND(TEST_MAX_SIZE); + set_digi_insert(&a, digi_init(vb)); + b.insert(DIGI{vb}); + break; + } + case TEST_ERASE: + { + const size_t erases = TEST_RAND(TEST_MAX_SIZE) / 4; + for(size_t i = 0; i < erases; i++) + if(a.size > 0) + { + const int key = TEST_RAND(TEST_MAX_SIZE); + digi kd = digi_init(key); + set_digi_erase(&a, kd); + b.erase(DIGI{key}); + CHECK(a, b); + digi_free(&kd); + } + break; + } + case TEST_ERASE_IT: + { + const size_t erases = TEST_RAND(TEST_MAX_SIZE) / 4; + for(size_t i = 0; i < erases; i++) + if(a.size > 1) + { + set_digi_it it = set_digi_it_each(&a); + it.step(&it); + set_digi_erase_it(&a, &it); + auto iter = b.begin(); + iter++; + b.erase(iter); + CHECK(a, b); + } + break; + } + case TEST_REMOVE_IF: + { + size_t b_erases = 0; + { // C++20 STD::ERASE_IF + auto iter = b.begin(); + auto end = b.end(); + while(iter != end) + { + if(*iter->value % 2) + { + iter = b.erase(iter); + b_erases++; + } + else + iter++; + } + } + size_t a_erases = set_digi_remove_if(&a, digi_is_odd); + assert(a_erases == b_erases); + break; + } + case TEST_ERASE_IF: + { + size_t b_erases = 0; +#if __cpp_lib_erase_if >= 202002000L + b_erases = std::erase_if(b, DIGIc_is_odd); +#else + { + auto iter = b.begin(); + auto end = b.end(); + while(iter != end) + { + if(*iter->value % 2) + { + iter = b.erase(iter); + b_erases++; + } + else + iter++; + } + } +#endif + size_t a_erases = set_digi_erase_if(&a, digi_is_odd); + assert(a_erases == b_erases); + break; + } + case TEST_CLEAR: + { + b.clear(); + set_digi_clear(&a); + break; + } + case TEST_SWAP: + { + set_digi aa = set_digi_copy(&a); + set_digi aaa = set_digi_init(digi_key_compare); + absl::btree_set bb = b; + absl::btree_set bbb; + set_digi_swap(&aaa, &aa); + std::swap(bb, bbb); + CHECK(aaa, bbb); + set_digi_free(&aaa); + break; + } + case TEST_COUNT: + { + int key = TEST_RAND(TEST_MAX_SIZE); + int aa = set_digi_count(&a, digi_init(key)); + int bb = b.count(DIGI{key}); + assert(aa == bb); + CHECK(a, b); + break; + } + case TEST_CONTAINS: // C++20 + { + int key = TEST_RAND(TEST_MAX_SIZE); + int aa = set_digi_contains(&a, digi_init(key)); +#if __cpp_lib_erase_if >= 202002L + int bb = b.contains(DIGI{key}); +#else + int bb = b.count(DIGI{key}) == 1; +#endif + assert(aa == bb); + break; + } + case TEST_FIND: + { + int key = TEST_RAND(TEST_MAX_SIZE); + digi kd = digi_init(key); + set_digi_node* aa = set_digi_find(&a, kd); + auto bb = b.find(DIGI{key}); + if(bb == b.end()) + assert(set_digi_end(&a) == aa); + else + assert(*bb->value == *aa->key.value); + CHECK(a, b); + digi_free(&kd); + break; + } + case TEST_COPY: + { + set_digi aa = set_digi_copy(&a); + absl::btree_set bb = b; + CHECK(aa, bb); + set_digi_free(&aa); + break; + } + case TEST_EQUAL: + { + set_digi aa = set_digi_copy(&a); + absl::btree_set bb = b; + assert(set_digi_equal(&a, &aa)); + assert(b == bb); + set_digi_free(&aa); + break; + } +#ifdef DEBUG + case TEST_UNION: + { + set_digi aa; + absl::btree_set bb; + setup_sets(&aa, bb); + set_digi aaa = set_digi_union(&a, &aa); +#if 0 + absl::btree_set bbb; + absl::btree_set::set_union(b.begin(), b.end(), bb.begin(), bb.end(), + std::inserter(bbb, bbb.begin())); + CHECK(aa, bb); + CHECK(aaa, bbb); + set_digi_free(&aaa); +#endif + set_digi_free(&aa); + break; + } + case TEST_INTERSECTION: + { + set_digi aa; + absl::btree_set bb; + setup_sets(&aa, bb); + set_digi aaa = set_digi_intersection(&a, &aa); +#if 0 + absl::btree_set bbb; + absl::btree_set::set_intersection(b.begin(), b.end(), bb.begin(), bb.end(), + std::inserter(bbb, bbb.begin())); + CHECK(aa, bb); + CHECK(aaa, bbb); + set_digi_free(&aaa); +#endif + set_digi_free(&aa); + break; + } + case TEST_SYMMETRIC_DIFFERENCE: + { + set_digi aa; + absl::btree_set bb; + setup_sets(&aa, bb); + set_digi aaa = set_digi_symmetric_difference(&a, &aa); +#if 0 + absl::btree_set bbb; + absl::btree_set::set_symmetric_difference(b.begin(), b.end(), bb.begin(), bb.end(), + std::inserter(bbb, bbb.begin())); + CHECK(aa, bb); + CHECK(aaa, bbb); + set_digi_free(&aaa); +#endif + set_digi_free(&aa); + break; + } + case TEST_DIFFERENCE: + { + set_digi aa; + absl::btree_set bb; + setup_sets(&aa, bb); + set_digi aaa = set_digi_difference(&a, &aa); +#if 0 + absl::btree_set bbb; + absl::btree_set::set_difference(b.begin(), b.end(), bb.begin(), bb.end(), + std::inserter(bbb, bbb.begin())); + CHECK(aa, bb); + CHECK(aaa, bbb); + set_digi_free(&aaa); +#endif + set_digi_free(&aa); + break; + } + case TEST_FIND_IF: + { + set_digi_node *n = set_digi_find_if(&a, digi_is_odd); + auto it = find_if(b.begin(), b.end(), DIGIc_is_odd); + CHECK_ITER(n, b, it); + break; + } + case TEST_COUNT_IF: + { + size_t count_a = set_digi_count_if(&a, digi_is_odd); + size_t count_b = count_if(b.begin(), b.end(), DIGIc_is_odd); + assert(count_a == count_b); + break; + } + case TEST_COUNT_RANGE: + { + int test_value = 0; + int v = TEST_RAND(2) ? TEST_RAND(TEST_MAX_VALUE) + : test_value; + set_digi_it first_a, last_a; + absl::btree_set::iterator first_b, last_b; + get_random_iters (&a, &first_a, &last_a, b, first_b, last_b); + size_t numa = set_digi_count_range(&first_a, &last_a, digi_init(v)); // leak? + size_t numb = count(first_b, last_b, DIGI{v}); + assert(numa == numb); // fails with SEED=2490491988 + break; + } + case TEST_COUNT_IF_RANGE: + { + set_digi_it first_a, last_a; + absl::btree_set::iterator first_b, last_b; + get_random_iters (&a, &first_a, &last_a, b, first_b, last_b); + size_t numa = set_digi_count_if_range(&first_a, &last_a, + digi_is_odd); + size_t numb = count_if(first_b, last_b, DIGIc_is_odd); + if (numa != numb) + { + print_set(&a); + print_setpp(b); + printf ("%d != %d FAIL\n", (int)numa, (int)numb); + errors++; + } + assert(numa == numb); // off by one, counts one too much + break; + } + case TEST_ALL_OF: + { + bool is_a = set_digi_all_of(&a, digi_is_odd); + bool is_b = all_of(b.begin(), b.end(), DIGIc_is_odd); + assert(is_a == is_b); + break; + } + case TEST_ALL_OF_RANGE: + { + set_digi_it first_a, last_a; + absl::btree_set::iterator first_b, last_b; + get_random_iters (&a, &first_a, &last_a, b, first_b, last_b); + bool aa = set_digi_all_of_range(&first_a, &last_a, + digi_is_odd); + bool bb = all_of(first_b, last_b, DIGIc_is_odd); + if (aa != bb) + { + print_set(&a); + print_setpp(b); + printf ("%d != %d is_odd\n", (int)aa, (int)bb); + errors++; + } + assert(aa == bb); + break; + } + case TEST_ANY_OF: + { + bool is_a = set_digi_any_of(&a, digi_is_odd); + bool is_b = any_of(b.begin(), b.end(), DIGIc_is_odd); + assert(is_a == is_b); + break; + } + case TEST_ANY_OF_RANGE: + { + set_digi_it first_a, last_a; + absl::btree_set::iterator first_b, last_b; + get_random_iters (&a, &first_a, &last_a, b, first_b, last_b); + bool aa = set_digi_any_of_range(&first_a, &last_a, + digi_is_odd); + bool bb = any_of(first_b, last_b, DIGIc_is_odd); + if (aa != bb) + { + print_set(&a); + print_setpp(b); + printf ("%d != %d is_odd\n", (int)aa, (int)bb); + errors++; + } + assert(aa == bb); + break; + } + case TEST_NONE_OF: + { + bool is_a = set_digi_none_of(&a, digi_is_odd); + bool is_b = none_of(b.begin(), b.end(), DIGIc_is_odd); + assert(is_a == is_b); + break; + } + case TEST_FIND_IF_NOT: + { + set_digi_node *n = set_digi_find_if_not(&a, digi_is_odd); + auto it = find_if_not(b.begin(), b.end(), DIGIc_is_odd); + CHECK_ITER(n, b, it); + break; + } + case TEST_FIND_RANGE: + { + int vb = TEST_RAND(2) ? TEST_RAND(TEST_MAX_VALUE) + : random_element(&a); + digi key = digi_init(vb); + set_digi_it first_a, last_a; + absl::btree_set::iterator first_b, last_b; + get_random_iters (&a, &first_a, &last_a, b, first_b, last_b); + LOG("find %d\n", vb); + set_digi_node *n = set_digi_find_range(&first_a, &last_a, key); + auto it = find(first_b, last_b, vb); + print_set(&a); + LOG("%d\n", n == last_a.node ? -1 : *n->key.value); + print_setpp(b); + LOG("vs %d\n", it == last_b ? -1 : *it->value); + if (n == last_a.node) // not found + { + assert(it == last_b); + } + else + CHECK_ITER(n, b, it); + digi_free (&key); // special + CHECK(a, b); + break; + } + case TEST_FIND_IF_RANGE: + { + set_digi_it first_a, last_a; + absl::btree_set::iterator first_b, last_b; + get_random_iters (&a, &first_a, &last_a, b, first_b, last_b); + set_digi_node *n = set_digi_find_if_range(&first_a, &last_a, digi_is_odd); + auto it = find_if(first_b, last_b, DIGIc_is_odd); + print_set(&a); + LOG("%d\n", *n->key.value); + print_setpp(b); + LOG("vs %d\n", *it->value); + if (n == last_a.node) // not found + { + assert(it == last_b); + } + else + CHECK_ITER(n, b, it); + break; + } + case TEST_FIND_IF_NOT_RANGE: // not ok + { + set_digi_it first_a, last_a; + absl::btree_set::iterator first_b, last_b; + get_random_iters (&a, &first_a, &last_a, b, first_b, last_b); + set_digi_node *n = + set_digi_find_if_not_range(&first_a, &last_a, digi_is_odd); + auto it = find_if_not(first_b, last_b, DIGIc_is_odd); + if (n == last_a.node) // not found + { + assert(it == last_b); + } + else + CHECK_ITER(n, b, it); + break; + } +#endif // DEBUG +#if 0 // algorithm and ranges + case TEST_EQUAL_RANGE: + printf("nyi\n"); + break; + case TEST_ERASE_RANGE: + { + const size_t erases = TEST_RAND(TEST_MAX_SIZE) / 4; + for(size_t i = 0; i < erases; i++) + if(a.size > 2) + { + set_digi_it it = set_digi_it_each(&a); + it.step(&it); + set_digi_it end = set_digi_it_range(&a, NULL, NULL); + set_digi_erase_range(&a, &it, &end); + auto iter = b.begin(); + iter++; + b.erase(iter, b.end()); + print_set(&a); + print_setpp(b); + CHECK(a, b); + } + break; + } + case TEST_NONE_OF_RANGE: + { + set_digi_it first_a, last_a; + absl::btree_set::iterator first_b, last_b; + get_random_iters (&a, &first_a, &last_a, b, first_b, last_b); + bool aa = set_digi_none_of_range(&first_a, &last_a, + digi_is_odd); + bool bb = none_of(first_b, last_b, DIGIc_is_odd); + if (aa != bb) + { + print_set(&a); + print_setpp(b); + printf ("%d != %d is_odd\n", (int)aa, (int)bb); + errors++; + } + //assert(aa == bb); + break; + } +#endif + } + CHECK(a, b); + set_digi_free(&a); + } + if (errors) + TEST_FAIL(__FILE__); + else + TEST_PASS(__FILE__); +}