/***************************************************************************
**
**  This file is part of QGpCoreMath.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2012-11-26
**  Copyright: 2012-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "Covariance.h"

namespace QGpCoreMath {

  /*!
    \class Covariance Covariance.h
    \brief Brief description of class still missing

    Full description of class still missing
  */

  /*!
    Description of constructor still missing
    \a n is the number of random variables
  */
  Covariance::Covariance(int n)
  {
    TRACE;
    ASSERT(n>=1);
    _n=n;
    _nx=_n*(_n+1)/2;
    _sums=new double[_n];
    _xProducts=new double[_nx];
    reset();
  }

  /*!
    Description of destructor still missing
  */
  Covariance::~Covariance()
  {
    TRACE;
    delete [] _xProducts;
    delete [] _sums;
  }

  /*!
    Add vector \a val to the covariance computation
  */
  void Covariance::add(double * val)
  {
    TRACE;
    for(int i=0; i<_n; i++) {
      _sums[i]+=val[i];
    }
    int k=0;
    for(int i=0; i<_n; i++) {
      for(int j=i; j<_n; j++) {
        _xProducts[k++]+=val[i]*val[j];
      }
    }
    _count++;
  }

  void Covariance::reset()
  {
    TRACE;
    for(int i=0; i<_n; i++) {
      _sums[i]=0.0;
    }
    for(int i=0; i<_nx; i++) {
      _xProducts[i]=0.0;
    }
    _count=0;
  }

  DoubleMatrix Covariance::matrix() const
  {
    TRACE;
    DoubleMatrix cov(_n);
    int k=0;
    double c=1.0/(_count-1);
    double c2=c/_count;
    for(int i=0; i<_n; i++) {
      for(int j=i; j<_n; j++) {
        double v=_xProducts[k++]*c-_sums[i]*_sums[j]*c2;
        cov.at(i, j)=v;
        cov.at(j, i)=v;
      }
    }
    return cov;
  }

  Ellipse Covariance::stddev2D() const
  {
    TRACE;
    Ellipse res;
    if(_n!=2) {
      return res;
    }
    if(_count>1) {
      DoubleMatrix cov=matrix();
      QVector<double> values;
      DoubleMatrix u;
      cov.singularValues(values, &u);
      Point2D majorAxis=u.columnAt(0);
      //Point2D minorAxis=eigenVectors.columnAt(1);
      res.setCenter(Point2D(mean(0), mean(1)));
      if(values.at(0)>0.0) {
        res.setMajorRadius(::sqrt(values.at(0)));
      } else {
        res.setMajorRadius(0.0);
      }
      if(values.at(1)>0.0) {
        res.setMinorRadius(::sqrt(values.at(1)));
      } else {
        res.setMinorRadius(0.0);
      }
      res.setOrientation(majorAxis.azimuth());
    } else {
      res.setCenter(Point2D(mean(0), mean(1)));
    }
    return res;
  }

} // namespace QGpCoreMath
