/*
 
    Texel - A UCI chess engine.
 
    Copyright (C) 2013  Peter Ă–sterlund, peterosterlund2@gmail.com
 
 
 
    This program is free software: you can redistribute it and/or modify
 
    it under the terms of the GNU General Public License as published by
 
    the Free Software Foundation, either version 3 of the License, or
 
    (at your option) any later version.
 
 
 
    This program is distributed in the hope that it will be useful,
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
    GNU General Public License for more details.
 
 
 
    You should have received a copy of the GNU General Public License
 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
*/
 
 
 
/*
 
 * timeUtil.hpp
 
 *
 
 *  Created on: Sep 20, 2013
 
 *      Author: petero
 
 */
 
 
 
#ifndef TIMEUTIL_HPP_
 
#define TIMEUTIL_HPP_
 
 
 
#include "util.hpp"
 
 
 
#include <array>
 
#include <cmath>
 
#include <cassert>
 
 
 
 
 
/** Return current wall clock time in milliseconds, starting at some arbitrary point in time. */
 
S64 currentTimeMillis();
 
 
 
/** Return current wall clock time in seconds, starting at some arbitrary point in time. */
 
double currentTime();
 
 
 
 
 
 
 
/** Class that measures average CPU utilization. */
 
class UtilizationTimer {
 
public:
 
    /** Constructor. All times start at zero. */
 
    UtilizationTimer();
 
 
 
    /** Reset times to zero. */
 
    void reset();
 
 
 
    /** Set the current CPU efficiency factor to p.
 
     * A negative p indicates the CPU is idle. */
 
    void setPUseful(double p);
 
 
 
    /** Return elapsed time size last reset, the amount of useful time spent,
 
     * and the amount of time the CPU has been idle. */
 
    void getStats(double& elapsed, double& useful, double& sleep);
 
 
 
private:
 
    /** Update tElapsed, tUseful, tSleep and increase t0 to current time. */
 
    void update();
 
 
 
    double t0;          // Time stand for last update()
 
    double pUseful;     // Current CPU efficiency factor
 
 
 
    double tElapsed;    // Total elapsed time since reset()
 
    double tUseful;     // Total spent useful time
 
    double tSleep;      // total idle time.
 
};
 
 
 
/** Class that tracks statistics for a set of samples. */
 
class SampleStatistics {
 
public:
 
    /** Constructor. */
 
    SampleStatistics();
 
 
 
    /** Remove all samples. */
 
    void reset();
 
 
 
    /** Add a sample. */
 
    void addSample(double value);
 
 
 
    /** Return number of samples. */
 
    int numSamples() const;
 
    /** Return average sample value. */
 
    double avg() const;
 
    /** Return standard deviation of samples. */
 
    double std() const;
 
 
 
    /** Add other to *this. */
 
    SampleStatistics& operator+=(const SampleStatistics& other);
 
 
 
private:
 
    int nSamples;
 
    double sum;
 
    double sqSum;
 
};
 
 
 
/** Gather statistics about time samples. */
 
class TimeSampleStatistics {
 
public:
 
    /** Constructor. */
 
    TimeSampleStatistics();
 
 
 
    /** Start timer. */
 
    void start();
 
 
 
    /** Stop timer and add elapsed time as a sample. */
 
    void stop();
 
 
 
    /** Remove all time samples. */
 
    void reset();
 
 
 
    /** Return true if timer is currently running. */
 
    bool isStarted() const;
 
 
 
    /** Return number of time samples. */
 
    int numSamples() const;
 
    /** Return average sample time. */
 
    double avg() const;
 
    /** Return standard deviation of time samples. */
 
    double std() const;
 
 
 
    /** Print to "os", time values displayed in nanoseconds. */
 
    void printNs(std::ostream& os) const;
 
 
 
private:
 
    double t0;
 
    bool started;
 
    SampleStatistics stats;
 
};
 
 
 
/** A fixed size vector of TimeSampleStatistics objects. */
 
template <int N>
 
class TimeSampleStatisticsVector {
 
private:
 
    std::array<TimeSampleStatistics, N> vec;
 
 
 
public:
 
    TimeSampleStatistics& operator[](int i);
 
 
 
    using iterator = typename std::array<TimeSampleStatistics, N>::iterator;
 
    iterator begin();
 
    iterator end();
 
};
 
 
 
/** Utility function to add a time sample to a TimeSampleStatistics object.
 
 * The sample value is equal to the amount of time this object is in scope. */
 
class ScopedTimeSample {
 
public:
 
    ScopedTimeSample(TimeSampleStatistics& tStat);
 
