Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/integer/mat_z.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@

use flint_sys::fmpz_mat::fmpz_mat_struct;

mod cmp;
mod from;
mod get;
mod mul;
mod ownership;
mod set;
mod to_string;
mod transpose;

/// [`MatZ`] is a matrix with entries of type [`Z`](crate::integer::Z).
///
Expand Down
105 changes: 105 additions & 0 deletions src/integer/mat_z/cmp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright © 2023 Niklas Siemer
//
// This file is part of qFALL-math.
//
// qFALL-math is free software: you can redistribute it and/or modify it under
// the terms of the Mozilla Public License Version 2.0 as published by the
// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.

//! This module contains implementations for comparison of [`MatZ`].

use super::MatZ;
use flint_sys::fmpz_mat::fmpz_mat_equal;

impl PartialEq for MatZ {
/// Checks if two [`MatZ`] instances are equal. Used by the `==` and `!=` operators.
///
/// Parameters:
/// - `other`: the other value that is compare against `self`
///
/// Returns `true` if the elements are equal, otherwise `false`.
///
/// # Example
/// ```
/// use math::integer::MatZ;
/// use std::str::FromStr;
///
/// let a = MatZ::from_str("[[1,2],[3,4]]").unwrap();
/// let b = MatZ::from_str("[[1,2],[2,4]]").unwrap();
///
/// // These are all equivalent and return false.
/// let compared: bool = (a == b);
/// # assert!(!compared);
/// let compared: bool = (&a == &b);
/// # assert!(!compared);
/// let compared: bool = (a.eq(&b));
/// # assert!(!compared);
/// let compared: bool = (MatZ::eq(&a,&b));
/// # assert!(!compared);
/// ```
fn eq(&self, other: &Self) -> bool {
unsafe { fmpz_mat_equal(&self.matrix, &other.matrix) != 0 }
}
}

// With the [`Eq`] trait, `a == a` is always true.
// This is not guaranteed by the [`PartialEq`] trait.
impl Eq for MatZ {}

/// Test that the [`PartialEq`] trait is correctly implemented.
#[cfg(test)]
mod test_partial_eq {

use super::MatZ;
use std::str::FromStr;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the tests are all 2x2 matrices, please also add one for different sizes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now one 3x3 test was included

/// Ensures that different instantiations do not break the equality between matrices
#[test]
fn equality_between_instantiations() {
let a = MatZ::from_str("[[0,1],[0,0]]").unwrap();
let mut b = MatZ::new(2, 2).unwrap();
b.set_entry(0, 1, 1).unwrap();

assert_eq!(a, b);
}

/// Checks that large and small entries (and different points in storage) do not break equality
#[test]
fn equality_for_large_and_small_entries() {
let a = MatZ::from_str(&format!(
"[[{},{}, 1],[-10, 10, 0],[0, 1, -10]]",
i64::MIN,
i64::MAX
))
.unwrap();
let b = MatZ::from_str(&format!(
"[[{},{}, 1],[-10, 10, 0],[0, 1, -10]]",
i64::MIN,
i64::MAX
))
.unwrap();

assert_eq!(&a, &b);
}

/// Checks that different unequal matrices are unequal
#[test]
fn not_equal() {
let a = MatZ::from_str(&format!("[[{},{}],[-10, 10]]", i64::MIN, i64::MAX)).unwrap();
let b = MatZ::from_str(&format!("[[0,{}],[-10, 10]]", i64::MAX)).unwrap();
let c = MatZ::from_str(&format!("[[{},{}],[-10, 10],[0,0]]", i64::MIN, i64::MAX)).unwrap();
let d = MatZ::from_str(&format!("[[{},{}]]", i64::MIN, i64::MAX)).unwrap();
let e = MatZ::from_str("[[0]]").unwrap();

assert_ne!(&a, &b);
assert_ne!(&a, &c);
assert_ne!(&a, &d);
assert_ne!(&a, &e);
assert_ne!(&b, &c);
assert_ne!(&b, &d);
assert_ne!(&b, &e);
assert_ne!(&c, &d);
assert_ne!(&c, &e);
assert_ne!(&d, &e);
}
}
115 changes: 115 additions & 0 deletions src/integer/mat_z/mul.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright © 2023 Niklas Siemer
//
// This file is part of qFALL-math.
//
// qFALL-math is free software: you can redistribute it and/or modify it under
// the terms of the Mozilla Public License Version 2.0 as published by the
// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.

