package org.cloudbus.cloudsim.codes.test;

import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.DecompositionSolver;
import org.apache.commons.math3.linear.LUDecomposition;
import org.apache.commons.math3.linear.RealMatrix;

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

/**
 * ClassName Test2
 * Description
 * Create by HW
 * Date 2021/5/17 17:14
 */
public class Test2 {
    public static void main(String[] args) {
        // 模拟30年降雨量毫米 mm 从1981-2010年 数据非真实
        double[] rainFall = new double[] {
                1.59,1.61,1.64,1.66,1.67,1.75,1.92,2.02,2.21,2.89,3.2,3.93};
        double[] rainfall1=new double[]{
                1,2,3,4,5,6,7,8,9,10
        };
        //4.37
        /*
        136
144
167
162
160
153
147
146
148
150
156
168
172
150
150
149
149
154
165
165
152
156
151
151
155
165
165
149
148
146
148
151
162
176
180
151
150
155
155
168

         */
       /* double[] rainFall = new double[] {
                620, 680, 749.9, 943.3, 435.6, 734.5, 847.3, 534.6, 934.7, 673.7,
                500, 780, 649.9, 743.3, 835.6, 834.5, 547.3, 634.6, 834.7, 873.7,
                470, 590, 729.1, 823.5, 637.6, 834.8, 469.3, 734.5, 864.7, 872.1
        };*/
        // levelList 内的值是0、1组合 共有30个值
        List<Integer> levelList = new ArrayList<Integer>();
        // 把30年降雨量分为5个等级 枯水年、偏枯年、平水年、偏丰年、丰水年 对应0 1 2 3 4
        // 对应降水量分别为 <600 <700 <800 <900 >900
        int[] statusValue = new int[] {1, 2, 3, 4 };
        // 此数组用于存储每种状态对应出现的次数及其状态值 如 状态0 在所列数据中出现了5次
        int[] statusValueNum = new int[statusValue.length];
        /*for (int i = 0; i < rainFall.length; i++) {
            if (rainFall[i] < 1300) {
                statusValueNum[0]++;
                levelList.add(0);
            } else if (rainFall[i] < 1500) {
                statusValueNum[1]++;
                levelList.add(1);
            } else if (rainFall[i] < 1600) {
                statusValueNum[2]++;
                levelList.add(2);
            } else if (rainFall[i] < 1800) {
                statusValueNum[3]++;
                levelList.add(3);
            } else {
                statusValueNum[4]++;
                levelList.add(4);
            }
        }*/
        // 每年的降雨量对应的状态值
        System.out.println("每年的降雨量对应的状态值:\n" + levelList + "\n");
        System.out.println("状态0、1、2、3、4分别出现的次数:\n" + Arrays.toString(statusValueNum) + "\n");
        // 获取转移概率矩阵

        Double[][] transProbablityMatrix = statusTransProbablity(statusValueNum, levelList);
        System.out.println("一步转移概论矩阵结果：");
        for (int i = 0; i < transProbablityMatrix.length; i++) {
            for (int j = 0; j < transProbablityMatrix.length; j++) {
                System.out.printf("%.4f\t", transProbablityMatrix[i][j]);// 输出格式化后的数据
            }
            System.out.println();
        }
        //获得预测的值
        double[] predictionList = getPredictionList(rainFall);
        /*for (int i = 0; i < predictionList.length; i++) {
            System.out.println("第"+(i+1)+"个预测值为："+predictionList[i]);
        }*/
        //获得状态以及转移矩阵
        int[] statusNumbers = getStatusNumbers(rainFall);
        List<Integer> levelList1 = getLevelList(rainFall);//获得状态值
        //

        double[][] probability = getTransitionProbability(statusNumbers,levelList1);

        double finalPredictionValue = getFinalPredictionValue(statusNumbers, levelList1,rainFall);
        System.out.println("最终的利用率为："+finalPredictionValue);

        double[] are=new double[rainFall.length+1];
        for (int i = 0; i < rainFall.length; i++) {
            are[i]=rainFall[i];
        }
        are[rainFall.length]=finalPredictionValue;
       // System.out.println("原始的值为"+Arrays.toString(are));

        int[] statusNumbers1 = getStatusNumbers(are);
        List<Integer> levelList2 = getLevelList(are);
        double finalPredictionValueByTwo1 = getFinalPredictionValueByTwo(statusNumbers1, levelList2, are);
        System.out.println("根据二步转移概率算出来的值为：" + finalPredictionValueByTwo1);

        System.out.println("一部转移概率矩阵结果为：");
        for (int i = 0; i < probability.length; i++) {
            for (int j = 0; j < probability.length; j++) {
                System.out.printf("%.4f\t", probability[i][j]);// 输出格式化后的数据
            }
            System.out.println();
        }

        System.out.println("________________________________");
        double[] list = getPredictionList(rainFall);
        System.out.println("最后一年的预测值为"+list[list.length - 1]);
    }
    //获取新的序列
    public static double[] getNewList(double[] oldlist){
        double[] newList=new double[oldlist.length];
        newList[0]=oldlist[0];
        for (int i = 1; i <oldlist.length; i++) {
            newList[i]=newList[i-1]+oldlist[i];
        }
        Array2DRowRealMatrix array2DRowRealMatrix = new Array2DRowRealMatrix(newList);
        //System.out.println("新序列的长度为："+newList.length);
        //System.out.println("最终得到新序列的矩阵为：" + array2DRowRealMatrix);
        return newList;
    }

