using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace Misc
{
    public class MyMath
    {


        /// <summary>
        /// 获取x 的下一个 以interval为倍数的 数
        /// </summary>
        /// <param name="x"></param>
        /// <param name="interval"></param>
        /// <returns></returns>
        public static int ROUNDUP2(int x, int interval)
        {
            int i = x % interval;
            x += (interval - i);
            return x;
        }
        /// <summary>
        /// 解方程 ax²+bx+c=0
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="c"></param>
        /// <param name="x1"></param>
        /// <param name="x2"></param>
        /// <returns></returns>
        public static bool SolveAX2_BX_C(double a, double b, double c, out double x1, out double x2)
        {
            //ax²+bx+c=0(a≠0)
            //x=[-b±√(b²-4ac)]/2a

            if (a == 0)
            {
                if (b != 0)
                {
                    x1 = x2 = -c / b;
                    return true;
                }
                else
                {
                    x1 = x2 = 0;
                    return false;
                }
            }
            else
            {
                double q2 = b * b - 4 * a * c;
                if (q2 < 0)
                {
                    x1 = x2 = 0;
                    return false;
                }
                else
                {
                    x1 = (-b + Math.Sqrt(q2)) / 2 / a;
                    x2 = (-b - Math.Sqrt(q2)) / 2 / a;
                    return true;
                }
            }
        }
        /// <summary>
        /// 排除 NULL_VALUE, 求最大值
        /// </summary>
        /// <param name="buf"></param>
        /// <returns></returns>
        public static int Max(IEnumerable<int> buf)
        {
            return Max(buf, 0, buf.Count() - 1);
        }
        public static double Max(IEnumerable<double> buf)
        {
            return Max(buf, 0, buf.Count() - 1);
        }
        /// <summary>
        /// 排除 NULL_VALUE, 求最大值
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="first">开始序号</param>
        /// <param name="last">结束序号</param>
        /// <returns></returns>
        public static int Max(IEnumerable<int> buf, int first, int last)
        {
            int max = MyBase.NULL_VALUE;
            for (int i = first; i <= last; i++)
            {
                if (MyBase.ISVALIDATA(buf.ElementAt(i)))
                {
                    if (max == MyBase.NULL_VALUE)
                        max = buf.ElementAt(i);
                    else if (max < buf.ElementAt(i))
                        max = buf.ElementAt(i);
                }
            }
            return max;
        }
        public static double Max(IEnumerable<double> buf, int first, int last)
        {
            double max = double.NaN;
            for (int i = first; i <= last; i++)
            {
                if (!double.IsNaN(buf.ElementAt(i)))
                {
                    if (max == double.NaN)
                        max = buf.ElementAt(i);
                    else if (max < buf.ElementAt(i))
                        max = buf.ElementAt(i);
                }
            }
            return max;
        }
        /// <summary>
        /// 排除 NULL_VALUE, 求最小值
        /// </summary>
        /// <param name="buf"></param>
        /// <returns></returns>
        public static int Min(IEnumerable<int> buf)
        {
            return Min(buf, 0, buf.Count() - 1);
        }
        public static double Min(IEnumerable<double> buf)
        {
            return Min(buf, 0, buf.Count() - 1);
        }        /// <summary>
                 /// 排除 NULL_VALUE, 求最小值
                 /// </summary>
                 /// <param name="buf"></param>
                 /// <param name="first">开始序号</param>
                 /// <param name="last">结束序号</param>
                 /// <returns></returns>
        public static int Min(IEnumerable<int> buf, int first, int last)
        {
            int min = MyBase.NULL_VALUE;

            for (int i = first; i <= last; i++)
            {
                if (MyBase.ISVALIDATA(buf.ElementAt(i)))
                {
                    if (min == MyBase.NULL_VALUE)
                        min = buf.ElementAt(i);
                    else if (min > buf.ElementAt(i))
                        min = buf.ElementAt(i);
                }
            }
            return min;
        }
        public static double Min(IEnumerable<double> buf, int first, int last)
        {
            double min = double.NaN;

            for (int i = first; i <= last; i++)
            {
                if (!double.IsNaN(buf.ElementAt(i)))
                {
                    if (min == double.NaN)
                        min = buf.ElementAt(i);
                    else if (min > buf.ElementAt(i))
                        min = buf.ElementAt(i);
                }
            }
            return min;
        }
        /// <summary>
        /// 排除 NULL_VALUE, 求平均值
        /// </summary>
        /// <param name="buf"></param>
        /// <returns></returns>
        public static int Avg(IEnumerable<int> buf)
        {
            return Avg(buf, 0, buf.Count() - 1);
        }
        public static double Avg(IEnumerable<double> buf)
        {
            return Avg(buf, 0, buf.Count() - 1);
        }
        /// <summary>
        /// 排除 NULL_VALUE, 求平均值
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="first">开始序号</param>
        /// <param name="last">结束序号</param>
        /// <returns></returns>
        public static int Avg(IEnumerable<int> buf, int first, int last)
        {
            int sum = 0;
            int cnt = 0;

            for (int i = first; i <= last; i++)
            {
                if ((i >= 0) && (i < buf.Count()))
                {
                    if (MyBase.ISVALIDATA(buf.ElementAt(i)))
                    {
                        sum += buf.ElementAt(i);
                        cnt++;
                    }
                }
            }
            if (cnt != 0)
                return sum / cnt;
            else
                return MyBase.NULL_VALUE;
        }
        public static double Avg(IEnumerable<double> buf, int first, int last)
        {
            double sum = 0;
            int cnt = 0;

            for (int i = first; i <= last; i++)
            {
                if ((i >= 0) && (i < buf.Count()))
                {
                    if (!double.IsNaN(buf.ElementAt(i)))
                    {
                        sum += buf.ElementAt(i);
                        cnt++;
                    }
                }
            }
            if (cnt != 0)
                return sum / cnt;
            else
                return double.NaN;
        }
        /// <summary>
        /// 排除 NULL_VALUE, 求平均值, 无方向 last &lt; first 也可以
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="first">开始序号</param>
        /// <param name="last">结束序号</param>
        /// <returns></returns>
        public static int AvgNoDirection(IEnumerable<int> buf, int first, int last)
        {
            int temp;
            if (first > last)
            {
                temp = first;
                first = last;
                last = temp;
            }
            return Avg(buf, first, last);
        }

        /// <summary>
        /// 排除 NULL_VALUE, 求Sigma
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="first">开始序号</param>
        /// <param name="last">结束序号</param>
        /// <returns></returns>
        public static int Sigma(IEnumerable<int> buf, int first, int last)
        {
            int avg = Avg(buf, first, last);
            if (!MyBase.ISVALIDATA(avg))
                return MyBase.NULL_VALUE;

            double sum = 0;
            int cnt = 0;

            for (int i = first; i <= last; i++)
            {
                if (MyBase.ISVALIDATA(buf.ElementAt(i)))
                {
                    sum += (buf.ElementAt(i) - avg) * (buf.ElementAt(i) - avg);
                    cnt++;
                }
            }
            if (cnt >= 3)
                return (int)(Math.Sqrt(sum / (cnt - 1)));
            else
                return 0;
        }
        /// <summary>
        /// 排除 NULL_VALUE, 求Sigma
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="first">开始序号</param>
        /// <param name="last">结束序号</param>
        /// <returns></returns>
        public static double Sigma(IEnumerable<double> buf, int first, int last)
        {
            
            double avg = Avg(buf, first, last);
            if (double.IsNaN(avg))
                return double.NaN;

            double sum = 0;
            int cnt = 0;

            for (int i = first; i <= last; i++)
            {
                if ((i >= 0) && (i < buf.Count()))
                {
                    if (!double.IsNaN(buf.ElementAt(i)))
                    {
                        sum += (buf.ElementAt(i) - avg) * (buf.ElementAt(i) - avg);
                        cnt++;
                    }
                }
            }
            if (cnt >= 3)
                return (Math.Sqrt(sum / (cnt - 1)));
            else
                return 0;
        }
        /// <summary>
        /// 排除 NULL_VALUE, 求Sigma
        /// </summary>
        /// <param name="buf"></param>
        /// <returns></returns>
        public static int Sigma(IEnumerable<int> buf)
        {
            return Sigma(buf, 0, buf.Count() - 1);
        }
        static double Relevency(IEnumerable<int> x, IEnumerable<int> y)
        {
            return Relevency(x, 0, y, 0, x.Count());
        }
        static double Relevency(IEnumerable<int> x, int x_offset, IEnumerable<int> y, int y_offset, int len)
        {
            double sum_x = 0;
            double sum_y = 0;
            double sum_x2 = 0;
            double sum_y2 = 0;
            double sum_xy = 0;
            int cnt = 0;
            ////避免溢出
            //int max_x = Max(x, x_offset, x_offset + len - 1);
            //if (max_x > 30000)
            //{ 

            //}

            for (int i = 0; i < len; i++)
            {
                int x_i = i + x_offset;
                int y_i = i + y_offset;

                if (MyBase.ISVALIDATA(x.ElementAt(x_i)) && MyBase.ISVALIDATA(y.ElementAt(y_i)))
                {
                    sum_x += x.ElementAt(x_i);
                    sum_y += y.ElementAt(y_i);
                    sum_x2 += (double)x.ElementAt(x_i) * x.ElementAt(x_i);
                    sum_y2 += (double)y.ElementAt(y_i) * y.ElementAt(y_i);
                    sum_xy += (double)x.ElementAt(x_i) * y.ElementAt(y_i);
                    cnt++;
                }
            }
            if (cnt > 0)
            {
                double s1 = cnt * sum_xy - sum_x * sum_y;
                double s2 = Math.Sqrt(cnt * sum_x2 - sum_x * sum_x);
                double s3 = Math.Sqrt(cnt * sum_y2 - sum_y * sum_y);
                double s4 = s1 / s2 / s3;
                return s4;
            }
            else
                return 0;
        }


        /// <summary>
        /// 求相关性
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public static double Correl(IEnumerable<double> x, IEnumerable<double> y)
        {
            return Correl(x, 0, y, 0, x.Count());
        }

        /// <summary>
        /// 求相关性
        /// </summary>
        /// <param name="x"></param>
        /// <param name="x_offset"></param>
        /// <param name="y"></param>
        /// <param name="y_offset"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        public static double Correl(IEnumerable<double> x, int x_offset, IEnumerable<double> y, int y_offset, int len)
        {
            double sum_x = 0;
            double sum_y = 0;
            double sum_x2 = 0;
            double sum_y2 = 0;
            double sum_xy = 0;
            int cnt = 0;
            ////避免溢出
            //int max_x = Max(x, x_offset, x_offset + len - 1);
            //if (max_x > 30000)
            //{ 

            //}

            for (int i = 0; i < len; i++)
            {
                int x_i = i + x_offset;
                int y_i = i + y_offset;
                double xv = x.ElementAt(x_i);
                double yv = y.ElementAt(y_i);
                if ((!double.IsNaN(xv)) && (!double.IsNaN(yv)))
                {
                    sum_x += xv;
                    sum_y += yv;
                    sum_x2 += xv * xv;
                    sum_y2 += yv * yv;
                    sum_xy += xv * yv;
                    cnt++;
                }
            }
            if (cnt > 0)
            {
                double s1 = cnt * sum_xy - sum_x * sum_y;
                double s2 = Math.Sqrt(cnt * sum_x2 - sum_x * sum_x);
                double s3 = Math.Sqrt(cnt * sum_y2 - sum_y * sum_y);
                double s4 = s1 / s2 / s3;
                return s4;
            }
            else
                return 0;
        }
        /// <summary>
        /// 求相关性
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static double Correl(IEnumerable<int> x, IEnumerable<int> y)
        {
            return Relevency(x, y);
        }
        /// <summary>
        /// 求相关性
        /// </summary>
        /// <param name="x"></param>
        /// <param name="x_offset"></param>
        /// <param name="y"></param>
        /// <param name="y_offset"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        public static double Correl(IEnumerable<int> x, int x_offset, IEnumerable<int> y, int y_offset, int len)
        {
            return Relevency(x, x_offset, y, y_offset, len);
        }
        /// <summary>
        /// 求2次拟合 a,b
        /// </summary>
        /// <param name="y"></param>
        /// <param name="x"></param>
        /// <param name="a"></param>
        /// <param name="b"></param>
        public static void Linest(IEnumerable<int> y, IEnumerable<int> x, out double a, out double b)
        {
            if (y.Count() != x.Count())
            {
                throw new Exception("Linest() 参数异常 (y.Length=" + y.Count() + ") != (x.Length=" + x.Count() + ")");
            }

            double sum_x = 0;
            double sum_y = 0;
            double sum_x2 = 0;
            double sum_xy = 0;
            int n = y.Count();
            int cnt = 0;
            for (int i = 0; i < n; i++)
            {
                if (MyBase.ISVALIDATA(x.ElementAt(i)) && MyBase.ISVALIDATA(y.ElementAt(i)))
                {
                    sum_xy += y.ElementAt(i) * x.ElementAt(i);
                    sum_x2 += x.ElementAt(i) * x.ElementAt(i);
                    sum_x += x.ElementAt(i);
                    sum_y += y.ElementAt(i);
                    cnt++;
                }
            }
            if (cnt > 1)
            {
                a = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x);
                b = sum_y / n - a * sum_x / n;
            }
            else
            {
                a = 1;
                b = 0;
            }
        }

        public static void Linest(IEnumerable<double> y, IEnumerable<double> x, out double a, out double b)
        {
            if (y.Count() != x.Count())
            {
                throw new Exception("Linest() 参数异常 (y.Length=" + y.Count() + ") != (x.Length=" + x.Count() + ")");
            }

            double sum_x = 0;
            double sum_y = 0;
            double sum_x2 = 0;
            double sum_xy = 0;
            int n = y.Count();
            int cnt = 0;
            for (int i = 0; i < n; i++)
            {
                if (!double.IsNaN(x.ElementAt(i)) && !double.IsNaN(y.ElementAt(i)))
                {
                    sum_xy += y.ElementAt(i) * x.ElementAt(i);
                    sum_x2 += x.ElementAt(i) * x.ElementAt(i);
                    sum_x += x.ElementAt(i);
                    sum_y += y.ElementAt(i);
                    cnt++;
                }
            }
            if (cnt > 1)
            {
                a = (cnt * sum_xy - sum_x * sum_y) / (cnt * sum_x2 - sum_x * sum_x);
                b = sum_y / cnt - a * sum_x / cnt;
            }
            else
            {
                a = 1;
                b = 0;
            }
        }


        /// <summary>
        /// 对环形数据 分区移位
        /// </summary>
        /// <param name="data">原始数据</param>
        /// <param name="map">key=old_idx, value=new_idx</param>
        /// <returns></returns>
        public static int[] Map(IEnumerable<int> data, Dictionary<int, int> map)
        {
            if (map == null || map.Count() == 0)
                return data.ToArray();
            int[] data_new = new int[data.Count()];
            if (map.Count() == 1)
            {
                //只是平移而已
                int old_idx0 = map.Keys.ElementAt(0);
                int new_idx0 = map[old_idx0];

                for (int i = 0; i < data.Count(); i++)
                {
                    int new_idx = i;
                    int old_idx = (old_idx0 - new_idx0) + new_idx;
                    if (old_idx >= data.Count())
                        old_idx -= data.Count();
                    else if (old_idx < 0)
                        old_idx += data.Count();
                    data_new[new_idx] = data.ElementAt(old_idx);
                }
                return data_new;
            }


            for (int i = 0; i < map.Count(); i++)
            {
                int old_idx0 = map.Keys.ElementAt(i);
                int new_idx0 = map[old_idx0];


                int i_next = i + 1;
                if (i_next >= map.Count())
                    i_next = 0;

                int old_idx1 = map.Keys.ElementAt(i_next);
                int new_idx1 = map[old_idx1];

                int cnt = new_idx1 - new_idx0;
                if (cnt < 0)
                    cnt += data.Count();

                int cnt_old = old_idx1 - old_idx0;
                if (cnt_old < 0)
                    cnt_old += data.Count();

                double w = 1.0 * cnt_old / cnt;
                for (int j = 0; j < cnt; j++)
                {
                    int new_idx = j + new_idx0;
                    if (new_idx >= data.Count())
                        new_idx -= data.Count();

                    double old_idx = j * w + old_idx0;
                    int o1 = (int)Math.Ceiling(old_idx);
                    int o0 = (int)old_idx;

                    if (o0 == o1)
                    {
                        int old_idx_0 = o0;
                        if (old_idx_0 >= data.Count())
                            old_idx_0 -= data.Count();
                        data_new[new_idx] = data.ElementAt(old_idx_0);
                    }
                    else
                    {
                        int old_idx_0 = o0;
                        if (old_idx_0 >= data.Count())
                            old_idx_0 -= data.Count();

                        int old_idx_1 = o1;
                        if (old_idx_1 >= data.Count())
                            old_idx_1 -= data.Count();

                        if (MyBase.ISVALIDATA(data.ElementAt(old_idx_1)) && MyBase.ISVALIDATA(data.ElementAt(old_idx_0)))
                        {
                            data_new[new_idx] = (int)(data.ElementAt(old_idx_1) * (old_idx - o0) + data.ElementAt(old_idx_0) * (o1 - old_idx));
                        }
                        else if (MyBase.ISVALIDATA(data.ElementAt(old_idx_0)))
                        {
                            data_new[new_idx] = data.ElementAt(old_idx_0);
                        }
                        else if (MyBase.ISVALIDATA(data.ElementAt(old_idx_1)))
                        {
                            data_new[new_idx] = data.ElementAt(old_idx_1);
                        }
                        else
                        {
                            data_new[new_idx] = MyBase.NULL_VALUE;
                        }

                    }
                }
            }
            return data_new;
        }


        /// <summary>
        /// 对环形数据 分区移位
        /// </summary>
        /// <param name="data">原始数据</param>
        /// <param name="map">key=old_idx, value=new_idx</param>
        /// <returns></returns>
        public static double[] Map(IEnumerable<double> data, Dictionary<int, int> map)
        {
            if (map == null || map.Count() == 0)
                return data.ToArray();
            double[] data_new = new double[data.Count()];
            if (map.Count() == 1)
            {
                //只是平移而已
                int old_idx0 = map.Keys.ElementAt(0);
                int new_idx0 = map[old_idx0];

                for (int i = 0; i < data.Count(); i++)
                {
                    int new_idx = i;
                    int old_idx = (old_idx0 - new_idx0) + new_idx;
                    if (old_idx >= data.Count())
                        old_idx -= data.Count();
                    else if (old_idx < 0)
                        old_idx += data.Count();
                    data_new[new_idx] = data.ElementAt(old_idx);
                }
                return data_new;
            }


            for (int i = 0; i < map.Count(); i++)
            {
                int old_idx0 = map.Keys.ElementAt(i);
                int new_idx0 = map[old_idx0];


                int i_next = i + 1;
                if (i_next >= map.Count())
                    i_next = 0;

                int old_idx1 = map.Keys.ElementAt(i_next);
                int new_idx1 = map[old_idx1];

                int cnt = new_idx1 - new_idx0;
                if (cnt < 0)
                    cnt += data.Count();

                int cnt_old = old_idx1 - old_idx0;
                if (cnt_old < 0)
                    cnt_old += data.Count();

                double w = 1.0 * cnt_old / cnt;
                for (int j = 0; j < cnt; j++)
                {
                    int new_idx = j + new_idx0;
                    if (new_idx >= data.Count())
                        new_idx -= data.Count();

                    double old_idx = j * w + old_idx0;
                    int o1 = (int)Math.Ceiling(old_idx);
                    int o0 = (int)old_idx;

                    if (o0 == o1)
                    {
                        int old_idx_0 = o0;
                        if (old_idx_0 >= data.Count())
                            old_idx_0 -= data.Count();
                        data_new[new_idx] = data.ElementAt(old_idx_0);
                    }
                    else
                    {
                        int old_idx_0 = o0;
                        if (old_idx_0 >= data.Count())
                            old_idx_0 -= data.Count();

                        int old_idx_1 = o1;
                        if (old_idx_1 >= data.Count())
                            old_idx_1 -= data.Count();

                        if (!double.IsNaN(data.ElementAt(old_idx_1)) && !double.IsNaN(data.ElementAt(old_idx_0)))
                        {
                            data_new[new_idx] = (data.ElementAt(old_idx_1) * (old_idx - o0) + data.ElementAt(old_idx_0) * (o1 - old_idx));
                        }
                        else if (!double.IsNaN(data.ElementAt(old_idx_0)))
                        {
                            data_new[new_idx] = data.ElementAt(old_idx_0);
                        }
                        else if (!double.IsNaN(data.ElementAt(old_idx_1)))
                        {
                            data_new[new_idx] = data.ElementAt(old_idx_1);
                        }
                        else
                        {
                            data_new[new_idx] = double.NaN;
                        }

                    }
                }
            }
            return data_new;
        }
    }
}