1 // $Id: runreport.cpp,v 1.59 2011/03/07 06:08:51 bobgian Exp $
2
3 /*
4 Copyright 2002 Peter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein
5
6 This software is distributed free of charge for non-commercial use
7 and is copyrighted. Of course, we do not guarantee that the software
8 works, and are not responsible for any damage you may cause or have.
9 */
10
11 #include <cassert>
12
13 #include "runreport.h"
14 #include "chainout.h"
15 #include "chainpack.h"
16 #include "force.h"
17 #include "forcesummary.h"
18 #include "stringx.h"
19 #include "types.h"
20 #include "registry.h" // for GetDataPack() in MakeReport()
21
22 #ifdef DMALLOC_FUNC_CHECK
23 #include "/usr/local/include/dmalloc.h"
24 #endif
25
26 using namespace std;
27
28 //------------------------------------------------------------------------------------
29
30 string UNKNOWN_STRING = "<unknown>";
31
32 //------------------------------------------------------------------------------------
33
34 /****************************************************************
35 This function predicts the end of the chain process, given
36 the number of steps so far and the total required number of
37 steps. It is meant to be called by the chain manager.
38 *****************************************************************/
RunReport(const ForceSummary & fs,verbosity_type progress)39 RunReport::RunReport(const ForceSummary& fs, verbosity_type progress)
40 : m_level(progress),
41 m_filelevel(registry.GetUserParameters().GetVerbosity()),
42 m_forcesum(fs)
43 {
44 m_profiletotal = static_cast<time_t>(0.0);
45 };
46
PrognoseRegion(const ChainPack & chpack,long int region,long int steps,long int total)47 void RunReport::PrognoseRegion(const ChainPack& chpack, long int region,
48 long int steps, long int total)
49 {
50
51 if (m_level == CONCISE || m_level == NONE) return;
52 if (steps == total) return; //Let's not predict the current time.
53
54 string timestr;
55
56 #ifdef NOTIME_FUNC
57 // system does not provide a clock so no prognosis can be made
58 timestr = "unknown";
59 #else
60 time_t tnow = GetTime();
61 time_t tstart = chpack.GetStartTime(region); // start of the whole thing
62 if (steps == 0) timestr = "unknown";
63 else
64 {
65 double proportion = static_cast<double>(total) / static_cast<double>(steps);
66 time_t predict = static_cast<time_t>(((tnow-tstart) * proportion) + tstart);
67 timestr = PrintTime(predict,string("%c"));
68 }
69 #endif
70
71 string msg = "Predicted end of chains for this region: ";
72 msg += timestr + "\n";
73 ReportNormal(msg);
74
75 } // PrognoseRegion
76
77 //------------------------------------------------------------------------------------
78
79 // This functions are used to predict the end of the profile
80 // process and print a report on progress so far. When first
81 // beginning a profile, call RecordProfileStart() to set the time;
82 // then call PrognoseProfiles to report on progress.
83
RecordProfileStart()84 void RunReport::RecordProfileStart()
85 {
86 // Don't even try if there is no system clock.
87 #ifndef NOTIME_FUNC
88 m_profilestart = GetTime();
89 #endif
90 ReportUrgent("Beginning profiling, please be patient");
91 } // RecordProfileStart
92
93 //------------------------------------------------------------------------------------
94
PrognoseProfiles(long int thisprof,long int totprofs)95 void RunReport::PrognoseProfiles(long int thisprof, long int totprofs)
96 {
97 if (m_level == NONE || m_level == CONCISE) return;
98 long int profile = thisprof + 1; // elsewhere they are counted starting from 0
99 if (profile==totprofs)
100 {
101 m_profiletotal += (GetTime() - m_profilestart);
102 string msg = "Finished profile "+ ToString(profile)
103 + " of " + ToString(totprofs) + ".\n";
104 ReportNormal(msg);
105 return;
106 }
107 string timestr;
108
109 #ifdef NOTIME_FUNC
110 timestr = "unknown";
111 #else
112 time_t tnow = GetTime();
113 double proportion = static_cast<double>(totprofs) / static_cast<double>(profile);
114 time_t predict = static_cast<time_t>(((tnow - m_profilestart) * proportion) + m_profilestart);
115 timestr = PrintTime(predict, string("%c"));
116 #endif
117 string msg = "Finished profile " + ToString(profile) + " of " + ToString(totprofs)
118 + ". Predicted end of this set of profiles: " + timestr + "\n";
119 ReportNormal(msg);
120
121 } // PrognoseProfiles
122
PrognoseAll(const ChainPack & chpack,long int thisregion,long int totalregions)123 void RunReport::PrognoseAll(const ChainPack& chpack, long int thisregion, long int totalregions)
124 {
125 if (m_level == CONCISE || m_level == NONE) return;
126 thisregion++;
127 if ((thisregion == totalregions) && (totalregions == 1)) return;
128 //The program is already done, since there's no 'overall' region to profile.
129
130 string timestr;
131
132 #ifdef NOTIME_FUNC
133 // system does not provide a clock so no prognosis can be made
134 timestr = "unknown";
135 #else
136 time_t tnow = GetTime();
137 time_t tstart = chpack.GetStartTime(); // start of the whole thing
138 double proportion = static_cast<double>(totalregions) / static_cast<double>(thisregion);
139 time_t predict = static_cast<time_t>(((tnow-tstart) * proportion) + tstart);
140 if (totalregions > 1)
141 {
142 //We will probably spend as much time profiling over all regions as
143 // we spend profiling all the regions individually.
144 predict += static_cast<time_t>(m_profiletotal * proportion);
145 }
146 timestr = PrintTime(predict,string("%c"));
147 #endif
148
149 string msg = "Predicted end of this LAMARC run: ";
150 msg += timestr;
151 ReportNormal(msg);
152
153 } // PrognoseAll
154
155 /**************************************************************
156 This function prints the "moving bar" which shows progress
157 of the run. It is meant to be called by the chain.
158 You must call SetBarParameters before calling this. Also,
159 for PrintBar to work properly it must be called for the
160 first time with steps = 0 and for the last time with steps = m_totalsteps.
161 **************************************************************/
162
PrintBar(long int steps)163 void RunReport::PrintBar(long int steps)
164 {
165
166 #ifdef NOPROGRESSBAR
167 if (steps == 0)
168 {
169 string msg = m_chaintype + " Chain " + ToString(m_chainno+1) + " of "
170 + ToString(registry.GetChainParameters().GetNChains(m_chaintype_num)) + ":";
171 ReportNormal(msg, false);
172 }
173 return;
174 #else
175 if (m_level == CONCISE || m_level == NONE) return;
176
177 long int i;
178 static long int previous = 0;
179 long int width = 12;
180
181 if (steps == 0)
182 { // Initial display of bar
183 long int chaintot = registry.GetChainParameters().GetNChains(m_chaintype_num);
184 string msg = m_chaintype + " Chain " + ToString(m_chainno+1) + " of "
185 + ToString(chaintot) + ":";
186 SaveOutput(msg, false);
187 cout << endl << PrintTime(GetTime()) << " ";
188 cout << MakeJustified(m_chaintype + string(" chain "),-14)
189 << Pretty(m_chainno + 1L, 3)
190 << " of " << Pretty(chaintot, 3) << ": ";
191 cout << "[| ] ";
192 cout << Pretty(steps + 1L, width);
193 cout.flush();
194 previous = 0;
195 return;
196 }
197
198 long int counterDisplay = steps - m_burnin;
199
200 if(counterDisplay % m_counter_display_increment == 0)
201 {
202 long int percent = static_cast<long int>(100.0 * static_cast<double>(steps)
203 / static_cast<double>(m_totalsteps));
204
205 if (percent > previous && ((percent/5) * 5) - percent < EPSILON)
206 {
207 for (i = 0; i < width+22; i++) cout << "\b"; // back up
208 for (i = 0; i < percent/5; i++)
209 {
210 cout << "=";
211 }
212 if (percent != 100) cout << "|";
213 for (i = 0; i < 19-(percent/5); i++) cout << " ";
214 cout << "] ";
215 }
216 else
217 {
218 for (i = 0; i < width; ++i) cout << "\b";
219 }
220 cout << Pretty(counterDisplay, width);
221 if (steps == m_totalsteps) cout << " steps " << endl;
222 cout.flush();
223 previous = percent;
224 }
225 #endif
226 } // PrintBar
227
228 //------------------------------------------------------------------------------------
229
AddForceToTable(const Force * force,StringVec1d & table,const ChainOut & chout) const230 void RunReport::AddForceToTable(const Force* force, StringVec1d& table,
231 const ChainOut& chout) const
232 {
233 unsigned long int i;
234
235 StringVec1d temptable = force->MakeChainParamReport(chout);
236
237 // add the header
238 table[0] += MakeCentered(force->GetShortparamname(), temptable[0].size());
239
240 // Code added to deal with cases where the tables are not the same
241 // size, as with divergence
242
243 if (temptable.size() + 1 > table.size())
244 {
245 // put in blank entries
246 assert(table.size() > 0); // no entries at all??
247 string blankentry(' ',table[1].size());
248 for (i = table.size(); i < temptable.size() + 1; ++i)
249 {
250 table.push_back(blankentry);
251 }
252 }
253 // add the parameter values
254 for (i = 1; i < table.size() && i < temptable.size() + 1; ++i)
255 {
256 table[i] += " " + temptable[i-1];
257 }
258
259 } // AddForceToTable
260
261 //------------------------------------------------------------------------------------
262
MakeReport(const ChainOut & chout) const263 void RunReport::MakeReport(const ChainOut& chout) const
264 {
265 // Prepare header lines
266 unsigned long int i;
267
268 // Prepare tables of chain MLEs
269 m_scalars.erase();
270 m_tables.clear();
271
272 const vector<Force *>& forces = m_forcesum.GetAllForces();
273
274 // initialize the cross partition tables with naming info
275 // the partition tables will be handled in the loop below
276 const DataPack& datapack = registry.GetDataPack();
277 StringVec1d xpartnames = datapack.GetAllCrossPartitionNames();
278
279 StringVec1d tables2d;
280 StringVec1d::iterator name;
281 if (xpartnames.size() > 1)
282 {
283 // all cross-partitions will use the Class name "Class".
284 tables2d.push_back(MakeJustified("Class",-22));
285 for (name = xpartnames.begin(); name != xpartnames.end(); ++name)
286 tables2d.push_back(MakeJustified(*name, -20));
287 }
288
289 StringVec2d tables3d;
290
291 for (i = 0; i < forces.size(); ++i) // fill up all tables
292 {
293 if (forces[i]->ReportDimensionality() == 1) // scalar
294 {
295 m_scalars += MakeJustified(forces[i]->GetShortparamname(), -10) + " ";
296 m_scalars += forces[i]->MakeChainParamReport(chout)[0] + " ";
297 }
298 else
299 { // tabular
300 // crosswise tables
301 if (forces[i]->ReportDimensionality() == 2)
302 {
303 AddForceToTable(forces[i],tables2d,chout);
304 }
305 else
306 {
307 // partition tables
308 assert (forces[i]->ReportDimensionality() == 3);
309 StringVec1d table3d;
310 StringVec1d partnames = datapack.
311 GetAllPartitionNames(forces[i]->GetTag());
312 table3d.push_back(MakeJustified(forces[i]->GetClassName(),-23));
313 for (name = partnames.begin(); name != partnames.end(); ++name)
314 table3d.push_back(MakeJustified(*name, -22));
315 AddForceToTable(forces[i],table3d,chout);
316 tables3d.push_back(table3d);
317 }
318 }
319 }
320
321 // put all the tables together into the big table
322 m_tables.push_back(string("")); // add an empty line between m_scalars
323 // and tables
324 m_tables.assign(tables2d.begin(), tables2d.end());
325 m_tables.push_back(string("")); // add an empty line between tables
326 StringVec2d::iterator tab3d;
327 for(tab3d = tables3d.begin(); tab3d != tables3d.end(); ++tab3d)
328 {
329 m_tables.insert(m_tables.end(), tab3d->begin(), tab3d->end());
330 m_tables.push_back(string("")); // add an empty line between tables
331 }
332
333 // if heating, make swapping report
334 long int numtemps = chout.GetNumtemps();
335 if (numtemps > 1)
336 {
337 DoubleVec1d swaprates = chout.GetSwaprates();
338 DoubleVec1d temperatures = chout.GetTemperatures();
339 assert(swaprates.size() == static_cast<unsigned long int>(numtemps));
340 assert(temperatures.size() == static_cast<unsigned long int>(numtemps));
341 string tableline = "Temperature: ";
342 long int temp;
343 for (temp = 0; temp < numtemps; ++temp)
344 {
345 tableline += Pretty(temperatures[temp],7) + " ";
346 }
347 m_tables.push_back(tableline);
348 tableline = " Swap rate: ";
349 for (temp = 0; temp < numtemps-1; ++temp)
350 {
351 if (swaprates[temp] >= 0.0)
352 {
353 tableline += Pretty(100.0 * swaprates[temp], 7) + " ";
354 }
355 else
356 {
357 tableline += "------- ";
358 }
359 }
360 tableline += "-------";
361 m_tables.push_back(tableline);
362 }
363 } // MakeReport
364
365 //------------------------------------------------------------------------------------
366
FormatReport(const ChainOut & chout,bool current,long int linelength) const367 vector<string> RunReport::FormatReport(const ChainOut& chout, bool current,
368 long int linelength) const
369 {
370 bool bayesanalysis = registry.GetChainParameters().IsBayesian();
371 string skiptimestamp;
372 if (current) skiptimestamp = " ";
373 else skiptimestamp = "";
374
375 vector<string> report;
376 vector<string> temptable;
377
378 // header
379 //If this is a summary over regions or replicates, we do not want the
380 // header information at all. Everything is set 'FLAGLONG' or 'FLAGDOUBLE'
381 // so we check that. It's sort of a hack, but enh.
382 if (!(chout.GetAccrate() == FLAGDOUBLE &&
383 chout.GetLlikedata() == FLAGDOUBLE &&
384 chout.GetNumBadTrees() == FLAGDOUBLE &&
385 chout.GetTinyPopTrees() == FLAGDOUBLE &&
386 chout.GetZeroDLTrees() == FLAGDOUBLE &&
387 chout.GetStretchedTrees() == FLAGDOUBLE ))
388 {
389 string tempstring = "";
390 if (current) tempstring = PrintTime(GetTime()) + " ";
391 tempstring += "Accepted ";
392 if (chout.GetAccrate() == FLAGDOUBLE)
393 {
394 tempstring += UNKNOWN_STRING;
395 }
396 else
397 {
398 tempstring += MakeJustified(ToString(100.0 * chout.GetAccrate()), 5) + "%";
399 }
400 if (bayesanalysis)
401 tempstring += " | Point Likelihood " + Pretty(chout.GetLlikemle(), 10);
402 else
403 tempstring += " | Posterior lnL " + Pretty(chout.GetLlikemle(), 10);
404 tempstring += " | Data lnL ";
405 if (chout.GetLlikedata() == NEG_MAX)
406 {
407 tempstring += UNKNOWN_STRING;
408 }
409 else
410 {
411 tempstring += Pretty(chout.GetLlikedata(), 10);
412 }
413 report.push_back(tempstring);
414 long int badtrees = chout.GetNumBadTrees();
415 long int tinytrees = chout.GetTinyPopTrees();
416 long int zerodltrees = chout.GetZeroDLTrees();
417 long int stretchedtrees = chout.GetStretchedTrees();
418 if (badtrees == 0 && tinytrees == 0 &&
419 (zerodltrees == 0 || zerodltrees == FLAGLONG) &&
420 (stretchedtrees == 0 || stretchedtrees == FLAGLONG))
421 {
422 tempstring = "No trees discarded due to limit violations.";
423 report.push_back(tempstring);
424 }
425 else
426 {
427 tempstring = "Trees discarded due to too many events: ";
428 if (badtrees == FLAGLONG)
429 {
430 tempstring += UNKNOWN_STRING;
431 }
432 else
433 {
434 tempstring += Pretty(badtrees);
435 }
436 report.push_back(tempstring);
437
438 tempstring = "Trees discarded due to too small population sizes: ";
439 if (tinytrees == FLAGLONG)
440 {
441 tempstring += UNKNOWN_STRING;
442 }
443 else
444 {
445 tempstring += Pretty(tinytrees);
446 }
447 report.push_back(tempstring);
448
449 tempstring = "Trees discarded due to an infinitesimal data likelihood: ";
450 if (zerodltrees == FLAGLONG)
451 {
452 tempstring += UNKNOWN_STRING;
453 }
454 else
455 {
456 tempstring += Pretty(zerodltrees);
457 }
458 report.push_back(tempstring);
459
460 tempstring = "Trees discarded due to extremely long branch lengths: ";
461 if (stretchedtrees == FLAGLONG)
462 {
463 tempstring += UNKNOWN_STRING;
464 }
465 else
466 {
467 tempstring += Pretty(stretchedtrees);
468 }
469 report.push_back(tempstring);
470 }
471 // If multiple Arrangers, print an Arranger report
472 ratemap arrates = chout.GetAllAccrates();
473 ratemap::const_iterator rate;
474 if (arrates.size() > 1)
475 {
476 for (rate = arrates.begin(); rate != arrates.end(); ++rate)
477 {
478 report.push_back(MakeJustified(rate->first + " accepted ", -28) + " " +
479 MakeJustified(ToString(rate->second.first), 8) + "/" +
480 ToString(rate->second.second) + " proposals");
481 }
482 }
483 }
484
485 LongVec1d bayesunique = chout.GetBayesUnique();
486 if (bayesunique.size()>0)
487 {
488 //We have bayesian stuff.
489 report.push_back("");
490 report.push_back("Number of unique sampled values for each parameter:");
491 const ParamVector paramvec(true);
492 assert(bayesunique.size() == paramvec.size());
493 bool toofew = false;
494 for (unsigned long int pnum = 0; pnum < paramvec.size(); pnum++)
495 {
496 ParamStatus mystatus = paramvec[pnum].GetStatus();
497 if (mystatus.Varies())
498 {
499 string numbayes = ToString(bayesunique[pnum]);
500 if (bayesunique[pnum] < 50)
501 {
502 toofew = true;
503 numbayes = numbayes + "*";
504 }
505 report.push_back(MakeJustified(numbayes, std::max(static_cast<int>(numbayes.size()), 6))
506 + ": " + MakeJustified(paramvec[pnum].GetName(),-70));
507 }
508 }
509 if (toofew)
510 {
511 report.push_back("* Note! This parameter has too few sampled values to provide a reasonable estimate. "
512 "Consider collecting more samples or narrowing your prior for this value.");
513 }
514 report.push_back("");
515 }
516
517 // Actually print the estimates
518 if (m_scalars.size())
519 {
520 report.push_back(skiptimestamp + m_scalars);
521 }
522 temptable.insert(temptable.end(),m_tables.begin(),m_tables.end());
523
524 temptable = Linewrap(temptable, linelength);
525
526 transform(temptable.begin(),temptable.end(),temptable.begin(),
527 bind1st(plus<string>(),skiptimestamp));
528 report.insert(report.end(),temptable.begin(),temptable.end());
529
530 return(report);
531 } // FormatReport
532
533 //------------------------------------------------------------------------------------
534
DisplayReport(const ChainOut & chout)535 void RunReport::DisplayReport(const ChainOut& chout)
536 {
537 MakeReport(chout);
538 vector<string> rpt = FormatReport(chout,true,80);
539 for (unsigned long int it = 0; it < rpt.size(); ++it)
540 {
541 ReportNormal(rpt[it], false, 80);
542 }
543 } // DisplayReport
544
545 //------------------------------------------------------------------------------------
546
SetBarParameters(long int totsteps,long int burn,long int chno,long int chtype)547 void RunReport::SetBarParameters(long int totsteps, long int burn, long int chno, long int chtype)
548 {
549 m_totalsteps = totsteps;
550 m_chainno = chno;
551 m_burnin = burn;
552 m_counter_display_increment = 10;
553 if (m_totalsteps % 10L != 0)
554 {
555 m_counter_display_increment = registry.GetChainParameters().GetInterval(chtype);
556 }
557 m_burnpercent = 100.0 * static_cast<double>(burn)/static_cast<double>(totsteps);
558 m_chaintype_num = chtype;
559 switch (chtype)
560 {
561 case 0:
562 m_chaintype = "Initial";
563 break;
564 case 1:
565 m_chaintype = "Final";
566 break;
567 default:
568 assert(false);
569 m_chaintype = "Unknown";
570 break;
571 }
572 } // SetBarParameters
573
574 //------------------------------------------------------------------------------------
575
ReportUrgent(const string & msg,bool printtime,long int linelength)576 void RunReport::ReportUrgent(const string& msg, bool printtime, long int linelength)
577 {
578 if (m_level != NONE)
579 {
580 PrettyPrint(msg, printtime, linelength);
581 }
582 if (m_filelevel != NONE)
583 {
584 SaveOutput(msg, printtime);
585 }
586 }
587
ReportNormal(const string & msg,bool printtime,long int linelength)588 void RunReport::ReportNormal(const string& msg, bool printtime, long int linelength)
589 {
590 if (m_level == NORMAL || m_level==VERBOSE)
591 {
592 PrettyPrint(msg, printtime, linelength);
593 }
594 if (m_filelevel == NORMAL || m_filelevel==VERBOSE)
595 {
596 SaveOutput(msg, printtime);
597 }
598 }
599
ReportNormal(const StringVec1d & msgs,bool printtime,long int linelength)600 void RunReport::ReportNormal(const StringVec1d& msgs, bool printtime, long int linelength)
601 {
602 if (msgs.size() > 0)
603 {
604 ReportNormal(msgs[0], printtime, linelength);
605 }
606 for (unsigned long int line = 1; line<msgs.size(); line++)
607 {
608 ReportNormal(" " + msgs[line], false, linelength);
609 }
610 }
611
ReportChat(const string & msg,bool printtime,long int linelength)612 void RunReport::ReportChat(const string& msg, bool printtime, long int linelength)
613 {
614 if (m_level==VERBOSE)
615 {
616 PrettyPrint(msg, printtime, linelength);
617 }
618 if (m_filelevel==VERBOSE)
619 {
620 SaveOutput(msg, printtime);
621 }
622 }
623
ReportDebug(const string & msg,bool printtime,long int linelength)624 void RunReport::ReportDebug(const string& msg, bool printtime, long int linelength)
625 {
626 #ifdef NDEBUG
627 return;
628 #else
629 string debugmsg = "Debug message: " + msg;
630 PrettyPrint(debugmsg, printtime, linelength);
631 SaveOutput(debugmsg, printtime);
632 #endif
633 }
634
ReportOnce(const string & msg,OnceKey key,bool doNormal)635 void RunReport::ReportOnce(const string& msg, OnceKey key, bool doNormal)
636 {
637 if (m_keyfrequencies.find(key) == m_keyfrequencies.end())
638 {
639 //This is the first time we've seen this message. Add it, and report
640 // it to the user, either with ReportNormal or ReportDebug.
641 m_keyfrequencies.insert(make_pair(key, 1L));
642 if (doNormal)
643 {
644 ReportNormal(msg);
645 m_keyindices.insert(make_pair(static_cast<long int>(m_messages.size())-1, key));
646 }
647 else
648 {
649 ReportDebug(msg);
650 #ifndef NDEBUG
651 m_keyindices.insert(make_pair(static_cast<long int>(m_messages.size())-1, key));
652 #endif
653 }
654 //Whether we actually reported it or not, we stick it in the index. This
655 // forces each OnceKey to come from exactly one call (or that all such
656 // calls be the same version of doNormal); at present, there
657 // are only four such calls, and they're all unique.
658 }
659 else
660 {
661 //We've seen this message before. Increment its count, but don't report.
662 KeyToLongMapiter oldkey = m_keyfrequencies.find(key);
663 long int count = oldkey->second;
664 count++;
665 m_keyfrequencies.erase(oldkey);
666 m_keyfrequencies.insert(make_pair(key, count));
667 }
668 }
669
670 //Private--use one of the above routines instead.
PrettyPrint(const string & msg,bool printtime,long int linelength)671 void RunReport::PrettyPrint(const string& msg, bool printtime, long int linelength)
672 {
673 vector<string> msgline;
674 msgline.push_back(msg);
675 long int full_line = linelength - 10;
676
677 string timestr, skiptime;
678 #ifdef NOTIME_FUNC
679 // system does not provide a clock
680 timestr = "";
681 skiptime = "";
682 full_line = linelength;
683 #else
684 if (printtime)
685 {
686 time_t tnow = GetTime();
687 timestr = PrintTime(tnow) + " ";
688 skiptime = " ";
689 }
690 else
691 {
692 timestr = "";
693 skiptime = "";
694 full_line = linelength;
695 }
696 #endif
697
698 vector<string> msglines = Linewrap(msgline, full_line);
699
700 vector<string>::iterator oneline = msglines.begin();
701 cout << timestr << (*oneline) << endl;
702 for(oneline++; oneline != msglines.end(); ++oneline)
703 cout << skiptime << (*oneline) << endl;
704 }
705
SaveOutput(const string & msg,bool printtime)706 void RunReport::SaveOutput(const string& msg, bool printtime)
707 {
708 string timestr;
709 #ifdef NOTIME_FUNC
710 // system does not provide a clock
711 timestr = "";
712 #else
713 if (printtime)
714 {
715 time_t tnow = GetTime();
716 timestr = PrintTime(tnow) + " ";
717 }
718 else
719 {
720 timestr = "";
721 }
722 #endif
723 m_messages.push_back(timestr + msg);
724 }
725
726 //____________________________________________________________________________________
727