    /**
     * 得到平均序列模型
     * @param oldList
     * @return
     */
    public  static double[] getAverageList(double[] oldlist){
        double  k=0.5;
        double[] newList = getNewList(oldlist);
        double[] averageList=new double[oldlist.length-1];
        for (int i = 0; i <averageList.length ; i++) {
            averageList[i]=k*newList[i]+(1-k)*newList[i+1];
        }
        //System.out.println("输出得到平均序列的长度"+averageList.length);
        Array2DRowRealMatrix array2DRowRealMatrix = new Array2DRowRealMatrix(averageList);
      // System.out.println("最终得到平均序列的的矩阵为：" + array2DRowRealMatrix);
        return averageList;
    }
    public  static Array2DRowRealMatrix getBMatrix(double[] oldlist){
        double[][] XMatrix=new double[oldlist.length-1][2];
        for (int i = 0; i < XMatrix.length; i++) {
            XMatrix[i][0]=-getAverageList(oldlist)[i];
            XMatrix[i][1]=1;
        }
        Array2DRowRealMatrix array2DRowRealMatrix = new Array2DRowRealMatrix(XMatrix);
      // System.out.println("最终得到的B矩阵为" + array2DRowRealMatrix);
        return array2DRowRealMatrix;
    }

    /**
     * 最终得到是关于Y的矩阵，是从序列号为2到结束
     * @param oldList
     * @return
     */
    public static Array2DRowRealMatrix getYMatrix(double[] oldlist){
        double[] YMatrix=new double[oldlist.length-1];
        for (int i = 1; i <oldlist.length ; i++) {
            YMatrix[i-1]=oldlist[i];
        }
        Array2DRowRealMatrix matrix = new Array2DRowRealMatrix(YMatrix);
        RealMatrix transposeYMatrix = matrix.transpose();
       // System.out.println("最终得到的Y矩阵为"+matrix);
        return (Array2DRowRealMatrix) matrix;
    }

    /**
     * 计算得到常数矩阵
     * @param oldList
     * @return
     */
    public  static Array2DRowRealMatrix getResultMatrix(double[] oldlist){
        Array2DRowRealMatrix bMatrix = getBMatrix(oldlist);//计算得到的B矩阵
        Array2DRowRealMatrix yMatrix = getYMatrix(oldlist);//计算得到的Y矩阵

        RealMatrix transpose = bMatrix.transpose();//计算的到B的转置矩阵
        RealMatrix multiply = transpose.multiply(bMatrix);//计算得到转置乘以该矩阵
       // System.out.println("矩阵与转置矩阵相乘的结果"+multiply);
        LUDecomposition luDecomposition = new LUDecomposition(multiply);
        DecompositionSolver solver = luDecomposition.getSolver();
        RealMatrix inverse = solver.getInverse();//计算得到矩阵相乘结果的逆
        //System.out.println("逆矩阵为："+inverse);
        //RealMatrix Ytranspose = yMatrix.transpose();
        RealMatrix multiply1 = inverse.multiply(transpose).multiply(yMatrix);//最终得到计算的结果为两行一列的矩阵
       // System.out.println("常数矩阵的值为" + multiply1);
        return (Array2DRowRealMatrix) multiply1;
    }

    /**
     * 最终得到常数的矩阵
     * @param oldList
     * @return
     */
    public static Array2DRowRealMatrix getPMatrix(double[] oldlist){
        double[][] pMatrix=new double[2][1];
        Array2DRowRealMatrix resultMatrix = getResultMatrix(oldlist);
        double[][] data = resultMatrix.getData();
        pMatrix[0][0]=data[0][0];
        pMatrix[1][0]=data[1][0];
        Array2DRowRealMatrix PMatrix = new Array2DRowRealMatrix(pMatrix);
        //System.out.println("最终得到的常数矩阵为" + PMatrix);
        return PMatrix;
    }

