package org.cloudbus.cloudsim.power;

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 org.cloudbus.cloudsim.*;
import org.cloudbus.cloudsim.codes.arima.ARIMAPredictionValue;
import org.cloudbus.cloudsim.power.lists.PowerVmList;
import org.cloudbus.cloudsim.util.MathUtil;

import java.util.*;

public class PowerVmAllocationPolicyMigrationqcreatahp extends PowerVmAllocationPolicyMigrationAbstract{

    public PowerVmAllocationPolicyMigrationqcreatahp(List<? extends Host> hostList,
                                                                 PowerVmSelectionPolicy vmSelectionPolicy,
                                                                 PowerVmAllocationPolicyMigrationAbstract fallbackVmAllocationPolicy) {
        super(hostList, vmSelectionPolicy);
        setFallbackVmAllocationPolicy(fallbackVmAllocationPolicy);
}
    public PowerVmAllocationPolicyMigrationqcreatahp(
            List<? extends Host> hostList,
            PowerVmSelectionPolicy vmSelectionPolicy,
            double safetyParameter,
            double schedulingInterval,
            PowerVmAllocationPolicyMigrationAbstract fallbackVmAllocationPolicy) {
        super(hostList, vmSelectionPolicy);
        setSafetyParameter(safetyParameter);
        setSchedulingInterval(schedulingInterval);
        setFallbackVmAllocationPolicy(fallbackVmAllocationPolicy);
    }
    private double schedulingInterval;
    private double safetyParameter;

    public double getSchedulingInterval() {
        return schedulingInterval;
    }

    public void setSchedulingInterval(double schedulingInterval) {
        this.schedulingInterval = schedulingInterval;
    }

    public double getSafetyParameter() {
        return safetyParameter;
    }

    public void setSafetyParameter(double safetyParameter) {
        this.safetyParameter = safetyParameter;
    }

    private PowerVmAllocationPolicyMigrationAbstract fallbackVmAllocationPolicy;

    public PowerVmAllocationPolicyMigrationAbstract getFallbackVmAllocationPolicy() {
        return fallbackVmAllocationPolicy;
    }

    public void setFallbackVmAllocationPolicy(PowerVmAllocationPolicyMigrationAbstract fallbackVmAllocationPolicy) {
        this.fallbackVmAllocationPolicy = fallbackVmAllocationPolicy;
    }


    //实现对主机的过载进行检查
    @Override
    protected boolean isHostOverUtilized(PowerHost host) {

        double[] utilizationHistory = getUtilizationHistory(host);//直接获取主机的利用率
        double predictUtilization = 0.0;
        int length = 18;

        //这里最终获得下一时间序列的利用率的预测值
        if (utilizationHistory.length == length) {
            predictUtilization = //getPredictionUtlizationByMarkov(host);
                    0.45 * getPredictionList(utilizationHistory)[length - 1] + 0.55 *
                            ARIMAPredictionValue.getFinalPredictionValue(utilizationHistory);
        }
        //System.out.println("主机序号为" + host.getId() + "的利用率长度为：" + utilizationHistory.length+"其预测利用率为："+predictUtilization);
        //计算得出主机的利用率
        double totalRequestedMips = 0;
        //设置tu阈值为
        double tu = 0.8;
        for (Vm vm : host.getVmList()) {
            totalRequestedMips += vm.getCurrentUtilizationOfCpu() * vm.getMips();
        }
        double utilzationOfCpu = 0;
        utilzationOfCpu = totalRequestedMips / host.getTotalMips();
        //System.out.println("主机序号为" + host.getId() +"；该主机的真实利用率为"+utilzationOfCpu+"真实的利用率为："+host.getUtilizationOfCpu());
        if (utilizationHistory.length < length) {
            if (utilzationOfCpu > tu) {
                //addHistoryEntry(host, utilzationOfCpu);
                return true;
            } else {
                return false;
            }
        }

        else{
//            predictUtilization=0.45 * getPredictionList(utilizationHistory)[length - 1] + 0.55 *
//                    ARIMAPredictionValue.getFinalPredictionValue(utilizationHistory);
            if ( utilzationOfCpu > tu &&  predictUtilization > tu) {
                //  addHistoryEntry(host, predictUtilization);
                System.out.println("真实CPU利用率为"+utilzationOfCpu+"\t"+"预测的CPU利用率为:"+predictUtilization);
                return true;
            } else {
                return false;
            }
        }

    }

