blob: cb599c18f6a27a44117b010f0b1ca49296cf453f [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// 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
//
// http://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 "algorithms/forculus/polynomial_computations.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"
namespace cobalt {
namespace forculus {
namespace {
FieldElement FromBytes(std::vector<byte>&& bytes) {
return FieldElement(std::move(bytes));
}
FieldElement FromInt(uint32_t x) {
std::vector<byte> bytes(sizeof(x));
std::memcpy(bytes.data(), &x, sizeof(x));
return FieldElement(std::move(bytes));
}
} // namespace
TEST(PolynomialComputationsTest, TestEvaluateSmallPolynomial) {
// NOTE: This test only makes sense with our temporary implementation of
// FieldElement. When we switch to the real implementation this test
// will have to change.
// Construct the 2nd degree polynomial 5 + 7x + 9x^2
std::vector<FieldElement> coefficients;
for (byte i = 5; i <=9 ; i+=2) {
coefficients.emplace_back(FromInt(i));
}
// When we evaluate a polynomial at x=0 we should get the constant term.
EXPECT_EQ(coefficients[0], Evaluate(coefficients, FieldElement(false)));
// When we evaluate a polynomial at x=1 we should get the sum of the
// coefficients.
FieldElement sum(false);
for (const FieldElement& el : coefficients) {
sum += el;
}
EXPECT_EQ(sum, Evaluate(coefficients, FieldElement(true)));
// Evaluate at x = 2. Expect 5 + 14 + 36 = 55.
EXPECT_EQ(FromInt(55), Evaluate(coefficients, FromInt(2)));
// Evaluate at x = 10. Expect 5 + 70 + 900 = 975 = ox3CF.
EXPECT_EQ(FromBytes({0xCF, 3}), Evaluate(coefficients, FromInt(10)));
}
TEST(PolynomialComputationsTest, TestEvaluateLargerPolynomial) {
// Construct a 19th degree polynomial.
std::vector<FieldElement> coefficients;
for (byte i = 1; i < 21; i++) {
coefficients.emplace_back(FromInt(i));
}
// When we evaluate a polynomial at x=0 we should get the constant term.
EXPECT_EQ(coefficients[0], Evaluate(coefficients, FieldElement(false)));
// When we evaluate a polynomial at x=1 we should get the sum of the
// coefficients.
FieldElement sum(false);
for (const FieldElement& el : coefficients) {
sum += el;
}
EXPECT_EQ(sum, Evaluate(coefficients, FieldElement(true)));
}
TEST(PolynomialComputationsTest, TestInterpolateSmallPolynomial) {
// NOTE: This test only makes sense with our temporary implementation of
// FieldElement. When we switch to the real implementation this test
// will have to change.
// Construct the 2nd degree polynomial 5 + 7x + 9x^2
std::vector<FieldElement> coefficients;
for (byte i = 5; i <=9 ; i+=2) {
coefficients.emplace_back(FromInt(i));
}
// Construct the x-values 2, 3, 4
std::vector<FieldElement> x_values;
for (byte i = 2; i < 5; i++) {
x_values.emplace_back(FromInt(i));
}
// Evaluate the 3 corresponding y values.
std::vector<FieldElement> y_values(3, FieldElement(false));
size_t i = 0;
for (const FieldElement& x : x_values) {
y_values[i++] = Evaluate(coefficients, x);
}
// The InterpolateConstant function wants vectors of pointers.
std::vector<const FieldElement*> x_value_pointers(3);
std::vector<const FieldElement*> y_value_pointers(3);
for (size_t i = 0; i < 3; i++) {
x_value_pointers[i] = &x_values[i];
y_value_pointers[i] = &y_values[i];
}
// Interpolate to recover the constant term.
FieldElement constant_term =
InterpolateConstant(x_value_pointers, y_value_pointers);
EXPECT_EQ(coefficients[0], constant_term);
}
// Constructs the polynomial f(x) = c0 + c1*x + ... c_{n-1}*x^{n-1}
// where ci = c0 + i*c_step and n = num_points.
//
// Constructs n x-values: x0, x1, ... x_{n-1} where x_i = x.
//
// Evaluates n y-values: y0, y1, ... y_{n-1} where y_i = f(x_i)
//
// Invokes the function InterpolateConstat() and checks that we get back c0.
void DoInterpolationTest(size_t num_points, uint32_t c0, uint32_t c_step,
uint32_t x0, uint32_t x_step) {
// Construct the coefficients of the polynomial.
std::vector<FieldElement> coefficients;
uint32_t c = c0;
for (size_t i = 0; i < num_points; i++) {
coefficients.emplace_back(FromInt(c));
c+= c_step;
}
// Construct x values x0=10, x1=11, x2=12, ... x{n-1}=10+{n-1}.
std::vector<FieldElement> x_values;
uint32_t x = x0;
for (size_t i = 0; i < num_points; i++) {
x_values.emplace_back(FromInt(x));
x+= x_step;
}
// Evaluate the polynomial at each of the x values.
std::vector<FieldElement> y_values(num_points, FieldElement(false));
for (size_t i = 0; i < num_points; i++) {
y_values[i] = Evaluate(coefficients, x_values[i]);
}
// The InterpolateConstant function wants vectors of pointers.
std::vector<const FieldElement*> x_value_pointers(num_points);
std::vector<const FieldElement*> y_value_pointers(num_points);
for (size_t i = 0; i < num_points; i++) {
x_value_pointers[i] = &x_values[i];
y_value_pointers[i] = &y_values[i];
}
// Interpolate to recover the constant term.
FieldElement constant_term =
InterpolateConstant(x_value_pointers, y_value_pointers);
// Check that we got the right constant term.
EXPECT_EQ(coefficients[0], constant_term) << num_points << ", " << c0 <<
", " << c_step << ", " << x0 << ", " << x_step;
}
TEST(PolynomialComputationsTest, TestInterpolate) {
std::vector<size_t> num_points_cases({2, 3, 20, 50});
std::vector<uint32_t> c0_cases({1, 10000, 100000, 1000000000});
std::vector<uint32_t> c_step_cases({1, 7, 111});
std::vector<uint32_t> x0_cases({1, 999});
for (size_t num_points : num_points_cases) {
for (uint32_t c0 : c0_cases) {
for (int32_t c_step : c_step_cases) {
for (int32_t x0 : x0_cases) {
DoInterpolationTest(num_points, c0, c_step, x0, 1);
}
}
}
}
}
} // namespace forculus
} // namespace cobalt