/*
 *  genrand.C from ObjectProDSP 0.1
 *  Copyright (C) 1994, Mountain Math Software. All rights reserved.
 *  
 *  This file is part of ObjectProDSP, a tool for Digital Signal
 *  Processing design, development and implementation. It is free
 *  software provided you use and distribute it under the terms of
 *  version 2 of the GNU General Public License as published
 *  by the Free Software Foundation. You may NOT distribute it or
 *  works derived from it or code that it generates under ANY
 *  OTHER terms.  In particular NONE of the ObjectProDSP system is
 *  licensed for use under the GNU General Public LIBRARY License.
 *  Mountain Math Software plans to offer a commercial version of
 *  ObjectProDSP for a fee. That version will allow redistribution
 *  of generated code under standard commercial terms.
 *  
 *  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 General Public License for more details.
 *  
 *  You should have received a copy of version 2 of the GNU General
 *  Public License along with this program. See file COPYING. If not
 *  or if you wish information on commercial versions and licensing
 *  write Mountain Math Software, P. O. Box 2124, Saratoga, CA 95070,
 *  USA, or send us e-mail at: support@mtnmath.com.
 *  
 *  You may also obtain the GNU General Public License by writing the
 *  Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 *  USA.  However if you received a copy of this program without the
 *  file COPYING or without a copyright notice attached to all text
 *  files, libraries and executables please inform Mountain Math Software.
 *  
 *  ObjectProDSP is a trademark of Mountain Math Software.
 */
#include <math.h>
#include <limits.h>
#include <stream.h>
#include <stdlib.h>
#include <float.h>
// #include "sysrand.h"
#include "genrand.h"

#include "cgidbg.h"
	// remove this include - for debuggin only

// routines for generating various random number distributions

/*	Algorithms taken from:
 *
 *	Simulation and the Monte Carlo Method, Reuven Y. Rubinstein,
 *	_____________________________________
 *
 *	John Wiley and Sons, 1981.
 */

static int DoubleCompare(const void * a, const void *b)
{
	double A = *(double *) a;
	double B = *(double *) b;
	int SignA = A < 0.0 ;
	int SignB = B < 0.0 ;
	// don't do subtract if signs differ to avoid overflow
	if (SignA^SignB) {
		if (SignA) return -1;
		return 1 ;
	}
	double diff = A - B ;
	if (diff == 0.0) return 0;
	if (diff < 0.0) return -1;
	return 1;
}

GaussRand::GaussRand(int32 seed):NextElt(prev_size),shift(bits_in_un_long),
	uniform_long(0),save_state(0)
{
#ifdef __NT_VC__
	srand(seed);
#else
	state = (char *) new int32[state_size >> 2] ;
	for (int i = 0 ; i < state_size;i++) state[i]=0 ;
	save_state = (char *) initstate(seed,state,(state_size));
	restore_state();
#endif
}

void GaussRand::dump_state(const char * where)
{
	TheLog << "GaussRand state in `" << where << "'\n" ;
	for (int i = 0 ; i < state_size;i++) {
		TheLog << " 0x" <<  hex << (unsigned) state[i] << dec ;
		if (i %8 ==7) TheLog << "\n" ;
	}
	TheLog << "\n" ;
}

GaussRand::~GaussRand()
{
	delete state ;
}

void GaussRand::restore_state()
{
#ifndef __NT_VC__
	setstate(save_state);
#endif
}

void GaussRand::set_state()
{
#ifndef __NT_VC__
	save_state = (char *) setstate(state);
#endif
}

// Algorithm E-2 page 70

#ifndef RAND_MAX
#define RAND_MAX INT_MAX
#endif
#ifndef __NT_VC__
double inline drandom() {return ((double) random())/RAND_MAX;}
#else
double inline drandom() {return ((double) rand())/RAND_MAX;}
#endif

double GaussRand::exp_rand()
{
	double Temp[prev_size+1];

	if (NextElt < prev_size) return Previous[NextElt++] ;

	Temp[0] = 0.0;
	for (int i = 1 ; i < prev_size ; i++) Temp[i] = drandom();
	Temp[prev_size] = 1.0 ;

	qsort ((char *) (Temp+1), prev_size-1 ,sizeof(Temp[0]),DoubleCompare);

	for (i = 0 ; i < prev_size ; i++) Previous[i] = drandom();
	
	double Prod = 1.0;
	for (i=0; i < prev_size;i++) Prod *= Previous[i] ;
	if (Prod < DBL_MIN * 4)  Prod = -200.;
	else Prod = log(Prod);

	for (i=0 ; i < prev_size ; i++) Previous[i] = (Temp[i] - Temp[i+1]) * Prod ;
	NextElt = 0;

	return Previous[NextElt++] ;
}
	


double GaussRand::gauss_rand()
{
	// LogOut << "GaussRand enter\n" ;
// #if TheArithType == ArithDouble || TheArithType == ArithFloat

// Algorithm N-2 page 88

	double V1,V2,V1m;
	do {
		V1 = exp_rand();
		V2 = exp_rand();
		V1m = V1 - 1.;
	} while (V2*2. >= V1m*V1m);
	if (shift++ < bits_in_un_long) uniform_long = uniform_long >> 1;
	else {
#ifdef __NT_VC__
		uniform_long = rand();
#else
		uniform_long = random();
#endif
		shift = 1;
	}
	if (uniform_long & 1) V1 = - V1 ;
	return V1 ;
#if 0
// Algorithm N-5 page 90 (more efficient if no floating point hardware)

	static double ScaleFactor = 1./0x7ffffff ;
	int32 Sum = 0;
	for (int i = 0 ; i < 12 ; i ++) Sum += (random() >> 4) ;
	// LogOut << "GaussRand exit\n" ;
	return (double) Sum * ScaleFactor - 6.)
#endif
}

