1/*
2* Copyright (c) 2005, Bull S.A..  All rights reserved.
3* Created by: Sebastien Decugis
4
5* This program is free software; you can redistribute it and/or modify it
6* under the terms of version 2 of the GNU General Public License as
7* published by the Free Software Foundation.
8*
9* This program is distributed in the hope that it would be useful, but
10* WITHOUT ANY WARRANTY; without even the implied warranty of
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12*
13* You should have received a copy of the GNU General Public License along
14* with this program; if not, write the Free Software Foundation, Inc., 59
15* Temple Place - Suite 330, Boston MA 02111-1307, USA.
16
17
18* This scalability sample aims to test the following assertion:
19*  -> The sem_init() duration does not depend on the # of semaphores
20*     in the system
21
22* The steps are:
23* -> Init semaphores until failure
24
25* The test fails if the sem_init duration tends to grow with the # of semaphores,
26* or if the failure at last semaphore creation is unexpected.
27*/
28
29
30/* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
31#define _POSIX_C_SOURCE 200112L
32
33/********************************************************************************************/
34/****************************** standard includes *****************************************/
35/********************************************************************************************/
36#include <pthread.h>
37#include <stdarg.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42
43#include <math.h>
44#include <errno.h>
45#include <time.h>
46#include <semaphore.h>
47
48/********************************************************************************************/
49/******************************   Test framework   *****************************************/
50/********************************************************************************************/
51#include "testfrmw.h"
52#include "testfrmw.c"
53/* This header is responsible for defining the following macros:
54 * UNRESOLVED(ret, descr);
55 *    where descr is a description of the error and ret is an int (error code for example)
56 * FAILED(descr);
57 *    where descr is a short text saying why the test has failed.
58 * PASSED();
59 *    No parameter.
60 *
61 * Both three macros shall terminate the calling process.
62 * The testcase shall not terminate in any other maneer.
63 *
64 * The other file defines the functions
65 * void output_init()
66 * void output(char * string, ...)
67 *
68 * Those may be used to output information.
69 */
70
71/********************************************************************************************/
72/********************************** Configuration ******************************************/
73/********************************************************************************************/
74#ifndef SCALABILITY_FACTOR
75#define SCALABILITY_FACTOR 1
76#endif
77#ifndef VERBOSE
78#define VERBOSE 1
79#endif
80
81#define BLOCKSIZE (1000 * SCALABILITY_FACTOR)
82
83#define NSEM_LIMIT ( 1000 * BLOCKSIZE )
84
85#ifdef PLOT_OUTPUT
86#undef VERBOSE
87#define VERBOSE 0
88#endif
89
90/********************************************************************************************/
91/***********************************       Test     *****************************************/
92/********************************************************************************************/
93
94/* The next structure is used to save the tests measures */
95
96typedef struct __mes_t
97{
98	int nsem;
99	long _data_open; /* As we store ��sec values, a long type should be enough. */
100	long _data_close; /* As we store ��sec values, a long type should be enough. */
101
102	struct __mes_t *next;
103
104	struct __mes_t *prev;
105}
106
107mes_t;
108
109/* Forward declaration */
110int parse_measure( mes_t * measures );
111
112
113/* Structure to store created semaphores */
114
115typedef struct __test_t
116{
117	sem_t sems[ BLOCKSIZE ];
118
119	struct __test_t * next;
120
121	struct __test_t * prev;
122}
123
124test_t;
125
126
127/* Test routine */
128int main ( int argc, char *argv[] )
129{
130	int ret, status, locerrno;
131	int nsem, i;
132
133	struct timespec ts_ref, ts_fin;
134	mes_t sentinel;
135	mes_t *m_cur, *m_tmp;
136
137	test_t sems;
138
139	struct __test_t * sems_cur = &sems, * sems_tmp;
140
141	long SEM_MAX = sysconf( _SC_SEM_NSEMS_MAX );
142
143	/* Initialize the measure list */
144	m_cur = &sentinel;
145	m_cur->next = NULL;
146	m_cur->prev = NULL;
147
148	/* Initialize output routine */
149	output_init();
150
151	/* Initialize sems */
152	sems_cur->next = NULL;
153	sems_cur->prev = NULL;
154
155
156#if VERBOSE > 1
157	output( "SEM_NSEMS_MAX: %ld\n", SEM_MAX );
158
159#endif
160
161#ifdef PLOT_OUTPUT
162	output( "# COLUMNS 3 Semaphores sem_init sem_destroy\n" );
163
164#endif
165
166	nsem = 0;
167	status = 0;
168
169	while ( 1 )                                                                                                                                 /* we will break */
170	{
171		/* Create a new block */
172		sems_tmp = ( test_t * ) malloc( sizeof( test_t ) );
173
174		if ( sems_tmp == NULL )
175		{
176			/* We stop here */
177#if VERBOSE > 0
178			output( "malloc failed with error %d (%s)\n", errno, strerror( errno ) );
179#endif
180			/* We can proceed anyway */
181			status = 1;
182
183			break;
184		}
185
186
187
188		/* read clock */
189		ret = clock_gettime( CLOCK_REALTIME, &ts_ref );
190
191		if ( ret != 0 )
192		{
193			UNRESOLVED( errno, "Unable to read clock" );
194		}
195
196		/* Open all semaphores in the current block */
197		for ( i = 0; i < BLOCKSIZE; i++ )
198		{
199			ret = sem_init( &( sems_tmp->sems[ i ] ), i & 1, i & 3 );
200
201			if ( ret != 0 )
202			{
203#if VERBOSE > 0
204				output( "sem_init failed with error %d (%s)\n", errno, strerror( errno ) );
205#endif
206				/* Check error code */
207
208				if ( ( errno == EMFILE ) || ( errno == ENFILE ) || ( errno == ENOSPC ) || ( errno == ENOMEM ) )
209				{
210					status = 2;
211				}
212				else
213				{
214					UNRESOLVED( errno, "Unexpected error!" );
215				}
216
217				break;
218			}
219
220			if ( ( SEM_MAX > 0 ) && ( nsem > SEM_MAX ) )
221			{
222				/* Erreur */
223				FAILED( "sem_open opened more than SEM_NSEMS_MAX semaphores" );
224			}
225
226			nsem++;
227		}
228
229		/* read clock */
230		ret = clock_gettime( CLOCK_REALTIME, &ts_fin );
231
232		if ( ret != 0 )
233		{
234			UNRESOLVED( errno, "Unable to read clock" );
235		}
236
237		if ( status == 2 )
238		{
239			/* We were not able to fill this bloc, so we can discard it */
240
241			for ( --i; i >= 0; i-- )
242			{
243				ret = sem_destroy( &( sems_tmp->sems[ i ] ) );
244
245				if ( ret != 0 )
246				{
247					UNRESOLVED( errno, "Failed to close" );
248				}
249
250			}
251
252			free( sems_tmp );
253			break;
254
255
256		}
257
258		sems_tmp->prev = sems_cur;
259		sems_cur->next = sems_tmp;
260		sems_cur = sems_tmp;
261		sems_cur->next = NULL;
262
263		/* add to the measure list */
264		m_tmp = ( mes_t * ) malloc( sizeof( mes_t ) );
265
266		if ( m_tmp == NULL )
267		{
268			/* We stop here */
269#if VERBOSE > 0
270			output( "malloc failed with error %d (%s)\n", errno, strerror( errno ) );
271#endif
272			/* We can proceed anyway */
273			status = 3;
274
275			break;
276		}
277
278		m_tmp->nsem = nsem;
279		m_tmp->next = NULL;
280		m_tmp->prev = m_cur;
281		m_cur->next = m_tmp;
282
283		m_cur = m_tmp;
284
285		m_cur->_data_open = ( ( ts_fin.tv_sec - ts_ref.tv_sec ) * 1000000 ) + ( ( ts_fin.tv_nsec - ts_ref.tv_nsec ) / 1000 ) ;
286		m_cur->_data_close = 0 ;
287
288		if ( nsem >= NSEM_LIMIT )
289			break;
290	}
291
292	locerrno = errno;
293
294	/* Free all semaphore blocs */
295#if VERBOSE > 0
296	output( "Detroy and free semaphores\n" );
297
298#endif
299
300	/* Reverse list order */
301
302	while ( sems_cur != &sems )
303	{
304		/* read clock */
305		ret = clock_gettime( CLOCK_REALTIME, &ts_ref );
306
307		if ( ret != 0 )
308		{
309			UNRESOLVED( errno, "Unable to read clock" );
310		}
311
312		/* Empty the sems_cur block */
313
314		for ( i = 0; i < BLOCKSIZE; i++ )
315		{
316			ret = sem_destroy( &( sems_cur->sems[ i ] ) );
317
318			if ( ret != 0 )
319			{
320				UNRESOLVED( errno, "Failed to destroy a semaphore" );
321			}
322		}
323
324		/* read clock */
325		ret = clock_gettime( CLOCK_REALTIME, &ts_fin );
326
327		if ( ret != 0 )
328		{
329			UNRESOLVED( errno, "Unable to read clock" );
330		}
331
332		/* add this measure to measure list */
333
334		m_cur->_data_close = ( ( ts_fin.tv_sec - ts_ref.tv_sec ) * 1000000 ) + ( ( ts_fin.tv_nsec - ts_ref.tv_nsec ) / 1000 ) ;
335
336		m_cur = m_cur->prev;
337
338		/* remove the sem bloc */
339		sems_cur = sems_cur->prev;
340
341		free( sems_cur->next );
342
343		sems_cur->next = NULL;
344	}
345
346
347#if VERBOSE > 0
348	output( "Parse results\n" );
349
350#endif
351
352	/* Compute the results */
353	ret = parse_measure( &sentinel );
354
355
356	/* Free the resources and output the results */
357
358#if VERBOSE > 5
359	output( "Dump : \n" );
360
361	output( "  nsem  |  open  |  close \n" );
362
363#endif
364
365	while ( sentinel.next != NULL )
366	{
367		m_cur = sentinel.next;
368#if (VERBOSE > 5) || defined(PLOT_OUTPUT)
369		output( "%8.8i %1.1li.%6.6li %1.1li.%6.6li\n"
370		        , m_cur->nsem
371		        , m_cur->_data_open / 1000000 , m_cur->_data_open % 1000000
372		        , m_cur->_data_close / 1000000, m_cur->_data_close % 1000000
373		      );
374
375#endif
376		sentinel.next = m_cur->next;
377
378		free( m_cur );
379	}
380
381
382	if ( ret != 0 )
383	{
384		FAILED( "The function is not scalable, add verbosity for more information" );
385	}
386
387	/* Check status */
388	if ( status )
389	{
390		UNRESOLVED( locerrno, "Function is scalable, but test terminated with error" );
391	}
392
393
394#if VERBOSE > 0
395	output( "-----\n" );
396
397	output( "All test data destroyed\n" );
398
399	output( "Test PASSED\n" );
400
401#endif
402
403	PASSED;
404}
405
406
407
408
409
410/***
411 * The next function will seek for the better model for each series of measurements.
412 *
413 * The tested models are: -- X = # threads; Y = latency
414 * -> Y = a;      -- Error is r1 = avg( (Y - Yavg)�� );
415 * -> Y = aX + b; -- Error is r2 = avg( (Y -aX -b)�� );
416 *                -- where a = avg ( (X - Xavg)(Y - Yavg) ) / avg( ( X - Xavg)�� )
417 *                --         Note: We will call _q = sum( (X - Xavg) * (Y - Yavg) );
418 *                --                       and  _d = sum( (X - Xavg)�� );
419 *                -- and   b = Yavg - a * Xavg
420 * -> Y = c * X^a;-- Same as previous, but with log(Y) = a log(X) + b; and b = log(c). Error is r3
421 * -> Y = exp(aX + b); -- log(Y) = aX + b. Error is r4
422 *
423 * We compute each error factor (r1, r2, r3, r4) then search which is the smallest (with ponderation).
424 * The function returns 0 when r1 is the best for all cases (latency is constant) and !0 otherwise.
425 */
426
427struct row
428{
429	long X;  /* the X values -- copied from function argument */
430	long Y_o;  /* the Y values -- copied from function argument */
431	long Y_c;  /* the Y values -- copied from function argument */
432	long _x; /* Value X - Xavg */
433	long _y_o; /* Value Y - Yavg */
434	long _y_c; /* Value Y - Yavg */
435	double LnX; /* Natural logarithm of X values */
436	double LnY_o; /* Natural logarithm of Y values */
437	double LnY_c; /* Natural logarithm of Y values */
438	double _lnx; /* Value LnX - LnXavg */
439	double _lny_o; /* Value LnY - LnYavg */
440	double _lny_c; /* Value LnY - LnYavg */
441};
442
443int parse_measure( mes_t * measures )
444{
445	int ret, r;
446
447	mes_t *cur;
448
449	double Xavg, Yavg_o, Yavg_c;
450	double LnXavg, LnYavg_o, LnYavg_c;
451
452	int N;
453
454	double r1_o, r2_o, r3_o, r4_o;
455	double r1_c, r2_c, r3_c, r4_c;
456
457	/* Some more intermediate vars */
458	long double _q_o[ 3 ];
459	long double _d_o[ 3 ];
460	long double _q_c[ 3 ];
461	long double _d_c[ 3 ];
462
463	long double t; /* temp value */
464
465	struct row *Table = NULL;
466
467	/* This array contains the last element of each serie */
468	int array_max;
469
470	/* Initialize the datas */
471
472	array_max = -1; /* means no data */
473	Xavg = 0.0;
474	LnXavg = 0.0;
475	Yavg_o = 0.0;
476	LnYavg_o = 0.0;
477	r1_o = 0.0;
478	r2_o = 0.0;
479	r3_o = 0.0;
480	r4_o = 0.0;
481	_q_o[ 0 ] = 0.0;
482	_q_o[ 1 ] = 0.0;
483	_q_o[ 2 ] = 0.0;
484	_d_o[ 0 ] = 0.0;
485	_d_o[ 1 ] = 0.0;
486	_d_o[ 2 ] = 0.0;
487	Yavg_c = 0.0;
488	LnYavg_c = 0.0;
489	r1_c = 0.0;
490	r2_c = 0.0;
491	r3_c = 0.0;
492	r4_c = 0.0;
493	_q_c[ 0 ] = 0.0;
494	_q_c[ 1 ] = 0.0;
495	_q_c[ 2 ] = 0.0;
496	_d_c[ 0 ] = 0.0;
497	_d_c[ 1 ] = 0.0;
498	_d_c[ 2 ] = 0.0;
499
500	N = 0;
501	cur = measures;
502
503#if VERBOSE > 1
504	output( "Data analysis starting\n" );
505#endif
506
507	/* We start with reading the list to find:
508	 * -> number of elements, to assign an array.
509	 * -> average values
510	 */
511
512	while ( cur->next != NULL )
513	{
514		cur = cur->next;
515
516		N++;
517
518		if ( cur->_data_open != 0 )
519		{
520			array_max = N;
521			Xavg += ( double ) cur->nsem;
522			LnXavg += log( ( double ) cur->nsem );
523			Yavg_o += ( double ) cur->_data_open;
524			LnYavg_o += log( ( double ) cur->_data_open );
525			Yavg_c += ( double ) cur->_data_close;
526			LnYavg_c += log( ( double ) cur->_data_close );
527		}
528
529	}
530
531	/* We have the sum; we can divide to obtain the average values */
532	if ( array_max != -1 )
533	{
534		Xavg /= array_max;
535		LnXavg /= array_max;
536		Yavg_o /= array_max;
537		LnYavg_o /= array_max;
538		Yavg_c /= array_max;
539		LnYavg_c /= array_max;
540	}
541
542#if VERBOSE > 1
543	output( " Found %d rows\n", N );
544
545#endif
546
547
548	/* We will now alloc the array ... */
549
550	Table = calloc( N, sizeof( struct row ) );
551
552	if ( Table == NULL )
553	{
554		UNRESOLVED( errno, "Unable to alloc space for results parsing" );
555	}
556
557	/* ... and fill it */
558	N = 0;
559
560	cur = measures;
561
562	while ( cur->next != NULL )
563	{
564		cur = cur->next;
565
566		Table[ N ].X = ( long ) cur->nsem;
567		Table[ N ].LnX = log( ( double ) cur->nsem );
568
569		if ( array_max > N )
570		{
571			Table[ N ]._x = Table[ N ].X - Xavg ;
572			Table[ N ]._lnx = Table[ N ].LnX - LnXavg;
573			Table[ N ].Y_o = cur->_data_open;
574			Table[ N ]._y_o = Table[ N ].Y_o - Yavg_o ;
575			Table[ N ].LnY_o = log( ( double ) cur->_data_open );
576			Table[ N ]._lny_o = Table[ N ].LnY_o - LnYavg_o;
577			Table[ N ].Y_c = cur->_data_close;
578			Table[ N ]._y_c = Table[ N ].Y_c - Yavg_c ;
579			Table[ N ].LnY_c = log( ( double ) cur->_data_close );
580			Table[ N ]._lny_c = Table[ N ].LnY_c - LnYavg_c;
581		}
582
583		N++;
584	}
585
586	/* We won't need the list anymore -- we'll work with the array which should be faster. */
587#if VERBOSE > 1
588	output( " Data was stored in an array.\n" );
589
590#endif
591
592	/* We need to read the full array at least twice to compute all the error factors */
593
594	/* In the first pass, we'll compute:
595	 * -> r1 for each scenar.
596	 * -> "a" factor for linear (0), power (1) and exponential (2) approximations -- with using the _d and _q vars.
597	 */
598#if VERBOSE > 1
599	output( "Starting first pass...\n" );
600
601#endif
602	for ( r = 0; r < array_max; r++ )
603	{
604		r1_o += ( ( double ) Table[ r ]._y_o / array_max ) * ( double ) Table[ r ]._y_o;
605
606		_q_o[ 0 ] += Table[ r ]._y_o * Table[ r ]._x;
607		_d_o[ 0 ] += Table[ r ]._x * Table[ r ]._x;
608
609		_q_o[ 1 ] += Table[ r ]._lny_o * Table[ r ]._lnx;
610		_d_o[ 1 ] += Table[ r ]._lnx * Table[ r ]._lnx;
611
612		_q_o[ 2 ] += Table[ r ]._lny_o * Table[ r ]._x;
613		_d_o[ 2 ] += Table[ r ]._x * Table[ r ]._x;
614
615
616		r1_c += ( ( double ) Table[ r ]._y_c / array_max ) * ( double ) Table[ r ]._y_c;
617
618		_q_c[ 0 ] += Table[ r ]._y_c * Table[ r ]._x;
619		_d_c[ 0 ] += Table[ r ]._x * Table[ r ]._x;
620
621		_q_c[ 1 ] += Table[ r ]._lny_c * Table[ r ]._lnx;
622		_d_c[ 1 ] += Table[ r ]._lnx * Table[ r ]._lnx;
623
624		_q_c[ 2 ] += Table[ r ]._lny_c * Table[ r ]._x;
625		_d_c[ 2 ] += Table[ r ]._x * Table[ r ]._x;
626
627	}
628
629	/* First pass is terminated; a2 = _q[0]/_d[0]; a3 = _q[1]/_d[1]; a4 = _q[2]/_d[2] */
630
631	/* In the first pass, we'll compute:
632	 * -> r2, r3, r4 for each scenar.
633	 */
634
635#if VERBOSE > 1
636	output( "Starting second pass...\n" );
637
638#endif
639	for ( r = 0; r < array_max; r++ )
640	{
641		/* r2 = avg((y - ax -b)��);  t = (y - ax - b) = (y - yavg) - a (x - xavg); */
642		t = ( Table[ r ]._y_o - ( ( _q_o[ 0 ] * Table[ r ]._x ) / _d_o[ 0 ] ) );
643		r2_o += t * t / array_max ;
644
645		t = ( Table[ r ]._y_c - ( ( _q_c[ 0 ] * Table[ r ]._x ) / _d_c[ 0 ] ) );
646		r2_c += t * t / array_max ;
647
648		/* r3 = avg(( y - c.x^a) ��);
649		    t = y - c * x ^ a
650		      = y - log (LnYavg - (_q[1]/_d[1]) * LnXavg) * x ^ (_q[1]/_d[1])
651		*/
652		t = ( Table[ r ].Y_o
653		      - ( logl ( LnYavg_o - ( _q_o[ 1 ] / _d_o[ 1 ] ) * LnXavg )
654		          * powl( Table[ r ].X, ( _q_o[ 1 ] / _d_o[ 1 ] ) )
655		        ) );
656		r3_o += t * t / array_max ;
657
658		t = ( Table[ r ].Y_c
659		      - ( logl ( LnYavg_c - ( _q_c[ 1 ] / _d_c[ 1 ] ) * LnXavg )
660		          * powl( Table[ r ].X, ( _q_c[ 1 ] / _d_c[ 1 ] ) )
661		        ) );
662		r3_c += t * t / array_max ;
663
664		/* r4 = avg(( y - exp(ax+b))��);
665		    t = y - exp(ax+b)
666		      = y - exp( _q[2]/_d[2] * x + ( LnYavg - (_q[2]/_d[2] * Xavg) ));
667		      = y - exp( _q[2]/_d[2] * (x - Xavg) + LnYavg );
668		*/
669		t = ( Table[ r ].Y_o
670		      - expl( ( _q_o[ 2 ] / _d_o[ 2 ] ) * Table[ r ]._x + LnYavg_o ) );
671		r4_o += t * t / array_max ;
672
673		t = ( Table[ r ].Y_c
674		      - expl( ( _q_c[ 2 ] / _d_c[ 2 ] ) * Table[ r ]._x + LnYavg_c ) );
675		r4_c += t * t / array_max ;
676
677	}
678
679#if VERBOSE > 1
680	output( "All computing terminated.\n" );
681
682#endif
683	ret = 0;
684
685#if VERBOSE > 1
686	output( " # of data: %i\n", array_max );
687
688	output( "  Model: Y = k\n" );
689
690	output( "   sem_open:\n" );
691
692	output( "       k = %g\n", Yavg_o );
693
694	output( "    Divergence %g\n", r1_o );
695
696	output( "   sem_close:\n" );
697
698	output( "       k = %g\n", Yavg_c );
699
700	output( "    Divergence %g\n", r1_c );
701
702	output( "  Model: Y = a * X + b\n" );
703
704	output( "   sem_open:\n" );
705
706	output( "       a = %Lg\n", _q_o[ 0 ] / _d_o[ 0 ] );
707
708	output( "       b = %Lg\n", Yavg_o - ( ( _q_o[ 0 ] / _d_o[ 0 ] ) * Xavg ) );
709
710	output( "    Divergence %g\n", r2_o );
711
712	output( "   sem_close:\n" );
713
714	output( "       a = %Lg\n", _q_c[ 0 ] / _d_c[ 0 ] );
715
716	output( "       b = %Lg\n", Yavg_c - ( ( _q_c[ 0 ] / _d_c[ 0 ] ) * Xavg ) );
717
718	output( "    Divergence %g\n", r2_c );
719
720	output( "  Model: Y = c * X ^ a\n" );
721
722	output( "   sem_open:\n" );
723
724	output( "       a = %Lg\n", _q_o[ 1 ] / _d_o[ 1 ] );
725
726	output( "       c = %Lg\n", logl ( LnYavg_o - ( _q_o[ 1 ] / _d_o[ 1 ] ) * LnXavg ) );
727
728	output( "    Divergence %g\n", r3_o );
729
730	output( "   sem_close:\n" );
731
732	output( "       a = %Lg\n", _q_c[ 1 ] / _d_c[ 1 ] );
733
734	output( "       c = %Lg\n", logl ( LnYavg_c - ( _q_c[ 1 ] / _d_c[ 1 ] ) * LnXavg ) );
735
736	output( "    Divergence %g\n", r3_c );
737
738	output( "  Model: Y = exp(a * X + b)\n" );
739
740	output( "   sem_open:\n" );
741
742	output( "       a = %Lg\n", _q_o[ 2 ] / _d_o[ 2 ] );
743
744	output( "       b = %Lg\n", LnYavg_o - ( ( _q_o[ 2 ] / _d_o[ 2 ] ) * Xavg ) );
745
746	output( "    Divergence %g\n", r4_o );
747
748	output( "   sem_close:\n" );
749
750	output( "       a = %Lg\n", _q_c[ 2 ] / _d_c[ 2 ] );
751
752	output( "       b = %Lg\n", LnYavg_c - ( ( _q_c[ 2 ] / _d_c[ 2 ] ) * Xavg ) );
753
754	output( "    Divergence %g\n", r4_c );
755
756#endif
757
758	if ( array_max != -1 )
759	{
760		/* Compare r1 to other values, with some ponderations */
761
762		if ( ( r1_o > 1.1 * r2_o ) || ( r1_o > 1.2 * r3_o ) || ( r1_o > 1.3 * r4_o )
763		        || ( r1_c > 1.1 * r2_c ) || ( r1_c > 1.2 * r3_c ) || ( r1_c > 1.3 * r4_c ) )
764			ret++;
765
766#if VERBOSE > 1
767		else
768			output( " Sanction: OK\n" );
769
770#endif
771
772	}
773
774	/* We need to free the array */
775	free( Table );
776
777	/* We're done */
778	return ret;
779}
780
781