qfall-math 0.1.1

Mathematical foundations for rapid prototyping of lattice-based cryptography
Documentation
// Copyright © 2023 Marcel Luca Schmidt
//
// 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/>.

//! This module includes functionality about properties of [`MatPolynomialRingZq`] instances.

use super::MatPolynomialRingZq;
use crate::{
    integer::PolyOverZ,
    integer_mod_q::MatNTTPolynomialRingZq,
    traits::{MatrixDimensions, MatrixGetEntry},
};

impl MatPolynomialRingZq {
    /// Checks if a [`MatPolynomialRingZq`] is the identity matrix.
    ///
    /// Returns `true` if every diagonal entry of the  matrix is
    /// the constant polynomial `1` and all other entries are `0`.
    ///
    /// # Examples
    /// ```
    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
    /// use qfall_math::integer::MatPolyOverZ;
    /// use std::str::FromStr;
    ///
    /// let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
    /// let id_mat = MatPolyOverZ::identity(2, 2);
    ///
    /// let poly_ring_mat = MatPolynomialRingZq::from((id_mat, modulus));
    /// assert!(poly_ring_mat.is_identity());
    /// ```
    ///
    /// ```
    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
    /// use qfall_math::integer::MatPolyOverZ;
    /// use std::str::FromStr;
    ///
    /// let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
    /// let id_mat = MatPolyOverZ::from_str("[[1  1, 0],[0, 1  1],[0, 0]]").unwrap();
    ///
    /// let poly_ring_mat = MatPolynomialRingZq::from((id_mat, modulus));
    /// assert!(poly_ring_mat.is_identity());
    /// ```
    pub fn is_identity(&self) -> bool {
        self.matrix.is_identity()
    }

    /// Checks if a [`MatPolynomialRingZq`] is a square matrix.
    ///
    /// Returns `true` if the number of rows and columns is identical.
    ///
    /// # Examples
    /// ```
    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
    /// use qfall_math::integer::MatPolyOverZ;
    /// use std::str::FromStr;
    ///
    /// let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
    /// let poly_mat = MatPolyOverZ::from_str("[[1  13, 0],[2  1 1, 1  1]]").unwrap();
    ///
    /// let poly_ring_mat = MatPolynomialRingZq::from((poly_mat, modulus));
    /// assert!(poly_ring_mat.is_square());
    /// ```
    pub fn is_square(&self) -> bool {
        self.matrix.is_square()
    }

    /// Checks if every entry of a [`MatPolynomialRingZq`] is `0`.
    ///
    /// Returns `true` if every entry is `0`.
    ///
    /// # Examples
    /// ```
    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, PolyOverZq};
    /// use qfall_math::integer::MatPolyOverZ;
    /// use std::str::FromStr;
    ///
    /// let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
    /// let poly_mat = MatPolyOverZ::new(2,2);
    ///
    /// let poly_ring_mat = MatPolynomialRingZq::from((poly_mat, modulus));
    /// assert!(poly_ring_mat.is_zero());
    /// ```
    pub fn is_zero(&self) -> bool {
        self.matrix.is_zero()
    }

    /// Checks if a [`MatPolynomialRingZq`] is symmetric.
    ///
    /// Returns `true` if we have `a_ij == a_ji` for all i,j.
    ///
    /// # Examples
    /// ```
    /// use qfall_math::integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq};
    /// use std::str::FromStr;
    ///
    /// let modulus = ModulusPolynomialRingZq::from_str("2  2 1 mod 17").unwrap();
    /// let value = MatPolynomialRingZq::identity(2,2, modulus);
    /// assert!(value.is_symmetric());
    /// ```
    pub fn is_symmetric(&self) -> bool {
        if !self.is_square() {
            return false;
        }
        for row in 0..self.get_num_rows() {
            for column in 0..row {
                if unsafe {
                    MatrixGetEntry::<PolyOverZ>::get_entry_unchecked(self, row, column)
                        != MatrixGetEntry::<PolyOverZ>::get_entry_unchecked(self, column, row)
                } {
                    return false;
                }
            }
        }
        true
    }

    /// Returns the NTT representation of `self`.
    ///
    /// # Examples
    /// ```
    /// use qfall_math::integer_mod_q::{MatNTTPolynomialRingZq, MatPolynomialRingZq, ModulusPolynomialRingZq, PolyOverZq};
    /// use crate::qfall_math::traits::SetCoefficient;
    ///
    /// let n = 4;
    /// let modulus = 7681;
    ///
    /// let mut mod_poly = PolyOverZq::from(modulus);
    /// mod_poly.set_coeff(0, 1).unwrap();
    /// mod_poly.set_coeff(n, 1).unwrap();
    ///
    /// let mut polynomial_modulus = ModulusPolynomialRingZq::from(&mod_poly);
    /// polynomial_modulus.set_ntt_unchecked(1925);
    ///
    /// let mat_poly_ring = MatPolynomialRingZq::sample_uniform(2, 3, &polynomial_modulus);
    ///
    /// let mat_ntt_poly_ring = mat_poly_ring.ntt();
    /// ```
    ///
    /// # Panics ...
    /// - if the [`NTTBasisPolynomialRingZq`](crate::integer_mod_q::NTTBasisPolynomialRingZq),
    ///   which is part of the [`ModulusPolynomialRingZq`](crate::integer_mod_q::ModulusPolynomialRingZq) in `self`
    ///   is not set.
    pub fn ntt(&self) -> MatNTTPolynomialRingZq {
        MatNTTPolynomialRingZq::from(self)
    }
}

#[cfg(test)]
mod test_is_identity {
    use crate::{
        integer::MatPolyOverZ,
        integer_mod_q::{MatPolynomialRingZq, PolyOverZq},
    };
    use std::str::FromStr;

