/*
 * M*LIB - dynamic ARRAY module
 *
 * Copyright (c) 2017-2023, Patrick Pelissier
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * + Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * + Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MSTARLIB_ARRAY_H
#define MSTARLIB_ARRAY_H

#include "m-core.h"

/* Define a dynamic array of the given type and its associated functions.
   USAGE: ARRAY_DEF(name, type [, oplist_of_the_type]) */
#define M_ARRAY_DEF(name, ...)                                                \
  M_ARRAY_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__)


/* Define a dynamic array of the given type and its associated functions
  as the provided type name_t with the iterator named it_t
   USAGE: ARRAY_DEF_AS(name, name_t, it_t, type [, oplist_of_the_type]) */
#define M_ARRAY_DEF_AS(name, name_t, it_t, ...)                               \
  M_BEGIN_PROTECTED_CODE                                                      \
  M_ARRA4_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__)                                  \
             ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \
              (name, __VA_ARGS__,                                        name_t, it_t ))) \
  M_END_PROTECTED_CODE


/* Define the oplist of a dynamic array given its name and its oplist.
   If no oplist is given it is assumed to be M_BASIC_OPLIST
   USAGE: ARRAY_OPLIST(name[, oplist of the type]) */
#define M_ARRAY_OPLIST(...)                                                   \
  M_ARRA4_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__)                               \
                   ((__VA_ARGS__, M_BASIC_OPLIST),                            \
                    (__VA_ARGS__ )))


/* Define an init value to init global variables of type array.
  USAGE:
    array_t global_variable = ARRAY_INIT_VALUE();
 */
#define M_ARRAY_INIT_VALUE()                                                  \
  { { 0, 0, NULL } }


/*****************************************************************************/
/********************************** INTERNAL *********************************/
/*****************************************************************************/

/* Deferred evaluation for the oplist definition,
   so that all arguments are evaluated before further expansion */
#define M_ARRA4_OPLIST_P1(arg) M_ARRA4_OPLIST_P2 arg

/* Validation of the given oplist */
#define M_ARRA4_OPLIST_P2(name, oplist)                                       \
  M_IF_OPLIST(oplist)(M_ARRA4_OPLIST_P3, M_ARRA4_OPLIST_FAILURE)(name, oplist)

/* Prepare a clean compilation failure */
#define M_ARRA4_OPLIST_FAILURE(name, oplist)                                  \
  ((M_LIB_ERROR(ARGUMENT_OF_ARRAY_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))

/* OPLIST definition of a dynamic array */
/* FIXME: Do we want to export some methods as they are slow and 
   are not fit to be used for building other methods (like _it_remove)? */
#define M_ARRA4_OPLIST_P3(name, oplist)                                       \
  (INIT(M_F(name, _init))                                                     \
   ,M_IF_METHOD2(INIT_SET,SET, oplist)(INIT_SET(M_F(name, _init_set)),)       \
   ,M_IF_METHOD(INIT_SET, oplist)(INIT_WITH(API_1(M_INIT_WITH_VAI)),)         \
   ,M_IF_METHOD2(INIT_SET,SET, oplist)(SET(M_F(name, _set)), )                \
   ,CLEAR(M_F(name, _clear))                                                  \
   ,INIT_MOVE(M_F(name, _init_move))                                          \
   ,MOVE(M_F(name, _move))                                                    \
   ,SWAP(M_F(name, _swap))                                                    \
   ,TYPE(M_F(name,_ct))                                                       \
   ,NAME(name)                                                                \
   ,SUBTYPE(M_F(name, _subtype_ct))                                           \
   ,EMPTY_P(M_F(name,_empty_p))                                               \
   ,IT_TYPE(M_F(name,_it_ct)),                                                \
   ,IT_FIRST(M_F(name,_it))                                                   \
   ,IT_LAST(M_F(name,_it_last))                                               \
   ,IT_END(M_F(name,_it_end))                                                 \
   ,IT_SET(M_F(name,_it_set))                                                 \
   ,IT_END_P(M_F(name,_end_p))                                                \
   ,IT_LAST_P(M_F(name,_last_p))                                              \
   ,IT_EQUAL_P(M_F(name,_it_equal_p))                                         \
   ,IT_NEXT(M_F(name,_next))                                                  \
   ,IT_PREVIOUS(M_F(name,_previous))                                          \
   ,IT_REF(M_F(name,_ref))                                                    \
   ,IT_CREF(M_F(name,_cref))                                                  \
   ,M_IF_METHOD(INIT_SET, oplist)(IT_INSERT(M_F(name,_insert)) ,)             \
   ,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(IT_REMOVE(M_F(name,_remove)),) \
   ,RESET(M_F(name,_reset))                                                   \
   ,KEY_TYPE(size_t)                                                          \
   ,VALUE_TYPE(M_F(name, _subtype_ct))                                        \
   ,KEY_OPLIST(M_BASIC_OPLIST)                                                \
   ,VALUE_OPLIST(oplist)                                                      \
   ,M_IF_METHOD(SET, oplist)(SET_KEY(M_F(name, _set_at)) ,)                   \
   ,GET_KEY(M_F(name, _get))                                                  \
   ,M_IF_METHOD(INIT, oplist)(SAFE_GET_KEY(M_F(name, _safe_get)) ,)           \
   ,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(ERASE_KEY(M_F(name, _erase)),) \
   ,GET_SIZE(M_F(name, _size))                                                \
   ,M_IF_METHOD(INIT_SET, oplist)(PUSH(M_F(name,_push_back)) ,)               \
   ,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(POP(M_F(name,_pop_back)) ,)    \
   ,M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist)(PUSH_MOVE(M_F(name,_push_move)) ,) \
   ,M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist)(POP_MOVE(M_F(name,_pop_move)) ,) \
   ,OPLIST(oplist)                                                            \
   ,M_IF_METHOD(CMP, oplist)(SORT(M_F(name, _special_sort)),)                 \
   ,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),)               \
   ,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),)         \
   ,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),)               \
   ,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),)                  \
   ,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),)      \
   ,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),)         \
   ,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),)                   \
   ,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),)                        \
   )


