#ifndef MOVING_MEDIAN_H
#define MOVING_MEDIAN_H

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

  \author Satofumi KAMIMURA

  $Id: MovingMedian.h 1414 2009-10-14 23:14:02Z 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 */
