From 3f203d4ac8d17c3a5864d4e43150bbb2735635bf Mon Sep 17 00:00:00 2001 From: Neil Date: Wed, 7 Sep 2022 22:13:56 -0700 Subject: [PATCH] update --- src/to_string.h | 3 +- src/tree.h | 1369 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 925 insertions(+), 447 deletions(-) diff --git a/src/to_string.h b/src/to_string.h index e5cde77..c1c74d9 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -115,9 +115,10 @@ static const char *STR_(to_string)(const PSTR_(box) *const box) { is_sep = 1, *b++ = comma, *b++ = space; /* Greedy typesetting: enough for "XXXXXXXXXXX" "," "…" ")" "\0". */ if((size_t)(b - buffer) - > to_string_buffer_size - 11 - 1 - ellipsis_len - 1 - 1) + > to_string_buffer_size - 11 - 1 - ellipsis_len - 1 - 1) { if(BOX_(is_element_c)(BOX_(next_c)(&it))) goto ellipsis; else break; + } } if(is_sep) b -= 2; *b++ = right; diff --git a/src/tree.h b/src/tree.h index f0b5c49..9c9f213 100644 --- a/src/tree.h +++ b/src/tree.h @@ -1,10 +1,13 @@ /** @license 2022 Neil Edelman, distributed under the terms of the [MIT License](https://opensource.org/licenses/MIT). - @abstract Stand-alone header ; examples . On a - compatible workstation, `make` creates the test suite of the examples. + @abstract Stand-alone header ; examples ; + article . On a compatible workstation, `make` creates the test + suite of the examples. - @subtitle Ordered key-tree + @subtitle Ordered tree + + ![Example of an order-3 tree.](../doc/tree.png) A tree> is an ordered set or map contained in a tree. For memory locality, this is implemented B-tree, described in @@ -12,28 +15,37 @@ @param[TREE_NAME, TREE_KEY] `` that satisfies `C` naming conventions when mangled, required, and - `TREE_KEY`, a comparable type, key>, whose default is - `unsigned int`. `` is private, whose names are prefixed in a manner to - avoid collisions. + `TREE_KEY`, a type, key>, whose default is `unsigned int`. + `` is private, whose names are prefixed in a manner to avoid collisions. @param[TREE_VALUE] - `TRIE_VALUE` is an optional payload to go with the type, value>. - The makes it a map of tree_entry> instead of a set. + Optional payload to go with the type, value>. The makes it a map + of tree_entry> instead of a set. @param[TREE_COMPARE] A function satisfying compare_fn>. Defaults to ascending order. Required if `TREE_KEY` is changed to an incomparable type. + @param[TREE_ORDER] + Sets the branching factor, or order as , to the range + `[3, UINT_MAX+1]`. Default 65 is tuned to an integer to pointer map, and + should be okay for most variations. 4 is isomorphic to left-leaning red-black + tree, . The above illustration is 5. + @param[TREE_EXPECT_TRAIT] Do not un-define certain variables for subsequent inclusion in a parameterized trait. - @param[TREE_TO_STRING_NAME, TREE_TO_STRING] - To string trait contained in ; an optional unique `` - that satisfies `C` naming conventions when mangled and function implementing - to_string_fn>. + @param[TREE_DEFAULT_NAME, TREE_DEFAULT] + Default trait; a name that satisfies `C` naming conventions when mangled and a + value> used in treeget>. There can be multiple + defaults, but only one can omit `TREE_DEFAULT_NAME`. + + @param[TREE_TO_STRING_NAME, TREE_TO_STRING] + To string trait contained in ; an optional unique `` + that satisfies `C` naming conventions when mangled and function implementing + to_string_fn>. - @fixme multi-key; implementation of order statistic tree? @fixme merge, difference @std C89 */ @@ -41,30 +53,37 @@ #if !defined(TREE_NAME) #error Name TREE_NAME undefined. #endif +#if defined(TREE_DEFAULT_NAME) || defined(TREE_DEFAULT) +#define TREE_DEFAULT_TRAIT 1 +#else +#define TREE_DEFAULT_TRAIT 0 +#endif #if defined(TREE_TO_STRING_NAME) || defined(TREE_TO_STRING) #define TREE_TO_STRING_TRAIT 1 #else #define TREE_TO_STRING_TRAIT 0 #endif -#define TREE_TRAITS TREE_TO_STRING_TRAIT +#define TREE_TRAITS TREE_DEFAULT_TRAIT + TREE_TO_STRING_TRAIT #if TREE_TRAITS > 1 #error Only one trait per include is allowed; use TREE_EXPECT_TRAIT. #endif +#if defined(TREE_DEFAULT_NAME) && !defined(TREE_DEFAULT) +#error TREE_DEFAULT_NAME requires TREE_DEFAULT. +#endif #if defined(TREE_TO_STRING_NAME) && !defined(TREE_TO_STRING) #error TREE_TO_STRING_NAME requires TREE_TO_STRING. #endif #ifndef TREE_H /* */ #if TREE_TRAITS == 0 /* */ @@ -231,10 +262,10 @@ static int PB_(to_predecessor)(struct PB_(tree) tree, struct PB_(ref) *const ref) { assert(ref); if(!tree.node || tree.height == UINT_MAX) return 0; /* Empty. */ - if(!ref->node) { /* Null: `ret` is the last key. */ + if(!ref->node) { /* Null: `ref` is the last key. */ struct PB_(tree) descend = tree; - while(descend.height) descend.height--, - descend.node = PB_(branch)(descend.node)->child[descend.node->size]; + while(descend.height) descend.height--, descend.node + = PB_(as_branch)(descend.node)->child[descend.node->size]; /* While bulk-loading, could have empty right. */ if(descend.node->size) ref->node = descend.node, ref->height = 0, ref->idx = descend.node->size - 1; @@ -243,7 +274,7 @@ static int PB_(to_predecessor)(struct PB_(tree) tree, return 1; } while(ref->height) ref->height--, - ref->node = PB_(branch_c)(ref->node)->child[ref->idx], + ref->node = PB_(as_branch_c)(ref->node)->child[ref->idx], ref->idx = ref->node->size; if(ref->idx) return ref->idx--, 1; /* Likely. */ { /* Re-descend; pick the minimum height node that has a previous key. */ @@ -251,9 +282,8 @@ static int PB_(to_predecessor)(struct PB_(tree) tree, unsigned a0; PB_(key) x; for(prev.node = 0, x = ref->node->key[0]; tree.height; - tree.node = PB_(branch_c)(tree.node)->child[a0], tree.height--) { - /* fixme: This is repeated. */ - unsigned a1 = tree.node->size; + tree.node = PB_(as_branch_c)(tree.node)->child[a0], tree.height--) { + unsigned a1 = tree.node->size; /* This is repeated code; sigh. */ a0 = 0; while(a0 < a1) { const unsigned m = (a0 + a1) / 2; @@ -262,7 +292,7 @@ static int PB_(to_predecessor)(struct PB_(tree) tree, if(a0) prev.node = tree.node, prev.height = tree.height, prev.idx = a0 - 1; } - if(!prev.node) return 0; /* Off the left. */ + if(!prev.node) return 0; /* Off left. */ *ref = prev; } return 1; /* Jumped nodes. */ } @@ -277,15 +307,15 @@ static int PB_(to_successor_c)(struct PB_(tree) tree, \ else \ ref->idx++; \ while(ref->height) ref->height--, \ - ref->node = PB_(branch_c)(ref->node)->child[ref->idx], ref->idx = 0; \ + ref->node = PB_(as_branch_c)(ref->node)->child[ref->idx], ref->idx = 0; \ if(ref->idx < ref->node->size) return 1; /* Likely. */ \ if(!ref->node->size) return 0; /* When bulk-loading. */ \ -{ /* Re-descend; pick the minimum height node that has a next key. */ \ +{ /* Re-descend; pick the minimum height node that has a next key. */ \ struct PB_(ref_c) next; \ unsigned a0; \ PB_(key) x; \ for(next.node = 0, x = ref->node->key[ref->node->size - 1]; tree.height; \ - tree.node = PB_(branch_c)(tree.node)->child[a0], tree.height--) { \ + tree.node = PB_(as_branch_c)(tree.node)->child[a0], tree.height--) { \ unsigned a1 = tree.node->size; \ a0 = 0; \ while(a0 < a1) { \ @@ -295,11 +325,11 @@ static int PB_(to_successor_c)(struct PB_(tree) tree, \ if(a0 < tree.node->size) \ next.node = tree.node, next.height = tree.height, next.idx = a0; \ } \ - if(!next.node) return 0; /* Off the right. */ \ + if(!next.node) return 0; /* Off right. */ \ *ref = next; \ } return 1; /* Jumped nodes. */ \ } -TREE_TO_SUCCESSOR(to_successor, ref) +TREE_TO_SUCCESSOR(to_successor, ref) /* For cursor. */ TREE_TO_SUCCESSOR(to_successor_c, ref_c) /* For forward iteration. */ #undef TREE_TO_SUCCESSOR @@ -312,22 +342,24 @@ static int PB_(is_element_c)(PB_(entry_c) e) { return !!e; #endif } + /* @implements `forward` */ struct PB_(forward) { const struct PB_(tree) *root; struct PB_(ref_c) next; }; -/** @return Before `tree`. @implements `forward` */ -static struct PB_(forward) PB_(forward)(const struct B_(tree) *const - tree) { struct PB_(forward) it; + +/** @return Before `tree`, (can be null.) @implements `forward` */ +static struct PB_(forward) PB_(forward)(const struct B_(tree) *const tree) { + struct PB_(forward) it; it.root = tree ? &tree->root : 0, it.next.node = 0; return it; } /** Move to next `it`. @return Element or null. @implements `next_c` */ static PB_(entry_c) PB_(next_c)(struct PB_(forward) *const it) { return assert(it), PB_(to_successor_c)(*it->root, &it->next) ? - PB_(leaf_to_entry_c)(it->next.node, it->next.idx) : PB_(null_entry_c)(); + PB_(cons_entry_c)(it->next.node, it->next.idx) : PB_(null_entry_c)(); } #define BOX_ITERATOR PB_(entry) -/** Is `x` not null? @implements `is_element` */ +/** Is `e` not null? @implements `is_element` */ static int PB_(is_element)(const PB_(entry) e) { #ifdef TREE_VALUE return !!e.key; @@ -335,69 +367,129 @@ static int PB_(is_element)(const PB_(entry) e) { return !!e; #endif } -/* @implements `iterator` */ -struct PB_(iterator) { struct PB_(tree) *root; struct PB_(ref) i; int seen; }; -/** @return Iterator to null in `tree`. @implements `iterator` */ -static struct PB_(iterator) PB_(iterator)(struct B_(tree) *const tree) { - struct PB_(iterator) it; - it.root = tree ? &tree->root : 0, it.i.node = 0, it.seen = 0; + +/* @implements `cursor` */ +struct PB_(cursor) { struct PB_(tree) *root; struct PB_(ref) ref; int seen; }; + +/** Eliminates code-re-use from begin> and end>. + @return Fills `it` and returns if `tree` has contents, in which case, `idx` + is uninitialized. */ +static int PB_(cursor_fill_part)(struct PB_(cursor) *const it, + struct B_(tree) *const tree) { + assert(it); + it->seen = 0; + if(!(it->root = tree ? &tree->root : 0) + || !(it->ref.node = tree->root.node) + || (it->ref.height = tree->root.height) == UINT_MAX) { + it->ref.node = 0; + it->ref.height = 0; + it->ref.idx = 0; + return 0; + } + return 1; +} +/** @return Before the start of `tree`, (can be null.) @implements `begin` */ +static struct PB_(cursor) PB_(begin)(struct B_(tree) *const tree) { + struct PB_(cursor) it; + if(PB_(cursor_fill_part)(&it, tree)) { + for(it.ref.node = tree->root.node; it.ref.height; + it.ref.node = PB_(as_branch)(it.ref.node)->child[0], it.ref.height--); + it.ref.idx = 0; + } return it; } -/** Advances `it`. @return Element or null. @implements `next` */ -static PB_(entry) PB_(next)(struct PB_(iterator) *const it) { - assert(it); - if(!it->root - || (it->seen || !it->i.node) && !PB_(to_successor)(*it->root, &it->i)) { - it->i.node = 0, it->seen = 0; - return PB_(null_entry)(); +/** @return Iterator after the end of `tree`, (can be null.) + @implements `end` */ +static struct PB_(cursor) PB_(end)(struct B_(tree) *const tree) { + struct PB_(cursor) it; + if(PB_(cursor_fill_part)(&it, tree)) { + for(it.ref.node = tree->root.node; it.ref.height; + it.ref.node = PB_(as_branch)(it.ref.node)->child[it.ref.node->size], + it.ref.height--); + it.ref.idx = it.ref.node->size; } - it->seen = 1; - return PB_(leaf_to_entry)(it->i.node, it->i.idx); -} -/** Move to previous `it`. @return Element or null. @implements `previous` */ -static PB_(entry) PB_(previous)(struct PB_(iterator) *const it) { - assert(it && 0); - if(!it->root || !it->i.node && it->seen != -1 - || it->seen && !PB_(to_predecessor)(*it->root, &it->i)) - return PB_(null_entry)(); - it->seen = -1; - return PB_(leaf_to_entry)(it->i.node, it->i.idx); + return it; } -/* Want to find slightly different things; code re-use is bad. Confusing. */ +/** Advances `it`. @return Element or null. @implements `next` */ +static PB_(entry) PB_(next)(struct PB_(cursor) *const it) { + assert(it); + if(!it->root || (it->seen || !it->ref.node) + && !PB_(to_successor)(*it->root, &it->ref)) + return it->ref.node = 0, it->seen = 0, PB_(null_entry)(); + assert(it->ref.node); + return it->ref.idx < it->ref.node->size + ? (it->seen = 1, PB_(cons_entry)(it->ref.node, it->ref.idx)) + : (it->seen = 0, PB_(null_entry)()); +} +/** Move to previous `it`. @return Element or null. @implements `previous` */ +static PB_(entry) PB_(previous)(struct PB_(cursor) *const it) { + assert(it); + if(!it->root || !PB_(to_predecessor)(*it->root, &it->ref)) + return it->ref.node = 0, it->seen = 0, PB_(null_entry)(); + return it->seen = 1, PB_(cons_entry)(it->ref.node, it->ref.idx); +} + +/* Want to find slightly different things; code re-use is bad. Confusing. + This is the lower-bound. */ #define TREE_FORTREE(i) i.node = tree->node, i.height = tree->height; ; \ - i.node = PB_(branch_c)(i.node)->child[i.idx], i.height-- + i.node = PB_(as_branch_c)(i.node)->child[i.idx], i.height-- #define TREE_START(i) unsigned hi = i.node->size; i.idx = 0; -#define TREE_FORNODE(i, continue) if(!hi) continue; \ -do { \ +#define TREE_FORNODE(i) do { \ const unsigned m = (i.idx + hi) / 2; \ if(PB_(compare)(key, i.node->key[m]) > 0) i.idx = m + 1; \ else hi = m; \ } while(i.idx < hi); #define TREE_FLIPPED(i) PB_(compare)(i.node->key[i.idx], key) <= 0 -/** One height at a time. */ +/** Finds `key` in `lo` one node at a time. */ static void PB_(find_idx)(struct PB_(ref) *const lo, const PB_(key) key) { TREE_START((*lo)) - TREE_FORNODE((*lo), return) + if(!lo) return; + TREE_FORNODE((*lo)) } -/** Finds lower-bound of `key` in `tree`. */ +/** Finds lower-bound of `key` in non-empty `tree`, or, if `key` is greater + than all `tree`, one off the end. */ static struct PB_(ref) PB_(lower_r)(struct PB_(tree) *const tree, const PB_(key) key) { struct PB_(ref) i, lo = { 0, 0, 0 }; for(TREE_FORTREE(i)) { TREE_START(i) - TREE_FORNODE(i, continue) + if(!hi) continue; + TREE_FORNODE(i) if(i.idx < i.node->size) { lo = i; - /* Might be useful expanding this to multi-keys. */ - if(TREE_FLIPPED(i)) break; + if(TREE_FLIPPED(i)) break; /* Multi-keys go here. */ + } + if(!i.height) { + if(!lo.node) lo = i; /* Want one-off-end if last. */ + break; } - if(!i.height) break; } return lo; } -/** Finds lower-bound of `key` in `tree` while counting the non-filled `hole` - and `is_equal`. (fixme: is_equal useless) */ +/** @return Lower bound of `x` in `tree`. @order \O(\log |`tree`|) */ +static struct PB_(ref) PB_(lower)(struct PB_(tree) tree, const PB_(key) x) { + if(!tree.node || tree.height == UINT_MAX) { + struct PB_(ref) ref; ref.node = 0; return ref; + } else { + return PB_(lower_r)(&tree, x); + } +} +/** Finds an exact `key` in non-empty `tree`. */ +static struct PB_(ref) PB_(find)(const struct PB_(tree) *const tree, + const PB_(key) key) { + struct PB_(ref) i; + for(TREE_FORTREE(i)) { + TREE_START(i) + if(!hi) continue; + TREE_FORNODE(i) + if(i.idx < i.node->size && TREE_FLIPPED(i)) break; + if(!i.height) { i.node = 0; return i; } + } + return i; +} +/** Finds lower-bound of `key` in non-empty `tree` while counting the + non-filled `hole` and `is_equal`. */ static struct PB_(ref) PB_(lookup_insert)(struct PB_(tree) *const tree, const PB_(key) key, struct PB_(ref) *const hole, int *const is_equal) { struct PB_(ref) lo; @@ -405,34 +497,35 @@ static struct PB_(ref) PB_(lookup_insert)(struct PB_(tree) *const tree, for(TREE_FORTREE(lo)) { TREE_START(lo) if(hi < TREE_MAX) *hole = lo; - TREE_FORNODE(lo, continue) + if(!hi) continue; + TREE_FORNODE(lo) if(lo.node->size < TREE_MAX) hole->idx = lo.idx; if(lo.idx < lo.node->size && TREE_FLIPPED(lo)) { *is_equal = 1; break; } if(!lo.height) break; } return lo; } -/** Finds lower-bound of `key` in `tree` while counting the non-minimum `hole` - and `is_equal`. (fixme: is_equal useless) */ +/** Finds exact `key` in non-empty `tree`. If `node` is found, temporarily, the + nodes that have `TREE_MIN` keys have + `as_branch(node).child[TREE_MAX] = parent` or, for leaves, `leaf_parent`, + which must be set. (Patently terrible for running concurrently; hack, would be + nice to go down tree maybe.) */ static struct PB_(ref) PB_(lookup_remove)(struct PB_(tree) *const tree, - const PB_(key) key, struct PB_(ref) *const lump) { + const PB_(key) key, struct PB_(node) **leaf_parent) { + struct PB_(node) *parent = 0; struct PB_(ref) lo; - lump->node = 0; for(TREE_FORTREE(lo)) { TREE_START(lo) - TREE_FORNODE(lo, continue) - if(lo.node->size > TREE_MIN || lo.height && ( - lo.idx - && PB_(branch)(lo.node)->child[lo.idx - 1]->size > TREE_MIN - || lo.idx < lo.node->size - && PB_(branch)(lo.node)->child[lo.idx + 1]->size > TREE_MIN - )) *lump = lo; + /* Cannot delete bulk add. */ + if(parent && hi < TREE_MIN || !parent && !hi) { lo.node = 0; break; } + if(hi <= TREE_MIN) { /* Remember the parent temporarily. */ + if(lo.height) PB_(as_branch)(lo.node)->child[TREE_MAX] = parent; + else *leaf_parent = parent; + } + TREE_FORNODE(lo) if(lo.idx < lo.node->size && TREE_FLIPPED(lo)) break; if(!lo.height) { lo.node = 0; break; } /* Was not in. */ - } - if(!lump->node) { - /* Check for root. */ - assert(0); + parent = lo.node; } return lo; } @@ -441,19 +534,18 @@ static struct PB_(ref) PB_(lookup_remove)(struct PB_(tree) *const tree, #undef TREE_FORNODE #undef TREE_FLIPPED -/** @param[tree] Can be null. @return Lower bound of `x` in `tree`. - @order \O(\log |`tree`|) */ -static struct PB_(ref) PB_(lower)(struct PB_(tree) tree, const PB_(key) x) { - if(!tree.node || tree.height == UINT_MAX) { - struct PB_(ref) ref; ref.node = 0; return ref; - } else { - return PB_(lower_r)(&tree, x); - } + +/** Zeroed data (not all-bits-zero) is initialized. @return An idle tree. + @order \Theta(1) @allow */ +static struct B_(tree) B_(tree)(void) { + struct B_(tree) tree; + tree.root.node = 0; tree.root.height = 0; + return tree; } -/** Frees non-empty `tree` and it's children recursively, but doesn't put it - to idle or clear pointers. - @param[one] If `one` is valid, tries to keep one leaf. Set to null before. */ +/** Private: frees non-empty `tree` and it's children recursively, but doesn't + put it to idle or clear pointers. + @param[keep] Keep one leaf if non-null. Set to null before. */ static void PB_(clear_r)(struct PB_(tree) tree, struct PB_(node) **const keep) { assert(tree.node); if(!tree.height) { @@ -464,35 +556,25 @@ static void PB_(clear_r)(struct PB_(tree) tree, struct PB_(node) **const keep) { unsigned i; child.height = tree.height - 1; for(i = 0; i <= tree.node->size; i++) - child.node = PB_(branch)(tree.node)->child[i], + child.node = PB_(as_branch)(tree.node)->child[i], PB_(clear_r)(child, keep); - free(PB_(branch)(tree.node)); + free(PB_(as_branch)(tree.node)); } } -/** `tree` can be null. */ +/** Private: `tree` can be null. */ static void PB_(clear)(struct B_(tree) *tree) { struct PB_(node) *one = 0; /* Already not there/idle/empty. */ if(!tree || !tree->root.node || tree->root.height == UINT_MAX) return; PB_(clear_r)(tree->root, &one), assert(one); /* This is a special state where the tree has one leaf, but it is empty. - This state exists because it gives hysteresis to 0 -- 1 transition. */ + This state exists because it gives hysteresis to 0 -- 1 transition because + we have no advanced memory management. */ tree->root.node = one; tree->root.height = UINT_MAX; } - -/* Box override information. */ -#define BOX_ PB_ -#define BOX struct B_(tree) - -/** @return Initializes `tree` to idle. @order \Theta(1) @allow */ -static struct B_(tree) B_(tree)(void) { - struct B_(tree) tree; - tree.root.node = 0; tree.root.height = 0; - return tree; -} - -/** Returns an initialized `tree` to idle, `tree` can be null. @allow */ +/** Returns an initialized `tree` to idle, `tree` can be null. + @order \O(|`tree`|) @allow */ static void B_(tree_)(struct B_(tree) *const tree) { if(!tree) return; /* Null. */ if(!tree->root.node) { /* Idle. */ @@ -505,42 +587,74 @@ static void B_(tree_)(struct B_(tree) *const tree) { *tree = B_(tree)(); } -/** Stores an iteration in a tree. Generally, changes in the topology of the - tree invalidate it. (Future: have insert and delete with iterators.) */ -struct B_(tree_iterator) { struct PB_(iterator) _; }; -/** @return An iterator before the first element of `tree`. Can be null. - @allow */ -static struct B_(tree_iterator) B_(tree_iterator)(struct B_(tree) *const tree) - { struct B_(tree_iterator) it; it._ = PB_(iterator)(tree); return it; } -/** Advances `it` to the next element. @return A pointer to the current - element or null. @allow */ -static PB_(entry) B_(tree_next)(struct B_(tree_iterator) *const it) - { return PB_(next)(&it->_); } +/** Clears `tree`, which can be null, idle, empty, or full. If it is empty or + full, it remains active. @order \O(|`tree`|) @allow */ +static void B_(tree_clear)(struct B_(tree) *const tree) { PB_(clear)(tree); } -/** @param[tree] Can be null. @return Finds the smallest entry in `tree` that - is at the lower bound of `x`. If `x` is higher than any of `tree`, it will be - placed just passed the end. @order \O(\log |`tree`|) @allow */ -static struct B_(tree_iterator) B_(tree_lower_iterator) - (struct B_(tree) *const tree, const PB_(key) x) { - struct B_(tree_iterator) it; - if(!tree) return it._.root = 0, it; - it._.i = PB_(lower)(tree->root, x); - it._.root = &tree->root; - it._.seen = 0; - return it; +/** Private: counts a sub-tree, `tree`. */ +static size_t PB_(count_r)(const struct PB_(tree) tree) { + size_t c = tree.node->size; + if(tree.height) { + const struct PB_(branch) *const branch = PB_(as_branch)(tree.node); + struct PB_(tree) sub; + size_t i; + sub.height = tree.height - 1; + for(i = 0; i <= tree.node->size; i++) { + sub.node = branch->child[i]; + c += PB_(count_r)(sub); + } + } + return c; +} +/** Counts all the keys on `tree`, which can be null. + @order \O(|`tree`|) @allow */ +static size_t B_(tree_count)(const struct B_(tree) *const tree) { + return tree && tree->root.height != UINT_MAX + ? PB_(count_r)(tree->root) : 0; +} + +/** @return Is `x` in `tree`? @order \O(\log |`tree`|) @allow */ +static int B_(tree_contains)(const struct B_(tree) *const tree, + const PB_(key) x) { + return tree && tree->root.node && tree->root.height != UINT_MAX + && PB_(find)(&tree->root, x).node ? 1 : 0; +} + +/* @return Get the value of `x` in `tree`, or if no `x`, null. The map type is + a pointer to `TREE_VALUE` and the set type is a pointer to `TREE_KEY`. + @order \O(\log |`tree`|) @allow */ +/*static PB_(value) *B_(tree_get)(const struct B_(tree) *const tree, + const PB_(key) x) { + struct PB_(ref) ref; + if(!tree || !tree->root.node || tree->root.height == UINT_MAX + || !(ref = PB_(find)(&tree->root, x)).node) return 0; + return PB_(ref_to_valuep)(ref); +}*/ +/* fixme: entry tree_query -- there is no functionality that returns the + key. */ + +/** @return Get the value of `key` in `tree`, or if no key, `default_value`. + The map type is `TREE_VALUE` and the set type is `TREE_KEY`. + @order \O(\log |`tree`|) @allow */ +static PB_(value) B_(tree_get_or)(const struct B_(tree) *const tree, + const PB_(key) key, const PB_(value) default_value) { + struct PB_(ref) ref; + return tree && tree->root.node && tree->root.height != UINT_MAX + && (ref = PB_(find)(&tree->root, key)).node + ? *PB_(ref_to_valuep)(ref) : default_value; } /** For example, `tree = { 10 }`, `x = 5 -> 10`, `x = 10 -> 10`, - `x = 11 -> null`. - @return Lower-bound value match for `x` in `tree` or null if `x` is greater - than all in `tree`. @order \O(\log |`tree`|) @allow */ -static PB_(value) *B_(tree_lower_value)(struct B_(tree) *const tree, - const PB_(key) x) - { return tree ? PB_(ref_to_value)(PB_(lower)(tree->root, x)) : 0; } - -/** Clears `tree`, which can be null, idle, empty, or full. If it is empty or - full, it remains active. */ -static void B_(tree_clear)(struct B_(tree) *const tree) { PB_(clear)(tree); } + `x = 11 -> null`. (There is no upper value.) + @return Lower-bound value match for `key` in `tree` or `default_value` if + `key` is greater than all in `tree`. The map type is `TREE_VALUE` and the set + type is `TREE_KEY`. @order \O(\log |`tree`|) @allow */ +static PB_(value) B_(tree_at_or)(const struct B_(tree) *const tree, + const PB_(key) key, const PB_(value) default_value) { + struct PB_(ref) ref; + return tree && (ref = PB_(lower)(tree->root, key)).node + && ref.idx < ref.node->size ? *PB_(ref_to_valuep)(ref) : default_value; +} #ifdef TREE_VALUE /* */ -{ struct PB_(node) *new_head = 0; struct PB_(ref) add, hole, cursor; - int is_growing = 0; - assert(tree); - if(!(add.node = tree->root.node)) goto idle; - else if(tree->root.height == UINT_MAX) goto empty; + assert(root); + if(!(add.node = root->node)) goto idle; + else if(root->height == UINT_MAX) goto empty; goto descend; idle: /* No reserved memory. */ - assert(!add.node && !tree->root.height); + assert(!add.node && !root->height); if(!(add.node = malloc(sizeof *add.node))) goto catch; - tree->root.node = add.node; - tree->root.height = UINT_MAX; + root->node = add.node; + root->height = UINT_MAX; goto empty; empty: /* Reserved dynamic memory, but tree is empty. */ - assert(add.node && tree->root.height == UINT_MAX); - add.height = tree->root.height = 0; + assert(add.node && root->height == UINT_MAX); + add.height = root->height = 0; add.node->size = 0; add.idx = 0; goto insert; descend: /* Record last node that has space. */ { int is_equal = 0; - add = PB_(lookup_insert)(&tree->root, key, &hole, &is_equal); + add = PB_(lookup_insert)(root, key, &hole, &is_equal); if(is_equal) { - /* Assumes key is unique; we might not want this for multi-maps, - but that is not implemented yet. */ + if(eject) { + *eject = add.node->key[add.idx]; + add.node->key[add.idx] = key; + } #ifdef TREE_VALUE - if(value) *value = PB_(ref_to_value)(add); + if(value) *value = PB_(ref_to_valuep)(add); #endif - return TREE_YIELD; + return TREE_PRESENT; } } if(hole.node == add.node) goto insert; else goto grow; insert: /* Leaf has space to spare; usually end up here. */ - assert(add.node && add.idx <= add.node->size && add.node->size < TREE_MAX - && (!add.height || is_growing)); + assert(add.node && add.idx <= add.node->size && add.node->size < TREE_MAX); memmove(add.node->key + add.idx + 1, add.node->key + add.idx, sizeof *add.node->key * (add.node->size - add.idx)); #ifdef TREE_VALUE @@ -796,11 +897,11 @@ insert: /* Leaf has space to spare; usually end up here. */ add.node->size++; add.node->key[add.idx] = key; #ifdef TREE_VALUE - if(value) *value = PB_(ref_to_value)(add); + if(value) *value = PB_(ref_to_valuep)(add); #endif return TREE_UNIQUE; grow: /* Leaf is full. */ { - unsigned new_no = hole.node ? hole.height : tree->root.height + 2; + unsigned new_no = hole.node ? hole.height : root->height + 2; struct PB_(node) **new_next = &new_head, *new_leaf; struct PB_(branch) *new_branch; assert(new_no); @@ -818,7 +919,7 @@ grow: /* Leaf is full. */ { *new_next = new_leaf; /* Attach new nodes to the tree. The hole is now an actual hole. */ if(hole.node) { /* New nodes are a sub-structure of the tree. */ - struct PB_(branch) *holeb = PB_(branch)(hole.node); + struct PB_(branch) *holeb = PB_(as_branch)(hole.node); memmove(hole.node->key + hole.idx + 1, hole.node->key + hole.idx, sizeof *hole.node->key * (hole.node->size - hole.idx)); #ifdef TREE_VALUE @@ -830,10 +931,10 @@ grow: /* Leaf is full. */ { holeb->child[hole.idx + 1] = new_head; hole.node->size++; } else { /* New nodes raise tree height. */ - struct PB_(branch) *const new_root = PB_(branch)(new_head); - hole.node = new_head, hole.height = ++tree->root.height, hole.idx = 0; + struct PB_(branch) *const new_root = PB_(as_branch)(new_head); + hole.node = new_head, hole.height = ++root->height, hole.idx = 0; new_head = new_root->child[1] = new_root->child[0]; - new_root->child[0] = tree->root.node, tree->root.node = hole.node; + new_root->child[0] = root->node, root->node = hole.node; hole.node->size = 1; } cursor = hole; /* Go down; (as opposed to doing it on paper.) */ @@ -842,9 +943,10 @@ grow: /* Leaf is full. */ { struct PB_(node) *sibling; assert(cursor.node && cursor.node->size && cursor.height); sibling = new_head; + /*PB_(graph_usual)(tree, "graph/work.gv");*/ /* Descend now while split hasn't happened -- easier. */ - new_head = --cursor.height ? PB_(branch)(new_head)->child[0] : 0; - cursor.node = PB_(branch)(cursor.node)->child[cursor.idx]; + new_head = --cursor.height ? PB_(as_branch)(new_head)->child[0] : 0; + cursor.node = PB_(as_branch)(cursor.node)->child[cursor.idx]; PB_(find_idx)(&cursor, key); assert(!sibling->size && cursor.node->size == TREE_MAX); /* Atomic. */ /* Expand `cursor`, which is full, to multiple nodes. */ @@ -868,8 +970,8 @@ grow: /* Leaf is full. */ { sizeof *cursor.node->value * (TREE_SPLIT - 1 - cursor.idx)); #endif if(cursor.height) { - struct PB_(branch) *const cb = PB_(branch)(cursor.node), - *const sb = PB_(branch)(sibling); + struct PB_(branch) *const cb = PB_(as_branch)(cursor.node), + *const sb = PB_(as_branch)(sibling); struct PB_(node) *temp = sb->child[0]; memcpy(sb->child, cb->child + TREE_SPLIT, sizeof *cb->child * (TREE_MAX - TREE_SPLIT + 1)); @@ -896,8 +998,8 @@ grow: /* Leaf is full. */ { sizeof *sibling->value * (TREE_MAX - cursor.idx)); #endif if(cursor.height) { - struct PB_(branch) *const cb = PB_(branch)(cursor.node), - *const sb = PB_(branch)(sibling); + struct PB_(branch) *const cb = PB_(as_branch)(cursor.node), + *const sb = PB_(as_branch)(sibling); struct PB_(node) *temp = sb->child[0]; memcpy(sb->child, cb->child + TREE_SPLIT + 1, sizeof *cb->child * (hole.idx + 1)); @@ -913,8 +1015,8 @@ grow: /* Leaf is full. */ { sizeof *sibling->value * (TREE_MAX - TREE_SPLIT)); #endif if(cursor.height) { - struct PB_(branch) *const cb = PB_(branch)(cursor.node), - *const sb = PB_(branch)(sibling); + struct PB_(branch) *const cb = PB_(as_branch)(cursor.node), + *const sb = PB_(as_branch)(sibling); memcpy(sb->child + 1, cb->child + TREE_SPLIT + 1, sizeof *cb->child * (TREE_MAX - TREE_SPLIT)); } @@ -924,182 +1026,376 @@ grow: /* Leaf is full. */ { if(cursor.height) goto split; /* Loop max `\log_{TREE_MIN} size`. */ hole.node->key[hole.idx] = key; #ifdef TREE_VALUE - if(value) *value = PB_(ref_to_value)(hole); + if(value) *value = PB_(ref_to_valuep)(hole); #endif assert(!new_head); return TREE_UNIQUE; -} catch: +} catch: /* Didn't work. Reset. */ while(new_head) { - struct PB_(branch) *const top = PB_(branch)(new_head); + struct PB_(branch) *const top = PB_(as_branch)(new_head); new_head = top->child[0]; free(top); } if(!errno) errno = ERANGE; /* Non-POSIX OSs not mandated to set errno. */ return TREE_ERROR; +#ifdef TREE_VALUE } +#else +} +#endif -/** Tries to remove `key` from `tree`. @return Success. */ -static int B_(tree_remove)(struct B_(tree) *const tree, - const PB_(key) key) { - struct PB_(ref) rm, lump; - struct { - struct { - PB_(key) key; -#ifdef TREE_VALUE - PB_(value) value; -#endif - struct PB_(node) *link; - int link_lt; - } store[2]; - unsigned next; - } temp; - temp.next = 0; - assert(tree); - /* Traverse down the tree until the `key`. */ - if(!(rm.node = tree->root.node) || tree->root.height == UINT_MAX - || !(rm = PB_(lookup_remove)(&tree->root, key, &lump)).node) return 0; - if(rm.height) goto branch; else goto leaf; -branch: { - struct PB_(ref) succ; - struct PB_(ref) pred; - pred = rm; - succ = rm; - /* This will be more efficient duplicating code? it actually doesn't need - all the code. - while(ref->height) ref->height--, - ref->node = PB_(branch_c)(ref->node)->child[ref->idx], - ref->idx = ref->node->size; - if(ref->idx) return ref->idx--, 1; <-- predecessor - ref->idx++; - while(ref->height) ref->height--, - ref->node = PB_(branch_c)(ref->node)->child[ref->idx], ref->idx = 0; - if(ref->idx < ref->node->size) return 1; <-- successor */ - assert(0); -} leaf: - if(rm.node == lump.node) goto excess; - else if(lump.node) /* and size <= MIN */ goto balance; - else { - assert(0); /* Root is a special case, it can have down to one. */ - goto shrink; - } -balance: { - struct PB_(branch) *const lumpb = PB_(branch)(lump.node); - struct PB_(ref) child; +#ifdef TREE_VALUE /* */ + +#ifdef TREE_VALUE /* */ + +/** Removes `x` from `tree` which must have contents. */ +static int PB_(remove)(struct PB_(tree) *const tree, const PB_(key) x) { + struct PB_(ref) rm, parent /* Only if `key.size <= TREE_MIN`. */; + struct PB_(branch) *parentb; struct { struct PB_(node) *less, *more; } sibling; - assert(lump.height && lump.idx <= lump.node->size && lump.node->size > 0); - /* Find the child and siblings. */ - child.node = lumpb->child[lump.idx]; - if(child.height = lump.height - 1) PB_(find_idx)(&child, key); - else assert(child.node == rm.node), child.idx = rm.idx; - assert(child.node->size == TREE_MIN); - sibling.less = lump.idx ? lumpb->child[lump.idx - 1] : 0; - sibling.more = lump.idx < lump.node->size ? lumpb->child[lump.idx + 1] : 0; - assert(sibling.less || sibling.more); - /* Pick the sibling with the most nodes to balance. */ - if((sibling.less ? sibling.less->size : 0) - > (sibling.more ? sibling.more->size : 0)) { /* Split left. */ - const unsigned combined = child.node->size + sibling.less->size, - to_promote = combined / 2, to_more = to_promote + 1, - transfer = sibling.less->size - to_more; - assert(sibling.less && lump.idx - && to_promote >= TREE_MIN && to_more <= sibling.less->size); - printf("combined %u; to_promote %u; to_more %u -> transfer %u\n", - combined, to_promote, to_more, transfer); - /* Make way for the keys from the less. */ - printf("move child1 (%u)\n", child.node->size - child.idx - 1); - memmove(child.node->key + child.idx + 1 + transfer, - child.node->key + child.idx + 1, - sizeof *child.node->key * (child.node->size - child.idx - 1)); - printf("move child2 (%u)\n", child.idx); - memmove(child.node->key + 1 + transfer, child.node->key, - sizeof *child.node->key * child.idx); - child.node->key[transfer] = lump.node->key[lump.idx - 1]; - printf("less %u(%u) -> 0\n", to_more, transfer); - memcpy(child.node->key, sibling.less->key + to_more, - sizeof *sibling.less->key * transfer); - lump.node->key[lump.idx - 1] = sibling.less->key[to_promote]; - assert(child.node->size <= TREE_MAX - transfer); - child.node->size += transfer; - sibling.less->size = (unsigned char)to_promote; - if(lump.height > 1) { - struct PB_(branch) *const lessb = PB_(branch)(sibling.less); - assert(0); - } - } else { /* Split right. ***incorrect***? */ - const unsigned combined = child.node->size + sibling.more->size, - to_promote = (combined - 1) / 2, to_more = to_promote - 1; - assert(sibling.more && to_promote && to_promote < sibling.more->size); - printf("to_promote %u, to_less %u\n", to_promote, to_more); - /* Make way for the keys from the less. */ - printf("move child (%u)\n", child.node->size - child.idx - 1); - memmove(child.node->key + child.idx, child.node->key + child.idx + 1, - sizeof *child.node->key * (child.node->size - child.idx - 1)); - child.node->key[child.node->size - 1] = lump.node->key[lump.idx]; - memcpy(child.node->key + child.node->size, sibling.more->key, - sizeof *sibling.more->key * to_more); - assert(0); - } - goto end; -merge: - if(lump.idx < lump.node->size && lumpb->child[lump.idx + 1] && (lump.node->size > TREE_MIN)) { -lean_left: /* Prefer left-leaning: less work for copying. */ - /*left = child.node, right = lumpb->child[lump.idx + 1];*/ - temp.store[temp.next].key = lump.node->key[lump.idx]; -#ifdef TREE_VALUE - temp.store[temp.next].value = lump.node->value[lump.idx]; -#endif - temp.store[temp.next].link = lumpb->child[lump.idx + 1]; - /* Not necessarily! */ - memmove(lump.node->key + lump.idx, lump.node->key + lump.idx + 1, - sizeof *lump.node->key * (lump.node->size - lump.idx - 1)); -#ifdef TREE_VALUE - memmove(lump.node->value + lump.idx, lump.node->value + lump.idx + 1, - sizeof *lump.node->value * (lump.node->size - lump.idx - 1)); -#endif - memmove(lumpb->child + lump.idx + 1, lumpb->child + lump.idx + 2, - sizeof *lumpb->child * (lump.node->size - lump.idx - 1)); - lump.node->size--; + PB_(key) provisional_x = x; + parent.node = 0; + assert(tree && tree->node && tree->height != UINT_MAX); + /* Traverse down the tree until `key`, leaving breadcrumbs for parents of + minimum key nodes. */ + if(!(rm = PB_(lookup_remove)(tree, x, &parent.node)).node) return 0; + /* Important when `rm = parent`; `find_idx` later. */ + parent.height = rm.height + 1; + assert(rm.idx < rm.node->size); + if(rm.height) goto branch; else goto upward; +branch: { + struct { struct PB_(ref) leaf; struct PB_(node) *parent; unsigned top; } + pred, succ, chosen; + assert(rm.height); + /* Predecessor leaf. */ + pred.leaf = rm, pred.top = UINT_MAX; + do { + struct PB_(node) *const up = pred.leaf.node; + pred.leaf.node = PB_(as_branch_c)(pred.leaf.node)->child[pred.leaf.idx]; + pred.leaf.idx = pred.leaf.node->size; + pred.leaf.height--; + if(pred.leaf.node->size < TREE_MIN) /* Possible in bulk-add? */ + { pred.leaf.node = 0; goto no_pred; } + else if(pred.leaf.node->size > TREE_MIN) pred.top = pred.leaf.height; + else if(pred.leaf.height) + PB_(as_branch)(pred.leaf.node)->child[TREE_MAX] = up; + else pred.parent = up; + } while(pred.leaf.height); + pred.leaf.idx--; +no_pred: + /* Successor leaf. */ + succ.leaf = rm, succ.top = UINT_MAX; + succ.leaf.idx++; + do { + struct PB_(node) *const up = succ.leaf.node; + succ.leaf.node = PB_(as_branch_c)(succ.leaf.node)->child[succ.leaf.idx]; + succ.leaf.idx = 0; + succ.leaf.height--; + if(succ.leaf.node->size < TREE_MIN) + { succ.leaf.node = 0; goto no_succ; } + else if(succ.leaf.node->size > TREE_MIN) succ.top = succ.leaf.height; + else if(succ.leaf.height) + PB_(as_branch)(succ.leaf.node)->child[TREE_MAX] = up; + else succ.parent = up; + } while(succ.leaf.height); +no_succ: + /* Choose the predecessor or successor. */ + if(!pred.leaf.node) { + assert(succ.leaf.node); + chosen = succ; + } else if(!succ.leaf.node) { + assert(pred.leaf.node); + chosen = pred; + } else if(pred.leaf.node->size < succ.leaf.node->size) { + chosen = succ; + } else if(pred.leaf.node->size > succ.leaf.node->size) { + chosen = pred; + } else if(pred.top > succ.top) { + chosen = succ; } else { - /*left = lumpb->child[lump.idx - 1], right = child.node;*/ - assert(0); + chosen = pred; } - temp.next = !temp.next; - /*printf("remove: merging %s and %s.\n", orcify(left), orcify(right)); - assert(left->size == TREE_MIN && right->size == TREE_MIN);*/ - assert(0); - PB_(find_idx)(&lump, key); - return 0; -} shrink: /* Every node along the path is minimal, the height decreases. */ - assert(0); - return 0; -excess: - assert(rm.node && rm.idx < rm.node->size && rm.node->size > TREE_MIN - && !rm.height); + /* Replace `rm` with the predecessor or the successor leaf. */ + provisional_x = rm.node->key[rm.idx] + = chosen.leaf.node->key[chosen.leaf.idx]; +#ifdef TREE_VALUE + rm.node->value[rm.idx] = chosen.leaf.node->value[chosen.leaf.idx]; +#endif + rm = chosen.leaf; + if(chosen.leaf.node->size <= TREE_MIN) parent.node = chosen.parent; + parent.height = 1; + goto upward; +} upward: /* The first iteration, this will be a leaf. */ + assert(rm.node); + if(!parent.node) goto space; + assert(rm.node->size <= TREE_MIN); /* Condition on `parent.node`. */ + /* Retrieve forgotten information about the index in parent. (This is not + as fast at it could be, but holding parent data in minimum keys allows it + to be in place, if a hack. We could go down, but new problems arise.) */ + PB_(find_idx)(&parent, provisional_x); + parentb = PB_(as_branch)(parent.node); + assert(parent.idx <= parent.node->size + && parentb->child[parent.idx] == rm.node); + /* Sibling edges. */ + sibling.less = parent.idx ? parentb->child[parent.idx - 1] : 0; + sibling.more = parent.idx < parent.node->size + ? parentb->child[parent.idx + 1] : 0; + assert(sibling.less || sibling.more); + /* It's not clear which of `{ <, <= }` would be better. */ + if((sibling.more ? sibling.more->size : 0) + > (sibling.less ? sibling.less->size : 0)) goto balance_more; + else goto balance_less; +balance_less: { + const unsigned combined = rm.node->size + sibling.less->size; + unsigned promote, more, transfer; + assert(parent.idx); + if(combined < 2 * TREE_MIN + 1) goto merge_less; /* Don't have enough. */ + assert(sibling.less->size > TREE_MIN); /* Since `rm.size <= TREE_MIN`. */ + promote = (combined - 1 + 1) / 2, more = promote + 1; + transfer = sibling.less->size - more; + assert(transfer < TREE_MAX && rm.node->size <= TREE_MAX - transfer); + /* Make way for the keys from the less. */ + memmove(rm.node->key + rm.idx + 1 + transfer, rm.node->key + rm.idx + 1, + sizeof *rm.node->key * (rm.node->size - rm.idx - 1)); + memmove(rm.node->key + transfer + 1, rm.node->key, + sizeof *rm.node->key * rm.idx); + rm.node->key[transfer] = parent.node->key[parent.idx - 1]; + memcpy(rm.node->key, sibling.less->key + more, + sizeof *sibling.less->key * transfer); + parent.node->key[parent.idx - 1] = sibling.less->key[promote]; +#ifdef TREE_VALUE + memmove(rm.node->value + rm.idx + 1 + transfer, rm.node->value + rm.idx + 1, + sizeof *rm.node->value * (rm.node->size - rm.idx - 1)); + memmove(rm.node->value + transfer + 1, rm.node->value, + sizeof *rm.node->value * rm.idx); + rm.node->value[transfer] = parent.node->value[parent.idx - 1]; + memcpy(rm.node->value, sibling.less->value + more, + sizeof *sibling.less->value * transfer); + parent.node->value[parent.idx - 1] = sibling.less->value[promote]; +#endif + if(rm.height) { + struct PB_(branch) *const lessb = PB_(as_branch)(sibling.less), + *const rmb = PB_(as_branch)(rm.node); + unsigned transferb = transfer + 1; + /* This is already moved; inefficient. */ + memmove(rmb->child + transferb, rmb->child, + sizeof *rmb->child * (rm.node->size + 1 - 1)); + memcpy(rmb->child, lessb->child + promote + 1, + sizeof *lessb->child * transferb); + } + rm.node->size += transfer; + sibling.less->size = promote; + goto end; +} balance_more: { + const unsigned combined = rm.node->size + sibling.more->size; + unsigned promote; + assert(rm.node->size); + if(combined < 2 * TREE_MIN + 1) goto merge_more; /* Don't have enough. */ + assert(sibling.more->size > TREE_MIN); /* Since `rm.size <= TREE_MIN`. */ + promote = (combined - 1) / 2 - rm.node->size; /* In `more`. Could be +1. */ + assert(promote < TREE_MAX && rm.node->size <= TREE_MAX - promote); + /* Delete key. */ + memmove(rm.node->key + rm.idx, rm.node->key + rm.idx + 1, + sizeof *rm.node->key * (rm.node->size - rm.idx - 1)); + /* Demote into hole. */ + rm.node->key[rm.node->size - 1] = parent.node->key[parent.idx]; + /* Transfer some keys from more to child. */ + memcpy(rm.node->key + rm.node->size, sibling.more->key, + sizeof *sibling.more->key * promote); + /* Promote one key from more. */ + parent.node->key[parent.idx] = sibling.more->key[promote]; + /* Move back in more. */ + memmove(sibling.more->key, sibling.more->key + promote + 1, + sizeof *sibling.more->key * (sibling.more->size - promote - 1)); +#ifdef TREE_VALUE + memmove(rm.node->value + rm.idx, rm.node->value + rm.idx + 1, + sizeof *rm.node->value * (rm.node->size - rm.idx - 1)); + rm.node->value[rm.node->size - 1] = parent.node->value[parent.idx]; + memcpy(rm.node->value + rm.node->size, sibling.more->value, + sizeof *sibling.more->value * promote); + parent.node->value[parent.idx] = sibling.more->value[promote]; + memmove(sibling.more->value, sibling.more->value + promote + 1, + sizeof *sibling.more->value * (sibling.more->size - promote - 1)); +#endif + if(rm.height) { + struct PB_(branch) *const moreb = PB_(as_branch)(sibling.more), + *const rmb = PB_(as_branch)(rm.node); + unsigned transferb = promote + 1; + /* This is already moved; inefficient. */ + memcpy(rmb->child + rm.node->size, moreb->child, + sizeof *moreb->child * transferb); + memmove(moreb->child, moreb->child + transferb, + sizeof *rmb->child * (moreb->base.size + 1 - transferb)); + } + rm.node->size += promote; + sibling.more->size -= promote + 1; + goto end; +} merge_less: + assert(parent.idx && parent.idx <= parent.node->size && parent.node->size + && rm.idx < rm.node->size && rm.node->size == TREE_MIN + && sibling.less->size == TREE_MIN + && sibling.less->size + rm.node->size <= TREE_MAX); + /* There are (maybe) two spots that we can merge, this is the less. */ + parent.idx--; + /* Bring down key from `parent` to append to `less`. */ + sibling.less->key[sibling.less->size] = parent.node->key[parent.idx]; + /* Copy the keys, leaving out deleted. */ + memcpy(sibling.less->key + sibling.less->size + 1, rm.node->key, + sizeof *rm.node->key * rm.idx); + memcpy(sibling.less->key + sibling.less->size + 1 + rm.idx, + rm.node->key + rm.idx + 1, + sizeof *rm.node->key * (rm.node->size - rm.idx - 1)); +#ifdef TREE_VALUE + sibling.less->value[sibling.less->size] = parent.node->value[parent.idx]; + memcpy(sibling.less->value + sibling.less->size + 1, rm.node->value, + sizeof *rm.node->value * rm.idx); + memcpy(sibling.less->value + sibling.less->size + 1 + rm.idx, + rm.node->value + rm.idx + 1, + sizeof *rm.node->value * (rm.node->size - rm.idx - 1)); +#endif + if(rm.height) { /* The `parent` links will have one less. Copying twice. */ + struct PB_(branch) *const lessb = PB_(as_branch)(sibling.less), + *const rmb = PB_(as_branch)(rm.node); + memcpy(lessb->child + sibling.less->size + 1, rmb->child, + sizeof *rmb->child * rm.node->size); /* _Sic_. */ + } + sibling.less->size += rm.node->size; + /* Remove references to `rm` from `parent`. The parent will have one less + link than key (_ie_, an equal number.) This is by design. */ + memmove(parentb->child + parent.idx + 1, parentb->child + parent.idx + 2, + sizeof *parentb->child * (parent.node->size - parent.idx - 1)); + /* This is the same pointer, but future-proof. */ + if(rm.height) free(PB_(as_branch)(rm.node)); else free(rm.node); + goto ascend; +merge_more: + assert(parent.idx < parent.node->size && parent.node->size + && rm.idx < rm.node->size && rm.node->size == TREE_MIN + && sibling.more->size == TREE_MIN + && rm.node->size + sibling.more->size <= TREE_MAX); /* Violated bulk? */ + /* Remove `rm`. */ + memmove(rm.node->key + rm.idx, rm.node->key + rm.idx + 1, + sizeof *rm.node->key * (rm.node->size - rm.idx - 1)); + /* Bring down key from `parent` to append to `rm`. */ + rm.node->key[rm.node->size - 1] = parent.node->key[parent.idx]; + /* Merge `more` into `rm`. */ + memcpy(rm.node->key + rm.node->size, sibling.more->key, + sizeof *sibling.more->key * sibling.more->size); +#ifdef TREE_VALUE + memmove(rm.node->value + rm.idx, rm.node->value + rm.idx + 1, + sizeof *rm.node->value * (rm.node->size - rm.idx - 1)); + rm.node->value[rm.node->size - 1] = parent.node->value[parent.idx]; + memcpy(rm.node->value + rm.node->size, sibling.more->value, + sizeof *sibling.more->value * sibling.more->size); +#endif + if(rm.height) { /* The `parent` links will have one less. */ + struct PB_(branch) *const rmb = PB_(as_branch)(rm.node), + *const moreb = PB_(as_branch)(sibling.more); + memcpy(rmb->child + rm.node->size, moreb->child, + sizeof *moreb->child * (sibling.more->size + 1)); + } + rm.node->size += sibling.more->size; + /* Remove references to `more` from `parent`. The parent will have one less + link than key (_ie_, an equal number.) This is by design. */ + memmove(parentb->child + parent.idx + 1, parentb->child + parent.idx + 2, + sizeof *parentb->child * (parent.node->size - parent.idx - 1)); + /* This is the same pointer, but future-proof. */ + if(rm.height) free(PB_(as_branch)(sibling.more)); else free(sibling.more); + goto ascend; +ascend: + /* Fix the hole by moving it up the tree. */ + rm = parent; + if(rm.node->size <= TREE_MIN) { + if(!(parent.node = PB_(as_branch)(rm.node)->child[TREE_MAX])) { + assert(tree->height == rm.height); + } else { + parent.height++; + } + } else { + parent.node = 0; + } + goto upward; +space: /* Node is root or has more than `TREE_MIN`; branches taken care of. */ + assert(rm.node); + assert(rm.idx < rm.node->size); + assert(rm.node->size > TREE_MIN || rm.node == tree->node); memmove(rm.node->key + rm.idx, rm.node->key + rm.idx + 1, sizeof *rm.node->key * (rm.node->size - rm.idx - 1)); #ifdef TREE_VALUE memmove(rm.node->value + rm.idx, rm.node->value + rm.idx + 1, sizeof *rm.node->value * (rm.node->size - rm.idx - 1)); #endif - rm.node->size--; + if(!--rm.node->size) { + assert(rm.node == tree->node); + if(tree->height) { + tree->node = PB_(as_branch)(rm.node)->child[0]; + tree->height--; + free(PB_(as_branch)(rm.node)); + } else { /* Just deleted the last one. Set flag for zero container. */ + tree->height = UINT_MAX; + } + } goto end; end: return 1; } - - - -/****************************/ +/** Tries to remove `key` from `tree`. @return Success, otherwise it was not in + `tree`. @order \Theta(\log |`tree`|) @allow */ +static int B_(tree_remove)(struct B_(tree) *const tree, + const PB_(key) key) { return !!tree && !!tree->root.node + && tree->root.height != UINT_MAX && PB_(remove)(&tree->root, key); } /* All these are used in clone; it's convenient to use `\O(\log size)` stack space. [existing branches][new branches][existing leaves][new leaves] no */ struct PB_(scaffold) { - struct tree_count victim, source; + struct tree_node_count victim, source; size_t no; struct PB_(node) **data; struct { struct PB_(node) **head, **fresh, **cursor; } branch, leaf; }; -static int PB_(count_r)(struct PB_(tree) tree, struct tree_count *const no) { +/** Counts the nodes `no` in `tree` for nodes>. */ +static int PB_(nodes_r)(struct PB_(tree) tree, + struct tree_node_count *const no) { assert(tree.node && tree.height); if(!++no->branches) return 0; if(tree.height == 1) { @@ -1107,18 +1403,19 @@ static int PB_(count_r)(struct PB_(tree) tree, struct tree_count *const no) { if(no->leaves + tree.node->size + 1 < no->leaves) return 0; no->leaves += tree.node->size + 1; } else { - unsigned char i; + unsigned i; for(i = 0; i <= tree.node->size; i++) { struct PB_(tree) child; - child.node = PB_(branch)(tree.node)->child[i]; + child.node = PB_(as_branch)(tree.node)->child[i]; child.height = tree.height - 1; - if(!PB_(count_r)(child, no)) return 0; + if(!PB_(nodes_r)(child, no)) return 0; } } return 1; } -static int PB_(count)(const struct B_(tree) *const tree, - struct tree_count *const no) { +/** Counts the nodes `no` in `tree`. */ +static int PB_(nodes)(const struct B_(tree) *const tree, + struct tree_node_count *const no) { assert(tree && no); no->branches = no->leaves = 0; if(!tree->root.node) { /* Idle. */ @@ -1126,13 +1423,14 @@ static int PB_(count)(const struct B_(tree) *const tree, no->leaves = 1; } else { /* Complex. */ struct PB_(tree) sub = tree->root; - if(!PB_(count_r)(sub, no)) return 0; + if(!PB_(nodes_r)(sub, no)) return 0; } return 1; } +/** `ref` with `sc` work under cannibalize>. */ static void PB_(cannibalize_r)(struct PB_(ref) ref, struct PB_(scaffold) *const sc) { - struct PB_(branch) *branch = PB_(branch)(ref.node); + struct PB_(branch) *branch = PB_(as_branch)(ref.node); const int keep_branch = sc->branch.cursor < sc->branch.fresh; assert(ref.node && ref.height && sc); if(keep_branch) *sc->branch.cursor = ref.node, sc->branch.cursor++; @@ -1146,7 +1444,7 @@ static void PB_(cannibalize_r)(struct PB_(ref) ref, } } else while(ref.idx <= ref.node->size) { struct PB_(ref) child; - child.node = PB_(branch)(ref.node)->child[ref.idx]; + child.node = PB_(as_branch)(ref.node)->child[ref.idx]; child.height = ref.height - 1; child.idx = 0; PB_(cannibalize_r)(child, sc); @@ -1154,6 +1452,7 @@ static void PB_(cannibalize_r)(struct PB_(ref) ref, } if(!keep_branch) free(branch); } +/** Disassemble `tree` and put in into `sc`. */ static void PB_(cannibalize)(const struct B_(tree) *const tree, struct PB_(scaffold) *const sc) { struct PB_(ref) ref; @@ -1170,18 +1469,20 @@ static void PB_(cannibalize)(const struct B_(tree) *const tree, *sc->leaf.cursor = ref.node; } } +/** Do the work of `src` cloned with `sc`. Called from clone>. */ static struct PB_(node) *PB_(clone_r)(struct PB_(tree) src, struct PB_(scaffold) *const sc) { struct PB_(node) *node; if(src.height) { - struct PB_(branch) *const cpyb = PB_(branch)(src.node), - *const branch = PB_(branch)(node = *sc->branch.cursor++); + struct PB_(branch) *const srcb = PB_(as_branch)(src.node), + *const branch = PB_(as_branch)(node = *sc->branch.cursor++); unsigned i; + struct PB_(tree) child; *node = *src.node; /* Copy node. */ - src.height--; + child.height = src.height - 1; for(i = 0; i <= src.node->size; i++) { /* Different links. */ - src.node = cpyb->child[i]; - branch->child[i] = PB_(clone_r)(src, sc); + child.node = srcb->child[i]; + branch->child[i] = PB_(clone_r)(child, sc); } } else { /* Leaves. */ node = *sc->leaf.cursor++; @@ -1189,6 +1490,7 @@ static struct PB_(node) *PB_(clone_r)(struct PB_(tree) src, } return node; } +/** `src` is copied with the cloning scaffold `sc`. */ static struct PB_(tree) PB_(clone)(const struct PB_(tree) *const src, struct PB_(scaffold) *const sc) { struct PB_(tree) sub; @@ -1208,7 +1510,8 @@ static struct PB_(tree) PB_(clone)(const struct PB_(tree) *const src, it continues to be. @return Success, otherwise `tree` is not modified. @throws[malloc] @throws[EDOM] `tree` is null. @throws[ERANGE] The size of - `source` doesn't fit into `size_t`. @allow */ + `source` nodes doesn't fit into `size_t`. + @order \O(|`source`| + |`tree`|) time and temporary space. @allow */ static int B_(tree_clone)(struct B_(tree) *const tree, const struct B_(tree) *const source) { struct PB_(scaffold) sc; @@ -1216,22 +1519,18 @@ static int B_(tree_clone)(struct B_(tree) *const tree, sc.data = 0; /* Need to keep this updated to catch. */ if(!tree) { errno = EDOM; goto catch; } /* Count the number of nodes and set up to copy. */ - if(!PB_(count)(tree, &sc.victim) || !PB_(count)(source, &sc.source) + if(!PB_(nodes)(tree, &sc.victim) || !PB_(nodes)(source, &sc.source) || (sc.no = sc.source.branches + sc.source.leaves) < sc.source.branches) { errno = ERANGE; goto catch; } /* Overflow. */ - printf("tree_clone: victim.branches %zu; victim.leaves %zu; " - "source.branches %zu; source.leaves %zu.\n", sc.victim.branches, - sc.victim.leaves, sc.source.branches, sc.source.leaves); if(!sc.no) { PB_(clear)(tree); goto finally; } /* No need to allocate. */ if(!(sc.data = malloc(sizeof *sc.data * sc.no))) { if(!errno) errno = ERANGE; goto catch; } - /* debug */ - { + { /* Makes debugging easier; not necessary. */ size_t i; for(i = 0; i < sc.no; i++) sc.data[i] = 0; } { /* Ready scaffold. */ - struct tree_count need; + struct tree_node_count need; need.leaves = sc.source.leaves > sc.victim.leaves ? sc.source.leaves - sc.victim.leaves : 0; need.branches = sc.source.branches > sc.victim.branches @@ -1272,7 +1571,7 @@ catch: free(leaf); } while(sc.branch.cursor != sc.branch.fresh) { - struct PB_(branch) *branch = PB_(branch)(*(--sc.branch.cursor)); + struct PB_(branch) *branch = PB_(as_branch)(*(--sc.branch.cursor)); assert(branch); free(branch); } @@ -1281,6 +1580,121 @@ finally: return success; } + +/* Box override information. */ +#define BOX_ PB_ +#define BOX struct B_(tree) + + +/** Adding, deleting, or changes in the topology of the tree invalidate it. */ +struct B_(tree_cursor); +struct B_(tree_cursor) { struct PB_(cursor) _; }; + + +/** @return Cursor before the first element of `tree`. Can be null. + @order \Theta(\log |`tree`|) @allow */ +static struct B_(tree_cursor) B_(tree_begin)(struct B_(tree) *const tree) + { struct B_(tree_cursor) cur; cur._ = PB_(begin)(tree); return cur; } +/** @param[tree] Can be null. @return Cursor in `tree` between elements, such + that if tree_next> is called, it will be smallest key that is not + smaller than `x`, or, tree_end> if `x` is greater than all in `tree`. + @order \Theta(\log |`tree`|) @allow */ +static struct B_(tree_cursor) B_(tree_begin_at)(struct B_(tree) *const tree, + const PB_(key) x) { + struct B_(tree_cursor) cur; + if(!tree) return cur._.root = 0, cur; + cur._.ref = PB_(lower)(tree->root, x); + cur._.root = &tree->root; + cur._.seen = 0; + return cur; +} +/** @return Cursor after the last element of `tree`. Can be null. + @order \Theta(\log |`tree`|) @allow */ +static struct B_(tree_cursor) B_(tree_end)(struct B_(tree) *const tree) + { struct B_(tree_cursor) cur; cur._ = PB_(end)(tree); return cur; } +/** Advances `cur` to the next element. @return A pointer to the current + element, or null if it ran out of elements. The type is either a set + pointer-to-key or a map tree_entry> (with `TREE_VALUE`, both fields + are null if null). @order \O(\log |`tree`|) @allow */ +static PB_(entry) B_(tree_next)(struct B_(tree_cursor) *const cur) + { return PB_(next)(&cur->_); } +/** Reverses `cur` to the previous element. @return A pointer to the previous + element, or null if it ran out of elements. The type is either a set + pointer-to-key or a map tree_entry> (with `TREE_VALUE`, both fields + are null if null). @order \O(\log |`tree`|) @allow */ +static PB_(entry) B_(tree_previous)(struct B_(tree_cursor) *const cur) + { return PB_(previous)(&cur->_); } + +#ifdef TREE_VALUE /* */ + enum { NONODE, ITERATING, END } where; + PB_(key) anchor; + enum tree_result ret; + memset(&anchor, 0, sizeof anchor); /* Silence warnings. */ + if(!cur || !cur->_.root) return TREE_ERROR; /* No tree. */ + if(cur->_.ref.node && cur->_.root->height != UINT_MAX) { + where = (cur->_.ref.idx < cur->_.ref.node->size) ? ITERATING : END; + } else { + where = NONODE; + } + if(where == ITERATING) anchor = cur->_.ref.node->key[cur->_.ref.idx]; + if(where == NONODE || where == END) cur->_.seen = 0; /* Should be already. */ +#ifdef TREE_VALUE + ret = PB_(update)(cur->_.root, key, 0, value); +#else + ret = PB_(update)(cur->_.root, key, 0); +#endif + if(ret == TREE_ERROR) return TREE_ERROR; + assert(cur->_.root->height != UINT_MAX); /* Can't be empty. */ + switch(where) { + case NONODE: cur->_.ref.node = 0; cur->_.seen = 0; break; + case ITERATING: cur->_.ref = PB_(lower)(*cur->_.root, anchor); break; + case END: + assert(cur->_.root->node); + cur->_.ref.node = cur->_.root->node; + cur->_.ref.height = cur->_.root->height; + cur->_.ref.idx = cur->_.root->node->size; + while(cur->_.ref.height) { + cur->_.ref.node + = PB_(as_branch_c)(cur->_.ref.node)->child[cur->_.ref.idx]; + cur->_.ref.idx = cur->_.ref.node->size; + cur->_.ref.height--; + } + cur->_.seen = 0; + break; + } + return ret; +#ifdef TREE_VALUE +} +#else +} +#endif + +/** Removes the last entry returned by a valid `cur`. All other cursors on the + same object are invalidated, but `cur` is now between on the removed node. + @return Success, otherwise `cur` is not at a valid element. + @order \Theta(\log |`tree`|) */ +static int B_(tree_cursor_remove)(struct B_(tree_cursor) *const cur) { + PB_(key) remove; + if(!cur || !cur->_.seen || !cur->_.root || !cur->_.ref.node + || cur->_.root->height == UINT_MAX + || cur->_.ref.idx >= cur->_.ref.node->size + || (remove = cur->_.ref.node->key[cur->_.ref.idx], + !PB_(remove)(cur->_.root, remove))) return 0; + /* tree_begin_at>. */ + cur->_.ref = PB_(lower)(*cur->_.root, remove); + cur->_.seen = 0; + return 1; +} + #ifdef TREE_TEST /* */ + static void PB_(unused_base_coda)(void); static void PB_(unused_base)(void) { - PB_(key) k; - memset(&k, 0, sizeof k); + PB_(key) k; PB_(value) v; memset(&k, 0, sizeof k); memset(&v, 0, sizeof v); PB_(is_element_c); PB_(forward); PB_(next_c); PB_(is_element); - B_(tree)(); B_(tree_)(0); B_(tree_iterator)(0); B_(tree_next)(0); - B_(tree_clear)(0); - B_(tree_lower_iterator)(0, k); B_(tree_lower_value)(0, k); + B_(tree)(); B_(tree_)(0); B_(tree_clear)(0); B_(tree_count)(0); + B_(tree_contains)(0, k); B_(tree_get_or)(0, k, v); B_(tree_at_or)(0, k, v); #ifdef TREE_VALUE - B_(tree_bulk_add)(0, k, 0); B_(tree_add)(0, k, 0); + B_(tree_bulk_add)(0, k, 0); B_(tree_try)(0, k, 0); + B_(tree_assign)(0, k, 0, 0); B_(tree_cursor_try)(0, k, 0); #else - B_(tree_bulk_add)(0, k); B_(tree_add)(0, k); + B_(tree_bulk_add)(0, k); B_(tree_try)(0, k); + B_(tree_assign)(0, k, 0); B_(tree_cursor_try)(0, k); #endif B_(tree_bulk_finish)(0); B_(tree_remove)(0, k); B_(tree_clone)(0, 0); + B_(tree_begin)(0); B_(tree_begin_at)(0, k); B_(tree_end)(0); + B_(tree_previous)(0); B_(tree_next)(0); + B_(tree_cursor_remove)(0); PB_(unused_base_coda)(); } static void PB_(unused_base_coda)(void) { PB_(unused_base)(); } -#elif defined(TREE_TO_STRING) /* base code --> */ +#undef TREE_DEFAULT_TRAIT #undef TREE_TO_STRING_TRAIT #undef TREE_TRAITS