#ifndef PERCENTILE_GENERATOR_H
#define PERCENTILE_GENERATOR_H
#pragma once
#include <tier0/dbg.h>
#include <vstdlib/random.h>
#include <tier0/memdbgoff.h>
#include <algorithm>
#include <tier0/memdbgon.h>
template < typename T, int MAX_SAMPLES = 1000 >
class CPercentileGenerator
{
public:
CPercentileGenerator() { Clear(); }
void Clear() { m_nSamples = m_nSamplesTotal = 0; m_bNeedSort = false; }
void AddSample( T x );
int NumSamples() const { return m_nSamples; }
int NumSamplesTotal() const { return m_nSamplesTotal; }
static int MaxSamples() { return MAX_SAMPLES; }
T GetPercentile( float flPct ) const;
const T *RawSamples() const { return m_arSamples; }
T *MutableRawSamples() { m_bNeedSort = true; return m_arSamples; }
void SetNumSamples( int n ) { m_nSamples = n; }
void SetNumSamplesTotal( int n ) { m_nSamplesTotal = n; }
void SetNeedSort() { m_bNeedSort = true; }
void Sort( bool bForce ) const;
private:
int m_nSamples;
int m_nSamplesTotal;
mutable bool m_bNeedSort;
T m_arSamples[ MAX_SAMPLES ];
};
template < typename T, int MAX_SAMPLES >
void CPercentileGenerator<T,MAX_SAMPLES>::AddSample( T x )
{
if ( m_nSamples < MAX_SAMPLES )
{
m_arSamples[ m_nSamples ] = x;
++m_nSamples;
m_bNeedSort = true;
}
else
{
int r = WeakRandomInt( 0, m_nSamplesTotal );
if ( r < MAX_SAMPLES )
{
m_arSamples[r] = x;
m_bNeedSort = true;
}
}
++m_nSamplesTotal;
}
template < typename T, int MAX_SAMPLES >
void CPercentileGenerator<T,MAX_SAMPLES>::Sort( bool bForce ) const
{
if ( bForce || m_bNeedSort )
{
T *pBegin = const_cast<T*>( m_arSamples );
std::sort( pBegin, pBegin+m_nSamples );
m_bNeedSort = false;
}
}
template < typename T, int MAX_SAMPLES >
T CPercentileGenerator<T,MAX_SAMPLES>::GetPercentile( float flPct )const
{
Assert( 0 < flPct && flPct < 1.0f );
if ( m_nSamples < 1 )
{
Assert( m_nSamples > 0 );
return T();
}
Sort(false);
float flIdx = flPct * float(m_nSamples-1);
if ( flIdx <= 0.0f )
return m_arSamples[ 0 ];
int idx = (int)flIdx;
if ( idx >= m_nSamples-1 )
return m_arSamples[m_nSamples-1];
float l = (float)m_arSamples[idx];
float r = (float)m_arSamples[idx+1];
return T( l + (r-l)*(flIdx-idx) );
}
#endif