    /*
        接收到的是host  输出的是预测与实际利用率所产生的功率除以所包含虚拟机的多少
    */
    public double getCalculatingUnderloadHost(PowerHost host) {
        double finalPredictionValue = ARIMAPredictionValue.getFinalPredictionValue(getUtilizationHistory(host));
        double power_prediction = host.getPower(finalPredictionValue);
        double totalRequestMips = 0;
        for (Vm vm : host.getVmList()) {
            totalRequestMips += vm.getCurrentRequestedTotalMips();
        }
        double utilizationOfCPU = totalRequestMips / host.getTotalMips();
        double value = power_prediction + host.getPower(utilizationOfCPU) / host.getVmList().size();
        return value;
    }

    /**
     * 主机负载不足检测
     *
     * @param host
     * @return
     */
    protected boolean isHostUnderUtilized(PowerHost host) {
        double[] utilizationHistory = getUtilizationHistory(host);
        //double[] predictUtilization = getPredictionList(utilizationHistory);
        double predictNextStepValue = 0.0;
        int length = 18;
        //
        if (utilizationHistory.length == length) {
            predictNextStepValue =//getPredictionUtlizationByMarkov(host);
                    0.45 * getPredictionList(utilizationHistory)[length - 1] + 0.55 *
                            ARIMAPredictionValue.getFinalPredictionValue(utilizationHistory);
        }
        //计算得出主机的利用率
        double totalRequestedMips = 0;
        //设置tu阈值为Z
        double tu = 0.3;
        for (Vm vm : host.getVmList()) {
            totalRequestedMips += vm.getCurrentRequestedTotalMips();
        }
        double utilzationOfCpu = 0;
        utilzationOfCpu = totalRequestedMips / host.getTotalMips();
        //System.out.println("主机序号为" + host.getId() +"；该主机的真实利用率为"+utilzationOfCpu+"真实的利用率为："+host.getUtilizationOfCpu());
        if (utilizationHistory.length < length) {
            if (utilzationOfCpu <= tu) {
                //addHistoryEntry(host, utilzationOfCpu);
                return true;
            } else {
                return false;
            }
        }else{
//            predictNextStepValue =//getPredictionUtlizationByMarkov(host);
//                    0.45 * getPredictionList(utilizationHistory)[length - 1] + 0.55 *
//                            ARIMAPredictionValue.getFinalPredictionValue(utilizationHistory);
            if (utilzationOfCpu <= tu &&
                    predictNextStepValue <= tu) {
                //addHistoryEntry(host, predictNextStepValue);
                return true;
            } else {
                return false;
            }
        }


    }

