package com.gyee.alarm.util.realtimesource.math;

import com.gyee.alarm.model.vo.PointVo;

import java.util.ArrayList;
import java.util.List;

public class LineUtil {

    /// <summary>
    /// 用最小二乘法拟合二元多次曲线
    /// </summary>
    /// <param name="arrX">已知点的x坐标集合</param>
    /// <param name="arrY">已知点的y坐标集合</param>
    /// <param name="length">已知点的个数</param>
    /// <param name="dimension">方程的最高次数</param>
    /// <param name="scale">曲线的刻度</param>
    /// <returns></returns>
    public static List<PointVo> buildLine(double[] arrX, double[] arrY, int length, int dimension, double scale)
    {

        List<PointVo> points =new ArrayList<PointVo>();

        if (arrX.length != arrY.length || arrX.length<3)
        {
            return points;
        }

        double minValue=arrY[0];
        double maxValue=arrY[arrY.length-1];


        double min = 0;
        double max= 0;

        double[] coefficient = multiLine(arrX, arrY, length, dimension);

        for (double i = arrX[0]; i <= arrX[arrX.length - 1]; i += scale)
        {
            PointVo point = new PointVo();
            point.setX(i);


            for (int j = 0; j < coefficient.length; j++)
            {
                if(j==0)
                {
                    point.setY(coefficient[j] * Math.pow(point.getX(), (double)j));
                }else
                {
                    double temp=coefficient[j] * Math.pow(point.getX(), (double)j);
                    point.setY(point.getY()+temp);
                }

            }
            if (point.getY() < minValue)
            {
                point.setY(minValue);
   
            }
            if (point.getY() > maxValue)
            {
                point.setY(maxValue);
            }

            if (point.getY() < min)
            {
                min = point.getY();
            }
            if (point.getY() > max)
            {
                max = point.getY();
            }

            points.add(point);
        }
        builder(points, min, max);
        return points;
    }

    private static void builder(List<PointVo> points, double min, double max)
    {
        boolean b = false;
        for (int i = 0; i < points.size(); i++)
        {
            if (b)
            {
                points.get(i).setY(max);
            }
            else
            {
                if (max == points.get(i).getY())
                {
                    b = true;
                }
            }

        }

        for (int i = points.size()-1; i > -1; i--)
        {
            if (!b)
            {
                points.get(i).setY(min);
            }
            else
            {
                if (min == points.get(i).getY())
                {
                    b = false;
                }
            }

        }
    }



    ///<summary>
    ///用最小二乘法拟合二元多次曲线
    ///</summary>
    ///<param name="arrX">已知点的x坐标集合</param>
    ///<param name="arrY">已知点的y坐标集合</param>
    ///<param name="length">已知点的个数</param>
    ///<param name="dimension">方程的最高次数</param>
    public static double[] multiLine(double[] arrX, double[] arrY, int length, int dimension)//二元多次线性方程拟合曲线
    {
        int n = dimension + 1;                  //dimension次方程需要求 dimension+1个 系数
        double[][] guass = new double[n][n+1];      //高斯矩阵 例如：y=a0+a1*x+a2*x*x
        for (int i = 0; i < n; i++)
        {
            int j;
            for (j = 0; j < n; j++)
            {
                guass[i][j] = sumArr(arrX, j + i, length);
            }
            guass[i][j]= sumArr(arrX, i, arrY, 1, length);
        }
        return computGauss(guass, n);
    }
    public static double sumArr(double[] arr, int n, int length) //求数组的元素的n次方的和
    {
        double s = 0;
        for (int i = 0; i < length; i++)
        {
            if (arr[i] != 0 || n != 0)
                s = s + Math.pow(arr[i], n);
            else
                s = s + 1;
        }
        return s;
    }
    public static double sumArr(double[] arr1, int n1, double[] arr2, int n2, int length)
    {
        double s = 0;
        for (int i = 0; i < length; i++)
        {
            if ((arr1[i] != 0 || n1 != 0) && (arr2[i] != 0 || n2 != 0))
                s = s + Math.pow(arr1[i], n1) * Math.pow(arr2[i], n2);
            else
                s = s + 1;
        }
        return s;

    }
    public static double[] computGauss(double[][] guass, int n)
    {
        int i, j;
        int k, m;
        double temp;
        double max;
        double s;
        double[] x = new double[n];
        for (i = 0; i < n; i++) x[i] = 0.0;//初始化

        for (j = 0; j < n; j++)
        {
            max = 0;
            k = j;
            for (i = j; i < n; i++)
            {
                if (Math.abs(guass[i][j]) > max)
                {
                    max = guass[i][j];
                    k = i;
                }
            }


            if (k != j)
            {
                for (m = j; m < n + 1; m++)
                {
                    temp = guass[j][m];
                    guass[j][m]= guass[k][m];
                    guass[k][m] = temp;
                }
            }
            if (0 == max)
            {
                // "此线性方程为奇异线性方程" 
                return x;
            }

            for (i = j + 1; i < n; i++)
            {
                s = guass[i][j];
                for (m = j; m < n + 1; m++)
                {
                    guass[i][m] = guass[i][m] - guass[j][m] * s / (guass[j][j]);
                }
            }

        }//结束for (j=0;j<n;j++)

        for (i = n - 1; i >= 0; i--)
        {
            s = 0;
            for (j = i + 1; j < n; j++)
            {
                s = s + guass[i][j] * x[j];
            }
            x[i] = (guass[i][n]- s) / guass[i][i];
        }
        return x;
    }//返回值是函数的系数
}

