diff options
Diffstat (limited to 'src/decoder/test/integer_sequence_codec_test.cc')
-rw-r--r-- | src/decoder/test/integer_sequence_codec_test.cc | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/src/decoder/test/integer_sequence_codec_test.cc b/src/decoder/test/integer_sequence_codec_test.cc new file mode 100644 index 0000000..b66ff2b --- /dev/null +++ b/src/decoder/test/integer_sequence_codec_test.cc @@ -0,0 +1,337 @@ +// Copyright 2018 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 "src/decoder/integer_sequence_codec.h" +#include "src/base/uint128.h" + +#include <random> +#include <string> +#include <vector> + +#include <gtest/gtest.h> + +using astc_codec::base::UInt128; +using astc_codec::base::BitStream; +using astc_codec::IntegerSequenceCodec; +using astc_codec::IntegerSequenceEncoder; +using astc_codec::IntegerSequenceDecoder; + +namespace { + +// Make sure that the counts returned for a specific range match what's +// expected. In particular, make sure that it fits with Table C.2.7 +TEST(ASTCIntegerSequenceCodecTest, TestGetCountsForRange) { + std::array<int, 3> kExpectedCounts[31] = { + {{ 0, 0, 1 }}, // 1 + {{ 1, 0, 0 }}, // 2 + {{ 0, 0, 2 }}, // 3 + {{ 0, 1, 0 }}, // 4 + {{ 1, 0, 1 }}, // 5 + {{ 0, 0, 3 }}, // 6 + {{ 0, 0, 3 }}, // 7 + {{ 0, 1, 1 }}, // 8 + {{ 0, 1, 1 }}, // 9 + {{ 1, 0, 2 }}, // 10 + {{ 1, 0, 2 }}, // 11 + {{ 0, 0, 4 }}, // 12 + {{ 0, 0, 4 }}, // 13 + {{ 0, 0, 4 }}, // 14 + {{ 0, 0, 4 }}, // 15 + {{ 0, 1, 2 }}, // 16 + {{ 0, 1, 2 }}, // 17 + {{ 0, 1, 2 }}, // 18 + {{ 0, 1, 2 }}, // 19 + {{ 1, 0, 3 }}, // 20 + {{ 1, 0, 3 }}, // 21 + {{ 1, 0, 3 }}, // 22 + {{ 1, 0, 3 }}, // 23 + {{ 0, 0, 5 }}, // 24 + {{ 0, 0, 5 }}, // 25 + {{ 0, 0, 5 }}, // 26 + {{ 0, 0, 5 }}, // 27 + {{ 0, 0, 5 }}, // 28 + {{ 0, 0, 5 }}, // 29 + {{ 0, 0, 5 }}, // 30 + {{ 0, 0, 5 }}, // 31 + }; + + int t, q, b; + for (int i = 1; i < 32; ++i) { + IntegerSequenceCodec::GetCountsForRange(i, &t, &q, &b); + EXPECT_EQ(t, kExpectedCounts[i - 1][0]); + EXPECT_EQ(q, kExpectedCounts[i - 1][1]); + EXPECT_EQ(b, kExpectedCounts[i - 1][2]); + } + + ASSERT_DEBUG_DEATH(IntegerSequenceCodec::GetCountsForRange(0, &t, &q, &b), ""); + ASSERT_DEBUG_DEATH( + IntegerSequenceCodec::GetCountsForRange(256, &t, &q, &b), ""); + + IntegerSequenceCodec::GetCountsForRange(1, &t, &q, &b); + EXPECT_EQ(t, 0); + EXPECT_EQ(q, 0); + EXPECT_EQ(b, 1); +} + +// Test to make sure that we're calculating the number of bits needed to +// encode a given number of values based on the range of the values. +TEST(ASTCIntegerSequenceCodecTest, TestNumBitsForCounts) { + int trits = 0; + int quints = 0; + int bits = 0; + + // A range of one should have single bits, so n 1-bit values should be n bits. + trits = 0; + quints = 0; + bits = 1; + for (int i = 0; i < 64; ++i) { + EXPECT_EQ(IntegerSequenceCodec::GetBitCount(i, trits, quints, bits), i); + EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(i, 1), i); + } + + // Similarly, N two-bit values should be 2n bits... + trits = 0; + quints = 0; + bits = 2; + for (int i = 0; i < 64; ++i) { + int bit_counts = IntegerSequenceCodec::GetBitCount(i, trits, quints, bits); + EXPECT_EQ(bit_counts, 2 * i); + EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(i, 3), 2 * i); + } + + // Trits are a bit more complicated -- there are five trits in a block, so + // if we encode 15 values with 3 bits each in trits, we'd get three blocks, + // each with eight bits of trits. + trits = 1; + quints = 0; + bits = 3; + EXPECT_EQ(IntegerSequenceCodec::GetBitCount(15, trits, quints, bits), + 8 * 3 + 15 * 3); + EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(15, 23), + IntegerSequenceCodec::GetBitCount(15, trits, quints, bits)); + + // However, if instead we encode 13 values, we don't need to use the remaining + // two values, so we only need bits as they will be encoded. As it turns out, + // this means we can avoid three bits in the final block (one for the high + // order trit encoding and two for one of the values), resulting in 47 bits. + trits = 1; + quints = 0; + bits = 2; + EXPECT_EQ(IntegerSequenceCodec::GetBitCount(13, trits, quints, bits), 47); + EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(13, 11), + IntegerSequenceCodec::GetBitCount(13, trits, quints, bits)); + + // Quints have a similar property -- if we encode six values using a quint and + // four bits, then we have two quint blocks each with three values and a seven + // bit encoded quint triplet... + trits = 0; + quints = 1; + bits = 4; + EXPECT_EQ(IntegerSequenceCodec::GetBitCount(6, trits, quints, bits), + 7 * 2 + 6 * 4); + EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(6, 79), + IntegerSequenceCodec::GetBitCount(6, trits, quints, bits)); + + // If we have fewer values than blocks we can again avoid about 2 + nbits + // bits... + trits = 0; + quints = 1; + bits = 3; + EXPECT_EQ(IntegerSequenceCodec::GetBitCount(7, trits, quints, bits), + /* first two quint blocks */ 7 * 2 + + /* first two blocks of bits */ 6 * 3 + + /* last quint block without the high order four bits */ 3 + + /* last block with one set of three bits */ 3); +} + +// Tests that the encoder knows how to encode values of the form 5*2^k. +TEST(ASTCIntegerSequenceCodecTest, TestQuintCodec) { + // In this case, k = 4 + + // Setup bit src/sink + BitStream<UInt128> bit_sink; + + const int kValueRange = 79; + IntegerSequenceEncoder enc(kValueRange); + enc.AddValue(3); + enc.AddValue(79); + enc.AddValue(37); + enc.Encode(&bit_sink); + + // quint: 1000101 m0: 0011 m1: 1111 m2: 0101 + // 100 0100 0111 1101 0010 + // interleaved 10m200m1101m0 + // should be 100 1010 0111 1101 0011 = 0x4A7D3 + EXPECT_EQ(bit_sink.Bits(), 19); + + uint64_t encoded = 0; + bit_sink.GetBits(19, &encoded); + EXPECT_EQ(encoded, 0x4A7D3); + + // Now check that decoding it works as well + BitStream<UInt128> bit_src(encoded, 19); + + IntegerSequenceDecoder dec(kValueRange); + auto decoded_vals = dec.Decode(3, &bit_src); + ASSERT_EQ(decoded_vals.size(), 3); + EXPECT_EQ(decoded_vals[0], 3); + EXPECT_EQ(decoded_vals[1], 79); + EXPECT_EQ(decoded_vals[2], 37); +} + +// Tests that the encoder knows how to encode values of the form 3*2^k. +TEST(ASTCIntegerSequenceCodecTest, TestTritCodec) { + uint64_t encoded = 0; + + // Setup bit src/sink + BitStream<UInt128> bit_sink(encoded, 0); + + const int kValueRange = 11; + IntegerSequenceEncoder enc(kValueRange); + enc.AddValue(7); + enc.AddValue(5); + enc.AddValue(3); + enc.AddValue(6); + enc.AddValue(10); + enc.Encode(&bit_sink); + + EXPECT_EQ(bit_sink.Bits(), 18); + + bit_sink.GetBits(18, &encoded); + EXPECT_EQ(encoded, 0x37357); + + // Now check that decoding it works as well + BitStream<UInt128> bit_src(encoded, 19); + + IntegerSequenceDecoder dec(kValueRange); + auto decoded_vals = dec.Decode(5, &bit_src); + ASSERT_EQ(decoded_vals.size(), 5); + EXPECT_EQ(decoded_vals[0], 7); + EXPECT_EQ(decoded_vals[1], 5); + EXPECT_EQ(decoded_vals[2], 3); + EXPECT_EQ(decoded_vals[3], 6); + EXPECT_EQ(decoded_vals[4], 10); +} + +// Test a specific quint encoding/decoding. This test makes sure that the way we +// encode and decode integer sequences matches what we should expect out of the +// reference ASTC encoder. +TEST(ASTCIntegerSequenceCodecTest, TestDecodeThenEncode) { + std::vector<int> vals = {{ 16, 18, 17, 4, 7, 14, 10, 0 }}; + const uint64_t kValEncoding = 0x2b9c83dc; + + BitStream<UInt128> bit_src(kValEncoding, 64); + IntegerSequenceDecoder dec(19); + auto decoded_vals = dec.Decode(8, &bit_src); + ASSERT_EQ(decoded_vals.size(), vals.size()); + for (size_t i = 0; i < decoded_vals.size(); ++i) { + EXPECT_EQ(decoded_vals[i], vals[i]); + } + + // Setup bit src/sink + BitStream<UInt128> bit_sink; + IntegerSequenceEncoder enc(19); + for (const auto& v : vals) { + enc.AddValue(v); + } + enc.Encode(&bit_sink); + EXPECT_EQ(bit_sink.Bits(), 35); + + uint64_t encoded = 0; + EXPECT_TRUE(bit_sink.GetBits(35, &encoded)); + EXPECT_EQ(encoded, kValEncoding) + << std::hex << encoded << " -- " << kValEncoding; +} + +// Same as the previous test, except it uses a trit encoding rather than a +// quint encoding. +TEST(ASTCIntegerSequenceCodecTest, TestDecodeThenEncodeTrits) { + std::vector<int> vals = {{ 6, 0, 0, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 8, 8, 0 }}; + const uint64_t kValEncoding = 0x0004c0100001006ULL; + + BitStream<UInt128> bit_src(kValEncoding, 64); + IntegerSequenceDecoder dec(11); + auto decoded_vals = dec.Decode(vals.size(), &bit_src); + ASSERT_EQ(decoded_vals.size(), vals.size()); + for (size_t i = 0; i < decoded_vals.size(); ++i) { + EXPECT_EQ(decoded_vals[i], vals[i]); + } + + // Setup bit src/sink + BitStream<UInt128> bit_sink; + IntegerSequenceEncoder enc(11); + for (const auto& v : vals) { + enc.AddValue(v); + } + enc.Encode(&bit_sink); + EXPECT_EQ(bit_sink.Bits(), 58); + + uint64_t encoded = 0; + EXPECT_TRUE(bit_sink.GetBits(58, &encoded)); + EXPECT_EQ(encoded, kValEncoding) + << std::hex << encoded << " -- " << kValEncoding; +} + +// Generate a random sequence of integer codings with different ranges to test +// the reciprocability of our codec (encoded sequences should be able to +// decoded) +TEST(ASTCIntegerSequenceCodecTest, TestRandomReciprocation) { + std::mt19937 mt(0xbad7357); + std::uniform_int_distribution<int> rand(0, 255); + + for (int test = 0; test < 1600; ++test) { + // Generate a random number of values and a random range + int num_vals = 4 + rand(mt) % 44; // Up to 48 weights in a grid + int range = 1 + rand(mt) % 63; + + // If this produces a bit pattern larger than our buffer, then ignore + // it... we already know what our bounds are for the integer sequences + int num_bits = IntegerSequenceCodec::GetBitCountForRange(num_vals, range); + if (num_bits >= 64) { + continue; + } + + std::vector<int> generated_vals(num_vals); + for (auto& val : generated_vals) { + val = rand(mt) % (range + 1); + } + + // Encode the values using the + BitStream<UInt128> bit_sink; + + // Add them to the encoder + IntegerSequenceEncoder enc(range); + for (int v : generated_vals) { + enc.AddValue(v); + } + enc.Encode(&bit_sink); + + uint64_t encoded = 0; + bit_sink.GetBits(bit_sink.Bits(), &encoded); + ASSERT_GE(encoded, 0); + EXPECT_LT(encoded, 1ULL << num_bits); + + BitStream<UInt128> bit_src(encoded, 64); + + IntegerSequenceDecoder dec(range); + auto decoded_vals = dec.Decode(num_vals, &bit_src); + + ASSERT_EQ(decoded_vals.size(), generated_vals.size()); + for (size_t i = 0; i < decoded_vals.size(); ++i) { + EXPECT_EQ(decoded_vals[i], generated_vals[i]); + } + } +} + +} // namespace |