#ifndef MIAO_H_INCLUDED
#define MIAO_H_INCLUDED

/* MIAO Minimalistic Array Operations, inspired by kvec->h-> */

#include <stdlib.h>

#ifndef miao_realloc
#define miao_realloc(PTR, SIZE) realloc((PTR), (SIZE))
#endif

#define miao_of_type(TYPE) { TYPE * a; TYPE * aux; size_t n; size_t m; }
#define miao_t(TYPE)  struct miao_of_type(TYPE) 
#define miao_struct(NAME, TYPE)  struct NAME miao_of_type(TYPE)
#define miao_init(ARR) ((ARR)->n = (ARR)->m = 0, (ARR)->a = NULL, (ARR))
#define miao_done(ARR) do { free((ARR)->a); miao_init((ARR)); } while (0)
#define miao_unsafe_get_ptr(ARR, I) (((ARR)->a)+(I))
#define miao_unsafe_get(ARR, I) (((ARR)->a)[(I)])
#define miao_elsize(ARR) (sizeof  *((ARR)->a))
#define miao_cap(ARR) ((ARR)->m)
#define miao_size(ARR) ((ARR)->n)
#define miao_size_for(ARR, AMOUNT) (miao_elsize(ARR) * (AMOUNT))
#define miao_total_size(ARR) miao_size_for(ARR, miao_size(ARR))

#define miao_out_of_bounds(ARR, I) ((size_t)(I) >= (ARR)->m)
#define miao_get_ptr(ARR, I) (miao_out_of_bounds(ARR, I) ?  NULL: miao_unsafe_get_ptr(ARR, I))
#define miao_get(ARR, I, DEFAULT) (miao_out_of_bounds(ARR, I) ?  DEFAULT : miao_unsafe_get(ARR, I))

#define miao_resize(ARR, CAP)                                          \
  (                                                                    \
    (ARR)->m = (CAP),                                                  \
    (ARR)->aux = miao_realloc((ARR)->a, miao_size_for(ARR, (ARR)->m)), \
    (ARR)->aux ? ((ARR)->a = ARR->aux) : NULL                          \
  )

#define miao_grow(ARR, SIZE)                                           \
  (SIZE >= miao_cap(ARR) ?  miao_resize(ARR, SIZE) : (ARR)->a )


#define miao_unsafe_pop_ptr(ARR) ((ARR)->n--, ((ARR)->a + (ARR)->n))
#define miao_unsafe_pop(ARR) ((ARR)->n--, (ARR)->a[(ARR)->n])

#define miao_pop(ARR, DEFAULT) (((ARR)->n > 0) ? miao_unsafe_pop(ARR) : DEFAULT)
#define miao_pop_ptr(ARR) (((ARR)->n > 0) ? miao_unsafe_pop_ptr(ARR) : NULL)

#define miao_unsafe_push(ARR, VAL)                                    \
          ((ARR)->a[(ARR)->n] = (VAL), (ARR)->n++, ((ARR)->a + (ARR)->n)) 

#define miao_push(ARR, VAL)                                           \
  (                                                                   \
    (miao_grow(ARR, ((ARR)->n + 1))) ?                                \
    miao_unsafe_push(ARR, VAL) : NULL                                 \
  )
          
#define miao_push_ptr(ARR)                                            \
  (                                                                   \
    (miao_grow(ARR, ((ARR)->n + 1))) ?                                \
    ((ARR)->n++,  miao_get_ptr(ARR, (ARR)->n - 1))                    \
    : NULL                                                            \
  )


#define miao_copy(DEST, SRC)                                          \
  (                                                                   \
    miao_grow(DEST, miao_size(SRC)) ?                                 \
    ( (DEST)->n = miao_size(SRC),                                     \
      memcpy((DEST)->a, (SRC)->a, miao_total_size(DEST))              \
      , DEST                                                          \
    ) : NULL                                                          \
  )

#define miao_qsort(ARR, COMPARE)                                      \
    (                                                                 \
      (ARR)->a ?                                                      \
      ( qsort((ARR)->a, miao_size(ARR), miao_elsize(ARR), COMPARE)    \
      , (ARR)->a )                                                    \
      : NULL                                                          \
    )

#define miao_bsearch(ARR, COMPARE, KEY)                               \
    (bsearch(KEY, (ARR)->a, miao_size(ARR), miao_elsize(ARR), COMPARE))


#define miao_each_ptr(ARR, ACTION, DATA)                                      \
  { size_t miao_i__ ;                                                         \
    for (miao_i__ = 0; miao_i__ < miao_size(ARR) ; miao_i__ ++) {             \
      if(ACTION(miao_i__, miao_unsafe_get_ptr(ARR, miao_i__), DATA)) break;   \
    }                                                                         \
  } while(0);


#define miao_each(ARR, ACTION, DATA)                                          \
  { size_t miao_i__ ;                                                         \
    for (miao_i__ = 0; miao_i__ < miao_size(ARR) ; miao_i__ ++) {             \
      if(ACTION(miao_i__, miao_unsafe_get(ARR, miao_i__), DATA)) break;       \
    }                                                                         \
  } while(0);


#define miao_each_with_result(ARR, ACTION, RESULT, CHECK, DATA)               \
  { size_t miao_i__ ;                                                         \
    for (miao_i__ = 0; miao_i__ < miao_size(ARR) ; miao_i__ ++) {             \
      (RESULT) = ACTION(miao_i__, miao_unsafe_get(ARR, miao_i__), DATA);      \
      if (CHECK(RESULT, DATA)) break;                                         \
    }                                                                         \
  } while(0);

#define miao_each_ptr_with_result(ARR, ACTION, RESULT, CHECK, DATA)           \
  { size_t miao_i__ ;                                                         \
    for (miao_i__ = 0; miao_i__ < miao_size(ARR) ; miao_i__ ++) {             \
      (RESULT) = ACTION(miao_i__, miao_unsafe_get_ptr(ARR, miao_i__), DATA);  \
      if (CHECK(RESULT, DATA)) break;                                         \
    }                                                                         \
  } while(0);
    
#define miao_delete(ARR, INDEX)                                               \
  ( miao_out_of_bounds(ARR, ((size_t)INDEX)) ? 0 :                            \
    /* Copy over everything from index + 1 */                                 \
    (memmove(ARR->a + INDEX, ARR->a + INDEX + 1,                              \
      (ARR->n - INDEX - 1) * sizeof(ARR->n) ) ,                               \
    (1 + ARR->n--))                                                           \
  )

#define miao_index_of(ARR, ENTRY) ( (ENTRY) - ((ARR)->a) )

#define miao_delete_entry(ARR, ENTRY)                                          \
  ( miao_delete(ARR, miao_index_of(ARR, ENTRY)) )

#define miao_delete_bsearch(ARR, COMPARE, ENTRY)                               \
  ( ARR->aux = miao_bsearch(ARR, COMPARE, ENTRY) ,                             \
    ARR->aux ?  miao_delete_entry(ARR, ARR->aux)   : 0  )


#endif