    //对获取最负载不足的主机进行选择
    @Override
    protected PowerHost getUnderUtilizedHost(Set<? extends Host> excludedHosts) {
        PowerHost underUtilizedHost = null;
        int length = 18 ;//设置主机的历史的CPU资源利用率的长度
        for (PowerHost host : this.<PowerHost>getHostList()) {
            if (excludedHosts.contains(host)) {
                continue;
            }
            double minUtilization = 1;
            //获取负载不足的主机的历史利用率
            double[] utilizationHistory = getUtilizationHistory(host);
            //计算得出主机的利用率
            double totalRequestedMips = 0;
            //设置tu阈值为Z
            double tu = 0.3;
            for (Vm vm : host.getVmList()) {
                totalRequestedMips += vm.getCurrentRequestedTotalMips();
            }
            double utilzationOfCpu = totalRequestedMips / host.getTotalMips();
            if (utilizationHistory.length < length) {
                double utilization_length_less_length = host.getUtilizationOfCpu();
                if ( utilization_length_less_length < minUtilization && !areAllVmsMigratingOutOrAnyVmMigratingIn(host)) {
                    minUtilization = utilization_length_less_length;
                    underUtilizedHost = host;
                }
                return underUtilizedHost;
            } else {
                minUtilization=1;
                //获取该利用率
                double prediction_utilization = //getPredictionUtlizationByMarkov(host);
                        0.45 * getPredictionList(utilizationHistory)[utilizationHistory.length - 1] +
                                0.55 * ARIMAPredictionValue.getFinalPredictionValue(utilizationHistory);
                double meanUtilization = (prediction_utilization + utilzationOfCpu) / 2;
                if (prediction_utilization < tu && utilzationOfCpu < tu) {
                    if (meanUtilization < minUtilization){
                        minUtilization=meanUtilization;
                        underUtilizedHost=host;
                    }
                    return underUtilizedHost;
                }
                if (prediction_utilization > tu && utilzationOfCpu < tu){
                    if ( meanUtilization < minUtilization
                            && !areAllVmsMigratingOutOrAnyVmMigratingIn(host)) {
                        minUtilization = meanUtilization;
                        underUtilizedHost = host;
                    }
                    System.out.println("负载不足的主机编号为：" + underUtilizedHost.getId() + "的负载不足的利用率为" + underUtilizedHost.getUtilizationOfCpu()+"其预测的利用率为"+prediction_utilization);
                    return underUtilizedHost;
                }
            }
        }
        return underUtilizedHost;
    }
    @Override
    protected List<Map<String, Object>> getNewVmPlacement(List<? extends Vm> vmsToMigrate, Set<? extends Host> excludedHosts){
        List<Map<String, Object>> migrationMap = new LinkedList<Map<String, Object>>();
        //关闭的主机
        List<PowerHost> switchedOffHosts = this.<PowerHost>getSwitchedOffHosts();
        //PowerVmList.sortByCpuUtilization(vmsToMigrate);
        for (Vm vm :vmsToMigrate){
            double minCriterion=Double.MIN_VALUE;
            Host allocatedHost = null;
            /*List<Double> slatahlist = new ArrayList<Double>();
            for(Host host1 :this.getHostList()){
                PowerHost host2 = (PowerHost)host1;
                double slatah = getSlatah(host2);
                slatahlist.add(slatah);
            }
            System.out.println(slatahlist);*/

            for (Host host :this.getHostList()){
                //HostDynamicWorkload host12 = (HostDynamicWorkload) host;
                //<HostStateHistoryEntry> a = host12.getStateHistory();
                if ( excludedHosts.contains(host)) {
                   continue;
                }
                if ( switchedOffHosts.contains(host)) {
                    continue;
                }
                double dertaUtilizaiton  = 1 - ((PowerHost) host).getUtilizationOfCpu() - (vm.getCurrentUtilizationOfCpu() * vm.getMips()) / host.getTotalMips();
                if (dertaUtilizaiton > 0){
                    if (host.isSuitableForVm(vm)) {
                        double power = getPowerAfterAllocation((PowerHost) host, vm);
                        double powerDiff = power - ((PowerHost) host).getPower();
                        double rwas = (getpositive(host.getUtilizaionOfCpu() - host.getUtilizationOfRam()) + 0.0001)/(host.getUtilizaionOfCpu() + host.getUtilizationOfRam());
                        //double comparevalue = 0.288*(Math.exp(-powerDiff))+0.479*(1-host.getUtilizaionOfCpu())-0.059*rwas+0.174/host.getUtilizaionOfCpu();
                        double comparevalue = 0.28*(Math.exp(-powerDiff))+0.549*(1-host.getUtilizaionOfCpu())+0.056*rwas+0.115/host.getUtilizaionOfCpu();
                        HostDynamicWorkload host2 = (HostDynamicWorkload)host;
//                        double previousAllocated1 = 0;
//                        double previousRequested1 = 0;
//                        for (HostStateHistoryEntry suv : host2.getStateHistory()){
//                            previousAllocated1+=suv.getAllocatedMips();
//                            previousRequested1+=suv.getRequestedMips();
//                        }
//                        System.out.println(previousAllocated1);
//                        System.out.println(previousRequested1);

                        /*PowerHost host2 = (PowerHost)host;
                        double predictvalue =getpredictutilization(host2);
                        HostDynamicWorkload host3 = (HostDynamicWorkload) host2;*/
//                        boolean conform = isConform(host2);
//                        if (conform = false){
//                            continue;
//                        }
                        double slatah = getSlatah(host2);
                        double pdm = getSlaMetrics(vm);
                        //double comparevalue = slatah;
                        if (comparevalue > minCriterion) {//谁da选谁
                            minCriterion = comparevalue;
                            allocatedHost = host;
                        }
                    }
                }

            }
            if (allocatedHost != null) {
                Log.printConcatLine("VM #", vm.getId(), " allocated to host #", allocatedHost.getId());
                Map<String, Object> migrate = new HashMap<String, Object>();
                migrate.put("vm", vm);
                migrate.put("host", allocatedHost);
                migrationMap.add(migrate);
            }
        }
        return migrationMap;
    }
    protected boolean isConform (HostDynamicWorkload host){
        boolean a = true;
        for (HostStateHistoryEntry entry : host.getStateHistory()) {
            double previousAllocated = entry.getAllocatedMips();
            double previousRequested = entry.getRequestedMips();

                if (previousAllocated < previousRequested) {
                    a = false;
                }
        }
        return a;
    }
    //计算主机的SLATAH值
    protected static double getSlatah(HostDynamicWorkload host) {
        double slaViolationTimePerHost = 0;
        double totalTime = 0;
            double previousTime = -1;
            double previousAllocated = 0;
            double previousRequested = 0;
            boolean previousIsActive = true;

            for (HostStateHistoryEntry entry : host.getStateHistory()) {
                if (previousTime != -1 && previousIsActive) {
                    double timeDiff = entry.getTime() - previousTime;
                    totalTime += timeDiff;
                    if (previousAllocated < previousRequested) {
                        slaViolationTimePerHost += timeDiff;
                    }
                }

                previousAllocated = entry.getAllocatedMips();
                previousRequested = entry.getRequestedMips();
                previousTime = entry.getTime();
                previousIsActive = entry.isActive();
            }


        return slaViolationTimePerHost / totalTime;
    }
    //计算PDM的值
    protected double getSlaMetrics(Vm vm) {
        Map<String, Double> metrics = new HashMap<String, Double>();
        List<Double> slaViolation = new LinkedList<Double>();
        double totalAllocated = 0;
        double totalRequested = 0;
        double totalUnderAllocatedDueToMigration = 0;


            double vmTotalAllocated = 0;
            double vmTotalRequested = 0;
            double vmUnderAllocatedDueToMigration = 0;
            double previousTime = -1;
            double previousAllocated = 0;
            double previousRequested = 0;
            boolean previousIsInMigration = false;

            for (VmStateHistoryEntry entry : vm.getStateHistory()) {
                if (previousTime != -1) {
                    double timeDiff = entry.getTime() - previousTime;
                    vmTotalAllocated += previousAllocated * timeDiff;
                    vmTotalRequested += previousRequested * timeDiff;

                    if (previousAllocated < previousRequested) {
                        slaViolation.add((previousRequested - previousAllocated) / previousRequested);
                        if (previousIsInMigration) {
                            vmUnderAllocatedDueToMigration += (previousRequested - previousAllocated)
                                    * timeDiff;
                        }
                    }
                }

                previousAllocated = entry.getAllocatedMips();
                previousRequested = entry.getRequestedMips();
                previousTime = entry.getTime();
                previousIsInMigration = entry.isInMigration();
            }

            totalAllocated += vmTotalAllocated;
            totalRequested += vmTotalRequested;
            totalUnderAllocatedDueToMigration += vmUnderAllocatedDueToMigration;


        metrics.put("overall", (totalRequested - totalAllocated) / totalRequested);
        if (slaViolation.isEmpty()) {
            metrics.put("average", 0.);
        } else {
            metrics.put("average", MathUtil.mean(slaViolation));
        }
        metrics.put("underallocated_migration", totalUnderAllocatedDueToMigration / totalRequested);
        // metrics.put("sla_time_per_vm_with_migration", slaViolationTimePerVmWithMigration /
        // totalTime);
        // metrics.put("sla_time_per_vm_without_migration", slaViolationTimePerVmWithoutMigration /
        // totalTime);

        return totalUnderAllocatedDueToMigration / totalRequested;
    }
    public double getpositive(double x){
        if (x >= 0){
            return x;}
        else {
            return -x;
        }
    }
    public double getpredictutilization(PowerHost host){
        double[] utilizationHistory = getUtilizationHistory(host);//直接获取主机的利用率
        double predictUtilization = 0.0;
        double predict = 0.0;
        int length = 18;
        if (utilizationHistory.length == length) {
            predict = //getPredictionUtlizationByMarkov(host);
                    0.45 * getPredictionList(utilizationHistory)[length - 1] + 0.55 *
                            ARIMAPredictionValue.getFinalPredictionValue(utilizationHistory);
        }
        double totalRequestedMips = 0;
        for (Vm vm1 : host.getVmList()) {
            totalRequestedMips += vm1.getCurrentUtilizationOfCpu() * vm1.getMips();
        }
        double utilzationOfCpu = 0.0;
        utilzationOfCpu = totalRequestedMips / host.getTotalMips();
        if (utilizationHistory.length < length){
            predictUtilization = utilzationOfCpu;
        }
        else {
            predictUtilization = predict;
        }
        return predictUtilization;
    }



