/* $Id: part.c,v 1.6 2006/11/16 14:59:34 knolli Exp $ */

/**
 * \file part.c
 *
 * Provides particle structure and functions working on them.
 */


/***********************************************************************\
 *    Includes                                                         * 
\***********************************************************************/
#include "part.h"
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>


/***********************************************************************\
 *    Prototypes of local functions                                    * 
\***********************************************************************/

/**
 * \brief Small helper function to flip a coin with unequal chance to be
 *        head or tail
 *
 * \param frac  The probability to be tail (1). Must be between 0.0 and
 *              1.0.
 *
 * \return Returns either 1 or 0.
 */
static int8_t
local_flipcoin(double frac);


/***********************************************************************\
 *    Implementation of global functions                               * 
\***********************************************************************/

#undef CREATEPARTICLES
#define CREATEPARTICLES(pt, ptstruct, num) \
	pt dummy; \
	\
	if (num == 0)\
		return NULL;\
	\
	dummy = (pt)malloc(sizeof(ptstruct)*num);\
	if (dummy == NULL) {\
		io_logging_memfatal(log, "particle array");\
		return NULL;\
	}\
	\
	return dummy;

extern dpart_t
dpart_create(io_logging_t log, uint64_t num)
{
	CREATEPARTICLES(dpart_t, dpart_struct_t, num)
}

extern spart_t
spart_create(io_logging_t log, uint64_t num)
{
	CREATEPARTICLES(spart_t, spart_struct_t, num)
}

#undef CREATEPARTICLES

extern void
dpart_del(dpart_t *parts)
{
	if ( (parts == NULL) || (*parts == NULL) )
		return;

	free(*parts);
	*parts = NULL;

	return;
}

extern void
spart_del(spart_t *parts)
{
	if ( (parts == NULL) || (*parts == NULL) )
		return;

	free(*parts);
	*parts = NULL;

	return;
}

extern spart_t
spart_reduce(io_logging_t log,
             spart_t parts,
             uint64_t num,
             uint64_t newnum,
             unsigned int seed)
{
	spart_t dummy;
	uint64_t i;
	uint64_t picked, left;
	double fac;

	/* Get new particle storage */
	dummy = spart_create(log, newnum);
	if (dummy == NULL)
		return NULL;

	/* Figure out the factor by which the particle amount gets reduced */
	fac = (double)num / (double)newnum;
	io_logging_msg(log, INT32_C(1),
	               "Reducing the particle amount by a factor of %g.",
	               fac);

	/* Initialize random-thingie */
	srand(seed);

	/* Select particles */
	left = newnum;
	picked = UINT64_C(0);
	for (i=0; i<num; i++) {
		if (    (left == num-i)
		     || (local_flipcoin(1./fac) == 1)) {
			if (left == 0)
				break;
			memcpy(dummy+picked, parts+i, sizeof(spart_struct_t));
			picked++;
			left--;
			io_logging_msg(log, INT32_C(10),
			               "Picked particle %" PRIu64
			               " (%" PRIu64 " of %" PRIu64 ")",
			               i, picked, newnum);
		}
	}

	/* Done! */
	return dummy;
}

extern uint64_t
spart_scale(io_logging_t log,
            spart_t parts,
            uint64_t num,
            double scpos,
            double scmom,
            double scweight)
{
	uint64_t i;

	if (parts == NULL)
		return UINT64_C(0);

	for (i=0; i<num; i++) {
		parts[i].pos[0] *= scpos;
		parts[i].pos[1] *= scpos;
		parts[i].pos[2] *= scpos;
		parts[i].mom[0] *= scmom;
		parts[i].mom[1] *= scmom;
		parts[i].mom[2] *= scmom;
		parts[i].weight *= scweight;
	}

	return i;
}

extern void
spart_dump(spart_t parts, uint64_t num)
{
	uint64_t i;

	for (i=0; i<num; i++) {
		printf("%g %g %g %g %g %g\n",
		       parts[i].pos[0], parts[i].pos[1], parts[i].pos[2],
		       parts[i].mom[0], parts[i].mom[1], parts[i].mom[2]);
	}
}


/***********************************************************************\
 *    Implementation of local functionss                               * 
\***********************************************************************/
static int8_t
local_flipcoin(double frac)
{
	int rnd;

	rnd = rand();

	if (rnd <= (int)(RAND_MAX*frac))
		return INT8_C(1);

	return INT8_C(0);
}
