Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

760 lines
17 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1997
  6. //
  7. // File: bntest.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. //
  11. // BNTEST.CPP
  12. //
  13. #include <windows.h>
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <time.h>
  17. #include <math.h>
  18. #include <float.h>
  19. #include "bnparse.h" // Parser class
  20. #include "bnreg.h" // Registry management
  21. #include "testinfo.h" // Output test file generation
  22. #include "distdense.hxx" // Distribution classes
  23. #include "distsparse.h"
  24. #ifdef TIME_DYN_CASTS
  25. // Global variable containing count of calls to all forms of DynCastThrow function template
  26. int g_cDynCasts = 0;
  27. #endif
  28. enum EFN // File name in file name array
  29. {
  30. EFN_IN, // input DSC file
  31. EFN_OUT, // output DSC file
  32. EFN_INFER // output inference test file (see testinfo.cpp for format)
  33. };
  34. static
  35. inline
  36. double RNan ()
  37. {
  38. double rnan = sqrt(-1.0);
  39. #ifndef NTALPHA
  40. assert( _isnan( rnan ) );
  41. #endif
  42. return rnan;
  43. }
  44. static
  45. inline
  46. bool BFlag ( ULONG fCtl, ULONG fFlag )
  47. {
  48. return (fCtl & fFlag) > 0;
  49. }
  50. static
  51. void usage ()
  52. {
  53. cout << "\nBNTEST: Belief Network Test program"
  54. << "\nCommand line:"
  55. << "\n\tbntest [options] <input.DSC> [/s <output.DSC>] [/p <output.DMP>]"
  56. << "\nOptions:"
  57. << "\n\t/v\t\tverbose output"
  58. << "\n\t/c\t\tclique the network"
  59. << "\n\t/e\t\ttest CI network expansion"
  60. << "\n\t/inn\t\ttest inference; nn = iterations (default 1)"
  61. << "\n\t/p <filename>\twrite inference output (.dmp) file (sets /i)"
  62. << "\n\t/s <filename>\trewrite input DSC into output file"
  63. << "\n\t/t\t\tdisplay start and stop times"
  64. << "\n\t/x\t\tpause at various stages (for memory measurement)"
  65. << "\n\t/n\t\tuse symbolic names in inference output (default is full)"
  66. << "\n\t/y\t\tclone the network (write cloned version if /s)"
  67. << "\n\t/u\t\tinclude entropic utility records in /p output"
  68. << "\n\t/b\t\tinclude troubleshooting recommendations in /p output"
  69. << "\n\t/r\t\tstore property types in Registry for persistence"
  70. << "\n\t/b\t\tcompute troubleshooting recommendations"
  71. << "\n\t/z\t\tshow inference engine statistics"
  72. << "\n\t/m<nnnnnn>\tset maximum estimated inference engine size"
  73. << "\n\t/a<n.n>\t\tflag impossible evidence with numeric value"
  74. << "\n\nInput DSC is read and parsed; errors and warnings go to stderr."
  75. << "\nParse errors stop testing. If cloning, output file is cloned version."
  76. << "\nIf CI expansion (/e), output (/s) has pre- and post- expansion versions."
  77. << "\nInference (/i or /p) takes precedence over CI expansion (/e)."
  78. << "\nInference output (/p) writes file in common format with DXTEST."
  79. << "\nCliquing (/c) just creates and destroys junction tree."
  80. << "\n";
  81. }
  82. static
  83. void die( SZC szcFormat, ... )
  84. {
  85. ZSTR zsMsg;
  86. va_list valist;
  87. va_start(valist, szcFormat);
  88. zsMsg.Vsprintf( szcFormat, valist );
  89. va_end(valist);
  90. cerr << "\nBNTEST error: "
  91. << zsMsg.Szc()
  92. << "\n";
  93. exit(1);
  94. }
  95. // Show the debugging build options
  96. static
  97. void showOptions ( ULONG fCtl )
  98. {
  99. bool bComma = false;
  100. ZSTR zs = TESTINFO::ZsOptions( fCtl );
  101. cout << "(options: "
  102. << zs;
  103. bComma = zs.length() > 0;
  104. // Show DYNAMIC CAST option
  105. if ( bComma )
  106. cout << ",";
  107. cout <<
  108. #ifdef USE_STATIC_CAST
  109. "STATICCAST"
  110. #else
  111. "DYNCAST"
  112. #endif
  113. ;
  114. bComma = true;
  115. // Show DUMP option
  116. #ifdef DUMP
  117. if ( bComma )
  118. cout << ",";
  119. cout << "DUMP";
  120. bComma = true;
  121. #endif
  122. // Show DEBUG option
  123. #ifdef _DEBUG
  124. if ( bComma )
  125. cout << ",";
  126. cout << "DEBUG";
  127. bComma = true;
  128. #endif
  129. cout << ")";
  130. }
  131. // Show memory leaks for primary object types, if any
  132. static
  133. void showResiduals ()
  134. {
  135. #ifdef _DEBUG
  136. if (GEDGE::CNew() + GNODE::CNew() + GNODE::CNew() )
  137. {
  138. cout << "\n(GEDGEs = "
  139. << GEDGE::CNew()
  140. << ", GNODESs = "
  141. << GNODE::CNew()
  142. << ", BNDISTs = "
  143. << GNODE::CNew()
  144. << ")";
  145. }
  146. if ( VMARGSUB::CNew() + MARGSUBREF::CNew() )
  147. {
  148. cout << "\n(VMARGSUBs = "
  149. << VMARGSUB::CNew()
  150. << ", MARGSUBREFs = "
  151. << MARGSUBREF::CNew()
  152. << ")";
  153. }
  154. #endif
  155. }
  156. static
  157. void printResiduals ()
  158. {
  159. #ifdef _DEBUG
  160. showResiduals();
  161. #endif
  162. #ifdef TIME_DYN_CASTS
  163. cout << "\ntotal number of dynamic casts was "
  164. << g_cDynCasts;
  165. #endif
  166. }
  167. // Display the message and pause if the "pause" option is active
  168. inline
  169. static
  170. void pauseIf ( ULONG fCtl, SZC szcMsg )
  171. {
  172. if ( (fCtl & fPause) == 0 )
  173. return;
  174. showResiduals();
  175. char c;
  176. cout << "\n"
  177. << szcMsg
  178. << " (pause)"
  179. ;
  180. cin.get(c);
  181. }
  182. // Display the phase message and, optionally, the time
  183. typedef DWORD CTICKS;
  184. inline
  185. static
  186. CTICKS showPhase ( ULONG fCtl, SZC szcPhase, CTICKS * ptmLast = NULL )
  187. {
  188. // Display the phase message
  189. cout << "\n" << szcPhase;
  190. CTICKS cticks = 0;
  191. if ( fCtl & fShowTime )
  192. {
  193. // Save the current tick count
  194. cticks = ::GetTickCount();
  195. // Prepare to display the current date/time
  196. time_t timeNow;
  197. time(& timeNow);
  198. ZSTR zsTime = ctime(&timeNow);
  199. int cnl = zsTime.find( '\n' );
  200. if ( cnl != 0 )
  201. zsTime.resize( cnl );
  202. cout << " " << zsTime;
  203. // Display the elapsed time if we know it
  204. if ( ptmLast && *ptmLast != 0 )
  205. {
  206. CTICKS ticksElapsed = cticks - *ptmLast;
  207. cout << " (elapsed time "
  208. << ticksElapsed
  209. << " milliseconds)";
  210. }
  211. }
  212. return cticks;
  213. }
  214. static
  215. void testRegistry ( MBNET & mbnet )
  216. {
  217. BNREG bnr;
  218. bnr.StorePropertyTypes( mbnet, true );
  219. }
  220. #ifdef TESTDIST
  221. static void loadDistDenseFromMpcpdd ( DISTDENSE & ddense, const MPCPDD & mpcpdd )
  222. {
  223. ddense.AllocateParams();
  224. CST cstNode = ddense.CstNode();
  225. // Find the default vector in the map or create a uniform vector
  226. const VLREAL * pvlrDefault = mpcpdd.PVlrDefault();
  227. VLREAL vlrDefault;
  228. if ( pvlrDefault )
  229. {
  230. vlrDefault = *pvlrDefault;
  231. }
  232. else
  233. {
  234. vlrDefault.resize( cstNode );
  235. REAL rDefault = 1 / cstNode ;
  236. vlrDefault = rDefault;
  237. }
  238. // Fill the dense array with the default value
  239. UINT cParamgrp = ddense.Cparamgrp();
  240. UINT igrp = 0;
  241. for ( ; igrp < cParamgrp; igrp++ )
  242. {
  243. for ( UINT ist = 0; ist < cstNode; ist++ )
  244. {
  245. ddense.Param(ist, igrp) = vlrDefault[ist];
  246. }
  247. }
  248. // Iterate over the sparse map, storing probabilities as parameters
  249. const VCST & vcstParent = ddense.VcstParent();
  250. VIST vist;
  251. for ( MPCPDD::iterator mpitcpd = mpcpdd.begin();
  252. mpitcpd != mpcpdd.end();
  253. mpitcpd++ )
  254. {
  255. const VIMD & vimd = (*mpitcpd).first;
  256. const VLREAL & vlr = (*mpitcpd).second;
  257. // State vector size must match state space
  258. assert( vlr.size() == cstNode );
  259. // Parent dimensions must match dimension index
  260. assert( vdimchk( vimd, vcstParent ) );
  261. // Convert the vector of unsigneds to a vector of signeds
  262. vdup( vist, vimd );
  263. // Get the parameter group index
  264. UINT igrp = ddense.Iparamgrp( vist );
  265. // Copy the probabilities as parameters
  266. for ( UINT ist = 0; ist < cstNode; ist++ )
  267. {
  268. ddense.Param(ist, igrp) = vlr[ist];
  269. }
  270. }
  271. }
  272. static void testDistDenseWithMpcpdd( DISTDENSE & ddense, const MPCPDD & mpcpdd )
  273. {
  274. }
  275. static void loadDistSparseFromMpcpdd ( DISTSPARSE & dsparse, const MPCPDD & mpcpdd )
  276. {
  277. dsparse.Init( mpcpdd );
  278. }
  279. static void testDistSparseWithMpcpdd ( DISTSPARSE & dsparse, const MPCPDD & mpcpdd )
  280. {
  281. MPCPDD mpcpddNew;
  282. dsparse.Fill( mpcpddNew );
  283. assert( mpcpddNew == mpcpdd );
  284. }
  285. #endif
  286. // Bind the model's distibutions and verify behavior of the DISTSPARSE
  287. // and DISTDENSE classes.
  288. static
  289. void testDistributions ( MBNETDSC & mbnetdsc, ULONG fCtl )
  290. {
  291. #ifdef TESTDIST
  292. // Bind the distributions
  293. mbnetdsc.BindDistributions();
  294. GOBJMBN * pgmobj;
  295. for ( MBNETDSC::ITER mbnit( mbnetdsc, GOBJMBN::EBNO_NODE );
  296. pgmobj = *mbnit ;
  297. ++mbnit)
  298. {
  299. ZSREF zsrName = mbnit.ZsrCurrent();
  300. GNODEMBND * pgndd;
  301. DynCastThrow( pgmobj, pgndd );
  302. // Convert this node's distribution to a DISTDENSE and
  303. // a DISTSPARSE, then compare them to the original
  304. assert( pgndd->BHasDist() );
  305. const BNDIST & bndist = pgndd->Bndist();
  306. assert( bndist.BSparse() );
  307. const MPCPDD & mpcpdd = bndist.Mpcpdd();
  308. // Get the parent list for this node; convert to a state count vector
  309. VPGNODEMBN vpgndParents;
  310. VIMD vimdParents;
  311. if ( ! pgndd->BGetVimd( vimdParents ) )
  312. continue; // Skip non-discrete ensembles
  313. VCST vcstParents;
  314. vdup( vcstParents, vimdParents );
  315. CST cStates = pgndd->CState();
  316. DISTDENSE ddense( cStates, vcstParents );
  317. DISTSPARSE dsparse( cStates, vcstParents );
  318. loadDistDenseFromMpcpdd( ddense, mpcpdd );
  319. testDistDenseWithMpcpdd( ddense, mpcpdd );
  320. loadDistSparseFromMpcpdd( dsparse, mpcpdd );
  321. testDistSparseWithMpcpdd( dsparse, mpcpdd );
  322. }
  323. // Release the distributions
  324. mbnetdsc.ClearDistributions();
  325. #endif
  326. }
  327. static
  328. void
  329. showInferStats ( TESTINFO & testinfo )
  330. {
  331. GOBJMBN_INFER_ENGINE * pInferEng = testinfo.Mbnet().PInferEngine();
  332. assert( pInferEng );
  333. GOBJMBN_CLIQSET * pCliqset = dynamic_cast<GOBJMBN_CLIQSET *>(pInferEng);
  334. if ( pCliqset == NULL )
  335. return; // Don't know how to get statistics from this inference engine
  336. CLIQSETSTAT & cqstats = pCliqset->CqsetStat();
  337. cout << "\n\nInference statistics: "
  338. << "\n\treloads = " << cqstats._cReload
  339. << "\n\tcollects = " << cqstats._cCollect
  340. << "\n\tset evidence = " << cqstats._cEnterEv
  341. << "\n\tget belief = " << cqstats._cGetBel
  342. << "\n\tprob norm = " << cqstats._cProbNorm
  343. << "\n"
  344. ;
  345. }
  346. static
  347. void testInference ( ULONG fCtl, MBNETDSC & mbnet, SZC szcFnInfer, REAL rImposs )
  348. {
  349. ofstream ofs;
  350. bool bOutput = (fCtl & fOutputFile) > 0 ;
  351. int cPass = fCtl & fPassCountMask;
  352. GOBJMBN_INFER_ENGINE * pInferEng = mbnet.PInferEngine();
  353. assert( pInferEng );
  354. if ( bOutput )
  355. {
  356. if ( szcFnInfer == NULL )
  357. szcFnInfer = "infer.dmp";
  358. ofs.open(szcFnInfer);
  359. }
  360. // Construct the test data container
  361. TESTINFO testinfo( fCtl, mbnet, bOutput ? & ofs : NULL );
  362. testinfo._rImposs = rImposs;
  363. // Run the test
  364. testinfo.InferTest();
  365. if ( bOutput )
  366. ofs.close();
  367. if ( fCtl & fInferStats )
  368. showInferStats( testinfo );
  369. }
  370. static
  371. void testCliquingStart ( ULONG fCtl, MBNETDSC & mbnet, REAL rMaxEstSize = -1.0 )
  372. {
  373. #ifdef DUMP
  374. if ( BFlag( fCtl, fVerbose ) )
  375. {
  376. cout << "\nBNTEST: BEGIN model before cliquing";
  377. mbnet.Dump();
  378. cout << "\nBNTEST: END model before cliquing\n";
  379. }
  380. #endif
  381. mbnet.CreateInferEngine( rMaxEstSize );
  382. #ifdef DUMP
  383. if ( BFlag( fCtl, fVerbose ) )
  384. {
  385. cout << "\nBNTEST: BEGIN model after cliquing";
  386. mbnet.Dump();
  387. cout << "\nBNTEST: END model after cliquing\n";
  388. }
  389. #endif
  390. }
  391. static
  392. void testCliquingEnd ( MBNETDSC & mbnet, ULONG fCtl )
  393. {
  394. GOBJMBN_INFER_ENGINE * pInferEng = mbnet.PInferEngine();
  395. if ( pInferEng == NULL )
  396. return;
  397. mbnet.DestroyInferEngine();
  398. // For testing, nuke the topology
  399. mbnet.DestroyTopology( true );
  400. // Create arcs from the given conditional probability distributions
  401. mbnet.CreateTopology();
  402. // For testing, nuke the topology
  403. mbnet.DestroyTopology( false );
  404. }
  405. static
  406. void testParser (
  407. ULONG fCtl,
  408. SZC rgfn[],
  409. REAL rMaxEstSize = -1.0,
  410. REAL rImposs = -1.0 )
  411. {
  412. SZC szcFn = rgfn[EFN_IN];
  413. SZC szcFnOut = rgfn[EFN_OUT];
  414. SZC szcFnInfer = rgfn[EFN_INFER];
  415. // Instantiate the belief network
  416. MBNETDSC mbnet;
  417. // See if there's an output file to write a DSC into
  418. FILE * pfOut = NULL;
  419. if ( (fCtl & fSaveDsc) > 0 && szcFnOut != NULL )
  420. {
  421. pfOut = fopen(szcFnOut,"w");
  422. if ( pfOut == NULL )
  423. die("error creating output DSC file \'%s\'", szcFnOut);
  424. }
  425. // Input file wrapper object
  426. PARSIN_DSC flpIn;
  427. // Output file wrapper object
  428. PARSOUT_STD flpOut(stderr);
  429. // Construct the parser; errors go to 'stderr'
  430. DSCPARSER parser(mbnet, flpIn, flpOut);
  431. UINT cError, cWarning;
  432. try
  433. {
  434. // Attempt to open the file
  435. if ( ! parser.BInitOpen( szcFn ) )
  436. die("unable to access input file");
  437. pauseIf( fCtl, "input DSC file open" );
  438. // Parse the file
  439. if ( ! parser.BParse( cError, cWarning ) )
  440. die("parse failure; %d errors, %d warnings", cError, cWarning);
  441. if ( cWarning )
  442. cout << "\nBNTEST: file "
  443. << szcFn
  444. << " had "
  445. << cWarning
  446. << " warnings\n";
  447. if ( BFlag( fCtl, fReg ) )
  448. testRegistry( mbnet );
  449. pauseIf( fCtl, "DSC file read and processed" );
  450. if ( BFlag( fCtl, fDistributions ) )
  451. {
  452. testDistributions( mbnet, fCtl );
  453. }
  454. // If requested, test cloning
  455. if ( BFlag( fCtl, fClone ) )
  456. {
  457. MBNETDSC mbnetClone;
  458. mbnetClone.Clone( mbnet );
  459. if ( pfOut )
  460. mbnetClone.Print( pfOut );
  461. }
  462. else
  463. // If requested, write out a DSC file
  464. if ( pfOut )
  465. {
  466. mbnet.Print( pfOut );
  467. }
  468. // Test cliquing if requested (/c) or required (/i)
  469. if ( BFlag( fCtl, fCliquing ) || BFlag( fCtl, fInference ) )
  470. {
  471. testCliquingStart( fCtl, mbnet, rMaxEstSize );
  472. pauseIf( fCtl, "Cliquing completed" );
  473. if ( BFlag( fCtl, fInference ) )
  474. {
  475. // Generate inference results (/i)
  476. testInference( fCtl, mbnet, szcFnInfer, rImposs );
  477. pauseIf( fCtl, "Inference output generation completed" );
  478. }
  479. testCliquingEnd( mbnet, fCtl ) ;
  480. pauseIf( fCtl, "Cliquing and inference completed" );
  481. }
  482. else
  483. // Test if CI expansion requested (/e)
  484. if ( BFlag( fCtl, fExpand ) )
  485. {
  486. // Perform CI expansion on the network.
  487. mbnet.ExpandCI();
  488. pauseIf( fCtl, "Network expansion complete" );
  489. // If output file generation, do "before" and "after" expansion and reversal
  490. if ( pfOut )
  491. {
  492. fprintf( pfOut, "\n\n//////////////////////////////////////////////////////////////" );
  493. fprintf( pfOut, "\n// Network After Expansion //" );
  494. fprintf( pfOut, "\n//////////////////////////////////////////////////////////////\n\n" );
  495. mbnet.Print( pfOut );
  496. }
  497. // Undo the expansion
  498. mbnet.UnexpandCI();
  499. if ( pfOut )
  500. {
  501. fprintf( pfOut, "\n\n//////////////////////////////////////////////////////////////" );
  502. fprintf( pfOut, "\n// Network After Expansion Reversal //" );
  503. fprintf( pfOut, "\n//////////////////////////////////////////////////////////////\n\n" );
  504. mbnet.Print( pfOut );
  505. }
  506. }
  507. // For testing, nuke the topology
  508. mbnet.DestroyTopology();
  509. }
  510. catch ( GMException & exbn )
  511. {
  512. die( exbn.what() );
  513. }
  514. if ( pfOut )
  515. fclose( pfOut );
  516. }
  517. int main (int argc, char * argv[])
  518. {
  519. int iArg ;
  520. short cPass = 1;
  521. int cFile = 0 ;
  522. const int cFnMax = 10 ;
  523. SZC rgfn [cFnMax+1] ;
  524. ULONG fCtl = 0;
  525. REAL rMaxEstSize = -1.0;
  526. REAL rImposs = RNan();
  527. for ( int i = 0 ; i < cFnMax ; i++ )
  528. {
  529. rgfn[i] = NULL ;
  530. }
  531. for ( iArg = 1 ; iArg < argc ; iArg++ )
  532. {
  533. switch ( argv[iArg][0] )
  534. {
  535. case '/':
  536. case '-':
  537. {
  538. char chOpt = toupper( argv[iArg][1] ) ;
  539. switch ( chOpt )
  540. {
  541. case 'V':
  542. // Provide verbose output
  543. fCtl |= fVerbose;
  544. break;
  545. case 'C':
  546. // Perform cliquing
  547. fCtl |= fCliquing;
  548. break;
  549. case 'E':
  550. // Test network CI expansion
  551. fCtl |= fExpand;
  552. break;
  553. case 'I':
  554. // Exercise inference and optionally write the results in a standard form
  555. {
  556. int c = atoi( & argv[iArg][2] );
  557. if ( c > 0 )
  558. {
  559. fCtl |= fMulti;
  560. cPass = c;
  561. }
  562. fCtl |= fInference;
  563. break;
  564. }
  565. case 'P':
  566. // Get the name of the inference output file
  567. fCtl |= fOutputFile | fInference;
  568. if ( ++iArg == argc )
  569. die("no output inference result file name given");
  570. rgfn[EFN_INFER] = argv[iArg];
  571. break;
  572. case 'S':
  573. // Write the input DSC file as an output file
  574. fCtl |= fSaveDsc;
  575. if ( ++iArg == argc )
  576. die("no output DSC file name given");
  577. rgfn[EFN_OUT] = argv[iArg];
  578. break;
  579. case 'T':
  580. // Display start and stop times
  581. fCtl |= fShowTime;
  582. break;
  583. case 'X':
  584. // Pause at times during execution to allow the user to measure
  585. // memory usage
  586. fCtl |= fPause;
  587. break;
  588. case 'Y':
  589. // Clone the network after loading
  590. fCtl |= fClone;
  591. break;
  592. case 'N':
  593. // Write the symbolic name into the inference exercise output file
  594. // instead of the default full name.
  595. fCtl |= fSymName;
  596. break;
  597. case 'U':
  598. // Compute utilities using inference
  599. fCtl |= fUtil | fInference;
  600. break;
  601. case 'B':
  602. // Compute troubleshooting utilities using inference
  603. fCtl |= fTSUtil | fInference;
  604. break;
  605. case 'R':
  606. fCtl |= fReg;
  607. break;
  608. case 'Z':
  609. fCtl |= fInferStats;
  610. break;
  611. case 'M':
  612. { // Get the maximum estimated clique tree size
  613. float f = atof( & argv[iArg][2] );
  614. if ( f > 0.0 )
  615. rMaxEstSize = f;
  616. break;
  617. }
  618. case 'A':
  619. {
  620. if ( strlen( & argv[iArg][2] ) > 0 )
  621. {
  622. rImposs = atof( & argv[iArg][2] );
  623. }
  624. fCtl |= fImpossible;
  625. break;
  626. }
  627. case 'D':
  628. fCtl |= fDistributions;
  629. break;
  630. default:
  631. die("unrecognized option") ;
  632. break ;
  633. }
  634. }
  635. break;
  636. default:
  637. if ( cFile == 0 )
  638. rgfn[cFile++] = argv[iArg] ;
  639. else
  640. die("too many file names given");
  641. break ;
  642. }
  643. }
  644. fCtl |= fPassCountMask & cPass;
  645. if ( cFile == 0 )
  646. {
  647. usage();
  648. return 0;
  649. }
  650. // Display options and the debugging build mode
  651. showOptions( fCtl );
  652. // Display the start message
  653. CTICKS tmStart = showPhase( fCtl, "BNTEST starts" );
  654. if ( rMaxEstSize > 0.0 )
  655. cout << "\nMaximum clique tree size estimate is " << rMaxEstSize;
  656. // Test the parser and everything else
  657. testParser( fCtl, rgfn, rMaxEstSize, rImposs );
  658. // Display the stop message
  659. showPhase( fCtl, "BNTEST completed", & tmStart );
  660. // Print memory leaks of primary objects, if any
  661. printResiduals();
  662. cout << "\n";
  663. return 0;
  664. }