// cl_make_heap_GV_I().

// General includes.
#include "cl_sysdep.h"

CL_PROVIDE(cl_GV_I)

// Specification.
#include "cl_GV_integer.h"


// Implementation.

#include "cl_I.h"
#include "cl_abort.h"
#include "cl_offsetof.h"


// Memory-efficient integer vectors: If all entries are known in advance to
// be >= 0 and < 2^m, we reserve only m bits for each entry. (m=1,2,4,8,16,32).
// Thus we end up with 6 kinds of bit/byte vectors, and the general integer
// vectors.
// For enquiring purposes, we store m in the vectorops table. Because of this,
// treating a cl_GV_RA as cl_GV_I is wrong. In particular, we cannot use the
// cl_null_GV_N to initialize a cl_GV_I; need a special cl_null_GV_I.


static void cl_gvector_integer_destructor (cl_heap* pointer)
{
#if (defined(__mips__) || defined(__mips64__)) && !defined(__GNUC__) // workaround SGI CC bug
	(*(cl_heap_GV_I*)pointer).~cl_heap_GV();
#else
	(*(cl_heap_GV_I*)pointer).~cl_heap_GV_I();
#endif
}
AT_INITIALIZATION(ini_cl_gvector_integer)
{ cl_register_typetag(cl_typetag_gvector_integer,cl_gvector_integer_destructor); }


static inline cl_heap_GV_I * outcast (cl_GV_inner<cl_I>* vec)
{
	return (cl_heap_GV_I *)((char *) vec - offsetof(cl_heap_GV_I,v));
}
static inline const cl_heap_GV_I * outcast (const cl_GV_inner<cl_I>* vec)
{
	return (const cl_heap_GV_I *)((const char *) vec - offsetof(cl_heap_GV_I,v));
}


// Add more info to the vectorops tables.

struct cl_GV_I_vectorops {
	cl_GV_vectorops<cl_I> ops;
	sintL m; // for maxbits
};

static inline cl_GV_I_vectorops* outcast (cl_GV_vectorops<cl_I>* vectorops)
{
	return (cl_GV_I_vectorops*)((char *) vectorops - offsetof(cl_GV_I_vectorops,ops));
}


// Vectors of general integers.

struct cl_heap_GV_I_general : public cl_heap_GV_I {
	cl_I data[1];
	// Standard allocation disabled.
	void* operator new (size_t size) { unused size; cl_abort(); return NULL; }
	// Standard deallocation disabled.
	void operator delete (void* ptr) { unused ptr; cl_abort(); }
	// No default constructor.
	cl_heap_GV_I_general ();
};

static cl_I general_element (const cl_GV_inner<cl_I>* vec, uintL index)
{
	return ((const cl_heap_GV_I_general *) outcast(vec))->data[index];
}

static void general_set_element (cl_GV_inner<cl_I>* vec, uintL index, const cl_I& x)
{
	((cl_heap_GV_I_general *) outcast(vec))->data[index] = x;
}

static void general_do_delete (cl_GV_inner<cl_I>* vec)
{
	var cl_heap_GV_I_general* hv = (cl_heap_GV_I_general *) outcast(vec);
	var uintL len = hv->v.length();
	for (var uintL i = 0; i < len; i++)
		hv->data[i].~cl_I();
}

static cl_GV_I_vectorops general_vectorops = {{
	general_element,
	general_set_element,
	general_do_delete },
	-1
};

cl_heap_GV_I* cl_make_heap_GV_I (uintL len)
{
	var cl_heap_GV_I_general* hv = (cl_heap_GV_I_general*) cl_malloc_hook(offsetofa(cl_heap_GV_I_general,data)+sizeof(cl_I)*len);
	hv->refcount = 1;
	hv->typetag = cl_typetag_gvector_integer;
	new (&hv->v) cl_GV_inner<cl_I> (len,&general_vectorops.ops);
	for (var uintL i = 0; i < len; i++)
		init1(cl_I, hv->data[i]) ();
	return hv;
}


// Vectors of integers requiring only few bits.

#define DEFINE_cl_heap_GV_I_bits(m,uint_t)  \
struct cl_heap_GV_I_bits##m : public cl_heap_GV_I {			\
	uint_t data[1];							\
	/* Standard allocation disabled. */				\
	void* operator new (size_t size) { unused size; cl_abort(); return NULL; } \
	/* Standard deallocation disabled. */				\
	void operator delete (void* ptr) { unused ptr; cl_abort(); }	\
	/* No default constructor. */					\
	cl_heap_GV_I_bits##m ();					\
};									\
static cl_I bits##m##_element (const cl_GV_inner<cl_I>* vec, uintL index); \
static void bits##m##_set_element (cl_GV_inner<cl_I>* vec, uintL index, const cl_I& x); \
static cl_GV_I_vectorops bits##m##_vectorops = {{			\
	bits##m##_element,						\
	bits##m##_set_element,						\
	bits_do_delete },						\
	m								\
};

static void bits_do_delete (cl_GV_inner<cl_I>* vec)
{
	unused vec;
}


DEFINE_cl_heap_GV_I_bits(1,uint8)

static cl_I bits1_element (const cl_GV_inner<cl_I>* vec, uintL index)
{
	return (unsigned int)((((const cl_heap_GV_I_bits1 *) outcast(vec))->data[index/8] >> (index%8)) & 0x1);
}
static void bits1_set_element (cl_GV_inner<cl_I>* vec, uintL index, const cl_I& x)
{
	var uint32 xval;
	if (fixnump(x)) {
		xval = FN_to_UL(x);
		if (xval <= 0x1) {
			var uint8* ptr = &((cl_heap_GV_I_bits1 *) outcast(vec))->data[index/8];
			index = index%8;
			*ptr = *ptr ^ ((*ptr ^ (xval << index)) & (0x1 << index));
			return;
		}
	}
	cl_abort();
}


