/* coot-utils/coot-coord-rama.hh
 * 
 * Copyright 2014 by Medical Research Council
 * Author: Paul Emsley
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA
 */

#ifndef COOT_DENSITY_STATS_HH
#define COOT_DENSITY_STATS_HH

#include <vector>
#include <cmath>

namespace coot {

   enum map_stats_t {
      SIMPLE,
      WITH_KOLMOGOROV_SMIRNOV_DIFFERENCE_MAP_TEST};

   namespace util {

      class density_stats_info_t {
         
      public:
         // these are doubles because we can potentially do lots (millions) of tiny adds.
         double n;
         double sum_sq; // sum of squared elements
         double sum;
         double sum_weight;

         density_stats_info_t() {
            n = 0.0;
            sum = 0.0;
            sum_sq = 0.0;
            sum_weight = 0.0;
         }
         void add(const double &v) {
            n += 1.0;
            sum += v;
            sum_sq += v*v;
            sum_weight += 1.0;
         } 
         void add(const double &v, const double &weight) {
            n += weight;
            sum += weight*v;
            sum_sq += weight*v*v;
            sum_weight += 1.0;
         }
         std::pair<double, double> mean_and_variance() const {
            double mean = -1;
            double var  = -1;
            if (n > 0) {
               mean = sum/sum_weight;
               var = sum_sq/sum_weight - mean*mean;
            }
            return std::pair<double, double> (mean, var);
         }
      };

      class correlation_parts_t {
         public:
            double top;
            double var_x, var_y;
      };

      //! density correlation state - between map and model typically.
      class density_correlation_stats_info_t {
      public:
         double n;
         double sum_xy;
         double sum_sqrd_x;
         double sum_sqrd_y;
         double sum_x;
         double sum_y;
         // for doing KS tests (against normal distribution) , we want
         // all the density samples
         std::vector<double> density_values;
         //! constructor
         density_correlation_stats_info_t() {
            n = 0;
            sum_xy = 0;
            sum_sqrd_x = 0;
            sum_sqrd_y = 0;
            sum_x = 0;
            sum_y = 0;
         }
         //! constructor
         density_correlation_stats_info_t(double n_in,
                                          double sum_xy_in,
                                          double sum_sqrd_x_in,
                                          double sum_sqrd_y_in,
                                          double sum_x_in,
                                          double sum_y_in) {
            n = n_in;
            sum_xy = sum_xy_in;
            sum_sqrd_x = sum_sqrd_x_in;
            sum_sqrd_y = sum_sqrd_y_in;
            sum_x = sum_x_in;
            sum_y = sum_y_in;
         }
         double var_x() const {
            double mean_x = sum_x/n;
            return (sum_sqrd_x/n - mean_x * mean_x);
         } 
         double var_y() const {
            double mean_y = sum_y/n;
            return (sum_sqrd_y/n - mean_y * mean_y);
         }
         void add(const double &x, const double &y) {
            n += 1;
            sum_x  += x;
            sum_y  += y;
            sum_xy += x * y;
            sum_sqrd_x += x * x;
            sum_sqrd_y += y * y;
         }
         //! the correlation
         double correlation() const {
            double top = n * sum_xy - sum_x * sum_y;
            double b_1 = n * sum_sqrd_x - sum_x * sum_x;
            double b_2 = n * sum_sqrd_y - sum_y * sum_y;
            if (b_1 < 0) b_1 = 0;
            if (b_2 < 0) b_2 = 0;
            double c = top/(std::sqrt(b_1) * std::sqrt(b_2));
            return c;
         }
         //! the correlation by parts
         correlation_parts_t correlation_by_parts() const {
            double top = n * sum_xy - sum_x * sum_y;
            double b_1 = n * sum_sqrd_x - sum_x * sum_x;
            double b_2 = n * sum_sqrd_y - sum_y * sum_y;
            if (b_1 < 0) b_1 = 0;
            if (b_2 < 0) b_2 = 0;
            correlation_parts_t c;
            c.top = top; c.var_x = b_1; c.var_y = b_2;
            return c;
         }
         bool operator<(const density_correlation_stats_info_t &dcsi) const {
            return sum_xy < dcsi.sum_xy; // or something.
         };
      };

   }
}

#endif // COOT_DENSITY_STATS_HH
