diff --git a/Makefile b/Makefile index a9d26c3..fa69778 100644 --- a/Makefile +++ b/Makefile @@ -39,14 +39,14 @@ bin/test-text: build/text.o build/test_text.o bin/test-kjv: build/text.o build/kjv.o build/test_kjv.o bin/test-journal: build/text.o build/journal.o build/test_journal.o bin/kjv: build/text.o build/journal.o build/kjv.o build/scan_kjv.o -bin/flight: build/text.o build/journal.o build/scan_flight.o +bin/flight: build/text.o build/journal.o build/scan_flight.o build/driver_flighthours.o bin/%: @echo "\033[1;36mlinking $@\033[0m" @mkdir -p bin $(CC) $(OF) -o $@ $^ -build/%.o: src/%.c src/%.h +build/%.o: src/%.c #src/%.h @echo "\033[0;36mcompile src $@\033[0m" @mkdir -p build $(CC) $(CF) -c -o $@ $< diff --git a/src/driver_flighthours.c b/src/driver_flighthours.c new file mode 100644 index 0000000..b794c70 --- /dev/null +++ b/src/driver_flighthours.c @@ -0,0 +1,50 @@ +/** @license 2023 Neil Edelman, distributed under the terms of the + [MIT License](https://opensource.org/licenses/MIT). + + Date _vs_ hours flown. */ + +#include "journal.h" +#include "scan_flight.h" +#include +#include +#include +#include + +int main(void) { + int success = EXIT_SUCCESS; + struct journal j; + struct journal_iterator it; + union date32 k; + union load *v; + size_t i; + j = journal(); + if(!journal_is_valid(&j)) goto catch; + fprintf(stderr, "Journal: %s.\n", journal_to_string(&j)); + printf("set term postscript eps enhanced\n" + "set output \"flighthours.eps\"\n" + "$Data <text, &kj)) goto catch; + printf("EOD\n" + "set monochrome\n" + "set xdata time\n" + "set timefmt \"%%Y-%%m-%%d\"\n" + "set xtics format \"%%Y-%%m-%%d\" rotate by -30\n" + "set ylabel \"words in KJV\"\n" + "set format y \"%%g%%%%\"\n" + "set key top left\n" + "set grid\n" + "unset border\n" + "#set style fill solid 0.1 #pattern 5 (better, but restarts)\n" + "plot $Data using 1:($3)*100/%zu with fillsteps lw 2 title \"set\", \\\n" + "$Data using 1:($4)*100/%zu with steps lw 1 title \"cumulative\"\n", + kj.words.total, kj.words.total); + goto finally; +catch: + success = EXIT_FAILURE; + perror("journal"); +finally: + journal_(&j); + return success; +} diff --git a/src/flight.h b/src/flight.h index 8f77f6d..534541f 100644 --- a/src/flight.h +++ b/src/flight.h @@ -22,41 +22,51 @@ static const char *flight_type_string[] = { FLIGHT_TYPE }; #undef X #undef FLIGHT_TYPE +struct glider { + struct substring type, reg, launch; + enum launch_type how; + unsigned height_ft, pilot_min, dual_min, instr_min; + struct substring remarks; +}; + +struct power { + struct substring type, reg, launch, landing, pilot, copilot; + unsigned dual_min, pilot_min, ifrsim_min, ifr_min; + struct substring remarks; +}; + +struct flight { + enum { GLIDER, POWER } type; + union { struct glider glider; struct power power; }; +}; + /*void kjvcite_to_string(const union kjvcite, char (*)[12]);*/ #else /* base --> */ -#ifndef OMIT_VERSES /* */ - +struct flight_tree_iterator { struct tree_flight_iterator _; }; +#else /* external --> */ #ifndef OMIT_PROTO /* */ diff --git a/src/helper.h b/src/helper.h index 6601e2d..fcce8ba 100644 --- a/src/helper.h +++ b/src/helper.h @@ -1,6 +1,9 @@ #include #include +/** `printf`-compatible substring. */ +struct substring { const char *sub; int size; }; + /** Parse unsigned; [`s`,`e`) => `n`. */ static int helper_natural(const char *s, const char *const e, uint32_t *const n) { diff --git a/src/journal.h b/src/journal.h index 9165eab..1af65cc 100644 --- a/src/journal.h +++ b/src/journal.h @@ -2,13 +2,17 @@ #include union load { const char *text; size_t offset; }; #include /* C99 */ -/** Assumes: reverse ordering of byte-fields; unsigned is defined; C11 and GNU +/** Assumes: reverse ordering of byte-fields; unsigned is defined; C11 or GNU anonymous unions. */ union date32 { struct { uint32_t day : 5, month : 4, year : 23; }; /* C11, reverse */ uint32_t u32; }; void date32_to_string(const union date32 d, char (*const a)[12]); +union line64 { + struct { union date32 date; uint32_t line; }; /* C11, endian? */ + uint64_t u64; /* C99 optional */ +}; #else /* base --> */ diff --git a/src/journal.re.c b/src/journal.re.c index 9cc9e6f..a725779 100644 --- a/src/journal.re.c +++ b/src/journal.re.c @@ -244,12 +244,12 @@ struct journal_iterator journal_begin(struct journal *const j) { return it; } -struct journal_iterator journal_begin_at(struct journal *const j, +/*struct journal_iterator journal_begin_at(struct journal *const j, const union date32 x) { struct journal_iterator it; it._ = day_tree_begin_at(&j->days, x); return it; -} +}*/ int journal_next(struct journal_iterator *const it, union date32 *const k, union load **v) { diff --git a/src/scan_flight.h b/src/scan_flight.h new file mode 100644 index 0000000..0dc01c2 --- /dev/null +++ b/src/scan_flight.h @@ -0,0 +1,4 @@ +#include "helper.h" +#include "flight.h" + +int scan(union date32, const char *, struct flights *); diff --git a/src/scan_flight.re.c b/src/scan_flight.re.c index a145b4b..1f00c34 100644 --- a/src/scan_flight.re.c +++ b/src/scan_flight.re.c @@ -4,53 +4,24 @@ Scan journal entries for kjv references. */ #include "../src/journal.h" -#include "../src/helper.h" +#include "../src/scan_flight.h" #include /* C99 */ #include #include #include #include -struct substring { const char *str; int len; }; - -/** type, reg, launch, how, height, landing, pilot_min, dual_min, instr_min, - remarks */ -struct glider { - struct substring type, reg, launch; - enum launch how; - unsigned height_ft, pilot_min, dual_min, instr_min; - struct substring remarks; -}; - -/** type, reg, launch -- landing, pilot, copilot, dual_min, pilot_min, - ifrsim_min, ifr_min, remarks */ -struct power { - struct substring type, reg, launch, landing, pilot, copilot; - unsigned dual_min, pilot_min, ifrsim_min, ifr_min; - struct substring remarks; -}; - -/** Could be a glider or power. */ -struct flight { - enum { GLIDER, POWER } type; - union { struct glider glider; struct power power; }; -}; - -struct flights { -}; - /*!conditions:re2c*/ -static int scan(union date32 date, const char *const buffer, +int scan(union date32 date, const char *const buffer, struct flights *const f) { const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *yyt3, *s0, *s1, *t0, *t1; enum flight_type type = Glider; - uint32_t chapter = 0, verse = 0, verse_end = 0; enum YYCONDTYPE condition = yycline; size_t line = 1; char datestr[12] = {0}; const char *why = "unexpected"; - assert(buffer && kj); + assert(buffer && f); YYCURSOR = YYMARKER = yyt1 = buffer; /*!re2c /**/ re2c:define:YYCTYPE = char; @@ -64,62 +35,25 @@ static int scan(union date32 date, const char *const buffer, ws = [ \t]; glyph = [^] \ ("\x00" | "\n" | unix_control | ws); natural = [1-9][0-9]*; - lookat = ws* natural ":" natural [ab]? ("-" natural [ab]?)? engage; */ for( ; ; ) { /*!re2c /**/ + /* Default ignore. */ [^\n\x00] { continue; } "\x00" { why = "no newline at end of file"; goto catch; } - "\n" => line { line++; continue; } "\x00" { return 1; } - "\n" { line++; continue; } - * :=> skip /* Guess it can't be simplified? */ + "\n" => line { line++; continue; } + * :=> skip + + /* Except these two. */ "[glider]" :=> glider "[flight]" :=> flight + /* "M" - Motor Car Tow "W" - Winch "A" - Aero Tow */ /* type; registration; launch -- landing; pic; sic; single engine day dual; pilot; instrument simulated; actual; remarks */ - * { why = "default unrecognized"; goto catch; } - /* 19:15a, just ignore the a. */ - ws+ @s0 natural @s1 ":" @t0 natural @t1 [ab]? { - if(chapter || verse || verse_end) - { why = "reference unrecognized"; goto catch; } - if(!helper_natural(s0, s1, &chapter) - || !helper_natural(t0, t1, &verse)) - { why = "reference numerical error"; goto catch; } - continue; - } - "-" @s0 natural @s1 [ab]? { /* Verse range. */ - if(!chapter || !verse || verse_end) - { why = "range unrecognized"; goto catch; } - if(!helper_natural(s0, s1, &verse_end)) - { why = "range numerical error"; goto catch; } - continue; - } - engage => skip { - char citestr[12]; - if(!chapter || !verse) { why = "missing information"; goto catch; } - if(verse_end && verse_end <= verse) - { why = "interval error"; goto catch; } - union kjvcite cite - = { .book = book, .chapter = chapter, .verse = verse }; - if(!datestr[0]) date32_to_string(date, &datestr); /* Only once. */ - kjvcite_to_string(cite, &citestr); - for( ; ; verse++, cite.verse++) { - if(!kjv_add(kj, cite)) { why = "add to set"; goto catch; } - if(!verse_end || verse_end <= verse) break; - } - printf("%s\t%zu\t%zu\t%zu\t# ", - datestr, kj->words.verse, kj->words.set, kj->words.cumulative); - if(verse_end) { - printf("%s-%" PRIu32 "\n", citestr, verse_end); - } else { - printf("%s\n", citestr); - } - book = Revelation, chapter = 0, verse = 0, verse_end = 0; - continue; - } + * { why = "default unrecognized"; goto catch; } */ } assert(0); /* Never gets here. */ catch: @@ -129,43 +63,3 @@ catch: "%s line %zu: %s.\n", buffer, datestr, line, why); return 0; } - -int main(void) { - int success = EXIT_SUCCESS; - struct journal j; - struct journal_iterator it; - struct kjv kj = kjv(); - union date32 k; - union load *v; - size_t i; - j = journal(); - if(!journal_is_valid(&j)) goto catch; - fprintf(stderr, "Journal: %s.\n", journal_to_string(&j)); - printf("set term postscript eps enhanced\n" - "set output \"kjv.eps\"\n" - "$Data <text, &kj)) goto catch; - printf("EOD\n" - "set monochrome\n" - "set xdata time\n" - "set timefmt \"%%Y-%%m-%%d\"\n" - "set xtics format \"%%Y-%%m-%%d\" rotate by -30\n" - "set ylabel \"words in KJV\"\n" - "set format y \"%%g%%%%\"\n" - "set key top left\n" - "set grid\n" - "unset border\n" - "#set style fill solid 0.1 #pattern 5 (better, but restarts)\n" - "plot $Data using 1:($3)*100/%zu with fillsteps lw 2 title \"set\", \\\n" - "$Data using 1:($4)*100/%zu with steps lw 1 title \"cumulative\"\n", - kj.words.total, kj.words.total); - goto finally; -catch: - success = EXIT_FAILURE; - perror("journal"); -finally: - journal_(&j); - return success; -} diff --git a/src/scan_kjv.re.c b/src/scan_kjv.re.c index a99ea77..d2ac5de 100644 --- a/src/scan_kjv.re.c +++ b/src/scan_kjv.re.c @@ -46,12 +46,14 @@ static int scan(union date32 date, const char *const buffer, third = ("III" | "3") " "?; */ for( ; ; ) { /*!re2c /**/ + /* Default ignore. */ [^\n\x00] { continue; } "\x00" { why = "no newline at end of file"; goto catch; } - "\n" => line { line++; continue; } "\x00" { return 1; } - "\n" { line++; continue; } - * :=> skip /* Guess it can't be simplified? */ + "\n" => line { line++; continue; } + * :=> skip + + /* Books. */ "Genesis" / lookat => book { book = Genesis; continue; } "Exodus" / lookat => book { book = Exodus; continue; } "Leviticus" / lookat => book { book = Leviticus; continue; } @@ -127,6 +129,8 @@ static int scan(union date32 date, const char *const buffer, third "John" / lookat => book { book = IIIJohn; continue; } "Jude" / lookat => book { book = Jude; continue; } "Revelation" / lookat => book { book = Revelation; continue; } + + /* Extract further information. */ * { why = "default unrecognized"; goto catch; } /* 19:15a, just ignore the a. */ ws+ @s0 natural @s1 ":" @t0 natural @t1 [ab]? { diff --git a/src/tree.h b/src/tree.h index f22b0e9..73f8eee 100644 --- a/src/tree.h +++ b/src/tree.h @@ -173,11 +173,7 @@ static struct PB_(branch) *PB_(as_branch)(struct PB_(node) *const as_leaf) static const struct PB_(branch) *PB_(as_branch_c)(const struct PB_(node) * const as_node) { return (const struct PB_(branch) *)(const void *) ((const char *)as_node - offsetof(struct PB_(branch), base)); } -/* Address of a specific key by node. There is a need for node plus index - without height, but we'll just let height be unused. */ -struct PB_(ref) { struct PB_(node) *node; unsigned height, idx; }; -/* Node plus height is a sub-tree. A tree> is a sub-tree of the tree. - fixme: 0-based height is problematic. UINT_MAX? No. */ +/* Node plus height is a [sub]-tree. */ struct PB_(tree) { struct PB_(node) *node; unsigned height; }; /** To initialize it to an idle state, see tree>, `{0}` (`C99`), or being `static`. @@ -186,19 +182,21 @@ struct PB_(tree) { struct PB_(node) *node; unsigned height; }; struct B_(tree); struct B_(tree) { struct PB_(tree) root; }; +/* Address of a specific key by node. Height might not be used, but there's too + many structs in this file anyway. */ +struct PB_(ref) { + struct PB_(node) *node; /* If null, others ignored. */ + unsigned height, idx; /* `idx < node.size` means valid. */ +}; #ifdef TREE_VALUE /* */ struct PB_(iterator) { struct PB_(tree) *root; struct PB_(ref) ref; int seen; }; @@ -209,7 +207,7 @@ static struct PB_(iterator) PB_(begin)(struct B_(tree) *const tree) { it.ref.height = tree ? tree->root.height : 0; if(tree && tree->root.height != UINT_MAX) 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.node = PB_(as_branch_c)(it.ref.node)->child[0], it.ref.height--); else it.ref.node = 0; it.ref.idx = 0; it.seen = 0; @@ -315,109 +313,172 @@ predecessor: return 1; } -/* 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_(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) do { \ - const unsigned m = (i.idx + hi) / 2; \ - if(B_(compare)(key, i.node->key[m]) > 0) i.idx = m + 1; \ - else hi = m; \ -} while(i.idx < hi); -#define TREE_FLIPPED(i) B_(compare)(i.node->key[i.idx], key) <= 0 -/** 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)) - if(!lo) return; - TREE_FORNODE((*lo)) +/** Finds `idx` of 'greatest lower-bound' (C++ parlance) minorant of `x` in + `lo` only in one node at a time. */ +static void PB_(node_lb)(struct PB_(ref) *const lo, const PB_(key) x) { + unsigned hi = lo->node->size; lo->idx = 0; + assert(lo && lo->node && hi); + do { + const unsigned mid = (lo->idx + hi) / 2; /* Will not overflow. */ + if(B_(compare)(x, lo->node->key[mid]) > 0) lo->idx = mid + 1; + else hi = mid; + } while(lo->idx < hi); } -/** 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) +/** Finds `idx` of 'least upper-bound' (C++ parlance) majorant of `x` in `hi` + only in one node at a time. */ +static void PB_(node_ub)(struct PB_(ref) *const hi, const PB_(key) x) { + unsigned lo = 0; + assert(hi->node && hi->idx); + do { + const unsigned mid = (lo + hi->idx) / 2; + if(B_(compare)(hi->node->key[mid], x) <= 0) lo = mid + 1; + else hi->idx = mid; + } while(lo < hi->idx); +} + +/** @return A reference to the element first element at or less than `x` in + `tree`, or `node` will be null if the `x` is less than all `tree`. */ +static struct PB_(ref) PB_(lookup_left)(const struct PB_(tree) tree, + const PB_(key) x) { + struct PB_(ref) hi, found; + found.node = 0; + if(!tree.node) return found; + for(hi.node = tree.node, hi.height = tree.height; ; + hi.node = PB_(as_branch_c)(hi.node)->child[hi.idx], hi.height--) { + if(!(hi.idx = hi.node->size)) continue; + PB_(node_ub)(&hi, x); + if(hi.idx) { /* Within bounds to record the current predecessor. */ + found = hi, found.idx--; + /* Equal. */ + if(B_(compare)(x, found.node->key[found.idx]) <= 0) break; + } + if(!hi.height) break; /* Reached the bottom. */ + } + return found; +} +/** Iterator version of lookup_left> of `x` in `tree` that goes + one-off the end. */ +static struct PB_(ref) PB_(ref_left)(const struct PB_(tree) tree, + const PB_(key) x) { + struct PB_(ref) hi, found; + found.node = 0; + if(!tree.node) return found; + for(hi.node = tree.node, hi.height = tree.height; ; + hi.node = PB_(as_branch_c)(hi.node)->child[hi.idx], hi.height--) { + if(!(hi.idx = hi.node->size)) continue; + PB_(node_ub)(&hi, x); + if(hi.idx < hi.node->size) { + found = hi; + if(hi.idx && B_(compare)(x, found.node->key[found.idx - 1]) <= 0) + break; + } + if(!hi.height) { if(!found.node) found = hi; break; } + } + return found; +} + +/** @return A reference the element at the greatest lower bound of `x` in + `tree`, or if the element doesn't exist, `node` will be null. */ +static struct PB_(ref) PB_(lookup_right)(const struct PB_(tree) tree, + const PB_(key) x) { + struct PB_(ref) lo, found; + found.node = 0; + if(!tree.node || tree.height == UINT_MAX) return found; + for(lo.node = tree.node, lo.height = tree.height; ; + lo.node = PB_(as_branch_c)(lo.node)->child[lo.idx], lo.height--) { + unsigned hi = lo.node->size; lo.idx = 0; if(!hi) continue; - TREE_FORNODE(i) - if(i.idx < i.node->size) { - lo = i; - if(TREE_FLIPPED(i)) break; /* Multi-keys go here. */ + PB_(node_lb)(&lo, x); + if(lo.idx < lo.node->size) { + found = lo; + if(B_(compare)(x, lo.node->key[lo.idx]) > 0) break; } - if(!i.height) { - if(!lo.node) lo = i; /* Want one-off-end if last. */ + if(!lo.height) break; + } + return found; +} +/** Iterator version of lookup_right> of `x` in `tree` that goes + one-off the end. */ +static struct PB_(ref) PB_(ref_right)(const struct PB_(tree) tree, + const PB_(key) x) { + struct PB_(ref) lo, found; + found.node = 0; + if(!tree.node || tree.height == UINT_MAX) return found; + for(lo.node = tree.node, lo.height = tree.height; ; + lo.node = PB_(as_branch_c)(lo.node)->child[lo.idx], lo.height--) { + unsigned hi = lo.node->size; lo.idx = 0; + if(!hi) continue; + PB_(node_lb)(&lo, x); + if(lo.idx < lo.node->size) { + found = lo; + if(B_(compare)(x, lo.node->key[lo.idx]) > 0) break; + } + if(!lo.height) { if(!found.node) found = lo; break; } + } + return found; +} +/** Finds an exact key `x` in non-empty `tree`. */ +static struct PB_(ref) PB_(lookup_find)(const struct PB_(tree) tree, + const PB_(key) x) { + struct PB_(ref) lo; + if(!tree.node || tree.height == UINT_MAX) return lo.node = 0, lo; + for(lo.node = tree.node, lo.height = tree.height; ; + lo.node = PB_(as_branch_c)(lo.node)->child[lo.idx], lo.height--) { + unsigned hi = lo.node->size; lo.idx = 0; + if(!hi) continue; + PB_(node_lb)(&lo, x); + /* Absolutely will not equivalent `x > lo`, investigate? */ + if(lo.idx < lo.node->size && B_(compare)(lo.node->key[lo.idx], x) <= 0) break; - } + if(!lo.height) { lo.node = 0; break; } } return lo; } -/** @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 +/** Finds lower-bound of key `x` 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) { +static struct PB_(ref) PB_(lookup_insert)(struct PB_(tree) tree, + const PB_(key) x, struct PB_(ref) *const hole, int *const is_equal) { struct PB_(ref) lo; hole->node = 0; - for(TREE_FORTREE(lo)) { - TREE_START(lo) + for(lo.node = tree.node, lo.height = tree.height; ; + lo.node = PB_(as_branch_c)(lo.node)->child[lo.idx], lo.height--) { + unsigned hi = lo.node->size; lo.idx = 0; if(hi < TREE_MAX) *hole = lo; if(!hi) continue; - TREE_FORNODE(lo) + PB_(node_lb)(&lo, x); 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.idx < lo.node->size && B_(compare)(lo.node->key[lo.idx], x) <= 0) + { *is_equal = 1; break; } if(!lo.height) break; } return lo; } -/** Finds exact `key` in non-empty `tree`. If `node` is found, temporarily, the - nodes that have `TREE_MIN` keys have +/** Finds exact key `x` 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_(node) **leaf_parent) { +static struct PB_(ref) PB_(lookup_remove)(struct PB_(tree) tree, + const PB_(key) x, struct PB_(node) **leaf_parent) { struct PB_(node) *parent = 0; struct PB_(ref) lo; - for(TREE_FORTREE(lo)) { - TREE_START(lo) + for(lo.node = tree.node, lo.height = tree.height; ; + lo.node = PB_(as_branch_c)(lo.node)->child[lo.idx], lo.height--) { + unsigned hi = lo.node->size; lo.idx = 0; /* 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; + PB_(node_lb)(&lo, x); + if(lo.idx < lo.node->size && B_(compare)(lo.node->key[lo.idx], x) <= 0) + break; if(!lo.height) { lo.node = 0; break; } /* Was not in. */ parent = lo.node; } return lo; } -#undef TREE_FORTREE -#undef TREE_START -#undef TREE_FORNODE -#undef TREE_FLIPPED /** Zeroed data (not all-bits-zero) is initialized. @return An idle tree. @order \Theta(1) @allow */ @@ -497,12 +558,10 @@ static size_t B_(tree_count)(const struct B_(tree) *const tree) { ? PB_(count_r)(tree->root) : 0; } -/** @return Is `x` in `tree`? @order \O(\log |`tree`|) @allow */ +/** @return Is `x` in `tree` (which can be null)? + @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; -} + const PB_(key) x) { return tree && PB_(lookup_find)(tree->root, x).node; } /* fixme: entry tree_query -- there is no functionality that returns the key. */ @@ -513,20 +572,33 @@ 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 + && (ref = PB_(lookup_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`. (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) { +/** For example, `tree = { 10 }`, `x = 5 -> default_value`, `x = 10 -> 10`, + `x = 11 -> 10`. + @return Value in `tree` less-then-or-equal to `x` or `default_value` if `x` + is smaller than all in `tree`. + @order \O(\log |`tree`|) @allow */ +static PB_(value) B_(tree_left_or)(const struct B_(tree) *const tree, + const PB_(key) x, 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; + return tree && (ref = PB_(lookup_left)(tree->root, x)).node ? + (assert(ref.idx < ref.node->size), *PB_(ref_to_valuep)(ref)) + : default_value; +} + +/** For example, `tree = { 10 }`, `x = 5 -> 10`, `x = 10 -> 10`, + `x = 11 -> default_value`. + @return Value in `tree` greater-than-or-equal to `x` or `default_value` if `x` + is greater than all in `tree`. + @order \O(\log |`tree`|) @allow */ +static PB_(value) B_(tree_right_or)(const struct B_(tree) *const tree, + const PB_(key) x, const PB_(value) default_value) { + struct PB_(ref) ref; + return tree && (ref = PB_(lookup_right)(tree->root, x)).node + ? *PB_(ref_to_valuep)(ref) : default_value; } #ifdef TREE_VALUE /*