| #include "src/algorithms/privacy/numeric_encoding.h" |
| |
| #include <limits> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "src/algorithms/random/random.h" |
| |
| using ::testing::AnyOf; |
| using ::testing::DoubleEq; |
| using ::testing::Eq; |
| |
| namespace cobalt { |
| |
| TEST(NumericEncodingTest, DoubleToIndexEqualsMaxValue) { |
| double min_value = -4.0; |
| double max_value = 6.0; |
| uint64_t num_index_points = 6; |
| double value = 6.0; |
| uint64_t expected_index = 5u; |
| RandomNumberGenerator gen; |
| |
| EXPECT_EQ(expected_index, DoubleToIndex(value, min_value, max_value, num_index_points, &gen)); |
| } |
| |
| TEST(NumericEncodingTest, DoubleToIndexEqualsMinValue) { |
| double min_value = -4.0; |
| double max_value = 6.0; |
| uint64_t num_index_points = 6; |
| double value = -4.0; |
| uint64_t expected_index = 0u; |
| RandomNumberGenerator gen; |
| |
| EXPECT_EQ(expected_index, DoubleToIndex(value, min_value, max_value, num_index_points, &gen)); |
| } |
| |
| TEST(NumericEncodingTest, DoubleToIndexEqualsInteriorIndexPoint) { |
| double min_value = -4.0; |
| double max_value = 6.0; |
| uint64_t num_index_points = 6; |
| double value = -2.0; |
| uint64_t expected_index = 1u; |
| RandomNumberGenerator gen; |
| |
| EXPECT_EQ(expected_index, DoubleToIndex(value, min_value, max_value, num_index_points, &gen)); |
| } |
| |
| TEST(NumericEncodingTest, DoubleToIndexBetweenTwoIndicesNoSeed) { |
| double min_value = -4.0; |
| double max_value = 6.0; |
| uint64_t num_index_points = 6; |
| double value = -1.0; |
| uint64_t lower_index = 1u; |
| uint64_t upper_index = 2u; |
| RandomNumberGenerator gen; |
| |
| for (int i = 0; i < 1000; i++) { |
| EXPECT_THAT(DoubleToIndex(value, min_value, max_value, num_index_points, &gen), |
| AnyOf(Eq(lower_index), Eq(upper_index))); |
| } |
| } |
| |
| TEST(NumericEncodingTest, IntegerToIndexBetweenTwoIndicesNoSeed) { |
| int64_t min_value = -4; |
| int64_t max_value = 6; |
| uint64_t num_index_points = 6; |
| int64_t value = -1; |
| uint64_t lower_index = 1u; |
| uint64_t upper_index = 2u; |
| RandomNumberGenerator gen; |
| |
| for (int i = 0; i < 1000; i++) { |
| EXPECT_THAT(IntegerToIndex(value, min_value, max_value, num_index_points, &gen), |
| AnyOf(Eq(lower_index), Eq(upper_index))); |
| } |
| } |
| |
| TEST(NumericEncodingTest, CountToIndex) { |
| uint64_t max_count = 10u; |
| uint64_t num_index_points = 6u; |
| uint64_t count = 4u; |
| RandomNumberGenerator gen; |
| |
| uint64_t expected = 8u; |
| |
| EXPECT_EQ(CountToIndex(count, max_count, num_index_points, &gen), expected); |
| } |
| |
| TEST(NumericEncodingTest, HistogramBucketAndCountToIndex) { |
| uint64_t max_count = 10u; |
| uint64_t num_index_points = 6u; |
| uint64_t bucket_count = 4u; |
| uint32_t bucket_index = 7u; |
| RandomNumberGenerator gen; |
| |
| uint64_t expected = 44u; |
| |
| EXPECT_EQ( |
| HistogramBucketAndCountToIndex(bucket_index, bucket_count, max_count, num_index_points, &gen), |
| expected); |
| } |
| |
| TEST(NumericEncodingTest, DoubleFromIndex) { |
| // The approximate numeric values are all integers. |
| double min_value = -4.0; |
| double max_value = 6.0; |
| uint64_t num_index_points = 6u; |
| |
| double expected = min_value; |
| for (uint64_t index = 0u; index <= num_index_points; index++) { |
| EXPECT_THAT(DoubleFromIndex(index, min_value, max_value, num_index_points), DoubleEq(expected)); |
| expected += 2.0; |
| } |
| |
| // The approximate numeric values are not all integers. |
| num_index_points = 5u; |
| expected = min_value; |
| for (uint64_t index = 0u; index <= num_index_points; index++) { |
| EXPECT_THAT(DoubleFromIndex(index, min_value, max_value, num_index_points), DoubleEq(expected)); |
| expected += 2.5; |
| } |
| } |
| |
| TEST(NumericEncodingTest, CountFromIndex) { |
| // The approximate counts are all integers. |
| uint64_t max_count = 10u; |
| uint64_t num_index_points = 6u; |
| double expected = 0.0; |
| |
| for (uint64_t index = num_index_points; index < 2 * num_index_points; index++) { |
| EXPECT_THAT(CountFromIndex(index, max_count, num_index_points), DoubleEq(expected)); |
| expected += 2.0; |
| } |
| |
| // The approximate counts are not all integers. |
| num_index_points = 5u; |
| expected = 0.0; |
| |
| for (uint64_t index = num_index_points; index < 2 * num_index_points; index++) { |
| EXPECT_THAT(CountFromIndex(index, max_count, num_index_points), DoubleEq(expected)); |
| expected += 2.5; |
| } |
| } |
| |
| TEST(NumericEncodingTest, HistogramBucketAndCountFromIndex) { |
| // The approximate counts are all integers. |
| uint64_t max_count = 10u; |
| uint64_t num_index_points = 6u; |
| uint64_t num_buckets = 10u; |
| |
| double expected_bucket_count = 0.0; |
| uint64_t expected_bucket_index = 0u; |
| |
| for (uint64_t index = 0u; index < num_index_points * num_buckets; ++index) { |
| double bucket_count; |
| uint32_t bucket_index; |
| |
| HistogramBucketAndCountFromIndex(index, max_count, num_index_points, &bucket_index, |
| &bucket_count); |
| EXPECT_EQ(bucket_index, expected_bucket_index); |
| EXPECT_THAT(bucket_count, DoubleEq(expected_bucket_count)); |
| |
| expected_bucket_count += 2.0; |
| if (expected_bucket_count > static_cast<double>(max_count)) { |
| expected_bucket_count = 0.0; |
| ++expected_bucket_index; |
| } |
| } |
| |
| // The approximate counts are not all integers. |
| num_index_points = 5u; |
| expected_bucket_count = 0.0; |
| expected_bucket_index = 0u; |
| |
| for (uint64_t index = 0u; index < num_index_points * num_buckets; ++index) { |
| double bucket_count; |
| uint32_t bucket_index; |
| |
| HistogramBucketAndCountFromIndex(index, max_count, num_index_points, &bucket_index, |
| &bucket_count); |
| EXPECT_EQ(bucket_index, expected_bucket_index); |
| EXPECT_THAT(bucket_count, DoubleEq(expected_bucket_count)); |
| |
| expected_bucket_count += 2.5; |
| if (expected_bucket_count > static_cast<double>(max_count)) { |
| expected_bucket_count = 0.0; |
| ++expected_bucket_index; |
| } |
| } |
| } |
| |
| TEST(NumericEncodingTest, HistogramBucketAndCountFromIndexLargeIndex) { |
| uint64_t max_count = 100u; |
| uint64_t num_index_points = 5u; |
| |
| // Choose a bucket_index that exceeds UINT32_MAX to simulate a large 64-bit index. |
| // Adding 4 ensures that when large_bucket_index is truncated to uint32_t, it wraps around |
| // to 3 (a distinct, non-zero value that differs from remainder_index). |
| uint64_t large_bucket_index = static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 4; |
| |
| // Choose remainder_index = 2 (in range [0, num_index_points - 1]) to represent the count |
| // component. |
| uint64_t remainder_index = 2; |
| |
| // Construct the packed 64-bit index: (bucket_index * num_index_points) + remainder. |
| uint64_t index = large_bucket_index * num_index_points + remainder_index; |
| |
| double bucket_count = -1.0; |
| uint32_t bucket_index = 0; |
| HistogramBucketAndCountFromIndex(index, max_count, num_index_points, &bucket_index, |
| &bucket_count); |
| |
| // The 32-bit bucket_index receives the truncated lower 32 bits of large_bucket_index (3). |
| EXPECT_EQ(bucket_index, static_cast<uint32_t>(large_bucket_index)); |
| |
| // The decoded bucket_count is derived strictly from remainder_index (2), independent of the |
| // large bucket_index. For range [0, 100] with 5 index points (step size = 100 / (5 - 1) = 25), |
| // index 2 maps to: 0 + (25 * 2) = 50.0. |
| // Ensure bucket_count is correctly decoded and remains within [0, max_count]. |
| EXPECT_THAT(bucket_count, DoubleEq(50.0)); |
| EXPECT_LE(bucket_count, static_cast<double>(max_count)); |
| } |
| |
| TEST(NumericEncodingTest, ValueAndEventVectorIndicesToIndex) { |
| uint64_t num_index_points = 6; |
| uint64_t max_event_vector_index = 9; |
| |
| uint64_t expected_index = 0; |
| for (uint64_t value_index_in = 0; value_index_in < num_index_points; ++value_index_in) { |
| for (uint64_t ev_index_in = 0; ev_index_in <= max_event_vector_index; ++ev_index_in) { |
| uint64_t index = |
| ValueAndEventVectorIndicesToIndex(value_index_in, ev_index_in, max_event_vector_index); |
| EXPECT_EQ(index, expected_index); |
| |
| uint64_t value_index_out; |
| uint64_t ev_index_out; |
| ValueAndEventVectorIndicesFromIndex(index, max_event_vector_index, &value_index_out, |
| &ev_index_out); |
| EXPECT_EQ(value_index_in, value_index_out); |
| EXPECT_EQ(ev_index_in, ev_index_out); |
| ++expected_index; |
| } |
| } |
| } |
| |
| TEST(NumericEncodingTest, IsCountIndex) { |
| uint64_t num_index_points = 5; |
| EXPECT_TRUE(IsCountIndex(7u, num_index_points)); |
| EXPECT_FALSE(IsCountIndex(3u, num_index_points)); |
| } |
| |
| } // namespace cobalt |