Skip to main content

igraph/
matrix.rs

1//! Owned, column-major igraph matrices.
2//!
3//! [`Matrix`] (`igraph_matrix_t`, reals), [`MatrixInt`] (`igraph_matrix_int_t`),
4//! [`MatrixBool`] (`igraph_matrix_bool_t`), [`MatrixChar`]
5//! (`igraph_matrix_char_t`) and [`MatrixComplex`] (`igraph_matrix_complex_t`)
6//! are the C structs themselves,
7//! enriched with Rusty behaviour: they own their storage (freed on [`Drop`]),
8//! are indexed with `m[(row, col)]`, and convert from and to row-major
9//! `Vec<Vec<T>>`. The elements are stored in **column-major** order, as in
10//! igraph, and [`as_slice`](Matrix::as_slice) exposes them in that order.
11//!
12//! ```
13//! use igraph::prelude::*;
14//!
15//! let mut m = Matrix::from_rows(&[vec![1.0, 2.0], vec![3.0, 4.0], vec![5.0, 6.0]]).unwrap();
16//! assert_eq!((m.nrow(), m.ncol()), (3, 2));
17//! assert_eq!(m[(2, 1)], 6.0);
18//! m[(0, 0)] = 10.0;
19//! assert_eq!(m.as_slice(), &[10.0, 3.0, 5.0, 2.0, 4.0, 6.0]); // column-major
20//! assert_eq!(m.row(1), vec![3.0, 4.0]);
21//! assert_eq!(m.column(1), &[2.0, 4.0, 6.0]);
22//! ```
23//!
24//! The module covers `igraph_matrix_pmt.h`:
25//!
26//! | Group | Methods | Types |
27//! |-------|---------|-------|
28//! | construction | `zeros`, `new`, `from_rows`, `from_row_major`, `from_column_major`, `view` (zero-copy input), `identity` (real) | all |
29//! | access | `[(i, j)]`, `get`, `row`, `column`, `rows`, `columns`, `diagonal`, `indexed_iter`, `to_rows`, `as_slice` | all |
30//! | structure | `set_row`, `set_col`, `select_rows`, `select_cols`, `select_rows_cols`, `swap_rows`, `swap_cols`, `transpose`, `transposed`, `rbind`, `cbind`, `add_rows`, `add_cols`, `remove_row`, `remove_col`, `resize` | all |
31//! | queries | `is_symmetric`, `contains`, `search`, `fill`, `capacity`, `shrink_to_fit` | all |
32//! | order | `min`, `max`, `which_min`, `which_max`, `minmax`, `maxdifference`, `all_l`, `all_g`, `all_le`, `all_ge` | real, int, char |
33//! | arithmetic | `add`, `sub`, `mul_elements`, `div_elements`, `scale`, `add_constant`, `sum`, `prod`, `rowsums`, `colsums` | real, int, complex |
34//! | real only | `identity`, `matmul`, `mul_vec`, `all_almost_e`, `zapsmall` | real |
35//! | complex only | `from_parts`, `from_polar`, `real`, `imag`, `realimag`, `all_almost_e`, `zapsmall` | complex |
36//! | bool only | `count_true` | bool |
37//!
38//! Integer arithmetic is done in Rust with wrapping semantics (signed
39//! overflow and division by zero are undefined behaviour in the C
40//! implementation); char matrices have no arithmetic. Every index is checked
41//! before reaching igraph, which does not check them.
42//!
43//! ```
44//! use igraph::prelude::*;
45//!
46//! // The transition matrix of a random walk on the path 0 - 1 - 2.
47//! let path = Graph::ring(3, false, false, false).unwrap();
48//! let adj = path.get_adjacency(GetAdjacency::Both, None, Loops::Twice).unwrap();
49//! assert_eq!(adj, Matrix::from_rows(&[[0.0, 1.0, 0.0], [1.0, 0.0, 1.0], [0.0, 1.0, 0.0]]).unwrap());
50//! assert!(adj.is_symmetric());
51//! // Row sums of the adjacency matrix are the degrees...
52//! let degrees = adj.rowsums();
53//! assert_eq!(degrees, vec![1.0, 2.0, 1.0]);
54//! // ...and dividing each row by them gives the row-stochastic matrix.
55//! let mut p = adj.clone();
56//! for (i, d) in degrees.iter().enumerate() {
57//!     let row: Vec<f64> = p.row(i).iter().map(|x| x / d).collect();
58//!     p.set_row(i, &row).unwrap();
59//! }
60//! assert_eq!(p, path.get_stochastic(false, None).unwrap());
61//! assert_eq!(p.rowsums(), vec![1.0, 1.0, 1.0]);
62//! // Two steps from the middle vertex bring the walker back with probability 1.
63//! assert_eq!(p.matmul(&p).unwrap()[(1, 1)], 1.0);
64//! ```
65//!
66//! See also [`Graph::get_adjacency`](crate::Graph::get_adjacency) and
67//! [`Graph::adjacency`](crate::Graph::adjacency) to convert between graphs
68//! and matrices, [`MatrixList`](crate::list::MatrixList) for lists of
69//! matrices, [`linalg`](crate::linalg) for eigenvalue problems, BLAS/LAPACK
70//! routines and sparse matrices, and [`layout`](crate::layout), whose
71//! layouts are `n × 2` (or `n × 3`) coordinate matrices.
72
73use crate::{
74    error::{Error, Result},
75    ffi::*,
76    vector::{Vector, VectorBool, VectorChar, VectorComplex, VectorInt},
77};
78use std::{
79    fmt,
80    mem::MaybeUninit,
81    ops::{Index, IndexMut},
82};
83
84/// Owned column-major matrix of reals (`igraph_matrix_t`), see the [module docs](self).
85pub type Matrix = igraph_matrix_t;
86/// Owned column-major matrix of integers (`igraph_matrix_int_t`), see the [module docs](self).
87pub type MatrixInt = igraph_matrix_int_t;
88/// Owned column-major matrix of booleans (`igraph_matrix_bool_t`), see the [module docs](self).
89pub type MatrixBool = igraph_matrix_bool_t;
90/// Owned column-major matrix of chars (`igraph_matrix_char_t`), see the [module docs](self).
91pub type MatrixChar = igraph_matrix_char_t;
92/// Owned column-major matrix of complex numbers (`igraph_matrix_complex_t`),
93/// see the [module docs](self); igraph returns such matrices e.g. as the
94/// eigenvectors of non-symmetric matrices.
95pub type MatrixComplex = igraph_matrix_complex_t;
96
97/// How a matrix element is written by the matrices' `Display`.
98trait Cell {
99    fn cell(&self) -> String;
100}
101
102macro_rules! debug_cell {
103    ($($t:ty),*) => {$(
104        impl Cell for $t {
105            fn cell(&self) -> String {
106                format!("{self:?}")
107            }
108        }
109    )*};
110}
111debug_cell!(igraph_real_t, igraph_int_t, igraph_bool_t, std::ffi::c_char);
112
113impl Cell for igraph_complex_t {
114    fn cell(&self) -> String {
115        self.to_string()
116    }
117}
118
119macro_rules! impl_matrix {
120    ($ty:ident, $elem:ty, init = $init:ident, init_copy = $init_copy:ident,
121     destroy = $destroy:ident, resize = $resize:ident) => {
122        impl $ty {
123            /// Creates a `nrow` × `ncol` matrix filled with zeros
124            /// ([`igraph_matrix_init`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_init)).
125            ///
126            /// # Panics
127            /// If igraph cannot allocate the matrix, or the size overflows,
128            /// like [`Vec::with_capacity`].
129            pub fn zeros(nrow: usize, ncol: usize) -> Self {
130                crate::error::ensure_init();
131                let mut raw = MaybeUninit::<Self>::uninit();
132                crate::error::check(unsafe {
133                    $init(
134                        raw.as_mut_ptr(),
135                        crate::error::int_size(nrow),
136                        crate::error::int_size(ncol),
137                    )
138                })
139                .expect("igraph failed to allocate a matrix");
140                unsafe { raw.assume_init() }
141            }
142
143            /// Creates an empty 0 × 0 matrix.
144            pub fn new() -> Self {
145                Self::zeros(0, 0)
146            }
147
148            /// Builds a matrix from its elements given in column-major order
149            /// (igraph's storage order, see
150            /// [`igraph_matrix_init_array`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_init_array)).
151            ///
152            /// # Errors
153            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
154            /// if `data.len() != nrow * ncol`.
155            pub fn from_column_major(nrow: usize, ncol: usize, data: &[$elem]) -> Result<Self> {
156                if nrow.checked_mul(ncol) != Some(data.len()) {
157                    return Err(Error::invalid(format!(
158                        "{} elements given for a {nrow}x{ncol} matrix",
159                        data.len()
160                    )));
161                }
162                let mut m = Self::zeros(nrow, ncol);
163                m.as_mut_slice().copy_from_slice(data);
164                Ok(m)
165            }
166
167            /// Builds a matrix from its elements given in row-major order.
168            ///
169            /// # Errors
170            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
171            /// if `data.len() != nrow * ncol`.
172            pub fn from_row_major(nrow: usize, ncol: usize, data: &[$elem]) -> Result<Self> {
173                if nrow.checked_mul(ncol) != Some(data.len()) {
174                    return Err(Error::invalid(format!(
175                        "{} elements given for a {nrow}x{ncol} matrix",
176                        data.len()
177                    )));
178                }
179                let mut m = Self::zeros(nrow, ncol);
180                for i in 0..nrow {
181                    for j in 0..ncol {
182                        m[(i, j)] = data[i * ncol + j];
183                    }
184                }
185                Ok(m)
186            }
187
188            /// Builds a matrix from a slice of rows (a `0 × 0` matrix for no
189            /// rows).
190            ///
191            /// # Errors
192            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
193            /// if the rows do not all have the same length.
194            pub fn from_rows<R: AsRef<[$elem]>>(rows: &[R]) -> Result<Self> {
195                let nrow = rows.len();
196                let ncol = rows.first().map_or(0, |r| r.as_ref().len());
197                let mut m = Self::zeros(nrow, ncol);
198                for (i, row) in rows.iter().enumerate() {
199                    let row = row.as_ref();
200                    if row.len() != ncol {
201                        return Err(Error::invalid(format!(
202                            "row {i} has {} elements, expected {ncol}",
203                            row.len()
204                        )));
205                    }
206                    for (j, &x) in row.iter().enumerate() {
207                        m[(i, j)] = x;
208                    }
209                }
210                Ok(m)
211            }
212
213            /// Number of rows ([`igraph_matrix_nrow`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_nrow)).
214            pub fn nrow(&self) -> usize {
215                self.nrow as usize
216            }
217
218            /// Number of columns ([`igraph_matrix_ncol`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_ncol)).
219            pub fn ncol(&self) -> usize {
220                self.ncol as usize
221            }
222
223            /// `(nrow, ncol)`.
224            pub fn shape(&self) -> (usize, usize) {
225                (self.nrow(), self.ncol())
226            }
227
228            /// Whether the matrix has no elements.
229            pub fn is_empty(&self) -> bool {
230                self.nrow() * self.ncol() == 0
231            }
232
233            /// All the elements, in column-major order.
234            pub fn as_slice(&self) -> &[$elem] {
235                self.data.as_slice()
236            }
237
238            /// All the elements, in column-major order, mutably.
239            pub fn as_mut_slice(&mut self) -> &mut [$elem] {
240                self.data.as_mut_slice()
241            }
242
243            /// The element at `(row, col)`, if in bounds.
244            pub fn get(&self, row: usize, col: usize) -> Option<&$elem> {
245                if row < self.nrow() && col < self.ncol() {
246                    Some(&self.as_slice()[col * self.nrow() + row])
247                } else {
248                    None
249                }
250            }
251
252            /// The `j`-th column, as a slice (columns are contiguous in
253            /// column-major storage).
254            ///
255            /// # Panics
256            /// If `j >= ncol`.
257            pub fn column(&self, j: usize) -> &[$elem] {
258                let n = self.nrow();
259                &self.as_slice()[j * n..(j + 1) * n]
260            }
261
262            /// A copy of the `i`-th row.
263            ///
264            /// # Panics
265            /// If `i >= nrow`.
266            pub fn row(&self, i: usize) -> Vec<$elem> {
267                assert!(i < self.nrow(), "row index {i} out of bounds");
268                (0..self.ncol()).map(|j| self[(i, j)]).collect()
269            }
270
271            /// Iterates over copies of the rows.
272            pub fn rows(&self) -> impl Iterator<Item = Vec<$elem>> + '_ {
273                (0..self.nrow()).map(move |i| self.row(i))
274            }
275
276            /// Iterates over the columns, as slices.
277            pub fn columns(&self) -> impl Iterator<Item = &[$elem]> + '_ {
278                (0..self.ncol()).map(move |j| self.column(j))
279            }
280
281            /// Converts into a row-major `Vec` of rows.
282            pub fn to_rows(&self) -> Vec<Vec<$elem>> {
283                self.rows().collect()
284            }
285
286            /// Returns the transposed matrix.
287            pub fn transposed(&self) -> Self {
288                let mut t = Self::zeros(self.ncol(), self.nrow());
289                for i in 0..self.nrow() {
290                    for j in 0..self.ncol() {
291                        t[(j, i)] = self[(i, j)];
292                    }
293                }
294                t
295            }
296
297            /// Resizes to `nrow` × `ncol`
298            /// ([`igraph_matrix_resize`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_resize)); igraph
299            /// does not preserve the content meaningfully, so this wrapper
300            /// resets every element to zero. Use
301            /// [`add_rows`](Self::add_rows), [`add_cols`](Self::add_cols),
302            /// [`remove_row`](Self::remove_row) and
303            /// [`remove_col`](Self::remove_col) to keep the content.
304            pub fn resize(&mut self, nrow: usize, ncol: usize) {
305                crate::error::check(unsafe {
306                    $resize(
307                        self,
308                        crate::error::int_size(nrow),
309                        crate::error::int_size(ncol),
310                    )
311                })
312                .expect("igraph failed to resize a matrix");
313                for x in self.as_mut_slice() {
314                    *x = Default::default();
315                }
316            }
317        }
318
319        impl Drop for $ty {
320            fn drop(&mut self) {
321                if !self.data.stor_begin.is_null() {
322                    unsafe { $destroy(self) };
323                    // `data` has been destroyed by igraph: forget it.
324                    self.data.stor_begin = std::ptr::null_mut();
325                }
326            }
327        }
328
329        impl Default for $ty {
330            fn default() -> Self {
331                Self::new()
332            }
333        }
334
335        impl Clone for $ty {
336            fn clone(&self) -> Self {
337                crate::error::ensure_init();
338                let mut raw = MaybeUninit::<Self>::uninit();
339                crate::error::check(unsafe { $init_copy(raw.as_mut_ptr(), self) })
340                    .expect("igraph failed to copy a matrix");
341                unsafe { raw.assume_init() }
342            }
343        }
344
345        impl PartialEq for $ty {
346            fn eq(&self, other: &Self) -> bool {
347                self.shape() == other.shape() && self.as_slice() == other.as_slice()
348            }
349        }
350
351        impl Index<(usize, usize)> for $ty {
352            type Output = $elem;
353            fn index(&self, (i, j): (usize, usize)) -> &$elem {
354                assert!(
355                    i < self.nrow() && j < self.ncol(),
356                    "matrix index ({i}, {j}) out of bounds"
357                );
358                let n = self.nrow();
359                &self.as_slice()[j * n + i]
360            }
361        }
362
363        impl IndexMut<(usize, usize)> for $ty {
364            fn index_mut(&mut self, (i, j): (usize, usize)) -> &mut $elem {
365                assert!(
366                    i < self.nrow() && j < self.ncol(),
367                    "matrix index ({i}, {j}) out of bounds"
368                );
369                let n = self.nrow();
370                &mut self.as_mut_slice()[j * n + i]
371            }
372        }
373
374        impl fmt::Display for $ty {
375            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376                for i in 0..self.nrow() {
377                    let row = self.row(i);
378                    let cells: Vec<String> = row.iter().map(Cell::cell).collect();
379                    writeln!(f, "[{}]", cells.join(", "))?;
380                }
381                Ok(())
382            }
383        }
384
385        impl From<$ty> for Vec<Vec<$elem>> {
386            fn from(m: $ty) -> Self {
387                m.to_rows()
388            }
389        }
390
391        unsafe impl Send for $ty {}
392        unsafe impl Sync for $ty {}
393    };
394}
395
396impl_matrix!(
397    igraph_matrix_t,
398    igraph_real_t,
399    init = igraph_matrix_init,
400    init_copy = igraph_matrix_init_copy,
401    destroy = igraph_matrix_destroy,
402    resize = igraph_matrix_resize
403);
404impl_matrix!(
405    igraph_matrix_int_t,
406    igraph_int_t,
407    init = igraph_matrix_int_init,
408    init_copy = igraph_matrix_int_init_copy,
409    destroy = igraph_matrix_int_destroy,
410    resize = igraph_matrix_int_resize
411);
412impl_matrix!(
413    igraph_matrix_bool_t,
414    igraph_bool_t,
415    init = igraph_matrix_bool_init,
416    init_copy = igraph_matrix_bool_init_copy,
417    destroy = igraph_matrix_bool_destroy,
418    resize = igraph_matrix_bool_resize
419);
420impl_matrix!(
421    igraph_matrix_char_t,
422    std::ffi::c_char,
423    init = igraph_matrix_char_init,
424    init_copy = igraph_matrix_char_init_copy,
425    destroy = igraph_matrix_char_destroy,
426    resize = igraph_matrix_char_resize
427);
428impl_matrix!(
429    igraph_matrix_complex_t,
430    igraph_complex_t,
431    init = igraph_matrix_complex_init,
432    init_copy = igraph_matrix_complex_init_copy,
433    destroy = igraph_matrix_complex_destroy,
434    resize = igraph_matrix_complex_resize
435);
436
437// ---------------------------------------------------------------------------
438// Structural operations, for every element type (`igraph_matrix_pmt.h`).
439// ---------------------------------------------------------------------------
440
441fn check_positions(index: &[igraph_int_t], len: usize, what: &str) -> Result<()> {
442    match index.iter().find(|&&i| i < 0 || i as usize >= len) {
443        Some(i) => Err(Error::invalid(format!(
444            "{what} index {i} out of bounds (there are {len})"
445        ))),
446        None => Ok(()),
447    }
448}
449
450macro_rules! impl_matrix_common {
451    (
452        $ty:ident, $elem:ty, $vec:ident,
453        view = $view:ident, fill = $fill:ident, set_row = $set_row:ident, set_col = $set_col:ident,
454        select_rows = $select_rows:ident, select_cols = $select_cols:ident,
455        select_rows_cols = $select_rows_cols:ident, swap_rows = $swap_rows:ident,
456        swap_cols = $swap_cols:ident, transpose = $transpose:ident,
457        rbind = $rbind:ident, cbind = $cbind:ident, add_rows = $add_rows:ident,
458        add_cols = $add_cols:ident, remove_row = $remove_row:ident, remove_col = $remove_col:ident,
459        is_symmetric = $is_symmetric:ident, contains = $contains:ident, search = $search:ident,
460        capacity = $capacity:ident, resize_min = $resize_min:ident
461    ) => {
462        impl $ty {
463            /// A read-only matrix *view* of column-major `data`, without
464            /// copying it; the view dereferences to the matrix type, so it
465            /// can be handed to igraph as a `const` matrix input
466            /// ([`igraph_matrix_view`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_view)).
467            ///
468            /// # Errors
469            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
470            /// if `data.len() != nrow * ncol`.
471            pub fn view(
472                data: &[$elem],
473                nrow: usize,
474                ncol: usize,
475            ) -> Result<crate::vector::View<'_, Self>> {
476                if nrow.checked_mul(ncol) != Some(data.len()) {
477                    return Err(Error::invalid(format!(
478                        "{} elements given for a {nrow}x{ncol} matrix",
479                        data.len()
480                    )));
481                }
482                let raw = unsafe {
483                    $view(
484                        data.as_ptr(),
485                        crate::error::int_size(nrow),
486                        crate::error::int_size(ncol),
487                    )
488                };
489                // SAFETY: the view borrows `data` for its whole lifetime.
490                Ok(unsafe { crate::vector::View::from_raw(raw) })
491            }
492
493            /// Sets every element to `value`
494            /// ([`igraph_matrix_fill`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_fill)).
495            pub fn fill(&mut self, value: $elem) {
496                if !self.is_empty() {
497                    unsafe { $fill(self, value) };
498                }
499            }
500
501            /// Replaces row `i` with `values`
502            /// ([`igraph_matrix_set_row`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_set_row)).
503            ///
504            /// # Errors
505            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
506            /// if `i` is out of bounds or `values.len() != ncol`.
507            pub fn set_row(&mut self, i: usize, values: &[$elem]) -> Result<()> {
508                if i >= self.nrow() || values.len() != self.ncol() {
509                    return Err(Error::invalid(format!(
510                        "cannot set row {i} of a {}x{} matrix with {} values",
511                        self.nrow(),
512                        self.ncol(),
513                        values.len()
514                    )));
515                }
516                let v = $vec::view(values);
517                crate::igraph_call!($set_row(self, v.as_ptr(), i as igraph_int_t))
518            }
519
520            /// Replaces column `j` with `values`
521            /// ([`igraph_matrix_set_col`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_set_col)).
522            ///
523            /// # Errors
524            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
525            /// if `j` is out of bounds or `values.len() != nrow`.
526            pub fn set_col(&mut self, j: usize, values: &[$elem]) -> Result<()> {
527                if j >= self.ncol() || values.len() != self.nrow() {
528                    return Err(Error::invalid(format!(
529                        "cannot set column {j} of a {}x{} matrix with {} values",
530                        self.nrow(),
531                        self.ncol(),
532                        values.len()
533                    )));
534                }
535                let v = $vec::view(values);
536                crate::igraph_call!($set_col(self, v.as_ptr(), j as igraph_int_t))
537            }
538
539            /// A new matrix made of the given rows, in the given order
540            /// (repetitions allowed)
541            /// ([`igraph_matrix_select_rows`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_select_rows)).
542            ///
543            /// # Errors
544            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for out of bounds rows.
545            pub fn select_rows(&self, rows: &[igraph_int_t]) -> Result<Self> {
546                check_positions(rows, self.nrow(), "row")?;
547                let r = VectorInt::view(rows);
548                let mut res = Self::zeros(rows.len(), self.ncol());
549                crate::igraph_call!($select_rows(self, &mut res, r.as_ptr()))?;
550                Ok(res)
551            }
552
553            /// A new matrix made of the given columns, in the given order
554            /// ([`igraph_matrix_select_cols`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_select_cols)).
555            ///
556            /// # Errors
557            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for out of bounds columns.
558            pub fn select_cols(&self, cols: &[igraph_int_t]) -> Result<Self> {
559                check_positions(cols, self.ncol(), "column")?;
560                let c = VectorInt::view(cols);
561                let mut res = Self::zeros(self.nrow(), cols.len());
562                crate::igraph_call!($select_cols(self, &mut res, c.as_ptr()))?;
563                Ok(res)
564            }
565
566            /// The submatrix at the intersection of the given rows and columns
567            /// ([`igraph_matrix_select_rows_cols`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_select_rows_cols)).
568            ///
569            /// # Errors
570            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for out of bounds indices.
571            pub fn select_rows_cols(
572                &self,
573                rows: &[igraph_int_t],
574                cols: &[igraph_int_t],
575            ) -> Result<Self> {
576                check_positions(rows, self.nrow(), "row")?;
577                check_positions(cols, self.ncol(), "column")?;
578                let r = VectorInt::view(rows);
579                let c = VectorInt::view(cols);
580                let mut res = Self::zeros(rows.len(), cols.len());
581                crate::igraph_call!($select_rows_cols(self, &mut res, r.as_ptr(), c.as_ptr()))?;
582                Ok(res)
583            }
584
585            /// Swaps rows `i` and `j`
586            /// ([`igraph_matrix_swap_rows`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_swap_rows)).
587            ///
588            /// # Errors
589            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for out of bounds rows.
590            pub fn swap_rows(&mut self, i: usize, j: usize) -> Result<()> {
591                check_positions(&[i as igraph_int_t, j as igraph_int_t], self.nrow(), "row")?;
592                crate::igraph_call!($swap_rows(self, i as igraph_int_t, j as igraph_int_t))
593            }
594
595            /// Swaps columns `i` and `j`
596            /// ([`igraph_matrix_swap_cols`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_swap_cols)).
597            ///
598            /// # Errors
599            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for out of bounds columns.
600            pub fn swap_cols(&mut self, i: usize, j: usize) -> Result<()> {
601                check_positions(
602                    &[i as igraph_int_t, j as igraph_int_t],
603                    self.ncol(),
604                    "column",
605                )?;
606                crate::igraph_call!($swap_cols(self, i as igraph_int_t, j as igraph_int_t))
607            }
608
609            /// Transposes the matrix in place
610            /// ([`igraph_matrix_transpose`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_transpose));
611            /// see also [`transposed`](Self::transposed).
612            pub fn transpose(&mut self) {
613                crate::error::check(unsafe { $transpose(self) })
614                    .expect("igraph failed to transpose a matrix");
615            }
616
617            /// Appends the rows of `other` below this matrix
618            /// ([`igraph_matrix_rbind`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_rbind)).
619            ///
620            /// # Errors
621            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the column counts differ.
622            pub fn rbind(&mut self, other: &Self) -> Result<()> {
623                if self.ncol() != other.ncol() {
624                    return Err(Error::invalid("rbind: the number of columns differ"));
625                }
626                crate::igraph_call!($rbind(self, other))
627            }
628
629            /// Appends the columns of `other` to the right of this matrix
630            /// ([`igraph_matrix_cbind`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_cbind)).
631            ///
632            /// # Errors
633            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the row counts differ.
634            pub fn cbind(&mut self, other: &Self) -> Result<()> {
635                if self.nrow() != other.nrow() {
636                    return Err(Error::invalid("cbind: the number of rows differ"));
637                }
638                crate::igraph_call!($cbind(self, other))
639            }
640
641            /// Appends `n` rows of zeros
642            /// ([`igraph_matrix_add_rows`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add_rows)).
643            pub fn add_rows(&mut self, n: usize) {
644                let old = self.nrow();
645                crate::error::check(unsafe { $add_rows(self, crate::error::int_size(n)) })
646                    .expect("igraph failed to grow a matrix");
647                for j in 0..self.ncol() {
648                    for i in old..self.nrow() {
649                        self[(i, j)] = Default::default();
650                    }
651                }
652            }
653
654            /// Appends `n` columns of zeros
655            /// ([`igraph_matrix_add_cols`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add_cols)).
656            pub fn add_cols(&mut self, n: usize) {
657                let old = self.ncol();
658                crate::error::check(unsafe { $add_cols(self, crate::error::int_size(n)) })
659                    .expect("igraph failed to grow a matrix");
660                let nrow = self.nrow();
661                for x in &mut self.as_mut_slice()[old * nrow..] {
662                    *x = Default::default();
663                }
664            }
665
666            /// Removes row `i`
667            /// ([`igraph_matrix_remove_row`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_remove_row)).
668            ///
669            /// # Errors
670            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if out of bounds.
671            pub fn remove_row(&mut self, i: usize) -> Result<()> {
672                check_positions(&[i as igraph_int_t], self.nrow(), "row")?;
673                crate::igraph_call!($remove_row(self, i as igraph_int_t))
674            }
675
676            /// Removes column `j`
677            /// ([`igraph_matrix_remove_col`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_remove_col)).
678            ///
679            /// # Errors
680            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if out of bounds.
681            pub fn remove_col(&mut self, j: usize) -> Result<()> {
682                check_positions(&[j as igraph_int_t], self.ncol(), "column")?;
683                crate::igraph_call!($remove_col(self, j as igraph_int_t))
684            }
685
686            /// Whether the matrix is square and equal to its transpose
687            /// ([`igraph_matrix_is_symmetric`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_is_symmetric)).
688            pub fn is_symmetric(&self) -> bool {
689                unsafe { $is_symmetric(self) }
690            }
691
692            /// Whether some element equals `value`
693            /// ([`igraph_matrix_contains`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_contains)).
694            pub fn contains(&self, value: $elem) -> bool {
695                !self.is_empty() && unsafe { $contains(self, value) }
696            }
697
698            /// The `(row, col)` of the first occurrence of `value` at or after
699            /// the column-major position `from`, if any
700            /// ([`igraph_matrix_search`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_search)).
701            pub fn search(&self, from: usize, value: $elem) -> Option<(usize, usize)> {
702                if from >= self.as_slice().len() {
703                    return None;
704                }
705                let (mut pos, mut row, mut col) = (0, 0, 0);
706                unsafe {
707                    $search(
708                        self,
709                        from as igraph_int_t,
710                        value,
711                        &mut pos,
712                        &mut row,
713                        &mut col,
714                    )
715                }
716                .then_some((row as usize, col as usize))
717            }
718
719            /// Number of elements the matrix can hold without reallocating
720            /// ([`igraph_matrix_capacity`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_capacity)).
721            pub fn capacity(&self) -> usize {
722                unsafe { $capacity(self) as usize }
723            }
724
725            /// Frees the unused storage
726            /// ([`igraph_matrix_resize_min`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_resize_min)).
727            pub fn shrink_to_fit(&mut self) {
728                unsafe { $resize_min(self) };
729            }
730
731            /// The main diagonal, `m[(i, i)]` for `i < min(nrow, ncol)`.
732            pub fn diagonal(&self) -> Vec<$elem> {
733                (0..self.nrow().min(self.ncol()))
734                    .map(|i| self[(i, i)])
735                    .collect()
736            }
737
738            /// Iterates over `((row, col), &value)` in column-major order.
739            pub fn indexed_iter(&self) -> impl Iterator<Item = ((usize, usize), &$elem)> + '_ {
740                let n = self.nrow().max(1);
741                self.as_slice()
742                    .iter()
743                    .enumerate()
744                    .map(move |(k, x)| ((k % n, k / n), x))
745            }
746        }
747    };
748}
749
750impl_matrix_common!(
751    igraph_matrix_t,
752    igraph_real_t,
753    Vector,
754    view = igraph_matrix_view,
755    fill = igraph_matrix_fill,
756    set_row = igraph_matrix_set_row,
757    set_col = igraph_matrix_set_col,
758    select_rows = igraph_matrix_select_rows,
759    select_cols = igraph_matrix_select_cols,
760    select_rows_cols = igraph_matrix_select_rows_cols,
761    swap_rows = igraph_matrix_swap_rows,
762    swap_cols = igraph_matrix_swap_cols,
763    transpose = igraph_matrix_transpose,
764    rbind = igraph_matrix_rbind,
765    cbind = igraph_matrix_cbind,
766    add_rows = igraph_matrix_add_rows,
767    add_cols = igraph_matrix_add_cols,
768    remove_row = igraph_matrix_remove_row,
769    remove_col = igraph_matrix_remove_col,
770    is_symmetric = igraph_matrix_is_symmetric,
771    contains = igraph_matrix_contains,
772    search = igraph_matrix_search,
773    capacity = igraph_matrix_capacity,
774    resize_min = igraph_matrix_resize_min
775);
776impl_matrix_common!(
777    igraph_matrix_int_t,
778    igraph_int_t,
779    VectorInt,
780    view = igraph_matrix_int_view,
781    fill = igraph_matrix_int_fill,
782    set_row = igraph_matrix_int_set_row,
783    set_col = igraph_matrix_int_set_col,
784    select_rows = igraph_matrix_int_select_rows,
785    select_cols = igraph_matrix_int_select_cols,
786    select_rows_cols = igraph_matrix_int_select_rows_cols,
787    swap_rows = igraph_matrix_int_swap_rows,
788    swap_cols = igraph_matrix_int_swap_cols,
789    transpose = igraph_matrix_int_transpose,
790    rbind = igraph_matrix_int_rbind,
791    cbind = igraph_matrix_int_cbind,
792    add_rows = igraph_matrix_int_add_rows,
793    add_cols = igraph_matrix_int_add_cols,
794    remove_row = igraph_matrix_int_remove_row,
795    remove_col = igraph_matrix_int_remove_col,
796    is_symmetric = igraph_matrix_int_is_symmetric,
797    contains = igraph_matrix_int_contains,
798    search = igraph_matrix_int_search,
799    capacity = igraph_matrix_int_capacity,
800    resize_min = igraph_matrix_int_resize_min
801);
802impl_matrix_common!(
803    igraph_matrix_bool_t,
804    igraph_bool_t,
805    VectorBool,
806    view = igraph_matrix_bool_view,
807    fill = igraph_matrix_bool_fill,
808    set_row = igraph_matrix_bool_set_row,
809    set_col = igraph_matrix_bool_set_col,
810    select_rows = igraph_matrix_bool_select_rows,
811    select_cols = igraph_matrix_bool_select_cols,
812    select_rows_cols = igraph_matrix_bool_select_rows_cols,
813    swap_rows = igraph_matrix_bool_swap_rows,
814    swap_cols = igraph_matrix_bool_swap_cols,
815    transpose = igraph_matrix_bool_transpose,
816    rbind = igraph_matrix_bool_rbind,
817    cbind = igraph_matrix_bool_cbind,
818    add_rows = igraph_matrix_bool_add_rows,
819    add_cols = igraph_matrix_bool_add_cols,
820    remove_row = igraph_matrix_bool_remove_row,
821    remove_col = igraph_matrix_bool_remove_col,
822    is_symmetric = igraph_matrix_bool_is_symmetric,
823    contains = igraph_matrix_bool_contains,
824    search = igraph_matrix_bool_search,
825    capacity = igraph_matrix_bool_capacity,
826    resize_min = igraph_matrix_bool_resize_min
827);
828impl_matrix_common!(
829    igraph_matrix_char_t,
830    std::ffi::c_char,
831    VectorChar,
832    view = igraph_matrix_char_view,
833    fill = igraph_matrix_char_fill,
834    set_row = igraph_matrix_char_set_row,
835    set_col = igraph_matrix_char_set_col,
836    select_rows = igraph_matrix_char_select_rows,
837    select_cols = igraph_matrix_char_select_cols,
838    select_rows_cols = igraph_matrix_char_select_rows_cols,
839    swap_rows = igraph_matrix_char_swap_rows,
840    swap_cols = igraph_matrix_char_swap_cols,
841    transpose = igraph_matrix_char_transpose,
842    rbind = igraph_matrix_char_rbind,
843    cbind = igraph_matrix_char_cbind,
844    add_rows = igraph_matrix_char_add_rows,
845    add_cols = igraph_matrix_char_add_cols,
846    remove_row = igraph_matrix_char_remove_row,
847    remove_col = igraph_matrix_char_remove_col,
848    is_symmetric = igraph_matrix_char_is_symmetric,
849    contains = igraph_matrix_char_contains,
850    search = igraph_matrix_char_search,
851    capacity = igraph_matrix_char_capacity,
852    resize_min = igraph_matrix_char_resize_min
853);
854impl_matrix_common!(
855    igraph_matrix_complex_t,
856    igraph_complex_t,
857    VectorComplex,
858    view = igraph_matrix_complex_view,
859    fill = igraph_matrix_complex_fill,
860    set_row = igraph_matrix_complex_set_row,
861    set_col = igraph_matrix_complex_set_col,
862    select_rows = igraph_matrix_complex_select_rows,
863    select_cols = igraph_matrix_complex_select_cols,
864    select_rows_cols = igraph_matrix_complex_select_rows_cols,
865    swap_rows = igraph_matrix_complex_swap_rows,
866    swap_cols = igraph_matrix_complex_swap_cols,
867    transpose = igraph_matrix_complex_transpose,
868    rbind = igraph_matrix_complex_rbind,
869    cbind = igraph_matrix_complex_cbind,
870    add_rows = igraph_matrix_complex_add_rows,
871    add_cols = igraph_matrix_complex_add_cols,
872    remove_row = igraph_matrix_complex_remove_row,
873    remove_col = igraph_matrix_complex_remove_col,
874    is_symmetric = igraph_matrix_complex_is_symmetric,
875    contains = igraph_matrix_complex_contains,
876    search = igraph_matrix_complex_search,
877    capacity = igraph_matrix_complex_capacity,
878    resize_min = igraph_matrix_complex_resize_min
879);
880
881// ---------------------------------------------------------------------------
882// Ordered element types: reals and integers.
883// ---------------------------------------------------------------------------
884
885macro_rules! impl_matrix_ordered {
886    (
887        $ty:ident, $elem:ty,
888        which_min = $which_min:ident,
889        which_max = $which_max:ident, maxdifference = $maxdifference:ident,
890        all_l = $all_l:ident, all_g = $all_g:ident, all_le = $all_le:ident, all_ge = $all_ge:ident
891    ) => {
892        impl $ty {
893            /// The smallest element, or `None` if the matrix is empty
894            /// ([`igraph_matrix_min`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_min)).
895            pub fn min(&self) -> Option<$elem> {
896                self.which_min().map(|ij| self[ij])
897            }
898
899            /// The largest element, or `None` if the matrix is empty
900            /// ([`igraph_matrix_max`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_max)).
901            pub fn max(&self) -> Option<$elem> {
902                self.which_max().map(|ij| self[ij])
903            }
904
905            /// `(row, col)` of the smallest element, or `None` if empty
906            /// ([`igraph_matrix_which_min`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_which_min)).
907            pub fn which_min(&self) -> Option<(usize, usize)> {
908                if self.is_empty() {
909                    return None;
910                }
911                let (mut i, mut j) = (0, 0);
912                unsafe { $which_min(self, &mut i, &mut j) };
913                Some((i as usize, j as usize))
914            }
915
916            /// `(row, col)` of the largest element, or `None` if empty
917            /// ([`igraph_matrix_which_max`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_which_max)).
918            pub fn which_max(&self) -> Option<(usize, usize)> {
919                if self.is_empty() {
920                    return None;
921                }
922                let (mut i, mut j) = (0, 0);
923                unsafe { $which_max(self, &mut i, &mut j) };
924                Some((i as usize, j as usize))
925            }
926
927            /// `(min, max)`, or `None` if empty (as `igraph_matrix_minmax`).
928            pub fn minmax(&self) -> Option<($elem, $elem)> {
929                Some((self.min()?, self.max()?))
930            }
931
932            /// The largest absolute element-wise difference, comparing the
933            /// elements in column-major order
934            /// ([`igraph_matrix_maxdifference`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_maxdifference)).
935            pub fn maxdifference(&self, other: &Self) -> f64 {
936                if self.is_empty() || other.is_empty() {
937                    return 0.0;
938                }
939                unsafe { $maxdifference(self, other) }
940            }
941
942            /// Whether the shapes agree and every element is `<` the other's
943            /// ([`igraph_matrix_all_l`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_all_l)).
944            pub fn all_l(&self, other: &Self) -> bool {
945                self.shape() == other.shape() && (self.is_empty() || unsafe { $all_l(self, other) })
946            }
947
948            /// Whether the shapes agree and every element is `>` the other's
949            /// ([`igraph_matrix_all_g`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_all_g)).
950            pub fn all_g(&self, other: &Self) -> bool {
951                self.shape() == other.shape() && (self.is_empty() || unsafe { $all_g(self, other) })
952            }
953
954            /// Whether the shapes agree and every element is `<=` the other's
955            /// ([`igraph_matrix_all_le`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_all_le)).
956            pub fn all_le(&self, other: &Self) -> bool {
957                self.shape() == other.shape()
958                    && (self.is_empty() || unsafe { $all_le(self, other) })
959            }
960
961            /// Whether the shapes agree and every element is `>=` the other's
962            /// ([`igraph_matrix_all_ge`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_all_ge)).
963            pub fn all_ge(&self, other: &Self) -> bool {
964                self.shape() == other.shape()
965                    && (self.is_empty() || unsafe { $all_ge(self, other) })
966            }
967        }
968    };
969}
970
971impl_matrix_ordered!(
972    igraph_matrix_t,
973    igraph_real_t,
974    which_min = igraph_matrix_which_min,
975    which_max = igraph_matrix_which_max,
976    maxdifference = igraph_matrix_maxdifference,
977    all_l = igraph_matrix_all_l,
978    all_g = igraph_matrix_all_g,
979    all_le = igraph_matrix_all_le,
980    all_ge = igraph_matrix_all_ge
981);
982impl_matrix_ordered!(
983    igraph_matrix_int_t,
984    igraph_int_t,
985    which_min = igraph_matrix_int_which_min,
986    which_max = igraph_matrix_int_which_max,
987    maxdifference = igraph_matrix_int_maxdifference,
988    all_l = igraph_matrix_int_all_l,
989    all_g = igraph_matrix_int_all_g,
990    all_le = igraph_matrix_int_all_le,
991    all_ge = igraph_matrix_int_all_ge
992);
993impl_matrix_ordered!(
994    igraph_matrix_char_t,
995    std::ffi::c_char,
996    which_min = igraph_matrix_char_which_min,
997    which_max = igraph_matrix_char_which_max,
998    maxdifference = igraph_matrix_char_maxdifference,
999    all_l = igraph_matrix_char_all_l,
1000    all_g = igraph_matrix_char_all_g,
1001    all_le = igraph_matrix_char_all_le,
1002    all_ge = igraph_matrix_char_all_ge
1003);
1004
1005// ---------------------------------------------------------------------------
1006// Arithmetic on real matrices (igraph) and integer matrices (Rust, wrapping).
1007// ---------------------------------------------------------------------------
1008
1009impl igraph_matrix_t {
1010    /// The `n` × `n` identity matrix.
1011    pub fn identity(n: usize) -> Self {
1012        let mut m = Self::zeros(n, n);
1013        for i in 0..n {
1014            m[(i, i)] = 1.0;
1015        }
1016        m
1017    }
1018
1019    fn check_same_shape(&self, other: &Self) -> Result<()> {
1020        if self.shape() != other.shape() {
1021            return Err(Error::invalid(format!(
1022                "matrices of different shapes ({}x{} and {}x{})",
1023                self.nrow(),
1024                self.ncol(),
1025                other.nrow(),
1026                other.ncol()
1027            )));
1028        }
1029        Ok(())
1030    }
1031
1032    /// Element-wise `self += other`
1033    /// ([`igraph_matrix_add`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add)).
1034    ///
1035    /// # Errors
1036    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1037    pub fn add(&mut self, other: &Self) -> Result<()> {
1038        self.check_same_shape(other)?;
1039        crate::igraph_call!(igraph_matrix_add(self, other))
1040    }
1041
1042    /// Element-wise `self -= other`
1043    /// ([`igraph_matrix_sub`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_sub)).
1044    ///
1045    /// # Errors
1046    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1047    pub fn sub(&mut self, other: &Self) -> Result<()> {
1048        self.check_same_shape(other)?;
1049        crate::igraph_call!(igraph_matrix_sub(self, other))
1050    }
1051
1052    /// Element-wise (Hadamard) product `self[(i,j)] *= other[(i,j)]`
1053    /// ([`igraph_matrix_mul_elements`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_mul_elements)).
1054    ///
1055    /// # Errors
1056    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1057    pub fn mul_elements(&mut self, other: &Self) -> Result<()> {
1058        self.check_same_shape(other)?;
1059        crate::igraph_call!(igraph_matrix_mul_elements(self, other))
1060    }
1061
1062    /// Element-wise division `self[(i,j)] /= other[(i,j)]`
1063    /// ([`igraph_matrix_div_elements`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_div_elements)).
1064    ///
1065    /// # Errors
1066    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1067    pub fn div_elements(&mut self, other: &Self) -> Result<()> {
1068        self.check_same_shape(other)?;
1069        crate::igraph_call!(igraph_matrix_div_elements(self, other))
1070    }
1071
1072    /// Multiplies every element by `factor`
1073    /// ([`igraph_matrix_scale`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_scale)).
1074    pub fn scale(&mut self, factor: f64) {
1075        unsafe { igraph_matrix_scale(self, factor) }
1076    }
1077
1078    /// Adds `value` to every element
1079    /// ([`igraph_matrix_add_constant`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add_constant)).
1080    pub fn add_constant(&mut self, value: f64) {
1081        unsafe { igraph_matrix_add_constant(self, value) }
1082    }
1083
1084    /// Sum of all elements
1085    /// ([`igraph_matrix_sum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_sum)).
1086    pub fn sum(&self) -> f64 {
1087        unsafe { igraph_matrix_sum(self) }
1088    }
1089
1090    /// Product of all elements (one for an empty matrix)
1091    /// ([`igraph_matrix_prod`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_prod)).
1092    pub fn prod(&self) -> f64 {
1093        unsafe { igraph_matrix_prod(self) }
1094    }
1095
1096    /// The sum of each row
1097    /// ([`igraph_matrix_rowsum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_rowsum)).
1098    pub fn rowsums(&self) -> Vec<f64> {
1099        let mut res = Vector::new();
1100        crate::error::check(unsafe { igraph_matrix_rowsum(self, &mut res) })
1101            .expect("igraph failed to allocate a vector");
1102        res.into()
1103    }
1104
1105    /// The sum of each column
1106    /// ([`igraph_matrix_colsum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_colsum)).
1107    pub fn colsums(&self) -> Vec<f64> {
1108        let mut res = Vector::new();
1109        crate::error::check(unsafe { igraph_matrix_colsum(self, &mut res) })
1110            .expect("igraph failed to allocate a vector");
1111        res.into()
1112    }
1113
1114    /// Whether the shapes agree and all elements are equal up to the
1115    /// relative tolerance `eps`
1116    /// ([`igraph_matrix_all_almost_e`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_all_almost_e)).
1117    pub fn all_almost_e(&self, other: &Self, eps: f64) -> bool {
1118        self.shape() == other.shape() && unsafe { igraph_matrix_all_almost_e(self, other, eps) }
1119    }
1120
1121    /// Replaces the elements smaller in magnitude than the *absolute*
1122    /// tolerance `tol` by exact zeros; `tol = 0` uses igraph's default,
1123    /// `f64::EPSILON^(2/3)` (about `1e-10`)
1124    /// ([`igraph_matrix_zapsmall`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_zapsmall)).
1125    ///
1126    /// # Errors
1127    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if `tol < 0`.
1128    pub fn zapsmall(&mut self, tol: f64) -> Result<()> {
1129        crate::igraph_call!(igraph_matrix_zapsmall(self, tol))
1130    }
1131
1132    /// The matrix product `self · other` (a plain Rust triple loop; for
1133    /// BLAS-backed products see the linear algebra module).
1134    ///
1135    /// ```
1136    /// use igraph::prelude::*;
1137    /// let a = Matrix::from_rows(&[[1.0, 2.0], [3.0, 4.0]]).unwrap();
1138    /// let b = a.matmul(&Matrix::identity(2)).unwrap();
1139    /// assert_eq!(a, b);
1140    /// assert_eq!(a.matmul(&a).unwrap().to_rows(), vec![vec![7.0, 10.0], vec![15.0, 22.0]]);
1141    /// ```
1142    ///
1143    /// # Errors
1144    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if
1145    /// `self.ncol() != other.nrow()`.
1146    pub fn matmul(&self, other: &Self) -> Result<Self> {
1147        if self.ncol() != other.nrow() {
1148            return Err(Error::invalid(format!(
1149                "cannot multiply a {}x{} matrix by a {}x{} one",
1150                self.nrow(),
1151                self.ncol(),
1152                other.nrow(),
1153                other.ncol()
1154            )));
1155        }
1156        let mut res = Self::zeros(self.nrow(), other.ncol());
1157        for j in 0..other.ncol() {
1158            for k in 0..self.ncol() {
1159                let b = other[(k, j)];
1160                if b == 0.0 {
1161                    continue;
1162                }
1163                for i in 0..self.nrow() {
1164                    res[(i, j)] += self[(i, k)] * b;
1165                }
1166            }
1167        }
1168        Ok(res)
1169    }
1170
1171    /// The matrix-vector product `self · x`.
1172    ///
1173    /// # Errors
1174    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if `x.len() != ncol`.
1175    pub fn mul_vec(&self, x: &[f64]) -> Result<Vec<f64>> {
1176        if x.len() != self.ncol() {
1177            return Err(Error::invalid(
1178                "vector length does not match the number of columns",
1179            ));
1180        }
1181        let mut res = vec![0.0; self.nrow()];
1182        for (j, col) in self.columns().enumerate() {
1183            for (r, a) in res.iter_mut().zip(col) {
1184                *r += a * x[j];
1185            }
1186        }
1187        Ok(res)
1188    }
1189}
1190
1191impl igraph_matrix_int_t {
1192    fn zip_op(
1193        &mut self,
1194        other: &Self,
1195        f: impl Fn(igraph_int_t, igraph_int_t) -> igraph_int_t,
1196    ) -> Result<()> {
1197        if self.shape() != other.shape() {
1198            return Err(Error::invalid("matrices of different shapes"));
1199        }
1200        self.as_mut_slice()
1201            .iter_mut()
1202            .zip(other.as_slice())
1203            .for_each(|(a, &b)| *a = f(*a, b));
1204        Ok(())
1205    }
1206
1207    /// Element-wise `self += other`, wrapping on overflow (Rust counterpart
1208    /// of `igraph_matrix_int_add`).
1209    ///
1210    /// # Errors
1211    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1212    pub fn add(&mut self, other: &Self) -> Result<()> {
1213        self.zip_op(other, igraph_int_t::wrapping_add)
1214    }
1215
1216    /// Element-wise `self -= other`, wrapping on overflow (Rust counterpart
1217    /// of `igraph_matrix_int_sub`).
1218    ///
1219    /// # Errors
1220    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1221    pub fn sub(&mut self, other: &Self) -> Result<()> {
1222        self.zip_op(other, igraph_int_t::wrapping_sub)
1223    }
1224
1225    /// Element-wise product, wrapping on overflow (Rust counterpart of
1226    /// `igraph_matrix_int_mul_elements`).
1227    ///
1228    /// # Errors
1229    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1230    pub fn mul_elements(&mut self, other: &Self) -> Result<()> {
1231        self.zip_op(other, igraph_int_t::wrapping_mul)
1232    }
1233
1234    /// Multiplies every element by `factor`, wrapping on overflow (Rust
1235    /// counterpart of `igraph_matrix_int_scale`).
1236    pub fn scale(&mut self, factor: igraph_int_t) {
1237        self.as_mut_slice()
1238            .iter_mut()
1239            .for_each(|x| *x = x.wrapping_mul(factor));
1240    }
1241
1242    /// Adds `value` to every element, wrapping on overflow (Rust
1243    /// counterpart of `igraph_matrix_int_add_constant`).
1244    pub fn add_constant(&mut self, value: igraph_int_t) {
1245        self.as_mut_slice()
1246            .iter_mut()
1247            .for_each(|x| *x = x.wrapping_add(value));
1248    }
1249
1250    /// Element-wise integer division `self[(i,j)] /= other[(i,j)]`, rounding
1251    /// towards zero (Rust counterpart of `igraph_matrix_int_div_elements`,
1252    /// whose C version traps on a zero divisor).
1253    ///
1254    /// # Errors
1255    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if
1256    /// the shapes differ or some divisor is zero (nothing is modified then).
1257    pub fn div_elements(&mut self, other: &Self) -> Result<()> {
1258        if other.as_slice().contains(&0) {
1259            return Err(Error::invalid("integer division by zero"));
1260        }
1261        self.zip_op(other, igraph_int_t::wrapping_div)
1262    }
1263
1264    /// Product of all elements (one for an empty matrix), wrapping on
1265    /// overflow (Rust counterpart of `igraph_matrix_int_prod`).
1266    pub fn prod(&self) -> igraph_int_t {
1267        self.as_slice().iter().fold(1, |a, &b| a.wrapping_mul(b))
1268    }
1269
1270    /// Sum of all elements, wrapping on overflow (Rust counterpart of
1271    /// `igraph_matrix_int_sum`).
1272    pub fn sum(&self) -> igraph_int_t {
1273        self.as_slice().iter().fold(0, |a, &b| a.wrapping_add(b))
1274    }
1275
1276    /// The sum of each row (Rust counterpart of `igraph_matrix_int_rowsum`).
1277    pub fn rowsums(&self) -> Vec<igraph_int_t> {
1278        self.rows()
1279            .map(|r| r.iter().fold(0, |a: igraph_int_t, &b| a.wrapping_add(b)))
1280            .collect()
1281    }
1282
1283    /// The sum of each column (Rust counterpart of `igraph_matrix_int_colsum`).
1284    pub fn colsums(&self) -> Vec<igraph_int_t> {
1285        self.columns()
1286            .map(|c| c.iter().fold(0, |a: igraph_int_t, &b| a.wrapping_add(b)))
1287            .collect()
1288    }
1289}
1290
1291impl igraph_matrix_complex_t {
1292    fn check_same_shape(&self, other: &Self) -> Result<()> {
1293        if self.shape() != other.shape() {
1294            return Err(Error::invalid(format!(
1295                "matrices of different shapes ({}x{} and {}x{})",
1296                self.nrow(),
1297                self.ncol(),
1298                other.nrow(),
1299                other.ncol()
1300            )));
1301        }
1302        Ok(())
1303    }
1304
1305    /// Builds a complex matrix from the matrices of its real and imaginary
1306    /// parts ([`igraph_matrix_complex_create`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_create)).
1307    ///
1308    /// ```
1309    /// use igraph::prelude::*;
1310    /// let re = Matrix::from_rows(&[[1.0, 0.0], [0.0, 1.0]]).unwrap();
1311    /// let im = Matrix::from_rows(&[[0.0, -1.0], [1.0, 0.0]]).unwrap();
1312    /// let z = MatrixComplex::from_parts(&re, &im).unwrap();
1313    /// assert_eq!(z[(1, 0)].im(), 1.0);
1314    /// let (r, i) = z.realimag();
1315    /// assert_eq!((r, i), (re, im));
1316    /// ```
1317    ///
1318    /// # Errors
1319    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1320    pub fn from_parts(re: &Matrix, im: &Matrix) -> Result<Self> {
1321        re.check_same_shape(im)?;
1322        // `igraph_matrix_complex_create` *initializes* its output.
1323        let mut res = MaybeUninit::<Self>::uninit();
1324        crate::igraph_call!(igraph_matrix_complex_create(res.as_mut_ptr(), re, im))?;
1325        Ok(unsafe { res.assume_init() })
1326    }
1327
1328    /// Builds a complex matrix from the moduli `r` and arguments `theta`
1329    /// (radians) of its elements
1330    /// ([`igraph_matrix_complex_create_polar`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_create_polar)).
1331    ///
1332    /// # Errors
1333    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1334    pub fn from_polar(r: &Matrix, theta: &Matrix) -> Result<Self> {
1335        r.check_same_shape(theta)?;
1336        let mut res = MaybeUninit::<Self>::uninit();
1337        crate::igraph_call!(igraph_matrix_complex_create_polar(
1338            res.as_mut_ptr(),
1339            r,
1340            theta
1341        ))?;
1342        Ok(unsafe { res.assume_init() })
1343    }
1344
1345    /// The real parts
1346    /// ([`igraph_matrix_complex_real`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_real)).
1347    pub fn real(&self) -> Matrix {
1348        let mut res = Matrix::new();
1349        crate::error::check(unsafe { igraph_matrix_complex_real(self, &mut res) })
1350            .expect("igraph failed to allocate a matrix");
1351        res
1352    }
1353
1354    /// The imaginary parts
1355    /// ([`igraph_matrix_complex_imag`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_imag)).
1356    pub fn imag(&self) -> Matrix {
1357        let mut res = Matrix::new();
1358        crate::error::check(unsafe { igraph_matrix_complex_imag(self, &mut res) })
1359            .expect("igraph failed to allocate a matrix");
1360        res
1361    }
1362
1363    /// `(real parts, imaginary parts)` in one call
1364    /// ([`igraph_matrix_complex_realimag`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_realimag)).
1365    pub fn realimag(&self) -> (Matrix, Matrix) {
1366        let (mut re, mut im) = (Matrix::new(), Matrix::new());
1367        crate::error::check(unsafe { igraph_matrix_complex_realimag(self, &mut re, &mut im) })
1368            .expect("igraph failed to allocate a matrix");
1369        (re, im)
1370    }
1371
1372    /// Element-wise `self += other`
1373    /// ([`igraph_matrix_complex_add`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add)).
1374    ///
1375    /// # Errors
1376    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1377    pub fn add(&mut self, other: &Self) -> Result<()> {
1378        self.check_same_shape(other)?;
1379        crate::igraph_call!(igraph_matrix_complex_add(self, other))
1380    }
1381
1382    /// Element-wise `self -= other`
1383    /// ([`igraph_matrix_complex_sub`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_sub)).
1384    ///
1385    /// # Errors
1386    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1387    pub fn sub(&mut self, other: &Self) -> Result<()> {
1388        self.check_same_shape(other)?;
1389        crate::igraph_call!(igraph_matrix_complex_sub(self, other))
1390    }
1391
1392    /// Element-wise product
1393    /// ([`igraph_matrix_complex_mul_elements`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_mul_elements)).
1394    ///
1395    /// # Errors
1396    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1397    pub fn mul_elements(&mut self, other: &Self) -> Result<()> {
1398        self.check_same_shape(other)?;
1399        crate::igraph_call!(igraph_matrix_complex_mul_elements(self, other))
1400    }
1401
1402    /// Element-wise division
1403    /// ([`igraph_matrix_complex_div_elements`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_div_elements)).
1404    ///
1405    /// # Errors
1406    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1407    pub fn div_elements(&mut self, other: &Self) -> Result<()> {
1408        self.check_same_shape(other)?;
1409        crate::igraph_call!(igraph_matrix_complex_div_elements(self, other))
1410    }
1411
1412    /// Multiplies every element by `factor`
1413    /// ([`igraph_matrix_complex_scale`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_scale)).
1414    pub fn scale(&mut self, factor: igraph_complex_t) {
1415        unsafe { igraph_matrix_complex_scale(self, factor) }
1416    }
1417
1418    /// Adds `value` to every element
1419    /// ([`igraph_matrix_complex_add_constant`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add_constant)).
1420    pub fn add_constant(&mut self, value: igraph_complex_t) {
1421        unsafe { igraph_matrix_complex_add_constant(self, value) }
1422    }
1423
1424    /// Sum of all elements
1425    /// ([`igraph_matrix_complex_sum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_sum)).
1426    pub fn sum(&self) -> igraph_complex_t {
1427        unsafe { igraph_matrix_complex_sum(self) }
1428    }
1429
1430    /// Product of all elements (one for an empty matrix)
1431    /// ([`igraph_matrix_complex_prod`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_prod)).
1432    pub fn prod(&self) -> igraph_complex_t {
1433        unsafe { igraph_matrix_complex_prod(self) }
1434    }
1435
1436    /// The sum of each row
1437    /// ([`igraph_matrix_complex_rowsum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_rowsum)).
1438    pub fn rowsums(&self) -> Vec<igraph_complex_t> {
1439        let mut res = VectorComplex::new();
1440        crate::error::check(unsafe { igraph_matrix_complex_rowsum(self, &mut res) })
1441            .expect("igraph failed to allocate a vector");
1442        res.into()
1443    }
1444
1445    /// The sum of each column
1446    /// ([`igraph_matrix_complex_colsum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_colsum)).
1447    pub fn colsums(&self) -> Vec<igraph_complex_t> {
1448        let mut res = VectorComplex::new();
1449        crate::error::check(unsafe { igraph_matrix_complex_colsum(self, &mut res) })
1450            .expect("igraph failed to allocate a vector");
1451        res.into()
1452    }
1453
1454    /// Whether the shapes agree and all elements are equal up to the
1455    /// relative tolerance `eps`
1456    /// ([`igraph_matrix_complex_all_almost_e`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_all_almost_e)).
1457    pub fn all_almost_e(&self, other: &Self, eps: f64) -> bool {
1458        // igraph takes non-const pointers but only reads the matrices.
1459        self.shape() == other.shape()
1460            && unsafe {
1461                igraph_matrix_complex_all_almost_e(
1462                    (self as *const Self).cast_mut(),
1463                    (other as *const Self).cast_mut(),
1464                    eps,
1465                )
1466            }
1467    }
1468
1469    /// Replaces the real and imaginary parts smaller in magnitude than the
1470    /// absolute tolerance `tol` by exact zeros (`tol = 0`: igraph's default,
1471    /// about `1e-10`)
1472    /// ([`igraph_matrix_complex_zapsmall`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_zapsmall)).
1473    ///
1474    /// # Errors
1475    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if `tol < 0`.
1476    pub fn zapsmall(&mut self, tol: f64) -> Result<()> {
1477        crate::igraph_call!(igraph_matrix_complex_zapsmall(self, tol))
1478    }
1479}
1480
1481impl igraph_matrix_bool_t {
1482    /// Number of `true` elements.
1483    pub fn count_true(&self) -> usize {
1484        self.as_slice().iter().filter(|&&b| b).count()
1485    }
1486}
1487
1488impl From<&igraph_matrix_int_t> for igraph_matrix_t {
1489    /// Converts an integer matrix to a real one.
1490    fn from(m: &igraph_matrix_int_t) -> Self {
1491        let mut res = Self::zeros(m.nrow(), m.ncol());
1492        for (r, &x) in res.as_mut_slice().iter_mut().zip(m.as_slice()) {
1493            *r = x as f64;
1494        }
1495        res
1496    }
1497}