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

namespace Misc
{
    /// <summary>
    /// 扩展 Enumerable 计算
    /// </summary>
    public static class Enumerable
    {

        /// <summary>
        /// 获取 σ
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="selector"></param>
        /// <returns></returns>
        public static double Sigma<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector)
        {
            if (source.Count() == 0)
                return double.NaN;
            double avg = source.Average(selector);
            double sum_pow = source.Sum((s) => Math.Pow((selector(s) - avg), 2));
            return Math.Sqrt(sum_pow / (source.Count() - 1));
        }
        /// <summary>
        /// 获取 σ
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static double Sigma(this IEnumerable<int> source)
        {
            return source.Sigma(d => (int)d);
        }
        /// <summary>
        /// 获取 σ
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static double Sigma(this IEnumerable<double> source)
        {
            var list = source.Where((d) =>
            {
                if (double.IsNaN(d))
                    return false;
                else
                    return true;
            });
            return list.Sigma(d => d);
        }
        /// <summary>
        /// 获取CPK
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="selector"></param>
        /// <param name="usl"></param>
        /// <param name="lsl"></param>
        /// <returns></returns>
        public static double CPK<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector, double usl, double lsl)
        {
            if (source.Count() == 0)
                return -1;

            //CPK = Min（CPKu,CPKl)
            //USL(Upper specification limit): 规格上限。
            //LSL(Low specification limit): 规格下限。
            //ˉx = (x1 + x2 +...+ xn) / n : 平均值。
            //T = USL - LSL : 规格公差。
            //U = (USL + LSL) / 2：规格中心。
            //CPKu = | USL - ˉx | / 3σ 
            //CPKl = | ˉx - LSL | / 3σ

            double avg = source.Average(selector);
            double sigma3 = source.Sigma(selector) * 3;

            double CPKu = Math.Abs(usl - avg) / sigma3;
            double CPKl = Math.Abs(lsl - avg) / sigma3;
            double CPK = Math.Min(CPKu, CPKl);
            return CPK;
        }

        /// <summary>
        /// 获取CPK
        /// </summary>
        /// <param name="source"></param>
        /// <param name="usl"></param>
        /// <param name="lsl"></param>
        /// <returns></returns>
        public static double CPK(this IEnumerable<double> source, double usl, double lsl)
        {
            var list = source.Where((d) =>
            {
                if (double.IsNaN(d))
                    return false;
                else
                    return true;
            });
            return list.CPK(d => d, usl, lsl);
        }
        /// <summary>
        /// 获取直方图，统计数据
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="selector"></param>
        /// <param name="step"></param>
        /// <returns></returns>
        public static List<XY> GetHistogram<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector, double step)
        {
            List<XY> list = new List<XY>();

            foreach (var v in source)
            {
                double x = ((int)(selector(v) / step)) * step;
                XY xy = list.Find(_xy => _xy.X == x);
                if (xy != null)
                {
                    xy.Y++;
                }
                else
                {
                    xy = new XY(x, 1);
                    list.Add(xy);
                }

            }
            //没有数据也能排序的，不会错
            list.Sort();

            

            return list;
        }
        /// <summary>
        /// 获取直方图，统计数据
        /// </summary>
        /// <param name="source"></param>
        /// <param name="step"></param>
        /// <returns></returns>
        public static List<XY> GetHistogram(this IEnumerable<double> source, double step)
        {
            var list = source.Where((d) =>
            {
                if (double.IsNaN(d))
                    return false;
                else
                    return true;
            });
            return list.GetHistogram(d => d, step);
        }


        /// <summary>
        /// 当有 NaN 数据，不统计它。 
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static double AverageNoNull(this IEnumerable<double> source)
        {
            return source.AverageNoNull(d => d);
        }

        /// <summary>
        /// 当有 NaN 数据，不统计它。 
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="selector"></param>
        /// <returns></returns>
        public static double AverageNoNull<TSource>(this IEnumerable<TSource> source, Func<TSource, double> selector)
        {
            if (source.Count() == 0)
                return double.NaN;

            var list = source.Select(selector);

            var list2 = list.Where((d) =>
            {
                if (double.IsNaN(d))
                    return false;
                else
                    return true;
            });
            if (list2.Count() == 0)
                return double.NaN;

            return list2.Average();
        }

        /// <summary>
        /// 扩展 System.Linq.Enumerable.SequenceEqual
        /// 支持 first,second 为null 的情况
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <param name="comparer"></param>
        /// <returns></returns>
        public static bool SequenceEqual<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
        {
            if (first == second)
            {
                return true;
            }
            else if (first == null || second == null)
            {
                return false;
            }
            return System.Linq.Enumerable.SequenceEqual(first, second, comparer);
        }
    }
    /// <summary>
    /// X Y 组合
    /// </summary>
    public class XY : IComparable, ICloneable, ICopiable
    {
        public double X { get; set; }
        public double Y { get; set; }

        public XY()
        {

        }
        public XY(double x, double y)
        {
            X = x;
            Y = y;
        }

        public int CompareTo(object obj)
        {
            XY xy = obj as XY;
            return X.CompareTo(xy.X);
        }

        public object Clone()
        {
            XY xy = new XY();
            xy.Copy(this);
            return xy;
        }
        public override string ToString()
        {
            return $"{X}.{Y}";
        }
        public void Copy(object src)
        {
            Misc.PropertiesManager.CopyTo(src, this);
        }
    }
}
