///|
// =====================================================================
// matrix2.mbt — 矩陣演算法(續):秩、逆、分解、特徵值、子空間
// =====================================================================

/// M-21 秩 = RREF 中主元數
pub fn mat_rank(a : Mat, eps : Double) -> Int {
  let r = mat_rref(a, eps)
  let mut rank = 0
  for i = 0; i < r.rows; i = i + 1 {
    let mut nonzero = false
    for j = 0; j < r.cols; j = j + 1 {
      if mat_get(r, i, j).abs() > eps {
        nonzero = true
      }
    }
    if nonzero {
      rank = rank + 1
    }
  }
  rank
}

/// M-22 主元行位置(RREF 中每列首個非零元之行號)
pub fn mat_pivot_cols(a : Mat, eps : Double) -> Array[Int] {
  let r = mat_rref(a, eps)
  let pivs : Array[Int] = []
  for i = 0; i < r.rows; i = i + 1 {
    let mut j = 0
    while j < r.cols && mat_get(r, i, j).abs() < eps {
      j = j + 1
    }
    if j < r.cols {
      pivs.push(j)
    }
  }
  pivs
}

/// M-23 逆矩陣(Gauss–Jordan;奇異時回 None)
pub fn mat_inv(a : Mat) -> Option[Mat] {
  let n = a.rows
  // 增廣 [A | I]
  let aug = mat_new(n, 2 * n)
  for i = 0; i < n; i = i + 1 {
    for j = 0; j < n; j = j + 1 {
      mat_set(aug, i, j, mat_get(a, i, j))
    }
    mat_set(aug, i, n + i, 1.0)
  }
  let r = mat_rref(aug, 1.0e-12)
  // 左半須為單位陣
  let mut ok = true
  for i = 0; i < n; i = i + 1 {
    for j = 0; j < n; j = j + 1 {
      let want = if i == j { 1.0 } else { 0.0 }
      if (mat_get(r, i, j) - want).abs() > 1.0e-8 {
        ok = false
      }
    }
  }
  if !ok {
    Option::None
  } else {
    let out = mat_new(n, n)
    for i = 0; i < n; i = i + 1 {
      for j = 0; j < n; j = j + 1 {
        mat_set(out, i, j, mat_get(r, i, n + j))
      }
    }
    Some(out)
  }
}

/// M-24 解 Ax = b(唯一解;奇異時回 None)
pub fn mat_solve(a : Mat, b : Array[Double]) -> Option[Array[Double]] {
  match mat_inv(a) {
    Some(inv) => Some(mat_vec(inv, b))
    None => None
  }
}

/// M-25 LU 分解(部分主元):PA = LU,回傳 (L, U, perm)
pub fn mat_lu(a : Mat) -> (Mat, Mat, Array[Int]) {
  let n = a.rows
  let u = a.data.copy()
  let l = mat_eye(n)
  let perm : Array[Int] = []
  for i = 0; i < n; i = i + 1 {
    perm.push(i)
  }
  for col = 0; col < n - 1; col = col + 1 {
    let mut piv = col
    for r = col + 1; r < n; r = r + 1 {
      if u[r * n + col].abs() > u[piv * n + col].abs() {
        piv = r
      }
    }
    if piv != col {
      for j = 0; j < n; j = j + 1 {
        let t = u[col * n + j]
        u[col * n + j] = u[piv * n + j]
        u[piv * n + j] = t
      }
      let tp = perm[col]
      perm[col] = perm[piv]
      perm[piv] = tp
      // 交換 L 已算出的部分
      for j = 0; j < col; j = j + 1 {
        let t = mat_get(l, col, j)
        mat_set(l, col, j, mat_get(l, piv, j))
        mat_set(l, piv, j, t)
      }
    }
    if u[col * n + col].abs() < 1.0e-15 {
      // 奇異:保持現狀
      ()
    } else {
      for r = col + 1; r < n; r = r + 1 {
        let f = u[r * n + col] / u[col * n + col]
        mat_set(l, r, col, f)
        for j = col; j < n; j = j + 1 {
          u[r * n + j] = u[r * n + j] - f * u[col * n + j]
        }
      }
    }
  }
  ({ rows: n, cols: n, data: l.data }, { rows: n, cols: n, data: u }, perm)
}

/// M-26 由 LU 解方程:先解 Ly = Pb,再解 Ux = y
pub fn lu_solve(
  l : Mat,
  u : Mat,
  perm : Array[Int],
  b : Array[Double],
) -> Array[Double] {
  let n = b.length()
  // 前代
  let y = Array::make(n, 0.0)
  for i = 0; i < n; i = i + 1 {
    let mut s = b[perm[i]]
    for j = 0; j < i; j = j + 1 {
      s = s - mat_get(l, i, j) * y[j]
    }
    y[i] = s
  }
  // 回代
  let x = Array::make(n, 0.0)
  for i = n - 1; i >= 0; i = i - 1 {
    let mut s = y[i]
    for j = i + 1; j < n; j = j + 1 {
      s = s - mat_get(u, i, j) * x[j]
    }
    x[i] = s / mat_get(u, i, i)
  }
  x
}

