#ifndef ARRAY_HEADER
#define ARRAY_HEADER
#include <stdlib.h>
#include <string.h>
#ifndef AUser_Assert
    #include <assert.h>
    #define AUser_Assert(x) assert(x)
#endif

#ifndef Array_LongNames
    #define Add A_Add
    #define Pop A_Pop
    #define Last A_Last
    #define Reserve A_Reserve
    #define SetLen A_SetLen
    #define UnorderedRemove A_UnorderedRemove
    #define OrderedRemove A_OrderedRemove
    #define Insert A_Insert
#endif

#define Array(T)      \
    struct {          \
        T *data;      \
        int cap, len; \
    }
typedef Array(void) Array_Void;
#define For(i, n) for (int i = 0; i < n; i += 1)
#define FoR(i, n) for (int i = n - 1; i >= 0; i -= 1)

#define A_ElementSize(a) sizeof(*(a)->data)
#define A_Add(a, e) (A__Grow((Array_Void *)a, A_ElementSize(a)), (a)->data[(a)->len++] = (e))
#define A_Pop(a) (AUser_Assert((a)->len > 0), (a)->data[--(a)->len])
#define A_Last(a) (AUser_Assert((a)->len > 0), (a)->data[(a)->len - 1])
#define A_Reserve(a, n) (A__Reserve((Array_Void *)(a), A_ElementSize(a), (n)))
#define A_SetLen(a, n) (A__Reserve((Array_Void *)(a), A_ElementSize(a), (n)), (a)->len = (n))
#define A_UnorderedRemove(a, n) (             \
    AUser_Assert((n) >= 0 && (n) < (a)->len), \
    (a)->data[(n)] = A_Pop(a))
#define A_OrderedRemoveN(a, i, n) (                                                          \
    AUser_Assert((n) >= 0 && (n) < (a)->len),                                                \
    memcpy((a)->data + i, (a)->data + (i) + (n), A_ElementSize(a) * ((a)->len - (i) - (n))), \
    (a)->len -= (n))
#define A_OrderedRemove(a, i) A_OrderedRemoveN(a, i, 1)

#define A_InsertN(a, i, n) (                                            \
    AUser_Assert((i) >= 0 && (i) < (a)->len),                           \
    A__Grow((Array_Void *)a, A_ElementSize(a)),                         \
    memcpy((a)->data + (i) + (n), (a)->data + i, (n)*A_ElementSize(a)), \
    (a)->len += (n))
#define A_Insert(a, i, e) (A_InsertN(a, i, 1), (a)->data[i] = (e))
#define A_Free(a) (free((a)->data), memset(a, 0, sizeof(*a)))

static void A__Reserve(Array_Void *a, int element_size, int new_cap) {
    if (new_cap > a->cap) {
        a->cap = new_cap;
        a->data = realloc(a->data, a->cap * element_size);
        AUser_Assert(a->data);
    }
}

static void A__Grow(Array_Void *a, int element_size) {
    if (a->cap == 0) {
        a->cap = 16;
        a->data = realloc(0, element_size * a->cap);
    }
    else if (a->len + 1 > a->cap) {
        a->cap *= 2;
        a->data = realloc(a->data, element_size * a->cap);
    }
    AUser_Assert(a->data);
}

#endif // ARRAY_HEADER