/*
 * Copyright (c) 2004, Bull S.A..  All rights reserved.
 * Created by: Sebastien Decugis

 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
 *
 
 
 * This file is a scalability test for the pthread_cond_timedwait function.
 *
 * It aims to measure the time between end of timeout and actual wakeup
 * with different factors.

 * The steps are:
 * -*- Number of threads waiting on the conditionnal variable
 *     -> for an increaing number of threads,
 *        -> create the other threads which will do a pthread_cond_wait on the same cond/mutex
 *        -> When the threads are waiting, create one thread which will measure the time
 *        -> once the timeout has expired and the measure is done, broadcast the condition.
 *     -> do each measure 10 times (with different attributes i.e.)
 *
 * -*- other possible influencial parameters
 *     -> To be defined.
 */

 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
 #define _POSIX_C_SOURCE 200112L
 
 #ifndef WITHOUT_XOPEN
 #define _XOPEN_SOURCE 600
 #endif
 
/********************************************************************************************/
/****************************** standard includes *****************************************/
/********************************************************************************************/
 #include <pthread.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h> 
 #include <unistd.h>

 #include <time.h>
 #include <errno.h>
 #include <math.h>
 
/********************************************************************************************/
/******************************   Test framework   *****************************************/
/********************************************************************************************/
 #include "testfrmw.h"
 #include "testfrmw.c"
 /* This header is responsible for defining the following macros:
  * UNRESOLVED(ret, descr);  
  *    where descr is a description of the error and ret is an int (error code for example)
  * FAILED(descr);
  *    where descr is a short text saying why the test has failed.
  * PASSED();
  *    No parameter.
  * 
  * Both three macros shall terminate the calling process.
  * The testcase shall not terminate in any other maneer.
  * 
  * The other file defines the functions
  * void output_init()
  * void output(char * string, ...)
  * 
  * Those may be used to output information.
  */

/********************************************************************************************/
/********************************** Configuration ******************************************/
/********************************************************************************************/
#ifndef SCALABILITY_FACTOR
#define SCALABILITY_FACTOR 1
#endif
#ifndef VERBOSE
#define VERBOSE 1
#endif

#ifndef WITHOUT_ALTCLK
#define USE_ALTCLK  /* make tests with MONOTONIC CLOCK if supported */
#endif

#define MES_TIMEOUT  (1000000) /* ns, offset for the pthread_cond_timedwait call */

#ifdef PLOT_OUTPUT
#undef VERBOSE
#define VERBOSE 0
#endif

// #define USE_CANCEL  /* Will cancel the threads instead of signaling the cond */#define 

/********************************************************************************************/
/***********************************    Test case   *****************************************/
/********************************************************************************************/

long altclk_ok, pshared_ok;

typedef struct 
{
	pthread_cond_t *cnd;
	pthread_mutex_t *mtx;
	int * predicate;
	int * tnum;
} test_t;