    protected List<Map<String, Object>> getNewVmPlacement123(List<? extends Vm> vmsToMigrate, Set<? extends Host> excludedHosts) {
        List<Map<String, Object>> migrationMap = new LinkedList<Map<String, Object>>();
        //获取整个过程中的主机列表
        //List<PowerHost> allhostList = this.<PowerHost>getHostList();
        //关闭的主机
        List<PowerHost> switchedOffHosts = this.<PowerHost>getSwitchedOffHosts();
        // ArrayList<? extends Host> hostlist = new ArrayList<>(excludedHosts);
        PowerVmList.sortByCpuUtilization(vmsToMigrate);
        for (Vm vm : vmsToMigrate) {
            double minProfomance=Double.MIN_VALUE;
            double minPower = Double.MAX_VALUE;
            Host allocatedHost = null;
            for (Host host : excludedHosts) {
//               if ( isHostUnderUtilized((PowerHost) host)) {
//                   continue;
//                }
//               else{
                double dertaUtilizaiton  = //host.getTotalMips()-vm.getCurrentRequestedTotalMips();
                        1 - ((PowerHost) host).getUtilizationOfCpu() - (vm.getCurrentUtilizationOfCpu() * vm.getMips()) / host.getTotalMips();
                // System.out.println("利用率之差为："+dertaUtilizaiton);
                //然后计算相关的关于主机的消耗模型，就是得获得相关主机的消耗模型
                if (dertaUtilizaiton > 0){
//                   System.out.println("该主机的利用率差为"+(host.getAvailableMips()-vm.getCurrentRequestedTotalMips()));
                    //  if ((host.getAvailableMips()-vm.getCurrentRequestedTotalMips()) > 0){
                    if (host.isSuitableForVm(vm)) {
                        double power = getPowerAfterAllocation((PowerHost) host, vm);
                        double powerDiff = power - ((PowerHost) host).getPower();
                        //double resouceAvaiable=Math.abs(((PowerHost) host).getUtilizationOfCpu()-host.getUtilizationOfStorge())/((PowerHost) host).getUtilizationOfCpu()+host.getUtilizationOfStorge();
                        double profomance =(1-host.getUtilizaionOfCpu())* (Math.exp(-powerDiff));//e得-powerDiff次方
                        if (profomance > minProfomance) {//谁大选谁
                            minProfomance = power;
                            allocatedHost = host;
                        }
                    }
                }
                //          }
                //设置所产生的利用率差值^U
                if (allocatedHost != null) {
                    Log.printConcatLine("VM #", vm.getId(), " allocated to host #", allocatedHost.getId());
                    Map<String, Object> migrate = new HashMap<String, Object>();
                    migrate.put("vm", vm);
                    migrate.put("host", allocatedHost);
                    migrationMap.add(migrate);
                }
            }
            if (vm != null) {
                System.out.println("还存在虚拟机未分配完");
                minProfomance=Double.MIN_VALUE;
                for(PowerHost host: switchedOffHosts){
                    if (host.isSuitableForVm(vm)){
                        double power = getPowerAfterAllocation((PowerHost) host, vm);
                        double powerDiff = power - ((PowerHost) host).getPower();
                        //double resouceAvaiable=Math.abs(((PowerHost) host).getUtilizationOfCpu()-host.getUtilizationOfStorge())/((PowerHost) host).getUtilizationOfCpu()+host.getUtilizationOfStorge();
                        double profomance =(1-host.getUtilizaionOfCpu())*
                                (Math.exp(-powerDiff));
                        if (profomance > minProfomance) {
                            minProfomance = power;
                            allocatedHost = host;
                        }
                    }
                }
                if (allocatedHost != null) {
                    Log.printConcatLine("VM #", vm.getId(), " allocated to host #", allocatedHost.getId());
                    Map<String, Object> migrate = new HashMap<String, Object>();
                    migrate.put("vm", vm);
                    migrate.put("host", allocatedHost);
                    migrationMap.add(migrate);
                }
            }
        }
        return migrationMap;
    }
    //获得利用率的阈值
    public double getThreldUtilization(PowerHost host){
        double[] utilizationHistory = getUtilizationHistory(host);
        double tu=1-getSafetyParameter() * MathUtil.mad(utilizationHistory);
        return tu;//获得最终的利用率
    }

