Added tree. This is much nicer.
This commit is contained in:
parent
fd1a6d4745
commit
68d2412d65
324
src/array.h
324
src/array.h
|
@ -1,11 +1,12 @@
|
||||||
/** @license 2016 Neil Edelman, distributed under the terms of the
|
/** @license 2016 Neil Edelman, distributed under the terms of the
|
||||||
[MIT License](https://opensource.org/licenses/MIT).
|
[MIT License](https://opensource.org/licenses/MIT).
|
||||||
|
|
||||||
@abstract Source <src/array.h>; examples <test/test_array.c>.
|
@abstract Stand-alone header <src/array.h>; examples <test/test_array.c>; on a
|
||||||
|
compatible workstation, `make` creates the test suite of the examples.
|
||||||
|
|
||||||
@subtitle Contiguous dynamic array
|
@subtitle Contiguous dynamic array
|
||||||
|
|
||||||
![Example of array.](../web/array.png)
|
![Example of array.](../doc/array.png)
|
||||||
|
|
||||||
<tag:<A>array> is a dynamic array that stores contiguous <typedef:<PA>type>.
|
<tag:<A>array> is a dynamic array that stores contiguous <typedef:<PA>type>.
|
||||||
Resizing may be necessary when increasing the size of the array; this incurs
|
Resizing may be necessary when increasing the size of the array; this incurs
|
||||||
|
@ -16,26 +17,18 @@
|
||||||
<typedef:<PA>type>, associated therewith; required. `<PA>` is private, whose
|
<typedef:<PA>type>, associated therewith; required. `<PA>` is private, whose
|
||||||
names are prefixed in a manner to avoid collisions.
|
names are prefixed in a manner to avoid collisions.
|
||||||
|
|
||||||
@param[ARRAY_CODA]
|
|
||||||
Include more functions contained in <src/array_coda.h>, where `<AC>` is
|
|
||||||
`<A>array`.
|
|
||||||
|
|
||||||
@param[ARRAY_MIN_CAPACITY]
|
|
||||||
Default is 3; optional number in `[2, SIZE_MAX]` that the capacity can not go
|
|
||||||
below.
|
|
||||||
|
|
||||||
@param[ARRAY_EXPECT_TRAIT]
|
@param[ARRAY_EXPECT_TRAIT]
|
||||||
Do not un-define certain variables for subsequent inclusion in a parameterized
|
Do not un-define certain variables for subsequent inclusion in a parameterized
|
||||||
trait.
|
trait.
|
||||||
|
|
||||||
@param[ARRAY_COMPARE_NAME, ARRAY_COMPARE, ARRAY_IS_EQUAL]
|
@param[ARRAY_COMPARE_NAME, ARRAY_COMPARE, ARRAY_IS_EQUAL]
|
||||||
Compare trait contained in <src/array_coda.h>. An optional mangled name for
|
Compare trait contained in <src/compare.h>. An optional mangled name for
|
||||||
uniqueness and a function implementing either <typedef:<PAC>compare_fn> or
|
uniqueness and a function implementing either <typedef:<PCMP>compare_fn> or
|
||||||
<typedef:<PAC>bipredicate_fn>.
|
<typedef:<PCMP>bipredicate_fn>.
|
||||||
|
|
||||||
@param[ARRAY_TO_STRING_NAME, ARRAY_TO_STRING]
|
@param[ARRAY_TO_STRING_NAME, ARRAY_TO_STRING]
|
||||||
To string trait contained in <src/to_string.h>. An optional mangled name for
|
To string trait contained in <src/to_string.h>. An optional mangled name for
|
||||||
uniqueness and function implementing <typedef:<PSZ>to_string_fn>.
|
uniqueness and function implementing <typedef:<PSTR>to_string_fn>.
|
||||||
|
|
||||||
@std C89 */
|
@std C89 */
|
||||||
|
|
||||||
|
@ -57,6 +50,9 @@
|
||||||
#if ARRAY_TRAITS > 1
|
#if ARRAY_TRAITS > 1
|
||||||
#error Only one trait per include is allowed; use ARRAY_EXPECT_TRAIT.
|
#error Only one trait per include is allowed; use ARRAY_EXPECT_TRAIT.
|
||||||
#endif
|
#endif
|
||||||
|
#if ARRAY_TRAITS && !defined(BOX)
|
||||||
|
#error Trying to define a trait without defining the base datatype.
|
||||||
|
#endif
|
||||||
#if defined(ARRAY_TO_STRING_NAME) && !defined(ARRAY_TO_STRING)
|
#if defined(ARRAY_TO_STRING_NAME) && !defined(ARRAY_TO_STRING)
|
||||||
#error ARRAY_TO_STRING_NAME requires ARRAY_TO_STRING.
|
#error ARRAY_TO_STRING_NAME requires ARRAY_TO_STRING.
|
||||||
#endif
|
#endif
|
||||||
|
@ -71,8 +67,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#if defined(ARRAY_CAT_) || defined(ARRAY_CAT) || defined(A_) || defined(PA_) \
|
#if defined(ARRAY_CAT_) || defined(ARRAY_CAT) || defined(A_) || defined(PA_)
|
||||||
|| defined(ARRAY_IDLE)
|
|
||||||
#error Unexpected defines.
|
#error Unexpected defines.
|
||||||
#endif
|
#endif
|
||||||
/* <Kernighan and Ritchie, 1988, p. 231>. */
|
/* <Kernighan and Ritchie, 1988, p. 231>. */
|
||||||
|
@ -80,9 +75,14 @@
|
||||||
#define ARRAY_CAT(n, m) ARRAY_CAT_(n, m)
|
#define ARRAY_CAT(n, m) ARRAY_CAT_(n, m)
|
||||||
#define A_(n) ARRAY_CAT(ARRAY_NAME, n)
|
#define A_(n) ARRAY_CAT(ARRAY_NAME, n)
|
||||||
#define PA_(n) ARRAY_CAT(array, A_(n))
|
#define PA_(n) ARRAY_CAT(array, A_(n))
|
||||||
#define ARRAY_IDLE { 0, 0, 0 }
|
|
||||||
#endif /* idempotent --> */
|
#endif /* idempotent --> */
|
||||||
|
|
||||||
|
#if !defined(restrict) && (!defined(__STDC__) || !defined(__STDC_VERSION__) \
|
||||||
|
|| __STDC_VERSION__ < 199901L)
|
||||||
|
#define ARRAY_RESTRICT /* Undo this at the end. */
|
||||||
|
#define restrict /* Attribute only in C99+. */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if ARRAY_TRAITS == 0 /* <!-- base code */
|
#if ARRAY_TRAITS == 0 /* <!-- base code */
|
||||||
|
|
||||||
|
@ -93,24 +93,152 @@
|
||||||
|
|
||||||
/** A valid tag type set by `ARRAY_TYPE`. */
|
/** A valid tag type set by `ARRAY_TYPE`. */
|
||||||
typedef ARRAY_TYPE PA_(type);
|
typedef ARRAY_TYPE PA_(type);
|
||||||
|
typedef const ARRAY_TYPE PA_(type_c);
|
||||||
|
|
||||||
/** Manages the array field `data` which has `size` elements. The space is
|
/** Manages the array field `data` which has `size` elements. The space is
|
||||||
indexed up to `capacity`, which is at least `size`. To initialize it to an
|
indexed up to `capacity`, which is at least `size`. The fields should be
|
||||||
idle state, see <fn:<A>array>, `ARRAY_IDLE`, `{0}` (`C99`,) or being `static`.
|
treated as read-only; any modification is liable to cause the array to go into
|
||||||
The fields should be treated as read-only; any modification is liable to cause
|
an invalid state.
|
||||||
the array to go into an invalid state.
|
|
||||||
|
|
||||||
![States.](../web/states.png) */
|
![States.](../doc/states.png) */
|
||||||
struct A_(array) { PA_(type) *data; size_t size, capacity; };
|
struct A_(array) { PA_(type) *data; size_t size, capacity; };
|
||||||
/* !data -> !size, data -> capacity >= min && size <= capacity <= max */
|
/* !data -> !size, data -> capacity >= min && size <= capacity <= max */
|
||||||
|
|
||||||
/** Initialises `a` to idle. @order \Theta(1) @allow */
|
#define BOX_CONTENT PA_(type_c) *
|
||||||
static void A_(array)(struct A_(array) *const a)
|
/** Is `x` not null? @implements `is_element_c` */
|
||||||
{ assert(a), a->data = 0, a->capacity = a->size = 0; }
|
static int PA_(is_element_c)(PA_(type_c) *const x) { return !!x; }
|
||||||
|
/* Enumerate the contents (`input_or_output_const_iterator`.)
|
||||||
|
@implements `forward` */
|
||||||
|
struct PA_(forward) { const struct A_(array) *a; size_t next; };
|
||||||
|
/** @return Before `a`. @implements `forward_begin` */
|
||||||
|
static struct PA_(forward) PA_(forward_begin)(const struct A_(array) *const a) {
|
||||||
|
struct PA_(forward) it; it.a = a, it.next = 0; return it; }
|
||||||
|
/** Move to next `it`. @return Element or null. @implements `forward_next` */
|
||||||
|
static PA_(type_c) *PA_(forward_next)(struct PA_(forward) *const it)
|
||||||
|
{ return assert(it), it->a && it->next < it->a->size
|
||||||
|
? it->a->data + it->next++ : 0; }
|
||||||
|
|
||||||
/** Destroys `a` and returns it to idle. @allow */
|
#define BOX_ITERATOR PA_(type) *
|
||||||
|
/** Is `x` not null? @implements `is_element` */
|
||||||
|
static int PA_(is_element)(const PA_(type) *const x) { return !!x; }
|
||||||
|
/* More complex iterator that supports bi-directional movement and write. The
|
||||||
|
cursor is half way between elements, `cur = cursor - 0.5`, pointing left
|
||||||
|
(`dir` false) or right (`dir` true). @implements `iterator` */
|
||||||
|
struct PA_(iterator) { struct A_(array) *a; size_t cur; int dir; };
|
||||||
|
/** @return Before `a`. @implements `begin` */
|
||||||
|
static struct PA_(iterator) PA_(begin)(struct A_(array) *const a)
|
||||||
|
{ struct PA_(iterator) it; it.a = a, it.cur = 0, it.dir = 0; return it; }
|
||||||
|
/** After `a`. @implements `end` */
|
||||||
|
static struct PA_(iterator) PA_(end)(struct A_(array) *const a)
|
||||||
|
{ struct PA_(iterator) it; it.a = a, it.cur = a ? a->size : 0, it.dir = 1;
|
||||||
|
return it; }
|
||||||
|
/** Move to next `it`. @return Element or null. @implements `next` */
|
||||||
|
static PA_(type) *PA_(next)(struct PA_(iterator) *const it) {
|
||||||
|
size_t size;
|
||||||
|
PA_(type) *element;
|
||||||
|
assert(it);
|
||||||
|
if(!it->a || !(size = it->a->size)) { /* Null or empty. */
|
||||||
|
it->cur = 0, it->dir = 0, element = 0;
|
||||||
|
} else if(!it->dir) { /* Left. */
|
||||||
|
it->dir = 1;
|
||||||
|
if(it->cur < size) element = it->a->data + it->cur;
|
||||||
|
else it->cur = size, element = 0; /* Ended by prev-next. */
|
||||||
|
} else if(size <= it->cur) { /* Right and already ended. */
|
||||||
|
it->cur = size, element = 0;
|
||||||
|
} else { /* Right. */
|
||||||
|
if(++it->cur < size) element = it->a->data + it->cur;
|
||||||
|
else element = 0; /* Just ended. */
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
/** Move to previous `it`. @return Element or null. @implements `previous` */
|
||||||
|
static PA_(type) *PA_(previous)(struct PA_(iterator) *const it) {
|
||||||
|
size_t size;
|
||||||
|
PA_(type) *element;
|
||||||
|
assert(it);
|
||||||
|
if(!it->a || !(size = it->a->size)) { /* Null or empty. */
|
||||||
|
it->cur = 0, it->dir = 0, element = 0;
|
||||||
|
} else if(it->dir) { /* Right. */
|
||||||
|
it->dir = 0;
|
||||||
|
/* Clip. */
|
||||||
|
if(size < it->cur) element = it->a->data + (it->cur = size) - 1;
|
||||||
|
else if(it->cur) element = it->a->data + it->cur - 1;
|
||||||
|
else element = 0; /* Ended by next-prev. */
|
||||||
|
} else if(!it->cur) { /* Left and already ended. */
|
||||||
|
element = 0;
|
||||||
|
} else { /* Left. */
|
||||||
|
if(size < it->cur) element = it->a->data + (it->cur = size) - 1;
|
||||||
|
else if(--it->cur) element = it->a->data + it->cur - 1;
|
||||||
|
else element = 0; /* Just ended. */
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
/** Removes the element last returned by `it`. (Untested.)
|
||||||
|
@return There was an element. @order \O(`a.size`). @implements `remove` */
|
||||||
|
static int PA_(remove)(struct PA_(iterator) *const it) {
|
||||||
|
assert(0 && it);
|
||||||
|
if(!it->dir || !it->a) return 0;
|
||||||
|
if(it->dir) {
|
||||||
|
if(it->a->size <= it->cur) return 0;
|
||||||
|
} else {
|
||||||
|
if(!it->cur || it->a->size < it->cur) return 0;
|
||||||
|
it->cur--;
|
||||||
|
}
|
||||||
|
memmove(it->a->data + it->cur, it->a->data + it->cur + 1,
|
||||||
|
sizeof *it->a->data * (--it->a->size - it->cur));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BOX_ACCESS
|
||||||
|
/** @return Iterator immediately before element `idx` of `a`.
|
||||||
|
@implements `index` */
|
||||||
|
static struct PA_(iterator) PA_(index)(struct A_(array) *a, size_t idx)
|
||||||
|
{ struct PA_(iterator) it; it.a = a, it.cur = idx, it.dir = 0; return it; }
|
||||||
|
/** Size of `a`. @implements `size` */
|
||||||
|
static size_t PA_(size)(const struct A_(array) *a) { return a ? a->size : 0; }
|
||||||
|
/** @return Element `idx` of `a`. @implements `at` */
|
||||||
|
static PA_(type) *PA_(at)(const struct A_(array) *a, const size_t idx)
|
||||||
|
{ return a->data + idx; }
|
||||||
|
|
||||||
|
#define BOX_CONTIGUOUS /* Depends on `BOX_ACCESS`. Also, `append` later. */
|
||||||
|
/** Writes `size` to `a`. @implements `tell_size` */
|
||||||
|
static void PA_(tell_size)(struct A_(array) *a, const size_t size)
|
||||||
|
{ assert(a); a->size = size; }
|
||||||
|
|
||||||
|
/* Box override information. */
|
||||||
|
#define BOX_ PA_
|
||||||
|
#define BOX struct A_(array)
|
||||||
|
|
||||||
|
/** Cursor; may become invalid after a topological change to any items
|
||||||
|
previous. */
|
||||||
|
struct A_(array_iterator);
|
||||||
|
struct A_(array_iterator) { struct PA_(iterator) _; };
|
||||||
|
|
||||||
|
/** Zeroed data (not all-bits-zero) is initialized.
|
||||||
|
@return An idle array. @order \Theta(1) @allow */
|
||||||
|
static struct A_(array) A_(array)(void)
|
||||||
|
{ struct A_(array) a; a.data = 0, a.capacity = a.size = 0; return a; }
|
||||||
|
|
||||||
|
/** If `a` is not null, destroys and returns it to idle. @allow */
|
||||||
static void A_(array_)(struct A_(array) *const a)
|
static void A_(array_)(struct A_(array) *const a)
|
||||||
{ assert(a), free(a->data), A_(array)(a); }
|
{ if(a) free(a->data), *a = A_(array)(); }
|
||||||
|
|
||||||
|
/** @return A cursor before the front of `a`. */
|
||||||
|
static struct A_(array_iterator) A_(array_begin)(struct A_(array) *a)
|
||||||
|
{ struct A_(array_iterator) it; it._ = PA_(begin)(a); return it; }
|
||||||
|
/** @return An iterator after the end of `a`. */
|
||||||
|
static struct A_(array_iterator) A_(array_end)(struct A_(array) *a)
|
||||||
|
{ struct A_(array_iterator) it; it._ = PA_(end)(a); return it; }
|
||||||
|
/** @return An iterator at `idx` of `a`. */
|
||||||
|
static struct A_(array_iterator) A_(array_index)(struct A_(array) *a,
|
||||||
|
size_t idx) { struct A_(array_iterator) it;
|
||||||
|
it._ = PA_(index)(a, idx); return it; }
|
||||||
|
/** @return `it` next element. */
|
||||||
|
static PA_(type) *A_(array_next)(struct A_(array_iterator) *const it)
|
||||||
|
{ return assert(it), PA_(next)(&it->_); }
|
||||||
|
/** @return `it` previous element. */
|
||||||
|
static PA_(type) *A_(array_previous)(struct A_(array_iterator) *const it)
|
||||||
|
{ return assert(it), PA_(previous)(&it->_); }
|
||||||
|
|
||||||
/** Ensures `min` capacity of `a`. Invalidates pointers in `a`. @param[min] If
|
/** Ensures `min` capacity of `a`. Invalidates pointers in `a`. @param[min] If
|
||||||
zero, does nothing. @return Success; otherwise, `errno` will be set.
|
zero, does nothing. @return Success; otherwise, `errno` will be set.
|
||||||
|
@ -119,7 +247,7 @@ static void A_(array_)(struct A_(array) *const a)
|
||||||
static int A_(array_reserve)(struct A_(array) *const a, const size_t min) {
|
static int A_(array_reserve)(struct A_(array) *const a, const size_t min) {
|
||||||
size_t c0;
|
size_t c0;
|
||||||
PA_(type) *data;
|
PA_(type) *data;
|
||||||
const size_t max_size = (size_t)-1 / sizeof *a->data;
|
const size_t max_size = (size_t)~0 / sizeof *a->data;
|
||||||
assert(a);
|
assert(a);
|
||||||
if(a->data) {
|
if(a->data) {
|
||||||
assert(a->size <= a->capacity);
|
assert(a->size <= a->capacity);
|
||||||
|
@ -148,31 +276,32 @@ static int A_(array_reserve)(struct A_(array) *const a, const size_t min) {
|
||||||
size. Invalidates any pointers in `a`.
|
size. Invalidates any pointers in `a`.
|
||||||
@return The start of the buffered space at the back of the array. If `a` is
|
@return The start of the buffered space at the back of the array. If `a` is
|
||||||
idle and `buffer` is zero, a null pointer is returned, otherwise null
|
idle and `buffer` is zero, a null pointer is returned, otherwise null
|
||||||
indicates an error. @throws[realloc, ERANGE] @allow */
|
indicates an error. @throws[realloc] @allow */
|
||||||
static PA_(type) *A_(array_buffer)(struct A_(array) *const a, const size_t n) {
|
static PA_(type) *A_(array_buffer)(struct A_(array) *const a, const size_t n) {
|
||||||
assert(a);
|
assert(a);
|
||||||
if(a->size > (size_t)-1 - n) { errno = ERANGE; return 0; }
|
if(a->size > (size_t)~0 - n) { errno = ERANGE; return 0; }
|
||||||
return A_(array_reserve)(a, a->size + n) && a->data ? a->data + a->size : 0;
|
return A_(array_reserve)(a, a->size + n) && a->data ? a->data + a->size : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Appends `n` items on the back of `a`. This is used in the coda and
|
/** Appends `n` contiguous items on the back of `a`.
|
||||||
<fn:<A>array_append>. */
|
@implements `append` from `BOX_CONTIGUOUS` */
|
||||||
static PA_(type) *PA_(append)(struct A_(array) *const a, const size_t n) {
|
static PA_(type) *PA_(append)(struct A_(array) *const a, const size_t n) {
|
||||||
PA_(type) *b;
|
PA_(type) *b;
|
||||||
assert(a);
|
|
||||||
if(!(b = A_(array_buffer)(a, n))) return 0;
|
if(!(b = A_(array_buffer)(a, n))) return 0;
|
||||||
assert(n <= a->capacity && a->size <= a->capacity - n);
|
assert(n <= a->capacity && a->size <= a->capacity - n);
|
||||||
return a->size += n, b;
|
return a->size += n, b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds `n` un-initialised elements at position `at` in `a`. The buffer holds
|
/** Adds `n` un-initialised elements at position `at` in `a`. It will
|
||||||
enough elements or it will invalidate any pointers in `a`.
|
invalidate any pointers in `a` if the buffer holds too few elements.
|
||||||
@param[at] A number smaller than or equal to `a.size`; if `a.size`, this
|
@param[at] A number smaller than or equal to `a.size`; if `a.size`, this
|
||||||
function behaves as <fn:<A>array_append>.
|
function behaves as <fn:<A>array_append>.
|
||||||
@return A pointer to the start of the new region, where there are `n`
|
@return A pointer to the start of the new region, where there are `n`
|
||||||
elements. @throws[realloc, ERANGE] @allow */
|
elements. @throws[realloc, ERANGE] @allow */
|
||||||
static PA_(type) *A_(array_insert)(struct A_(array) *const a,
|
static PA_(type) *A_(array_insert)(struct A_(array) *const a,
|
||||||
const size_t n, const size_t at) {
|
const size_t n, const size_t at) {
|
||||||
|
/* Investigate `n` is better than `element`; all the other are element. But
|
||||||
|
also, when would I ever use this? */
|
||||||
const size_t old_size = a->size;
|
const size_t old_size = a->size;
|
||||||
PA_(type) *const b = PA_(append)(a, n);
|
PA_(type) *const b = PA_(append)(a, n);
|
||||||
assert(a && at <= old_size);
|
assert(a && at <= old_size);
|
||||||
|
@ -181,15 +310,15 @@ static PA_(type) *A_(array_insert)(struct A_(array) *const a,
|
||||||
return a->data + at;
|
return a->data + at;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return Adds (push back) one new element of `a`. The buffer holds an
|
/** @return Adds (push back) one new element of `a`. The buffer space holds at
|
||||||
element or it will invalidate pointers in `a`.
|
least one element, or it may invalidate pointers in `a`.
|
||||||
@order amortised \O(1) @throws[realloc, ERANGE] @allow */
|
@order amortised \O(1) @throws[realloc, ERANGE] @allow */
|
||||||
static PA_(type) *A_(array_new)(struct A_(array) *const a)
|
static PA_(type) *A_(array_new)(struct A_(array) *const a)
|
||||||
{ return PA_(append)(a, 1); }
|
{ return PA_(append)(a, 1); }
|
||||||
|
|
||||||
/** Shrinks the capacity `a` to the size, freeing unused memory. If the size is
|
/** Shrinks the capacity `a` to the size, freeing unused memory. If the size is
|
||||||
zero, it will be in an idle state. Invalidates pointers in `a`.
|
zero, it will be in an idle state. Invalidates pointers in `a`.
|
||||||
@return Success. @throws[ERANGE, realloc] Unlikely `realloc` error. */
|
@return Success. @throws[ERANGE, realloc] (Unlikely) `realloc` error. */
|
||||||
static int A_(array_shrink)(struct A_(array) *const a) {
|
static int A_(array_shrink)(struct A_(array) *const a) {
|
||||||
PA_(type) *data;
|
PA_(type) *data;
|
||||||
size_t c;
|
size_t c;
|
||||||
|
@ -202,16 +331,17 @@ static int A_(array_shrink)(struct A_(array) *const a) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Removes `datum` from `a`. @order \O(`a.size`). @allow */
|
/** Removes `element` from `a`. Do not attempt to remove an element that is not
|
||||||
|
in `a`. @order \O(`a.size`). @allow */
|
||||||
static void A_(array_remove)(struct A_(array) *const a,
|
static void A_(array_remove)(struct A_(array) *const a,
|
||||||
PA_(type) *const datum) {
|
PA_(type) *const element) {
|
||||||
const size_t n = (size_t)(datum - a->data);
|
const size_t n = (size_t)(element - a->data);
|
||||||
assert(a && datum && datum >= a->data && datum < a->data + a->size);
|
assert(a && element && element >= a->data && element < a->data + a->size);
|
||||||
memmove(datum, datum + 1, sizeof *datum * (--a->size - n));
|
memmove(element, element + 1, sizeof *element * (--a->size - n));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Removes `datum` from `a` and replaces it with the tail.
|
/** Removes `datum` from `a` and replaces it with the tail. Do not attempt to
|
||||||
@order \O(1). @allow */
|
remove an element that is not in `a`. @order \O(1). @allow */
|
||||||
static void A_(array_lazy_remove)(struct A_(array) *const a,
|
static void A_(array_lazy_remove)(struct A_(array) *const a,
|
||||||
PA_(type) *const datum) {
|
PA_(type) *const datum) {
|
||||||
size_t n = (size_t)(datum - a->data);
|
size_t n = (size_t)(datum - a->data);
|
||||||
|
@ -240,13 +370,13 @@ static PA_(type) *A_(array_pop)(struct A_(array) *const a)
|
||||||
pointer will be returned, otherwise null indicates an error.
|
pointer will be returned, otherwise null indicates an error.
|
||||||
@throws[realloc, ERANGE] @allow */
|
@throws[realloc, ERANGE] @allow */
|
||||||
static PA_(type) *A_(array_append)(struct A_(array) *const a, const size_t n)
|
static PA_(type) *A_(array_append)(struct A_(array) *const a, const size_t n)
|
||||||
{ return PA_(append)(a, n); }
|
{ return assert(a), PA_(append)(a, n); }
|
||||||
|
|
||||||
/** Indices [`i0`, `i1`) of `a` will be replaced with a copy of `b`.
|
/** Indices [`i0`, `i1`) of `a` will be replaced with a copy of `b`.
|
||||||
@param[b] Can be null, which acts as empty, but cannot be `a`.
|
@param[b] Can be null, which acts as empty, but cannot overlap with `a`.
|
||||||
@return Success. @throws[realloc, ERANGE] @allow */
|
@return Success. @throws[realloc, ERANGE] @allow */
|
||||||
static int A_(array_splice)(/*restrict*/ struct A_(array) *const a,
|
static int A_(array_splice)(struct A_(array) *restrict const a,
|
||||||
/*restrict*/ const struct A_(array) *const b,
|
const struct A_(array) *restrict const b,
|
||||||
const size_t i0, const size_t i1) {
|
const size_t i0, const size_t i1) {
|
||||||
const size_t a_range = i1 - i0, b_range = b ? b->size : 0;
|
const size_t a_range = i1 - i0, b_range = b ? b->size : 0;
|
||||||
assert(a && a != b && i0 <= i1 && i1 <= a->size);
|
assert(a && a != b && i0 <= i1 && i1 <= a->size);
|
||||||
|
@ -265,52 +395,33 @@ static int A_(array_splice)(/*restrict*/ struct A_(array) *const a,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* <!-- iterate interface */
|
#ifdef HAVE_ITERATE_H /* <!-- iterate */
|
||||||
/* Contains all iteration parameters. */
|
#define ITR_(n) ARRAY_CAT(A_(array), n)
|
||||||
struct PA_(iterator) { const struct A_(array) *a; size_t i; };
|
#include "iterate.h" /** \include */
|
||||||
/** Loads `a` into `it`. @implements begin */
|
#undef ITR_
|
||||||
static void PA_(begin)(struct PA_(iterator) *const it,
|
#endif /* iterate --> */
|
||||||
const struct A_(array) *const a) { assert(it && a), it->a = a, it->i = 0; }
|
|
||||||
/** Advances `it`. @implements next */
|
|
||||||
static PA_(type) *PA_(next)(struct PA_(iterator) *const it) {
|
|
||||||
return assert(it && it->a), it->i < it->a->size ? it->a->data + it->i++ : 0;
|
|
||||||
}
|
|
||||||
#define BOX_ PA_
|
|
||||||
#define BOX_CONTAINER struct A_(array)
|
|
||||||
#define BOX_CONTENTS PA_(type)
|
|
||||||
/* iterate --> */
|
|
||||||
|
|
||||||
/* <!-- coda interface */
|
|
||||||
/** @return `a`. */
|
|
||||||
static const struct A_(array) *PA_(id_c)(const struct A_(array) *const a)
|
|
||||||
{ return a; }
|
|
||||||
/** @return `a`. */
|
|
||||||
static struct A_(array) *PA_(id)(struct A_(array) *const a) { return a; }
|
|
||||||
#define ARRAY_CODA_TYPE struct A_(array) /* Also box. */
|
|
||||||
#define ARRAY_CODA_BOX_TO_C &PA_(id_c)
|
|
||||||
#define ARRAY_CODA_BOX_TO &PA_(id)
|
|
||||||
#define AC_(n) ARRAY_CAT(A_(array), n)
|
|
||||||
/* coda --> */
|
|
||||||
|
|
||||||
#ifdef ARRAY_CODA /* <!-- coda: More functions. */
|
|
||||||
#include "array_coda.h" /** \include */
|
|
||||||
#endif /* coda --> */
|
|
||||||
|
|
||||||
#ifdef ARRAY_TEST /* <!-- test */
|
#ifdef ARRAY_TEST /* <!-- test */
|
||||||
/* Forward-declare. */
|
/* Forward-declare. */
|
||||||
static void (*PA_(to_string))(const PA_(type) *, char (*)[12]);
|
static void (*PA_(to_string))(const PA_(type) *, char (*)[12]);
|
||||||
static const char *(*PA_(array_to_string))(const struct A_(array) *);
|
static const char *(*PA_(array_to_string))(const struct A_(array) *);
|
||||||
#include "../test/test_array.h" /* (this will needlessly confuse) \include */
|
#include "../test/test_array.h"
|
||||||
#endif /* test --> */
|
#endif /* test --> */
|
||||||
|
|
||||||
static void PA_(unused_base_coda)(void);
|
static void PA_(unused_base_coda)(void);
|
||||||
static void PA_(unused_base)(void)
|
static void PA_(unused_base)(void) {
|
||||||
{ A_(array_)(0); A_(array_insert)(0, 0, 0); A_(array_new)(0);
|
PA_(is_element_c)(0); PA_(forward_begin)(0); PA_(forward_next)(0);
|
||||||
|
PA_(is_element)(0); PA_(remove)(0); PA_(size)(0); PA_(at)(0, 0);
|
||||||
|
PA_(tell_size)(0, 0);
|
||||||
|
A_(array)(); A_(array_)(0);
|
||||||
|
A_(array_begin)(0); A_(array_end)(0); A_(array_index)(0, 0);
|
||||||
|
A_(array_previous)(0); A_(array_next)(0); A_(array_previous)(0);
|
||||||
|
A_(array_insert)(0, 0, 0); A_(array_new)(0);
|
||||||
A_(array_shrink)(0); A_(array_remove)(0, 0); A_(array_lazy_remove)(0, 0);
|
A_(array_shrink)(0); A_(array_remove)(0, 0); A_(array_lazy_remove)(0, 0);
|
||||||
A_(array_clear)(0); A_(array_peek)(0); A_(array_pop)(0);
|
A_(array_clear)(0); A_(array_peek)(0); A_(array_pop)(0);
|
||||||
A_(array_append)(0, 0); A_(array_splice)(0, 0, 0, 0);
|
A_(array_append)(0, 0); A_(array_splice)(0, 0, 0, 0);
|
||||||
PA_(begin)(0, 0); PA_(next)(0); PA_(id)(0); PA_(id_c)(0);
|
PA_(unused_base_coda)();
|
||||||
PA_(unused_base_coda)(); }
|
}
|
||||||
static void PA_(unused_base_coda)(void) { PA_(unused_base)(); }
|
static void PA_(unused_base_coda)(void) { PA_(unused_base)(); }
|
||||||
|
|
||||||
|
|
||||||
|
@ -318,19 +429,19 @@ static void PA_(unused_base_coda)(void) { PA_(unused_base)(); }
|
||||||
|
|
||||||
|
|
||||||
#ifdef ARRAY_TO_STRING_NAME
|
#ifdef ARRAY_TO_STRING_NAME
|
||||||
#define SZ_(n) ARRAY_CAT(A_(array), ARRAY_CAT(ARRAY_TO_STRING_NAME, n))
|
#define STR_(n) ARRAY_CAT(A_(array), ARRAY_CAT(ARRAY_TO_STRING_NAME, n))
|
||||||
#else
|
#else
|
||||||
#define SZ_(n) ARRAY_CAT(A_(array), n)
|
#define STR_(n) ARRAY_CAT(A_(array), n)
|
||||||
#endif
|
#endif
|
||||||
#define TO_STRING ARRAY_TO_STRING
|
#define TO_STRING ARRAY_TO_STRING
|
||||||
#include "to_string.h" /** \include */
|
#include "to_string.h" /** \include */
|
||||||
#ifdef ARRAY_TEST /* <!-- expect: greedy satisfy forward-declared. */
|
#ifdef ARRAY_TEST /* <!-- expect: greedy satisfy forward-declared. */
|
||||||
#undef ARRAY_TEST
|
#undef ARRAY_TEST
|
||||||
static PSZ_(to_string_fn) PA_(to_string) = PSZ_(to_string);
|
static PSTR_(to_string_fn) PA_(to_string) = PSTR_(to_string);
|
||||||
static const char *(*PA_(array_to_string))(const struct A_(array) *)
|
static const char *(*PA_(array_to_string))(const struct A_(array) *)
|
||||||
= &SZ_(to_string);
|
= &STR_(to_string);
|
||||||
#endif /* expect --> */
|
#endif /* expect --> */
|
||||||
#undef SZ_
|
#undef STR_
|
||||||
#undef ARRAY_TO_STRING
|
#undef ARRAY_TO_STRING
|
||||||
#ifdef ARRAY_TO_STRING_NAME
|
#ifdef ARRAY_TO_STRING_NAME
|
||||||
#undef ARRAY_TO_STRING_NAME
|
#undef ARRAY_TO_STRING_NAME
|
||||||
|
@ -341,20 +452,20 @@ static const char *(*PA_(array_to_string))(const struct A_(array) *)
|
||||||
|
|
||||||
|
|
||||||
#ifdef ARRAY_COMPARE_NAME
|
#ifdef ARRAY_COMPARE_NAME
|
||||||
#define ARRAY_CODA_NAME ARRAY_COMPARE_NAME
|
#define CMP_(n) ARRAY_CAT(A_(array), ARRAY_CAT(ARRAY_COMPARE_NAME, n))
|
||||||
|
#else
|
||||||
|
#define CMP_(n) ARRAY_CAT(A_(array), n)
|
||||||
#endif
|
#endif
|
||||||
#ifdef ARRAY_COMPARE /* <!-- cmp */
|
#ifdef ARRAY_COMPARE /* <!-- cmp */
|
||||||
#define BOX_COMPARE ARRAY_COMPARE
|
#define COMPARE ARRAY_COMPARE
|
||||||
#else /* cmp --><!-- eq */
|
#else /* cmp --><!-- eq */
|
||||||
#define BOX_IS_EQUAL ARRAY_IS_EQUAL
|
#define COMPARE_IS_EQUAL ARRAY_IS_EQUAL
|
||||||
#endif /* eq --> */
|
#endif /* eq --> */
|
||||||
#include "array_coda.h" /* (Already included.) */
|
#include "compare.h" /** \include */
|
||||||
#ifdef ARRAY_TEST /* <!-- test: this detects and outputs compare test. */
|
#ifdef ARRAY_TEST /* <!-- test: this detects and outputs compare test. */
|
||||||
#include "../test/test_array.h"
|
#include "../test/test_array.h"
|
||||||
#endif /* test --> */
|
#endif /* test --> */
|
||||||
#undef ACC_
|
#undef CMP_
|
||||||
#undef PACC_
|
|
||||||
#undef ARRAY_CODA_NAME
|
|
||||||
#ifdef ARRAY_COMPARE_NAME
|
#ifdef ARRAY_COMPARE_NAME
|
||||||
#undef ARRAY_COMPARE_NAME
|
#undef ARRAY_COMPARE_NAME
|
||||||
#endif
|
#endif
|
||||||
|
@ -377,20 +488,17 @@ static const char *(*PA_(array_to_string))(const struct A_(array) *)
|
||||||
#endif
|
#endif
|
||||||
#undef ARRAY_NAME
|
#undef ARRAY_NAME
|
||||||
#undef ARRAY_TYPE
|
#undef ARRAY_TYPE
|
||||||
/* Iteration. */
|
|
||||||
#undef BOX_
|
#undef BOX_
|
||||||
#undef BOX_CONTAINER
|
#undef BOX
|
||||||
#undef BOX_CONTENTS
|
#undef BOX_CONTENT
|
||||||
/* Coda. */
|
#undef BOX_ITERATOR
|
||||||
#undef ARRAY_CODA_TYPE
|
#undef BOX_ACCESS
|
||||||
#undef ARRAY_CODA_BOX_TO_C
|
#undef BOX_CONTIGUOUS
|
||||||
#undef ARRAY_CODA_BOX_TO
|
|
||||||
#undef AC_
|
|
||||||
#undef ARRAY_CODA_ONCE
|
|
||||||
#ifdef ARRAY_CODA_COMPARE_ONCE
|
|
||||||
#undef ARRAY_CODA_COMPARE_ONCE
|
|
||||||
#endif
|
|
||||||
#endif /* !trait --> */
|
#endif /* !trait --> */
|
||||||
#undef ARRAY_TO_STRING_TRAIT
|
#undef ARRAY_TO_STRING_TRAIT
|
||||||
#undef ARRAY_COMPARE_TRAIT
|
#undef ARRAY_COMPARE_TRAIT
|
||||||
#undef ARRAY_TRAITS
|
#undef ARRAY_TRAITS
|
||||||
|
#ifdef ARRAY_RESTRICT
|
||||||
|
#undef ARRAY_RESTRICT
|
||||||
|
#undef restrict
|
||||||
|
#endif
|
||||||
|
|
437
src/array_coda.h
437
src/array_coda.h
|
@ -1,437 +0,0 @@
|
||||||
/* @license 2020 Neil Edelman, distributed under the terms of the
|
|
||||||
[MIT License](https://opensource.org/licenses/MIT).
|
|
||||||
|
|
||||||
@subtitle Array coda
|
|
||||||
|
|
||||||
This defines an optional set of functions that is nice, for any child of
|
|
||||||
`array` not providing additional constraints. (Thus, `array`?)
|
|
||||||
|
|
||||||
@param[ARRAY_CODA_TYPE]
|
|
||||||
Type of array.
|
|
||||||
|
|
||||||
@param[ARRAY_CODA_BOX_TO_C, ARRAY_CODA_BOX_TO]
|
|
||||||
Function picking out the array satisfying <typedef:<PAC>box_to_array_c> and
|
|
||||||
<typedef:<PAC>box_to_array>.
|
|
||||||
|
|
||||||
@param[AC_]
|
|
||||||
A one-argument macro producing a name that is responsible for the name of the
|
|
||||||
functions. Should be something like `AC_(x) -> foo_widget_x`. The caller is
|
|
||||||
responsible for undefining `AC_`.
|
|
||||||
|
|
||||||
@std C89 */
|
|
||||||
|
|
||||||
#if !defined(BOX_) || !defined(BOX_CONTAINER) || !defined(BOX_CONTENTS) \
|
|
||||||
|| !defined(ARRAY_CODA_TYPE) || !defined(ARRAY_CODA_BOX_TO_C) \
|
|
||||||
|| !defined(ARRAY_CODA_BOX_TO) || !defined(AC_) \
|
|
||||||
|| defined(BOX_IS_EQUAL) && defined(BOX_COMPARE)
|
|
||||||
#error Unexpected preprocessor symbols.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ARRAY_CODA_H /* <!-- idempotent */
|
|
||||||
#define ARRAY_CODA_H
|
|
||||||
#include <limits.h>
|
|
||||||
#ifdef PAC_
|
|
||||||
#error Unexpected defines.
|
|
||||||
#endif
|
|
||||||
#define PAC_(n) ARRAY_CAT(array_coda, AC_(n))
|
|
||||||
#endif /* idempotent --> */
|
|
||||||
|
|
||||||
#ifndef ARRAY_CODA_ONCE /* <!-- once */
|
|
||||||
#define ARRAY_CODA_ONCE
|
|
||||||
/** <src/array_coda.h>: an alias to the box. */
|
|
||||||
typedef BOX_CONTAINER PAC_(box);
|
|
||||||
/** <src/array_coda.h>: an alias to the individual type contained in the box. */
|
|
||||||
typedef BOX_CONTENTS PAC_(type);
|
|
||||||
/* Downcasting. */
|
|
||||||
typedef ARRAY_CODA_TYPE PAC_(array);
|
|
||||||
typedef const PAC_(array) *(*PAC_(box_to_array_c))(const PAC_(box) *);
|
|
||||||
static PAC_(box_to_array_c) PAC_(b2a_c) = (ARRAY_CODA_BOX_TO_C);
|
|
||||||
typedef PAC_(array) *(*PAC_(box_to_array))(PAC_(box) *);
|
|
||||||
static PAC_(box_to_array) PAC_(b2a) = (ARRAY_CODA_BOX_TO);
|
|
||||||
#endif /* once --> */
|
|
||||||
|
|
||||||
|
|
||||||
#if !defined(BOX_IS_EQUAL) && !defined(BOX_COMPARE) /* <!-- functions */
|
|
||||||
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Operates by side-effects on <typedef:<PAC>type>. */
|
|
||||||
typedef void (*PAC_(action_fn))(PAC_(type) *);
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Returns a boolean given read-only <typedef:<PAC>type>. */
|
|
||||||
typedef int (*PAC_(predicate_fn))(const PAC_(type) *);
|
|
||||||
|
|
||||||
/** <src/array_coda.h> @param[x] A valid entry or null to start from the last.
|
|
||||||
@return The previous valid entry from `box` (which could be null) or null if
|
|
||||||
this was the first. @allow */
|
|
||||||
static PAC_(type) *AC_(previous)(const PAC_(box) *const box,
|
|
||||||
const PAC_(type) *const x) {
|
|
||||||
const PAC_(array) *a;
|
|
||||||
size_t i;
|
|
||||||
if(!box || !(a = PAC_(b2a_c)(box))->data) return 0;
|
|
||||||
if(!x) return a->size ? a->data + a->size - 1 : 0;
|
|
||||||
return (i = (size_t)(x - a->data)) ? a->data + i - 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h> @param[x] A valid entry or null to start from the first.
|
|
||||||
@return The next valid entry from `box` (which could be null) or null if this
|
|
||||||
was the last. @allow */
|
|
||||||
static PAC_(type) *AC_(next)(const PAC_(box) *const box,
|
|
||||||
const PAC_(type) *const x) {
|
|
||||||
const PAC_(array) *a;
|
|
||||||
size_t i;
|
|
||||||
if(!box || !(a = PAC_(b2a_c)(box))->data) return 0;
|
|
||||||
if(!x) return a->size ? a->data + 0 : 0;
|
|
||||||
return (i = (size_t)(x - a->data) + 1) < a->size ? a->data + i : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h> @return Converts `i` to an index in `box` from
|
|
||||||
[0, `box.size`]. Negative values are wrapped. @order \Theta(1) @allow */
|
|
||||||
static size_t AC_(clip)(const PAC_(box) *const box, const long i) {
|
|
||||||
const PAC_(array) *const a = PAC_(b2a_c)(box);
|
|
||||||
/* `SIZE_MAX` is `C99`. This is not guaranteed at all, but is common? */
|
|
||||||
assert(box && a && ~((size_t)0) >= (size_t)LONG_MAX
|
|
||||||
&& (unsigned long)~((size_t)0) >= LONG_MAX);
|
|
||||||
return i < 0
|
|
||||||
? (size_t)-i >= a->size ? 0 : a->size - (size_t)-i
|
|
||||||
: (size_t)i > a->size ? a->size : (size_t)i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: For all elements of `b`, calls `copy`, and if true, lazily
|
|
||||||
copies the elements to `a`. `a` and `b` can not be the same but `b` can be
|
|
||||||
null, (in which case, it does nothing.)
|
|
||||||
@order \O(`b.size` \times `copy`) @throws[ERANGE, realloc] @allow */
|
|
||||||
static int AC_(copy_if)(PAC_(box) *const a, const PAC_(predicate_fn) copy,
|
|
||||||
const PAC_(box) *const b) {
|
|
||||||
PAC_(array) *const aa = PAC_(b2a)(a);
|
|
||||||
const PAC_(array) *const bb = b ? PAC_(b2a_c)(b) : 0;
|
|
||||||
PAC_(type) *i, *fresh;
|
|
||||||
const PAC_(type) *end, *rise = 0;
|
|
||||||
size_t add;
|
|
||||||
int difcpy = 0;
|
|
||||||
assert(a && aa && !(!b ^ !bb) && copy && a != b && aa != bb);
|
|
||||||
if(!b) return 1;
|
|
||||||
for(i = bb->data, end = i + bb->size; i < end; i++) {
|
|
||||||
if(!(!!rise ^ (difcpy = copy(i)))) continue; /* Not falling/rising. */
|
|
||||||
if(difcpy) { /* Rising edge. */
|
|
||||||
assert(!rise);
|
|
||||||
rise = i;
|
|
||||||
} else { /* Falling edge. */
|
|
||||||
assert(rise && !difcpy && rise < i);
|
|
||||||
if(!(fresh = BOX_(append)(a, add = (size_t)(i - rise)))) return 0;
|
|
||||||
memcpy(fresh, rise, sizeof *fresh * add);
|
|
||||||
rise = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rise) { /* Delayed copy. */
|
|
||||||
assert(!difcpy && rise < i);
|
|
||||||
if(!(fresh = BOX_(append)(a, add = (size_t)(i - rise)))) return 0;
|
|
||||||
memcpy(fresh, rise, sizeof *fresh * add);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: For all elements of `box`, calls `keep`, and if false, lazy
|
|
||||||
deletes that item, calling `destruct` (if not-null).
|
|
||||||
@order \O(`a.size` \times `keep` \times `destruct`) @allow */
|
|
||||||
static void AC_(keep_if)(PAC_(box) *const box,
|
|
||||||
const PAC_(predicate_fn) keep, const PAC_(action_fn) destruct) {
|
|
||||||
PAC_(array) *const a = PAC_(b2a)(box);
|
|
||||||
PAC_(type) *erase = 0, *t;
|
|
||||||
const PAC_(type) *retain = 0, *end;
|
|
||||||
int keep0 = 1, keep1 = 0;
|
|
||||||
assert(box && a && keep);
|
|
||||||
for(t = a->data, end = t + a->size; t < end; keep0 = keep1, t++) {
|
|
||||||
if(!(keep1 = !!keep(t)) && destruct) destruct(t);
|
|
||||||
if(!(keep0 ^ keep1)) continue; /* Not a falling/rising edge. */
|
|
||||||
if(keep1) { /* Rising edge. */
|
|
||||||
assert(erase && !retain);
|
|
||||||
retain = t;
|
|
||||||
} else if(erase) { /* Falling edge. */
|
|
||||||
size_t n = (size_t)(t - retain);
|
|
||||||
assert(erase < retain && retain < t);
|
|
||||||
memmove(erase, retain, sizeof *t * n);
|
|
||||||
erase += n;
|
|
||||||
retain = 0;
|
|
||||||
} else { /* Falling edge, (first time only.) */
|
|
||||||
erase = t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!erase) return; /* All elements were kept. */
|
|
||||||
if(keep1) { /* Delayed move when the iteration ended; repeat. */
|
|
||||||
size_t n = (size_t)(t - retain);
|
|
||||||
assert(retain && erase < retain && retain < t);
|
|
||||||
memmove(erase, retain, sizeof *t * n);
|
|
||||||
erase += n;
|
|
||||||
}
|
|
||||||
/* Adjust the size. */
|
|
||||||
assert((size_t)(erase - a->data) <= a->size);
|
|
||||||
a->size = (size_t)(erase - a->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Removes at either end of `box` of things that `predicate`
|
|
||||||
returns true. @order \O(`box.size` \times `predicate`) @allow */
|
|
||||||
static void AC_(trim)(PAC_(box) *const box,
|
|
||||||
const PAC_(predicate_fn) predicate) {
|
|
||||||
PAC_(array) *const a = PAC_(b2a)(box);
|
|
||||||
size_t i;
|
|
||||||
assert(box && a && predicate);
|
|
||||||
while(a->size && predicate(a->data + a->size - 1)) a->size--;
|
|
||||||
for(i = 0; i < a->size && predicate(a->data + i); i++);
|
|
||||||
if(!i) return;
|
|
||||||
assert(i < a->size);
|
|
||||||
memmove(a->data, a->data + i, sizeof *a->data * i), a->size -= i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Iterates through `box` and calls `action` on all the
|
|
||||||
elements. The topology of the list should not change while in this function.
|
|
||||||
@order \O(`box.size` \times `action`) @allow */
|
|
||||||
static void AC_(each)(PAC_(box) *const box, const PAC_(action_fn) action) {
|
|
||||||
PAC_(array) *const a = PAC_(b2a)(box);
|
|
||||||
PAC_(type) *i, *end;
|
|
||||||
assert(box && a && action);
|
|
||||||
for(i = a->data, end = i + a->size; i < end; i++) action(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Iterates through `box` and calls `action` on all the
|
|
||||||
elements for which `predicate` returns true. The topology of the list should
|
|
||||||
not change while in this function.
|
|
||||||
@order \O(`box.size` \times `predicate` \times `action`) @allow */
|
|
||||||
static void AC_(if_each)(PAC_(box) *const box,
|
|
||||||
const PAC_(predicate_fn) predicate, const PAC_(action_fn) action) {
|
|
||||||
PAC_(array) *const a = PAC_(b2a)(box);
|
|
||||||
PAC_(type) *i, *end;
|
|
||||||
assert(box && a && predicate && action);
|
|
||||||
for(i = a->data, end = i + a->size; i < end; i++)
|
|
||||||
if(predicate(i)) action(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Iterates through `box` and calls `predicate` until it
|
|
||||||
returns true.
|
|
||||||
@return The first `predicate` that returned true, or, if the statement is
|
|
||||||
false on all, null. @order \O(`box.size` \times `predicate`) @allow */
|
|
||||||
static const PAC_(type) *AC_(any)(const PAC_(box) *const box,
|
|
||||||
const PAC_(predicate_fn) predicate) {
|
|
||||||
const PAC_(array) *const a = PAC_(b2a_c)(box);
|
|
||||||
PAC_(type) *i, *end;
|
|
||||||
assert(box && a && predicate);
|
|
||||||
for(i = a->data, end = i + a->size; i < end; i++)
|
|
||||||
if(predicate(i)) return i;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PAC_(unused_function_coda)(void);
|
|
||||||
static void PAC_(unused_function)(void)
|
|
||||||
{ AC_(previous)(0, 0); AC_(next)(0, 0); AC_(clip)(0, 0);
|
|
||||||
AC_(copy_if)(0, 0, 0); AC_(keep_if)(0, 0, 0); AC_(trim)(0, 0);
|
|
||||||
AC_(each)(0, 0); AC_(if_each)(0, 0, 0); AC_(any)(0, 0);
|
|
||||||
PAC_(unused_function_coda)(); }
|
|
||||||
static void PAC_(unused_function_coda)(void) { PAC_(unused_function)(); }
|
|
||||||
|
|
||||||
#else /* functions --><!-- compare/is equal */
|
|
||||||
|
|
||||||
#ifndef ARRAY_CODA_COMPARE_ONCE /* <!-- once */
|
|
||||||
#define ARRAY_CODA_COMPARE_ONCE
|
|
||||||
/** <src/array_coda.h>: Returns a boolean given two read-only <typedef:<PAC>type>. */
|
|
||||||
typedef int (*PAC_(bipredicate_fn))(const PAC_(type) *, const PAC_(type) *);
|
|
||||||
/** <src/array_coda.h>: Three-way comparison on a totally order set of
|
|
||||||
<typedef:<PAC>type>; returns an integer value less then, equal to, greater
|
|
||||||
then zero, if `a < b`, `a == b`, `a > b`, respectively. */
|
|
||||||
typedef int (*PAC_(compare_fn))(const PAC_(type) *a, const PAC_(type) *b);
|
|
||||||
/** <src/array_coda.h>: Returns a boolean given two <typedef:<PAC>type>. */
|
|
||||||
typedef int (*PAC_(biaction_fn))(PAC_(type) *, PAC_(type) *);
|
|
||||||
#endif /* once --> */
|
|
||||||
|
|
||||||
#ifdef ARRAY_CODA_NAME
|
|
||||||
#define ACC_(n) AC_(ARRAY_CAT(ARRAY_CODA_NAME, n))
|
|
||||||
#else /* name --><!-- !name */
|
|
||||||
#define ACC_(n) AC_(n)
|
|
||||||
#endif /* !name --> */
|
|
||||||
#define PACC_(n) ARRAY_CAT(array_coda, ACC_(n))
|
|
||||||
|
|
||||||
#ifdef BOX_COMPARE /* <!-- compare */
|
|
||||||
|
|
||||||
/* Check that `BOX_COMPARE` is a function implementing
|
|
||||||
<typedef:<PAC>compare_fn>. */
|
|
||||||
static const PAC_(compare_fn) PACC_(compare) = (BOX_COMPARE);
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Lexicographically compares `a` to `b`. Both can be null,
|
|
||||||
with null values before everything. @return `a < b`: negative; `a == b`: zero;
|
|
||||||
`a > b`: positive. @order \O(`a.size`) @allow */
|
|
||||||
static int ACC_(compare)(const PAC_(box) *const a, const PAC_(box) *const b) {
|
|
||||||
const PAC_(array) *aa, *bb;
|
|
||||||
PAC_(type) *ad, *bd, *end;
|
|
||||||
int diff;
|
|
||||||
/* Null counts as `-\infty`. */
|
|
||||||
if(!a) return b ? -1 : 0;
|
|
||||||
else if(!b) return 1;
|
|
||||||
aa = PAC_(b2a_c)(a), bb = PAC_(b2a_c)(b), assert(aa && bb);
|
|
||||||
if(aa->size > bb->size) {
|
|
||||||
for(ad = aa->data, bd = bb->data, end = bd + bb->size; bd < end;
|
|
||||||
ad++, bd++) if((diff = PACC_(compare)(ad, bd))) return diff;
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
for(ad = a->data, bd = b->data, end = ad + a->size; ad < end;
|
|
||||||
ad++, bd++) if((diff = PACC_(compare)(ad, bd))) return diff;
|
|
||||||
return -(aa->size != bb->size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: `box` should be partitioned true/false with less-then
|
|
||||||
`value`. @return The first index of `a` that is not less than `value`.
|
|
||||||
@order \O(log `a.size`) @allow */
|
|
||||||
static size_t ACC_(lower_bound)(const PAC_(box) *const box,
|
|
||||||
const PAC_(type) *const value) {
|
|
||||||
const PAC_(array) *a = PAC_(b2a_c)(box);
|
|
||||||
size_t low = 0, high = a->size, mid;
|
|
||||||
assert(box && a && value);
|
|
||||||
while(low < high)
|
|
||||||
if(PACC_(compare)(value, a->data + (mid = low + (high - low) / 2)) <= 0)
|
|
||||||
high = mid;
|
|
||||||
else
|
|
||||||
low = mid + 1;
|
|
||||||
return low;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: `box` should be partitioned false/true with greater-than or
|
|
||||||
equal-to <typedef:<PAC>type> `value`. @return The first index of `box` that is
|
|
||||||
greater than `value`. @order \O(log `a.size`) @allow */
|
|
||||||
static size_t ACC_(upper_bound)(const PAC_(box) *const box,
|
|
||||||
const PAC_(type) *const value) {
|
|
||||||
const PAC_(array) *a = PAC_(b2a_c)(box);
|
|
||||||
size_t low = 0, high = a->size, mid;
|
|
||||||
assert(box && a && value);
|
|
||||||
while(low < high) if(PACC_(compare)(value, a->data
|
|
||||||
+ (mid = low + ((high - low) >> 1))) >= 0) low = mid + 1;
|
|
||||||
else high = mid;
|
|
||||||
return low;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Copies `value` at the upper bound of a sorted `box`.
|
|
||||||
@return Success. @order \O(`a.size`) @throws[realloc, ERANGE] @allow */
|
|
||||||
static int ACC_(insert_after)(PAC_(box) *const box,
|
|
||||||
const PAC_(type) *const value) {
|
|
||||||
PAC_(array) *a = PAC_(b2a)(box);
|
|
||||||
size_t bound;
|
|
||||||
assert(box && a && value);
|
|
||||||
bound = ACC_(upper_bound)(a, value);
|
|
||||||
if(!A_(array_new)(a)) return 0; /* @fixme Reference to array. */
|
|
||||||
memmove(a->data + bound + 1, a->data + bound,
|
|
||||||
sizeof *a->data * (a->size - bound - 1));
|
|
||||||
memcpy(a->data + bound, value, sizeof *value);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Wrapper with void `a` and `b`. @implements qsort bsearch */
|
|
||||||
static int PACC_(vcompar)(const void *const a, const void *const b)
|
|
||||||
{ return PACC_(compare)(a, b); }
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Sorts `box` by `qsort`.
|
|
||||||
@order \O(`a.size` \log `box.size`) @allow */
|
|
||||||
static void ACC_(sort)(PAC_(box) *const box) {
|
|
||||||
const PAC_(array) *a = PAC_(b2a_c)(box);
|
|
||||||
assert(box && a);
|
|
||||||
qsort(a->data, a->size, sizeof *a->data, &PACC_(vcompar));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Wrapper with void `a` and `b`. @implements qsort bsearch */
|
|
||||||
static int PACC_(vrevers)(const void *const a, const void *const b)
|
|
||||||
{ return PACC_(compare)(b, a); }
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Sorts `box` in reverse by `qsort`.
|
|
||||||
@order \O(`a.size` \log `a.size`) @allow */
|
|
||||||
static void ACC_(reverse)(PAC_(box) *const box) {
|
|
||||||
const PAC_(array) *a = PAC_(b2a_c)(box);
|
|
||||||
assert(box && a);
|
|
||||||
qsort(a->data, a->size, sizeof *a->data, &PACC_(vrevers));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** !compare(`a`, `b`) == equals(`a`, `b`).
|
|
||||||
@implements <typedef:<PAC>bipredicate_fn> */
|
|
||||||
static int PACC_(is_equal)(const PAC_(type) *const a, const PAC_(type) *const b)
|
|
||||||
{ return !PACC_(compare)(a, b); }
|
|
||||||
|
|
||||||
#else /* compare --><!-- is equal */
|
|
||||||
|
|
||||||
/* Check that `BOX_IS_EQUAL` is a function implementing
|
|
||||||
<typedef:<PAC>bipredicate_fn>. */
|
|
||||||
static const PAC_(bipredicate_fn) PACC_(is_equal) = (BOX_IS_EQUAL);
|
|
||||||
|
|
||||||
#endif /* is equal --> */
|
|
||||||
|
|
||||||
/** <src/array_coda.h> @return If `a` piecewise equals `b`, which both can be null.
|
|
||||||
@order \O(`size`) @allow */
|
|
||||||
static int ACC_(is_equal)(const PAC_(box) *const a, const PAC_(box) *const b)
|
|
||||||
{
|
|
||||||
const PAC_(array) *aa, *bb;
|
|
||||||
const PAC_(type) *ad, *bd, *end;
|
|
||||||
if(!a) return !b;
|
|
||||||
if(!b) return 0;
|
|
||||||
aa = PAC_(b2a_c)(a), bb = PAC_(b2a_c)(a), assert(aa && bb);
|
|
||||||
if(aa->size != bb->size) return 0;
|
|
||||||
for(ad = aa->data, bd = bb->data, end = ad + aa->size; ad < end; ad++, bd++)
|
|
||||||
if(!PACC_(is_equal)(ad, bd)) return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Removes consecutive duplicate elements in `box`.
|
|
||||||
@param[merge] Controls surjection. Called with duplicate elements, if false
|
|
||||||
`(x, y)->(x)`, if true `(x,y)->(y)`. More complex functions, `(x, y)->(x+y)`
|
|
||||||
can be simulated by mixing the two in the value returned. Can be null: behaves
|
|
||||||
like false. @order \O(`a.size` \times `merge`) @allow */
|
|
||||||
static void ACC_(unique_merge)(PAC_(box) *const box,
|
|
||||||
const PAC_(biaction_fn) merge) {
|
|
||||||
PAC_(array) *a = PAC_(b2a)(box);
|
|
||||||
size_t target, from, cursor, choice, next, move;
|
|
||||||
const size_t last = a->size;
|
|
||||||
int is_first, is_last;
|
|
||||||
assert(box && a);
|
|
||||||
for(target = from = cursor = 0; cursor < last; cursor += next) {
|
|
||||||
/* Bijective `[from, cursor)` is moved lazily. */
|
|
||||||
for(choice = 0, next = 1; cursor + next < last && PAC_(is_equal)(a->data
|
|
||||||
+ cursor + choice, a->data + cursor + next); next++)
|
|
||||||
if(merge && merge(a->data + choice, a->data + next)) choice = next;
|
|
||||||
if(next == 1) continue;
|
|
||||||
/* Must move injective `cursor + choice \in [cursor, cursor + next)`. */
|
|
||||||
is_first = !choice;
|
|
||||||
is_last = (choice == next - 1);
|
|
||||||
move = cursor - from + (size_t)is_first;
|
|
||||||
memmove(a->data + target, a->data + from, sizeof *a->data * move),
|
|
||||||
target += move;
|
|
||||||
if(!is_first && !is_last) memcpy(a->data + target,
|
|
||||||
a->data + cursor + choice, sizeof *a->data), target++;
|
|
||||||
from = cursor + next - (size_t)is_last;
|
|
||||||
}
|
|
||||||
/* Last differed move. */
|
|
||||||
move = last - from;
|
|
||||||
memmove(a->data + target, a->data + from, sizeof *a->data * move),
|
|
||||||
target += move, assert(a->size >= target);
|
|
||||||
a->size = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** <src/array_coda.h>: Removes consecutive duplicate elements in `a`.
|
|
||||||
@order \O(`a.size`) @allow */
|
|
||||||
static void ACC_(unique)(PAC_(box) *const a) { ACC_(unique_merge)(a, 0); }
|
|
||||||
|
|
||||||
static void PACC_(unused_compare_coda)(void);
|
|
||||||
static void PACC_(unused_compare)(void) {
|
|
||||||
#ifdef BOX_COMPARE /* <!-- compare */
|
|
||||||
ACC_(compare)(0, 0); ACC_(lower_bound)(0, 0); ACC_(upper_bound)(0, 0);
|
|
||||||
ACC_(insert_after)(0, 0); ACC_(sort)(0); ACC_(reverse)(0);
|
|
||||||
#endif /* compare --> */
|
|
||||||
ACC_(is_equal)(0, 0); ACC_(unique_merge)(0, 0); ACC_(unique)(0);
|
|
||||||
PACC_(unused_compare_coda)(); }
|
|
||||||
static void PACC_(unused_compare_coda)(void) { PACC_(unused_compare)(); }
|
|
||||||
|
|
||||||
#ifdef BOX_COMPARE
|
|
||||||
#undef BOX_COMPARE
|
|
||||||
#endif
|
|
||||||
#ifdef BOX_IS_EQUAL
|
|
||||||
#undef BOX_IS_EQUAL
|
|
||||||
#endif
|
|
||||||
#ifdef BOX_COMPARE_NAME
|
|
||||||
#undef BOX_COMPARE_NAME
|
|
||||||
#endif
|
|
||||||
/*#undef AC_C_
|
|
||||||
#undef PACC_ Need for tests. */
|
|
||||||
|
|
||||||
#endif /* compare/is equal --> */
|
|
432
src/heap.h
432
src/heap.h
|
@ -1,432 +0,0 @@
|
||||||
/** @license 2020 Neil Edelman, distributed under the terms of the
|
|
||||||
[MIT License](https://opensource.org/licenses/MIT).
|
|
||||||
|
|
||||||
@abstract Source <src/heap.h>, depends on <src/array.h>; examples
|
|
||||||
<test/test_heap.c>.
|
|
||||||
|
|
||||||
@subtitle Priority-queue
|
|
||||||
|
|
||||||
![Example of heap.](../web/heap.png)
|
|
||||||
|
|
||||||
A <tag:<H>heap> is a binary heap, proposed by
|
|
||||||
<Williams, 1964, Heapsort, p. 347> using terminology of
|
|
||||||
<Knuth, 1973, Sorting>. It can be used as an implementation of a priority
|
|
||||||
queue; internally, it is a `<<PH>node>array` with implicit heap properties on
|
|
||||||
<typedef:<PH>priority> and an optional <typedef:<PH>value> pointer value.
|
|
||||||
|
|
||||||
@param[HEAP_NAME, HEAP_TYPE]
|
|
||||||
`<H>` that satisfies `C` naming conventions when mangled and an assignable
|
|
||||||
type <typedef:<PH>priority> associated therewith. `HEAP_NAME` is required;
|
|
||||||
`HEAP_TYPE` defaults to `unsigned int`. `<PH>` is private, whose names are
|
|
||||||
prefixed in a manner to avoid collisions.
|
|
||||||
|
|
||||||
@param[HEAP_COMPARE]
|
|
||||||
A function satisfying <typedef:<PH>compare_fn>. Defaults to minimum-hash.
|
|
||||||
Required if `HEAP_TYPE` is changed to an incomparable type.
|
|
||||||
|
|
||||||
@param[HEAP_VALUE]
|
|
||||||
Optional value <typedef:<PH>value>, that is stored as a reference in
|
|
||||||
<tag:<H>heapnode>; declaring it is sufficient. If set, has no effect on the
|
|
||||||
ranking, but affects <typedef:<PH>value>, (otherwise, it's the same field as
|
|
||||||
<typedef:<PH>priority>.)
|
|
||||||
|
|
||||||
@param[HEAP_EXPECT_TRAIT]
|
|
||||||
Do not un-define certain variables for subsequent inclusion in a parameterized
|
|
||||||
trait.
|
|
||||||
|
|
||||||
@param[HEAP_TO_STRING_NAME, HEAP_TO_STRING]
|
|
||||||
To string trait contained in <to_string.h>; an optional unique `<SZ>`
|
|
||||||
that satisfies `C` naming conventions when mangled and function implementing
|
|
||||||
<typedef:<PSZ>to_string_fn>.
|
|
||||||
|
|
||||||
@depend [array](https://github.com/neil-edelman/array)
|
|
||||||
@std C89
|
|
||||||
@fixme Add decrease priority.
|
|
||||||
@fixme Add replace.
|
|
||||||
@fixme `HEAP_VALUE` has to be a pointer; use `memcpy` instead. */
|
|
||||||
|
|
||||||
#ifndef HEAP_NAME
|
|
||||||
#error Generic HEAP_NAME undefined.
|
|
||||||
#endif
|
|
||||||
#if defined(HEAP_TO_STRING_NAME) || defined(HEAP_TO_STRING) /* <!-- str */
|
|
||||||
#define HEAP_TO_STRING_TRAIT 1
|
|
||||||
#else /* str --><!-- !str */
|
|
||||||
#define HEAP_TO_STRING_TRAIT 0
|
|
||||||
#endif /* !str --> */
|
|
||||||
#define HEAP_TRAITS HEAP_TO_STRING_TRAIT
|
|
||||||
#if HEAP_TRAITS > 1
|
|
||||||
#error Only one trait per include is allowed; use HEAP_EXPECT_TRAIT.
|
|
||||||
#endif
|
|
||||||
#if defined(HEAP_TO_STRING_NAME) && !defined(HEAP_TO_STRING)
|
|
||||||
#error HEAP_TO_STRING_NAME requires HEAP_TO_STRING.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HEAP_H /* <!-- idempotent */
|
|
||||||
#define HEAP_H
|
|
||||||
#if defined(HEAP_CAT_) || defined(HEAP_CAT) || defined(H_) || defined(PH_) \
|
|
||||||
|| defined(HEAP_IDLE)
|
|
||||||
#error Unexpected defines.
|
|
||||||
#endif
|
|
||||||
/* <Kernighan and Ritchie, 1988, p. 231>. */
|
|
||||||
#define HEAP_CAT_(n, m) n ## _ ## m
|
|
||||||
#define HEAP_CAT(n, m) HEAP_CAT_(n, m)
|
|
||||||
#define H_(n) HEAP_CAT(HEAP_NAME, n)
|
|
||||||
#define PH_(n) HEAP_CAT(heap, H_(n))
|
|
||||||
#define HEAP_IDLE { ARRAY_IDLE }
|
|
||||||
#endif /* idempotent --> */
|
|
||||||
|
|
||||||
|
|
||||||
#if HEAP_TRAITS == 0 /* <!-- base code */
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef HEAP_TYPE
|
|
||||||
#define HEAP_TYPE unsigned
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** Valid assignable type used for priority in <typedef:<PH>node>. Defaults to
|
|
||||||
`unsigned int` if not set by `HEAP_TYPE`. */
|
|
||||||
typedef HEAP_TYPE PH_(priority);
|
|
||||||
|
|
||||||
/** Returns a positive result if `a` is out-of-order with respect to `b`,
|
|
||||||
inducing a strict pre-order. This is compatible, but less strict then the
|
|
||||||
comparators from `bsearch` and `qsort`; it only needs to divide entries into
|
|
||||||
two instead of three categories. */
|
|
||||||
typedef int (*PH_(compare_fn))(const PH_(priority) a, const PH_(priority) b);
|
|
||||||
#ifndef HEAP_COMPARE /* <!-- !cmp */
|
|
||||||
/** The default `HEAP_COMPARE` on `a` and `b` is `a > b`, which makes a
|
|
||||||
minimum-hash. @implements <typedef:<PH>compare_fn> */
|
|
||||||
static int PH_(default_compare)(const PH_(priority) a, const PH_(priority) b)
|
|
||||||
{ return a > b; }
|
|
||||||
#define HEAP_COMPARE &PH_(default_compare)
|
|
||||||
#endif /* !cmp --> */
|
|
||||||
/* Check that `HEAP_COMPARE` is a function implementing
|
|
||||||
<typedef:<PH>compare_fn>, if defined. */
|
|
||||||
static const PH_(compare_fn) PH_(compare) = (HEAP_COMPARE);
|
|
||||||
|
|
||||||
#ifdef HEAP_VALUE /* <!-- value */
|
|
||||||
typedef HEAP_VALUE PH_(value_data);
|
|
||||||
typedef PH_(value_data) *PH_(value);
|
|
||||||
/** If `HEAP_VALUE` is set, this becomes <typedef:<PH>node>; make a temporary
|
|
||||||
structure to add a pointer to the value and a priority (which may be something
|
|
||||||
cached from the value) and copy it using <fn:<H>heap_add>. */
|
|
||||||
struct H_(heapnode) { PH_(priority) priority; PH_(value) value; };
|
|
||||||
/** If `HEAP_VALUE` is set, (priority, value) set by <tag:<H>heapnode>,
|
|
||||||
otherwise it's a (priority) set directly by <typedef:<PH>priority>. */
|
|
||||||
typedef struct H_(heapnode) PH_(node);
|
|
||||||
#else /* value --><!-- !value */
|
|
||||||
typedef PH_(priority) PH_(value);
|
|
||||||
typedef PH_(priority) PH_(node);
|
|
||||||
#endif /* !value --> */
|
|
||||||
|
|
||||||
/* This relies on `array.h` which must be in the same directory. */
|
|
||||||
#define ARRAY_NAME PH_(node)
|
|
||||||
#define ARRAY_TYPE PH_(node)
|
|
||||||
#include "array.h"
|
|
||||||
|
|
||||||
/** Stores the heap as an implicit binary tree in an array called `a`. To
|
|
||||||
initialize it to an idle state, see <fn:<H>heap>, `HEAP_IDLE`, `{0}` (`C99`),
|
|
||||||
or being `static`.
|
|
||||||
|
|
||||||
![States.](../web/states.png) */
|
|
||||||
struct H_(heap) { struct PH_(node_array) a; };
|
|
||||||
|
|
||||||
/** Extracts the <typedef:<PH>priority> of `node`, which must not be null. */
|
|
||||||
static PH_(priority) PH_(get_priority)(const PH_(node) *const node) {
|
|
||||||
#ifdef HEAP_VALUE
|
|
||||||
return node->priority;
|
|
||||||
#else
|
|
||||||
return *node;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Extracts the <typedef:<PH>value> of `node`, which must not be null. */
|
|
||||||
static PH_(value) PH_(get_value)(const PH_(node) *const node) {
|
|
||||||
#ifdef HEAP_VALUE /* <-- value */
|
|
||||||
return node->value;
|
|
||||||
#else /* value --><!-- !value */
|
|
||||||
return *node;
|
|
||||||
#endif /* !value --> */
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Copies `src` to `dest`. */
|
|
||||||
static void PH_(copy)(const PH_(node) *const src, PH_(node) *const dest) {
|
|
||||||
#ifdef HEAP_VALUE /* <!-- value */
|
|
||||||
dest->priority = src->priority;
|
|
||||||
dest->value = src->value;
|
|
||||||
#else /* value --><!-- !value */
|
|
||||||
*dest = *src;
|
|
||||||
#endif /* !value --> */
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Find the spot in `heap` where `node` goes and put it there.
|
|
||||||
@param[heap] At least one entry; the last entry will be replaced by `node`.
|
|
||||||
@order \O(log `size`) */
|
|
||||||
static void PH_(sift_up)(struct H_(heap) *const heap, PH_(node) *const node) {
|
|
||||||
PH_(node) *const n0 = heap->a.data;
|
|
||||||
PH_(priority) p = PH_(get_priority)(node);
|
|
||||||
size_t i = heap->a.size - 1;
|
|
||||||
assert(heap && heap->a.size && node);
|
|
||||||
if(i) {
|
|
||||||
size_t i_up;
|
|
||||||
do { /* Note: don't change the `<=`; it's a queue. */
|
|
||||||
i_up = (i - 1) >> 1;
|
|
||||||
if(PH_(compare)(PH_(get_priority)(n0 + i_up), p) <= 0) break;
|
|
||||||
PH_(copy)(n0 + i_up, n0 + i);
|
|
||||||
} while((i = i_up));
|
|
||||||
}
|
|
||||||
PH_(copy)(node, n0 + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Pop the head of `heap` and restore the heap by sifting down the last
|
|
||||||
element. @param[heap] At least one entry. The head is popped, and the size
|
|
||||||
will be one less. */
|
|
||||||
static void PH_(sift_down)(struct H_(heap) *const heap) {
|
|
||||||
const size_t size = (assert(heap && heap->a.size), --heap->a.size),
|
|
||||||
half = size >> 1;
|
|
||||||
size_t i = 0, c;
|
|
||||||
PH_(node) *const n0 = heap->a.data,
|
|
||||||
*const down = n0 + size /* Put it at the top. */, *child;
|
|
||||||
const PH_(priority) down_p = PH_(get_priority)(down);
|
|
||||||
while(i < half) {
|
|
||||||
c = (i << 1) + 1;
|
|
||||||
if(c + 1 < size && PH_(compare)(PH_(get_priority)(n0 + c),
|
|
||||||
PH_(get_priority)(n0 + c + 1)) > 0) c++;
|
|
||||||
child = n0 + c;
|
|
||||||
if(PH_(compare)(down_p, PH_(get_priority)(child)) <= 0) break;
|
|
||||||
PH_(copy)(child, n0 + i);
|
|
||||||
i = c;
|
|
||||||
}
|
|
||||||
PH_(copy)(down, n0 + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Restore the `heap` by permuting the elements so `i` is in the proper place.
|
|
||||||
This reads from the an arbitrary leaf-node into a temporary value, so is
|
|
||||||
slightly more complex than <fn:<PH>sift_down>, but the same thing.
|
|
||||||
@param[heap] At least `i + 1` entries. */
|
|
||||||
static void PH_(sift_down_i)(struct H_(heap) *const heap, size_t i) {
|
|
||||||
const size_t size = (assert(heap && i < heap->a.size), heap->a.size),
|
|
||||||
half = size >> 1;
|
|
||||||
size_t c;
|
|
||||||
PH_(node) *const n0 = heap->a.data, *child, temp;
|
|
||||||
int temp_valid = 0;
|
|
||||||
while(i < half) {
|
|
||||||
c = (i << 1) + 1;
|
|
||||||
if(c + 1 < size && PH_(compare)(PH_(get_priority)(n0 + c),
|
|
||||||
PH_(get_priority)(n0 + c + 1)) > 0) c++;
|
|
||||||
child = n0 + c;
|
|
||||||
if(temp_valid) {
|
|
||||||
if(PH_(compare)(PH_(get_priority)(&temp),
|
|
||||||
PH_(get_priority)(child)) <= 0) break;
|
|
||||||
} else {
|
|
||||||
/* Only happens on the first compare when `i` is in it's original
|
|
||||||
position. */
|
|
||||||
if(PH_(compare)(PH_(get_priority)(n0 + i),
|
|
||||||
PH_(get_priority)(child)) <= 0) break;
|
|
||||||
PH_(copy)(n0 + i, &temp), temp_valid = 1;
|
|
||||||
}
|
|
||||||
PH_(copy)(child, n0 + i);
|
|
||||||
i = c;
|
|
||||||
}
|
|
||||||
if(temp_valid) PH_(copy)(&temp, n0 + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create a `heap` from an array. @order \O(`heap.size`) */
|
|
||||||
static void PH_(heapify)(struct H_(heap) *const heap) {
|
|
||||||
size_t i;
|
|
||||||
assert(heap);
|
|
||||||
if(heap->a.size > 1)
|
|
||||||
for(i = heap->a.size / 2 - 1; (PH_(sift_down_i)(heap, i), i); i--);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Removes from `heap`. Must have a non-zero size. */
|
|
||||||
static PH_(node) PH_(remove)(struct H_(heap) *const heap) {
|
|
||||||
const PH_(node) result = *heap->a.data;
|
|
||||||
assert(heap);
|
|
||||||
if(heap->a.size > 1) {
|
|
||||||
PH_(sift_down)(heap);
|
|
||||||
} else {
|
|
||||||
assert(heap->a.size == 1);
|
|
||||||
heap->a.size = 0;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Initializes `heap` to be idle. @order \Theta(1) @allow */
|
|
||||||
static void H_(heap)(struct H_(heap) *const heap)
|
|
||||||
{ assert(heap), PH_(node_array)(&heap->a); }
|
|
||||||
|
|
||||||
/** Returns `heap` to the idle state where it takes no dynamic memory.
|
|
||||||
@order \Theta(1) @allow */
|
|
||||||
static void H_(heap_)(struct H_(heap) *const heap)
|
|
||||||
{ assert(heap), PH_(node_array_)(&heap->a); }
|
|
||||||
|
|
||||||
/** Sets `heap` to be empty. That is, the size of `heap` will be zero, but if
|
|
||||||
it was previously in an active non-idle state, it continues to be.
|
|
||||||
@param[heap] If null, does nothing. @order \Theta(1) @allow */
|
|
||||||
static void H_(heap_clear)(struct H_(heap) *const heap)
|
|
||||||
{ assert(heap), PH_(node_array_clear)(&heap->a); }
|
|
||||||
|
|
||||||
/** If the `heap` requires differentiation between empty and zero. (One may
|
|
||||||
access it directly at `!heap.a.size`.) @return If the heap is empty. @allow */
|
|
||||||
static int H_(heap_is_empty)(const struct H_(heap) *const heap)
|
|
||||||
{ return assert(heap), !heap->a.size; }
|
|
||||||
|
|
||||||
/** Copies `node` into `heap`.
|
|
||||||
@return Success. @throws[ERANGE, realloc] @order \O(log `heap.size`) @allow */
|
|
||||||
static int H_(heap_add)(struct H_(heap) *const heap, PH_(node) node) {
|
|
||||||
assert(heap);
|
|
||||||
return PH_(node_array_new)(&heap->a) && (PH_(sift_up)(heap, &node), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return The lowest element in `heap` according to `HEAP_COMPARE` or
|
|
||||||
null/zero if the heap is empty. On some heaps, one may have to call
|
|
||||||
<fn:<H>heap_is_empty> in order to differentiate. @order \O(1) @allow */
|
|
||||||
static PH_(value) H_(heap_peek)(const struct H_(heap) *const heap)
|
|
||||||
{ return assert(heap), heap->a.size ? PH_(get_value)(heap->a.data) : 0; }
|
|
||||||
|
|
||||||
/** Remove the lowest element in `heap` according to `HEAP_COMPARE`.
|
|
||||||
@return The same as <fn:<H>heap_peek>. @order \O(log `size`) @allow */
|
|
||||||
static PH_(value) H_(heap_pop)(struct H_(heap) *const heap) {
|
|
||||||
PH_(node) n;
|
|
||||||
return assert(heap), heap->a.size
|
|
||||||
? (n = PH_(remove)(heap), PH_(get_value)(&n)) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The capacity of `heap` will be increased to at least `n` elements beyond
|
|
||||||
the size. Invalidates pointers in `heap.a`. All the elements in `heap.a.size`
|
|
||||||
are part of the heap, but `heap.a.size` <= `index` < `heap.a.capacity`
|
|
||||||
can be used to construct new elements without immediately making them part of
|
|
||||||
the heap, then <fn:<H>heap_append>.
|
|
||||||
@return The start of the buffered space. If `a` is idle and `buffer` is zero,
|
|
||||||
a null pointer is returned, otherwise null indicates an error.
|
|
||||||
@throws[realloc, ERANGE] @allow */
|
|
||||||
static PH_(node) *H_(heap_buffer)(struct H_(heap) *const heap,
|
|
||||||
const size_t n) { return PH_(node_array_buffer)(&heap->a, n); }
|
|
||||||
|
|
||||||
/** Adds and heapifies `n` elements to `heap`. Uses <Floyd, 1964, Treesort> to
|
|
||||||
sift-down all the internal nodes of heap. The heap elements must exist, see
|
|
||||||
<fn:<H>heap_buffer>.
|
|
||||||
@param[n] If zero, returns true without heapifying.
|
|
||||||
@return Success. @order \O(`heap.size` + `n`) <Doberkat, 1984, Floyd> @allow */
|
|
||||||
static void H_(heap_append)(struct H_(heap) *const heap, const size_t n) {
|
|
||||||
PH_(node) *more;
|
|
||||||
/* In practice, pushing uninitialized elements onto the heap does not make
|
|
||||||
sense, so we assert that the elements exist first. */
|
|
||||||
assert(heap && n <= heap->a.capacity - heap->a.size);
|
|
||||||
more = PH_(node_array_append)(&heap->a, n), assert(more);
|
|
||||||
if(n) PH_(heapify)(heap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Shallow-copies and heapifies `master` into `heap`.
|
|
||||||
@param[master] If null, does nothing. @return Success.
|
|
||||||
@order \O(`heap.size` + `copy.size`) @throws[ERANGE, realloc] @allow */
|
|
||||||
static int H_(heap_affix)(struct H_(heap) *const heap,
|
|
||||||
const struct H_(heap) *const master) {
|
|
||||||
PH_(node) *n;
|
|
||||||
assert(heap);
|
|
||||||
if(!master || !master->a.size) return 1;
|
|
||||||
assert(master->a.data);
|
|
||||||
if(!(n = PH_(node_array_buffer)(&heap->a, master->a.size))) return 0;
|
|
||||||
memcpy(n, master->a.data, sizeof *n * master->a.size);
|
|
||||||
n = PH_(node_array_append)(&heap->a, master->a.size), assert(n);
|
|
||||||
PH_(heapify)(heap);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* <!-- iterate interface: Forward the responsibility to array. */
|
|
||||||
#define PAH_(n) HEAP_CAT(array, HEAP_CAT(PH_(node), n))
|
|
||||||
struct PH_(iterator) { struct PAH_(iterator) a; };
|
|
||||||
/** Begins the forward iteration `it` at `h`. */
|
|
||||||
static void PH_(begin)(struct PH_(iterator) *const it,
|
|
||||||
const struct H_(heap) *const h) { PAH_(begin)(&it->a, &h->a); }
|
|
||||||
/** @return The next `it` or null. */
|
|
||||||
static PH_(node) *PH_(next)(struct PH_(iterator) *const it)
|
|
||||||
{ return PAH_(next)(&it->a); }
|
|
||||||
#undef PAH_
|
|
||||||
/* iterate --> */
|
|
||||||
|
|
||||||
/* Define these for traits. */
|
|
||||||
#define BOX_ PH_
|
|
||||||
#define BOX_CONTAINER struct H_(heap)
|
|
||||||
#define BOX_CONTENTS PH_(node)
|
|
||||||
|
|
||||||
#ifdef HEAP_TEST /* <!-- test */
|
|
||||||
/* Forward-declare. */
|
|
||||||
static void (*PH_(to_string))(const PH_(node) *, char (*const)[12]);
|
|
||||||
static const char *(*PH_(heap_to_string))(const struct H_(heap) *);
|
|
||||||
#include "../test/test_heap.h"
|
|
||||||
#endif /* test --> */
|
|
||||||
|
|
||||||
static void PH_(unused_base_coda)(void);
|
|
||||||
static void PH_(unused_base)(void) {
|
|
||||||
PH_(node) unused; memset(&unused, 0, sizeof unused);
|
|
||||||
H_(heap)(0); H_(heap_)(0); H_(heap_clear)(0); H_(heap_is_empty)(0);
|
|
||||||
H_(heap_add)(0, unused); H_(heap_peek)(0); H_(heap_pop)(0);
|
|
||||||
H_(heap_buffer)(0, 0); H_(heap_append)(0, 0); H_(heap_affix)(0, 0);
|
|
||||||
PH_(begin)(0, 0); PH_(next)(0); PH_(unused_base_coda)();
|
|
||||||
}
|
|
||||||
static void PH_(unused_base_coda)(void) { PH_(unused_base)(); }
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(HEAP_TO_STRING) /* base code --><!-- to string trait */
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef HEAP_TO_STRING_NAME /* <!-- name */
|
|
||||||
#define SZ_(n) HEAP_CAT(H_(heap), HEAP_CAT(HEAP_TO_STRING_NAME, n))
|
|
||||||
#else /* name --><!-- !name */
|
|
||||||
#define SZ_(n) HEAP_CAT(H_(heap), n)
|
|
||||||
#endif /* !name --> */
|
|
||||||
#define TSZ_(n) HEAP_CAT(heap_sz, SZ_(n))
|
|
||||||
#ifdef HEAP_VALUE /* <!-- value */
|
|
||||||
/* Check that `HEAP_TO_STRING` is a function implementing this prototype. */
|
|
||||||
static void (*const TSZ_(actual_to_string))(const PH_(value_data) *,
|
|
||||||
char (*const)[12]) = (HEAP_TO_STRING);
|
|
||||||
/** Call <data:<TSZ>actual_to_string> with just the value of `node` and `z`. */
|
|
||||||
static void TSZ_(thunk_to_string)(const PH_(node) *const node,
|
|
||||||
char (*const z)[12]) { TSZ_(actual_to_string)(node->value, z); }
|
|
||||||
#define TO_STRING &TSZ_(thunk_to_string)
|
|
||||||
#else /* value --><!-- !value */
|
|
||||||
#define TO_STRING HEAP_TO_STRING
|
|
||||||
#endif /* !value --> */
|
|
||||||
#include "to_string.h" /** \include */
|
|
||||||
#ifdef HEAP_TEST /* <!-- expect: greedy satisfy forward-declared. */
|
|
||||||
#undef HEAP_TEST
|
|
||||||
static PSZ_(to_string_fn) PH_(to_string) = PSZ_(to_string);
|
|
||||||
static const char *(*PH_(heap_to_string))(const struct H_(heap) *)
|
|
||||||
= &SZ_(to_string);
|
|
||||||
#endif /* expect --> */
|
|
||||||
#undef TSZ_
|
|
||||||
#undef SZ_
|
|
||||||
#undef HEAP_TO_STRING
|
|
||||||
#ifdef HEAP_TO_STRING_NAME
|
|
||||||
#undef HEAP_TO_STRING_NAME
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void PH_(unused_to_string_coda)(void);
|
|
||||||
static void PH_(unused_to_string)(void) { H_(heap_to_string)(0);
|
|
||||||
PH_(unused_to_string_coda)(); }
|
|
||||||
static void PH_(unused_to_string_coda)(void) { PH_(unused_to_string)(); }
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* traits --> */
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef HEAP_EXPECT_TRAIT /* <!-- trait */
|
|
||||||
#undef HEAP_EXPECT_TRAIT
|
|
||||||
#else /* trait --><!-- !trait */
|
|
||||||
#if defined(HEAP_TEST)
|
|
||||||
#error No HEAP_TO_STRING traits defined for HEAP_TEST.
|
|
||||||
#endif
|
|
||||||
#undef HEAP_NAME
|
|
||||||
#undef HEAP_TYPE
|
|
||||||
#undef HEAP_COMPARE
|
|
||||||
#ifdef HEAP_VALUE
|
|
||||||
#undef HEAP_VALUE
|
|
||||||
#endif
|
|
||||||
#undef BOX_
|
|
||||||
#undef BOX_CONTAINER
|
|
||||||
#undef BOX_CONTENTS
|
|
||||||
/* box (multiple traits) --> */
|
|
||||||
#endif /* !trait --> */
|
|
||||||
#undef HEAP_TO_STRING_TRAIT
|
|
||||||
#undef HEAP_TRAITS
|
|
|
@ -67,13 +67,17 @@ static int leap(int y) {
|
||||||
if(!(y % 4)) return 1;
|
if(!(y % 4)) return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/** Not defined for some implementations. (I think the standard also doesn't
|
/** Not defined for some implementations. C11 */
|
||||||
say which order they should be, that's a problem.) */
|
|
||||||
union date32 {
|
union date32 {
|
||||||
uint32_t i32;
|
uint32_t u32;
|
||||||
struct { unsigned year : 23, month : 4, day : 5; };
|
struct { unsigned day : 5, month : 4, year : 23; };
|
||||||
/* This is unsetting. Obv. it should be `struct anonymous {};`. */
|
|
||||||
};
|
};
|
||||||
|
static int date_mixup(union date32 a, union date32 b) { return a.u32 > b.u32; }
|
||||||
|
static void date32_to_string(const union date32 d, char (*const z)[12]) {
|
||||||
|
assert(d.year < 10000 && d.month && d.month <= 31 && d.day && d.day <= 31);
|
||||||
|
sprintf(*z, "%u-%2.2u-%2.2u", d.year % 10000, d.month, d.day);
|
||||||
|
}
|
||||||
|
|
||||||
/** Convert or narrower type or return zero. */
|
/** Convert or narrower type or return zero. */
|
||||||
static union date32 date_to_32(const int y, const int m, const int d) {
|
static union date32 date_to_32(const int y, const int m, const int d) {
|
||||||
union date32 d32 = { 0 };
|
union date32 d32 = { 0 };
|
||||||
|
@ -98,33 +102,37 @@ static unsigned weekday(union date32 d) {
|
||||||
+ "-bed=pen+mad."[d.month] + d.day) % 7;
|
+ "-bed=pen+mad."[d.month] + d.day) % 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define ARRAY_NAME lex
|
#define ARRAY_NAME lex
|
||||||
#define ARRAY_TYPE struct lex
|
#define ARRAY_TYPE struct lex
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
|
struct page_tree_entry_c;
|
||||||
|
static void entry_to_string(struct page_tree_entry_c, char (*)[12]);
|
||||||
struct page {
|
struct page {
|
||||||
union date32 date;
|
|
||||||
struct char_array entry;
|
struct char_array entry;
|
||||||
struct lex_array lexx;
|
struct lex_array lexx;
|
||||||
};
|
};
|
||||||
#define POOL_NAME page
|
#define TREE_NAME page
|
||||||
#define POOL_TYPE struct page
|
#define TREE_KEY union date32
|
||||||
#include "pool.h"
|
#define TREE_VALUE struct page
|
||||||
|
#define TREE_COMPARE &date_mixup
|
||||||
|
#include "tree.h"
|
||||||
|
static void entry_to_string(const struct page_tree_entry_c entry,
|
||||||
|
char (*const z)[12]) { date32_to_string(*entry.key, z); }
|
||||||
|
|
||||||
struct source { char *key, *desc; };
|
struct source { char *key, *desc; };
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
/* Return value. */
|
int success = EXIT_SUCCESS;
|
||||||
int success = EXIT_FAILURE;
|
|
||||||
|
|
||||||
/* For reading in files, overwritten. */
|
/* For reading in files, overwritten. */
|
||||||
DIR *dir = 0;
|
DIR *dir = 0;
|
||||||
struct dirent *de;
|
struct dirent *de;
|
||||||
struct int_array years = ARRAY_IDLE, months = ARRAY_IDLE, days = ARRAY_IDLE;
|
struct int_array years = int_array(), months = int_array(),
|
||||||
|
days = int_array();
|
||||||
int *y, *y_end;
|
int *y, *y_end;
|
||||||
|
|
||||||
/* Backing for individual pages; temporary page yet to be placed. */
|
struct page_tree journal = page_tree();
|
||||||
struct page_pool pages = POOL_IDLE;
|
|
||||||
struct page *page = 0;
|
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
if(argc != 2) { fprintf(stderr, "Needs journal location.\n"
|
if(argc != 2) { fprintf(stderr, "Needs journal location.\n"
|
||||||
|
@ -172,6 +180,7 @@ int main(int argc, char **argv) {
|
||||||
for(m = months.data, m_end = m + months.size; m < m_end; m++) {
|
for(m = months.data, m_end = m + months.size; m < m_end; m++) {
|
||||||
int *d, *d_end;
|
int *d, *d_end;
|
||||||
sprintf(fn, "%.2d", *m);
|
sprintf(fn, "%.2d", *m);
|
||||||
|
printf("month %s\n", fn);
|
||||||
|
|
||||||
/* Get the days as files. */
|
/* Get the days as files. */
|
||||||
if(chdir(fn) == -1 || !(dir = opendir("."))) goto catch;
|
if(chdir(fn) == -1 || !(dir = opendir("."))) goto catch;
|
||||||
|
@ -192,13 +201,17 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
for(d = days.data, d_end = d + days.size; d < d_end; d++) {
|
for(d = days.data, d_end = d + days.size; d < d_end; d++) {
|
||||||
union date32 d32;
|
union date32 d32;
|
||||||
|
struct page *page = 0;
|
||||||
printf("Date: %d-%.2d-%.2d\n", *y, *m, *d);
|
printf("Date: %d-%.2d-%.2d\n", *y, *m, *d);
|
||||||
if(!(d32 = date_to_32(*y, *m, *d)).year)
|
if(!(d32 = date_to_32(*y, *m, *d)).year)
|
||||||
{ errno = EILSEQ; goto catch; }
|
{ errno = EILSEQ; goto catch; }
|
||||||
sprintf(fn, "%.2d.txt", *d);
|
sprintf(fn, "%.2d.txt", *d);
|
||||||
if(!(page = page_pool_new(&pages))) goto catch;
|
if(page_tree_bulk_add(&journal, d32, &page) != TREE_UNIQUE) {
|
||||||
char_array(&page->entry);
|
if(!errno) fprintf(stderr, "Not unique?\n"), errno = EDOM;
|
||||||
lex_array(&page->lexx);
|
goto catch;
|
||||||
|
}
|
||||||
|
page->entry = char_array();
|
||||||
|
page->lexx = lex_array();
|
||||||
if(!append_file(&page->entry, fn)) goto catch;
|
if(!append_file(&page->entry, fn)) goto catch;
|
||||||
printf("%s", page->entry.data);
|
printf("%s", page->entry.data);
|
||||||
printf("Lexing:\n");
|
printf("Lexing:\n");
|
||||||
|
@ -235,20 +248,32 @@ int main(int argc, char **argv) {
|
||||||
if(chdir("..") == -1) goto catch;
|
if(chdir("..") == -1) goto catch;
|
||||||
break; /* fixme */
|
break; /* fixme */
|
||||||
}
|
}
|
||||||
{ success = EXIT_SUCCESS; goto finally; }
|
page_tree_bulk_finish(&journal);
|
||||||
|
goto finally;
|
||||||
catch:
|
catch:
|
||||||
|
success = EXIT_FAILURE;
|
||||||
perror("interpret");
|
perror("interpret");
|
||||||
finally:
|
finally:
|
||||||
if(dir && closedir(dir)) success = EXIT_FAILURE, perror("dir");
|
if(dir && closedir(dir)) success = EXIT_FAILURE, perror("dir");
|
||||||
int_array_(&years);
|
int_array_(&years);
|
||||||
int_array_(&months);
|
int_array_(&months);
|
||||||
int_array_(&days);
|
int_array_(&days);
|
||||||
|
struct page_tree_entry entry;
|
||||||
|
for(struct page_tree_iterator it = page_tree_begin(&journal);
|
||||||
|
(entry = page_tree_next(&it)).key; ) {
|
||||||
|
struct page *const page = entry.value;
|
||||||
|
char z[12];
|
||||||
|
date32_to_string(*entry.key, &z);
|
||||||
|
printf("Page %s gone.\n", z);
|
||||||
|
lex_array_(&page->lexx);
|
||||||
|
char_array_(&page->entry);
|
||||||
|
}
|
||||||
/* Got up to creating a page, but didn't add to the journal. */
|
/* Got up to creating a page, but didn't add to the journal. */
|
||||||
if(page) { lex_array_(&page->lexx); char_array(&page->entry); }
|
/*if(page) { lex_array_(&page->lexx); char_array(&page->entry); }
|
||||||
{
|
{
|
||||||
struct page *page;
|
struct page *page;
|
||||||
while(page = page_array_pop(&pages))
|
while(page = page_array_pop(&journal))
|
||||||
lex_array_(&page->lexx), char_array_(&page->entry);
|
lex_array_(&page->lexx), char_array_(&page->entry);
|
||||||
}
|
} memory leak! */
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
406
src/list_coda.h
406
src/list_coda.h
|
@ -1,406 +0,0 @@
|
||||||
/* @license 2021 Neil Edelman, distributed under the terms of the
|
|
||||||
[MIT License](https://opensource.org/licenses/MIT).
|
|
||||||
|
|
||||||
@subtitle Recur trait
|
|
||||||
|
|
||||||
Included by `list.h`.
|
|
||||||
|
|
||||||
@param[LC_]
|
|
||||||
A one-argument macro producing a name that is responsible for the name of the
|
|
||||||
functions. The caller is responsible for undefining `LC_`.
|
|
||||||
|
|
||||||
@param[COMPARE_NAME, COMPARE_IS_EQUAL, COMPARE]
|
|
||||||
Optional unique name that satisfies `C` naming conventions when mangled,
|
|
||||||
and a function implementing, for `COMPARE_IS_EQUAL`,
|
|
||||||
<typedef:<PLC>bipredicate_fn> that establishes an equivalence relation, or
|
|
||||||
for `COMPARE`, <typedef:<PLC>compare_fn> that establishes a total
|
|
||||||
order. There can be multiple comparable functions, but only one can omit
|
|
||||||
`COMPARE_NAME`.
|
|
||||||
|
|
||||||
@std C89 */
|
|
||||||
|
|
||||||
#if !defined(LC_) || !(defined(LIST_IS_EQUAL) ^ defined(LIST_COMPARE)) \
|
|
||||||
|| !defined(L_) || !defined(PL_)
|
|
||||||
#error Unexpected preprocessor symbols.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LIST_CODA_H /* <!-- idempotent */
|
|
||||||
#define LIST_CODA_H
|
|
||||||
#if defined(PLC_)
|
|
||||||
#error Unexpected defines.
|
|
||||||
#endif
|
|
||||||
#define PLC_(n) LIST_CAT(list_coda, LC_(n))
|
|
||||||
/* <fn:<PLC>boolean> operations bit-vector; dummy ensures closed. */
|
|
||||||
enum list_operation {
|
|
||||||
LIST_SUBTRACTION_AB = 1,
|
|
||||||
LIST_SUBTRACTION_BA = 2, RECURA,
|
|
||||||
LIST_INTERSECTION = 4, RECURB, RECURC, RECURD,
|
|
||||||
LIST_DEFAULT_A = 8, RECURE, RECURF, RECURG, RECURH, RECURI, RECURJ,
|
|
||||||
RECURK,
|
|
||||||
LIST_DEFAULT_B = 16, RECURL, RECURM, RECURN, RECURO, RECURP, RECURQ,
|
|
||||||
RECURR, RECURS, RECURT, RECURU, RECURV, RECURW, RECURX, RECURY, RECURZ
|
|
||||||
};
|
|
||||||
#endif /* idempotent --> */
|
|
||||||
|
|
||||||
/** Returns a boolean given two read-only <tag:<L>listlink>. */
|
|
||||||
typedef int (*PLC_(bipredicate_fn))(const struct L_(listlink) *,
|
|
||||||
const struct L_(listlink) *);
|
|
||||||
|
|
||||||
#ifdef LIST_COMPARE /* <!-- compare */
|
|
||||||
|
|
||||||
/** Three-way comparison on a totally order set of <tag:<L>listlink>;
|
|
||||||
returns an integer value less then, equal to, greater then zero, if
|
|
||||||
`a < b`, `a == b`, `a > b`, respectively. */
|
|
||||||
typedef int (*PLC_(compare_fn))(const struct L_(listlink) *a,
|
|
||||||
const struct L_(listlink) *b);
|
|
||||||
|
|
||||||
/* Check that `LIST_COMPARE` is a function implementing
|
|
||||||
<typedef:<PLC>compare_fn>. */
|
|
||||||
static const PLC_(compare_fn) PLC_(compare) = (LIST_COMPARE);
|
|
||||||
|
|
||||||
/** Lexicographically compares `alist` to `blist`. Null values are before
|
|
||||||
everything.
|
|
||||||
@return `a < b`: negative; `a == b`: zero; `a > b`: positive.
|
|
||||||
@implements <typedef:<PLC>compare_fn> (one can `qsort` an array of lists, as
|
|
||||||
long as one calls <fn:<L>list_self_correct> on it's elements)
|
|
||||||
@order \Theta(min(|`alist`|, |`blist`|)) @allow */
|
|
||||||
static int LC_(compare)(const struct L_(list) *const alist,
|
|
||||||
const struct L_(list) *const blist) {
|
|
||||||
struct L_(listlink) *a, *b;
|
|
||||||
int diff;
|
|
||||||
/* Null counts as `-\infty`. */
|
|
||||||
if(!alist) {
|
|
||||||
return blist ? -1 : 0;
|
|
||||||
} else if(!blist) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
/* Compare element by element. */
|
|
||||||
for(a = alist->u.flat.next, b = blist->u.flat.next; ;
|
|
||||||
a = a->next, b = b->next) {
|
|
||||||
if(!a->next) {
|
|
||||||
return b->next ? -1 : 0;
|
|
||||||
} else if(!b->next) {
|
|
||||||
return 1;
|
|
||||||
} else if((diff = PLC_(compare)(a, b))) {
|
|
||||||
return diff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Merges `from` into `to`, preferring elements from `to` go in the front.
|
|
||||||
@order \O(|`from`| + |`to`|). */
|
|
||||||
static void LC_(merge)(struct L_(list) *const to, struct L_(list) *const from) {
|
|
||||||
struct L_(listlink) *head, **x = &head, *prev = &to->u.as_head.head, *t, *f;
|
|
||||||
assert(to && to->u.flat.next && to->u.flat.prev
|
|
||||||
&& from && from->u.flat.next && from->u.flat.prev && from != to);
|
|
||||||
/* Empty. */
|
|
||||||
if(!(f = from->u.flat.next)->next) return;
|
|
||||||
if(!(t = to->u.flat.next)->next)
|
|
||||||
{ PL_(move)(from, &to->u.as_tail.tail); return; }
|
|
||||||
/* Exclude sentinel. */
|
|
||||||
from->u.flat.prev->next = to->u.flat.prev->next = 0;
|
|
||||||
/* Merge. */
|
|
||||||
for( ; ; ) {
|
|
||||||
if(PLC_(compare)(t, f) <= 0) {
|
|
||||||
t->prev = prev, prev = *x = t, x = &t->next;
|
|
||||||
if(!(t = t->next)) { *x = f; goto from_left; }
|
|
||||||
} else {
|
|
||||||
f->prev = prev, prev = *x = f, x = &f->next;
|
|
||||||
if(!(f = f->next)) { *x = t; break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(0) {
|
|
||||||
from_left:
|
|
||||||
f->prev = prev;
|
|
||||||
/* Switch sentinels. */
|
|
||||||
f = from->u.flat.prev;
|
|
||||||
to->u.flat.prev = f;
|
|
||||||
f->next = &from->u.as_tail.tail;
|
|
||||||
} else {
|
|
||||||
t->prev = prev;
|
|
||||||
}
|
|
||||||
/* Erase `from`. */
|
|
||||||
from->u.flat.next = &from->u.as_tail.tail;
|
|
||||||
from->u.flat.prev = &from->u.as_head.head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Merges the two top runs referenced by `head_ptr` in stack form. */
|
|
||||||
static void PLC_(merge_runs)(struct L_(listlink) **const head_ptr) {
|
|
||||||
struct L_(listlink) *head = *head_ptr, **x = &head, *b = head, *a = b->prev,
|
|
||||||
*const prev = a->prev;
|
|
||||||
assert(head_ptr && a && b);
|
|
||||||
for( ; ; ) {
|
|
||||||
if(PLC_(compare)(a, b) <= 0) {
|
|
||||||
*x = a, x = &a->next;
|
|
||||||
if(!(a = a->next)) { *x = b; break; }
|
|
||||||
} else {
|
|
||||||
*x = b, x = &b->next;
|
|
||||||
if(!(b = b->next)) { *x = a; break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
head->prev = prev, *head_ptr = head; /* `prev` is the previous run. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The list form of `list` is restored from `head` in stack form with two
|
|
||||||
runs. */
|
|
||||||
static void PLC_(merge_final)(struct L_(list) *const list,
|
|
||||||
struct L_(listlink) *head) {
|
|
||||||
struct L_(listlink) *prev = 0, **x = &list->u.flat.next,
|
|
||||||
*b = head, *a = head->prev;
|
|
||||||
assert(list && b && a && !a->prev);
|
|
||||||
for( ; ; ) {
|
|
||||||
if(PLC_(compare)(a, b) <= 0) {
|
|
||||||
a->prev = prev, prev = *x = a, x = &a->next;
|
|
||||||
if(!(a = a->next)) { a = *x = b; break; }
|
|
||||||
} else {
|
|
||||||
b->prev = prev, prev = *x = b, x = &b->next;
|
|
||||||
if(!(b = b->next)) { *x = a; break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
do; while(a->prev = prev, prev = a, a = a->next);
|
|
||||||
prev->next = &list->u.as_tail.tail, list->u.flat.prev = prev;
|
|
||||||
/* Not empty. */
|
|
||||||
assert(list->u.flat.next && list->u.flat.next != &list->u.as_tail.tail);
|
|
||||||
list->u.flat.next->prev = &list->u.as_head.head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Natural merge sort `list`; the requirement for \O(\log |`list`|) space is
|
|
||||||
satisfied by converting it to a singly-linked with `prev` as a stack of
|
|
||||||
increasing lists, which are merged. */
|
|
||||||
static void PLC_(sort)(struct L_(list) *const list) {
|
|
||||||
/* Add `[-1,0,1]`: unique identifier for nine weakly-ordered transitions. */
|
|
||||||
enum { DEC = 1, EQ = 4, INC = 7 };
|
|
||||||
int mono = EQ, cmp;
|
|
||||||
struct L_(listlink) *a, *b, *c, *dec_iso = /* Unused. */0;
|
|
||||||
struct { size_t count; struct L_(listlink) *head, *prev; } run;
|
|
||||||
/* Closed sentinel list. */
|
|
||||||
assert(list
|
|
||||||
&& list->u.flat.next && !list->u.flat.zero && list->u.flat.prev);
|
|
||||||
if(a = list->u.flat.next, !(b = a->next)) return; /* Empty. */
|
|
||||||
/* Identify runs of monotonicity until `b` sentinel. */
|
|
||||||
run.count = 0, run.prev = 0, run.head = a;
|
|
||||||
for(c = b->next; c; a = b, b = c, c = c->next) {
|
|
||||||
cmp = PLC_(compare)(b, a);
|
|
||||||
switch(mono + (0 < cmp) - (cmp < 0)) {
|
|
||||||
/* Valley and mountain inflection. */
|
|
||||||
case INC - 1: a->next = 0; /* _Sic_. */
|
|
||||||
case DEC + 1: break;
|
|
||||||
/* Decreasing more and levelled off from decreasing. */
|
|
||||||
case DEC - 1: b->next = dec_iso; dec_iso = run.head = b; continue;
|
|
||||||
case DEC + 0: b->next = a->next; a->next = b; continue;
|
|
||||||
/* Turning down and up. */
|
|
||||||
case EQ - 1: a->next = 0; b->next = run.head; dec_iso = run.head = b;
|
|
||||||
mono = DEC; continue;
|
|
||||||
case EQ + 1: mono = INC; continue;
|
|
||||||
case EQ + 0: /* Same. _Sic_. */
|
|
||||||
case INC + 0: /* Levelled off from increasing. _Sic_. */
|
|
||||||
case INC + 1: continue; /* Increasing more. */
|
|
||||||
}
|
|
||||||
/* Binary carry sequence, <https://oeis.org/A007814>, one delayed so
|
|
||||||
always room for final merge. */
|
|
||||||
if(run.count) {
|
|
||||||
size_t rc;
|
|
||||||
for(rc = run.count - 1; rc & 1; rc >>= 1)
|
|
||||||
PLC_(merge_runs)(&run.prev);
|
|
||||||
}
|
|
||||||
/* Add to runs, advance; `b` becomes `a` forthwith. */
|
|
||||||
run.head->prev = run.prev, run.prev = run.head, run.count++;
|
|
||||||
run.head = b, mono = EQ;
|
|
||||||
}
|
|
||||||
/* Last run; go into an accepting state. */
|
|
||||||
if(mono != DEC) {
|
|
||||||
if(!run.count) return; /* Sorted already. */
|
|
||||||
a->next = 0; /* Last one of increasing or equal. */
|
|
||||||
} else { /* Decreasing. */
|
|
||||||
assert(dec_iso);
|
|
||||||
run.head = dec_iso;
|
|
||||||
if(!run.count) { /* Restore the pointers without having two runs. */
|
|
||||||
list->u.flat.next = dec_iso, dec_iso->prev = &list->u.as_head.head;
|
|
||||||
for(a = dec_iso, b = a->next; b; a = b, b = b->next) b->prev = a;
|
|
||||||
list->u.flat.prev = a, a->next = &list->u.as_tail.tail;
|
|
||||||
return; /* Reverse sorted; now good as well. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(run.count);
|
|
||||||
/* (Actually slower to merge the last one eagerly. So do nothing.) */
|
|
||||||
run.head->prev = run.prev, run.count++;
|
|
||||||
/* Merge leftovers from the other direction, saving one for final. */
|
|
||||||
while(run.head->prev->prev) PLC_(merge_runs)(&run.head);
|
|
||||||
PLC_(merge_final)(list, run.head);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Performs a stable, adaptive sort of `list` according to `compare`.
|
|
||||||
@order \Omega(|`list`|), \O(|`list`| log |`list`|) @allow */
|
|
||||||
static void LC_(sort)(struct L_(list) *const list) { PLC_(sort)(list); }
|
|
||||||
|
|
||||||
/** Private: `alist` `mask` `blist` -> `result`. Prefers `a` to `b` when equal.
|
|
||||||
Either could be null.
|
|
||||||
@order \O(|`a`| + |`b`|) */
|
|
||||||
static void PLC_(boolean)(struct L_(list) *const alist,
|
|
||||||
struct L_(list) *const blist,
|
|
||||||
const enum list_operation mask, struct L_(list) *const result) {
|
|
||||||
struct L_(listlink) *temp,
|
|
||||||
*a = alist ? alist->u.flat.next : 0,
|
|
||||||
*b = blist ? blist->u.flat.next : 0;
|
|
||||||
int comp;
|
|
||||||
assert((!result || (result != alist && result != blist))
|
|
||||||
&& (!alist || (alist != blist)));
|
|
||||||
if(a && b) {
|
|
||||||
while(a->next && b->next) {
|
|
||||||
comp = PLC_(compare)(a, b);
|
|
||||||
if(comp < 0) {
|
|
||||||
temp = a, a = a->next;
|
|
||||||
if(mask & LIST_SUBTRACTION_AB) {
|
|
||||||
PL_(remove)(temp);
|
|
||||||
if(result) PL_(push)(result, temp);
|
|
||||||
}
|
|
||||||
} else if(comp > 0) {
|
|
||||||
temp = b, b = b->next;
|
|
||||||
if(mask & LIST_SUBTRACTION_BA) {
|
|
||||||
PL_(remove)(temp);
|
|
||||||
if(result) PL_(push)(result, temp);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
temp = a, a = a->next, b = b->next;
|
|
||||||
if(mask & LIST_INTERSECTION) {
|
|
||||||
PL_(remove)(temp);
|
|
||||||
if(result) PL_(push)(result, temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(a && mask & LIST_DEFAULT_A) {
|
|
||||||
while((temp = a, a = a->next)) {
|
|
||||||
PL_(remove)(temp);
|
|
||||||
if(result) PL_(push)(result, temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(b && mask & LIST_DEFAULT_B) {
|
|
||||||
while((temp = b, b = b->next)) {
|
|
||||||
PL_(remove)(temp);
|
|
||||||
if(result) PL_(push)(result, temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Subtracts `a` from `b`, as sequential sorted individual elements, and moves
|
|
||||||
it to `result`. All elements are removed from `a`. All parameters must be
|
|
||||||
unique or can be null.
|
|
||||||
|
|
||||||
For example, if `a` contains `(A, B, D)` and `b` contains `(B, C)` then
|
|
||||||
`(a:A, a:D)` would be moved to `result`.
|
|
||||||
@order \O(|`a`| + |`b`|) @allow */
|
|
||||||
static void LC_(subtraction_to)(struct L_(list) *const a,
|
|
||||||
struct L_(list) *const b, struct L_(list) *const result) {
|
|
||||||
PLC_(boolean)(a, b, LIST_SUBTRACTION_AB | LIST_DEFAULT_A, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Moves the union of `a` and `b` as sequential sorted individual elements to
|
|
||||||
`result`. Equal elements are moved from `a`. All parameters must be unique or
|
|
||||||
can be null.
|
|
||||||
|
|
||||||
For example, if `a` contains `(A, B, D)` and `b` contains `(B, C)` then
|
|
||||||
`(a:A, a:B, b:C, a:D)` would be moved to `result`.
|
|
||||||
@order \O(|`a`| + |`b`|) @allow */
|
|
||||||
static void LC_(union_to)(struct L_(list) *const a,
|
|
||||||
struct L_(list) *const b, struct L_(list) *const result) {
|
|
||||||
PLC_(boolean)(a, b, LIST_SUBTRACTION_AB | LIST_SUBTRACTION_BA
|
|
||||||
| LIST_INTERSECTION | LIST_DEFAULT_A | LIST_DEFAULT_B, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Moves the intersection of `a` and `b` as sequential sorted individual
|
|
||||||
elements to `result`. Equal elements are moved from `a`. All parameters must
|
|
||||||
be unique or can be null.
|
|
||||||
|
|
||||||
For example, if `a` contains `(A, B, D)` and `b` contains `(B, C)` then
|
|
||||||
`(a:B)` would be moved to `result`.
|
|
||||||
@order \O(|`a`| + |`b`|) @allow */
|
|
||||||
static void LC_(intersection_to)(struct L_(list) *const a,
|
|
||||||
struct L_(list) *const b, struct L_(list) *const result) {
|
|
||||||
PLC_(boolean)(a, b, LIST_INTERSECTION, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Moves `a` exclusive-or `b` as sequential sorted individual elements to
|
|
||||||
`result`. Equal elements are moved from `a`. All parameters must be unique or
|
|
||||||
can be null.
|
|
||||||
|
|
||||||
For example, if `a` contains `(A, B, D)` and `b` contains `(B, C)` then
|
|
||||||
`(a:A, b:C, a:D)` would be moved to `result`.
|
|
||||||
@order O(|`a`| + |`b`|) @allow */
|
|
||||||
static void LC_(xor_to)(struct L_(list) *const a, struct L_(list) *const b,
|
|
||||||
struct L_(list) *const result) {
|
|
||||||
PLC_(boolean)(a, b, LIST_SUBTRACTION_AB | LIST_SUBTRACTION_BA
|
|
||||||
| LIST_DEFAULT_A | LIST_DEFAULT_B, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** !compare(`a`, `b`) == equals(`a`, `b`).
|
|
||||||
@implements <typedef:<PLC>bipredicate_fn> */
|
|
||||||
static int PLC_(is_equal)(const struct L_(listlink) *const a,
|
|
||||||
const struct L_(listlink) *const b) { return !PLC_(compare)(a, b); }
|
|
||||||
|
|
||||||
#else /* compare --><!-- is equal */
|
|
||||||
|
|
||||||
/* Check that `LIST_IS_EQUAL` is a function implementing
|
|
||||||
<typedef:<PLC>bipredicate_fn>. */
|
|
||||||
static const PLC_(bipredicate_fn) PLC_(is_equal) = (LIST_IS_EQUAL);
|
|
||||||
|
|
||||||
#endif /* is equal --> */
|
|
||||||
|
|
||||||
/** @return If `lista` piecewise equals `listb`, which both can be null.
|
|
||||||
@order \O(min(|`lista`|, |`listb`|)) @allow */
|
|
||||||
static int LC_(is_equal)(const struct L_(list) *const lista,
|
|
||||||
const struct L_(list) *const listb) {
|
|
||||||
const struct L_(listlink) *a, *b;
|
|
||||||
if(!lista) return !listb;
|
|
||||||
if(!listb) return 0;
|
|
||||||
for(a = lista->u.flat.next, b = listb->u.flat.next; ;
|
|
||||||
a = a->next, b = b->next) {
|
|
||||||
if(!a->next) return !b->next;
|
|
||||||
if(!b->next) return 0;
|
|
||||||
if(!PLC_(is_equal)(a, b)) return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Moves all local-duplicates of `from` to the end of `to`.
|
|
||||||
|
|
||||||
For example, if `from` is `(A, B, B, A)`, it would concatenate the second
|
|
||||||
`(B)` to `to` and leave `(A, B, A)` in `from`. If one <fn:<LC>sort> `from`
|
|
||||||
first, `(A, A, B, B)`, the global duplicates will be transferred, `(A, B)`.
|
|
||||||
@order \O(|`from`|) @allow */
|
|
||||||
static void LC_(duplicates_to)(struct L_(list) *const from,
|
|
||||||
struct L_(list) *const to) {
|
|
||||||
struct L_(listlink) *a = from->u.flat.next, *b, *temp;
|
|
||||||
assert(from);
|
|
||||||
if(!(b = a->next)) return;
|
|
||||||
while(b->next) {
|
|
||||||
if(!PLC_(is_equal)(a, b)) {
|
|
||||||
a = b, b = b->next;
|
|
||||||
} else {
|
|
||||||
temp = b, b = b->next;
|
|
||||||
PL_(remove)(temp);
|
|
||||||
if(to) PL_(push)(to, temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PLC_(unused_coda_coda)(void);
|
|
||||||
static void PLC_(unused_coda)(void) {
|
|
||||||
#ifdef LIST_COMPARE /* <!-- compare */
|
|
||||||
LC_(compare)(0, 0); LC_(merge)(0, 0); LC_(sort)(0);
|
|
||||||
LC_(subtraction_to)(0, 0, 0); LC_(union_to)(0, 0, 0);
|
|
||||||
LC_(intersection_to)(0, 0, 0); LC_(xor_to)(0, 0, 0);
|
|
||||||
#endif /* compare --> */
|
|
||||||
LC_(is_equal)(0, 0); LC_(duplicates_to)(0, 0);
|
|
||||||
PLC_(unused_coda_coda)();
|
|
||||||
}
|
|
||||||
static void PLC_(unused_coda_coda)(void) { PLC_(unused_coda)(); }
|
|
||||||
|
|
||||||
#ifdef BOX_COMPARE
|
|
||||||
#undef BOX_COMPARE
|
|
||||||
#endif
|
|
||||||
#ifdef BOX_IS_EQUAL
|
|
||||||
#undef BOX_IS_EQUAL
|
|
||||||
#endif
|
|
||||||
#ifdef BOX_COMPARE_NAME
|
|
||||||
#undef BOX_COMPARE_NAME
|
|
||||||
#endif
|
|
|
@ -3,13 +3,13 @@
|
||||||
|
|
||||||
@subtitle To string trait
|
@subtitle To string trait
|
||||||
|
|
||||||
A trait relying on the iterate interface (`iterator`, `begin`, `next`.)
|
Interface defined by `BOX_`, `BOX`, and `BOX_CONTENT`.
|
||||||
|
|
||||||
@param[SZ_]
|
@param[STR_(n)]
|
||||||
A one-argument macro producing a name that is responsible for the name of the
|
A one-argument macro producing a name that is responsible for the name of the
|
||||||
to string function. Should be something like
|
to string function. Should be something like
|
||||||
`SZ_(to_string) -> widget_foo_to_string`. The caller is responsible for
|
`STR_(to_string) -> widget_foo_to_string`. The caller is responsible for
|
||||||
undefining `SZ_`.
|
undefining `STR_`.
|
||||||
|
|
||||||
@param[TO_STRING]
|
@param[TO_STRING]
|
||||||
Function implementing <typedef:<PZ>to_string_fn>.
|
Function implementing <typedef:<PZ>to_string_fn>.
|
||||||
|
@ -27,9 +27,9 @@
|
||||||
|
|
||||||
@std C89 */
|
@std C89 */
|
||||||
|
|
||||||
#if !defined(BOX_) || !defined(BOX_CONTAINER) || !defined(BOX_CONTENTS) \
|
#if !defined(BOX_) || !defined(BOX) || !defined(BOX_CONTENT) \
|
||||||
|| !defined(SZ_) || !defined(TO_STRING)
|
|| !defined(STR_) || !defined(TO_STRING)
|
||||||
#error Unexpected preprocessor symbols. Check that one including it as a trait.
|
#error Unexpected preprocessor symbols.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(TO_STRING_H) \
|
#if defined(TO_STRING_H) \
|
||||||
|
@ -44,13 +44,13 @@
|
||||||
#ifndef TO_STRING_H /* <!-- idempotent */
|
#ifndef TO_STRING_H /* <!-- idempotent */
|
||||||
#define TO_STRING_H
|
#define TO_STRING_H
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#if defined(TO_STRING_CAT_) || defined(TO_STRING_CAT) || defined(PSZ_)
|
#if defined(TO_STRING_CAT_) || defined(TO_STRING_CAT) || defined(PSTR_)
|
||||||
#error Unexpected defines.
|
#error Unexpected defines.
|
||||||
#endif
|
#endif
|
||||||
/* <Kernighan and Ritchie, 1988, p. 231>. */
|
/* <Kernighan and Ritchie, 1988, p. 231>. */
|
||||||
#define TO_STRING_CAT_(n, m) n ## _ ## m
|
#define TO_STRING_CAT_(n, m) n ## _ ## m
|
||||||
#define TO_STRING_CAT(n, m) TO_STRING_CAT_(n, m)
|
#define TO_STRING_CAT(n, m) TO_STRING_CAT_(n, m)
|
||||||
#define PSZ_(n) TO_STRING_CAT(to_string, SZ_(n))
|
#define PSTR_(n) TO_STRING_CAT(to_string, STR_(n))
|
||||||
#if defined(TO_STRING_EXTERN) || defined(TO_STRING_INTERN) /* <!-- ntern */
|
#if defined(TO_STRING_EXTERN) || defined(TO_STRING_INTERN) /* <!-- ntern */
|
||||||
extern char to_string_buffers[4][256];
|
extern char to_string_buffers[4][256];
|
||||||
extern const unsigned to_string_buffers_no;
|
extern const unsigned to_string_buffers_no;
|
||||||
|
@ -78,51 +78,46 @@ static unsigned to_string_buffer_i;
|
||||||
#define TO_STRING_RIGHT ')'
|
#define TO_STRING_RIGHT ')'
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* An alias to the box. */
|
typedef BOX PSTR_(box);
|
||||||
typedef BOX_CONTAINER PSZ_(box);
|
typedef BOX_CONTENT PSTR_(element_c);
|
||||||
|
|
||||||
/* An alias to the individual type contained in the box. */
|
/** <src/to_string.h>: responsible for turning the argument into a 12-`char`
|
||||||
typedef BOX_CONTENTS PSZ_(type);
|
null-terminated output string. */
|
||||||
|
typedef void (*PSTR_(to_string_fn))(PSTR_(element_c), char (*)[12]);
|
||||||
/** <to_string.h>: responsible for turning the argument into a 12-`char`
|
|
||||||
null-terminated output string. `<PSZ>type` is contracted to be an internal
|
|
||||||
iteration type of the box. */
|
|
||||||
typedef void (*PSZ_(to_string_fn))(const PSZ_(type) *, char (*)[12]);
|
|
||||||
/* Check that `TO_STRING` is a function implementing
|
/* Check that `TO_STRING` is a function implementing
|
||||||
<typedef:<PSZ>to_string>. */
|
<typedef:<PSTR>to_string>. */
|
||||||
static const PSZ_(to_string_fn) PSZ_(to_string) = (TO_STRING);
|
static const PSTR_(to_string_fn) PSTR_(to_string) = (TO_STRING);
|
||||||
|
|
||||||
/** <src/to_string.h>: print the contents of `box` in a static string buffer of
|
/** <src/to_string.h>: print the contents of `box` in a static string buffer of
|
||||||
256 bytes, with limitations of only printing 4 things at a time. `<PSZ>box` is
|
256 bytes, with limitations of only printing 4 things at a time. `<STR>` is
|
||||||
contracted to be the box itself. `<SZ>` is loosely contracted to be a name
|
loosely contracted to be a name `<X>box[<X_TO_STRING_NAME>]`.
|
||||||
`<X>box[<X_TO_STRING_NAME>]`.
|
|
||||||
@return Address of the static buffer. @order \Theta(1) @allow */
|
@return Address of the static buffer. @order \Theta(1) @allow */
|
||||||
static const char *SZ_(to_string)(const PSZ_(box) *const box) {
|
static const char *STR_(to_string)(const PSTR_(box) *const box) {
|
||||||
const char comma = ',', space = ' ', *const ellipsis = "…",
|
const char comma = ',', space = ' ', ellipsis[] = "…",
|
||||||
left = TO_STRING_LEFT, right = TO_STRING_RIGHT;
|
left = TO_STRING_LEFT, right = TO_STRING_RIGHT;
|
||||||
const size_t ellipsis_len = strlen(ellipsis);
|
const size_t ellipsis_len = sizeof ellipsis - 1;
|
||||||
char *const buffer = to_string_buffers[to_string_buffer_i++], *b = buffer;
|
char *const buffer = to_string_buffers[to_string_buffer_i++], *b = buffer;
|
||||||
size_t advance, size;
|
size_t advance;
|
||||||
const PSZ_(type) *x;
|
PSTR_(element_c) x;
|
||||||
struct BOX_(iterator) it;
|
struct BOX_(forward) it;
|
||||||
int is_sep = 0;
|
int is_sep = 0;
|
||||||
/* Minimum size: "(" "XXXXXXXXXXX" "," "…" ")" "\0". */
|
/* Minimum size: "(" "XXXXXXXXXXX" "," "…" ")" "\0". */
|
||||||
assert(box && !(to_string_buffers_no & (to_string_buffers_no - 1))
|
assert(box && !(to_string_buffers_no & (to_string_buffers_no - 1))
|
||||||
&& to_string_buffer_size >= 1 + 11 + 1 + ellipsis_len + 1 + 1);
|
&& to_string_buffer_size >= 1 + 11 + 1 + ellipsis_len + 1 + 1);
|
||||||
/* Advance the buffer for next time. */
|
/* Advance the buffer for next time. */
|
||||||
to_string_buffer_i &= to_string_buffers_no - 1;
|
to_string_buffer_i &= to_string_buffers_no - 1;
|
||||||
/* Begin iteration. */
|
it = BOX_(forward_begin)(box);
|
||||||
BOX_(begin)(&it, box);
|
|
||||||
*b++ = left;
|
*b++ = left;
|
||||||
while(x = BOX_(next)(&it)) {
|
while(BOX_(is_element_c)(x = BOX_(forward_next)(&it))) {
|
||||||
PSZ_(to_string)(x, (char (*)[12])b);
|
PSTR_(to_string)(x, (char (*)[12])b);
|
||||||
/* Paranoid about '\0'. */
|
/* Paranoid about '\0'. */
|
||||||
for(advance = 0; *b != '\0' && advance < 11; b++, advance++);
|
for(advance = 0; *b != '\0' && advance < 11; b++, advance++);
|
||||||
is_sep = 1, *b++ = comma, *b++ = space;
|
is_sep = 1, *b++ = comma, *b++ = space;
|
||||||
/* Greedy typesetting: enough for "XXXXXXXXXXX" "," "…" ")" "\0". */
|
/* Greedy typesetting: enough for "XXXXXXXXXXX" "," "…" ")" "\0". */
|
||||||
if((size = (size_t)(b - buffer))
|
if((size_t)(b - buffer)
|
||||||
> to_string_buffer_size - 11 - 1 - ellipsis_len - 1 - 1)
|
> to_string_buffer_size - 11 - 1 - ellipsis_len - 1 - 1)
|
||||||
{ if(BOX_(next)(&it)) goto ellipsis; else break; }
|
if(BOX_(is_element_c)(BOX_(forward_next)(&it))) goto ellipsis;
|
||||||
|
else break;
|
||||||
}
|
}
|
||||||
if(is_sep) b -= 2;
|
if(is_sep) b -= 2;
|
||||||
*b++ = right;
|
*b++ = right;
|
||||||
|
@ -137,10 +132,10 @@ terminate:
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PSZ_(unused_to_string_coda)(void);
|
static void PSTR_(unused_to_string_coda)(void);
|
||||||
static void PSZ_(unused_to_string)(void)
|
static void PSTR_(unused_to_string)(void)
|
||||||
{ SZ_(to_string)(0); PSZ_(unused_to_string_coda)(); }
|
{ STR_(to_string)(0); PSTR_(unused_to_string_coda)(); }
|
||||||
static void PSZ_(unused_to_string_coda)(void) { PSZ_(unused_to_string)(); }
|
static void PSTR_(unused_to_string_coda)(void) { PSTR_(unused_to_string)(); }
|
||||||
|
|
||||||
#undef TO_STRING
|
#undef TO_STRING
|
||||||
#ifdef TO_STRING_NAME
|
#ifdef TO_STRING_NAME
|
||||||
|
|
|
@ -0,0 +1,877 @@
|
||||||
|
/** @license 2022 Neil Edelman, distributed under the terms of the
|
||||||
|
[MIT License](https://opensource.org/licenses/MIT).
|
||||||
|
|
||||||
|
@abstract Stand-alone header <src/tree.h>; examples <test/test_tree.c>. On a
|
||||||
|
compatible workstation, `make` creates the test suite of the examples.
|
||||||
|
|
||||||
|
@subtitle Ordered tree
|
||||||
|
|
||||||
|
A <tag:<B>tree> is an ordered set or map.
|
||||||
|
|
||||||
|
@param[TREE_NAME, TREE_KEY]
|
||||||
|
`<B>` that satisfies `C` naming conventions when mangled, required, and
|
||||||
|
`TREE_KEY`, a comparable type, <typedef:<PB>key>, whose default is
|
||||||
|
`unsigned int`. `<PB>` is private, whose names are prefixed in a manner to
|
||||||
|
avoid collisions.
|
||||||
|
|
||||||
|
@param[TREE_VALUE]
|
||||||
|
`TRIE_VALUE` is an optional payload to go with the type, <typedef:<PB>value>.
|
||||||
|
The makes it a map of <tag:<B>tree_entry> instead of a set.
|
||||||
|
|
||||||
|
@param[TREE_COMPARE]
|
||||||
|
A function satisfying <typedef:<PB>compare_fn>. Defaults to ascending order.
|
||||||
|
Required if `TREE_KEY` is changed to an incomparable type.
|
||||||
|
|
||||||
|
@param[TREE_EXPECT_TRAIT]
|
||||||
|
Do not un-define certain variables for subsequent inclusion in a parameterized
|
||||||
|
trait.
|
||||||
|
|
||||||
|
@param[TREE_TO_STRING_NAME, TREE_TO_STRING]
|
||||||
|
To string trait contained in <to_string.h>; an optional unique `<SZ>`
|
||||||
|
that satisfies `C` naming conventions when mangled and function implementing
|
||||||
|
<typedef:<PSZ>to_string_fn>.
|
||||||
|
|
||||||
|
@fixme multi-key; implementation of order statistic tree
|
||||||
|
@fixme merge, difference
|
||||||
|
|
||||||
|
@std C89 */
|
||||||
|
|
||||||
|
#if !defined(TREE_NAME)
|
||||||
|
#error Name TREE_NAME undefined.
|
||||||
|
#endif
|
||||||
|
#if defined(TREE_TO_STRING_NAME) || defined(TREE_TO_STRING)
|
||||||
|
#define TREE_TO_STRING_TRAIT 1
|
||||||
|
#else
|
||||||
|
#define TREE_TO_STRING_TRAIT 0
|
||||||
|
#endif
|
||||||
|
#define TREE_TRAITS TREE_TO_STRING_TRAIT
|
||||||
|
#if TREE_TRAITS > 1
|
||||||
|
#error Only one trait per include is allowed; use TREE_EXPECT_TRAIT.
|
||||||
|
#endif
|
||||||
|
#if defined(TREE_TO_STRING_NAME) && !defined(TREE_TO_STRING)
|
||||||
|
#error TREE_TO_STRING_NAME requires TREE_TO_STRING.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TREE_H /* <!-- idempotent */
|
||||||
|
#define TREE_H
|
||||||
|
#include <stddef.h> /* fixme: stdlib, string should do it; what is going on? */
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
/* <Kernighan and Ritchie, 1988, p. 231>. */
|
||||||
|
#if defined(TREE_CAT_) || defined(TREE_CAT) || defined(B_) || defined(PB_) \
|
||||||
|
|| defined(TREE_IDLE)
|
||||||
|
#error Unexpected defines.
|
||||||
|
#endif
|
||||||
|
#define TREE_CAT_(n, m) n ## _ ## m
|
||||||
|
#define TREE_CAT(n, m) TREE_CAT_(n, m)
|
||||||
|
#define B_(n) TREE_CAT(TREE_NAME, n)
|
||||||
|
#define PB_(n) TREE_CAT(tree, B_(n))
|
||||||
|
/* Leaf: `TREE_MAX type`; branch: `TREE_MAX type + TREE_ORDER pointer`. */
|
||||||
|
#define TREE_MAX 2
|
||||||
|
#if TREE_MAX < 2 || TREE_MAX > UCHAR_MAX
|
||||||
|
#error TREE_MAX parameter range `[3, UCHAR_MAX]`.
|
||||||
|
#endif
|
||||||
|
/* This is the worst-case branching factor; the performance will be
|
||||||
|
\O(log_{`TREE_MIN`+1} `size`). Usually this is `⌈(TREE_MAX+1)/2⌉-1`. However,
|
||||||
|
smaller values are less-eager; this has been chosen to provide hysteresis. In
|
||||||
|
the extreme, <Johnson, Shasha, 1993, Free-at-Empty> show good results. (Except
|
||||||
|
`TREE_MAX 2`, one can be the only value.) */
|
||||||
|
#define TREE_MIN (TREE_MAX / 3 ? TREE_MAX / 3 : 1)
|
||||||
|
#if TREE_MIN == 0 || TREE_MIN > TREE_MAX / 2
|
||||||
|
#error TREE_MIN parameter range `[1, \floor(TREE_MAX / 2)]`.
|
||||||
|
#endif
|
||||||
|
#define TREE_ORDER (TREE_MAX + 1) /* Maximum degree, (branching factor.) */
|
||||||
|
#define TREE_SPLIT (TREE_ORDER / 2) /* Split index: even order left-leaning. */
|
||||||
|
#define TREE_RESULT X(ERROR), X(UNIQUE), X(YIELD)
|
||||||
|
#define X(n) TREE_##n
|
||||||
|
/** A result of modifying the tree, of which `TREE_ERROR` is false.
|
||||||
|
![A diagram of the result states.](../doc/put.png) */
|
||||||
|
enum tree_result { TREE_RESULT };
|
||||||
|
#undef X
|
||||||
|
#define X(n) #n
|
||||||
|
/** A static array of strings describing the <tag:tree_result>. */
|
||||||
|
static const char *const tree_result_str[] = { TREE_RESULT };
|
||||||
|
#undef X
|
||||||
|
#undef TREE_RESULT
|
||||||
|
#endif /* idempotent --> */
|
||||||
|
|
||||||
|
|
||||||
|
#if TREE_TRAITS == 0 /* <!-- base code */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef TREE_KEY
|
||||||
|
#define TREE_KEY unsigned
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** A comparable type, defaults to `unsigned`. */
|
||||||
|
typedef TREE_KEY PB_(key);
|
||||||
|
typedef const TREE_KEY PB_(key_c);
|
||||||
|
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
/** On `TREE_VALUE`, otherwise just a set of <typedef:<PB>key>. */
|
||||||
|
typedef TREE_VALUE PB_(value);
|
||||||
|
typedef const TREE_VALUE PB_(value_c);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Returns a positive result if `a` is out-of-order with respect to `b`,
|
||||||
|
inducing a strict weak order. This is compatible, but less strict then the
|
||||||
|
comparators from `bsearch` and `qsort`; it only needs to divide entries
|
||||||
|
into two instead of three categories. */
|
||||||
|
typedef int (*PB_(compare_fn))(PB_(key_c) a, PB_(key_c) b);
|
||||||
|
|
||||||
|
#ifndef TREE_COMPARE /* <!-- !cmp */
|
||||||
|
/** The default `TREE_COMPARE` on `a` and `b` is integer comparison that
|
||||||
|
results in ascending order. @implements <typedef:<PH>compare_fn> */
|
||||||
|
static int PB_(default_compare)(PB_(key_c) a, PB_(key_c) b)
|
||||||
|
{ return a > b; }
|
||||||
|
#define TREE_COMPARE &PB_(default_compare)
|
||||||
|
#endif /* !cmp --> */
|
||||||
|
|
||||||
|
/* Check that `TREE_COMPARE` is a function implementing
|
||||||
|
<typedef:<PB>compare_fn>, if defined. */
|
||||||
|
static const PB_(compare_fn) PB_(compare) = (TREE_COMPARE);
|
||||||
|
|
||||||
|
/* B-tree node, as <Bayer, McCreight, 1972, Large>. These rules are more lazy
|
||||||
|
than the original so as to not exhibit worst-case behaviour in small trees, as
|
||||||
|
<Johnson, Shasha, 1993, Free-at-Empty>, but lookup is potentially slower after
|
||||||
|
deleting; this is a design decision that nodes are not cached. In the
|
||||||
|
terminology of <Knuth, 1998 Art 3>,
|
||||||
|
* Every branch has at most `TREE_ORDER == TREE_MAX + 1` children, which is at
|
||||||
|
minimum three.
|
||||||
|
* Every non-root and non-bulk-loaded node has at least `TREE_MIN` keys,
|
||||||
|
(`⎣TREE_MAX/3⎦`.)
|
||||||
|
* Every branch has at least one child, `k`, and contains `k - 1` keys, (this
|
||||||
|
is a consequence of the fact that they are implicitly storing a complete
|
||||||
|
binary sub-tree.)
|
||||||
|
* All leaves are at the maximum depth and height zero; they do'n't carry links
|
||||||
|
to other nodes. (The height is one less then the original paper, as
|
||||||
|
<Knuth, 1998 Art 3>, for computational simplicity.)
|
||||||
|
* There are two empty B-trees to facilitate allocation hysteresis between
|
||||||
|
0 -- 1: idle `{ 0, 0 }`, and `{ garbage leaf, UINT_MAX }`, one could test,
|
||||||
|
`!root || height == UINT_MAX`.
|
||||||
|
* Bulk-loading always is on the right side.
|
||||||
|
* A branch node is a specialization of a (leaf) node with children. One can
|
||||||
|
tell if it's a branch by the non-zero height. */
|
||||||
|
struct PB_(node) {
|
||||||
|
unsigned char size; /* `[0, TREE_MAX]`. */
|
||||||
|
PB_(key) key[TREE_MAX]; /* Cache-friendly lookup. */
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
PB_(value) value[TREE_MAX];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
/* B-tree branch is a <tag:<PB>node> and links to `size + 1` nodes. */
|
||||||
|
struct PB_(branch) { struct PB_(node) base, *child[TREE_ORDER]; };
|
||||||
|
/** @return Upcasts `as_node` to a branch. */
|
||||||
|
static struct PB_(branch) *PB_(branch)(struct PB_(node) *const as_leaf)
|
||||||
|
{ return (struct PB_(branch) *)(void *)
|
||||||
|
((char *)as_leaf - offsetof(struct PB_(branch), base)); }
|
||||||
|
/** @return Upcasts `as_node` to a branch. */
|
||||||
|
static const struct PB_(branch) *PB_(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)); }
|
||||||
|
|
||||||
|
/* Subtree is a node with a height. */
|
||||||
|
struct PB_(sub) { struct PB_(node) *node; unsigned height; };
|
||||||
|
/* Address specific entry. */
|
||||||
|
struct PB_(ref) { struct PB_(node) *node; unsigned height, idx; };
|
||||||
|
struct PB_(ref_c) { const struct PB_(node) *node; unsigned height, idx; };
|
||||||
|
|
||||||
|
#ifdef TREE_VALUE /* <!-- value */
|
||||||
|
|
||||||
|
/** On `TREE_VALUE`, creates a map from pointer-to-<typedef:<PB>key> to
|
||||||
|
pointer-to-<typedef:<PB>value>. The reason these are pointers is because it
|
||||||
|
is not connected in memory. */
|
||||||
|
struct B_(tree_entry) { PB_(key) *key; PB_(value) *value; };
|
||||||
|
struct B_(tree_entry_c) { PB_(key_c) *key; PB_(value_c) *value; };
|
||||||
|
/** On `TREE_VALUE`, otherwise it's just an alias for
|
||||||
|
pointer-to-<typedef:<PB>key>. */
|
||||||
|
typedef struct B_(tree_entry) PB_(entry);
|
||||||
|
typedef struct B_(tree_entry_c) PB_(entry_c);
|
||||||
|
static PB_(entry) PB_(null_entry)(void)
|
||||||
|
{ const PB_(entry) e = { 0, 0 }; return e; }
|
||||||
|
static PB_(entry_c) PB_(null_entry_c)(void)
|
||||||
|
{ const PB_(entry_c) e = { 0, 0 }; return e; }
|
||||||
|
static PB_(entry) PB_(leaf_to_entry)(struct PB_(node) *const leaf,
|
||||||
|
const unsigned i) { PB_(entry) e;
|
||||||
|
e.key = leaf->key + i, e.value = leaf->value + i; return e; }
|
||||||
|
static PB_(entry_c) PB_(leaf_to_entry_c)(const struct PB_(node) *const leaf,
|
||||||
|
const unsigned i) { PB_(entry_c) e;
|
||||||
|
e.key = leaf->key + i, e.value = leaf->value + i; return e; }
|
||||||
|
static PB_(value) *PB_(ref_to_value)(const struct PB_(ref) ref)
|
||||||
|
{ return ref.node ? ref.node->value + ref.idx : 0; }
|
||||||
|
|
||||||
|
#else /* value --><!-- !value */
|
||||||
|
|
||||||
|
typedef PB_(key) PB_(value);
|
||||||
|
typedef PB_(key) *PB_(entry);
|
||||||
|
typedef PB_(key_c) *PB_(entry_c);
|
||||||
|
static PB_(entry_c) PB_(null_entry_c)(void) { return 0; }
|
||||||
|
static PB_(entry) PB_(null_entry)(void) { return 0; }
|
||||||
|
static PB_(entry) PB_(leaf_to_entry)(struct PB_(node) *const leaf,
|
||||||
|
const unsigned i) { return leaf->key + i; }
|
||||||
|
static PB_(entry_c) PB_(leaf_to_entry_c)(const struct PB_(node) *const leaf,
|
||||||
|
const unsigned i) { return leaf->key + i; }
|
||||||
|
static PB_(value) *PB_(ref_to_value)(const struct PB_(ref) ref)
|
||||||
|
{ return ref.node ? ref.node->key + ref.idx : 0; }
|
||||||
|
|
||||||
|
#endif /* !value --> */
|
||||||
|
|
||||||
|
/** To initialize it to an idle state, see <fn:<B>tree>, `TRIE_IDLE`, `{0}`
|
||||||
|
(`C99`), or being `static`. This is a B-tree, as
|
||||||
|
<Bayer, McCreight, 1972 Large>.
|
||||||
|
|
||||||
|
![States.](../doc/states.png) */
|
||||||
|
struct B_(tree);
|
||||||
|
struct B_(tree) { struct PB_(sub) root; };
|
||||||
|
|
||||||
|
#define BOX_CONTENT PB_(entry_c)
|
||||||
|
/** Is `e` not null? @implements `is_element_c` */
|
||||||
|
static int PB_(is_element_c)(PB_(entry_c) e) {
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
return !!e.key;
|
||||||
|
#else
|
||||||
|
return !!e;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/* Two copies of the same code, with and without `const`.
|
||||||
|
@param[sub] A copy of the tree's root.
|
||||||
|
@param[ref] If it has a null node, starts at the first key; if it's past the
|
||||||
|
node's limits, uses `sub` to go to the next node.
|
||||||
|
@return True unless there are no more `ref`. */
|
||||||
|
#define TREE_PIN(pin_c, ref_c) \
|
||||||
|
static int PB_(pin_c)(struct PB_(sub) sub, struct PB_(ref_c) *const ref) { \
|
||||||
|
struct PB_(ref_c) next; \
|
||||||
|
unsigned a0; \
|
||||||
|
PB_(key) x; \
|
||||||
|
assert(ref); \
|
||||||
|
if(!sub.node || sub.height == UINT_MAX) return 0; \
|
||||||
|
/* Start. */ \
|
||||||
|
if(!ref->node) \
|
||||||
|
ref->node = sub.node, ref->height = sub.height, ref->idx = 0; \
|
||||||
|
/* Descend. */ \
|
||||||
|
while(ref->height) ref->height--, \
|
||||||
|
ref->node = PB_(branch_c)(ref->node)->child[ref->idx], ref->idx = 0; \
|
||||||
|
if(ref->idx < ref->node->size) return 1; /* Likely. */ \
|
||||||
|
/* Empty nodes are always at the end, (when bulk loading.) */ \
|
||||||
|
if(!ref->node->size) return 0; \
|
||||||
|
/* Re-descend tree and note the minimum height node that has a next key. */\
|
||||||
|
for(next.node = 0, x = ref->node->key[ref->node->size - 1]; sub.height; \
|
||||||
|
sub.node = PB_(branch_c)(sub.node)->child[a0], sub.height--) { \
|
||||||
|
unsigned a1 = sub.node->size; a0 = 0; \
|
||||||
|
while(a0 < a1) { \
|
||||||
|
const unsigned m = (a0 + a1) / 2; \
|
||||||
|
if(PB_(compare)(x, sub.node->key[m]) > 0) a0 = m + 1; else a1 = m; \
|
||||||
|
} \
|
||||||
|
if(a0 < sub.node->size) \
|
||||||
|
next.node = sub.node, next.height = sub.height, next.idx = a0; \
|
||||||
|
} \
|
||||||
|
if(!next.node) return 0; /* Off the right. */ \
|
||||||
|
*ref = next; \
|
||||||
|
return 1; /* Jumped nodes. */ \
|
||||||
|
}
|
||||||
|
TREE_PIN(pin_c, ref_c)
|
||||||
|
TREE_PIN(pin, ref)
|
||||||
|
#undef TREE_PIN
|
||||||
|
/* This could be expanded! */
|
||||||
|
|
||||||
|
/* A constant iterator. @implements `forward` */
|
||||||
|
struct PB_(forward) { const struct PB_(sub) *root; struct PB_(ref_c) ref; };
|
||||||
|
/** @return Before `tree`. @implements `forward_begin` */
|
||||||
|
static struct PB_(forward) PB_(forward_begin)(const struct B_(tree) *const
|
||||||
|
tree) {
|
||||||
|
struct PB_(forward) it;
|
||||||
|
it.root = tree ? &tree->root : 0, it.ref.node = 0,
|
||||||
|
it.ref.height = 0, it.ref.idx = 0;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
/** Advances `it` to the next element. @return A pointer to the current
|
||||||
|
element or null. @implements `forward_next` */
|
||||||
|
static PB_(entry_c) PB_(forward_next)(struct PB_(forward) *const it) {
|
||||||
|
return assert(it), PB_(pin_c)(*it->root, &it->ref) ?
|
||||||
|
PB_(leaf_to_entry_c)(it->ref.node, it->ref.idx++) : PB_(null_entry_c)();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BOX_ITERATOR PB_(entry)
|
||||||
|
/** Is `x` not null? @implements `is_element` */
|
||||||
|
static int PB_(is_element)(const PB_(entry) e) {
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
return !!e.key;
|
||||||
|
#else
|
||||||
|
return !!e;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
/* A certain position and the top level tree for backtracking.
|
||||||
|
@implements `iterator` */
|
||||||
|
struct PB_(iterator) { struct PB_(sub) *root; struct PB_(ref) ref; };
|
||||||
|
/** @return Before `tree`. @implements `forward_begin` */
|
||||||
|
static struct PB_(iterator) PB_(begin)(struct B_(tree) *const tree) {
|
||||||
|
struct PB_(iterator) it;
|
||||||
|
it.root = tree ? &tree->root : 0, it.ref.node = 0,
|
||||||
|
it.ref.height = 0, it.ref.idx = 0;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
/** Advances `it` to the next element. @return A pointer to the current
|
||||||
|
element or null. @implements `next` */
|
||||||
|
static PB_(entry) PB_(next)(struct PB_(iterator) *const it) {
|
||||||
|
return assert(it), PB_(pin)(*it->root, &it->ref) ?
|
||||||
|
PB_(leaf_to_entry)(it->ref.node, it->ref.idx++) : PB_(null_entry)();
|
||||||
|
}
|
||||||
|
|
||||||
|
//#include "../test/orcish.h"
|
||||||
|
|
||||||
|
static void PB_(find_idx)(struct PB_(ref) *const lo, const PB_(key) key) {
|
||||||
|
unsigned hi = lo->node->size;
|
||||||
|
lo->idx = 0;
|
||||||
|
if(!hi) return;
|
||||||
|
do {
|
||||||
|
const unsigned m = (lo->idx + hi) / 2;
|
||||||
|
if(PB_(compare)(key, lo->node->key[m]) > 0) lo->idx = m + 1;
|
||||||
|
else hi = m;
|
||||||
|
} while(lo->idx < hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Assume `tree` and `x` are checked for non-empty validity. */
|
||||||
|
static struct PB_(ref) PB_(lower_r)(struct PB_(sub) *const tree,
|
||||||
|
const PB_(key) key, struct PB_(ref) *const unfull, int *const is_equal) {
|
||||||
|
struct PB_(ref) lo;
|
||||||
|
for(lo.node = tree->node, lo.height = tree->height; ;
|
||||||
|
lo.node = PB_(branch_c)(lo.node)->child[lo.idx], lo.height--) {
|
||||||
|
unsigned hi = lo.node->size;
|
||||||
|
lo.idx = 0;
|
||||||
|
if(unfull && hi < TREE_MAX) *unfull = lo;
|
||||||
|
if(!hi) continue; /* No nodes; bulk-add? */
|
||||||
|
do {
|
||||||
|
const unsigned m = (lo.idx + hi) / 2;
|
||||||
|
if(PB_(compare)(key, lo.node->key[m]) > 0) lo.idx = m + 1;
|
||||||
|
else hi = m;
|
||||||
|
} while(lo.idx < hi);
|
||||||
|
if(unfull && hi < TREE_MAX) unfull->idx = lo.idx; /* Update. */
|
||||||
|
if(!lo.height) break; /* Leaf node. */
|
||||||
|
if(lo.idx == lo.node->size) continue; /* Off the end. */
|
||||||
|
/* Total order and monotonic, otherwise have to check right. */
|
||||||
|
if(PB_(compare)(lo.node->key[lo.idx], key) > 0) continue;
|
||||||
|
if(is_equal) *is_equal = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param[tree] Can be null. @return Lower bound of `x` in `tree`.
|
||||||
|
@order \O(\log |`tree`|) */
|
||||||
|
static struct PB_(ref) PB_(lower)(struct PB_(sub) sub,
|
||||||
|
const PB_(key) x, struct PB_(ref) *const unfull, int *const is_equal) {
|
||||||
|
if(!sub.node || sub.height == UINT_MAX) {
|
||||||
|
struct PB_(ref) ref; ref.node = 0; return ref;
|
||||||
|
} else {
|
||||||
|
return PB_(lower_r)(&sub, x, unfull, is_equal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clears non-empty `tree` and it's children recursively, but doesn't put it
|
||||||
|
to idle or clear pointers. If `one` is valid, tries to keep one leaf. */
|
||||||
|
static void PB_(clear_r)(struct PB_(sub) sub, struct PB_(node) **const one) {
|
||||||
|
assert(sub.node);
|
||||||
|
if(!sub.height) {
|
||||||
|
if(one && !*one) *one = sub.node;
|
||||||
|
else free(sub.node);
|
||||||
|
} else {
|
||||||
|
struct PB_(sub) child;
|
||||||
|
unsigned i;
|
||||||
|
child.height = sub.height - 1;
|
||||||
|
for(i = 0; i <= sub.node->size; i++)
|
||||||
|
child.node = PB_(branch)(sub.node)->child[i],
|
||||||
|
PB_(clear_r)(child, one);
|
||||||
|
free(PB_(branch)(sub.node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Box override information. */
|
||||||
|
#define BOX_ PB_
|
||||||
|
#define BOX struct B_(tree)
|
||||||
|
|
||||||
|
/** Initializes `tree` to idle. @order \Theta(1) @allow */
|
||||||
|
static struct B_(tree) B_(tree)(void)
|
||||||
|
{ struct B_(tree) tree; tree.root.node = 0; tree.root.height = 0;
|
||||||
|
return tree; }
|
||||||
|
|
||||||
|
/** Returns an initialized `tree` to idle, `tree` can be null. @allow */
|
||||||
|
static void B_(tree_)(struct B_(tree) *const tree) {
|
||||||
|
if(!tree) return; /* Null. */
|
||||||
|
if(!tree->root.node) { /* Idle. */
|
||||||
|
assert(!tree->root.height);
|
||||||
|
} else if(tree->root.height == UINT_MAX) { /* Empty. */
|
||||||
|
assert(tree->root.node); free(tree->root.node);
|
||||||
|
} else {
|
||||||
|
PB_(clear_r)(tree->root, 0);
|
||||||
|
}
|
||||||
|
*tree = B_(tree)();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Stores an iteration in a tree. Generally, changes in the topology of the
|
||||||
|
tree invalidate it. */
|
||||||
|
struct B_(tree_iterator) { struct PB_(iterator) _; };
|
||||||
|
/** @return An iterator before the first element of `tree`. Can be null.
|
||||||
|
@allow */
|
||||||
|
static struct B_(tree_iterator) B_(tree_begin)(struct B_(tree) *const tree)
|
||||||
|
{ struct B_(tree_iterator) it; it._ = PB_(begin)(tree); return it; }
|
||||||
|
/** Advances `it` to the next element. @return A pointer to the current
|
||||||
|
element or null. @allow */
|
||||||
|
static PB_(entry) B_(tree_next)(struct B_(tree_iterator) *const it)
|
||||||
|
{ return PB_(next)(&it->_); }
|
||||||
|
|
||||||
|
/** @param[tree] Can be null. @return Finds the smallest entry in `tree` that
|
||||||
|
is at the lower bound of `x`. If `x` is higher than any of `tree`, it will be
|
||||||
|
placed just passed the end. @order \O(\log |`tree`|) @allow */
|
||||||
|
static struct B_(tree_iterator) B_(tree_lower)(struct B_(tree) *const tree,
|
||||||
|
const PB_(key) x) {
|
||||||
|
struct B_(tree_iterator) it;
|
||||||
|
if(!tree) return it._.root = 0, it;
|
||||||
|
it._.ref = PB_(lower)(tree->root, x, 0, 0);
|
||||||
|
it._.root = &tree->root;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For example, `tree = { 10 }`, `x = 5 -> 10`, `x = 10 -> 10`,
|
||||||
|
`x = 11 -> null`.
|
||||||
|
@return Lower-bound value match for `x` in `tree` or null if `x` is greater
|
||||||
|
than all in `tree`. @order \O(\log |`tree`|) @allow */
|
||||||
|
static PB_(value) *B_(tree_get_next)(struct B_(tree) *const tree,
|
||||||
|
const PB_(key) x) {
|
||||||
|
struct PB_(ref) ref;
|
||||||
|
return tree && (ref = PB_(lower)(tree->root, x, 0, 0),
|
||||||
|
PB_(pin)(tree->root, &ref)) ? PB_(ref_to_value)(ref) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#include "../test/orcish.h"
|
||||||
|
static void PB_(print)(const struct B_(tree) *const tree);
|
||||||
|
#ifndef TREE_TEST
|
||||||
|
static void PB_(print)(const struct B_(tree) *const tree)
|
||||||
|
{ (void)tree, printf("not printable\n"); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TREE_VALUE /* <!-- map */
|
||||||
|
/** Packs `key` on the right side of `tree` without doing the usual
|
||||||
|
restructuring. This is best followed by <fn:<B>tree_bulk_finish>.
|
||||||
|
@param[value] A pointer to the key's value which is set by the function on
|
||||||
|
returning true. A null pointer in this parameter causes the value to go
|
||||||
|
uninitialized. This parameter is not there if one didn't specify `TREE_VALUE`.
|
||||||
|
@return One of <tag:tree_result>: `TREE_ERROR` and `errno` will be set,
|
||||||
|
`TREE_YIELD` if the key is already (the highest) in the tree, and
|
||||||
|
`TREE_UNIQUE`, added, the `value` (if specified) is uninitialized.
|
||||||
|
@throws[EDOM] `x` is smaller than the largest key in `tree`. @throws[malloc] */
|
||||||
|
static enum tree_result B_(tree_bulk_add)(struct B_(tree) *const tree,
|
||||||
|
PB_(key) key, PB_(value) **const value)
|
||||||
|
#else /* map --><!-- set */
|
||||||
|
static enum tree_result B_(tree_bulk_add)(struct B_(tree) *const tree,
|
||||||
|
PB_(key) key)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
struct PB_(node) *node = 0, *head = 0; /* The original and new. */
|
||||||
|
assert(tree);
|
||||||
|
if(!tree->root.node) { /* Idle tree. */
|
||||||
|
assert(!tree->root.height);
|
||||||
|
if(!(node = malloc(sizeof *node))) goto catch;
|
||||||
|
node->size = 0;
|
||||||
|
tree->root.node = node;
|
||||||
|
printf("bulk: idle\n");
|
||||||
|
} else if(tree->root.height == UINT_MAX) { /* Empty tree. */
|
||||||
|
tree->root.height = 0;
|
||||||
|
tree->root.node->size = 0;
|
||||||
|
printf("bulk: empty\n");
|
||||||
|
} else {
|
||||||
|
struct PB_(sub) unfull = { 0, 0 };
|
||||||
|
unsigned new_nodes, n; /* Count new nodes. */
|
||||||
|
struct PB_(node) *tail = 0, *last = 0;
|
||||||
|
struct PB_(branch) *pretail = 0;
|
||||||
|
struct PB_(sub) scout;
|
||||||
|
PB_(key) i;
|
||||||
|
printf("bulk: tree...\n"), PB_(print)(tree);
|
||||||
|
for(scout = tree->root; ; scout.node = PB_(branch)(scout.node)
|
||||||
|
->child[scout.node->size], scout.height--) {
|
||||||
|
if(scout.node->size < TREE_MAX) unfull = scout;
|
||||||
|
if(scout.node->size) last = scout.node;
|
||||||
|
if(!scout.height) break;
|
||||||
|
}
|
||||||
|
assert(last), i = last->key[last->size - 1];
|
||||||
|
/* Verify that the argument is not smaller than the largest. */
|
||||||
|
if(PB_(compare)(i, key) > 0) return errno = EDOM, TREE_ERROR;
|
||||||
|
if(PB_(compare)(key, i) <= 0) {
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
if(value) { /* Last value in the last node. */
|
||||||
|
struct PB_(ref) ref;
|
||||||
|
ref.node = last, ref.idx = last->size - 1;
|
||||||
|
*value = PB_(ref_to_value)(ref);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return TREE_YIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* One leaf, and the rest branches. */
|
||||||
|
new_nodes = n = unfull.node ? unfull.height : tree->root.height + 2;
|
||||||
|
/*printf("new_nodes: %u, tree height %u\n", new_nodes, tree->height);*/
|
||||||
|
if(!n) {
|
||||||
|
node = unfull.node;
|
||||||
|
} else {
|
||||||
|
if(!(node = tail = malloc(sizeof *tail))) goto catch;
|
||||||
|
tail->size = 0;
|
||||||
|
/*printf("new tail: %s.\n", orcify(tail));*/
|
||||||
|
while(--n) {
|
||||||
|
struct PB_(branch) *b;
|
||||||
|
if(!(b = malloc(sizeof *b))) goto catch;
|
||||||
|
b->base.size = 0;
|
||||||
|
/*printf("new branch: %s.\n", orcify(b));*/
|
||||||
|
if(!head) b->child[0] = 0, pretail = b; /* First loop. */
|
||||||
|
else b->child[0] = head; /* Not first loop. */
|
||||||
|
head = &b->base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Post-error; modify the original as needed. */
|
||||||
|
if(pretail) pretail->child[0] = tail;
|
||||||
|
else head = node;
|
||||||
|
if(!unfull.node) { /* Add tree to head. */
|
||||||
|
struct PB_(branch) *const branch = PB_(branch)(head);
|
||||||
|
/*printf("adding the existing root, %s to %s\n",
|
||||||
|
orcify(tree->root), orcify(head));*/
|
||||||
|
assert(new_nodes > 1);
|
||||||
|
branch->child[1] = branch->child[0];
|
||||||
|
branch->child[0] = tree->root.node;
|
||||||
|
node = tree->root.node = head, tree->root.height++;
|
||||||
|
} else if(unfull.height) { /* Add head to tree. */
|
||||||
|
struct PB_(branch) *const branch = PB_(branch)(node = unfull.node);
|
||||||
|
/*printf("adding the linked list, %s to %s at %u\n",
|
||||||
|
orcify(head), orcify(inner), inner->base.size + 1);*/
|
||||||
|
assert(new_nodes);
|
||||||
|
branch->child[branch->base.size + 1] = head;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(node && node->size < TREE_MAX);
|
||||||
|
node->key[node->size] = key;
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
if(value) {
|
||||||
|
struct PB_(ref) ref;
|
||||||
|
ref.node = node, ref.idx = node->size;
|
||||||
|
*value = PB_(ref_to_value)(ref);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
node->size++;
|
||||||
|
return TREE_UNIQUE;
|
||||||
|
catch:
|
||||||
|
free(node); /* Didn't work out. */
|
||||||
|
if(head) for( ; ; ) {
|
||||||
|
struct PB_(node) *const next = PB_(branch)(head)->child[0];
|
||||||
|
free(head); /* Didn't work out. */
|
||||||
|
if(!next) break;
|
||||||
|
head = next;
|
||||||
|
}
|
||||||
|
if(!errno) errno = ERANGE;
|
||||||
|
return TREE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Distributes `tree` on the right side so that, after a series of
|
||||||
|
<fn:<B>tree_bulk_add>, it will be consistent with the minimum number of keys
|
||||||
|
in a node. @return The distribution was a success and all nodes are within
|
||||||
|
rules. The only time that it would be false is if, maybe, a regular insertion
|
||||||
|
instead of a bulk insertion was performed interspersed with a bulk insertion
|
||||||
|
without calling this function. */
|
||||||
|
static int B_(tree_bulk_finish)(struct B_(tree) *const tree) {
|
||||||
|
struct PB_(sub) s;
|
||||||
|
struct PB_(node) *right;
|
||||||
|
if(!tree || !tree->root.node || tree->root.height == UINT_MAX) return 1;
|
||||||
|
for(s = tree->root; s.height; s.node = right, s.height--) {
|
||||||
|
unsigned distribute, right_want, right_move, take_sibling;
|
||||||
|
struct PB_(branch) *parent = PB_(branch)(s.node);
|
||||||
|
struct PB_(node) *sibling = (assert(parent->base.size),
|
||||||
|
parent->child[parent->base.size - 1]);
|
||||||
|
right = parent->child[parent->base.size];
|
||||||
|
if(TREE_MIN <= right->size) continue; /* Has enough. */
|
||||||
|
distribute = sibling->size + right->size;
|
||||||
|
if(distribute < 2 * TREE_MIN) return 0;
|
||||||
|
right_want = distribute / 2;
|
||||||
|
right_move = right_want - right->size;
|
||||||
|
take_sibling = right_move - 1;
|
||||||
|
/* Either the right has met the properties of a B-tree node, (covered
|
||||||
|
above,) or the left sibling is full from bulk-loading (relaxed.) */
|
||||||
|
assert(right->size < right_want && right_want >= TREE_MIN
|
||||||
|
&& sibling->size - take_sibling >= TREE_MIN + 1);
|
||||||
|
/* Move the right node to accept more keys. */
|
||||||
|
memmove(right->key + right_move, right->key,
|
||||||
|
sizeof *right->key * right->size);
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
memmove(right->value + right_move, right->value,
|
||||||
|
sizeof *right->value * right->size);
|
||||||
|
#endif
|
||||||
|
if(s.height > 1) { /* (Parent height.) */
|
||||||
|
struct PB_(branch) *rbranch = PB_(branch)(right),
|
||||||
|
*sbranch = PB_(branch)(sibling);
|
||||||
|
memmove(rbranch->child + right_move, rbranch->child,
|
||||||
|
sizeof *rbranch->child * (right->size + 1));
|
||||||
|
memcpy(rbranch->child, sbranch->child + sibling->size + 1
|
||||||
|
- right_move, sizeof *sbranch->child * right_move);
|
||||||
|
}
|
||||||
|
right->size += right_move;
|
||||||
|
/* Move one node from the parent. */
|
||||||
|
memcpy(right->key + take_sibling,
|
||||||
|
parent->base.key + parent->base.size - 1, sizeof *right->key);
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
memcpy(right->value + take_sibling,
|
||||||
|
parent->base.value + parent->base.size - 1, sizeof *right->value);
|
||||||
|
#endif
|
||||||
|
/* Move the others from the sibling. */
|
||||||
|
memcpy(right->key, sibling->key + sibling->size - take_sibling,
|
||||||
|
sizeof *right->key * take_sibling);
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
memcpy(right->value, sibling->value + sibling->size - take_sibling,
|
||||||
|
sizeof *right->value * take_sibling);
|
||||||
|
#endif
|
||||||
|
sibling->size -= take_sibling;
|
||||||
|
/* Sibling's key is now the parent's. */
|
||||||
|
memcpy(parent->base.key + parent->base.size - 1,
|
||||||
|
sibling->key + sibling->size - 1, sizeof *right->key);
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
memcpy(parent->base.value + parent->base.size - 1,
|
||||||
|
sibling->value + sibling->size - 1, sizeof *right->value);
|
||||||
|
#endif
|
||||||
|
sibling->size--;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TREE_VALUE /* <!-- map */
|
||||||
|
static enum tree_result B_(tree_add)(struct B_(tree) *const tree,
|
||||||
|
PB_(key) key, PB_(value) **const value)
|
||||||
|
#else /* map --><!-- set */
|
||||||
|
static enum tree_result B_(tree_add)(struct B_(tree) *const tree,
|
||||||
|
PB_(key) key)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
struct PB_(node) *leaf = 0, *head = 0, **next = 0,
|
||||||
|
*sibling;
|
||||||
|
unsigned new_nodes, i;
|
||||||
|
struct PB_(ref) add, parent, hole, cursor;
|
||||||
|
int is_equal;
|
||||||
|
assert(tree);
|
||||||
|
if(!(add.node = tree->root.node)) goto idle;
|
||||||
|
else if(tree->root.height == UINT_MAX) goto empty;
|
||||||
|
goto content;
|
||||||
|
idle: /* No reserved memory. */
|
||||||
|
assert(!add.node && !tree->root.height);
|
||||||
|
if(!(add.node = malloc(sizeof *add.node))) goto catch;
|
||||||
|
tree->root.node = add.node;
|
||||||
|
tree->root.height = UINT_MAX;
|
||||||
|
goto empty;
|
||||||
|
empty: /* Reserved dynamic memory, but tree is empty. */
|
||||||
|
assert(add.node && tree->root.height == UINT_MAX);
|
||||||
|
tree->root.height = 0;
|
||||||
|
add.node->size = 0;
|
||||||
|
add.idx = 0;
|
||||||
|
goto insert;
|
||||||
|
content: /* Descend the tree; record last node that has space. */
|
||||||
|
parent.node = 0, is_equal = 0;
|
||||||
|
add = PB_(lower_r)(&tree->root, key, &parent, &is_equal);
|
||||||
|
if(is_equal) goto yield; /* Assumes key is unique. */
|
||||||
|
if(parent.node == add.node) goto insert; else goto allocate;
|
||||||
|
allocate: /* Pre-allocate new nodes in order. */
|
||||||
|
new_nodes = parent.node ? parent.height + 1 : tree->root.height + 2;
|
||||||
|
for(i = 0; i < new_nodes - 1; i++) {
|
||||||
|
struct PB_(branch) *branch;
|
||||||
|
if(!(branch = malloc(sizeof *branch))) goto catch;
|
||||||
|
branch->base.size = 0;
|
||||||
|
if(!head) head = &branch->base; else *next = &branch->base;
|
||||||
|
next = branch->child;
|
||||||
|
}
|
||||||
|
if(!(leaf = malloc(sizeof *leaf))) goto catch;
|
||||||
|
leaf->size = 0;
|
||||||
|
*next = leaf;
|
||||||
|
hole.node = 0;
|
||||||
|
if(parent.node) goto split; else goto grow;
|
||||||
|
grow: /* Raise tree height with zero-size one-child branch. */
|
||||||
|
assert(!parent.node && new_nodes);
|
||||||
|
parent.node = head, head = PB_(branch)(head)->child[0], new_nodes--;
|
||||||
|
parent.height = ++tree->root.height, parent.idx = 0;
|
||||||
|
PB_(branch)(parent.node)->child[0] = tree->root.node;
|
||||||
|
tree->root.node = parent.node;
|
||||||
|
goto split;
|
||||||
|
split: /* Simulates bottom-up, overfull node, split; really this is problematic
|
||||||
|
because we don't have parent pointers and we don't have space for overfull
|
||||||
|
nodes. So split top-down and leave some nodes blank for filling in next. */
|
||||||
|
assert(parent.node && parent.height && parent.node->size < TREE_MAX
|
||||||
|
&& new_nodes);
|
||||||
|
sibling = head, head = --new_nodes ? PB_(branch)(head)->child[0] : 0;
|
||||||
|
cursor.node = PB_(branch)(parent.node)->child[parent.idx];
|
||||||
|
cursor.height = parent.height - 1;
|
||||||
|
PB_(find_idx)(&cursor, key);
|
||||||
|
/* Add one space to unfull parent. This is double-copying when we loop
|
||||||
|
around a second time in `⎣(TREE_MAX-1)/2⎦/TREE_MAX` cases; not going to
|
||||||
|
optimize this because it would require a lookahead. */
|
||||||
|
memmove(parent.node->key + parent.idx + 1, parent.node->key + parent.idx,
|
||||||
|
sizeof *parent.node->key * (parent.node->size - parent.idx));
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
memmove(parent.node->value + parent.idx + 1,
|
||||||
|
parent.node->value + parent.idx,
|
||||||
|
sizeof *parent.node->value * (parent.node->size - parent.idx));
|
||||||
|
#endif
|
||||||
|
assert(0);
|
||||||
|
/*{
|
||||||
|
struct PB_(branch) *const pbranch = PB_(branch)(parent.node);
|
||||||
|
memmove(<#void *__dst#>, <#const void *__src#>, <#size_t __len#>)...
|
||||||
|
}*/
|
||||||
|
parent.node->size++;
|
||||||
|
if(cursor.idx == TREE_SPLIT) { /* Down the middle. */
|
||||||
|
printf("down middle\n");
|
||||||
|
if(!hole.node) hole = parent; /* Maybe? */
|
||||||
|
sibling->size = TREE_MAX - TREE_SPLIT - 1;
|
||||||
|
memcpy(sibling->key, cursor.node->key + TREE_SPLIT,
|
||||||
|
sizeof *sibling->key * (TREE_MAX - TREE_SPLIT));
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
memcpy(sibling->value, cursor.node->value + TREE_SPLIT,
|
||||||
|
sizeof *sibling->value * (TREE_MAX - TREE_SPLIT));
|
||||||
|
#endif
|
||||||
|
if(cursor.height) {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
} else if(cursor.idx < TREE_SPLIT) {
|
||||||
|
assert(0);
|
||||||
|
} else /* Greater than. */ {
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
//found.node = tree->root.node, PB_(find_idx)(&found, key);
|
||||||
|
assert(0);
|
||||||
|
insert: /* `add` is referencing an unfull node that we want to insert. */
|
||||||
|
assert(add.node && add.idx <= add.node->size && add.node->size < TREE_MAX);
|
||||||
|
memmove(add.node->key + add.idx + 1, add.node->key + add.idx,
|
||||||
|
sizeof *add.node->key * (add.node->size - add.idx));
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
memmove(add.node->value + add.idx + 1, add.node->value + add.idx,
|
||||||
|
sizeof *add.node->value * (add.node->size - add.idx));
|
||||||
|
#endif
|
||||||
|
add.node->size++;
|
||||||
|
add.node->key[add.idx] = key;
|
||||||
|
goto unique;
|
||||||
|
yield: /* `add` is an existing value. */
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
if(value) *value = PB_(ref_to_value)(add);
|
||||||
|
#endif
|
||||||
|
return TREE_YIELD;
|
||||||
|
unique: /* `add` is a new value. */
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
if(value) *value = PB_(ref_to_value)(add);
|
||||||
|
#endif
|
||||||
|
return TREE_UNIQUE;
|
||||||
|
catch:
|
||||||
|
while(head) {
|
||||||
|
struct PB_(branch) *const branch = PB_(branch)(head);
|
||||||
|
head = branch->child[0];
|
||||||
|
}
|
||||||
|
if(!errno) errno = ERANGE;
|
||||||
|
return TREE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/** Updates or adds a pointer to `x` into `trie`.
|
||||||
|
@param[eject] If not null, on success it will hold the overwritten value or
|
||||||
|
a pointer-to-null if it did not overwrite any value.
|
||||||
|
@return Success. @throws[realloc, ERANGE] @order \O(|`key`|) @allow */
|
||||||
|
static int B_(trie_put)(struct B_(trie) *const trie, const PB_(entry) x,
|
||||||
|
PB_(entry) */*const fixme*/eject)
|
||||||
|
{ return assert(trie && x), PB_(put)(trie, x, &eject, 0); }
|
||||||
|
|
||||||
|
/** Adds a pointer to `x` to `trie` only if the entry is absent or if calling
|
||||||
|
`replace` returns true or is null.
|
||||||
|
@param[eject] If not null, on success it will hold the overwritten value or
|
||||||
|
a pointer-to-null if it did not overwrite any value. If a collision occurs and
|
||||||
|
`replace` does not return true, this will be a pointer to `x`.
|
||||||
|
@param[replace] Called on collision and only replaces it if the function
|
||||||
|
returns true. If null, it is semantically equivalent to <fn:<T>trie_put>.
|
||||||
|
@return Success. @throws[realloc, ERANGE] @order \O(|`key`|) @allow */
|
||||||
|
static int B_(trie_policy)(struct B_(trie) *const trie, const PB_(entry) x,
|
||||||
|
PB_(entry) */*const*/ eject, const PB_(replace_fn) replace)
|
||||||
|
{ return assert(trie && x), PB_(put)(trie, x, &eject, replace); }
|
||||||
|
|
||||||
|
/** Tries to remove `key` from `trie`. @return Success. */
|
||||||
|
static int B_(trie_remove)(struct B_(trie) *const trie,
|
||||||
|
const char *const key) { return PB_(remove)(trie, key); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TREE_TEST /* <!-- test */
|
||||||
|
/* Forward-declare. */
|
||||||
|
static void (*PB_(to_string))(PB_(entry_c), char (*)[12]);
|
||||||
|
static const char *(*PB_(tree_to_string))(const struct B_(tree) *);
|
||||||
|
#include "../test/test_tree.h"
|
||||||
|
#endif /* test --> */
|
||||||
|
|
||||||
|
static void PB_(unused_base_coda)(void);
|
||||||
|
static void PB_(unused_base)(void) {
|
||||||
|
PB_(key) k;
|
||||||
|
memset(&k, 0, sizeof k);
|
||||||
|
PB_(is_element_c); PB_(forward_begin); PB_(forward_next);
|
||||||
|
PB_(is_element);
|
||||||
|
B_(tree)(); B_(tree_)(0); B_(tree_begin)(0); B_(tree_next)(0);
|
||||||
|
B_(tree_lower)(0, k);
|
||||||
|
B_(tree_get_next)(0, k);
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
B_(tree_bulk_add)(0, k, 0);
|
||||||
|
B_(tree_add)(0, k, 0);
|
||||||
|
#else
|
||||||
|
B_(tree_bulk_add)(0, k);
|
||||||
|
B_(tree_add)(0, k);
|
||||||
|
#endif
|
||||||
|
B_(tree_bulk_finish)(0);
|
||||||
|
PB_(unused_base_coda)();
|
||||||
|
}
|
||||||
|
static void PB_(unused_base_coda)(void) { PB_(unused_base)(); }
|
||||||
|
|
||||||
|
|
||||||
|
#elif defined(TREE_TO_STRING) /* base code --><!-- to string trait */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef TREE_TO_STRING_NAME
|
||||||
|
#define STR_(n) TREE_CAT(B_(tree), TREE_CAT(TREE_TO_STRING_NAME, n))
|
||||||
|
#else
|
||||||
|
#define STR_(n) TREE_CAT(B_(tree), n)
|
||||||
|
#endif
|
||||||
|
#define TO_STRING TREE_TO_STRING
|
||||||
|
#define TO_STRING_LEFT '{'
|
||||||
|
#define TO_STRING_RIGHT '}'
|
||||||
|
#include "to_string.h" /** \include */
|
||||||
|
#ifdef TREE_TEST /* <!-- expect: greedy satisfy forward-declared. */
|
||||||
|
#undef TREE_TEST
|
||||||
|
static PSTR_(to_string_fn) PB_(to_string) = PSTR_(to_string);
|
||||||
|
static const char *(*PB_(tree_to_string))(const struct B_(tree) *)
|
||||||
|
= &STR_(to_string);
|
||||||
|
#endif /* expect --> */
|
||||||
|
#undef STR_
|
||||||
|
#undef TREE_TO_STRING
|
||||||
|
#ifdef TREE_TO_STRING_NAME
|
||||||
|
#undef TREE_TO_STRING_NAME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* traits --> */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef TREE_EXPECT_TRAIT /* <!-- trait */
|
||||||
|
#undef TREE_EXPECT_TRAIT
|
||||||
|
#else /* trait --><!-- !trait */
|
||||||
|
#ifdef TREE_TEST
|
||||||
|
#error No TREE_TO_STRING traits defined for TREE_TEST.
|
||||||
|
#endif
|
||||||
|
#undef TREE_NAME
|
||||||
|
#undef TREE_KEY
|
||||||
|
#undef TREE_COMPARE
|
||||||
|
#ifdef TREE_VALUE
|
||||||
|
#undef TREE_VALUE
|
||||||
|
#endif
|
||||||
|
#ifdef TREE_TEST
|
||||||
|
#undef TREE_TEST
|
||||||
|
#endif
|
||||||
|
#undef BOX_
|
||||||
|
#undef BOX
|
||||||
|
#undef BOX_CONTENT
|
||||||
|
#undef BOX_ITERATOR
|
||||||
|
#endif /* !trait --> */
|
||||||
|
#undef TREE_TO_STRING_TRAIT
|
||||||
|
#undef TREE_TRAITS
|
Loading…
Reference in New Issue