struct {
	int mutex_type;
	int pshared;
	clockid_t cid;
	char * desc;
} test_scenar[] = {
	 { PTHREAD_MUTEX_DEFAULT   , PTHREAD_PROCESS_PRIVATE, CLOCK_REALTIME , "Default" }
	,{ PTHREAD_MUTEX_DEFAULT   , PTHREAD_PROCESS_SHARED , CLOCK_REALTIME , "PShared" }
#ifndef WITHOUT_XOPEN
	,{ PTHREAD_MUTEX_NORMAL    , PTHREAD_PROCESS_PRIVATE, CLOCK_REALTIME , "Normal" }
	,{ PTHREAD_MUTEX_NORMAL    , PTHREAD_PROCESS_SHARED , CLOCK_REALTIME , "Normal+PShared" }
	,{ PTHREAD_MUTEX_ERRORCHECK, PTHREAD_PROCESS_PRIVATE, CLOCK_REALTIME , "Errorcheck" }
	,{ PTHREAD_MUTEX_ERRORCHECK, PTHREAD_PROCESS_SHARED , CLOCK_REALTIME , "Errorcheck+PShared" }
	,{ PTHREAD_MUTEX_RECURSIVE , PTHREAD_PROCESS_PRIVATE, CLOCK_REALTIME , "Recursive" }
	,{ PTHREAD_MUTEX_RECURSIVE , PTHREAD_PROCESS_SHARED , CLOCK_REALTIME , "Recursive+PShared" }
#endif
#ifdef USE_ALTCLK
	,{ PTHREAD_MUTEX_DEFAULT   , PTHREAD_PROCESS_PRIVATE, CLOCK_MONOTONIC, "Monotonic" }
	,{ PTHREAD_MUTEX_DEFAULT   , PTHREAD_PROCESS_SHARED , CLOCK_MONOTONIC, "PShared+Monotonic" }
#ifndef WITHOUT_XOPEN
	,{ PTHREAD_MUTEX_NORMAL    , PTHREAD_PROCESS_PRIVATE, CLOCK_MONOTONIC, "Normal+Monotonic" }
	,{ PTHREAD_MUTEX_NORMAL    , PTHREAD_PROCESS_SHARED , CLOCK_MONOTONIC, "Normal+PShared+Monotonic" }
	,{ PTHREAD_MUTEX_ERRORCHECK, PTHREAD_PROCESS_PRIVATE, CLOCK_MONOTONIC, "Errorcheck+Monotonic" }
	,{ PTHREAD_MUTEX_ERRORCHECK, PTHREAD_PROCESS_SHARED , CLOCK_MONOTONIC, "Errorcheck+PShared+Monotonic" }
	,{ PTHREAD_MUTEX_RECURSIVE , PTHREAD_PROCESS_PRIVATE, CLOCK_MONOTONIC, "Recursive+Monotonic" }
	,{ PTHREAD_MUTEX_RECURSIVE , PTHREAD_PROCESS_SHARED , CLOCK_MONOTONIC, "Recursive+PShared+Monotonic" }
#endif
#endif
};

#define NSCENAR (sizeof(test_scenar) / sizeof(test_scenar[0]))

pthread_attr_t  ta;

/* The next structure is used to save the tests measures */
typedef struct __mes_t
{
	int  nthreads;
	long _data[ NSCENAR ]; /* As we store µsec values, a long type should be amply enough. */
	struct __mes_t *next;
} mes_t;


/**** do_measure
 * This function will do a timedwait on the cond cnd after locking mtx.
 * Once the timedwait times out, it will read the clock cid then 
 * compute the difference and put it into ts.
 * This function must be called once test is ready, as the timeout will be very short. */
void do_measure(pthread_mutex_t *mtx, pthread_cond_t *cnd, clockid_t cid, struct timespec *ts)
{
	int ret, rc;
	
	struct timespec ts_cnd, ts_clk;
	
	/* lock the mutex */
	ret = pthread_mutex_lock(mtx);
	if (ret != 0)  {  UNRESOLVED(ret, "Unable to lock mutex");  }
	
	/* Prepare the timeout parameter */
	ret = clock_gettime(cid, &ts_cnd);
	if (ret != 0)  {  UNRESOLVED(errno, "Unable to read clock");  }
	
	ts_cnd.tv_nsec += MES_TIMEOUT;
	while (ts_cnd.tv_nsec >= 1000000000)
	{
		ts_cnd.tv_nsec -= 1000000000;
		ts_cnd.tv_sec++;
	}
	
	/* Do the timedwait */
	do {
		rc = pthread_cond_timedwait(cnd, mtx, &ts_cnd);
		/* Re-read the clock as soon as possible */
		ret = clock_gettime(cid, &ts_clk);
		if (ret != 0)  {  UNRESOLVED(errno, "Unable to read clock");  }
	}
	while (rc == 0);
	if (rc != ETIMEDOUT)  {  UNRESOLVED(rc, "Timedwait returned an unexpected error (expected ETIMEDOUT)");  }
	
	/* Compute the difference time */
	ts->tv_sec = ts_clk.tv_sec - ts_cnd.tv_sec;
	ts->tv_nsec = ts_clk.tv_nsec - ts_cnd.tv_nsec;
	if (ts->tv_nsec < 0)
	{
		ts->tv_nsec += 1000000000;
		ts->tv_sec -= 1;
	}
	
	if (ts->tv_sec < 0)
	{
		FAILED("The function returned from wait with a timeout before the time has passed\n");
	}

	/* unlock the mutex */
	ret = pthread_mutex_unlock(mtx);
	if (ret != 0)  {  UNRESOLVED(ret, "Unable to unlock mutex");  }

	return;
}