    //通过主机直接获得最终的利用率
    public double getPredictionUtlizationByMarkov(PowerHost host){
        double[] utilizationHistory = getUtilizationHistory(host);
        double finalPredictionValue = getFinalPredictionValue(utilizationHistory);
        return finalPredictionValue;
    }
    //计算得出host的利用率
    protected double[] getUtilizationHistory(PowerHost host) {
        PowerHostUtilizationHistory historyHost = (PowerHostUtilizationHistory) host;
        double[] oldutilizationHistory = historyHost.getUtilizationHistory();
        Array2DRowRealMatrix array2DRowRealMatrix = new Array2DRowRealMatrix(oldutilizationHistory);
        return oldutilizationHistory;
    }

    /**
     * 使用历史利用率对主机的利用率进行预测
     * 生成最后的x(1)(n)=x(1)(n-1)+x(0)(n)
     */

    //计算得出主机的新的利用率
    protected double[] getNewHostUtilization(PowerHost host) {

        double[] passUtilization = getUtilizationHistory(host);
        double[] newUtilizationList = new double[passUtilization.length];
        newUtilizationList[0] = passUtilization[0];
        for (int i = 1; i < passUtilization.length; i++) {
            newUtilizationList[i] = newUtilizationList[i - 1] + passUtilization[i];
        }
        //System.out.println("主机序号为：" + host.getId() + "的新利用率为：" + new Array2DRowRealMatrix(newUtilizationList));
        return newUtilizationList;
    }

