Skip to content

Commit bc6e02f

Browse files
authored
Merge pull request #531 from qfall/get_least_abs_residue
Broadly implement `get_representative_least_absolute_residue`
2 parents e71d8d7 + 1ea26bb commit bc6e02f

4 files changed

Lines changed: 212 additions & 9 deletions

File tree

src/integer_mod_q/mat_polynomial_ring_zq/get.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use super::MatPolynomialRingZq;
1212
use crate::{
1313
integer::{MatPolyOverZ, PolyOverZ},
1414
integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
15-
traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix},
15+
traits::{MatrixDimensions, MatrixGetEntry, MatrixGetSubmatrix, MatrixSetEntry},
1616
};
1717
use flint_sys::{fmpz_poly::fmpz_poly_struct, fmpz_poly_mat::fmpz_poly_mat_entry};
1818

@@ -37,11 +37,46 @@ impl MatPolynomialRingZq {
3737
}
3838

3939
impl MatPolynomialRingZq {
40+
/// Creates a [`MatPolyOverZ`] where each entry is a representative of the
41+
/// equivalence class of each entry from a [`MatPolynomialRingZq`] with coefficients centered around `0`.
42+
///
43+
/// The representation of the coefficients is in the range `[-modulus/2, modulus/2]` and
44+
/// the representation of the polynomials is in the range `[0, modulus_polynomial)`.
45+
/// Use [`MatPolynomialRingZq::get_representative_least_nonnegative_residue`] if they should be
46+
/// in the range `[0, modulus)`.
47+
///
48+
/// # Examples
49+
/// ```
50+
/// use qfall_math::integer_mod_q::MatPolynomialRingZq;
51+
/// use qfall_math::integer::MatPolyOverZ;
52+
/// use std::str::FromStr;
53+
///
54+
/// let poly_ring_mat = MatPolynomialRingZq::from_str("[[1 10, 1 11]] / 4 1 0 0 1 mod 21").unwrap();
55+
///
56+
/// let matrix = poly_ring_mat.get_representative_least_absolute_residue();
57+
///
58+
/// let cmp_poly_mat = MatPolyOverZ::from_str("[[1 10, 1 -10]]").unwrap();
59+
/// assert_eq!(cmp_poly_mat, matrix);
60+
/// ```
61+
pub fn get_representative_least_absolute_residue(&self) -> MatPolyOverZ {
62+
let mut out = MatPolyOverZ::new(self.get_num_rows(), self.get_num_columns());
63+
for row in 0..self.get_num_rows() {
64+
for col in 0..self.get_num_columns() {
65+
let poly: PolynomialRingZq = unsafe { self.get_entry_unchecked(row, col) };
66+
let lar_poly = poly.get_representative_least_absolute_residue();
67+
unsafe { out.set_entry_unchecked(row, col, lar_poly) };
68+
}
69+
}
70+
out
71+
}
72+
4073
/// Creates a [`MatPolyOverZ`] where each entry is a representative of the
4174
/// equivalence class of each entry from a [`MatPolynomialRingZq`].
4275
///
4376
/// The representation of the coefficients is in the range `[0, modulus)` and
4477
/// the representation of the polynomials is in the range `[0, modulus_polynomial)`.
78+
/// Use [`MatPolynomialRingZq::get_representative_least_absolute_residue`] if they should be
79+
/// in the range `[-modulus/2, modulus/2]`.
4580
///
4681
/// # Examples
4782
/// ```
@@ -440,6 +475,40 @@ mod test_mod {
440475
}
441476
}
442477

