#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <time.h>
#include <immintrin.h>


/* Closer approximation of ScanKeyData - has function pointer and Datum */
typedef void (*RegProcedure)(void);
typedef uintptr_t Datum;

typedef struct ScanKeyData
{
    int         sk_flags;
    int         sk_attno;
    RegProcedure sk_func;
    Datum       sk_argument;
} ScanKeyData;

/*  */
const ScanKeyData * version1(int n, const ScanKeyData  * key)
{
    ScanKeyData *idxkey = (ScanKeyData *) malloc(n * sizeof(ScanKeyData));

    memcpy(&idxkey, &key, n * sizeof(ScanKeyData));
    for (int i = 0; i < n; i++)
    {
        idxkey[i].sk_attno = i + 1;
    }

    return idxkey;
}

/* */
const ScanKeyData * version2(int n, const ScanKeyData *key)
{
    ScanKeyData *idxkey = (ScanKeyData *) malloc(n * sizeof(ScanKeyData));

    for (int i = 0; i < n; i++)
    {
        memcpy(&idxkey[i], &key[i], sizeof(ScanKeyData));
        idxkey[i].sk_attno = i + 1;
    }

    return idxkey;
}




#define NANOSEC_PER_SEC 1000000000

// Returns difference in nanoseconds
int64_t
get_clock_diff(struct timespec *t1, struct timespec *t2)
{
	int64_t nanosec = (t1->tv_sec - t2->tv_sec) * NANOSEC_PER_SEC;
	nanosec += (t1->tv_nsec - t2->tv_nsec);

	return nanosec;
}




//#define NKEYS 10000000 version2 does not finish
#define NKEYS 4
#define LOOPS 10000000

void test1(int n)
{
	ScanKeyData *keys;
	ScanKeyData *idx;

        keys = (ScanKeyData *) malloc(NKEYS * sizeof(ScanKeyData));
	memset(keys, 0, NKEYS * sizeof(ScanKeyData));

	for(int i = 0; i < n; i++)
	{
		idx = version1(NKEYS, keys);
		free(idx);
	}
	free(keys);
}

void test2(int n)
{
	ScanKeyData *keys;
	ScanKeyData *idx;

        keys = (ScanKeyData *) malloc(NKEYS * sizeof(ScanKeyData));
	memset(keys, 0, NKEYS * sizeof(ScanKeyData));

	for(int i = 0; i < n; i++)
	{
		idx = version2(NKEYS, keys);
		free(idx);
	}
	free(keys);
}


int main(void)
{
	struct timespec start,end;
	int64_t version1_time, version2_time;

	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
	test1(LOOPS);
	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);
	version1_time = get_clock_diff(&end, &start);
	printf("version1: done in %lld nanoseconds\n", version1_time);	

	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
	test2(LOOPS);
	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end);
	version2_time = get_clock_diff(&end, &start);
	printf("version2: done in %lld nanoseconds\n", version2_time);	

	printf("(%g times faster)\n", (double) version1_time / version2_time);

	return 0;
}