/********************************** INTERNAL *********************************/

/* Define the internal contract of an array */
#define M_ARRA4_CONTRACT(a) do {                                              \
    M_ASSERT (a != NULL);                                                     \
    M_ASSERT (a->size <= a->alloc);                                           \
    M_ASSERT (a->size == 0 || a->ptr != NULL);                                \
    M_ASSERT (a->alloc == 0 || a->ptr != NULL);                               \
  } while (0)


/* Deferred evaluation for the array definition,
   so that all arguments are fully evaluated before further expansion
   (ensuring good performance)
   and performed a final step evaluation of the returning expansion
   (ensuring delayed evaluation are still expanded)
*/
#define M_ARRA4_DEF_P1(arg) M_ID( M_ARRA4_DEF_P2 arg )

/* Validate the oplist before going further */
#define M_ARRA4_DEF_P2(name, type, oplist, array_t, it_t)                     \
  M_IF_OPLIST(oplist)(M_ARRA4_DEF_P3, M_ARRA4_DEF_FAILURE)(name, type, oplist, array_t, it_t)

/* Stop processing with a compilation failure */
#define M_ARRA4_DEF_FAILURE(name, type, oplist, array_t, it_t)                \
  M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ARRAY_DEF): the given argument is not a valid oplist: " #oplist)

/* Internal definition:
   - name: prefix to be used
   - type: type of the elements of the array
   - oplist: oplist of the type of the elements of the array
   - array_t: alias for the type of the array
   - it_t: alias for the iterator of the array
*/
#define M_ARRA4_DEF_P3(name, type, oplist, array_t, it_t)                     \
  M_ARRA4_DEF_TYPE(name, type, oplist, array_t, it_t)                         \
  M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist)                            \
  M_ARRA4_DEF_CORE(name, type, oplist, array_t, it_t)                         \
  M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t)                           \
  M_EMPLACE_QUEUE_DEF(name, array_t, M_F(name, _emplace_back), oplist, M_ARRA4_EMPLACE_DEF)

/* Define the types */
#define M_ARRA4_DEF_TYPE(name, type, oplist, array_t, it_t)                   \
                                                                              \
  /* Define a dynamic array */                                                \
  typedef struct M_F(name, _s) {                                              \
    size_t size;            /* Number of elements in the array */             \
    size_t alloc;           /* Allocated size for the array base */           \
    type *ptr;              /* Pointer to the array base */                   \
  } array_t[1];                                                               \
                                                                              \
  /* Define an iterator over an array */                                      \
  typedef struct M_F(name, _it_s) {                                           \
    size_t index;                       /* Index of the element */            \
    const struct M_F(name, _s) *array;  /* Reference of the array */          \
  } it_t[1];                                                                  \
                                                                              \
  /* Definition of the synonyms of the type */                                \
  typedef struct M_F(name, _s) *M_F(name, _ptr);                              \
  typedef const struct M_F(name, _s) *M_F(name, _srcptr);                     \
  typedef array_t M_F(name, _ct);                                             \
  typedef it_t M_F(name, _it_ct);                                             \
  typedef type M_F(name, _subtype_ct);                                        \

