#include <../source/TAL/objbase.h>

/*
 * Cut-and-hack from NR
 */

/*
 * Long period (> 2*10^18) random number generator of L'Ecuyer with
 * Bays-Durham shuffle and added safeguards. Returns a uniform random
 * deviate between 0.0 and 1.0 (exclusive of the endpoint values).
 * Call with idum a negative integer to initialize; thereafter, do not
 * alter idum between successive deviates in a sequence. RNMX should
 * approximate the largest floating value that is less than 1.
 */
#define IM1 2147483563
#define IM2 2147483399
#define AM (1.0/IM1)
#define IMM1 (IM1-1)
#define IA1 40014
#define IA2 40692
#define IQ1 53668
#define IQ2 52774
#define IR1 12211
#define IR2 3791
#define NDIV (1+IMM1/NTAB)
#define EPS 1.2e-9
#define RNMX (1.0-EPS)

#ifndef	CONFIG_FAST_RGEN

#define NTAB 32

typedef struct ran_context_s
{
	libv_object_t	base;
	int idum;
	long idum2;
	long iy;
	long iv[NTAB];
} ran_context_t, *ran_context_p;

static char *rgen_name = "NR ran2() wrapper";
#define	RGEN_MAGIC	0x5247454E /* RGEN */

V_LOCAL u32_t V_CALL
rgen_destroy(vhandle_t handle)
{
	ran_context_p self=(ran_context_p)handle;
	if(--self->base.ref_count)
		return self->base.ref_count;
	memFreeObject(self);
	return 0;
}

#endif	/* !CONFIG_FAST_RGEN */

vhandle_t V_GLOBAL
urnCreateGenerator(int seed)
{
	int j,k;
#ifdef	CONFIG_FAST_RGEN
	ran_context_p ctx = (ran_context_p)memAlloc(sizeof(ran_context_t));
#else
	ran_context_p ctx;
	ALLOC_OBJECT(ctx,ran_context_t,0,RGEN_MAGIC);
	ctx->base.destroy = rgen_destroy;
#endif
	if (seed < 1)
		ctx->idum = 1;
	else
		ctx->idum = seed;
	ctx->idum2 = ctx->idum;
	for (j=NTAB+7;j>=0;j--)
	{
		k = (ctx->idum)/IQ1;
		ctx->idum=IA1*(ctx->idum-k*IQ1)-k*IR1;
		if (ctx->idum < 0)
			ctx->idum += IM1;
		if (j < NTAB)
			ctx->iv[j] = ctx->idum;
	}
	ctx->iy = ctx->iv[0];
	return (vhandle_t)ctx;
}

/*
 * Returns uniform-deviated integer random number in range [0..upper_bound-1]
 */
long V_GLOBAL
urnNextLong(vhandle_t gen,long upper_bound)
{
	int j;
	long k;
#ifdef	CONFIG_FAST_RGEN
	ran_context_p ctx = (ran_context_p)gen;
#else
	ran_context_p ctx = (ran_context_p)libvIsHandle(gen,
		"urnNextLong",rgen_name,RGEN_MAGIC);
#endif
	if(!ctx)
		return 0;
	k = (ctx->idum)/IQ1;
	ctx->idum = IA1*(ctx->idum-k*IQ1)-k*IR1;
	if (ctx->idum < 0)
		ctx->idum += IM1;
	k = ctx->idum2/IQ2;
	ctx->idum2 = IA2*(ctx->idum2-k*IQ2)-k*IR2;
	if (ctx->idum2 < 0)
		ctx->idum2 += IM2;
	j = ctx->iy/NDIV;
	ctx->iy = ctx->iv[j]-ctx->idum2;
	ctx->iv[j] = ctx->idum;
	if (ctx->iy < 1)
		ctx->iy += IMM1;
	return ctx->iy % upper_bound;
}

/*
 * Returns uniform-deviated float random number in range [0..1)
 */
V_LOCAL vec_t
urn_next_float(ran_context_p ctx)
{
	long k, j;
	vec_t temp;
	k = (ctx->idum)/IQ1;
	ctx->idum = IA1*(ctx->idum-k*IQ1)-k*IR1;
	if (ctx->idum < 0)
		ctx->idum += IM1;
	k = ctx->idum2/IQ2;
	ctx->idum2 = IA2*(ctx->idum2-k*IQ2)-k*IR2;
	if (ctx->idum2 < 0)
		ctx->idum2 += IM2;
	j = ctx->iy/NDIV;
	ctx->iy = ctx->iv[j]-ctx->idum2;
	ctx->iv[j] = ctx->idum;
	if (ctx->iy < 1)
		ctx->iy += IMM1;
	temp = AM * ctx->iy;
	if (temp > RNMX)
		return RNMX;
	else
		return temp;
}

/*
 * Returns uniform-deviated float random number in range [0..1)
 */
