using LightweightMatrixCSharp;
using LabelerCore.Model.Math;

namespace LabelerCore.Model
{
    public struct MultivariateGaussianResult
    {
        public double probDensity;
        public double mahalonobisDistance;
    }

    public class MultivariateGaussian
    {
        private double[][] _covMat;
        private double[] _mean;

        public MultivariateGaussian() { }

        public void Fit(double[][] X, int[] y = null)
        {
            _covMat = Stats.CovMat(X, rowvar:false);
            _mean = Stats.Mean(X);
        }

        public MultivariateGaussianResult Predict(double[] X)
        {
            //convet arrays to Matrix
            Matrix mCovMat = new Matrix(_covMat.Length, _covMat[0].Length);
            for(int i = 0; i < _covMat.Length; i++)
                for(int j = 0; j < _covMat[0].Length; j++)
                    mCovMat[i,j] = _covMat[i][j];

            Matrix mMean = new Matrix(_mean.Length, 1); 
            for (int i = 0; i < _mean.Length; i++)
                mMean[i, 0] = _mean[i];

            Matrix mX = new Matrix(X.Length, 1);
            for (int i = 0; i < X.Length; i++)
                mX[i, 0] = X[i];

            double a = System.Math.Pow(2 * System.Math.PI, X.Length / 2);
            double b = System.Math.Pow(mCovMat.Det(), 0.5);
            Matrix c = Matrix.Transpose((mX - mMean)) * mCovMat.Invert() * (mX - mMean);
            MultivariateGaussianResult res = new MultivariateGaussianResult();
            res.probDensity = 1 / a / b * System.Math.Exp( -0.5 * c[0, 0]);
            res.mahalonobisDistance = System.Math.Sqrt(c[0,0]);
            return res; 

        }
    }
}
