almost make flight work

This commit is contained in:
Neil 2023-02-01 00:58:51 -08:00
parent 7923a9e0f6
commit 82852d94b0
10 changed files with 332 additions and 264 deletions

View File

@ -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-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/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/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/%: bin/%:
@echo "\033[1;36mlinking $@\033[0m" @echo "\033[1;36mlinking $@\033[0m"
@mkdir -p bin @mkdir -p bin
$(CC) $(OF) -o $@ $^ $(CC) $(OF) -o $@ $^
build/%.o: src/%.c src/%.h build/%.o: src/%.c #src/%.h
@echo "\033[0;36mcompile src $@\033[0m" @echo "\033[0;36mcompile src $@\033[0m"
@mkdir -p build @mkdir -p build
$(CC) $(CF) -c -o $@ $< $(CC) $(CF) -c -o $@ $<

50
src/driver_flighthours.c Normal file
View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
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 <<EOD\n"
"# date\tminutes\tcumulative\n");
it = journal_begin(&j), i = 0; while(journal_next(&it, &k, &v))
if(!scan(k, v->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;
}

View File

@ -22,41 +22,51 @@ static const char *flight_type_string[] = { FLIGHT_TYPE };
#undef X #undef X
#undef FLIGHT_TYPE #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]);*/ /*void kjvcite_to_string(const union kjvcite, char (*)[12]);*/
#else /* base --><!-- !base */ #else /* base --><!-- !base */
#undef OMIT_BASE #undef OMIT_BASE
#endif /* !base --> */ #endif /* !base --> */
#ifndef OMIT_VERSES /* <!-- verses: For external inclusion. */ #ifndef OMIT_INTERNAL /* <!-- external */
struct table_kjvset_bucket; struct tree_flight_node;
struct kjvset_table { struct tree_flight_tree { struct tree_flight_node *node; unsigned height; };
struct table_kjvset_bucket *buckets; struct flight_tree { struct tree_flight_tree root; };
uint32_t log_capacity, size, top; struct tree_flight_ref { struct tree_flight_node *node; unsigned height, idx; };
struct tree_flight_iterator {
struct tree_flight_tree *root; struct tree_flight_ref ref; int seen;
}; };
struct table_verse_bucket; struct flight_tree_iterator { struct tree_flight_iterator _; };
struct verse_table { #else /* external --><!-- internal */
struct table_verse_bucket *buckets; #undef OMIT_INTERNAL
uint32_t log_capacity, size, top; #endif /* internal --> */
};
#else /* verses --><!-- !verses */
#undef OMIT_VERSES
#endif /* !verses --> */
#ifndef OMIT_PROTO /* <!-- proto */ #ifndef OMIT_PROTO /* <!-- proto */
#include <stddef.h> #include <stddef.h>
struct flights { struct flights { struct flight_tree flights; };
struct kjvset_table set; struct flights flights(void);
struct verse_table verses; void flights_(struct flights *);
struct { size_t total, cumulative, set, verse; } words; int flights_is_valid(const struct flights *);
}; int flights_add(struct flights *, const struct flight);
struct kjv kjv(void); const char *flight_to_string(const struct flight *);
void kjv_(struct kjv *);
int kjv_is_valid(const struct kjv *const kjv);
int kjv_add(struct kjv *const kjv, const union kjvcite cite);
const char *kjv_to_string(const struct kjv *const kjv);
const char *kjv_set_to_string(const struct kjv *const kjv);
#else /* proto --><!-- !proto */ #else /* proto --><!-- !proto */
#undef OMIT_PROTO #undef OMIT_PROTO
#endif /* !proto --> */ #endif /* !proto --> */

View File

@ -1,6 +1,9 @@
#include <errno.h> #include <errno.h>
#include <stdint.h> #include <stdint.h>
/** `printf`-compatible substring. */
struct substring { const char *sub; int size; };
/** Parse unsigned; [`s`,`e`) => `n`. */ /** Parse unsigned; [`s`,`e`) => `n`. */
static int helper_natural(const char *s, const char *const e, uint32_t *const n) static int helper_natural(const char *s, const char *const e, uint32_t *const n)
{ {

View File

@ -2,13 +2,17 @@
#include <stddef.h> #include <stddef.h>
union load { const char *text; size_t offset; }; union load { const char *text; size_t offset; };
#include <stdint.h> /* C99 */ #include <stdint.h> /* 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. */ anonymous unions. */
union date32 { union date32 {
struct { uint32_t day : 5, month : 4, year : 23; }; /* C11, reverse */ struct { uint32_t day : 5, month : 4, year : 23; }; /* C11, reverse */
uint32_t u32; uint32_t u32;
}; };
void date32_to_string(const union date32 d, char (*const a)[12]); 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 --><!-- !base */ #else /* base --><!-- !base */
#undef OMIT_BASE #undef OMIT_BASE
#endif /* !base --> */ #endif /* !base --> */

View File

@ -244,12 +244,12 @@ struct journal_iterator journal_begin(struct journal *const j) {
return it; 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) { const union date32 x) {
struct journal_iterator it; struct journal_iterator it;
it._ = day_tree_begin_at(&j->days, x); it._ = day_tree_begin_at(&j->days, x);
return it; return it;
} }*/
int journal_next(struct journal_iterator *const it, int journal_next(struct journal_iterator *const it,
union date32 *const k, union load **v) { union date32 *const k, union load **v) {

4
src/scan_flight.h Normal file
View File

@ -0,0 +1,4 @@
#include "helper.h"
#include "flight.h"
int scan(union date32, const char *, struct flights *);

View File

@ -4,53 +4,24 @@
Scan journal entries for kjv references. */ Scan journal entries for kjv references. */
#include "../src/journal.h" #include "../src/journal.h"
#include "../src/helper.h" #include "../src/scan_flight.h"
#include <inttypes.h> /* C99 */ #include <inttypes.h> /* C99 */
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <limits.h> #include <limits.h>
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*/ /*!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) { struct flights *const f) {
const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *yyt3, *s0, *s1, *t0, *t1; const char *YYCURSOR, *YYMARKER, *yyt1, *yyt2, *yyt3, *s0, *s1, *t0, *t1;
enum flight_type type = Glider; enum flight_type type = Glider;
uint32_t chapter = 0, verse = 0, verse_end = 0;
enum YYCONDTYPE condition = yycline; enum YYCONDTYPE condition = yycline;
size_t line = 1; size_t line = 1;
char datestr[12] = {0}; char datestr[12] = {0};
const char *why = "unexpected"; const char *why = "unexpected";
assert(buffer && kj); assert(buffer && f);
YYCURSOR = YYMARKER = yyt1 = buffer; YYCURSOR = YYMARKER = yyt1 = buffer;
/*!re2c /**/ /*!re2c /**/
re2c:define:YYCTYPE = char; re2c:define:YYCTYPE = char;
@ -64,62 +35,25 @@ static int scan(union date32 date, const char *const buffer,
ws = [ \t]; ws = [ \t];
glyph = [^] \ ("\x00" | "\n" | unix_control | ws); glyph = [^] \ ("\x00" | "\n" | unix_control | ws);
natural = [1-9][0-9]*; natural = [1-9][0-9]*;
lookat = ws* natural ":" natural [ab]? ("-" natural [ab]?)? engage;
*/ */
for( ; ; ) { /*!re2c /**/ for( ; ; ) { /*!re2c /**/
/* Default ignore. */
<skip> [^\n\x00] { continue; } <skip> [^\n\x00] { continue; }
<skip> "\x00" { why = "no newline at end of file"; goto catch; } <skip> "\x00" { why = "no newline at end of file"; goto catch; }
<skip> "\n" => line { line++; continue; }
<line> "\x00" { return 1; } <line> "\x00" { return 1; }
<line> "\n" { line++; continue; } <line, skip> "\n" => line { line++; continue; }
<line> * :=> skip /* Guess it can't be simplified? */ <line> * :=> skip
/* Except these two. */
<line> "[glider]" :=> glider <line> "[glider]" :=> glider
<line> "[flight]" :=> flight <line> "[flight]" :=> flight
/* "M" - Motor Car Tow /* "M" - Motor Car Tow
"W" - Winch "W" - Winch
"A" - Aero Tow */ "A" - Aero Tow */
/* type; registration; launch -- landing; pic; sic; /* type; registration; launch -- landing; pic; sic;
single engine day dual; pilot; instrument simulated; actual; remarks */ single engine day dual; pilot; instrument simulated; actual; remarks */
<book> * { why = "default unrecognized"; goto catch; } <glider, flight> * { why = "default unrecognized"; goto catch; }
/* 19:15a, just ignore the a. */
<book> 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;
}
<book> "-" @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;
}
<book> 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;
}
*/ } */ }
assert(0); /* Never gets here. */ assert(0); /* Never gets here. */
catch: catch:
@ -129,43 +63,3 @@ catch:
"%s line %zu: %s.\n", buffer, datestr, line, why); "%s line %zu: %s.\n", buffer, datestr, line, why);
return 0; 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 <<EOD\n"
"# date\tverse\tset\tcumulative / %zu\n", kj.words.total);
it = journal_begin(&j), i = 0; while(journal_next(&it, &k, &v))
if(!scan(k, v->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;
}

View File

@ -46,12 +46,14 @@ static int scan(union date32 date, const char *const buffer,
third = ("III" | "3") " "?; third = ("III" | "3") " "?;
*/ */
for( ; ; ) { /*!re2c /**/ for( ; ; ) { /*!re2c /**/
/* Default ignore. */
<skip> [^\n\x00] { continue; } <skip> [^\n\x00] { continue; }
<skip> "\x00" { why = "no newline at end of file"; goto catch; } <skip> "\x00" { why = "no newline at end of file"; goto catch; }
<skip> "\n" => line { line++; continue; }
<line> "\x00" { return 1; } <line> "\x00" { return 1; }
<line> "\n" { line++; continue; } <line, skip> "\n" => line { line++; continue; }
<line> * :=> skip /* Guess it can't be simplified? */ <line> * :=> skip
/* Books. */
<line> "Genesis" / lookat => book { book = Genesis; continue; } <line> "Genesis" / lookat => book { book = Genesis; continue; }
<line> "Exodus" / lookat => book { book = Exodus; continue; } <line> "Exodus" / lookat => book { book = Exodus; continue; }
<line> "Leviticus" / lookat => book { book = Leviticus; continue; } <line> "Leviticus" / lookat => book { book = Leviticus; continue; }
@ -127,6 +129,8 @@ static int scan(union date32 date, const char *const buffer,
<line> third "John" / lookat => book { book = IIIJohn; continue; } <line> third "John" / lookat => book { book = IIIJohn; continue; }
<line> "Jude" / lookat => book { book = Jude; continue; } <line> "Jude" / lookat => book { book = Jude; continue; }
<line> "Revelation" / lookat => book { book = Revelation; continue; } <line> "Revelation" / lookat => book { book = Revelation; continue; }
/* Extract further information. */
<book> * { why = "default unrecognized"; goto catch; } <book> * { why = "default unrecognized"; goto catch; }
/* 19:15a, just ignore the a. */ /* 19:15a, just ignore the a. */
<book> ws+ @s0 natural @s1 ":" @t0 natural @t1 [ab]? { <book> ws+ @s0 natural @s1 ":" @t0 natural @t1 [ab]? {

View File

@ -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) * static const struct PB_(branch) *PB_(as_branch_c)(const struct PB_(node) *
const as_node) { return (const struct PB_(branch) *)(const void *) const as_node) { return (const struct PB_(branch) *)(const void *)
((const char *)as_node - offsetof(struct PB_(branch), base)); } ((const char *)as_node - offsetof(struct PB_(branch), base)); }
/* Address of a specific key by node. There is a need for node plus index /* Node plus height is a [sub]-tree. */
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 <tag:<B>tree> is a sub-tree of the tree.
fixme: 0-based height is problematic. UINT_MAX? No. */
struct PB_(tree) { struct PB_(node) *node; unsigned height; }; struct PB_(tree) { struct PB_(node) *node; unsigned height; };
/** To initialize it to an idle state, see <fn:<B>tree>, `{0}` (`C99`), or /** To initialize it to an idle state, see <fn:<B>tree>, `{0}` (`C99`), or
being `static`. being `static`.
@ -186,19 +182,21 @@ struct PB_(tree) { struct PB_(node) *node; unsigned height; };
struct B_(tree); struct B_(tree);
struct B_(tree) { struct PB_(tree) root; }; 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 /* <!-- value */ #ifdef TREE_VALUE /* <!-- value */
/** Gets the value of `ref`. */ /** Gets the value of `ref`. */
static PB_(value) *PB_(ref_to_valuep)(const struct PB_(ref) ref) static PB_(value) *PB_(ref_to_valuep)(const struct PB_(ref) ref)
{ return ref.node ? ref.node->value + ref.idx : 0; } { return ref.node ? ref.node->value + ref.idx : 0; }
#else /* value --><!-- !value */ #else /* value --><!-- !value */
typedef PB_(key) PB_(value); typedef PB_(key) PB_(value);
/** Gets the value of `ref`. */ /** Gets the value of `ref`. */
static PB_(value) *PB_(ref_to_valuep)(const struct PB_(ref) ref) static PB_(value) *PB_(ref_to_valuep)(const struct PB_(ref) ref)
{ return ref.node ? ref.node->key + ref.idx : 0; } { return ref.node ? ref.node->key + ref.idx : 0; }
#endif /* !value --> */ #endif /* !value --> */
struct PB_(iterator) { struct PB_(tree) *root; struct PB_(ref) ref; int seen; }; 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; it.ref.height = tree ? tree->root.height : 0;
if(tree && tree->root.height != UINT_MAX) if(tree && tree->root.height != UINT_MAX)
for(it.ref.node = tree->root.node; it.ref.height; 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; else it.ref.node = 0;
it.ref.idx = 0; it.ref.idx = 0;
it.seen = 0; it.seen = 0;
@ -315,109 +313,172 @@ predecessor:
return 1; return 1;
} }
/* Want to find slightly different things; code re-use is bad. Confusing. /** Finds `idx` of 'greatest lower-bound' (C++ parlance) minorant of `x` in
This is the lower-bound. */ `lo` only in one node at a time. */
#define TREE_FORTREE(i) i.node = tree->node, i.height = tree->height; ; \ static void PB_(node_lb)(struct PB_(ref) *const lo, const PB_(key) x) {
i.node = PB_(as_branch_c)(i.node)->child[i.idx], i.height-- unsigned hi = lo->node->size; lo->idx = 0;
#define TREE_START(i) unsigned hi = i.node->size; i.idx = 0; assert(lo && lo->node && hi);
#define TREE_FORNODE(i) do { \ do {
const unsigned m = (i.idx + hi) / 2; \ const unsigned mid = (lo->idx + hi) / 2; /* Will not overflow. */
if(B_(compare)(key, i.node->key[m]) > 0) i.idx = m + 1; \ if(B_(compare)(x, lo->node->key[mid]) > 0) lo->idx = mid + 1;
else hi = m; \ else hi = mid;
} while(i.idx < hi); } while(lo->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 lower-bound of `key` in non-empty `tree`, or, if `key` is greater /** Finds `idx` of 'least upper-bound' (C++ parlance) majorant of `x` in `hi`
than all `tree`, one off the end. */ only in one node at a time. */
static struct PB_(ref) PB_(lower_r)(struct PB_(tree) *const tree, static void PB_(node_ub)(struct PB_(ref) *const hi, const PB_(key) x) {
const PB_(key) key) { unsigned lo = 0;
struct PB_(ref) i, lo = { 0, 0, 0 }; assert(hi->node && hi->idx);
for(TREE_FORTREE(i)) { do {
TREE_START(i) 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 <fn:<PB>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; if(!hi) continue;
TREE_FORNODE(i) PB_(node_lb)(&lo, x);
if(i.idx < i.node->size) { if(lo.idx < lo.node->size) {
lo = i; found = lo;
if(TREE_FLIPPED(i)) break; /* Multi-keys go here. */ if(B_(compare)(x, lo.node->key[lo.idx]) > 0) break;
} }
if(!i.height) { if(!lo.height) break;
if(!lo.node) lo = i; /* Want one-off-end if last. */ }
return found;
}
/** Iterator version of <fn:<PB>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; break;
} if(!lo.height) { lo.node = 0; break; }
} }
return lo; return lo;
} }
/** @return Lower bound of `x` in `tree`. @order \O(\log |`tree`|) */ /** Finds lower-bound of key `x` in non-empty `tree` while counting the
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`. */ non-filled `hole` and `is_equal`. */
static struct PB_(ref) PB_(lookup_insert)(struct PB_(tree) *const tree, static struct PB_(ref) PB_(lookup_insert)(struct PB_(tree) tree,
const PB_(key) key, struct PB_(ref) *const hole, int *const is_equal) { const PB_(key) x, struct PB_(ref) *const hole, int *const is_equal) {
struct PB_(ref) lo; struct PB_(ref) lo;
hole->node = 0; hole->node = 0;
for(TREE_FORTREE(lo)) { for(lo.node = tree.node, lo.height = tree.height; ;
TREE_START(lo) 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 < TREE_MAX) *hole = lo;
if(!hi) continue; if(!hi) continue;
TREE_FORNODE(lo) PB_(node_lb)(&lo, x);
if(lo.node->size < TREE_MAX) hole->idx = lo.idx; 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; if(!lo.height) break;
} }
return lo; return lo;
} }
/** Finds exact `key` in non-empty `tree`. If `node` is found, temporarily, the /** Finds exact key `x` in non-empty `tree`. If `node` is found, temporarily,
nodes that have `TREE_MIN` keys have the nodes that have `TREE_MIN` keys have
`as_branch(node).child[TREE_MAX] = parent` or, for leaves, `leaf_parent`, `as_branch(node).child[TREE_MAX] = parent` or, for leaves, `leaf_parent`,
which must be set. (Patently terrible for running concurrently; hack, would be which must be set. (Patently terrible for running concurrently; hack, would be
nice to go down tree maybe.) */ nice to go down tree maybe.) */
static struct PB_(ref) PB_(lookup_remove)(struct PB_(tree) *const tree, static struct PB_(ref) PB_(lookup_remove)(struct PB_(tree) tree,
const PB_(key) key, struct PB_(node) **leaf_parent) { const PB_(key) x, struct PB_(node) **leaf_parent) {
struct PB_(node) *parent = 0; struct PB_(node) *parent = 0;
struct PB_(ref) lo; struct PB_(ref) lo;
for(TREE_FORTREE(lo)) { for(lo.node = tree.node, lo.height = tree.height; ;
TREE_START(lo) 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. */ /* Cannot delete bulk add. */
if(parent && hi < TREE_MIN || !parent && !hi) { lo.node = 0; break; } if(parent && hi < TREE_MIN || !parent && !hi) { lo.node = 0; break; }
if(hi <= TREE_MIN) { /* Remember the parent temporarily. */ if(hi <= TREE_MIN) { /* Remember the parent temporarily. */
if(lo.height) PB_(as_branch)(lo.node)->child[TREE_MAX] = parent; if(lo.height) PB_(as_branch)(lo.node)->child[TREE_MAX] = parent;
else *leaf_parent = parent; else *leaf_parent = parent;
} }
TREE_FORNODE(lo) PB_(node_lb)(&lo, x);
if(lo.idx < lo.node->size && TREE_FLIPPED(lo)) break; 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. */ if(!lo.height) { lo.node = 0; break; } /* Was not in. */
parent = lo.node; parent = lo.node;
} }
return lo; 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. /** Zeroed data (not all-bits-zero) is initialized. @return An idle tree.
@order \Theta(1) @allow */ @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; ? 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, static int B_(tree_contains)(const struct B_(tree) *const tree,
const PB_(key) x) { const PB_(key) x) { return tree && PB_(lookup_find)(tree->root, x).node; }
return tree && tree->root.node && tree->root.height != UINT_MAX
&& PB_(find)(&tree->root, x).node ? 1 : 0;
}
/* fixme: entry <B>tree_query -- there is no functionality that returns the /* fixme: entry <B>tree_query -- there is no functionality that returns the
key. */ 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) { const PB_(key) key, const PB_(value) default_value) {
struct PB_(ref) ref; struct PB_(ref) ref;
return tree && tree->root.node && tree->root.height != UINT_MAX 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; ? *PB_(ref_to_valuep)(ref) : default_value;
} }
/** For example, `tree = { 10 }`, `x = 5 -> 10`, `x = 10 -> 10`, /** For example, `tree = { 10 }`, `x = 5 -> default_value`, `x = 10 -> 10`,
`x = 11 -> null`. (There is no upper value.) `x = 11 -> 10`.
@return Lower-bound value match for `key` in `tree` or `default_value` if @return Value in `tree` less-then-or-equal to `x` or `default_value` if `x`
`key` is greater than all in `tree`. The map type is `TREE_VALUE` and the set is smaller than all in `tree`.
type is `TREE_KEY`. @order \O(\log |`tree`|) @allow */ @order \O(\log |`tree`|) @allow */
static PB_(value) B_(tree_at_or)(const struct B_(tree) *const tree, static PB_(value) B_(tree_left_or)(const struct B_(tree) *const tree,
const PB_(key) key, const PB_(value) default_value) { const PB_(key) x, const PB_(value) default_value) {
struct PB_(ref) ref; struct PB_(ref) ref;
return tree && (ref = PB_(lower)(tree->root, key)).node return tree && (ref = PB_(lookup_left)(tree->root, x)).node ?
&& ref.idx < ref.node->size ? *PB_(ref_to_valuep)(ref) : default_value; (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 /* <!-- map */ #ifdef TREE_VALUE /* <!-- map */
@ -746,7 +818,7 @@ empty: /* Reserved dynamic memory, but tree is empty. */
descend: /* Record last node that has space. */ descend: /* Record last node that has space. */
{ {
int is_equal = 0; int is_equal = 0;
add = PB_(lookup_insert)(root, key, &hole, &is_equal); add = PB_(lookup_insert)(*root, key, &hole, &is_equal);
if(is_equal) { if(is_equal) {
if(eject) { if(eject) {
*eject = add.node->key[add.idx]; *eject = add.node->key[add.idx];
@ -820,7 +892,7 @@ grow: /* Leaf is full. */ {
/* Descend now while split hasn't happened -- easier. */ /* Descend now while split hasn't happened -- easier. */
new_head = --iterator.height ? PB_(as_branch)(new_head)->child[0] : 0; new_head = --iterator.height ? PB_(as_branch)(new_head)->child[0] : 0;
iterator.node = PB_(as_branch)(iterator.node)->child[iterator.idx]; iterator.node = PB_(as_branch)(iterator.node)->child[iterator.idx];
PB_(find_idx)(&iterator, key); PB_(node_lb)(&iterator, key);
assert(!sibling->size && iterator.node->size == TREE_MAX); /* Atomic. */ assert(!sibling->size && iterator.node->size == TREE_MAX); /* Atomic. */
/* Expand `iterator`, which is full, to multiple nodes. */ /* Expand `iterator`, which is full, to multiple nodes. */
if(iterator.idx < TREE_SPLIT) { /* Descend hole to `iterator`. */ if(iterator.idx < TREE_SPLIT) { /* Descend hole to `iterator`. */
@ -972,7 +1044,7 @@ static int PB_(remove)(struct PB_(tree) *const tree, const PB_(key) x) {
assert(tree && tree->node && tree->height != UINT_MAX); assert(tree && tree->node && tree->height != UINT_MAX);
/* Traverse down the tree until `key`, leaving breadcrumbs for parents of /* Traverse down the tree until `key`, leaving breadcrumbs for parents of
minimum key nodes. */ minimum key nodes. */
if(!(rm = PB_(lookup_remove)(tree, x, &parent.node)).node) return 0; if(!(rm = PB_(lookup_remove)(*tree, x, &parent.node)).node) return 0;
/* Important when `rm = parent`; `find_idx` later. */ /* Important when `rm = parent`; `find_idx` later. */
parent.height = rm.height + 1; parent.height = rm.height + 1;
assert(rm.idx < rm.node->size); assert(rm.idx < rm.node->size);
@ -1046,7 +1118,7 @@ no_succ:
/* Retrieve forgotten information about the index in parent. (This is not /* 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 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.) */ to be in place, if a hack. We could go down, but new problems arise.) */
PB_(find_idx)(&parent, provisional_x); PB_(node_lb)(&parent, provisional_x);
parentb = PB_(as_branch)(parent.node); parentb = PB_(as_branch)(parent.node);
assert(parent.idx <= parent.node->size assert(parent.idx <= parent.node->size
&& parentb->child[parent.idx] == rm.node); && parentb->child[parent.idx] == rm.node);
@ -1467,19 +1539,31 @@ static struct B_(tree_iterator) B_(tree_begin)(struct B_(tree) *const tree)
@order \Theta(\log |`tree`|) @allow */ @order \Theta(\log |`tree`|) @allow */
static struct B_(tree_iterator) B_(tree_end)(struct B_(tree) *const tree) static struct B_(tree_iterator) B_(tree_end)(struct B_(tree) *const tree)
{ struct B_(tree_iterator) it; it._ = PB_(end)(tree); return it; } { struct B_(tree_iterator) it; it._ = PB_(end)(tree); return it; }
/** @return Cursor in `tree` between elements, such /** @return Cursor in `tree` such that <fn:<B>tree_previous> is the greatest
that if <fn:<B>tree_next> is called, it will be smallest key that is not key that is less-than-or-equal to `x`, or, <fn:<B>tree_begin> if `x` is less
smaller than `x`, or, <fn:<B>tree_end> if `x` is greater than all in `tree`. than all in `tree`. @order \Theta(\log |`tree`|) @allow */
@order \Theta(\log |`tree`|) @allow */ static struct B_(tree_iterator) B_(tree_left_previous)(struct B_(tree) *const
static struct B_(tree_iterator) B_(tree_begin_at)(struct B_(tree) *const tree, tree, const PB_(key) x) {
const PB_(key) x) {
struct B_(tree_iterator) cur; struct B_(tree_iterator) cur;
if(!tree) return cur._.root = 0, cur; if(!tree) return cur._.root = 0, cur;
cur._.ref = PB_(lower)(tree->root, x); cur._.ref = PB_(ref_left)(tree->root, x);
cur._.root = &tree->root; cur._.root = &tree->root;
cur._.seen = 0; cur._.seen = 0;
return cur; return cur;
} }
/** @return Cursor in `tree` such that <fn:<B>tree_next> is the least key that
is greater-than-or-equal to `x`, or, <fn:<B>tree_end> if `x` is greater than
all in `tree`. @order \Theta(\log |`tree`|) @allow */
static struct B_(tree_iterator) B_(tree_right_next)(struct B_(tree) *const
tree, const PB_(key) x) {
struct B_(tree_iterator) cur;
if(!tree) return cur._.root = 0, cur;
cur._.ref = PB_(ref_right)(tree->root, x);
cur._.root = &tree->root;
cur._.seen = 0;
return cur;
}
#ifdef TREE_VALUE /* <!-- map */ #ifdef TREE_VALUE /* <!-- map */
/** @return Whether advancing `it` to the next element and filling `k`, (and /** @return Whether advancing `it` to the next element and filling `k`, (and
`v` if a map, otherwise absent,) if not-null. `v` if a map, otherwise absent,) if not-null.
@ -1559,7 +1643,8 @@ static enum tree_result B_(tree_iterator_try)(struct B_(tree_iterator) *const
assert(it->_.root->height != UINT_MAX); /* Can't be empty. */ assert(it->_.root->height != UINT_MAX); /* Can't be empty. */
switch(where) { switch(where) {
case TREE_NONODE: it->_.ref.node = 0; it->_.seen = 0; break; case TREE_NONODE: it->_.ref.node = 0; it->_.seen = 0; break;
case TREE_ITERATING: it->_.ref = PB_(lower)(*it->_.root, anchor); break; case TREE_ITERATING:
it->_.ref = PB_(lookup_right)(*it->_.root, anchor); break;
case TREE_END: case TREE_END:
assert(it->_.root->node); assert(it->_.root->node);
it->_.ref.node = it->_.root->node; it->_.ref.node = it->_.root->node;
@ -1593,7 +1678,7 @@ static int B_(tree_iterator_remove)(struct B_(tree_iterator) *const it) {
|| (remove = it->_.ref.node->key[it->_.ref.idx], || (remove = it->_.ref.node->key[it->_.ref.idx],
!PB_(remove)(it->_.root, remove))) return 0; !PB_(remove)(it->_.root, remove))) return 0;
/* <fn:<B>tree_begin_at>. */ /* <fn:<B>tree_begin_at>. */
it->_.ref = PB_(lower)(*it->_.root, remove); it->_.ref = PB_(lookup_right)(*it->_.root, remove);
it->_.seen = 0; it->_.seen = 0;
return 1; return 1;
} }
@ -1602,7 +1687,8 @@ static void PB_(unused_base_coda)(void);
static void PB_(unused_base)(void) { static void PB_(unused_base)(void) {
PB_(key) k; PB_(value) v; memset(&k, 0, sizeof k); memset(&v, 0, sizeof v); PB_(key) k; PB_(value) v; memset(&k, 0, sizeof k); memset(&v, 0, sizeof v);
B_(tree)(); B_(tree_)(0); B_(tree_clear)(0); B_(tree_count)(0); 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); B_(tree_contains)(0, k); B_(tree_get_or)(0, k, v);
B_(tree_left_or)(0, k, v); B_(tree_right_or)(0, k, v);
#ifdef TREE_VALUE #ifdef TREE_VALUE
B_(tree_bulk_add)(0, k, 0); B_(tree_try)(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_iterator_try)(0, k, 0); B_(tree_assign)(0, k, 0, 0); B_(tree_iterator_try)(0, k, 0);
@ -1613,7 +1699,8 @@ static void PB_(unused_base)(void) {
B_(tree_next)(0, 0); B_(tree_previous)(0, 0); B_(tree_next)(0, 0); B_(tree_previous)(0, 0);
#endif #endif
B_(tree_bulk_finish)(0); B_(tree_remove)(0, k); B_(tree_clone)(0, 0); 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_begin)(0); B_(tree_end)(0);
B_(tree_left_previous)(0, k); B_(tree_right_next)(0, k);
B_(tree_iterator_remove)(0); B_(tree_iterator_remove)(0);
PB_(unused_base_coda)(); PB_(unused_base_coda)();
} }
@ -1685,25 +1772,37 @@ static PB_(value) B_D_(tree, get)(const struct B_(tree) *const tree,
const PB_(key) key) { const PB_(key) key) {
struct PB_(ref) ref; struct PB_(ref) ref;
return tree && tree->root.node && tree->root.height != UINT_MAX 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) : PB_D_(default, value); ? *PB_(ref_to_valuep)(ref) : PB_D_(default, value);
} }
/** This is functionally identical to <fn:<B>tree_at_or>, but a with a trait /** This is functionally identical to <fn:<B>tree_left_or>, but a with a trait
specifying a constant default value. specifying a constant default value.
@return The value associated with `key` in `tree`, (which can be null.) If @return The value associated with `key` in `tree`, (which can be null.) If
no such value exists, the `TREE_DEFAULT` is returned. no such value exists, the `TREE_DEFAULT` is returned.
@order \O(\log |`tree`|). @allow */ @order \O(\log |`tree`|). @allow */
static PB_(value) B_D_(tree, at)(const struct B_(tree) *const tree, static PB_(value) B_D_(tree, left)(const struct B_(tree) *const tree,
const PB_(key) key) { const PB_(key) key) {
struct PB_(ref) ref; struct PB_(ref) ref;
return tree && (ref = PB_(lower)(tree->root, key)).node return tree && (ref = PB_(lookup_left)(tree->root, key)).node ?
(assert(ref.idx < ref.node->size), *PB_(ref_to_valuep)(ref))
: PB_D_(default, value);
}
/** This is functionally identical to <fn:<B>tree_right_or>, but a with a trait
specifying a constant default value.
@return The value associated with `key` in `tree`, (which can be null.) If
no such value exists, the `TREE_DEFAULT` is returned.
@order \O(\log |`tree`|). @allow */
static PB_(value) B_D_(tree, right)(const struct B_(tree) *const tree,
const PB_(key) key) {
struct PB_(ref) ref;
return tree && (ref = PB_(lookup_right)(tree->root, key)).node
&& ref.idx < ref.node->size && ref.idx < ref.node->size
? *PB_(ref_to_valuep)(ref) : PB_D_(default, value); ? *PB_(ref_to_valuep)(ref) : PB_D_(default, value);
} }
static void PB_D_(unused, default_coda)(void); static void PB_D_(unused, default_coda)(void);
static void PB_D_(unused, default)(void) { static void PB_D_(unused, default)(void) {
PB_(key) k; memset(&k, 0, sizeof k); PB_(key) k; memset(&k, 0, sizeof k);
B_D_(tree, get)(0, k); B_D_(tree, at)(0, k); B_D_(tree, get)(0, k); B_D_(tree, left)(0, k); B_D_(tree, right)(0, k);
PB_D_(unused, default_coda)(); PB_D_(unused, default_coda)();
} }
static void PB_D_(unused, default_coda)(void) { PB_D_(unused, default)(); } static void PB_D_(unused, default_coda)(void) { PB_D_(unused, default)(); }