/* Libvisual - The audio visualisation framework.
 * 
 * Copyright (C) 2004, 2005, 2006 Dennis Smit <ds@nerds-incorporated.org>
 *
 * Authors: Dennis Smit <ds@nerds-incorporated.org>
 * 	    Vitaly V. Bursov <vitalyvb@ukr.net>
 *
 * $Id: lv_random.c,v 1.19 2006/01/22 13:23:37 synap Exp $
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include "lv_random.h"

/* Thanks Burkhard Plaum <plaum@ipf.uni-stuttgart.de> for these values and some other hints */
#define val_a 1664525L		/* As suggested by Knuth */
#define val_c 1013904223L	/* As suggested by H.W. Lewis and is a prime close to 2^32 * (sqrt(5) - 2)) */

VisRandomContext __lv_internal_random_context;

/**
 * @defgroup VisRandom VisRandom
 * @{
 */

/**
 * Creates a new VisRandomContext data structure.
 *
 * @param seed The seed to be used to initialize the VisRandomContext with.
 *
 * @return A newly allocated VisRandomContext, or NULL on failure.
 */
VisRandomContext *visual_random_context_new (uint32_t seed)
{
	VisRandomContext *rcontext;

	rcontext = visual_mem_new0 (VisRandomContext, 1);

	visual_random_context_init (rcontext, seed);

	/* Do the VisObject initialization */
	visual_object_set_allocated (VISUAL_OBJECT (rcontext), TRUE);
	visual_object_ref (VISUAL_OBJECT (rcontext));

	return rcontext;
}

int visual_random_context_init (VisRandomContext *rcontext, uint32_t seed)
{
	visual_log_return_val_if_fail (rcontext != NULL, -VISUAL_ERROR_RANDOM_CONTEXT_NULL);

	/* Do the VisObject initialization */
	visual_object_clear (VISUAL_OBJECT (rcontext));
	visual_object_set_dtor (VISUAL_OBJECT (rcontext), NULL);
	visual_object_set_allocated (VISUAL_OBJECT (rcontext), FALSE);

	/* Set the VisRandomContext data */
	visual_random_context_set_seed (rcontext, seed);

	return VISUAL_OK;
}

/**
 * Set the seed to for a VisRandomContext.
 *
 * @param rcontext Pointer to the VisRandomContext for which the seed it set.
 * @param seed The seed which is set in the VisRandomContext.
 *
 * @return VISUAL_OK on succes, -VISUAL_ERROR_RANDOM_CONTEXT_NULL on failure.
 */
int visual_random_context_set_seed (VisRandomContext *rcontext, uint32_t seed)
{
	visual_log_return_val_if_fail (rcontext != NULL, -VISUAL_ERROR_RANDOM_CONTEXT_NULL);

	rcontext->seed = seed;
	rcontext->seed_state = seed;

	return VISUAL_OK;
}

/**
 * Get the seed that has been set to the VisRandomContext. This returns
 * the initial seed. Not the state seed.
 *
 * @see visual_random_context_get_seed_state
 *
 * @param rcontext The pointer to the VisRandomContext of which the initial random seed is requested.
 *
 * @return The initial random seed.
 */
uint32_t visual_random_context_get_seed (VisRandomContext *rcontext)
{
	visual_log_return_val_if_fail (rcontext != NULL, 0);

	return rcontext->seed;
}

/**
 * Get the current state seed for the VisRandomContext.
 *
 * @see visual_random_context_get_seed
 *
 * @param rcontext The pointer to the VisRandomContext of which the state seed is requested.
 *
 * @return The current state seed for the randomizer.
 */
uint32_t visual_random_context_get_seed_state (VisRandomContext *rcontext)
{
	visual_log_return_val_if_fail (rcontext != NULL, 0);

	return rcontext->seed_state;
}

/**
 * Gives a random integer using the VisRandomContext as context for the randomizer.
 *
 * @param rcontext The pointer to the VisRandomContext in which the state of the randomizer is set.
 *
 * @return A pseudo random integer.
 */
