// MatrixEigenJacobiPass
/*
------------------------------------------------------
作者   : Black Ghost
日期   : 2018-11-30
版本   : 0.0.0
------------------------------------------------------
    求解n阶对称矩阵A的全部特征值及其特征向量，雅可比过关法
理论：
    参考 李信真, 车刚明, 欧阳洁, 等. 计算方法. 西北工业大学
       出版社, 2000, pp 90.
------------------------------------------------------
输入   :
    A       系数矩阵
    tol     最大容许误差
    n       最大迭代步数
输出   :
    Bbar    主特征值矩阵（n阶对角矩阵）
    Rbar    主特征值所对应的特征向量（n维矩阵，第i列即对应于
            第i个特征值的特征向量）
   (err)    解出标志：false-未解出或达到步数上限；
                     true-全部解出
------------------------------------------------------
*/

package goNum

// MatrixEigenJacobiPass 求解n阶对称矩阵A的全部特征值及其特征向量，雅可比过关法
func MatrixEigenJacobiPass(A Matrix, tol float64, n int) (Matrix, Matrix, bool) {
	/*
		求解n阶对称矩阵A的全部特征值及其特征向量，雅可比过关法
		输入   :
		    A       系数矩阵
		    tol     最大容许误差
		    n       最大迭代步数
		输出   :
		    Bbar    主特征值矩阵（n阶对角矩阵）
		    Rbar    主特征值所对应的特征向量（n维矩阵，第i列即对应于
		            第i个特征值的特征向量）
		   (err)    解出标志：false-未解出或达到步数上限；
		                     true-全部解出
	*/

	//判断A是否对称矩阵
	if !isSymMatrix_MatrixEigenClassicalJacobi(A) {
		return ZeroMatrix(A.Rows, A.Columns), ZeroMatrix(A.Rows, A.Columns), false
	}
	//1.
	//Rbar最终为特征向量矩阵
	Rbar := IdentityE(A.Rows)
	//复制A矩阵为B，B为迭代过程中逐渐改变的矩阵，最终将成为特征值矩阵
	B := ZeroMatrix(A.Rows, A.Columns)
	Bbar := ZeroMatrix(A.Rows, A.Columns)
	for i := 0; i < len(A.Data); i++ {
		B.Data[i] = A.Data[i]
	}
	//2. 计算非对角元素平方和
	v0 := sum2Else_MatrixEigenClassicalJacobi(B)
	//3. 设置阀值v1
	v1 := v0 / float64(B.Rows)

	//迭代步
	for i := 0; i < n; i++ {
		for i0 := 0; i0 < B.Rows-1; i0++ {
			for j := i + 1; j < B.Columns; j++ {
				//逐个扫描，判断是否大于阀值
				if B.GetFromMatrix(i, j) > v1 {
					//Jocobi正交相似变换（古典Jocobi法）
					//计算cos(theta)及sin(theta)
					cost, sint := cosSinTheta_MatrixEigenClassicalJacobi(B, i, j)
					//R为迭代矩阵
					R := IdentityE(A.Rows)
					R.SetMatrix(i, i, cost)
					R.SetMatrix(i, j, sint)
					R.SetMatrix(j, i, -1.0*sint)
					R.SetMatrix(j, j, cost)
					Bbar = DotPruduct(DotPruduct(R, B), R.Transpose()) //A1 = RARt
					//Rbar = Rbar*Rt
					Rbar = DotPruduct(Rbar, R.Transpose())
				}
			}
		}
		//计算并判断v1是否满足误差需求，否则迭代
		v0 = sum2Else_MatrixEigenClassicalJacobi(Bbar)
		if v0 < tol {
			return Bbar, Rbar, true
		}
		v1 = v0 / float64(B.Rows)
		//A = A1
		for i := 0; i < len(Bbar.Data); i++ {
			B.Data[i] = Bbar.Data[i]
		}
	}

	return ZeroMatrix(A.Rows, A.Columns), ZeroMatrix(A.Rows, A.Columns), false
}
