From 68d2412d65a430c65e4e971ee85f67f0a83aea51 Mon Sep 17 00:00:00 2001 From: Neil Date: Fri, 27 May 2022 23:09:38 -0700 Subject: [PATCH] Added tree. This is much nicer. --- src/array.h | 324 +++++++++++------ src/array_coda.h | 437 ----------------------- src/heap.h | 432 ----------------------- src/interpret.c | 69 ++-- src/list_coda.h | 406 ---------------------- src/to_string.h | 73 ++-- src/tree.h | 877 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1174 insertions(+), 1444 deletions(-) delete mode 100644 src/array_coda.h delete mode 100644 src/heap.h delete mode 100644 src/list_coda.h create mode 100644 src/tree.h diff --git a/src/array.h b/src/array.h index a8c4df9..69aa958 100644 --- a/src/array.h +++ b/src/array.h @@ -1,11 +1,12 @@ /** @license 2016 Neil Edelman, distributed under the terms of the [MIT License](https://opensource.org/licenses/MIT). - @abstract Source ; examples . + @abstract Stand-alone header ; examples ; on a + compatible workstation, `make` creates the test suite of the examples. @subtitle Contiguous dynamic array - ![Example of array.](../web/array.png) + ![Example of array.](../doc/array.png) array> is a dynamic array that stores contiguous type>. Resizing may be necessary when increasing the size of the array; this incurs @@ -16,26 +17,18 @@ type>, associated therewith; required. `` is private, whose names are prefixed in a manner to avoid collisions. - @param[ARRAY_CODA] - Include more functions contained in , where `` is - `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 . An optional mangled name for - uniqueness and a function implementing either compare_fn> or - bipredicate_fn>. + Compare trait contained in . An optional mangled name for + uniqueness and a function implementing either compare_fn> or + bipredicate_fn>. @param[ARRAY_TO_STRING_NAME, ARRAY_TO_STRING] To string trait contained in . An optional mangled name for - uniqueness and function implementing to_string_fn>. + uniqueness and function implementing to_string_fn>. @std C89 */ @@ -57,6 +50,9 @@ #if ARRAY_TRAITS > 1 #error Only one trait per include is allowed; use ARRAY_EXPECT_TRAIT. #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) #error ARRAY_TO_STRING_NAME requires ARRAY_TO_STRING. #endif @@ -71,8 +67,7 @@ #include #include #include -#if defined(ARRAY_CAT_) || defined(ARRAY_CAT) || defined(A_) || defined(PA_) \ - || defined(ARRAY_IDLE) +#if defined(ARRAY_CAT_) || defined(ARRAY_CAT) || defined(A_) || defined(PA_) #error Unexpected defines. #endif /* . */ @@ -80,9 +75,14 @@ #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 !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 /* */ - -/* */ - -#ifdef ARRAY_CODA /* */ +#ifdef HAVE_ITERATE_H /* */ #ifdef ARRAY_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); +static void PA_(unused_base)(void) { + 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_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)(); } + PA_(unused_base_coda)(); +} 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 -#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 -#define SZ_(n) ARRAY_CAT(A_(array), n) +#define STR_(n) ARRAY_CAT(A_(array), n) #endif #define TO_STRING ARRAY_TO_STRING #include "to_string.h" /** \include */ #ifdef ARRAY_TEST /* */ -#undef SZ_ +#undef STR_ #undef ARRAY_TO_STRING #ifdef 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 -#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 #ifdef ARRAY_COMPARE /* */ -#include "array_coda.h" /* (Already included.) */ +#include "compare.h" /** \include */ #ifdef ARRAY_TEST /* */ -#undef ACC_ -#undef PACC_ -#undef ARRAY_CODA_NAME +#undef CMP_ #ifdef ARRAY_COMPARE_NAME #undef ARRAY_COMPARE_NAME #endif @@ -377,20 +488,17 @@ static const char *(*PA_(array_to_string))(const struct A_(array) *) #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 +#undef BOX +#undef BOX_CONTENT +#undef BOX_ITERATOR +#undef BOX_ACCESS +#undef BOX_CONTIGUOUS #endif /* !trait --> */ #undef ARRAY_TO_STRING_TRAIT #undef ARRAY_COMPARE_TRAIT #undef ARRAY_TRAITS +#ifdef ARRAY_RESTRICT +#undef ARRAY_RESTRICT +#undef restrict +#endif diff --git a/src/array_coda.h b/src/array_coda.h deleted file mode 100644 index a9c8bfe..0000000 --- a/src/array_coda.h +++ /dev/null @@ -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 box_to_array_c> and - 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 /* */ - -#ifndef ARRAY_CODA_ONCE /* */ - - -#if !defined(BOX_IS_EQUAL) && !defined(BOX_COMPARE) /* */ - -#ifdef ARRAY_CODA_NAME -#define ACC_(n) AC_(ARRAY_CAT(ARRAY_CODA_NAME, n)) -#else /* name --> */ -#define PACC_(n) ARRAY_CAT(array_coda, ACC_(n)) - -#ifdef BOX_COMPARE /* */ - -/** @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; -} - -/** : 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; -} - -/** : 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 /* */ - 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 --> */ diff --git a/src/heap.h b/src/heap.h deleted file mode 100644 index cdbe175..0000000 --- a/src/heap.h +++ /dev/null @@ -1,432 +0,0 @@ -/** @license 2020 Neil Edelman, distributed under the terms of the - [MIT License](https://opensource.org/licenses/MIT). - - @abstract Source , depends on ; examples - . - - @subtitle Priority-queue - - ![Example of heap.](../web/heap.png) - - A heap> is a binary heap, proposed by - using terminology of - . It can be used as an implementation of a priority - queue; internally, it is a `<node>array` with implicit heap properties on - priority> and an optional value> pointer value. - - @param[HEAP_NAME, HEAP_TYPE] - `` that satisfies `C` naming conventions when mangled and an assignable - type priority> associated therewith. `HEAP_NAME` is required; - `HEAP_TYPE` defaults to `unsigned int`. `` is private, whose names are - prefixed in a manner to avoid collisions. - - @param[HEAP_COMPARE] - A function satisfying compare_fn>. Defaults to minimum-hash. - Required if `HEAP_TYPE` is changed to an incomparable type. - - @param[HEAP_VALUE] - Optional value value>, that is stored as a reference in - heapnode>; declaring it is sufficient. If set, has no effect on the - ranking, but affects value>, (otherwise, it's the same field as - 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 ; an optional unique `` - that satisfies `C` naming conventions when mangled and function implementing - 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) /* */ -#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 /* */ - - -#if HEAP_TRAITS == 0 /* */ -/* Check that `HEAP_COMPARE` is a function implementing - compare_fn>, if defined. */ -static const PH_(compare_fn) PH_(compare) = (HEAP_COMPARE); - -#ifdef HEAP_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 heap>, `HEAP_IDLE`, `{0}` (`C99`), - or being `static`. - - ![States.](../web/states.png) */ -struct H_(heap) { struct PH_(node_array) a; }; - -/** Extracts the 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 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 --> */ -} - -/** Copies `src` to `dest`. */ -static void PH_(copy)(const PH_(node) *const src, PH_(node) *const dest) { -#ifdef HEAP_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 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 - 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 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 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 to - sift-down all the internal nodes of heap. The heap elements must exist, see - heap_buffer>. - @param[n] If zero, returns true without heapifying. - @return Success. @order \O(`heap.size` + `n`) @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; -} - -/* */ - -/* Define these for traits. */ -#define BOX_ PH_ -#define BOX_CONTAINER struct H_(heap) -#define BOX_CONTENTS PH_(node) - -#ifdef HEAP_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 --> */ -#define TSZ_(n) HEAP_CAT(heap_sz, SZ_(n)) -#ifdef HEAP_VALUE /* */ -#include "to_string.h" /** \include */ -#ifdef HEAP_TEST /* */ -#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 /* */ -#endif /* !trait --> */ -#undef HEAP_TO_STRING_TRAIT -#undef HEAP_TRAITS diff --git a/src/interpret.c b/src/interpret.c index e8c417b..43fad93 100644 --- a/src/interpret.c +++ b/src/interpret.c @@ -67,13 +67,17 @@ static int leap(int y) { if(!(y % 4)) return 1; return 0; } -/** Not defined for some implementations. (I think the standard also doesn't - say which order they should be, that's a problem.) */ +/** Not defined for some implementations. C11 */ union date32 { - uint32_t i32; - struct { unsigned year : 23, month : 4, day : 5; }; - /* This is unsetting. Obv. it should be `struct anonymous {};`. */ + uint32_t u32; + struct { unsigned day : 5, month : 4, year : 23; }; }; +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. */ static union date32 date_to_32(const int y, const int m, const int d) { union date32 d32 = { 0 }; @@ -98,33 +102,37 @@ static unsigned weekday(union date32 d) { + "-bed=pen+mad."[d.month] + d.day) % 7; } + #define ARRAY_NAME lex #define ARRAY_TYPE struct lex #include "array.h" +struct page_tree_entry_c; +static void entry_to_string(struct page_tree_entry_c, char (*)[12]); struct page { - union date32 date; struct char_array entry; struct lex_array lexx; }; -#define POOL_NAME page -#define POOL_TYPE struct page -#include "pool.h" +#define TREE_NAME page +#define TREE_KEY union date32 +#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; }; int main(int argc, char **argv) { - /* Return value. */ - int success = EXIT_FAILURE; + int success = EXIT_SUCCESS; /* For reading in files, overwritten. */ DIR *dir = 0; 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; - /* Backing for individual pages; temporary page yet to be placed. */ - struct page_pool pages = POOL_IDLE; - struct page *page = 0; + struct page_tree journal = page_tree(); errno = 0; 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++) { int *d, *d_end; sprintf(fn, "%.2d", *m); + printf("month %s\n", fn); /* Get the days as files. */ 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++) { union date32 d32; + struct page *page = 0; printf("Date: %d-%.2d-%.2d\n", *y, *m, *d); if(!(d32 = date_to_32(*y, *m, *d)).year) { errno = EILSEQ; goto catch; } sprintf(fn, "%.2d.txt", *d); - if(!(page = page_pool_new(&pages))) goto catch; - char_array(&page->entry); - lex_array(&page->lexx); + if(page_tree_bulk_add(&journal, d32, &page) != TREE_UNIQUE) { + if(!errno) fprintf(stderr, "Not unique?\n"), errno = EDOM; + goto catch; + } + page->entry = char_array(); + page->lexx = lex_array(); if(!append_file(&page->entry, fn)) goto catch; printf("%s", page->entry.data); printf("Lexing:\n"); @@ -235,20 +248,32 @@ int main(int argc, char **argv) { if(chdir("..") == -1) goto catch; break; /* fixme */ } - { success = EXIT_SUCCESS; goto finally; } + page_tree_bulk_finish(&journal); + goto finally; catch: + success = EXIT_FAILURE; perror("interpret"); finally: if(dir && closedir(dir)) success = EXIT_FAILURE, perror("dir"); int_array_(&years); int_array_(&months); 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. */ - if(page) { lex_array_(&page->lexx); char_array(&page->entry); } + /*if(page) { lex_array_(&page->lexx); char_array(&page->entry); } { struct page *page; - while(page = page_array_pop(&pages)) + while(page = page_array_pop(&journal)) lex_array_(&page->lexx), char_array_(&page->entry); - } + } memory leak! */ return success; } diff --git a/src/list_coda.h b/src/list_coda.h deleted file mode 100644 index 9ee744d..0000000 --- a/src/list_coda.h +++ /dev/null @@ -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`, - bipredicate_fn> that establishes an equivalence relation, or - for `COMPARE`, 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 /* */ - -/** Returns a boolean given two read-only listlink>. */ -typedef int (*PLC_(bipredicate_fn))(const struct L_(listlink) *, - const struct L_(listlink) *); - -#ifdef LIST_COMPARE /* */ - -/** @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 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 /* */ - 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 diff --git a/src/to_string.h b/src/to_string.h index 0bc249e..f300c5c 100644 --- a/src/to_string.h +++ b/src/to_string.h @@ -3,13 +3,13 @@ @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 to string function. Should be something like - `SZ_(to_string) -> widget_foo_to_string`. The caller is responsible for - undefining `SZ_`. + `STR_(to_string) -> widget_foo_to_string`. The caller is responsible for + undefining `STR_`. @param[TO_STRING] Function implementing to_string_fn>. @@ -27,9 +27,9 @@ @std C89 */ -#if !defined(BOX_) || !defined(BOX_CONTAINER) || !defined(BOX_CONTENTS) \ - || !defined(SZ_) || !defined(TO_STRING) -#error Unexpected preprocessor symbols. Check that one including it as a trait. +#if !defined(BOX_) || !defined(BOX) || !defined(BOX_CONTENT) \ + || !defined(STR_) || !defined(TO_STRING) +#error Unexpected preprocessor symbols. #endif #if defined(TO_STRING_H) \ @@ -44,13 +44,13 @@ #ifndef TO_STRING_H /* */ + + +#if TREE_TRAITS == 0 /* */ + +/* Check that `TREE_COMPARE` is a function implementing + compare_fn>, if defined. */ +static const PB_(compare_fn) PB_(compare) = (TREE_COMPARE); + +/* B-tree node, as . These rules are more lazy + than the original so as to not exhibit worst-case behaviour in small trees, as + , but lookup is potentially slower after + deleting; this is a design decision that nodes are not cached. In the + terminology of , + * 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 + , 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 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 /* */ + +/** To initialize it to an idle state, see tree>, `TRIE_IDLE`, `{0}` + (`C99`), or being `static`. This is a B-tree, as + . + + ![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 /* */ + +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 --> */ +#undef STR_ +#undef TREE_TO_STRING +#ifdef TREE_TO_STRING_NAME +#undef TREE_TO_STRING_NAME +#endif + + +#endif /* traits --> */ + + +#ifdef TREE_EXPECT_TRAIT /* */ +#undef TREE_TO_STRING_TRAIT +#undef TREE_TRAITS