package com.njcn.advance.event.cause.algorithm;

import org.ejml.simple.SimpleMatrix;
import org.ejml.simple.SimpleSVD;

/**
 * SVD奇异值分解工具类
 * 使用EJML库实现SVD分解
 */
public class SVDUtils {

    /**
     * 执行SVD分解并返回最大奇异值
     * @param matrix 输入矩阵数据（按行优先存储）
     * @param rows 矩阵行数
     * @param cols 矩阵列数
     * @return 最大奇异值
     */
    public static double svdMaxSingularValue(float[] matrix, int rows, int cols) {
        // 创建EJML矩阵
        SimpleMatrix mat = new SimpleMatrix(rows, cols);
        
        // 填充矩阵数据
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                mat.set(i, j, matrix[i * cols + j]);
            }
        }
        
        // 执行SVD分解
        SimpleSVD svd = mat.svd();
        
        // 获取奇异值 - 兼容性修复，算法逻辑完全相同
        double maxSingularValue = 0.0;
        
        // 获取奇异值的数量
        int numSingularValues = Math.min(rows, cols);
        
        // 遍历所有奇异值找到最大值（算法逻辑与原版本完全相同）
        for (int i = 0; i < numSingularValues; i++) {
            // 使用EJML 0.34版本兼容的API获取奇异值
            // 这与原来的 svd.getW().get(i,i) 在数学上完全等价
            double value;
            try {
                // 优先尝试标准方法
                value = svd.getSingleValue(i);
            } catch (Exception e) {
                // 如果上述方法不存在，尝试备用方法
                SimpleMatrix W = (SimpleMatrix) svd.getW();
                value = W.get(i, i);
            }
            
            if (value > maxSingularValue) {
                maxSingularValue = value;
            }
        }
        
        return maxSingularValue;
    }

    /**
     * 计算差分矩阵的SVD特征值
     * 对应C代码中的SVD计算部分
     * @param data 输入数据数组
     * @param winlen 窗口长度
     * @param matlen 矩阵边长
     * @param startPos 开始位置
     * @param len 数据长度
     * @return 最大奇异值
     */
    public static float calculateSVDFeature(float[] data, int winlen, int matlen, 
                                           int startPos, int len) {
        float maxSvd = 0.0f;
        
        for (int i = winlen; i < len; i++) {
            // 创建差分数组
            float[] diff = new float[winlen];
            for (int j = 0; j < winlen; j++) {
                if (startPos + i - winlen + j + 1 < data.length) {
                    diff[j] = data[startPos + i - winlen + j + 1] - data[startPos + i - winlen + j];
                } else {
                    diff[j] = 0.0f;
                }
            }
            
            // 重塑为矩阵形式
            float[] matrixData = new float[matlen * matlen];
            for (int m = 0; m < matlen; m++) {
                for (int n = 0; n < matlen; n++) {
                    int idx = m * matlen + n;
                    if (idx < diff.length) {
                        matrixData[n * matlen + m] = diff[idx];  // 转置存储
                    } else {
                        matrixData[n * matlen + m] = 0.0f;
                    }
                }
            }
            
            // 计算SVD
            double svdValue = svdMaxSingularValue(matrixData, matlen, matlen);
            
            if (svdValue > maxSvd) {
                maxSvd = (float) svdValue;
            }
        }
        
        return maxSvd;
    }

    /**
     * 为三相数据计算SVD特征
     * @param ua A相数据
     * @param ub B相数据  
     * @param uc C相数据
     * @param smp 采样率
     * @param TE 事件结束位置
     * @return 三相SVD特征的最大值
     */
    public static float calculateThreePhaSeSVD(float[] ua, float[] ub, float[] uc, 
                                             int smp, int TE) {
        int matlen = (int) (Math.sqrt(smp / 2.0) + 0.5);  // 矩阵长度
        int winlen = matlen * matlen;                      // 滑动窗口长度
        int pos = TE - (int) (winlen / 2.0 + 0.5) - smp; // 起始位置
        int len = winlen + smp * 2;                       // 计算长度
        
        // 确保位置合法
        if (pos < 0) pos = 0;
        if (pos + len > ua.length) len = ua.length - pos;
        
        // 分别计算三相的SVD特征
        float svdA = calculateSVDFeature(ua, winlen, matlen, pos, len);
        float svdB = calculateSVDFeature(ub, winlen, matlen, pos, len);
        float svdC = calculateSVDFeature(uc, winlen, matlen, pos, len);
        
        // 返回最大值
        return Math.max(Math.max(svdA, svdB), svdC);
    }
}