diff --git a/src/integer/mat_z.rs b/src/integer/mat_z.rs index 3caaf4149..2d54ced7f 100644 --- a/src/integer/mat_z.rs +++ b/src/integer/mat_z.rs @@ -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). /// diff --git a/src/integer/mat_z/cmp.rs b/src/integer/mat_z/cmp.rs new file mode 100644 index 000000000..a1898060e --- /dev/null +++ b/src/integer/mat_z/cmp.rs @@ -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 . + +//! 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; + + /// 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); + } +} diff --git a/src/integer/mat_z/mul.rs b/src/integer/mat_z/mul.rs new file mode 100644 index 000000000..2d32920f3 --- /dev/null +++ b/src/integer/mat_z/mul.rs @@ -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 . + +//! 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; + /// ``` + /// + /// # 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; + } +} diff --git a/src/integer/mat_z/transpose.rs b/src/integer/mat_z/transpose.rs new file mode 100644 index 000000000..4c0305948 --- /dev/null +++ b/src/integer/mat_z/transpose.rs @@ -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 . + +//! 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(); + 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()); + } +} diff --git a/src/utils/dimensions.rs b/src/utils/dimensions.rs index 02689a8bd..9795d12de 100644 --- a/src/utils/dimensions.rs +++ b/src/utils/dimensions.rs @@ -6,7 +6,7 @@ // the terms of the Mozilla Public License Version 2.0 as published by the // Mozilla Foundation. See . -//! Implements methods for finding matrix dimensions. +//! Implements methods for finding matrix dimensions and enums for detecting vector directions. use crate::error::MathError; diff --git a/src/utils/parse.rs b/src/utils/parse.rs index f6e4a2073..b8b9a75a2 100644 --- a/src/utils/parse.rs +++ b/src/utils/parse.rs @@ -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. @@ -116,6 +115,7 @@ pub(crate) fn matrix_to_string + 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. @@ -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.