diff --git a/src/integer_mod_q/mat_polynomial_ring_zq/set.rs b/src/integer_mod_q/mat_polynomial_ring_zq/set.rs index 38d602d5..de96ba87 100644 --- a/src/integer_mod_q/mat_polynomial_ring_zq/set.rs +++ b/src/integer_mod_q/mat_polynomial_ring_zq/set.rs @@ -9,7 +9,7 @@ //! Implementation to set elements of a [`MatPolynomialRingZq`] matrix. use super::MatPolynomialRingZq; -use crate::integer_mod_q::PolynomialRingZq; +use crate::integer_mod_q::{Modulus, ModulusPolynomialRingZq, PolynomialRingZq}; use crate::macros::for_others::implement_for_owned; use crate::traits::{MatrixSetSubmatrix, MatrixSwaps}; use crate::{error::MathError, integer::PolyOverZ, traits::MatrixSetEntry}; @@ -381,6 +381,60 @@ impl MatPolynomialRingZq { pub fn reverse_rows(&mut self) { self.matrix.reverse_rows() } + + /// Changes the modulus of the given matrix to the new modulus. + /// It takes the representation of each entry with coefficients in [0, q) as the new + /// matrix entries and reduces them by the new [`ModulusPolynomialRingZq`]. + /// + /// Parameters: + /// - `modulus`: the new modulus of the matrix + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq}; + /// use std::str::FromStr; + /// let modulus0 = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap(); + /// let modulus1 = ModulusPolynomialRingZq::from_str("3 1 0 1 mod 19").unwrap(); + /// + /// let mut matrix = MatPolynomialRingZq::new(4, 3, modulus0); + /// + /// matrix.change_modulus(modulus1); + /// ``` + /// + /// # Panics ... + /// - if `modulus` is smaller than `2`, or + /// - if the modulus polynomial is of degree smaller than `1`. + /// - if the leading coefficient is not `1.` + pub fn change_modulus(&mut self, modulus: impl Into) { + self.modulus = modulus.into(); + self.reduce(); + } + + /// Changes the modulus `q` of the given matrix to the new modulus `q`. + /// It takes the representation of each entry with coefficients in `[0, q)` as the new + /// matrix entries and reduces them by the new [`Modulus`]. + /// + /// Parameters: + /// - `q`: the new modulus of the matrix + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq}; + /// use std::str::FromStr; + /// + /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap(); + /// + /// let mut matrix = MatPolynomialRingZq::new(4, 3, modulus); + /// + /// matrix.change_q(19); + /// ``` + /// + /// # Panics ... + /// - if `modulus` is smaller than `2`. + pub fn change_q(&mut self, q: impl Into) { + self.modulus.change_q(q); + self.reduce(); + } } #[cfg(test)] @@ -1029,3 +1083,124 @@ mod test_set_submatrix { assert!(mat1.set_submatrix(0, 0, &mat2.clone(), 0, 9, 0, 9).is_err()); } } + +#[cfg(test)] +mod test_change_modulus { + use super::MatPolynomialRingZq; + use crate::integer_mod_q::ModulusPolynomialRingZq; + use std::str::FromStr; + + /// Ensures that the modulus is changed correctly. + #[test] + fn modulus_correct() { + let mut matrix = MatPolynomialRingZq::from_str( + "[[1 1, 1 2, 1 3],[1 4, 1 5, 1 6]] / 4 1 0 0 1 mod 7", + ) + .unwrap(); + let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 8").unwrap(); + + matrix.change_modulus(&modulus); + + assert_eq!( + "[[1 1, 1 2, 1 3],[1 4, 1 5, 1 6]] / 4 1 0 0 1 mod 8", + matrix.to_string() + ); + } + + /// Ensures that the modulus is changed correctly, if the modulus is big. + #[test] + fn big_modulus_correct() { + let mut matrix = MatPolynomialRingZq::from_str(&format!( + "[[1 1, 1 2, 1 3],[1 4, 1 5, 1 6]] / 4 1 0 0 1 mod {}", + i64::MAX + )) + .unwrap(); + let modulus = + ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {}", u64::MAX)).unwrap(); + + matrix.change_modulus(&modulus); + + assert_eq!( + format!( + "[[1 1, 1 2, 1 3],[1 4, 1 5, 1 6]] / 4 1 0 0 1 mod {}", + u64::MAX + ), + matrix.to_string() + ); + } + + /// Ensures that the matrix is reduced correctly. + #[test] + fn reduced_correct() { + let mut matrix = MatPolynomialRingZq::from_str( + "[[1 1, 1 2, 1 3],[1 4, 1 5, 4 1 0 0 1]] / 8 1 0 0 0 0 0 0 1 mod 7", + ) + .unwrap(); + let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 2").unwrap(); + + matrix.change_modulus(&modulus); + + assert_eq!( + "[[1 1, 0, 1 1],[0, 1 1, 0]] / 4 1 0 0 1 mod 2", + matrix.to_string() + ); + } +} + +#[cfg(test)] +mod test_change_q { + use super::MatPolynomialRingZq; + use std::str::FromStr; + + /// Ensures that the modulus `q` is changed correctly. + #[test] + fn q_correct() { + let mut matrix = MatPolynomialRingZq::from_str( + "[[1 1, 1 2, 1 3],[1 4, 1 5, 1 6]] / 4 1 0 0 1 mod 7", + ) + .unwrap(); + + matrix.change_q(8); + + assert_eq!( + "[[1 1, 1 2, 1 3],[1 4, 1 5, 1 6]] / 4 1 0 0 1 mod 8", + matrix.to_string() + ); + } + + /// Ensures that the modulus `q` is changed correctly, if the modulus is big. + #[test] + fn big_q_correct() { + let mut matrix = MatPolynomialRingZq::from_str(&format!( + "[[1 1, 1 2, 1 3],[1 4, 1 5, 1 6]] / 4 1 0 0 1 mod {}", + i64::MAX + )) + .unwrap(); + + matrix.change_q(u64::MAX); + + assert_eq!( + format!( + "[[1 1, 1 2, 1 3],[1 4, 1 5, 1 6]] / 4 1 0 0 1 mod {}", + u64::MAX + ), + matrix.to_string() + ); + } + + /// Ensures that the matrix is reduced correctly. + #[test] + fn reduced_correct() { + let mut matrix = MatPolynomialRingZq::from_str( + "[[1 1, 1 2, 1 3],[1 4, 1 5, 4 1 0 0 1]] / 4 1 0 0 1 mod 7", + ) + .unwrap(); + + matrix.change_q(2); + + assert_eq!( + "[[1 1, 0, 1 1],[0, 1 1, 0]] / 4 1 0 0 1 mod 2", + matrix.to_string() + ); + } +} diff --git a/src/integer_mod_q/modulus_polynomial_ring_zq.rs b/src/integer_mod_q/modulus_polynomial_ring_zq.rs index 28df45de..c0d375e3 100644 --- a/src/integer_mod_q/modulus_polynomial_ring_zq.rs +++ b/src/integer_mod_q/modulus_polynomial_ring_zq.rs @@ -22,10 +22,12 @@ mod norm; mod ntt_basis; mod ownership; mod serialize; +mod set; mod to_string; /// [`ModulusPolynomialRingZq`] represents the modulus object for -/// [`PolynomialRingZq`](crate::integer_mod_q::PolynomialRingZq) +/// [`PolynomialRingZq`](crate::integer_mod_q::PolynomialRingZq). +/// The underlying polynomials need to be monic, i.e. the leading coefficient needs to be `1`. /// /// Attributes /// - `modulus`: holds the specific content, i.e. the modulus `q` and f(X) diff --git a/src/integer_mod_q/modulus_polynomial_ring_zq/from.rs b/src/integer_mod_q/modulus_polynomial_ring_zq/from.rs index 96c8199c..eff56716 100644 --- a/src/integer_mod_q/modulus_polynomial_ring_zq/from.rs +++ b/src/integer_mod_q/modulus_polynomial_ring_zq/from.rs @@ -47,7 +47,8 @@ impl> From<(&PolyOverZ, Mod)> for ModulusPolynomialRingZq { /// /// # Panics ... /// - if `modulus` is smaller than `2`, or - /// - if the degree of the polynomial is smaller than `1`. + /// - if the modulus polynomial is of degree smaller than `1`. + /// - if the leading coefficient is not `1.` fn from((poly, modulus): (&PolyOverZ, Mod)) -> Self { let poly_zq = PolyOverZq::from((poly, modulus)); @@ -85,6 +86,7 @@ impl> From<(PolyOverZ, Mod)> for ModulusPolynomialRingZq { /// # Panics ... /// - if `modulus` is smaller than `2`, or /// - if the modulus polynomial is of degree smaller than `1`. + /// - if the leading coefficient is not `1.` fn from((poly, modulus): (PolyOverZ, Mod)) -> Self { let poly_zq = PolyOverZq::from((poly, modulus)); @@ -96,7 +98,8 @@ impl> From<(PolyOverZ, Mod)> for ModulusPolynomialRingZq { impl From<&PolyOverZq> for ModulusPolynomialRingZq { /// Creates a Modulus object of type [`ModulusPolynomialRingZq`] - /// for [`PolynomialRingZq`](crate::integer_mod_q::PolynomialRingZq) + /// for [`PolynomialRingZq`](crate::integer_mod_q::PolynomialRingZq). + /// It requires that the leading coefficient is `1`. /// /// Parameters: /// - `poly`: the polynomial which is used as the modulus. @@ -117,15 +120,12 @@ impl From<&PolyOverZq> for ModulusPolynomialRingZq { /// # Panics ... /// - if `modulus` is smaller than `2`, or /// - if the modulus polynomial is of degree smaller than `1`. + /// - if the leading coefficient is not `1.` fn from(poly: &PolyOverZq) -> Self { check_poly_mod(poly).unwrap(); - let mut non_zero = Vec::new(); - for i in 0..poly.get_degree() { - let coeff: Z = poly.get_coeff(i).unwrap(); - if coeff != 0 { - non_zero.push(i.try_into().unwrap()); - } - } + + let non_zero = non_zero_positions(poly); + Self { modulus: Rc::new(poly.clone()), ntt_basis: Rc::new(None), @@ -192,6 +192,7 @@ impl FromStr for ModulusPolynomialRingZq { /// [`InvalidModulus`](MathError::InvalidModulus) /// - if `modulus` is smaller than `2`, or /// - if the modulus polynomial is of degree smaller than `1`. + /// - if the leading coefficient is not `1`. fn from_str(s: &str) -> Result { let poly_zq = PolyOverZq::from_str(s)?; @@ -201,6 +202,33 @@ impl FromStr for ModulusPolynomialRingZq { } } +/// Fills a vector with the position of all non-zero coefficients in a [`PolyOverZq`]. +/// +/// Parameters: +/// - `poly`: defines the polynomial whose positions of non-zero coefficients are output +/// +/// Returns a [`Vec`] containing the positions of all non-zero coefficients. +/// +/// # Examples +/// ```compile_fail +/// use qfall_math::integer_mod_q::PolyOverZq; +/// use std::str::FromStr; +/// +/// let poly_zq = PolyOverZq::from_str("4 1 0 0 1 mod 17").unwrap(); +/// +/// let non_zero = non_zero_positions(&poly_zq); +/// ``` +pub(crate) fn non_zero_positions(poly: &PolyOverZq) -> Vec { + let mut non_zero = Vec::new(); + for i in 0..=poly.get_degree() { + let coeff: Z = poly.get_coeff(i).unwrap(); + if coeff != 0 { + non_zero.push(i.try_into().unwrap()); + } + } + non_zero +} + /// Checks weather a given [`PolyOverZq`] can be used as a [`ModulusPolynomialRingZq`]. /// It requires that the leading coefficient is `1`. /// @@ -223,6 +251,9 @@ impl FromStr for ModulusPolynomialRingZq { /// - Returns a [`MathError`] of type /// [`InvalidModulus`](MathError::InvalidModulus) /// if the modulus polynomial is of degree less than `1`. +/// - Returns a [`MathError`] of type +/// [`InvalidModulus`](MathError::InvalidModulus) +/// if the leading coefficient is not `1`. pub(crate) fn check_poly_mod(poly_zq: &PolyOverZq) -> Result<(), MathError> { let leading_coefficient: Z = poly_zq.get_coeff(poly_zq.get_degree())?; if poly_zq.get_degree() < 1 { @@ -343,6 +374,15 @@ mod test_try_from_poly_zq { let _ = ModulusPolynomialRingZq::from(poly); } + + /// Ensure that the function panics if the leading coefficient is not `1` + #[test] + #[should_panic] + fn panic_leading_coefficient() { + let poly = PolyOverZq::from_str("2 1 2 mod 10").unwrap(); + + let _ = ModulusPolynomialRingZq::from(poly); + } } /// most tests with specific values are covered in [`PolyOverZq`](crate::integer_mod_q::PolyOverZq) diff --git a/src/integer_mod_q/modulus_polynomial_ring_zq/set.rs b/src/integer_mod_q/modulus_polynomial_ring_zq/set.rs new file mode 100644 index 00000000..c029e45a --- /dev/null +++ b/src/integer_mod_q/modulus_polynomial_ring_zq/set.rs @@ -0,0 +1,105 @@ +// Copyright 2026 Jan 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 manipulate a [`ModulusPolynomialRingZq`]. + +use crate::integer_mod_q::{ + Modulus, ModulusPolynomialRingZq, PolyOverZq, + modulus_polynomial_ring_zq::from::non_zero_positions, +}; +use std::rc::Rc; + +impl ModulusPolynomialRingZq { + /// Changes the modulus `q` of the given [`ModulusPolynomialRingZq`] to the new modulus `q` + /// and resets all NTT-related attributes. + /// It takes the representation of each coefficient in [0, q) as the new + /// coefficients and reduces them by the new [`Modulus`]. + /// + /// Parameters: + /// - `modulus`: the new modulus `q` + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::ModulusPolynomialRingZq; + /// use std::str::FromStr; + /// + /// let mut poly = ModulusPolynomialRingZq::from_str("4 5 3 7 1 mod 13").unwrap(); + /// + /// poly.change_q(5); + /// + /// assert_eq!(ModulusPolynomialRingZq::from_str("4 0 3 2 1 mod 5").unwrap(), poly); + /// ``` + /// + /// # Panics ... + /// - if `modulus` is smaller than `2`. + pub fn change_q(&mut self, modulus: impl Into) { + self.ntt_basis = Rc::new(None); + + let new_poly_zq = + PolyOverZq::from((self.get_representative_least_nonnegative_residue(), modulus)); + self.non_zero = non_zero_positions(&new_poly_zq); + + self.modulus = Rc::new(new_poly_zq); + } +} + +#[cfg(test)] +mod test_change_q { + use super::ModulusPolynomialRingZq; + use crate::integer_mod_q::Modulus; + use std::str::FromStr; + + /// Ensures that the modulus is changed correctly. + #[test] + fn q_correct() { + let mut modulus = ModulusPolynomialRingZq::from_str("6 1 2 3 4 5 1 mod 7").unwrap(); + let q = Modulus::from(8); + + modulus.change_q(&q); + + assert_eq!("6 1 2 3 4 5 1 mod 8", modulus.to_string()); + } + + /// Ensures that the modulus is changed correctly, if the modulus is big. + #[test] + fn big_q_correct() { + let mut modulus = + ModulusPolynomialRingZq::from_str(&format!("6 1 2 3 4 5 1 mod {}", i64::MAX)).unwrap(); + let q = Modulus::from(u64::MAX); + + modulus.change_q(&q); + + assert_eq!( + format!("6 1 2 3 4 5 1 mod {}", u64::MAX), + modulus.to_string() + ); + } + + /// Ensures that the poly is reduced correctly. + #[test] + fn reduced_correct() { + let mut modulus = ModulusPolynomialRingZq::from_str("6 1 2 3 4 5 1 mod 7").unwrap(); + let q = Modulus::from(2); + + modulus.change_q(&q); + + assert_eq!("6 1 0 1 0 1 1 mod 2", modulus.to_string()); + } + + /// Ensures that all NTT-related attributes are reset. + #[test] + fn reset_ntt_values() { + let mut modulus = ModulusPolynomialRingZq::from_str("6 1 2 3 4 5 1 mod 7").unwrap(); + let q = Modulus::from(2); + + modulus.change_q(&q); + + assert_eq!(vec![0, 2, 4, 5], modulus.non_zero); + assert!(modulus.ntt_basis.is_none()); + } +} diff --git a/src/integer_mod_q/poly_over_zq/set.rs b/src/integer_mod_q/poly_over_zq/set.rs index 641c49f2..96e39022 100644 --- a/src/integer_mod_q/poly_over_zq/set.rs +++ b/src/integer_mod_q/poly_over_zq/set.rs @@ -10,9 +10,12 @@ use super::PolyOverZq; use crate::{ - integer::Z, integer_mod_q::Zq, macros::for_others::implement_for_owned, traits::SetCoefficient, + integer::Z, + integer_mod_q::{Modulus, Zq}, + macros::for_others::implement_for_owned, + traits::SetCoefficient, }; -use flint_sys::fmpz_mod_poly::fmpz_mod_poly_set_coeff_fmpz; +use flint_sys::fmpz_mod_poly::{fmpz_mod_poly_set_coeff_fmpz, fmpz_mod_poly_set_fmpz_poly}; impl> SetCoefficient for PolyOverZq { /// Sets the coefficient of a polynomial [`PolyOverZq`]. @@ -92,6 +95,41 @@ impl SetCoefficient<&Zq> for PolyOverZq { implement_for_owned!(Zq, PolyOverZq, SetCoefficient); +impl PolyOverZq { + /// Changes the modulus of the given polynomial to the new modulus. + /// It takes the representation of each coefficient in [0, q) as the new + /// coefficients and reduces them by the new [`Modulus`]. + /// + /// Parameters: + /// - `modulus`: the new modulus of the polynomial + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::PolyOverZq; + /// use std::str::FromStr; + /// + /// let mut poly = PolyOverZq::from_str("4 5 3 7 4 mod 13").unwrap(); + /// + /// poly.change_modulus(5); + /// + /// assert_eq!(PolyOverZq::from_str("4 0 3 2 4 mod 5").unwrap(), poly); + /// ``` + /// + /// # Panics ... + /// - if `modulus` is smaller than `2`. + pub fn change_modulus(&mut self, modulus: impl Into) { + self.modulus = modulus.into(); + let lnr = self.get_representative_least_nonnegative_residue(); + unsafe { + fmpz_mod_poly_set_fmpz_poly( + &mut self.poly, + &lnr.poly, + self.modulus.get_fmpz_mod_ctx_struct(), + ); + } + } +} + #[cfg(test)] mod test_set_coeff_z { use crate::{integer::Z, integer_mod_q::PolyOverZq, traits::SetCoefficient}; @@ -195,3 +233,43 @@ mod test_set_coeff_zq { ) } } + +#[cfg(test)] +mod test_change_modulus { + use super::PolyOverZq; + use crate::integer_mod_q::Modulus; + use std::str::FromStr; + + /// Ensures that the modulus is changed correctly. + #[test] + fn modulus_correct() { + let mut poly = PolyOverZq::from_str("6 1 2 3 4 5 6 mod 7").unwrap(); + let modulus = Modulus::from(8); + + poly.change_modulus(&modulus); + + assert_eq!("6 1 2 3 4 5 6 mod 8", poly.to_string()); + } + + /// Ensures that the modulus is changed correctly, if the modulus is big. + #[test] + fn big_modulus_correct() { + let mut poly = PolyOverZq::from_str(&format!("6 1 2 3 4 5 6 mod {}", i64::MAX)).unwrap(); + let modulus = Modulus::from(u64::MAX); + + poly.change_modulus(&modulus); + + assert_eq!(format!("6 1 2 3 4 5 6 mod {}", u64::MAX), poly.to_string()); + } + + /// Ensures that the poly is reduced correctly. + #[test] + fn reduced_correct() { + let mut poly = PolyOverZq::from_str("6 1 2 3 4 5 6 mod 7").unwrap(); + let modulus = Modulus::from(2); + + poly.change_modulus(&modulus); + + assert_eq!("5 1 0 1 0 1 mod 2", poly.to_string()); + } +} diff --git a/src/integer_mod_q/polynomial_ring_zq/set.rs b/src/integer_mod_q/polynomial_ring_zq/set.rs index 01e16cc9..ba2e9140 100644 --- a/src/integer_mod_q/polynomial_ring_zq/set.rs +++ b/src/integer_mod_q/polynomial_ring_zq/set.rs @@ -10,7 +10,10 @@ use super::PolynomialRingZq; use crate::{ - integer::Z, integer_mod_q::Zq, macros::for_others::implement_for_owned, traits::SetCoefficient, + integer::Z, + integer_mod_q::{Modulus, ModulusPolynomialRingZq, Zq}, + macros::for_others::implement_for_owned, + traits::SetCoefficient, }; use flint_sys::fmpz_poly::fmpz_poly_set_coeff_fmpz; @@ -93,6 +96,64 @@ impl SetCoefficient<&Zq> for PolynomialRingZq { implement_for_owned!(Zq, PolynomialRingZq, SetCoefficient); +impl PolynomialRingZq { + /// Changes the modulus of the given polynomial to the new modulus. + /// It takes the representation of each entry with coefficients in [0, q) as the new + /// coefficients and reduces them by the new [`ModulusPolynomialRingZq`]. + /// + /// Parameters: + /// - `modulus`: the new modulus of the polynomial + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq}; + /// use std::str::FromStr; + /// let modulus0 = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap(); + /// let modulus1 = ModulusPolynomialRingZq::from_str("3 1 0 1 mod 12").unwrap(); + /// + /// let mut poly = PolynomialRingZq::from((13, modulus0)); + /// + /// poly.change_modulus(modulus1); + /// + /// assert_eq!("1 1 / 3 1 0 1 mod 12", poly.to_string()); + /// ``` + /// + /// # Panics ... + /// - if `modulus` is smaller than `2`, or + /// - if the modulus polynomial is of degree smaller than `1`. + /// - if the leading coefficient is not `1.` + pub fn change_modulus(&mut self, modulus: impl Into) { + self.modulus = modulus.into(); + self.reduce(); + } + + /// Changes the modulus `q` of the given polynomial to the new modulus `q`. + /// It takes the representation of each entry with coefficients in `[0, q)` as the new + /// coefficients and reduces them by the new [`Modulus`]. + /// + /// Parameters: + /// - `q`: the new modulus of the polynomial + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::PolynomialRingZq; + /// use std::str::FromStr; + /// + /// let mut poly = PolynomialRingZq::from_str("3 12 2 13 / 4 1 0 0 1 mod 17").unwrap(); + /// + /// poly.change_q(12); + /// + /// assert_eq!("3 0 2 1 / 4 1 0 0 1 mod 12", poly.to_string()); + /// ``` + /// + /// # Panics ... + /// - if `modulus` is smaller than `2`. + pub fn change_q(&mut self, q: impl Into) { + self.modulus.change_q(q); + self.reduce(); + } +} + #[cfg(test)] mod test_set_coeff { use crate::{ @@ -213,3 +274,89 @@ mod test_set_coeff { assert!(poly_ring.set_coeff(1, &value).is_err()); } } + +#[cfg(test)] +mod test_change_modulus { + use super::PolynomialRingZq; + use crate::integer_mod_q::ModulusPolynomialRingZq; + use std::str::FromStr; + + /// Ensures that the modulus is changed correctly. + #[test] + fn modulus_correct() { + let mut matrix = PolynomialRingZq::from_str("1 6 / 4 1 0 0 1 mod 7").unwrap(); + let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 5").unwrap(); + + matrix.change_modulus(&modulus); + + assert_eq!("1 1 / 4 1 0 0 1 mod 5", matrix.to_string()); + } + + /// Ensures that the modulus is changed correctly, if the modulus is big. + #[test] + fn big_modulus_correct() { + let mut matrix = + PolynomialRingZq::from_str(&format!("1 6 / 4 1 0 0 1 mod {}", i64::MAX)).unwrap(); + let modulus = + ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {}", u64::MAX)).unwrap(); + + matrix.change_modulus(&modulus); + + assert_eq!( + format!("1 6 / 4 1 0 0 1 mod {}", u64::MAX), + matrix.to_string() + ); + } + + /// Ensures that the matrix is reduced correctly. + #[test] + fn reduced_correct() { + let mut matrix = + PolynomialRingZq::from_str("4 1 0 0 1 / 8 1 0 0 0 0 0 0 1 mod 7").unwrap(); + let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 2").unwrap(); + + matrix.change_modulus(&modulus); + + assert_eq!("0 / 4 1 0 0 1 mod 2", matrix.to_string()); + } +} + +#[cfg(test)] +mod test_change_q { + use super::PolynomialRingZq; + use std::str::FromStr; + + /// Ensures that the modulus `q` is changed correctly. + #[test] + fn q_correct() { + let mut matrix = PolynomialRingZq::from_str("1 6 / 4 1 0 0 1 mod 7").unwrap(); + + matrix.change_q(8); + + assert_eq!("1 6 / 4 1 0 0 1 mod 8", matrix.to_string()); + } + + /// Ensures that the modulus `q` is changed correctly, if the modulus is big. + #[test] + fn big_q_correct() { + let mut matrix = + PolynomialRingZq::from_str(&format!("1 6 / 4 1 0 0 1 mod {}", i64::MAX)).unwrap(); + + matrix.change_q(u64::MAX); + + assert_eq!( + format!("1 6 / 4 1 0 0 1 mod {}", u64::MAX), + matrix.to_string() + ); + } + + /// Ensures that the matrix is reduced correctly. + #[test] + fn reduced_correct() { + let mut matrix = PolynomialRingZq::from_str("2 5 4 / 4 1 0 0 1 mod 7").unwrap(); + + matrix.change_q(2); + + assert_eq!("1 1 / 4 1 0 0 1 mod 2", matrix.to_string()); + } +} diff --git a/src/integer_mod_q/z_q.rs b/src/integer_mod_q/z_q.rs index 01a56383..c4677f2f 100644 --- a/src/integer_mod_q/z_q.rs +++ b/src/integer_mod_q/z_q.rs @@ -31,6 +31,7 @@ mod get; mod properties; mod reduce; mod sample; +mod set; mod to_string; mod unsafe_functions; diff --git a/src/integer_mod_q/z_q/set.rs b/src/integer_mod_q/z_q/set.rs new file mode 100644 index 00000000..65a6eca3 --- /dev/null +++ b/src/integer_mod_q/z_q/set.rs @@ -0,0 +1,79 @@ +// Copyright 2026 Jan 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 manipulate a [`Zq`] polynomial. + +use crate::integer_mod_q::{Modulus, Zq}; + +impl Zq { + /// Changes the modulus of the given [`Zq`] value to the new modulus. + /// It takes the representation of `self` in [0, q) as the new + /// value and reduces it by the new modulus. + /// + /// Parameters: + /// - `modulus`: the new modulus of the [`Zq`] value + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::{Zq, Modulus}; + /// use std::str::FromStr; + /// + /// let mut value = Zq::from_str("2 mod 3").unwrap(); + /// value.change_modulus(2); + /// + /// assert_eq!("0 mod 2", value.to_string()); + /// ``` + /// + /// # Panics ... + /// - if `modulus` is smaller than `2`. + pub fn change_modulus(&mut self, modulus: impl Into) { + self.modulus = modulus.into(); + self.value = self.get_representative_least_nonnegative_residue(); + self.reduce(); + } +} + +#[cfg(test)] +mod test_change_modulus { + use super::Zq; + use crate::integer_mod_q::Modulus; + use std::str::FromStr; + + /// Ensures that the modulus is changed correctly. + #[test] + fn modulus_correct() { + let mut matrix = Zq::from_str("6 mod 7").unwrap(); + let modulus = Modulus::from(5); + + matrix.change_modulus(&modulus); + + assert_eq!("1 mod 5", matrix.to_string()); + } + + /// Ensures that the modulus is changed correctly, if the modulus is big. + #[test] + fn big_modulus_correct() { + let mut matrix = Zq::from_str(&format!("6 mod {}", i64::MAX)).unwrap(); + let modulus = Modulus::from(u64::MAX); + + matrix.change_modulus(&modulus); + + assert_eq!(format!("6 mod {}", u64::MAX), matrix.to_string()); + } + + /// Ensures that the matrix is reduced correctly. + #[test] + fn reduced_correct() { + let mut matrix = Zq::from_str("6 mod 7").unwrap(); + let modulus = Modulus::from(2); + + matrix.change_modulus(&modulus); + + assert_eq!("0 mod 2", matrix.to_string()); + } +}