use std::fmt;
use super::error::FiniteFieldError;
use super::field::{FieldElement, FiniteField, NmodPoly};
use crate::flint::ffi;
const MAX_ENTRIES: usize = 1 << 32;
struct NmodMat {
inner: ffi::NmodMatStruct,
}
unsafe impl Send for NmodMat {}
unsafe impl Sync for NmodMat {}
impl NmodMat {
fn new(rows: usize, cols: usize, p: u64) -> Self {
let mut inner: ffi::NmodMatStruct = unsafe { std::mem::zeroed() };
crate::flint::note_thread_uses_flint();
unsafe { ffi::nmod_mat_init(&mut inner, rows as ffi::slong, cols as ffi::slong, p) };
Self { inner }
}
fn rows(&self) -> usize {
self.inner.r as usize
}
fn cols(&self) -> usize {
self.inner.c as usize
}
fn get(&self, i: usize, j: usize) -> u64 {
debug_assert!(i < self.rows() && j < self.cols());
unsafe { ffi::nmod_mat_get_entry(&self.inner, i as ffi::slong, j as ffi::slong) }
}
fn set(&mut self, i: usize, j: usize, v: u64) {
debug_assert!(i < self.rows() && j < self.cols());
unsafe { ffi::nmod_mat_set_entry(&mut self.inner, i as ffi::slong, j as ffi::slong, v) };
}
}
impl Drop for NmodMat {
fn drop(&mut self) {
unsafe { ffi::nmod_mat_clear(&mut self.inner) };
}
}
struct FqMat {
inner: ffi::FqNmodMatStruct,
field: FiniteField,
}
unsafe impl Send for FqMat {}
unsafe impl Sync for FqMat {}
impl FqMat {
fn new(rows: usize, cols: usize, field: &FiniteField) -> Self {
let mut inner: ffi::FqNmodMatStruct = unsafe { std::mem::zeroed() };
crate::flint::note_thread_uses_flint();
unsafe {
ffi::fq_nmod_mat_init(
&mut inner,
rows as ffi::slong,
cols as ffi::slong,
field.ctx_ptr(),
)
};
Self {
inner,
field: field.clone(),
}
}
fn rows(&self) -> usize {
self.inner.r as usize
}
fn cols(&self) -> usize {
self.inner.c as usize
}
fn ctx(&self) -> *const ffi::FqNmodCtxBuf {
self.field.ctx_ptr()
}
fn get(&self, i: usize, j: usize) -> FieldElement {
debug_assert!(i < self.rows() && j < self.cols());
let k = self.field.degree();
unsafe {
let e = ffi::fq_nmod_mat_entry(&self.inner, i as ffi::slong, j as ffi::slong);
let coeffs: Vec<u64> = (0..k)
.map(|t| ffi::nmod_poly_get_coeff_ui(e, t as ffi::slong))
.collect();
self.field
.element(&coeffs)
.expect("k coefficients always fit a degree-k field")
}
}
fn set(&mut self, i: usize, j: usize, scratch: &mut NmodPoly, v: &FieldElement) {
debug_assert!(i < self.rows() && j < self.cols());
unsafe {
ffi::fq_nmod_zero(scratch.as_mut_ptr(), self.ctx());
for (t, &c) in v.coefficients().iter().enumerate() {
ffi::nmod_poly_set_coeff_ui(scratch.as_mut_ptr(), t as ffi::slong, c);
}
ffi::fq_nmod_mat_entry_set(
&mut self.inner,
i as ffi::slong,
j as ffi::slong,
scratch.as_ptr(),
self.ctx(),
);
}
}
}
impl Drop for FqMat {
fn drop(&mut self) {
unsafe { ffi::fq_nmod_mat_clear(&mut self.inner, self.field.ctx_ptr()) };
}
}
enum Repr {
Prime(NmodMat),
Ext(FqMat),
}
pub struct GfMatrix {
field: FiniteField,
repr: Repr,
}
pub struct Rref {
pub matrix: GfMatrix,
pub transform: GfMatrix,
pub rank: usize,
pub pivots: Vec<usize>,
}
fn check_shape(rows: usize, cols: usize) -> Result<(), FiniteFieldError> {
let too_big = rows > i64::MAX as usize
|| cols > i64::MAX as usize
|| rows
.checked_mul(cols)
.map(|n| n > MAX_ENTRIES)
.unwrap_or(true);
if too_big {
return Err(FiniteFieldError::DimensionTooLarge { rows, cols });
}
Ok(())
}
fn check_alloc(field: &FiniteField, shapes: &[(usize, usize)]) -> Result<(), FiniteFieldError> {
let per_entry: u64 = if field.is_prime_field() {
8
} else {
48 + 8 * field.degree() as u64
};
crate::budget::clear_trip();
let mut bytes: u64 = 0;
for &(rows, cols) in shapes {
check_shape(rows, cols)?;
let entries = rows as u64 * cols as u64;
bytes = bytes
.saturating_add(entries.saturating_mul(per_entry))
.saturating_add(rows as u64 * 8);
}
if let Err(trip) = crate::budget::preflight_bytes(bytes) {
crate::budget::record_trip(trip);
let (rows, cols) = shapes
.iter()
.copied()
.max_by_key(|&(r, c)| r as u128 * c as u128)
.unwrap_or((0, 0));
return Err(FiniteFieldError::DimensionTooLarge { rows, cols });
}
Ok(())
}
impl GfMatrix {
fn alloc(field: &FiniteField, rows: usize, cols: usize) -> Result<Self, FiniteFieldError> {
check_alloc(field, &[(rows, cols)])?;
Ok(Self::zeros_unchecked(field, rows, cols))
}
pub fn zeros(field: &FiniteField, rows: usize, cols: usize) -> Result<Self, FiniteFieldError> {
Self::alloc(field, rows, cols)
}
fn zeros_unchecked(field: &FiniteField, rows: usize, cols: usize) -> Self {
let repr = if field.is_prime_field() {
Repr::Prime(NmodMat::new(rows, cols, field.characteristic()))
} else {
Repr::Ext(FqMat::new(rows, cols, field))
};
Self {
field: field.clone(),
repr,
}
}
pub fn identity(field: &FiniteField, n: usize) -> Result<Self, FiniteFieldError> {
let mut m = Self::zeros(field, n, n)?;
let one = field.one();
let mut scratch = NmodPoly::new(field.characteristic());
for i in 0..n {
m.put(i, i, &one, &mut scratch);
}
Ok(m)
}
pub fn from_u64(
field: &FiniteField,
rows: usize,
cols: usize,
entries: &[u64],
) -> Result<Self, FiniteFieldError> {
check_alloc(field, &[(rows, cols)])?;
if entries.len() != rows * cols {
return Err(FiniteFieldError::DimensionMismatch {
op: "fill",
lhs: (rows, cols),
rhs: (entries.len(), 1),
});
}
let mut m = Self::zeros_unchecked(field, rows, cols);
let mut scratch = NmodPoly::new(field.characteristic());
for i in 0..rows {
for j in 0..cols {
let e = field.scalar(entries[i * cols + j]);
m.put(i, j, &e, &mut scratch);
}
}
Ok(m)
}
pub fn from_elements(
field: &FiniteField,
rows: usize,
cols: usize,
entries: &[FieldElement],
) -> Result<Self, FiniteFieldError> {
check_alloc(field, &[(rows, cols)])?;
if entries.len() != rows * cols {
return Err(FiniteFieldError::DimensionMismatch {
op: "fill",
lhs: (rows, cols),
rhs: (entries.len(), 1),
});
}
for e in entries {
if e.coefficients().len() > field.degree() {
return Err(FiniteFieldError::MalformedElement {
got: e.coefficients().len(),
degree: field.degree(),
});
}
}
let mut m = Self::zeros_unchecked(field, rows, cols);
let mut scratch = NmodPoly::new(field.characteristic());
for i in 0..rows {
for j in 0..cols {
m.put(i, j, &entries[i * cols + j], &mut scratch);
}
}
Ok(m)
}
pub fn field(&self) -> &FiniteField {
&self.field
}
pub fn nrows(&self) -> usize {
match &self.repr {
Repr::Prime(m) => m.rows(),
Repr::Ext(m) => m.rows(),
}
}
pub fn ncols(&self) -> usize {
match &self.repr {
Repr::Prime(m) => m.cols(),
Repr::Ext(m) => m.cols(),
}
}
pub fn shape(&self) -> (usize, usize) {
(self.nrows(), self.ncols())
}
pub fn entry(&self, i: usize, j: usize) -> Result<FieldElement, FiniteFieldError> {
self.check_index(i, j)?;
Ok(self.get(i, j))
}
pub fn set_entry(
&mut self,
i: usize,
j: usize,
value: &FieldElement,
) -> Result<(), FiniteFieldError> {
self.check_index(i, j)?;
if value.coefficients().len() > self.field.degree() {
return Err(FiniteFieldError::MalformedElement {
got: value.coefficients().len(),
degree: self.field.degree(),
});
}
let mut scratch = NmodPoly::new(self.field.characteristic());
self.put(i, j, value, &mut scratch);
Ok(())
}
pub fn to_elements(&self) -> Vec<FieldElement> {
let (r, c) = self.shape();
let mut out = Vec::with_capacity(r * c);
for i in 0..r {
for j in 0..c {
out.push(self.get(i, j));
}
}
out
}
pub fn to_u64(&self) -> Option<Vec<u64>> {
self.to_elements()
.iter()
.map(FieldElement::as_u64)
.collect()
}
pub fn is_zero(&self) -> bool {
match &self.repr {
Repr::Prime(m) => (0..m.rows()).all(|i| (0..m.cols()).all(|j| m.get(i, j) == 0)),
Repr::Ext(m) => (0..m.rows()).all(|i| (0..m.cols()).all(|j| m.get(i, j).is_zero())),
}
}
fn check_index(&self, i: usize, j: usize) -> Result<(), FiniteFieldError> {
let (rows, cols) = self.shape();
if i >= rows || j >= cols {
return Err(FiniteFieldError::IndexOutOfBounds { i, j, rows, cols });
}
Ok(())
}
fn get(&self, i: usize, j: usize) -> FieldElement {
match &self.repr {
Repr::Prime(m) => self.field.scalar(m.get(i, j)),
Repr::Ext(m) => m.get(i, j),
}
}
fn put(&mut self, i: usize, j: usize, v: &FieldElement, scratch: &mut NmodPoly) {
match &mut self.repr {
Repr::Prime(m) => m.set(i, j, v.as_u64().unwrap_or(0)),
Repr::Ext(m) => m.set(i, j, scratch, v),
}
}
fn prime_mut(&mut self) -> &mut NmodMat {
match &mut self.repr {
Repr::Prime(m) => m,
Repr::Ext(_) => unreachable!("prime_mut() on an extension-field matrix"),
}
}
fn ext_mut(&mut self) -> &mut FqMat {
match &mut self.repr {
Repr::Ext(m) => m,
Repr::Prime(_) => unreachable!("ext_mut() on a prime-field matrix"),
}
}
fn same_field(&self, other: &Self) -> Result<(), FiniteFieldError> {
if self.field != other.field {
return Err(FiniteFieldError::FieldMismatch {
lhs: self.field.to_string(),
rhs: other.field.to_string(),
});
}
Ok(())
}
fn require_square(&self, op: &'static str) -> Result<usize, FiniteFieldError> {
let (r, c) = self.shape();
if r != c {
return Err(FiniteFieldError::NotSquare { op, shape: (r, c) });
}
Ok(r)
}
pub fn add(&self, other: &Self) -> Result<Self, FiniteFieldError> {
self.same_field(other)?;
if self.shape() != other.shape() {
return Err(FiniteFieldError::DimensionMismatch {
op: "add",
lhs: self.shape(),
rhs: other.shape(),
});
}
let (r, c) = self.shape();
let mut out = Self::alloc(&self.field, r, c)?;
match (&self.repr, &other.repr) {
(Repr::Prime(a), Repr::Prime(b)) => unsafe {
ffi::nmod_mat_add(&mut out.prime_mut().inner, &a.inner, &b.inner)
},
(Repr::Ext(a), Repr::Ext(b)) => unsafe {
ffi::fq_nmod_mat_add(
&mut out.ext_mut().inner,
&a.inner,
&b.inner,
self.field.ctx_ptr(),
)
},
_ => unreachable!("equal fields imply equal representations"),
}
Ok(out)
}
pub fn sub(&self, other: &Self) -> Result<Self, FiniteFieldError> {
self.same_field(other)?;
if self.shape() != other.shape() {
return Err(FiniteFieldError::DimensionMismatch {
op: "subtract",
lhs: self.shape(),
rhs: other.shape(),
});
}
let (r, c) = self.shape();
let mut out = Self::alloc(&self.field, r, c)?;
match (&self.repr, &other.repr) {
(Repr::Prime(a), Repr::Prime(b)) => unsafe {
ffi::nmod_mat_sub(&mut out.prime_mut().inner, &a.inner, &b.inner)
},
(Repr::Ext(a), Repr::Ext(b)) => unsafe {
ffi::fq_nmod_mat_sub(
&mut out.ext_mut().inner,
&a.inner,
&b.inner,
self.field.ctx_ptr(),
)
},
_ => unreachable!("equal fields imply equal representations"),
}
Ok(out)
}
pub fn neg(&self) -> Self {
let (r, c) = self.shape();
let mut out = Self::zeros_unchecked(&self.field, r, c);
match &self.repr {
Repr::Prime(a) => unsafe { ffi::nmod_mat_neg(&mut out.prime_mut().inner, &a.inner) },
Repr::Ext(a) => unsafe {
ffi::fq_nmod_mat_neg(&mut out.ext_mut().inner, &a.inner, self.field.ctx_ptr())
},
}
out
}
pub fn scalar_mul(&self, c: &FieldElement) -> Result<Self, FiniteFieldError> {
if c.coefficients().len() > self.field.degree() {
return Err(FiniteFieldError::MalformedElement {
got: c.coefficients().len(),
degree: self.field.degree(),
});
}
let (r, cols) = self.shape();
let mut out = Self::alloc(&self.field, r, cols)?;
match &self.repr {
Repr::Prime(a) => {
unsafe {
ffi::nmod_mat_scalar_mul(
&mut out.prime_mut().inner,
&a.inner,
c.as_u64().unwrap_or(0),
)
}
}
Repr::Ext(a) => {
let ctx = self.field.ctx_ptr();
let p = self.field.characteristic();
let mut coeff = NmodPoly::new(p);
let mut acc = NmodPoly::new(p);
let mut scratch = NmodPoly::new(p);
unsafe {
ffi::fq_nmod_zero(coeff.as_mut_ptr(), ctx);
for (t, &v) in c.coefficients().iter().enumerate() {
ffi::nmod_poly_set_coeff_ui(coeff.as_mut_ptr(), t as ffi::slong, v);
}
}
for i in 0..r {
for j in 0..cols {
let e = unsafe {
ffi::fq_nmod_mat_entry(&a.inner, i as ffi::slong, j as ffi::slong)
};
unsafe {
ffi::fq_nmod_mul(acc.as_mut_ptr(), e, coeff.as_ptr(), ctx);
}
let v = self
.field
.element(&acc.to_coefficients())
.expect("a reduced product has degree < k");
out.put(i, j, &v, &mut scratch);
}
}
}
}
Ok(out)
}
pub fn mul(&self, other: &Self) -> Result<Self, FiniteFieldError> {
self.same_field(other)?;
if self.ncols() != other.nrows() {
return Err(FiniteFieldError::DimensionMismatch {
op: "multiply",
lhs: self.shape(),
rhs: other.shape(),
});
}
let mut out = Self::alloc(&self.field, self.nrows(), other.ncols())?;
match (&self.repr, &other.repr) {
(Repr::Prime(a), Repr::Prime(b)) => unsafe {
ffi::nmod_mat_mul(&mut out.prime_mut().inner, &a.inner, &b.inner)
},
(Repr::Ext(a), Repr::Ext(b)) => unsafe {
ffi::fq_nmod_mat_mul(
&mut out.ext_mut().inner,
&a.inner,
&b.inner,
self.field.ctx_ptr(),
)
},
_ => unreachable!("equal fields imply equal representations"),
}
Ok(out)
}
pub fn transpose(&self) -> Self {
let (r, c) = self.shape();
let mut out = Self::zeros_unchecked(&self.field, c, r);
match &self.repr {
Repr::Prime(a) => unsafe {
ffi::nmod_mat_transpose(&mut out.prime_mut().inner, &a.inner)
},
Repr::Ext(a) => unsafe {
ffi::fq_nmod_mat_transpose(&mut out.ext_mut().inner, &a.inner, self.field.ctx_ptr())
},
}
out
}
pub fn equals(&self, other: &Self) -> bool {
if self.field != other.field || self.shape() != other.shape() {
return false;
}
match (&self.repr, &other.repr) {
(Repr::Prime(a), Repr::Prime(b)) => unsafe {
ffi::nmod_mat_equal(&a.inner, &b.inner) != 0
},
(Repr::Ext(a), Repr::Ext(b)) => unsafe {
ffi::fq_nmod_mat_equal(&a.inner, &b.inner, self.field.ctx_ptr()) != 0
},
_ => false,
}
}
pub fn rank(&self) -> usize {
match &self.repr {
Repr::Prime(a) => unsafe { ffi::nmod_mat_rank(&a.inner).max(0) as usize },
Repr::Ext(a) => unsafe {
ffi::fq_nmod_mat_rank(&a.inner, self.field.ctx_ptr()).max(0) as usize
},
}
}
pub fn rref(&self) -> Result<Rref, FiniteFieldError> {
let (m, n) = self.shape();
let wide = n
.checked_add(m)
.ok_or(FiniteFieldError::DimensionTooLarge { rows: m, cols: n })?;
check_alloc(&self.field, &[(m, wide), (m, wide), (m, n), (m, m)])?;
let mut aug = Self::zeros_unchecked(&self.field, m, n + m);
let mut scratch = NmodPoly::new(self.field.characteristic());
let one = self.field.one();
for i in 0..m {
for j in 0..n {
let v = self.get(i, j);
aug.put(i, j, &v, &mut scratch);
}
aug.put(i, n + i, &one, &mut scratch);
}
let reduced = match &mut aug.repr {
Repr::Prime(a) => {
unsafe { ffi::nmod_mat_rref(&mut a.inner) };
None
}
Repr::Ext(a) => {
let mut dst = FqMat::new(m, n + m, &self.field);
unsafe { ffi::fq_nmod_mat_rref(&mut dst.inner, &a.inner, self.field.ctx_ptr()) };
Some(dst)
}
};
if let Some(dst) = reduced {
aug.repr = Repr::Ext(dst);
}
let mut r_mat = Self::zeros_unchecked(&self.field, m, n);
let mut u_mat = Self::zeros_unchecked(&self.field, m, m);
for i in 0..m {
for j in 0..n {
let v = aug.get(i, j);
r_mat.put(i, j, &v, &mut scratch);
}
for j in 0..m {
let v = aug.get(i, n + j);
u_mat.put(i, j, &v, &mut scratch);
}
}
let mut pivots = Vec::new();
for i in 0..m {
if let Some(j) = (0..n).find(|&j| !r_mat.get(i, j).is_zero()) {
pivots.push(j);
}
}
let rank = pivots.len();
Ok(Rref {
matrix: r_mat,
transform: u_mat,
rank,
pivots,
})
}
pub fn nullspace(&self) -> Result<Self, FiniteFieldError> {
let (_, n) = self.shape();
check_alloc(&self.field, &[(n, n), (n, n)])?;
if n == 0 {
return Self::zeros(&self.field, 0, 0);
}
let mut basis = Self::zeros_unchecked(&self.field, n, n);
let nullity = match (&self.repr, &mut basis.repr) {
(Repr::Prime(a), Repr::Prime(x)) => unsafe {
ffi::nmod_mat_nullspace(&mut x.inner, &a.inner)
},
(Repr::Ext(a), Repr::Ext(x)) => unsafe {
ffi::fq_nmod_mat_nullspace(&mut x.inner, &a.inner, self.field.ctx_ptr())
},
_ => unreachable!("one matrix, one representation"),
};
let d = nullity.max(0) as usize;
let mut out = Self::zeros_unchecked(&self.field, n, d);
let mut scratch = NmodPoly::new(self.field.characteristic());
for i in 0..n {
for j in 0..d {
let v = basis.get(i, j);
out.put(i, j, &v, &mut scratch);
}
}
Ok(out)
}
pub fn solve(&self, rhs: &Self) -> Result<Self, FiniteFieldError> {
self.same_field(rhs)?;
if self.nrows() != rhs.nrows() {
return Err(FiniteFieldError::DimensionMismatch {
op: "solve",
lhs: self.shape(),
rhs: rhs.shape(),
});
}
let wide = self.ncols().saturating_add(rhs.ncols());
check_alloc(
&self.field,
&[(self.ncols(), rhs.ncols()), (self.nrows(), wide)],
)?;
let mut out = Self::zeros_unchecked(&self.field, self.ncols(), rhs.ncols());
let ok = match (&self.repr, &rhs.repr, &mut out.repr) {
(Repr::Prime(a), Repr::Prime(b), Repr::Prime(x)) => unsafe {
ffi::nmod_mat_can_solve(&mut x.inner, &a.inner, &b.inner)
},
(Repr::Ext(a), Repr::Ext(b), Repr::Ext(x)) => unsafe {
ffi::fq_nmod_mat_can_solve(&mut x.inner, &a.inner, &b.inner, self.field.ctx_ptr())
},
_ => unreachable!("equal fields imply equal representations"),
};
if ok == 0 {
return Err(FiniteFieldError::Inconsistent);
}
Ok(out)
}
pub fn inverse(&self) -> Result<Self, FiniteFieldError> {
let n = self.require_square("inverse")?;
check_alloc(&self.field, &[(n, n), (n, n)])?;
let mut out = Self::zeros_unchecked(&self.field, n, n);
let ok = match (&self.repr, &mut out.repr) {
(Repr::Prime(a), Repr::Prime(b)) => unsafe {
ffi::nmod_mat_inv(&mut b.inner, &a.inner)
},
(Repr::Ext(a), Repr::Ext(b)) => unsafe {
ffi::fq_nmod_mat_inv(&mut b.inner, &a.inner, self.field.ctx_ptr())
},
_ => unreachable!("one matrix, one representation"),
};
if ok == 0 {
return Err(FiniteFieldError::Singular { shape: (n, n) });
}
Ok(out)
}
pub fn determinant(&self) -> Result<FieldElement, FiniteFieldError> {
let n = self.require_square("determinant")?;
match &self.repr {
Repr::Prime(a) => Ok(self.field.scalar(unsafe { ffi::nmod_mat_det(&a.inner) })),
Repr::Ext(_) => {
let cp = self.charpoly()?;
let c0 = cp.first().cloned().unwrap_or_else(|| self.field.zero());
Ok(if n % 2 == 0 {
c0
} else {
self.negate_elem(&c0)
})
}
}
}
pub fn charpoly(&self) -> Result<Vec<FieldElement>, FiniteFieldError> {
self.require_square("charpoly")?;
match &self.repr {
Repr::Prime(a) => {
let mut poly = NmodPoly::new(self.field.characteristic());
unsafe { ffi::nmod_mat_charpoly(poly.as_mut_ptr(), &a.inner) };
Ok(poly
.to_coefficients()
.into_iter()
.map(|c| self.field.scalar(c))
.collect())
}
Repr::Ext(a) => {
let ctx = self.field.ctx_ptr();
let k = self.field.degree();
let mut buf = ffi::FqNmodPolyBuf([0u8; 128]);
let mut coeff = NmodPoly::new(self.field.characteristic());
crate::flint::note_thread_uses_flint();
let out = unsafe {
ffi::fq_nmod_poly_init(&mut buf, ctx);
ffi::fq_nmod_mat_charpoly(&mut buf, &a.inner, ctx);
let len = ffi::fq_nmod_poly_length(&buf, ctx).max(0);
let mut v = Vec::with_capacity(len as usize);
for t in 0..len {
ffi::fq_nmod_poly_get_coeff(coeff.as_mut_ptr(), &buf, t, ctx);
let coeffs: Vec<u64> = (0..k)
.map(|s| ffi::nmod_poly_get_coeff_ui(coeff.as_ptr(), s as ffi::slong))
.collect();
v.push(
self.field
.element(&coeffs)
.expect("a reduced element has degree < k"),
);
}
ffi::fq_nmod_poly_clear(&mut buf, ctx);
v
};
Ok(out)
}
}
}
fn negate_elem(&self, e: &FieldElement) -> FieldElement {
let p = self.field.characteristic();
let v: Vec<u64> = e
.coefficients()
.iter()
.map(|&c| if c == 0 { 0 } else { p - c })
.collect();
self.field
.element(&v)
.expect("negation preserves the coefficient count")
}
}
impl Clone for GfMatrix {
fn clone(&self) -> Self {
let (r, c) = self.shape();
let mut out = Self::zeros_unchecked(&self.field, r, c);
match (&self.repr, &mut out.repr) {
(Repr::Prime(a), Repr::Prime(b)) => unsafe {
ffi::nmod_mat_set(&mut b.inner, &a.inner)
},
(Repr::Ext(a), Repr::Ext(b)) => unsafe {
ffi::fq_nmod_mat_set(&mut b.inner, &a.inner, self.field.ctx_ptr())
},
_ => unreachable!("one matrix, one representation"),
}
out
}
}
impl fmt::Debug for GfMatrix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (r, c) = self.shape();
writeln!(f, "GfMatrix {r}x{c} over {}", self.field)?;
for i in 0..r {
let row: Vec<String> = (0..c).map(|j| self.field.render(&self.get(i, j))).collect();
writeln!(f, " [{}]", row.join(", "))?;
}
Ok(())
}
}