Way overcomplicated.

This commit is contained in:
Neil 2022-02-11 00:28:28 -08:00
parent 58b228e9d0
commit b87906bb49
9 changed files with 3713 additions and 20 deletions

396
src/array.h Normal file
View File

@ -0,0 +1,396 @@
/** @license 2016 Neil Edelman, distributed under the terms of the
[MIT License](https://opensource.org/licenses/MIT).
@abstract Source <src/array.h>; examples <test/test_array.c>.
@subtitle Contiguous dynamic array
![Example of array.](../web/array.png)
<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
amortised cost, and any pointers to this memory may become stale.
@param[ARRAY_NAME, ARRAY_TYPE]
`<A>` that satisfies `C` naming conventions when mangled and a valid tag-type,
<typedef:<PA>type>, associated therewith; required. `<PA>` is private, whose
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]
Do not un-define certain variables for subsequent inclusion in a parameterized
trait.
@param[ARRAY_COMPARE_NAME, ARRAY_COMPARE, ARRAY_IS_EQUAL]
Compare trait contained in <src/array_coda.h>. An optional mangled name for
uniqueness and a function implementing either <typedef:<PAC>compare_fn> or
<typedef:<PAC>bipredicate_fn>.
@param[ARRAY_TO_STRING_NAME, ARRAY_TO_STRING]
To string trait contained in <src/to_string.h>. An optional mangled name for
uniqueness and function implementing <typedef:<PSZ>to_string_fn>.
@std C89 */
#if !defined(ARRAY_NAME) || !defined(ARRAY_TYPE)
#error Name ARRAY_NAME or tag type ARRAY_TYPE undefined.
#endif
#if defined(ARRAY_TO_STRING_NAME) || defined(ARRAY_TO_STRING)
#define ARRAY_TO_STRING_TRAIT 1
#else
#define ARRAY_TO_STRING_TRAIT 0
#endif
#if defined(ARRAY_COMPARE_NAME) || defined(ARRAY_COMPARE) \
|| defined(ARRAY_IS_EQUAL)
#define ARRAY_COMPARE_TRAIT 1
#else
#define ARRAY_COMPARE_TRAIT 0
#endif
#define ARRAY_TRAITS ARRAY_TO_STRING_TRAIT + ARRAY_COMPARE_TRAIT
#if ARRAY_TRAITS > 1
#error Only one trait per include is allowed; use ARRAY_EXPECT_TRAIT.
#endif
#if defined(ARRAY_TO_STRING_NAME) && !defined(ARRAY_TO_STRING)
#error ARRAY_TO_STRING_NAME requires ARRAY_TO_STRING.
#endif
#if defined(ARRAY_COMPARE_NAME) \
&& (!(!defined(ARRAY_COMPARE) ^ !defined(ARRAY_IS_EQUAL)))
#error ARRAY_COMPARE_NAME requires ARRAY_COMPARE or ARRAY_IS_EQUAL not both.
#endif
#ifndef ARRAY_H /* <!-- idempotent */
#define ARRAY_H
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#if defined(ARRAY_CAT_) || defined(ARRAY_CAT) || defined(A_) || defined(PA_) \
|| defined(ARRAY_IDLE)
#error Unexpected defines.
#endif
/* <Kernighan and Ritchie, 1988, p. 231>. */
#define ARRAY_CAT_(n, m) n ## _ ## m
#define ARRAY_CAT(n, m) ARRAY_CAT_(n, m)
#define A_(n) ARRAY_CAT(ARRAY_NAME, n)
#define PA_(n) ARRAY_CAT(array, A_(n))
#define ARRAY_IDLE { 0, 0, 0 }
#endif /* idempotent --> */
#if ARRAY_TRAITS == 0 /* <!-- base code */
#ifndef ARRAY_MIN_CAPACITY /* <!-- !min; */
#define ARRAY_MIN_CAPACITY 3 /* > 1 */
#endif /* !min --> */
/** A valid tag type set by `ARRAY_TYPE`. */
typedef ARRAY_TYPE PA_(type);
/** 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
idle state, see <fn:<A>array>, `ARRAY_IDLE`, `{0}` (`C99`,) or being `static`.
The fields should be treated as read-only; any modification is liable to cause
the array to go into an invalid state.
![States.](../web/states.png) */
struct A_(array) { PA_(type) *data; size_t size, capacity; };
/* !data -> !size, data -> capacity >= min && size <= capacity <= max */
/** Initialises `a` to idle. @order \Theta(1) @allow */
static void A_(array)(struct A_(array) *const a)
{ assert(a), a->data = 0, a->capacity = a->size = 0; }
/** Destroys `a` and returns it to idle. @allow */
static void A_(array_)(struct A_(array) *const a)
{ assert(a), free(a->data), A_(array)(a); }
/** Ensures `min` capacity of `a`. Invalidates pointers in `a`. @param[min] If
zero, does nothing. @return Success; otherwise, `errno` will be set.
@throws[ERANGE] Tried allocating more then can fit in `size_t` or `realloc`
doesn't follow POSIX. @throws[realloc] @allow */
static int A_(array_reserve)(struct A_(array) *const a, const size_t min) {
size_t c0;
PA_(type) *data;
const size_t max_size = (size_t)-1 / sizeof *a->data;
assert(a);
if(a->data) {
assert(a->size <= a->capacity);
if(min <= a->capacity) return 1;
c0 = a->capacity < ARRAY_MIN_CAPACITY
? ARRAY_MIN_CAPACITY : a->capacity;
} else { /* Idle. */
assert(!a->size && !a->capacity);
if(!min) return 1;
c0 = ARRAY_MIN_CAPACITY;
}
if(min > max_size) return errno = ERANGE, 0;
/* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */
while(c0 < min) { /* \O(\log min), in practice, negligible. */
size_t c1 = c0 + (c0 >> 1) + (c0 >> 3);
if(c0 >= c1) { c0 = max_size; break; } /* Unlikely. */
c0 = c1;
}
if(!(data = realloc(a->data, sizeof *a->data * c0)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data, a->capacity = c0;
return 1;
}
/** The capacity of `a` will be increased to at least `n` elements beyond the
size. Invalidates any pointers in `a`.
@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
indicates an error. @throws[realloc, ERANGE] @allow */
static PA_(type) *A_(array_buffer)(struct A_(array) *const a, const size_t n) {
assert(a);
if(a->size > (size_t)-1 - n) { errno = ERANGE; return 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
<fn:<A>array_append>. */
static PA_(type) *PA_(append)(struct A_(array) *const a, const size_t n) {
PA_(type) *b;
assert(a);
if(!(b = A_(array_buffer)(a, n))) return 0;
assert(n <= a->capacity && a->size <= a->capacity - n);
return a->size += n, b;
}
/** Adds `n` un-initialised elements at position `at` in `a`. The buffer holds
enough elements or it will invalidate any pointers in `a`.
@param[at] A number smaller than or equal to `a.size`; if `a.size`, this
function behaves as <fn:<A>array_append>.
@return A pointer to the start of the new region, where there are `n`
elements. @throws[realloc, ERANGE] @allow */
static PA_(type) *A_(array_insert)(struct A_(array) *const a,
const size_t n, const size_t at) {
const size_t old_size = a->size;
PA_(type) *const b = PA_(append)(a, n);
assert(a && at <= old_size);
if(!b) return 0;
memmove(a->data + at + n, a->data + at, sizeof *a->data * (old_size - at));
return a->data + at;
}
/** @return Adds (push back) one new element of `a`. The buffer holds an
element or it will invalidate pointers in `a`.
@order amortised \O(1) @throws[realloc, ERANGE] @allow */
static PA_(type) *A_(array_new)(struct A_(array) *const a)
{ return PA_(append)(a, 1); }
/** 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`.
@return Success. @throws[ERANGE, realloc] Unlikely `realloc` error. */
static int A_(array_shrink)(struct A_(array) *const a) {
PA_(type) *data;
size_t c;
assert(a && a->capacity >= a->size);
if(!a->data) return assert(!a->size && !a->capacity), 1;
c = a->size && a->size > ARRAY_MIN_CAPACITY ? a->size : ARRAY_MIN_CAPACITY;
if(!(data = realloc(a->data, sizeof *a->data * c)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data, a->capacity = c;
return 1;
}
/** Removes `datum` from `a`. @order \O(`a.size`). @allow */
static void A_(array_remove)(struct A_(array) *const a,
PA_(type) *const datum) {
const size_t n = (size_t)(datum - a->data);
assert(a && datum && datum >= a->data && datum < a->data + a->size);
memmove(datum, datum + 1, sizeof *datum * (--a->size - n));
}
/** Removes `datum` from `a` and replaces it with the tail.
@order \O(1). @allow */
static void A_(array_lazy_remove)(struct A_(array) *const a,
PA_(type) *const datum) {
size_t n = (size_t)(datum - a->data);
assert(a && datum && datum >= a->data && datum < a->data + a->size);
if(--a->size != n) memcpy(datum, a->data + a->size, sizeof *datum);
}
/** Sets `a` to be empty. That is, the size of `a` will be zero, but if it was
previously in an active non-idle state, it continues to be.
@order \Theta(1) @allow */
static void A_(array_clear)(struct A_(array) *const a)
{ assert(a), a->size = 0; }
/** @return The last element or null if `a` is empty. @order \Theta(1) @allow */
static PA_(type) *A_(array_peek)(const struct A_(array) *const a)
{ return assert(a), a->size ? a->data + a->size - 1 : 0; }
/** @return Value from the the top of `a` that is removed or null if the array
is empty. @order \Theta(1) @allow */
static PA_(type) *A_(array_pop)(struct A_(array) *const a)
{ return assert(a), a->size ? a->data + --a->size : 0; }
/** Adds `n` elements to the back of `a`. It will invalidate pointers in `a` if
`n` is greater than the buffer space.
@return A pointer to the elements. If `a` is idle and `n` is zero, a null
pointer will be returned, otherwise null indicates an error.
@throws[realloc, ERANGE] @allow */
static PA_(type) *A_(array_append)(struct A_(array) *const a, const size_t n)
{ return PA_(append)(a, n); }
/** 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`.
@return Success. @throws[realloc, ERANGE] @allow */
static int A_(array_splice)(/*restrict*/ struct A_(array) *const a,
/*restrict*/ const struct A_(array) *const b,
const size_t i0, const size_t i1) {
const size_t a_range = i1 - i0, b_range = b ? b->size : 0;
assert(a && a != b && i0 <= i1 && i1 <= a->size);
if(a_range < b_range) { /* The output is bigger. */
const size_t diff = b_range - a_range;
if(!A_(array_buffer)(a, diff)) return 0;
memmove(a->data + i1 + diff, a->data + i1,
(a->size - i1) * sizeof *a->data);
a->size += diff;
} else if(b_range < a_range) { /* The output is smaller. */
memmove(a->data + i0 + b_range, a->data + i1,
(a->size - i1) * sizeof *a->data);
a->size -= a_range - b_range;
}
if(b) memcpy(a->data + i0, b->data, b->size * sizeof *a->data);
return 1;
}
/* <!-- iterate interface */
/* Contains all iteration parameters. */
struct PA_(iterator) { const struct A_(array) *a; size_t i; };
/** Loads `a` into `it`. @implements begin */
static void PA_(begin)(struct PA_(iterator) *const it,
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 */
/* Forward-declare. */
static void (*PA_(to_string))(const PA_(type) *, char (*)[12]);
static const char *(*PA_(array_to_string))(const struct A_(array) *);
#include "../test/test_array.h" /* (this will needlessly confuse) \include */
#endif /* test --> */
static void PA_(unused_base_coda)(void);
static void PA_(unused_base)(void)
{ A_(array_)(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_clear)(0); A_(array_peek)(0); A_(array_pop)(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)(); }
static void PA_(unused_base_coda)(void) { PA_(unused_base)(); }
#elif defined(ARRAY_TO_STRING) /* base code --><!-- to string trait */
#ifdef ARRAY_TO_STRING_NAME
#define SZ_(n) ARRAY_CAT(A_(array), ARRAY_CAT(ARRAY_TO_STRING_NAME, n))
#else
#define SZ_(n) ARRAY_CAT(A_(array), n)
#endif
#define TO_STRING ARRAY_TO_STRING
#include "to_string.h" /** \include */
#ifdef ARRAY_TEST /* <!-- expect: greedy satisfy forward-declared. */
#undef ARRAY_TEST
static PSZ_(to_string_fn) PA_(to_string) = PSZ_(to_string);
static const char *(*PA_(array_to_string))(const struct A_(array) *)
= &SZ_(to_string);
#endif /* expect --> */
#undef SZ_
#undef ARRAY_TO_STRING
#ifdef ARRAY_TO_STRING_NAME
#undef ARRAY_TO_STRING_NAME
#endif
#else /* to string trait --><!-- compare trait */
#ifdef ARRAY_COMPARE_NAME
#define ARRAY_CODA_NAME ARRAY_COMPARE_NAME
#endif
#ifdef ARRAY_COMPARE /* <!-- cmp */
#define BOX_COMPARE ARRAY_COMPARE
#else /* cmp --><!-- eq */
#define BOX_IS_EQUAL ARRAY_IS_EQUAL
#endif /* eq --> */
#include "array_coda.h" /* (Already included.) */
#ifdef ARRAY_TEST /* <!-- test: this detects and outputs compare test. */
#include "../test/test_array.h"
#endif /* test --> */
#undef ACC_
#undef PACC_
#undef ARRAY_CODA_NAME
#ifdef ARRAY_COMPARE_NAME
#undef ARRAY_COMPARE_NAME
#endif
#ifdef ARRAY_COMPARE
#undef ARRAY_COMPARE
#endif
#ifdef ARRAY_IS_EQUAL
#undef ARRAY_IS_EQUAL
#endif
#endif /* traits --> */
#ifdef ARRAY_EXPECT_TRAIT /* <!-- trait */
#undef ARRAY_EXPECT_TRAIT
#else /* trait --><!-- !trait */
#ifdef ARRAY_TEST
#error No ARRAY_TO_STRING traits defined for ARRAY_TEST.
#endif
#undef ARRAY_NAME
#undef ARRAY_TYPE
/* Iteration. */
#undef BOX_
#undef BOX_CONTAINER
#undef BOX_CONTENTS
/* Coda. */
#undef ARRAY_CODA_TYPE
#undef ARRAY_CODA_BOX_TO_C
#undef ARRAY_CODA_BOX_TO
#undef AC_
#undef ARRAY_CODA_ONCE
#ifdef ARRAY_CODA_COMPARE_ONCE
#undef ARRAY_CODA_COMPARE_ONCE
#endif
#endif /* !trait --> */
#undef ARRAY_TO_STRING_TRAIT
#undef ARRAY_COMPARE_TRAIT
#undef ARRAY_TRAITS

437
src/array_coda.h Normal file
View File

@ -0,0 +1,437 @@
/* @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 Normal file
View File

@ -0,0 +1,432 @@
/** @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

View File

@ -82,38 +82,104 @@ scan:
*/
}
static int parse_uint(const char *s, const char *e, unsigned *u) {
uint32_t n = 0;
for ( ; s < e; ++s) {
unsigned digit = (unsigned)(*s - '0');
assert(digit < 10);
if(n > (UINT_MAX - digit) / 10) return errno = ERANGE, 0; /* check */
n = n * 10 + digit;
}
*u = n;
return 1;
}
/*#define POOL_NAME char
#define POOL_TYPE char
#include "../src/pool.h"*/
static int parse_double(const char *s0, const char *s1, double *d) {
char *finish;
assert(d && s0 && s0 < s1);
*d = strtod(s0, &finish);
return !errno && ((finish == s1) || (errno = EDOM, 0));
struct year_listlink;
static int year_link_compare(const struct year_listlink *,
const struct year_listlink *);
static void yearlist_to_string(const struct year_listlink *, char (*)[12]);
#define LIST_NAME year
#define LIST_EXPECT_TRAIT
#include "../src/list.h"
#define LIST_COMPARE &year_link_compare
#define LIST_EXPECT_TRAIT
#include "../src/list.h"
#define LIST_TO_STRING &yearlist_to_string
#include "../src/list.h"
struct year {
struct year_listlink link;
int year;
char as_string[8];
};
static const size_t year_string_size = sizeof ((struct year *)0)->as_string;
static int year_link_compare(const struct year_listlink *const al,
const struct year_listlink *const bl) {
const struct year *const a = (const struct year *)al,
*const b = (const struct year *)bl;
return (b->year < a->year) - (a->year < b->year);
}
#define POOL_NAME year
#define POOL_TYPE struct year
#include "../src/pool.h"
static unsigned hash_year(const struct year *const year)
{ return (unsigned)(year->year - INT_MIN); }
static int year_is_equal(const struct year *const a, const struct year *const b)
{ return a->year == b->year; }
static void year_to_string(const struct year *const y, char (*const a)[12])
{ strncpy(*a, y->as_string, sizeof(*a) - 1), (*a)[sizeof(*a) - 1] = '\0'; }
static void yearlist_to_string(const struct year_listlink *const y,
char (*const a)[12]) { year_to_string((const struct year *)y, a); }
#define TABLE_NAME year
#define TABLE_KEY struct year *
#define TABLE_UINT unsigned
#define TABLE_HASH &hash_year
#define TABLE_IS_EQUAL &year_is_equal
#define TABLE_EXPECT_TRAIT
#include "../src/table.h"
#define TABLE_DEFAULT 0
#define TABLE_EXPECT_TRAIT
#include "../src/table.h"
#define TABLE_TO_STRING &year_to_string
#include "../src/table.h"
#include <unistd.h> /* chdir (POSIX, not ANSI) */
#include <sys/types.h> /* mode_t (umask) */
#include <sys/stat.h> /* umask */
#include <unistd.h> /* chdir (POSIX) */
#include <sys/types.h> /* mode_t (POSIX) */
#include <sys/stat.h> /* umask (POSIX) */
#include <dirent.h> /* opendir readdir closedir */
#include <limits.h>
int main(int argc, char **argv) {
int success = EXIT_FAILURE;
DIR *year_dir;
struct dirent *year_de;
struct year_pool year_pool = POOL_IDLE;
struct year_list order;
struct year_table years = TABLE_IDLE;
/* Get the years list. fixme: this is way overkill; `int_array