    public static double[] getPredictionList(double[] oldlist){
        double[] PredictionList=new double[oldlist.length];
        for (int i = 1; i <oldlist.length+1 ; i++) {
            PredictionList[i-1]=(oldlist[0]-getResultMatrix(oldlist).getData()[1][0]/
                    getResultMatrix(oldlist).getData()[0][0])*Math.exp(-getResultMatrix(oldlist).getData()[0][0]*i)
            *(1-Math.exp(getResultMatrix(oldlist).getData()[0][0]));
        }
        //System.out.println("初始值为："+Arrays.toString(oldlist));
       //System.out.println("预测值为："+Arrays.toString(PredictionList));
        return PredictionList;
    }
    public static double getPredictNextStepValue(double[] oldlist){
        double PredictNextStepValue=0.0;
        int size=oldlist.length+1;
            PredictNextStepValue=(oldlist[0]-getResultMatrix(oldlist).getData()[1][0]/
                    getResultMatrix(oldlist).getData()[0][0])*Math.exp(-getResultMatrix(oldlist).getData()[0][0]*size)
                    *(1-Math.exp(getResultMatrix(oldlist).getData()[0][0]));
        //System.out.println("第十八个预测值为"+PredictNextStepValue);
        return PredictNextStepValue;
    }


    /* 一步转移概率矩阵 解释：状态有0 1 2 3 4 ,当 当前状态如0且下一状态为如1,称为一步状态转移 */
    public static Double[][] statusTransProbablity(int[] statusValueNum, List<Integer> levelList) {
        Double[][] transProbablityMatrix = new Double[4][4];
        // 一步转移概率矩阵
        // /**********************************遍历结果是一步状态转移概率矩阵*******************************/
        for (int s = 0; s < statusValueNum.length; s++) {
            // 控制数组的行数变化 s充当了状态值
            int status = 1;
            // 状态的类型取值为1 2 3 4 当每一次遍历完之后,status要重新变化
            for (int i = 0; i < statusValueNum.length; i++) {
                int index = 0; // 记录一步转移状态的次数 每一次遍历完毕清0
                for (int j = 0; j < levelList.size() - 1; j++) {
                    // levelList.size()-1 原因是k+1 已经可以达到链尾
                    int k = j;
                    // k值是为了在当前遍历中使用 如果当前状态值和下一次状态值相等遍历整个数据链 每次只查找一次状态的改变
                    // 例如先查找0转移到0，然后是0到1 依次类推
                    while (levelList.get(k).intValue() == s && levelList.get(k + 1).intValue() == status) {
                        index++;// 计数器加1
                        if (k < levelList.size() - 2) {// 减2解释 当k=28时 levelList.get(k+1) 此处会下标越界，已超出表述范围
                            k++;// 当前位置向后移动一位 即为了查询某个状态值连续出现的次数
                        } else {
                            break; // 当不满足当前条件，终止最近的循环体
                        }
                    }
                }
                // System.out.println("Trans[" + s + "][" + i + "] " + index);
                // 计算概率值
                transProbablityMatrix[s][i] = (double) index / (double) statusValueNum[s];
                status++;
            }
        }
        return transProbablityMatrix;
    }

    public static double[][] getTransitionProbability(int[] statusValueNum ,List<Integer> levelList){
        double[][] transProbablityMatrix = new double[4][4];
        int size = levelList.size();//长度
        int point1=0,point2=1;
        while(point2<size){
            transProbablityMatrix[levelList.get(point1)-1][levelList.get(point2)-1]+=1;
            ++point1;
            ++point2;

        }
        for (int i = 0; i < transProbablityMatrix.length; i++) {
            int status=statusValueNum[i];
            for (int j = 0; j <transProbablityMatrix.length; j++) {
                transProbablityMatrix[i][j]=(double)transProbablityMatrix[i][j]/(double)status;
            }
        }
        Array2DRowRealMatrix Two=new Array2DRowRealMatrix(transProbablityMatrix);
        Array2DRowRealMatrix multiply = null;
        Array2DRowRealMatrix y=null;
        Array2DRowRealMatrix x=null;
        multiply=Two.multiply(Two);
        System.out.println("二步转移概率"+multiply);
        return transProbablityMatrix;
    }
    public static Array2DRowRealMatrix getTwoTransitionProability(int[] statusValueNum ,List<Integer> levelList){
        double[][] transitionProbability = getTransitionProbability(statusValueNum, levelList);
        //获得第二步转移概率
        Array2DRowRealMatrix Two=new Array2DRowRealMatrix(transitionProbability);
        Array2DRowRealMatrix multiply = Two.multiply(Two);
        return multiply;
    }

