#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <assert.h>
#include <my_assert.h>

#define HEAP_TEST

#define dfwmalloc_assert(s)      assert(s)

typedef unsigned short cb_int_t;
typedef unsigned long long ULONG64;

struct control_block {
    cb_int_t cb_magic_flags;
    cb_int_t free_count;
    cb_int_t cb_free_list[0];
    int n_masks;
    int sz_shift_mul;
    int cb_first_block;
    int n;
    ULONG64 cb_bit_mask[32];
};


#ifndef MIN_ALIGN
#define MIN_ALIGN               3
#endif

#define CB_FLAG_PACKING         3
#define CB_FLAG_PACKING_H       0
#define CB_FLAG_PACKING_L       1
#define CB_FLAG_PACKING_S       2
#define CB_FLAG_PACKING_M       3

#undef STATICFNINLINE
#define STATICFNINLINE          static


#include "vecheap.h"


void run_it (int packing)
{
    int n;
    for (n = 1; n < 300; n++) {
        int j;
        for (j = 0; j < 300; j++) {
            int *a, k;
            struct control_block *cb;
            a = (int *) malloc (n * sizeof (int));
            cb = (struct control_block *) malloc (sizeof (*cb) + n * sizeof (cb->cb_free_list[0]));
            memset (cb, '\0', sizeof (*cb) + n * sizeof (cb->cb_free_list[0]));
            cb->free_count = 0;
            cb->cb_magic_flags = packing;
            cb->n_masks = (n + 63) / 64;
            cb->n = n;
            if (packing == CB_FLAG_PACKING_M)
                cb->sz_shift_mul = 1 << cb->sz_shift_mul;
            else if (packing == CB_FLAG_PACKING_S)
                cb->sz_shift_mul = 3;
            cb->cb_first_block = 37;

            for (k = 0; k < n; k++)
                a[k] = k;
            for (k = 0; k < n * 2; k++) {
                cb_int_t r;
                r = random();
                HSWAP (a[k % n], a[r % n]);
            }

/* just check that it *looks* random: */
            if (j == 30 && n == 30) {
                for (k = 0; k < n; k++)
                    printf ("%d ", a[k]);
                printf ("\n");
            }

            for (k = 0; k < n; k++) {
                if (packing == CB_FLAG_PACKING_H)
                    dfwmalloc_cb_insert_free_block (cb, (char *) cb + ((a[k] * 11) << MIN_ALIGN));
                else if (packing == CB_FLAG_PACKING_L)
                    dfwmalloc_cb_insert_free_block (cb, (char *) cb + ((a[k] * 11) << MIN_ALIGN));
                else if (packing == CB_FLAG_PACKING_S)
                    dfwmalloc_cb_insert_free_block (cb, (char *) cb + ((a[k] * 1) << cb->sz_shift_mul) + cb->cb_first_block);
                else if (packing == CB_FLAG_PACKING_M)
                    dfwmalloc_cb_insert_free_block (cb, (char *) cb + ((a[k] * 1) * cb->sz_shift_mul) + cb->cb_first_block);
            }
            assert (cb->free_count == n);
            for (k = 0; k < n; k++) {
                int u;
                if (packing == CB_FLAG_PACKING_H) {
                    u = (((char *) dfwmalloc_cb_get_min_block (cb) - (char *) cb) >> MIN_ALIGN);
                    assert (u == k * 11);
                } else if (packing == CB_FLAG_PACKING_L) {
                    u = (((char *) dfwmalloc_cb_get_min_block (cb) - (char *) cb) >> MIN_ALIGN);
                    /* do not care about order */
                } else if (packing == CB_FLAG_PACKING_S) {
                    u = (((char *) dfwmalloc_cb_get_min_block (cb) - (char *) cb - cb->cb_first_block) >> cb->sz_shift_mul);
                    assert (u == k * 1);
                } else if (packing == CB_FLAG_PACKING_M) {
                    u = (((char *) dfwmalloc_cb_get_min_block (cb) - (char *) cb - cb->cb_first_block) / cb->sz_shift_mul);
                    assert (u == k * 1);
                }
                assert (cb->free_count == n - k - 1);
            }
            free (cb);
            free (a);
        }
    }
}


void entry (void)
{
    long t1, t2, t3, t4, t5;

    t1 = (long) clock ();
    run_it (CB_FLAG_PACKING_S);
    t2 = (long) clock ();
    run_it (CB_FLAG_PACKING_H);
    t3 = (long) clock ();
    run_it (CB_FLAG_PACKING_M);
    t4 = (long) clock ();
    run_it (CB_FLAG_PACKING_L);
    t5 = (long) clock ();

    printf ("M=%ldms H=%ldms S=%ldms L=%ldms\n", (t2 - t1) / 1000, (t3 - t2) / 1000, (t4 - t3) / 1000, (t5 - t4) / 1000);
}

