using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace U.ListOperations.UStats
{
    public class Correlation
    {
        /*
        // Correlation
        public enum Method { Pearson, Spearman, Euclidean1, Euclidean2, Cosine1, Cosine2 }

        public static double get(List<int> values1, List<int> values2, Method method)
        {
            if (values1 == null || values2 == null) { return -100.0; }
            if (values1.Count != values2.Count) { return -100.0; }
            switch (method)
            {
                case Method.Pearson: return getPearson(values1, values2);
                case Method.Spearman: return getSpearman(values1, values2);
                case Method.Euclidean1: return getEuclidean1(values1, values2);
                case Method.Euclidean2: return getEuclidean2(values1, values2);
                case Method.Cosine1: return getCosine1(values1, values2);
                case Method.Cosine2: return getCosine2(values1, values2);
                default: return -1000.0;
            }
        }
        public static double get(List<double> values1, List<double> values2, Method method)
        {
            if (values1 == null || values2 == null) { return -100.0; }
            if (values1.Count != values2.Count) { return -100.0; }
            switch (method)
            {
                case Method.Pearson: return getPearson(values1, values2);
                case Method.Spearman: return getSpearman(values1, values2);
                case Method.Euclidean1: return getEuclidean1(values1, values2);
                case Method.Euclidean2: return getEuclidean2(values1, values2);
                case Method.Cosine1: return getCosine1(values1, values2);
                case Method.Cosine2: return getCosine2(values1, values2);
                default: return -1000.0;
            }
        }
        public static double get(List<long> values1, List<long> values2, Method method)
        {
            if (values1 == null || values2 == null) { return -100.0; }
            if (values1.Count != values2.Count) { return -100.0; }
            switch (method)
            {
                case Method.Pearson: return getPearson(values1, values2);
                case Method.Spearman: return getSpearman(values1, values2);
                case Method.Euclidean1: return getEuclidean1(values1, values2);
                case Method.Euclidean2: return getEuclidean2(values1, values2);
                case Method.Cosine1: return getCosine1(values1, values2);
                case Method.Cosine2: return getCosine2(values1, values2);
                default: return -1000.0;
            }
        }

        // Correlation Matrix
        public static List<List<double>> getMatrix(List<List<int>> matrix, Method method)
        {
            List<List<double>> m = UList.sameValues(-100.0, matrix.Count, matrix.Count);
            for (int i = 0; i < matrix.Count; i++) { m[i][i] = 1.0; }
            for (int i = 0; i < matrix.Count; i++) { for (int j = i + 1; j < matrix.Count; j++) { m[i][j] = get(matrix[i], matrix[j], method); m[j][i] = m[i][j]; } }
            return m;
        }
        public static List<List<double>> getMatrix(List<List<double>> matrix, Method method)
        {
            List<List<double>> m = UList.sameValues(-100.0, matrix.Count, matrix.Count);
            for (int i = 0; i < matrix.Count; i++) { m[i][i] = 1.0; }
            for (int i = 0; i < matrix.Count; i++) { for (int j = i + 1; j < matrix.Count; j++) { m[i][j] = get(matrix[i], matrix[j], method); m[j][i] = m[i][j]; } }
            return m;
        }
        public static List<List<double>> getMatrix(List<List<long>> matrix, Method method)
        {
            List<List<double>> m = UList.sameValues(-100.0, matrix.Count, matrix.Count);
            for (int i = 0; i < matrix.Count; i++) { m[i][i] = 1.0; }
            for (int i = 0; i < matrix.Count; i++) { for (int j = i + 1; j < matrix.Count; j++) { m[i][j] = get(matrix[i], matrix[j], method); m[j][i] = m[i][j]; } }
            return m;
        }


        // PEARSON
        private static double getPearson(List<double> l1, List<double> l2)
        {
            if (l1 == null || l2 == null) { return -100.0; }
            if (l1.Count != l2.Count) { return -100.0; }
            double m1 = Descriptive.getMean(l1);
            double m2 = Descriptive.getMean(l2);
            double ss1 = Descriptive.getSumSquaredErrors(l1, m1);
            double ss2 = Descriptive.getSumSquaredErrors(l2, m2);
            double sum = 0.0;
            for (int i = 0; i < l1.Count; i++) { sum += (l1[i] - m1) * (l2[i] - m2); }
            return sum / Math.Sqrt(ss1 * ss2);
        }
        private static double getPearson(List<int> l1, List<int> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } double m1 = Descriptive.getMean(l1); double m2 = Descriptive.getMean(l2); double ss1 = Descriptive.getSumSquaredErrors(l1, m1); double ss2 = Descriptive.getSumSquaredErrors(l2, m2); double sum = 0.0; for (int i = 0; i < l1.Count; i++) { sum += (l1[i] - m1) * (l2[i] - m2); } return sum / Math.Sqrt(ss1 * ss2); }
        private static double getPearson(List<long> l1, List<long> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } double m1 = Descriptive.getMean(l1); double m2 = Descriptive.getMean(l2); double ss1 = Descriptive.getSumSquaredErrors(l1, m1); double ss2 = Descriptive.getSumSquaredErrors(l2, m2); double sum = 0.0; for (int i = 0; i < l1.Count; i++) { sum += (l1[i] - m1) * (l2[i] - m2); } return sum / Math.Sqrt(ss1 * ss2); }

        // SPEARMAN
        private static double getSpearman(List<double> l1, List<double> l2)
        {
            if (l1 == null || l2 == null) { return -100.0; }
            if (l1.Count != l2.Count) { return -100.0; }
            List<double> r1 = Descriptive.getSpearmanRanks(l1, true);
            List<double> r2 = Descriptive.getSpearmanRanks(l2, true);
            return getPearson(r1, r2);
        }
        private static double getSpearman(List<int> l1, List<int> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } List<double> r1 = Descriptive.getSpearmanRanks(l1, true); List<double> r2 = Descriptive.getSpearmanRanks(l2, true); return getPearson(r1, r2); }
        private static double getSpearman(List<long> l1, List<long> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } List<double> r1 = Descriptive.getSpearmanRanks(l1, true); List<double> r2 = Descriptive.getSpearmanRanks(l2, true); return getPearson(r1, r2); }
        
        // EUCLIDEAN 1
        private static double getEuclidean1(List<double> l1, List<double> l2)
        {
            if (l1 == null || l2 == null) { return -100.0; }
            if (l1.Count != l2.Count) { return -100.0; }
            double sum = 0.0; double m1 = Descriptive.getMean(l1);
            for (int i = 0; i < l1.Count; i++) { sum += Math.Pow(l1[i] - m1 - l2[i], 2.0); }
            return Math.Sqrt(sum);
        }
        private static double getEuclidean1(List<int> l1, List<int> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } double sum = 0.0; double m1 = Descriptive.getMean(l1); for (int i = 0; i < l1.Count; i++) { sum += Math.Pow(l1[i] - m1 - l2[i], 2.0); } return Math.Sqrt(sum); }
        private static double getEuclidean1(List<long> l1, List<long> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } double sum = 0.0; double m1 = Descriptive.getMean(l1); for (int i = 0; i < l1.Count; i++) { sum += Math.Pow(l1[i] - m1 - l2[i], 2.0); } return Math.Sqrt(sum); }

        // EUCLIDEAN 2
        private static double getEuclidean2(List<double> l1, List<double> l2)
        {
            if (l1 == null || l2 == null) { return -100.0; }
            if (l1.Count != l2.Count) { return -100.0; }
            double sum = 0.0;
            for (int i = 0; i < l1.Count; i++) { sum += Math.Pow(l1[i] - l2[i], 2.0); }
            return Math.Sqrt(sum);
        }
        private static double getEuclidean2(List<int> l1, List<int> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } double sum = 0.0; for (int i = 0; i < l1.Count; i++) { sum += Math.Pow(l1[i] - l2[i], 2.0); } return Math.Sqrt(sum); }
        private static double getEuclidean2(List<long> l1, List<long> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } double sum = 0.0; for (int i = 0; i < l1.Count; i++) { sum += Math.Pow(l1[i] - l2[i], 2.0); } return Math.Sqrt(sum); }

        // COSINE 1
        private static double getCosine1(List<double> l1, List<double> l2)
        {
            if (l1 == null || l2 == null) { return -100.0; }
            if (l1.Count != l2.Count) { return -100.0; }
            double m1 = Descriptive.getMean(l1);
            double sum1 = 0.0; double sum2 = 0.0; double sum3 = 0.0;
            for (int i = 0; i < l2.Count; i++) { sum3 += Math.Pow(l2[i], 2.0); } sum3 = Math.Sqrt(sum3);
            for (int i = 0; i < l1.Count; i++) { sum1 += (l1[i] - m1) * l2[i]; }
            for (int i = 0; i < l1.Count; i++) { sum2 += Math.Pow(l1[i] - m1, 2.0) * sum3; }
            return sum1 / Math.Sqrt(sum3);
        }
        private static double getCosine1(List<int> l1, List<int> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } double m1 = Descriptive.getMean(l1); double sum1 = 0.0; double sum2 = 0.0; double sum3 = 0.0; for (int i = 0; i < l2.Count; i++) { sum3 += Math.Pow(l2[i], 2.0); } sum3 = Math.Sqrt(sum3); for (int i = 0; i < l1.Count; i++) { sum1 += (l1[i] - m1) * l2[i]; } for (int i = 0; i < l1.Count; i++) { sum2 += Math.Pow(l1[i] - m1, 2.0) * sum3; } return sum1 / Math.Sqrt(sum3); }
        private static double getCosine1(List<long> l1, List<long> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } double m1 = Descriptive.getMean(l1); double sum1 = 0.0; double sum2 = 0.0; double sum3 = 0.0; for (int i = 0; i < l2.Count; i++) { sum3 += Math.Pow(l2[i], 2.0); } sum3 = Math.Sqrt(sum3); for (int i = 0; i < l1.Count; i++) { sum1 += (l1[i] - m1) * l2[i]; } for (int i = 0; i < l1.Count; i++) { sum2 += Math.Pow(l1[i] - m1, 2.0) * sum3; } return sum1 / Math.Sqrt(sum3); }
        
        // COSINE 2
        private static double getCosine2(List<double> l1, List<double> l2)
        {
            if (l1 == null || l2 == null) { return -100.0; }
            if (l1.Count != l2.Count) { return -100.0; }
            double sum1 = 0.0; double sum2 = 0.0; double sum3 = 0.0;
            for (int i = 0; i < l1.Count; i++) { sum1 += l1[i] * l2[i]; sum2 += l1[i] * l1[i]; sum3 += l2[i] * l2[i]; }
            return sum1 / Math.Sqrt(sum2 * sum3);
        }
        private static double getCosine2(List<int> l1, List<int> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } double sum1 = 0.0; double sum2 = 0.0; double sum3 = 0.0; for (int i = 0; i < l1.Count; i++) { sum1 += l1[i] * l2[i]; sum2 += l1[i] * l1[i]; sum3 += l2[i] * l2[i]; } return sum1 / Math.Sqrt(sum2 * sum3); }
        private static double getCosine2(List<long> l1, List<long> l2) { if (l1 == null || l2 == null) { return -100.0; } if (l1.Count != l2.Count) { return -100.0; } double sum1 = 0.0; double sum2 = 0.0; double sum3 = 0.0; for (int i = 0; i < l1.Count; i++) { sum1 += l1[i] * l2[i]; sum2 += l1[i] * l1[i]; sum3 += l2[i] * l2[i]; } return sum1 / Math.Sqrt(sum2 * sum3); }
        //*/
    }
}