//! Implementation of the [`Mul`] trait for [`MatZ`] values.

use super::MatZ;
use crate::{
macros::arithmetics::{
arithmetic_trait_borrowed_to_owned, arithmetic_trait_mixed_borrowed_owned,
},
traits::{GetNumColumns, GetNumRows},
};
use flint_sys::fmpz_mat::fmpz_mat_mul;
use std::ops::Mul;

impl Mul for &MatZ {
type Output = MatZ;

/// Implements the [`Mul`] trait for two [`MatZ`] values.
///
/// [`Mul`] is implemented for any combination of owned and borrowed [`MatZ`].
///
/// Parameters:
/// - `other`: specifies the value to multiply with `self`
///
/// Returns the product of `self` and `other` as a [`MatZ`].
///
/// # Example
/// ```
/// use math::integer::MatZ;
/// use std::str::FromStr;
///
/// let a = MatZ::from_str("[[2,1],[1,2]]").unwrap();
/// let b = MatZ::from_str("[[1,0],[0,1]]").unwrap();
///
/// let c = &a * &b;
/// let d = a * b;
/// let e = &c * d;
/// let f = c * &e;
/// ```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Panics is missing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

///
/// # Errors and Failures
/// - Panics if the dimensions of `self` and `other` do not match for multiplication.
fn mul(self, other: Self) -> Self::Output {
// TODO: mul_safe
if self.get_num_columns() != other.get_num_rows() {
panic!("Matrix dimensions do not match for matrix multiplication!");
}

let mut new = MatZ::new(self.get_num_rows(), other.get_num_columns()).unwrap();
unsafe { fmpz_mat_mul(&mut new.matrix, &self.matrix, &other.matrix) };
new
}
}

arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZ);
arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZ);

#[cfg(test)]
mod test_mul {

use super::MatZ;
use crate::integer::Z;
use std::str::FromStr;

/// Checks if matrix multiplication works fine for sqaured matrices
#[test]
fn square_correctness() {
let mat_1 = MatZ::from_str("[[2,1],[1,2]]").unwrap();
let mat_2 = MatZ::from_str("[[1,0],[0,1]]").unwrap();
let mat_3 = MatZ::from_str("[[1,2],[2,1]]").unwrap();
let cmp = MatZ::from_str("[[4,5],[5,4]]").unwrap();

assert_eq!(mat_1, &mat_1 * &mat_2);
assert_eq!(cmp, &mat_1 * &mat_3);
}

/// Checks if matrix multiplication works fine for matrices of different dimensions
#[test]
fn different_dimensions_correctness() {
let mat = MatZ::from_str("[[2,1],[1,2]]").unwrap();
let vec = MatZ::from_str("[[1],[0]]").unwrap();
let cmp = MatZ::from_str("[[2],[1]]").unwrap();

assert_eq!(cmp, &mat * &vec);
}

/// Checks if matrix multiplication works fine for large entries
#[test]
fn large_entries() {
let mat = MatZ::from_str(&format!("[[{},1],[0,2]]", i64::MAX)).unwrap();
let vec = MatZ::from_str(&format!("[[{}],[0]]", i64::MAX)).unwrap();
let mut cmp = MatZ::new(2, 1).unwrap();
let max: Z = i64::MAX.into();
cmp.set_entry_ref_z(0, 0, &(&max * &max)).unwrap();

assert_eq!(cmp, mat * vec);
}

/// Checks if matrix multiplication with incompatible matrix dimensions
/// results in panic
#[test]
#[should_panic]
fn incompatible_dimensions() {
let mat_1 = MatZ::from_str("[[2,1],[1,2]]").unwrap();
let mat_2 = MatZ::from_str("[[1,0],[0,1],[0,0]]").unwrap();

let _ = mat_1 * mat_2;
}
}
69 changes: 69 additions & 0 deletions src/integer/mat_z/transpose.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright © 2023 Niklas Siemer
//
// This file is part of qFALL-math.
//
// qFALL-math is free software: you can redistribute it and/or modify it under
// the terms of the Mozilla Public License Version 2.0 as published by the
// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.

