package com.njcn.advance.responsibility.utils;

import com.njcn.advance.responsibility.constant.HarmonicConstants;
import org.apache.commons.math3.linear.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 数学工具类
 * 提供基础数学计算功能
 * 
 * @author hongawen
 * @version 1.0
 */
public class MathUtils {
    
    private static final Logger logger = LoggerFactory.getLogger(MathUtils.class);
    
    /**
     * 计算协方差
     * 
     * @param x 数据序列1
     * @param y 数据序列2
     * @param width 数据窗宽度
     * @return 协方差值
     */
    public static double covariance(double[] x, double[] y, int width) {
        if (x == null || y == null || width <= 0) {
            throw new IllegalArgumentException("Invalid input parameters for covariance calculation");
        }
        
        if (x.length < width || y.length < width) {
            throw new IllegalArgumentException("Data length is less than window width");
        }
        
        double meanX = 0.0;
        double meanY = 0.0;
        
        // 计算均值
        for (int i = 0; i < width; i++) {
            meanX += x[i];
            meanY += y[i];
        }
        meanX /= width;
        meanY /= width;
        
        // 计算协方差
        double cov = 0.0;
        for (int i = 0; i < width; i++) {
            cov += (x[i] - meanX) * (y[i] - meanY);
        }
        
        return cov / (width - 1);
    }
    
    /**
     * 计算协方差（float版本）
     */
    public static float covariance(float[] x, float[] y, int width) {
        double[] dx = new double[x.length];
        double[] dy = new double[y.length];
        for (int i = 0; i < x.length; i++) dx[i] = x[i];
        for (int i = 0; i < y.length; i++) dy[i] = y[i];
        return (float) covariance(dx, dy, width);
    }
    
    /**
     * 计算Pearson相关系数
     * 
     * @param x 数据序列1
     * @param y 数据序列2
     * @param count 数据长度
     * @return Pearson相关系数
     */
    public static double pearsonCorrelation(double[] x, double[] y, int count) {
        if (x == null || y == null || count <= 0) {
            throw new IllegalArgumentException("Invalid input parameters for Pearson correlation");
        }
        
        double meanX = 0.0;
        double meanY = 0.0;
        
        // 计算均值
        for (int i = 0; i < count; i++) {
            meanX += x[i];
            meanY += y[i];
        }
        meanX /= count;
        meanY /= count;
        
        // 计算相关系数的各个部分
        double numerator = 0.0;
        double denomX = 0.0;
        double denomY = 0.0;
        
        for (int i = 0; i < count; i++) {
            double dx = x[i] - meanX;
            double dy = y[i] - meanY;
            numerator += dx * dy;
            denomX += dx * dx;
            denomY += dy * dy;
        }
        
        double denominator = Math.sqrt(denomX * denomY);
        
        if (Math.abs(denominator) < HarmonicConstants.EPSILON) {
            logger.warn("Denominator is too small in Pearson correlation calculation");
            return 0.0;
        }
        
        return numerator / denominator;
    }
    
    /**
     * 计算Pearson相关系数（float版本）
     */
    public static float pearsonCorrelation(float[] x, float[] y, int count) {
        double[] dx = new double[count];
        double[] dy = new double[count];
        for (int i = 0; i < count; i++) {
            dx[i] = x[i];
            dy[i] = y[i];
        }
        return (float) pearsonCorrelation(dx, dy, count);
    }
    
    /**
     * 计算协方差矩阵（SXX）
     * 
     * @param data 数据矩阵 [时间][节点]
     * @param width 窗口宽度
     * @param nodeCount 节点数
     * @return 协方差矩阵
     */
    public static double[][] covarianceMatrix(double[][] data, int width, int nodeCount) {
        double[][] covMatrix = new double[nodeCount][nodeCount];
        
        for (int i = 0; i < nodeCount; i++) {
            for (int j = 0; j < nodeCount; j++) {
                double[] col1 = new double[width];
                double[] col2 = new double[width];
                
                for (int k = 0; k < width; k++) {
                    col1[k] = data[k][i];
                    col2[k] = data[k][j];
                }
                
                covMatrix[i][j] = covariance(col1, col2, width);
            }
        }
        
        return covMatrix;
    }
    