DEFINE_cl_heap_GV_I_bits(2,uint8)

static cl_I bits2_element (const cl_GV_inner<cl_I>* vec, uintL index)
{
	return (unsigned int)((((const cl_heap_GV_I_bits2 *) outcast(vec))->data[index/4] >> (2*(index%4))) & 0x3);
}
static void bits2_set_element (cl_GV_inner<cl_I>* vec, uintL index, const cl_I& x)
{
	var uint32 xval;
	if (fixnump(x)) {
		xval = FN_to_UL(x);
		if (xval <= 0x3) {
			var uint8* ptr = &((cl_heap_GV_I_bits2 *) outcast(vec))->data[index/4];
			index = 2*(index%4);
			*ptr = *ptr ^ ((*ptr ^ (xval << index)) & (0x3 << index));
			return;
		}
	}
	cl_abort();
}


DEFINE_cl_heap_GV_I_bits(4,uint8)

static cl_I bits4_element (const cl_GV_inner<cl_I>* vec, uintL index)
{
	return (unsigned int)((((const cl_heap_GV_I_bits4 *) outcast(vec))->data[index/2] >> (4*(index%2))) & 0xF);
}
static void bits4_set_element (cl_GV_inner<cl_I>* vec, uintL index, const cl_I& x)
{
	var uint32 xval;
	if (fixnump(x)) {
		xval = FN_to_UL(x);
		if (xval <= 0xF) {
			var uint8* ptr = &((cl_heap_GV_I_bits4 *) outcast(vec))->data[index/2];
			index = 4*(index%2);
			*ptr = *ptr ^ ((*ptr ^ (xval << index)) & (0xF << index));
			return;
		}
	}
	cl_abort();
}


DEFINE_cl_heap_GV_I_bits(8,uint8)

static cl_I bits8_element (const cl_GV_inner<cl_I>* vec, uintL index)
{
	return (unsigned int)(((const cl_heap_GV_I_bits8 *) outcast(vec))->data[index]);
}
static void bits8_set_element (cl_GV_inner<cl_I>* vec, uintL index, const cl_I& x)
{
	var uint32 xval;
	if (fixnump(x)) {
		xval = FN_to_UL(x);
		if (xval <= 0xFF) {
			((cl_heap_GV_I_bits8 *) outcast(vec))->data[index] = xval;
			return;
		}
	}
	cl_abort();
}


DEFINE_cl_heap_GV_I_bits(16,uint16)

static cl_I bits16_element (const cl_GV_inner<cl_I>* vec, uintL index)
{
	return (unsigned int)(((const cl_heap_GV_I_bits16 *) outcast(vec))->data[index]);
}
static void bits16_set_element (cl_GV_inner<cl_I>* vec, uintL index, const cl_I& x)
{
	var uint32 xval;
	if (fixnump(x)) {
		xval = FN_to_UL(x);
		if (xval <= 0xFFFF) {
			((cl_heap_GV_I_bits16 *) outcast(vec))->data[index] = xval;
			return;
		}
	}
	cl_abort();
}


DEFINE_cl_heap_GV_I_bits(32,uint32)

static cl_I bits32_element (const cl_GV_inner<cl_I>* vec, uintL index)
{
	return (unsigned long)(((const cl_heap_GV_I_bits32 *) outcast(vec))->data[index]);
}
static void bits32_set_element (cl_GV_inner<cl_I>* vec, uintL index, const cl_I& x)
{
	var uint32 xval = cl_I_to_UL(x);
	((cl_heap_GV_I_bits32 *) outcast(vec))->data[index] = xval;
}


static cl_GV_I_vectorops* bits_vectorops[6] = {
	&bits1_vectorops,
	&bits2_vectorops,
	&bits4_vectorops,
	&bits8_vectorops,
	&bits16_vectorops,
	&bits32_vectorops
};

cl_heap_GV_I* cl_make_heap_GV_I (uintL len, sintL m)
{
	// Determine log2(bits).
	var uintL log2_bits;
	switch (m) {
		case 0: case 1:
			log2_bits = 0; break;
		case 2:
			log2_bits = 1; break;
		case 3: case 4:
			log2_bits = 2; break;
		case 5: case 6: case 7: case 8:
			log2_bits = 3; break;
		case 9: case 10: case 11: case 12:
		case 13: case 14: case 15: case 16:
			log2_bits = 4; break;
		case 17: case 18: case 19: case 20:
		case 21: case 22: case 23: case 24:
		case 25: case 26: case 27: case 28:
		case 29: case 30: case 31: case 32:
			log2_bits = 5; break;
		default:
			return cl_make_heap_GV_I(len);
	}
	// For room allocation purposes, be pessimistic: assume the bits32 case.
	var uintL words = // ceiling(len*2^log2_bits,32)
	  (((sintL)len-1)>>(5-log2_bits))+1;
	var cl_heap_GV_I_bits32* hv = (cl_heap_GV_I_bits32*) cl_malloc_hook(offsetofa(cl_heap_GV_I_bits32,data)+sizeof(uint32)*words);
	hv->refcount = 1;
	hv->typetag = cl_typetag_gvector_integer;
	new (&hv->v) cl_GV_inner<cl_I> (len,&bits_vectorops[log2_bits]->ops);
	for (var uintL i = 0; i < words; i++)
		hv->data[i] = 0;
	return (cl_heap_GV_I*) hv;
}


sintL cl_heap_GV_I::maxbits () const
{
	return outcast(v.vectorops)->m;
}


// An empty vector.
cl_GV_I cl_null_GV_I = cl_GV_I(0);

CL_PROVIDE_END(cl_GV_I)
