| /* | |
| * Copyright 2017 Google LLC. | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * https://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include "polynomial.h" | |
| #include <cmath> | |
| #include <memory> | |
| #include <random> | |
| #include <vector> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include "constants.h" | |
| #include "montgomery.h" | |
| #include "ntt_parameters.h" | |
| #include "prng/integral_prng_testing_types.h" | |
| #include "serialization.pb.h" | |
| #include "status_macros.h" | |
| #include "statusor.h" | |
| #include "testing/coefficient_polynomial.h" | |
| #include "testing/status_matchers.h" | |
| #include "testing/status_testing.h" | |
| #include "testing/testing_prng.h" | |
| namespace { | |
| using ::rlwe::testing::StatusIs; | |
| using ::testing::Eq; | |
| using ::testing::HasSubstr; | |
| using ::testing::Ne; | |
| // Useful typedefs. | |
| using uint_m = rlwe::MontgomeryInt<rlwe::Uint16>; | |
| using CoefficientPolynomial = rlwe::testing::CoefficientPolynomial<uint_m>; | |
| using Polynomial = rlwe::Polynomial<uint_m>; | |
| unsigned int seed = 0; | |
| std::mt19937 mt_rand(seed); | |
| const absl::string_view kPrngSeed = | |
| "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; | |
| // Test fixture to take care of messy setup. | |
| template <typename Prng> | |
| class PolynomialTest : public ::testing::Test { | |
| protected: | |
| PolynomialTest() | |
| : params14_(uint_m::Params::Create(rlwe::kNewhopeModulus).value()), | |
| zero_(uint_m::ImportZero(params14_.get())) {} | |
| void SetUp() override { srand(0); } | |
| void SetParams(int n, int log_n) { | |
| // Prng to generate random values | |
| ASSERT_OK_AND_ASSIGN(auto prng, | |
| Prng::Create(kPrngSeed.substr(0, Prng::SeedLength()))); | |
| std::vector<uint_m> p_coeffs(n, zero_); | |
| std::vector<uint_m> q_coeffs(n, zero_); | |
| std::vector<uint_m> r_coeffs(n, zero_); | |
| // Create some random polynomials. Ensure that they are different. | |
| for (int j = 0; j < n; j++) { | |
| ASSERT_OK_AND_ASSIGN(p_coeffs[j], | |
| uint_m::ImportRandom(prng.get(), params14_.get())); | |
| ASSERT_OK_AND_ASSIGN(q_coeffs[j], | |
| uint_m::ImportRandom(prng.get(), params14_.get())); | |
| ASSERT_OK_AND_ASSIGN(r_coeffs[j], | |
| uint_m::ImportRandom(prng.get(), params14_.get())); | |
| } | |
| // Ensure the polynomials p and q are different. | |
| uint_m::Int rand_index = mt_rand() % n; | |
| auto one = uint_m::ImportOne(params14_.get()); | |
| p_coeffs[rand_index] = q_coeffs[rand_index].Add(one, params14_.get()); | |
| p_.reset(new CoefficientPolynomial(p_coeffs, params14_.get())); | |
| q_.reset(new CoefficientPolynomial(q_coeffs, params14_.get())); | |
| r_.reset(new CoefficientPolynomial(r_coeffs, params14_.get())); | |
| // Acquire all of the NTT parameters. | |
| ASSERT_OK_AND_ASSIGN(auto ntt_params, rlwe::InitializeNttParameters<uint_m>( | |
| log_n, params14_.get())); | |
| ntt_params_ = | |
| std::make_unique<rlwe::NttParameters<uint_m>>(std::move(ntt_params)); | |
| // Put p and q in the NTT domain. | |
| ntt_p_ = | |
| Polynomial::ConvertToNtt(p_coeffs, ntt_params_.get(), params14_.get()); | |
| ntt_q_ = | |
| Polynomial::ConvertToNtt(q_coeffs, ntt_params_.get(), params14_.get()); | |
| ntt_r_ = Polynomial::ConvertToNttFast(r_coeffs, ntt_params_.get(), | |
| params14_.get()); | |
| } | |
| std::unique_ptr<Prng> MakePrng(absl::string_view seed) { | |
| auto prng = Prng::Create(seed.substr(0, Prng::SeedLength())).value(); | |
| return prng; | |
| } | |
| std::unique_ptr<const uint_m::Params> params14_; | |
| std::unique_ptr<rlwe::NttParameters<uint_m>> ntt_params_; | |
| std::unique_ptr<CoefficientPolynomial> p_; | |
| std::unique_ptr<CoefficientPolynomial> q_; | |
| std::unique_ptr<CoefficientPolynomial> r_; | |
| Polynomial ntt_p_; | |
| Polynomial ntt_q_; | |
| rlwe::StatusOr<Polynomial> ntt_r_; | |
| uint_m zero_; | |
| }; | |
| TYPED_TEST_SUITE(PolynomialTest, rlwe::TestingPrngTypes); | |
| // Ensure that a default NTT polynomial is invalid. | |
| TYPED_TEST(PolynomialTest, DefaultIsInvalid) { | |
| EXPECT_FALSE(Polynomial().IsValid()); | |
| } | |
| TYPED_TEST(PolynomialTest, CoeffsCorrectlyReturnsCoefficients) { | |
| auto prng = this->MakePrng(kPrngSeed); | |
| for (int i = 2; i < 11; i++) { | |
| int n = 1 << i; | |
| this->SetParams(n, i); | |
| // coeffs = {1, 0, 0, ...}, ntt_coeffs = {1, 1, 1, ...} | |
| // Test that NTT(coeffs) == ntt_coeffs | |
| std::vector<uint_m::Int> coeffs(n, 0), ntt_coeffs(n, 1); | |
| coeffs[0] = 1; | |
| std::vector<uint_m> v; | |
| for (const uint_m::Int& coeff : coeffs) { | |
| ASSERT_OK_AND_ASSIGN(auto elt, | |
| uint_m::ImportInt(coeff, this->params14_.get())); | |
| v.push_back(elt); | |
| } | |
| Polynomial ntt_v = Polynomial::ConvertToNtt(v, this->ntt_params_.get(), | |
| this->params14_.get()); | |
| for (int j = 0; j < n; j++) { | |
| EXPECT_EQ(ntt_v.Coeffs()[j].ExportInt(this->params14_.get()), | |
| ntt_coeffs[j]); | |
| } | |
| // Test that coeffs are the same when explicitly constructed | |
| std::vector<uint_m> coeffs2(n, this->zero_); | |
| for (int j = 0; j < n; j++) { | |
| ASSERT_OK_AND_ASSIGN( | |
| coeffs2[j], uint_m::ImportRandom(prng.get(), this->params14_.get())); | |
| } | |
| Polynomial ntt2 = Polynomial(coeffs2); | |
| std::vector<uint_m> nttcoeffs2 = ntt2.Coeffs(); | |
| for (int j = 0; j < n; j++) { | |
| EXPECT_EQ(coeffs2[j], nttcoeffs2[j]); | |
| } | |
| // Test that coeffs are all zero when only length is given | |
| Polynomial ntt3 = Polynomial(n, this->params14_.get()); | |
| std::vector<uint_m> nttcoeffs3 = ntt3.Coeffs(); | |
| for (int j = 0; j < n; j++) { | |
| EXPECT_EQ(nttcoeffs3[j], this->zero_); | |
| } | |
| } | |
| } | |
| // Ensure that a polynomial converted to NTT form can be converted back. | |
| TYPED_TEST(PolynomialTest, Symmetry) { | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| EXPECT_TRUE(this->ntt_p_.IsValid()); | |
| CoefficientPolynomial p_prime( | |
| this->ntt_p_.InverseNtt(this->ntt_params_.get(), this->params14_.get()), | |
| this->params14_.get()); | |
| EXPECT_EQ(*this->p_, p_prime); | |
| } | |
| } | |
| // Ensure that equality holds properly. | |
| TYPED_TEST(PolynomialTest, Equality) { | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| Polynomial ntt_p_cpy = this->ntt_p_; | |
| Polynomial ntt_q_cpy = this->ntt_q_; | |
| EXPECT_TRUE(this->ntt_p_ == ntt_p_cpy); | |
| EXPECT_TRUE(this->ntt_q_ == ntt_q_cpy); | |
| EXPECT_FALSE(this->ntt_p_ != ntt_p_cpy); | |
| EXPECT_FALSE(this->ntt_q_ != ntt_q_cpy); | |
| EXPECT_TRUE(this->ntt_p_ != this->ntt_q_); | |
| EXPECT_TRUE(this->ntt_q_ != this->ntt_p_); | |
| EXPECT_FALSE(this->ntt_p_ == this->ntt_q_); | |
| EXPECT_FALSE(this->ntt_q_ == this->ntt_p_); | |
| } | |
| } | |
| // Ensure that a polynomial whose size is not a power of two gets rejected. | |
| TYPED_TEST(PolynomialTest, NotPowerOfTwo) { | |
| for (int i = 2; i < 11; i++) { | |
| // j is any value that isn't a power of 2. | |
| for (int j = 1 + (1 << (i - 1)); j < (1 << i); j++) { | |
| this->SetParams(j, i); | |
| EXPECT_FALSE(this->ntt_p_.IsValid()); | |
| } | |
| } | |
| } | |
| // Ensure that adding or multiplying two polynomials of different lengths gets | |
| // rejected. | |
| TYPED_TEST(PolynomialTest, BinopOfDifferentLengths) { | |
| for (int i = 2; i < 11; i++) { | |
| for (int j = 2; j < 11; j++) { | |
| if (j == i) { | |
| continue; | |
| } | |
| int bigger = std::max(i, j); | |
| this->SetParams(1 << bigger, bigger); | |
| std::vector<uint_m> x(1 << i, this->zero_); | |
| std::vector<uint_m> y(1 << j, this->zero_); | |
| std::vector<uint_m> z(1 << j, this->zero_); | |
| this->ntt_params_->bitrevs = rlwe::internal::BitrevArray(i); | |
| Polynomial ntt_x = Polynomial::ConvertToNtt(x, this->ntt_params_.get(), | |
| this->params14_.get()); | |
| this->ntt_params_->bitrevs = rlwe::internal::BitrevArray(j); | |
| Polynomial ntt_y = Polynomial::ConvertToNtt(y, this->ntt_params_.get(), | |
| this->params14_.get()); | |
| Polynomial ntt_z = Polynomial::ConvertToNtt(y, this->ntt_params_.get(), | |
| this->params14_.get()); | |
| EXPECT_TRUE(ntt_x.IsValid()); | |
| EXPECT_TRUE(ntt_y.IsValid()); | |
| EXPECT_TRUE(ntt_z.IsValid()); | |
| // Lengths are different | |
| EXPECT_FALSE(ntt_x == ntt_y); | |
| EXPECT_FALSE(ntt_x == ntt_z); | |
| EXPECT_THAT(ntt_x.Mul(ntt_y, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_y.Mul(ntt_x, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_x.Add(ntt_y, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_y.Add(ntt_x, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_x.Sub(ntt_y, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_y.Sub(ntt_x, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| // In-place operations return the original polynomial if invalid | |
| // To check, we keep a copy of the original and check that the output | |
| // remains the same | |
| Polynomial orig_ntt_x = ntt_x; | |
| Polynomial orig_ntt_y = ntt_y; | |
| EXPECT_THAT(ntt_x.MulInPlace(ntt_y, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_x.FusedMulAddInPlace(ntt_y, ntt_y, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_y.FusedMulAddInPlace(ntt_x, ntt_y, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_y.MulInPlace(ntt_x, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_x.AddInPlace(ntt_y, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_y.AddInPlace(ntt_x, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_x.SubInPlace(ntt_y, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| EXPECT_THAT(ntt_y.SubInPlace(ntt_x, this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("are not of same size"))); | |
| } | |
| } | |
| } | |
| // Test that the convolution property holds. Let p, q be polynomials. | |
| // CoefficientPolynomial multiplication of p and q = | |
| // NTT_INV(the coordinate-wise product of NTT(p) and NTT(q)) | |
| TYPED_TEST(PolynomialTest, Multiply) { | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| EXPECT_TRUE(this->ntt_p_.IsValid()); | |
| EXPECT_TRUE(this->ntt_q_.IsValid()); | |
| ASSERT_OK_AND_ASSIGN(Polynomial ntt_res1, | |
| this->ntt_p_.Mul(this->ntt_q_, this->params14_.get())); | |
| ASSERT_OK_AND_ASSIGN(Polynomial ntt_res2, | |
| this->ntt_q_.Mul(this->ntt_p_, this->params14_.get())); | |
| CoefficientPolynomial res1( | |
| ntt_res1.InverseNtt(this->ntt_params_.get(), this->params14_.get()), | |
| this->params14_.get()); | |
| CoefficientPolynomial res2( | |
| ntt_res2.InverseNtt(this->ntt_params_.get(), this->params14_.get()), | |
| this->params14_.get()); | |
| ASSERT_OK_AND_ASSIGN(CoefficientPolynomial expected, | |
| (*this->p_) * (*this->q_)); | |
| EXPECT_EQ(res1, expected); | |
| EXPECT_EQ(res2, expected); | |
| EXPECT_EQ(res1, res2); | |
| } | |
| } | |
| // Test fused multiply add. | |
| TYPED_TEST(PolynomialTest, FusedMulAddInPlace) { | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| EXPECT_TRUE(this->ntt_p_.IsValid()); | |
| EXPECT_TRUE(this->ntt_q_.IsValid()); | |
| EXPECT_TRUE((*this->ntt_r_).IsValid()); | |
| ASSERT_OK(this->ntt_p_.FusedMulAddInPlace(this->ntt_q_, (*this->ntt_r_), | |
| this->params14_.get())); | |
| CoefficientPolynomial res( | |
| this->ntt_p_.InverseNtt(this->ntt_params_.get(), this->params14_.get()), | |
| this->params14_.get()); | |
| ASSERT_OK_AND_ASSIGN(CoefficientPolynomial product, | |
| (*this->q_) * (*this->r_)); | |
| ASSERT_OK_AND_ASSIGN(CoefficientPolynomial expected, (*this->p_) + product); | |
| EXPECT_EQ(res, expected); | |
| } | |
| } | |
| // Test scalar multiplication. | |
| TYPED_TEST(PolynomialTest, ScalarMultiply) { | |
| auto prng = this->MakePrng(kPrngSeed); | |
| ASSERT_OK_AND_ASSIGN(uint_m scalar, | |
| uint_m::ImportRandom(prng.get(), this->params14_.get())); | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| EXPECT_TRUE(this->ntt_p_.IsValid()); | |
| ASSERT_OK_AND_ASSIGN(Polynomial ntt_res, | |
| this->ntt_p_.Mul(scalar, this->params14_.get())); | |
| CoefficientPolynomial res( | |
| ntt_res.InverseNtt(this->ntt_params_.get(), this->params14_.get()), | |
| this->params14_.get()); | |
| CoefficientPolynomial expected = (*this->p_) * scalar; | |
| EXPECT_EQ(res, expected); | |
| } | |
| } | |
| // Test that p + (-p) = 0. | |
| TYPED_TEST(PolynomialTest, Negate) { | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| // An NTT polynomial of all zeros. | |
| Polynomial zeros_ntt = Polynomial::ConvertToNtt( | |
| std::vector<uint_m>(1 << i, this->zero_), this->ntt_params_.get(), | |
| this->params14_.get()); | |
| auto minus_p = this->ntt_p_.Negate(this->params14_.get()); | |
| ASSERT_OK_AND_ASSIGN(auto p0, | |
| this->ntt_p_.Add(minus_p, this->params14_.get())); | |
| EXPECT_EQ(zeros_ntt, p0); | |
| ASSERT_OK_AND_ASSIGN(p0, minus_p.Add(this->ntt_p_, this->params14_.get())); | |
| EXPECT_EQ(zeros_ntt, p0); | |
| } | |
| } | |
| // Test that p + q = NTT_INV(NTT(p) + NTT(q)). | |
| TYPED_TEST(PolynomialTest, Add) { | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| EXPECT_TRUE(this->ntt_p_.IsValid()); | |
| EXPECT_TRUE(this->ntt_q_.IsValid()); | |
| ASSERT_OK_AND_ASSIGN(Polynomial ntt_res1, | |
| this->ntt_p_.Add(this->ntt_q_, this->params14_.get())); | |
| ASSERT_OK_AND_ASSIGN(Polynomial ntt_res2, | |
| this->ntt_q_.Add(this->ntt_p_, this->params14_.get())); | |
| CoefficientPolynomial res1( | |
| ntt_res1.InverseNtt(this->ntt_params_.get(), this->params14_.get()), | |
| this->params14_.get()); | |
| CoefficientPolynomial res2( | |
| ntt_res2.InverseNtt(this->ntt_params_.get(), this->params14_.get()), | |
| this->params14_.get()); | |
| ASSERT_OK_AND_ASSIGN(CoefficientPolynomial expected, | |
| (*this->p_) + (*this->q_)); | |
| EXPECT_EQ(res1, expected); | |
| EXPECT_EQ(res2, expected); | |
| EXPECT_EQ(res1, res2); | |
| } | |
| } | |
| // Test that p - q = NTT_INV(NTT(p) - NTT(q)) and | |
| // q - p = NTT_INV(NTT(q) - NTT(p)). | |
| TYPED_TEST(PolynomialTest, Sub) { | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| EXPECT_TRUE(this->ntt_p_.IsValid()); | |
| EXPECT_TRUE(this->ntt_q_.IsValid()); | |
| ASSERT_OK_AND_ASSIGN(Polynomial ntt_res1, | |
| this->ntt_p_.Sub(this->ntt_q_, this->params14_.get())); | |
| ASSERT_OK_AND_ASSIGN(Polynomial ntt_res2, | |
| this->ntt_q_.Sub(this->ntt_p_, this->params14_.get())); | |
| CoefficientPolynomial res1( | |
| ntt_res1.InverseNtt(this->ntt_params_.get(), this->params14_.get()), | |
| this->params14_.get()); | |
| CoefficientPolynomial res2( | |
| ntt_res2.InverseNtt(this->ntt_params_.get(), this->params14_.get()), | |
| this->params14_.get()); | |
| ASSERT_OK_AND_ASSIGN(CoefficientPolynomial expected_res1, | |
| (*this->p_) - (*this->q_)); | |
| ASSERT_OK_AND_ASSIGN(CoefficientPolynomial expected_res2, | |
| (*this->q_) - (*this->p_)); | |
| EXPECT_EQ(res1, expected_res1); | |
| EXPECT_EQ(res2, expected_res2); | |
| } | |
| } | |
| TYPED_TEST(PolynomialTest, SubstitutionPowerMalformed) { | |
| // Ensure substitution fails on powers that are negative, even, and greater | |
| // than 2 * kDimension. | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| EXPECT_THAT( | |
| this->ntt_p_.Substitute(2, this->ntt_params_.get(), | |
| this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("must be a non-negative odd integer less than"))); | |
| // Even when not in debugging mode, the following two tests will yield a | |
| // segmentation fault. We therefore only do the tests in debug mode. | |
| EXPECT_THAT( | |
| this->ntt_p_.Substitute(-10, this->ntt_params_.get(), | |
| this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("must be a non-negative odd integer less than"))); | |
| EXPECT_THAT( | |
| this->ntt_p_.Substitute(2 * (1 << i) + 1, this->ntt_params_.get(), | |
| this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("must be a non-negative odd integer less than"))); | |
| } | |
| } | |
| TYPED_TEST(PolynomialTest, Substitution) { | |
| // Tests substitutions of the form N/2^k + 1. | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| int dimension = 1 << i; | |
| for (int k = 0; k < i; k++) { | |
| int power = (dimension >> k) + 1; | |
| ASSERT_OK_AND_ASSIGN( | |
| auto ntt_res, this->ntt_p_.Substitute(power, this->ntt_params_.get(), | |
| this->params14_.get())); | |
| CoefficientPolynomial res( | |
| ntt_res.InverseNtt(this->ntt_params_.get(), this->params14_.get()), | |
| this->params14_.get()); | |
| ASSERT_OK_AND_ASSIGN(auto r, this->p_->Substitute(power)); | |
| EXPECT_EQ(res, r); | |
| } | |
| } | |
| } | |
| TYPED_TEST(PolynomialTest, Serialize) { | |
| for (int i = 2; i < 11; i++) { | |
| this->SetParams(1 << i, i); | |
| ASSERT_OK_AND_ASSIGN(rlwe::SerializedNttPolynomial serialized_p, | |
| this->ntt_p_.Serialize(this->params14_.get())); | |
| ASSERT_OK_AND_ASSIGN(rlwe::SerializedNttPolynomial serialized_q, | |
| this->ntt_q_.Serialize(this->params14_.get())); | |
| // Ensure that a serialized polynomial can be deserialized. | |
| ASSERT_OK_AND_ASSIGN( | |
| auto deserialized_p, | |
| Polynomial::Deserialize(serialized_p, this->params14_.get())); | |
| EXPECT_EQ(this->ntt_p_, deserialized_p); | |
| ASSERT_OK_AND_ASSIGN( | |
| auto deserialized_q, | |
| Polynomial::Deserialize(serialized_q, this->params14_.get())); | |
| EXPECT_EQ(this->ntt_q_, deserialized_q); | |
| // Ensure that the length of a Serialized polynomial is at most | |
| // SerializedSize times the number of coefficients. | |
| EXPECT_LE(serialized_p.coeffs().size(), | |
| this->ntt_p_.Len() * this->params14_->SerializedSize()); | |
| EXPECT_LE(serialized_q.coeffs().size(), | |
| this->ntt_q_.Len() * this->params14_->SerializedSize()); | |
| } | |
| } | |
| TYPED_TEST(PolynomialTest, | |
| SamplePolynomialFromPrngReturnsPolynomialWithCorrectSize) { | |
| auto prng = this->MakePrng(kPrngSeed); | |
| ASSERT_OK_AND_ASSIGN(auto polynomial, | |
| rlwe::SamplePolynomialFromPrng<uint_m>( | |
| 16, prng.get(), this->params14_.get())); | |
| EXPECT_THAT(polynomial.Len(), Eq(16)); | |
| } | |
| TYPED_TEST(PolynomialTest, SamplePolynomialFromPrngFailsWithLengthLessThanOne) { | |
| auto prng = this->MakePrng(kPrngSeed); | |
| EXPECT_THAT(rlwe::SamplePolynomialFromPrng<uint_m>(0, prng.get(), | |
| this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("must be a non-negative integer"))); | |
| EXPECT_THAT(rlwe::SamplePolynomialFromPrng<uint_m>(-10, prng.get(), | |
| this->params14_.get()), | |
| StatusIs(::absl::StatusCode::kInvalidArgument, | |
| HasSubstr("must be a non-negative integer"))); | |
| } | |
| TYPED_TEST(PolynomialTest, | |
| SamplePolynomialFromPrngSamplesSamePolynomialFromSameSeed) { | |
| auto prng1 = this->MakePrng(kPrngSeed); | |
| auto prng2 = this->MakePrng(kPrngSeed); | |
| ASSERT_OK_AND_ASSIGN(auto polynomial1, | |
| rlwe::SamplePolynomialFromPrng<uint_m>( | |
| 16, prng1.get(), this->params14_.get())); | |
| ASSERT_OK_AND_ASSIGN(auto polynomial2, | |
| rlwe::SamplePolynomialFromPrng<uint_m>( | |
| 16, prng2.get(), this->params14_.get())); | |
| EXPECT_THAT(polynomial1, Eq(polynomial2)); | |
| } | |
| TYPED_TEST(PolynomialTest, | |
| SamplePolynomialFromPrngSamplesOtherPolynomialFromOtherSeed) { | |
| auto prng1 = this->MakePrng(kPrngSeed); | |
| auto prng2 = this->MakePrng( | |
| "0000000000000000000000000000000000000000000000000000000000000000"); | |
| ASSERT_OK_AND_ASSIGN(auto polynomial1, | |
| rlwe::SamplePolynomialFromPrng<uint_m>( | |
| 16, prng1.get(), this->params14_.get())); | |
| ASSERT_OK_AND_ASSIGN(auto polynomial2, | |
| rlwe::SamplePolynomialFromPrng<uint_m>( | |
| 16, prng2.get(), this->params14_.get())); | |
| EXPECT_THAT(polynomial1, Ne(polynomial2)); | |
| } | |
| } // namespace |