/* Define the core functions */
#define M_ARRA4_DEF_CORE(name, type, oplist, array_t, it_t)                   \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _init)(array_t v)                                                 \
  {                                                                           \
    M_ASSERT (v != NULL);                                                     \
    /* Initially, the array is empty with nothing allocated */                \
    v->size  = 0;                                                             \
    v->alloc = 0;                                                             \
    v->ptr   = NULL;                                                          \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _reset)(array_t v)                                                \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    for(size_t i = 0; i < v->size; i++)                                       \
      M_CALL_CLEAR(oplist, v->ptr[i]);                                        \
    v->size = 0;                                                              \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _clear)(array_t v)                                                \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_F(name, _reset)(v);                                                     \
    M_CALL_FREE(oplist, v->ptr);                                              \
    /* This is so reusing the object implies an assertion failure */          \
    v->alloc = 1;                                                             \
    v->ptr = NULL;                                                            \
  }                                                                           \
                                                                              \
  M_IF_METHOD2(INIT_SET, SET, oplist)(                                        \
  M_INLINE void                                                               \
  M_F(name, _set)(array_t d, const array_t s)                                 \
  {                                                                           \
    M_ARRA4_CONTRACT(d);                                                      \
    M_ARRA4_CONTRACT(s);                                                      \
    if (M_UNLIKELY (d == s)) return;                                          \
    if (s->size > d->alloc) {                                                 \
      const size_t alloc = s->size;                                           \
      type *ptr = M_CALL_REALLOC(oplist, type, d->ptr, alloc);                \
      if (M_UNLIKELY_NOMEM (ptr == NULL)) {                                   \
        M_MEMORY_FULL(sizeof (type) * alloc);                                 \
        return ;                                                              \
      }                                                                       \
      d->ptr = ptr;                                                           \
      d->alloc = alloc;                                                       \
    }                                                                         \
    size_t i;                                                                 \
    size_t step1 = M_MIN(s->size, d->size);                                   \
    for(i = 0; i < step1; i++)                                                \
      M_CALL_SET(oplist, d->ptr[i], s->ptr[i]);                               \
    for( ; i < s->size; i++)                                                  \
      M_CALL_INIT_SET(oplist, d->ptr[i], s->ptr[i]);                          \
    for( ; i < d->size; i++)                                                  \
      M_CALL_CLEAR(oplist, d->ptr[i]);                                        \
    d->size = s->size;                                                        \
    M_ARRA4_CONTRACT(d);                                                      \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _init_set)(array_t d, const array_t s)                            \
  {                                                                           \
    M_ASSERT (d != s);                                                        \
    M_F(name, _init)(d);                                                      \
    M_F(name, _set)(d, s);                                                    \
  }                                                                           \
  , /* No SET & INIT_SET */)                                                  \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _init_move)(array_t d, array_t s)                                 \
  {                                                                           \
    M_ASSERT (d != s);                                                        \
    M_ARRA4_CONTRACT(s);                                                      \
    d->size  = s->size;                                                       \
    d->alloc = s->alloc;                                                      \
    d->ptr   = s->ptr;                                                        \
    /* Robustness */                                                          \
    s->alloc = 1;                                                             \
    s->ptr   = NULL;                                                          \
    M_ARRA4_CONTRACT(d);                                                      \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _move)(array_t d, array_t s)                                      \
  {                                                                           \
    M_ASSERT (d != s);                                                        \
    M_F(name, _clear)(d);                                                     \
    M_F(name, _init_move)(d, s);                                              \
  }                                                                           \
                                                                              \
  M_IF_METHOD(SET, oplist)(                                                   \
  M_INLINE void                                                               \
  M_F(name, _set_at)(array_t v, size_t i, type const x)                       \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT(v->size > 0 && v->ptr != NULL);                                  \
    M_ASSERT_INDEX(i, v->size);                                               \
    M_CALL_SET(oplist, v->ptr[i], x);                                         \
  }                                                                           \
  , /* No SET */)                                                             \
                                                                              \
  M_INLINE type  *                                                            \
  M_F(name, _back)(array_t v)                                                 \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT(v->ptr != NULL);                                                 \
    M_ASSERT_INDEX(0, v->size);                                               \
    return &v->ptr[v->size-1];                                                \
  }                                                                           \
                                                                              \
  M_INLINE type *                                                             \
  M_F(name, _push_raw)(array_t v)                                             \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    if (M_UNLIKELY (v->size >= v->alloc)) {                                   \
      M_ASSERT(v->size == v->alloc);                                          \
      size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc);                      \
      if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) {                             \
        M_MEMORY_FULL(sizeof (type) * alloc);                                 \
        return NULL;                                                          \
      }                                                                       \
      M_ASSERT (alloc > v->size);                                             \
      type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc);                \
      if (M_UNLIKELY_NOMEM (ptr == NULL) ) {                                  \
        M_MEMORY_FULL(sizeof (type) * alloc);                                 \
        return NULL;                                                          \
      }                                                                       \
      v->ptr = ptr;                                                           \
      v->alloc = alloc;                                                       \
    }                                                                         \
    M_ASSERT(v->ptr != NULL);                                                 \
    type *ret = &v->ptr[v->size];                                             \
    v->size++;                                                                \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSUME(ret != NULL);                                                    \
    return ret;                                                               \
  }                                                                           \
                                                                              \
  M_IF_METHOD(INIT_SET, oplist)(                                              \
  M_INLINE void                                                               \
  M_F(name, _push_back)(array_t v, type const x)                              \
  {                                                                           \
    type *data = M_F(name, _push_raw)(v);                                     \
    if (M_UNLIKELY (data == NULL) )                                           \
      return;                                                                 \
    M_CALL_INIT_SET(oplist, *data, x);                                        \
  }                                                                           \
  , /* No INIT_SET */ )                                                       \
                                                                              \
  M_IF_METHOD(INIT, oplist)(                                                  \
  M_INLINE type *                                                             \
  M_F(name, _push_new)(array_t v)                                             \
  {                                                                           \
    type *data = M_F(name, _push_raw)(v);                                     \
    if (M_UNLIKELY (data == NULL) )                                           \
      return NULL;                                                            \
    M_CALL_INIT(oplist, *data);                                               \
    return data;                                                              \
  }                                                                           \
  , /* No INIT */ )                                                           \
                                                                              \
  M_IF_AT_LEAST_METHOD(INIT_SET, INIT_MOVE, oplist)(                          \
  M_INLINE void                                                               \
  M_F(name, _push_move)(array_t v, type *x)                                   \
  {                                                                           \
    M_ASSERT (x != NULL);                                                     \
    type *data = M_F(name, _push_raw)(v);                                     \
    if (M_UNLIKELY (data == NULL) )                                           \
      return;                                                                 \
    M_DO_INIT_MOVE (oplist, *data, *x);                                       \
  }                                                                           \
  , /* INIT_SET | INIT_MOVE */ )                                              \
                                                                              \
  M_IF_METHOD(INIT_SET, oplist)(                                              \
  M_INLINE void                                                               \
  M_F(name, _push_at)(array_t v, size_t key, type const x)                    \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT_INDEX(key, v->size+1);                                           \
    if (M_UNLIKELY (v->size >= v->alloc) ) {                                  \
      M_ASSERT(v->size == v->alloc);                                          \
      size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc);                      \
      if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) {                             \
        M_MEMORY_FULL(sizeof (type) * alloc);                                 \
        return ;                                                              \
      }                                                                       \
      M_ASSERT (alloc > v->size);                                             \
      type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc);                \
      if (M_UNLIKELY_NOMEM (ptr == NULL) ) {                                  \
        M_MEMORY_FULL(sizeof (type) * alloc);                                 \
        return;                                                               \
      }                                                                       \
      v->ptr = ptr;                                                           \
      v->alloc = alloc;                                                       \
    }                                                                         \
    M_ASSERT(v->ptr != NULL);                                                 \
    memmove(&v->ptr[key+1], &v->ptr[key], (v->size-key)*sizeof(type));        \
    v->size++;                                                                \
    M_CALL_INIT_SET(oplist, v->ptr[key], x);                                  \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
  , /* No INIT_SET */ )                                                       \
                                                                              \
  M_IF_METHOD(INIT, oplist)(                                                  \
  M_INLINE void                                                               \
  M_F(name, _resize)(array_t v, size_t size)                                  \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    if (v->size > size) {                                                     \
      /* Decrease size of array */                                            \
      for(size_t i = size ; i < v->size; i++)                                 \
        M_CALL_CLEAR(oplist, v->ptr[i]);                                      \
      v->size = size;                                                         \
    } else if (v->size < size) {                                              \
      /* Increase size of array */                                            \
      if (size > v->alloc) {                                                  \
        size_t alloc = size ;                                                 \
        type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc);              \
        if (M_UNLIKELY_NOMEM (ptr == NULL) ) {                                \
          M_MEMORY_FULL(sizeof (type) * alloc);                               \
          return;                                                             \
        }                                                                     \
        v->ptr = ptr;                                                         \
        v->alloc = alloc;                                                     \
      }                                                                       \
      for(size_t i = v->size ; i < size; i++)                                 \
        M_CALL_INIT(oplist, v->ptr[i]);                                       \
      v->size = size;                                                         \
    }                                                                         \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
  , /* No INIT */ )                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _reserve)(array_t v, size_t alloc)                                \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    /* NOTE: Reserve below needed size to perform a shrink to fit */          \
    if (v->size > alloc) {                                                    \
      alloc = v->size;                                                        \
    }                                                                         \
    if (M_UNLIKELY (alloc == 0)) {                                            \
      M_CALL_FREE(oplist, v->ptr);                                            \
      v->size = v->alloc = 0;                                                 \
      v->ptr = NULL;                                                          \
    } else {                                                                  \
      type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc);                \
      if (M_UNLIKELY_NOMEM (ptr == NULL) ) {                                  \
        M_MEMORY_FULL(sizeof (type) * alloc);                                 \
        return;                                                               \
      }                                                                       \
      v->ptr = ptr;                                                           \
      v->alloc = alloc;                                                       \
    }                                                                         \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
                                                                              \
  M_IF_METHOD(INIT, oplist)(                                                  \
  M_INLINE type *                                                             \
  M_F(name, _safe_get)(array_t v, size_t idx)                                 \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    const size_t size = idx + 1;                                              \
    /* resize if needed */                                                    \
    if (v->size <= size) {                                                    \
      /* Increase size of array */                                            \
      if (M_UNLIKELY (size > v->alloc) ) {                                    \
        size_t alloc = M_CALL_INC_ALLOC(oplist, size) ;                       \
        /* In case of overflow of size_t */                                   \
        if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) {                           \
          M_MEMORY_FULL(sizeof (type) * alloc);                               \
          return NULL;                                                        \
        }                                                                     \
        type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc);              \
        if (M_UNLIKELY_NOMEM (ptr == NULL) ) {                                \
          M_MEMORY_FULL(sizeof (type) * alloc);                               \
          return NULL;                                                        \
        }                                                                     \
        v->ptr = ptr;                                                         \
        v->alloc = alloc;                                                     \
      }                                                                       \
      for(size_t i = v->size ; i < size; i++)                                 \
        M_CALL_INIT(oplist, v->ptr[i]);                                       \
      v->size = size;                                                         \
    }                                                                         \
    M_ASSERT (idx < v->size);                                                 \
    M_ARRA4_CONTRACT(v);                                                      \
    return &v->ptr[idx];                                                      \
  }                                                                           \
  , /* No INIT */)                                                            \
                                                                              \
  M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)(                               \
  M_INLINE void                                                               \
  M_F(name, _pop_back)(type *dest, array_t v)                                 \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT (v->ptr != NULL);                                                \
    M_ASSERT_INDEX(0, v->size);                                               \
    v->size--;                                                                \
    if (dest) {                                                               \
      M_DO_MOVE (oplist, *dest, v->ptr[v->size]);                             \
    } else {                                                                  \
      M_CALL_CLEAR(oplist, v->ptr[v->size]);                                  \
    }                                                                         \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
  , /* SET | INIT_MOVE */ )                                                   \
                                                                              \
  M_IF_AT_LEAST_METHOD(INIT_SET, INIT_MOVE, oplist)(                          \
  M_INLINE void                                                               \
  M_F(name, _pop_move)(type *dest, array_t v)                                 \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT (v->ptr != NULL);                                                \
    M_ASSERT_INDEX(0, v->size);                                               \
    M_ASSERT (dest != NULL);                                                  \
    v->size--;                                                                \
    M_DO_INIT_MOVE (oplist, *dest, v->ptr[v->size]);                          \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
  , /* INIT_SET | INIT_MOVE */ )                                              \
                                                                              \
  M_IF_METHOD(INIT, oplist)(                                                  \
  M_INLINE void                                                               \
  M_F(name, _pop_until)(array_t v, it_t pos)                                  \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT (v == pos->array);                                               \
    M_ASSERT_INDEX(pos->index, v->size+1);                                    \
    M_F(name, _resize)(v, pos->index);                                        \
  }                                                                           \
  , /* No INIT */ )                                                           \
                                                                              \
  M_INLINE bool                                                               \
  M_F(name, _empty_p)(const array_t v)                                        \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    return v->size == 0;                                                      \
  }                                                                           \
                                                                              \
  M_INLINE size_t                                                             \
  M_F(name, _size)(const array_t v)                                           \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    return v->size;                                                           \
  }                                                                           \
                                                                              \
  M_INLINE size_t                                                             \
  M_F(name, _capacity)(const array_t v)                                       \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    return v->alloc;                                                          \
  }                                                                           \
                                                                              \
  M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)(                               \
  M_INLINE void                                                               \
  M_F(name, _pop_at)(type *dest, array_t v, size_t i)                         \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT (v->size > 0 && v->ptr != NULL);                                 \
    M_ASSERT_INDEX(i, v->size);                                               \
    if (dest)                                                                 \
      M_DO_MOVE (oplist, *dest, v->ptr[i]);                                   \
    else                                                                      \
      M_CALL_CLEAR(oplist, v->ptr[i]);                                        \
    memmove(&v->ptr[i], &v->ptr[i+1], sizeof(type)*(v->size-1-i));            \
    v->size--;                                                                \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
                                                                              \
  M_INLINE bool                                                               \
  M_F(name, _erase)(array_t a, size_t i)                                      \
  {                                                                           \
    M_ARRA4_CONTRACT(a);                                                      \
    if (i >= a->size) return false;                                           \
    M_F(name, _pop_at)(NULL, a, i);                                           \
    return true;                                                              \
  }                                                                           \
  , /* SET | INIT_MOVE */ )                                                   \
                                                                              \
  M_IF_METHOD(INIT, oplist)(                                                  \
  M_INLINE void                                                               \
  M_F(name, _insert_v)(array_t v, size_t i, size_t num)                       \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT_INDEX(i, v->size+1);                                             \
    size_t size = v->size + num;                                              \
    /* Test for overflow of variable size */                                  \
    if (M_UNLIKELY_NOMEM (size <= v->size)) {                                 \
      /* Unlikely case of nothing to do */                                    \
      if (num == 0) return;                                                   \
      M_MEMORY_FULL(sizeof (type) * v->size);                                 \
      return ;                                                                \
    }                                                                         \
    /* Test if alloc array is sufficient */                                   \
    if (size > v->alloc) {                                                    \
      size_t alloc = M_CALL_INC_ALLOC(oplist, size) ;                         \
      if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) {                             \
        M_MEMORY_FULL(sizeof (type) * alloc);                                 \
        return ;                                                              \
      }                                                                       \
      type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc);                \
      if (M_UNLIKELY_NOMEM (ptr == NULL) ) {                                  \
        M_MEMORY_FULL(sizeof (type) * alloc);                                 \
        return;                                                               \
      }                                                                       \
      v->ptr = ptr;                                                           \
      v->alloc = alloc;                                                       \
    }                                                                         \
    memmove(&v->ptr[i+num], &v->ptr[i], sizeof(type)*(v->size - i) );         \
    for(size_t k = i ; k < i+num; k++)                                        \
      M_CALL_INIT(oplist, v->ptr[k]);                                         \
    v->size = size;                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
  , /* No INIT */)                                                            \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _remove_v)(array_t v, size_t i, size_t j)                         \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT(i < j && v->ptr != NULL);                                        \
    M_ASSERT_INDEX(i, v->size);                                               \
    M_ASSERT_INDEX(j, v->size+1);                                             \
    for(size_t k = i ; k < j; k++)                                            \
      M_CALL_CLEAR(oplist, v->ptr[k]);                                        \
    memmove(&v->ptr[i], &v->ptr[j], sizeof(type)*(v->size - j) );             \
    v->size -= (j-i);                                                         \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _swap)(array_t v1, array_t v2)                                    \
  {                                                                           \
    M_ARRA4_CONTRACT(v1);                                                     \
    M_ARRA4_CONTRACT(v2);                                                     \
    M_SWAP(size_t, v1->size, v2->size);                                       \
    M_SWAP(size_t, v1->alloc, v2->alloc);                                     \
    M_SWAP(type *, v1->ptr, v2->ptr);                                         \
    M_ARRA4_CONTRACT(v1);                                                     \
    M_ARRA4_CONTRACT(v2);                                                     \
  }                                                                           \
                                                                              \
  M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist) (                           \
  M_INLINE void                                                               \
  M_F(name, _swap_at)(array_t v, size_t i, size_t j)                          \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT(v->ptr != NULL);                                                 \
    M_ASSERT_INDEX(i, v->size);                                               \
    M_ASSERT_INDEX(j, v->size);                                               \
    type tmp;                                                                 \
    M_DO_INIT_MOVE(oplist, tmp, v->ptr[i]);                                   \
    M_DO_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]);                             \
    M_DO_INIT_MOVE(oplist, v->ptr[j], tmp);                                   \
    M_ARRA4_CONTRACT(v);                                                      \
  }                                                                           \
  , /* INIT_SET | INIT_MOVE */ )                                              \
                                                                              \
  M_INLINE type *                                                             \
  M_F(name, _get)(const array_t v, size_t i)                                  \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT (v->ptr != NULL);                                                \
    M_ASSERT_INDEX(i, v->size);                                               \
    return &v->ptr[i];                                                        \
  }                                                                           \
                                                                              \
  M_INLINE type const *                                                       \
  M_F(name, _cget)(const array_t v, size_t i)                                 \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT (v->ptr != NULL);                                                \
    M_ASSERT_INDEX(i, v->size);                                               \
    return M_CONST_CAST(type, &v->ptr[i]);                                    \
  }                                                                           \
                                                                              \
  M_INLINE type *                                                             \
  M_F(name, _front)(const array_t v)                                          \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT_INDEX(0, v->size);                                               \
    return M_F(name, _get)(v, 0);                                             \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _it)(it_t it, const array_t v)                                    \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT (it != NULL);                                                    \
    it->index = 0;                                                            \
    it->array = v;                                                            \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _it_last)(it_t it, const array_t v)                               \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT (it != NULL);                                                    \
    /* If size is 0, index is -1 as unsigned, so it is greater than end */    \
    it->index = v->size - 1;                                                  \
    it->array = v;                                                            \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _it_end)(it_t it, const array_t v)                                \
  {                                                                           \
    M_ARRA4_CONTRACT(v);                                                      \
    M_ASSERT (it != NULL);                                                    \
    it->index = v->size;                                                      \
    it->array = v;                                                            \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _it_set)(it_t it, const it_t org)                                 \
  {                                                                           \
    M_ASSERT (it != NULL && org != NULL);                                     \
    it->index = org->index;                                                   \
    it->array = org->array;                                                   \
    M_ARRA4_CONTRACT(it->array);                                              \
  }                                                                           \
                                                                              \
  M_INLINE bool                                                               \
  M_F(name, _end_p)(const it_t it)                                            \
  {                                                                           \
    M_ASSERT(it != NULL && it->array != NULL);                                \
    return it->index >= it->array->size;                                      \
  }                                                                           \
                                                                              \
  M_INLINE bool                                                               \
  M_F(name, _last_p)(const it_t it)                                           \
  {                                                                           \
    M_ASSERT(it != NULL && it->array != NULL);                                \
    /* NOTE: Can not compute 'size-1' due to potential overflow               \
       if size is 0 */                                                        \
    return it->index + 1 >= it->array->size;                                  \
  }                                                                           \
                                                                              \
  M_INLINE bool                                                               \
  M_F(name, _it_equal_p)(const it_t it1,                                      \
                         const it_t it2)                                      \
  {                                                                           \
    M_ASSERT(it1 != NULL && it2 != NULL);                                     \
    return it1->array == it2->array && it1->index == it2->index;              \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _next)(it_t it)                                                   \
  {                                                                           \
    M_ASSERT(it != NULL && it->array != NULL);                                \
    it->index ++;                                                             \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _previous)(it_t it)                                               \
  {                                                                           \
    M_ASSERT(it != NULL && it->array != NULL);                                \
    /* NOTE: In the case index=0, it will be set to (unsigned) -1             \
       ==> it will be greater than size ==> end_p will return true */         \
    it->index --;                                                             \
  }                                                                           \
                                                                              \
  M_INLINE type *                                                             \
  M_F(name, _ref)(const it_t it)                                              \
  {                                                                           \
    M_ASSERT(it != NULL);                                                     \
    return M_F(name, _get)(it->array, it->index);                             \
  }                                                                           \
                                                                              \
  M_INLINE type const *                                                       \
  M_F(name, _cref)(const it_t it)                                             \
  {                                                                           \
    M_ASSERT(it != NULL);                                                     \
    return M_F(name, _cget)(it->array, it->index);                            \
  }                                                                           \
                                                                              \
  M_IF_METHOD(INIT_SET, oplist)(                                              \
  M_INLINE void                                                               \
  M_F(name, _insert)(array_t a, it_t it, type const x)                        \
  {                                                                           \
    M_ASSERT (it != NULL && a == it->array);                                  \
    size_t index = M_F(name, _end_p)(it) ? 0 : it->index+1;                   \
    M_F(name, _push_at)(a, index, x);                                         \
    it->index = index;                                                        \
  }                                                                           \
  , /* End of INIT_SET */ )                                                   \
                                                                              \
  M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)(                               \
  M_INLINE void                                                               \
  M_F(name, _remove)(array_t a, it_t it)                                      \
  {                                                                           \
    M_ASSERT (it != NULL && a == it->array);                                  \
    M_F(name, _pop_at)(NULL, a, it->index);                                   \
    /* NOTE: it->index will naturaly point to the next element */             \
  }                                                                           \
  , /* End of SET | INIT_SET */ )                                             \
                                                                              \
  M_IF_METHOD(CMP, oplist)                                                    \
  (                                                                           \
   M_INLINE void M_F(name, _special_sort)(array_t l,                          \
              int (*func_type) (type const *a, type const *b))                \
  {                                                                           \
    /* Using qsort is more compact but slower than a full templated           \
       version which can be twice faster */                                   \
    int (*func_void)(const void*, const void*);                               \
    /* There is no way (?) to avoid the cast */                               \
    func_void = (int (*)(const void*, const void*))func_type;                 \
    qsort (l->ptr, l->size, sizeof(type), func_void);                         \
  }                                                                           \
                                                                              \
  M_IF_METHOD2(SWAP, SET, oplist)(                                            \
  M_INLINE void                                                               \
  M_C3(m_arra4_,name,_stable_sort_noalloc)(type tab[], size_t size, type tmp[]) \
  {                                                                           \
    size_t th = 4;                                                            \
    M_IF_DEBUG(type *org_tab = tab;)                                          \
    M_ASSERT (size > 1);                                                      \
    /* Let's select the threshold of the pass 1 to be sure                    \
       the final result is in tab.*/                                          \
    if (m_core_clz64(size-1) & 1)                                             \
      th += th;                                                               \
                                                                              \
    /* Pass 1: Partial insertion sort (stable) up to 'th' size */             \
    for(size_t k = 0 ; k < size; ) {                                          \
      size_t max = size - k < 2*th ? size - k : th;                           \
      for(size_t i = 1; i < max; i++) {                                       \
        size_t j = i;                                                         \
        while (j > 0 && M_CALL_CMP(oplist, tab[k+j-1], tab[k+j]) > 0) {       \
          M_CALL_SWAP(oplist, tab[k+j-1], tab[k+j]);                          \
          j = j - 1;                                                          \
        }                                                                     \
      }                                                                       \
      k += max;                                                               \
    }                                                                         \
                                                                              \
    /* N Pass of merge sort (stable) */                                       \
    while (th < size) {                                                       \
      type *dest = tmp;                                                       \
      /* Pass n: Merge 2 sections of 'th' elements */                         \
      for(size_t k = 0 ; k < size; ) {                                        \
        type *el1 = &tab[k];                                                  \
        type *el2 = &tab[k+th];                                               \
        size_t n1 = th;                                                       \
        size_t n2 = size-k <= 3*th ? size-k-th : th;                          \
        M_ASSERT (size-k > th);                                               \
        M_ASSERT (0 < n1 && n1 <= size);                                      \
        M_ASSERT (0 < n2 && n2 <= size);                                      \
        k += n1+n2;                                                           \
        for (;;) {                                                            \
          if (M_CALL_CMP(oplist, *el1, *el2) <= 0) {                          \
            M_DO_INIT_MOVE(oplist, *dest, *el1);                              \
            dest++;                                                           \
            el1++;                                                            \
            if (M_UNLIKELY(-- n1 == 0)) {                                     \
              if (n2 > 0) {                                                   \
                memcpy (dest, el2, n2 * sizeof (type));                       \
                dest += n2;                                                   \
              }                                                               \
              break;                                                          \
            }                                                                 \
          } else {                                                            \
            M_DO_INIT_MOVE(oplist, *dest, *el2);                              \
            dest++;                                                           \
            el2++;                                                            \
            if (M_UNLIKELY(-- n2 == 0)) {                                     \
              if (n1 > 0) {                                                   \
                memcpy (dest, el1, n1 * sizeof (type));                       \
                dest += n1;                                                   \
              }                                                               \
              break;                                                          \
            }                                                                 \
          }                                                                   \
        }                                                                     \
      }                                                                       \
      /* Swap t & tab */                                                      \
      M_SWAP(type *, tab, tmp);                                               \
      /* Increase th for next pass */                                         \
      th += th;                                                               \
    }                                                                         \
    M_ASSERT (org_tab == tab);                                                \
  }                                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _special_stable_sort)(array_t l)                                  \
  {                                                                           \
    if (M_UNLIKELY (l->size < 2))                                             \
      return;                                                                 \
    /* NOTE: if size is <= 4, no need to perform an allocation */             \
    type *temp = M_CALL_REALLOC(oplist, type, NULL, l->size);                 \
    if (M_UNLIKELY_NOMEM (temp == NULL)) {                                    \
      M_MEMORY_FULL(sizeof (type) * l->size);                                 \
      return ;                                                                \
    }                                                                         \
    M_C3(m_arra4_,name,_stable_sort_noalloc)(l->ptr, l->size, temp);          \
    M_CALL_FREE(oplist, temp);                                                \
  }                                                                           \
  ,) /* IF SWAP & SET methods */                                              \
                                                                              \
  ,) /* IF CMP oplist */                                                      \
                                                                              \
  M_IF_METHOD(EQUAL, oplist)(                                                 \
  M_INLINE bool                                                               \
  M_F(name, _equal_p)(const array_t array1,                                   \
                      const array_t array2)                                   \
  {                                                                           \
    M_ARRA4_CONTRACT(array1);                                                 \
    M_ARRA4_CONTRACT(array2);                                                 \
    if (array1->size != array2->size) return false;                           \
    size_t i;                                                                 \
    for(i = 0; i < array1->size; i++) {                                       \
      type const *item1 = M_F(name, _cget)(array1, i);                        \
      type const *item2 = M_F(name, _cget)(array2, i);                        \
      bool b = M_CALL_EQUAL(oplist, *item1, *item2);                          \
      if (!b) return false;                                                   \
    }                                                                         \
    return i == array1->size;                                                 \
  }                                                                           \
  , /* no EQUAL */ )                                                          \
                                                                              \
  M_IF_METHOD(HASH, oplist)(                                                  \
  M_INLINE size_t                                                             \
  M_F(name, _hash)(const array_t array)                                       \
  {                                                                           \
    M_ARRA4_CONTRACT(array);                                                  \
    M_HASH_DECL(hash);                                                        \
    for(size_t i = 0 ; i < array->size; i++) {                                \
      size_t hi = M_CALL_HASH(oplist, array->ptr[i]);                         \
      M_HASH_UP(hash, hi);                                                    \
    }                                                                         \
    return M_HASH_FINAL (hash);                                               \
  }                                                                           \
  , /* no HASH */ )                                                           \
                                                                              \
  M_INLINE void                                                               \
  M_F(name, _splice)(array_t a1, array_t a2)                                  \
  {                                                                           \
    M_ARRA4_CONTRACT(a1);                                                     \
    M_ARRA4_CONTRACT(a2);                                                     \
    if (M_LIKELY (a2->size > 0)) {                                            \
      size_t newSize = a1->size + a2->size;                                   \
      /* To overflow newSize, we need to a1 and a2 a little bit above         \
         SIZE_MAX/2, which is not possible in the classic memory model as we  \
         should have exhausted all memory before reaching such sizes. */      \
      M_ASSERT(newSize > a1->size);                                           \
      if (newSize > a1->alloc) {                                              \
        type *ptr = M_CALL_REALLOC(oplist, type, a1->ptr, newSize);           \
        if (M_UNLIKELY_NOMEM (ptr == NULL) ) {                                \
          M_MEMORY_FULL(sizeof (type) * newSize);                             \
        }                                                                     \
        a1->ptr = ptr;                                                        \
        a1->alloc = newSize;                                                  \
      }                                                                       \
      M_ASSERT(a1->ptr != NULL);                                              \
      M_ASSERT(a2->ptr != NULL);                                              \
      memcpy(&a1->ptr[a1->size], &a2->ptr[0], a2->size * sizeof (type));      \
      /* a2 is now empty */                                                   \
      a2->size = 0;                                                           \
      /* a1 has been expanded with the items of a2 */                         \
      a1->size = newSize;                                                     \
    }                                                                         \
  }                                                                           \