vec_t V_GLOBAL
urnNextFloat(vhandle_t gen)
{
#ifdef	CONFIG_FAST_RGEN
	ran_context_p ctx = (ran_context_p)gen;
#else
	ran_context_p ctx = (ran_context_p)libvIsHandle(gen,
		"urnNextFloat",rgen_name,RGEN_MAGIC);
#endif
	if(!ctx)
		return 0;
	return urn_next_float(ctx);
}

vec_t V_GLOBAL
urnNextFloatRange(vhandle_t gen,vec_t min,vec_t max)
{
	vec_t r;
#ifdef	CONFIG_FAST_RGEN
	ran_context_p ctx = (ran_context_p)gen;
#else
	ran_context_p ctx = (ran_context_p)libvIsHandle(gen,
		"urnNextFloatRange",rgen_name,RGEN_MAGIC);
#endif
	if(!ctx)
		return 0;
	r = urn_next_float(ctx);
	return min + r * (max - min);
}

V_LOCAL void
urn_next_vector_range(ran_context_p ctx,u32_t ndims,
	vec_t ret[],vec_t min,vec_t max)
{
	u32_t i;
	vec_t r, delta = max - min;
	for(i=0;i<ndims;i++)
	{
		r = urn_next_float(ctx);
		ret[i] = min + r * delta;
	}
}

V_LOCAL void
urn_next_vector_range_v(ran_context_p ctx,u32_t ndims,
	vec_t ret[],vec_t mins[],vec_t maxs[])
{
	u32_t i;
	vec_t r;
	for(i=0;i<ndims;i++)
	{
		r = urn_next_float(ctx);
		ret[i] = mins[i] + r * (maxs[i] - mins[i]);
	}
}

void V_GLOBAL
urnNextVec3Range(vhandle_t gen,vec3_t vec3_ret,vec_t min,vec_t max)
{
#ifdef	CONFIG_FAST_RGEN
	ran_context_p ctx = (ran_context_p)gen;
#else
	ran_context_p ctx = (ran_context_p)libvIsHandle(gen,
		"urnNextVec3Range",rgen_name,RGEN_MAGIC);
#endif
	if(!ctx)
	{
		vec3Zero(vec3_ret);
		return;
	}
	urn_next_vector_range(ctx,3,vec3_ret,min,max);
}

void V_GLOBAL
urnNextVec3RangeV(vhandle_t gen,vec3_t vec3_ret,vec3_t mins,vec3_t maxs)
{
#ifdef	CONFIG_FAST_RGEN
	ran_context_p ctx = (ran_context_p)gen;
#else
	ran_context_p ctx = (ran_context_p)libvIsHandle(gen,
		"urnNextVec3Range3",rgen_name,RGEN_MAGIC);
#endif
	if(!ctx)
	{
		vec3Zero(vec3_ret);
		return;
	}
	urn_next_vector_range_v(ctx,3,vec3_ret,mins,maxs);
}

void	V_GLOBAL
urnNextVectorRange(vhandle_t gen,vector_p vec_ret,vec_t min,vec_t max)
{
#ifdef	CONFIG_FAST_RGEN
	ran_context_p ctx = (ran_context_p)gen;
#else
	ran_context_p ctx = (ran_context_p)libvIsHandle(gen,
		"urnNextVectorRange",rgen_name,RGEN_MAGIC);
#endif
	if(!ctx)
	{
		if(vec_ret)
			vecZero(vec_ret);
		return;
	}
	if(!vec_ret)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"urnNextVectorRange(%g,%g): NULL dest.\n",
			min,max);
#endif	/* CHECKED_BUILD */
		return;
	}
	urn_next_vector_range(ctx,vec_ret->size,vec_ret->elements,min,max);
}

void	V_GLOBAL
urnNextVectorRangeV(vhandle_t gen,
	vector_p vec_ret,vector_p mins,vector_p maxs)
{
#ifdef	CONFIG_FAST_RGEN
	ran_context_p ctx = (ran_context_p)gen;
#else
	ran_context_p ctx = (ran_context_p)libvIsHandle(gen,
		"urnNextVectorRangeV",rgen_name,RGEN_MAGIC);
#endif
	if(!ctx)
	{
		if(vec_ret)
			vecZero(vec_ret);
		return;
	}
	if(!vec_ret || !mins || !maxs 
		|| vec_ret->size != mins->size || vec_ret->size != maxs->size)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"urnNextVectorRangeV(): invalid parameter(s).\n");
#endif	/* CHECKED_BUILD */
		if(vec_ret)
			vecZero(vec_ret);
		return;
	}
	urn_next_vector_range_v(ctx,vec_ret->size,vec_ret->elements,
		mins->elements,maxs->elements);
}

void V_GLOBAL
urnDestroyGenerator(vhandle_t gen)
{
#ifdef	CONFIG_FAST_RGEN
	memFree(gen);
#else
	libvCloseHandle(gen,"urnDestroyGenerator",rgen_name,RGEN_MAGIC);
#endif
}
