From 6f5fa12367b14a099218c9fdf831872a2018e66d Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 17:20:30 +0200 Subject: [PATCH 01/15] Add macros for cross type arithmetics --- src/macros/arithmetics.rs | 77 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/macros/arithmetics.rs b/src/macros/arithmetics.rs index 2a448fbb9..3f884b2f3 100644 --- a/src/macros/arithmetics.rs +++ b/src/macros/arithmetics.rs @@ -1,4 +1,4 @@ -// Copyright © 2023 Phil Milewski +// Copyright © 2023 Phil Milewski, Niklas Siemer // // This file is part of qFALL-math. // @@ -78,3 +78,78 @@ macro_rules! arithmetic_trait_mixed_borrowed_owned { } pub(crate) use arithmetic_trait_mixed_borrowed_owned; + +/// Implements the [`*trait*`] for [`*type*<*other_type*>`] using the +/// [`*trait<*other_type*>*`] for [`&*type*`]. +/// +/// Parameters: +/// - `trait`: the trait that is implemented (e.g. [`Add`], [`Sub`], ...). +/// - `trait_function`: the function the trait implements +/// (e.g. add for [`Add`], ...). +/// - `type`: the type the trait is implemented for (e.g. [`Z`], [`Q`]) +/// - `other_type`: the other type that the arithm. operation is applied with +/// - `out_type`: the type that the `Output` should have +/// +/// Returns the owned Implementation code for the [`*trait*`] +/// trait with the signature: +/// +/// ```impl *trait*<*other_type*> for *type*``` +macro_rules! arithmetic_trait_cross_types_borrowed_to_owned { + ($trait:ident, $trait_function:ident, $type:ident, $other_type:ident, $out_type:ident) => { + impl $trait<$other_type> for $type { + type Output = $out_type; + + paste::paste! { + #[doc = "Documentation at [`" $type "::" $trait_function "`]."] + fn $trait_function(self, other: $other_type) -> Self::Output { + (&self).$trait_function(&other) + } + } + } + }; +} + +pub(crate) use arithmetic_trait_cross_types_borrowed_to_owned; + +/// Implements the [`*trait*<*other_type*>`] for owned [`*type*`] on borrowed [`*type*`] and +/// reverse using the [`*trait*<*other_type*>`] for [`&*type*`]. +/// +/// Parameters: +/// - `trait`: the trait that is implemented (e.g. [`Add`], [`Sub`], ...). +/// - `trait_function`: the function the trait implements +/// (e.g. add for [`Add`], ...). +/// - `type`: the type the trait is implemented for (e.g. [`Z`], [`Q`], ...). +/// - `other_type`: the other type that the arithm. operation is applied with +/// - `out_type`: the type that the `Output` should have +/// +/// Returns the mixed owned and borrowed Implementation code for the +/// [`*trait*`] trait with the signatures: +/// +/// ```impl *trait*<&*other_type*> for *type*``` +/// +/// ```impl *trait*<*other_type*> for &*type*``` +macro_rules! arithmetic_trait_cross_types_mixed_borrowed_owned { + ($trait:ident, $trait_function:ident, $type:ident, $other_type:ident, $out_type:ident) => { + impl $trait<$other_type> for &$type { + type Output = $out_type; + paste::paste! { + #[doc = "Documentation at [`" $type "::" $trait_function "`]."] + fn $trait_function(self, other: $other_type) -> Self::Output { + self.$trait_function(&other) + } + } + } + + impl $trait<&$other_type> for $type { + type Output = $out_type; + paste::paste! { + #[doc = "Documentation at [`" $type "::" $trait_function "`]."] + fn $trait_function(self, other: &$other_type) -> Self::Output { + (&self).$trait_function(other) + } + } + } + }; +} + +pub(crate) use arithmetic_trait_cross_types_mixed_borrowed_owned; From 439aab0a903a5af8373429eae1c8846f829de8d1 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 17:25:34 +0200 Subject: [PATCH 02/15] Implement multiplication for MatZ --- src/integer/mat_z.rs | 3 ++ src/integer/mat_z/mul.rs | 59 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/integer/mat_z/mul.rs diff --git a/src/integer/mat_z.rs b/src/integer/mat_z.rs index 5a89b4b37..c7b08eed3 100644 --- a/src/integer/mat_z.rs +++ b/src/integer/mat_z.rs @@ -11,10 +11,13 @@ use flint_sys::fmpz_mat::fmpz_mat_struct; +mod cmp; mod from; mod get; +mod mul; mod ownership; mod set; +mod transpose; /// [`MatZ`] is a matrix with entries of type [`Z`](crate::integer::Z). /// diff --git a/src/integer/mat_z/mul.rs b/src/integer/mat_z/mul.rs new file mode 100644 index 000000000..97382a0be --- /dev/null +++ b/src/integer/mat_z/mul.rs @@ -0,0 +1,59 @@ +// 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::{ + integer::VecZ, + macros::arithmetics::{ + arithmetic_trait_borrowed_to_owned, arithmetic_trait_cross_types_borrowed_to_owned, + arithmetic_trait_cross_types_mixed_borrowed_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`] and any kind of [`VecZ`]. + /// + /// 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; + /// ``` + fn mul(self, other: Self) -> Self::Output { + 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); From ffb150bc3728b66558ffbe293ef7af50084d8b11 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 17:27:23 +0200 Subject: [PATCH 03/15] Implement PartialEq for MatZ --- src/integer/mat_z/cmp.rs | 95 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/integer/mat_z/cmp.rs diff --git a/src/integer/mat_z/cmp.rs b/src/integer/mat_z/cmp.rs new file mode 100644 index 000000000..df5f9ee67 --- /dev/null +++ b/src/integer/mat_z/cmp.rs @@ -0,0 +1,95 @@ +// 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!("[[{},{}],[-10, 10]]", i64::MIN, i64::MAX)).unwrap(); + let b = MatZ::from_str(&format!("[[{},{}],[-10, 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); + } +} From 39d5cdc1526622fd99fd7352ce394dc9066c24a0 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 17:27:57 +0200 Subject: [PATCH 04/15] Implement transpose for MatZ --- src/integer/mat_z/transpose.rs | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/integer/mat_z/transpose.rs diff --git a/src/integer/mat_z/transpose.rs b/src/integer/mat_z/transpose.rs new file mode 100644 index 000000000..1916b2f63 --- /dev/null +++ b/src/integer/mat_z/transpose.rs @@ -0,0 +1,59 @@ +// 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. + /// + /// # 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()); + } +} From 3d7fd86777f3d01ca9c5e74edf2320c69b14bbab Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 18:05:39 +0200 Subject: [PATCH 05/15] Add VecZ --- src/integer.rs | 2 ++ src/integer/vec_z.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/integer/vec_z.rs diff --git a/src/integer.rs b/src/integer.rs index e3c23dae6..d62d5a46f 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -12,9 +12,11 @@ mod mat_poly_over_z; mod mat_z; mod poly_over_z; +mod vec_z; mod z; pub use mat_poly_over_z::MatPolyOverZ; pub use mat_z::MatZ; pub use poly_over_z::PolyOverZ; +pub use vec_z::VecZ; pub use z::Z; diff --git a/src/integer/vec_z.rs b/src/integer/vec_z.rs new file mode 100644 index 000000000..0ed16f706 --- /dev/null +++ b/src/integer/vec_z.rs @@ -0,0 +1,29 @@ +// 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 . + +//! [`VecZ`] is a vector with integer entries of arbitrary length. +//! This implementation uses the [FLINT](https://flintlib.org/) library. + +mod from; +mod get; +mod mul; +mod set; +mod transpose; + +use super::MatZ; + +/// [`VecZ`] is a vector with entries of type [`Z`](crate::integer::Z). +/// +/// Attributes: +/// - `matrix`: holds a [`MatZ`] instance with either #rows or #columns being `1` +/// +/// # Examples +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct VecZ { + pub(crate) matrix: MatZ, +} From 8281d0a0b6616936f0dd3e4ef007dc143fe43cc1 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 18:06:39 +0200 Subject: [PATCH 06/15] Implement MatZ * VecZ + tests --- src/integer/mat_z/mul.rs | 106 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/src/integer/mat_z/mul.rs b/src/integer/mat_z/mul.rs index 97382a0be..31395965c 100644 --- a/src/integer/mat_z/mul.rs +++ b/src/integer/mat_z/mul.rs @@ -57,3 +57,109 @@ impl Mul for &MatZ { arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZ); arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZ); + +impl Mul<&VecZ> for &MatZ { + type Output = MatZ; + + /// Implements the [`Mul`] trait for a [`MatZ`] multiplied with a [`VecZ`] vector. + /// [`Mul`] is implemented for any combination of owned and borrowed [`MatZ`] and any kind of [`VecZ`]. + /// + /// Parameters: + /// - `other`: specifies the [`VecZ`] value to multiply with `self` + /// + /// Returns the product of `self` and `other` as a [`MatZ`]. + /// + /// # Example + /// ``` + /// use math::integer::{MatZ, VecZ}; + /// use std::str::FromStr; + /// + /// let a = MatZ::from_str("[[2,1],[1,2]]").unwrap(); + /// let b = VecZ::from_str("[1,0]").unwrap(); + /// + /// let _ = &a * &b; + /// let _ = a.clone() * b.clone(); + /// let _ = &a * b.clone(); + /// let _ = a.clone() * &b; + /// ``` + fn mul(self, other: &VecZ) -> Self::Output { + self.mul(&other.matrix) + } +} + +arithmetic_trait_cross_types_borrowed_to_owned!(Mul, mul, MatZ, VecZ, MatZ); +arithmetic_trait_cross_types_mixed_borrowed_owned!(Mul, mul, MatZ, VecZ, MatZ); + +#[cfg(test)] +mod test_mul { + + use super::{MatZ, VecZ}; + use crate::integer::Z; + use std::str::FromStr; + + /// Checks if matrix multiplication works fine for sqaured matrices + #[test] + fn matrix_mul_matrix_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 matrix_mul_matrix_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 matrix_mul_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 matrix_mul_matrix_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; + } + + /// Checks if cross-type multiplications works fine for [`MatZ`] * [`VecZ`] + #[test] + fn matrix_mul_vector_correctness() { + let mat = MatZ::from_str("[[2,1],[1,2]]").unwrap(); + let vec = VecZ::from_str("[1,0]").unwrap(); + let cmp = MatZ::from_str("[[2],[1]]").unwrap(); + + assert_eq!(cmp, &mat * &vec); + } + + /// Checks if multiplication of [`MatZ`] and [`VecZ`] with incompatible dimensions + /// results in panic + #[test] + #[should_panic] + fn matrix_mul_vector_incompatible_dimensions() { + let mat_1 = MatZ::from_str("[[2,1],[1,2]]").unwrap(); + let mat_2 = VecZ::from_str("[1,0,1]").unwrap(); + + let _ = mat_1 * mat_2; + } +} From e59492fc91fcb6be0cb439ad81515d0e357e6675 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 18:09:09 +0200 Subject: [PATCH 07/15] Add instantiation methods for VecZ --- src/integer/vec_z/from.rs | 124 ++++++++++++++++++++++++++++++++++++++ src/integer/vec_z/set.rs | 97 +++++++++++++++++++++++++++++ src/utils/parse.rs | 92 +++++++++++++++++++++++++++- 3 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 src/integer/vec_z/from.rs create mode 100644 src/integer/vec_z/set.rs diff --git a/src/integer/vec_z/from.rs b/src/integer/vec_z/from.rs new file mode 100644 index 000000000..8acf9ac98 --- /dev/null +++ b/src/integer/vec_z/from.rs @@ -0,0 +1,124 @@ +// 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 . + +//! Implementations to create a [`VecZ`](crate::integer::VecZ) vector from other types. +//! For each reasonable type, an explicit function with the format +//! `from_` and the [`From`] trait should be implemented. +//! Furthermore, an instantiation of a zero matrix is implemented. +//! +//! The explicit functions contain the documentation. + +use super::{MatZ, VecZ}; +use crate::error::MathError; +use crate::integer::Z; +use crate::utils::parse::parse_vector_string; +use std::{fmt::Display, str::FromStr}; + +impl VecZ { + /// Creates a new vector with `num_rows` rows and + /// zeros as entries. + /// + /// Parameters: + /// - `num_rows`: number of columns the new vector should have + /// + /// Returns a [`VecZ`] or an error, if the number of rows is + /// less or equal to `0`. + /// + /// # Example + /// ``` + /// use math::integer::VecZ; + /// + /// let vector = VecZ::new(5).unwrap(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type + /// [`InvalidMatrix`](MathError::InvalidMatrix) + /// if the number of rows is `0`. + /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) + /// if the number of rows is negative or it does not fit into an [`i64`]. + pub fn new(num_rows: impl TryInto + Display + Copy) -> Result { + Ok(VecZ { + matrix: MatZ::new(num_rows, 1)?, + }) + } +} + +impl FromStr for VecZ { + type Err = MathError; + + /// Creates a [`VecZ`] vector with entries in [`Z`] from a [`String`]. + /// The format of that string looks like this `[1,2,3]` for a row vector + /// with three entries (`1` in the first row, `2` in the second one, ...) + /// + /// Parameters: + /// - `string`: the vector representation as a string + /// + /// Returns a [`VecZ`] or an error, if the vector is not formatted in a suitable way, + /// the number of rows is too big (must fit into [`i64`]), or if the regular expression + /// inside of the function could not be processed. + /// + /// # Example + /// ``` + /// use math::integer::VecZ; + /// use std::str::FromStr; + /// + /// let string = String::from("[1, 2, 3]"); + /// let matrix = VecZ::from_str(&string).unwrap(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`InvalidMatrix`](MathError::InvalidMatrix) + /// if the vector is not formatted in a suitable way, + /// the number of rows is too big (must fit into [`i64`]). + /// - Returns a [`MathError`] of type + /// [`InvalidStringToCStringInput`](MathError::InvalidStringToCStringInput) + /// if an entry contains a Nul byte. + /// - Returns a [`MathError`] of type + /// [`InvalidStringToZInput`](MathError::InvalidStringToZInput) + /// if an entry is not formatted correctly. + fn from_str(string: &str) -> Result { + let entries = parse_vector_string(string)?; + let num_rows = entries.len(); + let mut vector = VecZ::new(num_rows)?; + + // fill entries of matrix according to entries in string_matrix + for (row_num, entry) in entries.iter().enumerate() { + let z_entry = Z::from_str(entry)?; + vector.set_entry(row_num, z_entry)?; + } + Ok(vector) + } +} + +#[cfg(test)] +mod test_new { + use crate::integer::{VecZ, Z}; + + /// Ensure that entries of a new vector are `0`. + #[test] + fn entry_zero() { + let matrix = VecZ::new(2).unwrap(); + + let entry1 = matrix.get_entry(0).unwrap(); + let entry2 = matrix.get_entry(0).unwrap(); + + assert_eq!(Z::from_i64(0), entry1); + assert_eq!(Z::from_i64(0), entry2); + } + + /// Ensure that a new zero vector fails with `0` or a negative value as input. + #[test] + fn error_zero_negative() { + let matrix1 = VecZ::new(0); + let matrix2 = VecZ::new(-1); + + assert!(matrix1.is_err()); + assert!(matrix2.is_err()); + } +} diff --git a/src/integer/vec_z/set.rs b/src/integer/vec_z/set.rs new file mode 100644 index 000000000..f61137653 --- /dev/null +++ b/src/integer/vec_z/set.rs @@ -0,0 +1,97 @@ +// 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 to set entries from a [`VecZ`] vector. + +use super::VecZ; +use crate::{error::MathError, integer::Z}; +use std::fmt::Display; + +impl VecZ { + /// Sets the value of a specific vector entry according to a given `value` of type [`Z`](crate::integer::Z). + /// + /// Parameters: + /// - `row`: specifies the row in which the entry is located + /// - `value`: specifies the value to which the entry is set + /// + /// # Example + /// ``` + /// use math::integer::VecZ; + /// use math::integer::Z; + /// + /// let mut vector = VecZ::new(5).unwrap(); + /// let value = Z::from_i64(5); + /// vector.set_entry(1, value).unwrap(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) + /// if the given row is greater than the matrix or negative. + pub fn set_entry + Display + Copy, T: Into>( + &mut self, + row: S, + value: T, + ) -> Result<(), MathError> { + self.set_entry_ref_z(row, &value.into()) + } + + /// Sets the value of a specific matrix entry according to a given `value` of type [`Z`]. + /// + /// Parameters: + /// - `row`: specifies the row in which the entry is located + /// - `value`: specifies the value to which the entry is set + /// + /// # Example + /// ``` + /// use math::integer::VecZ; + /// use math::integer::Z; + /// + /// let mut vector = VecZ::new(5).unwrap(); + /// let value = Z::from_i64(5); + /// vector.set_entry_ref_z(1, &value).unwrap(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) + /// if the given row is greater than the matrix or negative. + pub fn set_entry_ref_z + Display + Copy>( + &mut self, + row: S, + value: &Z, + ) -> Result<(), MathError> { + self.matrix.set_entry_ref_z(row, 0, value)?; + Ok(()) + } +} + +#[cfg(test)] +mod test_setter { + use super::Z; + use crate::integer::VecZ; + + /// Ensure that setting the correct entries works fine + #[test] + fn correct_entry_set() { + let mut vector = VecZ::new(5).unwrap(); + vector.set_entry(4, 869).unwrap(); + + let entry = vector.get_entry(4).unwrap(); + + assert_eq!(entry, Z::from_i64(869)); + } + + /// Ensure that a wrong given row returns a [`MathError`](crate::error::MathError) + #[test] + fn error_wrong_row() { + let mut vector = VecZ::new(5).unwrap(); + let value = Z::from_i64(i64::MAX); + + assert!(vector.set_entry_ref_z(5, &value).is_err()); + assert!(vector.set_entry_ref_z(-1, &value).is_err()); + } +} diff --git a/src/utils/parse.rs b/src/utils/parse.rs index 77f782a62..c00a908fd 100644 --- a/src/utils/parse.rs +++ b/src/utils/parse.rs @@ -1,4 +1,4 @@ -// Copyright © 2023 Marcel Luca Schmidt +// Copyright © 2023 Marcel Luca Schmidt, Niklas Siemer // // This file is part of qFALL-math. // @@ -67,6 +67,35 @@ pub(crate) fn parse_matrix_string(string: &str) -> Result>, Math Ok(matrix) } +pub(crate) fn parse_vector_string(string: &str) -> Result, MathError> { + // check if the matrix format is correct + let entry_str = r"([^\[\],]+)"; + let row_str = format!(r"^\[({},)*({})\]$", entry_str, entry_str); + let regex = Regex::new(&row_str).expect("The regular expression could not be processed."); + + // explanation of this regex: + // it checks whether the string start with a '[' and ends with a ']' + // we differ between the first/several and the last entry the vector (as there is no comma after the last entry) + // each entry can contain any symbol but `[`, `]` and `,`. It needs to have at least one symbol. + if !regex.is_match(string) { + return Err(MathError::InvalidMatrix( + "The matrix is not formatted in a suitable way.".to_owned(), + )); + } + + // delete `[` in front and `]` in the end and split the string into its entries + let entries = string[1..string.len() - 1].split(','); + + let mut vector: Vec = Vec::new(); + for entry in entries { + // delete leading and trailing whitespaces from the entry and + // adds it to the row vector + vector.push(entry.trim().to_owned()); + } + + Ok(vector) +} + #[cfg(test)] mod test_parse_matrix_string { use crate::utils::parse::parse_matrix_string; @@ -125,3 +154,64 @@ mod test_parse_matrix_string { ); } } + +#[cfg(test)] +mod test_parse_vector_string { + use crate::utils::parse::parse_vector_string; + + // Ensure that correct strings of a vector are accepted. + #[test] + fn correct_vector_work() { + let vector_string1 = String::from("[1, 2, 3]"); + let vector_string2 = String::from("[1/3, -2/7, 3]"); + let vector_string3 = String::from("[4 0 1 2 3, 2 0 1]"); + let vector_string4 = String::from("[sdclin, =§&%, +57n4]"); + + assert!(parse_vector_string(&vector_string1).is_ok()); + assert!(parse_vector_string(&vector_string2).is_ok()); + assert!(parse_vector_string(&vector_string3).is_ok()); + assert!(parse_vector_string(&vector_string4).is_ok()); + } + + // Ensure that incorrect strings of a vector are rejected. + #[test] + fn incorrect_entries_error() { + let vector_string1 = String::from("[1, 2, 3],"); + let vector_string2 = String::from("[1/3, -2/7, 3,[3, 4, -5/-2]]"); + let vector_string3 = String::from("[1, [2], 3],[3, 4, 5]"); + let vector_string4 = String::from("[1, 2, 3][3, 4, 5]"); + let vector_string5 = String::from("[[1, 2, 3]]"); + + assert!(parse_vector_string(&vector_string1).is_err()); + assert!(parse_vector_string(&vector_string2).is_err()); + assert!(parse_vector_string(&vector_string3).is_err()); + assert!(parse_vector_string(&vector_string4).is_err()); + assert!(parse_vector_string(&vector_string5).is_err()); + } + + // Ensure that correct strings of a vector are prepared correctly. + #[test] + fn correct_vector_format() { + let vector_string1 = String::from("[1, 2, 3]"); + let vector_string2 = String::from("[1/3, -2/7, 3,3, 4, -5/-2]"); + let vector_string3 = String::from("[4 0 1 2 3, 2 0 1,1 5, 2 7 8]"); + let vector_string4 = String::from("[sdclin, +dk<, 37 ffew, 8fh2n]"); + + assert_eq!( + parse_vector_string(&vector_string1).unwrap()[0], + "1".to_owned() + ); + assert_eq!( + parse_vector_string(&vector_string2).unwrap()[1], + "-2/7".to_owned() + ); + assert_eq!( + parse_vector_string(&vector_string3).unwrap()[2], + "1 5".to_owned() + ); + assert_eq!( + parse_vector_string(&vector_string4).unwrap()[3], + "8fh2n".to_owned() + ); + } +} From 9f5aa9ef5ec5de55d08292fd581f3dfe47b224c3 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 18:09:54 +0200 Subject: [PATCH 08/15] Add getter and transpose to VecZ --- src/integer/vec_z/get.rs | 72 ++++++++++++++++++++++++++++++++++ src/integer/vec_z/transpose.rs | 45 +++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 src/integer/vec_z/get.rs create mode 100644 src/integer/vec_z/transpose.rs diff --git a/src/integer/vec_z/get.rs b/src/integer/vec_z/get.rs new file mode 100644 index 000000000..1c57285ba --- /dev/null +++ b/src/integer/vec_z/get.rs @@ -0,0 +1,72 @@ +// 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 . + +//! Implementations to get entries from a [`VecZ`] matrix. + +use super::VecZ; +use crate::{error::MathError, integer::Z}; +use std::fmt::Display; + +impl VecZ { + /// Outputs the [`Z`] value of a specific vector entry. + /// + /// Parameters: + /// - `row`: specifies the row in which the entry is located + /// + /// Returns the [`Z`] value of the vector at the position of the given + /// row or an error, if the given row is greater than the vector or negative. + /// + /// # Example + /// ``` + /// use math::integer::VecZ; + /// + /// let matrix = VecZ::new(5).unwrap(); + /// let entry = matrix.get_entry(1).unwrap(); + /// ``` + /// + /// # Errors and Failures + /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) + /// if the given row is greater than the matrix or negative. + pub fn get_entry + Display + Copy>(&self, row: S) -> Result { + self.matrix.get_entry(row, 0) + } +} + +#[cfg(test)] +mod test_get_entry { + + use super::VecZ; + use super::Z; + use std::str::FromStr; + + /// Ensure that getting the correct entries works fine + #[test] + fn correct_entry_fetched() { + let vector = VecZ::from_str(&format!("[0, 10, -10, {}, {}]", i64::MAX, i64::MIN)).unwrap(); + let zero: Z = 0.into(); + let ten: Z = 10.into(); + let minus_ten: Z = (-10).into(); + let max: Z = i64::MAX.into(); + let min: Z = i64::MIN.into(); + + assert_eq!(zero, vector.get_entry(0).unwrap()); + assert_eq!(ten, vector.get_entry(1).unwrap()); + assert_eq!(minus_ten, vector.get_entry(2).unwrap()); + assert_eq!(max, vector.get_entry(3).unwrap()); + assert_eq!(min, vector.get_entry(4).unwrap()); + } + + /// Ensure that a wrong number of rows yields an Error. + #[test] + fn error_out_bounds() { + let matrix = VecZ::new(5).unwrap(); + + assert!(matrix.get_entry(5).is_err()); + assert!(matrix.get_entry(-1).is_err()); + } +} diff --git a/src/integer/vec_z/transpose.rs b/src/integer/vec_z/transpose.rs new file mode 100644 index 000000000..1ddf67a71 --- /dev/null +++ b/src/integer/vec_z/transpose.rs @@ -0,0 +1,45 @@ +// 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 super::{MatZ, VecZ}; + +impl VecZ { + /// Returns the transposed form of the given matrix, i.e. rows get transformed to columns. + /// + /// # 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) -> MatZ { + self.matrix.transpose() + } +} + +#[cfg(test)] +mod test_transpose { + + use super::{MatZ, VecZ}; + use std::str::FromStr; + + // Checks if a vector is correctly converted to a one column matrix + #[test] + fn row_to_column() { + let mat = VecZ::from_str("[1,2,3]").unwrap(); + let cmp = MatZ::from_str("[[1,2,3]]").unwrap(); + + assert_eq!(cmp, mat.transpose()); + } +} From 58631ab6acac0a741f0999f8b8433e8e71bbdf16 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 18:10:41 +0200 Subject: [PATCH 09/15] Implement multiplication for VecZ in combination with MatZ --- src/integer/vec_z/mul.rs | 129 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/integer/vec_z/mul.rs diff --git a/src/integer/vec_z/mul.rs b/src/integer/vec_z/mul.rs new file mode 100644 index 000000000..8ab535bbc --- /dev/null +++ b/src/integer/vec_z/mul.rs @@ -0,0 +1,129 @@ +// 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 [`VecZ`] values. + +use super::{MatZ, VecZ}; +use crate::macros::arithmetics::{ + arithmetic_trait_cross_types_borrowed_to_owned, + arithmetic_trait_cross_types_mixed_borrowed_owned, +}; +use std::ops::Mul; + +impl Mul for &VecZ { + type Output = MatZ; + + /// Implements the [`Mul`] trait for two [`VecZ`] values. + /// [`Mul`] is implemented for any combination of owned and borrowed [`MatZ`] and any kind of [`VecZ`]. + /// + /// Parameters: + /// - `other`: specifies the value to multiply with `self` + /// + /// Returns the product of `self` and `other` as a [`MatZ`]. + /// + /// # Example + /// ``` + /// use math::integer::VecZ; + /// use std::str::FromStr; + /// + /// let a = VecZ::from_str("[2,1,1,2]").unwrap(); + /// let b = VecZ::from_str("[1,0,0,1]").unwrap(); + /// + /// let _ = &a.transpose() * &b; + /// let _ = a.transpose() * b.clone(); + /// let _ = &a.transpose() * b.clone(); + /// let _ = a.transpose() * &b; + /// ``` + fn mul(self, other: Self) -> Self::Output { + (&self.matrix).mul(other) + } +} + +arithmetic_trait_cross_types_borrowed_to_owned!(Mul, mul, VecZ, VecZ, MatZ); +arithmetic_trait_cross_types_mixed_borrowed_owned!(Mul, mul, VecZ, VecZ, MatZ); + +impl Mul<&MatZ> for &VecZ { + type Output = MatZ; + + /// Implements the [`Mul`] trait for a [`VecZ`] multiplied with a [`MatZ`] vector. + /// [`Mul`] is implemented for any combination of owned and borrowed [`MatZ`] and any kind of [`VecZ`]. + /// + /// Parameters: + /// - `other`: specifies the [`MatZ`] value to multiply with `self` + /// + /// Returns the product of `self` and `other` as a [`MatZ`]. + /// + /// # Example + /// ``` + /// use math::integer::{MatZ, VecZ}; + /// use std::str::FromStr; + /// + /// let a = VecZ::from_str("[1,0]").unwrap(); + /// let b = MatZ::from_str("[[2,1]]").unwrap(); + /// + /// let c = &a * &b; + /// let d = a.clone() * b.clone(); + /// let e = &a * b.clone(); + /// let f = a.clone() * &b; + /// ``` + fn mul(self, other: &MatZ) -> Self::Output { + (&self.matrix).mul(other) + } +} + +arithmetic_trait_cross_types_borrowed_to_owned!(Mul, mul, VecZ, MatZ, MatZ); +arithmetic_trait_cross_types_mixed_borrowed_owned!(Mul, mul, VecZ, MatZ, MatZ); + +#[cfg(test)] +mod test_mul { + + use super::{MatZ, VecZ}; + use std::str::FromStr; + + /// Checks if vector multiplication works fine for vectors of same length + #[test] + fn vector_mul_vector_correctness() { + let vec_1 = VecZ::from_str("[2,1,1,2]").unwrap(); + let vec_2 = VecZ::from_str("[1,2,2,1]").unwrap(); + let cmp = MatZ::from_str("[[8]]").unwrap(); + + assert_eq!(cmp, vec_1.transpose() * vec_2); + } + + /// Checks if vector multiplication with incompatible vector length + /// results in panic + #[test] + #[should_panic] + fn vector_mul_vector_incompatible_length() { + let mat_1 = MatZ::from_str("[2,1,1,2]").unwrap(); + let mat_2 = MatZ::from_str("[1,0,0,1,0]").unwrap(); + + let _ = mat_1 * mat_2; + } + + /// Checks if cross-type multiplications works fine for [`VecZ`] * [`MatZ`] + #[test] + fn vector_mul_matrix_correctness() { + let mat = MatZ::from_str("[[2,1]]").unwrap(); + let vec = VecZ::from_str("[1,0]").unwrap(); + let cmp = MatZ::from_str("[[2,1],[0,0]]").unwrap(); + + assert_eq!(cmp, &vec * &mat); + } + + /// Checks if multiplication of [`VecZ`] * [`MatZ`]` with incompatible dimensions + /// results in panic + #[test] + #[should_panic] + fn vector_mul_matrix_incompatible_dimensions() { + let mat = MatZ::from_str("[[2,1],[1,2]]").unwrap(); + let vec = VecZ::from_str("[1,0,1]").unwrap(); + + let _ = vec * mat; + } +} From ecdd67d982b4a0f0af3a8f3948fa35d7e5c70656 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 23:05:38 +0200 Subject: [PATCH 10/15] Apply requested PR changes and add from_str tests for VecZ --- src/integer/mat_z/mul.rs | 1 + src/integer/mat_z/transpose.rs | 14 ++++- src/integer/vec_z/from.rs | 106 ++++++++++++++++++++++++++++++++- src/integer/vec_z/transpose.rs | 6 +- 4 files changed, 121 insertions(+), 6 deletions(-) diff --git a/src/integer/mat_z/mul.rs b/src/integer/mat_z/mul.rs index 31395965c..dca4ab989 100644 --- a/src/integer/mat_z/mul.rs +++ b/src/integer/mat_z/mul.rs @@ -45,6 +45,7 @@ impl Mul for &MatZ { /// let f = c * &e; /// ``` 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!"); } diff --git a/src/integer/mat_z/transpose.rs b/src/integer/mat_z/transpose.rs index 1916b2f63..4c0305948 100644 --- a/src/integer/mat_z/transpose.rs +++ b/src/integer/mat_z/transpose.rs @@ -14,7 +14,8 @@ 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. + /// Returns the transposed form of the given matrix, i.e. rows get transformed to columns + /// and vice versa. /// /// # Example /// ``` @@ -39,7 +40,7 @@ mod test_transpose { use super::MatZ; use std::str::FromStr; - // Checks if a row is correctly converted to a column + /// Checks if a row is correctly converted to a column #[test] fn row_to_column() { let mat = MatZ::from_str("[[1],[2],[3]]").unwrap(); @@ -56,4 +57,13 @@ mod test_transpose { 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/integer/vec_z/from.rs b/src/integer/vec_z/from.rs index 8acf9ac98..e736dfbee 100644 --- a/src/integer/vec_z/from.rs +++ b/src/integer/vec_z/from.rs @@ -20,7 +20,7 @@ use crate::utils::parse::parse_vector_string; use std::{fmt::Display, str::FromStr}; impl VecZ { - /// Creates a new vector with `num_rows` rows and + /// Creates a new row-vector with `num_rows` rows and /// zeros as entries. /// /// Parameters: @@ -52,7 +52,7 @@ impl VecZ { impl FromStr for VecZ { type Err = MathError; - /// Creates a [`VecZ`] vector with entries in [`Z`] from a [`String`]. + /// Creates a [`VecZ`] row-vector with entries in [`Z`] from a [`String`]. /// The format of that string looks like this `[1,2,3]` for a row vector /// with three entries (`1` in the first row, `2` in the second one, ...) /// @@ -122,3 +122,105 @@ mod test_new { assert!(matrix2.is_err()); } } + +#[cfg(test)] +mod test_from_str { + use crate::integer::{VecZ, Z}; + use std::str::FromStr; + + /// Ensure that initialization works + #[test] + fn init_works() { + let vector_string = String::from("[1, 2, 3]"); + + assert_eq!( + Z::from_i64(1), + VecZ::from_str(&vector_string) + .unwrap() + .get_entry(0) + .unwrap() + ); + } + + /// Ensure that initialization with positive numbers that are larger than [`i64`] works. + #[test] + fn init_works_large_numbers() { + let vector_string = format!("[{}, 2, 3]", "1".repeat(65)); + + assert_eq!( + Z::from_str(&"1".repeat(65)).unwrap(), + VecZ::from_str(&vector_string) + .unwrap() + .get_entry(0) + .unwrap() + ); + } + + /// Ensure that initialization with negative numbers that are larger than [`i64`] works. + #[test] + fn init_works_small_numbers() { + let vector_string = format!("[-{}, 2, 3]", "1".repeat(65)); + + let entry = format!("-{}", "1".repeat(65)); + + assert_eq!( + Z::from_str(&entry).unwrap(), + VecZ::from_str(&vector_string) + .unwrap() + .get_entry(0) + .unwrap() + ); + } + + /// Ensure that entries can have leading and trailing whitespaces. + #[test] + fn whitespaces_in_entries_works() { + let vector_string = String::from("[ 1, 2 , 3 ]"); + + assert_eq!( + Z::from_i64(1), + VecZ::from_str(&vector_string) + .unwrap() + .get_entry(0) + .unwrap() + ); + } + + /// Ensure that entries are set correctly. + #[test] + fn entries_set_correctly() { + let vector_string = format!("[0,{},{}, -10, 10]", i64::MIN, i64::MAX); + + let vector = VecZ::from_str(&vector_string).unwrap(); + + assert_eq!(vector.get_entry(0).unwrap(), 0.into()); + assert_eq!(vector.get_entry(1).unwrap(), i64::MIN.into()); + assert_eq!(vector.get_entry(2).unwrap(), i64::MAX.into()); + assert_eq!(vector.get_entry(3).unwrap(), (-10).into()); + assert_eq!(vector.get_entry(4).unwrap(), 10.into()); + } + + /// Ensure that a wrong format causes an error. + #[test] + fn wrong_format_error() { + let vector_string1 = String::from("[1, 2, 3],"); + let vector_string2 = String::from("[[1, 2, 3]]"); + let vector_string3 = String::from("[[1, 2, 3],3, 4, 5]"); + let vector_string4 = String::from("[1, 2, 3, 4,, 5]"); + let vector_string5 = String::from("[[1, 2, 3],[3, 4, 5]]"); + let vector_string6 = String::from("[[1, 2, 38,]"); + let vector_string7 = String::from(""); + let vector_string8 = String::from("[]"); + let vector_string9 = String::from("[[]]"); + + assert!(VecZ::from_str(&vector_string1).is_err()); + assert!(VecZ::from_str(&vector_string2).is_err()); + assert!(VecZ::from_str(&vector_string3).is_err()); + assert!(VecZ::from_str(&vector_string4).is_err()); + assert!(VecZ::from_str(&vector_string5).is_err()); + assert!(VecZ::from_str(&vector_string6).is_err()); + assert!(VecZ::from_str(&vector_string7).is_err()); + assert!(VecZ::from_str(&vector_string8).is_err()); + assert!(VecZ::from_str(&vector_string9).is_err()); + } +} diff --git a/src/integer/vec_z/transpose.rs b/src/integer/vec_z/transpose.rs index 1ddf67a71..821ef8bfa 100644 --- a/src/integer/vec_z/transpose.rs +++ b/src/integer/vec_z/transpose.rs @@ -30,11 +30,13 @@ impl VecZ { #[cfg(test)] mod test_transpose { - + /// Further tests regarding large or small entries are omitted + /// as they are part of the tests of the here called function + /// [`MatZ::transpose`](crate::integer::MatZ::transpose) use super::{MatZ, VecZ}; use std::str::FromStr; - // Checks if a vector is correctly converted to a one column matrix + /// Checks if a vector is correctly converted to a one column matrix #[test] fn row_to_column() { let mat = VecZ::from_str("[1,2,3]").unwrap(); From 4c27d3d81a0a0a2776c9babf869205cc27bdc9c3 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Mon, 27 Mar 2023 23:42:44 +0200 Subject: [PATCH 11/15] Apply rustfmt + add import of GetEntry for VecZ --- src/integer/mat_z.rs | 2 +- src/integer/vec_z/get.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/integer/mat_z.rs b/src/integer/mat_z.rs index 3669f2ef3..2d54ced7f 100644 --- a/src/integer/mat_z.rs +++ b/src/integer/mat_z.rs @@ -17,8 +17,8 @@ mod get; mod mul; mod ownership; mod set; -mod transpose; mod to_string; +mod transpose; /// [`MatZ`] is a matrix with entries of type [`Z`](crate::integer::Z). /// diff --git a/src/integer/vec_z/get.rs b/src/integer/vec_z/get.rs index 1c57285ba..0af453b77 100644 --- a/src/integer/vec_z/get.rs +++ b/src/integer/vec_z/get.rs @@ -9,7 +9,7 @@ //! Implementations to get entries from a [`VecZ`] matrix. use super::VecZ; -use crate::{error::MathError, integer::Z}; +use crate::{error::MathError, integer::Z, traits::GetEntry}; use std::fmt::Display; impl VecZ { @@ -32,7 +32,7 @@ impl VecZ { /// # Errors and Failures /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) /// if the given row is greater than the matrix or negative. - pub fn get_entry + Display + Copy>(&self, row: S) -> Result { + pub fn get_entry(&self, row: impl TryInto + Display + Copy) -> Result { self.matrix.get_entry(row, 0) } } From b04df0df46e6c1f8f1132dd1c27b4438ae26c258 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Tue, 28 Mar 2023 14:42:15 +0200 Subject: [PATCH 12/15] Make vector viable as Column and RowVector --- src/error.rs | 5 ++ src/integer/mat_z/mul.rs | 10 +-- src/integer/vec_z/from.rs | 109 ++++++++++++++++-------- src/integer/vec_z/get.rs | 147 ++++++++++++++++++++++++++++++--- src/integer/vec_z/mul.rs | 30 +++---- src/integer/vec_z/set.rs | 53 +++++++----- src/integer/vec_z/transpose.rs | 33 +++++--- src/utils.rs | 2 + src/utils/dimensions.rs | 8 +- src/utils/parse.rs | 111 +------------------------ 10 files changed, 299 insertions(+), 209 deletions(-) diff --git a/src/error.rs b/src/error.rs index 0929ff3db..692c73e86 100644 --- a/src/error.rs +++ b/src/error.rs @@ -38,6 +38,8 @@ use thiserror::Error; /// not formatted correctly. /// - `InvalidStringToQInput` is thrown if an invalid string is given to /// construct a [`Q`](crate::rational::Q) +/// - `InvalidStringToVectorInput` is thrown if a valid input string for a matrix was given, +/// but it was not valid as a string input for a vector, i.e. no matrix dimension was 1 /// - `InvalidStringToZInput` is thrown if an invalid string is given to /// construct a [`Z`](crate::integer::Z) /// - `InvalidStringToZqInput` is thrown if an invalid string is given to @@ -109,6 +111,9 @@ pub enum MathError { /// parse string to [`Q`](crate::rational::Q) error #[error("invalid string input to parse to Q {0}")] InvalidStringToQInput(String), + /// parse string to [`VecZ`](crate::integer::VecZ) or other vectors error + #[error("invalid string input to parse to a vector (no dimension is 1) {0}")] + InvalidStringToVectorInput(String), /// parse string to [`Z`](crate::integer::Z) error #[error("invalid string input to parse to Z {0}")] InvalidStringToZInput(String), diff --git a/src/integer/mat_z/mul.rs b/src/integer/mat_z/mul.rs index dca4ab989..b85005cb8 100644 --- a/src/integer/mat_z/mul.rs +++ b/src/integer/mat_z/mul.rs @@ -76,7 +76,7 @@ impl Mul<&VecZ> for &MatZ { /// use std::str::FromStr; /// /// let a = MatZ::from_str("[[2,1],[1,2]]").unwrap(); - /// let b = VecZ::from_str("[1,0]").unwrap(); + /// let b = VecZ::from_str("[[1],[0]]").unwrap(); /// /// let _ = &a * &b; /// let _ = a.clone() * b.clone(); @@ -147,7 +147,7 @@ mod test_mul { #[test] fn matrix_mul_vector_correctness() { let mat = MatZ::from_str("[[2,1],[1,2]]").unwrap(); - let vec = VecZ::from_str("[1,0]").unwrap(); + let vec = VecZ::from_str("[[1],[0]]").unwrap(); let cmp = MatZ::from_str("[[2],[1]]").unwrap(); assert_eq!(cmp, &mat * &vec); @@ -158,9 +158,9 @@ mod test_mul { #[test] #[should_panic] fn matrix_mul_vector_incompatible_dimensions() { - let mat_1 = MatZ::from_str("[[2,1],[1,2]]").unwrap(); - let mat_2 = VecZ::from_str("[1,0,1]").unwrap(); + let mat = MatZ::from_str("[[2,1],[1,2]]").unwrap(); + let vec = VecZ::from_str("[[1],[0],[1]]").unwrap(); - let _ = mat_1 * mat_2; + let _ = mat * vec; } } diff --git a/src/integer/vec_z/from.rs b/src/integer/vec_z/from.rs index e736dfbee..8b5d07ed5 100644 --- a/src/integer/vec_z/from.rs +++ b/src/integer/vec_z/from.rs @@ -14,9 +14,9 @@ //! The explicit functions contain the documentation. use super::{MatZ, VecZ}; -use crate::error::MathError; use crate::integer::Z; -use crate::utils::parse::parse_vector_string; +use crate::utils::{parse::parse_matrix_string, VectorDirection}; +use crate::{error::MathError, utils::dimensions::find_matrix_dimensions}; use std::{fmt::Display, str::FromStr}; impl VecZ { @@ -32,8 +32,9 @@ impl VecZ { /// # Example /// ``` /// use math::integer::VecZ; + /// use math::utils::VectorDirection; /// - /// let vector = VecZ::new(5).unwrap(); + /// let vector = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); /// ``` /// /// # Errors and Failures @@ -42,10 +43,18 @@ impl VecZ { /// if the number of rows is `0`. /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) /// if the number of rows is negative or it does not fit into an [`i64`]. - pub fn new(num_rows: impl TryInto + Display + Copy) -> Result { - Ok(VecZ { - matrix: MatZ::new(num_rows, 1)?, - }) + pub fn new( + num_entries: impl TryInto + Display + Copy, + orientation: VectorDirection, + ) -> Result { + match orientation { + VectorDirection::RowVector => Ok(VecZ { + matrix: MatZ::new(1, num_entries)?, + }), + VectorDirection::ColumnVector => Ok(VecZ { + matrix: MatZ::new(num_entries, 1)?, + }), + } } } @@ -68,7 +77,7 @@ impl FromStr for VecZ { /// use math::integer::VecZ; /// use std::str::FromStr; /// - /// let string = String::from("[1, 2, 3]"); + /// let string = String::from("[[1, 2, 3]]"); /// let matrix = VecZ::from_str(&string).unwrap(); /// ``` /// @@ -83,14 +92,23 @@ impl FromStr for VecZ { /// [`InvalidStringToZInput`](MathError::InvalidStringToZInput) /// if an entry is not formatted correctly. fn from_str(string: &str) -> Result { - let entries = parse_vector_string(string)?; - let num_rows = entries.len(); - let mut vector = VecZ::new(num_rows)?; + let entries_string = parse_matrix_string(string)?; + let (num_rows, num_cols) = find_matrix_dimensions(&entries_string)?; + let mut vector: VecZ; + match (num_rows, num_cols) { + (1, _) => vector = VecZ::new(num_cols, VectorDirection::RowVector)?, + (_, 1) => vector = VecZ::new(num_rows, VectorDirection::ColumnVector)?, + (_, _) => return Err(MathError::InvalidStringToVectorInput(String::from( + "The string either contained more than one column or row, or did have zero entries", + ))), + } // fill entries of matrix according to entries in string_matrix - for (row_num, entry) in entries.iter().enumerate() { - let z_entry = Z::from_str(entry)?; - vector.set_entry(row_num, z_entry)?; + for (row_num, row) in entries_string.iter().enumerate() { + for (col_num, entry) in row.iter().enumerate() { + let z_entry = Z::from_str(entry)?; + vector.matrix.set_entry(row_num, col_num, z_entry)?; + } } Ok(vector) } @@ -98,12 +116,14 @@ impl FromStr for VecZ { #[cfg(test)] mod test_new { + use crate::integer::{VecZ, Z}; + use crate::utils::VectorDirection; /// Ensure that entries of a new vector are `0`. #[test] fn entry_zero() { - let matrix = VecZ::new(2).unwrap(); + let matrix = VecZ::new(2, VectorDirection::ColumnVector).unwrap(); let entry1 = matrix.get_entry(0).unwrap(); let entry2 = matrix.get_entry(0).unwrap(); @@ -115,8 +135,8 @@ mod test_new { /// Ensure that a new zero vector fails with `0` or a negative value as input. #[test] fn error_zero_negative() { - let matrix1 = VecZ::new(0); - let matrix2 = VecZ::new(-1); + let matrix1 = VecZ::new(0, VectorDirection::ColumnVector); + let matrix2 = VecZ::new(-1, VectorDirection::ColumnVector); assert!(matrix1.is_err()); assert!(matrix2.is_err()); @@ -125,27 +145,30 @@ mod test_new { #[cfg(test)] mod test_from_str { + use crate::integer::{VecZ, Z}; + use crate::utils::VectorDirection; use std::str::FromStr; - /// Ensure that initialization works + /// Ensure that initialization works in both directions #[test] - fn init_works() { - let vector_string = String::from("[1, 2, 3]"); + fn directions() { + let row = VecZ::from_str("[[1, 2, 3]]").unwrap(); + let col = VecZ::from_str("[[1],[2],[3]]").unwrap(); - assert_eq!( - Z::from_i64(1), - VecZ::from_str(&vector_string) - .unwrap() - .get_entry(0) - .unwrap() - ); + assert_eq!(Z::from_i64(1), row.get_entry(0).unwrap()); + assert!(row.is_row_vector()); + assert_eq!(VectorDirection::RowVector, row.get_direction()); + + assert_eq!(Z::from_i64(1), col.get_entry(0).unwrap()); + assert!(col.is_column_vector()); + assert_eq!(VectorDirection::ColumnVector, col.get_direction()); } /// Ensure that initialization with positive numbers that are larger than [`i64`] works. #[test] - fn init_works_large_numbers() { - let vector_string = format!("[{}, 2, 3]", "1".repeat(65)); + fn large_numbers() { + let vector_string = format!("[[{}, 2, 3]]", "1".repeat(65)); assert_eq!( Z::from_str(&"1".repeat(65)).unwrap(), @@ -158,8 +181,8 @@ mod test_from_str { /// Ensure that initialization with negative numbers that are larger than [`i64`] works. #[test] - fn init_works_small_numbers() { - let vector_string = format!("[-{}, 2, 3]", "1".repeat(65)); + fn small_numbers() { + let vector_string = format!("[[-{}, 2, 3]]", "1".repeat(65)); let entry = format!("-{}", "1".repeat(65)); @@ -175,7 +198,7 @@ mod test_from_str { /// Ensure that entries can have leading and trailing whitespaces. #[test] fn whitespaces_in_entries_works() { - let vector_string = String::from("[ 1, 2 , 3 ]"); + let vector_string = String::from("[[ 1],[2 ],[ 3 ]]"); assert_eq!( Z::from_i64(1), @@ -186,10 +209,24 @@ mod test_from_str { ); } - /// Ensure that entries are set correctly. + /// Ensure that entries are set correctly for a row vector + #[test] + fn entries_set_correctly_row() { + let vector_string = format!("[[0,{},{}, -10, 10]]", i64::MIN, i64::MAX); + + let vector = VecZ::from_str(&vector_string).unwrap(); + + assert_eq!(vector.get_entry(0).unwrap(), 0.into()); + assert_eq!(vector.get_entry(1).unwrap(), i64::MIN.into()); + assert_eq!(vector.get_entry(2).unwrap(), i64::MAX.into()); + assert_eq!(vector.get_entry(3).unwrap(), (-10).into()); + assert_eq!(vector.get_entry(4).unwrap(), 10.into()); + } + + /// Ensure that entries are set correctly for a column vector #[test] - fn entries_set_correctly() { - let vector_string = format!("[0,{},{}, -10, 10]", i64::MIN, i64::MAX); + fn entries_set_correctly_col() { + let vector_string = format!("[[0],[{}],[{}],[-10],[10]]", i64::MIN, i64::MAX); let vector = VecZ::from_str(&vector_string).unwrap(); @@ -204,7 +241,7 @@ mod test_from_str { #[test] fn wrong_format_error() { let vector_string1 = String::from("[1, 2, 3],"); - let vector_string2 = String::from("[[1, 2, 3]]"); + let vector_string2 = String::from("[1, 2, 3]]"); let vector_string3 = String::from("[[1, 2, 3],3, 4, 5]"); let vector_string4 = String::from("[1, 2, 3, 4,, 5]"); let vector_string5 = String::from("[[1, 2, 3],[3, 4, 5]]"); diff --git a/src/integer/vec_z/get.rs b/src/integer/vec_z/get.rs index 0af453b77..c215ff4bd 100644 --- a/src/integer/vec_z/get.rs +++ b/src/integer/vec_z/get.rs @@ -9,31 +9,94 @@ //! Implementations to get entries from a [`VecZ`] matrix. use super::VecZ; -use crate::{error::MathError, integer::Z, traits::GetEntry}; +use crate::{ + error::MathError, + integer::Z, + traits::{GetEntry, GetNumColumns, GetNumRows}, + utils::VectorDirection, +}; use std::fmt::Display; impl VecZ { /// Outputs the [`Z`] value of a specific vector entry. /// /// Parameters: - /// - `row`: specifies the row in which the entry is located + /// - `entry`: specifies the entry in which the entry is located /// /// Returns the [`Z`] value of the vector at the position of the given - /// row or an error, if the given row is greater than the vector or negative. + /// entry or an error, if the given entry is greater than the vector or negative. /// /// # Example /// ``` /// use math::integer::VecZ; + /// use math::utils::VectorDirection; /// - /// let matrix = VecZ::new(5).unwrap(); + /// let matrix = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); /// let entry = matrix.get_entry(1).unwrap(); /// ``` /// /// # Errors and Failures /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) - /// if the given row is greater than the matrix or negative. - pub fn get_entry(&self, row: impl TryInto + Display + Copy) -> Result { - self.matrix.get_entry(row, 0) + /// if the given entry is outside the vector or negative. + pub fn get_entry(&self, entry: impl TryInto + Display + Copy) -> Result { + if self.is_column_vector() { + self.matrix.get_entry(entry, 0) + } else { + self.matrix.get_entry(0, entry) + } + } + + /// Returns `true` if the vector is a row vector with more than one column. + /// + /// # Example + /// ``` + /// use math::integer::VecZ; + /// use std::str::FromStr; + /// + /// let row = VecZ::from_str("[[1,2,3]]").unwrap(); + /// + /// assert!(row.is_row_vector()); + /// ``` + pub fn is_row_vector(&self) -> bool { + self.matrix.get_num_columns() > 1 + } + + /// Returns `true` if the vector is a column vector with more than one row. + /// + /// # Example + /// ``` + /// use math::integer::VecZ; + /// use std::str::FromStr; + /// + /// let col = VecZ::from_str("[[1],[2],[3]]").unwrap(); + /// + /// assert!(col.is_column_vector()); + /// ``` + pub fn is_column_vector(&self) -> bool { + self.matrix.get_num_rows() > 1 + } + + /// Returns [`VectorDirection::ColumnVector`] if the vector is a column vector with more than one row. + /// Otherwise, returns [`VectorDirection::RowVector`]. + /// + /// # Example + /// ``` + /// use math::integer::VecZ; + /// use std::str::FromStr; + /// use math::utils::VectorDirection; + /// + /// let row = VecZ::from_str("[[1,2,3]]").unwrap(); + /// let col = VecZ::from_str("[[1],[2],[3]]").unwrap(); + /// + /// assert_eq!(VectorDirection::RowVector, row.get_direction()); + /// assert_eq!(VectorDirection::ColumnVector, col.get_direction()); + /// ``` + pub fn get_direction(&self) -> VectorDirection { + if self.is_column_vector() { + VectorDirection::ColumnVector + } else { + VectorDirection::RowVector + } } } @@ -42,12 +105,32 @@ mod test_get_entry { use super::VecZ; use super::Z; + use crate::utils::VectorDirection; use std::str::FromStr; - /// Ensure that getting the correct entries works fine + /// Ensure that getting the correct entries works fine for row vectors + #[test] + fn correct_entry_fetched_row() { + let vector = + VecZ::from_str(&format!("[[0, 10, -10, {}, {}]]", i64::MAX, i64::MIN)).unwrap(); + let zero: Z = 0.into(); + let ten: Z = 10.into(); + let minus_ten: Z = (-10).into(); + let max: Z = i64::MAX.into(); + let min: Z = i64::MIN.into(); + + assert_eq!(zero, vector.get_entry(0).unwrap()); + assert_eq!(ten, vector.get_entry(1).unwrap()); + assert_eq!(minus_ten, vector.get_entry(2).unwrap()); + assert_eq!(max, vector.get_entry(3).unwrap()); + assert_eq!(min, vector.get_entry(4).unwrap()); + } + + /// Ensure that getting the correct entries works fine for column vectors #[test] - fn correct_entry_fetched() { - let vector = VecZ::from_str(&format!("[0, 10, -10, {}, {}]", i64::MAX, i64::MIN)).unwrap(); + fn correct_entry_fetched_col() { + let vector = + VecZ::from_str(&format!("[[0],[10],[-10],[{}],[{}]]", i64::MAX, i64::MIN)).unwrap(); let zero: Z = 0.into(); let ten: Z = 10.into(); let minus_ten: Z = (-10).into(); @@ -64,9 +147,47 @@ mod test_get_entry { /// Ensure that a wrong number of rows yields an Error. #[test] fn error_out_bounds() { - let matrix = VecZ::new(5).unwrap(); + let vector = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); + + assert!(vector.get_entry(5).is_err()); + assert!(vector.get_entry(-1).is_err()); + } +} + +#[cfg(test)] +mod test_directions { + + use super::VecZ; + use crate::utils::VectorDirection; + + /// Check whether a row vector is correctly detected + #[test] + fn row_detected() { + let vec = VecZ::new(5, VectorDirection::RowVector).unwrap(); + + assert!(vec.is_row_vector()); + assert!(!vec.is_column_vector()); + assert_eq!(vec.get_direction(), VectorDirection::RowVector); + } + + /// Check whether a column vector is correctly detected + #[test] + fn column_detected() { + let vec = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); + + assert!(!vec.is_row_vector()); + assert!(vec.is_column_vector()); + assert_eq!(vec.get_direction(), VectorDirection::ColumnVector); + } + + /// Check whether a 1x1 vector is neither of both + #[test] + fn one_by_one_detection() { + let vec = VecZ::new(1, VectorDirection::ColumnVector).unwrap(); - assert!(matrix.get_entry(5).is_err()); - assert!(matrix.get_entry(-1).is_err()); + assert!(!vec.is_row_vector()); + assert!(!vec.is_column_vector()); + assert_eq!(vec.get_direction(), VectorDirection::RowVector); + // returns RowVector as standard as this answer is not wrong for a one times one matrix } } diff --git a/src/integer/vec_z/mul.rs b/src/integer/vec_z/mul.rs index 8ab535bbc..a51be3e8f 100644 --- a/src/integer/vec_z/mul.rs +++ b/src/integer/vec_z/mul.rs @@ -31,13 +31,13 @@ impl Mul for &VecZ { /// use math::integer::VecZ; /// use std::str::FromStr; /// - /// let a = VecZ::from_str("[2,1,1,2]").unwrap(); - /// let b = VecZ::from_str("[1,0,0,1]").unwrap(); + /// let a = VecZ::from_str("[[2],[1],[1],[2]]").unwrap(); + /// let b = VecZ::from_str("[[1,0,0,1]]").unwrap(); /// - /// let _ = &a.transpose() * &b; - /// let _ = a.transpose() * b.clone(); - /// let _ = &a.transpose() * b.clone(); - /// let _ = a.transpose() * &b; + /// let _ = &a * &b; + /// let _ = a.clone() * b.clone(); + /// let _ = &a * b.clone(); + /// let _ = a.clone() * &b; /// ``` fn mul(self, other: Self) -> Self::Output { (&self.matrix).mul(other) @@ -63,7 +63,7 @@ impl Mul<&MatZ> for &VecZ { /// use math::integer::{MatZ, VecZ}; /// use std::str::FromStr; /// - /// let a = VecZ::from_str("[1,0]").unwrap(); + /// let a = VecZ::from_str("[[1],[0]]").unwrap(); /// let b = MatZ::from_str("[[2,1]]").unwrap(); /// /// let c = &a * &b; @@ -88,11 +88,11 @@ mod test_mul { /// Checks if vector multiplication works fine for vectors of same length #[test] fn vector_mul_vector_correctness() { - let vec_1 = VecZ::from_str("[2,1,1,2]").unwrap(); - let vec_2 = VecZ::from_str("[1,2,2,1]").unwrap(); + let vec_1 = VecZ::from_str("[[2,1,1,2]]").unwrap(); + let vec_2 = VecZ::from_str("[[1],[2],[2],[1]]").unwrap(); let cmp = MatZ::from_str("[[8]]").unwrap(); - assert_eq!(cmp, vec_1.transpose() * vec_2); + assert_eq!(cmp, vec_1 * vec_2); } /// Checks if vector multiplication with incompatible vector length @@ -100,17 +100,17 @@ mod test_mul { #[test] #[should_panic] fn vector_mul_vector_incompatible_length() { - let mat_1 = MatZ::from_str("[2,1,1,2]").unwrap(); - let mat_2 = MatZ::from_str("[1,0,0,1,0]").unwrap(); + let vec_1 = VecZ::from_str("[[2,1,1,2]]").unwrap(); + let vec_2 = VecZ::from_str("[[1],[0],[0],[1],[0]]").unwrap(); - let _ = mat_1 * mat_2; + let _ = vec_1 * vec_2; } /// Checks if cross-type multiplications works fine for [`VecZ`] * [`MatZ`] #[test] fn vector_mul_matrix_correctness() { let mat = MatZ::from_str("[[2,1]]").unwrap(); - let vec = VecZ::from_str("[1,0]").unwrap(); + let vec = VecZ::from_str("[[1],[0]]").unwrap(); let cmp = MatZ::from_str("[[2,1],[0,0]]").unwrap(); assert_eq!(cmp, &vec * &mat); @@ -122,7 +122,7 @@ mod test_mul { #[should_panic] fn vector_mul_matrix_incompatible_dimensions() { let mat = MatZ::from_str("[[2,1],[1,2]]").unwrap(); - let vec = VecZ::from_str("[1,0,1]").unwrap(); + let vec = VecZ::from_str("[[1],[0],[1]]").unwrap(); let _ = vec * mat; } diff --git a/src/integer/vec_z/set.rs b/src/integer/vec_z/set.rs index f61137653..49623dc42 100644 --- a/src/integer/vec_z/set.rs +++ b/src/integer/vec_z/set.rs @@ -16,55 +16,60 @@ impl VecZ { /// Sets the value of a specific vector entry according to a given `value` of type [`Z`](crate::integer::Z). /// /// Parameters: - /// - `row`: specifies the row in which the entry is located + /// - `entry`: specifies the entry in which the entry is located /// - `value`: specifies the value to which the entry is set /// /// # Example /// ``` - /// use math::integer::VecZ; - /// use math::integer::Z; + /// use math::integer::{VecZ, Z}; + /// use math::utils::VectorDirection; /// - /// let mut vector = VecZ::new(5).unwrap(); + /// let mut vector = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); /// let value = Z::from_i64(5); /// vector.set_entry(1, value).unwrap(); /// ``` /// /// # Errors and Failures /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) - /// if the given row is greater than the matrix or negative. + /// if the given entry outside the vector or negative. pub fn set_entry + Display + Copy, T: Into>( &mut self, - row: S, + entry: S, value: T, ) -> Result<(), MathError> { - self.set_entry_ref_z(row, &value.into()) + self.set_entry_ref_z(entry, &value.into()) } /// Sets the value of a specific matrix entry according to a given `value` of type [`Z`]. /// /// Parameters: - /// - `row`: specifies the row in which the entry is located + /// - `entry`: specifies the entry in which the entry is located /// - `value`: specifies the value to which the entry is set /// /// # Example /// ``` - /// use math::integer::VecZ; - /// use math::integer::Z; + /// use math::integer::{VecZ, Z}; + /// use math::utils::VectorDirection; /// - /// let mut vector = VecZ::new(5).unwrap(); + /// let mut vector = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); /// let value = Z::from_i64(5); /// vector.set_entry_ref_z(1, &value).unwrap(); /// ``` /// /// # Errors and Failures /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) - /// if the given row is greater than the matrix or negative. + /// if the given entry outside the vector or negative. pub fn set_entry_ref_z + Display + Copy>( &mut self, - row: S, + entry: S, value: &Z, ) -> Result<(), MathError> { - self.matrix.set_entry_ref_z(row, 0, value)?; + if self.is_column_vector() { + self.matrix.set_entry_ref_z(entry, 0, value)?; + } else { + self.matrix.set_entry_ref_z(0, entry, value)?; + } + Ok(()) } } @@ -73,25 +78,31 @@ impl VecZ { mod test_setter { use super::Z; use crate::integer::VecZ; + use crate::utils::VectorDirection; /// Ensure that setting the correct entries works fine #[test] fn correct_entry_set() { - let mut vector = VecZ::new(5).unwrap(); - vector.set_entry(4, 869).unwrap(); + let mut col = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); + let mut row = VecZ::new(5, VectorDirection::RowVector).unwrap(); - let entry = vector.get_entry(4).unwrap(); + col.set_entry(4, 869).unwrap(); + row.set_entry(3, 869).unwrap(); - assert_eq!(entry, Z::from_i64(869)); + assert_eq!(col.get_entry(4).unwrap(), Z::from_i64(869)); + assert_eq!(row.get_entry(3).unwrap(), Z::from_i64(869)); } /// Ensure that a wrong given row returns a [`MathError`](crate::error::MathError) #[test] fn error_wrong_row() { - let mut vector = VecZ::new(5).unwrap(); + let mut col = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); + let mut row = VecZ::new(5, VectorDirection::RowVector).unwrap(); let value = Z::from_i64(i64::MAX); - assert!(vector.set_entry_ref_z(5, &value).is_err()); - assert!(vector.set_entry_ref_z(-1, &value).is_err()); + assert!(col.set_entry_ref_z(5, &value).is_err()); + assert!(col.set_entry_ref_z(-1, &value).is_err()); + assert!(row.set_entry_ref_z(5, &value).is_err()); + assert!(row.set_entry_ref_z(-1, &value).is_err()); } } diff --git a/src/integer/vec_z/transpose.rs b/src/integer/vec_z/transpose.rs index 821ef8bfa..09d033f18 100644 --- a/src/integer/vec_z/transpose.rs +++ b/src/integer/vec_z/transpose.rs @@ -8,23 +8,25 @@ //! This module containts the implementation of the `transpose` function. -use super::{MatZ, VecZ}; +use super::VecZ; impl VecZ { /// Returns the transposed form of the given matrix, i.e. rows get transformed to columns. /// /// # Example /// ``` - /// use math::integer::MatZ; + /// use math::integer::VecZ; /// 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(); + /// let mat = VecZ::from_str("[[1,2,3]]").unwrap(); + /// let cmp = VecZ::from_str("[[1],[2],[3]]").unwrap(); /// /// assert_eq!(mat.transpose(), cmp); /// ``` - pub fn transpose(&self) -> MatZ { - self.matrix.transpose() + pub fn transpose(&self) -> VecZ { + Self { + matrix: self.matrix.transpose(), + } } } @@ -33,15 +35,24 @@ mod test_transpose { /// Further tests regarding large or small entries are omitted /// as they are part of the tests of the here called function /// [`MatZ::transpose`](crate::integer::MatZ::transpose) - use super::{MatZ, VecZ}; + use super::VecZ; use std::str::FromStr; - /// Checks if a vector is correctly converted to a one column matrix + /// Checks if a vector is correctly converted to a one-row matrix + #[test] + fn column_to_row() { + let vec = VecZ::from_str("[[1],[2],[3]]").unwrap(); + let cmp = VecZ::from_str("[[1,2,3]]").unwrap(); + + assert_eq!(cmp, vec.transpose()); + } + + /// Checks if a vector is correctly converted to a one-column matrix #[test] fn row_to_column() { - let mat = VecZ::from_str("[1,2,3]").unwrap(); - let cmp = MatZ::from_str("[[1,2,3]]").unwrap(); + let vec = VecZ::from_str("[[1,2,3]]").unwrap(); + let cmp = VecZ::from_str("[[1],[2],[3]]").unwrap(); - assert_eq!(cmp, mat.transpose()); + assert_eq!(cmp, vec.transpose()); } } diff --git a/src/utils.rs b/src/utils.rs index 36bbb873a..34f11ad4e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -14,3 +14,5 @@ pub mod coordinate; pub mod dimensions; pub mod parse; + +pub use dimensions::VectorDirection; diff --git a/src/utils/dimensions.rs b/src/utils/dimensions.rs index 02689a8bd..e8d198e89 100644 --- a/src/utils/dimensions.rs +++ b/src/utils/dimensions.rs @@ -6,7 +6,13 @@ // 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. + +#[derive(Debug, PartialEq)] +pub enum VectorDirection { + RowVector, + ColumnVector, +} use crate::error::MathError; diff --git a/src/utils/parse.rs b/src/utils/parse.rs index ebb8f7a78..3857f5cbf 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. @@ -72,50 +71,6 @@ pub(crate) fn parse_matrix_string(string: &str) -> Result>, Math Ok(matrix) } -/// Takes the string of a vector as input and parses it for easy use. -/// -/// The input should look like `[1, 2, 3]` to get a vector with -/// strings as entries. -/// Entries of the vector can contain all symbols but `[`, `]` and `,`. -/// -/// Parameters: -/// - `string`: a vector as a string -/// -/// Returns the Vector in form of a vector with the entries -/// stored as strings or an error, if the vector is not formatted correctly. -/// -/// # Errors and Failures -/// - Returns a [`MathError`] of type [`InvalidMatrix`](MathError::InvalidMatrix) -/// if the vector is not formatted in a suitable way. -pub(crate) fn parse_vector_string(string: &str) -> Result, MathError> { - // check if the matrix format is correct - let entry_str = r"([^\[\],]+)"; - let row_str = format!(r"^\[({},)*({})\]$", entry_str, entry_str); - let regex = Regex::new(&row_str).expect("The regular expression could not be processed."); - - // explanation of this regex: - // it checks whether the string start with a '[' and ends with a ']' - // we differ between the first/several and the last entry the vector (as there is no comma after the last entry) - // each entry can contain any symbol but `[`, `]` and `,`. It needs to have at least one symbol. - if !regex.is_match(string) { - return Err(MathError::InvalidMatrix( - "The matrix is not formatted in a suitable way.".to_owned(), - )); - } - - // delete `[` in front and `]` in the end and split the string into its entries - let entries = string[1..string.len() - 1].split(','); - - let mut vector: Vec = Vec::new(); - for entry in entries { - // delete leading and trailing whitespaces from the entry and - // adds it to the row vector - vector.push(entry.trim().to_owned()); - } - - Ok(vector) -} - /// Takes a matrix as input and converts it to a [`String`]. /// /// Parameters: @@ -160,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. @@ -169,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. @@ -217,67 +175,6 @@ mod test_parse_matrix_string { } } -#[cfg(test)] -mod test_parse_vector_string { - use crate::utils::parse::parse_vector_string; - - // Ensure that correct strings of a vector are accepted. - #[test] - fn correct_vector_work() { - let vector_string1 = String::from("[1, 2, 3]"); - let vector_string2 = String::from("[1/3, -2/7, 3]"); - let vector_string3 = String::from("[4 0 1 2 3, 2 0 1]"); - let vector_string4 = String::from("[sdclin, =§&%, +57n4]"); - - assert!(parse_vector_string(&vector_string1).is_ok()); - assert!(parse_vector_string(&vector_string2).is_ok()); - assert!(parse_vector_string(&vector_string3).is_ok()); - assert!(parse_vector_string(&vector_string4).is_ok()); - } - - // Ensure that incorrect strings of a vector are rejected. - #[test] - fn incorrect_entries_error() { - let vector_string1 = String::from("[1, 2, 3],"); - let vector_string2 = String::from("[1/3, -2/7, 3,[3, 4, -5/-2]]"); - let vector_string3 = String::from("[1, [2], 3],[3, 4, 5]"); - let vector_string4 = String::from("[1, 2, 3][3, 4, 5]"); - let vector_string5 = String::from("[[1, 2, 3]]"); - - assert!(parse_vector_string(&vector_string1).is_err()); - assert!(parse_vector_string(&vector_string2).is_err()); - assert!(parse_vector_string(&vector_string3).is_err()); - assert!(parse_vector_string(&vector_string4).is_err()); - assert!(parse_vector_string(&vector_string5).is_err()); - } - - // Ensure that correct strings of a vector are prepared correctly. - #[test] - fn correct_vector_format() { - let vector_string1 = String::from("[1, 2, 3]"); - let vector_string2 = String::from("[1/3, -2/7, 3,3, 4, -5/-2]"); - let vector_string3 = String::from("[4 0 1 2 3, 2 0 1,1 5, 2 7 8]"); - let vector_string4 = String::from("[sdclin, +dk<, 37 ffew, 8fh2n]"); - - assert_eq!( - parse_vector_string(&vector_string1).unwrap()[0], - "1".to_owned() - ); - assert_eq!( - parse_vector_string(&vector_string2).unwrap()[1], - "-2/7".to_owned() - ); - assert_eq!( - parse_vector_string(&vector_string3).unwrap()[2], - "1 5".to_owned() - ); - assert_eq!( - parse_vector_string(&vector_string4).unwrap()[3], - "8fh2n".to_owned() - ); - } -} - #[cfg(test)] mod test_matrix_to_string { use crate::{integer::MatZ, utils::parse::matrix_to_string}; From 1681eff4aa7539a8febfa0608b037c0605aadf72 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Tue, 28 Mar 2023 18:02:28 +0200 Subject: [PATCH 13/15] Apply requested PR changes to VecZ --- src/integer/mat_z/cmp.rs | 14 ++++- src/integer/mat_z/mul.rs | 8 ++- src/integer/vec_z.rs | 2 +- src/integer/vec_z/from.rs | 97 ++++++++++++++++++---------------- src/integer/vec_z/get.rs | 26 ++++----- src/integer/vec_z/mul.rs | 10 +++- src/integer/vec_z/set.rs | 4 +- src/integer/vec_z/transpose.rs | 13 ++--- src/utils/parse.rs | 2 +- 9 files changed, 101 insertions(+), 75 deletions(-) diff --git a/src/integer/mat_z/cmp.rs b/src/integer/mat_z/cmp.rs index df5f9ee67..a1898060e 100644 --- a/src/integer/mat_z/cmp.rs +++ b/src/integer/mat_z/cmp.rs @@ -66,8 +66,18 @@ mod test_partial_eq { /// 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!("[[{},{}],[-10, 10]]", i64::MIN, i64::MAX)).unwrap(); - let b = MatZ::from_str(&format!("[[{},{}],[-10, 10]]", i64::MIN, i64::MAX)).unwrap(); + 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); } diff --git a/src/integer/mat_z/mul.rs b/src/integer/mat_z/mul.rs index b85005cb8..4235483db 100644 --- a/src/integer/mat_z/mul.rs +++ b/src/integer/mat_z/mul.rs @@ -44,6 +44,9 @@ impl Mul for &MatZ { /// 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() { @@ -83,8 +86,11 @@ impl Mul<&VecZ> for &MatZ { /// let _ = &a * b.clone(); /// let _ = a.clone() * &b; /// ``` + /// + /// # Errors and Failures + /// - Panics if the dimensions of `self` and `other` do not match for multiplication. fn mul(self, other: &VecZ) -> Self::Output { - self.mul(&other.matrix) + self.mul(&other.vector) } } diff --git a/src/integer/vec_z.rs b/src/integer/vec_z.rs index 0ed16f706..1c63c2cb8 100644 --- a/src/integer/vec_z.rs +++ b/src/integer/vec_z.rs @@ -25,5 +25,5 @@ use super::MatZ; /// # Examples #[derive(Debug, PartialEq, Eq, Clone)] pub struct VecZ { - pub(crate) matrix: MatZ, + pub(crate) vector: MatZ, } diff --git a/src/integer/vec_z/from.rs b/src/integer/vec_z/from.rs index 8b5d07ed5..c8e190c2a 100644 --- a/src/integer/vec_z/from.rs +++ b/src/integer/vec_z/from.rs @@ -14,19 +14,22 @@ //! The explicit functions contain the documentation. use super::{MatZ, VecZ}; -use crate::integer::Z; -use crate::utils::{parse::parse_matrix_string, VectorDirection}; -use crate::{error::MathError, utils::dimensions::find_matrix_dimensions}; +use crate::error::MathError; +use crate::traits::{GetNumColumns, GetNumRows}; +use crate::utils::VectorDirection; use std::{fmt::Display, str::FromStr}; impl VecZ { - /// Creates a new row-vector with `num_rows` rows and + /// Creates a new vector with `num_entries` entries and /// zeros as entries. /// /// Parameters: - /// - `num_rows`: number of columns the new vector should have + /// - `num_entries`: number of rows or columns the new vector should have. + /// - `orientation`: defines the orientation the vector should have. + /// [`VectorDirection::ColumnVector`] and [`VectorDirection::RowVector`] + /// are valid inputs. /// - /// Returns a [`VecZ`] or an error, if the number of rows is + /// Returns a [`VecZ`] or an error, if the number of entries is /// less or equal to `0`. /// /// # Example @@ -40,19 +43,19 @@ impl VecZ { /// # Errors and Failures /// - Returns a [`MathError`] of type /// [`InvalidMatrix`](MathError::InvalidMatrix) - /// if the number of rows is `0`. + /// if the number of rows/columns is `0`. /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) - /// if the number of rows is negative or it does not fit into an [`i64`]. + /// if the number of rows/columns is negative or it does not fit into an [`i64`]. pub fn new( num_entries: impl TryInto + Display + Copy, orientation: VectorDirection, ) -> Result { match orientation { VectorDirection::RowVector => Ok(VecZ { - matrix: MatZ::new(1, num_entries)?, + vector: MatZ::new(1, num_entries)?, }), VectorDirection::ColumnVector => Ok(VecZ { - matrix: MatZ::new(num_entries, 1)?, + vector: MatZ::new(num_entries, 1)?, }), } } @@ -61,56 +64,58 @@ impl VecZ { impl FromStr for VecZ { type Err = MathError; - /// Creates a [`VecZ`] row-vector with entries in [`Z`] from a [`String`]. - /// The format of that string looks like this `[1,2,3]` for a row vector - /// with three entries (`1` in the first row, `2` in the second one, ...) + /// Creates a [`VecZ`] vector with entries in [`Z`] from a [`String`]. + /// The format of that string looks like this `[[1],[2],[3]]` for a column vector + /// with three entries (`1` in the first row, `2` in the second one, ...). + /// `[[1,2,3]]` is the correct format for a row vector. /// /// Parameters: /// - `string`: the vector representation as a string /// /// Returns a [`VecZ`] or an error, if the vector is not formatted in a suitable way, - /// the number of rows is too big (must fit into [`i64`]), or if the regular expression + /// the number of entries is too big (must fit into [`i64`]), or if the regular expression /// inside of the function could not be processed. /// - /// # Example + /// # Examples + /// Column Vector /// ``` /// use math::integer::VecZ; /// use std::str::FromStr; /// - /// let string = String::from("[[1, 2, 3]]"); - /// let matrix = VecZ::from_str(&string).unwrap(); + /// let string = String::from("[[1],[2],[3]]"); + /// let vector = VecZ::from_str(&string).unwrap(); + /// ``` + /// Row Vector + /// ``` + /// use math::integer::VecZ; + /// use std::str::FromStr; + /// + /// let string = String::from("[[1,2,3]]"); + /// let vector = VecZ::from_str(&string).unwrap(); /// ``` /// /// # Errors and Failures /// - Returns a [`MathError`] of type [`InvalidMatrix`](MathError::InvalidMatrix) /// if the vector is not formatted in a suitable way, - /// the number of rows is too big (must fit into [`i64`]). + /// the number of rows/columns is too big (must fit into [`i64`]). /// - Returns a [`MathError`] of type /// [`InvalidStringToCStringInput`](MathError::InvalidStringToCStringInput) /// if an entry contains a Nul byte. /// - Returns a [`MathError`] of type + /// [`InvalidStringToVectorInput`](MathError::InvalidStringToVectorInput) + /// if no dimension or the matrix is `1`. /// [`InvalidStringToZInput`](MathError::InvalidStringToZInput) /// if an entry is not formatted correctly. fn from_str(string: &str) -> Result { - let entries_string = parse_matrix_string(string)?; - let (num_rows, num_cols) = find_matrix_dimensions(&entries_string)?; - let mut vector: VecZ; - match (num_rows, num_cols) { - (1, _) => vector = VecZ::new(num_cols, VectorDirection::RowVector)?, - (_, 1) => vector = VecZ::new(num_rows, VectorDirection::ColumnVector)?, - (_, _) => return Err(MathError::InvalidStringToVectorInput(String::from( - "The string either contained more than one column or row, or did have zero entries", - ))), - } - - // fill entries of matrix according to entries in string_matrix - for (row_num, row) in entries_string.iter().enumerate() { - for (col_num, entry) in row.iter().enumerate() { - let z_entry = Z::from_str(entry)?; - vector.matrix.set_entry(row_num, col_num, z_entry)?; - } + let matrix = MatZ::from_str(string)?; + + if matrix.get_num_rows() == 1 || matrix.get_num_columns() == 1 { + Ok(VecZ { vector: matrix }) + } else { + Err(MathError::InvalidStringToVectorInput(String::from( + "The string either contained more than one column and row, or included zero entries", + ))) } - Ok(vector) } } @@ -128,8 +133,8 @@ mod test_new { let entry1 = matrix.get_entry(0).unwrap(); let entry2 = matrix.get_entry(0).unwrap(); - assert_eq!(Z::from_i64(0), entry1); - assert_eq!(Z::from_i64(0), entry2); + assert_eq!(Z::ZERO, entry1); + assert_eq!(Z::ZERO, entry2); } /// Ensure that a new zero vector fails with `0` or a negative value as input. @@ -156,11 +161,11 @@ mod test_from_str { let row = VecZ::from_str("[[1, 2, 3]]").unwrap(); let col = VecZ::from_str("[[1],[2],[3]]").unwrap(); - assert_eq!(Z::from_i64(1), row.get_entry(0).unwrap()); + assert_eq!(Z::ONE, row.get_entry(0).unwrap()); assert!(row.is_row_vector()); assert_eq!(VectorDirection::RowVector, row.get_direction()); - assert_eq!(Z::from_i64(1), col.get_entry(0).unwrap()); + assert_eq!(Z::ONE, col.get_entry(0).unwrap()); assert!(col.is_column_vector()); assert_eq!(VectorDirection::ColumnVector, col.get_direction()); } @@ -168,10 +173,10 @@ mod test_from_str { /// Ensure that initialization with positive numbers that are larger than [`i64`] works. #[test] fn large_numbers() { - let vector_string = format!("[[{}, 2, 3]]", "1".repeat(65)); + let vector_string = format!("[[{}, 2, 3]]", u64::MAX); assert_eq!( - Z::from_str(&"1".repeat(65)).unwrap(), + Z::from(u64::MAX), VecZ::from_str(&vector_string) .unwrap() .get_entry(0) @@ -182,12 +187,10 @@ mod test_from_str { /// Ensure that initialization with negative numbers that are larger than [`i64`] works. #[test] fn small_numbers() { - let vector_string = format!("[[-{}, 2, 3]]", "1".repeat(65)); - - let entry = format!("-{}", "1".repeat(65)); + let vector_string = format!("[[{}, 2, 3]]", i64::MIN); assert_eq!( - Z::from_str(&entry).unwrap(), + Z::from(i64::MIN), VecZ::from_str(&vector_string) .unwrap() .get_entry(0) @@ -201,7 +204,7 @@ mod test_from_str { let vector_string = String::from("[[ 1],[2 ],[ 3 ]]"); assert_eq!( - Z::from_i64(1), + Z::ONE, VecZ::from_str(&vector_string) .unwrap() .get_entry(0) diff --git a/src/integer/vec_z/get.rs b/src/integer/vec_z/get.rs index c215ff4bd..803ccb1d5 100644 --- a/src/integer/vec_z/get.rs +++ b/src/integer/vec_z/get.rs @@ -40,13 +40,13 @@ impl VecZ { /// if the given entry is outside the vector or negative. pub fn get_entry(&self, entry: impl TryInto + Display + Copy) -> Result { if self.is_column_vector() { - self.matrix.get_entry(entry, 0) + self.vector.get_entry(entry, 0) } else { - self.matrix.get_entry(0, entry) + self.vector.get_entry(0, entry) } } - /// Returns `true` if the vector is a row vector with more than one column. + /// Returns `true` if the vector has only one row. /// /// # Example /// ``` @@ -58,10 +58,10 @@ impl VecZ { /// assert!(row.is_row_vector()); /// ``` pub fn is_row_vector(&self) -> bool { - self.matrix.get_num_columns() > 1 + self.vector.get_num_rows() == 1 } - /// Returns `true` if the vector is a column vector with more than one row. + /// Returns `true` if the vector has only one column. /// /// # Example /// ``` @@ -73,11 +73,11 @@ impl VecZ { /// assert!(col.is_column_vector()); /// ``` pub fn is_column_vector(&self) -> bool { - self.matrix.get_num_rows() > 1 + self.vector.get_num_columns() == 1 } - /// Returns [`VectorDirection::ColumnVector`] if the vector is a column vector with more than one row. - /// Otherwise, returns [`VectorDirection::RowVector`]. + /// Returns [`VectorDirection::RowVector`] if the vector has only one row. + /// Otherwise, returns [`VectorDirection::ColumnVector`]. /// /// # Example /// ``` @@ -92,10 +92,10 @@ impl VecZ { /// assert_eq!(VectorDirection::ColumnVector, col.get_direction()); /// ``` pub fn get_direction(&self) -> VectorDirection { - if self.is_column_vector() { - VectorDirection::ColumnVector - } else { + if self.is_row_vector() { VectorDirection::RowVector + } else { + VectorDirection::ColumnVector } } } @@ -185,8 +185,8 @@ mod test_directions { fn one_by_one_detection() { let vec = VecZ::new(1, VectorDirection::ColumnVector).unwrap(); - assert!(!vec.is_row_vector()); - assert!(!vec.is_column_vector()); + assert!(vec.is_row_vector()); + assert!(vec.is_column_vector()); assert_eq!(vec.get_direction(), VectorDirection::RowVector); // returns RowVector as standard as this answer is not wrong for a one times one matrix } diff --git a/src/integer/vec_z/mul.rs b/src/integer/vec_z/mul.rs index a51be3e8f..f263390dc 100644 --- a/src/integer/vec_z/mul.rs +++ b/src/integer/vec_z/mul.rs @@ -39,8 +39,11 @@ impl Mul for &VecZ { /// let _ = &a * b.clone(); /// let _ = a.clone() * &b; /// ``` + /// + /// # Errors and Failures + /// - Panics if the dimensions of `self` and `other` do not match for multiplication. fn mul(self, other: Self) -> Self::Output { - (&self.matrix).mul(other) + (&self.vector).mul(other) } } @@ -71,8 +74,11 @@ impl Mul<&MatZ> for &VecZ { /// let e = &a * b.clone(); /// let f = a.clone() * &b; /// ``` + /// + /// # Errors and Failures + /// - Panics if the dimensions of `self` and `other` do not match for multiplication. fn mul(self, other: &MatZ) -> Self::Output { - (&self.matrix).mul(other) + (&self.vector).mul(other) } } diff --git a/src/integer/vec_z/set.rs b/src/integer/vec_z/set.rs index 49623dc42..31ee8f39d 100644 --- a/src/integer/vec_z/set.rs +++ b/src/integer/vec_z/set.rs @@ -65,9 +65,9 @@ impl VecZ { value: &Z, ) -> Result<(), MathError> { if self.is_column_vector() { - self.matrix.set_entry_ref_z(entry, 0, value)?; + self.vector.set_entry_ref_z(entry, 0, value)?; } else { - self.matrix.set_entry_ref_z(0, entry, value)?; + self.vector.set_entry_ref_z(0, entry, value)?; } Ok(()) diff --git a/src/integer/vec_z/transpose.rs b/src/integer/vec_z/transpose.rs index 09d033f18..ccdb7779c 100644 --- a/src/integer/vec_z/transpose.rs +++ b/src/integer/vec_z/transpose.rs @@ -11,7 +11,8 @@ use super::VecZ; impl VecZ { - /// Returns the transposed form of the given matrix, i.e. rows get transformed to columns. + /// Returns the transposed form of the given matrix, + /// i.e. rows get transformed to columns and vice versa. /// /// # Example /// ``` @@ -25,7 +26,7 @@ impl VecZ { /// ``` pub fn transpose(&self) -> VecZ { Self { - matrix: self.matrix.transpose(), + vector: self.vector.transpose(), } } } @@ -41,8 +42,8 @@ mod test_transpose { /// Checks if a vector is correctly converted to a one-row matrix #[test] fn column_to_row() { - let vec = VecZ::from_str("[[1],[2],[3]]").unwrap(); - let cmp = VecZ::from_str("[[1,2,3]]").unwrap(); + let vec = VecZ::from_str("[[1],[2],[3],[4]]").unwrap(); + let cmp = VecZ::from_str("[[1,2,3,4]]").unwrap(); assert_eq!(cmp, vec.transpose()); } @@ -50,8 +51,8 @@ mod test_transpose { /// Checks if a vector is correctly converted to a one-column matrix #[test] fn row_to_column() { - let vec = VecZ::from_str("[[1,2,3]]").unwrap(); - let cmp = VecZ::from_str("[[1],[2],[3]]").unwrap(); + let vec = VecZ::from_str("[[1,2,3,4,5]]").unwrap(); + let cmp = VecZ::from_str("[[1],[2],[3],[4],[5]]").unwrap(); assert_eq!(cmp, vec.transpose()); } diff --git a/src/utils/parse.rs b/src/utils/parse.rs index 3857f5cbf..b8b9a75a2 100644 --- a/src/utils/parse.rs +++ b/src/utils/parse.rs @@ -1,4 +1,4 @@ -// Copyright © 2023 Marcel Luca Schmidt, Niklas Siemer +// Copyright © 2023 Marcel Luca Schmidt // // This file is part of qFALL-math. // From 86c04e80995a97f6889efc7b80517321ed5f6bb9 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Wed, 29 Mar 2023 13:23:15 +0200 Subject: [PATCH 14/15] Remove vectors --- src/error.rs | 5 - src/integer.rs | 2 - src/integer/mat_z/mul.rs | 70 +-------- src/integer/vec_z.rs | 29 ---- src/integer/vec_z/from.rs | 266 --------------------------------- src/integer/vec_z/get.rs | 193 ------------------------ src/integer/vec_z/mul.rs | 135 ----------------- src/integer/vec_z/set.rs | 108 ------------- src/integer/vec_z/transpose.rs | 59 -------- src/macros/arithmetics.rs | 77 +--------- src/utils.rs | 2 - src/utils/dimensions.rs | 6 - 12 files changed, 7 insertions(+), 945 deletions(-) delete mode 100644 src/integer/vec_z.rs delete mode 100644 src/integer/vec_z/from.rs delete mode 100644 src/integer/vec_z/get.rs delete mode 100644 src/integer/vec_z/mul.rs delete mode 100644 src/integer/vec_z/set.rs delete mode 100644 src/integer/vec_z/transpose.rs diff --git a/src/error.rs b/src/error.rs index 692c73e86..0929ff3db 100644 --- a/src/error.rs +++ b/src/error.rs @@ -38,8 +38,6 @@ use thiserror::Error; /// not formatted correctly. /// - `InvalidStringToQInput` is thrown if an invalid string is given to /// construct a [`Q`](crate::rational::Q) -/// - `InvalidStringToVectorInput` is thrown if a valid input string for a matrix was given, -/// but it was not valid as a string input for a vector, i.e. no matrix dimension was 1 /// - `InvalidStringToZInput` is thrown if an invalid string is given to /// construct a [`Z`](crate::integer::Z) /// - `InvalidStringToZqInput` is thrown if an invalid string is given to @@ -111,9 +109,6 @@ pub enum MathError { /// parse string to [`Q`](crate::rational::Q) error #[error("invalid string input to parse to Q {0}")] InvalidStringToQInput(String), - /// parse string to [`VecZ`](crate::integer::VecZ) or other vectors error - #[error("invalid string input to parse to a vector (no dimension is 1) {0}")] - InvalidStringToVectorInput(String), /// parse string to [`Z`](crate::integer::Z) error #[error("invalid string input to parse to Z {0}")] InvalidStringToZInput(String), diff --git a/src/integer.rs b/src/integer.rs index d62d5a46f..e3c23dae6 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -12,11 +12,9 @@ mod mat_poly_over_z; mod mat_z; mod poly_over_z; -mod vec_z; mod z; pub use mat_poly_over_z::MatPolyOverZ; pub use mat_z::MatZ; pub use poly_over_z::PolyOverZ; -pub use vec_z::VecZ; pub use z::Z; diff --git a/src/integer/mat_z/mul.rs b/src/integer/mat_z/mul.rs index 4235483db..f91a65965 100644 --- a/src/integer/mat_z/mul.rs +++ b/src/integer/mat_z/mul.rs @@ -10,10 +10,8 @@ use super::MatZ; use crate::{ - integer::VecZ, macros::arithmetics::{ - arithmetic_trait_borrowed_to_owned, arithmetic_trait_cross_types_borrowed_to_owned, - arithmetic_trait_cross_types_mixed_borrowed_owned, arithmetic_trait_mixed_borrowed_owned, + arithmetic_trait_borrowed_to_owned, arithmetic_trait_mixed_borrowed_owned, }, traits::{GetNumColumns, GetNumRows}, }; @@ -62,51 +60,16 @@ impl Mul for &MatZ { arithmetic_trait_borrowed_to_owned!(Mul, mul, MatZ); arithmetic_trait_mixed_borrowed_owned!(Mul, mul, MatZ); -impl Mul<&VecZ> for &MatZ { - type Output = MatZ; - - /// Implements the [`Mul`] trait for a [`MatZ`] multiplied with a [`VecZ`] vector. - /// [`Mul`] is implemented for any combination of owned and borrowed [`MatZ`] and any kind of [`VecZ`]. - /// - /// Parameters: - /// - `other`: specifies the [`VecZ`] value to multiply with `self` - /// - /// Returns the product of `self` and `other` as a [`MatZ`]. - /// - /// # Example - /// ``` - /// use math::integer::{MatZ, VecZ}; - /// use std::str::FromStr; - /// - /// let a = MatZ::from_str("[[2,1],[1,2]]").unwrap(); - /// let b = VecZ::from_str("[[1],[0]]").unwrap(); - /// - /// let _ = &a * &b; - /// let _ = a.clone() * b.clone(); - /// let _ = &a * b.clone(); - /// let _ = a.clone() * &b; - /// ``` - /// - /// # Errors and Failures - /// - Panics if the dimensions of `self` and `other` do not match for multiplication. - fn mul(self, other: &VecZ) -> Self::Output { - self.mul(&other.vector) - } -} - -arithmetic_trait_cross_types_borrowed_to_owned!(Mul, mul, MatZ, VecZ, MatZ); -arithmetic_trait_cross_types_mixed_borrowed_owned!(Mul, mul, MatZ, VecZ, MatZ); - #[cfg(test)] mod test_mul { - use super::{MatZ, VecZ}; + use super::MatZ; use crate::integer::Z; use std::str::FromStr; /// Checks if matrix multiplication works fine for sqaured matrices #[test] - fn matrix_mul_matrix_square_correctness() { + 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(); @@ -118,7 +81,7 @@ mod test_mul { /// Checks if matrix multiplication works fine for matrices of different dimensions #[test] - fn matrix_mul_matrix_different_dimensions_correctness() { + 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(); @@ -128,7 +91,7 @@ mod test_mul { /// Checks if matrix multiplication works fine for large entries #[test] - fn matrix_mul_large_entries() { + 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(); @@ -142,31 +105,10 @@ mod test_mul { /// results in panic #[test] #[should_panic] - fn matrix_mul_matrix_incompatible_dimensions() { + 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; } - - /// Checks if cross-type multiplications works fine for [`MatZ`] * [`VecZ`] - #[test] - fn matrix_mul_vector_correctness() { - let mat = MatZ::from_str("[[2,1],[1,2]]").unwrap(); - let vec = VecZ::from_str("[[1],[0]]").unwrap(); - let cmp = MatZ::from_str("[[2],[1]]").unwrap(); - - assert_eq!(cmp, &mat * &vec); - } - - /// Checks if multiplication of [`MatZ`] and [`VecZ`] with incompatible dimensions - /// results in panic - #[test] - #[should_panic] - fn matrix_mul_vector_incompatible_dimensions() { - let mat = MatZ::from_str("[[2,1],[1,2]]").unwrap(); - let vec = VecZ::from_str("[[1],[0],[1]]").unwrap(); - - let _ = mat * vec; - } } diff --git a/src/integer/vec_z.rs b/src/integer/vec_z.rs deleted file mode 100644 index 1c63c2cb8..000000000 --- a/src/integer/vec_z.rs +++ /dev/null @@ -1,29 +0,0 @@ -// 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 . - -//! [`VecZ`] is a vector with integer entries of arbitrary length. -//! This implementation uses the [FLINT](https://flintlib.org/) library. - -mod from; -mod get; -mod mul; -mod set; -mod transpose; - -use super::MatZ; - -/// [`VecZ`] is a vector with entries of type [`Z`](crate::integer::Z). -/// -/// Attributes: -/// - `matrix`: holds a [`MatZ`] instance with either #rows or #columns being `1` -/// -/// # Examples -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct VecZ { - pub(crate) vector: MatZ, -} diff --git a/src/integer/vec_z/from.rs b/src/integer/vec_z/from.rs deleted file mode 100644 index c8e190c2a..000000000 --- a/src/integer/vec_z/from.rs +++ /dev/null @@ -1,266 +0,0 @@ -// 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 . - -//! Implementations to create a [`VecZ`](crate::integer::VecZ) vector from other types. -//! For each reasonable type, an explicit function with the format -//! `from_` and the [`From`] trait should be implemented. -//! Furthermore, an instantiation of a zero matrix is implemented. -//! -//! The explicit functions contain the documentation. - -use super::{MatZ, VecZ}; -use crate::error::MathError; -use crate::traits::{GetNumColumns, GetNumRows}; -use crate::utils::VectorDirection; -use std::{fmt::Display, str::FromStr}; - -impl VecZ { - /// Creates a new vector with `num_entries` entries and - /// zeros as entries. - /// - /// Parameters: - /// - `num_entries`: number of rows or columns the new vector should have. - /// - `orientation`: defines the orientation the vector should have. - /// [`VectorDirection::ColumnVector`] and [`VectorDirection::RowVector`] - /// are valid inputs. - /// - /// Returns a [`VecZ`] or an error, if the number of entries is - /// less or equal to `0`. - /// - /// # Example - /// ``` - /// use math::integer::VecZ; - /// use math::utils::VectorDirection; - /// - /// let vector = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type - /// [`InvalidMatrix`](MathError::InvalidMatrix) - /// if the number of rows/columns is `0`. - /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) - /// if the number of rows/columns is negative or it does not fit into an [`i64`]. - pub fn new( - num_entries: impl TryInto + Display + Copy, - orientation: VectorDirection, - ) -> Result { - match orientation { - VectorDirection::RowVector => Ok(VecZ { - vector: MatZ::new(1, num_entries)?, - }), - VectorDirection::ColumnVector => Ok(VecZ { - vector: MatZ::new(num_entries, 1)?, - }), - } - } -} - -impl FromStr for VecZ { - type Err = MathError; - - /// Creates a [`VecZ`] vector with entries in [`Z`] from a [`String`]. - /// The format of that string looks like this `[[1],[2],[3]]` for a column vector - /// with three entries (`1` in the first row, `2` in the second one, ...). - /// `[[1,2,3]]` is the correct format for a row vector. - /// - /// Parameters: - /// - `string`: the vector representation as a string - /// - /// Returns a [`VecZ`] or an error, if the vector is not formatted in a suitable way, - /// the number of entries is too big (must fit into [`i64`]), or if the regular expression - /// inside of the function could not be processed. - /// - /// # Examples - /// Column Vector - /// ``` - /// use math::integer::VecZ; - /// use std::str::FromStr; - /// - /// let string = String::from("[[1],[2],[3]]"); - /// let vector = VecZ::from_str(&string).unwrap(); - /// ``` - /// Row Vector - /// ``` - /// use math::integer::VecZ; - /// use std::str::FromStr; - /// - /// let string = String::from("[[1,2,3]]"); - /// let vector = VecZ::from_str(&string).unwrap(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`InvalidMatrix`](MathError::InvalidMatrix) - /// if the vector is not formatted in a suitable way, - /// the number of rows/columns is too big (must fit into [`i64`]). - /// - Returns a [`MathError`] of type - /// [`InvalidStringToCStringInput`](MathError::InvalidStringToCStringInput) - /// if an entry contains a Nul byte. - /// - Returns a [`MathError`] of type - /// [`InvalidStringToVectorInput`](MathError::InvalidStringToVectorInput) - /// if no dimension or the matrix is `1`. - /// [`InvalidStringToZInput`](MathError::InvalidStringToZInput) - /// if an entry is not formatted correctly. - fn from_str(string: &str) -> Result { - let matrix = MatZ::from_str(string)?; - - if matrix.get_num_rows() == 1 || matrix.get_num_columns() == 1 { - Ok(VecZ { vector: matrix }) - } else { - Err(MathError::InvalidStringToVectorInput(String::from( - "The string either contained more than one column and row, or included zero entries", - ))) - } - } -} - -#[cfg(test)] -mod test_new { - - use crate::integer::{VecZ, Z}; - use crate::utils::VectorDirection; - - /// Ensure that entries of a new vector are `0`. - #[test] - fn entry_zero() { - let matrix = VecZ::new(2, VectorDirection::ColumnVector).unwrap(); - - let entry1 = matrix.get_entry(0).unwrap(); - let entry2 = matrix.get_entry(0).unwrap(); - - assert_eq!(Z::ZERO, entry1); - assert_eq!(Z::ZERO, entry2); - } - - /// Ensure that a new zero vector fails with `0` or a negative value as input. - #[test] - fn error_zero_negative() { - let matrix1 = VecZ::new(0, VectorDirection::ColumnVector); - let matrix2 = VecZ::new(-1, VectorDirection::ColumnVector); - - assert!(matrix1.is_err()); - assert!(matrix2.is_err()); - } -} - -#[cfg(test)] -mod test_from_str { - - use crate::integer::{VecZ, Z}; - use crate::utils::VectorDirection; - use std::str::FromStr; - - /// Ensure that initialization works in both directions - #[test] - fn directions() { - let row = VecZ::from_str("[[1, 2, 3]]").unwrap(); - let col = VecZ::from_str("[[1],[2],[3]]").unwrap(); - - assert_eq!(Z::ONE, row.get_entry(0).unwrap()); - assert!(row.is_row_vector()); - assert_eq!(VectorDirection::RowVector, row.get_direction()); - - assert_eq!(Z::ONE, col.get_entry(0).unwrap()); - assert!(col.is_column_vector()); - assert_eq!(VectorDirection::ColumnVector, col.get_direction()); - } - - /// Ensure that initialization with positive numbers that are larger than [`i64`] works. - #[test] - fn large_numbers() { - let vector_string = format!("[[{}, 2, 3]]", u64::MAX); - - assert_eq!( - Z::from(u64::MAX), - VecZ::from_str(&vector_string) - .unwrap() - .get_entry(0) - .unwrap() - ); - } - - /// Ensure that initialization with negative numbers that are larger than [`i64`] works. - #[test] - fn small_numbers() { - let vector_string = format!("[[{}, 2, 3]]", i64::MIN); - - assert_eq!( - Z::from(i64::MIN), - VecZ::from_str(&vector_string) - .unwrap() - .get_entry(0) - .unwrap() - ); - } - - /// Ensure that entries can have leading and trailing whitespaces. - #[test] - fn whitespaces_in_entries_works() { - let vector_string = String::from("[[ 1],[2 ],[ 3 ]]"); - - assert_eq!( - Z::ONE, - VecZ::from_str(&vector_string) - .unwrap() - .get_entry(0) - .unwrap() - ); - } - - /// Ensure that entries are set correctly for a row vector - #[test] - fn entries_set_correctly_row() { - let vector_string = format!("[[0,{},{}, -10, 10]]", i64::MIN, i64::MAX); - - let vector = VecZ::from_str(&vector_string).unwrap(); - - assert_eq!(vector.get_entry(0).unwrap(), 0.into()); - assert_eq!(vector.get_entry(1).unwrap(), i64::MIN.into()); - assert_eq!(vector.get_entry(2).unwrap(), i64::MAX.into()); - assert_eq!(vector.get_entry(3).unwrap(), (-10).into()); - assert_eq!(vector.get_entry(4).unwrap(), 10.into()); - } - - /// Ensure that entries are set correctly for a column vector - #[test] - fn entries_set_correctly_col() { - let vector_string = format!("[[0],[{}],[{}],[-10],[10]]", i64::MIN, i64::MAX); - - let vector = VecZ::from_str(&vector_string).unwrap(); - - assert_eq!(vector.get_entry(0).unwrap(), 0.into()); - assert_eq!(vector.get_entry(1).unwrap(), i64::MIN.into()); - assert_eq!(vector.get_entry(2).unwrap(), i64::MAX.into()); - assert_eq!(vector.get_entry(3).unwrap(), (-10).into()); - assert_eq!(vector.get_entry(4).unwrap(), 10.into()); - } - - /// Ensure that a wrong format causes an error. - #[test] - fn wrong_format_error() { - let vector_string1 = String::from("[1, 2, 3],"); - let vector_string2 = String::from("[1, 2, 3]]"); - let vector_string3 = String::from("[[1, 2, 3],3, 4, 5]"); - let vector_string4 = String::from("[1, 2, 3, 4,, 5]"); - let vector_string5 = String::from("[[1, 2, 3],[3, 4, 5]]"); - let vector_string6 = String::from("[[1, 2, 38,]"); - let vector_string7 = String::from(""); - let vector_string8 = String::from("[]"); - let vector_string9 = String::from("[[]]"); - - assert!(VecZ::from_str(&vector_string1).is_err()); - assert!(VecZ::from_str(&vector_string2).is_err()); - assert!(VecZ::from_str(&vector_string3).is_err()); - assert!(VecZ::from_str(&vector_string4).is_err()); - assert!(VecZ::from_str(&vector_string5).is_err()); - assert!(VecZ::from_str(&vector_string6).is_err()); - assert!(VecZ::from_str(&vector_string7).is_err()); - assert!(VecZ::from_str(&vector_string8).is_err()); - assert!(VecZ::from_str(&vector_string9).is_err()); - } -} diff --git a/src/integer/vec_z/get.rs b/src/integer/vec_z/get.rs deleted file mode 100644 index 803ccb1d5..000000000 --- a/src/integer/vec_z/get.rs +++ /dev/null @@ -1,193 +0,0 @@ -// 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 . - -//! Implementations to get entries from a [`VecZ`] matrix. - -use super::VecZ; -use crate::{ - error::MathError, - integer::Z, - traits::{GetEntry, GetNumColumns, GetNumRows}, - utils::VectorDirection, -}; -use std::fmt::Display; - -impl VecZ { - /// Outputs the [`Z`] value of a specific vector entry. - /// - /// Parameters: - /// - `entry`: specifies the entry in which the entry is located - /// - /// Returns the [`Z`] value of the vector at the position of the given - /// entry or an error, if the given entry is greater than the vector or negative. - /// - /// # Example - /// ``` - /// use math::integer::VecZ; - /// use math::utils::VectorDirection; - /// - /// let matrix = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); - /// let entry = matrix.get_entry(1).unwrap(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) - /// if the given entry is outside the vector or negative. - pub fn get_entry(&self, entry: impl TryInto + Display + Copy) -> Result { - if self.is_column_vector() { - self.vector.get_entry(entry, 0) - } else { - self.vector.get_entry(0, entry) - } - } - - /// Returns `true` if the vector has only one row. - /// - /// # Example - /// ``` - /// use math::integer::VecZ; - /// use std::str::FromStr; - /// - /// let row = VecZ::from_str("[[1,2,3]]").unwrap(); - /// - /// assert!(row.is_row_vector()); - /// ``` - pub fn is_row_vector(&self) -> bool { - self.vector.get_num_rows() == 1 - } - - /// Returns `true` if the vector has only one column. - /// - /// # Example - /// ``` - /// use math::integer::VecZ; - /// use std::str::FromStr; - /// - /// let col = VecZ::from_str("[[1],[2],[3]]").unwrap(); - /// - /// assert!(col.is_column_vector()); - /// ``` - pub fn is_column_vector(&self) -> bool { - self.vector.get_num_columns() == 1 - } - - /// Returns [`VectorDirection::RowVector`] if the vector has only one row. - /// Otherwise, returns [`VectorDirection::ColumnVector`]. - /// - /// # Example - /// ``` - /// use math::integer::VecZ; - /// use std::str::FromStr; - /// use math::utils::VectorDirection; - /// - /// let row = VecZ::from_str("[[1,2,3]]").unwrap(); - /// let col = VecZ::from_str("[[1],[2],[3]]").unwrap(); - /// - /// assert_eq!(VectorDirection::RowVector, row.get_direction()); - /// assert_eq!(VectorDirection::ColumnVector, col.get_direction()); - /// ``` - pub fn get_direction(&self) -> VectorDirection { - if self.is_row_vector() { - VectorDirection::RowVector - } else { - VectorDirection::ColumnVector - } - } -} - -#[cfg(test)] -mod test_get_entry { - - use super::VecZ; - use super::Z; - use crate::utils::VectorDirection; - use std::str::FromStr; - - /// Ensure that getting the correct entries works fine for row vectors - #[test] - fn correct_entry_fetched_row() { - let vector = - VecZ::from_str(&format!("[[0, 10, -10, {}, {}]]", i64::MAX, i64::MIN)).unwrap(); - let zero: Z = 0.into(); - let ten: Z = 10.into(); - let minus_ten: Z = (-10).into(); - let max: Z = i64::MAX.into(); - let min: Z = i64::MIN.into(); - - assert_eq!(zero, vector.get_entry(0).unwrap()); - assert_eq!(ten, vector.get_entry(1).unwrap()); - assert_eq!(minus_ten, vector.get_entry(2).unwrap()); - assert_eq!(max, vector.get_entry(3).unwrap()); - assert_eq!(min, vector.get_entry(4).unwrap()); - } - - /// Ensure that getting the correct entries works fine for column vectors - #[test] - fn correct_entry_fetched_col() { - let vector = - VecZ::from_str(&format!("[[0],[10],[-10],[{}],[{}]]", i64::MAX, i64::MIN)).unwrap(); - let zero: Z = 0.into(); - let ten: Z = 10.into(); - let minus_ten: Z = (-10).into(); - let max: Z = i64::MAX.into(); - let min: Z = i64::MIN.into(); - - assert_eq!(zero, vector.get_entry(0).unwrap()); - assert_eq!(ten, vector.get_entry(1).unwrap()); - assert_eq!(minus_ten, vector.get_entry(2).unwrap()); - assert_eq!(max, vector.get_entry(3).unwrap()); - assert_eq!(min, vector.get_entry(4).unwrap()); - } - - /// Ensure that a wrong number of rows yields an Error. - #[test] - fn error_out_bounds() { - let vector = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); - - assert!(vector.get_entry(5).is_err()); - assert!(vector.get_entry(-1).is_err()); - } -} - -#[cfg(test)] -mod test_directions { - - use super::VecZ; - use crate::utils::VectorDirection; - - /// Check whether a row vector is correctly detected - #[test] - fn row_detected() { - let vec = VecZ::new(5, VectorDirection::RowVector).unwrap(); - - assert!(vec.is_row_vector()); - assert!(!vec.is_column_vector()); - assert_eq!(vec.get_direction(), VectorDirection::RowVector); - } - - /// Check whether a column vector is correctly detected - #[test] - fn column_detected() { - let vec = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); - - assert!(!vec.is_row_vector()); - assert!(vec.is_column_vector()); - assert_eq!(vec.get_direction(), VectorDirection::ColumnVector); - } - - /// Check whether a 1x1 vector is neither of both - #[test] - fn one_by_one_detection() { - let vec = VecZ::new(1, VectorDirection::ColumnVector).unwrap(); - - assert!(vec.is_row_vector()); - assert!(vec.is_column_vector()); - assert_eq!(vec.get_direction(), VectorDirection::RowVector); - // returns RowVector as standard as this answer is not wrong for a one times one matrix - } -} diff --git a/src/integer/vec_z/mul.rs b/src/integer/vec_z/mul.rs deleted file mode 100644 index f263390dc..000000000 --- a/src/integer/vec_z/mul.rs +++ /dev/null @@ -1,135 +0,0 @@ -// 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 [`VecZ`] values. - -use super::{MatZ, VecZ}; -use crate::macros::arithmetics::{ - arithmetic_trait_cross_types_borrowed_to_owned, - arithmetic_trait_cross_types_mixed_borrowed_owned, -}; -use std::ops::Mul; - -impl Mul for &VecZ { - type Output = MatZ; - - /// Implements the [`Mul`] trait for two [`VecZ`] values. - /// [`Mul`] is implemented for any combination of owned and borrowed [`MatZ`] and any kind of [`VecZ`]. - /// - /// Parameters: - /// - `other`: specifies the value to multiply with `self` - /// - /// Returns the product of `self` and `other` as a [`MatZ`]. - /// - /// # Example - /// ``` - /// use math::integer::VecZ; - /// use std::str::FromStr; - /// - /// let a = VecZ::from_str("[[2],[1],[1],[2]]").unwrap(); - /// let b = VecZ::from_str("[[1,0,0,1]]").unwrap(); - /// - /// let _ = &a * &b; - /// let _ = a.clone() * b.clone(); - /// let _ = &a * b.clone(); - /// let _ = a.clone() * &b; - /// ``` - /// - /// # Errors and Failures - /// - Panics if the dimensions of `self` and `other` do not match for multiplication. - fn mul(self, other: Self) -> Self::Output { - (&self.vector).mul(other) - } -} - -arithmetic_trait_cross_types_borrowed_to_owned!(Mul, mul, VecZ, VecZ, MatZ); -arithmetic_trait_cross_types_mixed_borrowed_owned!(Mul, mul, VecZ, VecZ, MatZ); - -impl Mul<&MatZ> for &VecZ { - type Output = MatZ; - - /// Implements the [`Mul`] trait for a [`VecZ`] multiplied with a [`MatZ`] vector. - /// [`Mul`] is implemented for any combination of owned and borrowed [`MatZ`] and any kind of [`VecZ`]. - /// - /// Parameters: - /// - `other`: specifies the [`MatZ`] value to multiply with `self` - /// - /// Returns the product of `self` and `other` as a [`MatZ`]. - /// - /// # Example - /// ``` - /// use math::integer::{MatZ, VecZ}; - /// use std::str::FromStr; - /// - /// let a = VecZ::from_str("[[1],[0]]").unwrap(); - /// let b = MatZ::from_str("[[2,1]]").unwrap(); - /// - /// let c = &a * &b; - /// let d = a.clone() * b.clone(); - /// let e = &a * b.clone(); - /// let f = a.clone() * &b; - /// ``` - /// - /// # Errors and Failures - /// - Panics if the dimensions of `self` and `other` do not match for multiplication. - fn mul(self, other: &MatZ) -> Self::Output { - (&self.vector).mul(other) - } -} - -arithmetic_trait_cross_types_borrowed_to_owned!(Mul, mul, VecZ, MatZ, MatZ); -arithmetic_trait_cross_types_mixed_borrowed_owned!(Mul, mul, VecZ, MatZ, MatZ); - -#[cfg(test)] -mod test_mul { - - use super::{MatZ, VecZ}; - use std::str::FromStr; - - /// Checks if vector multiplication works fine for vectors of same length - #[test] - fn vector_mul_vector_correctness() { - let vec_1 = VecZ::from_str("[[2,1,1,2]]").unwrap(); - let vec_2 = VecZ::from_str("[[1],[2],[2],[1]]").unwrap(); - let cmp = MatZ::from_str("[[8]]").unwrap(); - - assert_eq!(cmp, vec_1 * vec_2); - } - - /// Checks if vector multiplication with incompatible vector length - /// results in panic - #[test] - #[should_panic] - fn vector_mul_vector_incompatible_length() { - let vec_1 = VecZ::from_str("[[2,1,1,2]]").unwrap(); - let vec_2 = VecZ::from_str("[[1],[0],[0],[1],[0]]").unwrap(); - - let _ = vec_1 * vec_2; - } - - /// Checks if cross-type multiplications works fine for [`VecZ`] * [`MatZ`] - #[test] - fn vector_mul_matrix_correctness() { - let mat = MatZ::from_str("[[2,1]]").unwrap(); - let vec = VecZ::from_str("[[1],[0]]").unwrap(); - let cmp = MatZ::from_str("[[2,1],[0,0]]").unwrap(); - - assert_eq!(cmp, &vec * &mat); - } - - /// Checks if multiplication of [`VecZ`] * [`MatZ`]` with incompatible dimensions - /// results in panic - #[test] - #[should_panic] - fn vector_mul_matrix_incompatible_dimensions() { - let mat = MatZ::from_str("[[2,1],[1,2]]").unwrap(); - let vec = VecZ::from_str("[[1],[0],[1]]").unwrap(); - - let _ = vec * mat; - } -} diff --git a/src/integer/vec_z/set.rs b/src/integer/vec_z/set.rs deleted file mode 100644 index 31ee8f39d..000000000 --- a/src/integer/vec_z/set.rs +++ /dev/null @@ -1,108 +0,0 @@ -// 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 to set entries from a [`VecZ`] vector. - -use super::VecZ; -use crate::{error::MathError, integer::Z}; -use std::fmt::Display; - -impl VecZ { - /// Sets the value of a specific vector entry according to a given `value` of type [`Z`](crate::integer::Z). - /// - /// Parameters: - /// - `entry`: specifies the entry in which the entry is located - /// - `value`: specifies the value to which the entry is set - /// - /// # Example - /// ``` - /// use math::integer::{VecZ, Z}; - /// use math::utils::VectorDirection; - /// - /// let mut vector = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); - /// let value = Z::from_i64(5); - /// vector.set_entry(1, value).unwrap(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) - /// if the given entry outside the vector or negative. - pub fn set_entry + Display + Copy, T: Into>( - &mut self, - entry: S, - value: T, - ) -> Result<(), MathError> { - self.set_entry_ref_z(entry, &value.into()) - } - - /// Sets the value of a specific matrix entry according to a given `value` of type [`Z`]. - /// - /// Parameters: - /// - `entry`: specifies the entry in which the entry is located - /// - `value`: specifies the value to which the entry is set - /// - /// # Example - /// ``` - /// use math::integer::{VecZ, Z}; - /// use math::utils::VectorDirection; - /// - /// let mut vector = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); - /// let value = Z::from_i64(5); - /// vector.set_entry_ref_z(1, &value).unwrap(); - /// ``` - /// - /// # Errors and Failures - /// - Returns a [`MathError`] of type [`OutOfBounds`](MathError::OutOfBounds) - /// if the given entry outside the vector or negative. - pub fn set_entry_ref_z + Display + Copy>( - &mut self, - entry: S, - value: &Z, - ) -> Result<(), MathError> { - if self.is_column_vector() { - self.vector.set_entry_ref_z(entry, 0, value)?; - } else { - self.vector.set_entry_ref_z(0, entry, value)?; - } - - Ok(()) - } -} - -#[cfg(test)] -mod test_setter { - use super::Z; - use crate::integer::VecZ; - use crate::utils::VectorDirection; - - /// Ensure that setting the correct entries works fine - #[test] - fn correct_entry_set() { - let mut col = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); - let mut row = VecZ::new(5, VectorDirection::RowVector).unwrap(); - - col.set_entry(4, 869).unwrap(); - row.set_entry(3, 869).unwrap(); - - assert_eq!(col.get_entry(4).unwrap(), Z::from_i64(869)); - assert_eq!(row.get_entry(3).unwrap(), Z::from_i64(869)); - } - - /// Ensure that a wrong given row returns a [`MathError`](crate::error::MathError) - #[test] - fn error_wrong_row() { - let mut col = VecZ::new(5, VectorDirection::ColumnVector).unwrap(); - let mut row = VecZ::new(5, VectorDirection::RowVector).unwrap(); - let value = Z::from_i64(i64::MAX); - - assert!(col.set_entry_ref_z(5, &value).is_err()); - assert!(col.set_entry_ref_z(-1, &value).is_err()); - assert!(row.set_entry_ref_z(5, &value).is_err()); - assert!(row.set_entry_ref_z(-1, &value).is_err()); - } -} diff --git a/src/integer/vec_z/transpose.rs b/src/integer/vec_z/transpose.rs deleted file mode 100644 index ccdb7779c..000000000 --- a/src/integer/vec_z/transpose.rs +++ /dev/null @@ -1,59 +0,0 @@ -// 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 super::VecZ; - -impl VecZ { - /// Returns the transposed form of the given matrix, - /// i.e. rows get transformed to columns and vice versa. - /// - /// # Example - /// ``` - /// use math::integer::VecZ; - /// use std::str::FromStr; - /// - /// let mat = VecZ::from_str("[[1,2,3]]").unwrap(); - /// let cmp = VecZ::from_str("[[1],[2],[3]]").unwrap(); - /// - /// assert_eq!(mat.transpose(), cmp); - /// ``` - pub fn transpose(&self) -> VecZ { - Self { - vector: self.vector.transpose(), - } - } -} - -#[cfg(test)] -mod test_transpose { - /// Further tests regarding large or small entries are omitted - /// as they are part of the tests of the here called function - /// [`MatZ::transpose`](crate::integer::MatZ::transpose) - use super::VecZ; - use std::str::FromStr; - - /// Checks if a vector is correctly converted to a one-row matrix - #[test] - fn column_to_row() { - let vec = VecZ::from_str("[[1],[2],[3],[4]]").unwrap(); - let cmp = VecZ::from_str("[[1,2,3,4]]").unwrap(); - - assert_eq!(cmp, vec.transpose()); - } - - /// Checks if a vector is correctly converted to a one-column matrix - #[test] - fn row_to_column() { - let vec = VecZ::from_str("[[1,2,3,4,5]]").unwrap(); - let cmp = VecZ::from_str("[[1],[2],[3],[4],[5]]").unwrap(); - - assert_eq!(cmp, vec.transpose()); - } -} diff --git a/src/macros/arithmetics.rs b/src/macros/arithmetics.rs index 3f884b2f3..2a448fbb9 100644 --- a/src/macros/arithmetics.rs +++ b/src/macros/arithmetics.rs @@ -1,4 +1,4 @@ -// Copyright © 2023 Phil Milewski, Niklas Siemer +// Copyright © 2023 Phil Milewski // // This file is part of qFALL-math. // @@ -78,78 +78,3 @@ macro_rules! arithmetic_trait_mixed_borrowed_owned { } pub(crate) use arithmetic_trait_mixed_borrowed_owned; - -/// Implements the [`*trait*`] for [`*type*<*other_type*>`] using the -/// [`*trait<*other_type*>*`] for [`&*type*`]. -/// -/// Parameters: -/// - `trait`: the trait that is implemented (e.g. [`Add`], [`Sub`], ...). -/// - `trait_function`: the function the trait implements -/// (e.g. add for [`Add`], ...). -/// - `type`: the type the trait is implemented for (e.g. [`Z`], [`Q`]) -/// - `other_type`: the other type that the arithm. operation is applied with -/// - `out_type`: the type that the `Output` should have -/// -/// Returns the owned Implementation code for the [`*trait*`] -/// trait with the signature: -/// -/// ```impl *trait*<*other_type*> for *type*``` -macro_rules! arithmetic_trait_cross_types_borrowed_to_owned { - ($trait:ident, $trait_function:ident, $type:ident, $other_type:ident, $out_type:ident) => { - impl $trait<$other_type> for $type { - type Output = $out_type; - - paste::paste! { - #[doc = "Documentation at [`" $type "::" $trait_function "`]."] - fn $trait_function(self, other: $other_type) -> Self::Output { - (&self).$trait_function(&other) - } - } - } - }; -} - -pub(crate) use arithmetic_trait_cross_types_borrowed_to_owned; - -/// Implements the [`*trait*<*other_type*>`] for owned [`*type*`] on borrowed [`*type*`] and -/// reverse using the [`*trait*<*other_type*>`] for [`&*type*`]. -/// -/// Parameters: -/// - `trait`: the trait that is implemented (e.g. [`Add`], [`Sub`], ...). -/// - `trait_function`: the function the trait implements -/// (e.g. add for [`Add`], ...). -/// - `type`: the type the trait is implemented for (e.g. [`Z`], [`Q`], ...). -/// - `other_type`: the other type that the arithm. operation is applied with -/// - `out_type`: the type that the `Output` should have -/// -/// Returns the mixed owned and borrowed Implementation code for the -/// [`*trait*`] trait with the signatures: -/// -/// ```impl *trait*<&*other_type*> for *type*``` -/// -/// ```impl *trait*<*other_type*> for &*type*``` -macro_rules! arithmetic_trait_cross_types_mixed_borrowed_owned { - ($trait:ident, $trait_function:ident, $type:ident, $other_type:ident, $out_type:ident) => { - impl $trait<$other_type> for &$type { - type Output = $out_type; - paste::paste! { - #[doc = "Documentation at [`" $type "::" $trait_function "`]."] - fn $trait_function(self, other: $other_type) -> Self::Output { - self.$trait_function(&other) - } - } - } - - impl $trait<&$other_type> for $type { - type Output = $out_type; - paste::paste! { - #[doc = "Documentation at [`" $type "::" $trait_function "`]."] - fn $trait_function(self, other: &$other_type) -> Self::Output { - (&self).$trait_function(other) - } - } - } - }; -} - -pub(crate) use arithmetic_trait_cross_types_mixed_borrowed_owned; diff --git a/src/utils.rs b/src/utils.rs index 34f11ad4e..36bbb873a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -14,5 +14,3 @@ pub mod coordinate; pub mod dimensions; pub mod parse; - -pub use dimensions::VectorDirection; diff --git a/src/utils/dimensions.rs b/src/utils/dimensions.rs index e8d198e89..9795d12de 100644 --- a/src/utils/dimensions.rs +++ b/src/utils/dimensions.rs @@ -8,12 +8,6 @@ //! Implements methods for finding matrix dimensions and enums for detecting vector directions. -#[derive(Debug, PartialEq)] -pub enum VectorDirection { - RowVector, - ColumnVector, -} - use crate::error::MathError; /// Returns the dimensions of a matrix. From be011edb753edbb22929b9895d89db7647ba7992 Mon Sep 17 00:00:00 2001 From: Jan Niklas Siemer Date: Wed, 29 Mar 2023 13:39:48 +0200 Subject: [PATCH 15/15] Update doc comment to remove VecZ --- src/integer/mat_z/mul.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/integer/mat_z/mul.rs b/src/integer/mat_z/mul.rs index f91a65965..2d32920f3 100644 --- a/src/integer/mat_z/mul.rs +++ b/src/integer/mat_z/mul.rs @@ -22,7 +22,8 @@ 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`] and any kind of [`VecZ`]. + /// + /// [`Mul`] is implemented for any combination of owned and borrowed [`MatZ`]. /// /// Parameters: /// - `other`: specifies the value to multiply with `self`