/* -*- C++ -*-
 * src/lemon/time_measure.h - Part of LEMON, a generic C++ optimization library
 *
 * Copyright (C) 2005 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
 * (Egervary Combinatorial Optimization Research Group, EGRES).
 *
 * Permission to use, modify and distribute this software is granted
 * provided that this copyright notice appears in all copies. For
 * precise terms see the accompanying LICENSE file.
 *
 * This software is provided "AS IS" with no warranty of any kind,
 * express or implied, and with no claim as to its suitability for any
 * purpose.
 *
 */

#ifndef LEMON_TIME_MEASURE_H
#define LEMON_TIME_MEASURE_H

///\ingroup misc
///\file
///\brief Tools for measuring cpu usage

#include <sys/time.h>
#include <sys/times.h>
#include <fstream>
#include <iostream>
#include <unistd.h>

namespace lemon {

  /// \addtogroup misc
  /// @{

  /// A class to store (cpu)time instances.

  /// This class stores five time values.
  /// - a real time
  /// - a user cpu time
  /// - a system cpu time
  /// - a user cpu time of children
  /// - a system cpu time of children
  ///
  /// TimeStamp's can be added to or substracted from each other and
  /// they can be pushed to a stream.
  ///
  /// In most cases, perhaps \ref Timer class is what you want to use instead.
  ///
  ///\author Alpar Juttner

  class TimeStamp
  {
    tms ts;
    double real_time;
  
  public:

    tms &getTms() {return ts;}
    const tms &getTms() const {return ts;}
    ///Read the current time values of the process
    void stamp()
    {
      timeval tv;
      times(&ts);
      gettimeofday(&tv, 0);real_time=tv.tv_sec+double(tv.tv_usec)/1e6;
    }
  
    /// Constructor initializing with zero
    TimeStamp()
    { ts.tms_utime=ts.tms_stime=ts.tms_cutime=ts.tms_cstime=0; real_time=0;}
    ///Constructor initializing with the current time values of the process
    TimeStamp(void *) { stamp();}
  
    ///\e
    TimeStamp &operator+=(const TimeStamp &b)
    {
      ts.tms_utime+=b.ts.tms_utime;
      ts.tms_stime+=b.ts.tms_stime;
      ts.tms_cutime+=b.ts.tms_cutime;
      ts.tms_cstime+=b.ts.tms_cstime;
      real_time+=b.real_time;
      return *this;
    }
    ///\e
    TimeStamp operator+(const TimeStamp &b) const
    {
      TimeStamp t(*this);
      return t+=b;
    }
    ///\e
    TimeStamp &operator-=(const TimeStamp &b)
    {
      ts.tms_utime-=b.ts.tms_utime;
      ts.tms_stime-=b.ts.tms_stime;
      ts.tms_cutime-=b.ts.tms_cutime;
      ts.tms_cstime-=b.ts.tms_cstime;
      real_time-=b.real_time;
      return *this;
    }
    ///\e
    TimeStamp operator-(const TimeStamp &b) const
    {
      TimeStamp t(*this);
      return t-=b;
    }
    ///The time ellapsed since the last call of stamp()
    TimeStamp ellapsed() const
    {
      TimeStamp t(NULL);
      return t-*this;
    }
  
    friend std::ostream& operator<<(std::ostream& os,const TimeStamp &t);
  
    ///Gives back the user time of the process
    double getUserTime() const
    {
      return double(ts.tms_utime)/sysconf(_SC_CLK_TCK);
    }
    ///Gives back the system time of the process
    double getSystemTime() const
    {
      return double(ts.tms_stime)/sysconf(_SC_CLK_TCK);
    }
    ///Gives back the user time of the process' children
    double getCUserTime() const
    {
      return double(ts.tms_cutime)/sysconf(_SC_CLK_TCK);
    }
    ///Gives back the user time of the process' children
    double getCSystemTime() const
    {
      return double(ts.tms_cstime)/sysconf(_SC_CLK_TCK);
    }
    ///Gives back the real time of the process
    double getRealTime() const {return real_time;}
  };

  ///Class measuring the cpu time and real time usage of the process

  ///Class measuring the cpu time and real time usage of the process.
  ///It is quite easy-to-use, here is a short example.
  ///\code
  ///#include<lemon/time_measure.h>
  ///#include<iostream>
  ///
  ///int main()
  ///{
  ///
  ///  ...
  ///
  ///  Timer T;
  ///  doSomething();
  ///  std::cout << T << '\n';
  ///  T.reset();
  ///  doSomethingElse();
  ///  std::cout << T << '\n';
  ///
  ///  ...
  ///
  ///}
  ///\endcode
  ///
  ///\todo This shouldn't be Unix (Linux) specific.
  ///
  ///\author Alpar Juttner
  class Timer
  {
    TimeStamp start_time;

    void _reset() {start_time.stamp();}
  
  public: 
    ///Constructor. It starts with zero time counters
    Timer() {_reset();}

    ///Computes the ellapsed time

    ///This conversion computes the ellapsed time
    ///since the construction of \c t or since
    ///the last \c t.reset().
    operator TimeStamp () const
    {
      TimeStamp t;
      t.stamp();
      return t-start_time;
    }

    ///Resets the time counters

    ///Resets the time counters
    ///
    void reset()
    {
      _reset();
    }


    ///Gives back the ellapsed user time of the process
    double getUserTime() const
    {
      return operator TimeStamp().getUserTime();
    }
    ///Gives back the ellapsed system time of the process
    double getSystemTime() const
    {
      return operator TimeStamp().getSystemTime();
    }
    ///Gives back the ellapsed user time of the process' children
    double getCUserTime() const
    {
      return operator TimeStamp().getCUserTime();
    }
    ///Gives back the ellapsed user time of the process' children
    double getCSystemTime() const
    {
      return operator TimeStamp().getCSystemTime();
    }
    ///Gives back the ellapsed real time of the process
    double getRealTime() const
    {
      return operator TimeStamp().getRealTime();
    }

  };

  ///Prints the time counters

  ///Prints the time counters in the following form:
  ///
  /// <tt>u: XX.XXs s: XX.XXs cu: XX.XXs cs: XX.XXs real: XX.XXs</tt>
  ///
  /// where the values are the
  /// \li \c u: user cpu time,
  /// \li \c s: system cpu time,
  /// \li \c cu: user cpu time of children,
  /// \li \c cs: system cpu time of children,
  /// \li \c real: real time.
  /// \relates TimeStamp
  inline std::ostream& operator<<(std::ostream& os,const TimeStamp &t)
  {
    long cls = sysconf(_SC_CLK_TCK);
    os << "u: " << double(t.getTms().tms_utime)/cls <<
      "s, s: " << double(t.getTms().tms_stime)/cls <<
      "s, cu: " << double(t.getTms().tms_cutime)/cls <<
      "s, cs: " << double(t.getTms().tms_cstime)/cls <<
      "s, real: " << t.getRealTime() << "s";
    return os;
  }

  /// @}  

} //namespace lemon

#endif //LEMON_TIME_MEASURE_H
