

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.1.1.260726 (26 July 2026)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2026
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/*! @file Stats.h
    @brief Thread-safe running statistics over a sliding window of samples.

    Stats keeps up to @c maxCount most recent values (older ones are dropped)
    and computes aggregate measures on demand. Used throughout CMSDK for
    latency/throughput bookkeeping. */
#if !defined(_STATS_H_)
#define _STATS_H_

#include "Utils.h"

namespace cmlabs {

/*! @brief Sliding-window sample collector with average, variance, standard deviation and median.

    Unlike MovingAverage (which only keeps per-bin sums), Stats retains the
    individual sample values in a std::list, capped at @c maxCount entries;
    add() evicts the oldest entry once the cap is reached. This makes
    order-dependent statistics (median, variance) possible at the cost of
    O(n) memory and O(n) or O(n log n) query time, so it is intended for
    moderate window sizes (the default cap is 10000), not unbounded streams.

    All public methods take the internal mutex (recursive — aggregate getters
    may call each other under the lock) and are safe to call from multiple
    threads. Each getter walks the sample list while holding the lock, so
    very large windows queried at high frequency will contend.

    @code
    Stats latency(1000);              // keep the 1000 most recent samples
    latency.add(12.5);                // one measurement in ms
    latency.add(9.1);

    double mean = latency.getAverage();
    double sd   = latency.getStdDev();
    double med  = latency.getMedian();
    @endcode

    @warning getMedian() sorts the underlying sample list in place. Afterwards
    the list is ordered by value rather than by insertion time, so subsequent
    evictions at the cap remove the smallest value instead of the oldest
    sample. Avoid interleaving getMedian() with continued add() calls when
    strict oldest-first window semantics matter. */
class Stats {
public:
	/*! @brief Self test. @return true when all checks pass. */
	static bool UnitTest();

	/*! @brief Create a collector. @param maxCount Maximum samples retained; adding beyond this evicts the oldest. */
	Stats(uint32 maxCount = 10000);
	~Stats();
	
	/*! @brief Remove all samples. @return true on success. */
	bool clear();
	/*! @brief Add a sample, evicting the oldest when the window is full. @param val Sample value. @return true on success. */
	bool add(double val);

	double getAverage(); //!< @return Mean of the current window. @warning Divides by the sample count without an empty check; returns NaN/inf when no samples are present — check getCount() first.
	double getSum(); //!< @return Sum of the current window.
	uint32 getCount(); //!< @return Number of samples currently held.
	double getVariance(); //!< @return Population variance (sum of squared deviations divided by n, not n-1); 0 when fewer than 2 samples.
	double getStdDev(); //!< @return Population standard deviation (square root of getVariance()).
	double getMedian(); //!< @return Median of the current samples (0 when empty; mean of the two middle values for an even count). @note O(n log n): sorts the internal list <b>in place</b>, destroying insertion order — see the class-level warning.

protected:
	uint32 maxCount;
	utils::Mutex mutex;
	std::list<double> entries;
};


} // namespace cmlabs

#endif //_STATS_H_