    public double[] getNewList(double[] oldList) {
        //double[] utilizationHistory = getUtilizationHistory(host);
        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;
    }
    /**
     *使用历史利用率进行预测生成最后的
     * x(1)(k)=sum(1,k)x(0)(i)
     */

    /**
     * 生成均值序列。Z(1)(k)=1/2(x(1)(k)+x(1)(k-1))
     */

    public double[] getAverageList(double[] oldList) {
        //double[] utilizationHistory = getUtilizationHistory(host);
        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;
    }
    /**
     * 生成X*P=Y矩阵中的x矩阵
     * x矩阵为n行2列的矩阵
     * @return
     */
    /*protected Array2DRowRealMatrix getXMatrix(PowerHost host){
        double[][] xMatrix=new double[getUtilizationHistory(host).length-1][2];
        for (int i=0;i<getUtilizationHistory(host).length-1;i++){
            *//*for (j=0;j<xMatrix[0].length;j++){
                if (j == 0){
                    xMatrix[i][j]=-getAverageList(host)[i];
                }else{
                    xMatrix[i][j]=1;
                }
            }
            xMatrix[i][0]=-getAverageList(host)[i];
            xMatrix[i][1]=1;
        }
        Array2DRowRealMatrix XMatrix = new Array2DRowRealMatrix(xMatrix);
        //Log.print("主机序号为"+host.getId()+"；计算得到的X矩阵为：" + XMatrix);
        System.out.println("主机序号为"+host.getId()+"；计算得到的X矩阵为：" + XMatrix);
        return XMatrix;
    }*/

    /**
     * 获得B矩阵，为n-1行两列的矩阵
     *
     * @param oldList
     * @return
     */
    public Array2DRowRealMatrix getBMatrix(double[] oldList) {
        //double[] utilizationHistory = getUtilizationHistory(host);

        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 = null;
        try {
            array2DRowRealMatrix = new Array2DRowRealMatrix(XMatrix);
        } catch (Exception e) {
            System.out.println("MXatrix不能为空");
        }
        return array2DRowRealMatrix;
    }


    /**
     * 获得常数列 从利用率第二个到最后一个的利用率
     *
     * @param oldList
     * @return
     */
    public Array2DRowRealMatrix getYMatrix(double[] oldList) {

        //double[] utilizationHistory = getUtilizationHistory(host);
        double[] YMatrix = new double[oldList.length - 1];
        for (int i = 1; i < oldList.length; i++) {
            YMatrix[i - 1] = oldList[i];
        }
        Array2DRowRealMatrix matrix = null;
        try {
            matrix = new Array2DRowRealMatrix(YMatrix);
        } catch (Exception e) {
            System.out.println("YMatrix长度为0");
        }

        RealMatrix transposeYMatrix = matrix.transpose();
        return (Array2DRowRealMatrix) matrix;
    }

    public Array2DRowRealMatrix getResultMatrix(double[] oldList) {
        // double[] utilizationHistory = getUtilizationHistory(host);
        Array2DRowRealMatrix bMatrix = getBMatrix(oldList);//计算得到的B矩阵
        Array2DRowRealMatrix yMatrix = getYMatrix(oldList);//计算得到的Y矩阵

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

    /**
     * 计算得到解
     *
     * @return
     */
    public 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;
    }

