qfall-math 0.1.1

Mathematical foundations for rapid prototyping of lattice-based cryptography
Documentation
// Copyright © 2023 Marcel Luca Schmidt, Marvin Beckmann
//
// 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 get content of a [`PolynomialRingZq].

use super::PolynomialRingZq;
use crate::{
    integer::{PolyOverZ, Z},
    integer_mod_q::{ModulusPolynomialRingZq, Zq},
    traits::GetCoefficient,
};
use flint_sys::fmpz_poly::{fmpz_poly_degree, fmpz_poly_get_coeff_fmpz};

impl GetCoefficient<Zq> for PolynomialRingZq {
    /// Returns the coefficient of a [`PolynomialRingZq`] as a [`Zq`].
    ///
    /// If an index is provided which exceeds the highest set coefficient, `0` is returned.
    ///
    /// Parameters:
    /// - `index`: the index of the coefficient to get (has to be positive)
    ///
    /// Returns the coefficient as a [`Zq`], or a [`MathError`](crate::error::MathError) if the provided index
    /// is negative and therefore invalid, or it does not fit into an [`i64`].
    ///
    /// # Examples
    /// ```
    /// use qfall_math::traits::*;
    /// use qfall_math::integer_mod_q::{PolynomialRingZq, Zq};
    /// use std::str::FromStr;
    ///
    /// let poly_ring = PolynomialRingZq::from_str("3  0 1 1 / 4  1 0 0 1 mod 17").unwrap();
    ///
    /// let coeff_0: Zq = poly_ring.get_coeff(0).unwrap();
    /// let coeff_1: Zq = unsafe{ poly_ring.get_coeff_unchecked(1) };
    /// let coeff_3: Zq = poly_ring.get_coeff(3).unwrap();
    ///
    /// assert_eq!(Zq::from((0, 17)), coeff_0);
    /// assert_eq!(Zq::from((1, 17)), coeff_1);
    /// assert_eq!(Zq::from((0, 17)), coeff_3);
    /// ```
    ///
    /// # Safety
    /// To use this function safely, make sure that the selected index
    /// is greater or equal than `0`.
    unsafe fn get_coeff_unchecked(&self, index: i64) -> Zq {
        let mut out_z = Z::default();
        unsafe { fmpz_poly_get_coeff_fmpz(&mut out_z.value, &self.poly.poly, index) }
        Zq::from((out_z, &self.modulus.get_q()))
    }
}

impl GetCoefficient<Z> for PolynomialRingZq {
    /// Returns the coefficient of a [`PolynomialRingZq`] as a [`Z`].
    ///
    /// If an index is provided which exceeds the highest set coefficient, `0` is returned.
    ///
    /// Parameters:
    /// - `index`: the index of the coefficient to get (has to be positive)
    ///
    /// Returns the coefficient as a [`Z`], or a [`MathError`](crate::error::MathError) if the provided index
    /// is negative and therefore invalid, or it does not fit into an [`i64`].
    ///
    /// # Examples
    /// ```
    /// use qfall_math::traits::*;
    /// use qfall_math::integer::{PolyOverZ, Z};
    /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
    /// use std::str::FromStr;
    ///
    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
    /// let poly = PolyOverZ::from_str("3  0 1 1").unwrap();
    /// let poly_ring = PolynomialRingZq::from((&poly, &modulus));
    ///
    /// let coeff_0: Z = poly_ring.get_coeff(0).unwrap();
    /// let coeff_1: Z = unsafe{ poly_ring.get_coeff_unchecked(1) };
    /// let coeff_3: Z = poly_ring.get_coeff(3).unwrap();
    ///
    /// assert_eq!(Z::ZERO, coeff_0);
    /// assert_eq!(Z::ONE, coeff_1);
    /// assert_eq!(Z::ZERO, coeff_3);
    /// ```
    ///
    /// # Safety
    /// To use this function safely, make sure that the selected index
    /// is greater or equal than `0`.
    unsafe fn get_coeff_unchecked(&self, index: i64) -> Z {
        let mut out = Z::default();
        unsafe { fmpz_poly_get_coeff_fmpz(&mut out.value, &self.poly.poly, index) }
        out
    }
}

