/* @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 --> */