/* Define the I/O functions */
#define M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t)                     \
                                                                              \
  M_IF_METHOD(GET_STR, oplist)(                                               \
  M_INLINE void                                                               \
  M_F(name, _get_str)(m_string_t str, array_t const array,                    \
                      bool append)                                            \
  {                                                                           \
    M_ARRA4_CONTRACT(array);                                                  \
    (append ? m_string_cat_cstr : m_string_set_cstr) (str, "[");              \
    it_t it;                                                                  \
    for (M_F(name, _it)(it, array) ;                                          \
         !M_F(name, _end_p)(it);                                              \
         M_F(name, _next)(it)){                                               \
      type const *item = M_F(name, _cref)(it);                                \
      M_CALL_GET_STR(oplist, str, *item, true);                               \
      if (!M_F(name, _last_p)(it))                                            \
        m_string_push_back (str, M_GET_SEPARATOR oplist);                     \
    }                                                                         \
    m_string_push_back (str, ']');                                            \
  }                                                                           \
  , /* no GET_STR */ )                                                        \
                                                                              \
  M_IF_METHOD(OUT_STR, oplist)(                                               \
  M_INLINE void                                                               \
  M_F(name, _out_str)(FILE *file, const array_t array)                        \
  {                                                                           \
    M_ARRA4_CONTRACT(array);                                                  \
    M_ASSERT (file != NULL);                                                  \
    fputc ('[', file);                                                        \
    for (size_t i = 0; i < array->size; i++) {                                \
      type const *item = M_F(name, _cget)(array, i);                          \
      M_CALL_OUT_STR(oplist, file, *item);                                    \
      if (i != array->size-1)                                                 \
        fputc (M_GET_SEPARATOR oplist, file);                                 \
    }                                                                         \
    fputc (']', file);                                                        \
  }                                                                           \
  , /* no OUT_STR */ )                                                        \
                                                                              \
  M_IF_METHOD2(PARSE_STR, INIT, oplist)(                                      \
  M_INLINE bool                                                               \
  M_F(name, _parse_str)(array_t array, const char str[], const char**endp)    \
  {                                                                           \
    M_ARRA4_CONTRACT(array);                                                  \
    M_ASSERT (str != NULL);                                                   \
    M_F(name,_reset)(array);                                                  \
    int c = *str++;                                                           \
    if (M_UNLIKELY (c != '[')) { c = 0; goto exit; }                          \
    c = m_core_str_nospace(&str);                                             \
    if (M_UNLIKELY (c == ']')) { goto exit; }                                 \
    if (M_UNLIKELY (c == 0)) { goto exit; }                                   \
    str--;                                                                    \
    M_QLET(item, type, oplist) {                                              \
      do {                                                                    \
        bool b = M_CALL_PARSE_STR(oplist, item, str, &str);                   \
        c = m_core_str_nospace(&str);                                         \
        if (b == false || c == 0) { c = 0; break; }                           \
        M_F(name, _push_back)(array, item);                                   \
      } while (c == M_GET_SEPARATOR oplist);                                  \
    }                                                                         \
  exit:                                                                       \
    M_ARRA4_CONTRACT(array);                                                  \
    if (endp) *endp = str;                                                    \
    return c == ']';                                                          \
  }                                                                           \
  , /* no PARSE_STR & INIT */ )                                               \
                                                                              \
  M_IF_METHOD2(IN_STR, INIT, oplist)(                                         \
  M_INLINE bool                                                               \
  M_F(name, _in_str)(array_t array, FILE *file)                               \
  {                                                                           \
    M_ARRA4_CONTRACT(array);                                                  \
    M_ASSERT (file != NULL);                                                  \
    M_F(name,_reset)(array);                                                  \
    int c = fgetc(file);                                                      \
    if (M_UNLIKELY (c != '[')) return false;                                  \
    c = fgetc(file);                                                          \
    if (M_UNLIKELY (c == ']')) return true;                                   \
    if (M_UNLIKELY (c == EOF)) return false;                                  \
    ungetc(c, file);                                                          \
    M_QLET(item, type, oplist) {                                              \
      do {                                                                    \
        bool b = M_CALL_IN_STR(oplist, item, file);                           \
        c = m_core_fgetc_nospace(file);                                       \
        if (b == false || c == EOF) { c = 0; break; }                         \
        M_F(name, _push_back)(array, item);                                   \
      } while (c == M_GET_SEPARATOR oplist);                                  \
    }                                                                         \
    M_ARRA4_CONTRACT(array);                                                  \
    return c == ']';                                                          \
  }                                                                           \
  , /* no IN_STR & INIT */ )                                                  \
                                                                              \
  M_IF_METHOD(OUT_SERIAL, oplist)(                                            \
  M_INLINE m_serial_return_code_t                                             \
  M_F(name, _out_serial)(m_serial_write_t f, const array_t array)             \
  {                                                                           \
    M_ARRA4_CONTRACT(array);                                                  \
    M_ASSERT (f != NULL && f->m_interface != NULL);                           \
    m_serial_return_code_t ret;                                               \
    m_serial_local_t local;                                                   \
    ret = f->m_interface->write_array_start(local, f, array->size);           \
    for (size_t i = 0; i < array->size; i++) {                                \
      type const *item = M_F(name, _cget)(array, i);                          \
      if (i != 0) {                                                           \
        ret |= f->m_interface->write_array_next(local, f);                    \
      }                                                                       \
      ret |= M_CALL_OUT_SERIAL(oplist, f, *item);                             \
    }                                                                         \
    ret |= f->m_interface->write_array_end(local, f);                         \
    return ret & M_SERIAL_FAIL;                                               \
  }                                                                           \
  , /* no OUT_SERIAL */ )                                                     \
                                                                              \
  M_IF_METHOD2(IN_SERIAL, INIT, oplist)(                                      \
  M_INLINE m_serial_return_code_t                                             \
  M_F(name, _in_serial)(array_t array, m_serial_read_t f)                     \
  {                                                                           \
    M_ARRA4_CONTRACT(array);                                                  \
    M_ASSERT (f != NULL && f->m_interface != NULL);                           \
    m_serial_return_code_t ret;                                               \
    m_serial_local_t local;                                                   \
    size_t estimated_size = 0;                                                \
    M_F(name,_reset)(array);                                                  \
    ret = f->m_interface->read_array_start(local, f, &estimated_size);        \
    if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) {                           \
       return ret;                                                            \
    }                                                                         \
    M_F(name, _reserve)(array, estimated_size);                               \
    M_QLET(item, type, oplist) {                                              \
      do {                                                                    \
        ret = M_CALL_IN_SERIAL(oplist, item, f);                              \
        if (ret != M_SERIAL_OK_DONE) { break; }                               \
        M_F(name, _push_back)(array, item);                                   \
        ret = f->m_interface->read_array_next(local, f);                      \
      } while (ret == M_SERIAL_OK_CONTINUE);                                  \
    }                                                                         \
    M_ARRA4_CONTRACT(array);                                                  \
    return ret;                                                               \
  }                                                                           \
  , /* no IN_SERIAL & INIT */ )                                               \

/* Definition of the emplace_back function for arrays */
#define M_ARRA4_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
  M_INLINE void                                                               \
  function_name(name_t v                                                      \
                M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) )                \
  {                                                                           \
    M_F(name, _subtype_ct) *data = M_F(name, _push_raw)(v);                   \
    if (M_UNLIKELY (data == NULL) )                                           \
      return;                                                                 \
    M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type);       \
  }                                                                           \

/********************************** INTERNAL *********************************/

#if M_USE_SMALL_NAME
#define ARRAY_DEF M_ARRAY_DEF
#define ARRAY_DEF_AS M_ARRAY_DEF_AS
#define ARRAY_OPLIST M_ARRAY_OPLIST
#define ARRAY_INIT_VALUE M_ARRAY_INIT_VALUE
#endif

#endif
