/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 General Public License
 * along with MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef mia_core_statistics_hh
#define mia_core_statistics_hh

#include <cmath>
#include <mia/core/filter.hh>


NS_MIA_BEGIN

/**
   \ingroup misc
   \brief Functor to be called by mia::filter to evaluate mean and variance of
   a series of data.

*/
struct  FMeanVariance: public TFilter< std::pair<double, double>>  {

       /** result type of this functor
           - first = mean
           - second = variance
       */
       typedef TFilter< std::pair<double, double>>::result_type result_type;

       /**
          \tparam T container type T holding the data to be analyzed. Must provide
          the methods begin() and end() returning a forward_iterator;
          \param data the data to be anlyzed
          \returns a std::pair first=mean, second=variance
        */

       template <typename T>
       result_type operator()( const T& data) const;
};

/**
   \ingroup misc
   \brief Functor to be called by mia::filter to evaluate median and median average distance (MAD) of
   a series of data.
*/
struct  FMedianMAD: public TFilter< std::pair<double, double>>  {

       /** result type of this functor
           - first = median
           - second = MAD
       */
       typedef TFilter< std::pair<double, double>>::result_type result_type;

       /**
          \tparam T container type T holding the data to be analyzed. Must provide
          the methods begin() and end() returning a forward_iterator;
          \param data the data to be anlyzed
          \returns a std::pair first=median, second=MAD
        */
       template <typename T>
       result_type operator()( const T& data) const;
private:
       double median(std::vector<double>& buf)const;
};



template <typename T>
FMeanVariance::result_type FMeanVariance::operator()( const T& data) const
{
       double sum = 0.0;
       double sum2  = 0.0;
       double n = data.size();

       for (auto i = data.begin(); i != data.end(); ++i) {
              sum += *i;
              sum2 += *i * *i;
       }

       FMeanVariance::result_type result = {0.0, 0.0};

       if (n > 0) {
              result.first = 	sum / n;

              if (n > 1)
                     result.second = sqrt((sum2 - result.first * sum) / (n - 1));
       }

       return result;
}

template <typename T>
FMedianMAD::result_type FMedianMAD::operator()( const T& data) const
{
       std::vector<double> buffer(data.size());
       copy(data.begin(), data.end(), buffer.begin());
       FMedianMAD::result_type result;
       result.first = median(buffer);
       transform(buffer.begin(), buffer.end(), buffer.begin(),
       [&result](double x) {
              return fabs(x - result.first);
       });
       result.second = median(buffer);
       return result;
}

double FMedianMAD::median(std::vector<double>& buf)const
{
       if (buf.empty())
              return 0.0;

       if (buf.size() & 1) {
              auto i = buf.begin() + (buf.size() - 1) / 2;
              std::nth_element(buf.begin(), i, buf.end());
              return *i;
       } else {
              auto i1 = buf.begin() + buf.size() / 2 - 1;
              auto i2 = buf.begin() + buf.size() / 2;
              std::nth_element(buf.begin(), i1, buf.end());
              std::nth_element(buf.begin(), i2, buf.end());
              return (*i1 + *i2) / 2.0;
       }
}

NS_MIA_END
#endif
