public static double Calculate(IEnumerable <double> x, IEnumerable <double> y, bool?exact = null, bool correct = true)
        {
            double pVal;

            //TODO: remove infinite/Nan x,y

            double nx = x.Count();
            double ny = y.Count();

            if (nx < 1)
            {
                throw new ArgumentException("Not enough finite x oberservations");
            }
            if (nx < 1)
            {
                throw new ArgumentException("Not enough finite y oberservations");
            }

            List <double> ranks = Helpers.Rank(x.Concat(y)).Ranks;

            //Console.WriteLine("Ranks: {0}", string.Join(",", ranks));

            if (!exact.HasValue)
            {
                exact = (nx < 50) && (ny < 50);
            }

            //sum the ranks of the x samples and subtract stuff..
            double statistic = ranks.Take((int)nx).Sum() - nx * (nx + 1) / 2;

            bool ties = ranks.Count() != ranks.Distinct().Count();             //TODO: better check for ties?

            if (exact.Value && !ties)
            {
                double p = 0;

                if (statistic > (nx * ny / 2))
                {
                    p = pwilcox(statistic - 1, nx, ny, 0, 0);
                }
                else
                {
                    p = pwilcox(statistic, nx, ny, 1, 0);
                }

                pVal = Math.Min(2 * p, 1);
            }
            else
            {
                Dictionary <double, int> nties = Helpers.NumTies(ranks);

                double z     = statistic - nx * ny / 2;
                double sigma = Math.Sqrt((nx * ny / 12) * ((nx + ny + 1) - nties.Sum(a => Math.Pow(a.Value, 3) - a.Value) / ((nx + ny) * (nx + ny - 1))));

                double correction = 0;
                if (correct)
                {
                    correction = Math.Sign(z) * 0.5;
                }

                z = (z - correction) / sigma;

                bool lower = z < 0;
                pVal = 2 * NormalDistribution.pnorm(z, 0, 1, lower, false);
                //pVal = 2 * Math.Min(NormalDistribution.pnorm(z, 0, 1, true, false), NormalDistribution.pnorm(z, 0, 1, false, false));
            }

            return(pVal);
        }
        public static double Calculate(List <double> x, List <double> y, bool?exact = null, bool correct = true)
        {
            double pVal;

            //TODO: remove infinite/NaN x,y

            double nx = x.Count;
            double ny = y.Count;

            if (nx < 1)
            {
                throw new ArgumentException("Not enough finite x oberservations");
            }
            if (nx < 1)
            {
                throw new ArgumentException("Not enough finite y oberservations");
            }

            List <double> ranks = Helpers.Rank(x.Concat(y)).Ranks;

            if (!exact.HasValue)
            {
                exact = nx < 50 && ny < 50;
            }

            //sum the ranks of the x samples
            double sum = 0;

            for (int i = 0; i < (int)nx; i++)
            {
                sum += ranks[i];
            }

            double statistic = sum - nx * (nx + 1) / 2;

            bool ties = ranks.Count != ranks.Distinct().Count();             //TODO: better check for ties?

            if (exact.Value && !ties)
            {
                double p = statistic > (nx * ny / 2) ? pwilcox(statistic - 1, nx, ny, 0, 0) : pwilcox(statistic, nx, ny, 1, 0);

                pVal = Math.Min(2 * p, 1);
            }
            else
            {
                Dictionary <double, int> nties = Helpers.NumTies(ranks);
                double z = statistic - nx * ny / 2;

                double ntiesSum = 0;
                foreach (var kvp in nties)
                {
                    ntiesSum += Math.Pow(kvp.Value, 3) - kvp.Value;
                }

                double sigma = Math.Sqrt((nx * ny / 12) * ((nx + ny + 1) - ntiesSum / ((nx + ny) * (nx + ny - 1))));

                double correction = 0;
                if (correct)
                {
                    correction = Math.Sign(z) * 0.5;
                }

                z = (z - correction) / sigma;

                bool lower = z < 0;
                pVal = 2 * NormalDistribution.Pnorm(z, 0, 1, lower, false);
            }

            return(pVal);
        }