void * waiter(void * arg)
{
	test_t * dt = (test_t *) arg;
	
	int ret;
	
	ret = pthread_mutex_lock(dt->mtx);
	if (ret != 0)  {  UNRESOLVED(ret, "Mutex lock failed in waiter");  }
	
#ifdef USE_CANCEL
	pthread_cleanup_push( (void *)pthread_mutex_unlock, (void *)(dt->mtx));
#endif
	
	/* This thread is ready to wait */
	*(dt->tnum) += 1;
	
	do {  ret = pthread_cond_wait(dt->cnd, dt->mtx);  }
	while ((ret == 0) && (*(dt->predicate) == 0));
	if (ret != 0)  {  UNRESOLVED(ret, "pthread_cond_wait failed in waiter");  }
	
#ifdef USE_CANCEL
	pthread_cleanup_pop(0); /* We could put 1 and avoid the next line, but we would miss the return code */
#endif
	
	ret = pthread_mutex_unlock(dt->mtx);
	if (ret != 0)  {  UNRESOLVED(ret, "Mutex unlock failed in waiter");  }
	
	return NULL;
}


/**** do_threads_test
 * This function is responsible for all the testing with a given # of threads
 *  nthreads is the amount of threads to create.
 * the return value is: 
 *  < 0 if function was not able to create enough threads.
 *  cumulated # of nanoseconds otherwise.
 */
