Commit a22e2d17 authored by Morag Jean Williams's avatar Morag Jean Williams
Browse files

Merge branch 'clicpix2_Decoder' into 'master'

Move CLICpix2 Decoder into Corryvreckan sources

See merge request !59
parents 0f969096 725a727a
Pipeline #586095 failed with stages
in 19 minutes and 48 seconds
# - Try to find the Peary framework
# Once done this will define
# Peary_FOUND - System has Peary
# Peary_INCLUDE_DIRS - The Peary main include directory
# Peary_LIBRARIES - The libraries for Peary and the required components
MESSAGE(STATUS "Looking for Peary...")
FIND_PATH(Peary_INCLUDE_DIR NAMES "peary/device/device.hpp" PATHS "$ENV{PEARYPATH}")
FIND_LIBRARY(Peary_LIBRARIES NAMES "peary" HINTS "$ENV{PEARYPATH}/lib")
LIST(APPEND Peary_INCLUDE_DIRS "${Peary_INCLUDE_DIR}/peary/utils")
LIST(APPEND Peary_INCLUDE_DIRS "${Peary_INCLUDE_DIR}/devices")
IF(Peary_FIND_COMPONENTS)
FOREACH(component ${Peary_FIND_COMPONENTS})
FIND_PATH(Peary_COMP_INCLUDE NAMES "devices/${component}/${component}Device.hpp" PATHS "$ENV{PEARYPATH}")
FIND_LIBRARY(Peary_COMP_LIB NAMES "PearyDevice${component}" HINTS "$ENV{PEARYPATH}/lib")
IF(Peary_COMP_INCLUDE AND Peary_COMP_LIB)
LIST(APPEND Peary_LIBRARIES "${Peary_COMP_LIB}")
LIST(APPEND Peary_INCLUDE_DIRS "${Peary_COMP_INCLUDE}/devices/${component}")
SET(Peary_${component}_FOUND TRUE)
MESSAGE(STATUS "Looking for Peary component: ${component} -- Found")
ELSE()
MESSAGE(STATUS "Looking for Peary component: ${component} -- NOTFOUND")
ENDIF()
ENDFOREACH()
ENDIF()
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Peary REQUIRED_VARS Peary_INCLUDE_DIRS Peary_LIBRARIES
HANDLE_COMPONENTS
FAIL_MESSAGE "Could not find Peary or a required component, make sure all necessary components are compiled and that the variable PEARYPATH points to the installation location:\n$ export PEARYPATH=/your/path/to/Peary\n")
// Implementation of the CLICpix2 frame decoder
#include "clicpix2_frameDecoder.hpp"
#include <cmath>
#include <iterator>
using namespace caribou;
const clicpix2_frameDecoder::WORD_TYPE clicpix2_frameDecoder::DELIMITER(1, 0xf7);
clicpix2_frameDecoder::clicpix2_frameDecoder(const bool pixelCompression,
const bool DCandSuperPixelCompression,
const std::map<std::pair<uint8_t, uint8_t>, pixelConfig>& pixel_conf)
: pixelCompressionEnabled(pixelCompression), DCandSuperPixelCompressionEnabled(DCandSuperPixelCompression) {
// Resolve and store long-counter states:
for(const auto& pixel : pixel_conf) {
counter_config[pixel.first.first][pixel.first.second] = pixel.second.GetLongCounter();
}
}
void clicpix2_frameDecoder::decode(const std::vector<uint32_t>& frame, bool decodeCnt) {
std::vector<WORD_TYPE> dataVector = repackageFrame(frame);
if(dataVector.empty()) {
throw caribou::DataException("Frame is empty");
}
auto data = dataVector.cbegin();
auto dataEnd = dataVector.cend();
do {
decodeHeader(*data++); // header
extractColumns(data, dataEnd);
} while(std::distance(data, dataEnd) && !(std::distance(data, dataEnd) == 1 && *data == DELIMITER));
if(decodeCnt)
decodeCounter();
}
pearydata clicpix2_frameDecoder::getZerosuppressedFrame() {
pearydata decframe;
for(size_t r = 0; r < static_cast<size_t>(clicpix2_frameDecoder::CLICPIX2_ROW); ++r) {
for(size_t c = 0; c < static_cast<size_t>(clicpix2_frameDecoder::CLICPIX2_COL); ++c) {
// Only return pixels with flag set:
if(matrix[r][c].GetFlag()) {
decframe[std::make_pair(c, r)] = std::make_unique<pixelReadout>(matrix[r][c]);
continue;
}
}
}
return decframe;
}
std::vector<clicpix2_frameDecoder::WORD_TYPE> clicpix2_frameDecoder::repackageFrame(const std::vector<uint32_t>& frame) {
std::vector<WORD_TYPE> data;
for(auto const& it : frame) {
data.emplace_back((it >> 17) & 0x1, (it >> 8) & 0xFF); // MSByte
data.emplace_back((it >> 16) & 0x1, it & 0xFF); // LSByte
}
return data;
}
void clicpix2_frameDecoder::decodeHeader(const clicpix2_frameDecoder::WORD_TYPE word) {
if(word.is_control != 0) {
throw DataException("Packet header should be a regular data word: " + to_hex_string(word.word));
}
rcr = (word.word >> 6) & 0x3;
if(rcr == 0) {
throw DataException("Unsupported RCR in packet header");
}
firstColumn = word.word & 0x1F;
}
void clicpix2_frameDecoder::extractColumns(std::vector<clicpix2_frameDecoder::WORD_TYPE>::const_iterator& data,
std::vector<clicpix2_frameDecoder::WORD_TYPE>::const_iterator dataEnd) {
std::array<std::array<pixelReadout, 8>, CLICPIX2_ROW * 2>
pixels_dc; // stores results of the processed doube columns (up to 8 if compression is enabled)
std::array<size_t, 8> row_index = {
{0, 0, 0, 0, 0, 0, 0, 0}}; // 8 independent (in case of compression) counters navigating through the <pixels_dc>
std::array<int, 8> row_slice = {{CLICPIX2_PIXEL_SIZE - 1,
CLICPIX2_PIXEL_SIZE - 1,
CLICPIX2_PIXEL_SIZE - 1,
CLICPIX2_PIXEL_SIZE - 1,
CLICPIX2_PIXEL_SIZE - 1,
CLICPIX2_PIXEL_SIZE - 1,
CLICPIX2_PIXEL_SIZE - 1,
CLICPIX2_PIXEL_SIZE - 1}}; // 8 independent
//(in case of compression) counters navigating through the <pixels_dc>
#define DC_COUNTER_INIT (2 * (CLICPIX2_ROW * CLICPIX2_PIXEL_SIZE + CLICPIX2_ROW / CLICPIX2_SUPERPIXEL_SIZE) + 1)
std::array<unsigned int, 8> dc_counter = {{DC_COUNTER_INIT,
DC_COUNTER_INIT,
DC_COUNTER_INIT,
DC_COUNTER_INIT,
DC_COUNTER_INIT,
DC_COUNTER_INIT,
DC_COUNTER_INIT,
DC_COUNTER_INIT}}; // 8 independent (in case of compression)
// counters indicating number of bits processed for the given double-column
// value 3601 indicates beginning of the double-column
std::array<unsigned int, 8> sp_counter = {
{0, 0, 0, 0, 0, 0, 0, 0}}; // number of pixels in the processed super-pixel for the given column
do {
WORD_TYPE word = *data++;
if(word == DELIMITER) // end of double column
break;
if(word.is_control)
throw DataException("Found control word different than delimiter");
unraveDC(pixels_dc, dc_counter, sp_counter, row_index, row_slice, word.word);
} while(std::distance(data, dataEnd));
for(unsigned int i = 0; i < static_cast<unsigned int>(1 << rcr); i++)
if(dc_counter[i] != 3600)
throw DataException("Partial double column");
// remove snake pattern
for(unsigned int r = 0; r < 256; ++r)
for(unsigned int c = 0; c < static_cast<unsigned int>(1 << rcr); c++) {
switch(r % 4) {
case 0:
matrix[r / 2][c * 2 * 64 / static_cast<unsigned int>(1 << rcr) +
static_cast<unsigned int>(firstColumn * 2)] = pixels_dc[r][c];
break; // left column
case 1:
matrix[r / 2][c * 2 * 64 / static_cast<unsigned int>(1 << rcr) + static_cast<unsigned int>(firstColumn * 2) +
1] = pixels_dc[r][c];
break; // right column
case 2:
matrix[r / 2][c * 2 * 64 / static_cast<unsigned int>(1 << rcr) + static_cast<unsigned int>(firstColumn * 2) +
1] = pixels_dc[r][c];
break; // right column
case 3:
matrix[r / 2][c * 2 * 64 / static_cast<unsigned int>(1 << rcr) +
static_cast<unsigned int>(firstColumn * 2)] = pixels_dc[r][c];
break; // left column
default:
throw DataException("Invalid row ID");
break;
}
}
}
void clicpix2_frameDecoder::unraveDC(std::array<std::array<pixelReadout, 8>, CLICPIX2_ROW * 2>& pixels_dc,
std::array<unsigned int, 8>& dc_counter,
std::array<unsigned int, 8>& sp_counter,
std::array<size_t, 8>& row_index,
std::array<int, 8>& row_slice,
const uint8_t data) {
// unravel the double-columns
switch(rcr) {
case 1:
for(int i = 0; i < 8; i += 2) {
processDCbit(pixels_dc, dc_counter[0], sp_counter[0], row_index[0], 0, row_slice[0], (data >> (i)) & 0x1);
processDCbit(pixels_dc, dc_counter[1], sp_counter[1], row_index[1], 1, row_slice[1], (data >> (i + 1)) & 0x1);
}
break;
case 2:
for(int i = 0; i < 8; i += 4) {
processDCbit(pixels_dc, dc_counter[0], sp_counter[0], row_index[0], 0, row_slice[0], (data >> (i)) & 0x1);
processDCbit(pixels_dc, dc_counter[1], sp_counter[1], row_index[1], 1, row_slice[1], (data >> (i + 1)) & 0x1);
processDCbit(pixels_dc, dc_counter[2], sp_counter[2], row_index[2], 2, row_slice[2], (data >> (i + 2)) & 0x1);
processDCbit(pixels_dc, dc_counter[3], sp_counter[3], row_index[3], 3, row_slice[3], (data >> (i + 3)) & 0x1);
}
break;
case 3:
for(int i = 0; i < 8; i += 8) {
processDCbit(pixels_dc, dc_counter[0], sp_counter[0], row_index[0], 0, row_slice[0], (data >> (i)) & 0x1);
processDCbit(pixels_dc, dc_counter[1], sp_counter[1], row_index[1], 1, row_slice[1], (data >> (i + 1)) & 0x1);
processDCbit(pixels_dc, dc_counter[2], sp_counter[2], row_index[2], 2, row_slice[2], (data >> (i + 2)) & 0x1);
processDCbit(pixels_dc, dc_counter[3], sp_counter[3], row_index[3], 3, row_slice[3], (data >> (i + 3)) & 0x1);
processDCbit(pixels_dc, dc_counter[4], sp_counter[4], row_index[4], 4, row_slice[4], (data >> (i + 4)) & 0x1);
processDCbit(pixels_dc, dc_counter[5], sp_counter[5], row_index[5], 5, row_slice[5], (data >> (i + 5)) & 0x1);
processDCbit(pixels_dc, dc_counter[6], sp_counter[6], row_index[6], 6, row_slice[6], (data >> (i + 6)) & 0x1);
processDCbit(pixels_dc, dc_counter[7], sp_counter[7], row_index[7], 7, row_slice[7], (data >> (i + 7)) & 0x1);
}
break;
default:
throw DataException("Invalid RCR");
break;
}
}
void clicpix2_frameDecoder::processDCbit(std::array<std::array<pixelReadout, 8>, CLICPIX2_ROW * 2>& pixels_dc,
unsigned int& dc_counter,
unsigned int& sp_counter,
size_t& row_index,
const size_t col_index,
int& row_slice,
const bool data) {
// middle of the double-column
if(dc_counter < static_cast<int>(2 * (CLICPIX2_ROW * CLICPIX2_PIXEL_SIZE + CLICPIX2_ROW / CLICPIX2_SUPERPIXEL_SIZE))) {
// super-pixel bit
if(sp_counter == 0) {
if(data || // not empty super-pixel
!DCandSuperPixelCompressionEnabled) { // or sp compression disabled
dc_counter++;
sp_counter++;
} else { // empty super-pixel
for(auto i = 0; i < static_cast<int>(CLICPIX2_SUPERPIXEL_SIZE); ++i)
pixels_dc[row_index++][col_index].setLatches(0x00);
dc_counter += CLICPIX2_SUPERPIXEL_SIZE * CLICPIX2_PIXEL_SIZE + 1;
}
}
// pixel bit
else if(row_slice == static_cast<int>(CLICPIX2_PIXEL_SIZE - 1)) {
if(data || // not empty pixel
!pixelCompressionEnabled) { // or pixel compression disabled
pixels_dc[row_index][col_index].setLatches(data, static_cast<uint8_t>(row_slice--));
dc_counter++;
sp_counter++;
} else { // empty pixel
pixels_dc[row_index++][col_index].setLatches(0x00);
dc_counter += CLICPIX2_PIXEL_SIZE;
sp_counter += CLICPIX2_PIXEL_SIZE;
}
}
// pixel payload
else {
pixels_dc[row_index][col_index].setLatches(data, static_cast<uint8_t>(row_slice--));
dc_counter++;
sp_counter++;
if(row_slice < 0) {
row_index++;
row_slice = CLICPIX2_PIXEL_SIZE - 1;
}
}
if(sp_counter > static_cast<int>(CLICPIX2_SUPERPIXEL_SIZE * CLICPIX2_PIXEL_SIZE)) // reset sp_counter
sp_counter = 0;
}
// beginning of the double-column (dc_counter == 3601)
else {
if(dc_counter ==
static_cast<int>(2 * (CLICPIX2_ROW * CLICPIX2_PIXEL_SIZE + CLICPIX2_ROW / CLICPIX2_SUPERPIXEL_SIZE) + 1)) {
if(data || // not empty double-column
!DCandSuperPixelCompressionEnabled) // or collumn compression disabled
dc_counter = 0;
else { // empty double-column
for(auto i = 0; i < static_cast<int>(2 * CLICPIX2_ROW); ++i)
pixels_dc[row_index++][col_index].setLatches(0x00);
dc_counter = 2 * (CLICPIX2_ROW * CLICPIX2_PIXEL_SIZE + CLICPIX2_ROW / CLICPIX2_SUPERPIXEL_SIZE); // 3600
}
}
}
}
void clicpix2_frameDecoder::decodeCounter() {
for(size_t r = 0; r < static_cast<size_t>(clicpix2_frameDecoder::CLICPIX2_ROW); ++r) {
for(size_t c = 0; c < static_cast<size_t>(clicpix2_frameDecoder::CLICPIX2_COL); ++c) {
// Only decode pixels with a flag set
if(!matrix[r][c].GetFlag()) {
continue;
}
if(counter_config[r][c]) {
matrix[r][c].SetCounter(lfsr13_lut[matrix[r][c].GetLatches() & 0x1fff]);
} else {
matrix[r][c].SetTOT(lfsr5_lut[(matrix[r][c].GetLatches() >> 8) & 0x1f]);
matrix[r][c].SetTOA(lfsr8_lut[matrix[r][c].GetLatches() & 0xff]);
}
}
}
}
namespace caribou {
std::ostream& operator<<(std::ostream& out, const clicpix2_frameDecoder& decoder) {
for(size_t r = 0; r < static_cast<size_t>(clicpix2_frameDecoder::CLICPIX2_ROW); ++r)
for(size_t c = 0; c < static_cast<size_t>(clicpix2_frameDecoder::CLICPIX2_COL); ++c) {
out << "[" << r << "][" << c << "] [" << decoder.matrix[r][c] << "], ";
if(c % 64 == 63)
out << "\n";
}
return out;
}
} // namespace caribou
// CLICpix2 frame decoder
// Base on System Verilog CLICpix2_Readout_scoreboard
#ifndef CLICPIX2_FRAMEDECODER_HPP
#define CLICPIX2_FRAMEDECODER_HPP
#include <array>
#include <cstdint>
#include <ostream>
#include <vector>
#include "CLICpix2/clicpix2_pixels.hpp"
namespace caribou {
class clicpix2_frameDecoder {
// Internal class representing a SERDES word
class WORD_TYPE {
public:
WORD_TYPE(){};
WORD_TYPE(bool control, uint8_t w) : is_control(control), word(w){};
bool operator==(const WORD_TYPE& a) const { return a.is_control == is_control && a.word == word; };
bool operator!=(const WORD_TYPE& a) const { return a.is_control != is_control || a.word != word; };
bool is_control;
uint8_t word;
};
// Parameters of the Frame decoder
static const unsigned int CLICPIX2_ROW = 128;
static const unsigned int CLICPIX2_COL = 128;
// Number of pixels in the super-pixel
static const unsigned int CLICPIX2_SUPERPIXEL_SIZE = 16;
static const unsigned int CLICPIX2_PIXEL_SIZE = 14;
static const WORD_TYPE DELIMITER; // K23.7 Carrier extender
std::array<std::array<pixelReadout, CLICPIX2_COL>, CLICPIX2_ROW> matrix; //[row][column]
// repackage uint32_t words into WORD_TYPE
std::vector<WORD_TYPE> repackageFrame(const std::vector<uint32_t>& frame);
void decodeHeader(const WORD_TYPE word);
void extractColumns(std::vector<WORD_TYPE>::const_iterator& data, std::vector<WORD_TYPE>::const_iterator dataEnd);
void unraveDC(std::array<std::array<pixelReadout, 8>, CLICPIX2_ROW * 2>& pixels_dc,
std::array<unsigned int, 8>& dc_counter,
std::array<unsigned int, 8>& sp_counter,
std::array<size_t, 8>& row_index,
std::array<int, 8>& row_slice,
const uint8_t data);
void processDCbit(std::array<std::array<pixelReadout, 8>, CLICPIX2_ROW * 2>& pixels_dc,
unsigned int& dc_counter,
unsigned int& sp_counter,
size_t& row_index,
const size_t col_index,
int& row_slice,
const bool data);
void decodeCounter();
// current RCR register value
uint8_t rcr;
// firt column of the currently analyzed part of the package
uint16_t firstColumn;
// Configutation
bool pixelCompressionEnabled;
bool DCandSuperPixelCompressionEnabled;
std::map<size_t, std::map<size_t, bool>> counter_config; // [row][column]
public:
clicpix2_frameDecoder(const bool pixelCompressionEnabled,
const bool DCandSuperPixelCompressionEnabled,
const std::map<std::pair<uint8_t, uint8_t>, pixelConfig>& pixel_config);
void decode(const std::vector<uint32_t>& frame, bool decodeCnt = true);
pearydata getZerosuppressedFrame();
pixelReadout get(const unsigned int row, const unsigned int column) { return matrix[row][column]; };
static const uint16_t lfsr13_lut[8191];
static const uint8_t lfsr8_lut[255];
static const uint8_t lfsr5_lut[31];
/** Overloaded ostream operator for simple printing of pixel data
*/
friend std::ostream& operator<<(std::ostream& out, const clicpix2_frameDecoder& decoder);
};
} // namespace caribou
#endif
// It contains LUT for lfsr13 counter
#include "clicpix2_frameDecoder.hpp"
using namespace caribou;
const uint16_t clicpix2_frameDecoder::lfsr13_lut[8191] = {
0, 1, 2, 1857, 8101, 3, 1858, 7704, 8102, 3714, 3687, 4, 1859, 1932, 1989, 7705, 8103, 1622, 4757, 3715,
3688, 3115, 1370, 5, 5162, 1860, 1933, 3183, 1767, 1990, 7706, 719, 332, 8104, 1623, 3789, 7217, 4758, 3716, 3009,
3689, 3846, 6790, 3116, 1371, 8011, 7907, 6, 5163, 2731, 2490, 1861, 1934, 7614, 5544, 3184, 2234, 1768, 1991, 5818,
5571, 7707, 720, 6674, 1502, 333, 8105, 5040, 7553, 1624, 3790, 1721, 7218, 7019, 4284, 4759, 3717, 1881, 3597, 3010,
3690, 1445, 7757, 3847, 6791, 3162, 3624, 3117, 270, 1372, 8012, 2407, 2576, 7908, 7, 1509, 7366, 5164, 2732, 3227,
1099, 2491, 1862, 1899, 1935, 4972, 2450, 7615, 5545, 37, 671, 3185, 2235, 5863, 3200, 1769, 1992, 3325, 3479, 5819,
7444, 5572, 7708, 7876, 6614, 721, 6675, 1842, 7901, 1503, 334, 7401, 5325, 8106, 5041, 5448, 7554, 1280, 3896, 1625,
3791, 6857, 4077, 1722, 7219, 3025, 3978, 7020, 4285, 6069, 4588, 4760, 232, 3718, 1882, 3864, 4347, 3598, 3011, 4031,
4667, 3691, 1446, 7675, 7116, 7758, 3848, 192, 6792, 4091, 2696, 3163, 3625, 5006, 6548, 3118, 271, 6810, 5123, 1373,
8013, 3921, 7428, 2408, 1532, 2577, 7909, 4675, 340, 8, 1510, 7374, 2569, 7367, 5165, 1677, 7140, 2733, 3228, 5619,
1100, 1573, 4491, 2492, 1863, 7407, 4270, 1900, 1936, 2842, 4060, 4973, 2451, 4127, 456, 7616, 6562, 5546, 38, 629,
5703, 672, 3186, 1135, 1524, 2236, 5864, 2189, 4146, 3201, 1770, 5143, 1993, 5646, 5072, 3326, 3480, 2628, 5804, 5820,
7445, 6221, 3093, 5573, 7709, 7822, 4866, 7877, 860, 6615, 722, 5676, 883, 6676, 1843, 994, 1365, 7902, 1504, 1894,
5320, 335, 7402, 7817, 5326, 2528, 5331, 8107, 5042, 3908, 7094, 5449, 7555, 2708, 6644, 1281, 3897, 5998, 4307, 1626,
253, 3792, 6858, 1747, 6829, 4078, 1723, 7921, 5115, 7220, 3026, 1032, 5309, 3979, 7021, 734, 4286, 5084, 4252, 6070,
4589, 6844, 6700, 4761, 233, 5783, 2533, 3719, 1883, 4503, 3756, 3865, 6187, 4348, 3599, 50, 2956, 3012, 4032, 2884,
263, 4668, 3692, 5336, 4296, 1447, 7676, 6197, 7117, 5182, 8024, 7759, 3849, 7127, 2472, 193, 6793, 4157, 2651, 4092,
2697, 2919, 7720, 3164, 2462, 3626, 5007, 5277, 5057, 6549, 3119, 7151, 6802, 272, 6811, 1542, 242, 5124, 1374, 7564,
8014, 1110, 6572, 3922, 7429, 870, 2003, 2409, 1533, 7454, 1945, 2578, 7910, 7228, 280, 4676, 5172, 341, 9, 8112,
3699, 1511, 7375, 2244, 8005, 2570, 7368, 4264, 2522, 5166, 1678, 3548, 7141, 2127, 2144, 2734, 3229, 3611, 517, 5620,
1101, 6870, 5728, 1574, 4492, 6730, 4433, 2493, 5047, 1864, 7408, 1684, 3366, 4271, 1901, 5343, 3913, 1937, 2843, 5481,
2945, 4061, 4974, 8036, 2452, 5019, 6746, 4128, 457, 658, 4539, 7617, 6563, 3263, 3283, 5547, 39, 21, 3302, 630,
5267, 5704, 673, 6584, 1423, 3187, 1136, 4604, 2400, 1525, 2237, 3359, 6689, 5865, 2190, 2762, 4147, 6897, 3566, 3202,
1771, 2996, 2971, 5144, 1994, 7099, 6303, 5647, 5073, 4406, 3578, 3327, 2641, 3481, 2629, 5504, 1219, 5805, 5821, 7287,
7420, 7446, 6222, 3738, 5104, 3094, 5574, 6464, 7710, 5454, 3554, 7823, 4867, 706, 2156, 7878, 861, 7524, 6758, 6616,
723, 6429, 6141, 5677, 2909, 884, 6677, 1245, 685, 1844, 995, 4455, 3683, 1366, 7903, 2486, 5036, 1505, 1895, 7872,
5321, 228, 4663, 336, 7403, 2838, 5642, 7818, 5327, 4303, 6696, 2529, 5332, 7147, 7560, 8108, 2518, 5043, 3909, 1419,
2992, 7095, 5450, 6425, 4659, 7556, 2709, 2313, 4422, 6645, 1282, 4733, 3898, 5984, 180, 5999, 4308, 1399, 2713, 1627,
254, 472, 2317, 3793, 6859, 154, 4699, 1748, 7084, 6830, 4079, 3133, 5917, 1724, 7922, 5376, 1438, 5116, 7221, 4426,
2511, 3027, 1033, 8131, 5310, 3534, 7389, 3980, 7022, 6399, 392, 735, 4287, 3072, 1181, 5085, 4253, 6499, 7476, 6071,
7807, 4590, 6845, 6957, 806, 6701, 4762, 6649, 7667, 234, 5784, 1073, 2133, 2534, 3720, 1286, 1884, 6127, 4019, 4504,
3757, 6366, 4325, 3866, 6188, 1479, 2017, 4349, 3600, 6029, 6348, 51, 1355, 2957, 3013, 4737, 3430, 4033, 2885, 4223,
3840, 264, 4669, 4485, 3902, 3693, 5337, 2150, 4297, 7661, 4479, 1448, 7677, 6412, 4194, 6198, 7118, 6714, 1963, 5183,
8025, 557, 6929, 7760, 5988, 3850, 7128, 3403, 7503, 2473, 194, 6103, 184, 6794, 4158, 3381, 4528, 2652, 4093, 3507,
2698, 4046, 444, 2920, 7721, 4720, 3662, 3165, 2463, 7741, 415, 3627, 5008, 6379, 7000, 5278, 6634, 5058, 6550, 5201,
6003, 3120, 7152, 1791, 7750, 6803, 273, 6723, 1412, 6812, 1543, 5881, 243, 1488, 3081, 5125, 1375, 4312, 481, 7565,
8015, 1403, 3244, 1111, 6573, 612, 8078, 3923, 1737, 7430, 871, 1323, 4950, 2004, 2410, 2286, 7108, 1534, 7455, 7533,
3272, 1946, 2579, 6879, 7911, 2717, 5792, 7229, 281, 7463, 4166, 4677, 5173, 5872, 1454, 342, 10, 1631, 2740, 8113,
6819, 3700, 1512, 3034, 2851, 7376, 2245, 6230, 3110, 8006, 2571, 1094, 5443, 7369, 4265, 4861, 2523, 5778, 258, 5167,
1679, 5476, 6298, 3549, 7142, 1394, 801, 2128, 2145, 1786, 476, 2735, 5773, 3230, 3612, 6172, 6980, 518, 5621, 1915,
6540, 1102, 6871, 6532, 6453, 5729, 1575, 531, 4493, 3389, 7541, 6731, 4434, 1081, 2321, 2494, 5048, 3235, 3797, 1865,
7409, 7683, 2197, 1685, 3746, 3367, 4272, 5489, 1040, 1902, 5344, 1550, 3617, 3914, 1938, 6863, 6418, 2844, 5482, 1015,
2946, 214, 7354, 4062, 4975, 7341, 2341, 8037, 2453, 8069, 7786, 5020, 6747, 1648, 4553, 4129, 2874, 458, 659, 979,
5948, 4540, 7618, 158, 4998, 6564, 3264, 6524, 1234, 3284, 5548, 4703, 40, 1341, 6602, 22, 3303, 73, 1150, 631,
5268, 603, 86, 5705, 674, 7066, 2049, 6585, 6177, 1424, 3188, 1752, 782, 1137, 4605, 4899, 6784, 2401, 1526, 1567,
7088, 2238, 3360, 700, 6690, 1067, 3834, 5866, 2191, 1009, 7270, 2763, 4148, 2277, 7989, 6898, 3567, 1809, 7305, 3203,
6834, 1772, 2997, 6985, 7182, 2972, 5145, 958, 4083, 1995, 7100, 523, 7276, 6304, 5648, 4200, 5074, 5934, 7602, 4407,
3579, 2675, 3055, 3328, 2642, 4941, 1705, 3482, 2630, 2170, 5753, 5505, 4242, 1220, 5806, 6484, 3137, 5822, 7288, 7940,
3155, 7421, 7447, 5721, 2985, 6223, 3739, 581, 5105, 2089, 1610, 3095, 5575, 5921, 2103, 6465, 7711, 1728, 5626, 5455,
3555, 2769, 6204, 7824, 1022, 4868, 707, 588, 5888, 2157, 7879, 8138, 2688, 862, 7525, 6445, 3348, 6759, 6617, 4805,
724, 7926, 1920, 6430, 6142, 2264, 2360, 5678, 2910, 1314, 2073, 885, 6678, 5380, 4882, 1246, 5299, 686, 1845, 7974,
5835, 996, 4456, 921, 8098, 3684, 1367, 5159, 329, 7904, 2487, 5568, 5037, 1878, 1442, 1506, 1896, 4969, 3322, 7873,
5322, 3893, 4585, 229, 4664, 6545, 5120, 337, 1674, 7404, 2839, 1132, 5140, 5643, 7819, 5673, 5317, 5328, 4304, 250,
5112, 6697, 2530, 2953, 5333, 7124, 4154, 7148, 7561, 1107, 7225, 8109, 2519, 2141, 4430, 5044, 3910, 4536, 3280, 1420,
3356, 2993, 7096, 7284, 6461, 5451, 6426, 1242, 5033, 4660, 7557, 2515, 4656, 2710, 2314, 5914, 4423, 6396, 3069, 6646,
1283, 6124, 6026, 4734, 3899, 4476, 6926, 5985, 181, 3659, 412, 6000, 6720, 4309, 1400, 2283, 6876, 2714, 1628, 3031,
5440, 255, 473, 5770, 6537, 2318, 3794, 1037, 6860, 7338, 8066, 155, 4700, 1338, 7063, 1749, 7085, 3831, 7302, 6831,
4080, 3052, 1702, 3134, 5718, 5918, 1725, 8135, 4802, 7923, 5377, 7971, 326, 1439, 5117, 1671, 5314, 7222, 4427, 6458,
2512, 6121, 4473, 3028, 1034, 7335, 3049, 8132, 5311, 4470, 5402, 3535, 7390, 2603, 5405, 3981, 3538, 7023, 6400, 898,
4379, 393, 736, 4916, 7393, 4288, 3073, 5468, 3885, 1182, 5086, 8157, 4254, 2374, 6091, 6500, 7477, 4825, 2606, 6072,
7808, 7652, 5408, 4591, 6846, 3447, 4001, 6958, 7995, 807, 6702, 1969, 3984, 4763, 6650, 5734, 1495, 7668, 235, 3541,
221, 5785, 1074, 2096, 2134, 6904, 5189, 2535, 3721, 7026, 1580, 1287, 1885, 6403, 6911, 6128, 4020, 102, 5223, 4505,
2117, 3758, 6367, 373, 7200, 4326, 3867, 901, 7893, 6189, 1480, 6290, 4577, 2018, 4350, 4382, 3601, 396, 3495, 6030,
6349, 7852, 5526, 52, 1356, 4185, 7637, 2958, 3014, 739, 536, 4738, 507, 3431, 4034, 4919, 7585, 2886, 4224, 7244,
1617, 3841, 265, 7361, 7396, 4670, 4486, 3088, 3903, 4498, 4291, 3694, 5338, 8031, 3573, 2151, 4298, 5979, 7802, 7662,
4480, 5196, 3076, 1449, 5471, 7678, 6413, 777, 953, 4195, 6199, 2355, 3888, 7119, 6715, 5435, 1666, 1964, 5184, 7632,
8026, 6263, 2826, 558, 6930, 3640, 1185, 7761, 5989, 3394, 5089, 3851, 7129, 6268, 765, 3404, 2935, 7504, 2474, 305,
8160, 195, 6104, 4631, 7546, 185, 6795, 4257, 2831, 4159, 3382, 1603, 4529, 5216, 5972, 2653, 4094, 2377, 1202, 3508,
2699, 6094, 2554, 4047, 445, 2789, 4619, 2921, 648, 7722, 4721, 3941, 355, 3663, 3166, 6503, 1272, 2464, 7742, 4853,
1124, 416, 3628, 7480, 5009, 4828, 142, 6380, 7001, 3957, 1164, 5279, 6635, 7494, 3774, 5059, 6551, 2609, 5423, 5202,
6736, 6004, 3121, 6075, 563, 7153, 1792, 1815, 3783, 7751, 6804, 2563, 7811, 274, 6724, 5098, 1413, 4013, 7655, 6813,
1544, 2335, 2669, 5882, 244, 6920, 7646, 1489, 3082, 5417, 5411, 5126, 4594, 1376, 4313, 4439, 7311, 482, 7566, 6935,
6849, 8016, 1404, 1086, 5665, 3245, 1112, 3645, 6574, 5595, 3418, 613, 8079, 5235, 3450, 3924, 1738, 548, 4004, 7431,
872, 5358, 130, 1324, 5257, 4951, 2005, 2218, 6961, 2411, 2287, 4362, 1714, 7109, 1535, 7998, 5635, 7456, 7534, 2082,
3273, 95, 7795, 1947, 2580, 810, 3806, 6880, 7912, 6705, 2326, 2718, 5793, 2026, 1190, 7230, 3253, 282, 7464, 1463,
424, 4167, 4678, 1972, 4069, 5174, 5873, 3102, 5132, 1455, 343, 3987, 11, 4766, 2499, 1632, 2741, 3209, 7766, 8114,
6820, 1954, 2660, 3701, 1513, 6653, 2542, 3035, 3292, 2852, 7377, 5737, 6312, 2246, 6231, 6767, 3710, 3111, 8007, 2727,
1498, 2572, 1095, 7440, 5444, 3860, 7671, 7370, 4266, 4056, 5068, 4862, 2524, 5994, 6840, 5779, 259, 5053, 238, 5168,
3544, 1680, 5477, 4600, 2967, 6299, 3550, 6137, 224, 7143, 1395, 468, 1434, 802, 2129, 1351, 2146, 3399, 3377, 1787,
477, 3240, 5788, 2736, 5774, 1782, 1077, 3231, 3613, 5944, 1230, 6173, 696, 6981, 519, 7936, 2099, 5622, 1916, 4878,
1874, 6541, 1103, 2137, 5029, 6872, 6533, 5714, 6454, 894, 5464, 5730, 1576, 6907, 3491, 532, 4494, 5192, 3636, 3390,
7542, 351, 1120, 6732, 5094, 4435, 1082, 4358, 3802, 2322, 2495, 2538, 3856, 5049, 3236, 1778, 1870, 3798, 1866, 3724,
7410, 7029, 1382, 7684, 2198, 5582, 4982, 1686, 3747, 6625, 5656, 3368, 4273, 1583, 5556, 5490, 3728, 1041, 1903, 1290,
4101, 5345, 1551, 2587, 3003, 3618, 3915, 7134, 1888, 1939, 6864, 7414, 6419, 4319, 6406, 2845, 5483, 7348, 5928, 1016,
2947, 406, 3441, 215, 7355, 3768, 6914, 4063, 6131, 4976, 7342, 7033, 817, 2342, 8038, 2384, 4023, 2454, 8070, 1386,
4961, 7787, 5021, 5605, 6748, 4776, 2862, 1649, 4554, 292, 105, 4130, 2875, 7732, 5226, 459, 660, 4838, 6014, 980,
7514, 5949, 4541, 6273, 4508, 7619, 159, 7688, 3590, 4999, 6565, 2120, 7865, 3265, 6525, 3148, 1235, 7193, 770, 3285,
5549, 3761, 2202, 4704, 41, 6370, 5586, 1342, 6603,