From 18bf0ce2d0ea2bd2025764a18331d0094aa40d9b Mon Sep 17 00:00:00 2001 From: jnsiemer Date: Tue, 6 Oct 2026 19:37:18 +0100 Subject: [PATCH] Add Neg-Traits for other types --- src/integer/mat_poly_over_z/arithmetic.rs | 1 + src/integer/mat_poly_over_z/arithmetic/neg.rs | 90 +++++++++++++ src/integer/mat_z/arithmetic.rs | 1 + src/integer/mat_z/arithmetic/neg.rs | 89 +++++++++++++ src/integer/poly_over_z/arithmetic.rs | 1 + src/integer/poly_over_z/arithmetic/neg.rs | 84 ++++++++++++ .../mat_polynomial_ring_zq/arithmetic.rs | 1 + .../mat_polynomial_ring_zq/arithmetic/neg.rs | 115 ++++++++++++++++ src/integer_mod_q/mat_zq/arithmetic.rs | 1 + src/integer_mod_q/mat_zq/arithmetic/neg.rs | 112 ++++++++++++++++ src/integer_mod_q/poly_over_zq/arithmetic.rs | 1 + .../poly_over_zq/arithmetic/neg.rs | 110 ++++++++++++++++ .../polynomial_ring_zq/arithmetic.rs | 1 + .../polynomial_ring_zq/arithmetic/neg.rs | 116 +++++++++++++++++ src/integer_mod_q/z_q/arithmetic.rs | 1 + src/integer_mod_q/z_q/arithmetic/neg.rs | 123 ++++++++++++++++++ src/rational/mat_q/arithmetic.rs | 1 + src/rational/mat_q/arithmetic/neg.rs | 84 ++++++++++++ src/rational/poly_over_q/arithmetic.rs | 1 + src/rational/poly_over_q/arithmetic/neg.rs | 83 ++++++++++++ 20 files changed, 1016 insertions(+) create mode 100644 src/integer/mat_poly_over_z/arithmetic/neg.rs create mode 100644 src/integer/mat_z/arithmetic/neg.rs create mode 100644 src/integer/poly_over_z/arithmetic/neg.rs create mode 100644 src/integer_mod_q/mat_polynomial_ring_zq/arithmetic/neg.rs create mode 100644 src/integer_mod_q/mat_zq/arithmetic/neg.rs create mode 100644 src/integer_mod_q/poly_over_zq/arithmetic/neg.rs create mode 100644 src/integer_mod_q/polynomial_ring_zq/arithmetic/neg.rs create mode 100644 src/integer_mod_q/z_q/arithmetic/neg.rs create mode 100644 src/rational/mat_q/arithmetic/neg.rs create mode 100644 src/rational/poly_over_q/arithmetic/neg.rs diff --git a/src/integer/mat_poly_over_z/arithmetic.rs b/src/integer/mat_poly_over_z/arithmetic.rs index 0767e3a7..22c4bed3 100644 --- a/src/integer/mat_poly_over_z/arithmetic.rs +++ b/src/integer/mat_poly_over_z/arithmetic.rs @@ -13,4 +13,5 @@ mod add; mod modulo; mod mul; mod mul_scalar; +mod neg; mod sub; diff --git a/src/integer/mat_poly_over_z/arithmetic/neg.rs b/src/integer/mat_poly_over_z/arithmetic/neg.rs new file mode 100644 index 00000000..eeaac073 --- /dev/null +++ b/src/integer/mat_poly_over_z/arithmetic/neg.rs @@ -0,0 +1,90 @@ +// 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 . + +//! Implementation of the [`Neg`] trait for [`MatPolyOverZ`] values. + +use super::super::MatPolyOverZ; +use crate::traits::MatrixDimensions; +use flint3_sys::fmpz_poly_mat_neg; +use std::ops::Neg; + +impl Neg for MatPolyOverZ { + type Output = MatPolyOverZ; + + /// Implements the [`Neg`] trait for [`MatPolyOverZ`] values. + /// [`Neg`] is implemented for both borrowed and owned [`MatPolyOverZ`]. + /// + /// When called on owned [`MatPolyOverZ`] it will reuse the memory of `self`. + /// + /// Returns the additive inverse of `self` as a [`MatPolyOverZ`]. + /// + /// # Examples + /// ``` + /// use qfall_math::integer::MatPolyOverZ; + /// use std::str::FromStr; + /// + /// let a: MatPolyOverZ = MatPolyOverZ::from_str("[[2 1 -2, 0],[1 42, 3 1 2 3]]").unwrap(); + /// + /// let b: MatPolyOverZ = -&a; + /// let c: MatPolyOverZ = -a; + /// ``` + fn neg(mut self) -> Self::Output { + unsafe { fmpz_poly_mat_neg(&mut self.matrix, &self.matrix) }; + self + } +} + +impl Neg for &MatPolyOverZ { + type Output = MatPolyOverZ; + + /// Documentation at [`MatPolyOverZ::neg`]. + fn neg(self) -> Self::Output { + let mut out = MatPolyOverZ::new(self.get_num_rows(), self.get_num_columns()); + unsafe { fmpz_poly_mat_neg(&mut out.matrix, &self.matrix) }; + out + } +} + +#[cfg(test)] +mod test_neg { + use super::MatPolyOverZ; + use std::str::FromStr; + + /// Ensure that `neg` works for small coefficients. + #[test] + fn correct_small() { + let a = MatPolyOverZ::from_str("[[2 1 -2, 0, 1 5],[1 -3, 3 1 0 4, 0]]").unwrap(); + let b = MatPolyOverZ::from_str("[[2 -1 2, 0, 1 -5],[1 3, 3 -1 0 -4, 0]]").unwrap(); + let c = MatPolyOverZ::new(2, 3); + + assert_eq!(a, -(-&a)); + assert_eq!(a, -&b); + assert_eq!(b, -&a); + assert_eq!(a, -b.clone()); + assert_eq!(b, -a); + assert_eq!(c, -&c); + assert_eq!(c.clone(), -c); + } + + /// Ensure that `neg` works for large coefficients. + #[test] + fn correct_large() { + let a = + MatPolyOverZ::from_str(&format!("[[2 {} {}],[1 1]]", u64::MAX, i64::MIN)).unwrap(); + let b = MatPolyOverZ::from_str(&format!( + "[[2 -{} {}],[1 -1]]", + u64::MAX, + i64::MAX as u64 + 1 + )) + .unwrap(); + + assert_eq!(a, -(-&a)); + assert_eq!(b, -&a); + assert_eq!(b, -a); + } +} diff --git a/src/integer/mat_z/arithmetic.rs b/src/integer/mat_z/arithmetic.rs index aaeff03d..a23bd67b 100644 --- a/src/integer/mat_z/arithmetic.rs +++ b/src/integer/mat_z/arithmetic.rs @@ -14,4 +14,5 @@ mod div; mod modulo; mod mul; mod mul_scalar; +mod neg; mod sub; diff --git a/src/integer/mat_z/arithmetic/neg.rs b/src/integer/mat_z/arithmetic/neg.rs new file mode 100644 index 00000000..5d0926a1 --- /dev/null +++ b/src/integer/mat_z/arithmetic/neg.rs @@ -0,0 +1,89 @@ +// 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 . + +//! Implementation of the [`Neg`] trait for [`MatZ`] values. + +use super::super::MatZ; +use crate::traits::MatrixDimensions; +use flint3_sys::fmpz_mat_neg; +use std::ops::Neg; + +impl Neg for MatZ { + type Output = MatZ; + + /// Implements the [`Neg`] trait for [`MatZ`] values. + /// [`Neg`] is implemented for both borrowed and owned [`MatZ`]. + /// + /// When called on owned [`MatZ`] it will reuse the memory of `self`. + /// + /// Returns the additive inverse of `self` as a [`MatZ`]. + /// + /// # Examples + /// ``` + /// use qfall_math::integer::MatZ; + /// use std::str::FromStr; + /// + /// let a: MatZ = MatZ::from_str("[[1, -2],[3, 42]]").unwrap(); + /// + /// let b: MatZ = -&a; + /// let c: MatZ = -a; + /// ``` + fn neg(mut self) -> Self::Output { + unsafe { fmpz_mat_neg(&mut self.matrix, &self.matrix) }; + self + } +} + +impl Neg for &MatZ { + type Output = MatZ; + + /// Documentation at [`MatZ::neg`]. + fn neg(self) -> Self::Output { + let mut out = MatZ::new(self.get_num_rows(), self.get_num_columns()); + unsafe { fmpz_mat_neg(&mut out.matrix, &self.matrix) }; + out + } +} + +#[cfg(test)] +mod test_neg { + use super::MatZ; + use std::str::FromStr; + + /// Ensure that `neg` works for small entries. + #[test] + fn correct_small() { + let a = MatZ::from_str("[[1, -2, 0],[3, 4, -5]]").unwrap(); + let b = MatZ::from_str("[[-1, 2, 0],[-3, -4, 5]]").unwrap(); + let c = MatZ::new(2, 3); + + assert_eq!(a, -(-&a)); + assert_eq!(a, -&b); + assert_eq!(b, -&a); + assert_eq!(a, -b.clone()); + assert_eq!(b, -a); + assert_eq!(c, -&c); + assert_eq!(c.clone(), -c); + } + + /// Ensure that `neg` works for large entries. + #[test] + fn correct_large() { + let a = MatZ::from_str(&format!("[[{}, {}],[1, 2]]", u64::MAX, i64::MIN)).unwrap(); + let b = MatZ::from_str(&format!( + "[[-{}, {}],[-1, -2]]", + u64::MAX, + i64::MAX as u64 + 1 + )) + .unwrap(); + + assert_eq!(a, -(-&a)); + assert_eq!(b, -&a); + assert_eq!(b, -a); + } +} diff --git a/src/integer/poly_over_z/arithmetic.rs b/src/integer/poly_over_z/arithmetic.rs index 0767e3a7..22c4bed3 100644 --- a/src/integer/poly_over_z/arithmetic.rs +++ b/src/integer/poly_over_z/arithmetic.rs @@ -13,4 +13,5 @@ mod add; mod modulo; mod mul; mod mul_scalar; +mod neg; mod sub; diff --git a/src/integer/poly_over_z/arithmetic/neg.rs b/src/integer/poly_over_z/arithmetic/neg.rs new file mode 100644 index 00000000..f118aa54 --- /dev/null +++ b/src/integer/poly_over_z/arithmetic/neg.rs @@ -0,0 +1,84 @@ +// 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 . + +//! Implementation of the [`Neg`] trait for [`PolyOverZ`] values. + +use super::super::PolyOverZ; +use flint3_sys::fmpz_poly_neg; +use std::ops::Neg; + +impl Neg for PolyOverZ { + type Output = PolyOverZ; + + /// Implements the [`Neg`] trait for [`PolyOverZ`] values. + /// [`Neg`] is implemented for both borrowed and owned [`PolyOverZ`]. + /// + /// When called on owned [`PolyOverZ`] it will reuse the memory of `self`. + /// + /// Returns the additive inverse of `self` as a [`PolyOverZ`]. + /// + /// # Examples + /// ``` + /// use qfall_math::integer::PolyOverZ; + /// use std::str::FromStr; + /// + /// let a: PolyOverZ = PolyOverZ::from_str("3 1 -2 42").unwrap(); + /// + /// let b: PolyOverZ = -&a; + /// let c: PolyOverZ = -a; + /// ``` + fn neg(mut self) -> Self::Output { + unsafe { fmpz_poly_neg(&mut self.poly, &self.poly) }; + self + } +} + +impl Neg for &PolyOverZ { + type Output = PolyOverZ; + + /// Documentation at [`PolyOverZ::neg`]. + fn neg(self) -> Self::Output { + let mut out = PolyOverZ::default(); + unsafe { fmpz_poly_neg(&mut out.poly, &self.poly) }; + out + } +} + +#[cfg(test)] +mod test_neg { + use super::PolyOverZ; + use std::str::FromStr; + + /// Ensure that `neg` works for small coefficients. + #[test] + fn correct_small() { + let a = PolyOverZ::from_str("4 1 -2 0 3").unwrap(); + let b = PolyOverZ::from_str("4 -1 2 0 -3").unwrap(); + let c = PolyOverZ::default(); + + assert_eq!(a, -(-&a)); + assert_eq!(a, -&b); + assert_eq!(b, -&a); + assert_eq!(a, -b.clone()); + assert_eq!(b, -a); + assert_eq!(c, -&c); + assert_eq!(c.clone(), -c); + } + + /// Ensure that `neg` works for large coefficients. + #[test] + fn correct_large() { + let a = PolyOverZ::from_str(&format!("3 {} 0 {}", u64::MAX, i64::MIN)).unwrap(); + let b = + PolyOverZ::from_str(&format!("3 -{} 0 {}", u64::MAX, i64::MAX as u64 + 1)).unwrap(); + + assert_eq!(a, -(-&a)); + assert_eq!(b, -&a); + assert_eq!(b, -a); + } +} diff --git a/src/integer_mod_q/mat_polynomial_ring_zq/arithmetic.rs b/src/integer_mod_q/mat_polynomial_ring_zq/arithmetic.rs index f829920a..128e3620 100644 --- a/src/integer_mod_q/mat_polynomial_ring_zq/arithmetic.rs +++ b/src/integer_mod_q/mat_polynomial_ring_zq/arithmetic.rs @@ -12,4 +12,5 @@ mod add; mod mul; mod mul_scalar; +mod neg; mod sub; diff --git a/src/integer_mod_q/mat_polynomial_ring_zq/arithmetic/neg.rs b/src/integer_mod_q/mat_polynomial_ring_zq/arithmetic/neg.rs new file mode 100644 index 00000000..4d5a31bf --- /dev/null +++ b/src/integer_mod_q/mat_polynomial_ring_zq/arithmetic/neg.rs @@ -0,0 +1,115 @@ +// 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 . + +//! Implementation of the [`Neg`] trait for [`MatPolynomialRingZq`] values. + +use super::super::MatPolynomialRingZq; +use flint3_sys::fmpz_poly_mat_neg; +use std::ops::Neg; + +impl Neg for MatPolynomialRingZq { + type Output = MatPolynomialRingZq; + + /// Implements the [`Neg`] trait for [`MatPolynomialRingZq`] values. + /// [`Neg`] is implemented for both borrowed and owned [`MatPolynomialRingZq`]. + /// + /// When called on owned [`MatPolynomialRingZq`] it will reuse the memory of `self`. + /// + /// Returns the additive inverse of `self` as a [`MatPolynomialRingZq`]. + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq}; + /// use qfall_math::integer::MatPolyOverZ; + /// use std::str::FromStr; + /// + /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap(); + /// let poly_mat = MatPolyOverZ::from_str("[[2 1 2, 0],[1 42, 3 1 2 3]]").unwrap(); + /// let a: MatPolynomialRingZq = MatPolynomialRingZq::from((&poly_mat, &modulus)); + /// + /// let b: MatPolynomialRingZq = -&a; + /// let c: MatPolynomialRingZq = -a; + /// ``` + fn neg(mut self) -> Self::Output { + unsafe { fmpz_poly_mat_neg(&mut self.matrix.matrix, &self.matrix.matrix) }; + self.reduce(); + self + } +} + +impl Neg for &MatPolynomialRingZq { + type Output = MatPolynomialRingZq; + + /// Documentation at [`MatPolynomialRingZq::neg`]. + fn neg(self) -> Self::Output { + let mut out = MatPolynomialRingZq { + matrix: -&self.matrix, + modulus: self.modulus.clone(), + }; + out.reduce(); + out + } +} + +#[cfg(test)] +mod test_neg { + use super::MatPolynomialRingZq; + use crate::integer::MatPolyOverZ; + use crate::integer_mod_q::ModulusPolynomialRingZq; + use std::str::FromStr; + + /// Ensure that `neg` works for small coefficients. + #[test] + fn correct_small() { + let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap(); + let mat_a = MatPolyOverZ::from_str("[[2 1 -2, 0],[1 -3, 3 1 0 4]]").unwrap(); + let mat_b = MatPolyOverZ::from_str("[[2 -1 2, 0],[1 3, 3 -1 0 -4]]").unwrap(); + let a = MatPolynomialRingZq::from((&mat_a, &modulus)); + let b = MatPolynomialRingZq::from((&mat_b, &modulus)); + let c = MatPolynomialRingZq::new(2, 2, &modulus); + + assert_eq!(a, -(-&a)); + assert_eq!(a, -&b); + assert_eq!(b, -&a); + assert_eq!(a, -b.clone()); + assert_eq!(b, -a); + assert_eq!(c, -&c); + assert_eq!(c.clone(), -c); + } + + /// Ensure that `neg` works for large coefficients and moduli. + #[test] + fn correct_large() { + let modulus = + ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {}", u64::MAX)).unwrap(); + let mat_a = MatPolyOverZ::from_str(&format!("[[2 {} 1],[0]]", i64::MAX)).unwrap(); + let mat_b = MatPolyOverZ::from_str(&format!("[[2 -{} -1],[0]]", i64::MAX)).unwrap(); + let a = MatPolynomialRingZq::from((&mat_a, &modulus)); + let b = MatPolynomialRingZq::from((&mat_b, &modulus)); + + assert_eq!(a, -(-&a)); + assert_eq!(b, -&a); + assert_eq!(b, -a); + } + + /// Ensure that the result is reduced to the least non-negative residues + /// and that the modulus is kept. + #[test] + fn reduced() { + let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap(); + let mat_a = MatPolyOverZ::from_str("[[2 1 16, 0],[1 8, 3 1 0 4]]").unwrap(); + let a = MatPolynomialRingZq::from((&mat_a, &modulus)); + let cmp = MatPolyOverZ::from_str("[[2 16 1, 0],[1 9, 3 16 0 13]]").unwrap(); + + let neg_a = -&a; + + assert_eq!(cmp, neg_a.get_representative_least_nonnegative_residue()); + assert_eq!(cmp, (-a).get_representative_least_nonnegative_residue()); + assert_eq!(modulus, neg_a.get_mod()); + } +} diff --git a/src/integer_mod_q/mat_zq/arithmetic.rs b/src/integer_mod_q/mat_zq/arithmetic.rs index 95249458..5ad590df 100644 --- a/src/integer_mod_q/mat_zq/arithmetic.rs +++ b/src/integer_mod_q/mat_zq/arithmetic.rs @@ -12,4 +12,5 @@ mod add; mod mul; mod mul_scalar; +mod neg; mod sub; diff --git a/src/integer_mod_q/mat_zq/arithmetic/neg.rs b/src/integer_mod_q/mat_zq/arithmetic/neg.rs new file mode 100644 index 00000000..c3943961 --- /dev/null +++ b/src/integer_mod_q/mat_zq/arithmetic/neg.rs @@ -0,0 +1,112 @@ +// 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 . + +//! Implementation of the [`Neg`] trait for [`MatZq`] values. + +use super::super::MatZq; +use crate::traits::MatrixDimensions; +use flint3_sys::fmpz_mod_mat_neg; +use std::ops::Neg; + +impl Neg for MatZq { + type Output = MatZq; + + /// Implements the [`Neg`] trait for [`MatZq`] values. + /// [`Neg`] is implemented for both borrowed and owned [`MatZq`]. + /// + /// When called on owned [`MatZq`] it will reuse the memory of `self`. + /// + /// Returns the additive inverse of `self` as a [`MatZq`]. + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::MatZq; + /// use std::str::FromStr; + /// + /// let a: MatZq = MatZq::from_str("[[1, 2],[3, 42]] mod 17").unwrap(); + /// + /// let b: MatZq = -&a; + /// let c: MatZq = -a; + /// ``` + fn neg(mut self) -> Self::Output { + unsafe { + fmpz_mod_mat_neg( + &mut self.matrix, + &self.matrix, + self.modulus.get_fmpz_mod_ctx_struct(), + ) + }; + self + } +} + +impl Neg for &MatZq { + type Output = MatZq; + + /// Documentation at [`MatZq::neg`]. + fn neg(self) -> Self::Output { + let mut out = MatZq::new(self.get_num_rows(), self.get_num_columns(), self.get_mod()); + unsafe { + fmpz_mod_mat_neg( + &mut out.matrix, + &self.matrix, + self.modulus.get_fmpz_mod_ctx_struct(), + ) + }; + out + } +} + +#[cfg(test)] +mod test_neg { + use super::MatZq; + use crate::integer::MatZ; + use std::str::FromStr; + + /// Ensure that `neg` works for small entries. + #[test] + fn correct_small() { + let a = MatZq::from_str("[[1, -2, 0],[3, 4, -5]] mod 17").unwrap(); + let b = MatZq::from_str("[[-1, 2, 0],[-3, -4, 5]] mod 17").unwrap(); + let c = MatZq::new(2, 3, 17); + + assert_eq!(a, -(-&a)); + assert_eq!(a, -&b); + assert_eq!(b, -&a); + assert_eq!(a, -b.clone()); + assert_eq!(b, -a); + assert_eq!(c, -&c); + assert_eq!(c.clone(), -c); + } + + /// Ensure that `neg` works for large entries and moduli. + #[test] + fn correct_large() { + let a = MatZq::from_str(&format!("[[{}, 1],[0, 2]] mod {}", i64::MAX, u64::MAX)).unwrap(); + let b = + MatZq::from_str(&format!("[[-{}, -1],[0, -2]] mod {}", i64::MAX, u64::MAX)).unwrap(); + + assert_eq!(a, -(-&a)); + assert_eq!(b, -&a); + assert_eq!(b, -a); + } + + /// Ensure that the result is reduced to the least non-negative residues + /// and that the modulus is kept. + #[test] + fn reduced() { + let a = MatZq::from_str("[[1, 16, 0],[3, 8, 9]] mod 17").unwrap(); + let cmp = MatZ::from_str("[[16, 1, 0],[14, 9, 8]]").unwrap(); + + let neg_a = -&a; + + assert_eq!(cmp, neg_a.get_representative_least_nonnegative_residue()); + assert_eq!(cmp, (-a).get_representative_least_nonnegative_residue()); + assert_eq!(17, neg_a.get_mod()); + } +} diff --git a/src/integer_mod_q/poly_over_zq/arithmetic.rs b/src/integer_mod_q/poly_over_zq/arithmetic.rs index 95249458..5ad590df 100644 --- a/src/integer_mod_q/poly_over_zq/arithmetic.rs +++ b/src/integer_mod_q/poly_over_zq/arithmetic.rs @@ -12,4 +12,5 @@ mod add; mod mul; mod mul_scalar; +mod neg; mod sub; diff --git a/src/integer_mod_q/poly_over_zq/arithmetic/neg.rs b/src/integer_mod_q/poly_over_zq/arithmetic/neg.rs new file mode 100644 index 00000000..568a8a7a --- /dev/null +++ b/src/integer_mod_q/poly_over_zq/arithmetic/neg.rs @@ -0,0 +1,110 @@ +// 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 . + +//! Implementation of the [`Neg`] trait for [`PolyOverZq`] values. + +use super::super::PolyOverZq; +use flint3_sys::fmpz_mod_poly_neg; +use std::ops::Neg; + +impl Neg for PolyOverZq { + type Output = PolyOverZq; + + /// Implements the [`Neg`] trait for [`PolyOverZq`] values. + /// [`Neg`] is implemented for both borrowed and owned [`PolyOverZq`]. + /// + /// When called on owned [`PolyOverZq`] it will reuse the memory of `self`. + /// + /// Returns the additive inverse of `self` as a [`PolyOverZq`]. + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::PolyOverZq; + /// use std::str::FromStr; + /// + /// let a: PolyOverZq = PolyOverZq::from_str("3 1 2 42 mod 17").unwrap(); + /// + /// let b: PolyOverZq = -&a; + /// let c: PolyOverZq = -a; + /// ``` + fn neg(mut self) -> Self::Output { + unsafe { + fmpz_mod_poly_neg( + &mut self.poly, + &self.poly, + self.modulus.get_fmpz_mod_ctx_struct(), + ) + }; + self + } +} + +impl Neg for &PolyOverZq { + type Output = PolyOverZq; + + /// Documentation at [`PolyOverZq::neg`]. + fn neg(self) -> Self::Output { + let mut out = PolyOverZq::from(&self.modulus); + unsafe { + fmpz_mod_poly_neg( + &mut out.poly, + &self.poly, + self.modulus.get_fmpz_mod_ctx_struct(), + ) + }; + out + } +} + +#[cfg(test)] +mod test_neg { + use super::PolyOverZq; + use crate::integer::PolyOverZ; + use std::str::FromStr; + + /// Ensure that `neg` works for small coefficients. + #[test] + fn correct_small() { + let a = PolyOverZq::from_str("4 1 -2 0 3 mod 17").unwrap(); + let b = PolyOverZq::from_str("4 -1 2 0 -3 mod 17").unwrap(); + let c = PolyOverZq::from_str("0 mod 17").unwrap(); + + assert_eq!(a, -(-&a)); + assert_eq!(a, -&b); + assert_eq!(b, -&a); + assert_eq!(a, -b.clone()); + assert_eq!(b, -a); + assert_eq!(c, -&c); + assert_eq!(c.clone(), -c); + } + + /// Ensure that `neg` works for large coefficients and moduli. + #[test] + fn correct_large() { + let a = PolyOverZq::from_str(&format!("3 {} 0 1 mod {}", i64::MAX, u64::MAX)).unwrap(); + let b = PolyOverZq::from_str(&format!("3 -{} 0 -1 mod {}", i64::MAX, u64::MAX)).unwrap(); + + assert_eq!(a, -(-&a)); + assert_eq!(b, -&a); + assert_eq!(b, -a); + } + + /// Ensure that the result is reduced to the least non-negative residues + /// and that the modulus is kept. + #[test] + fn reduced() { + let a = PolyOverZq::from_str("4 1 16 0 8 mod 17").unwrap(); + let cmp = PolyOverZ::from_str("4 16 1 0 9").unwrap(); + + let neg_a = -&a; + + assert_eq!(cmp, neg_a.get_representative_least_nonnegative_residue()); + assert_eq!(cmp, (-a).get_representative_least_nonnegative_residue()); + assert_eq!(17, neg_a.get_mod()); + } +} diff --git a/src/integer_mod_q/polynomial_ring_zq/arithmetic.rs b/src/integer_mod_q/polynomial_ring_zq/arithmetic.rs index 95249458..5ad590df 100644 --- a/src/integer_mod_q/polynomial_ring_zq/arithmetic.rs +++ b/src/integer_mod_q/polynomial_ring_zq/arithmetic.rs @@ -12,4 +12,5 @@ mod add; mod mul; mod mul_scalar; +mod neg; mod sub; diff --git a/src/integer_mod_q/polynomial_ring_zq/arithmetic/neg.rs b/src/integer_mod_q/polynomial_ring_zq/arithmetic/neg.rs new file mode 100644 index 00000000..2a76df64 --- /dev/null +++ b/src/integer_mod_q/polynomial_ring_zq/arithmetic/neg.rs @@ -0,0 +1,116 @@ +// 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 . + +//! Implementation of the [`Neg`] trait for [`PolynomialRingZq`] values. + +use super::super::PolynomialRingZq; +use flint3_sys::fmpz_poly_neg; +use std::ops::Neg; + +impl Neg for PolynomialRingZq { + type Output = PolynomialRingZq; + + /// Implements the [`Neg`] trait for [`PolynomialRingZq`] values. + /// [`Neg`] is implemented for both borrowed and owned [`PolynomialRingZq`]. + /// + /// When called on owned [`PolynomialRingZq`] it will reuse the memory of `self`. + /// + /// Returns the additive inverse of `self` as a [`PolynomialRingZq`]. + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq}; + /// use qfall_math::integer::PolyOverZ; + /// use std::str::FromStr; + /// + /// let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap(); + /// let poly = PolyOverZ::from_str("3 1 2 42").unwrap(); + /// let a: PolynomialRingZq = PolynomialRingZq::from((&poly, &modulus)); + /// + /// let b: PolynomialRingZq = -&a; + /// let c: PolynomialRingZq = -a; + /// ``` + fn neg(mut self) -> Self::Output { + unsafe { fmpz_poly_neg(&mut self.poly.poly, &self.poly.poly) }; + self.reduce(); + self + } +} + +impl Neg for &PolynomialRingZq { + type Output = PolynomialRingZq; + + /// Documentation at [`PolynomialRingZq::neg`]. + fn neg(self) -> Self::Output { + let mut out = PolynomialRingZq { + poly: -&self.poly, + modulus: self.modulus.clone(), + }; + out.reduce(); + out + } +} + +#[cfg(test)] +mod test_neg { + use super::PolynomialRingZq; + use crate::integer::PolyOverZ; + use crate::integer_mod_q::ModulusPolynomialRingZq; + use std::str::FromStr; + + /// Ensure that `neg` works for small coefficients. + #[test] + fn correct_small() { + let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap(); + let a = PolynomialRingZq::from((&PolyOverZ::from_str("3 1 -2 3").unwrap(), &modulus)); + let b = PolynomialRingZq::from((&PolyOverZ::from_str("3 -1 2 -3").unwrap(), &modulus)); + let c = PolynomialRingZq::from((&PolyOverZ::default(), &modulus)); + + assert_eq!(a, -(-&a)); + assert_eq!(a, -&b); + assert_eq!(b, -&a); + assert_eq!(a, -b.clone()); + assert_eq!(b, -a); + assert_eq!(c, -&c); + assert_eq!(c.clone(), -c); + } + + /// Ensure that `neg` works for large coefficients and moduli. + #[test] + fn correct_large() { + let modulus = + ModulusPolynomialRingZq::from_str(&format!("4 1 0 0 1 mod {}", u64::MAX)).unwrap(); + let a = PolynomialRingZq::from(( + &PolyOverZ::from_str(&format!("3 {} 0 1", i64::MAX)).unwrap(), + &modulus, + )); + let b = PolynomialRingZq::from(( + &PolyOverZ::from_str(&format!("3 -{} 0 -1", i64::MAX)).unwrap(), + &modulus, + )); + + assert_eq!(a, -(-&a)); + assert_eq!(b, -&a); + assert_eq!(b, -a); + } + + /// Ensure that the result is reduced to the least non-negative residues + /// and that the modulus is kept. + #[test] + fn reduced() { + let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap(); + let a = PolynomialRingZq::from((&PolyOverZ::from_str("3 1 16 8").unwrap(), &modulus)); + let cmp = PolyOverZ::from_str("3 16 1 9").unwrap(); + + let neg_a = -&a; + + assert_eq!(cmp, neg_a.get_representative_least_nonnegative_residue()); + assert_eq!(cmp, (-a).get_representative_least_nonnegative_residue()); + assert_eq!(modulus, neg_a.get_mod()); + } +} diff --git a/src/integer_mod_q/z_q/arithmetic.rs b/src/integer_mod_q/z_q/arithmetic.rs index e8bbe62e..90357f6a 100644 --- a/src/integer_mod_q/z_q/arithmetic.rs +++ b/src/integer_mod_q/z_q/arithmetic.rs @@ -11,5 +11,6 @@ mod add; mod mul; +mod neg; mod pow; mod sub; diff --git a/src/integer_mod_q/z_q/arithmetic/neg.rs b/src/integer_mod_q/z_q/arithmetic/neg.rs new file mode 100644 index 00000000..29b06a29 --- /dev/null +++ b/src/integer_mod_q/z_q/arithmetic/neg.rs @@ -0,0 +1,123 @@ +// 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 . + +//! Implementation of the [`Neg`] trait for [`Zq`] values. + +use super::super::Zq; +use crate::integer::Z; +use flint3_sys::fmpz_mod_neg; +use std::ops::Neg; + +impl Neg for Zq { + type Output = Zq; + + /// Implements the [`Neg`] trait for [`Zq`] values. + /// [`Neg`] is implemented for both borrowed and owned [`Zq`]. + /// + /// When called on owned [`Zq`] it will reuse the memory of `self`. + /// + /// Returns the additive inverse of `self` as a [`Zq`]. + /// + /// # Examples + /// ``` + /// use qfall_math::integer_mod_q::Zq; + /// + /// let a: Zq = Zq::from((42, 17)); + /// + /// let b: Zq = -&a; + /// let c: Zq = -a; + /// ``` + fn neg(mut self) -> Self::Output { + unsafe { + fmpz_mod_neg( + &mut self.value.value, + &self.value.value, + self.modulus.get_fmpz_mod_ctx_struct(), + ) + }; + self + } +} + +impl Neg for &Zq { + type Output = Zq; + + /// Documentation at [`Zq::neg`]. + fn neg(self) -> Self::Output { + let mut out = Z::ZERO; + unsafe { + fmpz_mod_neg( + &mut out.value, + &self.value.value, + self.modulus.get_fmpz_mod_ctx_struct(), + ) + }; + Zq { + value: out, + modulus: self.modulus.clone(), + } + } +} + +#[cfg(test)] +mod test_neg { + use super::Zq; + use crate::integer::Z; + + /// Ensure that `neg` works for small values. + #[test] + fn correct_small() { + let a = Zq::from((1, 17)); + let b = Zq::from((-1, 17)); + let c = Zq::from((0, 17)); + + assert_eq!(a, -(-&a)); + assert_eq!(a, -&b); + assert_eq!(b, -&a); + assert_eq!(a, -b.clone()); + assert_eq!(b, -a); + assert_eq!(c, -&c); + assert_eq!(c.clone(), -c); + } + + /// Ensure that `neg` works for large values and moduli. + #[test] + fn correct_large() { + let a = Zq::from((i64::MAX, u64::MAX)); + let b = Zq::from((u64::MAX - i64::MAX as u64, u64::MAX)); + + assert_eq!(a, -(-&a)); + assert_eq!(b, -&a); + assert_eq!(b, -a); + } + + /// Ensure that the result is reduced to the least non-negative residue + /// and that the modulus is kept. + #[test] + fn reduced() { + let a = Zq::from((3, 17)); + let c = Zq::from((0, 17)); + + let neg_a = -&a; + let neg_c = -c; + + assert_eq!( + Z::from(14), + neg_a.get_representative_least_nonnegative_residue() + ); + assert_eq!( + Z::from(14), + (-a).get_representative_least_nonnegative_residue() + ); + assert_eq!( + Z::ZERO, + neg_c.get_representative_least_nonnegative_residue() + ); + assert_eq!(Z::from(17), neg_a.get_mod()); + } +} diff --git a/src/rational/mat_q/arithmetic.rs b/src/rational/mat_q/arithmetic.rs index 6b8278f9..2636f54c 100644 --- a/src/rational/mat_q/arithmetic.rs +++ b/src/rational/mat_q/arithmetic.rs @@ -13,4 +13,5 @@ mod add; mod div_scalar; mod mul; mod mul_scalar; +mod neg; mod sub; diff --git a/src/rational/mat_q/arithmetic/neg.rs b/src/rational/mat_q/arithmetic/neg.rs new file mode 100644 index 00000000..6450306e --- /dev/null +++ b/src/rational/mat_q/arithmetic/neg.rs @@ -0,0 +1,84 @@ +// 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 . + +//! Implementation of the [`Neg`] trait for [`MatQ`] values. + +use super::super::MatQ; +use crate::traits::MatrixDimensions; +use flint3_sys::fmpq_mat_neg; +use std::ops::Neg; + +impl Neg for MatQ { + type Output = MatQ; + + /// Implements the [`Neg`] trait for [`MatQ`] values. + /// [`Neg`] is implemented for both borrowed and owned [`MatQ`]. + /// + /// When called on owned [`MatQ`] it will reuse the memory of `self`. + /// + /// Returns the additive inverse of `self` as a [`MatQ`]. + /// + /// # Examples + /// ``` + /// use qfall_math::rational::MatQ; + /// use std::str::FromStr; + /// + /// let a: MatQ = MatQ::from_str("[[1/2, -2],[3, 1/42]]").unwrap(); + /// + /// let b: MatQ = -&a; + /// let c: MatQ = -a; + /// ``` + fn neg(mut self) -> Self::Output { + unsafe { fmpq_mat_neg(&mut self.matrix, &self.matrix) }; + self + } +} + +impl Neg for &MatQ { + type Output = MatQ; + + /// Documentation at [`MatQ::neg`]. + fn neg(self) -> Self::Output { + let mut out = MatQ::new(self.get_num_rows(), self.get_num_columns()); + unsafe { fmpq_mat_neg(&mut out.matrix, &self.matrix) }; + out + } +} + +#[cfg(test)] +mod test_neg { + use super::MatQ; + use std::str::FromStr; + + /// Ensure that `neg` works for small entries. + #[test] + fn correct_small() { + let a = MatQ::from_str("[[1/2, -2, 0],[3, 4/7, -5]]").unwrap(); + let b = MatQ::from_str("[[-1/2, 2, 0],[-3, -4/7, 5]]").unwrap(); + let c = MatQ::new(2, 3); + + assert_eq!(a, -(-&a)); + assert_eq!(a, -&b); + assert_eq!(b, -&a); + assert_eq!(a, -b.clone()); + assert_eq!(b, -a); + assert_eq!(c, -&c); + assert_eq!(c.clone(), -c); + } + + /// Ensure that `neg` works for large entries. + #[test] + fn correct_large() { + let a = MatQ::from_str(&format!("[[{}, 1/{}],[1, 2]]", u64::MAX, i64::MAX)).unwrap(); + let b = MatQ::from_str(&format!("[[-{}, -1/{}],[-1, -2]]", u64::MAX, i64::MAX)).unwrap(); + + assert_eq!(a, -(-&a)); + assert_eq!(b, -&a); + assert_eq!(b, -a); + } +} diff --git a/src/rational/poly_over_q/arithmetic.rs b/src/rational/poly_over_q/arithmetic.rs index 0cf420f6..fa5957b2 100644 --- a/src/rational/poly_over_q/arithmetic.rs +++ b/src/rational/poly_over_q/arithmetic.rs @@ -14,4 +14,5 @@ mod div_scalar; mod exp; mod mul; mod mul_scalar; +mod neg; mod sub; diff --git a/src/rational/poly_over_q/arithmetic/neg.rs b/src/rational/poly_over_q/arithmetic/neg.rs new file mode 100644 index 00000000..8ec4f7aa --- /dev/null +++ b/src/rational/poly_over_q/arithmetic/neg.rs @@ -0,0 +1,83 @@ +// 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 . + +//! Implementation of the [`Neg`] trait for [`PolyOverQ`] values. + +use super::super::PolyOverQ; +use flint3_sys::fmpq_poly_neg; +use std::ops::Neg; + +impl Neg for PolyOverQ { + type Output = PolyOverQ; + + /// Implements the [`Neg`] trait for [`PolyOverQ`] values. + /// [`Neg`] is implemented for both borrowed and owned [`PolyOverQ`]. + /// + /// When called on owned [`PolyOverQ`] it will reuse the memory of `self`. + /// + /// Returns the additive inverse of `self` as a [`PolyOverQ`]. + /// + /// # Examples + /// ``` + /// use qfall_math::rational::PolyOverQ; + /// use std::str::FromStr; + /// + /// let a: PolyOverQ = PolyOverQ::from_str("3 1/2 -2 42").unwrap(); + /// + /// let b: PolyOverQ = -&a; + /// let c: PolyOverQ = -a; + /// ``` + fn neg(mut self) -> Self::Output { + unsafe { fmpq_poly_neg(&mut self.poly, &self.poly) }; + self + } +} + +impl Neg for &PolyOverQ { + type Output = PolyOverQ; + + /// Documentation at [`PolyOverQ::neg`]. + fn neg(self) -> Self::Output { + let mut out = PolyOverQ::default(); + unsafe { fmpq_poly_neg(&mut out.poly, &self.poly) }; + out + } +} + +#[cfg(test)] +mod test_neg { + use super::PolyOverQ; + use std::str::FromStr; + + /// Ensure that `neg` works for small coefficients. + #[test] + fn correct_small() { + let a = PolyOverQ::from_str("4 1/2 -2 0 3/7").unwrap(); + let b = PolyOverQ::from_str("4 -1/2 2 0 -3/7").unwrap(); + let c = PolyOverQ::default(); + + assert_eq!(a, -(-&a)); + assert_eq!(a, -&b); + assert_eq!(b, -&a); + assert_eq!(a, -b.clone()); + assert_eq!(b, -a); + assert_eq!(c, -&c); + assert_eq!(c.clone(), -c); + } + + /// Ensure that `neg` works for large coefficients. + #[test] + fn correct_large() { + let a = PolyOverQ::from_str(&format!("3 {} 0 1/{}", u64::MAX, i64::MAX)).unwrap(); + let b = PolyOverQ::from_str(&format!("3 -{} 0 -1/{}", u64::MAX, i64::MAX)).unwrap(); + + assert_eq!(a, -(-&a)); + assert_eq!(b, -&a); + assert_eq!(b, -a); + } +}