qfall-math 0.1.1

Mathematical foundations for rapid prototyping of lattice-based cryptography
Documentation
// Copyright © 2023 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 evaluate a [`PolyOverZ`].
//! For each reasonable type, an implementation
//! of the [`Evaluate`] trait should be implemented.

use super::PolyOverZ;
use crate::{integer::Z, rational::Q, traits::Evaluate};
use flint_sys::fmpz_poly::{fmpz_poly_evaluate_fmpq, fmpz_poly_evaluate_fmpz};

impl<Integer: Into<Z>> Evaluate<Integer, Z> for PolyOverZ {
    /// Evaluates a [`PolyOverZ`] on a given input.
    ///
    /// Parameters:
    /// - `value`: the value with which to evaluate the polynomial.
    ///
    /// Returns the evaluation of the polynomial as a [`Z`].
    ///
    /// # Examples
    /// ```
    /// use qfall_math::traits::*;
    /// use qfall_math::integer::Z;
    /// use qfall_math::integer::PolyOverZ;
    /// use std::str::FromStr;
    ///
    /// let poly = PolyOverZ::from_str("5  0 1 2 -3 1").unwrap();
    /// let res: Z = poly.evaluate(3);
    /// ```
    fn evaluate(&self, value: Integer) -> Z {
        let value = value.into();
        let mut res = Z::default();

        unsafe { fmpz_poly_evaluate_fmpz(&mut res.value, &self.poly, &value.value) };

        res
    }
}

impl<Rational: Into<Q>> Evaluate<Rational, Q> for PolyOverZ {
    /// Evaluates a [`PolyOverZ`] on a given input.
    ///
    /// Parameters:
    /// - `value`: the value with which to evaluate the polynomial.
    ///
    /// Returns the evaluation of the polynomial as a [`Q`].
    ///
    /// # Examples
    /// ```
    /// use qfall_math::traits::*;
    /// use qfall_math::rational::Q;
    /// use qfall_math::integer::PolyOverZ;
    /// use std::str::FromStr;
    ///
    /// let poly = PolyOverZ::from_str("5  0 1 2 -3 1").unwrap();
    /// let value = Q::from((3, 2));
    /// let res: Q = poly.evaluate(&value);
    /// ```
    fn evaluate(&self, value: Rational) -> Q {
        let value = value.into();
        let mut res = Q::default();

        unsafe { fmpz_poly_evaluate_fmpq(&mut res.value, &self.poly, &value.value) };

        res
    }
}

#[cfg(test)]
mod test_evaluate_z {
    use crate::integer::{PolyOverZ, Z};
    use crate::traits::Evaluate;
    use std::str::FromStr;

    /// Tests if evaluate works for [`Z`] as input
    #[test]
    fn eval_z() {
        let poly = PolyOverZ::from_str("2  1 2").unwrap();

        let res: Z = poly.evaluate(Z::from(3));

        assert_eq!(7, res);
    }

    /// Tests if evaluate with a reference works
    #[test]
    fn eval_z_ref() {
        let poly = PolyOverZ::from_str("2  1 2").unwrap();

        let res: Z = poly.evaluate(&Z::from(3));

        assert_eq!(7, res);
    }

    /// Tests if evaluate works with negative values
    #[test]
    fn eval_z_negative() {
        let poly = PolyOverZ::from_str("2  1 2").unwrap();

        let res: Z = poly.evaluate(-5);

        assert_eq!(-9, res);
    }

    /// Tests if evaluate works with large integers
    #[test]
    fn eval_z_large() {
        let poly = PolyOverZ::from_str("2  1 2").unwrap();

        let res: Z = poly.evaluate(&Z::from_str(&"1".repeat(65)).unwrap());

        let mut res_cmp_str = "2".repeat(64);
        res_cmp_str.push('3');
        assert_eq!(Z::from_str(&res_cmp_str).unwrap(), res);
    }

    /// Test if evaluate works with max of [`i64`],[`i32`], ...
    #[test]
    fn eval_max() {
        let poly = PolyOverZ::from_str("2  1 2").unwrap();

        // signed
        let _: Z = poly.evaluate(i64::MAX);
        let _: Z = poly.evaluate(i32::MAX);
        let _: Z = poly.evaluate(i16::MAX);
        let _: Z = poly.evaluate(i8::MAX);

        //unsigned
        let _: Z = poly.evaluate(u64::MAX);
        let _: Z = poly.evaluate(u32::MAX);
        let _: Z = poly.evaluate(u16::MAX);
        let _: Z = poly.evaluate(u8::MAX);
    }

    /// Test if evaluate works with min of [`i64`],[`i32`], ...
    #[test]
    fn eval_min() {
        let poly = PolyOverZ::from_str("2  1 2").unwrap();

        // signed
        let _: Z = poly.evaluate(i64::MIN);
        let _: Z = poly.evaluate(i32::MIN);
        let _: Z = poly.evaluate(i16::MIN);
        let _: Z = poly.evaluate(i8::MIN);

        // unsigned
        let _: Z = poly.evaluate(u64::MIN);
        let _: Z = poly.evaluate(u32::MIN);
        let _: Z = poly.evaluate(u16::MIN);
        let _: Z = poly.evaluate(u8::MIN);
    }
}

#[cfg(test)]
mod test_evaluate_q {
    use crate::{integer::PolyOverZ, rational::Q, traits::Evaluate};
    use std::str::FromStr;

    /// Ensures that positive values return expected evaluation
    #[test]
    fn evaluate_positive() {
        let poly = PolyOverZ::from_str("2  1 3").unwrap();
        let value = Q::from((3, 2));

        let res_ref = poly.evaluate(&value);
        let res = poly.evaluate(value);

        assert_eq!(Q::from((11, 2)), res);
        assert_eq!(res_ref, res);
    }

    /// Ensures that negative values return expected evaluation
    #[test]
    fn evaluate_negative() {
        let poly = PolyOverZ::from_str("2  1 3").unwrap();
        let value = Q::from((-3, 2));

        let res_ref = poly.evaluate(&value);
        let res = poly.evaluate(value);

        assert_eq!(Q::from((-7, 2)), res);
        assert_eq!(res_ref, res);
    }

    /// Ensures that positive large values return expected evaluation
    #[test]
    fn evaluate_large_positive() {
        let poly = PolyOverZ::from_str(&format!("2  {} 1", (u64::MAX - 1) / 2)).unwrap();
        let value = Q::from((u64::MAX - 1) / 2);

        let res_ref = poly.evaluate(&value);
        let res = poly.evaluate(value);

        assert_eq!(Q::from(u64::MAX - 1), res);
        assert_eq!(res_ref, res);
    }

    /// Ensures that negative large values return expected evaluation
    #[test]
    fn evaluate_large_negative() {
        let poly = PolyOverZ::from_str(&format!("2  {} 2", u64::MAX)).unwrap();
        let value = Q::from_str(&format!("-{}", (u64::MAX - 1) / 2)).unwrap();

        let res_ref = poly.evaluate(&value);
        let res = poly.evaluate(value);

        assert_eq!(Q::ONE, res);
        assert_eq!(res_ref, res);
    }
}