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-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 $@ $<

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 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 --><!-- !base */
#undef OMIT_BASE
#endif /* !base --> */
#ifndef OMIT_VERSES /* <!-- verses: For external inclusion. */
struct table_kjvset_bucket;
struct kjvset_table {
struct table_kjvset_bucket *buckets;
uint32_t log_capacity, size, top;
#ifndef OMIT_INTERNAL /* <!-- external */
struct tree_flight_node;
struct tree_flight_tree { struct tree_flight_node *node; unsigned height; };
struct flight_tree { struct tree_flight_tree root; };
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 verse_table {
struct table_verse_bucket *buckets;
uint32_t log_capacity, size, top;
};
#else /* verses --><!-- !verses */
#undef OMIT_VERSES
#endif /* !verses --> */
struct flight_tree_iterator { struct tree_flight_iterator _; };
#else /* external --><!-- internal */
#undef OMIT_INTERNAL
#endif /* internal --> */
#ifndef OMIT_PROTO /* <!-- proto */
#include <stddef.h>
struct flights {
struct kjvset_table set;
struct verse_table verses;
struct { size_t total, cumulative, set, verse; } words;
};
struct kjv kjv(void);
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);
struct flights { struct flight_tree flights; };
struct flights flights(void);
void flights_(struct flights *);
int flights_is_valid(const struct flights *);
int flights_add(struct flights *, const struct flight);
const char *flight_to_string(const struct flight *);
#else /* proto --><!-- !proto */
#undef OMIT_PROTO
#endif /* !proto --> */

View File

@ -1,6 +1,9 @@
#include <errno.h>
#include <stdint.h>
/** `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)
{

View File

@ -2,13 +2,17 @@
#include <stddef.h>
union load { const char *text; size_t offset; };
#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. */
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 --><!-- !base */
#undef OMIT_BASE
#endif /* !base --> */

View File

@ -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) {

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. */
#include "../src/journal.h"
#include "../src/helper.h"
#include "../src/scan_flight.h"
#include <inttypes.h> /* C99 */
#include <stdlib.h>
#include <stdio.h>
#include <assert.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*/
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. */
<skip> [^\n\x00] { continue; }
<skip> "\x00" { why = "no newline at end of file"; goto catch; }
<skip> "\n" => line { line++; continue; }
<line> "\x00" { return 1; }
<line> "\n" { line++; continue; }
<line> * :=> skip /* Guess it can't be simplified? */
<line, skip> "\n" => line { line++; continue; }
<line> * :=> skip
/* Except these two. */
<line> "[glider]" :=> glider
<line> "[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 */
<book> * { 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;
}
<glider, flight> * { 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 <<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") " "?;
*/
for( ; ; ) { /*!re2c /**/
/* Default ignore. */
<skip> [^\n\x00] { continue; }
<skip> "\x00" { why = "no newline at end of file"; goto catch; }
<skip> "\n" => line { line++; continue; }
<line> "\x00" { return 1; }
<line> "\n" { line++; continue; }
<line> * :=> skip /* Guess it can't be simplified? */
<line, skip> "\n" => line { line++; continue; }
<line> * :=> skip
/* Books. */
<line> "Genesis" / lookat => book { book = Genesis; continue; }
<line> "Exodus" / lookat => book { book = Exodus; 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> "Jude" / lookat => book { book = Jude; continue; }
<line> "Revelation" / lookat => book { book = Revelation; continue; }
/* Extract further information. */
<book> * { why = "default unrecognized"; goto catch; }
/* 19:15a, just ignore the a. */
<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) *
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 <tag:<B>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 <fn:<B>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 /* <!-- value */
/** Gets the value of `ref`. */
static PB_(value) *PB_(ref_to_valuep)(const struct PB_(ref) ref)
{ return ref.node ? ref.node->value + ref.idx : 0; }
#else /* value --><!-- !value */
typedef PB_(key) PB_(value);
/** Gets the value of `ref`. */
static PB_(value) *PB_(ref_to_valuep)(const struct PB_(ref) ref)
{ return ref.node ? ref.node->key + ref.idx : 0; }
#endif /* !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 <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;
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 <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;
}
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 <B>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 /* <!-- map */
@ -746,7 +818,7 @@ empty: /* Reserved dynamic memory, but tree is empty. */
descend: /* Record last node that has space. */
{
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(eject) {
*eject = add.node->key[add.idx];
@ -820,7 +892,7 @@ grow: /* Leaf is full. */ {
/* Descend now while split hasn't happened -- easier. */
new_head = --iterator.height ? PB_(as_branch)(new_head)->child[0] : 0;
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. */
/* Expand `iterator`, which is full, to multiple nodes. */
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);
/* 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;
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);
@ -1046,7 +1118,7 @@ no_succ:
/* 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);
PB_(node_lb)(&parent, provisional_x);
parentb = PB_(as_branch)(parent.node);
assert(parent.idx <= parent.node->size
&& 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 */
static struct B_(tree_iterator) B_(tree_end)(struct B_(tree) *const tree)
{ struct B_(tree_iterator) it; it._ = PB_(end)(tree); return it; }
/** @return Cursor in `tree` between elements, such
that if <fn:<B>tree_next> is called, it will be smallest key that is not
smaller than `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_begin_at)(struct B_(tree) *const tree,
const PB_(key) x) {
/** @return Cursor in `tree` such that <fn:<B>tree_previous> is the greatest
key that is less-than-or-equal to `x`, or, <fn:<B>tree_begin> if `x` is less
than all in `tree`. @order \Theta(\log |`tree`|) @allow */
static struct B_(tree_iterator) B_(tree_left_previous)(struct B_(tree) *const
tree, const PB_(key) x) {
struct B_(tree_iterator) 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._.seen = 0;
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 */
/** @return Whether advancing `it` to the next element and filling `k`, (and
`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. */
switch(where) {
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:
assert(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],
!PB_(remove)(it->_.root, remove))) return 0;
/* <fn:<B>tree_begin_at>. */
it->_.ref = PB_(lower)(*it->_.root, remove);
it->_.ref = PB_(lookup_right)(*it->_.root, remove);
it->_.seen = 0;
return 1;
}
@ -1602,7 +1687,8 @@ static void PB_(unused_base_coda)(void);
static void PB_(unused_base)(void) {
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_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
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);
@ -1613,7 +1699,8 @@ static void PB_(unused_base)(void) {
B_(tree_next)(0, 0); B_(tree_previous)(0, 0);
#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_begin)(0); B_(tree_end)(0);
B_(tree_left_previous)(0, k); B_(tree_right_next)(0, k);
B_(tree_iterator_remove)(0);
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) {
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) : 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.
@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, at)(const struct B_(tree) *const tree,
static PB_(value) B_D_(tree, left)(const struct B_(tree) *const tree,
const PB_(key) key) {
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
? *PB_(ref_to_valuep)(ref) : PB_D_(default, value);
}
static void PB_D_(unused, default_coda)(void);
static void PB_D_(unused, default)(void) {
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)();
}
static void PB_D_(unused, default_coda)(void) { PB_D_(unused, default)(); }