//! This module containts the implementation of the `transpose` function.

use crate::traits::{GetNumColumns, GetNumRows};

use super::MatZ;
use flint_sys::fmpz_mat::fmpz_mat_transpose;

impl MatZ {
/// Returns the transposed form of the given matrix, i.e. rows get transformed to columns
/// and vice versa.
///
/// # Example
/// ```
/// use math::integer::MatZ;
/// use std::str::FromStr;
///
/// let mat = MatZ::from_str("[[2,1],[2,1],[2,1]]").unwrap();
/// let cmp = MatZ::from_str("[[2,2,2],[1,1,1]]").unwrap();
///
/// assert_eq!(mat.transpose(), cmp);
/// ```
pub fn transpose(&self) -> Self {
let mut out = Self::new(self.get_num_columns(), self.get_num_rows()).unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better use "expect" with a simple message that the number of rows or columns was saved wrongly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we know that this can not fail, it's ok to use unwrap

unsafe { fmpz_mat_transpose(&mut out.matrix, &self.matrix) };
out
}
}

#[cfg(test)]
mod test_transpose {

use super::MatZ;
use std::str::FromStr;

/// Checks if a row is correctly converted to a column
#[test]
fn row_to_column() {
let mat = MatZ::from_str("[[1],[2],[3]]").unwrap();
let cmp = MatZ::from_str("[[1,2,3]]").unwrap();

assert_eq!(cmp, mat.transpose());
}

/// Checks if a column is correctly converted to a row
#[test]
fn column_to_row() {
let mat = MatZ::from_str("[[1,2,3]]").unwrap();
let cmp = MatZ::from_str("[[1],[2],[3]]").unwrap();

assert_eq!(cmp, mat.transpose());
}

/// Checks if large, negative, and zero values are transposed correctly
#[test]
fn different_entry_values() {
let mat = MatZ::from_str(&format!("[[{},{},0]]", i64::MAX, i64::MIN)).unwrap();
let cmp = MatZ::from_str(&format!("[[{}],[{}],[0]]", i64::MAX, i64::MIN)).unwrap();

assert_eq!(cmp, mat.transpose());
}
}
2 changes: 1 addition & 1 deletion src/utils/dimensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// the terms of the Mozilla Public License Version 2.0 as published by the
// Mozilla Foundation. See <https://mozilla.org/en-US/MPL/2.0/>.

//! Implements methods for finding matrix dimensions.
//! Implements methods for finding matrix dimensions and enums for detecting vector directions.

use crate::error::MathError;

Expand Down
6 changes: 4 additions & 2 deletions src/utils/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@

//! Implements methods to parse a [`String`] e.g. matrix strings.

use std::fmt::Display;

use crate::{
error::MathError,
traits::{GetEntry, GetNumColumns, GetNumRows},
};
use regex::Regex;
use std::fmt::Display;
use string_builder::Builder;

/// Takes the string of a matrix as input and parses it for easy use.
Expand Down Expand Up @@ -116,6 +115,7 @@ pub(crate) fn matrix_to_string<T: GetEntry<impl Display> + GetNumRows + GetNumCo

#[cfg(test)]
mod test_parse_matrix_string {

use crate::utils::parse::parse_matrix_string;

// Ensure that correct strings of a matrix are accepted.
Expand All @@ -125,11 +125,13 @@ mod test_parse_matrix_string {
let matrix_string2 = String::from("[[1/3, -2/7, 3],[3, 4, -5/-2]]");
let matrix_string3 = String::from("[[4 0 1 2 3, 2 0 1],[1 5, 2 7 8]]");
let matrix_string4 = String::from("[[sdclin, =§&%, +57n4],[+dk<, 37 ffew, 8fh2n]]");
let matrix_string5 = String::from("[[0],[1]]");

assert!(parse_matrix_string(&matrix_string1).is_ok());
assert!(parse_matrix_string(&matrix_string2).is_ok());
assert!(parse_matrix_string(&matrix_string3).is_ok());
assert!(parse_matrix_string(&matrix_string4).is_ok());
assert!(parse_matrix_string(&matrix_string5).is_ok());
}

// Ensure that incorrect strings of a matrix are rejected.
Expand Down