/** Counter.h ---
*
* Copyright (C) 2011 OpenCog Foundation
*
* \author Nil Geisweiller
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License v3 as
* published by the Free Software Foundation and including the exceptions
* at http://opencog.org/wiki/Licenses
*
* 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 Affero General Public License
* along with this program; if not, write to:
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _OPENCOG_COUNTER_H
#define _OPENCOG_COUNTER_H
#include <initializer_list>
#include <map>
#include <numeric>
#include <ostream>
#include <set>
namespace opencog {
/** \addtogroup grp_cogutil
* @{
*/
//! Class that mimics python Counter container
/**
* This is basically a dictionary of key:count values.
* Given following pseudocode:
* @code
* for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
* cnt[word] += 1
* @endcode
* the counter will hold 'blue': 3, 'red': 2, 'green': 1
*/
template<typename T, typename CT, typename CMP = std::less<T>>
class Counter : public std::map<T, CT, CMP>
{
protected:
/** @todo this will be replaced by C++11 constructor
* delegation instead of init
*/
template<typename IT>
void init(IT from, IT to) {
while(from != to) {
//we don't use ++ to put the least assumption on on CT
this->operator[](*from) += 1;
++from;
}
}
public:
typedef std::map<T, CT, CMP> super;
typedef typename super::value_type value_type;
Counter() {}
template<typename IT>
Counter(IT from, IT to)
{
init(from, to);
}
template<typename Container>
Counter(const Container& c)
{
init(c.begin(), c.end());
}
Counter(const std::initializer_list<value_type>& il)
{
for(const auto& v : il)
this->operator[](v.first) = v.second;
}
/// Return the count of a key, possibly returning a default if none
/// is present. That method different that operator[] because it
/// doesn't insert the element if it is not present. This is very
/// useful for multi-threading programming, by helping avoid race
/// conditions.
CT get(const T& key, CT c = CT()) const
{
typename super::const_iterator it = this->find(key);
return it == this->cend()? c : it->second;
}
//! Return the total of all counted elements
CT total_count() const
{
CT sum = CT(0);
for (const auto& pair : *this) {
sum += pair.second;
}
return sum;
}
//! Return the mode, the element that occurs most frequently
T mode() const
{
T key = super::begin()->first;
CT cnt = super::begin()->second;
for (const auto& v : *this) {
if (cnt < v.second)
key = v.first;
}
return key;
}
/* Counter operators:
* examples with:
* c1 = {'a':1, 'b':2}
* c2 = {'b':2, 'c':3}
* v = 2
*/
//! add 2 counters,
/**
* c1 += c2
* =>
* c1 = {'a':1, 'b':4, 'c':3}
*/
Counter& operator+=(const Counter& other) {
for (const auto& v : other)
this->operator[](v.first) += v.second;
return *this;
}
//! subtract 2 counters,
/**
* c1 -= c2
* =>
* c1 = {'a':1, 'b':0, 'c':-3}
*/
Counter& operator-=(const Counter& other) {
for (const auto& v : other)
this->operator[](v.first) -= v.second;
return *this;
}
//! multiply (inner product of) 2 counters,
/**
* c1 *= c2
* =>
* c1 = {'a':0, 'b':4, 'c':0}
*/
Counter& operator*=(const Counter& other) {
auto it = this->begin();
auto other_it = other.begin();
while (it != this->end() and other_it != other.end()) {
if (this->key_comp()(it->first, other_it->first)) {
it->second = CT();
++it;
} else if (this->key_comp()(other_it->first, it->first)) {
this->emplace_hint(it, other_it->first, CT());
++other_it;
} else {
it->second *= other_it->second;
++it;
++other_it;
}
}
for (; it != this->end(); ++it)
it->second = CT();
for (; other_it != other.end(); ++other_it)
this->emplace_hint(it, other_it->first, CT());
return *this;
}
//! divide 2 counters,
/**
* c1 /= c2
* =>
* c1 = {'a':1, 'b':1, 'c':0}
*/
Counter& operator/=(const Counter& other) {
for (const auto& v : other)
this->operator[](v.first) /= v.second;
return *this;
}
//! add CT to counter
/**
* c1 += v
* =>
* c1 = {'a':3, 'b':4}
*/
Counter& operator+=(const CT& num) {
for (auto& v : *this)
v.second += num;
return *this;
}
/**
* c1 -= v
* =>
* c1 = {'a':-1, 'b':0}
*/
Counter& operator-=(const CT& num) {
for (auto& v : *this)
v.second -= num;
return *this;
}
/**
* c1 *= v
* =>
* c1 = {'a':2, 'b':4}
*/
Counter& operator*=(const CT& num) {
for (auto& v : *this)
v.second *= num;
return *this;
}
/**
* c1 /= v
* =>
* c1 = {'a':0.5, 'b':1}
*/
Counter& operator/=(const CT& num) {
for (auto& v : *this)
v.second /= num;
return *this;
}
/**
* Return all keys.
*
* For instance:
* c1.keys() = {'a', 'b'}
*/
std::set<T> keys() const {
std::set<T> ks;
for (auto& v : *this)
ks.insert(v.first);
return ks;
}
// Binary operators with another Counter
// These were previously provided by boost::arithmetic
Counter operator+(const Counter& other) const {
Counter result(*this);
result += other;
return result;
}
Counter operator-(const Counter& other) const {
Counter result(*this);
result -= other;
return result;
}
Counter operator*(const Counter& other) const {
Counter result(*this);
result *= other;
return result;
}
Counter operator/(const Counter& other) const {
Counter result(*this);
result /= other;
return result;
}
// Binary operators with CT type
// These were previously provided by boost::arithmetic2
Counter operator+(const CT& num) const {
Counter result(*this);
result += num;
return result;
}
Counter operator-(const CT& num) const {
Counter result(*this);
result -= num;
return result;
}
Counter operator*(const CT& num) const {
Counter result(*this);
result *= num;
return result;
}
Counter operator/(const CT& num) const {
Counter result(*this);
result /= num;
return result;
}
};
template<typename T, typename CT, typename CMP = std::less<T>>
std::ostream& operator<<(std::ostream& out, const Counter<T, CT, CMP>& c)
{
typedef Counter<T, CT, CMP> counter_t;
out << "{";
for (typename counter_t::const_iterator it = c.begin(); it != c.end();) {
out << it->first << ": " << it->second;
++it;
if(it != c.end())
out << ", ";
}
out << "}";
return out;
}
/** @}*/
} // ~namespace opencog
#endif // _OPENCOG_COUNTER_H