blob: 120e8b07b732a33cf5cde3757316dd1d903069bf [file] [log] [blame]
// 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_DEATH(IntegerSequenceCodec::GetCountsForRange(0, &t, &q, &b), "");
ASSERT_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