impl PolynomialRingZq {
    /// Returns the modulus object of the [`PolynomialRingZq`] element.
    ///
    /// # Examples
    /// ```
    /// use qfall_math::integer::PolyOverZ;
    /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
    /// use std::str::FromStr;
    ///
    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
    /// let poly = PolyOverZ::from_str("4  -1 0 1 1").unwrap();
    /// let poly_ring = PolynomialRingZq::from((&poly, &modulus));
    ///
    /// let poly_ring_mod = poly_ring.get_mod();
    ///
    /// assert_eq!(modulus, poly_ring_mod);
    /// ```
    pub fn get_mod(&self) -> ModulusPolynomialRingZq {
        self.modulus.clone()
    }

    /// Returns a representative polynomial of the [`PolynomialRingZq`] element.
    ///
    /// The representation of the coefficients is in the range `[0, modulus)` and
    /// the representation of the polynomial is in the range `[0, modulus_polynomial)`.
    ///
    /// # Examples
    /// ```
    /// use qfall_math::integer::PolyOverZ;
    /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
    /// use std::str::FromStr;
    ///
    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
    /// let poly = PolyOverZ::from_str("4  -1 0 1 1").unwrap();
    /// let poly_ring = PolynomialRingZq::from((&poly, &modulus));
    ///
    /// let poly_z = poly_ring.get_representative_least_nonnegative_residue();
    ///
    /// let cmp_poly = PolyOverZ::from_str("3  15 0 1").unwrap();
    /// assert_eq!(cmp_poly, poly_z);
    /// ```
    pub fn get_representative_least_nonnegative_residue(&self) -> PolyOverZ {
        self.poly.clone()
    }

    /// Returns the degree of a [`PolynomialRingZq`] as a [`i64`].
    /// The zero polynomial has degree `-1`.
    ///
    /// # Examples
    /// ```
    /// use qfall_math::integer::PolyOverZ;
    /// use qfall_math::integer_mod_q::{PolynomialRingZq, ModulusPolynomialRingZq};
    /// use std::str::FromStr;
    ///
    /// let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
    /// let poly = PolyOverZ::from_str("3  0 1 1").unwrap();
    /// let poly_ring = PolynomialRingZq::from((&poly, &modulus));
    ///
    /// let degree = poly_ring.get_degree();
    ///
    /// assert_eq!(2, degree);
    /// ```
    pub fn get_degree(&self) -> i64 {
        unsafe { fmpz_poly_degree(&self.poly.poly) }
    }
}

#[cfg(test)]
mod test_get_coeff {
    use crate::{
        integer::{PolyOverZ, Z},
        integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq, Zq},
        traits::GetCoefficient,
    };
    use std::str::FromStr;

    /// Ensure that 0 is returned if the provided index is not yet set.
    #[test]
    fn index_out_of_range() {
        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
        let poly = PolyOverZ::from_str("3  1 1 1").unwrap();
        let poly_ring = PolynomialRingZq::from((&poly, &modulus));

        let zero_coeff_z: Z = poly_ring.get_coeff(3).unwrap();
        let zero_coeff_zq = poly_ring.get_coeff(3).unwrap();

        assert_eq!(0, zero_coeff_z);
        assert_eq!(Zq::from((0, 17)), zero_coeff_zq);
    }

    /// Tests if coefficients are returned correctly.
    #[test]
    fn coeff_correct() {
        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
        let poly = PolyOverZ::from_str("3  1 0 3").unwrap();
        let poly_ring = PolynomialRingZq::from((&poly, &modulus));

        let coeff_z: Z = poly_ring.get_coeff(2).unwrap();
        let coeff_zq = poly_ring.get_coeff(2).unwrap();

        assert_eq!(3, coeff_z);
        assert_eq!(Zq::from((3, 17)), coeff_zq);
    }

    /// Tests if large coefficients are returned correctly.
    #[test]
    fn large_coeff() {
        let modulus =
            ModulusPolynomialRingZq::from_str(&format!("5  1 0 4 1 1 mod {}", u64::MAX)).unwrap();
        let poly = PolyOverZ::from_str(&format!("3  0 {} 1", i64::MAX)).unwrap();
        let poly_ring = PolynomialRingZq::from((&poly, &modulus));

        let coefficient_1: Z = poly_ring.get_coeff(1).unwrap();
        let coefficient_2: Zq = poly_ring.get_coeff(1).unwrap();

        assert_eq!(i64::MAX, coefficient_1);
        assert_eq!(Zq::from((i64::MAX, u64::MAX)), coefficient_2);
    }

    /// Tests if negative index yields an error in get_coeff with [`Z`].
    #[test]
    #[should_panic]
    fn negative_index_error_z() {
        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
        let poly = PolyOverZ::from_str("3  1 0 3").unwrap();
        let poly_ring = PolynomialRingZq::from((&poly, &modulus));

        let _: Z = poly_ring.get_coeff(-1).unwrap();
    }

    /// Tests if negative index yields an error in get_coeff with [`Zq`].
    #[test]
    #[should_panic]
    fn negative_index_error_zq() {
        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
        let poly = PolyOverZ::from_str("3  1 0 3").unwrap();
        let poly_ring = PolynomialRingZq::from((&poly, &modulus));

        let _: Zq = poly_ring.get_coeff(-1).unwrap();
    }
}

