//  - added changlog
//  - added arr_delitem
//  - addded some convenience methods eg. _is_last_item, _out_of_range
//  - added suffix 'documentation'
//  - added tests for extra delitem functionality

#include <stdio.h>
#include <stdlib.h>

//default alloc size empty array
#ifndef DEF_SIZE
#define DEF_SIZE 4

//function to get size to allocate for size of array.
//overallocates like python - apparently realloc is slow
#ifndef S_FUNC 
#define S_FUNC(s) (s * 2 + 1) //0 get converted to 1 and not 0

typedef struct _arr{
    void **arr;
    size_t len;
    size_t memsize; //number of items in declared array
} array, *Array;

//if returns int, unless specified:  0 = success; -1 = fail

array* new_arr(){
    array* a = (array*)malloc(sizeof(array*));
    a->len = (size_t)0;
    a->arr = malloc(DEF_SIZE*sizeof(void*));
    a->memsize = DEF_SIZE;
    return a;

void del_arr(array* a){
void free_arr(array* a){
} //=del_arr

array* make_arr(void* p_a[], size_t size){
    array* a = (array*)malloc(sizeof(array*));
    a->len = (size_t)size;
    size_t memsize = S_FUNC(size);
    a->arr = malloc(memsize*sizeof(void*));
    a->memsize = memsize;
    for(int i=0;i<size;i++){
        a->arr[i] = p_a[i];
    return a;

array* make_arr_memsize_empty(size_t memsize){
    array* a = (array*)malloc(sizeof(array*));
    a->len = (size_t)0;
    a->arr = malloc(memsize*sizeof(void*));
    a->memsize = memsize;
    return a;
array* make_arr_memsize(void* p_a[], size_t size, size_t memsize){
    if(memsize<size){return NULL;}
    array* a = make_arr_memsize_empty(memsize);
    a->len = size;
    for(int i = 0; i<size;i++){
        a -> arr[i] = p_a[i];
    return a;

int realloc_arr(array* a, size_t size){
    if(size < a -> len){return -1;}
    void* _temp = realloc(a->arr, size*sizeof(void*));
    if(_temp==NULL){return -1;}
    a -> arr = (void**)_temp;
    a -> memsize = size;
    return 0;
//may reduce size of allocated memory
int realloc_arr_from_len(array* a){
    return realloc_arr(a, S_FUNC(a->len));
//will always increase size
int realloc_arr_from_mem(array* a){
    return realloc_arr(a, S_FUNC(a->memsize));

int arr_mem_full(array* a){
    return a -> len >= a -> memsize;
//0=success, -1 = realloc failed

int realloc_arr_if_full(Array a){
        return realloc_arr_from_mem(a);
    return 0;

//prints array
//void p_arr(array* a, char* f_spec, type i_type); //i_type must be a pointer type
#define p_arr(a, f_spec, i_type) ({\
    for(int i=0;i<a->len;i++){\
        if(i!=0){printf(", ");}\

#define ARR_CAST_IN(item) ((void*)item)
#define ARR_CAST_OUT(item, type) ((type)item) //type must be a pointer type
#define ARR_DEREF_OUT(item, type) *(type*)item

//only unsigned indexes for now!
//function suffixes:
// arr_*_p means returns void*                                    -F
// arr_*_t means returns pointer (specify type)                   -MACRO
// arr_*_v means returns value of specified type (NOT pointer)    -MACRO

// arr_* means takes in any pointer                               -F
// _arr_*_v means specify value (NOT pointer),                    -MACRO
  //auto referencing, ONLY works on actual variables:
  // eg. can NOT just pass 5 as arg, MUST do: 
        //int _temp = 5; _arr_*_v(...,_temp)

int arr_append(Array a, void* item){
    if (arr_mem_full(a)){
        //allocated memory full
        if (!realloc_arr_from_mem(a)){return -1;}//realloc failed
    //now has enough space
    a -> arr[a->len] = item;
    return 0;
//see _arr_*_v for more info, this NOT RECOMMENDED! SIGNATURE: 
//  int _arr_append_v(Array a, (any) value);
#define _arr_append_v(a,value) arr_append(a,(void*)(&value))

//no neg indexes for now
void* arr_getitem_p(Array a, size_t index){
    if (!(0<=index<a->len)){return NULL;}
    return a -> arr[index];
//only use these if you are sure that index is within array otherwise compiler throws error:
//dereferncing NULL pointer esp. the ..._v
//type: pointer type eg. int *
#define arr_getitem_t(a, index, type) ((type)arr_getitem_p(a,index))
//type: a type eg. int, gets actual value NOT pointer
#define arr_getitem_v(a, index, type) (*arr_getitem_t(a, index, type *))

int arr_setitem(Array a, size_t index, void* value){
    if (!(0<=index<a->len)){return -1;}
    a -> arr[index] = value;
    return 0;
//similar to _arr_append_v
#define _arr_setitem_v(a,index,value) (arr_setitem(a,index,(void*)(&value)))

int _arr_del_last_item(Array a){
    //item (pointer) will still be there, just in memory 'marked' as invalid
    //but the data will be overwritten when increasing size of array
    if (a -> len <= 0){return -1;}
    return 0;
void* _arr_pop_last_item(Array a){
    if (a -> len <= 0){return NULL;}
    void* out = arr_getitem_p(a, a -> len - 1);
    return out;
int _is_last_item(Array a, size_t index){
    return index - 1 == a -> len ;
int _out_of_range(Array a, size_t index){
    return !(0<=index<a->len);
//works for -indexes too, can't use size_t (its unsigned!) so must use largest size type
int __out_of_range_neg(Array a, long long index){
    return !(-(a->len)<=index<a->len);
#define _out_of_range(a,index) __out_of_range_neg(a,(long long) index)

int arr_delitem(Array a, size_t index){
    if (_out_of_range(a,index)){return -1;}
    if (_is_last_item(a, index)){
        //simple case, don't need to move values
        return _arr_del_last_item(a); 
    size_t values_to_move = a -> len - index - 1;
    for(size_t i = index;i<(values_to_move+index);i++){
        a->arr[i] = a -> arr[i+1];//move everything back/left by 1
    return 0;

#define decloc(t,v1,v2,v3) t i1=v1;t i2=v2;t i3=v3;

#define DECL_P_ARR(t,v1,v2,v3,v4) \
t a1=v1, a2=v2,a3=v3,a4=v4;\
void* new_arr[] = {&a1, &a2, &a3, &a4};

int fail_test(int err_code){
    printf("FAIL: %i", err_code);
    return err_code;
int run_test(int (*f_ptr)(), int index, int failed){
    printf("Running test %i...",index);
    int _err_c = f_ptr();
    if (!_err_c){
        printf("FAIL: TEST %i\n", _err_c);
    return failed || _err_c;

int test1(){
    //printf("Running test 1...\n");
    double a1=1.3, a2=-7.9,a3=100.7,a4=-0.023;
    double* f_a[] = {&a1, &a2, &a3, &a4};
    array* a = make_arr_memsize((void**)f_a,4,4);
    return 0;
int test2(){
    int x = 77,y=-80;
    Array a = make_arr(new_arr,4);
    arr_append(a, &x);

    if (*ARR_CAST_OUT(a->arr[4], int*) != 77){
        return fail_test(1);
    if (*ARR_CAST_OUT(a->arr[1], int*) != -9){
        return fail_test(2);
    if (arr_getitem_v(a, 0, int) != 5){
        return fail_test(3);
    if (arr_getitem_v(a,2,int)!=-80){
        return fail_test(4);
    if (arr_getitem_v(a,2,int)!=11){
        return fail_test(5);
    if (a->len!=4){
        return fail_test(6);
    return 0;

int main() {
    printf("Hello world!\n");
    array* a = new_arr();
    int i4 = 4;
    int i5 = -3;
    //int i1=1;
    //int i2 = 4;
    //int i3=10;
    void* s_a[] = {&i1,&i2,&i3};
    a = make_arr(s_a,3);
    //printf("%i", arr_mem_full(a));
    p_arr(a, "%d", long *);
    //realloc_arr(a, 6);
    a->arr[3] = (void *)&i4;
    a->arr[4] = (void *)&i5;
    p_arr(a, "%d", long *);
    int failed = 0;
    failed = run_test(test1, 1, failed);
    failed = run_test(test2, 2, failed);
    printf(!failed ? "All tests passed.":"Test(s) failed!");
    if (failed){
        return 4;
    return 0;