harness_algebra/tensors/dynamic/
matrix.rs

1//! # Dynamic Matrix Module
2//!
3//! This module provides a flexible implementation of matrices with dynamically determined
4//! dimensions.
5//!
6//! ## Mathematical Background
7//!
8//! A matrix is a rectangular array of elements arranged in rows and columns. For a matrix $A$
9//! with $m$ rows and $n$ columns, we write $A \in F^{m \times n}$ where $F$ is the field of
10//! the matrix elements.
11//!
12//! ### Matrix Operations
13//!
14//! Matrices support various operations including:
15//!
16//! - **Transposition**: For a matrix $A$, its transpose $A^T$ has elements $A^T_{ij} = A_{ji}$
17//! - **Row Echelon Form**: A matrix is in row echelon form when:
18//!   - All rows consisting entirely of zeros are at the bottom
19//!   - The leading coefficient (pivot) of each non-zero row is to the right of the pivot in the row
20//!     above
21//!   - All entries in a column below a pivot are zeros
22//!
23//! ## Storage Orientations
24//!
25//! This implementation supports two matrix storage orientations:
26//!
27//! - **Row major**: Elements are stored row by row (each row is contiguous in memory)
28//! - **Column major**: Elements are stored column by column (each column is contiguous in memory)
29//!
30//! ## Examples
31//!
32//! ```
33//! use harness_algebra::{
34//!   prelude::*,
35//!   tensors::dynamic::{
36//!     matrix::{DynamicDenseMatrix, RowMajor},
37//!     vector::DynamicVector,
38//!   },
39//! };
40//!
41//! // Create a row-major matrix
42//! let mut matrix = DynamicDenseMatrix::<f64, RowMajor>::new();
43//!
44//! // Add rows to the matrix
45//! let row1 = DynamicVector::from([1.0, 2.0, 3.0]);
46//! let row2 = DynamicVector::from([4.0, 5.0, 6.0]);
47//! matrix.append_row(row1);
48//! matrix.append_row(row2);
49//!
50//! // Access elements
51//! let element = matrix.get_component(0, 1); // Gets element at row 0, column 1
52//! assert_eq!(*element, 2.0);
53//!
54//! // Transform to row echelon form
55//! let result = matrix.row_echelon_form();
56//! ```
57
58use std::{fmt, fmt::Debug, marker::PhantomData};
59
60use super::{vector::DynamicVector, *};
61
62/// Information about a pivot (non-zero entry) in a matrix.
63///
64/// When a matrix is transformed to row echelon form, pivots are the leading
65/// non-zero entries in each row. This struct stores the row and column indices
66/// of each pivot.
67#[derive(Debug, Clone, PartialEq, Eq, Hash)]
68pub struct PivotInfo {
69  /// The row index of the pivot
70  pub row: usize,
71  /// The column index of the pivot
72  pub col: usize,
73}
74
75/// Result of transforming a matrix to row echelon form.
76///
77/// This struct contains information about the rank of the matrix
78/// and the positions of all pivots found during the transformation.
79#[derive(Debug, Clone, PartialEq, Eq, Hash)]
80pub struct RowEchelonOutput {
81  /// The rank of the matrix (number of linearly independent rows/columns)
82  pub rank:   usize,
83  /// Positions of pivot elements in the row echelon form
84  pub pivots: Vec<PivotInfo>,
85}
86
87// Sealed trait pattern to prevent external implementations of MatrixOrientation
88mod sealed {
89  pub trait Sealed {}
90  impl Sealed for super::RowMajor {}
91  impl Sealed for super::ColumnMajor {}
92}
93
94/// A marker trait for matrix storage orientation (RowMajor or ColumnMajor).
95///
96/// This trait is sealed, meaning only types defined in this crate can implement it.
97/// The orientation determines how matrix elements are stored in memory, which affects
98/// the performance characteristics of different operations.
99pub trait MatrixOrientation: sealed::Sealed {}
100
101/// Marker type for row-major matrix storage.
102///
103/// In row-major storage, elements of a row are contiguous in memory.
104/// This is efficient for operations that access elements row by row.
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
106pub struct RowMajor;
107impl MatrixOrientation for RowMajor {}
108
109/// Marker type for column-major matrix storage.
110///
111/// In column-major storage, elements of a column are contiguous in memory.
112/// This is efficient for operations that access elements column by column.
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
114pub struct ColumnMajor;
115impl MatrixOrientation for ColumnMajor {}
116
117/// A dynamically-sized matrix with elements from a field `F`.
118///
119/// ## Mathematical Representation
120///
121/// For a matrix $A \in F^{m \times n}$, the elements are represented as:
122///
123/// $$ A = \begin{pmatrix}
124/// a_{11} & a_{12} & \cdots & a_{1n} \\
125/// a_{21} & a_{22} & \cdots & a_{2n} \\
126/// \vdots & \vdots & \ddots & \vdots \\
127/// a_{m1} & a_{m2} & \cdots & a_{mn}
128/// \end{pmatrix} $$
129///
130/// ## Storage Implementation
131///
132/// The storage orientation is determined by the type parameter `O`, which can be either
133/// `RowMajor` or `ColumnMajor`. This affects the internal representation and the performance
134/// characteristics of different operations:
135///
136/// - For `RowMajor`: Data is stored as a vector of row vectors
137/// - For `ColumnMajor`: Data is stored as a vector of column vectors
138///
139/// ## Usage Notes
140///
141/// Operations that align with the storage orientation (e.g., row operations on a row-major matrix)
142/// will generally be more efficient than those that don't.
143#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
144pub struct DynamicDenseMatrix<F, O: MatrixOrientation = RowMajor> {
145  /// For [`RowMajor`]: `components` is a [`DynamicVector`] of rows (each row is a
146  /// [`DynamicVector<F>`]).
147  /// For [`ColumnMajor`]: `components` is a [`DynamicVector`] of columns (each col is a
148  /// [`DynamicVector<F>`]).
149  components:  DynamicVector<DynamicVector<F>>,
150  /// The orientation of the matrix
151  orientation: PhantomData<O>,
152}
153
154impl<F, O: MatrixOrientation> DynamicDenseMatrix<F, O> {
155  /// Creates a new, empty `DynamicDenseMatrix` with the specified orientation.
156  ///
157  /// This constructor initializes a matrix with zero rows and zero columns.
158  ///
159  /// # Returns
160  ///
161  /// A new empty matrix with the orientation specified by the type parameter `O`.
162  ///
163  /// # Examples
164  ///
165  /// ```
166  /// use harness_algebra::tensors::dynamic::matrix::{ColumnMajor, DynamicDenseMatrix, RowMajor};
167  ///
168  /// // Create an empty row-major matrix of f64 values
169  /// let row_major: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
170  ///
171  /// // Create an empty column-major matrix of f64 values
172  /// let col_major: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
173  /// ```
174  pub const fn new() -> Self {
175    Self { components: DynamicVector::new(Vec::new()), orientation: PhantomData }
176  }
177}
178
179// TODO: Not all of these require Field and Copy, we should remove unneeded bbounds where possible.
180impl<F: Field + Copy> DynamicDenseMatrix<F, RowMajor> {
181  /// Creates a new all zeros `DynamicDenseMatrix` with the specified number of rows and columns.
182  ///
183  /// # Arguments
184  ///
185  /// * `rows` - The number of rows in the matrix
186  /// * `cols` - The number of columns in the matrix
187  ///
188  /// # Returns
189  ///
190  /// A new `DynamicDenseMatrix` with the specified number of rows and columns, all initialized to
191  /// zero.
192  pub fn zeros(rows: usize, cols: usize) -> Self {
193    let mut mat = Self::new();
194    for _ in 0..rows {
195      mat.append_row(DynamicVector::zeros(cols));
196    }
197    mat
198  }
199
200  /// Returns the number of rows in the matrix.
201  ///
202  /// For a row-major matrix, this is the number of row vectors stored.
203  ///
204  /// # Returns
205  ///
206  /// The number of rows in the matrix
207  pub const fn num_rows(&self) -> usize {
208    self.components.dimension() // Outer vector stores rows
209  }
210
211  /// Returns the number of columns in the matrix.
212  ///
213  /// For a row-major matrix, this is the length of the first row vector (if any).
214  /// Assumes a non-ragged matrix if rows > 0.
215  ///
216  /// # Returns
217  ///
218  /// The number of columns in the matrix
219  pub fn num_cols(&self) -> usize {
220    if self.components.dimension() == 0 {
221      0
222    } else {
223      self.components.components()[0].dimension()
224    }
225  }
226
227  /// Appends a new column to the matrix.
228  ///
229  /// If the matrix is empty, the column's elements become singleton rows.
230  /// Otherwise, each element of the column is appended to the corresponding row.
231  ///
232  /// # Arguments
233  ///
234  /// * `column` - The column vector to append
235  ///
236  /// # Panics
237  ///
238  /// Panics if the column's length doesn't match the number of existing rows (when the matrix is
239  /// not empty).
240  ///
241  /// # Warning
242  ///
243  /// For a row-major matrix, this operation requires updating every row vector.
244  /// If you're building a matrix primarily by adding columns, consider using
245  /// a column-major matrix for better performance.
246  ///
247  /// # Examples
248  ///
249  /// ```
250  /// use harness_algebra::tensors::dynamic::{
251  ///   matrix::{DynamicDenseMatrix, RowMajor},
252  ///   vector::DynamicVector,
253  /// };
254  ///
255  /// let mut matrix = DynamicDenseMatrix::<f64, RowMajor>::new();
256  ///
257  /// // Add a column to the empty matrix (will create rows)
258  /// let col = DynamicVector::from([1.0, 2.0]);
259  /// matrix.append_column(&col);
260  ///
261  /// // Add another column
262  /// let col2 = DynamicVector::from([3.0, 4.0]);
263  /// matrix.append_column(&col2);
264  /// ```
265  pub fn append_column(&mut self, column: &DynamicVector<F>) {
266    let num_r = self.num_rows();
267    if num_r == 0 {
268      if column.dimension() == 0 {
269        return;
270      }
271      for i in 0..column.dimension() {
272        self.components.components_mut().push(DynamicVector::new(vec![*column.get_component(i)]));
273      }
274    } else {
275      assert_eq!(num_r, column.dimension(), "Column length must match the number of rows");
276      for i in 0..num_r {
277        self.components.components_mut()[i].append(*column.get_component(i));
278      }
279    }
280  }
281
282  /// Returns a new DynamicVector representing the column at the given index.
283  ///
284  /// For a row-major matrix, this requires extracting the element at position `index`
285  /// from each row vector.
286  ///
287  /// # Arguments
288  ///
289  /// * `index` - The index of the column to retrieve (0-based)
290  ///
291  /// # Returns
292  ///
293  /// A new DynamicVector containing the elements of the specified column
294  ///
295  /// # Panics
296  ///
297  /// Panics if the column index is out of bounds.
298  ///
299  /// # Warning
300  ///
301  /// For a row-major matrix, this is a more expensive operation as it requires
302  /// reading from each row vector. If you need to access columns frequently,
303  /// consider using a column-major matrix instead.
304  pub fn get_column(&self, index: usize) -> DynamicVector<F> {
305    let num_r = self.num_rows();
306    if num_r == 0 {
307      return DynamicVector::new(Vec::new());
308    }
309    assert!(index < self.num_cols(), "Column index out of bounds");
310    let mut col_components = Vec::with_capacity(num_r);
311    for i in 0..num_r {
312      col_components.push(*self.components.components()[i].get_component(index));
313    }
314    DynamicVector::new(col_components)
315  }
316
317  /// Sets the column at the given index with the provided DynamicVector.
318  ///
319  /// # Arguments
320  ///
321  /// * `index` - The index of the column to set (0-based)
322  /// * `column` - The new column vector
323  ///
324  /// # Panics
325  ///
326  /// - Panics if the column index is out of bounds
327  /// - Panics if the column's length doesn't match the number of rows
328  ///
329  /// # Warning
330  ///
331  /// For a row-major matrix, this is a more expensive operation as it requires
332  /// updating every row vector. If you need to modify columns frequently,
333  /// consider using a column-major matrix instead.
334  pub fn set_column(&mut self, index: usize, column: &DynamicVector<F>) {
335    let num_r = self.num_rows();
336    assert_eq!(num_r, column.dimension(), "New column length must match the number of rows");
337    if num_r == 0 {
338      return;
339    }
340    assert!(index < self.num_cols(), "Column index out of bounds");
341    for i in 0..num_r {
342      self.components.components_mut()[i].set_component(index, *column.get_component(i));
343    }
344  }
345
346  /// Appends a new row to the matrix.
347  ///
348  /// # Arguments
349  ///
350  /// * `row` - The row vector to append
351  ///
352  /// # Panics
353  ///
354  /// Panics if the row's length doesn't match the number of existing columns (when the matrix is
355  /// not empty).
356  ///
357  /// # Examples
358  ///
359  /// ```
360  /// use harness_algebra::tensors::dynamic::{
361  ///   matrix::{DynamicDenseMatrix, RowMajor},
362  ///   vector::DynamicVector,
363  /// };
364  ///
365  /// let mut matrix = DynamicDenseMatrix::<f64, RowMajor>::new();
366  ///
367  /// // Add rows to the matrix
368  /// let row1 = DynamicVector::from([1.0, 2.0, 3.0]);
369  /// let row2 = DynamicVector::from([4.0, 5.0, 6.0]);
370  /// matrix.append_row(row1);
371  /// matrix.append_row(row2);
372  /// ```
373  pub fn append_row(&mut self, row: DynamicVector<F>) {
374    if self.num_rows() > 0 {
375      assert_eq!(
376        self.num_cols(),
377        row.dimension(),
378        "New row length must match existing number of columns"
379      );
380    }
381    self.components.components_mut().push(row);
382  }
383
384  /// Returns a reference to the row at the given index.
385  ///
386  /// For a row-major matrix, this directly accesses the stored row vector.
387  ///
388  /// # Arguments
389  ///
390  /// * `index` - The index of the row to retrieve (0-based)
391  ///
392  /// # Returns
393  ///
394  /// A reference to the row vector at the specified index
395  ///
396  /// # Panics
397  ///
398  /// Panics if the row index is out of bounds.
399  pub fn get_row(&self, index: usize) -> &DynamicVector<F> {
400    assert!(index < self.num_rows(), "Row index out of bounds");
401    &self.components.components()[index]
402  }
403
404  /// Sets the row at the given index with the provided DynamicVector.
405  ///
406  /// # Arguments
407  ///
408  /// * `index` - The index of the row to set (0-based)
409  /// * `row` - The new row vector
410  ///
411  /// # Panics
412  ///
413  /// - Panics if the row index is out of bounds
414  /// - Panics if the row's length doesn't match the number of columns
415  pub fn set_row(&mut self, index: usize, row: DynamicVector<F>) {
416    assert!(index < self.num_rows(), "Row index out of bounds");
417    if self.num_rows() > 0 {
418      assert_eq!(
419        self.num_cols(),
420        row.dimension(),
421        "New row length must match existing number of columns"
422      );
423    }
424    self.components.components_mut()[index] = row;
425  }
426
427  /// Returns the component at the given row and column.
428  ///
429  /// # Arguments
430  ///
431  /// * `row` - The row index (0-based)
432  /// * `col` - The column index (0-based)
433  ///
434  /// # Returns
435  ///
436  /// A reference to the component at the specified position
437  ///
438  /// # Panics
439  ///
440  /// Panics if either the row or column index is out of bounds.
441  pub fn get_component(&self, row: usize, col: usize) -> &F {
442    assert!(row < self.num_rows(), "Row index out of bounds");
443    assert!(col < self.num_cols(), "Column index out of bounds");
444    self.components.components()[row].get_component(col)
445  }
446
447  /// Sets the component at the given row and column to the given value.
448  ///
449  /// # Arguments
450  ///
451  /// * `row` - The row index (0-based)
452  /// * `col` - The column index (0-based)
453  /// * `value` - The value to set at the specified position
454  ///
455  /// # Panics
456  ///
457  /// Panics if either the row or column index is out of bounds.
458  pub fn set_component(&mut self, row: usize, col: usize, value: F) {
459    assert!(row < self.num_rows(), "Row index out of bounds");
460    assert!(col < self.num_cols(), "Column index out of bounds");
461    self.components.components_mut()[row].set_component(col, value);
462  }
463
464  /// Converts this row-major matrix to a column-major matrix by transposing it.
465  ///
466  /// The transpose of a matrix $A$ is denoted $A^T$ and has entries $A^T_{ij} = A_{ji}$.
467  ///
468  /// # Returns
469  ///
470  /// A new column-major matrix that is the transpose of this matrix
471  pub fn transpose(self) -> DynamicDenseMatrix<F, ColumnMajor> {
472    DynamicDenseMatrix { components: self.components, orientation: PhantomData }
473  }
474
475  /// Transforms this matrix into row echelon form using Gaussian elimination.
476  ///
477  /// Row echelon form has the following properties:
478  /// - All rows consisting entirely of zeros are at the bottom
479  /// - The leading coefficient (pivot) of each non-zero row is to the right of the pivot in the row
480  ///   above
481  /// - All entries in a column below a pivot are zeros
482  ///
483  /// This method performs in-place transformation of the matrix.
484  ///
485  /// # Returns
486  ///
487  /// A `RowEchelonOutput` containing the rank of the matrix and the positions of all pivots
488  ///
489  /// # Examples
490  ///
491  /// ```
492  /// use harness_algebra::tensors::dynamic::{
493  ///   matrix::{DynamicDenseMatrix, RowMajor},
494  ///   vector::DynamicVector,
495  /// };
496  ///
497  /// let mut matrix = DynamicDenseMatrix::<f64, RowMajor>::new();
498  /// // Add some rows
499  /// matrix.append_row(DynamicVector::from([1.0, 2.0, 3.0]));
500  /// matrix.append_row(DynamicVector::from([4.0, 5.0, 6.0]));
501  /// matrix.append_row(DynamicVector::from([7.0, 8.0, 9.0]));
502  ///
503  /// // Transform to row echelon form
504  /// let result = matrix.row_echelon_form();
505  ///
506  /// // The result contains the rank and pivot positions
507  /// assert_eq!(result.rank, 2); // The matrix has rank 2
508  /// ```
509  pub fn row_echelon_form(&mut self) -> RowEchelonOutput {
510    let matrix = self.components.components_mut();
511    if matrix.is_empty() || matrix[0].dimension() == 0 {
512      return RowEchelonOutput { rank: 0, pivots: Vec::new() };
513    }
514    let rows = matrix.len();
515    let cols = matrix[0].dimension();
516    let mut lead = 0; // current pivot column
517    let mut rank = 0;
518    let mut pivots = Vec::new();
519
520    for r in 0..rows {
521      if lead >= cols {
522        break;
523      }
524      let mut i = r;
525      while matrix[i].get_component(lead).is_zero() {
526        i += 1;
527        if i == rows {
528          i = r;
529          lead += 1;
530          if lead == cols {
531            return RowEchelonOutput { rank, pivots };
532          }
533        }
534      }
535      matrix.swap(i, r);
536
537      pivots.push(PivotInfo { row: r, col: lead });
538
539      let pivot_val = *matrix[r].get_component(lead);
540      let inv_pivot = pivot_val.multiplicative_inverse();
541
542      for j in lead..cols {
543        let val = *matrix[r].get_component(j);
544        matrix[r].set_component(j, val * inv_pivot);
545      }
546
547      for i_row in 0..rows {
548        if i_row != r {
549          let factor = *matrix[i_row].get_component(lead);
550          if !factor.is_zero() {
551            for j_col in lead..cols {
552              let val_r_j_col = *matrix[r].get_component(j_col);
553              let term = factor * val_r_j_col;
554              let val_i_row_j_col = *matrix[i_row].get_component(j_col); // Read from current row (i_row)
555              matrix[i_row].set_component(j_col, val_i_row_j_col - term);
556            }
557          }
558        }
559      }
560      lead += 1;
561      rank += 1;
562    }
563    RowEchelonOutput { rank, pivots }
564  }
565
566  /// Computes a basis for the image (column space) of the matrix.
567  /// The image is the span of the columns of the matrix.
568  /// This method finds the pivot columns by transforming a copy of the matrix to RREF.
569  /// The corresponding columns from the *original* matrix form the basis.
570  /// This method does not modify `self`.
571  pub fn image(&self) -> Vec<DynamicVector<F>> {
572    if self.num_rows() == 0 || self.num_cols() == 0 {
573      return Vec::new();
574    }
575
576    let mut rref_candidate = self.clone();
577    let echelon_output = rref_candidate.row_echelon_form(); // Modifies rref_candidate
578
579    let mut pivot_col_indices: Vec<usize> = echelon_output.pivots.iter().map(|p| p.col).collect();
580    pivot_col_indices.sort_unstable();
581    pivot_col_indices.dedup();
582
583    let mut image_basis: Vec<DynamicVector<F>> = Vec::new();
584    for &col_idx in &pivot_col_indices {
585      image_basis.push(self.get_column(col_idx)); // get_column for RowMajor returns owned
586                                                  // DynamicVector
587    }
588    image_basis
589  }
590
591  /// Computes a basis for the kernel (null space) of the matrix.
592  /// The kernel is the set of all vectors x such that Ax = 0.
593  /// This method returns a vector of `DynamicVector<F>` representing the basis vectors for the
594  /// kernel. An empty vector is returned if the kernel is the zero space (e.g., for an invertible
595  /// matrix, or if num_cols is 0). This method does not modify `self`.
596  pub fn kernel(&self) -> Vec<DynamicVector<F>> {
597    if self.num_cols() == 0 {
598      return Vec::new();
599    }
600
601    if self.num_rows() == 0 {
602      let mut basis: Vec<DynamicVector<F>> = Vec::with_capacity(self.num_cols());
603      for i in 0..self.num_cols() {
604        let mut v_data = vec![F::zero(); self.num_cols()];
605        if i < v_data.len() {
606          v_data[i] = F::one();
607        }
608        basis.push(DynamicVector::new(v_data));
609      }
610      return basis;
611    }
612
613    let mut rref_matrix = self.clone();
614    let echelon_output = rref_matrix.row_echelon_form();
615
616    let num_cols = rref_matrix.num_cols();
617    let num_rows_of_rref = rref_matrix.num_rows();
618
619    let mut is_pivot_col = vec![false; num_cols];
620    for pivot_info in &echelon_output.pivots {
621      if pivot_info.col < num_cols {
622        is_pivot_col[pivot_info.col] = true;
623      }
624    }
625
626    let mut free_col_indices: Vec<usize> = Vec::new();
627    (0..num_cols).for_each(|j| {
628      if !is_pivot_col[j] {
629        free_col_indices.push(j);
630      }
631    });
632
633    let mut kernel_basis: Vec<DynamicVector<F>> = Vec::new();
634
635    for &free_idx in &free_col_indices {
636      let mut basis_vector_comps = vec![F::zero(); num_cols];
637      if free_idx < num_cols {
638        basis_vector_comps[free_idx] = F::one();
639      }
640
641      for pivot_info in &echelon_output.pivots {
642        let pivot_col = pivot_info.col;
643        let pivot_row = pivot_info.row;
644
645        if pivot_col < num_cols && free_idx < num_cols && pivot_row < num_rows_of_rref {
646          let coefficient = *rref_matrix.get_component(pivot_row, free_idx);
647          basis_vector_comps[pivot_col] = -coefficient;
648        }
649      }
650      kernel_basis.push(DynamicVector::new(basis_vector_comps));
651    }
652    kernel_basis
653  }
654}
655
656impl<F: Field + Copy> DynamicDenseMatrix<F, ColumnMajor> {
657  /// Creates a new all zeros `DynamicDenseMatrix` with the specified number of rows and columns.
658  ///
659  /// # Arguments
660  ///
661  /// * `rows` - The number of rows in the matrix
662  /// * `cols` - The number of columns in the matrix
663  ///
664  /// # Returns
665  ///
666  /// A new `DynamicDenseMatrix` with the specified number of rows and columns, all initialized to
667  /// zero.
668  pub fn zeros(rows: usize, cols: usize) -> Self {
669    let mut mat = Self::new();
670    for _ in 0..cols {
671      mat.append_column(DynamicVector::zeros(rows));
672    }
673    mat
674  }
675
676  /// Returns the number of rows in the matrix.
677  ///
678  /// For a column-major matrix, this is the dimension of the first column vector (if any).
679  ///
680  /// # Returns
681  ///
682  /// The number of rows in the matrix
683  pub fn num_rows(&self) -> usize {
684    if self.components.dimension() == 0 {
685      0
686    } else {
687      self.components.components()[0].dimension()
688    }
689  }
690
691  /// Returns the number of columns in the matrix.
692  ///
693  /// For a column-major matrix, this is the number of column vectors stored.
694  ///
695  /// # Returns
696  ///
697  /// The number of columns in the matrix
698  pub const fn num_cols(&self) -> usize { self.components.dimension() }
699
700  /// Appends a new column to the matrix.
701  ///
702  /// For a column-major matrix, this is an efficient operation since columns are stored directly.
703  ///
704  /// # Arguments
705  ///
706  /// * `column` - The column vector to append
707  ///
708  /// # Panics
709  ///
710  /// Panics if the column's length doesn't match the number of existing rows (when the matrix is
711  /// not empty).
712  ///
713  /// # Examples
714  ///
715  /// ```
716  /// use harness_algebra::tensors::dynamic::{
717  ///   matrix::{ColumnMajor, DynamicDenseMatrix},
718  ///   vector::DynamicVector,
719  /// };
720  ///
721  /// let mut matrix = DynamicDenseMatrix::<f64, ColumnMajor>::new();
722  ///
723  /// // Add columns to the matrix
724  /// let col1 = DynamicVector::from([1.0, 2.0, 3.0]);
725  /// let col2 = DynamicVector::from([4.0, 5.0, 6.0]);
726  /// matrix.append_column(col1);
727  /// matrix.append_column(col2);
728  /// ```
729  pub fn append_column(&mut self, column: DynamicVector<F>) {
730    if self.num_cols() > 0 {
731      assert_eq!(
732        self.num_rows(),
733        column.dimension(),
734        "New column length must match existing number of rows"
735      );
736    }
737    self.components.components_mut().push(column); // Add the new column vector
738  }
739
740  /// Returns a reference to the column at the given index.
741  ///
742  /// For a column-major matrix, this is an efficient operation since columns are stored directly.
743  ///
744  /// # Arguments
745  ///
746  /// * `index` - The index of the column to retrieve (0-based)
747  ///
748  /// # Returns
749  ///
750  /// A reference to the column vector at the specified index
751  ///
752  /// # Panics
753  ///
754  /// Panics if the column index is out of bounds.
755  pub fn get_column(&self, index: usize) -> &DynamicVector<F> {
756    assert!(index < self.num_cols(), "Column index out of bounds");
757    &self.components.components()[index]
758  }
759
760  /// Sets the column at the given index with the provided DynamicVector.
761  ///
762  /// For a column-major matrix, this is an efficient operation since columns are stored directly.
763  ///
764  /// # Arguments
765  ///
766  /// * `index` - The index of the column to set (0-based)
767  /// * `column` - The new column vector
768  ///
769  /// # Panics
770  ///
771  /// - Panics if the column index is out of bounds
772  /// - Panics if the column's length doesn't match the number of rows
773  pub fn set_column(&mut self, index: usize, column: DynamicVector<F>) {
774    assert!(index < self.num_cols(), "Column index out of bounds");
775    if self.num_cols() > 0 {
776      assert_eq!(
777        self.num_rows(),
778        column.dimension(),
779        "New column length must match existing number of rows"
780      );
781    }
782    self.components.components_mut()[index] = column;
783  }
784
785  /// Appends a new row to the matrix.
786  ///
787  /// # Arguments
788  ///
789  /// * `row` - The row vector to append
790  ///
791  /// # Panics
792  ///
793  /// Panics if the row's length doesn't match the number of existing columns.
794  ///
795  /// # Warning
796  ///
797  /// For a column-major matrix, this is a more expensive operation as it requires
798  /// updating every column vector. If you need to add many rows, consider using
799  /// a row-major matrix or building the matrix from columns instead.
800  pub fn append_row(&mut self, row: &DynamicVector<F>) {
801    let num_c = self.num_cols();
802    if num_c == 0 {
803      if row.dimension() == 0 {
804        return;
805      }
806      for i in 0..row.dimension() {
807        self.components.components_mut().push(DynamicVector::new(vec![*row.get_component(i)]));
808      }
809    } else {
810      assert_eq!(num_c, row.dimension(), "Row length must match the number of columns");
811      for i in 0..num_c {
812        self.components.components_mut()[i].append(*row.get_component(i));
813      }
814    }
815  }
816
817  /// Returns a new DynamicVector representing the row at the given index.
818  ///
819  /// # Arguments
820  ///
821  /// * `index` - The index of the row to retrieve (0-based)
822  ///
823  /// # Returns
824  ///
825  /// A new DynamicVector containing the elements of the specified row
826  ///
827  /// # Panics
828  ///
829  /// Panics if the row index is out of bounds.
830  ///
831  /// # Warning
832  ///
833  /// For a column-major matrix, this is a more expensive operation as it requires
834  /// reading from each column vector. If you need to access rows frequently,
835  /// consider using a row-major matrix instead.
836  pub fn get_row(&self, index: usize) -> DynamicVector<F> {
837    let num_c = self.num_cols();
838    if num_c == 0 {
839      return DynamicVector::new(Vec::new());
840    }
841    assert!(index < self.num_rows(), "Row index out of bounds");
842    let mut row_components = Vec::with_capacity(num_c);
843    for i in 0..num_c {
844      row_components.push(*self.components.components()[i].get_component(index));
845    }
846    DynamicVector::new(row_components)
847  }
848
849  /// Sets the row at the given index with the provided DynamicVector.
850  ///
851  /// # Arguments
852  ///
853  /// * `index` - The index of the row to set (0-based)
854  /// * `row` - The new row vector
855  ///
856  /// # Panics
857  ///
858  /// - Panics if the row index is out of bounds
859  /// - Panics if the row's length doesn't match the number of columns
860  ///
861  /// # Warning
862  ///
863  /// For a column-major matrix, this is a more expensive operation as it requires
864  /// updating every column vector. If you need to modify rows frequently,
865  /// consider using a row-major matrix instead.
866  pub fn set_row(&mut self, index: usize, row: &DynamicVector<F>) {
867    let num_c = self.num_cols();
868    assert_eq!(num_c, row.dimension(), "New row length must match the number of columns");
869    if num_c == 0 {
870      return; // If no columns, setting an empty row to an empty matrix is fine.
871    }
872    assert!(index < self.num_rows(), "Row index out of bounds");
873
874    for i in 0..num_c {
875      self.components.components_mut()[i].set_component(index, *row.get_component(i));
876    }
877  }
878
879  /// Returns the component at the given row and column.
880  ///
881  /// # Arguments
882  ///
883  /// * `row` - The row index (0-based)
884  /// * `col` - The column index (0-based)
885  ///
886  /// # Returns
887  ///
888  /// A reference to the component at the specified position
889  ///
890  /// # Panics
891  ///
892  /// Panics if either the row or column index is out of bounds.
893  pub fn get_component(&self, row: usize, col: usize) -> &F {
894    assert!(col < self.num_cols(), "Column index out of bounds");
895    assert!(row < self.num_rows(), "Row index out of bounds");
896    self.components.components()[col].get_component(row)
897  }
898
899  /// Sets the component at the given row and column to the given value.
900  ///
901  /// # Arguments
902  ///
903  /// * `row` - The row index (0-based)
904  /// * `col` - The column index (0-based)
905  /// * `value` - The value to set at the specified position
906  ///
907  /// # Panics
908  ///
909  /// Panics if either the row or column index is out of bounds.
910  pub fn set_component(&mut self, row: usize, col: usize, value: F) {
911    assert!(col < self.num_cols(), "Column index out of bounds");
912    assert!(row < self.num_rows(), "Row index out of bounds");
913    self.components.components_mut()[col].set_component(row, value);
914  }
915
916  /// Converts this column-major matrix to a row-major matrix by transposing it.
917  ///
918  /// The transpose of a matrix $A$ is denoted $A^T$ and has entries $A^T_{ij} = A_{ji}$.
919  ///
920  /// # Returns
921  ///
922  /// A new row-major matrix that is the transpose of this matrix
923  pub fn transpose(self) -> DynamicDenseMatrix<F, RowMajor> {
924    DynamicDenseMatrix { components: self.components, orientation: PhantomData }
925  }
926
927  /// Transforms this matrix into row echelon form using Gaussian elimination.
928  ///
929  /// Row echelon form has the following properties:
930  /// - All rows consisting entirely of zeros are at the bottom
931  /// - The leading coefficient (pivot) of each non-zero row is to the right of the pivot in the row
932  ///   above
933  /// - All entries in a column below a pivot are zeros
934  ///
935  /// This method performs in-place transformation of the matrix.
936  ///
937  /// # Returns
938  ///
939  /// A `RowEchelonOutput` containing the rank of the matrix and the positions of all pivots
940  ///
941  /// # Warning
942  ///
943  /// While the algorithm for row echelon form works for column-major matrices,
944  /// it may be less efficient than for row-major matrices since row operations
945  /// require accessing multiple column vectors.
946  pub fn row_echelon_form(&mut self) -> RowEchelonOutput {
947    let matrix = self.components.components_mut();
948    if matrix.is_empty() || matrix[0].dimension() == 0 {
949      return RowEchelonOutput { rank: 0, pivots: Vec::new() };
950    }
951    let num_actual_cols = matrix.len();
952    let num_actual_rows = matrix[0].dimension();
953
954    let mut pivot_row_idx = 0;
955    let mut rank = 0;
956    let mut pivots = Vec::new();
957
958    for pivot_col_idx in 0..num_actual_cols {
959      if pivot_row_idx >= num_actual_rows {
960        break;
961      }
962
963      let mut i_search_row = pivot_row_idx;
964      while i_search_row < num_actual_rows
965        && matrix[pivot_col_idx].get_component(i_search_row).is_zero()
966      {
967        i_search_row += 1;
968      }
969
970      if i_search_row < num_actual_rows {
971        // Found a pivot in this column
972        if i_search_row != pivot_row_idx {
973          // Swap rows to bring pivot to pivot_row_idx
974          (0..num_actual_cols).for_each(|k_col| {
975            // Iterate through all columns to swap elements
976            let temp = *matrix[k_col].get_component(i_search_row);
977            let val_at_pivot_row = *matrix[k_col].get_component(pivot_row_idx);
978            matrix[k_col].set_component(i_search_row, val_at_pivot_row);
979            matrix[k_col].set_component(pivot_row_idx, temp);
980          });
981        }
982
983        pivots.push(PivotInfo { row: pivot_row_idx, col: pivot_col_idx });
984
985        let pivot_val = *matrix[pivot_col_idx].get_component(pivot_row_idx);
986
987        if !pivot_val.is_zero() {
988          let inv_pivot_val = pivot_val.multiplicative_inverse();
989          (pivot_col_idx..num_actual_cols).for_each(|k_col| {
990            let current_val = *matrix[k_col].get_component(pivot_row_idx);
991            matrix[k_col].set_component(pivot_row_idx, current_val * inv_pivot_val);
992          });
993        }
994
995        for k_row in 0..num_actual_rows {
996          if k_row != pivot_row_idx {
997            let factor = *matrix[pivot_col_idx].get_component(k_row);
998            if !factor.is_zero() {
999              (pivot_col_idx..num_actual_cols).for_each(|j_col_elim| {
1000                let val_from_pivot_row_at_j_col = *matrix[j_col_elim].get_component(pivot_row_idx);
1001                let term_to_subtract = factor * val_from_pivot_row_at_j_col;
1002                let current_val_in_k_row_at_j_col = *matrix[j_col_elim].get_component(k_row);
1003                matrix[j_col_elim]
1004                  .set_component(k_row, current_val_in_k_row_at_j_col - term_to_subtract);
1005              });
1006            }
1007          }
1008        }
1009        pivot_row_idx += 1;
1010        rank += 1;
1011      }
1012    }
1013    RowEchelonOutput { rank, pivots }
1014  }
1015
1016  /// Computes a basis for the image (column space) of the matrix.
1017  /// The image is the span of the columns of the matrix.
1018  /// This method finds the pivot columns by transforming a copy of the matrix to RREF.
1019  /// The corresponding columns from the *original* matrix form the basis.
1020  /// This method does not modify `self`.
1021  pub fn image(&self) -> Vec<DynamicVector<F>> {
1022    if self.num_rows() == 0 || self.num_cols() == 0 {
1023      return Vec::new();
1024    }
1025
1026    let mut rref_candidate = self.clone();
1027    let echelon_output = rref_candidate.row_echelon_form(); // Modifies rref_candidate
1028
1029    let mut pivot_col_indices: Vec<usize> = echelon_output.pivots.iter().map(|p| p.col).collect();
1030    pivot_col_indices.sort_unstable();
1031    pivot_col_indices.dedup();
1032
1033    let mut image_basis: Vec<DynamicVector<F>> = Vec::new();
1034    for &col_idx in &pivot_col_indices {
1035      // get_column for ColumnMajor returns &DynamicVector, so clone is needed.
1036      image_basis.push(self.get_column(col_idx).clone());
1037    }
1038    image_basis
1039  }
1040
1041  /// Computes a basis for the kernel (null space) of the matrix.
1042  /// The kernel is the set of all vectors x such that Ax = 0.
1043  /// This method returns a vector of `DynamicVector<F>` representing the basis vectors for the
1044  /// kernel. An empty vector is returned if the kernel is the zero space (e.g., for an invertible
1045  /// matrix, or if num_cols is 0). This method does not modify `self`.
1046  pub fn kernel(&self) -> Vec<DynamicVector<F>> {
1047    if self.num_cols() == 0 {
1048      return Vec::new();
1049    }
1050
1051    if self.num_rows() == 0 {
1052      let mut basis: Vec<DynamicVector<F>> = Vec::with_capacity(self.num_cols());
1053      for i in 0..self.num_cols() {
1054        let mut v_data = vec![F::zero(); self.num_cols()];
1055        if i < v_data.len() {
1056          v_data[i] = F::one();
1057        }
1058        basis.push(DynamicVector::new(v_data));
1059      }
1060      return basis;
1061    }
1062
1063    let mut rref_matrix = self.clone();
1064    let echelon_output = rref_matrix.row_echelon_form();
1065
1066    let num_cols = rref_matrix.num_cols();
1067    let num_rows_of_rref = rref_matrix.num_rows();
1068
1069    let mut is_pivot_col = vec![false; num_cols];
1070    for pivot_info in &echelon_output.pivots {
1071      if pivot_info.col < num_cols {
1072        is_pivot_col[pivot_info.col] = true;
1073      }
1074    }
1075
1076    let mut free_col_indices: Vec<usize> = Vec::new();
1077    (0..num_cols).for_each(|j| {
1078      if !is_pivot_col[j] {
1079        free_col_indices.push(j);
1080      }
1081    });
1082
1083    let mut kernel_basis: Vec<DynamicVector<F>> = Vec::new();
1084
1085    for &free_idx in &free_col_indices {
1086      let mut basis_vector_comps = vec![F::zero(); num_cols];
1087      if free_idx < num_cols {
1088        basis_vector_comps[free_idx] = F::one();
1089      }
1090
1091      for pivot_info in &echelon_output.pivots {
1092        let pivot_col = pivot_info.col;
1093        let pivot_row = pivot_info.row;
1094
1095        if pivot_col < num_cols && free_idx < num_cols && pivot_row < num_rows_of_rref {
1096          let coefficient = *rref_matrix.get_component(pivot_row, free_idx);
1097          basis_vector_comps[pivot_col] = -coefficient;
1098        }
1099      }
1100      kernel_basis.push(DynamicVector::new(basis_vector_comps));
1101    }
1102    kernel_basis
1103  }
1104}
1105
1106impl<T: Field + Copy> Mul<DynamicVector<T>> for DynamicDenseMatrix<T, RowMajor> {
1107  type Output = DynamicVector<T>;
1108
1109  fn mul(self, rhs: DynamicVector<T>) -> Self::Output {
1110    assert_eq!(self.num_cols(), rhs.dimension(), "Matrix-vector dimension mismatch");
1111
1112    let mut result = vec![T::zero(); self.num_rows()];
1113    (0..self.num_rows()).for_each(|i| {
1114      for j in 0..self.num_cols() {
1115        result[i] += *self.get_component(i, j) * *rhs.get_component(j);
1116      }
1117    });
1118
1119    DynamicVector::new(result)
1120  }
1121}
1122
1123impl<T: Field + Copy> Mul<DynamicVector<T>> for DynamicDenseMatrix<T, ColumnMajor> {
1124  type Output = DynamicVector<T>;
1125
1126  fn mul(self, rhs: DynamicVector<T>) -> Self::Output {
1127    assert_eq!(self.num_cols(), rhs.dimension(), "Matrix-vector dimension mismatch");
1128
1129    let mut result = vec![T::zero(); self.num_rows()];
1130    (0..self.num_rows()).for_each(|i| {
1131      for j in 0..self.num_cols() {
1132        result[i] += *self.get_component(i, j) * *rhs.get_component(j);
1133      }
1134    });
1135
1136    DynamicVector::new(result)
1137  }
1138}
1139
1140impl<T: Field + Copy> Mul<Self> for DynamicDenseMatrix<T, RowMajor> {
1141  type Output = Self;
1142
1143  fn mul(self, rhs: Self) -> Self::Output {
1144    let mut result = Self::new();
1145    for i in 0..self.num_rows() {
1146      let mut new_row = DynamicVector::<T>::zeros(rhs.num_cols());
1147      for j in 0..rhs.num_cols() {
1148        let col = rhs.get_column(j);
1149        let mut sum = T::zero();
1150        for k in 0..self.num_cols() {
1151          sum += *self.get_component(i, k) * *col.get_component(k);
1152        }
1153        new_row.set_component(j, sum);
1154      }
1155      result.append_row(new_row);
1156    }
1157    result
1158  }
1159}
1160
1161impl<T: Field + Copy> Mul<Self> for DynamicDenseMatrix<T, ColumnMajor> {
1162  type Output = Self;
1163
1164  fn mul(self, rhs: Self) -> Self::Output {
1165    assert_eq!(
1166      self.num_cols(),
1167      rhs.num_rows(),
1168      "Matrix dimensions incompatible for multiplication"
1169    );
1170    let m = self.num_rows();
1171    let n = self.num_cols(); // common dimension, also rhs.num_rows()
1172    let p = rhs.num_cols();
1173
1174    let mut result_matrix = Self::new();
1175
1176    for j_res in 0..p {
1177      // For each column j_res of the result matrix C
1178      let mut new_col_components = Vec::with_capacity(m);
1179      for i_res in 0..m {
1180        // For each row i_res in that result column
1181        let mut sum = T::zero();
1182        for k in 0..n {
1183          // Summation index
1184          // C(i_res, j_res) = sum_k A(i_res, k) * B(k, j_res)
1185          // self is A (RowMajor), rhs is B (ColumnMajor)
1186          sum += *self.get_component(i_res, k) * *rhs.get_component(k, j_res);
1187        }
1188        new_col_components.push(sum);
1189      }
1190      result_matrix.append_column(DynamicVector::new(new_col_components));
1191    }
1192    result_matrix
1193  }
1194}
1195
1196impl<T: Field + Copy> Mul<DynamicDenseMatrix<T, RowMajor>> for DynamicDenseMatrix<T, ColumnMajor> {
1197  type Output = Self;
1198
1199  fn mul(self, rhs: DynamicDenseMatrix<T, RowMajor>) -> Self::Output {
1200    assert_eq!(
1201      self.num_cols(),
1202      rhs.num_rows(),
1203      "Matrix dimensions incompatible for multiplication"
1204    );
1205    let m = self.num_rows();
1206    let n = self.num_cols(); // common dimension, also rhs.num_rows()
1207    let p = rhs.num_cols();
1208
1209    let mut result_matrix = Self::new();
1210
1211    for j_res in 0..p {
1212      // For each column j_res of the result matrix C
1213      let mut new_col_components = Vec::with_capacity(m);
1214      for i_res in 0..m {
1215        // For each row i_res in that result column
1216        let mut sum = T::zero();
1217        for k in 0..n {
1218          // Summation index
1219          // C(i_res, j_res) = sum_k A(i_res, k) * B(k, j_res)
1220          // self is A (RowMajor), rhs is B (ColumnMajor)
1221          sum += *self.get_component(i_res, k) * *rhs.get_component(k, j_res);
1222        }
1223        new_col_components.push(sum);
1224      }
1225      result_matrix.append_column(DynamicVector::new(new_col_components));
1226    }
1227    result_matrix
1228  }
1229}
1230
1231impl<F: Field + Copy + fmt::Display> fmt::Display for DynamicDenseMatrix<F, RowMajor> {
1232  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1233    if self.num_rows() == 0 {
1234      return write!(f, "( )");
1235    }
1236
1237    // First pass: calculate column widths for alignment
1238    let mut col_widths = vec![0; self.num_cols()];
1239    for i in 0..self.num_rows() {
1240      #[allow(clippy::needless_range_loop)]
1241      for j in 0..self.num_cols() {
1242        let element_str = format!("{}", self.get_component(i, j));
1243        col_widths[j] = col_widths[j].max(element_str.len());
1244      }
1245    }
1246
1247    // Second pass: format with proper alignment and mathematical parentheses
1248    for i in 0..self.num_rows() {
1249      // Print the appropriate parenthesis for this row
1250      if self.num_rows() == 1 {
1251        write!(f, "( ")?; // Single row: simple parentheses
1252      } else if i == 0 {
1253        write!(f, "⎛ ")?; // Top of parenthesis
1254      } else if i == self.num_rows() - 1 {
1255        write!(f, "⎝ ")?; // Bottom of parenthesis
1256      } else {
1257        write!(f, "⎜ ")?; // Middle of parenthesis
1258      }
1259
1260      // Print row elements
1261      #[allow(clippy::needless_range_loop)]
1262      for j in 0..self.num_cols() {
1263        if j > 0 {
1264          write!(f, "  ")?; // Space between elements
1265        }
1266        write!(f, "{:>width$}", self.get_component(i, j), width = col_widths[j])?;
1267      }
1268
1269      // Print closing parenthesis
1270      if self.num_rows() == 1 {
1271        write!(f, " )")?; // Single row: simple parentheses
1272      } else if i == 0 {
1273        writeln!(f, " ⎞")?; // Top of parenthesis
1274      } else if i == self.num_rows() - 1 {
1275        write!(f, " ⎠")?; // Bottom of parenthesis
1276      } else {
1277        writeln!(f, " ⎟")?; // Middle of parenthesis
1278      }
1279    }
1280
1281    Ok(())
1282  }
1283}
1284
1285impl<F: Field + Copy + fmt::Display> fmt::Display for DynamicDenseMatrix<F, ColumnMajor> {
1286  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1287    if self.num_rows() == 0 {
1288      return write!(f, "( )");
1289    }
1290
1291    // First pass: calculate column widths for alignment
1292    let mut col_widths = vec![0; self.num_cols()];
1293    #[allow(clippy::needless_range_loop)]
1294    for i in 0..self.num_rows() {
1295      #[allow(clippy::needless_range_loop)]
1296      for j in 0..self.num_cols() {
1297        let element_str = format!("{}", self.get_component(i, j));
1298        col_widths[j] = col_widths[j].max(element_str.len());
1299      }
1300    }
1301
1302    // Second pass: format with proper alignment and mathematical parentheses
1303    for i in 0..self.num_rows() {
1304      // Print the appropriate parenthesis for this row
1305      if self.num_rows() == 1 {
1306        write!(f, "( ")?; // Single row: simple parentheses
1307      } else if i == 0 {
1308        write!(f, "⎛ ")?; // Top of parenthesis
1309      } else if i == self.num_rows() - 1 {
1310        write!(f, "⎝ ")?; // Bottom of parenthesis
1311      } else {
1312        write!(f, "⎜ ")?; // Middle of parenthesis
1313      }
1314
1315      // Print row elements
1316      #[allow(clippy::needless_range_loop)]
1317      for j in 0..self.num_cols() {
1318        if j > 0 {
1319          write!(f, "  ")?; // Space between elements
1320        }
1321        write!(f, "{:>width$}", self.get_component(i, j), width = col_widths[j])?;
1322      }
1323
1324      // Print closing parenthesis
1325      if self.num_rows() == 1 {
1326        write!(f, " )")?; // Single row: simple parentheses
1327      } else if i == 0 {
1328        writeln!(f, " ⎞")?; // Top of parenthesis
1329      } else if i == self.num_rows() - 1 {
1330        write!(f, " ⎠")?; // Bottom of parenthesis
1331      } else {
1332        writeln!(f, " ⎟")?; // Middle of parenthesis
1333      }
1334    }
1335
1336    Ok(())
1337  }
1338}
1339
1340#[cfg(test)]
1341mod tests {
1342  #![allow(non_snake_case)]
1343  use super::*;
1344  use crate::{algebras::boolean::Boolean, fixtures::Mod7};
1345
1346  // Test constructor and basic properties
1347  #[test]
1348  fn test_new_matrix_properties() {
1349    // RowMajor f64
1350    let m_rm_f64: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1351    assert_eq!(m_rm_f64.num_rows(), 0);
1352    assert_eq!(m_rm_f64.num_cols(), 0);
1353
1354    // ColumnMajor Boolean
1355    let m_cm_bool: DynamicDenseMatrix<Boolean, ColumnMajor> = DynamicDenseMatrix::new();
1356    assert_eq!(m_cm_bool.num_rows(), 0);
1357    assert_eq!(m_cm_bool.num_cols(), 0);
1358  }
1359
1360  // Test append_row, get_row, set_row for RowMajor
1361  #[test]
1362  fn test_row_operations_row_major_mod7() {
1363    let mut m: DynamicDenseMatrix<Mod7, RowMajor> = DynamicDenseMatrix::new();
1364    let r0_data = vec![Mod7::new(1), Mod7::new(2)];
1365    let r1_data = vec![Mod7::new(3), Mod7::new(4)];
1366    let r0 = DynamicVector::new(r0_data.clone());
1367    let r1 = DynamicVector::new(r1_data.clone());
1368
1369    m.append_row(r0);
1370    assert_eq!(m.num_rows(), 1);
1371    assert_eq!(m.num_cols(), 2);
1372    assert_eq!(m.get_row(0).components(), &r0_data);
1373
1374    m.append_row(r1);
1375    assert_eq!(m.num_rows(), 2);
1376    assert_eq!(m.num_cols(), 2);
1377    assert_eq!(m.get_row(1).components(), &r1_data);
1378
1379    let r_new_data = vec![Mod7::new(5), Mod7::new(6)];
1380    let r_new = DynamicVector::new(r_new_data.clone());
1381    m.set_row(0, r_new);
1382    assert_eq!(m.num_rows(), 2);
1383    assert_eq!(m.num_cols(), 2);
1384    assert_eq!(m.get_row(0).components(), &r_new_data);
1385    assert_eq!(m.get_row(1).components(), &r1_data);
1386  }
1387
1388  // Test append_column, get_column, set_column for RowMajor
1389  #[test]
1390  fn test_column_operations_row_major_f64() {
1391    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1392
1393    // Append first column to empty matrix
1394    let c0_data = vec![1.0, 2.0];
1395    let c0 = DynamicVector::new(c0_data.clone());
1396    m.append_column(&c0);
1397    assert_eq!(m.num_rows(), 2);
1398    assert_eq!(m.num_cols(), 1);
1399    assert_eq!(m.get_column(0).components(), &c0_data);
1400    assert_eq!(*m.get_component(0, 0), 1.0);
1401    assert_eq!(*m.get_component(1, 0), 2.0);
1402
1403    // Append second column
1404    let c1_data = vec![3.0, 4.0];
1405    let c1 = DynamicVector::new(c1_data.clone());
1406    m.append_column(&c1);
1407    assert_eq!(m.num_rows(), 2);
1408    assert_eq!(m.num_cols(), 2);
1409    assert_eq!(m.get_column(1).components(), &c1_data);
1410    assert_eq!(*m.get_component(0, 1), 3.0);
1411    assert_eq!(*m.get_component(1, 1), 4.0);
1412
1413    // Set a column
1414    let c_new_data = vec![5.0, 6.0];
1415    let c_new = DynamicVector::new(c_new_data.clone());
1416    m.set_column(0, &c_new);
1417    assert_eq!(m.num_rows(), 2);
1418    assert_eq!(m.num_cols(), 2);
1419    assert_eq!(m.get_column(0).components(), &c_new_data);
1420    assert_eq!(m.get_column(1).components(), &c1_data);
1421  }
1422
1423  // Test append_column, get_column, set_column for ColumnMajor
1424  #[test]
1425  fn test_column_operations_col_major_boolean() {
1426    let mut m: DynamicDenseMatrix<Boolean, ColumnMajor> = DynamicDenseMatrix::new();
1427    let c0_data = vec![Boolean(true), Boolean(false)];
1428    let c1_data = vec![Boolean(false), Boolean(true)];
1429    let c0 = DynamicVector::new(c0_data.clone());
1430    let c1 = DynamicVector::new(c1_data.clone());
1431
1432    m.append_column(c0.clone());
1433    assert_eq!(m.num_rows(), 2);
1434    assert_eq!(m.num_cols(), 1);
1435    assert_eq!(m.get_column(0).components(), &c0_data);
1436
1437    m.append_column(c1.clone());
1438    assert_eq!(m.num_rows(), 2);
1439    assert_eq!(m.num_cols(), 2);
1440    assert_eq!(m.get_column(1).components(), &c1_data);
1441
1442    let c_new_data = vec![Boolean(true), Boolean(true)];
1443    let c_new = DynamicVector::new(c_new_data.clone());
1444    m.set_column(0, c_new.clone());
1445    assert_eq!(m.num_rows(), 2);
1446    assert_eq!(m.num_cols(), 2);
1447    assert_eq!(m.get_column(0).components(), &c_new_data);
1448    assert_eq!(m.get_column(1).components(), &c1_data);
1449  }
1450
1451  // Test append_row, get_row, set_row for ColumnMajor
1452  #[test]
1453  fn test_row_operations_col_major_f64() {
1454    let mut m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1455
1456    // Append first row to empty matrix
1457    let r0_data = vec![1.0, 2.0];
1458    let r0 = DynamicVector::new(r0_data.clone());
1459    m.append_row(&r0);
1460    assert_eq!(m.num_rows(), 1);
1461    assert_eq!(m.num_cols(), 2);
1462    assert_eq!(m.get_row(0).components(), &r0_data);
1463    assert_eq!(*m.get_component(0, 0), 1.0);
1464    assert_eq!(*m.get_component(0, 1), 2.0);
1465
1466    // Append second row
1467    let r1_data = vec![3.0, 4.0];
1468    let r1 = DynamicVector::new(r1_data.clone());
1469    m.append_row(&r1);
1470    assert_eq!(m.num_rows(), 2);
1471    assert_eq!(m.num_cols(), 2);
1472    assert_eq!(m.get_row(1).components(), &r1_data);
1473    assert_eq!(*m.get_component(1, 0), 3.0);
1474    assert_eq!(*m.get_component(1, 1), 4.0);
1475
1476    // Set a row
1477    let r_new_data = vec![5.0, 6.0];
1478    let r_new = DynamicVector::new(r_new_data.clone());
1479    m.set_row(0, &r_new);
1480    assert_eq!(m.num_rows(), 2);
1481    assert_eq!(m.num_cols(), 2);
1482    assert_eq!(m.get_row(0).components(), &r_new_data);
1483    assert_eq!(m.get_row(1).components(), &r1_data);
1484  }
1485
1486  // Test get_component, set_component for RowMajor and ColumnMajor
1487  #[test]
1488  fn test_get_set_component() {
1489    let mut m_rm: DynamicDenseMatrix<Mod7, RowMajor> = DynamicDenseMatrix::new();
1490    m_rm.append_row(DynamicVector::new(vec![Mod7::new(1), Mod7::new(2)]));
1491    m_rm.append_row(DynamicVector::new(vec![Mod7::new(3), Mod7::new(4)]));
1492    assert_eq!(*m_rm.get_component(0, 1), Mod7::new(2));
1493    m_rm.set_component(0, 1, Mod7::new(5));
1494    assert_eq!(*m_rm.get_component(0, 1), Mod7::new(5));
1495
1496    let mut m_cm: DynamicDenseMatrix<Mod7, ColumnMajor> = DynamicDenseMatrix::new();
1497    m_cm.append_column(DynamicVector::new(vec![Mod7::new(1), Mod7::new(2)]));
1498    m_cm.append_column(DynamicVector::new(vec![Mod7::new(3), Mod7::new(4)]));
1499    assert_eq!(*m_cm.get_component(1, 0), Mod7::new(2));
1500    m_cm.set_component(1, 0, Mod7::new(6));
1501    assert_eq!(*m_cm.get_component(1, 0), Mod7::new(6));
1502  }
1503
1504  // Test transpose
1505  #[test]
1506  fn test_transpose() {
1507    let mut m_rm: DynamicDenseMatrix<Mod7, RowMajor> = DynamicDenseMatrix::new();
1508    m_rm.append_row(DynamicVector::new(vec![Mod7::new(1), Mod7::new(2), Mod7::new(3)]));
1509    m_rm.append_row(DynamicVector::new(vec![Mod7::new(4), Mod7::new(5), Mod7::new(6)]));
1510    assert_eq!(m_rm.num_rows(), 2);
1511    assert_eq!(m_rm.num_cols(), 3);
1512
1513    let m_cm: DynamicDenseMatrix<Mod7, ColumnMajor> = m_rm.transpose();
1514    assert_eq!(m_cm.num_rows(), 3);
1515    assert_eq!(m_cm.num_cols(), 2);
1516
1517    assert_eq!(*m_cm.get_component(0, 0), Mod7::new(1));
1518    assert_eq!(*m_cm.get_component(1, 0), Mod7::new(2));
1519    assert_eq!(*m_cm.get_component(2, 0), Mod7::new(3));
1520    assert_eq!(*m_cm.get_component(0, 1), Mod7::new(4));
1521    assert_eq!(*m_cm.get_component(1, 1), Mod7::new(5));
1522    assert_eq!(*m_cm.get_component(2, 1), Mod7::new(6));
1523
1524    // Transpose back
1525    let m_rm_again: DynamicDenseMatrix<Mod7, RowMajor> = m_cm.transpose();
1526    assert_eq!(m_rm_again.num_rows(), 2);
1527    assert_eq!(m_rm_again.num_cols(), 3);
1528    assert_eq!(*m_rm_again.get_component(0, 0), Mod7::new(1));
1529    assert_eq!(*m_rm_again.get_component(0, 1), Mod7::new(2));
1530    assert_eq!(*m_rm_again.get_component(0, 2), Mod7::new(3));
1531    assert_eq!(*m_rm_again.get_component(1, 0), Mod7::new(4));
1532    assert_eq!(*m_rm_again.get_component(1, 1), Mod7::new(5));
1533    assert_eq!(*m_rm_again.get_component(1, 2), Mod7::new(6));
1534  }
1535
1536  #[test]
1537  #[should_panic]
1538  fn test_append_row_mismatch_cols_row_major() {
1539    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1540    m.append_row(DynamicVector::new(vec![1.0, 2.0]));
1541    m.append_row(DynamicVector::new(vec![3.0])); // Should panic
1542  }
1543
1544  #[test]
1545  #[should_panic]
1546  fn test_append_column_mismatch_rows_row_major() {
1547    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1548    m.append_column(&DynamicVector::new(vec![1.0, 2.0]));
1549    m.append_column(&DynamicVector::new(vec![3.0])); // Should panic
1550  }
1551
1552  #[test]
1553  #[should_panic]
1554  fn test_set_row_mismatch_cols_row_major() {
1555    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1556    m.append_row(DynamicVector::new(vec![1.0, 2.0]));
1557    m.set_row(0, DynamicVector::new(vec![3.0])); // Should panic
1558  }
1559
1560  #[test]
1561  #[should_panic]
1562  fn test_set_column_mismatch_rows_row_major() {
1563    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1564    m.append_column(&DynamicVector::new(vec![1.0]));
1565    m.set_column(0, &DynamicVector::new(vec![3.0, 4.0, 5.0])); // Should panic
1566  }
1567
1568  #[test]
1569  #[should_panic]
1570  fn test_append_column_mismatch_rows_col_major() {
1571    let mut m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1572    m.append_column(DynamicVector::new(vec![1.0, 2.0]));
1573    m.append_column(DynamicVector::new(vec![3.0])); // Should panic
1574  }
1575
1576  #[test]
1577  #[should_panic]
1578  fn test_append_row_mismatch_cols_col_major() {
1579    let mut m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1580    m.append_row(&DynamicVector::new(vec![1.0, 2.0]));
1581    m.append_row(&DynamicVector::new(vec![3.0])); // Should panic
1582  }
1583
1584  #[test]
1585  fn test_row_echelon_form_row_major() {
1586    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1587    m.append_row(DynamicVector::new(vec![1.0, 2.0, 3.0]));
1588    m.append_row(DynamicVector::new(vec![4.0, 5.0, 6.0]));
1589    m.append_row(DynamicVector::new(vec![7.0, 8.0, 9.0]));
1590    let result = m.row_echelon_form();
1591    assert_eq!(result.rank, 2);
1592    assert_eq!(result.pivots, vec![PivotInfo { row: 0, col: 0 }, PivotInfo { row: 1, col: 1 }]);
1593
1594    assert_eq!(m.num_rows(), 3);
1595    assert_eq!(m.num_cols(), 3);
1596
1597    assert_eq!(*m.get_component(0, 0), 1.0);
1598    assert_eq!(*m.get_component(0, 1), 0.0);
1599    assert_eq!(*m.get_component(0, 2), -1.0);
1600    assert_eq!(*m.get_component(1, 0), 0.0);
1601    assert_eq!(*m.get_component(1, 1), 1.0);
1602    assert_eq!(*m.get_component(1, 2), 2.0);
1603    assert_eq!(*m.get_component(2, 0), 0.0);
1604    assert_eq!(*m.get_component(2, 1), 0.0);
1605    assert_eq!(*m.get_component(2, 2), 0.0);
1606  }
1607
1608  #[test]
1609  fn test_row_echelon_form_col_major() {
1610    let mut m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1611    m.append_column(DynamicVector::new(vec![1.0, 2.0, 3.0]));
1612    m.append_column(DynamicVector::new(vec![4.0, 5.0, 6.0]));
1613    m.append_column(DynamicVector::new(vec![7.0, 8.0, 9.0]));
1614    let result = m.row_echelon_form();
1615    assert_eq!(result.rank, 2);
1616    assert_eq!(result.pivots, vec![PivotInfo { row: 0, col: 0 }, PivotInfo { row: 1, col: 1 }]);
1617
1618    assert_eq!(m.num_rows(), 3);
1619    assert_eq!(m.num_cols(), 3);
1620
1621    assert_eq!(*m.get_component(0, 0), 1.0);
1622    assert_eq!(*m.get_component(0, 1), 0.0);
1623    assert_eq!(*m.get_component(0, 2), -1.0);
1624    assert_eq!(*m.get_component(1, 0), 0.0);
1625    assert_eq!(*m.get_component(1, 1), 1.0);
1626    assert_eq!(*m.get_component(1, 2), 2.0);
1627    assert_eq!(*m.get_component(2, 0), 0.0);
1628    assert_eq!(*m.get_component(2, 1), 0.0);
1629    assert_eq!(*m.get_component(2, 2), 0.0);
1630  }
1631
1632  // Helper function to check if a vector is in a list of vectors (basis)
1633  // This is a simple check, assumes vectors in basis are unique and non-zero for simplicity.
1634  // For more robust checks, one might need to check for linear independence and spanning.
1635  fn contains_vector<F: Field + Copy + PartialEq>(
1636    basis: &[DynamicVector<F>],
1637    vector: &DynamicVector<F>,
1638  ) -> bool {
1639    basis.iter().any(|v| v == vector)
1640  }
1641
1642  #[test]
1643  fn test_image_kernel_row_major_simple() {
1644    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1645    // A = [[1, 0, -1],
1646    //      [0, 1,  2]]
1647    m.append_row(DynamicVector::from(vec![1.0, 0.0, -1.0]));
1648    m.append_row(DynamicVector::from(vec![0.0, 1.0, 2.0]));
1649
1650    let image = m.image();
1651    // Pivots are in col 0 and col 1. Image is span of original col 0 and col 1.
1652    let expected_image_basis = [
1653      DynamicVector::from(vec![1.0, 0.0]), // Original col 0
1654      DynamicVector::from(vec![0.0, 1.0]), // Original col 1
1655    ];
1656    assert_eq!(image.len(), 2);
1657    assert!(contains_vector(&image, &expected_image_basis[0]));
1658    assert!(contains_vector(&image, &expected_image_basis[1]));
1659
1660    let kernel = m.kernel();
1661    // RREF is [[1,0,-1],[0,1,2]]. x1 - x3 = 0, x2 + 2x3 = 0.
1662    // x3 is free. x1 = x3, x2 = -2x3. Vector: [1, -2, 1]^T * x3
1663    let expected_kernel_basis = [DynamicVector::from(vec![1.0, -2.0, 1.0])];
1664    assert_eq!(kernel.len(), 1);
1665    assert!(contains_vector(&kernel, &expected_kernel_basis[0]));
1666
1667    // Check Ax = 0 for kernel vectors
1668    for k_vec in &kernel {
1669      let mut Ax_components = vec![0.0; m.num_rows()];
1670      (0..m.num_rows()).for_each(|r| {
1671        let mut sum = 0.0;
1672        for c in 0..m.num_cols() {
1673          sum += m.get_component(r, c) * k_vec.get_component(c);
1674        }
1675        Ax_components[r] = sum;
1676      });
1677      let Ax = DynamicVector::new(Ax_components);
1678      let zero_vec = DynamicVector::new(vec![0.0; m.num_rows()]);
1679      assert_eq!(Ax, zero_vec, "Kernel vector validation failed: Ax != 0");
1680    }
1681  }
1682
1683  #[test]
1684  fn test_image_kernel_col_major_simple() {
1685    let mut m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1686    // A = [[1, 0],
1687    //      [0, 1],
1688    //      [-1, 2]]
1689    // This is the transpose of the RowMajor test for easier comparison logic.
1690    m.append_column(DynamicVector::from(vec![1.0, 0.0, -1.0]));
1691    m.append_column(DynamicVector::from(vec![0.0, 1.0, 2.0]));
1692
1693    // For A (3x2), RREF would be [[1,0],[0,1],[0,0]]
1694    // Image basis: col 0, col 1 of original matrix
1695    let image = m.image();
1696    let expected_image_basis =
1697      [DynamicVector::from(vec![1.0, 0.0, -1.0]), DynamicVector::from(vec![0.0, 1.0, 2.0])];
1698    assert_eq!(image.len(), 2);
1699    assert!(contains_vector(&image, &expected_image_basis[0]));
1700    assert!(contains_vector(&image, &expected_image_basis[1]));
1701
1702    // Kernel for this 3x2 matrix (rank 2) should be trivial (only zero vector)
1703    let kernel = m.kernel();
1704    assert_eq!(kernel.len(), 0, "Kernel should be trivial for this matrix");
1705  }
1706
1707  #[test]
1708  fn test_image_kernel_identity_row_major() {
1709    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1710    m.append_row(DynamicVector::from(vec![1.0, 0.0]));
1711    m.append_row(DynamicVector::from(vec![0.0, 1.0]));
1712
1713    let image = m.image();
1714    let expected_image_basis =
1715      [DynamicVector::from(vec![1.0, 0.0]), DynamicVector::from(vec![0.0, 1.0])];
1716    assert_eq!(image.len(), 2);
1717    assert!(contains_vector(&image, &expected_image_basis[0]));
1718    assert!(contains_vector(&image, &expected_image_basis[1]));
1719
1720    let kernel = m.kernel();
1721    assert_eq!(kernel.len(), 0, "Kernel of identity matrix should be trivial");
1722  }
1723
1724  #[test]
1725  fn test_image_kernel_zero_matrix_row_major() {
1726    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1727    m.append_row(DynamicVector::from(vec![0.0, 0.0]));
1728    m.append_row(DynamicVector::from(vec![0.0, 0.0]));
1729
1730    let image = m.image();
1731    assert_eq!(image.len(), 0, "Image of zero matrix should be trivial");
1732
1733    let kernel = m.kernel();
1734    // Kernel of 2x2 zero matrix is R^2, basis e.g., [[1,0],[0,1]]
1735    let expected_kernel_basis =
1736      [DynamicVector::from(vec![1.0, 0.0]), DynamicVector::from(vec![0.0, 1.0])];
1737    assert_eq!(kernel.len(), 2);
1738    // Order might differ, so check containment
1739    assert!(contains_vector(&kernel, &expected_kernel_basis[0]));
1740    assert!(contains_vector(&kernel, &expected_kernel_basis[1]));
1741  }
1742
1743  #[test]
1744  fn test_image_kernel_dependent_cols_row_major() {
1745    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1746    // A = [[1, 2, 3],
1747    //      [2, 4, 6]]
1748    // col2 = 2*col1, col3 = 3*col1. Rank = 1.
1749    m.append_row(DynamicVector::from(vec![1.0, 2.0, 3.0]));
1750    m.append_row(DynamicVector::from(vec![2.0, 4.0, 6.0]));
1751
1752    let image = m.image();
1753    // RREF will have pivot in first col. Image is span of original first col.
1754    let expected_image_basis = [DynamicVector::from(vec![1.0, 2.0])];
1755    assert_eq!(image.len(), 1);
1756    assert!(contains_vector(&image, &expected_image_basis[0]));
1757
1758    let kernel = m.kernel();
1759    // RREF: [[1, 2, 3], [0, 0, 0]]
1760    // x1 + 2x2 + 3x3 = 0. x2, x3 are free.
1761    // Basis vector 1 (x2=1, x3=0): [-2, 1, 0]^T
1762    // Basis vector 2 (x2=0, x3=1): [-3, 0, 1]^T
1763    let expected_kernel_vector1 = DynamicVector::from(vec![-2.0, 1.0, 0.0]);
1764    let expected_kernel_vector2 = DynamicVector::from(vec![-3.0, 0.0, 1.0]);
1765    assert_eq!(kernel.len(), 2);
1766    assert!(
1767      contains_vector(&kernel, &expected_kernel_vector1)
1768        || contains_vector(&kernel, &DynamicVector::from(vec![2.0, -1.0, 0.0]))
1769    );
1770    assert!(
1771      contains_vector(&kernel, &expected_kernel_vector2)
1772        || contains_vector(&kernel, &DynamicVector::from(vec![3.0, 0.0, -1.0]))
1773    );
1774
1775    for k_vec in &kernel {
1776      let mut Ax_components = vec![0.0; m.num_rows()];
1777      (0..m.num_rows()).for_each(|r| {
1778        let mut sum = 0.0;
1779        for c in 0..m.num_cols() {
1780          sum += m.get_component(r, c) * k_vec.get_component(c);
1781        }
1782        Ax_components[r] = sum;
1783      });
1784      let Ax = DynamicVector::new(Ax_components);
1785      let zero_vec = DynamicVector::new(vec![0.0; m.num_rows()]);
1786      assert_eq!(Ax, zero_vec, "Kernel vector validation failed: Ax != 0");
1787    }
1788  }
1789
1790  #[test]
1791  fn test_image_kernel_col_major_identity() {
1792    let mut m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1793    m.append_column(DynamicVector::from(vec![1.0, 0.0]));
1794    m.append_column(DynamicVector::from(vec![0.0, 1.0]));
1795
1796    let image = m.image();
1797    let expected_image_basis =
1798      [DynamicVector::from(vec![1.0, 0.0]), DynamicVector::from(vec![0.0, 1.0])];
1799    assert_eq!(image.len(), 2);
1800    assert!(contains_vector(&image, &expected_image_basis[0]));
1801    assert!(contains_vector(&image, &expected_image_basis[1]));
1802
1803    let kernel = m.kernel();
1804    assert_eq!(kernel.len(), 0, "Kernel of identity matrix should be trivial");
1805  }
1806
1807  #[test]
1808  fn test_image_kernel_col_major_zero_matrix() {
1809    let mut m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1810    m.append_column(DynamicVector::from(vec![0.0, 0.0]));
1811    m.append_column(DynamicVector::from(vec![0.0, 0.0])); // 2x2 zero matrix
1812
1813    let image = m.image();
1814    assert_eq!(image.len(), 0, "Image of zero matrix should be trivial");
1815
1816    let kernel = m.kernel();
1817    let expected_kernel_basis =
1818      [DynamicVector::from(vec![1.0, 0.0]), DynamicVector::from(vec![0.0, 1.0])];
1819    assert_eq!(kernel.len(), 2);
1820    assert!(contains_vector(&kernel, &expected_kernel_basis[0]));
1821    assert!(contains_vector(&kernel, &expected_kernel_basis[1]));
1822  }
1823
1824  #[test]
1825  fn test_empty_matrix_0x0_row_major() {
1826    let m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1827    assert_eq!(m.num_rows(), 0);
1828    assert_eq!(m.num_cols(), 0);
1829    assert_eq!(m.image().len(), 0);
1830    assert_eq!(m.kernel().len(), 0);
1831  }
1832
1833  #[test]
1834  fn test_matrix_3x0_row_major() {
1835    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1836    m.append_row(DynamicVector::from(vec![]));
1837    m.append_row(DynamicVector::from(vec![]));
1838    m.append_row(DynamicVector::from(vec![]));
1839    assert_eq!(m.num_rows(), 3);
1840    assert_eq!(m.num_cols(), 0);
1841    assert_eq!(m.image().len(), 0);
1842    assert_eq!(m.kernel().len(), 0);
1843  }
1844
1845  #[test]
1846  fn test_empty_matrix_0x0_col_major() {
1847    let m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1848    assert_eq!(m.num_rows(), 0);
1849    assert_eq!(m.num_cols(), 0);
1850    assert_eq!(m.image().len(), 0);
1851    assert_eq!(m.kernel().len(), 0);
1852  }
1853
1854  #[test]
1855  fn test_matrix_0x3_col_major() {
1856    let mut m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1857    m.append_column(DynamicVector::from(vec![]));
1858    m.append_column(DynamicVector::from(vec![]));
1859    m.append_column(DynamicVector::from(vec![]));
1860    assert_eq!(m.num_rows(), 0);
1861    assert_eq!(m.num_cols(), 3);
1862    assert_eq!(m.image().len(), 0);
1863    // Kernel for 0xN matrix (A x = 0 always true) is R^N
1864    let kernel = m.kernel();
1865    assert_eq!(kernel.len(), 3);
1866    assert!(contains_vector(&kernel, &DynamicVector::from(vec![1.0, 0.0, 0.0])));
1867    assert!(contains_vector(&kernel, &DynamicVector::from(vec![0.0, 1.0, 0.0])));
1868    assert!(contains_vector(&kernel, &DynamicVector::from(vec![0.0, 0.0, 1.0])));
1869  }
1870
1871  #[test]
1872  fn test_matrix_vector_mul_row_major() {
1873    let mut m: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1874    m.append_row(DynamicVector::from(vec![1.0, 2.0, 3.0]));
1875    m.append_row(DynamicVector::from(vec![4.0, 5.0, 6.0]));
1876    m.append_row(DynamicVector::from(vec![7.0, 8.0, 9.0]));
1877    let v = DynamicVector::from(vec![1.0, 2.0, 3.0]);
1878    let result = m * v;
1879    assert_eq!(result, DynamicVector::from(vec![14.0, 32.0, 50.0]));
1880  }
1881
1882  #[test]
1883  fn test_matrix_vector_mul_col_major() {
1884    let mut m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1885    m.append_row(&DynamicVector::from(vec![1.0, 2.0, 3.0]));
1886    m.append_row(&DynamicVector::from(vec![4.0, 5.0, 6.0]));
1887    m.append_row(&DynamicVector::from(vec![7.0, 8.0, 9.0]));
1888    let v = DynamicVector::from(vec![1.0, 2.0, 3.0]);
1889    let result = m * v;
1890    assert_eq!(result, DynamicVector::from(vec![14.0, 32.0, 50.0]));
1891  }
1892
1893  #[test]
1894  fn test_matrix_zeros() {
1895    let m = DynamicDenseMatrix::<f64, RowMajor>::zeros(2, 3);
1896    assert_eq!(m.num_rows(), 2);
1897    assert_eq!(m.num_cols(), 3);
1898    assert_eq!(m.image().len(), 0);
1899    assert_eq!(m.kernel().len(), 3);
1900
1901    let m = DynamicDenseMatrix::<f64, ColumnMajor>::zeros(2, 3);
1902    assert_eq!(m.num_rows(), 2);
1903    assert_eq!(m.num_cols(), 3);
1904    assert_eq!(m.image().len(), 0);
1905    assert_eq!(m.kernel().len(), 3);
1906  }
1907
1908  #[test]
1909  fn test_matrix_matmul() {
1910    let mut m: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1911    // m (CM, 2x3)
1912    // 1  2  3
1913    // 4  5  6
1914    m.append_column(DynamicVector::from(vec![1.0, 4.0]));
1915    m.append_column(DynamicVector::from(vec![2.0, 5.0]));
1916    m.append_column(DynamicVector::from(vec![3.0, 6.0]));
1917
1918    let mut n: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1919    // n (RM, 3x2)
1920    // 9  10
1921    // 11 12
1922    // 13 14
1923    n.append_row(DynamicVector::from(vec![9.0, 10.0]));
1924    n.append_row(DynamicVector::from(vec![11.0, 12.0]));
1925    n.append_row(DynamicVector::from(vec![13.0, 14.0]));
1926
1927    // m (CM 2x3) * n (RM 3x2) = result (RM 2x2)
1928    let result = m * n;
1929    assert_eq!(result.num_rows(), 2);
1930    assert_eq!(result.num_cols(), 2);
1931    // Expected:
1932    // (1*9 + 2*11 + 3*13) (1*10 + 2*12 + 3*14) = (9+22+39) (10+24+42) = (70) (76)
1933    // (4*9 + 5*11 + 6*13) (4*10 + 5*12 + 6*14) = (36+55+78) (40+60+84) = (169) (184)
1934    assert_eq!(*result.get_component(0, 0), 1.0 * 9.0 + 2.0 * 11.0 + 3.0 * 13.0);
1935    assert_eq!(*result.get_component(0, 1), 1.0 * 10.0 + 2.0 * 12.0 + 3.0 * 14.0);
1936    assert_eq!(*result.get_component(1, 0), 4.0 * 9.0 + 5.0 * 11.0 + 6.0 * 13.0);
1937    assert_eq!(*result.get_component(1, 1), 4.0 * 10.0 + 5.0 * 12.0 + 6.0 * 14.0);
1938  }
1939
1940  #[test]
1941  fn test_matrix_matmul_rm_rm() {
1942    // A (RM 2x2)
1943    // 1 2
1944    // 3 4
1945    let mut a_rm: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1946    a_rm.append_row(DynamicVector::from(vec![1.0, 2.0]));
1947    a_rm.append_row(DynamicVector::from(vec![3.0, 4.0]));
1948
1949    // B (RM 2x2)
1950    // 5 6
1951    // 7 8
1952    let mut b_rm: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
1953    b_rm.append_row(DynamicVector::from(vec![5.0, 6.0]));
1954    b_rm.append_row(DynamicVector::from(vec![7.0, 8.0]));
1955
1956    // Expected A * B (RM 2x2)
1957    // 19 22
1958    // 43 50
1959    let result = a_rm * b_rm;
1960    assert_eq!(result.num_rows(), 2);
1961    assert_eq!(result.num_cols(), 2);
1962    assert_eq!(*result.get_component(0, 0), 1.0 * 5.0 + 2.0 * 7.0);
1963    assert_eq!(*result.get_component(0, 1), 1.0 * 6.0 + 2.0 * 8.0);
1964    assert_eq!(*result.get_component(1, 0), 3.0 * 5.0 + 4.0 * 7.0);
1965    assert_eq!(*result.get_component(1, 1), 3.0 * 6.0 + 4.0 * 8.0);
1966  }
1967
1968  #[test]
1969  fn test_matrix_matmul_cm_cm() {
1970    // A (CM 2x2)
1971    // 1 2
1972    // 3 4
1973    let mut a_cm: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1974    a_cm.append_column(DynamicVector::from(vec![1.0, 3.0]));
1975    a_cm.append_column(DynamicVector::from(vec![2.0, 4.0]));
1976
1977    // B (CM 2x2)
1978    // 5 6
1979    // 7 8
1980    let mut b_cm: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
1981    b_cm.append_column(DynamicVector::from(vec![5.0, 7.0]));
1982    b_cm.append_column(DynamicVector::from(vec![6.0, 8.0]));
1983
1984    // A (CM 2x2) * B (CM 2x2) = result (CM 2x2)
1985    // If CM*CM impl is A*B:
1986    // Expected A * B (CM 2x2)
1987    // 19 22
1988    // 43 50
1989    // If CM*CM impl is B*A^T (as suspected from code reading):
1990    // B (CM 2x2) * A^T (RM 2x2)
1991    // A^T (RM):
1992    // 1 3
1993    // 2 4
1994    // B * A^T (CM * RM -> RM result, but CM*CM -> CM result. The code seems to produce (B*A^T)
1995    // stored as CM) (5*1 + 6*2) (5*3 + 6*4) = (5+12) (15+24) = 17 39
1996    // (7*1 + 8*2) (7*3 + 8*4) = (7+16) (21+32) = 23 53
1997    // Expected if B*A^T, stored as CM:
1998    // 17 23
1999    // 39 53
2000
2001    let result = a_cm * b_cm; // Output is ColumnMajor
2002    assert_eq!(result.num_rows(), 2);
2003    assert_eq!(result.num_cols(), 2);
2004
2005    // Assuming standard A*B for now. If this fails, the impl is non-standard.
2006    assert_eq!(*result.get_component(0, 0), 1.0 * 5.0 + 2.0 * 7.0); // row 0, col 0
2007    assert_eq!(*result.get_component(0, 1), 1.0 * 6.0 + 2.0 * 8.0); // row 0, col 1
2008    assert_eq!(*result.get_component(1, 0), 3.0 * 5.0 + 4.0 * 7.0); // row 1, col 0
2009    assert_eq!(*result.get_component(1, 1), 3.0 * 6.0 + 4.0 * 8.0); // row 1, col 1
2010  }
2011
2012  #[test]
2013  fn test_display_formatting() {
2014    // Test empty matrix
2015    let empty_rm: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
2016    println!("Empty RowMajor matrix: \n{empty_rm}");
2017
2018    // Test small row-major matrix
2019    let mut small_rm: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
2020    small_rm.append_row(DynamicVector::from([1.0, 2.0]));
2021    small_rm.append_row(DynamicVector::from([3.0, 4.0]));
2022    println!("Small RowMajor matrix: \n{small_rm}");
2023
2024    // Test larger row-major matrix with different sized numbers
2025    let mut large_rm: DynamicDenseMatrix<f64, RowMajor> = DynamicDenseMatrix::new();
2026    large_rm.append_row(DynamicVector::from([1.0, 123.456, -5.0]));
2027    large_rm.append_row(DynamicVector::from([42.0, 0.0, -999.123]));
2028    large_rm.append_row(DynamicVector::from([7.8, 100.0, 2.5]));
2029    println!("Large RowMajor matrix: \n{large_rm}");
2030
2031    // Test column-major matrix
2032    let mut col_major: DynamicDenseMatrix<f64, ColumnMajor> = DynamicDenseMatrix::new();
2033    col_major.append_column(DynamicVector::from([10.0, 20.0]));
2034    col_major.append_column(DynamicVector::from([30.0, 40.0]));
2035    col_major.append_column(DynamicVector::from([50.0, 60.0]));
2036    println!("ColumnMajor matrix: \n{col_major}");
2037  }
2038}