478+
#[cfg(test)]
479+
mod test_get_representative_least_absolute_residue {
480+
use crate::{integer::MatPolyOverZ, integer_mod_q::MatPolynomialRingZq};
481+
use std::str::FromStr;
482+
483+
/// Check whether `get_representative_least_absolute_residue` outputs the correct value for large values
484+
#[test]
485+
fn large_numbers() {
486+
let poly_ring = MatPolynomialRingZq::from_str(&format!(
487+
"[[2 {} {}]] / 4 1 0 0 1 mod {}",
488+
i64::MAX,
489+
u64::MAX - 1,
490+
u64::MAX
491+
))
492+
.unwrap();
493+
494+
let lar_mat = poly_ring.get_representative_least_absolute_residue();
495+
496+
let cmp_mat = MatPolyOverZ::from_str(&format!("[[2 {} -1]]", i64::MAX)).unwrap();
497+
assert_eq!(cmp_mat, lar_mat);
498+
}
499+
500+
/// Check whether `get_representative_least_absolute_residue` outputs the correct value for special cases
501+
#[test]
502+
fn special_numbers() {
503+
let mat = MatPolynomialRingZq::from_str("[[3 10 0 11]] / 4 1 0 0 1 mod 21").unwrap();
504+
505+
let lar_mat = mat.get_representative_least_absolute_residue();
506+
507+
let cmp_mat = MatPolyOverZ::from_str("[[3 10 0 -10]]").unwrap();
508+
assert_eq!(cmp_mat, lar_mat);
509+
}
510+
}
511+
443512
#[cfg(test)]
444513
mod test_get_representative_least_nonnegative_residue {
445514
use crate::{

src/integer_mod_q/poly_over_zq/get.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use super::PolyOverZq;
1212
use crate::{
1313
integer::{PolyOverZ, Z},
1414
integer_mod_q::{Modulus, Zq},
15-
traits::GetCoefficient,
15+
traits::{GetCoefficient, SetCoefficient},
1616
};
1717
use flint_sys::fmpz_mod_poly::{
1818
fmpz_mod_poly_degree, fmpz_mod_poly_get_coeff_fmpz, fmpz_mod_poly_get_fmpz_poly,
@@ -133,9 +133,40 @@ impl PolyOverZq {
133133
self.modulus.clone()
134134
}
135135

136+
/// Returns a representative polynomial of the [`PolyOverZq`] element with coefficients centered around `0`.
137+
///
138+
/// The output [`PolyOverZ`] has coefficients in the range of `[-modulus/2, modulus/2]`.
139+
/// For even moduli, the positive representative is chosen for the element `modulus / 2`.
140+
/// Use [`PolyOverZq::get_representative_least_nonnegative_residue`] if they should be
141+
/// in the range `[0, modulus)`.
142+
///
143+
/// # Examples
144+
/// ```
145+
/// use qfall_math::integer_mod_q::PolyOverZq;
146+
/// use qfall_math::integer::PolyOverZ;
147+
/// use std::str::FromStr;
148+
///
149+
/// let value = PolyOverZq::from_str("2 10 11 mod 21").unwrap();
150+
///
151+
/// let least_abs_residue = value.get_representative_least_absolute_residue();
152+
///
153+
/// assert_eq!(PolyOverZ::from_str("2 10 -10").unwrap(), least_abs_residue);
154+
/// ```
155+
pub fn get_representative_least_absolute_residue(&self) -> PolyOverZ {
156+
let mut out = PolyOverZ::default();
157+
for i in 0..=self.get_degree() {
158+
let coeff: Zq = unsafe { self.get_coeff_unchecked(i) };
159+
let lar_coeff = coeff.get_representative_least_absolute_residue();
160+
unsafe { out.set_coeff_unchecked(i, lar_coeff) };
161+
}
162+
out
163+
}
164+
136165
/// Returns a representative polynomial of the [`PolyOverZq`] element.
137166
///
138167
/// The representation of the coefficients is in the range `[0, modulus)`.
168+
/// Use [`PolyOverZq::get_representative_least_absolute_residue`] if they should be
169+
/// in the range `[-modulus/2, modulus/2]`.
139170
///
140171
/// # Examples
141172
/// ```
@@ -339,6 +370,40 @@ mod test_mod {
339370
}
340371
}
341372

373+
#[cfg(test)]
374+
mod test_get_representative_least_absolute_residue {
375+
use crate::{integer::PolyOverZ, integer_mod_q::PolyOverZq};
376+
use std::str::FromStr;
377+
378+
/// Check whether `get_representative_least_absolute_residue` outputs the correct value for large values
379+
#[test]
380+
fn large_numbers() {
381+
let poly_zq = PolyOverZq::from_str(&format!(
382+
"2 {} {} mod {}",
383+
i64::MAX,
384+
u64::MAX - 1,
385+
u64::MAX
386+
))
387+
.unwrap();
388+
389+
let poly_z = poly_zq.get_representative_least_absolute_residue();
390+
391+
let cmp_poly = PolyOverZ::from_str(&format!("2 {} -1", i64::MAX)).unwrap();
392+
assert_eq!(cmp_poly, poly_z);
393+
}
394+
395+
/// Check whether `get_representative_least_absolute_residue` outputs the correct value for special cases
396+
#[test]
397+
fn special_numbers() {
398+
let poly_zq = PolyOverZq::from_str("3 10 0 11 mod 21").unwrap();
399+
400+
let poly_z = poly_zq.get_representative_least_absolute_residue();
401+
402+
let cmp_poly = PolyOverZ::from_str("3 10 0 -10").unwrap();
403+
assert_eq!(cmp_poly, poly_z);
404+
}
405+
}
406+
342407
#[cfg(test)]
343408
mod test_get_representative_least_nonnegative_residue {
344409
use crate::{integer::PolyOverZ, integer_mod_q::PolyOverZq};

src/integer_mod_q/polynomial_ring_zq/get.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use super::PolynomialRingZq;
1212
use crate::{
1313
integer::{PolyOverZ, Z},
1414
integer_mod_q::{ModulusPolynomialRingZq, Zq},
15-
traits::GetCoefficient,
15+
traits::{GetCoefficient, SetCoefficient},
1616
};
1717
use flint_sys::fmpz_poly::{fmpz_poly_degree, fmpz_poly_get_coeff_fmpz};
1818

@@ -116,10 +116,41 @@ impl PolynomialRingZq {
116116
self.modulus.clone()
117117
}
118118

119+
/// Returns a representative polynomial of the [`PolynomialRingZq`] element with coefficients centered around `0`.
120+
///
121+
/// The output [`PolyOverZ`] has coefficients in the range of `[-modulus/2, modulus/2]`.
122+
/// For even moduli, the positive representative is chosen for the element `modulus / 2`.
123+
/// Use [`PolynomialRingZq::get_representative_least_nonnegative_residue`] if they should be
124+
/// in the range `[0, modulus)`.
125+
///
126+
/// # Examples
127+
/// ```
128+
/// use qfall_math::integer_mod_q::PolynomialRingZq;
129+
/// use qfall_math::integer::PolyOverZ;
130+
/// use std::str::FromStr;
131+
///
132+
/// let value = PolynomialRingZq::from_str("2 10 11 / 4 1 0 0 1 mod 21").unwrap();
133+
///
134+
/// let least_abs_residue = value.get_representative_least_absolute_residue();
135+
///
136+
/// assert_eq!(PolyOverZ::from_str("2 10 -10").unwrap(), least_abs_residue);
137+
/// ```
138+
pub fn get_representative_least_absolute_residue(&self) -> PolyOverZ {
139+
let mut out = PolyOverZ::default();
140+
for i in 0..=self.get_degree() {
141+
let coeff: Zq = unsafe { self.get_coeff_unchecked(i) };
142+
let lar_coeff = coeff.get_representative_least_absolute_residue();
143+
unsafe { out.set_coeff_unchecked(i, lar_coeff) };
144+
}
145+
out
146+
}
147+
119148
/// Returns a representative polynomial of the [`PolynomialRingZq`] element.
120149
///
121150
/// The representation of the coefficients is in the range `[0, modulus)` and
122151
/// the representation of the polynomial is in the range `[0, modulus_polynomial)`.
152+
/// Use [`PolynomialRingZq::get_representative_least_absolute_residue`] if they should be
153+
/// in the range `[-modulus/2, modulus/2]`.
123154
///
124155
/// # Examples
125156
/// ```
@@ -258,6 +289,40 @@ mod test_get_mod {
258289
}
259290
}
260291

292+
#[cfg(test)]
293+
mod test_get_representative_least_absolute_residue {
294+
use crate::{integer::PolyOverZ, integer_mod_q::PolynomialRingZq};
295+
use std::str::FromStr;
296+
297+
/// Check whether `get_representative_least_absolute_residue` outputs the correct value for large values
298+
#[test]
299+
fn large_numbers() {
300+
let poly_ring = PolynomialRingZq::from_str(&format!(
301+
"2 {} {} / 4 1 0 0 1 mod {}",
302+
i64::MAX,
303+
u64::MAX - 1,
304+
u64::MAX
305+
))
306+
.unwrap();
307+
308+
let poly_z = poly_ring.get_representative_least_absolute_residue();
309+
310+
let cmp_poly = PolyOverZ::from_str(&format!("2 {} -1", i64::MAX)).unwrap();
311+
assert_eq!(cmp_poly, poly_z);
312+
}
313+
314+
/// Check whether `get_representative_least_absolute_residue` outputs the correct value for special cases
315+
#[test]
316+
fn special_numbers() {
317+
let poly_ring = PolynomialRingZq::from_str("3 10 0 11 / 4 1 0 0 1 mod 21").unwrap();
318+
319+
let poly_z = poly_ring.get_representative_least_absolute_residue();
320+
321+
let cmp_poly = PolyOverZ::from_str("3 10 0 -10").unwrap();
322+
assert_eq!(cmp_poly, poly_z);
323+
}
324+
}
325+
261326
#[cfg(test)]
262327
mod test_get_representative_least_nonnegative_residue {
263328
use crate::{

src/integer_mod_q/z_q/get.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,15 @@ impl Zq {
4949
///
5050
/// let z_value = zq_value.get_representative_least_absolute_residue();
5151
///
52-
/// assert_eq!(Z::from(2), z_value);
52+
/// assert_eq!(Z::from(-2), z_value);
5353
/// ```
5454
pub fn get_representative_least_absolute_residue(&self) -> Z {
5555
let mod_z = Z::from(&self.modulus);
56-
if self.value < mod_z.div_ceil(2) {
57-
self.value.clone()
56+
let mod_half = mod_z.div_floor(2);
57+
if self.value > mod_half {
58+
&self.value - mod_z
5859
} else {
59-
Z::from(self.modulus.clone()) - self.value.clone()
60+
self.value.clone()
6061
}
6162
}
6263

@@ -123,7 +124,7 @@ mod test_get_representative_least_absolute_residue {
123124
let res_1 = value_1.get_representative_least_absolute_residue();
124125

125126
assert_eq!(res_0, Z::from(2));
126-
assert_eq!(res_1, Z::from(2));
127+
assert_eq!(res_1, Z::from(-2));
127128
}
128129

129130
/// Check whether `get_representative_least_absolute_residue` outputs the correct value for large values
@@ -136,20 +137,23 @@ mod test_get_representative_least_absolute_residue {
136137
let res_1 = value_1.get_representative_least_absolute_residue();
137138

138139
assert_eq!(res_0, Z::from(i64::MAX));
139-
assert_eq!(res_1, Z::from(1));
140+
assert_eq!(res_1, Z::from(-1));
140141
}
141142

142143
/// Check whether `get_representative_least_absolute_residue` outputs the correct value for special cases
143144
#[test]
144145
fn get_special() {
145146
let value_0 = Zq::from((10, 20));
146147
let value_1 = Zq::from((0, 20));
148+
let value_2 = Zq::from((11, 21));
147149

148150
let res_0 = value_0.get_representative_least_absolute_residue();
149151
let res_1 = value_1.get_representative_least_absolute_residue();
152+
let res_2 = value_2.get_representative_least_absolute_residue();
150153

151154
assert_eq!(res_0, Z::from(10));
152155
assert_eq!(res_1, Z::from(0));
156+
assert_eq!(res_2, Z::from(-10));
153157
}
154158
}
155159

0 commit comments

Comments
 (0)