uint32_t visual_random_context_int (VisRandomContext *rcontext)
{
	visual_log_return_val_if_fail (rcontext != NULL, 0);

	return (rcontext->seed_state = val_a * rcontext->seed_state + val_c);
}

/**
 * Gives a random integer ranging between min and max using the VisRandomContext as context
 * for the randomizer.
 * This function may use floating point instructions. Remeber, this will break
 * things if used inside of MMX code.
 *
 * @param rcontext The pointer to the VisRandomContext in which the state of the randomizer is set.
 * @param min The minimum for the output.
 * @param max The maximum for the output.
 *   
 * @return A pseudo random integer confirm to the minimum and maximum.
 */
uint32_t visual_random_context_int_range (VisRandomContext *rcontext, int min, int max)
{
#if VISUAL_RANDOM_FAST_FP_RND
	/* Uses fast floating number generator and two divisions elimitated.
	 * More than 2 times faster than original.
	 */
	float fm = min; /* +10% speedup... */

	visual_log_return_val_if_fail (rcontext != NULL, 0);

	return visual_random_context_float (rcontext) * (max - min + 1) + fm;
#else
	visual_log_return_val_if_fail (rcontext != NULL, 0);

	return (visual_random_context_int (rcontext) / (VISUAL_RANDOM_MAX / (max - min + 1))) + min;
#endif
}

/**
 * Gives a random double precision floating point value
 * using the VisRandomContext as context for the randomizer.
 *
 * @param rcontext The pointer to the VisRandomContext in which the state of the randomizer is set.
 *
 * @return A pseudo random integer.
 */
double visual_random_context_double (VisRandomContext *rcontext)
{
#if VISUAL_RANDOM_FAST_FP_RND
	union {
		unsigned int i[2];
		double d;
	} value;
#endif
	uint32_t irnd;

	visual_log_return_val_if_fail (rcontext != NULL, -1);

	irnd = (rcontext->seed_state = val_a * rcontext->seed_state + val_c);
#if VISUAL_RANDOM_FAST_FP_RND
	/* This saves floating point division (20 clocks on AXP, 
	 * 38 on P4) and introduces store-to-load data size mismatch penalty
	 * and substraction op.
	 * Faster on AXP anyway :)
	 */

	value.i[0] = (irnd << 20);
	value.i[1] = 0x3ff00000 | (irnd >> 12);

	return value.d - 1.0;
#else
	return (double) irnd / VISUAL_RANDOM_MAX;
#endif
}

/**
 * Gives a random single precision floating point value
 * using the VisRandomContext as context for the randomizer.
 *
 * @param rcontext The pointer to the VisRandomContext in which the state of the randomizer is set.
 *
 * @return A pseudo random integer.
 */
float visual_random_context_float (VisRandomContext *rcontext)
{
#if VISUAL_RANDOM_FAST_FP_RND
	union {
		unsigned int i;
		float f;
	} value;
#endif
	uint32_t irnd;

	visual_log_return_val_if_fail (rcontext != NULL, -1);

	irnd = (rcontext->seed_state = val_a * rcontext->seed_state + val_c);
#if VISUAL_RANDOM_FAST_FP_RND
	/* Saves floating point division. Introduces substraction.
	 * Yet faster! :)
	 */

	value.i = 0x3f800000 | (t >> 9);

	return value.f - 1.0f;
#else
	return (float) irnd / VISUAL_RANDOM_MAX;
#endif
}

/**
 * Function which returns 1 with a propability of p (0.0 <= p <= 1.0) using the VisRandomContext
 * as context for the randomizer.
 *
 * @param rcontext The pointer to the VisRandomContext in which the state of the randomizer is set.
 * @param a The float to be used in the decide.
 *
 * @returns 0 or 1, -VISUAL_ERROR_RANDOM_CONTEXT_NULL on failure.
 */
int visual_random_context_decide (VisRandomContext *rcontext, float a)
{
	visual_log_return_val_if_fail (rcontext != NULL, -VISUAL_ERROR_RANDOM_CONTEXT_NULL);

	return visual_random_context_float (rcontext) <= a;
}

/**
 * @}
 */

