/* ----------------------------------------------------------------------------- The copyright in this software is being made available under the Clear BSD License, included below. No patent rights, trademark rights and/or other Intellectual Property Rights other than the copyrights concerning the Software are granted under this license. The Clear BSD License Copyright (c) 2018-2023, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVdeC Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: * Redistributions of source code must retain the above copyright 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 the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. 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 HOLDER 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. ------------------------------------------------------------------------------------------- */ #pragma once #include #include #include #include #include #ifndef THROW #define THROW(x) throw( Exception( "\nERROR: In function \"" ) << __FUNCTION__ << "\" in " << __FILE__ << ":" << __LINE__ << ": " << x ) #endif #ifndef CHECK #define CHECK(c,x) if(c){ THROW(x); } #endif namespace vvdec { namespace StatCounters { //////////////////////////////////////////////////////////////////////////////////// // StatCounter template class Counter { public: Counter() : m_counter( 0 ) {} Counter( T init_val ) : m_counter( init_val ) {} Counter( T init_val, int id ) : m_counter( init_val ) {} ~Counter(){} Counter& operator+=( const Counter& other ) { m_counter += other.m_counter; return *this; } Counter& operator+=( const T& other ) { m_counter += other; return *this; } Counter& operator-=( const Counter& other ) { m_counter -= other.m_counter; return *this; } Counter& operator= ( const T& other ) { m_counter = other; return *this; } Counter& operator/= ( const T& other ) { m_counter /= other; return *this; } bool operator< ( const T& other ) { return m_counter < other; } bool operator> ( const T& other ) { return m_counter > other; } bool operator>= ( const T& other ) { return !( m_counter < other ); } bool operator<= ( const T& other ) { return !( m_counter > other ); } Counter& operator++() { ++m_counter; return *this; } Counter& operator++(int) { m_counter++; return *this; } friend std::ostream& operator<<( std::ostream& os, const Counter& cnt ) { os << cnt.m_counter; return os; } friend std::istream& operator>>( std::istream& os, Counter& cnt ) { os >> cnt.m_counter; return os; } void reset( T val ) { m_counter = val; } T& val() { return m_counter; } const T& val() const { return m_counter; } protected: T m_counter; }; template class StatCounter : public Counter { public: StatCounter() : Counter() , m_counter_name( "" ) , m_counter_id( 0 ) , m_dependenceIdx( -1 ), m_isPercentageOutput( false ) {} StatCounter( T init_val ) : Counter( init_val ), m_counter_name( "" ) , m_counter_id( 0 ) , m_dependenceIdx( -1 ), m_isPercentageOutput( false ) {} StatCounter( T init_val, const char* name ) : Counter( init_val ), m_counter_name( name ), m_counter_id( 0 ) , m_dependenceIdx( -1 ), m_isPercentageOutput( false ) {} StatCounter( T init_val, const char* name, int id ) : Counter( init_val ), m_counter_name( name ), m_counter_id( id ), m_dependenceIdx( -1 ), m_isPercentageOutput( false ) {} StatCounter( T init_val, int id ) : Counter( init_val ), m_counter_name( "" ) , m_counter_id( id ), m_dependenceIdx( -1 ), m_isPercentageOutput( false ) {} ~StatCounter(){} std::ostream& name( std::ostream& os ) { os << Counter::m_counter; return os; } void setName( const char* name ) { m_counter_name = std::string( name ); } void setName( const std::string& name ) { m_counter_name = name; } const std::string getName() const { return m_counter_name; } int id() { return m_counter_id; } const int& id() const { return m_counter_id; } double percentageFrom( const T& v ) const { return (double)Counter::m_counter * 100.0 / (double)v; } double percentageFrom( const StatCounter& other ) const { return (double)Counter::m_counter * 100.0 / (double)other.m_counter; } double diffAndPercentageFrom( const StatCounter& other ) const { return (double)(Counter::m_counter - other.m_counter) * 100.0 / (double)other.m_counter; } void setPercentageDependence( size_t idx ) { m_dependenceIdx = idx, m_isPercentageOutput = true; } bool isPercentageOutput() const { return m_isPercentageOutput; } size_t getDependenceIdx() const { return m_dependenceIdx; } std::ostream& streamOutValueInPercentage( std::ostream& os, T dep, size_t w ) const { double percentage = dep ? percentageFrom( dep ) : 0.0; os << std::fixed << std::setw( w - 1 ) << std::setprecision( 1 ) << percentage << "%"; return os; } std::ostream& streamOutValueInPercentage( std::ostream& os, const StatCounter& cntDep, size_t w ) const { streamOutValueInPercentage( os, cntDep.val(), w ); return os; } private: std::string m_counter_name; int m_counter_id; size_t m_dependenceIdx; bool m_isPercentageOutput; }; //////////////////////////////////////////////////////////////////////////////////// // StatCountersSet template class StatCountersSet { public: StatCountersSet(){} StatCountersSet( size_t num_counters, T init_val = 0 ){ m_counters.resize( num_counters, init_val ); } StatCountersSet( std::vector cntNames, T init_val = 0 ) { for( int i = 0; i < cntNames.size(); i++ ) { addCounter( i, cntNames[i], init_val ); } } ~StatCountersSet(){} void addCounter( const int id, const std::string& name, T init_val = 0 ) { m_counters.push_back( StatCounter{ init_val, name.c_str(), id } ); } void resize( size_t num_counters, T init_val ) { m_counters.resize( num_counters, init_val ); for( size_t i = 0; i < m_counters.size(); i++ ) m_counters[i].setName( std::to_string( i ) ); } StatCountersSet& operator+=( const StatCountersSet& other ) { auto i1 = m_counters.begin(); auto i2 = other.m_counters.cbegin(); for( ; i1 != m_counters.end() && i2 != other.m_counters.cend(); ++i1, ++i2 ) { *i1 += *i2; } return *this; } StatCountersSet& operator-=( const StatCountersSet& other ) { auto i1 = m_counters.begin(); auto i2 = other.m_counters.cbegin(); for( ; i1 != m_counters.end() && i2 != other.m_counters.cend(); ++i1, ++i2 ) { *i1 -= *i2; } return *this; } StatCountersSet& operator= ( const T& other ) { m_counters[0] = other; return *this; } StatCountersSet& operator/= ( const T& other ) { for( size_t i = 0; i < m_counters.size(); i++ ) m_counters[i] /= other; return *this; } StatCountersSet& operator= ( const StatCountersSet& other ) { CHECK( m_counters.size() != other.m_counters.size(), "Accessing counters with differen number of elements!" ); for( size_t i = 0; i < m_counters.size(); i++ ) m_counters[i] = other.m_counters[i]; return *this; } StatCounter& operator[]( std::size_t idx ) { return m_counters[idx]; } const StatCounter& operator[]( std::size_t idx ) const { return m_counters[idx]; } void inc( int cntIdx ) { ++m_counters[cntIdx]; } void add( int cntIdx, T val ) { m_counters[cntIdx] += val; } void reset( T val ) { for( size_t i = 0; i < m_counters.size(); i++ ) m_counters[i].reset( val ); } void scale( T val ) { for( size_t i = 0; i < m_counters.size(); i++ ) m_counters[i] /= val; } size_t size() const { return m_counters.size(); } T total( T accum = 0 ) { for( size_t i = 0; i < m_counters.size(); i++ ) accum += m_counters[i].val(); //accum = std::accumulate( m_counters.begin(), m_counters.end() - 1, accum, std::plus>() ); return accum; } T sumUp( size_t fromPos = 0, T accum = 0 ) { if( fromPos >= 0 && fromPos < m_counters.size() ) { for( size_t i = fromPos; i < m_counters.size(); i++ ) accum += m_counters[i].val(); } return accum; } std::ostream& streamOutNames( std::ostream& os, size_t w ) const { for( size_t i = 0; i < m_counters.size(); i++ ) os << std::setw( (m_counters[i].isPercentageOutput() ? w*2: w) ) << m_counters[i].getName(); return os; } std::ostream& streamOutValuesInPercentage( std::ostream& os, T dep, int w ) const { for( size_t i = 0; i < m_counters.size(); i++ ) { os << std::setw( w ) << m_counters[i]; m_counters[i].streamOutValueInPercentage( os, dep, w ); } return os; } std::ostream& streamOutValuesInPercentage( std::ostream& os, const StatCounter& cntDep, int w ) const { for( size_t i = 0; i < m_counters.size(); i++ ) { os << std::setw( w ) << m_counters[i]; m_counters[i].streamOutValueInPercentage( os, cntDep, w ); } return os; } std::ostream& streamOutValues( std::ostream& os, size_t w ) const { for( size_t i = 0; i < m_counters.size(); i++ ) { os << std::setw( w ) << m_counters[i]; if( m_counters[i].isPercentageOutput() ) m_counters[i].streamOutValueInPercentage( os, m_counters[m_counters[i].getDependenceIdx()].val(), w ); } return os; } std::ostream& streamOutResults( std::ostream& os, size_t w, bool isHorizontal = true ) { if( isHorizontal ) { //streamOutNames( os, w ); streamOutValues( os, w ); } else { size_t maxW = 0; for( size_t i = 0; i < m_counters.size(); i++ ) if( m_counters[i].getName().length() > maxW ) maxW = m_counters[i].getName().size(); for( size_t i = 0; i < m_counters.size(); i++ ) { os << std::setw( maxW > w ? maxW: w ) << m_counters[i].getName() << ": "; os << std::setw( w ) << m_counters[i]; if( m_counters[i].isPercentageOutput() ) m_counters[i].streamOutValueInPercentage( os, m_counters[m_counters[i].getDependenceIdx()].val(), w ); os << std::endl; } } return os; } std::ostream& streamOutValuesInPercentage( std::ostream& os ) { T totalSum = total(); for( size_t i = 0; i < m_counters.size(); i++ ) os << std::fixed << std::setw( 6 ) << std::setprecision( 1 ) << m_counters[i].percentageFrom( totalSum ); return os; } inline bool isEmpty() { for( size_t i = 0; i < m_counters.size(); i++ ){ if( m_counters[i].val() ) return false; } return true; } //template friend std::ostream& operator<<( std::ostream& os, const StatCountersSet& cnt ) { if( !cnt.m_counters.empty() ) { size_t i = 0; for( ; i < cnt.m_counters.size() - 1; i++ ) os << cnt.m_counters[i] << " "; os << cnt.m_counters[i]; } return os; } friend std::istream& operator>>( std::istream& os, StatCountersSet& cnt ) { if( !cnt.m_counters.empty() ) { size_t i = 0; for( ; i < cnt.m_counters.size() - 1; i++ ) os >> cnt.m_counters[i]; os >> cnt.m_counters[i]; } return os; } protected: std::vector> m_counters; }; template class StatCounterSetMapped : public StatCountersSet { public: StatCounterSetMapped() {} StatCounterSetMapped( std::vector cntId, T init_val = 0 ) { for( int i = 0; i < cntId.size(); i++ ) addCounter( cntId[i], std::to_string( i ), init_val ); } StatCounterSetMapped( std::vector cntId, std::vector cntNamesLUT, T init_val = 0 ) { for( int i = 0; i < cntId.size(); i++ ) { CHECK( cntId[i] >= cntNamesLUT.size(), "Name for id failed!" ); addCounter( cntId[i], cntNamesLUT[cntId[i]], init_val ); } } StatCounterSetMapped( std::vector cntNamesLUT, T init_val = 0 ) { for( int i = 0; i < cntNamesLUT.size(); i++ ) { addCounter( i, cntNamesLUT[i], init_val ); } } ~StatCounterSetMapped() {} void addCounter( const int id, const std::string& name, T init_val = 0 ) { StatCountersSet::addCounter( id, name, init_val ); m_id_map.insert( std::make_pair( id, m_id_map.size() ) ); } size_t cntPos( int cntIdx ) { auto it = m_id_map.find( cntIdx ); if( it != m_id_map.end() ) return it->second; else THROW( "Not found" ); } StatCounter& operator[]( int id ) { return StatCountersSet::m_counters[cntPos( id )]; } const StatCounter& operator[]( int id ) const { return StatCountersSet::m_counters[cntPos( id )]; } StatCounter& cnt( int idx ) { return StatCountersSet::m_counters[idx]; } const StatCounter& cnt( int idx ) const { return StatCountersSet::m_counters[idx]; } void inc( int id ) { ++StatCountersSet::m_counters[cntPos( id )]; } void add( int id, T val ) { StatCountersSet::m_counters[cntPos( id )] += val; } protected: std::map m_id_map; }; //////////////////////////////////////////////////////////////////////////////////// // Counter2D template class Counter2D { public: Counter2D() {} Counter2D( size_t xDim, size_t yDim ) { init( xDim, yDim ); } ~Counter2D(){} void init( size_t xDim, size_t yDim ) { m_counters.resize( yDim ); for( size_t y = 0; y < yDim; y++ ) { m_counters[y].resize( xDim ); } } public: T total() const { T accum = 0; for( size_t y = 0; y < m_counters.size(); y++ ) { for( size_t x = 0; x < m_counters[y].size(); x++ ) { accum += m_counters[y][x]; } } return accum; } void reset( const T val ) { for( size_t y = 0; y < m_counters.size(); y++ ) { for( size_t x = 0; x < m_counters[y].size(); x++ ) { m_counters[y][x] = val; } } } std::vector& operator[]( size_t y ) { return m_counters[y]; } const std::vector& operator[]( size_t y ) const { return m_counters[y]; } Counter2D& operator+=( const Counter2D& other ) { for( size_t y = 0; y < m_counters.size(); y++ ) { for( size_t x = 0; x < m_counters[y].size(); x++ ) { m_counters[y][x] += other.m_counters[y][x]; } } return *this; } Counter2D& operator/= ( const T& val ) { for( size_t y = 0; y < m_counters.size(); y++ ) { for( size_t x = 0; x < m_counters[y].size(); x++ ) { m_counters[y][x] /= val; } } return *this; } protected: std::vector> m_counters; }; //////////////////////////////////////////////////////////////////////////////////// // StatCounter2D template class StatCounter2D : public Counter2D { public: StatCounter2D() : Counter2D() , m_counter_name( "" ), m_counter_id( -1 ) {} StatCounter2D( size_t xDim, size_t yDim ) : Counter2D( xDim, yDim ), m_counter_name( "" ), m_counter_id( -1 ) {} StatCounter2D( size_t xDim, size_t yDim , const char* name ) : Counter2D( xDim, yDim ), m_counter_name( name ), m_counter_id( -1 ) {} StatCounter2D( size_t xDim, size_t yDim , const char* name, int id ) : Counter2D( xDim, yDim ), m_counter_name( name ), m_counter_id( id ) {} StatCounter2D( size_t xDim, size_t yDim , int id ) : Counter2D( xDim, yDim ), m_counter_name( "" ), m_counter_id( id ) {} ~StatCounter2D(){} std::ostream& name( std::ostream& os ) { os << Counter2D::m_counters; return os; } void setName( const char* name ) { m_counter_name = std::string( name ); } void setName( const std::string& name ) { m_counter_name = name; } const std::string getName() const { return m_counter_name; } int id() { return m_counter_id; } const int& id() const { return m_counter_id; } private: std::string m_counter_name; int m_counter_id; size_t m_dependenceIdx; bool m_isPercentageOutput; }; //////////////////////////////////////////////////////////////////////////////////// // StatCountersSet template class StatCounter2DSet { public: StatCounter2DSet(){} StatCounter2DSet( size_t num_counters, size_t xDim, size_t yDim , T init_val = 0 ) { m_counters.resize( num_counters ); reset( init_val ); m_xDim = xDim; m_yDim = yDim; } StatCounter2DSet( const std::vector cntNames, size_t xDim, size_t yDim, T init_val = 0 ) { init( cntNames, xDim, yDim, init_val ); } ~StatCounter2DSet(){} void init( const std::vector& cntNames, size_t xDim, size_t yDim, T init_val = 0 ) { for( int i = 0; i < cntNames.size(); i++ ) { addCounter( i, cntNames[i], xDim, yDim, init_val ); } m_xDim = xDim; m_yDim = yDim; } void init( unsigned numCounters, size_t xDim, size_t yDim, T init_val = 0 ) { for( int i = 0; i < numCounters; i++ ) { addCounter( i, "", xDim, yDim, init_val ); } m_xDim = xDim; m_yDim = yDim; } void init( size_t xDim, size_t yDim ) { m_xDim = xDim; m_yDim = yDim; } void addCounter( const int id, const std::string& name, size_t xDim, size_t yDim, T init_val = 0 ) { m_counters.push_back( StatCounter2D{ xDim, yDim, name.c_str(), id } ); m_counters.back().reset( init_val ); } void reset( T val = 0 ) { for( int i = 0; i < m_counters.size(); i++ ) m_counters[i].reset( val ); } void scale( T val ) { for( size_t j = 0; j < m_counters.size(); j++ ) m_counters[j] /= val; } T total( size_t xDim, size_t yDim ) const { T accum = 0; for( int i = 0; i < m_counters.size(); i++ ) { accum += m_counters[i][yDim][xDim]; } return accum; } T total( size_t cntId ) const { return m_counters[cntId].total(); } T total() const { T accum = 0; for( int i = 0; i < m_counters.size(); i++ ) { accum += m_counters[i].total(); } return accum; } void addOnXAxis( size_t xPosSrc1, size_t xPosSrc2, size_t xPosDst ) { for( size_t i = 0; i < m_counters.size(); i++ ) { for( size_t j = 0; j < m_yDim; j++ ) m_counters[i][j][xPosDst] = m_counters[i][j][xPosSrc1] + m_counters[i][j][xPosSrc2]; } } StatCounter2D& operator[]( int id ) { return m_counters[id]; } const StatCounter2D& operator[]( int id ) const { return m_counters[id]; } StatCounter2DSet& operator+=( const StatCounter2DSet& other ) { auto i1 = m_counters.begin(); auto i2 = other.m_counters.cbegin(); for( ; i1 != m_counters.end() && i2 != other.m_counters.cend(); ++i1, ++i2 ) { *i1 += *i2; } return *this; } std::vector>& getCounters() { return m_counters; } const std::vector>& getCounters() const { return m_counters; } size_t getDimHor() const { return m_xDim; } size_t getDimVer() const { return m_yDim; } size_t getNumCntTypes() const { return m_counters.size(); } protected: std::vector> m_counters; size_t m_xDim; size_t m_yDim; }; //////////////////////////////////////////////////////////////////////////////////// // StatCounter2DSetMapped template class StatCounter2DSetMapped : public StatCounter2DSet { public: StatCounter2DSetMapped() {} StatCounter2DSetMapped( const std::vector& cntId, size_t xDim, size_t yDim, T init_val = 0 ) { for( int i = 0; i < cntId.size(); i++ ) addCounter( cntId[i], std::to_string( i ), xDim, yDim, init_val ); } StatCounter2DSetMapped( const std::vector& cntId, const std::vector& cntNamesLUT, size_t xDim, size_t yDim, T init_val = 0 ) { init( cntId, cntNamesLUT, xDim, yDim, init_val ); } StatCounter2DSetMapped( const std::vector& cntNamesLUT, size_t xDim, size_t yDim, T init_val = 0 ) { for( int i = 0; i < cntNamesLUT.size(); i++ ) { addCounter( i, cntNamesLUT[i], xDim, yDim, init_val ); } } void init( const std::vector& cntId, const std::vector& cntNamesLUT, size_t xDim, size_t yDim, T init_val = 0 ) { StatCounter2DSet::init( xDim, yDim ); for( int i = 0; i < cntId.size(); i++ ) { CHECK( cntId[i] >= cntNamesLUT.size(), "Name for id failed!" ); addCounter( cntId[i], cntNamesLUT[cntId[i]], xDim, yDim, init_val ); } } ~StatCounter2DSetMapped() {} void addCounter( const int id, const std::string& name, size_t xDim, size_t yDim, T init_val = 0 ) { StatCounter2DSet::addCounter( id, name, xDim, yDim, init_val ); m_id_map.insert( std::make_pair( id, m_id_map.size() ) ); } size_t cntPos( int cntIdx ) const { auto it = m_id_map.find( cntIdx ); if( it != m_id_map.end() ) return it->second; else THROW( "Not found" ); } StatCounter2D& operator[]( int id ) { return StatCounter2DSet::m_counters[cntPos( id )]; } const StatCounter2D& operator[]( int id ) const { return StatCounter2DSet::m_counters[cntPos( id )]; } StatCounter2D& cnt( int idx ) { return StatCounter2DSet::m_counters[idx]; } const StatCounter2D& cnt( int idx ) const { return StatCounter2DSet::m_counters[idx]; } protected: std::map m_id_map; }; template std::ostream& report2D( std::ostream& os, const StatCounter2DSet& counters, bool axisInBlockSizes = false, bool absoluteNumbers = true, bool weightedByArea = false, bool secondColumnInPercentage = false, bool ratiosWithinSingleElement = false, int refCntId = -1 ); } }