    /*protected Array2DRowRealMatrix getResult(PowerHost host)  {
        //计算出关于x的矩阵
        Array2DRowRealMatrix xMatrix = getXMatrix(host);
        System.out.println("在计算结果中，x矩阵为"+xMatrix);
        //double[][] data = xMatrix.getData();
        //计算得出关于Y的矩阵
        Array2DRowRealMatrix yMatrix = getYMatrix(host);
        System.out.println("在计算结果中，Y矩阵为"+yMatrix);
        //计算得出Y的转置矩阵
        RealMatrix Ytranspose = yMatrix.transpose();
        System.out.println("在计算结果中，Y的转置矩阵"+Ytranspose);
        //Array2DRowRealMatrix yMatrix = new Array2DRowRealMatrix(yMatrix1);
        //使矩阵转置得使用的方法为：transponse
        //计算得出矩阵（xT*X）^-1
        //计算得出x的转置矩阵
        RealMatrix xtranspose = xMatrix.transpose();
        //计算出XT* X
        System.out.println("打印出X转置矩阵"+xtranspose);

        RealMatrix XTransposeMultiplyXMatrix = xtranspose.multiply(xMatrix);
        System.out.println("打印出y转置矩阵与y的矩阵相乘的结果"+XTransposeMultiplyXMatrix);
        LUDecomposition luDecomposition = new LUDecomposition(XTransposeMultiplyXMatrix);
        DecompositionSolver solver = luDecomposition.getSolver();
        RealMatrix inverse = solver.getInverse();
        System.out.println("打印得到y的逆矩阵"+inverse);
        Array2DRowRealMatrix m1 =(Array2DRowRealMatrix)inverse;
        System.out.println("打印得到y的逆矩阵"+m1);
        //得出【a,b】T的这个矩阵
        RealMatrix multiply = inverse.multiply(xtranspose);
        RealMatrix multiply1 = multiply.multiply(yMatrix);
        System.out.println("最终计算的到结果"+multiply1);
        RealMatrix transpose = multiply1.transpose();
        System.out.println("最终计算的常数矩阵为："+transpose);
        return (Array2DRowRealMatrix) transpose;
    }*/

    /**
     * 最后获得的是关于主机的利用率的一个预测公式：
     * 原始主机利用率
     *
     * @param oldList
     * @return
     */
   /* protected double[] getPredictionUtilization(double[] oldList){
        //double[] resultPrediction=new double[getUtilizationHistory(host).length];
        for(int i=1;i<getUtilizationHistory(host).length+1;i++){
            resultPrediction[i-1]=(getUtilizationHistory(host)[0]-getPMatrix(host).getData()[1][0]/
                    getPMatrix(host).getData()[0][0])*Math.exp(-getPMatrix(host).getData()[0][0]*(i-1))
                    *(1-Math.exp(getPMatrix(host).getData()[0][0]));
            System.out.println("主机序号为"+host.getId()+"预测得出原始主机利用率"+resultPrediction[i]);
        }
        return resultPrediction;
    }*/

    /**
     * Gets the maximum vm migration time.
     *
     * @param host the host
     * @return the maximum vm migration time
     */
    protected double getMaximumVmMigrationTime(PowerHost host) {
        int maxRam = Integer.MIN_VALUE;
        for (Vm vm : host.getVmList()) {
            int ram = vm.getRam();
            if (ram > maxRam) {
                maxRam = ram;
            }
        }
        return maxRam / ((double) host.getBw() / (2 * 8000));
    }
    protected  double getPredictionUtilizaiton(PowerHost host,double[] utilizationHistory){
        //获取历史利用率
        int n=utilizationHistory.length;
        double sumX = 0.0;
        double sumY=0.0;
        double[] x=new double[n];
        double[] y=new double[n];
        double yDividex=0.0;
        double average_x=0;
        double  average_y=0;
        double OneDivideX=0.0;
        for(int i=1;i<n;i++){
            x[i]=i+1;
            y[i]=utilizationHistory[i];
            sumX+=x[i];
            sumY+=y[i];
            yDividex+=  y[i]/x[i];
            average_x+=x[i]/n;
            average_y+=y[i]/n;
            OneDivideX+=1/x[i];
        }
        double beta1=(yDividex-average_y*OneDivideX)/(n-average_x*OneDivideX);
        double beta0=average_y-beta1*average_x;
        //此时应该指明主机的
        double MigrationInterval=getMaximumVmMigrationTime(host)/getSchedulingInterval();
        double predictUtilization=beta0+beta1*(n+MigrationInterval);
        double finalUtilization=getSafetyParameter()*predictUtilization;
        return finalUtilization;
    }


