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