    /// Ensure that is_identity returns `true` for identity matrices.
    #[test]
    fn identity_detection() {
        let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();

        let ident_1 = MatPolynomialRingZq::identity(2, 2, &modulus);
        let ident_2 = MatPolynomialRingZq::identity(2, 3, modulus);

        assert!(ident_1.is_identity());
        assert!(ident_2.is_identity());
    }

    /// Ensure that is_identity returns `false` for non-identity matrices.
    #[test]
    fn identity_rejection() {
        let modulus = PolyOverZq::from_str(&format!("5  1 0 0 0 1 mod {}", u64::MAX)).unwrap();
        let poly_mat_1 = MatPolyOverZ::from_str("[[0, 0],[0, 1  2]]").unwrap();
        let poly_mat_2 =
            MatPolyOverZ::from_str(&format!("[[1  1, 0],[2  1 {}, 1  1]]", i64::MAX)).unwrap();

        let small = MatPolynomialRingZq::from((poly_mat_1, &modulus));
        let large = MatPolynomialRingZq::from((poly_mat_2, modulus));

        assert!(!small.is_identity());
        assert!(!large.is_identity());
    }
}

#[cfg(test)]
mod test_is_zero {
    use crate::{
        integer::MatPolyOverZ,
        integer_mod_q::{MatPolynomialRingZq, PolyOverZq},
    };
    use std::str::FromStr;

    /// Ensure that is_zero returns `true` for all zero matrices.
    #[test]
    fn zero_detection() {
        let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
        let poly_mat_1 = MatPolyOverZ::new(2, 2);
        let poly_mat_2 = MatPolyOverZ::new(4, 2);

        let zero_1 = MatPolynomialRingZq::from((poly_mat_1, &modulus));
        let zero_2 = MatPolynomialRingZq::from((poly_mat_2, modulus));

        assert!(zero_1.is_zero());
        assert!(zero_2.is_zero());
    }

    /// Ensure that is_zero returns `false` for non-zero matrices.
    #[test]
    fn zero_rejection() {
        let modulus = PolyOverZq::from_str(&format!("5  1 0 0 0 1 mod {}", u64::MAX)).unwrap();
        let poly_mat_1 = MatPolyOverZ::from_str("[[0, 0],[0, 1  2]]").unwrap();
        let poly_mat_2 =
            MatPolyOverZ::from_str(&format!("[[1  1, 0],[2  1 {}, 0]]", i64::MAX)).unwrap();

        let small = MatPolynomialRingZq::from((poly_mat_1, &modulus));
        let large = MatPolynomialRingZq::from((poly_mat_2, modulus));

        assert!(!small.is_zero());
        assert!(!large.is_zero());
    }
}

#[cfg(test)]
mod test_is_square {
    use crate::{
        integer::MatPolyOverZ,
        integer_mod_q::{MatPolynomialRingZq, PolyOverZq},
    };
    use std::str::FromStr;

    /// Ensure that is_square returns `true` for square matrices.
    #[test]
    fn square_detection() {
        let modulus = PolyOverZq::from_str("5  1 0 0 0 1 mod 17").unwrap();
        let poly_mat_1 = MatPolyOverZ::from_str("[[1  3, 0],[0, 2  7 1]]").unwrap();
        let poly_mat_2 =
            MatPolyOverZ::from_str("[[0, 1  1, 2  2 3],[0, 0, 1  15],[0, 0, 0]]").unwrap();

        let square_1 = MatPolynomialRingZq::from((poly_mat_1, &modulus));
        let square_2 = MatPolynomialRingZq::from((poly_mat_2, modulus));

        assert!(square_1.is_square());
        assert!(square_2.is_square());
    }

    /// Ensure that is_square returns `false` for non-square matrices.
    #[test]
    fn square_rejection() {
        let modulus = PolyOverZq::from_str(&format!("5  1 0 0 0 1 mod {}", u64::MAX)).unwrap();
        let poly_mat_1 = MatPolyOverZ::new(1, 2);
        let poly_mat_2 =
            MatPolyOverZ::from_str(&format!("[[1  1, 0, 1  7],[2  1 {}, 0, 0]]", i64::MAX))
                .unwrap();

        let small = MatPolynomialRingZq::from((poly_mat_1, &modulus));
        let large = MatPolynomialRingZq::from((poly_mat_2, modulus));

        assert!(!small.is_square());
        assert!(!large.is_square());
    }
}

#[cfg(test)]
mod test_is_symmetric {
    use super::MatPolynomialRingZq;
    use std::str::FromStr;

    /// Ensure that is_symmetric returns `false` for non-symmetric matrices.
    #[test]
    fn symmetric_rejection() {
        let mat_2x3 =
            MatPolynomialRingZq::from_str("[[0, 1  6, 2  1 4],[1  2, 0, 2  1 1]] / 2  1 1 mod 17")
                .unwrap();
        let mat_2x2 =
            MatPolynomialRingZq::from_str("[[1  9, 0],[2  1 71, 0]] / 3  1 2 1 mod 17").unwrap();

        assert!(!mat_2x3.is_symmetric());
        assert!(!mat_2x2.is_symmetric());
    }

    /// Ensure that is_symmetric returns `true` for symmetric matrices.
    #[test]
    fn symmetric_detection() {
        let mat_2x2 = MatPolynomialRingZq::from_str(&format!(
            "[[2  1 {}, 2  3 {}],[2  3 {}, 3  1 {} 8]] / 2  1 1 mod {}",
            u64::MIN,
            i64::MAX,
            i64::MAX,
            i64::MAX,
            u64::MAX
        ))
        .unwrap();

        assert!(mat_2x2.is_symmetric());
    }
}