#[cfg(test)]
mod test_get_mod {
    use crate::{
        integer::PolyOverZ,
        integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
    };
    use std::str::FromStr;

    /// Ensure that the large modulus polynomial is returned correctly.
    #[test]
    fn large_positive() {
        let modulus =
            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {}", u64::MAX - 58))
                .unwrap();
        let poly = PolyOverZ::from_str("4  -1 0 1 1").unwrap();
        let poly_ring = PolynomialRingZq::from((&poly, &modulus));

        assert_eq!(modulus, poly_ring.get_mod());
    }
}

#[cfg(test)]
mod test_get_representative_least_nonnegative_residue {
    use crate::{
        integer::PolyOverZ,
        integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
    };
    use std::str::FromStr;

    /// Ensure that the getter works for large entries.
    #[test]
    fn large_positive() {
        let large_prime = u64::MAX - 58;
        let modulus =
            ModulusPolynomialRingZq::from_str(&format!("4  1 0 0 1 mod {large_prime}")).unwrap();
        let poly = PolyOverZ::from_str("4  -1 0 1 1").unwrap();
        let poly_ring = PolynomialRingZq::from((&poly, &modulus));

        let poly_z = poly_ring.get_representative_least_nonnegative_residue();

        let cmp_poly = PolyOverZ::from_str(&format!("3  {} 0 1", u64::MAX - 60)).unwrap();
        assert_eq!(cmp_poly, poly_z);
    }
}

#[cfg(test)]
mod test_get_degree {
    use crate::{
        integer::PolyOverZ,
        integer_mod_q::{ModulusPolynomialRingZq, PolynomialRingZq},
    };
    use std::str::FromStr;

    /// Ensure that degree is working.
    #[test]
    fn degree() {
        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
        let poly = PolyOverZ::from_str("3  0 1 1").unwrap();
        let poly_ring = PolynomialRingZq::from((&poly, &modulus));

        let deg = poly_ring.get_degree();

        assert_eq!(2, deg);
    }

    /// Ensure that degree is working for constant polynomials.
    #[test]
    fn degree_constant() {
        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
        let poly = PolyOverZ::from(1);
        let poly_ring = PolynomialRingZq::from((&poly, &modulus));

        let deg = poly_ring.get_degree();

        assert_eq!(0, deg);
    }

    /// Ensure that degree is working for polynomials with leading 0 coefficients.
    #[test]
    fn degree_leading_zeros() {
        let modulus = ModulusPolynomialRingZq::from_str("4  1 0 0 1 mod 17").unwrap();
        let poly = PolyOverZ::from_str("3  1 0 0").unwrap();
        let poly_ring = PolynomialRingZq::from((&poly, &modulus));

        let deg = poly_ring.get_degree();

        assert_eq!(0, deg);
    }
}