long do_threads_test( int nthreads, mes_t * measure )
{
	int ret;
	
	int scal, i, j;
	
	pthread_t *th;
	
	int s;
	pthread_mutexattr_t ma;
	pthread_condattr_t ca;
	
	pthread_cond_t cnd;
	pthread_mutex_t mtx;
	int predicate;
	int tnum;
	
	test_t td;
	
	struct timespec ts, ts_cumul;

	td.mtx = &mtx;
	td.cnd = &cnd;
	td.predicate = &predicate;
	td.tnum = &tnum;
	
	
	/* Allocate space for the threads structures */
	th = (pthread_t *)calloc(nthreads, sizeof(pthread_t));
	if (th == NULL)  {  UNRESOLVED(errno, "Not enough memory for thread storage");  }
	
		
	#ifdef PLOT_OUTPUT
	output("%d", nthreads);
	#endif	
	/* For each test scenario (mutex and cond attributes) */
	for (s=0; s < NSCENAR ; s++)
	{
		/* Initialize the attributes */
		ret = pthread_mutexattr_init(&ma);
		if (ret != 0)  {  UNRESOLVED(ret, "Unable to initialize mutex attribute object");  }
		
		ret = pthread_condattr_init(&ca); 
		if (ret != 0)  {  UNRESOLVED(ret, "Unable to initialize cond attribute object");  }
		
		/* Set the attributes according to the scenar and the impl capabilities */
		ret = pthread_mutexattr_settype(&ma, test_scenar[s].mutex_type);
		if (ret != 0)  {  UNRESOLVED(ret, "Unable to set mutex type");  }
		
		if (pshared_ok > 0)
		{
			ret = pthread_mutexattr_setpshared(&ma, test_scenar[s].pshared);
			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set mutex process-shared");  }
			
			ret = pthread_condattr_setpshared(&ca, test_scenar[s].pshared);
			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set cond process-shared");  }
		}
		
		#ifdef USE_ALTCLK
		if (altclk_ok > 0)
		{
			ret = pthread_condattr_setclock(&ca, test_scenar[s].cid);
			if (ret != 0)  {  UNRESOLVED(ret, "Unable to set clock for cond");  }
		}
		#endif			

		ts_cumul.tv_sec= 0;
		ts_cumul.tv_nsec=0;

		#if VERBOSE > 1
		output("Starting case %s\n", test_scenar[s].desc);
		#endif

		for (scal=0; scal <  5 * SCALABILITY_FACTOR; scal ++)
		{
			/* Initialize the mutex, the cond, and other data */
			ret = pthread_mutex_init(&mtx, &ma);
			if (ret != 0)  {  UNRESOLVED(ret, "Mutex init failed");  }
			
			ret = pthread_cond_init(&cnd, &ca);
			if (ret != 0)  {  UNRESOLVED(ret, "Cond init failed");  }
			
			predicate = 0;
			tnum = 0;
			
			/* Create the waiter threads */
			for (i=0; i< nthreads; i++)
			{
				ret = pthread_create(&th[i], &ta, waiter, (void *)&td);
				if (ret != 0) /* We reached the limits */
				{
					#if VERBOSE > 1
					output("Limit reached with %i threads\n", i);
					#endif
					#ifdef USE_CANCEL
					for (j = i-1; j>=0; j--)
					{
						ret = pthread_cancel(th[j]);
						if (ret != 0)  {  UNRESOLVED(ret, "Unable to cancel a thread");  }
					}
					#else
					predicate = 1;
					ret = pthread_cond_broadcast(&cnd);
					if (ret != 0)  {  UNRESOLVED(ret, "Unable to broadcast the condition");  }
					#endif
					for (j = i-1; j>=0; j--)
					{
						ret = pthread_join(th[j], NULL);
						if (ret != 0)  {  UNRESOLVED(ret, "Unable to join a canceled thread");  }
					}
					free(th);
					return -1;
				}
			}
			/* All waiter threads are created now */
			#if VERBOSE > 5
			output("%i waiter threads created successfully\n", i);
			#endif
			
			ret = pthread_mutex_lock(&mtx);
			if (ret != 0)  {  UNRESOLVED(ret, "Unable to lock mutex");  }
			
			/* Wait for all threads be waiting */
			while (tnum < nthreads)
			{
				ret = pthread_mutex_unlock(&mtx);
				if (ret != 0)  {  UNRESOLVED(ret, "Mutex unlock failed");  }
				
				sched_yield();
				
				ret = pthread_mutex_lock(&mtx);
				if (ret != 0)  {  UNRESOLVED(ret, "Mutex lock failed");  }
			}
			
			/* All threads are now waiting - we do the measure */
			
			ret = pthread_mutex_unlock(&mtx);
			if (ret != 0)  {  UNRESOLVED(ret, "Mutex unlock failed");  }
	
			#if VERBOSE > 5
			output("%i waiter threads are waiting; start measure\n", tnum);
			#endif
			
			do_measure(&mtx, &cnd, test_scenar[s].cid, &ts);
			
			#if VERBOSE > 5
			output("Measure for %s returned %d.%09d\n", test_scenar[s].desc, ts.tv_sec, ts.tv_nsec);
			#endif
			
			ts_cumul.tv_sec += ts.tv_sec;
			ts_cumul.tv_nsec += ts.tv_nsec;
			if (ts_cumul.tv_nsec >= 1000000000)
			{
				ts_cumul.tv_nsec -= 1000000000;
				ts_cumul.tv_sec += 1;
			}
			
			/* We can release the threads */
			predicate = 1;
			ret = pthread_cond_broadcast(&cnd);
			if (ret != 0)  {  UNRESOLVED(ret, "Unable to broadcast the condition");  }
			
			#if VERBOSE > 5
			output("Joining the waiters...\n");
			#endif
	
			/* We will join every threads */
			for (i=0; i<nthreads; i++)
			{
				ret = pthread_join(th[i], NULL);
				if (ret != 0)  {  UNRESOLVED(ret, "Unable to join a thread");  }
				
			}
			
			
			/* Destroy everything */
			ret = pthread_mutex_destroy(&mtx);
			if (ret != 0)  {  UNRESOLVED(ret, "Unable to destroy mutex");  }
			
			ret = pthread_cond_destroy(&cnd);
			if (ret != 0)  {  UNRESOLVED(ret, "Unable to destroy cond");  }
		}
		
		#ifdef PLOT_OUTPUT
		output(" %d.%09d", ts_cumul.tv_sec, ts_cumul.tv_nsec);
		#endif
		
		measure->_data[s] = ts_cumul.tv_sec * 1000000 + (ts_cumul.tv_nsec / 1000) ; /* We reduce precision */
		
		/* Destroy the mutex attributes */
		ret = pthread_mutexattr_destroy(&ma);
		if (ret != 0)  {  UNRESOLVED(ret, "Unable to destroy mutex attribute");  }
		
		ret = pthread_condattr_destroy(&ca);
		if (ret != 0)  {  UNRESOLVED(ret, "Unable to destroy cond attribute");  }
	}
	
	/* Free the memory */
	free(th);
	
	#if VERBOSE > 2
	output("%5d threads; %d.%09d s (%i loops)\n", nthreads, ts_cumul.tv_sec, ts_cumul.tv_nsec, scal);
	#endif
	
	#ifdef PLOT_OUTPUT
	output("\n");
	#endif	
	
	return ts_cumul.tv_sec * 1000000000 + ts_cumul.tv_nsec;
}