/// M-27 Cholesky 分解 A = LLᵀ(A 須對稱正定;失敗回 None)
pub fn mat_cholesky(a : Mat) -> Option[Mat] {
  let n = a.rows
  let l = mat_new(n, n)
  let mut ok = true
  for i = 0; i < n; i = i + 1 {
    for j = 0; j < i + 1; j = j + 1 {
      let mut s = 0.0
      for k = 0; k < j; k = k + 1 {
        s = s + mat_get(l, i, k) * mat_get(l, j, k)
      }
      if i == j {
        let d = mat_get(a, i, i) - s
        if d <= 0.0 {
          ok = false
        } else {
          mat_set(l, i, j, d.sqrt())
        }
      } else {
        let diag = mat_get(l, j, j)
        if diag == 0.0 {
          ok = false
        } else {
          mat_set(l, i, j, (mat_get(a, i, j) - s) / diag)
        }
      }
    }
  }
  if ok {
    Some(l)
  } else {
    Option::None
  }
}

/// M-28 冪迭代:主特徵值與特徵向量(Rayleigh 商)
pub fn power_iteration(a : Mat, iters : Int) -> (Double, Array[Double]) {
  let n = a.rows
  let x : Array[Double] = []
  for i = 0; i < n; i = i + 1 {
    x.push(@math.sin(i.to_double() * 12.9898 + 4.1414) + 1.1)
  }
  // 正規化
  let mut nrm = 0.0
  for v in x {
    nrm = nrm + v * v
  }
  nrm = nrm.sqrt()
  for i = 0; i < n; i = i + 1 {
    x[i] = x[i] / nrm
  }
  let mut lam = 0.0
  for it_ = 0; it_ < iters; it_ = it_ + 1 {
    let ax = mat_vec(a, x)
    // Rayleigh 商
    let mut num = 0.0
    for i = 0; i < n; i = i + 1 {
      num = num + x[i] * ax[i]
    }
    lam = num
    let mut n2 = 0.0
    for v in ax {
      n2 = n2 + v * v
    }
    n2 = n2.sqrt()
    if n2 > 0.0 {
      for i = 0; i < n; i = i + 1 {
        x[i] = ax[i] / n2
      }
    }
  }
  (lam, x)
}

/// M-29 Jacobi 旋轉法求對稱矩陣特徵值
/// 回傳 (特徵值, Q),Q 之行為對應標準正交特徵向量
pub fn jacobi_eigen(a : Mat, max_sweeps : Int) -> (Array[Double], Mat) {
  let n = a.rows
  let s = a.data.copy()
  let q = mat_eye(n)
  for it_ = 0; it_ < max_sweeps; it_ = it_ + 1 {
    // 離对角範數
    let mut off = 0.0
    for i = 0; i < n; i = i + 1 {
      for j = i + 1; j < n; j = j + 1 {
        off = off + s[i * n + j] * s[i * n + j]
      }
    }
    if off.sqrt() < 1.0e-13 {
      break
    }
    for p = 0; p < n - 1; p = p + 1 {
      for qq = p + 1; qq < n; qq = qq + 1 {
        let apq = s[p * n + qq]
        if apq.abs() < 1.0e-16 {
          continue
        }
        let app = s[p * n + p]
        let aqq = s[qq * n + qq]
        let theta = (aqq - app) / (2.0 * apq)
        let t =
          if theta >= 0.0 {
            1.0 / (theta + (theta * theta + 1.0).sqrt())
          } else {
            -1.0 / (-theta + (theta * theta + 1.0).sqrt())
          }
        let c = 1.0 / (t * t + 1.0).sqrt()
        let ss = t * c
        // 旋轉 S = Gᵀ S G
        for i = 0; i < n; i = i + 1 {
          let sip = s[i * n + p]
          let siq = s[i * n + qq]
          s[i * n + p] = c * sip - ss * siq
          s[i * n + qq] = ss * sip + c * siq
        }
        for j = 0; j < n; j = j + 1 {
          let spj = s[p * n + j]
          let sqj = s[qq * n + j]
          s[p * n + j] = c * spj - ss * sqj
          s[qq * n + j] = ss * spj + c * sqj
        }
        // 累積 Q = Q G
        for i = 0; i < n; i = i + 1 {
          let qip = q.data[i * n + p]
          let qiq = q.data[i * n + qq]
          q.data[i * n + p] = c * qip - ss * qiq
          q.data[i * n + qq] = ss * qip + c * qiq
        }
      }
    }
  }
  let vals : Array[Double] = []
  for i = 0; i < n; i = i + 1 {
    vals.push(s[i * n + i])
  }
  (vals, { rows: n, cols: n, data: q.data })
}