    ~ScopedTimeSample();
 
    ScopedTimeSample(ScopedTimeSample&) = delete;
 
    ScopedTimeSample& operator=(const ScopedTimeSample&) = delete;
 
private:
 
    TimeSampleStatistics& timeStat;
 
};
 
 
 
 
 
inline
 
UtilizationTimer::UtilizationTimer() {
 
    reset();
 
}
 
 
 
inline void
 
UtilizationTimer::reset() {
 
    t0 = currentTime();
 
    pUseful = -1;
 
    tElapsed = 0;
 
    tUseful = 0;
 
    tSleep = 0;
 
}
 
 
 
inline void
 
UtilizationTimer::setPUseful(double p) {
 
    update();
 
    pUseful = p;
 
}
 
 
 
inline void
 
UtilizationTimer::getStats(double& elapsed, double& useful, double& sleep) {
 
    update();
 
    elapsed = tElapsed;
 
    useful = tUseful;
 
    sleep = tSleep;
 
}
 
 
 
inline void
 
UtilizationTimer::update() {
 
    double tNow = currentTime();
 
    double dt = tNow - t0;
 
    tElapsed += dt;
 
    if (pUseful >= 0)
 
        tUseful += dt * pUseful;
 
    else
 
        tSleep += dt;
 
    t0 = tNow;
 
}
 
 
 
 
 
inline
 
SampleStatistics::SampleStatistics() {
 
    reset();
 
}
 
 
 
inline void
 
SampleStatistics::reset() {
 
    nSamples = 0;
 
    sum = 0.0;
 
    sqSum = 0.0;
 
}
 
 
 
inline void
 
SampleStatistics::addSample(double value) {
 
    nSamples++;
 
    sum += value;
 
    sqSum += value * value;
 
}
 
 
 
inline int
 
SampleStatistics::numSamples() const {
 
    return nSamples;
 
}
 
 
 
inline double
 
SampleStatistics::avg() const {
 
    return nSamples > 0 ? sum / nSamples : 0;
 
}
 
 
 
inline double
 
SampleStatistics::std() const {
 
    if (nSamples < 2)
 
        return 0;
 
    return ::sqrt((sqSum - sum*sum / nSamples) / (nSamples - 1));
 
}
 
 
 
inline void
 
TimeSampleStatistics::printNs(std::ostream& os) const {
 
    os << numSamples()
 
       << ' ' << static_cast<int>(avg() * 1e9)
 
       << ' ' << static_cast<int>(std() * 1e9);
 
}
 
 
 
 
 
inline
 
TimeSampleStatistics::TimeSampleStatistics() {
 
    reset();
 
}
 
 
 
inline void
 
TimeSampleStatistics::start() {
 
    t0 = currentTime();
 
    started = true;
 
}
 
 
 
inline void
 
TimeSampleStatistics::stop() {
 
    double now = currentTime();
 
    stats.addSample(now - t0);
 
    started = false;
 
}
 
 
 
inline void
 
TimeSampleStatistics::reset() {
 
    stats.reset();
 
    started = false;
 
}
 
 
 
inline bool
 
TimeSampleStatistics::isStarted() const {
 
    return started;
 
}
 
 
 
inline int
 
TimeSampleStatistics::numSamples() const {
 
    return stats.numSamples();
 
}
 
 
 
inline double
 
TimeSampleStatistics::avg() const {
 
    return stats.avg();
 
}
 
 
 
inline double
 
TimeSampleStatistics::std() const {
 
    return stats.std();
 
}
 
 
 
template <int N>
 
inline TimeSampleStatistics&
 
TimeSampleStatisticsVector<N>::operator[](int i) {
 
    return vec[i];
 
}
 
 
 
template <int N>
 
inline typename TimeSampleStatisticsVector<N>::iterator
 
TimeSampleStatisticsVector<N>::begin() {
 
    return vec.begin();
 
}
 
 
 
template <int N>
 
inline typename TimeSampleStatisticsVector<N>::iterator
 
TimeSampleStatisticsVector<N>::end() {
 
    return vec.end();
 
}
 
 
 
 
 
inline
 
ScopedTimeSample::ScopedTimeSample(TimeSampleStatistics& tStat)
 
  : timeStat(tStat) {
 
    assert(!timeStat.isStarted());
 
    timeStat.start();
 
}
 
 
 
inline
 
ScopedTimeSample::~ScopedTimeSample() {
 
    assert(timeStat.isStarted());
 
    timeStat.stop();
 
}
 
 
 
 
 
#endif /* TIMEUTIL_HPP_ */