/* Forward declaration */
int parse_measure(mes_t * measures);

/* Main 
 */
int main (int argc, char *argv[])
{
	int ret, nth;
	long dur;
	
	
	
	/* Initialize the measure list */
	mes_t sentinel;
	mes_t *m_cur, *m_tmp;
	m_cur = &sentinel;
	m_cur->next = NULL;
	
	/* Initialize the output */
	output_init();
	
	/* Test machine capabilities */
	/* -> clockid_t; pshared; ... */
	altclk_ok = sysconf(_SC_CLOCK_SELECTION);
	if (altclk_ok > 0)
		altclk_ok = sysconf(_SC_MONOTONIC_CLOCK);
	#ifndef USE_ALTCLK
	if (altclk_ok > 0)
		output("Implementation supports the MONOTONIC CLOCK but option is disabled in test.\n");
	#endif

	pshared_ok = sysconf(_SC_THREAD_PROCESS_SHARED);
	
	#if VERBOSE > 0
	output("Test starting\n");
	output(" Process-shared primitive %s be tested\n", (pshared_ok>0)?"will":"won't");
	output(" Alternative clock for cond %s be tested\n", (altclk_ok>0)?"will":"won't");
	#endif

	/* Prepare thread attribute */
	ret = pthread_attr_init(&ta);
	if (ret != 0)  {  UNRESOLVED(ret, "Unable to initialize thread attributes");  }
	
	ret = pthread_attr_setstacksize(&ta, sysconf(_SC_THREAD_STACK_MIN));
	if (ret != 0)
	{  UNRESOLVED(ret, "Unable to set stack size to minimum value");  }
	
	#ifdef PLOT_OUTPUT
	output("# COLUMNS %d #threads", NSCENAR + 1);
	for (nth=0; nth<NSCENAR; nth++) 
		output(" %s", test_scenar[nth].desc);
	output("\n");
	#endif	
	
	/* Do the testing */
	nth = 0;
	do 
	{
		nth += 100 * SCALABILITY_FACTOR;
		
		/* Create a new measure item */
		m_tmp = (mes_t *) malloc(sizeof(mes_t));
		if (m_tmp == NULL) { UNRESOLVED(errno, "Unable to alloc memory for measure saving");  }
		m_tmp->nthreads = nth;
		m_tmp->next = NULL;
		
		/* Run the test */
		dur = do_threads_test(nth, m_tmp);
		
		/* If test was success, add this measure to the list. Otherwise, free the mem */
		if (dur >= 0)
		{
			m_cur->next = m_tmp;
			m_cur = m_tmp;
		}
		else
		{
			free(m_tmp);
		}
	}
	while (dur >= 0);
	
	
	/* We will now parse the results to determine if the measure is ~ constant or is growing. */
	
	ret = parse_measure(&sentinel);
	
	/* Free the memory from the list */
	m_cur = sentinel.next;
	while (m_cur != NULL)
	{
		m_tmp = m_cur;
		m_cur = m_tmp->next;
		free(m_tmp);
	}
	
	if (ret != 0)
	{
		FAILED("This function is not scalable");
	}
	
	#if VERBOSE > 0
	output("The function is scalable\n");
	#endif
	
	PASSED;
}



/***
 * The next function will seek for the better model for each series of measurements.
 *
 * The tested models are: -- X = # threads; Y = latency
 * -> Y = a;      -- Error is r1 = avg( (Y - Yavg)² );
 * -> Y = aX + b; -- Error is r2 = avg( (Y -aX -b)² );
 *                -- where a = avg ( (X - Xavg)(Y - Yavg) ) / avg( ( X - Xavg)² )
 *                --         Note: We will call _q = sum( (X - Xavg) * (Y - Yavg) ); 
 *                --                       and  _d = sum( (X - Xavg)² );
 *                -- and   b = Yavg - a * Xavg
 * -> Y = c * X^a;-- Same as previous, but with log(Y) = a log(X) + b; and b = log(c). Error is r3
 * -> Y = exp(aX + b); -- log(Y) = aX + b. Error is r4
 *
 * We compute each error factor (r1, r2, r3, r4) then search which is the smallest (with ponderation).
 * The function returns 0 when r1 is the best for all cases (latency is constant) and !0 otherwise.
 */

