blob: 07650ba9bec17a96a67687cdeb7b5c944086a0fe [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/field_element.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"
namespace cobalt {
namespace forculus {
/****************************** Notice *************************************
*
* The tests currently in this file are based on the temporary, insecure
* implementation of FieldElement that interprets the first 32 bits of
* data as an integer in little-endian and discards the rest of the bytes.
* These will be replaced by different tests when the field changes to GF(2^128).
*
*****************************************************************************/
namespace {
// Make the FieldElement with the given vector of bytes. This wrapper around the
// constructor is necessary because the compiler can't tell which constructor
// to use with an expression like FieldElement({2}).
FieldElement FromBytes(std::vector<byte>&& bytes) {
return FieldElement(std::move(bytes));
}
FieldElement FromString(const std::string& data) {
return FieldElement(data);
}
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(FieldElementTest, TestConstructors) {
// Expect that the byte constructor discards all but the first 4 bytes.
FieldElement el = FromBytes({0, 1, 2, 3, 4, 5});
const byte* bytes = el.KeyBytes();
EXPECT_EQ(0, bytes[0]);
EXPECT_EQ(1, bytes[1]);
EXPECT_EQ(2, bytes[2]);
EXPECT_EQ(3, bytes[3]);
EXPECT_EQ(0, bytes[4]);
EXPECT_EQ(0, bytes[5]);
// Expect that the string constructor discards all but the first 4 bytes.
el = FromString({0, 1, 2, 3, 4, 5, 6});
bytes = el.KeyBytes();
EXPECT_EQ(0, bytes[0]);
EXPECT_EQ(1, bytes[1]);
EXPECT_EQ(2, bytes[2]);
EXPECT_EQ(3, bytes[3]);
EXPECT_EQ(0, bytes[4]);
EXPECT_EQ(0, bytes[5]);
// Expect that 1 is represented in little-endian as 1 0 0 0 ...
el = FieldElement(true);
bytes = el.KeyBytes();
EXPECT_EQ(1, bytes[0]);
EXPECT_EQ(0, bytes[1]);
EXPECT_EQ(0, bytes[2]);
EXPECT_EQ(0, bytes[3]);
EXPECT_EQ(0, bytes[4]);
EXPECT_EQ(0, bytes[5]);
// Expect that 0 is represented as 0 0 0 ...
el = FieldElement(0);
bytes = el.KeyBytes();
EXPECT_EQ(0, bytes[0]);
EXPECT_EQ(0, bytes[1]);
EXPECT_EQ(0, bytes[2]);
EXPECT_EQ(0, bytes[3]);
EXPECT_EQ(0, bytes[4]);
EXPECT_EQ(0, bytes[5]);
// Test the copy constructor
FieldElement x = FromBytes({0, 1, 2, 3, 4, 5});
FieldElement y(x);
EXPECT_EQ(x, y);
// Test the move constructor
FieldElement z(std::move(y));
EXPECT_EQ(x, z);
EXPECT_NE(x, y);
// Test the copy assignment operator
y = x;
EXPECT_EQ(x, y);
// Test the move assignment operator
z = std::move(y);
EXPECT_EQ(x, z);
EXPECT_NE(x, y);
}
TEST(FieldElementTest, TestCopyBytesToString) {
FieldElement el = FromBytes({0, 1, 2, 3, 4, 5});
std::string s;
el.CopyBytesToString(&s);
EXPECT_EQ(FieldElement::kDataSize, s.size());
std::string expected_string = std::string("\0\x1\x2\x3", 4) +
std::string(FieldElement::kDataSize - 4, 0);
EXPECT_EQ(expected_string, s);
}
TEST(FieldElementTest, TestArithmetic) {
// Test that 2 + 3 = 5.
EXPECT_EQ(FromInt(5), FromInt(2) + FromInt(3));
// Test that 2 + 3 = 5 with +=
FieldElement x = FromInt(2);
FieldElement y = FromInt(3);
FieldElement z = FromInt(5);
x+= y;
EXPECT_EQ(z, x);
// Test that -1 + 1 = 0.
// The bytes are kLargestPrime -1 in little-endian.
static const FieldElement kMinusOne = FromBytes({0xF4, 0xFE, 0xFF, 0xFF});
EXPECT_EQ(FieldElement(false), kMinusOne + FieldElement(true));
// Test that -1 + 1 = 0 with +=.
x = kMinusOne;
x+= FieldElement(true);
EXPECT_EQ(FieldElement(false), x);
// Test that 5 - 2 = 3
EXPECT_EQ(FromInt(3), FromInt(5) - FromInt(2));
// Test that 5 - 2 = 3 using -=.
x = FromInt(5);
y = FromInt(2);
x-= y;
EXPECT_EQ(FromInt(3), x);
// Test that 0 - 1 = -1
EXPECT_EQ(kMinusOne, FieldElement(false) - FieldElement(true));
// Test that 0 - 1 = -1 using -=.
x = FieldElement(false);
x -= FieldElement(true);
EXPECT_EQ(kMinusOne, x);
// Test that 1999000 - 1998999 = 1
EXPECT_EQ(FieldElement(true), FromInt(1999000) - FromInt(1998999));
// Test that 1999000 - 1998999 = 1 using -=
x = FromInt(1999000);
x -= FromInt(1998999);
EXPECT_EQ(FieldElement(true), x);
// Test that 3 * 5 = 15.
EXPECT_EQ(FromInt(15), FromInt(3) * FromInt(5));
// Test that 3 * 5 = 15 using *=
x = FromInt(3);
x*= FromInt(5);
EXPECT_EQ(FromInt(15), x);
// Test that -1 * 2 = -2.
static const FieldElement kMinus2 = FromBytes({0xF3, 0xFE, 0xFF, 0xFF});
EXPECT_EQ(kMinus2, kMinusOne * FromInt(2));
// Test that -1 * 2 = -2 using *=
x = kMinusOne;
x*= FromInt(2);
EXPECT_EQ(kMinus2, x);
// Check that 1/1 = 1.
x = FieldElement(true);
EXPECT_EQ(x, x/x);
// Check that 5/5 = 1
x = FromInt(5);
EXPECT_EQ(FieldElement(true), x/x);
// Check that 10/5 = 2
y = FromInt(10);
EXPECT_EQ(FromInt(2), y/x);
// Check that 10/5 = 2 using /=
y = FromInt(10);
y/=x;
EXPECT_EQ(FromInt(2), y);
// Check that 0/5 = 0
y = FieldElement(false);
EXPECT_EQ(y, y/x);
// Check that 1/2 * 2 = 1.
x = FieldElement(true)/FromInt(2);
x *= FromInt(2);
EXPECT_EQ(FieldElement(true), x);
// Check that 2/3 * 3 = 2.
x = FromInt(2)/FromInt(3);
x *= FromInt(3);
EXPECT_EQ(FromInt(2), x);
// Check that 2/3 * 2/3 = 4/9
x = FromInt(2)/FromInt(3);
x*= x;
EXPECT_EQ(FromInt(4)/FromInt(9), x);
// Check that 1999*1000/(1000 - 999) + 2001*999/(999 - 1000) = 1.
FieldElement x0 = FromInt(999);
FieldElement y0 = FromInt(1999);
FieldElement x1 = FromInt(1000);
FieldElement y1 = FromInt(2001);
EXPECT_EQ(FieldElement(true), y0*x1/(x1-x0) + y1*x0/(x0 -x1));
}
} // namespace forculus
} // namespace cobalt