use crate::error::AlgebraError;
use crate::matrix::{AlgebraLimits, Matrix};
use crate::semiring::Semiring;
impl<S: Semiring> Matrix<S> {
pub fn power(&self, k: usize, limits: AlgebraLimits) -> Result<Self, AlgebraError> {
self.validate()?;
if !self.is_square() {
return Err(AlgebraError::ShapeMismatch(format!(
"power requires a square matrix, got {}x{}",
self.rows, self.cols
)));
}
if self.rows > limits.max_dim {
return Err(AlgebraError::LimitExceeded(format!(
"power: dimension {} exceeds max_dim {}",
self.rows, limits.max_dim
)));
}
let n = self.rows;
let mut result = Matrix::try_identity_with_limits(n, limits)?;
let mut base = self.clone();
let mut e = k;
while e > 0 {
if e & 1 == 1 {
result = result.matmul(&base)?;
}
e >>= 1;
if e > 0 {
base = base.matmul(&base)?;
}
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Counting;
#[test]
fn power_zero_is_identity() {
let a = Matrix::from_rows(vec![
vec![Counting::from_u64(1), Counting::from_u64(1)],
vec![Counting::from_u64(0), Counting::from_u64(1)],
])
.unwrap();
assert_eq!(
a.power(0, AlgebraLimits::default()).unwrap(),
Matrix::identity(2)
);
}
#[test]
fn power_counts_walks() {
let mut a = Matrix::new(3, 3);
a.set(0, 1, Counting::from_u64(1)).unwrap();
a.set(1, 2, Counting::from_u64(1)).unwrap();
a.set(2, 0, Counting::from_u64(1)).unwrap();
let a2 = a.power(2, AlgebraLimits::default()).unwrap();
assert_eq!(a2.get(0, 2).unwrap(), &Counting::from_u64(1));
assert_eq!(a2.get(0, 0).unwrap(), &Counting::from_u64(0));
let a3 = a.power(3, AlgebraLimits::default()).unwrap();
assert_eq!(a3.get(0, 0).unwrap(), &Counting::from_u64(1));
}
#[test]
fn power_non_square_fails() {
let a: Matrix<Counting> = Matrix::new(2, 3);
assert!(matches!(
a.power(2, AlgebraLimits::default()),
Err(AlgebraError::ShapeMismatch(_))
));
}
}