struct row
{
	long X;  /* the X values -- copied from function argument */
	long Y[NSCENAR];  /* the Y values -- copied from function argument */
	long _x; /* Value X - Xavg */
	long _y[NSCENAR]; /* Value Y - Yavg */
	double LnX; /* Natural logarithm of X values */
	double LnY[NSCENAR]; /* Natural logarithm of Y values */
	double _lnx; /* Value LnX - LnXavg */
	double _lny[NSCENAR]; /* Value LnY - LnYavg */
};

int parse_measure(mes_t * measures)
{
	int ret, i, r;
	
	mes_t *cur;
	
	double Xavg, Yavg[NSCENAR];
	double LnXavg, LnYavg[NSCENAR];
	
	int N; 
	
	double r1[NSCENAR], r2[NSCENAR], r3[NSCENAR], r4[NSCENAR];
	
	/* Some more intermediate vars */
	long double _q[3][NSCENAR];
	long double _d[3][NSCENAR];
	
	long double t; /* temp value */
	
	struct row *Table = NULL;
	
	/* Initialize the datas */
	Xavg = 0.0;
	LnXavg = 0.0;
	for (i=0; i<NSCENAR; i++)
	{
		Yavg[i] = 0.0;
		LnYavg[i] = 0.0;
		r1[i] = 0.0;
		r2[i] = 0.0;
		r3[i] = 0.0;
		r4[i] = 0.0;
		_q[0][i]=0.0;
		_q[1][i]=0.0;
		_q[2][i]=0.0;
		_d[0][i]=0.0;
		_d[1][i]=0.0;
		_d[2][i]=0.0;
	}
	N=0;
	cur = measures;
	
	#if VERBOSE > 1
	output("Data analysis starting\n");
	#endif
	
	/* We start with reading the list to find:
	 * -> number of elements, to assign an array
	 * -> average values 
	 */
	while (cur->next != NULL)
	{
		cur = cur->next;
		
		N++;
		
		Xavg += (double) cur->nthreads;
		LnXavg += log( (double) cur->nthreads);
		
		for (i=0; i<NSCENAR; i++)
		{
			Yavg[i] += (double) cur->_data[i];
			LnYavg[i] += log( (double) cur->_data[i] );
		}
	}
	
	/* We have the sum; we can divide to obtain the average values */
	Xavg /= N;
	LnXavg /= N;
	
	for (i=0; i<NSCENAR; i++)
	{
		Yavg[i] /= N;
		LnYavg[i] /= N;
	}
	
	#if VERBOSE > 1
	output(" Found %d rows and %d columns\n", N, NSCENAR);
	#endif
	
	
	/* We will now alloc the array ... */
	Table = calloc(N, sizeof( struct row ));
	if (Table == NULL)  {  UNRESOLVED(errno, "Unable to alloc space for results parsing");  }
	
	/* ... and fill it */
	N = 0;
	cur = measures;
	
	while (cur->next != NULL)
	{
		cur = cur->next;
		
		Table[N].X = (long) cur->nthreads;
		Table[N]._x = Table[N].X - Xavg ;
		Table[N].LnX = log((double) cur->nthreads);
		Table[N]._lnx = Table[N].LnX - LnXavg;
		for (i=0; i<NSCENAR; i++)
		{
			Table[N].Y[i] = cur->_data[i];
			Table[N]._y[i] = Table[N].Y[i] - Yavg[i] ;
			Table[N].LnY[i] = log((double) cur->_data[i]);
			Table[N]._lny[i] = Table[N].LnY[i] - LnYavg[i];
		}
		
		N++;
	}
	
	/* We won't need the list anymore -- we'll work with the array which should be faster. */
	#if VERBOSE > 1
	output(" Data was stored in an array.\n");
	#endif
	
	/* We need to read the full array at least twice to compute all the error factors */
	
	/* In the first pass, we'll compute:
	 * -> r1 for each scenar.
	 * -> "a" factor for linear (0), power (1) and exponential (2) approximations -- with using the _d and _q vars.
	 */
	#if VERBOSE > 1
	output("Starting first pass...\n");
	#endif
	for (r=0; r<N; r++)
	{
		for (i=0; i<NSCENAR; i++)
		{
			r1[i] += ((double)Table[r]._y[i] / N) * (double)Table[r]._y[i];
			
			_q[0][i] += Table[r]._y[i] * Table[r]._x;
			_d[0][i] += Table[r]._x * Table[r]._x;
			
			_q[1][i] += Table[r]._lny[i] * Table[r]._lnx;
			_d[1][i] += Table[r]._lnx * Table[r]._lnx;
			
			_q[2][i] += Table[r]._lny[i] * Table[r]._x;
			_d[2][i] += Table[r]._x * Table[r]._x;
		}
	}
	
	/* First pass is terminated; a2 = _q[0]/_d[0]; a3 = _q[1]/_d[1]; a4 = _q[2]/_d[2] */

	/* In the first pass, we'll compute:
	 * -> r2, r3, r4 for each scenar.
	 */
	
	#if VERBOSE > 1
	output("Starting second pass...\n");
	#endif
	for (r=0; r<N; r++)
	{
		for (i=0; i<NSCENAR; i++)
		{
			/* r2 = avg((y - ax -b)²);  t = (y - ax - b) = (y - yavg) - a (x - xavg); */
			t = ( Table[r]._y[i] - ((_q[0][i] * Table[r]._x) / _d[0][i]));
			r2[i] += t * t / N  ;
			
			/* r3 = avg(( y - c.x^a) ²);  
			    t = y - c * x ^ a 
			      = y - log (LnYavg - (_q[1]/_d[1]) * LnXavg) * x ^ (_q[1]/_d[1])
			*/
			t = (   Table[r].Y[i]
			      - ( logl ( LnYavg[i] - (_q[1][i] / _d[1][i] ) * LnXavg)
			        * powl( Table[r].X,  (_q[1][i] / _d[1][i] ))
			    )   );
			r3[i] += t * t / N  ;
			
			/* r4 = avg(( y - exp(ax+b))²);
			    t = y - exp(ax+b)
			      = y - exp( _q[2]/_d[2] * x + ( LnYavg - (_q[2]/_d[2] * Xavg) ));
			      = y - exp( _q[2]/_d[2] * (x - Xavg) + LnYavg );
			*/
			t = (   Table[r].Y[i]
			      - expl( ( _q[2][i] / _d[2][i] ) * Table[r]._x + LnYavg[i] ) );
			r4[i] += t * t / N  ;
			
		}
	}

	#if VERBOSE > 1
	output("All computing terminated.\n");
	#endif
	ret = 0;
	for (i=0; i<NSCENAR; i++)
	{
		#if VERBOSE > 1
		output("\nScenario: %s\n", test_scenar[i].desc);
		
		output("  Model: Y = k\n");
		output("       k = %g\n", Yavg[i]);
		output("    Divergence %g\n", r1[i]);
		
		output("  Model: Y = a * X + b\n");
		output("       a = %Lg\n", _q[0][i] / _d[0][i]);
		output("       b = %Lg\n", Yavg[i] - ((_q[0][i] / _d[0][i]) * Xavg));
		output("    Divergence %g\n", r2[i]);
		
		output("  Model: Y = c * X ^ a\n");
		output("       a = %Lg\n", _q[1][i] / _d[1][i]);
		output("       c = %Lg\n", logl ( LnYavg[i] - (_q[1][i] / _d[1][i] ) * LnXavg));
		output("    Divergence %g\n", r2[i]);
		
		output("  Model: Y = exp(a * X + b)\n");
		output("       a = %Lg\n", _q[2][i] / _d[2][i]);
		output("       b = %Lg\n", LnYavg[i] - ((_q[2][i] / _d[2][i]) * Xavg));
		output("    Divergence %g\n", r2[i]);
		#endif
		/* Compare r1 to other values, with some ponderations */
		if ((r1[i] > 1.1 * r2[i]) || (r1[i] > 1.2 * r3[i]) || (r1[i] > 1.3 * r4[i]))
			ret++;
		#if VERBOSE > 1
		else
			output(" Sanction: OK\n");
		#endif
	}
	
	/* We need to free the array */
	free(Table);
	
	/* We're done */
	return ret;
}

