Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 176 additions & 1 deletion src/integer_mod_q/mat_polynomial_ring_zq/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<ModulusPolynomialRingZq>) {
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<Modulus>) {
self.modulus.change_q(q);
self.reduce();
}
}

#[cfg(test)]
Expand Down Expand Up @@ -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()
);
}
}
4 changes: 3 additions & 1 deletion src/integer_mod_q/modulus_polynomial_ring_zq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
21 changes: 19 additions & 2 deletions src/integer_mod_q/modulus_polynomial_ring_zq/from.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ impl<Mod: Into<Modulus>> 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));

Expand Down Expand Up @@ -85,6 +86,7 @@ impl<Mod: Into<Modulus>> 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));

Expand All @@ -96,7 +98,8 @@ impl<Mod: Into<Modulus>> 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.
Expand All @@ -117,6 +120,7 @@ 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();
Expand Down Expand Up @@ -192,6 +196,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<Self, Self::Err> {
let poly_zq = PolyOverZq::from_str(s)?;

Expand Down Expand Up @@ -223,6 +228,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 {
Expand Down Expand Up @@ -343,6 +351,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)
Expand Down
101 changes: 101 additions & 0 deletions src/integer_mod_q/modulus_polynomial_ring_zq/set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// 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 <https://mozilla.org/en-US/MPL/2.0/>.

//! Implementations to manipulate a [`ModulusPolynomialRingZq`].

use crate::integer_mod_q::{Modulus, ModulusPolynomialRingZq, PolyOverZq};
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.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nice, I was wondering whether you thought about this :)

/// 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<Modulus>) {
self.ntt_basis = Rc::new(None);
self.non_zero = Vec::new();

let new_poly_zq =
PolyOverZq::from((self.get_representative_least_nonnegative_residue(), modulus));
self.modulus = Rc::new(new_poly_zq);
}
Comment on lines +40 to +48
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this would cause problems I think.

The non-zero needs to be set fitting to the modulus, i.e., it captures the non-zero coefficients used for the reduction -> see the reduce call in PolynomialRingZq that uses it. By setting this to 0, I think that essentially the polynomial reduction would always assume that you are using a modulus polynomial of X^N, i.e., there is no proper reduction.

On that note: I just found, that I missed to describe the new attributes from the changed modulus reduction on the Modulus struct.

Please add a test for this case, i.e., do some higher degree modulo multiplication, where the context polynomial is also used for reduction for the tests in the Polynomial constructions

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I've addressed the issue with the non-zero vector.
I'd suggest that you address the additionally missing parts described after "On that note" in a separate PR. You have a better idea what's needed / about the edge cases here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

FYI I changed the non_zero vector s.t. it contains all positions of non-zero coefficients in the ModulusPolynomialRingZq. Previously, the highest coefficient (always 1) wasn't part of the vector. The tests all still pass, which tells me that either a test reg. this was missing or it didn't change anything and we might be doing double work somewhere now. As the one who wrote this logic, could you please check which one we want / need and add tests accordingly?

}

#[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!(modulus.non_zero.is_empty());
assert!(modulus.ntt_basis.is_none());
}
}
Loading
Loading