#ifndef MOVING_MEDIAN_H
#define MOVING_MEDIAN_H

/*!
  \file
  \brief 移動平均のメディアン演算版

  \author Satofumi KAMIMURA

  $Id: MovingMedian.h 1811 2010-04-30 16:12:05Z satofumi $

  \todo setMedianValue() を setValue() にすべきかを検討する
*/

#include <deque>
#include <algorithm>


namespace qrk
{
    /*!
      \brief 移動平均のメディアン演算版テンプレート

      \attention 和が使用している型より大きな値になると、結果が保証されない
    */
    template<class T> class MovingMedian
    {
    public:
        /*!
          \brief コンストラクタ

          \param[in] size バッファのサイズ
          \param[in] initial_value 初期値
        */
        MovingMedian(size_t size, T initial_value = 0)
            : buffer_size_(size), median_index_(size >> 1)
        {
            if (buffer_size_ == 0) {
                buffer_size_ = 1;
            }
            buffer_set(buffer_size_, initial_value);
        }


        /*!
          \brief 格納されている値の更新

          \param value [i] 更新する平均値
        */
        void setMedianValue(T value)
        {
            buffer_set(buffer_size_, value);
        }


        /*!
          \brief 中央値の取得

          \return 中央値
        */
        T median(void)
        {
            return median_;
        }


        /*!
          \brief 値の追加

          \param[in] value 追加する値

          \return 中央値
        */
        T push_back(T value)
        {
            buffer_.pop_front();
            buffer_.push_back(value);

            // 中央値を求める
            // !!! 可能ならば、コピーを除去する
            std::deque<T> copied = buffer_;
            std::nth_element(copied.begin(),
                             copied.begin() + median_index_, copied.end());
            median_ = copied[median_index_];

            return median_;
        }

    private:
        MovingMedian(const MovingMedian& rhs);
        MovingMedian& operator = (const MovingMedian& rhs);

        std::deque<T> buffer_;
        size_t buffer_size_;
        T median_;
        int median_index_;


        void buffer_set(size_t size, T& value)
        {
            buffer_.clear();
            buffer_.assign(size, value);
            median_ = value;
        }
    };
}

#endif /* !MOVING_MEDIAN_H */