    public double[] getPredictionList(double[] oldList) {
        //double[] utilizationHistory = getUtilizationHistory(host);
        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;
    }
        /*
        第二阶段，使用马尔可夫链预测模型进行编码
        首先将预测的值获取了之后进行求出所在的几个状态区间
         */

    //获取上下边界线，以及上下极限边界线

    /**
     * 计算得到上边界线的值A
     *
     * @param oldList
     * @return
     */
    public double getANumber(double[] oldList) {
        double aSum = 0.0;
        int aCount = 0;//计数历史利用率高于预测值的个数；
        double Ai = 0.0;
        double[] predictionUtilization = getPredictionList(oldList);//获取主机的预测利用率
        //double[] utilizationHistory = getUtilizationHistory(host);//获取主机的历史利用率
        for (int i = 0; i < oldList.length; i++) {
            if ((oldList[i] - predictionUtilization[i]) > 0) {
                ++aCount;
                aSum += oldList[i] - predictionUtilization[i];
            }
        }
        Ai = aSum / aCount;
        return Ai;
    }

    /**
     * 计算得到关于下边界线的值B
     *
     * @param oldList
     * @return
     */
    public double getBNumber(double[] oldList) {
        double bSum = 0.0;
        int bCount = 0;//计数历史利用率高于预测值的个数；
        double Bi = 0.0;
        double[] predictionUtilization = getPredictionList(oldList);//获取主机的预测利用率
        //double[] utilizationHistory = getUtilizationHistory(host);//获取主机的历史利用率
        for (int i = 0; i < oldList.length; i++) {
            if ((predictionUtilization[i] - oldList[i]) > 0) {
                ++bCount;
                bSum += predictionUtilization[i] - oldList[i];
            }
        }
        Bi = bSum / bCount;
        return Bi;
    }

    /**
     * 计算获得上极限边界曲线的值C
     *
     * @param oldList
     * @return
     */
    public double getCNumber(double[] oldList) {
        double C = 0.0, Cmax = 0.0;
        double[] predictionUtilization = getPredictionList(oldList);//获取主机的预测利用率
        //double[] utilizationHistory = getUtilizationHistory(host);//获取主机的历史利用率
        for (int i = 0; i < oldList.length; i++) {
            if ((C = oldList[i] - predictionUtilization[i]) > 0) {
                if (C > Cmax) {
                    Cmax = C;
                }
            }
        }
        return Cmax;
    }

    /**
     * 计算得到下边界极限曲线的值D
     *
     * @param oldList
     * @return
     */
    public double getDNumber(double[] oldList) {
        double D = 0.0, Dmax = 0.0;
        double[] predictionUtilization = getPredictionList(oldList);//获取主机的预测利用率
        //double[] utilizationHistory = getUtilizationHistory(host);//获取主机的历史利用率
        for (int i = 0; i < oldList.length; i++) {
            if ((D = predictionUtilization[i] - oldList[i]) > 0) {
                if (D > Dmax) {
                    Dmax = D;
                }
            }
        }
        return Dmax;
    }

    /**
     * 下一步操作的目的就是得出状态个数于相关的转移状态的个数
     */


    /**
     * 获取预测数据的状态转移值
     *
     * @return
     */
    public 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;
    }

    /**
     * 获得主机的状态数量
     *
     * @param oldList
     * @return
     */
    public 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;
    }

    /**
     * 获得转移矩阵
     *
     * @param statusValueNum
     * @param levelList
     * @return
     */
    public 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;
            }
        }
        return transProbablityMatrix;
    }

    /**
     * 获得下一步的预测值
     *
     * @param oldList
     * @return
     */
    public double getPredictNextStepValue(double[] oldList) {
        double PredictNextStepValue = 0.0;
        // double[] utilizationHistory = getUtilizationHistory(host);
        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;
    }

    public double getFinalPredictionValue(double[] oldList) {
        int[] statusNumbers = getStatusNumbers(oldList);
        List<Integer> levelList = getLevelList(oldList);
        double[][] transitionProbability = getTransitionProbability(statusNumbers, 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;
    }
}