    public static double getFinalPredictionValue(int[] statusValueNum, List<Integer> levelList,double[] oldlist){
        double[][] transitionProbability = getTwoTransitionProability(statusValueNum, levelList).getData();//获得转移概率
        int size = levelList.size();//获取状态序列的长度
        int statuValue=levelList.get(size-1);
        //System.out.println("最后一个预测值所对应的状态为："+statuValue);
        int i=0;
        double max=0.0;
        int row=0;
        while (i < transitionProbability.length){
            if (transitionProbability[statuValue-1][i] > max){
                max=transitionProbability[statuValue-1][i];
                row=i+1;
            }
            i++;
        }
        System.out.println("最大的概率所对应的列为："+row);
        double finalPredictionValue=0.0;
        double primitivePredictionValue=getPredictNextStepValue(oldlist);//获得最后一个原始最后一个预测值
        switch (row){
            case 1:finalPredictionValue=primitivePredictionValue+0.5*(-getDNumber(oldlist)-getBNumber(oldlist));
                    break;
            case 2:finalPredictionValue=primitivePredictionValue+0.5*(-getBNumber(oldlist));
                    break;
            case 3:finalPredictionValue=primitivePredictionValue+0.5*(getANumber(oldlist));
                    break;
            case 4:finalPredictionValue=primitivePredictionValue+0.5*(getANumber(oldlist)+getCNumber(oldlist));
                    break;
            default:
                System.out.println("请确认数据");
        }
        return finalPredictionValue;
    }
public static double getFinalPredictionValueByTwo(int[] statusValueNum, List<Integer> levelList,double[] oldlist){
    double[][] transitionProbability = getTransitionProbability(statusValueNum, levelList);//获得转移概率
    int size = levelList.size();//获取状态序列的长度
    int statuValue=levelList.get(size-1);
    //System.out.println("最后一个预测值所对应的状态为："+statuValue);
    int i=0;
    double max=0.0;
    int row=0;
    while (i < transitionProbability.length){
        if (transitionProbability[statuValue-1][i] > max){
            max=transitionProbability[statuValue-1][i];
            row=i+1;
        }
        i++;
    }
    System.out.println("最大的概率所对应的列为："+row);
    double finalPredictionValue=0.0;
    double primitivePredictionValue=getPredictNextStepValue(oldlist);//获得最后一个原始最后一个预测值
    switch (row){
        case 1:finalPredictionValue=primitivePredictionValue+0.5*(-getDNumber(oldlist)-getBNumber(oldlist));
            break;
        case 2:finalPredictionValue=primitivePredictionValue+0.5*(-getBNumber(oldlist));
            break;
        case 3:finalPredictionValue=primitivePredictionValue+0.5*(getANumber(oldlist));
            break;
        case 4:finalPredictionValue=primitivePredictionValue+0.5*(getANumber(oldlist)+getCNumber(oldlist));
            break;
        default:
            System.out.println("请确认数据");
    }
    return finalPredictionValue;
}

    public static double getANumber(double[] oldlist){
        double aSum=0.0;
        int aCount=0;//计数历史利用率高于预测值的个数；
        double Ai=0.0;
        double[] predictionNumber = getPredictionList(oldlist);//获取主机的预测利用值
        //double[] utilizationHistory = getUtilizationHistory(host);//获取主机的历史利用率
        for (int i = 0; i < oldlist.length; i++) {
            if ((oldlist[i]-predictionNumber[i])>0){
                ++aCount;
                aSum+=oldlist[i]-predictionNumber[i];
            }
        }
        Ai=aSum/aCount;
     //  System.out.println("上边界曲线为：" + Ai);
        return Ai;
    }
    public static double getBNumber(double[] oldlist){
        double bSum=0.0;
        int bCount=0;//计数历史利用率高于预测值的个数；
        double Bi=0.0;
        double[] predictionNumber = getPredictionList(oldlist);//获取主机的预测利用值
        //double[] utilizationHistory = getUtilizationHistory(host);//获取主机的历史利用率
        for (int i = 0; i < oldlist.length; i++) {
            if ((predictionNumber[i]-oldlist[i])>0){
                ++bCount;
                bSum+=predictionNumber[i]-oldlist[i];
            }
        }
        Bi=bSum/bCount;
       // System.out.println("下边界曲线为：" + Bi);
        return Bi;
    }