    /**
     * 计算协方差矩阵（float版本）
     */
    public static float[][] covarianceMatrix(float[][] data, int width, int nodeCount) {
        float[][] covMatrix = new float[nodeCount][nodeCount];
        
        for (int i = 0; i < nodeCount; i++) {
            for (int j = 0; j < nodeCount; j++) {
                float[] col1 = new float[width];
                float[] col2 = new float[width];
                
                for (int k = 0; k < width; k++) {
                    col1[k] = data[k][i];
                    col2[k] = data[k][j];
                }
                
                covMatrix[i][j] = covariance(col1, col2, width);
            }
        }
        
        return covMatrix;
    }
    
    /**
     * 计算协方差向量（SXY）
     * 
     * @param data 数据矩阵 [时间][节点]
     * @param y 目标向量
     * @param width 窗口宽度
     * @param nodeCount 节点数
     * @return 协方差向量
     */
    public static double[] covarianceVector(double[][] data, double[] y, int width, int nodeCount) {
        double[] covVector = new double[nodeCount];
        
        for (int i = 0; i < nodeCount; i++) {
            double[] col = new double[width];
            for (int k = 0; k < width; k++) {
                col[k] = data[k][i];
            }
            covVector[i] = covariance(col, y, width);
        }
        
        return covVector;
    }
    
    /**
     * 计算协方差向量（float版本）
     */
    public static float[] covarianceVector(float[][] data, float[] y, int width, int nodeCount) {
        float[] covVector = new float[nodeCount];
        
        for (int i = 0; i < nodeCount; i++) {
            float[] col = new float[width];
            for (int k = 0; k < width; k++) {
                col[k] = data[k][i];
            }
            covVector[i] = covariance(col, y, width);
        }
        
        return covVector;
    }
    
    /**
     * 矩阵求逆
     * 使用Apache Commons Math库
     * 
     * @param matrix 输入矩阵
     * @return 逆矩阵
     */
    public static double[][] matrixInverse(double[][] matrix) {
        RealMatrix realMatrix = new Array2DRowRealMatrix(matrix);
        
        try {
            // 使用LU分解求逆
            DecompositionSolver solver = new LUDecomposition(realMatrix).getSolver();
            RealMatrix inverseMatrix = solver.getInverse();
            return inverseMatrix.getData();
        } catch (SingularMatrixException e) {
            logger.error("Matrix is singular, cannot compute inverse", e);
            throw new RuntimeException("Matrix inversion failed: singular matrix");
        }
    }
    
    /**
     * 计算矩阵的特征值
     * 
     * @param matrix 输入矩阵
     * @return 特征值数组
     */
    public static double[] eigenvalues(double[][] matrix) {
        RealMatrix realMatrix = new Array2DRowRealMatrix(matrix);
        EigenDecomposition eigenDecomposition = new EigenDecomposition(realMatrix);
        return eigenDecomposition.getRealEigenvalues();
    }
    
    /**
     * 归一化处理
     * 将数据归一化到[0,1]区间
     * 
     * @param data 输入数据
     * @return 归一化后的数据
     */
    public static double[] normalize(double[] data) {
        if (data == null || data.length == 0) {
            return data;
        }
        
        double min = Double.MAX_VALUE;
        double max = Double.MIN_VALUE;
        
        // 找最大最小值
        for (double value : data) {
            min = Math.min(min, value);
            max = Math.max(max, value);
        }
        
        double range = max - min;
        if (Math.abs(range) < HarmonicConstants.EPSILON) {
            return new double[data.length]; // 返回全0数组
        }
        
        double[] normalized = new double[data.length];
        for (int i = 0; i < data.length; i++) {
            normalized[i] = (data[i] - min) / range;
        }
        
        return normalized;
    }
    
    /**
     * 数据对齐处理
     * 将不同采样间隔的数据对齐到相同的时间间隔
     * 
     * @param data 原始数据
     * @param originalInterval 原始采样间隔
     * @param targetInterval 目标采样间隔
     * @return 对齐后的数据
     */
    public static float[] alignData(float[] data, int originalInterval, int targetInterval) {
        if (targetInterval % originalInterval != 0) {
            throw new IllegalArgumentException(
                "Target interval must be multiple of original interval");
        }
        
        int ratio = targetInterval / originalInterval;
        int newLength = data.length / ratio;
        float[] alignedData = new float[newLength];
        
        for (int i = 0; i < newLength; i++) {
            float sum = 0;
            for (int j = 0; j < ratio; j++) {
                sum += data[i * ratio + j];
            }
            alignedData[i] = sum / ratio;
        }
        
        return alignedData;
    }
}