From b11c29af899923d8bb8904db0d8d215cb4249148 Mon Sep 17 00:00:00 2001 From: Xiening Dai Date: Sat, 14 Nov 2026 19:30:52 +0000 Subject: [PATCH 1/2] feat(util): add Mumbling bitmap reader Add an internal reader for the Mumbling bitmap format, which v4 manifest deletion vectors use (manifest_info.dv, tracking.deleted_positions and tracking.replaced_positions). The spec is still a draft, so the header is internal and not installed. MumblingBitmapView wraps the serialized bytes without copying them and supports Contains() and Cardinality(). Make() validates everything that memory safety depends on, so lookups never read out of bounds. Test: - New unit tests in util_test (mumbling_bitmap_internal_test.cc) cover the spec examples, Java-generated fixtures and malformed input. - The full test suite passes on both the SSE2 and the portable lookup path, and the new tests pass under ASan and UBSan. - New microbenchmarks in src/iceberg/benchmark (mumbling_bitmap_benchmark, built with -DICEBERG_BUILD_BENCHMARKS=ON). --- src/iceberg/CMakeLists.txt | 1 + src/iceberg/benchmark/CMakeLists.txt | 6 + .../benchmark/mumbling_bitmap_benchmark.cc | 238 ++++++++ src/iceberg/test/CMakeLists.txt | 1 + .../test/mumbling_bitmap_internal_test.cc | 562 ++++++++++++++++++ src/iceberg/test/mumbling_bitmap_test_util.h | 211 +++++++ .../mumbling/bitmap-java-fixtures.tsv | 22 + .../resources/mumbling/pfor-java-fixtures.tsv | 146 +++++ src/iceberg/util/mumbling_bitmap_internal.cc | 350 +++++++++++ src/iceberg/util/mumbling_bitmap_internal.h | 134 +++++ 10 files changed, 1671 insertions(+) create mode 100644 src/iceberg/benchmark/mumbling_bitmap_benchmark.cc create mode 100644 src/iceberg/test/mumbling_bitmap_internal_test.cc create mode 100644 src/iceberg/test/mumbling_bitmap_test_util.h create mode 100644 src/iceberg/test/resources/mumbling/bitmap-java-fixtures.tsv create mode 100644 src/iceberg/test/resources/mumbling/pfor-java-fixtures.tsv create mode 100644 src/iceberg/util/mumbling_bitmap_internal.cc create mode 100644 src/iceberg/util/mumbling_bitmap_internal.h diff --git a/src/iceberg/CMakeLists.txt b/src/iceberg/CMakeLists.txt index 4cde4fb23f..b2dac4dfb5 100644 --- a/src/iceberg/CMakeLists.txt +++ b/src/iceberg/CMakeLists.txt @@ -158,6 +158,7 @@ set(ICEBERG_SOURCES util/decimal.cc util/gzip_internal.cc util/location_util.cc + util/mumbling_bitmap_internal.cc util/murmurhash3_internal.cc util/property_util.cc util/retry_util.cc diff --git a/src/iceberg/benchmark/CMakeLists.txt b/src/iceberg/benchmark/CMakeLists.txt index 5d3c19d0f5..0bf98ce5b4 100644 --- a/src/iceberg/benchmark/CMakeLists.txt +++ b/src/iceberg/benchmark/CMakeLists.txt @@ -22,3 +22,9 @@ target_link_libraries(benchmark_smoke PRIVATE "$,iceberg_static,iceberg_shared>" "$,benchmark::benchmark,benchmark>" ) + +add_executable(mumbling_bitmap_benchmark mumbling_bitmap_benchmark.cc) +target_link_libraries(mumbling_bitmap_benchmark + PRIVATE "$,iceberg_static,iceberg_shared>" + "$,benchmark::benchmark,benchmark>" +) diff --git a/src/iceberg/benchmark/mumbling_bitmap_benchmark.cc b/src/iceberg/benchmark/mumbling_bitmap_benchmark.cc new file mode 100644 index 0000000000..770d40ba5d --- /dev/null +++ b/src/iceberg/benchmark/mumbling_bitmap_benchmark.cc @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +// Microbenchmarks for the Mumbling bitmap reader. +// +// Build with -DICEBERG_BUILD_BENCHMARKS=ON and run, for example: +// ./build/src/iceberg/benchmark/mumbling_bitmap_benchmark --benchmark_filter=Pfor +// +// The inputs are serialized with the test encoder in +// iceberg/test/mumbling_bitmap_test_util.h, which picks PFOR bit widths the way +// the Java encoder does (checked against Java-generated fixtures in +// mumbling_bitmap_internal_test.cc). + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "iceberg/test/mumbling_bitmap_test_util.h" +#include "iceberg/util/mumbling_bitmap_internal.h" + +namespace iceberg { +namespace { + +using mumbling_test::BuildBitmapFromPositions; +using mumbling_test::Bytes; +using mumbling_test::EncodePfor; +using mumbling_test::kBestWidth; + +constexpr int64_t kMaxPositions = MumblingBitmapView::kMaxCardinality; +constexpr uint32_t kSeed = 42; + +// --------------------------------------------------------------------------- +// PFOR descriptor decoding +// --------------------------------------------------------------------------- + +// Value distributions for PFOR inputs, modeled on Java's PFORRandomData. +enum class PforData { + kConstant, // every value is the same (one bit width of 0) + kSmall, // uniform 0-3 + kExceptions1Pct, // 0-3 with 1% exceptions in 0-255 + kExceptions10Pct, // 0-3 with 10% exceptions in 0-255 + kUniform, // uniform 0-255 (stored as raw bytes) +}; + +std::vector MakePforValues(PforData data, size_t count) { + std::mt19937 rng(kSeed); + std::uniform_int_distribution small(0, 3); + std::uniform_int_distribution any(0, 255); + std::uniform_real_distribution unit(0.0, 1.0); + std::vector values(count); + for (auto& value : values) { + switch (data) { + case PforData::kConstant: + value = 5; + break; + case PforData::kSmall: + value = static_cast(small(rng)); + break; + case PforData::kExceptions1Pct: + value = static_cast(unit(rng) < 0.01 ? any(rng) : small(rng)); + break; + case PforData::kExceptions10Pct: + value = static_cast(unit(rng) < 0.10 ? any(rng) : small(rng)); + break; + case PforData::kUniform: + value = static_cast(any(rng)); + break; + } + } + return values; +} + +// Arg: number of values. +void BM_PforDecode(benchmark::State& state, PforData data) { + const auto count = static_cast(state.range(0)); + const Bytes encoded = EncodePfor(MakePforValues(data, count), kBestWidth); + std::vector out(count); + for (auto _ : state) { + auto result = DecodePforBytes(encoded, out); + benchmark::DoNotOptimize(result); + benchmark::DoNotOptimize(out.data()); + benchmark::ClobberMemory(); + } + state.SetItemsProcessed(state.iterations() * static_cast(count)); + state.SetBytesProcessed(state.iterations() * static_cast(encoded.size())); + state.counters["encoded_bytes"] = static_cast(encoded.size()); +} + +void PforArgs(benchmark::internal::Benchmark* b) { + b->ArgName("count")->Arg(256)->Arg(8192); +} + +BENCHMARK_CAPTURE(BM_PforDecode, constant, PforData::kConstant)->Apply(PforArgs); +BENCHMARK_CAPTURE(BM_PforDecode, small, PforData::kSmall)->Apply(PforArgs); +BENCHMARK_CAPTURE(BM_PforDecode, exceptions_1pct, PforData::kExceptions1Pct) + ->Apply(PforArgs); +BENCHMARK_CAPTURE(BM_PforDecode, exceptions_10pct, PforData::kExceptions10Pct) + ->Apply(PforArgs); +BENCHMARK_CAPTURE(BM_PforDecode, uniform, PforData::kUniform)->Apply(PforArgs); + +// --------------------------------------------------------------------------- +// Bitmaps +// --------------------------------------------------------------------------- + +// Position layouts for bitmap inputs. +enum class Layout { + kRandom, // positions spread uniformly over the full range (mostly sparse) + kRuns, // runs of 64 consecutive positions (dense containers between gaps) + kPrefix, // the first N positions (all dense containers) +}; + +std::vector MakePositions(Layout layout, int64_t cardinality) { + std::vector positions; + positions.reserve(cardinality); + switch (layout) { + case Layout::kRandom: { + std::mt19937_64 rng(kSeed); + std::uniform_int_distribution dist(0, kMaxPositions - 1); + std::set unique; + while (static_cast(unique.size()) < cardinality) { + unique.insert(dist(rng)); + } + positions.assign(unique.begin(), unique.end()); + break; + } + case Layout::kRuns: { + // Spread the runs so that they cover the whole range when possible. + const int64_t runs = (cardinality + 63) / 64; + const int64_t stride = std::max(64, (kMaxPositions / runs) / 64 * 64); + for (int64_t run = 0; static_cast(positions.size()) < cardinality; ++run) { + for (int64_t i = 0; + i < 64 && static_cast(positions.size()) < cardinality; ++i) { + positions.push_back(run * stride + i); + } + } + break; + } + case Layout::kPrefix: + for (int64_t i = 0; i < cardinality; ++i) { + positions.push_back(i); + } + break; + } + return positions; +} + +Bytes MakeBitmapBytes(const benchmark::State& state, Layout layout) { + return BuildBitmapFromPositions(MakePositions(layout, state.range(0))); +} + +MumblingBitmapView MakeView(const Bytes& bytes) { + auto bitmap = MumblingBitmapView::Make(bytes); + if (!bitmap.has_value()) { + std::abort(); + } + return std::move(bitmap).value(); +} + +void BitmapArgs(benchmark::internal::Benchmark* b) { + b->ArgName("cardinality")->Arg(1'000)->Arg(100'000); +} + +// The largest bitmap: every position set. +void MaxBitmapArgs(benchmark::internal::Benchmark* b) { + b->ArgName("cardinality")->Arg(kMaxPositions); +} + +#define MUMBLING_BITMAP_BENCHMARK(fn) \ + BENCHMARK_CAPTURE(fn, random, Layout::kRandom)->Apply(BitmapArgs); \ + BENCHMARK_CAPTURE(fn, runs, Layout::kRuns)->Apply(BitmapArgs); \ + BENCHMARK_CAPTURE(fn, prefix, Layout::kPrefix)->Apply(BitmapArgs); \ + BENCHMARK_CAPTURE(fn, full, Layout::kPrefix)->Apply(MaxBitmapArgs) + +// Arg: cardinality. Creating a view: header checks, descriptor decoding and +// the container offset table. +void BM_Make(benchmark::State& state, Layout layout) { + const Bytes bytes = MakeBitmapBytes(state, layout); + for (auto _ : state) { + auto bitmap = MumblingBitmapView::Make(bytes); + benchmark::DoNotOptimize(bitmap); + } + state.SetBytesProcessed(state.iterations() * static_cast(bytes.size())); + state.counters["serialized_bytes"] = static_cast(bytes.size()); +} + +MUMBLING_BITMAP_BENCHMARK(BM_Make); + +// Arg: cardinality. Probes random positions over the full range. +void BM_Contains(benchmark::State& state, Layout layout) { + const Bytes bytes = MakeBitmapBytes(state, layout); + const MumblingBitmapView bitmap = MakeView(bytes); + constexpr size_t kProbeCount = 4096; + std::mt19937_64 rng(kSeed + 1); + std::uniform_int_distribution dist(0, kMaxPositions - 1); + std::vector probes(kProbeCount); + for (auto& probe : probes) { + probe = dist(rng); + } + + for (auto _ : state) { + int64_t hits = 0; + for (int64_t probe : probes) { + hits += bitmap.Contains(probe) ? 1 : 0; + } + benchmark::DoNotOptimize(hits); + } + state.SetItemsProcessed(state.iterations() * static_cast(kProbeCount)); +} + +MUMBLING_BITMAP_BENCHMARK(BM_Contains); + +} // namespace +} // namespace iceberg + +BENCHMARK_MAIN(); diff --git a/src/iceberg/test/CMakeLists.txt b/src/iceberg/test/CMakeLists.txt index 1874fc25e8..505d04bfda 100644 --- a/src/iceberg/test/CMakeLists.txt +++ b/src/iceberg/test/CMakeLists.txt @@ -155,6 +155,7 @@ add_iceberg_test(util_test lazy_test.cc location_util_test.cc math_util_internal_test.cc + mumbling_bitmap_internal_test.cc executor_util_test.cc roaring_position_bitmap_test.cc position_delete_index_test.cc diff --git a/src/iceberg/test/mumbling_bitmap_internal_test.cc b/src/iceberg/test/mumbling_bitmap_internal_test.cc new file mode 100644 index 0000000000..f1d4b92992 --- /dev/null +++ b/src/iceberg/test/mumbling_bitmap_internal_test.cc @@ -0,0 +1,562 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "iceberg/util/mumbling_bitmap_internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "iceberg/test/matchers.h" +#include "iceberg/test/mumbling_bitmap_test_util.h" +#include "iceberg/test/test_resource.h" + +namespace iceberg { + +namespace { + +using mumbling_test::BuildBitmap; +using mumbling_test::Bytes; +using mumbling_test::Dense; +using mumbling_test::DenseRange; +using mumbling_test::EncodePfor; +using mumbling_test::Header; +using mumbling_test::kBestWidth; + +std::vector Range(int64_t start, int64_t end, int64_t step = 1) { + std::vector result; + for (int64_t i = start; i < end; i += step) { + result.push_back(i); + } + return result; +} + +// Checks Contains() for every position up to one container past the last +// expected one, and for the ends of the position range. +void ExpectPositions(const MumblingBitmapView& bitmap, + const std::vector& expected) { + EXPECT_EQ(bitmap.Cardinality(), static_cast(expected.size())); + std::set expected_set(expected.begin(), expected.end()); + const int64_t limit = expected.empty() ? 512 : (expected.back() / 256 + 2) * 256; + for (int64_t pos = -1; pos < limit; ++pos) { + ASSERT_EQ(bitmap.Contains(pos), expected_set.contains(pos)) << "pos=" << pos; + } + for (int64_t pos : {int64_t{MumblingBitmapView::kMaxCardinality} - 1, + int64_t{MumblingBitmapView::kMaxCardinality}, + std::numeric_limits::max()}) { + ASSERT_EQ(bitmap.Contains(pos), expected_set.contains(pos)) << "pos=" << pos; + } +} + +Result> DecodePfor(const Bytes& data, size_t count, + size_t expected_bytes_read) { + std::vector out(count); + ICEBERG_ASSIGN_OR_RAISE(auto bytes_read, DecodePforBytes(data, out)); + EXPECT_EQ(bytes_read, expected_bytes_read); + return out; +} + +// One line of a fixture file: a hex string and a comma-separated list of +// integers, separated by a tab. +struct Fixture { + int line; + Bytes bytes; + std::vector values; +}; + +Bytes ParseHex(std::string_view hex) { + Bytes out; + for (size_t i = 0; i + 1 < hex.size(); i += 2) { + out.push_back( + static_cast(std::stoi(std::string(hex.substr(i, 2)), nullptr, 16))); + } + return out; +} + +std::vector ParseValues(std::string_view text) { + std::vector values; + size_t start = 0; + while (start < text.size()) { + size_t end = text.find(',', start); + if (end == std::string_view::npos) { + end = text.size(); + } + values.push_back(std::stoll(std::string(text.substr(start, end - start)))); + start = end + 1; + } + return values; +} + +// Reads `file_name` from the test resources. `hex_column` is the column (0 or 1) +// that holds the encoded bytes; the other column holds the values. +std::vector ReadFixtures(const std::string& file_name, int hex_column) { + std::ifstream file(GetResourcePath(file_name)); + EXPECT_TRUE(file.is_open()) << "Cannot open " << file_name; + std::vector fixtures; + std::string line; + for (int line_number = 1; std::getline(file, line); ++line_number) { + if (line.empty() || line.starts_with('#')) { + continue; + } + const size_t tab = line.find('\t'); + EXPECT_NE(tab, std::string::npos) << file_name << ":" << line_number; + if (tab == std::string::npos) { + continue; + } + std::string_view columns[] = {std::string_view(line).substr(0, tab), + std::string_view(line).substr(tab + 1)}; + fixtures.push_back({.line = line_number, + .bytes = ParseHex(columns[hex_column]), + .values = ParseValues(columns[1 - hex_column])}); + } + return fixtures; +} + +} // namespace + +// Examples from "Appendix A: PFOR encoding for unsigned bytes" of the Mumbling +// spec: https://github.com/apache/iceberg/blob/main/format/mumbling-spec.md#examples +TEST(PforDecodeTest, SpecExamples) { + ICEBERG_UNWRAP_OR_FAIL(auto all_zero, DecodePfor({0x00, 0x00, 0x00}, 256, 3)); + EXPECT_EQ(all_zero, std::vector(256, 0)); + + ICEBERG_UNWRAP_OR_FAIL(auto all_five, DecodePfor({0x00, 0x00, 0x05}, 51, 3)); + EXPECT_EQ(all_five, std::vector(51, 5)); + + ICEBERG_UNWRAP_OR_FAIL(auto exceptions_only, + DecodePfor({0x80, 0x02, 0x00, 0x04, 0x07, 0xFF, 0xFE}, 8, 7)); + EXPECT_EQ(exceptions_only, (std::vector{0, 0, 0, 0, 0xFF, 0, 0, 0xFE})); + + ICEBERG_UNWRAP_OR_FAIL(auto two_bits, DecodePfor({0x02, 0x00, 0x06, 0x18}, 3, 4)); + EXPECT_EQ(two_bits, (std::vector{6, 7, 8})); + + ICEBERG_UNWRAP_OR_FAIL(auto with_exception, + DecodePfor({0x32, 0x01, 0x06, 0x09, 0x01, 0xE0}, 4, 6)); + EXPECT_EQ(with_exception, (std::vector{6, 34, 8, 7})); +} + +// The Java encoder's choice for spec example 5 (b1 = 5, no exceptions), from +// TestPFOREncoding#testExample5 in apache/iceberg. +TEST(PforDecodeTest, JavaEncodingOfExample5) { + ICEBERG_UNWRAP_OR_FAIL(auto values, + DecodePfor({0x05, 0x00, 0x06, 0x07, 0x04, 0x10}, 4, 6)); + EXPECT_EQ(values, (std::vector{6, 34, 8, 7})); +} + +TEST(PforDecodeTest, HandPackedWidths) { + ICEBERG_UNWRAP_OR_FAIL(auto one_bit, DecodePfor({0x01, 0x00, 0x00, 0xB0, 0x80}, 9, 5)); + EXPECT_EQ(one_bit, (std::vector{1, 0, 1, 1, 0, 0, 0, 0, 1})); + + ICEBERG_UNWRAP_OR_FAIL(auto three_bits, + DecodePfor({0x03, 0x00, 0x00, 0xE2, 0xA0}, 4, 5)); + EXPECT_EQ(three_bits, (std::vector{7, 0, 5, 2})); + + ICEBERG_UNWRAP_OR_FAIL(auto four_bits, + DecodePfor({0x04, 0x00, 0x01, 0xA5, 0xF0}, 3, 5)); + EXPECT_EQ(four_bits, (std::vector{0xB, 0x6, 0x10})); + + ICEBERG_UNWRAP_OR_FAIL(auto six_bits, + DecodePfor({0x06, 0x00, 0x00, 0xFC, 0x18, 0x00}, 3, 6)); + EXPECT_EQ(six_bits, (std::vector{63, 1, 32})); + + ICEBERG_UNWRAP_OR_FAIL(auto seven_bits, + DecodePfor({0x07, 0x00, 0x00, 0xFE, 0x04}, 2, 5)); + EXPECT_EQ(seven_bits, (std::vector{0x7F, 0x01})); + + ICEBERG_UNWRAP_OR_FAIL(auto eight_bits, + DecodePfor({0x08, 0x00, 0x00, 0x20, 0x00, 0x1F}, 3, 6)); + EXPECT_EQ(eight_bits, (std::vector{0x20, 0x00, 0x1F})); +} + +TEST(PforDecodeTest, EmptyInput) { + ICEBERG_UNWRAP_OR_FAIL(auto values, DecodePfor({}, 0, 0)); + EXPECT_TRUE(values.empty()); +} + +TEST(PforDecodeTest, MultipleChunks) { + // 256 values of 7, then 44 raw values. + Bytes data = {0x00, 0x00, 0x07, 0x08, 0x00, 0x00}; + std::vector expected(256, 7); + for (int i = 0; i < 44; ++i) { + data.push_back(static_cast(i)); + expected.push_back(static_cast(i)); + } + // Trailing bytes after the encoded values are not consumed. + data.push_back(0xAB); + ICEBERG_UNWRAP_OR_FAIL(auto values, DecodePfor(data, 300, data.size() - 1)); + EXPECT_EQ(values, expected); +} + +TEST(PforDecodeTest, AllWidthsRoundTrip) { + std::mt19937 rng(42); + for (int b1 = 0; b1 <= 8; ++b1) { + for (size_t count : {1, 7, 255, 256, 257, 600}) { + std::vector values(count); + for (auto& value : values) { + // Mostly small values with a few larger ones to produce exceptions. + value = static_cast(rng() % 16 == 0 ? 3 + rng() % 253 : 3 + rng() % 4); + } + auto encoded = EncodePfor(values, b1); + ICEBERG_UNWRAP_OR_FAIL(auto decoded, DecodePfor(encoded, count, encoded.size())); + EXPECT_EQ(decoded, values) << "b1=" << b1 << " count=" << count; + } + } +} + +TEST(PforDecodeTest, Malformed) { + std::vector out(4); + auto decode = [&](const Bytes& data) { return DecodePforBytes(data, out); }; + + EXPECT_THAT(decode({0x00, 0x00}), HasErrorMessage("Truncated PFOR chunk header")); + EXPECT_THAT(decode({0x09, 0x00, 0x00, 0, 0, 0, 0, 0}), + HasErrorMessage("Invalid PFOR primary bit width: 9")); + EXPECT_THAT(decode({0x72, 0x00, 0x00, 0, 0, 0, 0}), + HasErrorMessage("Invalid PFOR exception bit width: 7")); + EXPECT_THAT(decode({0x18, 0x00, 0x00, 0, 0, 0, 0, 0}), + HasErrorMessage("Invalid PFOR exception bit width: 1")); + EXPECT_THAT(decode({0x08, 0x01, 0x00, 0, 0, 0, 0, 0}), + HasErrorMessage("exceptions with 8-bit primary values")); + EXPECT_THAT(decode({0x80, 0x05, 0x00, 0, 1, 2, 3, 4, 1, 1, 1, 1, 1}), + HasErrorMessage("5 exceptions for 4 values")); + EXPECT_THAT(decode({0x02, 0x00, 0x00}), HasErrorMessage("Truncated PFOR chunk:")); + EXPECT_THAT(decode({0x80, 0x01, 0x00, 0x00}), HasErrorMessage("Truncated PFOR chunk:")); + EXPECT_THAT(decode({0x80, 0x01, 0x00, 0x04, 0x01}), + HasErrorMessage("Invalid PFOR exception offset: 4")); + EXPECT_THAT(decode({0x01, 0x00, 0xFF, 0x80}), + HasErrorMessage("Invalid PFOR value at index 0: 256")); + EXPECT_THAT(decode({0x08, 0x00, 0x01, 0x00, 0x00, 0xFF, 0x00}), + HasErrorMessage("Invalid PFOR value at index 2: 256")); + + // The second chunk is missing. + std::vector two_chunks(257); + EXPECT_THAT(DecodePforBytes(Bytes{0x00, 0x00, 0x00}, two_chunks), + HasErrorMessage("Truncated PFOR chunk header")); +} + +TEST(MumblingBitmapTest, EmptyBitmap) { + const Bytes bitmap_bytes = Bytes{0x01, 0, 0, 0, 0, 0}; + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(bitmap_bytes)); + ExpectPositions(bitmap, {}); +} + +TEST(MumblingBitmapTest, EmptySparseContainers) { + const Bytes bitmap_bytes = BuildBitmap({{}, {}, {}}); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(bitmap_bytes)); + ExpectPositions(bitmap, {}); +} + +// Sparse container examples from the spec. +TEST(MumblingBitmapTest, SparseSpecExamples) { + // Hand-encoded: header, PFOR chunk {b1=0, e=0, m=3}, container `00 22 FF`. + Bytes bytes = {0x01, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x22, 0xFF}; + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(bytes)); + ExpectPositions(bitmap, {0, 34, 255}); + + Bytes full; + for (uint8_t i = 0; i < 31; ++i) { + full.push_back(i); + } + const Bytes full_bitmap_bytes = BuildBitmap({full}); + ICEBERG_UNWRAP_OR_FAIL(auto full_bitmap, MumblingBitmapView::Make(full_bitmap_bytes)); + ExpectPositions(full_bitmap, Range(0, 31)); +} + +// Dense container examples from the spec. +TEST(MumblingBitmapTest, DenseSpecExamples) { + Bytes first_32(32, 0); + std::fill_n(first_32.begin(), 4, 0xFF); + const Bytes bitmap1_bytes = BuildBitmap({first_32}); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap1, MumblingBitmapView::Make(bitmap1_bytes)); + ExpectPositions(bitmap1, Range(0, 32)); + + Bytes first_33 = first_32; + first_33[4] = 0x80; + const Bytes bitmap2_bytes = BuildBitmap({first_33}); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap2, MumblingBitmapView::Make(bitmap2_bytes)); + ExpectPositions(bitmap2, Range(0, 33)); + + Bytes ends(32, 0); + ends[0] = ends[1] = ends[30] = ends[31] = 0xFF; + const Bytes bitmap3_bytes = BuildBitmap({ends}); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap3, MumblingBitmapView::Make(bitmap3_bytes)); + auto expected = Range(0, 16); + auto tail = Range(240, 256); + expected.insert(expected.end(), tail.begin(), tail.end()); + ExpectPositions(bitmap3, expected); + + Bytes even(32, 0xAA); + const Bytes bitmap4_bytes = BuildBitmap({even}); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap4, MumblingBitmapView::Make(bitmap4_bytes)); + ExpectPositions(bitmap4, Range(0, 256, 2)); + + Bytes all(32, 0xFF); + const Bytes bitmap5_bytes = BuildBitmap({all}); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap5, MumblingBitmapView::Make(bitmap5_bytes)); + ExpectPositions(bitmap5, Range(0, 256)); +} + +TEST(MumblingBitmapTest, MixedContainers) { + // Same layout as SPARSE_DENSE_SPARSE in Java's TestMumblingBitmap: every + // other byte of the first 8 bytes of the dense container is 0xFF. + Bytes dense(32, 0); + dense[1] = dense[3] = dense[5] = dense[7] = 0xFF; + const Bytes bitmap_bytes = BuildBitmap({{0}, dense, {}, {0x01, 0xFF}, {}}); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(bitmap_bytes)); + + std::vector expected = {0}; + for (int64_t byte : {1, 3, 5, 7}) { + auto range = Range(256 + byte * 8, 256 + byte * 8 + 8); + expected.insert(expected.end(), range.begin(), range.end()); + } + expected.push_back(3 * 256 + 1); + expected.push_back(3 * 256 + 255); + ExpectPositions(bitmap, expected); +} + +TEST(MumblingBitmapTest, PforEncodedDescriptors) { + // Descriptors {0, 0, 5, 0, 32, 0, 1}, encoded with every primary bit width. + std::vector dense_positions; + for (int i = 0; i < 40; ++i) { + dense_positions.push_back(i * 6); + } + std::vector containers = {{}, {}, {1, 2, 3, 4, 200}, {}, Dense(dense_positions), + {}, {9}}; + std::vector expected = {512 + 1, 512 + 2, 512 + 3, 512 + 4, 512 + 200}; + for (int64_t i = 0; i < 40; ++i) { + expected.push_back(1024 + i * 6); + } + expected.push_back(6 * 256 + 9); + for (int b1 = 0; b1 <= 8; ++b1) { + const Bytes bitmap_bytes = BuildBitmap(containers, b1); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(bitmap_bytes)); + ExpectPositions(bitmap, expected); + } +} + +// The spec says readers must ignore the low bits of a dense descriptor. Note: +// the Java reader currently rejects such descriptors. +TEST(MumblingBitmapTest, DenseDescriptorIgnoresLowBits) { + Bytes bytes = Header(256, 1); + bytes.insert(bytes.end(), {0x08, 0x00, 0x00, 0x21}); + bytes.insert(bytes.end(), 32, 0xFF); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(bytes)); + ExpectPositions(bitmap, Range(0, 256)); +} + +TEST(MumblingBitmapTest, MaximumSize) { + // 8192 dense containers with every bit set. Descriptors are 32 PFOR chunks of + // 256 values, each encoded as `00 00 20`. + Bytes bytes = + Header(MumblingBitmapView::kMaxCardinality, MumblingBitmapView::kMaxContainerCount); + for (int chunk = 0; chunk < 32; ++chunk) { + bytes.insert(bytes.end(), {0x00, 0x00, 0x20}); + } + bytes.insert(bytes.end(), size_t{MumblingBitmapView::kMaxContainerCount} * 32, 0xFF); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(bytes)); + EXPECT_EQ(bitmap.Cardinality(), 2'097'152); + EXPECT_TRUE(bitmap.Contains(0)); + EXPECT_TRUE(bitmap.Contains(2'097'151)); + EXPECT_FALSE(bitmap.Contains(2'097'152)); + int64_t set = 0; + for (int64_t pos = 0; pos < MumblingBitmapView::kMaxCardinality; ++pos) { + set += bitmap.Contains(pos) ? 1 : 0; + } + EXPECT_EQ(set, MumblingBitmapView::kMaxCardinality); +} + +TEST(MumblingBitmapTest, OutOfRangePositions) { + const Bytes bitmap_bytes = BuildBitmap({{0, 255}}); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(bitmap_bytes)); + EXPECT_FALSE(bitmap.Contains(-1)); + EXPECT_FALSE(bitmap.Contains(std::numeric_limits::min())); + EXPECT_FALSE(bitmap.Contains(256)); + EXPECT_FALSE(bitmap.Contains(std::numeric_limits::max())); + EXPECT_TRUE(bitmap.Contains(255)); +} + +TEST(MumblingBitmapTest, RandomRoundTrip) { + std::mt19937 rng(7); + for (int iteration = 0; iteration < 20; ++iteration) { + const int container_count = 1 + static_cast(rng() % 40); + std::set positions; + std::vector containers; + for (int c = 0; c < container_count; ++c) { + // Pick a density: empty, sparse, borderline or dense. + const int target = std::vector{0, 3, 31, 32, 100, 256}[rng() % 6]; + std::set in_container; + while (static_cast(in_container.size()) < target) { + in_container.insert(static_cast(rng() % 256)); + } + for (int pos : in_container) { + positions.insert(int64_t{c} * 256 + pos); + } + std::vector sorted(in_container.begin(), in_container.end()); + if (sorted.size() < 32) { + containers.emplace_back(sorted.begin(), sorted.end()); + } else { + containers.push_back(Dense(sorted)); + } + } + const int b1 = static_cast(rng() % 9); + const Bytes bitmap_bytes = BuildBitmap(containers, b1); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(bitmap_bytes)); + ExpectPositions(bitmap, std::vector(positions.begin(), positions.end())); + } +} + +TEST(MumblingBitmapTest, InvalidHeader) { + EXPECT_THAT(MumblingBitmapView::Make(Bytes{}), + HasErrorMessage("Truncated Mumbling bitmap header")); + EXPECT_THAT(MumblingBitmapView::Make(Bytes{0x01, 0, 0, 0, 0}), + HasErrorMessage("Truncated Mumbling bitmap header")); + + // Versions 0 and 2, as in Java's TestMumblingBitmap#testInvalidVersion. + EXPECT_THAT(MumblingBitmapView::Make(Bytes{0x00, 0, 0, 0, 0, 0}), + IsError(ErrorKind::kNotSupported)); + EXPECT_THAT(MumblingBitmapView::Make(Bytes{0x02, 0, 0, 0, 0, 0}), + HasErrorMessage("Unsupported Mumbling bitmap version: 2")); + + // From Java's TestMumblingBitmap#testInvalidCardinality. + EXPECT_THAT(MumblingBitmapView::Make(Bytes{0x01, 0x01, 0x00, 0x20, 0x00, 0x00}), + HasErrorMessage("cardinality: 2097153")); + + EXPECT_THAT(MumblingBitmapView::Make(Header(0, 8193)), + HasErrorMessage("container count: 8193")); + EXPECT_THAT(MumblingBitmapView::Make(Bytes{0x01, 0, 0, 0, 0xFF, 0xFF}), + HasErrorMessage("container count: 65535")); +} + +TEST(MumblingBitmapTest, InvalidDescriptors) { + // Reserved descriptor bits, as in Java's TestMumblingBitmap#testInvalidDescriptor. + for (uint8_t descriptor : {0x40, 0x80, 0xC0, 0x5F}) { + Bytes bytes = Header(1, 1); + bytes.insert(bytes.end(), {0x08, 0x00, 0x00, descriptor}); + EXPECT_THAT(MumblingBitmapView::Make(bytes), HasErrorMessage("not sparse or dense")); + } + + // The descriptor array is truncated. + Bytes truncated = Header(0, 2); + truncated.insert(truncated.end(), {0x08, 0x00, 0x00, 0x00}); + EXPECT_THAT(MumblingBitmapView::Make(truncated), + HasErrorMessage("Truncated PFOR chunk")); + + // From Java's TestMumblingBitmap#testMaximumCardinalityAndContainers: the + // header is valid but no descriptors follow. + EXPECT_THAT(MumblingBitmapView::Make(Bytes{0x01, 0xFF, 0xFF, 0x1F, 0xFF, 0x1F}), + HasErrorMessage("Truncated PFOR chunk header")); +} + +TEST(MumblingBitmapTest, InvalidContainers) { + auto truncated = BuildBitmap({{1, 2, 3}, DenseRange(0, 64)}); + truncated.pop_back(); + EXPECT_THAT(MumblingBitmapView::Make(truncated), + HasErrorMessage("Truncated Mumbling bitmap containers: 34 bytes available, " + "35 required")); + + auto trailing = BuildBitmap({{1, 2, 3}}); + trailing.push_back(0x00); + EXPECT_THAT(MumblingBitmapView::Make(trailing), + HasErrorMessage("1 unexpected trailing bytes")); +} + +// Make() checks only what memory safety needs, like the Java reader. Input +// that breaks the spec's content rules is accepted and still gives defined +// answers. +TEST(MumblingBitmapTest, ContentRulesAreNotChecked) { + // The header cardinality is reported as stored. + const Bytes wrong_cardinality = BuildBitmap({{1, 2, 3}}, 8, 4); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap1, MumblingBitmapView::Make(wrong_cardinality)); + EXPECT_EQ(bitmap1.Cardinality(), 4); + EXPECT_TRUE(bitmap1.Contains(1)); + EXPECT_TRUE(bitmap1.Contains(3)); + EXPECT_FALSE(bitmap1.Contains(4)); + + // Unsorted and duplicate sparse positions: Contains() finds every stored + // position, including in a non-final container, which uses the SSE2 path + // on x86-64. + Bytes unsorted; + for (uint8_t i = 0; i < 31; ++i) { + unsorted.push_back(static_cast(250 - i * 8)); + } + const Bytes unsorted_bytes = BuildBitmap({unsorted, {5, 3, 3}}, 8, 34); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap2, MumblingBitmapView::Make(unsorted_bytes)); + for (uint8_t value : unsorted) { + EXPECT_TRUE(bitmap2.Contains(value)) << int{value}; + } + EXPECT_FALSE(bitmap2.Contains(251)); + EXPECT_TRUE(bitmap2.Contains(256 + 3)); + EXPECT_TRUE(bitmap2.Contains(256 + 5)); + EXPECT_FALSE(bitmap2.Contains(256 + 4)); + + // A dense container with fewer than 32 positions. + const Bytes sparse_dense = BuildBitmap({DenseRange(0, 3)}); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap3, MumblingBitmapView::Make(sparse_dense)); + ExpectPositions(bitmap3, {0, 1, 2}); +} + +// The view reads the caller's buffer instead of copying it. +TEST(MumblingBitmapTest, ViewDoesNotCopy) { + Bytes bytes = BuildBitmap({{1, 2, 3}}); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(bytes)); + EXPECT_FALSE(bitmap.Contains(4)); + bytes.back() = 4; // the container {1, 2, 3} becomes {1, 2, 4} + EXPECT_TRUE(bitmap.Contains(4)); + EXPECT_FALSE(bitmap.Contains(3)); +} + +// The fixtures in resources/mumbling were produced by the Java implementation +// (org.apache.iceberg.mumbling) and are copied from apache/iceberg-go#1750 +// (mumbling/testdata at e305815). +TEST(PforDecodeTest, JavaFixtures) { + auto fixtures = ReadFixtures("mumbling/pfor-java-fixtures.tsv", /*hex_column=*/1); + ASSERT_EQ(fixtures.size(), 130); + for (const auto& fixture : fixtures) { + SCOPED_TRACE("line " + std::to_string(fixture.line)); + std::vector expected(fixture.values.begin(), fixture.values.end()); + ICEBERG_UNWRAP_OR_FAIL( + auto decoded, DecodePfor(fixture.bytes, expected.size(), fixture.bytes.size())); + EXPECT_EQ(decoded, expected); + // The test encoder chooses bit widths like the Java encoder, so the + // benchmark inputs match what Java writes. + EXPECT_EQ(EncodePfor(expected, kBestWidth), fixture.bytes); + } +} + +TEST(MumblingBitmapTest, JavaFixtures) { + auto fixtures = ReadFixtures("mumbling/bitmap-java-fixtures.tsv", /*hex_column=*/0); + ASSERT_EQ(fixtures.size(), 6); + for (const auto& fixture : fixtures) { + SCOPED_TRACE("line " + std::to_string(fixture.line)); + ICEBERG_UNWRAP_OR_FAIL(auto bitmap, MumblingBitmapView::Make(fixture.bytes)); + ExpectPositions(bitmap, fixture.values); + } +} + +} // namespace iceberg diff --git a/src/iceberg/test/mumbling_bitmap_test_util.h b/src/iceberg/test/mumbling_bitmap_test_util.h new file mode 100644 index 0000000000..6637b9e760 --- /dev/null +++ b/src/iceberg/test/mumbling_bitmap_test_util.h @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#pragma once + +/// \file iceberg/test/mumbling_bitmap_test_util.h +/// Helpers that serialize Mumbling bitmaps for tests and benchmarks. The +/// library only has a reader, so these follow the spec directly. + +#include +#include +#include +#include +#include +#include +#include + +#include "iceberg/util/mumbling_bitmap_internal.h" + +namespace iceberg::mumbling_test { + +using Bytes = std::vector; + +// Packs `width`-bit values most significant bit first, padded to a byte. +inline Bytes PackBits(const std::vector& values, int width) { + Bytes out((values.size() * width + 7) / 8, 0); + size_t bit_pos = 0; + for (uint8_t value : values) { + for (int bit = width - 1; bit >= 0; --bit, ++bit_pos) { + if ((value >> bit) & 1) { + out[bit_pos / 8] |= static_cast(0x80 >> (bit_pos % 8)); + } + } + } + return out; +} + +// Encodes one PFOR chunk with the given primary bit width `b1` and base `m`, +// following Appendix A of the spec +// (https://github.com/apache/iceberg/blob/main/format/mumbling-spec.md#appendix-a-pfor-encoding-for-unsigned-bytes). +// Exceptions are the values that do not fit in `b1` bits after subtracting `m`. +inline Bytes EncodePforChunk(const std::vector& values, int b1, uint8_t m) { + std::vector primary; + std::vector offsets; + std::vector exceptions; + int max_width = 0; + for (uint8_t value : values) { + max_width = std::max( + max_width, static_cast(std::bit_width(static_cast(value - m)))); + } + const int b2 = std::max(0, max_width - b1); + for (size_t i = 0; i < values.size(); ++i) { + const auto normalized = static_cast(values[i] - m); + primary.push_back(b1 == 8 ? normalized + : static_cast(normalized & ((1 << b1) - 1))); + if (b1 < 8 && (normalized >> b1) != 0) { + offsets.push_back(static_cast(i)); + exceptions.push_back(static_cast(normalized >> b1)); + } + } + Bytes out = {static_cast((b2 << 4) | b1), static_cast(offsets.size()), + m}; + auto packed = PackBits(primary, b1); + out.insert(out.end(), packed.begin(), packed.end()); + out.insert(out.end(), offsets.begin(), offsets.end()); + packed = PackBits(exceptions, b2); + out.insert(out.end(), packed.begin(), packed.end()); + return out; +} + +// Passed as `b1` to choose the primary bit width the way the spec recommends. +constexpr int kBestWidth = -1; + +// Returns the primary bit width that minimizes the encoded size of a chunk, as +// recommended by the spec. Ties go to the larger width, as in the Java encoder. +inline int ChooseBitWidth(const std::vector& chunk, uint8_t m) { + int max_width = 0; + for (uint8_t value : chunk) { + max_width = std::max( + max_width, static_cast(std::bit_width(static_cast(value - m)))); + } + int best_width = 0; + size_t best_size = std::numeric_limits::max(); + for (int width = 0; width <= max_width; ++width) { + size_t exceptions = 0; + for (uint8_t value : chunk) { + exceptions += (static_cast(value - m) >> width) != 0 ? 1 : 0; + } + const size_t b2 = max_width - width; + const size_t size = + (chunk.size() * width + 7) / 8 + exceptions + (exceptions * b2 + 7) / 8; + if (size <= best_size) { + best_size = size; + best_width = width; + } + } + return best_width; +} + +// Encodes a whole value array as PFOR chunks of 256 values each, using primary +// bit width `b1` (0-8) or kBestWidth. +inline Bytes EncodePfor(const std::vector& values, int b1 = 8) { + Bytes out; + for (size_t start = 0; start < values.size(); start += 256) { + std::vector chunk( + values.begin() + start, + values.begin() + std::min(values.size(), start + size_t{256})); + const uint8_t min = *std::ranges::min_element(chunk); + const int width = b1 == kBestWidth ? ChooseBitWidth(chunk, min) : b1; + // With 8-bit values the spec recommends storing the original values (m = 0). + auto encoded = EncodePforChunk(chunk, width, width == 8 ? 0 : min); + out.insert(out.end(), encoded.begin(), encoded.end()); + } + return out; +} + +inline Bytes Header(int64_t cardinality, int32_t container_count) { + return {MumblingBitmapView::kVersion, + static_cast(cardinality & 0xFF), + static_cast((cardinality >> 8) & 0xFF), + static_cast((cardinality >> 16) & 0xFF), + static_cast(container_count & 0xFF), + static_cast((container_count >> 8) & 0xFF)}; +} + +inline int64_t ContainerCardinality(const Bytes& container) { + if (container.size() < 32) { + return static_cast(container.size()); + } + int64_t count = 0; + for (uint8_t b : container) { + count += std::popcount(b); + } + return count; +} + +// Serializes a bitmap from containers: containers with fewer than 32 bytes are +// sparse and 32-byte containers are dense. +inline Bytes BuildBitmap(const std::vector& containers, int b1 = 8, + std::optional cardinality = std::nullopt) { + std::vector descriptors; + int64_t actual_cardinality = 0; + for (const auto& container : containers) { + descriptors.push_back(static_cast(container.size())); + actual_cardinality += ContainerCardinality(container); + } + Bytes out = Header(cardinality.value_or(actual_cardinality), + static_cast(containers.size())); + auto encoded = EncodePfor(descriptors, b1); + out.insert(out.end(), encoded.begin(), encoded.end()); + for (const auto& container : containers) { + out.insert(out.end(), container.begin(), container.end()); + } + return out; +} + +// Builds a dense container with the given positions set. +inline Bytes Dense(const std::vector& positions) { + Bytes container(32, 0); + for (int pos : positions) { + container[pos / 8] |= static_cast(0x80 >> (pos % 8)); + } + return container; +} + +// Builds a dense container with positions [start, end) set. +inline Bytes DenseRange(int start, int end) { + std::vector positions; + for (int i = start; i < end; ++i) { + positions.push_back(i); + } + return Dense(positions); +} + +// Serializes a bitmap from sorted, unique positions in [0, 2,097,152), with +// descriptors encoded using `b1` (0-8 or kBestWidth). +inline Bytes BuildBitmapFromPositions(const std::vector& positions, + int b1 = kBestWidth) { + std::vector containers; + if (!positions.empty()) { + containers.resize(static_cast(positions.back() / 256) + 1); + } + for (int64_t pos : positions) { + containers[pos / 256].push_back(static_cast(pos % 256)); + } + for (auto& container : containers) { + if (container.size() >= 32) { + std::vector in_container(container.begin(), container.end()); + container = Dense(in_container); + } + } + return BuildBitmap(containers, b1); +} + +} // namespace iceberg::mumbling_test diff --git a/src/iceberg/test/resources/mumbling/bitmap-java-fixtures.tsv b/src/iceberg/test/resources/mumbling/bitmap-java-fixtures.tsv new file mode 100644 index 0000000000..5af4471939 --- /dev/null +++ b/src/iceberg/test/resources/mumbling/bitmap-java-fixtures.tsv @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +0103000001000000030022ff 0,34,255 +012000000100000020ffffffff00000000000000000000000000000000000000000000000000000000 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 +010200000300010000a0050a 5,522 +012200000200050002f000ffffffff0000000000000000000000000000000000000000000000000000000064c8 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,356,456 +010001000100000020ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 +014000004000000001000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f 0,257,514,771,1028,1285,1542,1799,2056,2313,2570,2827,3084,3341,3598,3855,4112,4369,4626,4883,5140,5397,5654,5911,6168,6425,6682,6939,7196,7453,7710,7967,8224,8481,8738,8995,9252,9509,9766,10023,10280,10537,10794,11051,11308,11565,11822,12079,12336,12593,12850,13107,13364,13621,13878,14135,14392,14649,14906,15163,15420,15677,15934,16191 diff --git a/src/iceberg/test/resources/mumbling/pfor-java-fixtures.tsv b/src/iceberg/test/resources/mumbling/pfor-java-fixtures.tsv new file mode 100644 index 0000000000..d38aa98658 --- /dev/null +++ b/src/iceberg/test/resources/mumbling/pfor-java-fixtures.tsv @@ -0,0 +1,146 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +0 000000 +0 000000 +2 000002 +1 000001 +6 000006 +56 000038 +232 0000e8 +0 000000 +3 000003 +1 000001 +0,0,0 000000 +1,0,1 010000a0 +1,1,0 010000c0 +0,6,5 0300001a80 +4,15,11 0400040b70 +26,43,3 0600035e8000 +188,227,203 0600bc0273c0 +2,2,2 000002 +2,2,2 000002 +2,2,3 01000220 +0,0,0,0 000000 +0,0,0,1 01000010 +0,2,0,1 02000021 +3,4,0,7 0300007070 +12,9,2,9 040002a707 +1,59,38,4 06000103a943 +122,70,167,129 07004668030bb0 +0,2,0,2 02000022 +1,0,2,1 02000049 +247,2,3,1 6201019800f4 +0,0,0,0,0,0,0 000000 +0,0,0,1,1,0,1 0100001a +2,0,1,0,1,2,2 0200008468 +2,0,7,5,6,4,5 03000043dd28 +14,2,2,0,12,10,4 040000e220ca40 +43,39,14,2,13,31,46 060002a653002ddb00 +255,96,35,30,246,138,111 080000ff60231ef68a6f +3,3,1,1,2,1,2 020001a044 +1,1,0,3,1,32,2 42010053480580 +3,3,216,2,167,1,209 0800000303d802a701d1 +0,0,0,0,0,0,0,0 000000 +1,0,1,1,0,0,0,1 010000b1 +3,2,1,2,1,2,1,2 0200019111 +0,6,1,2,5,5,7,5 03000018ab7d +5,8,12,8,6,11,14,10 04000503731695 +33,11,11,11,37,36,4,48 0600047471c786002c +184,217,92,22,93,40,177,74 080000b8d95c165d28b14a +0,2,1,1,2,3,1,1 02000025b5 +0,2,1,0,3,2,1,1 02000024e5 +13,3,2,2,2,22,145,2 35010258400051e00680 +0,0,0,0,0,0,0,0,0 000000 +0,1,1,1,1,1,1,1,1 0100007f80 +3,1,0,3,2,0,2,0,3 020000d388c0 +6,4,7,3,4,2,1,2,3 030001af264140 +3,3,10,9,13,13,14,1,13 0400012298ccd0c0 +47,16,34,3,32,14,14,54,28 060003b0d7c074b2f364 +83,252,142,51,162,173,201,86,85 08000053fc8e33a2adc95655 +2,2,3,3,2,0,2,1,225 620100af894008e0 +2,2,0,2,3,3,3,3,1 020000a2ff40 +39,3,3,3,118,3,2,0,0 430200edbcd00000044e +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 000000 +1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,0,1,0,0,1,1,1,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,0,0,1,0,1,0,1,0,0,1 010000916c4e36ff1520 +2,0,1,0,1,2,3,3,3,1,2,1,1,3,2,1,3,0,2,0,1,1,1,3,1,1,0,2,3,1,0,3,0,3,0,1,3,1,1,1,3,3,1,0,0,2,3,1,1,1,0 020000846fd979c85752d331d5f42d50 +0,2,5,1,5,4,4,4,3,3,0,6,5,5,1,3,6,2,5,4,4,2,3,2,7,2,1,4,5,4,2,6,6,4,0,3,6,7,5,1,2,3,7,7,3,4,2,4,5,6,2 0300000a9b246c6b4bcac89ae8cb16d03de94ff714b900 +15,10,14,13,8,6,13,12,3,14,10,7,1,14,13,4,8,4,15,9,15,6,0,0,2,8,14,2,11,14,11,4,4,3,2,0,14,9,4,15,6,15,0,7,13,12,9,6,3,6,3 040000faed86dc3ea71ed484f9f60028e2beb44320e94f6f07dc963630 +61,19,10,24,6,5,21,40,14,38,7,60,63,4,49,53,59,13,38,58,4,12,58,22,53,45,12,52,44,27,7,25,63,23,45,13,50,17,23,6,24,11,40,4,0,48,52,53,17,59,11 060000f532981855683a61fcfc4c75ecd9ba10ce96d6d334b1b1d9fd7b4dc915c660ba04030d3547b2c0 +106,202,74,117,151,161,54,252,70,104,177,73,11,173,214,104,45,195,90,193,255,21,194,173,52,50,50,107,121,151,50,10,109,209,5,174,148,31,176,126,119,33,53,139,166,217,233,148,91,86,163 0800006aca4a7597a136fc4668b1490badd6682dc35ac1ff15c2ad3432326b7997320a6dd105ae941fb07e7721358ba6d9e9945b56a3 +3,0,2,2,0,1,2,0,209,2,2,3,1,1,2,1,0,0,2,1,2,1,0,0,1,1,0,2,1,1,3,2,2,3,3,3,2,2,1,0,1,1,3,1,3,2,3,1,0,0,40 620200ca186b590990525ebfa45ded000832d0a0 +2,3,3,3,1,3,0,3,0,1,1,0,3,0,0,3,205,2,2,2,1,0,1,2,3,0,1,2,1,0,0,2,3,3,3,1,0,2,3,231,1,2,2,3,3,1,0,3,2,1,1 620200bf7314c36a46c642fd2f6bd3941027cf90 +0,179,27,1,1,0,3,2,142,29,2,3,12,1,3,2,159,2,3,245,2,1,1,3,3,238,122,3,176,0,1,81,2,134,3,2,3,3,2,1,217,3,0,79,1,24,2,1,2,2,1 620f003d4e9b1eed97eb05aef97349a4010208090c1013191a1c1f21282b2db068c70e7f7b7ac521d93180 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 000000 +0,1,0,0,0,0,1,0,1,0,1,1,0,0,1,0,1,0,1,0,0,1,0,1,0,0,1,0,1,1,0,1,0,1,1,1,1,0,1,1,0,0,1,0,1,1,1,1,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,1,0,1,1,1,0,0,1,0,1,0,1,1,0,1,0,1,0,1,1,0,0,1,0,1,1,0,1,1,1,1 01000042b2a52d7b2f922da5cad596f0 +2,2,0,2,3,3,2,3,2,3,2,2,1,1,1,2,1,1,2,1,2,2,0,2,0,0,3,0,1,0,1,2,3,3,1,3,2,1,1,2,1,2,1,2,2,1,0,3,0,0,1,2,2,1,0,3,1,3,2,3,2,1,2,3,2,3,3,2,3,1,2,3,3,1,3,0,0,2,0,2,1,0,1,1,2,3,0,1,1,2,1,0,1,3,0,3,2,1,0,3 020000a2fbba5659a20c46f796669306937b9bbedbdc2245b1647393 +6,2,1,0,7,7,5,5,5,4,6,4,6,7,1,6,6,0,0,5,7,6,3,0,7,7,7,6,5,3,6,0,1,0,0,6,4,2,3,4,5,4,0,1,2,2,0,1,5,6,6,3,2,0,3,1,1,3,4,3,6,1,7,2,5,2,6,2,4,4,3,2,6,2,6,4,7,4,3,7,0,0,5,6,5,6,1,6,5,7,7,4,0,7,5,2,4,4,1,3 030000c88fedb34dcec05f98ffeaf020689cb01481bb34192e3c7aab291acb4f1f02eb8ebfc1ea90b0 +12,11,4,7,2,13,10,7,9,5,0,11,10,15,7,2,3,8,3,2,12,15,15,1,0,12,1,12,15,4,3,13,2,8,13,1,1,15,0,1,15,9,13,13,6,1,8,4,3,4,4,3,3,11,15,13,0,15,7,0,9,15,10,4,0,10,0,13,15,5,7,1,7,8,6,8,6,14,12,13,8,8,7,1,1,2,12,9,0,11,5,3,14,5,15,8,2,10,8,1 040000cb472da7950baf723832cff10c1cf43d28d11f01f9dd618434433bfd0f709fa40a0df57178686ecd887112c90b53e5f82a81 +45,34,50,39,14,48,58,6,14,15,31,43,1,18,52,19,39,61,54,48,27,35,60,36,42,51,8,31,12,36,13,42,27,48,10,3,24,13,18,24,40,18,0,28,18,5,53,5,34,16,7,0,52,24,26,20,5,28,34,48,10,26,26,35,6,23,38,6,32,3,48,39,34,25,22,46,59,10,34,12,37,3,20,39,14,12,27,38,42,49,62,3,12,31,23,31,56,46,62,14 060000b62ca73b0e8638f7eb052d139fddb06e3f24ab321f32436a6f028360d498a1201c485d458901c0d1869415c8b029a6a3197986803c278995aeeca88c94352738c6e6ab1f8331f5dfe2ef8e +51,0,25,64,149,21,32,29,36,212,134,253,135,152,93,216,203,187,234,119,116,159,100,53,94,123,182,9,59,63,48,210,145,228,218,142,196,32,11,142,199,225,233,43,178,23,137,86,32,182,236,228,114,57,46,144,253,156,56,153,194,125,79,140,64,7,252,225,206,65,64,116,241,46,249,148,18,164,189,7,63,233,58,194,220,20,224,122,187,123,91,191,227,151,41,90,140,199,131,208 080000330019409515201d24d486fd87985dd8cbbbea77749f64355e7bb6093b3f30d291e4da8ec4200b8ec7e1e92bb217895620b6ece472392e90fd9c3899c27d4f8c4007fce1ce414074f12ef99412a4bd073fe93ac2dc14e07abb7b5bbfe397295a8cc783d0 +3,0,0,2,3,3,3,3,0,1,3,1,2,2,3,3,1,1,0,1,3,2,1,1,3,0,2,3,3,3,0,2,1,2,3,3,0,0,2,0,1,2,1,1,2,0,0,1,2,0,2,1,0,0,3,2,3,2,0,1,1,0,0,3,0,0,0,2,0,0,2,0,1,3,0,1,1,2,0,0,1,2,1,0,0,0,0,2,1,2,0,1,0,0,0,3,2,3,1,1 020000c2ff1daf51e5cbf26f086581890ee1430208716064026103b5 +1,2,2,1,3,1,2,0,1,1,2,77,3,3,3,1,0,2,3,0,0,3,1,3,1,1,1,1,0,2,2,3,2,2,1,0,3,0,2,0,2,0,1,247,0,76,1,0,3,2,3,3,0,1,2,3,1,1,2,3,2,0,2,28,0,0,2,1,0,0,2,0,2,0,2,0,2,3,2,2,0,0,3,2,3,0,1,1,0,3,0,1,0,1,0,1,0,2,3,2 62040069d859fd2c37552ba4c88704ef1b5b88090888ba0ec531112e0b2b2d3f4fd4c7 +2,0,3,0,1,0,2,1,1,1,91,3,1,0,2,1,2,127,3,2,1,2,3,65,2,0,3,0,1,131,140,3,0,1,1,1,2,0,0,1,3,0,0,1,203,4,86,0,48,3,152,3,109,0,95,0,3,0,3,2,2,3,1,1,1,1,0,2,2,2,0,2,181,0,2,3,0,113,2,0,3,0,3,37,2,3,1,0,122,3,1,2,191,0,3,2,3,0,1,2 6211008c495f49be6d8c731581c1c8334cceb552a24b18cdb4b6cec60a11171d1e2c2d2e30323436484d53585c59f4208f20553266d7b5c25ebc +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 000000 +0,0,0,1,1,0,1,1,0,1,1,1,1,1,0,1,0,1,1,0,1,1,1,1,0,1,1,0,0,1,1,1,1,1,0,1,0,1,0,1,1,0,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,1,0,1,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1,1,0,1,1,1,0,0,1,0,1,1,1,0,1,0,1,1,1,0,0,1,0,0,1,0,1,0,1,1,0,0,0,1,0,0,1,1,1,0,1,1,1,1,0,0,0,0,1,0,0,1,0,0,1,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,0,1,1,1,0,1,1,0,1,1,1,0,0,0,1,0,1,1,0,0,1,1,1,0,0,1,0,1,0,0,0,0,1,0,1,0,1,1,0,0,0,0,0,0,0,0,1,0,1,0,1 0100001b7d6f67d5b5f5fb61225c003408c6e5d7256277849abfe0e5076e2ce50ac02a +0,3,3,1,0,2,1,1,0,3,3,2,0,1,0,3,2,3,2,1,3,1,2,0,2,0,0,1,0,3,2,0,2,3,2,3,0,2,2,3,0,2,3,1,2,3,0,1,0,2,2,1,1,3,0,0,2,0,2,2,0,1,0,0,1,3,2,2,0,2,2,3,0,1,3,2,3,1,2,0,1,3,0,3,1,3,2,3,3,3,1,2,0,2,2,1,2,3,2,2,0,2,1,1,2,1,2,0,0,0,0,0,2,0,3,1,2,3,1,1,1,3,1,1,2,2,3,1,3,0,2,0,3,2,2,2,0,3,0,1,1,1,1,1,1,1,0,0,3,3,1,0,1,1,3,3,0,3,1,3,2,0,2,3,2,1,3,3,2,1,1,2,2,1,1,2,3,1,3,2,1,1,0,1,3,3,2,2,0,3,1,0,3,1,0,2,3,1,2,0,1,0,2,2,1,2,3,3,2,0,2,3,0,2,1,0,2,3,2,2,2,0,3,0,2,0,2,1,2,3,1,2,1,3,1,1,0,1,1,2,1,2,3,3,1,2,3,0,3,3,0,3,2,3,3 0200003d253e13b9d88138bb2b2db129708a107a2b1ed8737bf629ba2598008db575adc8ea315550f45f378b9f9696de51fa34d2d84a6f8b24ba8c89b675166f6cf3bc +2,0,7,3,5,3,1,7,3,5,0,6,4,6,4,2,5,3,5,7,6,4,3,6,6,2,2,5,2,2,0,6,0,3,3,2,1,1,7,3,6,4,3,4,5,5,6,7,7,5,7,3,6,7,5,3,5,2,3,5,5,6,5,6,3,3,6,6,5,4,1,1,4,0,4,6,0,6,6,4,7,2,3,7,2,4,6,7,1,6,7,0,2,6,5,7,7,4,0,1,5,1,5,5,0,6,5,4,7,3,5,3,1,3,1,6,6,1,4,0,1,1,7,4,4,4,4,0,2,0,3,6,3,3,3,0,5,0,2,2,5,7,7,5,0,5,5,5,0,4,0,0,1,7,0,5,5,7,0,2,3,6,0,1,7,7,4,1,0,4,7,4,0,0,3,5,5,4,6,6,3,2,7,3,1,6,0,0,6,1,7,4,1,2,3,5,5,4,3,3,4,1,1,0,3,1,1,6,4,1,5,6,6,3,1,6,0,0,5,6,7,3,1,3,6,5,7,0,3,6,4,7,6,3,7,0,7,4,5,5,5,7,6,6,4,0,7,4,5,0,2,1,3,5,5 03000043bacf7469a2aefd1ec954860da27bd1cb77f7bdeba9dbae6f6b098261b4e9f5373b85aff01a6d1aceeb2cec6027c92041e6d8a12bfd16d1003c5bc2781fe113c01db366bb380c7c29db1b84864e86ecce02eecbd787a7cf8f2dbf683ca11768 +6,10,6,7,2,6,11,11,15,1,12,6,11,10,9,1,10,10,5,10,5,12,1,5,5,12,10,10,12,3,14,0,0,2,11,2,14,15,5,13,0,1,8,6,0,15,15,7,4,13,10,14,7,15,5,3,1,10,1,2,11,3,4,15,3,1,14,15,1,6,15,15,0,1,4,14,4,10,1,7,2,7,12,12,10,0,10,13,1,9,4,8,10,8,0,9,3,13,3,6,3,6,8,0,10,11,9,1,10,9,13,5,9,14,14,8,14,12,14,1,6,1,6,8,3,10,7,9,2,14,11,12,1,8,14,10,13,6,1,7,1,7,8,6,9,0,7,11,13,15,1,2,12,6,10,4,13,14,11,4,2,1,13,7,3,0,2,15,0,10,10,1,3,3,4,10,15,13,8,8,5,5,5,9,12,9,11,7,9,12,3,5,8,9,6,11,14,6,1,4,2,9,15,13,14,5,13,1,2,11,6,9,2,13,15,8,9,8,10,3,13,7,4,5,6,1,3,6,11,14,0,15,7,11,0,10,1,12,2,6,13,11,8,5,5,6,3,2,2,0,2,7,9,4,9 0400006a6726bbf1c6ba91aa5a5c155caac3e002b2ef5d01860ff74dae7f531a12b34f31ef16ff014e4a1727cca0ad1948a8093d363680ab91a9d59ee8ece161683a792ebc18ead6171786907bdf12c6a4deb421d7302f0aa1334afd885559c9b79c35896be61429fde5d12b692df898a3d7456136be0f7b0a1c26db85563220279490 +33,53,31,14,7,48,19,54,41,14,49,12,22,30,52,46,56,3,57,2,38,53,53,17,33,19,51,31,5,25,25,2,54,6,46,34,35,26,46,42,19,8,30,17,57,10,62,0,50,28,35,16,36,1,48,32,21,62,3,51,14,62,6,14,15,28,17,52,61,54,48,17,34,50,11,54,14,51,1,23,42,26,9,56,23,9,3,33,60,26,27,52,19,21,61,19,36,12,35,4,10,33,44,29,4,30,46,53,19,24,39,46,60,11,41,15,50,4,59,22,59,39,34,3,33,31,63,35,24,19,22,42,55,11,61,33,24,32,1,19,30,55,4,21,15,51,38,11,31,37,0,1,13,36,20,41,59,10,59,21,40,45,52,10,15,52,49,58,59,32,59,30,61,51,27,61,13,11,50,1,45,12,20,50,14,56,54,27,53,35,35,50,5,17,41,32,30,11,50,6,45,29,47,63,57,45,58,13,5,55,31,5,8,29,18,27,22,10,22,45,3,16,2,47,41,35,56,4,35,17,12,37,30,39,5,19,18,34,36,14,34,1,25,24,29,47,30,20,35,49,27,15,20,31,24 0600008757ce1f04f6a4ec4c59ed2ee03e429b5d51853cdf159642d86ba28dabaa4c8791e4af80c9c8d0901c2057e0f33be18e3dc474f76c118b22f63b3057a9a2785c90e1f1a6f44d5f5390c8c42a1b1d11ebb54d89eef0ba4fc84ed6ee788385ffe36135aadcbf616200537b71153f398b7e5001364529ecaed5a2dd0a3f4c7aee0edef736fd34bc81b4c5323b8d9bd638f2151a6078bc86b5dbffe6de8d1777c521d49b58a5ad0d00afa63e048d13257a71534a290e88165876f7948f16cf51f600 +137,124,15,177,14,46,87,192,2,26,29,101,108,133,169,80,164,119,34,2,217,15,160,120,146,55,85,128,7,124,26,218,11,6,119,241,182,220,234,37,137,192,148,190,204,246,51,52,231,8,199,235,183,63,156,134,226,31,151,162,58,18,17,136,125,171,58,200,247,4,66,107,66,50,90,87,117,254,198,23,171,152,60,88,112,209,143,65,137,211,23,90,209,129,213,211,147,140,46,215,209,120,240,184,236,224,189,80,79,29,132,113,196,231,51,161,192,27,103,23,9,248,43,244,137,166,9,44,97,215,139,151,34,199,203,3,160,31,217,47,207,134,163,165,47,51,55,15,78,61,152,229,242,22,188,214,119,25,22,154,137,53,227,66,73,5,253,52,227,29,127,161,161,197,44,106,145,151,74,137,110,14,208,61,93,86,110,72,233,200,41,147,52,201,139,231,32,114,135,59,166,16,171,239,211,225,84,178,199,72,53,167,113,31,114,221,140,180,133,58,238,68,10,158,86,69,239,21,206,130,200,131,173,15,11,187,234,133,244,173,249,38,8,151,29,109,73,207,56,46,48,144,39,102,90 080000897c0fb10e2e57c0021a1d656c85a950a4772202d90fa07892375580077c1ada0b0677f1b6dcea2589c094beccf63334e708c7ebb73f9c86e21f97a23a1211887dab3ac8f704426b42325a5775fec617ab983c5870d18f4189d3175ad181d5d3938c2ed7d178f0b8ece0bd504f1d8471c4e733a1c01b671709f82bf489a6092c61d78b9722c7cb03a01fd92fcf86a3a52f33370f4e3d98e5f216bcd67719169a8935e3424905fd34e31d7fa1a1c52c6a91974a896e0ed03d5d566e48e9c8299334c98be72072873ba610abefd3e154b2c74835a7711f72dd8cb4853aee440a9e5645ef15ce82c883ad0f0bbbea85f4adf92608971d6d49cf382e309027665a +2,1,3,2,3,0,1,2,2,1,2,0,0,2,1,2,0,3,2,3,0,1,3,0,2,1,3,3,2,0,3,0,0,3,0,0,1,3,2,2,1,2,0,1,0,0,2,1,0,0,3,0,3,1,3,3,3,2,2,2,1,1,0,0,3,2,1,2,2,0,3,2,1,0,1,0,2,2,3,0,1,1,3,3,3,1,0,2,0,3,1,1,3,1,1,2,1,0,0,0,3,3,1,0,3,0,0,2,1,3,1,0,1,2,1,2,3,0,0,2,1,1,3,0,0,2,0,3,0,0,2,1,2,2,0,2,94,3,0,1,0,2,3,1,3,3,2,2,3,2,2,2,2,1,3,2,1,2,3,2,0,3,1,1,3,2,0,0,3,3,3,1,0,0,0,0,0,1,0,1,3,2,0,151,2,3,2,2,3,1,1,0,1,3,2,2,2,2,3,3,2,3,1,2,1,1,1,0,3,3,3,0,1,1,0,3,0,2,0,2,1,3,2,0,0,3,3,0,1,0,3,3,2,0,3,0,1,3,3,2,2,2,3,2,2,1,1,2,2,0,1,2,0,1,3 6202009ec698263b1c9f8c307a61090cdfea50e68e44ac5fd235d640f4c27466c25c2309a2b12dfaea9e6e35e0fd0011e3bad47aafb654fc5322783c4f8c7eae96861c88b75e50 +2,2,63,0,1,3,1,1,2,3,2,1,3,3,0,0,2,1,1,1,3,0,1,2,3,3,3,2,0,239,2,2,3,3,1,1,3,2,1,0,2,0,3,3,1,1,2,3,1,0,0,2,3,1,0,0,2,3,3,1,3,3,1,2,2,3,3,1,3,3,0,6,2,0,0,0,2,1,3,2,1,1,94,1,3,211,3,3,0,0,0,2,0,18,0,0,3,149,2,3,1,1,3,0,0,2,0,0,0,3,0,3,2,1,1,1,3,2,0,3,2,1,1,3,1,2,3,3,2,1,1,1,3,2,2,1,3,3,1,0,0,0,1,0,0,1,221,2,2,2,0,2,0,0,1,2,1,0,1,2,0,1,1,0,0,3,0,2,1,3,2,2,0,2,1,3,0,1,0,3,1,0,3,1,2,1,2,2,2,0,2,1,0,86,3,0,3,1,2,0,1,2,1,1,0,3,0,0,3,1,1,1,3,3,0,0,3,3,3,1,3,0,3,1,0,3,0,0,2,3,1,3,186,1,204,2,2,3,3,1,1,3,1,3,0,3,0,0,2,1,1,0,3,2,1 620b00ac75b9f095c6fe3af5e48f5b42d0bdf6bdf2809e59ff0220db5c203395e3976f95e9f40416a2064614327a27134d9a892cd86530d5f0fdcd30b792bd773094e4021d4752555d6192c1e8ea3fb057d0497756ecc0 +3,2,38,214,202,55,136,3,2,1,75,3,0,197,2,1,0,0,1,1,95,1,32,2,1,60,3,0,224,129,2,210,2,1,2,111,1,165,2,2,2,1,3,2,1,198,1,0,206,0,2,2,0,1,0,110,2,1,2,2,3,2,0,58,0,0,161,0,0,2,2,2,196,1,1,1,2,0,3,0,2,3,1,40,3,3,2,2,3,3,2,53,77,3,3,2,1,1,2,2,1,3,0,0,2,26,1,0,2,0,1,3,2,0,3,2,0,3,2,143,1,2,233,1,0,2,3,180,1,1,142,3,56,1,2,128,3,0,3,116,0,0,0,1,3,245,2,0,3,176,1,15,3,1,159,107,0,1,3,2,1,3,0,1,0,57,0,2,102,0,0,225,1,2,2,0,2,1,1,1,3,0,1,2,121,0,3,1,3,3,97,3,1,0,3,1,68,1,2,0,168,1,0,0,0,2,0,0,1,0,176,1,0,1,1,0,3,1,3,1,0,88,0,1,1,0,0,1,2,104,1,3,14,0,108,1,0,0,49,2,0,1,2,3,2,1,2,1,0,0,2,3,3,2,2 623200eab39f1905d24c1a9b5a9e648a129ae2042a158cb4faf97e5a70a4878e3b652c5b18cc01d8c7df1e7112816895c64df74d1810204114dd01418781061b990be802030405060a0d1416191c1d1f23252d30373f4248535b5c69777a7f8284878b9195979a9ba5a8abb8bec4c8d2dde5e8eaee275c8d892c5720fe20d1ba71cdb3a8c4a3531a3ead8ce81df6c0e768e67879846ab166836cc0 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 000000 +1,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,1,0,1,0,0,0,0,0,1,1,0,0,1,1,1,0,0,1,1,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,1,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,0,1,0,1,1,0,0,1,0,1,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,1,1,1,1,1,1,0,1,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,1,0,1,1,1,0,0,1,1,1,0,1,1,0,1,0,1,0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,0,0,0,1,1,0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0,1,0,1,1,1,1,0,0,1,1,0,1,1,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,1,1,1,1,0,1,1,1,0,1,0,0,0,1,0,1,1,1,0,0,0,0,1,1 010000a032a0ce605f1740e2cb2aae0482fd63fe2e76a626041637310bcdd9a6f745c3 +2,0,1,3,3,0,0,2,2,0,0,2,1,0,0,3,0,1,1,2,2,1,1,3,1,0,1,0,3,3,2,2,2,2,2,1,3,0,0,1,3,2,2,2,0,3,0,0,3,0,3,0,1,3,1,0,2,2,2,2,0,2,2,3,3,0,3,0,2,0,1,0,1,0,3,3,1,1,2,3,0,1,3,1,0,2,2,2,1,3,2,2,0,2,1,2,0,0,0,2,0,1,2,0,0,0,1,0,0,0,1,0,0,0,2,3,2,2,0,1,2,0,1,3,2,0,0,1,3,2,1,2,3,3,2,0,0,1,0,3,0,0,0,1,0,0,2,3,1,3,2,1,3,2,3,0,3,3,2,0,3,1,2,0,1,1,3,0,1,3,2,1,1,3,1,3,3,2,2,3,2,1,0,1,2,1,1,1,0,0,1,0,1,0,0,1,2,1,2,3,3,2,1,2,1,3,1,0,3,3,1,3,0,0,3,2,1,3,1,2,3,1,1,0,0,2,3,2,0,0,0,1,3,0,0,0,3,2,1,0,1,0,3,2,3,1,1,1,3,1,0,1,2,0,2,1 02000087c28243169744faa9c1ea30cc74aa2bcc844f5b1d2a7a26021804040ba18781e6f813010b79ecf8d85c7977eb919504419be674f70e76d42e01c0e44ed5d189 +3,7,6,0,7,5,4,1,1,2,5,0,6,7,5,3,3,0,1,4,2,5,2,7,6,4,4,4,2,0,2,7,4,2,6,4,0,1,7,3,7,4,0,7,3,0,5,0,0,6,3,3,0,1,2,3,3,7,0,2,3,7,1,7,4,1,0,2,7,1,3,1,4,4,2,5,6,2,0,3,6,5,4,0,1,5,4,6,2,5,7,6,3,1,1,0,7,3,6,2,1,0,3,4,5,5,4,1,1,4,7,6,1,0,1,6,4,5,7,5,1,5,2,1,7,2,4,2,4,7,5,2,5,6,6,4,4,4,5,4,1,4,3,3,0,1,3,3,4,2,6,1,1,5,3,6,5,4,1,1,6,3,5,7,6,2,4,5,1,1,4,0,1,0,3,4,2,0,5,1,1,4,3,0,4,0,2,6,1,5,4,6,4,3,5,3,0,4,7,0,4,2,0,7,0,4,6,2,3,3,0,4,6,7,6,1,2,4,3,0,7,6,2,7,0,0,3,0,3,2,1,4,1,0,2,4,5,6,6,7,2,1,0,0,5,6,6,3,1,7,3,6,2,5,0,7 0300007f0f612a8deb60c557d244178b407bf0762819b0537c27cf842e59915c83d6036657e648ef221cb6133e20e97d351ea29eabb492c31b05b8b135eb09cefca526021c4293188163668eb1388871326c4df1518f9701868c214bb7440bb33de547 +5,12,12,15,2,11,15,3,2,1,7,11,8,2,2,15,8,7,10,15,11,11,12,9,10,9,0,5,15,5,2,2,3,11,3,12,15,6,14,13,5,14,4,1,0,12,14,7,9,7,1,2,14,1,4,14,6,3,2,3,14,15,10,8,2,6,2,2,3,10,10,7,15,9,6,14,5,14,0,15,0,0,2,9,13,7,12,9,0,7,12,5,14,15,1,5,3,4,8,0,11,11,0,5,7,14,13,3,8,3,15,12,0,2,10,9,4,10,15,10,3,12,2,6,14,12,5,13,5,8,6,2,5,1,13,12,5,8,0,5,7,10,8,2,6,6,13,7,7,12,3,3,14,11,11,0,7,13,9,11,9,12,10,2,9,8,5,9,6,4,0,14,11,4,11,5,7,15,2,10,4,6,10,0,5,6,11,4,11,7,0,14,15,14,4,3,4,2,4,3,5,14,1,7,5,11,1,10,6,7,11,8,6,0,6,0,0,7,0,7,1,0,14,15,12,15,13,15,5,13,0,15,15,2,14,2,2,6,14,0,9,0,14,15,2,1,5,12,12,12,6,3,10,6,1,3 0400005ccf2bf3217b822f87afbbc9a905f5223b3cf6ed5e410ce79712e14e6323efa826223aa7f96e5e0f0029d7c907c5ef153480bb057ed383fc02a94afa3c26ec5d586251dc58057a8266d77c33ebb07d9b9ca29859640eb4b57f2a46a056b4b70efe4342435e175b1a67b86060070710efcfdf5d0ff2e226e090ef215ccc63a613 +10,14,0,7,16,42,6,4,49,6,51,5,16,40,43,16,43,20,60,42,42,26,50,17,27,3,38,8,29,28,60,8,57,8,20,23,13,24,14,20,26,8,28,12,59,21,56,3,47,38,20,13,34,41,40,8,25,29,14,31,13,34,36,54,5,30,21,55,21,23,1,3,53,41,10,10,47,19,56,40,22,19,27,48,42,41,3,3,54,19,34,35,9,47,40,22,33,31,8,13,60,12,12,53,6,12,9,13,55,2,47,55,34,37,4,40,15,45,3,17,37,14,42,1,19,45,36,34,54,8,33,43,56,52,12,35,35,48,36,14,20,0,47,23,61,38,53,25,16,39,46,58,4,62,15,0,31,37,5,28,12,23,16,21,47,31,40,7,5,11,26,54,35,12,26,59,45,49,49,55,58,40,31,54,42,27,4,13,6,3,24,4,55,39,46,9,18,11,2,47,5,53,30,49,5,59,32,55,19,41,43,40,46,22,58,41,49,17,63,38,6,13,24,25,21,1,33,62,45,45,19,35,21,30,51,58,19,46,16,55,31,46,47,39,34,29,32,0,12,55,44,13,29,40,52,3 06000028e00742a184c46cc5428ad0ad4f2aa9ac916c398875cf08e4851735839468870ced5e03be650d8a9a0865d39f36293615e577557043d6928abd3e285936f0aa90c3d938a326fa1685f20df0c33518c24ddc2bf78a51283ed0d194ea814ed922d8886be343238f090e500bd7f66d59427bba13e3c07e515c317415bdfa0714b6b68cc6bbb71c77ea87f6a9b10d183604de7b8948b0af1757b117b8374e9ae8b96ea9c51fe618d61954187eb6d4e355ecfa4ee4377eebe789d800337b0d768d03 +92,49,171,186,7,119,138,17,139,74,121,7,95,219,198,147,40,12,210,242,20,80,62,70,132,206,221,148,239,53,111,38,110,129,190,64,116,204,185,219,7,12,193,16,100,152,174,208,5,166,12,240,136,112,234,120,149,187,49,66,112,74,193,202,54,249,120,110,214,242,149,5,218,161,143,236,142,55,203,126,120,179,30,129,165,223,50,91,5,182,196,228,117,129,54,179,121,201,88,11,253,194,87,251,37,134,109,9,158,233,43,23,36,174,210,135,214,128,178,4,248,238,80,109,65,223,211,139,240,51,192,200,65,212,185,7,249,193,159,84,77,48,24,243,178,27,89,183,217,13,143,133,170,3,202,240,30,173,23,102,156,63,3,17,210,124,204,41,69,186,225,249,22,79,128,9,79,88,201,109,97,60,36,19,101,136,222,126,75,148,141,230,10,199,3,4,91,50,24,185,96,142,60,142,233,56,81,148,96,253,100,249,73,129,53,62,63,68,196,170,16,121,138,130,17,75,184,49,87,205,133,74,170,81,120,7,59,124,55,37,224,73,20,228,194,243,53,172,48,143,240,31,118,161,150,126 0800005c31abba07778a118b4a79075fdbc693280cd2f214503e4684cedd94ef356f266e81be4074ccb9db070cc1106498aed005a60cf08870ea7895bb3142704ac1ca36f9786ed6f29505daa18fec8e37cb7e78b31e81a5df325b05b6c4e4758136b379c9580bfdc257fb25866d099ee92b1724aed287d680b204f8ee506d41dfd38bf033c0c841d4b907f9c19f544d3018f3b21b59b7d90d8f85aa03caf01ead17669c3f0311d27ccc2945bae1f9164f80094f58c96d613c24136588de7e4b948de60ac703045b3218b9608e3c8ee938519460fd64f94981353e3f44c4aa10798a82114bb83157cd854aaa5178073b7c3725e04914e4c2f335ac308ff01f76a1967e +0,2,0,1,2,0,186,2,0,2,3,3,117,3,2,0,1,0,3,1,3,0,0,1,0,2,0,2,3,1,0,3,0,2,1,2,3,1,0,1,2,2,3,3,0,145,2,2,2,3,0,3,2,2,2,0,1,3,2,2,3,0,1,0,1,1,2,2,2,3,2,2,3,3,2,2,2,0,1,2,2,0,2,0,2,1,1,2,1,1,2,2,2,0,3,0,3,3,3,0,1,3,3,3,1,0,2,3,1,0,2,3,0,0,0,3,3,0,3,3,2,0,0,0,3,194,2,0,3,3,24,0,2,1,2,1,0,1,3,1,1,2,1,0,2,3,2,1,3,3,3,1,0,3,3,0,0,0,0,2,2,3,1,2,3,3,7,1,2,1,1,2,0,2,2,2,2,1,0,1,2,2,3,2,2,3,0,1,2,1,2,0,3,1,2,2,3,3,0,1,0,3,1,2,1,0,2,2,1,2,3,0,1,3,3,1,2,2,2,1,2,2,2,3,1,0,2,0,0,1,3,2,2,3,0,3,3,0,1,2,3,3,196,3,3,2,0,2,1,1,2,2,2,1,2,1 620700218a2f784dc122d326d1af1ab3a87ac45abafa8688965a8cfc7f4b4b03cf80e8f0991d64b9fd3c02b6fd962a91aeb198daf1364a6c7da9ab481eb3c6f3e25a99060c2d7d82a6f2b9d930181c40 +2,1,2,2,3,1,1,3,1,0,2,0,3,2,3,0,3,0,1,1,3,0,2,0,0,2,2,0,3,1,3,1,3,3,1,2,1,1,0,3,2,1,3,0,238,1,1,0,2,0,0,3,0,0,1,0,0,0,3,3,1,2,3,2,0,2,2,0,2,3,3,2,2,0,1,3,0,2,2,0,1,3,0,1,0,2,0,2,129,2,0,2,55,1,3,3,3,0,1,0,2,1,2,1,3,1,1,0,2,3,248,2,3,2,3,2,1,3,0,3,0,2,3,0,0,3,3,0,0,2,2,1,3,1,2,2,2,2,2,1,2,2,219,3,1,3,3,0,1,1,2,2,1,0,0,1,3,3,0,0,2,1,1,1,2,1,2,3,3,0,1,3,1,1,80,3,2,195,0,2,0,1,0,2,3,0,0,3,2,1,1,1,2,0,1,0,2,1,1,0,0,2,0,3,3,1,1,2,0,2,0,1,2,3,2,1,0,3,0,0,2,1,3,2,3,0,1,0,1,1,1,2,1,0,3,2,0,0,3,1,3,0,3,1,0,232,1,2,2,3,2,3,0,2,1,3 6208009ad748ecc5c828ddf6539c9483040f6e28be8728712262dfc499d4b2ee732c3c29daa9af7c5a41f0959bc753b212c395849423d621b9309ec4564e0dcd06bb272c585c6e8eaeb1f5ee037ed94c3a +2,1,3,3,2,110,3,2,0,0,1,0,1,1,133,3,2,3,3,1,2,3,0,1,3,1,2,0,2,1,42,3,146,2,2,1,0,196,1,2,2,0,2,0,3,1,3,2,85,0,1,0,0,1,2,115,0,0,3,2,0,0,3,53,1,0,0,223,3,1,1,196,0,129,38,3,2,0,2,1,3,0,3,98,1,0,0,3,1,146,2,1,254,182,2,1,236,0,3,0,3,1,1,102,1,1,2,1,2,2,247,3,3,2,2,2,3,82,250,2,47,1,1,112,2,53,3,1,2,247,0,2,1,154,3,2,1,3,3,50,3,1,255,2,1,2,162,2,1,189,0,1,0,1,94,2,3,170,1,2,0,0,238,64,138,2,100,122,166,3,1,2,91,0,2,1,148,0,0,3,3,1,3,98,1,3,3,3,205,0,1,3,217,118,3,0,0,3,0,3,0,2,1,0,2,42,0,1,1,86,0,222,3,1,36,1,1,1,1,2,81,2,3,2,2,2,142,14,49,1,64,123,3,0,2,3,0,1,0,3,2,2,0,2,1,0,20,0,3,240,3,171,226,0,4,1 623b009fae0457bdb1d89ba90688de441b0e0d43d41b89ce4369a90cd659afeaead49db26e7ede6a511ae608a2b6c903de7f476c3324a162d1566eaa53cb13a240cf81050e1e202530373f4347494a53595c5d60676e7576787b7d81858b8e92959a9da2a3a4a6a7a8acb0b7bcc0c1cdd1d3d6dce2e3e4e6e7f6f9fbfcfe6e12a4c5570ddf1809624feded9f54f8b70df6633fa2f5eaed08997a95a5633d9d295dc95230cc41e17cab8040 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 000000000000 +0,0,1,1,1,1,0,1,0,0,1,0,1,1,0,1,0,1,0,1,1,1,0,0,1,0,0,0,0,1,1,0,0,1,0,0,1,1,0,1,0,1,0,0,0,0,1,1,0,0,1,1,1,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,0,0,1,1,1,0,1,1,0,0,1,0,1,1,0,1,0,1,1,0,1,0,1,0,1,1,0,1,0,0,0,0,1,1,1,0,1,0,0,1,1,1,0,0,1,1,0,0,0,1,0,1,0,1,0,0,1,1,0,1,0,0,0,1,1,0,0,1,0,1,0,1,1,1,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,1,0,1,1,0,0,0,0,1,1,1,0,1,0,1,1,1,1,1,0,1,1,0,1,0,0,1,1,0,0,0,1,0,1,0,0,1,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,1,1,1,1,0,1,0 0100003d2d5c864d433a3ff3c90986d10765ad5a1d398a9a32bad2db0ebed314d5403d000000 +2,0,1,2,3,1,1,2,3,2,0,3,3,1,1,0,2,1,3,1,2,2,3,0,3,1,1,1,2,0,0,0,0,2,0,2,2,3,1,2,3,1,1,3,1,3,2,3,1,1,0,3,3,1,0,2,3,3,3,0,1,1,2,1,3,2,2,1,2,2,0,3,1,0,3,2,2,2,3,0,1,3,0,2,2,2,0,2,3,0,1,3,1,0,1,3,3,3,3,3,0,1,1,2,0,3,0,3,1,0,2,0,0,0,0,0,1,2,3,3,3,2,2,2,0,2,3,3,0,2,0,2,3,2,2,0,0,1,0,2,3,1,2,2,1,1,0,0,2,0,2,0,1,0,0,0,1,1,2,2,0,1,0,3,2,0,3,3,1,0,1,1,1,0,1,2,1,3,1,1,0,3,0,0,3,0,2,3,2,1,1,1,1,0,0,2,3,1,3,2,1,0,3,0,3,0,3,1,0,1,0,1,0,0,1,2,2,2,3,2,2,2,0,1,2,3,1,3,2,1,3,0,3,3,0,1,0,0,2,3,0,0,3,3,2,0,1,0,2,0,3,3,0,1,2,0,0 02000086d6e3d49dacd58022b6d77b53d2fc59e9a34eac72a2c747ff163348006fea2f22e812da5088405a138f45467530cb9542de4ccd1106aea1b79cf10b0f848f18000000 +6,4,3,7,5,6,0,2,5,5,7,6,7,1,6,7,6,4,2,1,2,7,0,5,0,7,4,3,7,5,7,5,0,2,4,4,4,3,5,7,5,2,6,6,4,7,1,7,2,5,0,1,5,1,3,5,4,3,7,3,5,4,0,4,4,3,1,6,3,3,4,4,2,7,1,0,0,3,5,6,4,6,4,1,5,2,5,6,2,3,3,5,6,3,7,0,0,2,7,7,1,7,3,5,5,7,1,3,5,3,5,4,2,1,3,1,6,6,1,4,1,5,1,2,3,3,3,2,3,3,6,3,1,4,7,2,4,6,6,5,0,3,3,2,5,2,2,7,1,7,4,1,4,0,2,0,6,2,1,1,4,1,5,1,1,7,4,0,5,3,2,6,2,2,1,3,3,7,3,1,7,5,2,0,5,7,0,7,1,1,7,3,5,6,5,1,2,2,5,0,7,7,1,7,0,1,5,3,0,4,3,4,5,7,0,0,6,2,3,4,0,1,6,1,4,4,5,5,1,6,1,2,6,1,4,3,5,5,4,3,6,5,3,6,3,1,5,5,7,7,0,3,6,1,4,4,5 030000d1fb82b7ee77d115c51e3f7d0a48efab69cf541a5d8fbb048ce6e45c80ee9a1aae4ddcf80bf3ddbcbaec459d8c34a6da6f333a9b50daa973e1810c898693e0ad648b7d9f50bc727bba94a8fcf06b11cbc0c9c07192d38ac63b63d5e66dfc3c64000005 +1,15,14,11,6,12,3,6,12,5,6,10,14,9,5,13,7,7,1,1,10,13,9,1,11,13,15,11,0,13,8,5,12,2,9,8,2,13,3,14,14,4,3,7,7,2,0,10,14,15,12,8,14,11,9,7,12,5,9,9,12,0,14,3,10,15,3,9,7,4,11,9,8,9,1,11,5,4,15,5,9,15,10,14,7,3,14,12,1,10,6,6,1,9,2,6,15,2,3,6,13,13,5,10,3,11,5,11,14,7,8,7,9,15,11,1,13,6,2,9,8,5,4,11,4,11,10,9,12,1,8,2,8,15,13,13,11,14,0,13,11,8,4,5,12,5,1,9,6,12,6,3,10,3,3,5,12,13,5,7,13,13,10,2,5,1,15,13,15,13,11,6,7,15,8,13,2,9,5,14,6,11,3,8,10,15,6,7,9,12,9,9,14,3,10,0,8,5,0,14,13,4,4,7,10,7,7,13,14,12,0,6,1,3,8,14,0,6,5,0,7,1,9,4,14,2,2,6,9,4,10,9,13,15,5,12,5,13,13,5,3,7,7,12,15,7,8,6,14,15,14,14,8,4,7,8,3 0400001feb6c36c56ae95d7711ad91bdfb0d85c2982d3ee437720aefc8eb97c599c0e3af3974b9891b54f59fae73ec1a661926f236dd5a3b5be7879fb1d629854b4ba9c1828fddbe0db845c5196c63a335cd57dda251fdfdb67f8d295e6b38af679c99e3a0850ed447a77dec06138e06507194e22694a9df5c5dd5377cf786efee8478000003 +47,14,46,45,27,53,30,58,36,2,9,55,17,49,59,44,42,1,22,46,47,25,43,42,51,16,61,50,34,5,9,60,10,61,33,18,0,41,9,14,37,1,42,37,53,36,44,52,62,26,50,10,25,21,48,21,21,41,61,32,58,54,10,8,57,4,8,59,39,5,13,29,24,47,11,22,17,45,25,60,61,39,31,5,50,55,33,49,29,17,41,56,15,60,8,3,50,39,53,41,11,6,14,4,43,27,4,10,29,20,63,17,2,52,31,10,17,55,20,47,3,61,26,54,27,25,32,40,38,54,52,39,14,37,4,35,16,61,3,45,26,20,61,25,55,57,35,20,59,4,6,1,13,53,6,13,36,4,53,41,26,62,25,0,10,58,8,9,51,11,7,48,39,51,30,50,50,59,42,36,30,28,47,18,45,52,34,56,15,3,5,11,19,4,1,21,60,48,46,21,12,4,45,47,45,12,7,55,63,18,41,39,50,53,57,3,17,13,14,26,54,10,52,58,4,61,32,2,41,20,35,55,50,27,5,0,16,38,28,5,48,54,16,55,49,40,60,8,3,0,41,37,6,50,26,38,10 060000bcebad6f57ba902277471eeca815aebd9aeacd0f7288527c2bd85202924e941aa5d64b34f9ac8a655c15569f60eb6288e4423b9c535d62f2d646d67cf677c5cb7871751a783fc203ca7d692c6384adb10a754fd10b47ca47752f0fd6b66d98289b6d273a512343d0ed694f59df98d4ec418137518d904d696be6402ba209ccb1f09f37b2cbbaa479cbd2b748b83c314b4c4055f30b95304b6fb4c1f7fd2a67cb5e4344d39ad8ad3a13d802a548f7c9b140426705c36437c68f080c0a651b26a600000a +63,49,112,166,200,81,146,73,105,173,84,54,239,52,122,107,143,81,49,200,151,141,146,237,99,194,121,81,230,47,102,169,74,57,3,135,109,84,157,135,138,221,14,78,65,87,85,72,68,118,19,78,183,100,219,240,200,30,88,48,97,161,95,126,87,186,9,145,187,103,161,202,158,157,137,20,217,157,67,89,255,205,145,82,100,89,138,158,71,59,43,51,116,103,222,153,153,62,110,33,47,149,68,34,19,145,57,245,142,33,97,17,67,53,154,216,101,230,94,115,142,128,249,111,145,137,22,118,122,233,47,128,45,65,153,60,60,7,246,231,215,84,246,192,0,151,25,43,77,14,154,46,22,90,181,53,59,97,114,137,186,255,202,166,127,20,248,53,4,93,7,31,20,254,34,25,121,191,110,78,187,115,254,79,251,8,13,254,106,192,60,0,219,37,90,116,243,75,177,241,140,77,86,87,157,175,21,99,204,51,185,23,245,242,217,98,254,140,171,43,219,206,254,118,76,250,177,217,155,214,43,116,59,167,100,41,3,47,70,188,118,155,120,130,100,109,209,134,60,72,114,20,234,42,50,250,247 0800003f3170a6c851924969ad5436ef347a6b8f5131c8978d92ed63c27951e62f66a94a3903876d549d878add0e4e415755484476134eb764dbf0c81e583061a15f7e57ba0991bb67a1ca9e9d8914d99d4359ffcd915264598a9e473b2b337467de99993e6e212f954422139139f58e21611143359ad865e65e738e80f96f918916767ae92f802d41993c3c07f6e7d754f6c00097192b4d0e9a2e165ab5353b617289baffcaa67f14f835045d071f14fe221979bf6e4ebb73fe4ffb080dfe6ac03c00db255a74f34bb1f18c4d56579daf1563cc33b917f5f2d962fe8cab2bdbcefe764cfab1d99bd62b743ba76429032f46bc769b7882646dd1863c487214ea2a32fa0000f7 +0,0,3,2,1,0,0,1,3,1,2,0,0,1,2,2,0,3,3,0,0,0,0,1,0,0,65,2,0,3,1,0,0,1,3,3,3,0,2,0,1,0,1,1,0,3,0,1,0,0,1,0,2,1,0,2,2,0,3,3,0,3,0,2,0,1,1,2,1,1,3,2,1,2,3,1,2,0,2,1,2,3,1,2,0,3,2,175,0,3,2,1,3,0,3,3,3,2,2,0,3,1,0,3,0,2,0,3,1,1,0,1,1,2,3,3,1,1,1,1,1,1,0,0,0,1,1,0,0,1,3,1,0,0,1,2,3,3,0,2,2,3,1,0,0,3,1,1,2,2,3,1,1,0,2,3,3,0,0,0,2,1,0,3,3,0,0,2,2,3,0,1,1,2,1,1,3,3,0,0,3,1,2,3,0,1,1,2,1,0,3,3,3,2,0,2,0,0,3,0,0,3,1,2,0,1,1,2,3,2,3,2,3,2,0,1,2,3,3,2,1,249,243,1,3,2,2,2,3,3,1,2,0,2,1,3,0,3,1,3,0,0,1,2,0,3,2,3,0,2,3,1,3,0,3,1,0 6204000e41d81a3c0106341fc8453104928f32165e6d89b63b39cfe8d323516f5550141d06f2b435ad4bc093c2b165f0db164fe20c3616eee1be5deaf62737063b2dcd1a57ddde42bfbc000000 +2,0,2,0,1,3,2,3,2,0,1,0,3,0,1,3,1,1,3,2,0,2,2,0,3,1,2,0,0,2,2,0,3,2,2,2,253,0,0,3,1,1,0,99,0,221,1,3,1,2,3,0,3,1,0,3,2,0,208,233,2,3,0,3,3,2,3,0,0,0,1,1,3,3,1,88,3,2,1,0,1,1,2,1,0,2,3,2,1,0,3,2,1,2,1,3,3,1,3,1,3,1,3,2,3,1,0,2,2,0,3,1,3,0,0,2,2,2,0,3,2,0,2,3,1,1,3,1,0,1,1,0,0,2,2,2,1,2,0,0,0,0,3,3,2,0,2,2,1,3,0,3,3,0,3,2,0,0,1,3,3,3,2,1,1,1,1,2,1,2,2,2,1,3,3,2,0,0,3,0,0,3,3,0,1,3,3,0,2,2,3,2,0,1,3,0,3,3,2,2,0,3,2,2,2,2,3,3,2,2,1,3,0,2,3,0,2,0,0,3,0,2,1,1,1,0,2,1,3,2,2,2,1,2,3,2,3,2,1,3,2,1,2,0,0,2,0,2,3,0,3,0,1,3,2,0,3 620600887b84c75e28d828ea4353176cd381b3ec05f4e4592e4e67ddded28dc2a38b5d142a600f8a73ce07f9566a7e0c3c7cae1cfa3aafa72c832549ea6ee79822cc78242b2d3a3b4bfd8df4e960000003 +1,241,2,0,3,1,3,90,2,1,3,0,1,0,86,0,3,3,53,2,0,1,3,3,1,0,0,84,3,2,1,2,3,237,3,0,3,124,0,2,1,2,3,3,0,3,3,1,2,2,1,1,3,3,3,1,1,120,1,3,0,0,3,1,0,2,0,143,225,2,2,3,1,0,0,1,219,1,1,20,2,0,2,58,3,108,2,0,3,138,0,3,2,199,92,0,235,3,1,1,0,74,0,3,164,3,131,1,0,3,2,2,1,3,190,101,1,0,1,1,3,0,3,0,15,1,2,0,3,0,0,0,1,1,87,2,0,96,0,162,3,1,3,1,2,2,2,2,92,0,1,95,59,239,0,2,57,0,2,2,3,3,2,0,169,1,2,3,2,139,0,2,0,0,32,3,3,1,0,1,2,0,3,2,0,0,0,97,51,3,2,2,3,230,1,3,0,0,0,1,0,73,3,0,231,1,3,2,0,1,3,2,0,195,3,2,3,2,0,1,3,1,2,0,15,0,0,3,248,0,1,1,0,1,2,245,1,2,3,3,1,248,213,151,1,2,0,0,3,9,2,1,216,2,249,219,0 62330058de9c48f61f40e6dcc26f3da5fd470d236b41d48ac8e3b0f5233d3a7945ccd8c05e02ddaa07f24af85bb203d18e01fae7011cde1e3ee1d8c305196f4760d92701070e121b21253943444c4f5355595d5e6065686a72737c86898b949798999ca4a9aebbbcc1c9ccd5e0e4ebf1f2f3f9fcfefff1654d57b7de8f8d8539b8b15fa4a982f643558a175ceeceaa22183394b9c03fbdfb5942dbed80000000 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 000000000000 +0,0,0,0,1,0,0,0,1,1,0,1,1,1,1,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,0,0,1,0,1,1,0,1,1,0,1,1,1,0,0,0,1,1,0,1,1,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,1,1,1,0,1,0,1,1,0,1,0,0,1,1,0,0,1,1,0,0,1,1,1,0,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,0,1,0,0,0,1,1,0,0,1,0,1,1,0,0,1,1,1,0,0,0,0,0,1,0,1,1,1,1,0,1,1,0,1,0,0,1,0,0,0,1,0,1,0,0,0,1,1,1,0,0,1,1,0,0,0,1,1,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,0,0,1,0,1,1,0,0,1,1,0,1,0,1,1,1,0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,1,0,0,1,0,1,0,1,0,0,1,0,1,1,1,0,1,1,1,1,0,1,1,0,0,1,0,1,0,1,1,0,1,1,0,1,0,1,1,0,0,1,0,1,0,1,1,0,0,1,0,0,1,1,0,1,1,1,1,1,0,0,1,1,0,0,1,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,1,1,1,1,0,1,1,0,1,0,1,0,1,1,0,1,1,0,1,1,0,1,1,1,1,0,1,0,0,1,0,1,0,0,0,1,0,1,0,0,1,1,0,1,0,1,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,1,1,0,1,0,0,0,1,0,0,0,0,1,1,1,0,1,0,1,0,1,0,1,1,1,1,0,0,0,0,1,0,1,1,1,0,1,1,0,0,1,0,0,1,0,1,0,0,0,1,1,1,1,1,0,0,1,1,1,0,1,0,1,1,0,1,0,1,1,0,0,0,1,0,1,0,1,1,0,1,0,1,0,1 01000008de037f2db8d9fdfff845ffeb93975a66770f8a32ce0bda451cc72d30b35d23010000ff1fc4a977b2b6b2b26f994622a7b56dbd28a6af87cd10eabc2ec947ceb58ad5 +1,0,1,2,3,1,2,3,2,1,2,3,3,2,0,3,1,2,2,1,1,3,2,2,3,3,1,3,1,2,3,1,3,1,2,3,3,0,3,1,3,2,1,1,2,1,3,3,1,0,3,0,3,1,3,1,2,0,0,1,1,1,2,1,0,2,3,0,0,3,3,2,0,3,1,3,2,3,3,2,1,0,2,3,1,1,2,1,3,3,3,1,2,0,0,1,0,1,1,0,3,1,1,0,3,3,0,2,3,2,2,0,0,3,1,1,1,3,1,1,1,0,0,0,2,2,0,3,3,3,3,1,0,2,1,0,1,0,0,2,1,2,2,0,1,1,3,3,0,0,1,0,1,3,2,0,0,2,3,2,3,3,0,3,3,3,2,0,2,0,2,1,3,1,1,2,1,1,3,1,1,3,3,3,0,0,0,1,3,1,0,0,2,0,3,2,2,3,3,1,0,1,3,3,1,3,0,2,1,2,1,2,1,0,3,3,2,0,1,2,0,0,0,1,2,2,2,0,1,2,1,2,2,0,3,2,0,3,0,3,3,0,0,1,2,1,1,3,3,1,1,3,1,2,1,1,1,1,3,1,3,3,3,0,0,0,1,0,1,0,1,3,2,3,2,1,0,0,0,0,3,2,2,1,2,0,3,3,2,2,1,3,1,3,0,0,0,0,0,0,1,3,2,1,0,3,0,1,2,0,3,3,1,0,0,0,1,2,0,0,2,2,1,1,1,3,0,0,1,2,0,1,3,0,0,2,0,0,1,0,1,0,1,1,1,1,2,2,1,2,3,3,0,0,3,1,2,0,2,0,2,0,0,3,0,0,2,0,2,1,3,0,3,2,0,0,0,1,3,1,0,1,2,0,3,0,2,1,2,0,3,2,0,0,0,3,1,2,3,0,2,3,1,1,2,2,1,2,3,3,0,1,0,2,1,2,3,2,1,3,1,1,2,0,2,3,0,2,3,0,0,0,0,3,0,0,3,2,2,3,1,0,2,2,0,0,2,2,0,1,2,2,3,1,0,2,2,3,3,1,2,3,2,3,2,2,3,3,3,1,1,2,3,1,1,2,1,1,0,2,1,0,2,1,0,0,0,3,2,1,0,2,3,1,1,0,1,2,2,3,0,2,3,2,0,2,2,0,1,2,0,3 02000046db9be3697af76ddbcde59f4cdd81592c3e37be4b59fd8114d4f2e8357540a3fd2442685f04782ef3f889d65d7f01d08ebd1f72664f8601a8668e33c197d7650200005dfc0447b900e98fa7700079318f4060a57061c204455a6f0d8883089ce01d18c98e036cb5a6f126e758b2c030eb4a0a1ad2bdbbafd6d652490392d46b2e2863 +6,5,3,1,3,6,7,5,1,6,2,1,5,5,7,5,4,7,3,7,2,7,2,4,6,7,2,3,1,7,6,6,1,4,7,4,6,3,5,1,4,1,5,0,6,1,6,6,0,2,7,6,1,7,5,4,1,1,1,3,0,1,7,2,7,4,2,0,4,0,0,4,3,3,6,7,7,0,7,4,1,3,3,6,1,0,7,4,6,4,1,5,6,6,1,5,2,1,6,3,0,7,3,2,4,5,0,7,2,5,6,5,4,4,7,1,0,0,3,2,0,4,6,0,3,5,0,4,4,3,2,5,4,2,5,2,6,6,6,2,4,2,7,1,6,5,6,4,4,6,2,6,6,3,5,7,7,0,1,6,3,0,5,1,2,5,5,3,3,1,4,3,6,5,2,3,1,0,0,3,1,7,6,7,1,2,6,5,1,4,3,5,6,5,3,6,7,6,0,4,6,6,7,5,2,3,1,7,0,4,4,0,7,4,6,3,3,4,2,3,0,2,6,4,3,7,6,1,0,1,2,2,0,0,2,3,6,5,5,5,6,7,3,7,0,2,0,5,5,6,0,1,4,0,4,3,7,4,7,2,3,1,1,3,6,6,1,1,2,3,1,5,2,0,3,7,0,5,6,0,4,5,2,2,0,1,0,1,5,4,4,1,1,1,6,5,6,2,3,7,3,7,6,1,4,2,7,2,5,2,5,1,7,2,3,2,7,7,6,0,0,0,3,0,4,1,6,2,0,7,7,0,0,5,2,1,3,6,2,7,6,7,4,4,2,1,6,0,0,3,5,5,1,1,6,1,0,0,6,1,0,0,7,1,1,3,2,3,2,0,6,6,6,5,7,1,2,3,3,0,3,7,1,6,4,3,4,4,7,4,3,5,2,6,4,0,6,3,6,4,6,0,6,5,4,2,7,4,4,0,2,2,3,4,5,3,7,2,3,1,7,3,1,6,6,6,5,4,0,2,6,6,0,4,7,0,7,4,6,7,7,2,3,6,7,6,7,0,2,2,4,2,6,2,5,0,3,1,5,0,5,6,1,0,3,4,7,5,4,5,2,4,0,2,4,2,4,0,4,6,7,3,6,4,6,7,5,4,4,5,3,1,2,0,7,4,3,4,7,2,7,4,3,5,1,3,2,5,5,2,6,0 030000d597bd391b7d9df5d4dd33f633cce9868c760be3ec24b07af108046f7e3c2de23cd0dd8d4731da94757593901a1307448d58aadb28b9d74996cefe0e62956b663d532033f72b531dd5ef84dbd4cf120f337130b47f1052013d6dddf085b81823030000f3a64bd894cd41f170952041b21275c9f7f18baaa9e9aff00188721f8151797de44700ed2710310392d3436d794d87ce8e4f1d5a0cf4c358bc81272be99ecedac0b6138f37e9efb84a2ca8668b8873d9540a2826ef4dec95943c73af1d2d5ab0 +15,13,4,10,7,12,11,6,4,11,2,8,10,2,5,7,2,14,9,11,15,8,7,3,0,1,14,9,11,3,13,5,3,9,11,11,8,4,4,12,9,3,8,7,15,9,1,1,3,0,3,7,10,6,10,8,15,8,3,9,1,1,4,11,15,8,14,4,9,1,15,9,9,11,7,4,9,14,7,3,1,7,0,8,7,0,3,2,7,1,1,1,13,6,9,7,8,10,4,7,6,5,9,13,8,15,13,14,5,7,6,4,6,13,3,15,14,3,12,13,2,10,12,3,9,15,2,3,9,8,0,1,2,0,2,7,2,0,9,4,15,2,4,1,9,5,14,2,9,1,13,2,10,11,6,11,14,5,9,13,12,0,12,5,10,3,5,12,14,3,5,5,3,7,6,4,14,9,13,1,7,13,8,7,13,14,11,1,12,11,1,9,1,1,14,0,13,5,13,8,8,7,15,6,1,11,10,9,15,12,14,10,5,14,8,11,0,9,8,7,11,2,0,10,14,12,12,7,0,5,5,14,14,7,6,6,1,15,11,0,4,3,3,9,1,3,10,6,7,14,12,4,2,1,5,5,5,8,5,1,13,1,5,7,6,4,7,5,5,9,6,1,3,13,5,3,5,13,6,5,9,9,14,13,15,4,6,15,5,8,7,7,15,4,8,0,5,1,11,14,8,6,11,10,13,4,9,8,10,9,11,14,0,11,13,8,12,2,8,6,13,1,4,1,15,3,2,11,2,9,14,13,14,9,5,0,15,0,13,9,10,3,10,10,2,14,11,3,5,5,6,1,4,8,5,13,9,4,10,3,13,3,9,15,3,14,0,8,11,13,4,1,2,7,14,7,1,3,11,3,2,12,2,2,1,15,9,2,2,12,7,3,8,0,3,7,10,13,8,6,14,5,13,13,6,12,3,6,9,15,14,0,13,0,7,7,0,9,15,2,7,9,7,6,13,11,15,1,9,4,10,2,3,9,8,1,6,15,4,0,6,6,5,1,7,4,13,0,14,8,3,9,14,12,14,7,7,4,8,15,4,7,14,5,0,3,13,2,5,14,12,15,8,8,10,6,9,15,7,8,8,13,3,2,0,6,2,5,13,15,14,8,8,4,0,9,6,15,15,6,15,5,5,6,6,0,6,11,4,12,3,14 040000fd4a7cb64b28a2572e9bf87301e9b3d539bb844c9387f9113037a6a8f839114bf8e491f99b749e73170870327111d6978a47659d8fde57646d3fe3cd2ac39f23980120272094f24195e291d2ab6be59dc0c5a35ce3553764e9d17d87deb1cb1911e0d5d887f61ba9fcea5e8b0987b20aecc7055ee7661fb0433913a67ec421550400005851d157647559613d535d6599edf46f5877f48051be86bad498a9be0bd8c286d141f32b29ede950f0d9a3aa2eb35561485d94a3d39f3e08bd4127e713b32c221f922c738037ad86e5dd6c369fe0d07709f27976dbf194a239816f40665174d0e839ece7748f47e503d25ecf88a69f788d320625dfe884096ff6f556606b4c3e +1,23,4,19,8,58,26,58,44,9,8,41,40,13,60,17,54,1,53,7,62,42,30,52,51,11,50,51,63,58,21,13,13,17,40,21,45,61,58,1,34,55,2,53,29,59,27,11,19,56,25,55,56,6,32,17,18,54,56,31,7,61,52,22,24,27,59,11,27,59,48,25,58,61,37,26,22,38,52,1,1,46,26,0,26,23,38,27,4,31,42,30,31,53,19,23,11,55,3,35,18,41,50,19,39,22,57,58,26,26,42,5,39,18,22,15,18,61,20,48,62,20,6,63,12,9,30,28,0,55,57,26,17,51,54,1,44,55,28,25,59,22,53,18,50,59,14,38,0,26,54,33,30,20,24,21,22,8,19,30,23,40,24,10,7,46,4,7,32,6,33,2,48,56,1,44,29,24,15,30,45,9,29,10,58,37,62,14,24,41,38,23,43,49,33,1,40,50,38,16,26,46,63,41,31,57,34,24,32,43,27,62,62,7,36,17,50,24,1,62,15,55,29,51,15,16,57,8,1,6,19,13,38,5,26,21,52,45,27,48,55,51,9,22,60,9,17,62,17,41,27,28,59,63,1,63,34,18,5,52,56,57,60,8,33,3,17,13,37,13,4,10,23,25,33,49,14,4,19,1,45,6,19,1,11,37,4,38,41,43,29,37,13,17,23,5,34,11,44,19,61,0,55,2,46,16,48,12,8,32,31,9,26,53,9,5,26,7,63,52,7,61,62,25,58,33,4,58,3,16,20,21,25,57,42,18,30,61,26,34,43,34,43,11,26,39,22,46,47,2,40,40,53,11,15,31,34,50,55,13,58,63,31,52,50,14,4,13,59,11,33,61,63,60,8,32,32,30,32,57,16,61,23,7,31,21,37,38,25,28,59,54,20,13,47,1,8,34,32,42,6,11,47,28,31,18,28,15,20,9,56,49,32,30,14,0,54,34,35,43,60,44,52,25,0,6,37,12,44,12,23,54,46,57,10,32,24,45,50,38,35,50,54,33,9,7,7,18,49,52,14,2,31,59,2,14,59,63,32,47,37,16,44,10,11,26,27,12,59,32,53,15,15,33,29,22,47,58,8,8,3,34,0,1,14,21,32,31,4,18,24,38,40,19,63,53,29,53,18,10,6,16,62,39,28,12,58,2,58,63,3,39 06000005711323a6bab09229a0df11d81d47faa7b4ccbcb3ffa54d351a15b7de818b70b577b6cb4f8677e068114b6e1f1fdd1661becb6fbc19ebd95a5a6d0106e68069799b11fa9e7f54d72f70e34a9c939d6e7a69aa859d258f4bd530f941bf30979c037e5a473d81b37719ed6d52cbb3a601ada17946155884de5e860a1ee107806842c3806c7583deb4974aea5f8e629997af1841a329906aefe97f989882b6fef87911c9807e3f77733d0e480464cd985695d2d6f0df3256f0947e4696dceff07f060000892174e39f0884344d94d10a5d98713844c1b464c12e5126a6b7653515c588bb13f40dc2b90c0c2207c96b5245687ff41fdf99ea113a0d0515679a927bd6a2ae2acb6a75aebc2a28d4b3df8b2dcdebf7f4c8e10decb87dffc22081e83943d5c77d596665cef650dbc122282a18bbdc7d270f509e3181e380da28ebf2cd1900694cb0c5f6bb92a062dca68f2da12471d2c743827fb08eeff82f950b0a2da6ccee0d4f3e1756bfa2080e200139581f112626a13ff577548a190fa770ce82ebf0e7 +217,186,90,3,155,51,127,185,6,206,246,132,12,145,163,146,36,136,130,43,19,11,33,90,151,14,46,130,97,229,187,15,15,177,216,3,248,122,159,95,140,73,248,173,17,245,99,240,100,10,87,126,233,1,235,173,124,166,146,16,153,247,218,14,73,187,45,148,146,26,55,240,208,92,23,29,125,113,127,214,225,7,43,224,45,249,120,197,248,141,113,172,43,81,134,253,185,44,156,94,208,137,178,16,204,161,158,120,69,73,234,165,222,14,213,90,212,142,121,79,17,99,202,213,204,250,56,117,150,93,76,234,243,8,118,49,66,72,20,155,227,169,19,130,16,114,22,154,239,64,246,206,0,167,19,133,40,150,143,7,138,22,1,30,233,43,176,53,229,191,100,133,215,29,205,65,191,222,191,49,174,254,83,53,143,218,218,185,49,67,113,244,160,232,200,239,168,27,160,157,23,233,23,28,226,99,208,120,164,14,248,87,58,191,1,72,93,104,175,12,95,6,7,62,52,236,2,200,166,18,230,188,203,97,15,69,111,37,74,56,6,62,57,151,223,134,118,59,130,229,212,237,99,99,20,159,96,124,96,35,208,154,58,110,14,76,75,206,170,255,26,229,158,191,99,245,2,119,217,37,22,19,60,226,198,131,31,237,115,73,67,220,166,4,35,209,169,51,63,180,204,114,15,189,186,62,135,13,113,85,223,164,20,54,75,102,253,132,250,91,221,108,5,182,214,158,45,136,105,115,36,52,23,42,213,121,25,50,212,156,122,13,113,88,33,52,231,219,112,97,35,34,39,220,177,148,248,41,15,4,133,175,223,60,2,36,232,154,22,2,51,12,90,179,123,153,184,8,77,17,235,240,124,15,99,211,154,232,206,5,80,74,167,218,233,167,22,50,48,219,136,72,4,104,124,131,129,243,44,244,46,219,6,142,218,117,165,164,100,218,155,157,222,195,25,66,196,213,254,93,171,69,235,209,135,80,6,92,99,236,25,18,154,70,253,211,243,5,247,115,242,159,13,151,39,152,136,52,121,24,70,158,180,102,67,189,118,88,56,56,221,135,171,163,48,220,116,247,31,13,81,104,189,135,51,53,39,2,247,168,155,109,74,123,100,55,71,87,55,100,32,34,72,19,75,182,27,125,183,66,115,148 080000d9ba5a039b337fb906cef6840c91a3922488822b130b215a970e2e8261e5bb0f0fb1d803f87a9f5f8c49f8ad11f563f0640a577ee901ebad7ca6921099f7da0e49bb2d94921a37f0d05c171d7d717fd6e1072be02df978c5f88d71ac2b5186fdb92c9c5ed089b210cca19e784549eaa5de0ed55ad48e794f1163cad5ccfa3875965d4ceaf30876314248149be3a913821072169aef40f6ce00a7138528968f078a16011ee92bb035e5bf6485d71dcd41bfdebf31aefe53358fdadab9314371f4a0e8c8efa81ba09d17e9171ce263d078a40ef8573abf01485d68af0c5f06073e34ec02c8a612e6bccb610f456f254a38063e3997df86763b82e5d4ed6363149f080000607c6023d09a3a6e0e4c4bceaaff1ae59ebf63f50277d92516133ce2c6831fed734943dca60423d1a9333fb4cc720fbdba3e870d7155dfa414364b66fd84fa5bdd6c05b6d69e2d8869732434172ad5791932d49c7a0d71582134e7db7061232227dcb194f8290f0485afdf3c0224e89a1602330c5ab37b99b8084d11ebf07c0f63d39ae8ce05504aa7dae9a7163230db884804687c8381f32cf42edb068eda75a5a464da9b9ddec31942c4d5fe5dab45ebd18750065c63ec19129a46fdd3f305f773f29f0d97279888347918469eb46643bd76583838dd87aba330dc74f71f0d5168bd8733352702f7a89b6d4a7b643747573764202248134bb61b7db7427394 +3,1,1,0,1,0,3,0,0,1,0,2,2,1,0,3,1,3,2,0,2,2,2,2,0,1,3,1,1,0,2,0,1,3,3,0,3,0,0,3,2,1,1,1,0,160,1,1,2,1,1,1,2,0,3,3,3,0,0,2,2,1,2,3,2,1,3,0,1,3,3,3,1,1,2,3,0,0,0,0,0,0,0,2,0,3,2,3,0,1,0,3,1,1,3,2,3,2,0,1,1,2,3,1,0,2,3,3,3,3,1,2,3,2,2,3,3,1,2,0,2,1,2,3,1,0,0,3,0,1,3,0,1,0,3,2,0,2,3,1,1,2,2,0,0,0,23,3,3,2,2,2,2,1,0,0,1,3,1,1,2,0,2,3,3,1,0,1,3,1,2,0,1,0,1,3,2,0,2,0,1,3,2,0,2,2,0,3,2,3,1,2,0,3,2,0,1,1,3,2,3,0,3,1,2,2,0,2,3,2,3,2,2,0,1,0,0,3,0,0,2,2,0,2,0,3,1,2,2,3,1,0,0,2,2,3,0,2,1,0,2,2,0,2,0,0,1,0,0,1,2,1,1,3,0,2,3,0,1,1,3,1,1,3,2,0,0,2,2,3,3,0,2,2,2,2,3,0,2,1,2,81,3,0,3,0,0,3,2,0,1,0,3,0,0,2,2,3,2,3,3,1,1,0,0,0,2,0,2,2,2,2,1,1,0,3,3,0,0,0,1,1,2,1,2,1,0,3,3,204,2,1,2,3,3,2,190,0,0,2,2,0,3,0,2,0,1,3,1,2,2,0,0,1,3,2,1,3,3,3,1,3,2,3,1,3,2,3,1,2,3,0,3,2,0,1,3,85,0,0,199,1,1,2,0,0,3,3,0,1,3,1,2,1,2,2,2,0,3,2,2,0,201,0,1,3,3,3,0,1,2,2,2,1,3,2,0,2,1,1,3,1,1,1,0,2,3,2,0,0,0,0,0,0,1,0,1,0,3,3,3,0,1,0,3,1,0,3,1,113,3,1,0,1,1,1,3,0,2,3,2,2,2,1,1,0,2,1,1,1,2,1,3,2,3,3,0,3,1,3,1,0,1,1,1,3,0,0,3,3,0,0,1,2,3,1,3,0,1,0,0,3,2,0,2,0,153,2,2,1,3,2 620200d44c129378aa1d487cc39505958fc29b9c7f5b00023b135ee16d2ff6ebd89b431c4e2d680fea90758bd1d8478878a3b6385ecda2ee8430a236b42b24a20419722d92a050620800c5d782bcaac99cc384c2bbd408aa53c05993c9be828c87681e7f7b7b6ce1d0d60f1d9a8e847f1a9e25d52e00044fc4d35d15cba94959ef374570f06dc438869e194950797c92c1fa533bd5c72726 +2,0,3,2,1,2,0,139,0,0,3,3,168,204,2,1,1,2,3,2,3,3,3,1,2,1,0,3,3,2,198,1,2,2,3,3,0,0,2,3,3,0,0,1,3,0,0,1,10,1,2,0,3,1,1,145,2,2,1,210,1,0,3,3,3,3,114,0,2,0,131,3,0,0,2,2,237,0,0,2,2,3,0,2,1,2,237,1,1,1,3,1,1,3,0,1,1,80,3,0,1,3,3,1,3,2,1,0,2,1,0,0,1,3,2,0,2,0,0,1,2,2,2,1,3,0,0,0,3,1,0,3,3,1,0,0,1,3,0,1,3,3,0,2,1,3,0,2,2,0,2,2,1,1,0,239,2,0,1,0,3,2,1,2,3,1,1,3,1,0,3,0,3,1,0,1,1,2,3,1,3,0,1,2,3,1,3,3,2,0,0,0,0,2,1,1,3,3,1,0,3,142,2,1,3,1,3,1,0,2,197,0,2,2,2,0,3,3,0,0,3,1,60,0,1,0,2,1,3,0,1,3,2,1,1,2,1,2,238,1,1,3,0,0,0,0,1,0,3,3,1,2,2,2,2,0,1,2,3,1,3,0,2,2,0,103,2,1,2,0,3,1,0,1,0,0,253,3,1,3,0,1,1,0,2,0,1,2,1,0,1,1,2,1,3,3,0,3,249,1,2,2,3,3,1,1,85,1,1,2,3,2,1,3,2,0,1,3,241,1,1,149,3,2,2,2,2,2,0,2,2,3,3,2,0,3,3,1,3,99,0,3,2,2,2,1,1,3,2,3,2,2,0,3,2,0,0,2,2,0,1,0,2,0,0,2,3,1,1,95,0,3,3,2,164,2,2,0,3,3,0,20,3,3,1,1,3,2,2,3,2,0,0,1,0,1,1,1,2,2,1,2,2,3,2,0,0,1,3,1,3,3,2,3,1,1,2,2,2,3,3,1,3,1,3,3,3,1,2,3,3,0,213,2,1,2,2,2,2,0,1,3,2,1,99,0,3,0,2,3,1,3,0,0,2,3,0,3,2,2,3,0,2,1,2,1,3,0,3,1,250,1,0,3,49,1,0,3,1,1,2,0,3,0,2,2,1,3,0,3,0,1,2,2,2,2,1,3,0,1,2,3,1,2,2,17,1,2,1,0,2,3 6211008e630f096efd93e9af0bc1c198d5a64ff88f0a42b2655d714c7de4907881a9c0d3d071f2728a5384e6d74cd16dc6df8025f4e9dd24a8f0d049c796697004f6a8070c0d1e30373b42464c56619bc9d2deee8aacf10a4d1c83bed4ee3c4fec620f006dca398d10771486459f35af556e78755eaa2be3df3a97ba3828482d73e28f0f5eb8115a6b81dfb5abddfdbc66a879ccb70b3ac99cd935358ca731aa71b6964b09142a323e415371767db0bcd6daf967ff95f25617a45d58f8c100 +3,1,1,149,3,3,1,0,0,0,0,2,0,1,61,3,0,3,253,2,220,210,211,3,0,3,101,2,2,2,3,1,2,2,1,3,1,1,1,239,2,175,3,1,2,3,3,1,2,1,3,0,44,3,3,2,3,0,3,0,0,0,3,1,0,1,0,97,2,3,0,223,0,2,3,1,2,3,2,1,0,3,3,3,1,3,1,1,2,119,147,2,3,1,2,1,1,57,244,0,0,0,0,0,3,1,1,0,0,109,3,1,232,3,198,1,2,1,56,59,3,0,0,1,15,3,2,3,0,57,0,0,2,242,1,0,3,100,47,1,0,2,3,2,2,0,3,0,1,2,1,2,200,0,3,1,3,3,3,2,0,174,2,162,2,2,1,2,86,0,2,3,2,0,1,51,1,0,131,34,3,1,1,0,1,1,71,1,2,3,1,2,1,0,193,0,3,1,67,2,0,0,2,2,105,0,3,0,43,2,2,1,218,3,66,2,2,2,0,3,1,2,145,213,184,0,2,0,172,100,1,0,2,167,3,61,3,217,221,2,1,1,3,2,1,0,139,0,208,0,0,0,177,3,2,18,0,2,1,2,3,2,0,3,1,3,0,203,3,2,0,1,246,208,2,3,107,2,70,1,1,2,0,2,3,2,0,1,140,3,3,0,3,2,0,22,3,1,2,2,2,1,0,0,0,2,0,2,1,0,0,1,1,0,2,0,254,1,2,1,1,1,3,128,1,112,2,2,0,0,2,0,142,133,2,1,3,0,1,0,155,0,1,0,9,3,0,90,0,169,1,141,0,0,3,0,2,131,110,0,2,0,0,2,2,1,107,0,2,1,1,3,247,2,0,1,2,1,202,231,1,66,3,1,2,3,122,0,2,3,0,1,127,0,3,0,99,1,1,76,2,3,3,163,2,0,0,3,232,0,1,0,2,2,3,1,10,2,2,1,154,3,0,2,0,2,0,2,2,0,171,1,1,188,2,1,1,144,3,1,2,3,11,1,3,59,3,0,0,3,0,0,0,3,2,2,3,2,2,3,2,3,0,97,0,1,2,2,251,3,3,2,2,1,3,0,2,1,1,1,108,1,1,3,95,3,2,0,2,3,3,2,1,1,42,2,2,1,2,95,3,2,19,2,2,22,2,1,0,1,2,217 623500d5f40217362f36ada757bdbd9c3ecc0d11b32db93f75bed95000d41d3993c1fb10a4cd2e8c660dfe2aa68b874ed45db644de0a4ce9baa3650804bdd65e4c007a030e121415161a2729344347595a61626d707276777c8185898a98a1a3a8afb2b3bac2c6ccd0d4d6dedfe0e4e5e9ebedeef6f8fcff94fff7d3467bacb6377643bd6fac4e3833bc64bcaba15320211c1068ad90935bab6693f6de2d2c10622e0026e373e18be962e13ce2da90224148995c4a0899c4c472150cb8829c97e19b6db8b1ccd4bf8304ada9b2228d494dbdfc303aebb11afe9c9517f8be5a9beea9190b1011141620273c43454c4d54585b5d5f65666e747a7b7d82888c8f9398a0a4aeb1b5babdcfd4e0e4eef3f6f9ffcbdd1a46317f81c8e19825aa8e06daf72e5079f613a3a0a6aaf902398f9b5ca5c41760 +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 000000000000000000000000 +0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,1,0,1,0,0,0,0,0,1,0,1,1,1,1,1,0,1,1,0,1,0,1,1,0,0,1,1,0,1,1,1,0,0,1,0,1,0,1,1,1,1,0,1,1,0,1,1,1,0,1,0,1,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,1,0,1,1,0,1,0,1,1,0,0,0,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1,0,1,0,1,0,0,0,1,1,0,1,1,1,1,0,1,1,1,1,0,0,0,0,1,0,1,1,0,1,0,0,0,1,0,0,0,1,0,1,1,1,1,0,1,1,0,1,0,0,1,1,0,1,1,0,0,0,1,0,1,0,1,1,1,0,1,0,0,1,1,1,1,1,0,1,1,1,1,0,0,1,0,0,1,0,0,1,0,0,1,1,1,0,1,1,1,0,0,1,1,1,1,1,1,0,1,0,0,0,0,1,1,1,1,1,1,1,0,1,0,0,1,0,1,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,1,0,1,0,1,0,1,0,0,1,1,0,1,1,0,0,0,0,0,0,1,0,1,1,0,0,0,0,1,0,0,0,0,1,1,0,0,1,1,1,0,0,1,1,0,1,1,0,0,1,0,0,1,0,1,1,0,0,0,0,1,0,1,0,1,1,1,0,1,0,0,1,0,0,1,1,0,0,1,1,0,0,1,1,1,0,1,0,1,0,1,0,1,1,0,0,1,1,1,1,1,0,0,0,1,0,1,0,1,0,1,0,0,1,1,1,1,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1,0,0,1,1,0,1,1,1,0,0,1,1,1,0,0,0,1,1,1,0,1,1,1,1,1,1,0,0,1,0,1,0,0,1,0,1,0,1,1,1,1,0,1,1,0,1,0,1,1,1,1,1,0,0,1,1,1,0,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,0,1,1,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,1,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,1,0,0,0,1,0,0,1,1,1,0,0,1,1,0,1,0,0,0,1,0,1,0,1,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,1,0,1,0,0,1,1,1,1,0,1,0,0,0,1,1,0,1,1,1,1,1,1,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,0,1,0,0,1,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,0,1,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,0,1,0,0,1,1,0,0,0,0,1,1,1,1,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,1,0,1,0,1,0,0,1,1,1,1,1,1,0,0,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,1,0,1,0,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,0,1,0,0,0,0,1,0,1,1,1,0,0,0,1,1,0,0,0,1,0,0,1,1,1,1,0,0,1,1,1,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,1,0,1,1,0,0,1,0,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,0,0,1,0,0,1,0,0,1,0,1,0,1,1,0,0,0,1,0,0,1,1,0,0,1,0,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,1,0,1,1,0,0,0,1,0,1,1,0,1,0,0,0,1,0,1,1,0,1,0,0,1,1,1,0,0,1,0,0,1,0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,0,1 0100004c87417dacdcaf6eb2426d621f8546f785a22f69b15d3ef249dcfd0fe96e80580100000b354d81610ce6c9615d2667559f154fd0ce6e71df94af6be71b6c64cdd5716d0100001139a2bb200a50a7a37ec9b77fedd25dfd030c209162c079e6987a499a9f90fe0100002b0ff942e313ce603c040ac86dfbddfc92b132f36e5f158b45a7245f11 +3,3,3,3,2,1,0,3,0,3,2,0,0,3,2,1,2,3,2,0,1,3,0,2,0,3,2,2,1,0,2,1,2,0,0,2,0,1,0,0,3,3,3,0,1,2,3,0,2,1,2,1,3,2,3,1,1,2,2,0,1,1,2,0,0,0,3,0,2,1,0,2,2,1,3,2,0,0,1,2,2,3,1,0,3,1,0,0,1,3,1,2,1,2,2,1,0,2,0,2,3,3,0,2,2,1,0,2,3,1,0,1,2,2,2,1,1,2,2,0,2,0,3,1,0,3,3,2,0,3,0,2,0,2,1,0,0,3,1,1,0,3,1,0,0,2,0,1,0,3,3,3,3,1,2,1,2,0,1,3,0,0,1,3,1,0,0,0,3,0,0,2,1,2,3,2,1,3,0,0,1,3,0,0,2,0,1,2,3,2,2,1,1,3,1,0,1,0,1,1,2,1,3,2,2,2,0,2,3,2,0,3,2,2,2,1,3,3,2,0,2,1,2,3,1,3,1,2,0,3,2,3,1,1,3,2,2,2,2,2,1,0,1,1,2,0,3,3,2,3,0,0,1,2,2,2,1,2,0,1,0,2,0,2,2,2,2,3,0,3,2,2,2,0,1,0,1,0,1,1,2,3,2,1,2,1,1,3,3,2,3,3,2,3,1,0,0,3,3,3,3,1,2,2,2,1,3,1,3,3,1,0,3,0,0,1,2,0,3,1,1,0,1,1,3,2,2,0,0,1,1,2,3,1,3,2,0,2,0,0,2,1,1,2,0,3,3,3,3,0,0,0,0,3,2,0,0,1,0,3,3,0,3,3,2,3,0,1,0,3,0,0,1,2,1,0,1,3,0,0,3,1,2,0,1,1,1,2,3,1,1,2,3,0,3,2,2,2,1,0,3,2,3,2,3,0,2,0,3,3,2,0,2,3,2,3,3,1,1,2,0,3,1,1,1,0,0,0,2,0,0,1,1,1,0,0,0,3,2,1,3,2,0,2,2,0,1,1,0,0,2,3,3,2,0,0,0,3,2,2,3,2,2,0,0,0,0,0,1,3,2,2,3,3,0,1,3,2,3,1,3,1,2,2,0,2,2,3,0,0,0,0,1,0,0,0,1,1,1,3,0,3,2,0,1,1,3,1,1,0,0,3,1,1,1,1,1,0,1,1,0,3,2,3,0,0,1,1,3,3,1,2,3,0,2,0,3,1,2,3,2,1,1,3,2,0,1,2,0,3,2,1,0,1,3,1,2,0,0,2,1,2,0,3,2,1,0,0,2,0,0,2,1,0,0,0,2,0,0,2,0,1,0,0,3,0,3,3,2,2,3,2,1,3,3,3,0,0,3,1,3,0,0,1,0,2,2,0,3,3,1,3,3,1,1,3,3,2,2,0,0,2,2,1,1,0,0,1,0,0,2,1,2,2,1,2,0,0,2,1,0,2,1,0,3,2,3,1,2,3,1,2,0,2,1,1,1,0,1,3,1,0,3,0,1,2,2,2,0,3,1,1,0,2,3,0,3,0,2,0,0,2,2,2,3,3,3,0,1,0,1,1,0,1,0,1,0,0,3,1,0,2,1,0,2,0,0,3,2,0,0,0,2,1,1,3,3,3,0,3,1,2,0,2,1,1,0,3,3,0,0,2,0,1,0,0,3,2,3,1,0,1,1,1,2,2,2,1,3,2,3,0,0,1,1,0,1,1,2,1,1,2,1,3,1,3,2,0,0,2,2,3,1,2,2,1,0,3,3,0,0,0,0,1,3,2,3,0,0,2,1,0,3,0,1,2,0,2,1,0,2,0,3,2,0,0,3,2,3,1,1,2,0,2,3,3,1,1,0,2,2,2,1,1,3,1,3,1,0,1,1,0,0,0,1,1,2,2,3,0,3,1,2,2,0,3,2,2,0,1,0,1,3,1,2,1,2,0,2,1,2,1,1,2,3,1,0,2,3,0,0,0,3,1,0,0,3,3,0,2,0,0,0,2,0,2,0,1,0,2,1,0,0,0,3,0,2,1,2,0,3,2,2,0,2,3,1,3,3,3,1,2,0,3,1,2,3,2,1,3,3,1,1,1,3,1,3,2,1,0,1,3,2,2,2,1,0,3,2,1,1,3,0,0,1,2,3,2,0,3,1,1,2,1,3,3,1,0,1,2,0,2,3,2,2,0,0,0,1,2,1,0,2,1,0,0,0,0,1,1,0,3,1,3,3,1,0,2,1,1,3,1,1,0,3,1,3,1,3,0 020000ff933839b8723a498210fc6c99ed68580c929e06b4d0766922f292d1a9688d3e32243534213fd9870740c26e707086e974459ea2e3a9f89b763b5eaa458fb06a0200006122ab3a8445b997efb43fda9df4c18d45e816de20963fc03813cfb1306470d856d6cea4eec8f8bbd63540815039e2850be03ae8007af1edda2b004057385d4302000055453b05f6c8db9786391d82639082408210cfae7f0dc128f7d7e8294109a60924edb625474c6a352cc82afc45110d24838097f36253c210ed15a9ec145967780200002b693c01ec24c6248e0ed62f52a5dd1405acda3a11d98996d2c0d0f20221240c98e8b7f636e7d5de47a939706e359f462e8064900537d25d4ddc +3,7,4,6,6,3,1,6,2,3,5,3,3,5,7,4,1,6,1,4,1,5,1,2,4,6,5,1,7,6,2,3,5,2,1,2,1,3,5,5,0,2,6,1,6,4,6,5,1,7,0,4,2,5,4,7,6,0,1,1,3,0,7,2,6,0,5,3,5,3,1,1,5,7,7,5,3,5,6,2,0,4,7,6,0,7,6,2,7,7,1,6,1,3,0,0,4,6,4,0,0,3,5,3,4,4,1,3,5,6,0,5,1,7,2,4,4,2,3,3,6,2,4,4,4,6,5,4,0,3,7,3,1,0,1,5,6,4,3,3,6,4,1,4,5,0,4,1,2,7,5,7,7,1,1,4,7,3,5,0,5,4,3,0,1,2,7,2,2,5,1,5,4,5,7,7,1,2,4,3,7,7,4,5,2,4,5,6,5,0,2,4,7,5,0,4,3,7,3,6,6,1,0,0,4,7,2,3,2,3,0,2,2,5,4,5,7,1,6,3,1,0,4,6,7,5,2,3,0,3,2,0,7,0,7,6,4,3,5,0,5,2,5,7,3,0,0,1,1,6,4,2,5,1,7,7,0,6,1,5,0,3,4,7,2,3,6,4,6,5,3,6,3,7,6,5,6,3,7,0,6,5,6,1,4,5,5,1,5,6,1,7,5,4,7,2,0,3,5,2,7,5,4,2,1,0,3,0,1,2,7,3,7,4,6,1,6,1,5,1,7,5,5,0,2,0,2,3,1,3,6,5,3,6,4,0,6,0,3,4,0,2,2,7,2,1,2,5,4,1,0,7,5,7,0,0,3,7,5,7,4,2,5,6,5,5,6,1,4,2,1,4,1,5,4,7,6,2,6,2,0,1,4,7,4,5,5,4,3,1,2,4,1,1,2,1,2,7,5,3,7,5,4,5,7,2,5,3,5,0,6,6,5,3,3,2,2,1,3,1,1,2,7,4,3,6,3,5,5,3,3,6,2,6,3,7,5,3,5,2,6,5,6,4,6,0,5,2,7,1,4,7,4,4,5,3,4,3,3,3,3,5,6,5,6,5,4,6,1,3,7,7,0,3,0,6,3,2,6,4,3,1,3,6,5,2,0,1,7,3,6,6,7,1,7,5,4,0,2,5,5,0,6,1,6,6,2,1,6,2,7,5,1,2,7,1,2,5,4,4,1,7,6,7,1,7,7,2,2,6,0,6,1,0,7,0,0,4,4,4,4,6,4,0,4,1,1,1,4,5,6,1,7,2,7,4,1,3,3,5,4,1,1,4,6,1,4,6,5,0,1,3,2,1,5,0,2,7,6,1,0,2,6,2,3,4,7,6,2,2,6,1,4,2,2,4,0,6,4,2,2,3,6,4,5,6,5,6,0,6,4,5,7,0,3,1,6,1,7,4,6,5,5,2,0,5,3,1,0,3,3,3,7,6,6,7,1,5,7,6,2,6,5,6,6,3,7,0,2,2,1,3,5,0,6,2,2,5,1,7,2,2,0,7,7,3,6,1,4,6,0,6,4,2,4,5,5,2,0,4,7,7,7,2,4,0,1,1,2,6,3,7,2,6,0,0,6,3,7,3,5,7,2,3,2,6,7,6,5,7,3,0,1,3,6,5,1,3,2,3,2,6,2,4,2,0,7,3,2,5,6,4,0,1,3,4,0,0,0,3,1,4,1,2,3,0,6,0,3,4,2,0,6,6,7,1,6,0,2,7,7,3,7,1,5,3,6,3,6,2,6,4,0,1,1,3,6,3,0,6,5,3,6,3,1,6,6,5,4,4,1,2,0,0,6,3,6,6,5,0,0,1,4,3,5,7,7,1,0,6,3,1,7,1,1,5,6,0,7,5,6,2,3,3,5,3,3,4,7,1,0,6,2,6,6,6,2,2,2,0,3,3,4,6,5,0,7,3,6,7,2,7,4,3,4,1,3,1,6,4,6,6,3,6,2,0,2,3,1,3,1,2,1,1,6,7,1,5,6,4,6,2,7,7,3,0,3,6,2,0,1,6,3,2,1,0,5,7,6,3,5,2,5,6,5,0,6,0,3,5,2,3,6,1,4,4,6,2,2,3,5,5,7,5,7,5,4,0,6,3,3,1,0,0,6,1,4,5,6,3,4,1,1,4,1,5,3,4,3,3,6,1,7,4,2,6,3,7,1,6,7,6,5,5,6,3,1,7,3,2,3,7,4,4,4,6,3,3,4,3,5,2,5,5,6,2,5,2,1,5,1,0,5,4,7,2,7,1,5,4 0300007e6cce4eb77c38c34a9a9f93a8a2ed0b1d353c4567c0963ac2bac9bfd77213e1f2fce2c09a00eb90bb853d489bca49ac0fb20dd1bd0ca215efe4cee8b182ba54d97f2a3fe552ea14f447dec409d34c2565e73226f530d0e3e8e8aaf6013a2a7f03000018d0e74f4d5e7f5cf8d71969b8fb3a0eaf622182bbf31c69f684132f57a0c1c097455847bc07ef8aeb7188c367cb206796c6542515ebf65eaba36ada4592bc79dade59faead74c2ae6792b8db775d662ff0c66b465ea81ef6e7d815a31d91cbd0300002b95643f73fa5862381249a0849971ebc2dd84cc66a0b4685f10b273e4b18941a24f4bae1a5e19c7cd6a1590dbfb737e5aecf848ba3254f487ef19868a5a84ffa80959f580cfbbd35bebd82f52d359443b57405c00330a630710db9c17ef9af3030000cb404bcc6af33b590a033da8063bf9199e4dc3dc9badce465b64906e6a3bdd78e1674d9e4132ca277374cbf61e40e688bf3aaea30753c64c93b7df60cd903197384c35c6f1f167cefadccf69f9266e3aadcaa348b3ae6c +10,0,2,1,7,5,10,12,8,14,6,0,3,12,6,10,14,12,1,0,2,15,2,4,12,1,5,8,15,6,15,15,8,11,5,11,2,6,10,3,3,5,9,15,7,8,6,6,11,6,6,3,5,11,10,15,14,0,5,4,0,14,3,0,15,12,9,7,11,5,12,12,5,4,1,1,12,13,13,3,10,9,14,5,12,9,9,1,6,2,14,10,6,13,2,0,14,7,9,10,14,3,4,9,9,6,5,15,11,1,3,11,11,9,2,9,13,14,7,13,12,6,10,7,8,2,14,6,9,15,13,4,4,1,8,1,9,15,9,10,13,8,8,13,9,2,15,5,15,10,3,2,2,12,11,2,1,2,1,15,8,15,15,12,6,12,14,1,15,1,11,6,14,9,7,0,6,3,11,5,1,3,2,6,8,7,13,9,0,10,14,12,4,3,13,5,2,4,11,9,7,2,14,8,12,6,9,1,14,5,14,0,9,11,12,10,1,15,8,13,14,7,6,4,13,6,15,1,2,11,5,13,15,6,14,2,6,7,14,2,2,2,2,6,12,4,13,0,0,2,7,10,1,1,15,11,14,15,15,8,8,3,9,14,12,13,9,7,7,8,12,2,7,13,9,5,1,10,9,9,15,5,1,10,9,13,8,12,15,8,7,14,5,7,7,10,0,10,12,7,7,6,8,0,7,3,15,10,9,4,4,7,15,12,6,14,10,7,4,1,14,13,14,1,14,3,11,3,10,11,1,10,15,15,8,6,15,5,9,10,5,3,6,8,5,12,15,6,3,0,5,15,10,11,6,0,10,14,4,12,12,7,4,7,12,6,1,14,14,3,3,0,1,6,11,1,15,0,2,5,4,0,4,7,4,4,2,12,5,0,2,14,7,1,3,7,4,2,10,12,4,4,3,8,9,10,12,7,1,2,11,9,6,0,4,5,0,5,11,10,6,14,3,5,9,15,5,14,0,9,9,7,0,1,9,3,12,0,9,15,4,14,14,3,3,11,12,6,6,13,9,2,11,0,13,1,9,9,3,0,0,4,8,10,4,8,10,11,14,3,7,9,1,14,14,13,13,2,10,13,1,8,11,14,13,7,10,14,2,6,10,14,11,11,2,3,11,5,9,14,13,10,8,14,9,1,6,12,10,8,4,9,12,1,6,15,1,9,2,11,6,6,14,11,9,12,8,1,5,5,9,7,7,4,1,9,2,3,14,2,7,3,13,3,13,13,8,4,12,7,9,12,11,11,5,13,3,12,13,3,9,10,15,1,5,15,12,13,1,10,6,8,12,1,9,9,4,5,3,13,9,3,15,8,5,10,0,5,15,2,15,0,1,11,9,5,11,14,4,7,0,14,11,10,8,7,4,11,3,5,11,7,7,9,11,4,1,13,1,2,9,11,2,2,15,11,13,2,8,11,11,8,9,8,5,3,7,0,11,1,1,0,5,8,0,5,10,13,0,15,15,12,0,9,6,13,3,0,3,7,15,14,6,10,1,14,11,10,9,9,5,13,15,1,10,11,10,10,10,6,5,9,4,11,10,8,9,6,9,10,11,0,14,11,12,6,5,15,2,13,10,14,15,13,13,2,9,10,10,9,9,12,2,6,7,15,4,8,9,5,9,9,5,1,0,14,9,11,14,3,5,10,0,10,9,9,13,10,2,14,0,9,8,3,5,8,10,2,4,12,4,10,5,1,7,1,7,2,8,7,11,9,2,1,9,8,5,13,14,2,15,14,3,10,9,11,7,7,5,15,15,13,2,12,11,14,3,10,11,7,2,14,1,8,7,14,2,8,4,0,3,14,13,4,9,13,13,7,14,5,1,0,3,14,2,8,4,3,5,4,9,12,12,1,4,0,12,12,9,1,15,13,14,6,15,13,13,11,0,14,6,13,15,4,5,13,14,0,5,14,8,3,5,0,8,6,9,11,1,2,9,1,2,10,9,3,13,6,13,8,0,7,14,4,8,7,13,2,4,8,13,4,6,10,12,2,8,3,14,14,12,13,3,3,4,5,12,1,12,13,0,12,13,7,15,7,14,11,15,14,4,9,6,4,4,9,3,10,6,4,3,15,8,8,0,6,1,5,11,14,14,15,10,4,6,15,0,0,6,7,5,14,3,4,7,15,14,8,15,0,7,7,12,8,4,1,1,6,13,5,8,2,2,8,13,11,1,8,0,14,1,15,11,0,12,0,2,9,9,0,8,0,10,3,15,11,15,11,7,3,15,13,9,7,10,9,9,15,1,11 040000a02175ac8e603c6aec102f24c158f6ff8b5b26a3359f7866b6635bafe0540e30fc97b5cc5411cdd3a9e5c99162ea6d20e79ae349965fb13bb929de7dc6a782e69fd441819f9ad88d92f5fa322cb2121f8ffc6ce1f1b6e97063b5132687d90aec43d524b972e8c691e5e09bca1f8de764d6f12b5df6e267e22226c4d0027a11fb040000eff8839ecd9778c27d951a99f51a9d8cf87e577a0ac7768073fa9447fc6ea741ede1e3b3ab1aff86f59a53685cf6305fab60ae4cc747c61ee33016b1f0254047442c502e713742ac44389ac712b9604505ba6e359f5e09970193c09f4ee33bc66d92b0d19930048a48abe3791eedd2ad18bed7ae26aebb23b59eda8e916ca849040000c16f192b66eb9c815597741923e273d3dd84c79cbb5d3cd39af15fcd1a68c199453d93f85a05f2f01b95be470eba874b35b779b41d129b22fbd28bb8985370b1105805ad0ffc096d3037fe6a1eba995df1abaaa6594ba8969ab0ebc65f2daefdd29aa99c267f489599510e9be35a0a99da2e098358a24c4a51717287b921985d040000e2fe3a9b775ffd2cbe3ab72e187e28403ed49dd7e5103e2843549cc140cc91fde6fddb0e6df45de05e8350869b12912a93d6d807e487d248d46ac283eecd3345c1cd0cd7f7ebfe4964493a643f880615beefa46f00675e347fe8f077c84116d58228db180e1fb0c0299080a3fbfb73fd97a99f1b +11,25,0,35,4,35,10,8,21,54,25,40,58,16,10,14,45,36,55,63,39,27,55,62,61,22,62,56,24,11,21,52,21,14,28,7,36,49,15,7,38,2,51,48,8,0,13,7,25,30,18,30,7,63,35,39,18,56,53,61,49,13,10,35,42,6,28,36,18,30,35,39,25,63,19,11,45,35,32,0,37,32,57,48,36,43,44,28,58,61,39,25,37,62,46,3,38,28,56,41,14,45,49,29,0,63,35,21,13,7,59,15,27,6,41,21,53,32,31,3,16,24,33,62,25,32,23,60,26,39,31,29,55,44,45,38,33,50,53,8,55,57,7,2,24,25,5,41,5,52,50,35,19,21,15,42,5,28,6,9,53,10,29,23,5,41,17,50,0,16,3,42,23,27,0,18,1,7,18,29,32,30,20,61,36,57,37,50,4,2,42,19,28,39,3,48,35,62,15,27,23,14,63,32,28,2,18,10,58,61,22,36,51,43,52,2,7,58,25,62,31,38,23,15,1,27,48,8,35,23,0,60,37,3,10,53,59,46,2,0,49,34,24,60,12,56,39,62,0,47,32,9,5,35,37,55,41,21,33,62,19,10,56,28,39,16,56,5,27,6,1,62,33,60,61,59,23,24,59,29,51,33,61,26,59,48,7,12,59,55,31,2,36,43,41,58,36,62,56,34,30,49,16,58,51,47,19,13,50,54,35,50,35,21,19,63,1,38,2,51,47,24,46,25,59,51,33,40,40,29,18,22,25,45,22,8,12,41,50,39,6,47,28,8,53,47,12,1,43,18,35,19,17,6,35,54,19,35,24,11,16,20,0,37,38,4,59,17,1,41,7,47,50,37,37,58,9,22,54,23,40,58,19,29,33,48,54,8,0,59,31,40,7,61,31,39,31,7,59,5,55,48,62,27,60,61,45,30,33,43,9,20,11,44,40,8,3,19,63,34,13,39,22,22,18,37,3,40,61,35,2,23,33,13,16,0,23,43,27,37,33,5,21,27,55,3,22,60,22,5,45,18,60,9,9,61,12,35,20,28,31,52,55,52,58,25,48,34,41,28,10,32,12,62,21,63,18,55,28,50,14,9,36,17,20,53,19,61,52,16,29,21,40,56,21,63,14,5,33,42,45,42,23,52,37,4,63,57,21,28,19,62,60,6,25,38,28,2,40,29,41,13,29,22,20,8,52,6,3,30,36,30,16,62,46,41,24,62,34,60,45,47,54,22,30,58,22,32,40,29,35,36,58,55,4,35,34,40,29,9,55,35,19,2,40,46,39,57,48,52,10,40,51,26,14,57,18,28,60,5,6,39,61,62,31,38,1,45,42,22,17,47,21,14,22,1,47,9,26,47,34,12,59,28,49,62,23,30,26,54,40,21,0,18,8,5,13,18,48,62,20,1,25,10,8,48,1,50,18,46,12,2,43,3,34,29,7,47,8,60,61,42,26,7,14,18,17,26,45,8,24,60,55,55,10,19,30,1,12,43,1,63,13,18,52,6,34,41,23,4,21,27,60,53,2,15,6,5,11,27,50,13,22,51,22,50,15,50,9,5,56,53,24,36,52,14,19,63,36,20,18,16,19,59,36,27,9,42,7,54,26,55,17,2,12,54,9,44,44,58,24,31,9,9,6,52,8,3,12,3,50,22,12,44,20,13,52,14,1,53,39,17,44,18,21,63,59,59,2,11,6,52,5,60,51,42,21,36,10,25,59,59,23,37,51,51,44,60,33,3,22,54,19,55,47,8,28,51,47,25,63,15,27,28,63,49,21,15,35,28,40,49,47,33,24,59,14,9,43,1,38,0,31,6,54,60,24,13,37,45,10,5,22,29,17,49,8,40,44,41,0,21,21,55,52,6,35,45,20,24,53,27,0,20,53,39,17,13,8,26,19,54,33,36,50,16,38,52,19,6,19,55,9,33,12,15,17,36,30,40,28,16,33,60,43,12,41,49,28,0,56,2,56,20,0,34,38,29,52,57,45,33,55,53,25,50,43,61,51,53,41,3,26,39,46,35,14,21,2,53,21,62,19,18,29,25,61,35,50,5,42,42,45,41,35,27,59,16,10,35,10,36,1,60,53,24,55,52,44,24,16,58,24,43,10,25,9,24,44,49,32,26,7,27,51,37,51,55,52,21,39,58,20,52,27,63,48,27,11,45,59,60,21,34,44,29,24,58,11,44,38,8,16,9,12,13,34,36,32,22,16,13,60,39,27,11,25,30,37,44,61,33,10,46,61,55,15,14,10,38 0600002d9023123288576668e9028eb64dff9dbdfef56fb860b57454e7079313c7982cf020034765e49e1ff8e74b8d7dc4d2a3a8672449e8e767f4cbb63800960e7092bb1cebd9d997eb8399ce293adc5d03f8d5347ecf6c6a55d607c341887e6605fc6a77dddecb66872d48df91c2619169174ca34d53ea15c189d4a7571694720100ea5db01204749d81e53d939972102a937270f08fe3db5cefe070248aebd5a4cebd021fa67e7e65cf05bc088d703c9432b5eee080c6263c3389fe02f809163977060000a5587e4cae1c9d0e056c607e87cf7b5d8eddce1f5aef01ccef77c292ba7a93ee227b143acef4cdcb68f28d54ff0660b3bd8b99ef3868a1d49666d588329ca71af708d6f301ad28d34468f64e360b414025984ed10691efca597a256d97a3a4dd870d8803b7e81fd7e77c7ec5df0f9bf3db5e86b2542eca080d3fe23675964a50e8f6309784d4005eb6e584555bdc35bc585b52f0927d32351c7f4df4e99c22a5c2a033e57f4b77323899115354fdd10755a3857f38586ab6a5f4944ff955c4fe060000f06666702a1da4d756508d060de91e43eba963e8bcb6fd967ba5a0a1d8e4eb71238a8749de34c2a2e9f9c342a8cda3b949cf051a7f7e7e606da9646f54e581bc96af88cedcc7e5de6b6a15012205352c3e50164a2300724ae302ac389d1ef23cf6a68739245ab4863cdf729378132b07f352d068a95c455bf3508f1852dbc8d5b35b23f2245e35624d0e4ff9144904fb91b26a1f66b744233626cb3a61f2491b4203303c9632c50dd0e0759d1b1257fefb08b1b417ccea564299efb5e5cf3b3c0600008435b64f7bc8733bd9fcf6dcff154f8dca31be163b389ac19807c6dbc60d96d28559d471228b29015577d068ed518d5b014d6744d21a4f6864c909b44c64f726130f4647a871087cacca71700e02e1402299dd39b61df5672afdcf5a436a7ba33950b557e4d2759f63c85aaab698dbed02a32a407cd58df4b1843a62b299258b3181a1dbce5cf7d159fa5346ffc1b2edefc562b1d63a2ec98840930d8a481640df276cb65e96cf612aef773ce2a6 +6,168,224,139,120,27,231,23,196,1,15,41,26,132,8,8,140,188,214,75,110,235,82,81,33,35,150,103,233,147,168,131,142,92,90,105,221,185,236,91,215,85,126,237,99,139,68,248,19,146,70,203,148,198,247,85,90,2,252,88,178,213,203,177,58,74,22,63,31,68,126,215,35,42,148,104,75,172,175,24,242,29,214,178,138,151,88,243,20,211,222,137,134,96,172,12,31,23,9,171,100,220,17,27,120,69,32,102,18,81,209,207,169,235,195,199,29,105,221,168,84,8,7,223,88,228,14,211,64,4,106,97,26,31,42,218,230,208,21,58,96,7,242,2,233,109,193,200,19,193,136,146,138,220,137,129,227,241,125,64,144,141,60,149,155,175,236,33,98,106,236,71,142,54,39,59,43,22,199,1,227,159,145,237,104,65,111,94,158,110,119,213,62,184,117,98,28,76,235,198,223,133,210,39,100,240,204,180,197,113,193,23,1,56,132,34,70,72,131,144,90,108,64,200,195,139,91,158,116,219,173,171,50,140,30,230,195,100,193,202,16,161,170,140,57,248,80,208,186,170,149,14,28,208,190,21,39,55,127,170,48,238,105,58,104,92,141,206,194,104,16,78,249,206,148,117,160,51,209,98,197,208,206,51,159,99,251,185,20,105,234,178,119,81,127,169,249,135,200,72,188,166,103,68,166,0,18,233,140,31,85,165,36,237,140,156,13,182,185,143,33,76,239,231,67,5,39,139,82,224,187,89,210,223,230,131,145,230,145,161,42,130,150,233,66,163,74,222,101,105,161,77,224,182,35,35,105,72,220,25,80,221,37,44,86,208,205,180,165,11,21,77,28,165,181,28,78,186,6,137,21,77,138,50,47,2,1,149,241,13,251,200,15,62,3,52,176,52,131,68,229,244,229,32,166,251,162,119,238,95,192,7,67,228,93,209,71,28,188,204,61,121,34,165,161,133,53,100,48,96,109,172,6,179,152,18,249,14,138,41,20,113,116,205,69,188,11,126,17,48,175,203,138,219,192,243,36,186,244,252,78,255,43,168,148,96,19,83,252,36,177,133,117,91,40,99,209,39,33,232,54,143,0,131,55,157,102,138,184,252,145,103,225,104,163,207,24,254,238,121,59,32,83,59,32,253,120,188,86,214,119,84,114,63,229,72,100,185,75,133,215,96,250,180,196,20,107,17,183,88,185,219,83,37,239,18,182,33,51,108,13,196,112,250,171,180,25,192,92,40,137,173,143,154,138,34,105,177,9,151,219,61,186,166,127,111,154,167,199,250,131,40,52,15,91,15,71,57,242,186,91,201,156,43,28,246,157,58,228,74,145,146,86,177,136,43,170,241,189,179,198,52,17,37,254,32,106,237,63,136,165,138,24,76,139,2,95,30,122,13,225,30,21,94,142,137,17,214,157,42,216,87,43,17,197,197,185,182,191,225,123,11,52,76,167,152,88,199,194,42,244,247,12,126,113,155,33,246,140,104,178,184,181,229,43,15,124,28,150,78,26,31,231,76,130,164,43,46,32,40,236,131,98,128,110,87,244,161,143,122,213,221,166,42,154,187,152,160,84,203,181,101,148,96,208,19,15,114,218,5,122,173,229,8,92,210,55,215,89,220,102,201,170,134,234,141,239,152,92,104,253,172,209,28,1,155,252,174,225,245,210,106,134,72,19,16,191,65,119,169,165,142,106,32,90,225,28,59,228,248,30,147,144,218,43,183,95,104,9,234,22,240,55,89,69,34,154,167,120,105,81,82,82,112,68,121,92,158,248,92,133,255,126,174,118,133,28,113,228,143,134,50,151,53,49,207,151,53,156,170,161,94,19,44,89,195,48,62,70,88,110,176,212,87,120,142,208,32,218,63,37,43,81,104,128,45,47,165,127,32,221,29,95,124,200,126,153,62,40,219,184,141,64,16,84,164,101,205,237,104,244,220,212,6,186,218,169,215,255,90,29,205,68,99,10,59,186,43,12,95,19,195,171,63,177,42,140,242,33,71,71,182,247,191,153,138,222,243,216,182,55,98,221,80,192,175,30,168,96,137,96,168,122,100,171,208,198,3,7,176,205,47,141,49,201,107,129,223,200,16,33,55,111,114,103,149,13,80,124,231,63,200,98,62,52,129,115,149,155,169,77,167,184,43,47,74,52,152,136,173,214,6,234,13,204,72,190,74,217,66,104,83,65,130,198,108,156,129,124,155,66,174,84,129,224,253,248,23,34,193,190,1,219,209,28,1,90,108,32,153 08000006a8e08b781be717c4010f291a8408088cbcd64b6eeb525121239667e993a8838e5c5a69ddb9ec5bd7557eed638b44f8139246cb94c6f7555a02fc58b2d5cbb13a4a163f1f447ed7232a94684bacaf18f21dd6b28a9758f314d3de898660ac0c1f1709ab64dc111b784520661251d1cfa9ebc3c71d69dda8540807df58e40ed340046a611a1f2adae6d0153a6007f202e96dc1c813c188928adc8981e3f17d40908d3c959bafec21626aec478e36273b2b16c701e39f91ed68416f5e9e6e77d53eb875621c4cebc6df85d22764f0ccb4c571c11701388422464883905a6c40c8c38b5b9e74dbadab328c1ee6c364c1ca10a1aa8c39f850d0baaa950e1cd0be1508000027377faa30ee693a685c8dcec268104ef9ce9475a033d162c5d0ce339f63fbb91469eab277517fa9f987c848bca66744a60012e98c1f55a524ed8c9c0db6b98f214cefe74305278b52e0bb59d2dfe68391e691a12a8296e942a34ade6569a14de0b623236948dc1950dd252c56d0cdb4a50b154d1ca5b51c4eba0689154d8a322f020195f10dfbc80f3e0334b0348344e5f4e520a6fba277ee5fc00743e45dd1471cbccc3d7922a5a185356430606dac06b39812f90e8a29147174cd45bc0b7e1130afcb8adbc0f324baf4fc4eff2ba894601353fc24b185755b2863d12721e8368f0083379d668ab8fc9167e168a3cf18feee793b20533b20fd78bc56d67754080000723fe54864b94b85d760fab4c4146b11b758b9db5325ef12b621336c0dc470faabb419c05c2889ad8f9a8a2269b10997db3dbaa67f6f9aa7c7fa8328340f5b0f4739f2ba5bc99c2b1cf69d3ae44a919256b1882baaf1bdb3c6341125fe206aed3f88a58a184c8b025f1e7a0de11e155e8e8911d69d2ad8572b11c5c5b9b6bfe17b0b344ca79858c7c22af4f70c7e719b21f68c68b2b8b5e52b0f7c1c964e1a1fe74c82a42b2e2028ec8362806e57f4a18f7ad5dda62a9abb98a054cbb5659460d0130f72da057aade5085cd237d759dc66c9aa86ea8def985c68fdacd11c019bfcaee1f5d26a86481310bf4177a9a58e6a205ae11c3be4f81e9390da2bb75f6808000009ea16f0375945229aa778695152527044795c9ef85c85ff7eae76851c71e48f8632973531cf97359caaa15e132c59c3303e46586eb0d457788ed020da3f252b5168802d2fa57f20dd1d5f7cc87e993e28dbb88d401054a465cded68f4dcd406badaa9d7ff5a1dcd44630a3bba2b0c5f13c3ab3fb12a8cf2214747b6f7bf998adef3d8b63762dd50c0af1ea8608960a87a64abd0c60307b0cd2f8d31c96b81dfc81021376f7267950d507ce73fc8623e348173959ba94da7b82b2f4a349888add606ea0dcc48be4ad94268534182c66c9c817c9b42ae5481e0fdf81722c1be01dbd11c015a6c2099 +2,2,3,2,0,0,3,1,0,3,2,3,3,2,1,0,0,3,3,3,0,0,2,1,2,0,1,3,0,3,1,2,2,3,0,2,1,1,2,0,0,136,2,2,0,1,2,3,2,0,0,0,3,2,1,3,2,0,1,0,1,1,2,0,0,0,185,1,3,2,3,0,0,2,0,3,2,0,1,0,167,1,1,3,2,1,2,3,0,1,0,89,0,1,3,3,0,1,1,0,232,3,1,2,0,2,3,3,0,2,2,192,0,1,2,1,0,2,2,3,3,1,2,0,3,2,3,1,240,2,0,3,1,0,3,1,2,88,3,2,3,0,1,1,2,2,1,3,1,1,2,103,2,1,3,1,2,2,3,1,2,3,1,1,0,3,2,0,3,1,0,2,0,2,1,0,3,1,1,3,2,3,3,3,0,2,1,2,2,0,2,2,0,3,1,1,3,0,1,1,1,3,3,1,0,2,2,2,2,0,2,1,1,3,1,2,0,3,2,1,2,1,3,3,3,2,0,3,3,3,3,2,0,2,1,1,1,1,200,2,2,3,3,0,1,2,0,2,1,3,3,2,2,2,2,3,0,0,2,150,3,0,0,3,3,3,1,0,0,2,2,1,3,1,2,2,0,2,1,1,0,1,2,1,3,3,2,1,3,1,3,0,1,0,3,3,0,0,3,1,2,2,1,3,1,1,1,1,2,0,2,1,2,0,0,0,3,3,1,0,3,0,2,1,0,2,2,0,3,0,3,1,2,1,3,1,0,3,1,3,2,1,2,0,2,0,3,2,0,0,3,1,1,1,3,1,1,1,0,2,3,0,0,0,2,0,2,0,2,1,0,2,2,3,1,0,0,3,2,1,3,3,3,2,0,3,1,3,0,0,1,3,3,2,2,2,3,1,0,0,0,2,3,3,3,0,3,0,3,1,3,3,0,2,1,3,2,1,3,2,2,2,0,0,2,3,3,2,0,3,1,1,3,1,0,2,0,1,43,2,0,2,1,2,1,3,1,3,0,3,1,1,2,2,0,1,1,3,1,3,0,0,244,3,3,2,3,2,1,3,2,0,0,3,1,3,3,3,1,2,0,1,1,3,0,1,3,0,0,2,0,0,0,2,0,3,177,3,2,0,3,2,2,2,1,2,3,1,1,0,2,0,2,0,0,0,2,2,0,3,2,1,2,3,3,0,2,0,1,0,0,2,0,3,1,0,0,1,1,1,2,1,1,0,3,0,1,3,0,0,1,2,0,2,2,0,0,2,2,0,0,0,2,1,0,3,2,1,1,0,0,0,0,1,3,1,1,1,0,3,1,0,2,0,2,0,2,0,0,0,3,3,0,0,3,2,0,1,2,1,1,0,0,3,3,3,0,0,3,2,3,3,2,0,0,0,3,1,1,2,3,3,1,2,2,1,1,3,1,109,2,3,3,2,0,2,0,2,3,1,0,1,1,2,3,1,1,2,3,1,0,0,3,1,3,1,3,3,1,1,3,3,3,0,0,1,3,2,2,3,0,0,0,1,1,1,1,0,2,0,2,1,1,2,0,1,2,0,1,2,1,0,3,3,1,2,0,1,2,2,2,1,3,0,0,0,1,0,1,1,1,1,0,1,0,1,3,2,3,3,1,0,0,1,1,2,3,2,199,3,0,2,3,3,3,3,3,2,1,2,3,0,3,1,2,0,1,0,2,1,3,0,2,2,2,3,2,2,2,3,1,0,1,2,1,1,3,1,3,3,2,1,2,1,2,1,1,3,2,93,0,1,14,1,2,2,2,1,0,0,3,3,0,1,0,2,2,3,1,3,1,3,1,2,3,2,0,2,1,0,0,2,0,0,1,1,1,0,1,0,3,1,3,1,2,3,2,2,0,2,0,3,3,89,0,3,1,3,1,1,0,0,1,1,3,3,3,3,2,1,0,3,0,0,0,0,0,2,0,1,1,3,1,3,1,0,1,0,2,2,2,1,79,3,0,1,1,3,0,0,0,1,3,172,2,12,3,3,0,2,2,2,0,1,3,1,0,0,0,3,0,0,3,3,3,1,0,2,2,0,2,3,2,1,2,1,2,1,0,0,0,3,0,1,1,0,0,1,0,1,2,2,3,0,3,2,2,0,3,0,2,0,2,1,3,0,3,0,3,1,0,0,0,0,3,2,3,1,3,0,0,3,2,3,0,0,3,2,1,1,0,0,2,3,2,0,1,2,0,3,0,3,3,2,3,1,3,0,3,0,3,2,2,3,2,2,2,3,3,3 620a00ae0d3be43f098736b2580a1b80e7845805ec2384d79b111f14362f28192bd8ed234d8ec5a75b9dadb538d224d7bf268a35c57d2a8976399fe3fe2552bc627eab2942505b646f808997ee8aea56eb0f1667206204000ac3f429da2519f9dc4f0da7558980f4c928cd9d37988e0d5d52c08892b439fe3707ead02fccdf279ea0be35d21e267735a17703ee7837f6171c202378ea6d4803b6cef094af6c62020080a39bc842341594c70628280939401d5348880f0e1943f0ef80d6f6975be22d16d6d0ddf5fc1eb01548961864f61a9c045511ef416ef2ffe6cd849cabab465d75d86f10620600f9997919a90f12b776e2420544ddba23d37505ff9300217744a9f1701c8f2a1d030fd28b999031411ace8c89ccd00edc3b0e50b8633edccebabf0b0e416873755c3593ac30 +3,1,3,3,2,1,0,2,3,3,0,1,3,0,1,0,3,2,3,1,3,0,2,1,1,55,3,0,2,0,0,2,3,0,0,3,3,3,1,1,0,3,1,1,1,2,2,1,0,3,3,2,2,3,1,2,0,0,3,0,3,40,0,2,1,0,1,3,84,1,2,1,0,2,1,3,2,1,3,1,3,0,0,0,0,3,3,0,3,2,2,3,3,0,1,1,0,2,1,3,3,164,2,2,0,2,3,0,1,1,3,0,1,0,3,2,1,3,3,1,2,3,3,3,191,0,0,3,0,0,0,3,3,1,3,3,3,3,0,1,0,3,0,3,0,3,2,2,0,0,1,0,1,2,3,2,2,3,2,2,0,1,1,3,179,1,1,0,211,0,2,1,1,3,2,1,0,0,1,2,2,1,1,1,0,0,1,0,1,0,0,3,2,2,1,3,3,3,1,0,2,0,233,2,2,1,1,3,2,1,3,0,1,3,0,0,0,3,3,3,0,2,1,3,3,3,0,0,1,3,1,1,3,3,2,2,2,1,0,3,3,3,3,0,1,3,2,1,0,2,2,3,2,0,2,2,3,2,1,3,2,1,3,1,212,2,3,3,3,1,3,2,102,0,0,2,3,0,1,2,0,3,2,3,0,3,2,2,4,0,10,2,0,1,0,3,2,0,3,3,3,3,1,0,3,3,3,3,2,3,3,3,3,2,2,3,3,0,50,140,3,3,3,3,3,0,3,1,3,123,2,0,1,2,3,1,0,1,0,3,0,1,2,1,2,2,3,1,1,2,3,2,3,0,0,3,3,94,3,3,1,2,3,2,27,2,1,3,2,0,1,0,3,0,2,0,1,1,3,82,3,2,3,2,0,1,2,3,2,3,3,3,2,0,3,1,1,0,1,3,2,1,0,3,0,2,95,1,228,0,0,2,1,0,3,0,2,3,2,3,0,2,3,1,0,0,0,2,3,3,0,0,3,3,3,0,0,0,2,2,0,1,1,0,3,3,3,1,3,0,3,0,3,2,0,2,1,1,1,1,3,1,3,3,3,0,1,3,0,2,2,3,2,2,3,1,1,0,118,0,0,2,0,143,1,120,3,3,0,2,143,0,3,1,3,3,90,3,0,1,3,3,2,3,1,0,3,3,2,1,3,2,1,2,3,0,0,1,122,0,3,3,2,17,3,2,3,0,0,3,2,1,2,0,2,183,1,1,1,1,3,2,3,0,0,3,1,2,2,3,0,0,1,1,2,0,0,2,2,245,2,2,2,0,0,3,2,3,0,1,2,2,0,2,3,0,1,2,0,0,0,0,2,0,0,1,2,1,2,3,1,0,2,1,1,0,0,2,2,3,3,0,3,1,3,3,1,2,2,2,0,0,2,3,1,2,3,0,1,144,0,2,3,0,1,3,0,149,3,3,0,2,3,2,2,0,1,2,3,0,0,1,0,0,3,1,3,2,2,2,1,1,3,3,3,0,2,3,3,0,3,2,155,147,0,92,2,0,0,2,2,2,1,1,0,0,0,3,1,3,1,1,2,141,2,1,0,2,2,0,1,1,1,0,1,105,0,1,3,0,3,1,0,0,1,2,2,2,2,0,3,98,3,2,0,0,0,0,0,0,1,0,3,3,2,0,3,3,2,0,2,0,3,0,1,3,3,0,0,2,0,1,3,2,3,2,3,2,0,2,1,3,2,2,0,0,3,1,3,0,2,1,0,2,2,1,1,1,0,3,2,0,1,2,2,1,1,2,0,2,0,1,2,198,3,2,1,3,0,1,2,0,3,3,3,3,2,0,3,3,0,1,2,1,2,78,3,1,1,1,0,3,2,0,3,0,1,2,3,0,1,2,2,1,2,0,3,2,1,3,2,2,3,3,3,3,2,0,0,3,3,0,3,2,3,0,3,0,1,0,0,2,0,1,1,3,3,1,3,1,2,0,1,3,1,3,1,0,3,2,0,0,3,0,3,0,1,1,3,2,0,1,3,3,166,2,0,1,1,3,1,0,0,0,3,2,0,2,2,0,1,3,3,3,3,3,3,1,2,1,1,3,3,0,0,1,3,2,2,1,3,1,0,112,1,2,3,0,1,3,1,3,2,1,3,0,2,1,3,1,3,2,0,2,2,3,1,1,0,3,3,0,1,2,3,2,2,0,1,3,1,2,2,2,1,3,3,202,3,3,2,0,3,0,1,2,3,0,0,2,0,0,3,2,0,3,0,3,3,0,1,2,1,0,0,3,1,107,3,3 620800df92f1c4edc97c82c3f535693eb60cc24719279dc03cebc527ca2c5c4e7dbfc303dff1333a046eba17d4c97906950443a7f486979c703f27f075fa93fc792b8a193d44657ca4a8ca34a569becd3a621100e79d2fde82c63b3a0a138ff4ffbfebc8ffcdf86d1319ad6ec3ef6ee784c85eee1bbf8d47932d024cbb2d02f0fc0a14fdcce255dfc72bad48234f2cdfb1fb4f9e081020223e3f49656c7b9698dee3e5eaf0d590423237971945f97637a358620c006c18f9ec398b55ec36b05829a83b1a2c600819b4942bcdf6a0b6c42c71f2e86c10dea5fcbcef082a5037599285451cd06a8ee0004f8f88c7c21eee27a0dc9295060b172f6b73969799abb7c7784b7d9259a45e36986206003869621ae718ff8f19ad538c6c698e7aff83cecc4217dd8774e0cc5e1fa1740e287ffd97c1e9d06c779c9de2b53c6e876a7ef8c6c20e33c6437f0f25749bc7e5c53a5cc9a0 +2,0,0,2,2,1,208,34,1,0,0,154,1,229,1,2,1,181,3,2,39,0,2,60,2,0,0,1,3,1,3,0,1,3,1,0,2,1,2,63,1,0,2,3,170,0,1,0,1,1,0,1,223,3,2,61,3,0,3,2,1,2,2,0,2,3,0,0,29,0,1,1,59,2,2,1,2,3,1,3,2,3,0,164,2,0,0,209,1,136,1,2,0,0,0,1,3,1,1,1,3,3,9,0,3,0,0,1,3,3,142,3,2,2,0,223,0,3,1,34,160,1,3,1,2,128,200,0,0,1,3,0,65,1,3,0,2,3,2,3,1,3,2,0,3,2,3,158,1,215,1,0,0,0,1,3,2,6,190,0,0,3,80,3,2,0,1,1,43,1,2,252,2,1,2,3,0,2,3,0,0,2,3,233,1,2,245,2,3,1,3,0,3,3,93,3,1,235,3,0,1,3,0,0,1,185,1,2,3,0,0,1,0,1,199,2,156,3,0,3,3,2,2,1,1,183,3,157,1,1,1,16,0,0,3,95,1,73,2,128,1,48,3,1,3,0,3,0,0,43,3,3,0,2,1,113,230,2,199,194,45,3,199,1,3,2,1,0,1,1,1,1,1,1,77,2,1,0,3,3,2,251,0,3,0,2,1,0,0,1,1,2,2,0,1,0,80,101,2,144,230,3,0,248,57,0,1,16,1,0,0,1,68,1,0,3,89,14,3,156,183,3,3,26,8,3,128,2,2,2,255,2,3,3,2,0,0,151,1,161,2,0,2,119,245,125,1,1,60,2,0,3,167,0,3,230,1,244,0,0,1,3,0,215,1,0,2,203,0,0,3,3,0,0,3,3,48,3,2,1,3,3,1,1,0,1,22,1,0,0,0,104,3,1,142,0,3,0,1,3,3,3,0,3,3,30,0,1,2,2,1,2,123,32,3,0,0,3,220,2,2,1,3,2,67,0,2,2,3,0,2,121,0,3,0,3,3,2,3,0,1,2,2,2,2,1,2,0,3,166,0,0,76,190,3,1,2,169,3,1,7,3,3,227,2,1,1,0,0,0,2,0,2,0,89,2,0,161,3,3,1,1,3,2,94,0,216,1,0,1,3,2,3,1,3,109,3,146,2,199,37,1,1,121,3,2,1,0,0,0,3,3,3,3,1,1,3,1,1,1,188,2,0,3,69,2,0,3,3,194,2,3,1,1,3,0,2,2,0,0,0,48,1,2,3,1,219,120,3,2,69,2,3,112,0,0,0,0,0,231,3,1,2,3,1,3,0,3,53,3,32,1,2,2,3,0,0,3,243,3,0,1,0,3,2,3,0,0,1,1,2,0,1,2,0,3,2,3,0,2,2,0,3,19,3,1,3,88,2,3,1,1,0,2,0,1,2,0,200,2,125,252,0,3,1,178,58,2,1,0,251,129,2,3,1,2,2,0,203,2,0,3,3,0,30,106,3,0,0,1,3,3,1,1,37,2,0,78,0,3,2,1,2,119,0,217,34,1,41,1,3,1,0,3,0,2,1,3,3,111,3,3,3,1,0,0,67,3,2,3,1,46,2,1,3,2,0,248,0,77,63,157,221,2,2,3,0,81,3,1,2,0,2,8,0,1,238,0,1,152,1,0,252,3,1,1,190,0,2,1,3,2,0,1,0,3,0,148,3,0,0,229,3,251,3,1,181,1,33,178,2,101,3,2,235,2,1,1,1,2,3,2,1,0,90,10,81,1,22,121,0,2,135,0,0,0,2,0,1,36,0,2,2,3,2,1,3,3,1,1,3,1,0,2,2,3,1,3,3,2,2,0,3,179,2,0,3,1,3,1,2,0,3,1,2,181,168,126,0,2,99,1,3,0,118,0,3,0,1,3,2,1,0,0,0,228,10,3,0,3,3,2,3,148,1,1,150,0,0,21,3,198,3,117,177,0,22,0,0,1,91,2,2,185,0,3,3,1,1,3,1,2,215,0,2,185,2,0,3,3,1,2,2,0,0,205,2,4,1,1,1,114,0,3,3,0,1,87,0,0,1,1,0,0,0,106,1,0,2,1,3,1,208,0,77,248,97,1,3,85,153,3,0,2,98,0,1,0,8,2,2,1,2,2,3,1,2,2,1,2,172,3,2,66,0,1,1,2,3,3,0,1,2,115,0,3,0,0,3,3,3,73,228,0,2,122,45,77,3,1,1,3,2,21,3,1,1,245,3,3,1,2,1,75,0,2,0,0,1,1,2,3,3,199,1,156,3,1,3,0 622f00829242565ec881dc749b4b8451f9ce68b045e9b7b0814601d5f4c1fba3361d801c5cbb78ee7407a83385d89b2c2d66dcf77c7056c11e33e97d540f584dcc3f2506070b0d111417272c34374448535759666e7377787d7e8493959d9ea2a8abb7bac2c5cdd6d8e1e3e7ebedeff1f9ffd089b9b493cfab73c73a9d220a3dc8a20c909f506f50affaf57eaec67b671174a030a700623b00ae7de455564fb324168418b04441136cfe32aef8358b5523ce40734b0f0f39f51900d8c7f3e1a6cc329ecac933ec6a98e02d9dff9408861f5e811eddeb55e40f0002030406121928292b2c2f3033383c3d3f404344464a51535758595c6063656b6f7882878a959c9da2a8afc1c4c5c9cccfdadde4e6eff1f3f4f7e71c0bc53f94664e7e3844560e7b460a0fe5a1df5f3e9e7dd723056a31de23741ea53bea0785a85f66e4c49780623700f5d52363eb5ca006dce6c003db7371ac3f13b05863b28fdcb52182436a4db68e3cac1f56239b195d327ffd0fb69e01d6b1d8818443589e130c1fd569ee56e4a5080c111d222326292f383a425b5f6a6c6d717276777e84858e9197999a9ca7aeb3b9bbbcbdbec3c9cccfd2d6e1e5e7eaecedeff2fcfdfebd1c0cd9e45ce4d23c116c9ffec3be8321da25377620a6d02fe4cf9f7502ee6fef979fad22c67a58250062310092c0842b9f5d2b7e8f8dd8d922dc8c7900b3ec581ed481e93d76c98f6818563c7050249d045d728429ada63a16f1b30fd0a5d79d5f67205bf4dc0001040b232f3031343843444b4e51535556585c5f686b75777b81899092939497989ca0acafb9c1c2c5c6c7cdd1d7e1e315e849b2da9f61de4296517176c156bb5bb305c55ad13f98566602ad0712e5e2d317d4b19c diff --git a/src/iceberg/util/mumbling_bitmap_internal.cc b/src/iceberg/util/mumbling_bitmap_internal.cc new file mode 100644 index 0000000000..a8971c8d67 --- /dev/null +++ b/src/iceberg/util/mumbling_bitmap_internal.cc @@ -0,0 +1,350 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "iceberg/util/mumbling_bitmap_internal.h" + +#include +#include +#include + +#include "iceberg/util/endian.h" +#include "iceberg/util/macros.h" + +// SSE2 is part of the x86-64 baseline, so it needs no extra compiler flags. +// Define ICEBERG_MUMBLING_SSE2=0 to force the portable scalar code. +#if !defined(ICEBERG_MUMBLING_SSE2) && (defined(__SSE2__) || defined(_M_X64)) && \ + !defined(_M_ARM64EC) +# define ICEBERG_MUMBLING_SSE2 1 +# include +#elif !defined(ICEBERG_MUMBLING_SSE2) +# define ICEBERG_MUMBLING_SSE2 0 +#endif + +namespace iceberg { + +namespace { + +constexpr size_t kHeaderSize = 6; +constexpr size_t kPforChunkSize = 256; +constexpr size_t kPforHeaderSize = 3; + +// The 3 most significant bits of a descriptor select the container type: +// 000 is sparse, 001 is dense, and the other patterns are reserved. +constexpr uint8_t kDenseTypeBit = 0b0010'0000; +constexpr uint8_t kReservedTypeBits = 0b1100'0000; + +constexpr size_t BytesForBits(size_t bits) { return (bits + 7) / 8; } + +// Returns true if `value` is one of the `length` (<= 31) bytes at `container`. +// Scans all of them, so the result doesn't depend on them being sorted. +bool ContainsByte(const uint8_t* container, uint8_t length, uint8_t value) { + for (uint8_t i = 0; i < length; ++i) { + if (container[i] == value) { + return true; + } + } + return false; +} + +// Portable SWAR (SIMD within a register) version of ContainsByte: compares 8 +// bytes per 64-bit word. Reads 32 bytes at `container`, so the caller must +// make sure they are inside the buffer. +bool ContainsByte32(const uint8_t* container, uint8_t length, uint8_t value) { + constexpr uint64_t kLow7 = 0x7F7F7F7F7F7F7F7FULL; + const uint64_t pattern = 0x0101010101010101ULL * value; + uint64_t found = 0; + for (int w = 0; w < 4; ++w) { + // Little-endian load, so byte i of the word is bits [8i, 8i + 8). + const uint64_t x = ReadLittleEndian(container + w * 8) ^ pattern; + // 0x80 in each byte of x that is zero. Exact per byte, unlike the common + // `(x - 0x01..) & ~x & 0x80..` trick, so bytes can be masked by position. + const uint64_t zero = ~(((x & kLow7) + kLow7) | x | kLow7); + const int valid_bytes = std::clamp(static_cast(length) - w * 8, 0, 8); + const uint64_t valid = + valid_bytes == 8 ? ~uint64_t{0} : (uint64_t{1} << (valid_bytes * 8)) - 1; + found |= zero & valid; + } + return found != 0; +} + +#if ICEBERG_MUMBLING_SSE2 +// SSE2 version of ContainsByte32. Reads 32 bytes at `container`, so the caller +// must make sure they are inside the buffer. +bool ContainsByte32Sse2(const uint8_t* container, uint8_t length, uint8_t value) { + const uint32_t valid = (uint32_t{1} << length) - 1; + const __m128i needle = _mm_set1_epi8(static_cast(value)); + const __m128i lo = _mm_loadu_si128(reinterpret_cast(container)); + const __m128i hi = _mm_loadu_si128(reinterpret_cast(container + 16)); + const auto mask = + static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8(lo, needle))) | + (static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8(hi, needle))) << 16); + return (mask & valid) != 0; +} +#endif + +// Reads the `index`-th `width`-bit value from `packed`, where values are packed +// most significant bit first. `width` must be at most 8 and `packed` must hold +// at least BytesForBits((index + 1) * width) bytes. +uint8_t UnpackValue(std::span packed, size_t index, uint8_t width) { + size_t bit_pos = index * width; + size_t byte_pos = bit_pos / 8; + uint32_t window = static_cast(packed[byte_pos]) << 8; + if (byte_pos + 1 < packed.size()) { + window |= packed[byte_pos + 1]; + } + uint32_t shift = 16 - static_cast(bit_pos % 8) - width; + return static_cast((window >> shift) & ((1U << width) - 1)); +} + +// Unpacks groups of 8 `Width`-bit values. Each group is exactly `Width` bytes, +// so it is read as one big-endian word and split with constant shifts; the +// compiler fully unrolls both inner loops. +template +void UnpackGroups(const uint8_t* in, uint8_t* out, size_t groups) { + constexpr uint64_t kMask = (uint64_t{1} << Width) - 1; + for (size_t group = 0; group < groups; ++group, in += Width, out += 8) { + uint64_t word = 0; + for (int i = 0; i < Width; ++i) { + word = (word << 8) | in[i]; + } + for (int i = 0; i < 8; ++i) { + out[i] = static_cast((word >> (Width * (7 - i))) & kMask); + } + } +} + +// Unpacks `out.size()` `width`-bit values from `packed`, which must hold at +// least BytesForBits(out.size() * width) bytes. +void UnpackBits(std::span packed, uint8_t width, std::span out) { + const size_t count = out.size(); + if (width == 0) { + std::ranges::fill(out, 0); + return; + } + if (width == 8) { + std::memcpy(out.data(), packed.data(), count); + return; + } + + const size_t groups = count / 8; + size_t unpacked = groups * 8; + switch (width) { + case 1: + UnpackGroups<1>(packed.data(), out.data(), groups); + break; + case 2: + UnpackGroups<2>(packed.data(), out.data(), groups); + break; + case 3: + UnpackGroups<3>(packed.data(), out.data(), groups); + break; + case 4: + UnpackGroups<4>(packed.data(), out.data(), groups); + break; + case 5: + UnpackGroups<5>(packed.data(), out.data(), groups); + break; + case 6: + UnpackGroups<6>(packed.data(), out.data(), groups); + break; + case 7: + UnpackGroups<7>(packed.data(), out.data(), groups); + break; + default: + unpacked = 0; + break; + } + for (size_t i = unpacked; i < count; ++i) { + out[i] = UnpackValue(packed, i, width); + } +} + +// Decodes one PFOR chunk of `out.size()` values from `data`, returning the +// number of bytes read. +Result DecodePforChunk(std::span data, std::span out) { + const size_t count = out.size(); + ICEBERG_PRECHECK(data.size() >= kPforHeaderSize, + "Truncated PFOR chunk header: {} bytes available, {} required", + data.size(), kPforHeaderSize); + + const uint8_t b1 = data[0] & 0x0F; + const uint8_t b2 = data[0] >> 4; + const uint8_t exception_count = data[1]; + const uint8_t base = data[2]; + + ICEBERG_PRECHECK(b1 <= 8, "Invalid PFOR primary bit width: {} (must be <= 8)", b1); + ICEBERG_PRECHECK(b2 <= 8 - b1, + "Invalid PFOR exception bit width: {} (must be <= {} when b1 is {})", + b2, 8 - b1, b1); + ICEBERG_PRECHECK(b1 != 8 || exception_count == 0, + "Invalid PFOR chunk: {} exceptions with 8-bit primary values", + exception_count); + ICEBERG_PRECHECK(exception_count <= count, + "Invalid PFOR chunk: {} exceptions for {} values", exception_count, + count); + + const size_t primary_size = BytesForBits(count * b1); + const size_t exception_values_size = BytesForBits(size_t{exception_count} * b2); + const size_t chunk_size = + kPforHeaderSize + primary_size + exception_count + exception_values_size; + ICEBERG_PRECHECK(data.size() >= chunk_size, + "Truncated PFOR chunk: {} bytes available, {} required", data.size(), + chunk_size); + + UnpackBits(data.subspan(kPforHeaderSize, primary_size), b1, out); + + if (exception_count > 0) { + auto exception_offsets = + data.subspan(kPforHeaderSize + primary_size, exception_count); + std::array exception_values; + UnpackBits(data.subspan(kPforHeaderSize + primary_size + exception_count, + exception_values_size), + b2, std::span(exception_values).first(exception_count)); + for (size_t i = 0; i < exception_count; ++i) { + const uint8_t offset = exception_offsets[i]; + ICEBERG_PRECHECK(offset < count, + "Invalid PFOR exception offset: {} (chunk has {} values)", offset, + count); + // b1 + b2 <= 8, so the patched value still fits in a byte. + out[offset] |= static_cast(exception_values[i] << b1); + } + } + + if (base != 0) { + // Check for overflow once with a max reduction, then add the base; both + // loops vectorize. + uint8_t max_value = 0; + for (uint8_t value : out) { + max_value = std::max(max_value, value); + } + if (max_value > 0xFF - base) [[unlikely]] { + const auto it = + std::ranges::find_if(out, [&](uint8_t v) { return v > 0xFF - base; }); + return InvalidArgument("Invalid PFOR value at index {}: {} exceeds 255", + it - out.begin(), uint32_t{*it} + base); + } + for (uint8_t& value : out) { + value = static_cast(value + base); + } + } + + return chunk_size; +} + +} // namespace + +Result DecodePforBytes(std::span data, std::span out) { + size_t bytes_read = 0; + for (size_t values_read = 0; values_read < out.size(); values_read += kPforChunkSize) { + const size_t chunk_length = std::min(kPforChunkSize, out.size() - values_read); + ICEBERG_ASSIGN_OR_RAISE(auto chunk_size, + DecodePforChunk(data.subspan(bytes_read), + out.subspan(values_read, chunk_length))); + bytes_read += chunk_size; + } + return bytes_read; +} + +Result MumblingBitmapView::Make(std::span bytes) { + ICEBERG_PRECHECK(bytes.size() >= kHeaderSize, + "Truncated Mumbling bitmap header: {} bytes available, {} required", + bytes.size(), kHeaderSize); + if (bytes[0] != kVersion) { + return NotSupported("Unsupported Mumbling bitmap version: {}", bytes[0]); + } + + const int64_t cardinality = static_cast(bytes[1]) | + (static_cast(bytes[2]) << 8) | + (static_cast(bytes[3]) << 16); + const int32_t container_count = + static_cast(bytes[4]) | (static_cast(bytes[5]) << 8); + ICEBERG_PRECHECK(container_count <= kMaxContainerCount, + "Invalid Mumbling bitmap container count: {} (must be <= {})", + container_count, kMaxContainerCount); + ICEBERG_PRECHECK(cardinality <= kMaxCardinality, + "Invalid Mumbling bitmap cardinality: {} (must be <= {})", cardinality, + kMaxCardinality); + + MumblingBitmapView bitmap; + bitmap.cardinality_ = cardinality; + bitmap.containers_.resize(container_count); + + // Decode into a stack buffer; only the offset table is kept. + std::array descriptors; + ICEBERG_ASSIGN_OR_RAISE(auto descriptors_size, + DecodePforBytes(bytes.subspan(kHeaderSize), + std::span(descriptors).first(container_count))); + + // Branch-free: a dense descriptor (type 001) is 32 bytes and a sparse one + // (type 000) is its own value. Reserved types are checked once at the end. + uint32_t offset = 0; + uint8_t reserved_bits = 0; + for (int32_t i = 0; i < container_count; ++i) { + const uint8_t descriptor = descriptors[i]; + reserved_bits |= descriptor; + const uint8_t length = (descriptor & kDenseTypeBit) != 0 ? kDenseLength : descriptor; + bitmap.containers_[i] = (offset << kOffsetShift) | length; + offset += length; + } + if ((reserved_bits & kReservedTypeBits) != 0) [[unlikely]] { + const auto* it = + std::find_if(descriptors.data(), descriptors.data() + container_count, + [](uint8_t d) { return (d & kReservedTypeBits) != 0; }); + return InvalidArgument( + "Invalid Mumbling bitmap descriptor for container {}: {:#04x} (not sparse " + "or dense)", + it - descriptors.data(), *it); + } + + // Every container must be inside the buffer; this is what makes the + // accessors memory-safe. + const size_t containers_start = kHeaderSize + descriptors_size; + const size_t available = bytes.size() - containers_start; + ICEBERG_PRECHECK(available >= offset, + "Truncated Mumbling bitmap containers: {} bytes available, {} " + "required", + available, offset); + ICEBERG_PRECHECK(available == offset, + "Invalid Mumbling bitmap: {} unexpected trailing bytes", + available - offset); + + bitmap.containers_data_ = bytes.subspan(containers_start, offset); + return bitmap; +} + +bool MumblingBitmapView::SparseContains(uint32_t ref, uint8_t pos_in_container) const { + const uint32_t offset = ref >> kOffsetShift; + const uint8_t* container = containers_data_.data() + offset; + const auto length = static_cast(ref & kLengthMask); + // Compare 32 bytes at once when they are all inside the buffer (every + // container except those in the last 31 bytes of the bitmap). + if (containers_data_.size() - offset >= kDenseLength) { +#if ICEBERG_MUMBLING_SSE2 + return ContainsByte32Sse2(container, length, pos_in_container); +#else + // A plain loop is faster than SWAR for short containers. + if (length >= 8) { + return ContainsByte32(container, length, pos_in_container); + } +#endif + } + return ContainsByte(container, length, pos_in_container); +} + +} // namespace iceberg diff --git a/src/iceberg/util/mumbling_bitmap_internal.h b/src/iceberg/util/mumbling_bitmap_internal.h new file mode 100644 index 0000000000..7bc256a157 --- /dev/null +++ b/src/iceberg/util/mumbling_bitmap_internal.h @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#pragma once + +/// \file iceberg/util/mumbling_bitmap_internal.h +/// A read-only Mumbling bitmap, the compressed bitmap format used by v4 +/// manifest deletion vectors. +/// +/// Spec: https://github.com/apache/iceberg/blob/main/format/mumbling-spec.md +/// The spec is still a draft, so this header is internal and not installed. + +#include +#include +#include +#include + +#include "iceberg/iceberg_export.h" +#include "iceberg/result.h" + +namespace iceberg { + +/// \brief Decodes a PFOR-encoded array of unsigned bytes. +/// +/// Implements "Appendix A: PFOR encoding for unsigned bytes" of the Mumbling +/// bitmap spec +/// (https://github.com/apache/iceberg/blob/main/format/mumbling-spec.md#appendix-a-pfor-encoding-for-unsigned-bytes), +/// which encodes the descriptor array. Values are encoded in +/// chunks of 256 values (the last chunk holds the remainder), each with a +/// 3-byte header, a bit-packed primary array, exception offsets and +/// bit-packed exception values. +/// +/// \param data the encoded bytes; may extend past the encoded values. +/// \param out receives the decoded values; its size is the number of values. +/// \return the number of bytes read from `data`, or an error if the input is +/// malformed or truncated. +ICEBERG_EXPORT Result DecodePforBytes(std::span data, + std::span out); + +/// \brief A read-only view of a serialized Mumbling bitmap (format version 1). +/// +/// Positions are split into a container index (the most significant bits) and +/// an 8-bit position within a 256-bit container. Each container is either +/// sparse (0-31 sorted one-byte positions) or dense (a 32-byte bit set). +/// +/// The view does not copy the bitmap: the serialized bytes must outlive it, +/// like the characters of a `std::string_view`. In v4 scan planning these are +/// the bytes of `manifest_info.dv` owned by the root manifest entry. +/// +/// Make() checks everything that memory safety depends on (header limits, +/// the PFOR descriptor array, descriptor types and the container sizes against +/// the buffer), so the accessors cannot fail and never read out of bounds. +/// Like the Java reader, it does not check container contents: for input that +/// breaks the spec's content rules (unsorted sparse positions, dense +/// containers with fewer than 32 positions, a wrong header cardinality), +/// Contains() still reports whether the position is stored, and Cardinality() +/// returns the header value. +class ICEBERG_EXPORT MumblingBitmapView { + public: + /// \brief The only supported format version. + static constexpr uint8_t kVersion = 1; + /// \brief Maximum number of containers in a bitmap. + static constexpr int32_t kMaxContainerCount = 8192; + /// \brief Number of positions covered by one container. + static constexpr int32_t kContainerSize = 256; + /// \brief Maximum number of set positions (8192 containers of 256 bits). + static constexpr int32_t kMaxCardinality = kMaxContainerCount * kContainerSize; + + /// \brief Creates a view of a serialized bitmap. `bytes` must outlive the + /// view and must hold exactly one bitmap. + static Result Make(std::span bytes); + + /// \brief Returns true if `pos` is set. Positions that are negative or past + /// the last container are never set. + /// + /// Inline so the common cases (out of range, empty or dense container) cost + /// a few instructions in the caller's loop; only sparse containers are + /// searched out of line. + bool Contains(int64_t pos) const { + if (pos < 0 || pos >= static_cast(containers_.size()) * kContainerSize) { + return false; + } + const uint32_t ref = containers_[static_cast(pos / kContainerSize)]; + const auto length = static_cast(ref & kLengthMask); + if (length == 0) { + return false; + } + const auto pos_in_container = static_cast(pos % kContainerSize); + if (length == kDenseLength) { + const uint8_t byte = containers_data_[(ref >> kOffsetShift) + pos_in_container / 8]; + return ((byte >> (7 - pos_in_container % 8)) & 1) != 0; + } + return SparseContains(ref, pos_in_container); + } + + /// \brief Returns the number of set positions, as stored in the header. + int64_t Cardinality() const { return cardinality_; } + + private: + // Each entry of `containers_` packs a container's byte offset in + // `containers_data_` (upper bits) with its length in bytes (low 8 bits): + // 0-31 for a sparse container, or kDenseLength for a dense one. + static constexpr uint32_t kOffsetShift = 8; + static constexpr uint32_t kLengthMask = 0xFF; + static constexpr uint8_t kDenseLength = 32; + + MumblingBitmapView() = default; + + // Searches the sparse container described by `ref`. + bool SparseContains(uint32_t ref, uint8_t pos_in_container) const; + + std::vector containers_; + // The container section of the caller's buffer (not owned). + std::span containers_data_; + int64_t cardinality_ = 0; +}; + +} // namespace iceberg From a4d65f0fb6fdf49055bb9dd68b5000c365edb70e Mon Sep 17 00:00:00 2001 From: Xiening Dai Date: Sat, 14 Nov 2026 21:04:13 +0000 Subject: [PATCH 2/2] fix(util): address clang-tidy warnings in Mumbling tests and benchmark Use KeepRunning() loops and an explicit main() in the benchmark (as in benchmark_smoke.cc), std::cmp_less for signed/unsigned comparisons, and std::array instead of a C array in the test. --- .../benchmark/mumbling_bitmap_benchmark.cc | 23 ++++++++++++------- .../test/mumbling_bitmap_internal_test.cc | 7 +++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/iceberg/benchmark/mumbling_bitmap_benchmark.cc b/src/iceberg/benchmark/mumbling_bitmap_benchmark.cc index 770d40ba5d..a33c96eb51 100644 --- a/src/iceberg/benchmark/mumbling_bitmap_benchmark.cc +++ b/src/iceberg/benchmark/mumbling_bitmap_benchmark.cc @@ -98,7 +98,7 @@ void BM_PforDecode(benchmark::State& state, PforData data) { const auto count = static_cast(state.range(0)); const Bytes encoded = EncodePfor(MakePforValues(data, count), kBestWidth); std::vector out(count); - for (auto _ : state) { + while (state.KeepRunning()) { auto result = DecodePforBytes(encoded, out); benchmark::DoNotOptimize(result); benchmark::DoNotOptimize(out.data()); @@ -140,7 +140,7 @@ std::vector MakePositions(Layout layout, int64_t cardinality) { std::mt19937_64 rng(kSeed); std::uniform_int_distribution dist(0, kMaxPositions - 1); std::set unique; - while (static_cast(unique.size()) < cardinality) { + while (std::cmp_less(unique.size(), cardinality)) { unique.insert(dist(rng)); } positions.assign(unique.begin(), unique.end()); @@ -150,9 +150,8 @@ std::vector MakePositions(Layout layout, int64_t cardinality) { // Spread the runs so that they cover the whole range when possible. const int64_t runs = (cardinality + 63) / 64; const int64_t stride = std::max(64, (kMaxPositions / runs) / 64 * 64); - for (int64_t run = 0; static_cast(positions.size()) < cardinality; ++run) { - for (int64_t i = 0; - i < 64 && static_cast(positions.size()) < cardinality; ++i) { + for (int64_t run = 0; std::cmp_less(positions.size(), cardinality); ++run) { + for (int64_t i = 0; i < 64 && std::cmp_less(positions.size(), cardinality); ++i) { positions.push_back(run * stride + i); } } @@ -198,7 +197,7 @@ void MaxBitmapArgs(benchmark::internal::Benchmark* b) { // the container offset table. void BM_Make(benchmark::State& state, Layout layout) { const Bytes bytes = MakeBitmapBytes(state, layout); - for (auto _ : state) { + while (state.KeepRunning()) { auto bitmap = MumblingBitmapView::Make(bytes); benchmark::DoNotOptimize(bitmap); } @@ -220,7 +219,7 @@ void BM_Contains(benchmark::State& state, Layout layout) { probe = dist(rng); } - for (auto _ : state) { + while (state.KeepRunning()) { int64_t hits = 0; for (int64_t probe : probes) { hits += bitmap.Contains(probe) ? 1 : 0; @@ -235,4 +234,12 @@ MUMBLING_BITMAP_BENCHMARK(BM_Contains); } // namespace } // namespace iceberg -BENCHMARK_MAIN(); +int main(int argc, char** argv) { + benchmark::Initialize(&argc, argv); + if (benchmark::ReportUnrecognizedArguments(argc, argv)) { + return 1; + } + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/src/iceberg/test/mumbling_bitmap_internal_test.cc b/src/iceberg/test/mumbling_bitmap_internal_test.cc index f1d4b92992..5ea2f6d835 100644 --- a/src/iceberg/test/mumbling_bitmap_internal_test.cc +++ b/src/iceberg/test/mumbling_bitmap_internal_test.cc @@ -20,6 +20,7 @@ #include "iceberg/util/mumbling_bitmap_internal.h" #include +#include #include #include #include @@ -130,8 +131,8 @@ std::vector ReadFixtures(const std::string& file_name, int hex_column) if (tab == std::string::npos) { continue; } - std::string_view columns[] = {std::string_view(line).substr(0, tab), - std::string_view(line).substr(tab + 1)}; + std::array columns = {std::string_view(line).substr(0, tab), + std::string_view(line).substr(tab + 1)}; fixtures.push_back({.line = line_number, .bytes = ParseHex(columns[hex_column]), .values = ParseValues(columns[1 - hex_column])}); @@ -411,7 +412,7 @@ TEST(MumblingBitmapTest, RandomRoundTrip) { // Pick a density: empty, sparse, borderline or dense. const int target = std::vector{0, 3, 31, 32, 100, 256}[rng() % 6]; std::set in_container; - while (static_cast(in_container.size()) < target) { + while (std::cmp_less(in_container.size(), target)) { in_container.insert(static_cast(rng() % 256)); } for (int pos : in_container) {