    public static double getCNumber(double[] oldlist){
        double C=0.0,Cmax=0.0;
        double[] predictionNumber = getPredictionList(oldlist);//获取主机的预测利用值
        //double[] utilizationHistory = getUtilizationHistory(host);//获取主机的历史利用率
        for (int i = 0; i < oldlist.length; i++) {
            if ((C=oldlist[i]-predictionNumber[i])>0){
                if (C>Cmax){
                    Cmax=C;
                }
            }
        }
       // System.out.println("上极限边界曲线为："+Cmax);
        return Cmax;
    }

    public static double getDNumber(double[] oldlist){
        double D=0.0,Dmax=0.0;
        double[] predictionNumber = getPredictionList(oldlist);//获取预测值
        for (int i = 0; i < oldlist.length; i++) {
            if ((D=predictionNumber[i]-oldlist[i])>0){
                if (D>Dmax){
                    Dmax=D;
                }
            }
        }
        //System.out.println("下极限边界曲线为："+Dmax);
        return Dmax;
    }
    public static int[] getStatusNumbers(double[] oldlist){
        double[] predictValues = getPredictionList(oldlist);//获取主机的预测利用率
        //double[] utilizationHistory = getUtilizationHistory(host);//获取主机的历史利用率
        int[] statusValue=new int[]{1,2,3,4};
        int[] statusValueNumber=new int[statusValue.length];//用来统计每种状态出现的次数及其状态值
        double aNumber = getANumber(oldlist);
        double bNumber = getBNumber(oldlist);
        double cNumber = getCNumber(oldlist);
        double dNumber = getDNumber(oldlist);
        List<Integer> levelList=new ArrayList<>();//存储每个利用率所对应的状态值
        for (int i = 0; i < oldlist.length; i++) {
            if ((oldlist[i] < (predictValues[i]-bNumber)) &&
                    oldlist[i] >= (predictValues[i]-dNumber)){
                statusValueNumber[0]++;
                levelList.add(1);
            }if ((oldlist[i] < predictValues[i]) &&
                    oldlist[i] >= (predictValues[i]-bNumber)){
                statusValueNumber[1]++;
                levelList.add(2);
            }if ((oldlist[i] < (predictValues[i]+aNumber)) &&
                    oldlist[i] >= (predictValues[i])){
                statusValueNumber[2]++;
                levelList.add(3);
            }if ((oldlist[i] <= (predictValues[i]+cNumber)) &&
                    oldlist[i] >= (predictValues[i]+aNumber)){
                statusValueNumber[3]++;
                levelList.add(4);
            }else{
                continue;
            }
        }
        System.out.println("状态1、2、3、4分别出现的次数:\n" + Arrays.toString(statusValueNumber) + "\n");
        return statusValueNumber;
    }

    public static List<Integer> getLevelList(double[] oldList){
        double[] predictValues = getPredictionList(oldList);//获取主机的预测利用率
        //double[] utilizationHistory = getUtilizationHistory(host);//获取主机的历史利用率
        int[] statusValue=new int[]{1,2,3,4};
        int[] statusValueNumber=new int[statusValue.length];//用来统计每种状态出现的次数及其状态值
        double aNumber = getANumber(oldList);
        double bNumber = getBNumber(oldList);
        double cNumber = getCNumber(oldList);
        double dNumber = getDNumber(oldList);
        List<Integer> levelList=new ArrayList<>();//存储每个利用率所对应的状态值
        for (int i = 0; i < oldList.length; i++) {
            if ((oldList[i] < (predictValues[i]-bNumber)) &&
                    oldList[i] >= (predictValues[i]-dNumber)){
                statusValueNumber[0]++;
                levelList.add(1);
            }if ((oldList[i] < predictValues[i]) &&
                    oldList[i] >= (predictValues[i]-bNumber)){
                statusValueNumber[1]++;
                levelList.add(2);
            }if ((oldList[i] < (predictValues[i]+aNumber)) &&
                    oldList[i] >= predictValues[i]){
                statusValueNumber[2]++;
                levelList.add(3);
            }if ((oldList[i] <= (predictValues[i]+cNumber)) &&
                    oldList[i] >= (predictValues[i]+aNumber)){
                statusValueNumber[3]++;
                levelList.add(4);
            }else{
                continue;
            }
        }
        System.out.println("每个时间序列所对应的状态值:\n" + levelList + "\n");
        return levelList;
    }
}
