1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2024-12-13 19:50:34 +00:00

Merge branch 'reed-solomon' into next

This commit is contained in:
Carles Fernandez 2021-03-28 19:23:57 +02:00
commit f5a8790b3b
11 changed files with 1627 additions and 43 deletions

View File

@ -24,6 +24,7 @@
#include "galileo_almanac.h"
#include "galileo_almanac_helper.h"
#include "galileo_ephemeris.h"
#include "galileo_has_data.h"
#include "galileo_iono.h"
#include "galileo_utc_model.h"
#include "geojson_printer.h"
@ -500,6 +501,7 @@ rtklib_pvt_gs::rtklib_pvt_gs(uint32_t nchannels,
d_galileo_utc_model_sptr_type_hash_code = typeid(std::shared_ptr<Galileo_Utc_Model>).hash_code();
d_galileo_almanac_helper_sptr_type_hash_code = typeid(std::shared_ptr<Galileo_Almanac_Helper>).hash_code();
d_galileo_almanac_sptr_type_hash_code = typeid(std::shared_ptr<Galileo_Almanac>).hash_code();
d_galileo_has_message_sptr_type_hash_code = typeid(std::shared_ptr<Galileo_HAS_data>).hash_code();
d_glonass_gnav_ephemeris_sptr_type_hash_code = typeid(std::shared_ptr<Glonass_Gnav_Ephemeris>).hash_code();
d_glonass_gnav_utc_model_sptr_type_hash_code = typeid(std::shared_ptr<Glonass_Gnav_Utc_Model>).hash_code();
d_glonass_gnav_almanac_sptr_type_hash_code = typeid(std::shared_ptr<Glonass_Gnav_Almanac>).hash_code();
@ -1315,6 +1317,10 @@ void rtklib_pvt_gs::msg_handler_telemetry(const pmt::pmt_t& msg)
d_user_pvt_solver->galileo_almanac_map[galileo_alm->PRN] = *galileo_alm;
}
}
else if (msg_type_hash_code == d_galileo_has_message_sptr_type_hash_code)
{
// Store HAS message and print its content
}
// **************** GLONASS GNAV Telemetry *************************
else if (msg_type_hash_code == d_glonass_gnav_ephemeris_sptr_type_hash_code)

View File

@ -216,6 +216,7 @@ private:
size_t d_galileo_utc_model_sptr_type_hash_code;
size_t d_galileo_almanac_helper_sptr_type_hash_code;
size_t d_galileo_almanac_sptr_type_hash_code;
size_t d_galileo_has_message_sptr_type_hash_code;
size_t d_glonass_gnav_ephemeris_sptr_type_hash_code;
size_t d_glonass_gnav_utc_model_sptr_type_hash_code;
size_t d_glonass_gnav_almanac_sptr_type_hash_code;

View File

@ -26,6 +26,7 @@
#include "display.h"
#include "galileo_almanac_helper.h" // for Galileo_Almanac_Helper
#include "galileo_ephemeris.h" // for Galileo_Ephemeris
#include "galileo_has_data.h" // For Galileo HAS messages
#include "galileo_iono.h" // for Galileo_Iono
#include "galileo_utc_model.h" // for Galileo_Utc_Model
#include "gnss_synchro.h"
@ -512,15 +513,15 @@ void galileo_telemetry_decoder_gs::decode_CNAV_word(float *page_symbols, int32_t
// 4. If we have a new full message, read it
if (d_cnav_nav.have_new_HAS_message() == true)
{
// TODO: Retrieve data from message and send it somewhere
// Galileo_HAS_data has_data = d_cnav_nav.get_HAS_data();
if (d_cnav_nav.is_HAS_message_dummy())
if (d_cnav_nav.is_HAS_message_dummy() == true)
{
std::cout << TEXT_MAGENTA << "New Galileo E6 HAS message received in channel " << d_channel << " from satellite " << d_satellite << TEXT_RESET << '\n';
std::cout << TEXT_MAGENTA << "New Galileo E6 HAS dummy message received in channel " << d_channel << " from satellite " << d_satellite << TEXT_RESET << '\n';
}
else
{
std::cout << TEXT_MAGENTA << "New Galileo E6 HAS dummy message received in channel " << d_channel << " from satellite " << d_satellite << TEXT_RESET << '\n';
const std::shared_ptr<Galileo_HAS_data> tmp_obj = std::make_shared<Galileo_HAS_data>(d_cnav_nav.get_HAS_data());
this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
std::cout << TEXT_MAGENTA << "New Galileo E6 HAS message received in channel " << d_channel << " from satellite " << d_satellite << TEXT_RESET << '\n';
}
}
}
@ -649,7 +650,7 @@ int galileo_telemetry_decoder_gs::general_work(int noutput_items __attribute__((
if (abs(corr_value) >= d_samples_per_preamble)
{
d_preamble_index = d_sample_counter; // record the preamble sample stamp
DLOG(INFO) << "Preamble detection for Galileo satellite " << this->d_satellite;
LOG(INFO) << "Preamble detection for Galileo satellite " << this->d_satellite << " in channel " << this->d_channel;
d_stat = 1; // enter into frame pre-detection status
}
}

View File

@ -24,6 +24,7 @@ set(SYSTEM_PARAMETERS_SOURCES
glonass_gnav_ephemeris.cc
glonass_gnav_utc_model.cc
glonass_gnav_navigation_message.cc
reed_solomon.cc
)
set(SYSTEM_PARAMETERS_HEADERS
@ -82,6 +83,7 @@ set(SYSTEM_PARAMETERS_HEADERS
Beidou_B3I.h
Beidou_DNAV.h
MATH_CONSTANTS.h
reed_solomon.h
)
list(SORT SYSTEM_PARAMETERS_HEADERS)

View File

@ -68,14 +68,16 @@ constexpr int32_t GALILEO_CNAV_CRC_LENGTH = 24;
constexpr int32_t GALILEO_CNAV_MESSAGE_BITS_PER_PAGE = 424;
constexpr int32_t GALILEO_CNAV_PAGE_HEADER_BITS = 24;
constexpr int32_t GALILEO_CNAV_PREAMBLE_LENGTH_BITS = 16;
constexpr int32_t GALILEO_CNAV_MAX_NUMBER_ENCODED_BLOCKS = 255;
constexpr int32_t GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK = 255;
constexpr int32_t GALILEO_CNAV_MT1_HEADER_BITS = 32;
constexpr int32_t GALILEO_CNAV_OCTETS_IN_SUBPAGE = 53;
constexpr int32_t GALILEO_CNAV_INFORMATION_VECTOR_LENGTH = 32;
constexpr int32_t HAS_MSG_MAX_SATS = 40;
constexpr int32_t HAS_MSG_MAX_SIGNALS = 16;
constexpr uint8_t HAS_MSG_GPS_SYSTEM = 0; // Table 8 ICD
constexpr uint8_t HAS_MSG_GALILEO_SYSTEM = 2; // Table 8 ICD
constexpr uint8_t HAS_MSG_GPS_SYSTEM = 0; // Table 8 ICD v1.2
constexpr uint8_t HAS_MSG_GALILEO_SYSTEM = 2; // Table 8 ICD v1.2
constexpr char GALILEO_CNAV_PREAMBLE[17] = "1011011101110000";

View File

@ -3,14 +3,14 @@
* \brief Implementation of a Galileo CNAV Data message as described in
* Galileo High Accuracy Service E6-B Signal-In-Space Message Specification v1.2
* (April 2020)
* \author Carles Fernandez-Prades, 2020 cfernandez(at)cttc.es
* \author Carles Fernandez-Prades, 2020-2021 cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
@ -60,10 +60,11 @@ void Galileo_Cnav_Message::read_HAS_page(const std::string& page_string)
if (CRC_test(Word_for_CRC_bits, checksum.to_ulong()) == true)
{
d_flag_CRC_test = true;
// CRC correct: Read HAS page header
// CRC correct: Read 24 bits of HAS page header
read_HAS_page_header(page_string.substr(GALILEO_CNAV_PAGE_RESERVED_BITS, GALILEO_CNAV_PAGE_HEADER_BITS));
bool use_has = false;
d_test_mode = false;
// HAS status as defined in ICD v1.2 Table 5 HAS Page Header
switch (d_has_page_status)
{
case 0: // HAS is in Test Mode
@ -73,11 +74,14 @@ void Galileo_Cnav_Message::read_HAS_page(const std::string& page_string)
case 1: // HAS is in Operational Mode
use_has = true;
break;
case 2: // HAS is in "reserved" status
case 3: // Do not use HAS
default:
break;
}
if (use_has)
{
// Process the 424 bits of encoded data
process_HAS_page(page_string.substr(GALILEO_CNAV_PAGE_RESERVED_BITS + GALILEO_CNAV_PAGE_HEADER_BITS, GALILEO_CNAV_MESSAGE_BITS_PER_PAGE));
}
}
@ -101,11 +105,12 @@ void Galileo_Cnav_Message::read_HAS_page_header(const std::string& page_string)
}
if (!d_page_dummy)
{
// ICD v1.2 Table 5: HAS page header
const std::bitset<GALILEO_CNAV_PAGE_HEADER_BITS> has_page_header(page_string);
d_has_page_status = read_has_page_header_parameter(has_page_header, GALILEO_HAS_STATUS);
d_received_message_type = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_TYPE);
d_received_message_id = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_ID);
d_received_message_size = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_SIZE);
d_received_message_size = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_SIZE) + 1; // "0" means 1
d_received_message_page_id = read_has_page_header_parameter(has_page_header, GALILEO_HAS_MESSAGE_PAGE_ID);
}
}
@ -116,14 +121,27 @@ void Galileo_Cnav_Message::process_HAS_page(const std::string& page_string)
if (d_current_message_id == d_received_message_id)
{
// if receiver pid was not there, store it.
if (std::find(d_list_pid.begin(), d_list_pid.end(), d_received_message_page_id) == d_list_pid.end())
if (d_received_message_page_id == 0)
{
if (d_received_message_type == 1) // contains satellite corrections
// reserved, ignore it
}
else
{
if (std::find(d_list_pid.begin(), d_list_pid.end(), d_received_message_page_id) == d_list_pid.end())
{
d_received_encoded_messages++;
d_list_pid.push_back(d_received_message_page_id);
// Store encoded page
d_encoded_message_type_1 += std::string(page_string);
if (d_received_message_type == 1) // contains satellite corrections
{
d_received_encoded_messages++;
d_list_pid.push_back(d_received_message_page_id);
// Pack encoded string into 53 octets and put it in
// the corresponding row of d_C_matrix.
for (int k = 0; k < GALILEO_CNAV_OCTETS_IN_SUBPAGE; k++)
{
std::string bits8 = page_string.substr(k * 8, 8);
std::bitset<8> bs(bits8);
d_C_matrix[d_received_message_page_id - 1][k] = static_cast<uint8_t>(bs.to_ulong());
}
}
}
}
}
@ -134,48 +152,112 @@ void Galileo_Cnav_Message::process_HAS_page(const std::string& page_string)
d_received_encoded_messages = 0;
d_new_message = false;
d_current_message_size = d_received_message_size;
// erase stored pages and start storing again
d_encoded_message_type_1.clear();
// erase stored pages and data, and start storing again
d_list_pid.clear();
d_HAS_data = Galileo_HAS_data();
if (d_received_message_type == 1)
{
d_encoded_message_type_1.reserve(GALILEO_CNAV_MAX_NUMBER_ENCODED_BLOCKS * GALILEO_CNAV_MESSAGE_BITS_PER_PAGE);
d_received_encoded_messages++;
d_list_pid.push_back(d_received_message_page_id);
d_encoded_message_type_1 += std::string(page_string);
// Pack encoded string into 53 octets and put it in
// the corresponding row of d_C_matrix.
for (int k = 0; k < GALILEO_CNAV_OCTETS_IN_SUBPAGE; k++)
{
std::string bits8 = page_string.substr(k * 8, 8);
std::bitset<8> bs(bits8);
d_C_matrix[d_received_message_page_id - 1][k] = static_cast<uint8_t>(bs.to_ulong());
}
}
}
if (d_received_encoded_messages == d_current_message_size)
{
// we have a full encoded message stored in d_encoded_message_type_1
// we have a full encoded message stored in d_C_matrix
d_received_encoded_messages = 0;
d_current_message_id = 0;
d_new_message = true;
decode_message_type1();
int res = decode_message_type1();
if (res == 0)
{
d_new_message = true;
}
else
{
d_new_message = false;
}
}
}
void Galileo_Cnav_Message::decode_message_type1()
int Galileo_Cnav_Message::decode_message_type1()
{
// TODO: Reed-Solomon decoding of d_encoded_message_type_1
// TODO: reordering
// decoded_message_type1 = ...
// read_HAS_message_type1(decoded_message_type1);
}
// All rows in d_C_matrix with no data are erasure positions
std::vector<int> erasure_positions;
erasure_positions.reserve(GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK - d_list_pid.size());
for (int mpid = 1; mpid <= GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK; mpid++)
{
if (std::find(d_list_pid.begin(), d_list_pid.end(), static_cast<uint8_t>(mpid)) == d_list_pid.end())
{
erasure_positions.push_back(static_cast<uint8_t>(mpid - 1));
}
else
{
d_list_pid.remove(static_cast<uint8_t>(mpid));
}
}
void Galileo_Cnav_Message::read_HAS_message_type1(const std::string& message_string)
{
d_HAS_data = Galileo_HAS_data();
read_MT1_header(message_string);
read_MT1_body(message_string);
// Vertical decoding of d_C_matrix
for (int col = 0; col < GALILEO_CNAV_OCTETS_IN_SUBPAGE; col++)
{
std::vector<uint8_t> C_column(GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK, 0);
for (int row = 0; row < GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK; row++)
{
C_column[row] = d_C_matrix[row][col];
}
int result = rs.decode(C_column, erasure_positions);
if (result < 0)
{
// Decoding failed
d_C_matrix = {GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)};
d_M_matrix = {GALILEO_CNAV_INFORMATION_VECTOR_LENGTH, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE)};
return -1;
}
std::vector<uint8_t> M_column(C_column.begin(), C_column.begin() + GALILEO_CNAV_INFORMATION_VECTOR_LENGTH);
for (int i = 0; i < GALILEO_CNAV_INFORMATION_VECTOR_LENGTH; i++)
{
d_M_matrix[i][col] = M_column[i];
}
}
// Form the decoded HAS message by reading rows of d_M_matrix
std::string decoded_message_type_1;
decoded_message_type_1.reserve(d_current_message_size * GALILEO_CNAV_OCTETS_IN_SUBPAGE * 8);
for (uint8_t row = 0; row < d_current_message_size; row++)
{
for (int col = 0; col < GALILEO_CNAV_OCTETS_IN_SUBPAGE; col++)
{
std::bitset<8> bs(d_M_matrix[row][col]);
decoded_message_type_1 += bs.to_string();
}
}
// reset d_C_matrix and d_M_matrix for next decoding
d_C_matrix = {GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)};
d_M_matrix = {GALILEO_CNAV_INFORMATION_VECTOR_LENGTH, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)};
// Trigger HAS message content reading
read_MT1_header(decoded_message_type_1);
read_MT1_body(decoded_message_type_1);
return 0;
}
void Galileo_Cnav_Message::read_MT1_header(const std::string& message_string)
{
// ICD v1.2 Table 6: MT1 Message Header.
const std::bitset<GALILEO_CNAV_MT1_HEADER_BITS> has_mt1_header(message_string);
d_HAS_data.header.toh = read_has_message_header_parameter_uint16(has_mt1_header, GALILEO_MT1_HEADER_TOH);
d_HAS_data.header.mask_id = read_has_message_header_parameter_uint8(has_mt1_header, GALILEO_MT1_HEADER_MASK_ID);
@ -192,6 +274,7 @@ void Galileo_Cnav_Message::read_MT1_header(const std::string& message_string)
void Galileo_Cnav_Message::read_MT1_body(const std::string& message_string)
{
// ICD v1.2 Table 7: MT1 Message Body.
auto message = std::string(message_string.begin() + GALILEO_CNAV_MT1_HEADER_BITS, message_string.end()); // Remove header
int Nsat = 0;
if (d_HAS_data.header.mask_flag)

View File

@ -3,14 +3,14 @@
* \brief Implementation of a Galileo CNAV Data message as described in
* Galileo High Accuracy Service E6-B Signal-In-Space Message Specification v1.2
* (April 2020)
* \author Carles Fernandez-Prades, 2020 cfernandez(at)cttc.es
* \author Carles Fernandez-Prades, 2020-2021 cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
* Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
@ -21,10 +21,12 @@
#include "Galileo_CNAV.h"
#include "galileo_has_data.h"
#include "reed_solomon.h"
#include <bitset>
#include <cstdint>
#include <list>
#include <string>
#include <vector>
/** \addtogroup Core
* \{ */
@ -68,23 +70,24 @@ private:
bool CRC_test(std::bitset<GALILEO_CNAV_BITS_FOR_CRC> bits, uint32_t checksum) const;
void read_HAS_page_header(const std::string& page_string);
void process_HAS_page(const std::string& page_string);
void decode_message_type1();
void read_HAS_message_type1(const std::string& message_string);
void read_MT1_header(const std::string& message_string);
void read_MT1_body(const std::string& message_string);
int decode_message_type1();
uint8_t read_has_page_header_parameter(std::bitset<GALILEO_CNAV_PAGE_HEADER_BITS> bits, const std::pair<int32_t, int32_t>& parameter) const;
uint8_t read_has_message_header_parameter_uint8(std::bitset<GALILEO_CNAV_MT1_HEADER_BITS> bits, const std::pair<int32_t, int32_t>& parameter) const;
uint16_t read_has_message_header_parameter_uint16(std::bitset<GALILEO_CNAV_MT1_HEADER_BITS> bits, const std::pair<int32_t, int32_t>& parameter) const;
bool read_has_message_header_parameter_bool(std::bitset<GALILEO_CNAV_MT1_HEADER_BITS> bits, const std::pair<int32_t, int32_t>& parameter) const;
uint8_t read_has_message_body_uint8(const std::string& bits) const;
uint16_t read_has_message_body_uint16(const std::string& bits) const;
uint64_t read_has_message_body_uint64(const std::string& bits) const;
int16_t read_has_message_body_int16(const std::string& bits) const;
Galileo_HAS_data d_HAS_data{};
std::string d_encoded_message_type_1;
ReedSolomon rs = ReedSolomon();
std::vector<std::vector<uint8_t>> d_C_matrix{GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)}; // 255 x 53
std::vector<std::vector<uint8_t>> d_M_matrix{GALILEO_CNAV_INFORMATION_VECTOR_LENGTH, std::vector<uint8_t>(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)}; // 32 x 53
std::list<uint8_t> d_list_pid;
uint8_t d_has_page_status{};

View File

@ -0,0 +1,805 @@
/*!
* \file reed_solomon.cc
* \brief Class implementing a Reed-Solomon encoder/decoder
* \author Carles Fernandez, 2021. cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "reed_solomon.h"
#include <cstring>
#include <iostream>
ReedSolomon::ReedSolomon()
{
d_nroots = 223; // number of parity symbols in a block
d_min_poly = 29; // minimal poly
d_prim = 1; // The primitive root of the generator poly.
d_fcr = 1; // first consecutive root of the Reed-Solomon generator polynomial
d_pad = 0; // the number of pad symbols in a block.
d_data_in_block = d_symbols_per_block - d_nroots;
d_a0 = static_cast<uint8_t>(d_symbols_per_block);
d_genmatrix = {
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{19, 143, 180, 59, 221, 29, 49, 45, 231, 9, 73, 73, 159, 2, 158, 136, 212, 218, 14, 113, 215, 20, 187, 55, 137, 181, 203, 113, 97, 135, 14, 251},
{27, 27, 1, 50, 255, 109, 251, 156, 148, 151, 85, 21, 74, 116, 250, 77, 60, 203, 113, 196, 213, 23, 202, 125, 31, 252, 90, 1, 176, 226, 44, 252},
{98, 153, 190, 38, 223, 28, 28, 149, 170, 219, 41, 235, 236, 175, 113, 182, 239, 31, 74, 241, 127, 121, 207, 137, 205, 70, 88, 218, 250, 129, 99, 5},
{95, 235, 199, 105, 168, 182, 233, 133, 201, 135, 171, 89, 50, 230, 115, 227, 21, 122, 41, 226, 93, 59, 20, 36, 30, 150, 134, 240, 34, 91, 183, 83},
{172, 171, 163, 123, 27, 81, 14, 251, 24, 56, 15, 35, 244, 148, 24, 35, 96, 195, 47, 232, 148, 85, 117, 91, 39, 5, 74, 71, 104, 116, 14, 128},
{117, 108, 167, 111, 201, 61, 244, 13, 5, 236, 75, 124, 11, 233, 60, 127, 101, 117, 144, 13, 51, 70, 138, 247, 188, 171, 120, 104, 141, 220, 39, 86},
{243, 8, 122, 204, 147, 89, 112, 127, 204, 217, 20, 179, 8, 167, 203, 254, 95, 38, 22, 249, 215, 127, 101, 46, 99, 252, 183, 17, 8, 122, 191, 32},
{90, 195, 11, 73, 110, 20, 55, 185, 206, 241, 12, 193, 185, 72, 141, 27, 97, 29, 251, 144, 6, 109, 129, 203, 222, 64, 164, 49, 173, 37, 167, 169},
{164, 41, 177, 10, 34, 58, 123, 165, 122, 70, 108, 145, 240, 246, 208, 134, 248, 114, 237, 129, 149, 218, 70, 63, 105, 5, 184, 222, 9, 255, 213, 153},
{211, 255, 215, 20, 146, 60, 12, 202, 1, 95, 234, 192, 175, 223, 81, 99, 59, 136, 191, 82, 138, 174, 112, 1, 21, 14, 137, 7, 4, 238, 50, 246},
{220, 94, 230, 67, 248, 163, 186, 77, 68, 20, 1, 180, 150, 94, 127, 36, 154, 47, 101, 114, 172, 174, 172, 248, 130, 250, 55, 68, 17, 106, 3, 123},
{110, 20, 220, 172, 53, 110, 224, 20, 10, 192, 59, 46, 159, 96, 14, 203, 214, 144, 215, 141, 13, 190, 175, 232, 55, 123, 104, 223, 79, 38, 146, 169},
{164, 29, 102, 221, 199, 97, 1, 114, 215, 130, 93, 166, 31, 208, 248, 5, 40, 197, 96, 173, 136, 209, 149, 17, 74, 236, 131, 18, 231, 29, 214, 172},
{251, 94, 49, 176, 56, 250, 251, 10, 237, 114, 111, 176, 78, 90, 148, 97, 69, 174, 3, 178, 4, 16, 151, 192, 36, 202, 212, 81, 210, 20, 219, 216},
{116, 79, 18, 201, 172, 40, 33, 232, 54, 187, 32, 61, 5, 227, 55, 18, 43, 107, 202, 220, 141, 66, 224, 166, 158, 176, 61, 11, 143, 232, 112, 47},
{187, 194, 174, 69, 228, 144, 68, 94, 189, 124, 254, 101, 65, 91, 176, 76, 117, 203, 236, 169, 202, 251, 11, 110, 242, 80, 181, 94, 162, 92, 111, 54},
{29, 150, 209, 144, 66, 224, 111, 137, 81, 38, 230, 100, 15, 45, 7, 31, 208, 240, 210, 18, 111, 85, 199, 64, 247, 215, 164, 75, 231, 34, 69, 82},
{191, 102, 106, 86, 63, 166, 105, 80, 243, 169, 231, 39, 86, 171, 77, 223, 72, 220, 171, 98, 179, 115, 160, 191, 202, 89, 192, 20, 178, 54, 121, 137},
{254, 252, 23, 88, 159, 236, 167, 50, 34, 70, 225, 175, 28, 89, 25, 150, 163, 25, 241, 87, 152, 213, 166, 176, 237, 50, 249, 60, 144, 205, 27, 81},
{138, 9, 193, 221, 141, 92, 54, 239, 124, 193, 92, 251, 33, 190, 134, 68, 160, 220, 80, 210, 146, 184, 240, 135, 188, 129, 101, 218, 102, 213, 132, 199},
{184, 160, 40, 202, 31, 235, 178, 121, 225, 205, 231, 122, 61, 178, 191, 195, 55, 13, 2, 41, 245, 69, 128, 182, 5, 90, 7, 28, 79, 58, 11, 43},
{247, 8, 171, 147, 180, 87, 67, 121, 151, 143, 177, 155, 64, 107, 163, 222, 211, 152, 178, 184, 68, 211, 218, 210, 252, 37, 84, 189, 44, 186, 133, 134},
{31, 50, 155, 253, 213, 220, 84, 174, 239, 85, 87, 105, 214, 81, 160, 211, 90, 32, 239, 171, 171, 238, 177, 234, 36, 233, 216, 77, 44, 173, 205, 253},
{113, 18, 35, 135, 205, 43, 156, 23, 127, 169, 162, 160, 15, 49, 202, 100, 165, 163, 175, 30, 199, 19, 141, 197, 211, 200, 134, 41, 215, 154, 34, 31},
{204, 239, 127, 208, 89, 187, 30, 192, 37, 152, 221, 214, 211, 49, 93, 9, 93, 38, 25, 9, 6, 86, 219, 250, 25, 161, 185, 32, 98, 177, 32, 121},
{72, 7, 24, 67, 1, 245, 154, 234, 84, 179, 37, 96, 222, 33, 64, 228, 78, 254, 194, 19, 197, 60, 60, 241, 58, 151, 184, 179, 233, 70, 85, 97},
{253, 151, 182, 118, 101, 136, 118, 241, 195, 26, 152, 14, 225, 28, 193, 165, 140, 82, 138, 36, 216, 2, 152, 228, 117, 234, 180, 94, 11, 25, 50, 148},
{20, 35, 254, 1, 198, 250, 222, 43, 98, 131, 180, 54, 101, 212, 227, 212, 85, 247, 217, 50, 117, 7, 116, 145, 101, 136, 176, 12, 83, 1, 146, 170},
{145, 235, 144, 178, 16, 181, 198, 59, 220, 241, 197, 242, 187, 44, 243, 109, 86, 53, 21, 48, 83, 149, 252, 147, 181, 124, 48, 89, 151, 149, 227, 188},
{214, 115, 72, 209, 6, 224, 24, 39, 114, 233, 248, 204, 31, 222, 125, 2, 236, 241, 19, 132, 104, 150, 172, 254, 222, 170, 104, 161, 199, 252, 179, 230},
{241, 67, 229, 75, 108, 250, 81, 179, 127, 247, 83, 66, 159, 206, 107, 96, 58, 217, 252, 157, 139, 17, 235, 115, 5, 174, 191, 230, 233, 49, 241, 241},
{165, 246, 113, 208, 142, 14, 235, 211, 178, 85, 75, 239, 238, 96, 147, 129, 143, 18, 30, 123, 124, 195, 21, 230, 104, 198, 220, 56, 202, 53, 246, 99},
{219, 121, 50, 105, 81, 61, 239, 218, 41, 238, 236, 242, 77, 40, 161, 123, 92, 58, 122, 26, 3, 147, 12, 163, 109, 207, 110, 216, 66, 41, 93, 220},
{56, 105, 223, 38, 38, 53, 34, 72, 93, 91, 133, 135, 1, 232, 7, 61, 70, 61, 102, 124, 94, 21, 181, 225, 227, 23, 51, 104, 159, 94, 117, 98},
{200, 107, 25, 252, 122, 136, 229, 62, 85, 8, 171, 117, 186, 197, 183, 103, 52, 41, 91, 19, 211, 165, 97, 52, 227, 241, 116, 70, 115, 251, 56, 164},
{99, 62, 142, 10, 191, 175, 135, 155, 202, 184, 151, 52, 17, 239, 5, 26, 201, 44, 159, 38, 76, 235, 82, 145, 61, 162, 223, 9, 169, 204, 77, 189},
{197, 14, 41, 244, 99, 82, 51, 75, 53, 246, 248, 215, 70, 118, 32, 124, 79, 180, 4, 127, 169, 157, 105, 103, 85, 151, 125, 63, 246, 69, 228, 179},
{55, 161, 79, 12, 207, 40, 253, 100, 230, 119, 111, 97, 76, 61, 94, 122, 5, 74, 200, 112, 206, 160, 19, 75, 142, 167, 222, 9, 180, 99, 57, 177},
{17, 80, 149, 28, 144, 190, 229, 240, 26, 182, 124, 100, 217, 51, 52, 9, 182, 169, 42, 94, 114, 239, 69, 95, 173, 11, 101, 72, 64, 50, 3, 135},
{12, 91, 119, 248, 135, 229, 140, 37, 129, 209, 39, 237, 182, 202, 102, 204, 89, 159, 208, 66, 154, 204, 54, 66, 32, 13, 61, 13, 184, 70, 75, 128},
{117, 204, 87, 187, 74, 161, 64, 143, 219, 117, 162, 84, 197, 171, 98, 1, 138, 76, 204, 242, 153, 72, 19, 180, 165, 172, 112, 31, 199, 12, 21, 19},
{24, 225, 130, 141, 144, 160, 197, 221, 109, 80, 74, 157, 237, 227, 1, 143, 161, 216, 190, 28, 103, 248, 231, 29, 74, 248, 192, 160, 226, 203, 254, 14},
{242, 17, 183, 221, 223, 54, 147, 94, 222, 19, 137, 147, 116, 241, 4, 34, 163, 217, 140, 42, 34, 191, 244, 240, 48, 18, 110, 84, 212, 155, 159, 85},
{198, 3, 198, 145, 91, 104, 40, 111, 171, 25, 48, 170, 91, 222, 108, 67, 99, 147, 168, 118, 148, 82, 76, 9, 226, 178, 78, 148, 151, 183, 234, 136},
{237, 10, 198, 207, 133, 149, 88, 94, 250, 23, 24, 49, 14, 86, 242, 63, 235, 232, 176, 37, 91, 230, 60, 107, 210, 175, 217, 195, 113, 111, 148, 57},
{252, 70, 251, 156, 71, 58, 104, 35, 181, 22, 29, 18, 45, 124, 115, 246, 91, 204, 171, 171, 10, 8, 109, 87, 86, 26, 6, 194, 111, 15, 44, 249},
{61, 247, 189, 11, 255, 205, 190, 159, 73, 215, 216, 211, 50, 194, 165, 173, 247, 237, 123, 131, 188, 226, 189, 197, 112, 84, 126, 46, 193, 255, 184, 53},
{40, 156, 37, 206, 118, 220, 97, 4, 164, 201, 150, 153, 5, 88, 33, 143, 80, 1, 230, 22, 33, 31, 14, 175, 218, 151, 224, 19, 52, 213, 244, 149},
{7, 121, 65, 169, 163, 244, 187, 17, 112, 237, 46, 113, 109, 50, 57, 188, 171, 241, 132, 47, 144, 234, 210, 48, 167, 146, 6, 41, 127, 185, 80, 151},
{33, 85, 209, 187, 99, 27, 241, 145, 182, 43, 152, 91, 166, 94, 114, 169, 45, 163, 104, 175, 26, 115, 76, 130, 55, 152, 136, 45, 135, 225, 32, 216},
{116, 149, 25, 41, 167, 115, 192, 226, 173, 224, 121, 202, 238, 11, 51, 244, 227, 3, 199, 183, 144, 92, 131, 125, 220, 163, 111, 87, 243, 189, 133, 212},
{160, 202, 250, 200, 192, 43, 249, 18, 14, 151, 249, 96, 181, 91, 160, 155, 39, 28, 47, 110, 5, 38, 203, 203, 1, 103, 73, 198, 63, 163, 145, 49},
{100, 7, 242, 101, 230, 151, 67, 247, 146, 170, 239, 129, 240, 215, 250, 144, 17, 158, 47, 155, 183, 246, 28, 5, 202, 8, 216, 253, 69, 13, 144, 119},
{186, 166, 166, 145, 230, 236, 133, 44, 96, 122, 206, 139, 96, 30, 65, 96, 251, 202, 46, 177, 105, 85, 144, 33, 232, 28, 135, 70, 64, 24, 189, 122},
{125, 253, 144, 215, 58, 109, 158, 6, 140, 237, 28, 168, 63, 148, 208, 125, 70, 43, 60, 183, 25, 111, 239, 227, 103, 164, 69, 30, 44, 240, 238, 236},
{79, 231, 215, 32, 107, 20, 43, 26, 230, 83, 183, 70, 84, 250, 132, 244, 30, 68, 74, 255, 253, 232, 200, 251, 43, 161, 44, 134, 187, 133, 145, 204},
{21, 229, 206, 84, 62, 194, 60, 102, 75, 4, 220, 56, 176, 209, 192, 112, 8, 94, 248, 15, 74, 182, 177, 114, 195, 206, 113, 105, 159, 63, 57, 165},
{112, 108, 180, 230, 202, 246, 252, 111, 117, 175, 210, 10, 195, 231, 143, 229, 10, 202, 230, 244, 135, 102, 250, 118, 242, 55, 43, 125, 231, 167, 135, 71},
{205, 154, 65, 115, 150, 138, 189, 176, 159, 48, 250, 135, 228, 77, 78, 173, 208, 178, 71, 189, 8, 130, 129, 62, 19, 152, 204, 112, 34, 15, 42, 112},
{195, 133, 16, 131, 217, 207, 15, 17, 168, 72, 182, 124, 156, 4, 38, 75, 208, 55, 40, 147, 80, 134, 226, 57, 75, 233, 92, 24, 247, 205, 149, 27},
{128, 91, 2, 15, 14, 219, 62, 231, 152, 107, 5, 251, 73, 170, 42, 255, 5, 28, 181, 87, 240, 145, 152, 73, 251, 215, 147, 35, 202, 183, 79, 5},
{95, 9, 5, 213, 129, 103, 46, 167, 187, 181, 27, 117, 34, 67, 118, 184, 92, 144, 42, 29, 251, 180, 252, 115, 222, 160, 23, 59, 219, 107, 129, 127},
{34, 145, 97, 163, 240, 99, 224, 52, 91, 27, 163, 13, 24, 220, 81, 216, 61, 25, 80, 27, 25, 185, 99, 100, 162, 201, 57, 38, 169, 202, 171, 224},
{155, 178, 152, 248, 234, 66, 116, 165, 4, 232, 10, 178, 59, 197, 10, 91, 34, 238, 48, 229, 220, 24, 121, 14, 142, 75, 92, 140, 53, 106, 227, 201},
{74, 184, 197, 204, 104, 42, 159, 160, 168, 203, 23, 245, 157, 180, 35, 108, 4, 247, 100, 221, 252, 211, 44, 40, 161, 48, 91, 177, 109, 16, 224, 231},
{226, 80, 154, 253, 172, 137, 170, 25, 31, 36, 56, 228, 57, 78, 159, 182, 128, 235, 244, 155, 5, 145, 21, 196, 90, 100, 238, 164, 152, 28, 19, 89},
{18, 25, 164, 149, 142, 135, 198, 151, 60, 180, 76, 80, 230, 139, 21, 246, 110, 97, 210, 120, 168, 133, 5, 145, 244, 247, 37, 98, 209, 145, 37, 68},
{248, 116, 245, 46, 159, 233, 159, 253, 83, 98, 58, 194, 2, 110, 157, 178, 162, 165, 254, 26, 224, 145, 178, 152, 114, 92, 76, 237, 158, 173, 14, 194},
{231, 91, 11, 41, 98, 144, 242, 73, 175, 207, 52, 108, 221, 155, 179, 74, 98, 154, 77, 47, 145, 115, 196, 31, 141, 207, 26, 157, 128, 99, 69, 145},
{75, 176, 108, 107, 23, 148, 51, 54, 134, 194, 17, 234, 222, 226, 184, 52, 25, 140, 39, 93, 210, 10, 104, 38, 9, 43, 85, 10, 104, 43, 222, 237},
{92, 94, 46, 231, 10, 36, 227, 154, 49, 80, 209, 2, 137, 25, 108, 20, 131, 193, 227, 149, 192, 55, 22, 75, 103, 122, 104, 231, 206, 70, 68, 7},
{121, 214, 117, 143, 206, 89, 179, 32, 21, 14, 178, 51, 248, 135, 228, 243, 2, 191, 235, 169, 138, 172, 49, 147, 211, 75, 49, 34, 221, 124, 108, 159},
{185, 39, 183, 74, 227, 158, 201, 236, 236, 6, 9, 181, 104, 219, 67, 64, 140, 148, 86, 111, 106, 201, 187, 196, 168, 45, 71, 181, 163, 15, 149, 111},
{15, 111, 192, 134, 62, 204, 46, 57, 198, 220, 244, 251, 221, 182, 220, 133, 4, 232, 180, 36, 154, 117, 97, 116, 109, 32, 152, 53, 121, 42, 47, 255},
{87, 1, 11, 170, 17, 250, 238, 55, 59, 146, 185, 145, 190, 62, 12, 21, 70, 84, 123, 167, 251, 10, 125, 123, 66, 246, 196, 139, 109, 220, 185, 22},
{71, 74, 17, 6, 15, 146, 107, 234, 137, 157, 221, 246, 241, 146, 72, 115, 22, 129, 144, 3, 158, 222, 200, 152, 18, 68, 90, 188, 142, 192, 24, 146},
{126, 156, 188, 60, 66, 222, 98, 216, 17, 255, 152, 216, 248, 200, 14, 74, 65, 139, 46, 19, 154, 57, 21, 115, 8, 118, 158, 217, 234, 177, 111, 160},
{47, 142, 147, 67, 44, 227, 21, 168, 151, 216, 89, 62, 250, 165, 74, 185, 147, 22, 5, 138, 55, 242, 24, 57, 100, 167, 83, 58, 175, 115, 63, 33},
{73, 144, 57, 155, 60, 182, 188, 241, 254, 163, 68, 197, 171, 184, 17, 18, 242, 11, 197, 242, 162, 153, 183, 129, 64, 242, 52, 164, 231, 5, 160, 210},
{202, 242, 96, 114, 134, 254, 154, 128, 117, 242, 17, 246, 223, 18, 112, 174, 3, 235, 3, 87, 136, 108, 179, 77, 236, 98, 152, 166, 151, 130, 13, 52},
{59, 228, 148, 40, 210, 184, 99, 13, 92, 252, 250, 25, 191, 183, 111, 210, 135, 47, 238, 31, 34, 63, 59, 150, 219, 190, 29, 132, 221, 4, 135, 219},
{65, 3, 105, 33, 78, 229, 48, 7, 5, 17, 117, 115, 16, 20, 101, 108, 249, 218, 89, 162, 68, 88, 31, 83, 78, 141, 9, 81, 249, 115, 114, 99},
{219, 157, 199, 113, 160, 253, 4, 1, 253, 89, 168, 204, 209, 214, 213, 141, 177, 76, 178, 93, 218, 171, 151, 169, 216, 233, 37, 13, 43, 26, 27, 88},
{1, 175, 221, 243, 223, 150, 131, 20, 195, 95, 120, 137, 81, 97, 19, 52, 129, 138, 123, 79, 185, 78, 132, 36, 16, 192, 99, 216, 25, 165, 45, 183},
{123, 99, 4, 20, 155, 224, 253, 96, 2, 165, 255, 216, 84, 34, 11, 83, 58, 203, 206, 214, 133, 224, 22, 122, 211, 12, 130, 206, 202, 170, 225, 179},
{55, 31, 34, 33, 47, 208, 79, 170, 205, 64, 60, 102, 67, 47, 10, 81, 42, 63, 183, 186, 103, 140, 110, 52, 147, 33, 69, 246, 69, 95, 214, 180},
{78, 217, 117, 166, 51, 55, 232, 219, 136, 176, 59, 71, 7, 54, 250, 207, 62, 19, 105, 137, 20, 2, 4, 201, 69, 77, 35, 123, 71, 98, 9, 88},
{1, 58, 153, 65, 8, 5, 73, 248, 25, 42, 145, 26, 218, 183, 243, 27, 195, 5, 36, 148, 109, 128, 45, 183, 112, 93, 199, 222, 111, 201, 85, 165},
{112, 120, 107, 177, 223, 192, 59, 26, 235, 253, 252, 71, 225, 141, 233, 214, 97, 1, 189, 40, 28, 65, 204, 234, 55, 132, 184, 203, 80, 87, 113, 43},
{247, 192, 115, 208, 207, 151, 104, 240, 244, 133, 129, 128, 125, 183, 156, 136, 198, 206, 190, 7, 69, 58, 222, 158, 160, 23, 138, 2, 251, 165, 232, 252},
{98, 117, 101, 84, 61, 44, 230, 6, 198, 187, 59, 63, 121, 152, 178, 208, 42, 229, 79, 62, 188, 233, 226, 157, 46, 249, 179, 10, 249, 202, 36, 193},
{210, 77, 203, 244, 98, 21, 100, 71, 96, 65, 54, 182, 156, 230, 250, 224, 97, 97, 31, 13, 209, 19, 108, 22, 14, 81, 255, 241, 196, 144, 48, 171},
{130, 162, 74, 188, 56, 12, 24, 172, 87, 250, 78, 57, 164, 215, 95, 252, 182, 219, 141, 135, 187, 37, 83, 188, 187, 162, 34, 103, 11, 133, 124, 229},
{196, 155, 245, 4, 123, 227, 238, 196, 192, 201, 155, 47, 214, 115, 221, 199, 165, 240, 196, 144, 236, 254, 136, 213, 193, 9, 247, 63, 140, 105, 154, 46},
{168, 253, 206, 153, 244, 90, 190, 188, 118, 131, 197, 151, 204, 138, 190, 46, 116, 159, 121, 214, 81, 142, 12, 49, 8, 186, 199, 229, 247, 216, 224, 39},
{35, 18, 213, 92, 18, 32, 163, 180, 130, 116, 180, 242, 103, 130, 93, 241, 167, 10, 104, 181, 54, 135, 118, 39, 89, 7, 169, 11, 99, 104, 47, 45},
{157, 150, 134, 244, 214, 20, 46, 134, 50, 218, 163, 99, 173, 61, 240, 43, 35, 238, 145, 233, 16, 104, 165, 150, 124, 224, 137, 40, 96, 163, 243, 130},
{83, 94, 239, 60, 225, 202, 211, 119, 171, 212, 59, 66, 104, 180, 180, 154, 216, 159, 161, 81, 129, 234, 220, 73, 126, 135, 22, 73, 32, 199, 236, 64},
{180, 51, 88, 137, 101, 242, 22, 92, 8, 209, 99, 140, 86, 232, 224, 9, 185, 92, 56, 176, 178, 232, 11, 157, 180, 56, 55, 7, 44, 122, 96, 192},
{193, 20, 57, 242, 98, 80, 139, 154, 221, 134, 21, 167, 176, 203, 20, 58, 108, 40, 168, 11, 136, 9, 214, 200, 135, 126, 245, 4, 168, 194, 142, 20},
{97, 223, 113, 66, 240, 219, 163, 213, 247, 105, 91, 200, 228, 152, 156, 102, 140, 2, 240, 50, 129, 133, 160, 93, 174, 246, 89, 111, 195, 22, 26, 78},
{70, 8, 143, 72, 73, 69, 52, 183, 169, 243, 7, 53, 53, 120, 43, 2, 105, 112, 241, 117, 239, 48, 104, 246, 141, 176, 208, 220, 126, 224, 229, 157},
{159, 27, 28, 198, 131, 35, 183, 49, 168, 168, 102, 146, 77, 18, 157, 130, 200, 86, 133, 151, 5, 132, 76, 243, 194, 4, 55, 182, 159, 191, 21, 13},
{199, 26, 140, 14, 238, 2, 67, 91, 6, 205, 170, 100, 199, 87, 74, 59, 207, 195, 16, 130, 205, 225, 88, 2, 88, 88, 210, 48, 97, 114, 249, 174},
{221, 62, 67, 44, 76, 233, 250, 18, 23, 177, 178, 213, 175, 134, 50, 222, 206, 224, 25, 32, 152, 125, 204, 99, 56, 175, 235, 226, 50, 129, 168, 28},
{249, 207, 146, 253, 136, 29, 143, 209, 20, 235, 30, 29, 26, 151, 85, 116, 134, 62, 72, 44, 92, 53, 101, 226, 57, 136, 158, 222, 10, 192, 41, 227},
{174, 229, 7, 70, 206, 29, 89, 189, 213, 188, 33, 212, 151, 193, 254, 218, 239, 38, 5, 110, 143, 97, 37, 81, 142, 18, 93, 184, 110, 93, 251, 91},
{52, 86, 100, 126, 146, 223, 48, 62, 75, 108, 70, 219, 245, 33, 187, 154, 183, 167, 3, 107, 238, 39, 158, 207, 110, 84, 216, 51, 15, 116, 120, 71},
{205, 222, 123, 163, 14, 210, 148, 124, 206, 14, 57, 19, 53, 123, 136, 153, 175, 15, 42, 88, 151, 235, 192, 90, 170, 4, 175, 131, 108, 231, 249, 143},
{148, 139, 48, 211, 158, 147, 117, 33, 102, 77, 237, 218, 77, 54, 170, 68, 39, 24, 6, 237, 106, 137, 131, 98, 25, 203, 36, 104, 92, 38, 238, 241},
{165, 147, 185, 5, 22, 252, 130, 247, 32, 76, 241, 81, 118, 178, 107, 64, 171, 15, 223, 129, 12, 34, 141, 142, 121, 218, 185, 163, 68, 128, 225, 124},
{23, 231, 58, 82, 90, 211, 40, 239, 63, 155, 129, 60, 128, 142, 31, 64, 164, 157, 221, 125, 225, 114, 37, 76, 217, 172, 3, 27, 146, 193, 82, 144},
{88, 207, 100, 97, 177, 177, 65, 193, 199, 91, 12, 22, 17, 189, 51, 16, 199, 144, 46, 188, 87, 110, 210, 240, 211, 202, 253, 98, 143, 190, 114, 1},
{19, 215, 123, 95, 188, 172, 128, 108, 38, 206, 18, 69, 137, 19, 35, 187, 196, 29, 158, 95, 107, 67, 213, 229, 121, 102, 1, 140, 3, 8, 176, 137},
{254, 80, 166, 73, 150, 111, 173, 219, 30, 147, 134, 90, 126, 134, 161, 248, 199, 149, 48, 98, 165, 13, 150, 197, 183, 129, 198, 253, 8, 124, 37, 152},
{192, 42, 26, 56, 12, 149, 104, 49, 152, 50, 118, 99, 251, 83, 191, 154, 145, 109, 86, 254, 190, 138, 28, 230, 102, 101, 198, 8, 70, 104, 191, 253},
{113, 205, 59, 6, 8, 242, 213, 43, 224, 222, 197, 129, 5, 28, 200, 123, 236, 104, 226, 167, 146, 6, 233, 104, 223, 138, 10, 55, 146, 240, 231, 109},
{41, 164, 95, 124, 213, 29, 32, 127, 210, 194, 190, 165, 202, 223, 58, 3, 138, 33, 84, 114, 225, 165, 197, 72, 206, 32, 180, 154, 57, 8, 204, 102},
{132, 124, 62, 144, 115, 15, 9, 136, 217, 163, 11, 119, 222, 6, 194, 64, 125, 170, 127, 248, 166, 74, 7, 152, 84, 50, 72, 24, 24, 123, 86, 214},
{134, 57, 102, 153, 222, 197, 231, 129, 183, 241, 40, 128, 43, 111, 140, 103, 38, 43, 154, 52, 249, 56, 182, 33, 235, 152, 83, 3, 178, 91, 75, 9},
{139, 5, 68, 152, 226, 43, 97, 191, 13, 246, 202, 19, 147, 57, 117, 48, 93, 98, 85, 68, 21, 77, 50, 36, 148, 159, 69, 141, 77, 121, 37, 59},
{218, 35, 129, 104, 183, 103, 180, 64, 135, 243, 110, 82, 44, 229, 61, 124, 225, 211, 61, 172, 216, 110, 173, 55, 22, 43, 189, 188, 227, 32, 38, 163},
{26, 166, 237, 51, 2, 49, 255, 9, 59, 85, 142, 19, 204, 119, 216, 15, 196, 197, 79, 10, 236, 140, 159, 216, 166, 123, 78, 138, 105, 238, 188, 120},
{91, 94, 229, 234, 63, 179, 33, 38, 122, 164, 161, 122, 132, 60, 152, 233, 156, 189, 47, 52, 17, 194, 93, 130, 145, 157, 169, 53, 34, 202, 4, 6},
{106, 94, 193, 127, 30, 113, 21, 207, 78, 76, 15, 10, 31, 136, 95, 143, 43, 122, 153, 20, 252, 105, 127, 239, 147, 8, 29, 146, 110, 23, 238, 36},
{22, 92, 183, 30, 142, 237, 219, 104, 197, 87, 160, 227, 70, 87, 224, 149, 103, 38, 159, 198, 144, 22, 65, 13, 1, 94, 91, 66, 183, 101, 242, 51},
{66, 178, 17, 94, 151, 227, 231, 143, 59, 115, 189, 74, 80, 32, 215, 221, 170, 119, 9, 201, 172, 75, 71, 225, 3, 127, 106, 13, 3, 150, 74, 255},
{87, 76, 214, 123, 201, 83, 193, 254, 141, 111, 22, 216, 15, 179, 154, 30, 30, 250, 228, 26, 22, 60, 67, 93, 215, 152, 155, 121, 85, 166, 5, 115},
{246, 147, 7, 89, 171, 183, 133, 26, 210, 65, 50, 75, 127, 233, 103, 26, 2, 138, 114, 163, 147, 164, 140, 162, 174, 239, 28, 220, 93, 46, 46, 36},
{22, 192, 122, 216, 168, 88, 29, 248, 16, 203, 173, 222, 7, 55, 129, 173, 242, 15, 111, 45, 39, 121, 140, 254, 76, 99, 188, 67, 249, 86, 203, 243},
{131, 18, 135, 57, 186, 240, 43, 197, 42, 40, 229, 131, 81, 252, 75, 102, 247, 115, 212, 10, 127, 71, 22, 239, 234, 248, 154, 217, 173, 54, 141, 178},
{36, 104, 231, 153, 223, 236, 110, 81, 143, 97, 248, 53, 135, 40, 74, 153, 203, 40, 1, 209, 108, 98, 114, 3, 143, 173, 122, 159, 51, 191, 68, 35},
{111, 152, 170, 153, 65, 127, 209, 208, 212, 169, 111, 246, 131, 193, 189, 31, 103, 250, 231, 20, 74, 234, 76, 133, 117, 110, 181, 111, 128, 138, 112, 66},
{146, 12, 235, 186, 103, 104, 193, 4, 124, 188, 140, 74, 193, 7, 180, 13, 137, 74, 65, 20, 68, 11, 96, 99, 119, 68, 85, 70, 200, 201, 49, 183},
{123, 240, 167, 34, 210, 88, 3, 34, 18, 26, 28, 44, 151, 178, 109, 244, 3, 195, 14, 236, 222, 29, 83, 158, 148, 107, 6, 248, 84, 123, 141, 175},
{206, 13, 29, 60, 189, 200, 145, 127, 137, 172, 44, 42, 120, 212, 73, 113, 213, 246, 23, 79, 33, 122, 139, 95, 45, 214, 19, 71, 155, 51, 175, 147},
{109, 154, 79, 11, 165, 113, 9, 15, 99, 246, 224, 96, 187, 67, 214, 195, 151, 146, 87, 229, 1, 146, 10, 7, 70, 252, 199, 225, 112, 35, 146, 236},
{79, 247, 176, 255, 183, 139, 55, 141, 239, 188, 172, 186, 156, 126, 83, 242, 160, 149, 243, 148, 175, 240, 53, 30, 207, 128, 116, 4, 68, 217, 66, 176},
{2, 167, 119, 216, 190, 219, 119, 23, 20, 182, 254, 238, 157, 225, 233, 140, 234, 214, 251, 20, 65, 154, 174, 78, 113, 255, 137, 147, 44, 69, 183, 7},
{121, 136, 140, 214, 241, 237, 76, 180, 152, 43, 84, 28, 20, 147, 28, 118, 154, 214, 252, 177, 11, 45, 156, 43, 214, 93, 180, 195, 169, 158, 111, 108},
{58, 35, 174, 240, 216, 249, 14, 203, 170, 179, 2, 125, 200, 204, 43, 95, 83, 141, 228, 29, 32, 40, 85, 10, 4, 156, 168, 85, 172, 180, 172, 21},
{114, 171, 242, 238, 47, 124, 59, 125, 65, 23, 39, 150, 161, 226, 5, 209, 61, 231, 91, 15, 64, 57, 58, 233, 229, 192, 112, 67, 243, 149, 98, 151},
{33, 32, 3, 8, 36, 151, 121, 17, 218, 26, 98, 82, 65, 146, 162, 149, 64, 53, 126, 112, 58, 163, 159, 106, 238, 218, 218, 91, 237, 109, 12, 234},
{37, 190, 149, 41, 64, 68, 119, 19, 153, 51, 235, 147, 203, 136, 225, 145, 52, 164, 112, 134, 242, 179, 185, 57, 179, 177, 210, 34, 165, 113, 40, 14},
{242, 44, 232, 202, 123, 230, 119, 236, 16, 231, 234, 50, 122, 215, 111, 194, 189, 76, 240, 228, 184, 42, 191, 174, 20, 235, 39, 70, 86, 220, 37, 131},
{64, 190, 225, 105, 2, 122, 16, 3, 38, 255, 79, 66, 166, 97, 192, 141, 229, 219, 13, 65, 91, 86, 37, 100, 207, 90, 214, 150, 47, 118, 157, 109},
{41, 149, 44, 166, 186, 23, 168, 186, 250, 4, 159, 47, 9, 124, 71, 11, 124, 40, 231, 157, 7, 108, 149, 132, 194, 48, 100, 70, 152, 181, 74, 28},
{249, 59, 57, 146, 2, 235, 113, 131, 188, 6, 171, 48, 224, 49, 175, 1, 83, 140, 128, 210, 225, 170, 116, 187, 222, 114, 1, 81, 174, 106, 29, 1},
{19, 118, 143, 2, 79, 31, 218, 92, 100, 181, 79, 226, 175, 226, 175, 39, 213, 137, 130, 241, 5, 245, 17, 67, 50, 107, 185, 112, 48, 41, 100, 230},
{241, 134, 224, 140, 191, 179, 174, 113, 4, 225, 15, 245, 177, 126, 87, 178, 31, 224, 132, 12, 254, 124, 136, 206, 184, 66, 126, 55, 56, 198, 36, 38},
{48, 196, 26, 73, 218, 118, 123, 137, 168, 15, 159, 113, 154, 253, 55, 144, 239, 187, 25, 57, 59, 60, 63, 148, 47, 2, 154, 195, 208, 32, 63, 18},
{11, 43, 62, 251, 191, 45, 35, 203, 140, 42, 121, 233, 87, 190, 201, 82, 228, 103, 71, 184, 123, 78, 40, 6, 227, 199, 165, 59, 95, 91, 220, 223},
{13, 53, 76, 103, 206, 252, 97, 243, 120, 229, 154, 201, 166, 244, 46, 208, 14, 246, 41, 210, 152, 81, 184, 156, 192, 91, 123, 48, 223, 215, 21, 243},
{131, 9, 114, 15, 5, 150, 143, 185, 33, 64, 203, 180, 70, 93, 136, 201, 138, 143, 45, 76, 128, 248, 62, 219, 136, 116, 162, 30, 222, 16, 12, 108},
{58, 217, 47, 14, 1, 13, 117, 8, 167, 10, 105, 226, 96, 158, 229, 203, 236, 157, 189, 204, 221, 163, 128, 168, 244, 194, 129, 67, 113, 195, 34, 118},
{169, 119, 204, 119, 80, 22, 46, 55, 120, 70, 39, 68, 156, 140, 150, 247, 116, 237, 35, 82, 233, 43, 126, 138, 204, 151, 134, 110, 159, 171, 125, 51},
{66, 13, 58, 37, 254, 61, 28, 122, 100, 206, 172, 205, 247, 250, 12, 171, 200, 100, 194, 117, 56, 50, 122, 222, 132, 178, 163, 208, 47, 190, 132, 112},
{195, 10, 135, 248, 143, 167, 184, 176, 98, 179, 72, 42, 214, 23, 145, 9, 214, 47, 254, 22, 152, 182, 82, 194, 171, 126, 118, 119, 87, 192, 36, 181},
{93, 162, 212, 56, 55, 138, 174, 1, 117, 22, 129, 122, 212, 161, 92, 220, 178, 53, 119, 177, 111, 233, 133, 194, 58, 192, 183, 57, 167, 247, 152, 81},
{138, 170, 159, 30, 237, 244, 80, 230, 79, 150, 12, 155, 244, 118, 126, 1, 234, 205, 124, 84, 116, 79, 204, 164, 206, 86, 151, 148, 99, 226, 190, 68},
{248, 236, 70, 21, 20, 138, 236, 107, 34, 17, 24, 130, 201, 124, 96, 217, 85, 33, 82, 180, 204, 77, 120, 81, 71, 102, 237, 95, 104, 31, 125, 89},
{18, 3, 24, 73, 102, 63, 197, 209, 78, 137, 121, 112, 128, 123, 39, 9, 1, 180, 24, 222, 135, 76, 217, 252, 97, 234, 39, 97, 42, 97, 38, 42},
{228, 45, 188, 152, 234, 51, 166, 35, 216, 41, 188, 76, 213, 212, 244, 206, 205, 116, 5, 211, 100, 181, 104, 188, 63, 244, 47, 236, 48, 88, 208, 80},
{153, 156, 164, 77, 144, 52, 216, 195, 138, 50, 122, 239, 93, 117, 149, 33, 44, 104, 51, 87, 193, 80, 43, 126, 57, 230, 104, 125, 215, 242, 31, 247},
{207, 155, 49, 11, 124, 188, 131, 180, 170, 150, 37, 109, 38, 174, 75, 104, 12, 226, 139, 143, 126, 241, 233, 148, 116, 99, 20, 212, 10, 62, 17, 173},
{232, 186, 3, 220, 51, 92, 23, 165, 204, 6, 50, 129, 26, 97, 116, 90, 252, 80, 42, 40, 241, 242, 12, 139, 40, 65, 144, 183, 117, 126, 246, 228},
{215, 126, 89, 118, 198, 245, 143, 230, 46, 91, 46, 26, 241, 207, 245, 100, 215, 96, 65, 70, 148, 160, 228, 189, 127, 47, 223, 252, 61, 144, 111, 95},
{120, 41, 21, 204, 241, 163, 28, 92, 171, 179, 152, 237, 125, 79, 247, 139, 126, 208, 125, 246, 189, 108, 137, 210, 156, 75, 238, 104, 210, 1, 141, 24},
{181, 108, 111, 71, 59, 212, 1, 131, 225, 115, 37, 14, 100, 77, 222, 171, 164, 193, 64, 145, 241, 64, 162, 123, 150, 194, 113, 2, 25, 6, 145, 13},
{199, 48, 251, 125, 111, 186, 180, 237, 180, 132, 113, 39, 91, 126, 21, 120, 230, 175, 135, 71, 203, 21, 156, 236, 208, 12, 20, 118, 213, 244, 64, 42},
{228, 248, 143, 123, 222, 58, 35, 82, 228, 211, 177, 68, 130, 15, 241, 252, 188, 147, 30, 76, 253, 249, 49, 249, 47, 69, 201, 223, 39, 167, 69, 54},
{29, 201, 235, 177, 124, 218, 197, 238, 93, 127, 73, 43, 46, 238, 83, 94, 96, 57, 138, 224, 138, 98, 197, 122, 96, 10, 177, 55, 102, 167, 190, 120},
{91, 89, 138, 236, 189, 205, 202, 28, 157, 194, 139, 189, 188, 222, 1, 98, 205, 25, 211, 241, 251, 164, 179, 216, 51, 91, 216, 202, 159, 197, 77, 4},
{76, 93, 179, 102, 191, 201, 9, 126, 167, 185, 251, 178, 251, 180, 156, 27, 21, 130, 33, 10, 138, 171, 114, 111, 198, 221, 80, 1, 83, 185, 253, 134},
{31, 137, 206, 229, 32, 215, 202, 228, 232, 101, 97, 35, 255, 234, 127, 236, 159, 230, 245, 56, 25, 32, 201, 66, 153, 211, 32, 73, 144, 210, 206, 133},
{42, 86, 219, 213, 217, 111, 135, 80, 70, 49, 102, 98, 210, 232, 158, 138, 9, 31, 131, 127, 79, 143, 146, 160, 50, 78, 110, 170, 123, 133, 183, 166},
{69, 223, 198, 190, 49, 54, 2, 163, 119, 185, 60, 107, 37, 131, 9, 62, 145, 184, 181, 28, 147, 95, 19, 12, 166, 4, 235, 241, 135, 215, 47, 217},
{103, 126, 39, 5, 127, 60, 220, 60, 120, 40, 162, 39, 65, 138, 112, 7, 160, 101, 210, 27, 244, 193, 20, 21, 219, 135, 56, 69, 78, 58, 189, 32},
{90, 87, 125, 20, 167, 248, 82, 21, 141, 69, 253, 119, 45, 1, 160, 160, 152, 226, 184, 84, 228, 78, 63, 186, 229, 248, 223, 190, 249, 99, 231, 171},
{130, 42, 80, 10, 216, 201, 245, 154, 5, 23, 74, 242, 101, 102, 184, 166, 246, 34, 14, 32, 226, 16, 14, 239, 23, 73, 139, 71, 68, 184, 143, 50},
{81, 169, 211, 130, 94, 168, 242, 140, 46, 186, 180, 233, 222, 1, 120, 13, 77, 60, 3, 41, 157, 45, 250, 153, 104, 220, 182, 172, 103, 226, 153, 121},
{72, 154, 94, 239, 83, 242, 137, 6, 24, 184, 7, 9, 225, 44, 112, 193, 74, 238, 216, 9, 229, 167, 71, 208, 89, 230, 197, 188, 101, 67, 6, 216},
{116, 252, 214, 166, 243, 67, 41, 154, 58, 78, 234, 85, 188, 76, 65, 246, 139, 100, 138, 7, 54, 163, 87, 118, 142, 205, 17, 26, 98, 95, 39, 242},
{144, 255, 15, 174, 25, 182, 1, 220, 175, 11, 41, 141, 69, 69, 174, 46, 120, 208, 177, 158, 130, 66, 119, 3, 235, 143, 255, 5, 149, 42, 138, 165},
{112, 233, 174, 39, 48, 209, 136, 82, 207, 75, 221, 255, 118, 18, 27, 139, 84, 186, 104, 189, 22, 174, 14, 176, 131, 31, 106, 243, 139, 173, 146, 244},
{250, 254, 133, 76, 108, 59, 53, 147, 15, 200, 135, 17, 138, 131, 147, 99, 199, 233, 75, 71, 240, 26, 199, 232, 60, 27, 173, 69, 39, 246, 92, 48},
{119, 210, 114, 33, 191, 38, 98, 22, 244, 162, 249, 182, 30, 234, 188, 43, 61, 164, 212, 142, 73, 23, 155, 62, 96, 128, 111, 104, 167, 146, 203, 65},
{167, 152, 96, 47, 165, 177, 203, 192, 142, 135, 92, 7, 61, 156, 32, 137, 220, 99, 13, 180, 186, 52, 77, 237, 74, 147, 251, 15, 108, 122, 59, 28},
{249, 181, 52, 222, 139, 244, 215, 224, 198, 114, 40, 243, 200, 5, 79, 102, 209, 44, 203, 56, 200, 23, 44, 99, 183, 250, 162, 206, 231, 158, 210, 112},
{195, 177, 63, 246, 116, 210, 113, 123, 248, 17, 244, 174, 232, 40, 110, 74, 27, 54, 182, 31, 213, 70, 119, 148, 22, 77, 62, 118, 73, 8, 4, 227},
{174, 223, 121, 235, 197, 225, 150, 67, 127, 80, 219, 62, 36, 51, 65, 225, 209, 187, 13, 144, 188, 232, 86, 67, 248, 61, 152, 24, 198, 30, 51, 118},
{169, 227, 202, 33, 181, 210, 194, 212, 51, 158, 125, 246, 64, 200, 59, 83, 94, 208, 5, 226, 181, 74, 53, 92, 39, 155, 121, 119, 196, 28, 160, 34},
{124, 154, 149, 143, 36, 8, 222, 81, 182, 28, 217, 58, 223, 4, 195, 230, 121, 181, 17, 97, 174, 39, 223, 245, 163, 115, 72, 29, 9, 250, 221, 93},
{94, 129, 132, 118, 175, 123, 131, 87, 207, 57, 77, 136, 126, 101, 29, 176, 73, 215, 180, 68, 41, 126, 101, 135, 219, 224, 57, 29, 241, 38, 251, 65},
{167, 177, 51, 217, 242, 161, 150, 33, 207, 188, 199, 179, 3, 252, 175, 40, 71, 23, 126, 212, 112, 84, 36, 19, 243, 40, 155, 89, 25, 44, 143, 44},
{142, 157, 145, 41, 142, 233, 158, 158, 64, 158, 34, 89, 115, 91, 16, 81, 46, 212, 130, 142, 166, 58, 205, 243, 193, 255, 109, 107, 83, 94, 185, 217},
{103, 181, 101, 82, 232, 131, 3, 160, 69, 31, 133, 57, 115, 220, 168, 30, 207, 218, 190, 44, 102, 244, 113, 203, 36, 224, 195, 195, 212, 238, 52, 182},
{104, 138, 170, 151, 231, 202, 217, 205, 81, 42, 246, 108, 123, 2, 40, 96, 196, 95, 144, 98, 49, 43, 23, 184, 181, 141, 105, 31, 176, 224, 164, 81},
{138, 159, 183, 96, 66, 36, 16, 145, 131, 178, 48, 236, 226, 217, 221, 117, 86, 187, 22, 179, 167, 17, 14, 54, 180, 217, 218, 74, 69, 245, 169, 120},
{91, 206, 220, 176, 108, 243, 52, 201, 226, 28, 70, 196, 123, 18, 54, 236, 230, 47, 81, 109, 168, 137, 192, 19, 127, 143, 11, 161, 226, 230, 31, 19},
{24, 207, 128, 6, 155, 134, 151, 169, 43, 105, 35, 121, 125, 93, 184, 219, 76, 180, 221, 129, 248, 201, 38, 206, 237, 34, 227, 219, 92, 238, 20, 4},
{76, 30, 37, 108, 85, 239, 66, 35, 18, 15, 80, 26, 63, 117, 31, 162, 172, 3, 140, 4, 250, 168, 31, 250, 208, 3, 41, 58, 66, 122, 214, 223},
{13, 114, 121, 124, 89, 22, 163, 146, 144, 123, 191, 224, 85, 156, 229, 6, 254, 190, 77, 25, 36, 208, 94, 171, 60, 104, 191, 188, 222, 202, 52, 249},
{61, 6, 137, 137, 31, 211, 146, 84, 248, 242, 181, 113, 192, 186, 69, 59, 7, 72, 9, 101, 14, 204, 101, 246, 140, 62, 12, 151, 191, 78, 125, 45},
{157, 136, 146, 168, 3, 25, 221, 183, 210, 160, 37, 98, 46, 154, 200, 51, 233, 78, 211, 136, 192, 80, 238, 133, 173, 53, 176, 141, 252, 127, 213, 208},
{236, 37, 13, 175, 18, 251, 87, 187, 224, 204, 128, 5, 91, 147, 115, 122, 151, 89, 90, 163, 65, 38, 17, 122, 231, 248, 212, 192, 124, 138, 107, 170},
{145, 19, 150, 65, 190, 97, 199, 178, 76, 115, 138, 198, 136, 18, 180, 253, 248, 247, 187, 179, 194, 161, 221, 246, 94, 254, 64, 61, 91, 186, 104, 69},
{235, 120, 75, 39, 150, 196, 72, 209, 145, 27, 180, 77, 11, 2, 154, 155, 125, 233, 102, 2, 252, 239, 45, 119, 156, 67, 142, 249, 160, 160, 43, 116},
{143, 165, 24, 101, 222, 187, 133, 80, 114, 98, 164, 11, 16, 227, 43, 133, 145, 213, 75, 107, 148, 34, 89, 73, 28, 136, 140, 131, 231, 105, 2, 209},
{255, 184, 148, 30, 2, 59, 196, 206, 224, 101, 11, 205, 173, 175, 148, 17, 245, 251, 207, 74, 117, 102, 216, 250, 162, 252, 162, 141, 19, 22, 115, 134},
{31, 58, 43, 194, 88, 106, 56, 41, 88, 34, 189, 211, 128, 188, 100, 228, 149, 6, 140, 214, 89, 223, 4, 232, 12, 183, 1, 187, 28, 146, 97, 11},
{173, 159, 50, 163, 30, 151, 172, 58, 118, 11, 139, 20, 227, 150, 135, 213, 107, 120, 100, 176, 68, 197, 190, 248, 82, 15, 225, 61, 55, 196, 240, 250},
{8, 42, 165, 143, 186, 179, 64, 44, 100, 15, 30, 158, 136, 10, 240, 220, 181, 174, 221, 223, 195, 144, 160, 79, 89, 146, 43, 90, 157, 51, 97, 249},
{61, 3, 209, 85, 236, 48, 55, 183, 70, 6, 193, 208, 190, 103, 211, 46, 221, 3, 25, 245, 200, 43, 37, 8, 104, 91, 246, 3, 89, 13, 132, 120},
{91, 121, 64, 214, 89, 93, 32, 238, 196, 217, 242, 53, 71, 78, 136, 226, 189, 164, 233, 98, 238, 230, 250, 56, 65, 83, 137, 141, 171, 250, 231, 62},
{133, 122, 163, 187, 119, 181, 55, 152, 138, 23, 49, 26, 211, 59, 150, 19, 144, 166, 205, 184, 82, 209, 107, 20, 157, 165, 177, 216, 27, 103, 147, 81},
{138, 114, 71, 105, 110, 180, 111, 127, 214, 105, 13, 43, 148, 113, 228, 203, 37, 239, 239, 238, 125, 114, 244, 74, 24, 241, 242, 146, 130, 94, 46, 79},
{85, 108, 150, 69, 191, 198, 106, 86, 228, 219, 78, 42, 73, 10, 92, 242, 16, 3, 18, 27, 228, 216, 36, 149, 19, 179, 28, 6, 226, 38, 163, 82},
{191, 46, 144, 17, 234, 91, 79, 85, 44, 28, 26, 143, 24, 237, 106, 132, 165, 28, 88, 162, 186, 248, 45, 92, 31, 189, 164, 172, 255, 51, 125, 111},
{15, 105, 201, 161, 101, 197, 235, 191, 127, 28, 238, 232, 231, 198, 234, 172, 192, 193, 60, 42, 87, 165, 80, 226, 245, 151, 8, 214, 96, 118, 19, 23},
{84, 157, 205, 255, 217, 251, 101, 194, 230, 208, 26, 232, 23, 201, 46, 29, 123, 221, 11, 53, 196, 102, 220, 130, 2, 70, 240, 1, 178, 74, 188, 195},
{244, 120, 86, 42, 110, 203, 209, 158, 119, 115, 207, 5, 104, 140, 138, 113, 25, 153, 59, 171, 105, 67, 136, 70, 30, 10, 203, 80, 13, 200, 172, 216},
{116, 64, 52, 174, 54, 126, 16, 194, 162, 33, 33, 157, 176, 197, 225, 12, 59, 55, 253, 228, 148, 47, 179, 185, 24, 138, 253, 20, 142, 55, 172, 88}};
d_genpoly_coeff = {88, 216, 195, 23, 111, 82, 79, 81, 62, 120, 249, 250, 11, 134, 209, 116, 69, 170, 208, 45, 249, 223, 4, 19, 120, 81, 182, 217, 44, 65, 93, 34, 118, 227, 112, 28, 65, 48, 244, 165, 242, 216, 121, 50, 171, 32, 217, 166, 133, 134, 4, 120, 54, 42, 13, 24, 95, 228, 173, 247, 80, 42, 89, 68, 81, 181, 112, 51, 118, 108, 243, 223, 18, 38, 230, 1, 28, 109, 131, 14, 234, 151, 21, 108, 7, 176, 236, 147, 175, 183, 66, 35, 178, 243, 36, 115, 255, 51, 36, 6, 120, 163, 59, 9, 214, 102, 109, 253, 152, 137, 1, 144, 124, 241, 143, 71, 91, 227, 28, 174, 13, 157, 78, 20, 192, 64, 130, 45, 39, 46, 229, 171, 193, 252, 43, 165, 88, 180, 179, 183, 88, 99, 219, 52, 210, 33, 160, 146, 22, 255, 111, 159, 7, 237, 145, 194, 68, 89, 231, 201, 224, 127, 5, 27, 112, 71, 165, 204, 236, 122, 119, 49, 212, 216, 151, 149, 53, 249, 57, 136, 85, 14, 19, 128, 135, 177, 179, 189, 164, 98, 220, 99, 241, 230, 188, 170, 148, 97, 121, 31, 253, 134, 43, 199, 81, 137, 82, 54, 47, 216, 172, 169, 123, 246, 153, 169, 32, 86, 128, 83, 5, 252, 251, 1};
init_log_tables();
init_alpha_tables();
}
ReedSolomon::ReedSolomon(int nroots,
int minpoly,
int prim,
int fcr,
int pad,
const std::vector<uint8_t>& genpoly_coeff,
const std::vector<std::vector<uint8_t>>& gen_matrix)
{
if (fcr < 0 || fcr >= (1 << 8))
{
std::cerr << "Reed Solomon bad configuration: fcr must be 0 < frc <= 255\n";
return;
}
if (prim <= 0 || prim >= (1 << 8))
{
std::cerr << "Reed Solomon bad configuration: prim must be 0 <= prim <= 255\n";
return;
}
if (nroots < 0 || nroots >= (1 << 8))
{
// Can't have more roots than symbol values!
std::cerr << "Reed Solomon bad configuration: nroots must be 0 < nroots <= 255\n";
return;
}
if (pad < 0 || pad >= ((1 << 8) - 1 - nroots))
{
// Too much padding
std::cerr << "Reed Solomon bad configuration: pad must be 0 < pad <= 255-nroots\n";
return;
}
d_nroots = nroots;
d_data_in_block = d_symbols_per_block - d_nroots;
d_min_poly = minpoly;
d_prim = prim;
d_pad = pad;
d_a0 = static_cast<uint8_t>(d_symbols_per_block);
d_fcr = fcr;
if (!genpoly_coeff.empty() && (int(genpoly_coeff.size()) == d_nroots + 1))
{
d_genpoly_coeff = genpoly_coeff;
}
else
{
d_genpoly_coeff = std::vector<uint8_t>(d_nroots + 1, 0);
}
size_t rows_G = gen_matrix.size();
size_t columns_G = gen_matrix[0].size();
if (!gen_matrix.empty() && ((rows_G == std::size_t(d_symbols_per_block)) && (columns_G == d_data_in_block)))
{
d_genmatrix = gen_matrix;
}
else
{
d_genmatrix = std::vector<std::vector<uint8_t>>(d_symbols_per_block, std::vector<uint8_t>(d_data_in_block, 0));
}
init_log_tables();
init_alpha_tables();
}
int ReedSolomon::mod255(int x) const
{
while (x >= d_symbols_per_block)
{
x -= d_symbols_per_block;
x = (x >> 8) + (x & d_symbols_per_block);
}
return x;
}
int ReedSolomon::rs_min(int a, int b) const
{
return (a) < (b) ? (a) : (b);
}
uint8_t ReedSolomon::galois_mul(uint8_t a, uint8_t b) const
{
uint8_t p = 0;
uint8_t carry;
int i;
for (i = 0; i < 8; i++)
{
if (b & 1)
{
p ^= a;
}
carry = a & 0x80;
a = a << 1;
if (carry)
{
a ^= d_min_poly;
}
b = b >> 1;
}
return p;
}
uint8_t ReedSolomon::galois_add(uint8_t a, uint8_t b) const
{
return a ^ b;
}
uint8_t ReedSolomon::galois_mul_table(uint8_t a, uint8_t b) const
{
if (a == 0 || b == 0)
{
return 0;
}
uint8_t x = d_log_table[a];
uint8_t y = d_log_table[b];
uint8_t log_mult = (x + y) % d_symbols_per_block;
return d_antilog[log_mult];
}
void ReedSolomon::init_log_tables()
{
d_log_table[0] = 0; // dummy value
for (int i = 0, x = 1; i < d_symbols_per_block; x = galois_mul(x, d_min_poly), i++)
{
d_log_table[x] = i;
d_antilog[i] = x;
}
}
void ReedSolomon::init_alpha_tables()
{
const int symsize = 8;
// Generate Galois field lookup tables
d_index_of[0] = d_a0;
d_alpha_to[d_a0] = 0;
int sr = 1;
for (int i = 0; i < d_symbols_per_block; i++)
{
d_index_of[sr] = i;
d_alpha_to[i] = sr;
sr <<= 1;
if (sr & (1 << symsize))
{
sr ^= d_min_poly;
}
sr &= d_symbols_per_block;
}
if (sr != 1)
{
std::cerr << "Field generator polynomial is not primitive!\n";
return;
}
// Find prim-th root of 1, used in decoding
int iprim;
for (iprim = 1; (iprim % d_prim) != 0; iprim += d_symbols_per_block)
{
};
d_iprim = iprim / d_prim;
// get indexes of generator polymonial coefficients
d_genpoly_index.reserve(d_nroots + 1);
for (int i = 0; i <= d_nroots; i++)
{
d_genpoly_index[i] = d_index_of[d_genpoly_coeff[i]];
}
}
std::vector<uint8_t> ReedSolomon::encode_with_generator_matrix(const std::vector<uint8_t>& data_to_encode) const
{
std::vector<uint8_t> encoded_output(d_symbols_per_block, 0);
if (data_to_encode.size() != d_data_in_block)
{
return encoded_output;
}
for (int i = 0; i < d_symbols_per_block; i++)
{
for (size_t k = 0; k < d_data_in_block; k++)
{
encoded_output[i] = galois_add(encoded_output[i], galois_mul_table(d_genmatrix[i][k], data_to_encode[k]));
}
}
return encoded_output;
}
std::vector<uint8_t> ReedSolomon::encode_with_generator_poly(const std::vector<uint8_t>& data_to_encode) const
{
std::vector<uint8_t> encoded_output(d_symbols_per_block, 0);
if (data_to_encode.size() != d_data_in_block)
{
return encoded_output;
}
for (size_t k = 0; k < d_data_in_block; k++)
{
encoded_output[k] = data_to_encode[k];
}
int parity_offset = d_symbols_per_block - d_nroots;
encode_rs_8(encoded_output.data(), encoded_output.data() + parity_offset);
return encoded_output;
}
void ReedSolomon::encode_rs_8(const uint8_t* data, uint8_t* parity) const
{
int i;
int j;
uint8_t feedback;
memset(parity, 0, d_nroots * sizeof(uint8_t));
for (i = 0; i < d_symbols_per_block - d_nroots - d_pad; i++)
{
feedback = d_index_of[data[i] ^ parity[0]];
if (feedback != d_a0)
{
// feedback term is non-zero
feedback = mod255(d_symbols_per_block - d_genpoly_index[d_nroots] + feedback);
for (j = 1; j < d_nroots; j++)
{
parity[j] ^= d_alpha_to[mod255(feedback + d_genpoly_index[d_nroots - j])];
}
}
// Shift
memmove(&parity[0], &parity[1], sizeof(uint8_t) * (d_nroots - 1));
if (feedback != d_a0)
{
parity[d_nroots - 1] = d_alpha_to[mod255(feedback + d_genpoly_index[0])];
}
else
{
parity[d_nroots - 1] = 0;
}
}
}
int ReedSolomon::decode(std::vector<uint8_t>& data_to_decode, const std::vector<int>& erasure_positions) const
{
if (data_to_decode.size() != d_symbols_per_block)
{
std::cerr << "Reed Solomon error: bad input length\n";
return -1;
}
int result = decode_rs_8(data_to_decode.data(), erasure_positions.data(), erasure_positions.size(), d_pad);
return result;
}
int ReedSolomon::decode_rs_8(uint8_t* data, const int* eras_pos, int no_eras, int pad) const
{
if (pad < 0 || pad > 222)
{
return -1;
}
int deg_lambda;
int el;
int deg_omega;
int i;
int j;
int r;
int k;
int syn_error;
int count;
uint8_t u;
uint8_t q;
uint8_t tmp;
uint8_t num1;
uint8_t num2;
uint8_t den;
uint8_t discr_r;
uint8_t lambda[d_nroots + 1]; // Err+Eras Locator poly
uint8_t s[d_nroots]; // syndrome poly
uint8_t b[d_nroots + 1];
uint8_t t[d_nroots + 1];
uint8_t omega[d_nroots + 1];
uint8_t root[d_nroots];
uint8_t reg[d_nroots + 1];
uint8_t loc[d_nroots];
// Syndrome computation
// form the syndromes; i.e., evaluate data(x) at roots of g(x)
for (i = 0; i < d_nroots; i++)
{
s[i] = data[0];
}
for (j = 1; j < d_symbols_per_block - d_pad; j++)
{
for (i = 0; i < d_nroots; i++)
{
if (s[i] == 0)
{
s[i] = data[j];
}
else
{
s[i] = data[j] ^ d_alpha_to[mod255(d_index_of[s[i]] + (d_fcr + i) * d_prim)];
}
}
}
// Convert syndromes to index form, checking for nonzero condition
syn_error = 0;
for (i = 0; i < d_nroots; i++)
{
syn_error |= s[i];
s[i] = d_index_of[s[i]];
}
if (!syn_error)
{
// if syndrome is zero, data[] is a codeword and there are no
// errors to correct. So return data[] unmodified
return 0;
}
memset(&lambda[1], 0, d_nroots * sizeof(lambda[0]));
lambda[0] = 1;
if (no_eras > 0)
{
// Init lambda to be the erasure locator polynomial
lambda[1] = d_alpha_to[mod255(d_prim * (d_symbols_per_block - 1 - eras_pos[0]))];
for (i = 1; i < no_eras; i++)
{
u = mod255(d_prim * (d_symbols_per_block - 1 - eras_pos[i]));
for (j = i + 1; j > 0; j--)
{
tmp = d_index_of[lambda[j - 1]];
if (tmp != d_a0)
{
lambda[j] ^= d_alpha_to[mod255(u + tmp)];
}
}
}
}
for (i = 0; i < d_nroots + 1; i++)
{
b[i] = d_index_of[lambda[i]];
}
// Begin Berlekamp-Massey algorithm to determine error+erasure
// locator polynomial
r = no_eras;
el = no_eras;
while (++r <= d_nroots) // r is the step number
{
// Compute discrepancy at the r-th step in poly-form
discr_r = 0;
for (i = 0; i < r; i++)
{
if ((lambda[i] != 0) && (s[r - i - 1] != d_a0))
{
discr_r ^= d_alpha_to[mod255(d_index_of[lambda[i]] + s[r - i - 1])];
}
}
discr_r = d_index_of[discr_r]; // Index form
if (discr_r == d_a0)
{
// 2 lines below: B(x) <-- x*B(x)
memmove(&b[1], b, d_nroots * sizeof(b[0]));
b[0] = d_a0;
}
else
{
// 12 lines below: T(x) <-- lambda(x) - discr_r*x*b(x)
t[0] = lambda[0];
for (i = 0; i < d_nroots; i++)
{
if (b[i] != d_a0)
{
t[i + 1] = lambda[i + 1] ^ d_alpha_to[mod255(discr_r + b[i])];
}
else
{
t[i + 1] = lambda[i + 1];
}
}
if (2 * el <= r + no_eras - 1)
{
el = r + no_eras - el;
// 4 lines below: B(x) <-- inv(discr_r) * lambda(x)
for (i = 0; i <= d_nroots; i++)
{
b[i] = (lambda[i] == 0) ? d_a0 : mod255(d_index_of[lambda[i]] - discr_r + d_symbols_per_block);
}
}
else
{
// 2 lines below: B(x) <-- x*B(x)
memmove(&b[1], b, d_nroots * sizeof(b[0]));
b[0] = d_a0;
}
memcpy(lambda, t, (d_nroots + 1) * sizeof(t[0]));
}
}
// Convert lambda to index form and compute deg(lambda(x))
deg_lambda = 0;
for (i = 0; i < d_nroots + 1; i++)
{
lambda[i] = d_index_of[lambda[i]];
if (lambda[i] != d_a0)
{
deg_lambda = i;
}
}
// Find roots of the error+erasure locator polynomial by Chien search
memcpy(&reg[1], &lambda[1], d_nroots * sizeof(reg[0]));
count = 0; // Number of roots of lambda(x)
for (i = 1, k = d_iprim - 1; i <= d_symbols_per_block; i++, k = mod255(k + d_iprim))
{
q = 1; // lambda[0] is always 0
for (j = deg_lambda; j > 0; j--)
{
if (reg[j] != d_a0)
{
reg[j] = mod255(reg[j] + j);
q ^= d_alpha_to[reg[j]];
}
}
if (q != 0)
{
// Not a root
// store root (index-form) and error location number
continue;
}
// Corrections of errors and erasures at the locations and magnitude
// as per Chien Search and Forney Algorithm
root[count] = i;
loc[count] = k;
// If we have already found max possible roots, abort the search to save time
if (++count == deg_lambda)
{
break;
}
}
if (deg_lambda != count)
{
// deg(lambda) unequal to number of roots => uncorrectable
// error detected
return -1;
}
// Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
// x**d_nroots). in index form. Also find deg(omega).
deg_omega = deg_lambda - 1;
for (i = 0; i <= deg_omega; i++)
{
tmp = 0;
for (j = i; j >= 0; j--)
{
if ((s[i - j] != d_a0) && (lambda[j] != d_a0))
{
tmp ^= d_alpha_to[mod255(s[i - j] + lambda[j])];
}
}
omega[i] = d_index_of[tmp];
}
// Compute error values in poly-form. num1 = omega(inv(X(l))),
// num2 = inv(X(l))**(d_fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form
for (j = count - 1; j >= 0; j--)
{
num1 = 0;
for (i = deg_omega; i >= 0; i--)
{
if (omega[i] != d_a0)
{
num1 ^= d_alpha_to[mod255(omega[i] + i * root[j])];
}
}
num2 = d_alpha_to[mod255(root[j] * (d_fcr - 1) + d_symbols_per_block)];
den = 0;
// lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i]
for (i = rs_min(deg_lambda, d_nroots - 1) & ~1; i >= 0; i -= 2)
{
if (lambda[i + 1] != d_a0)
{
den ^= d_alpha_to[mod255(lambda[i + 1] + i * root[j])];
}
}
// Apply error to data
if (num1 != 0 && loc[j] >= d_pad)
{
data[loc[j] - d_pad] ^=
d_alpha_to[mod255(d_index_of[num1] + d_index_of[num2] + d_symbols_per_block - d_index_of[den])];
}
}
return count;
}

View File

@ -0,0 +1,143 @@
/*!
* \file reed_solomon.h
* \brief Class implementing a Reed-Solomon encoder/decoder
* \author Carles Fernandez, 2021. cfernandez(at)cttc.es
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#ifndef GNSS_SDR_REED_SOLOMON_H
#define GNSS_SDR_REED_SOLOMON_H
#include <array>
#include <cstddef>
#include <cstdint>
#include <vector>
/** \addtogroup Core
* \{ */
/** \addtogroup System_Parameters
* \{ */
/*!
* \brief
* Class implementing a Reed-Solomon encoder and decoder RS(255,K,d) where
* k=255-nroots is the information vector length and d=nroots+1 is the minimum
* Hamming distance, with symbols of 8 bits.
*/
class ReedSolomon
{
public:
/*!
* \brief Default constructor.
* Constructs a Reed Solomon object with default settings for Galileo E6B
* HAS messages encoding and decoding. The encode_with_generator_poly
* and encode_with_generator_matrix methods are available for testing
* purposes.
*/
ReedSolomon();
/*!
* \brief Custom constructor for RS(255, 255-nroots, nroots+1). Parameters:
*
* nroots - the number of roots in the RS code generator polynomial,
* which is the same as the number of parity symbols in a block.
*
* minpoly - primitive polynomial.
*
* prim - the primitive root of the generator polynomial.
*
* fcr - first consecutive root of the Reed-Solomon generator polynomial.
*
* pad - the number of pad symbols in a block. Defaults to 0.
*
* genpoly_coeff - a vector of nroots+1 elements containing the generator
* polynomial coefficients. Only used for encoding. Defaults to empty.
* If defined, the encode_with_generator_poly method can be used.
*
* gen_matrix - a 255x(255-nroots) matrix containing the elements of the
* generator matrix. Only used for encoding. Defaults to empty.
* If defined, the encode_with_generator_matrix method can be used.
*
*/
ReedSolomon(int nroots,
int minpoly,
int prim,
int fcr,
int pad = 0,
const std::vector<uint8_t>& genpoly_coeff = std::vector<uint8_t>{},
const std::vector<std::vector<uint8_t>>& gen_matrix = std::vector<std::vector<uint8_t>>{});
/*!
* \brief Decode an encoded block.
*
* The decoded symbols are at the first 255-nroots elements
* of the input vector.
*
* The second parameter is optional, and contains a vector of erasure
* positions to be passed to the decoding algorithm. Defaults to empty.
*
* Returns the number of corrected errors or -1 if decoding failed.
*/
int decode(std::vector<uint8_t>& data_to_decode,
const std::vector<int>& erasure_positions = std::vector<int>{}) const;
/*!
* \brief Encode data with the generator matrix (for testing purposes)
*/
std::vector<uint8_t> encode_with_generator_matrix(const std::vector<uint8_t>& data_to_encode) const;
/*!
* \brief Encode data with the generator polynomial (for testing purposes)
*/
std::vector<uint8_t> encode_with_generator_poly(const std::vector<uint8_t>& data_to_encode) const;
private:
static const int d_symbols_per_block = 255; // the total number of symbols in a RS block.
int mod255(int x) const;
int rs_min(int a, int b) const;
int decode_rs_8(uint8_t* data, const int* eras_pos, int no_eras, int pad) const;
uint8_t galois_mul(uint8_t a, uint8_t b) const;
uint8_t galois_add(uint8_t a, uint8_t b) const;
uint8_t galois_mul_table(uint8_t a, uint8_t b) const;
void encode_rs_8(const uint8_t* data, uint8_t* parity) const;
void init_log_tables(); // initialize d_log_table and d_antilog
void init_alpha_tables(); // initialize d_alpha_to, d_index_of
std::array<uint8_t, 256> d_alpha_to{}; // used for decoding
std::array<uint8_t, 256> d_index_of{}; // used for decoding
std::array<uint8_t, 256> d_log_table{}; // used for encoding
std::array<uint8_t, 255> d_antilog{}; // used for encoding
std::vector<std::vector<uint8_t>> d_genmatrix; // used for encoding
std::vector<uint8_t> d_genpoly_coeff; // used for encoding
std::vector<uint8_t> d_genpoly_index; // used for encoding
size_t d_data_in_block{}; // number of information symbols in a block
int d_nroots{}; // number of parity symbols in a block
int d_prim{}; // The primitive root of the generator poly
int d_pad{}; // the number of pad symbols in a block
int d_iprim{}; // prim-th root of 1, index form
int d_fcr{}; // first consecutive root
uint8_t d_min_poly{}; // primitive polynomial
uint8_t d_a0{}; // auxiliar variable
};
/** \} */
/** \} */
#endif // GNSS_SDR_REED_SOLOMON_H

View File

@ -126,6 +126,7 @@ DECLARE_string(log_dir);
#include "unit-tests/signal-processing-blocks/pvt/rtcm_test.cc"
#include "unit-tests/signal-processing-blocks/pvt/serdes_monitor_pvt_test.cc"
#include "unit-tests/signal-processing-blocks/telemetry_decoder/galileo_fnav_inav_decoder_test.cc"
#include "unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc"
#include "unit-tests/system-parameters/glonass_gnav_crc_test.cc"
#include "unit-tests/system-parameters/glonass_gnav_ephemeris_test.cc"
#include "unit-tests/system-parameters/glonass_gnav_nav_message_test.cc"

View File

@ -0,0 +1,537 @@
/*!
* \file galileo_e6b_reed_solomon_test.cc
* \brief Tests for Reed Solomon encoder and decoder
* \author Carles Fernandez-Prades, 2021. cfernandez(at)cttc.es
*
*
* -----------------------------------------------------------------------------
*
* GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
* This file is part of GNSS-SDR.
*
* Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* -----------------------------------------------------------------------------
*/
#include "gnss_sdr_make_unique.h"
#include "reed_solomon.h"
#include <gtest/gtest.h>
TEST(ReedSolomonTest, EncodeWithGenMatrix)
{
// input vector as defined in Galileo HAS ICD v1.2, Annex D
const std::vector<uint8_t> input = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
// Expected encoded output as defined in Galileo HAS ICD v1.2, Annex D
const std::vector<uint8_t> expected_output = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
auto rs = std::make_unique<ReedSolomon>();
std::vector<uint8_t> encoded_output = rs->encode_with_generator_matrix(input);
EXPECT_TRUE(encoded_output == expected_output);
}
TEST(ReedSolomonTest, EncodeWithGenPoly)
{
// input vector as defined in Galileo HAS ICD v1.2, Annex D
const std::vector<uint8_t> input = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
// Expected encoded output as defined in Galileo HAS ICD v1.2, Annex D
const std::vector<uint8_t> expected_output = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
auto rs = std::make_unique<ReedSolomon>();
std::vector<uint8_t> encoded_output = rs->encode_with_generator_poly(input);
EXPECT_TRUE(encoded_output == expected_output);
}
TEST(ReedSolomonTest, EncodeWithCustomPoly)
{
// input vector as defined in Galileo HAS ICD v1.2, Annex D
const std::vector<uint8_t> input = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
// Expected encoded output as defined in Galileo HAS ICD v1.2, Annex D
const std::vector<uint8_t> expected_output = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
int nroots = 223;
int minpoly = 29;
int prim = 127;
int fcr = 209;
int pad = 0;
const std::vector<uint8_t> genpoly_coeff = {88, 216, 195, 23, 111, 82, 79, 81, 62, 120, 249, 250, 11, 134, 209, 116, 69, 170, 208, 45, 249, 223, 4, 19, 120, 81, 182, 217, 44, 65, 93, 34, 118, 227, 112, 28, 65, 48, 244, 165, 242, 216, 121, 50, 171, 32, 217, 166, 133, 134, 4, 120, 54, 42, 13, 24, 95, 228, 173, 247, 80, 42, 89, 68, 81, 181, 112, 51, 118, 108, 243, 223, 18, 38, 230, 1, 28, 109, 131, 14, 234, 151, 21, 108, 7, 176, 236, 147, 175, 183, 66, 35, 178, 243, 36, 115, 255, 51, 36, 6, 120, 163, 59, 9, 214, 102, 109, 253, 152, 137, 1, 144, 124, 241, 143, 71, 91, 227, 28, 174, 13, 157, 78, 20, 192, 64, 130, 45, 39, 46, 229, 171, 193, 252, 43, 165, 88, 180, 179, 183, 88, 99, 219, 52, 210, 33, 160, 146, 22, 255, 111, 159, 7, 237, 145, 194, 68, 89, 231, 201, 224, 127, 5, 27, 112, 71, 165, 204, 236, 122, 119, 49, 212, 216, 151, 149, 53, 249, 57, 136, 85, 14, 19, 128, 135, 177, 179, 189, 164, 98, 220, 99, 241, 230, 188, 170, 148, 97, 121, 31, 253, 134, 43, 199, 81, 137, 82, 54, 47, 216, 172, 169, 123, 246, 153, 169, 32, 86, 128, 83, 5, 252, 251, 1};
auto rs = std::make_unique<ReedSolomon>(nroots, minpoly, prim, fcr, pad, genpoly_coeff);
std::vector<uint8_t> encoded_output = rs->encode_with_generator_poly(input);
EXPECT_TRUE(encoded_output == expected_output);
}
TEST(ReedSolomonTest, EncodeWithCustomMatrix)
{
// input vector as defined in Galileo HAS ICD v1.2, Annex D
const std::vector<uint8_t> input = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
// Expected encoded output as defined in Galileo HAS ICD v1.2, Annex D
const std::vector<uint8_t> expected_output = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
int nroots = 223;
int minpoly = 29;
int prim = 1;
int fcr = 1;
int pad = 0;
std::vector<uint8_t> genpoly_coeff;
const std::vector<std::vector<uint8_t>> genmatrix = {
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{19, 143, 180, 59, 221, 29, 49, 45, 231, 9, 73, 73, 159, 2, 158, 136, 212, 218, 14, 113, 215, 20, 187, 55, 137, 181, 203, 113, 97, 135, 14, 251},
{27, 27, 1, 50, 255, 109, 251, 156, 148, 151, 85, 21, 74, 116, 250, 77, 60, 203, 113, 196, 213, 23, 202, 125, 31, 252, 90, 1, 176, 226, 44, 252},
{98, 153, 190, 38, 223, 28, 28, 149, 170, 219, 41, 235, 236, 175, 113, 182, 239, 31, 74, 241, 127, 121, 207, 137, 205, 70, 88, 218, 250, 129, 99, 5},
{95, 235, 199, 105, 168, 182, 233, 133, 201, 135, 171, 89, 50, 230, 115, 227, 21, 122, 41, 226, 93, 59, 20, 36, 30, 150, 134, 240, 34, 91, 183, 83},
{172, 171, 163, 123, 27, 81, 14, 251, 24, 56, 15, 35, 244, 148, 24, 35, 96, 195, 47, 232, 148, 85, 117, 91, 39, 5, 74, 71, 104, 116, 14, 128},
{117, 108, 167, 111, 201, 61, 244, 13, 5, 236, 75, 124, 11, 233, 60, 127, 101, 117, 144, 13, 51, 70, 138, 247, 188, 171, 120, 104, 141, 220, 39, 86},
{243, 8, 122, 204, 147, 89, 112, 127, 204, 217, 20, 179, 8, 167, 203, 254, 95, 38, 22, 249, 215, 127, 101, 46, 99, 252, 183, 17, 8, 122, 191, 32},
{90, 195, 11, 73, 110, 20, 55, 185, 206, 241, 12, 193, 185, 72, 141, 27, 97, 29, 251, 144, 6, 109, 129, 203, 222, 64, 164, 49, 173, 37, 167, 169},
{164, 41, 177, 10, 34, 58, 123, 165, 122, 70, 108, 145, 240, 246, 208, 134, 248, 114, 237, 129, 149, 218, 70, 63, 105, 5, 184, 222, 9, 255, 213, 153},
{211, 255, 215, 20, 146, 60, 12, 202, 1, 95, 234, 192, 175, 223, 81, 99, 59, 136, 191, 82, 138, 174, 112, 1, 21, 14, 137, 7, 4, 238, 50, 246},
{220, 94, 230, 67, 248, 163, 186, 77, 68, 20, 1, 180, 150, 94, 127, 36, 154, 47, 101, 114, 172, 174, 172, 248, 130, 250, 55, 68, 17, 106, 3, 123},
{110, 20, 220, 172, 53, 110, 224, 20, 10, 192, 59, 46, 159, 96, 14, 203, 214, 144, 215, 141, 13, 190, 175, 232, 55, 123, 104, 223, 79, 38, 146, 169},
{164, 29, 102, 221, 199, 97, 1, 114, 215, 130, 93, 166, 31, 208, 248, 5, 40, 197, 96, 173, 136, 209, 149, 17, 74, 236, 131, 18, 231, 29, 214, 172},
{251, 94, 49, 176, 56, 250, 251, 10, 237, 114, 111, 176, 78, 90, 148, 97, 69, 174, 3, 178, 4, 16, 151, 192, 36, 202, 212, 81, 210, 20, 219, 216},
{116, 79, 18, 201, 172, 40, 33, 232, 54, 187, 32, 61, 5, 227, 55, 18, 43, 107, 202, 220, 141, 66, 224, 166, 158, 176, 61, 11, 143, 232, 112, 47},
{187, 194, 174, 69, 228, 144, 68, 94, 189, 124, 254, 101, 65, 91, 176, 76, 117, 203, 236, 169, 202, 251, 11, 110, 242, 80, 181, 94, 162, 92, 111, 54},
{29, 150, 209, 144, 66, 224, 111, 137, 81, 38, 230, 100, 15, 45, 7, 31, 208, 240, 210, 18, 111, 85, 199, 64, 247, 215, 164, 75, 231, 34, 69, 82},
{191, 102, 106, 86, 63, 166, 105, 80, 243, 169, 231, 39, 86, 171, 77, 223, 72, 220, 171, 98, 179, 115, 160, 191, 202, 89, 192, 20, 178, 54, 121, 137},
{254, 252, 23, 88, 159, 236, 167, 50, 34, 70, 225, 175, 28, 89, 25, 150, 163, 25, 241, 87, 152, 213, 166, 176, 237, 50, 249, 60, 144, 205, 27, 81},
{138, 9, 193, 221, 141, 92, 54, 239, 124, 193, 92, 251, 33, 190, 134, 68, 160, 220, 80, 210, 146, 184, 240, 135, 188, 129, 101, 218, 102, 213, 132, 199},
{184, 160, 40, 202, 31, 235, 178, 121, 225, 205, 231, 122, 61, 178, 191, 195, 55, 13, 2, 41, 245, 69, 128, 182, 5, 90, 7, 28, 79, 58, 11, 43},
{247, 8, 171, 147, 180, 87, 67, 121, 151, 143, 177, 155, 64, 107, 163, 222, 211, 152, 178, 184, 68, 211, 218, 210, 252, 37, 84, 189, 44, 186, 133, 134},
{31, 50, 155, 253, 213, 220, 84, 174, 239, 85, 87, 105, 214, 81, 160, 211, 90, 32, 239, 171, 171, 238, 177, 234, 36, 233, 216, 77, 44, 173, 205, 253},
{113, 18, 35, 135, 205, 43, 156, 23, 127, 169, 162, 160, 15, 49, 202, 100, 165, 163, 175, 30, 199, 19, 141, 197, 211, 200, 134, 41, 215, 154, 34, 31},
{204, 239, 127, 208, 89, 187, 30, 192, 37, 152, 221, 214, 211, 49, 93, 9, 93, 38, 25, 9, 6, 86, 219, 250, 25, 161, 185, 32, 98, 177, 32, 121},
{72, 7, 24, 67, 1, 245, 154, 234, 84, 179, 37, 96, 222, 33, 64, 228, 78, 254, 194, 19, 197, 60, 60, 241, 58, 151, 184, 179, 233, 70, 85, 97},
{253, 151, 182, 118, 101, 136, 118, 241, 195, 26, 152, 14, 225, 28, 193, 165, 140, 82, 138, 36, 216, 2, 152, 228, 117, 234, 180, 94, 11, 25, 50, 148},
{20, 35, 254, 1, 198, 250, 222, 43, 98, 131, 180, 54, 101, 212, 227, 212, 85, 247, 217, 50, 117, 7, 116, 145, 101, 136, 176, 12, 83, 1, 146, 170},
{145, 235, 144, 178, 16, 181, 198, 59, 220, 241, 197, 242, 187, 44, 243, 109, 86, 53, 21, 48, 83, 149, 252, 147, 181, 124, 48, 89, 151, 149, 227, 188},
{214, 115, 72, 209, 6, 224, 24, 39, 114, 233, 248, 204, 31, 222, 125, 2, 236, 241, 19, 132, 104, 150, 172, 254, 222, 170, 104, 161, 199, 252, 179, 230},
{241, 67, 229, 75, 108, 250, 81, 179, 127, 247, 83, 66, 159, 206, 107, 96, 58, 217, 252, 157, 139, 17, 235, 115, 5, 174, 191, 230, 233, 49, 241, 241},
{165, 246, 113, 208, 142, 14, 235, 211, 178, 85, 75, 239, 238, 96, 147, 129, 143, 18, 30, 123, 124, 195, 21, 230, 104, 198, 220, 56, 202, 53, 246, 99},
{219, 121, 50, 105, 81, 61, 239, 218, 41, 238, 236, 242, 77, 40, 161, 123, 92, 58, 122, 26, 3, 147, 12, 163, 109, 207, 110, 216, 66, 41, 93, 220},
{56, 105, 223, 38, 38, 53, 34, 72, 93, 91, 133, 135, 1, 232, 7, 61, 70, 61, 102, 124, 94, 21, 181, 225, 227, 23, 51, 104, 159, 94, 117, 98},
{200, 107, 25, 252, 122, 136, 229, 62, 85, 8, 171, 117, 186, 197, 183, 103, 52, 41, 91, 19, 211, 165, 97, 52, 227, 241, 116, 70, 115, 251, 56, 164},
{99, 62, 142, 10, 191, 175, 135, 155, 202, 184, 151, 52, 17, 239, 5, 26, 201, 44, 159, 38, 76, 235, 82, 145, 61, 162, 223, 9, 169, 204, 77, 189},
{197, 14, 41, 244, 99, 82, 51, 75, 53, 246, 248, 215, 70, 118, 32, 124, 79, 180, 4, 127, 169, 157, 105, 103, 85, 151, 125, 63, 246, 69, 228, 179},
{55, 161, 79, 12, 207, 40, 253, 100, 230, 119, 111, 97, 76, 61, 94, 122, 5, 74, 200, 112, 206, 160, 19, 75, 142, 167, 222, 9, 180, 99, 57, 177},
{17, 80, 149, 28, 144, 190, 229, 240, 26, 182, 124, 100, 217, 51, 52, 9, 182, 169, 42, 94, 114, 239, 69, 95, 173, 11, 101, 72, 64, 50, 3, 135},
{12, 91, 119, 248, 135, 229, 140, 37, 129, 209, 39, 237, 182, 202, 102, 204, 89, 159, 208, 66, 154, 204, 54, 66, 32, 13, 61, 13, 184, 70, 75, 128},
{117, 204, 87, 187, 74, 161, 64, 143, 219, 117, 162, 84, 197, 171, 98, 1, 138, 76, 204, 242, 153, 72, 19, 180, 165, 172, 112, 31, 199, 12, 21, 19},
{24, 225, 130, 141, 144, 160, 197, 221, 109, 80, 74, 157, 237, 227, 1, 143, 161, 216, 190, 28, 103, 248, 231, 29, 74, 248, 192, 160, 226, 203, 254, 14},
{242, 17, 183, 221, 223, 54, 147, 94, 222, 19, 137, 147, 116, 241, 4, 34, 163, 217, 140, 42, 34, 191, 244, 240, 48, 18, 110, 84, 212, 155, 159, 85},
{198, 3, 198, 145, 91, 104, 40, 111, 171, 25, 48, 170, 91, 222, 108, 67, 99, 147, 168, 118, 148, 82, 76, 9, 226, 178, 78, 148, 151, 183, 234, 136},
{237, 10, 198, 207, 133, 149, 88, 94, 250, 23, 24, 49, 14, 86, 242, 63, 235, 232, 176, 37, 91, 230, 60, 107, 210, 175, 217, 195, 113, 111, 148, 57},
{252, 70, 251, 156, 71, 58, 104, 35, 181, 22, 29, 18, 45, 124, 115, 246, 91, 204, 171, 171, 10, 8, 109, 87, 86, 26, 6, 194, 111, 15, 44, 249},
{61, 247, 189, 11, 255, 205, 190, 159, 73, 215, 216, 211, 50, 194, 165, 173, 247, 237, 123, 131, 188, 226, 189, 197, 112, 84, 126, 46, 193, 255, 184, 53},
{40, 156, 37, 206, 118, 220, 97, 4, 164, 201, 150, 153, 5, 88, 33, 143, 80, 1, 230, 22, 33, 31, 14, 175, 218, 151, 224, 19, 52, 213, 244, 149},
{7, 121, 65, 169, 163, 244, 187, 17, 112, 237, 46, 113, 109, 50, 57, 188, 171, 241, 132, 47, 144, 234, 210, 48, 167, 146, 6, 41, 127, 185, 80, 151},
{33, 85, 209, 187, 99, 27, 241, 145, 182, 43, 152, 91, 166, 94, 114, 169, 45, 163, 104, 175, 26, 115, 76, 130, 55, 152, 136, 45, 135, 225, 32, 216},
{116, 149, 25, 41, 167, 115, 192, 226, 173, 224, 121, 202, 238, 11, 51, 244, 227, 3, 199, 183, 144, 92, 131, 125, 220, 163, 111, 87, 243, 189, 133, 212},
{160, 202, 250, 200, 192, 43, 249, 18, 14, 151, 249, 96, 181, 91, 160, 155, 39, 28, 47, 110, 5, 38, 203, 203, 1, 103, 73, 198, 63, 163, 145, 49},
{100, 7, 242, 101, 230, 151, 67, 247, 146, 170, 239, 129, 240, 215, 250, 144, 17, 158, 47, 155, 183, 246, 28, 5, 202, 8, 216, 253, 69, 13, 144, 119},
{186, 166, 166, 145, 230, 236, 133, 44, 96, 122, 206, 139, 96, 30, 65, 96, 251, 202, 46, 177, 105, 85, 144, 33, 232, 28, 135, 70, 64, 24, 189, 122},
{125, 253, 144, 215, 58, 109, 158, 6, 140, 237, 28, 168, 63, 148, 208, 125, 70, 43, 60, 183, 25, 111, 239, 227, 103, 164, 69, 30, 44, 240, 238, 236},
{79, 231, 215, 32, 107, 20, 43, 26, 230, 83, 183, 70, 84, 250, 132, 244, 30, 68, 74, 255, 253, 232, 200, 251, 43, 161, 44, 134, 187, 133, 145, 204},
{21, 229, 206, 84, 62, 194, 60, 102, 75, 4, 220, 56, 176, 209, 192, 112, 8, 94, 248, 15, 74, 182, 177, 114, 195, 206, 113, 105, 159, 63, 57, 165},
{112, 108, 180, 230, 202, 246, 252, 111, 117, 175, 210, 10, 195, 231, 143, 229, 10, 202, 230, 244, 135, 102, 250, 118, 242, 55, 43, 125, 231, 167, 135, 71},
{205, 154, 65, 115, 150, 138, 189, 176, 159, 48, 250, 135, 228, 77, 78, 173, 208, 178, 71, 189, 8, 130, 129, 62, 19, 152, 204, 112, 34, 15, 42, 112},
{195, 133, 16, 131, 217, 207, 15, 17, 168, 72, 182, 124, 156, 4, 38, 75, 208, 55, 40, 147, 80, 134, 226, 57, 75, 233, 92, 24, 247, 205, 149, 27},
{128, 91, 2, 15, 14, 219, 62, 231, 152, 107, 5, 251, 73, 170, 42, 255, 5, 28, 181, 87, 240, 145, 152, 73, 251, 215, 147, 35, 202, 183, 79, 5},
{95, 9, 5, 213, 129, 103, 46, 167, 187, 181, 27, 117, 34, 67, 118, 184, 92, 144, 42, 29, 251, 180, 252, 115, 222, 160, 23, 59, 219, 107, 129, 127},
{34, 145, 97, 163, 240, 99, 224, 52, 91, 27, 163, 13, 24, 220, 81, 216, 61, 25, 80, 27, 25, 185, 99, 100, 162, 201, 57, 38, 169, 202, 171, 224},
{155, 178, 152, 248, 234, 66, 116, 165, 4, 232, 10, 178, 59, 197, 10, 91, 34, 238, 48, 229, 220, 24, 121, 14, 142, 75, 92, 140, 53, 106, 227, 201},
{74, 184, 197, 204, 104, 42, 159, 160, 168, 203, 23, 245, 157, 180, 35, 108, 4, 247, 100, 221, 252, 211, 44, 40, 161, 48, 91, 177, 109, 16, 224, 231},
{226, 80, 154, 253, 172, 137, 170, 25, 31, 36, 56, 228, 57, 78, 159, 182, 128, 235, 244, 155, 5, 145, 21, 196, 90, 100, 238, 164, 152, 28, 19, 89},
{18, 25, 164, 149, 142, 135, 198, 151, 60, 180, 76, 80, 230, 139, 21, 246, 110, 97, 210, 120, 168, 133, 5, 145, 244, 247, 37, 98, 209, 145, 37, 68},
{248, 116, 245, 46, 159, 233, 159, 253, 83, 98, 58, 194, 2, 110, 157, 178, 162, 165, 254, 26, 224, 145, 178, 152, 114, 92, 76, 237, 158, 173, 14, 194},
{231, 91, 11, 41, 98, 144, 242, 73, 175, 207, 52, 108, 221, 155, 179, 74, 98, 154, 77, 47, 145, 115, 196, 31, 141, 207, 26, 157, 128, 99, 69, 145},
{75, 176, 108, 107, 23, 148, 51, 54, 134, 194, 17, 234, 222, 226, 184, 52, 25, 140, 39, 93, 210, 10, 104, 38, 9, 43, 85, 10, 104, 43, 222, 237},
{92, 94, 46, 231, 10, 36, 227, 154, 49, 80, 209, 2, 137, 25, 108, 20, 131, 193, 227, 149, 192, 55, 22, 75, 103, 122, 104, 231, 206, 70, 68, 7},
{121, 214, 117, 143, 206, 89, 179, 32, 21, 14, 178, 51, 248, 135, 228, 243, 2, 191, 235, 169, 138, 172, 49, 147, 211, 75, 49, 34, 221, 124, 108, 159},
{185, 39, 183, 74, 227, 158, 201, 236, 236, 6, 9, 181, 104, 219, 67, 64, 140, 148, 86, 111, 106, 201, 187, 196, 168, 45, 71, 181, 163, 15, 149, 111},
{15, 111, 192, 134, 62, 204, 46, 57, 198, 220, 244, 251, 221, 182, 220, 133, 4, 232, 180, 36, 154, 117, 97, 116, 109, 32, 152, 53, 121, 42, 47, 255},
{87, 1, 11, 170, 17, 250, 238, 55, 59, 146, 185, 145, 190, 62, 12, 21, 70, 84, 123, 167, 251, 10, 125, 123, 66, 246, 196, 139, 109, 220, 185, 22},
{71, 74, 17, 6, 15, 146, 107, 234, 137, 157, 221, 246, 241, 146, 72, 115, 22, 129, 144, 3, 158, 222, 200, 152, 18, 68, 90, 188, 142, 192, 24, 146},
{126, 156, 188, 60, 66, 222, 98, 216, 17, 255, 152, 216, 248, 200, 14, 74, 65, 139, 46, 19, 154, 57, 21, 115, 8, 118, 158, 217, 234, 177, 111, 160},
{47, 142, 147, 67, 44, 227, 21, 168, 151, 216, 89, 62, 250, 165, 74, 185, 147, 22, 5, 138, 55, 242, 24, 57, 100, 167, 83, 58, 175, 115, 63, 33},
{73, 144, 57, 155, 60, 182, 188, 241, 254, 163, 68, 197, 171, 184, 17, 18, 242, 11, 197, 242, 162, 153, 183, 129, 64, 242, 52, 164, 231, 5, 160, 210},
{202, 242, 96, 114, 134, 254, 154, 128, 117, 242, 17, 246, 223, 18, 112, 174, 3, 235, 3, 87, 136, 108, 179, 77, 236, 98, 152, 166, 151, 130, 13, 52},
{59, 228, 148, 40, 210, 184, 99, 13, 92, 252, 250, 25, 191, 183, 111, 210, 135, 47, 238, 31, 34, 63, 59, 150, 219, 190, 29, 132, 221, 4, 135, 219},
{65, 3, 105, 33, 78, 229, 48, 7, 5, 17, 117, 115, 16, 20, 101, 108, 249, 218, 89, 162, 68, 88, 31, 83, 78, 141, 9, 81, 249, 115, 114, 99},
{219, 157, 199, 113, 160, 253, 4, 1, 253, 89, 168, 204, 209, 214, 213, 141, 177, 76, 178, 93, 218, 171, 151, 169, 216, 233, 37, 13, 43, 26, 27, 88},
{1, 175, 221, 243, 223, 150, 131, 20, 195, 95, 120, 137, 81, 97, 19, 52, 129, 138, 123, 79, 185, 78, 132, 36, 16, 192, 99, 216, 25, 165, 45, 183},
{123, 99, 4, 20, 155, 224, 253, 96, 2, 165, 255, 216, 84, 34, 11, 83, 58, 203, 206, 214, 133, 224, 22, 122, 211, 12, 130, 206, 202, 170, 225, 179},
{55, 31, 34, 33, 47, 208, 79, 170, 205, 64, 60, 102, 67, 47, 10, 81, 42, 63, 183, 186, 103, 140, 110, 52, 147, 33, 69, 246, 69, 95, 214, 180},
{78, 217, 117, 166, 51, 55, 232, 219, 136, 176, 59, 71, 7, 54, 250, 207, 62, 19, 105, 137, 20, 2, 4, 201, 69, 77, 35, 123, 71, 98, 9, 88},
{1, 58, 153, 65, 8, 5, 73, 248, 25, 42, 145, 26, 218, 183, 243, 27, 195, 5, 36, 148, 109, 128, 45, 183, 112, 93, 199, 222, 111, 201, 85, 165},
{112, 120, 107, 177, 223, 192, 59, 26, 235, 253, 252, 71, 225, 141, 233, 214, 97, 1, 189, 40, 28, 65, 204, 234, 55, 132, 184, 203, 80, 87, 113, 43},
{247, 192, 115, 208, 207, 151, 104, 240, 244, 133, 129, 128, 125, 183, 156, 136, 198, 206, 190, 7, 69, 58, 222, 158, 160, 23, 138, 2, 251, 165, 232, 252},
{98, 117, 101, 84, 61, 44, 230, 6, 198, 187, 59, 63, 121, 152, 178, 208, 42, 229, 79, 62, 188, 233, 226, 157, 46, 249, 179, 10, 249, 202, 36, 193},
{210, 77, 203, 244, 98, 21, 100, 71, 96, 65, 54, 182, 156, 230, 250, 224, 97, 97, 31, 13, 209, 19, 108, 22, 14, 81, 255, 241, 196, 144, 48, 171},
{130, 162, 74, 188, 56, 12, 24, 172, 87, 250, 78, 57, 164, 215, 95, 252, 182, 219, 141, 135, 187, 37, 83, 188, 187, 162, 34, 103, 11, 133, 124, 229},
{196, 155, 245, 4, 123, 227, 238, 196, 192, 201, 155, 47, 214, 115, 221, 199, 165, 240, 196, 144, 236, 254, 136, 213, 193, 9, 247, 63, 140, 105, 154, 46},
{168, 253, 206, 153, 244, 90, 190, 188, 118, 131, 197, 151, 204, 138, 190, 46, 116, 159, 121, 214, 81, 142, 12, 49, 8, 186, 199, 229, 247, 216, 224, 39},
{35, 18, 213, 92, 18, 32, 163, 180, 130, 116, 180, 242, 103, 130, 93, 241, 167, 10, 104, 181, 54, 135, 118, 39, 89, 7, 169, 11, 99, 104, 47, 45},
{157, 150, 134, 244, 214, 20, 46, 134, 50, 218, 163, 99, 173, 61, 240, 43, 35, 238, 145, 233, 16, 104, 165, 150, 124, 224, 137, 40, 96, 163, 243, 130},
{83, 94, 239, 60, 225, 202, 211, 119, 171, 212, 59, 66, 104, 180, 180, 154, 216, 159, 161, 81, 129, 234, 220, 73, 126, 135, 22, 73, 32, 199, 236, 64},
{180, 51, 88, 137, 101, 242, 22, 92, 8, 209, 99, 140, 86, 232, 224, 9, 185, 92, 56, 176, 178, 232, 11, 157, 180, 56, 55, 7, 44, 122, 96, 192},
{193, 20, 57, 242, 98, 80, 139, 154, 221, 134, 21, 167, 176, 203, 20, 58, 108, 40, 168, 11, 136, 9, 214, 200, 135, 126, 245, 4, 168, 194, 142, 20},
{97, 223, 113, 66, 240, 219, 163, 213, 247, 105, 91, 200, 228, 152, 156, 102, 140, 2, 240, 50, 129, 133, 160, 93, 174, 246, 89, 111, 195, 22, 26, 78},
{70, 8, 143, 72, 73, 69, 52, 183, 169, 243, 7, 53, 53, 120, 43, 2, 105, 112, 241, 117, 239, 48, 104, 246, 141, 176, 208, 220, 126, 224, 229, 157},
{159, 27, 28, 198, 131, 35, 183, 49, 168, 168, 102, 146, 77, 18, 157, 130, 200, 86, 133, 151, 5, 132, 76, 243, 194, 4, 55, 182, 159, 191, 21, 13},
{199, 26, 140, 14, 238, 2, 67, 91, 6, 205, 170, 100, 199, 87, 74, 59, 207, 195, 16, 130, 205, 225, 88, 2, 88, 88, 210, 48, 97, 114, 249, 174},
{221, 62, 67, 44, 76, 233, 250, 18, 23, 177, 178, 213, 175, 134, 50, 222, 206, 224, 25, 32, 152, 125, 204, 99, 56, 175, 235, 226, 50, 129, 168, 28},
{249, 207, 146, 253, 136, 29, 143, 209, 20, 235, 30, 29, 26, 151, 85, 116, 134, 62, 72, 44, 92, 53, 101, 226, 57, 136, 158, 222, 10, 192, 41, 227},
{174, 229, 7, 70, 206, 29, 89, 189, 213, 188, 33, 212, 151, 193, 254, 218, 239, 38, 5, 110, 143, 97, 37, 81, 142, 18, 93, 184, 110, 93, 251, 91},
{52, 86, 100, 126, 146, 223, 48, 62, 75, 108, 70, 219, 245, 33, 187, 154, 183, 167, 3, 107, 238, 39, 158, 207, 110, 84, 216, 51, 15, 116, 120, 71},
{205, 222, 123, 163, 14, 210, 148, 124, 206, 14, 57, 19, 53, 123, 136, 153, 175, 15, 42, 88, 151, 235, 192, 90, 170, 4, 175, 131, 108, 231, 249, 143},
{148, 139, 48, 211, 158, 147, 117, 33, 102, 77, 237, 218, 77, 54, 170, 68, 39, 24, 6, 237, 106, 137, 131, 98, 25, 203, 36, 104, 92, 38, 238, 241},
{165, 147, 185, 5, 22, 252, 130, 247, 32, 76, 241, 81, 118, 178, 107, 64, 171, 15, 223, 129, 12, 34, 141, 142, 121, 218, 185, 163, 68, 128, 225, 124},
{23, 231, 58, 82, 90, 211, 40, 239, 63, 155, 129, 60, 128, 142, 31, 64, 164, 157, 221, 125, 225, 114, 37, 76, 217, 172, 3, 27, 146, 193, 82, 144},
{88, 207, 100, 97, 177, 177, 65, 193, 199, 91, 12, 22, 17, 189, 51, 16, 199, 144, 46, 188, 87, 110, 210, 240, 211, 202, 253, 98, 143, 190, 114, 1},
{19, 215, 123, 95, 188, 172, 128, 108, 38, 206, 18, 69, 137, 19, 35, 187, 196, 29, 158, 95, 107, 67, 213, 229, 121, 102, 1, 140, 3, 8, 176, 137},
{254, 80, 166, 73, 150, 111, 173, 219, 30, 147, 134, 90, 126, 134, 161, 248, 199, 149, 48, 98, 165, 13, 150, 197, 183, 129, 198, 253, 8, 124, 37, 152},
{192, 42, 26, 56, 12, 149, 104, 49, 152, 50, 118, 99, 251, 83, 191, 154, 145, 109, 86, 254, 190, 138, 28, 230, 102, 101, 198, 8, 70, 104, 191, 253},
{113, 205, 59, 6, 8, 242, 213, 43, 224, 222, 197, 129, 5, 28, 200, 123, 236, 104, 226, 167, 146, 6, 233, 104, 223, 138, 10, 55, 146, 240, 231, 109},
{41, 164, 95, 124, 213, 29, 32, 127, 210, 194, 190, 165, 202, 223, 58, 3, 138, 33, 84, 114, 225, 165, 197, 72, 206, 32, 180, 154, 57, 8, 204, 102},
{132, 124, 62, 144, 115, 15, 9, 136, 217, 163, 11, 119, 222, 6, 194, 64, 125, 170, 127, 248, 166, 74, 7, 152, 84, 50, 72, 24, 24, 123, 86, 214},
{134, 57, 102, 153, 222, 197, 231, 129, 183, 241, 40, 128, 43, 111, 140, 103, 38, 43, 154, 52, 249, 56, 182, 33, 235, 152, 83, 3, 178, 91, 75, 9},
{139, 5, 68, 152, 226, 43, 97, 191, 13, 246, 202, 19, 147, 57, 117, 48, 93, 98, 85, 68, 21, 77, 50, 36, 148, 159, 69, 141, 77, 121, 37, 59},
{218, 35, 129, 104, 183, 103, 180, 64, 135, 243, 110, 82, 44, 229, 61, 124, 225, 211, 61, 172, 216, 110, 173, 55, 22, 43, 189, 188, 227, 32, 38, 163},
{26, 166, 237, 51, 2, 49, 255, 9, 59, 85, 142, 19, 204, 119, 216, 15, 196, 197, 79, 10, 236, 140, 159, 216, 166, 123, 78, 138, 105, 238, 188, 120},
{91, 94, 229, 234, 63, 179, 33, 38, 122, 164, 161, 122, 132, 60, 152, 233, 156, 189, 47, 52, 17, 194, 93, 130, 145, 157, 169, 53, 34, 202, 4, 6},
{106, 94, 193, 127, 30, 113, 21, 207, 78, 76, 15, 10, 31, 136, 95, 143, 43, 122, 153, 20, 252, 105, 127, 239, 147, 8, 29, 146, 110, 23, 238, 36},
{22, 92, 183, 30, 142, 237, 219, 104, 197, 87, 160, 227, 70, 87, 224, 149, 103, 38, 159, 198, 144, 22, 65, 13, 1, 94, 91, 66, 183, 101, 242, 51},
{66, 178, 17, 94, 151, 227, 231, 143, 59, 115, 189, 74, 80, 32, 215, 221, 170, 119, 9, 201, 172, 75, 71, 225, 3, 127, 106, 13, 3, 150, 74, 255},
{87, 76, 214, 123, 201, 83, 193, 254, 141, 111, 22, 216, 15, 179, 154, 30, 30, 250, 228, 26, 22, 60, 67, 93, 215, 152, 155, 121, 85, 166, 5, 115},
{246, 147, 7, 89, 171, 183, 133, 26, 210, 65, 50, 75, 127, 233, 103, 26, 2, 138, 114, 163, 147, 164, 140, 162, 174, 239, 28, 220, 93, 46, 46, 36},
{22, 192, 122, 216, 168, 88, 29, 248, 16, 203, 173, 222, 7, 55, 129, 173, 242, 15, 111, 45, 39, 121, 140, 254, 76, 99, 188, 67, 249, 86, 203, 243},
{131, 18, 135, 57, 186, 240, 43, 197, 42, 40, 229, 131, 81, 252, 75, 102, 247, 115, 212, 10, 127, 71, 22, 239, 234, 248, 154, 217, 173, 54, 141, 178},
{36, 104, 231, 153, 223, 236, 110, 81, 143, 97, 248, 53, 135, 40, 74, 153, 203, 40, 1, 209, 108, 98, 114, 3, 143, 173, 122, 159, 51, 191, 68, 35},
{111, 152, 170, 153, 65, 127, 209, 208, 212, 169, 111, 246, 131, 193, 189, 31, 103, 250, 231, 20, 74, 234, 76, 133, 117, 110, 181, 111, 128, 138, 112, 66},
{146, 12, 235, 186, 103, 104, 193, 4, 124, 188, 140, 74, 193, 7, 180, 13, 137, 74, 65, 20, 68, 11, 96, 99, 119, 68, 85, 70, 200, 201, 49, 183},
{123, 240, 167, 34, 210, 88, 3, 34, 18, 26, 28, 44, 151, 178, 109, 244, 3, 195, 14, 236, 222, 29, 83, 158, 148, 107, 6, 248, 84, 123, 141, 175},
{206, 13, 29, 60, 189, 200, 145, 127, 137, 172, 44, 42, 120, 212, 73, 113, 213, 246, 23, 79, 33, 122, 139, 95, 45, 214, 19, 71, 155, 51, 175, 147},
{109, 154, 79, 11, 165, 113, 9, 15, 99, 246, 224, 96, 187, 67, 214, 195, 151, 146, 87, 229, 1, 146, 10, 7, 70, 252, 199, 225, 112, 35, 146, 236},
{79, 247, 176, 255, 183, 139, 55, 141, 239, 188, 172, 186, 156, 126, 83, 242, 160, 149, 243, 148, 175, 240, 53, 30, 207, 128, 116, 4, 68, 217, 66, 176},
{2, 167, 119, 216, 190, 219, 119, 23, 20, 182, 254, 238, 157, 225, 233, 140, 234, 214, 251, 20, 65, 154, 174, 78, 113, 255, 137, 147, 44, 69, 183, 7},
{121, 136, 140, 214, 241, 237, 76, 180, 152, 43, 84, 28, 20, 147, 28, 118, 154, 214, 252, 177, 11, 45, 156, 43, 214, 93, 180, 195, 169, 158, 111, 108},
{58, 35, 174, 240, 216, 249, 14, 203, 170, 179, 2, 125, 200, 204, 43, 95, 83, 141, 228, 29, 32, 40, 85, 10, 4, 156, 168, 85, 172, 180, 172, 21},
{114, 171, 242, 238, 47, 124, 59, 125, 65, 23, 39, 150, 161, 226, 5, 209, 61, 231, 91, 15, 64, 57, 58, 233, 229, 192, 112, 67, 243, 149, 98, 151},
{33, 32, 3, 8, 36, 151, 121, 17, 218, 26, 98, 82, 65, 146, 162, 149, 64, 53, 126, 112, 58, 163, 159, 106, 238, 218, 218, 91, 237, 109, 12, 234},
{37, 190, 149, 41, 64, 68, 119, 19, 153, 51, 235, 147, 203, 136, 225, 145, 52, 164, 112, 134, 242, 179, 185, 57, 179, 177, 210, 34, 165, 113, 40, 14},
{242, 44, 232, 202, 123, 230, 119, 236, 16, 231, 234, 50, 122, 215, 111, 194, 189, 76, 240, 228, 184, 42, 191, 174, 20, 235, 39, 70, 86, 220, 37, 131},
{64, 190, 225, 105, 2, 122, 16, 3, 38, 255, 79, 66, 166, 97, 192, 141, 229, 219, 13, 65, 91, 86, 37, 100, 207, 90, 214, 150, 47, 118, 157, 109},
{41, 149, 44, 166, 186, 23, 168, 186, 250, 4, 159, 47, 9, 124, 71, 11, 124, 40, 231, 157, 7, 108, 149, 132, 194, 48, 100, 70, 152, 181, 74, 28},
{249, 59, 57, 146, 2, 235, 113, 131, 188, 6, 171, 48, 224, 49, 175, 1, 83, 140, 128, 210, 225, 170, 116, 187, 222, 114, 1, 81, 174, 106, 29, 1},
{19, 118, 143, 2, 79, 31, 218, 92, 100, 181, 79, 226, 175, 226, 175, 39, 213, 137, 130, 241, 5, 245, 17, 67, 50, 107, 185, 112, 48, 41, 100, 230},
{241, 134, 224, 140, 191, 179, 174, 113, 4, 225, 15, 245, 177, 126, 87, 178, 31, 224, 132, 12, 254, 124, 136, 206, 184, 66, 126, 55, 56, 198, 36, 38},
{48, 196, 26, 73, 218, 118, 123, 137, 168, 15, 159, 113, 154, 253, 55, 144, 239, 187, 25, 57, 59, 60, 63, 148, 47, 2, 154, 195, 208, 32, 63, 18},
{11, 43, 62, 251, 191, 45, 35, 203, 140, 42, 121, 233, 87, 190, 201, 82, 228, 103, 71, 184, 123, 78, 40, 6, 227, 199, 165, 59, 95, 91, 220, 223},
{13, 53, 76, 103, 206, 252, 97, 243, 120, 229, 154, 201, 166, 244, 46, 208, 14, 246, 41, 210, 152, 81, 184, 156, 192, 91, 123, 48, 223, 215, 21, 243},
{131, 9, 114, 15, 5, 150, 143, 185, 33, 64, 203, 180, 70, 93, 136, 201, 138, 143, 45, 76, 128, 248, 62, 219, 136, 116, 162, 30, 222, 16, 12, 108},
{58, 217, 47, 14, 1, 13, 117, 8, 167, 10, 105, 226, 96, 158, 229, 203, 236, 157, 189, 204, 221, 163, 128, 168, 244, 194, 129, 67, 113, 195, 34, 118},
{169, 119, 204, 119, 80, 22, 46, 55, 120, 70, 39, 68, 156, 140, 150, 247, 116, 237, 35, 82, 233, 43, 126, 138, 204, 151, 134, 110, 159, 171, 125, 51},
{66, 13, 58, 37, 254, 61, 28, 122, 100, 206, 172, 205, 247, 250, 12, 171, 200, 100, 194, 117, 56, 50, 122, 222, 132, 178, 163, 208, 47, 190, 132, 112},
{195, 10, 135, 248, 143, 167, 184, 176, 98, 179, 72, 42, 214, 23, 145, 9, 214, 47, 254, 22, 152, 182, 82, 194, 171, 126, 118, 119, 87, 192, 36, 181},
{93, 162, 212, 56, 55, 138, 174, 1, 117, 22, 129, 122, 212, 161, 92, 220, 178, 53, 119, 177, 111, 233, 133, 194, 58, 192, 183, 57, 167, 247, 152, 81},
{138, 170, 159, 30, 237, 244, 80, 230, 79, 150, 12, 155, 244, 118, 126, 1, 234, 205, 124, 84, 116, 79, 204, 164, 206, 86, 151, 148, 99, 226, 190, 68},
{248, 236, 70, 21, 20, 138, 236, 107, 34, 17, 24, 130, 201, 124, 96, 217, 85, 33, 82, 180, 204, 77, 120, 81, 71, 102, 237, 95, 104, 31, 125, 89},
{18, 3, 24, 73, 102, 63, 197, 209, 78, 137, 121, 112, 128, 123, 39, 9, 1, 180, 24, 222, 135, 76, 217, 252, 97, 234, 39, 97, 42, 97, 38, 42},
{228, 45, 188, 152, 234, 51, 166, 35, 216, 41, 188, 76, 213, 212, 244, 206, 205, 116, 5, 211, 100, 181, 104, 188, 63, 244, 47, 236, 48, 88, 208, 80},
{153, 156, 164, 77, 144, 52, 216, 195, 138, 50, 122, 239, 93, 117, 149, 33, 44, 104, 51, 87, 193, 80, 43, 126, 57, 230, 104, 125, 215, 242, 31, 247},
{207, 155, 49, 11, 124, 188, 131, 180, 170, 150, 37, 109, 38, 174, 75, 104, 12, 226, 139, 143, 126, 241, 233, 148, 116, 99, 20, 212, 10, 62, 17, 173},
{232, 186, 3, 220, 51, 92, 23, 165, 204, 6, 50, 129, 26, 97, 116, 90, 252, 80, 42, 40, 241, 242, 12, 139, 40, 65, 144, 183, 117, 126, 246, 228},
{215, 126, 89, 118, 198, 245, 143, 230, 46, 91, 46, 26, 241, 207, 245, 100, 215, 96, 65, 70, 148, 160, 228, 189, 127, 47, 223, 252, 61, 144, 111, 95},
{120, 41, 21, 204, 241, 163, 28, 92, 171, 179, 152, 237, 125, 79, 247, 139, 126, 208, 125, 246, 189, 108, 137, 210, 156, 75, 238, 104, 210, 1, 141, 24},
{181, 108, 111, 71, 59, 212, 1, 131, 225, 115, 37, 14, 100, 77, 222, 171, 164, 193, 64, 145, 241, 64, 162, 123, 150, 194, 113, 2, 25, 6, 145, 13},
{199, 48, 251, 125, 111, 186, 180, 237, 180, 132, 113, 39, 91, 126, 21, 120, 230, 175, 135, 71, 203, 21, 156, 236, 208, 12, 20, 118, 213, 244, 64, 42},
{228, 248, 143, 123, 222, 58, 35, 82, 228, 211, 177, 68, 130, 15, 241, 252, 188, 147, 30, 76, 253, 249, 49, 249, 47, 69, 201, 223, 39, 167, 69, 54},
{29, 201, 235, 177, 124, 218, 197, 238, 93, 127, 73, 43, 46, 238, 83, 94, 96, 57, 138, 224, 138, 98, 197, 122, 96, 10, 177, 55, 102, 167, 190, 120},
{91, 89, 138, 236, 189, 205, 202, 28, 157, 194, 139, 189, 188, 222, 1, 98, 205, 25, 211, 241, 251, 164, 179, 216, 51, 91, 216, 202, 159, 197, 77, 4},
{76, 93, 179, 102, 191, 201, 9, 126, 167, 185, 251, 178, 251, 180, 156, 27, 21, 130, 33, 10, 138, 171, 114, 111, 198, 221, 80, 1, 83, 185, 253, 134},
{31, 137, 206, 229, 32, 215, 202, 228, 232, 101, 97, 35, 255, 234, 127, 236, 159, 230, 245, 56, 25, 32, 201, 66, 153, 211, 32, 73, 144, 210, 206, 133},
{42, 86, 219, 213, 217, 111, 135, 80, 70, 49, 102, 98, 210, 232, 158, 138, 9, 31, 131, 127, 79, 143, 146, 160, 50, 78, 110, 170, 123, 133, 183, 166},
{69, 223, 198, 190, 49, 54, 2, 163, 119, 185, 60, 107, 37, 131, 9, 62, 145, 184, 181, 28, 147, 95, 19, 12, 166, 4, 235, 241, 135, 215, 47, 217},
{103, 126, 39, 5, 127, 60, 220, 60, 120, 40, 162, 39, 65, 138, 112, 7, 160, 101, 210, 27, 244, 193, 20, 21, 219, 135, 56, 69, 78, 58, 189, 32},
{90, 87, 125, 20, 167, 248, 82, 21, 141, 69, 253, 119, 45, 1, 160, 160, 152, 226, 184, 84, 228, 78, 63, 186, 229, 248, 223, 190, 249, 99, 231, 171},
{130, 42, 80, 10, 216, 201, 245, 154, 5, 23, 74, 242, 101, 102, 184, 166, 246, 34, 14, 32, 226, 16, 14, 239, 23, 73, 139, 71, 68, 184, 143, 50},
{81, 169, 211, 130, 94, 168, 242, 140, 46, 186, 180, 233, 222, 1, 120, 13, 77, 60, 3, 41, 157, 45, 250, 153, 104, 220, 182, 172, 103, 226, 153, 121},
{72, 154, 94, 239, 83, 242, 137, 6, 24, 184, 7, 9, 225, 44, 112, 193, 74, 238, 216, 9, 229, 167, 71, 208, 89, 230, 197, 188, 101, 67, 6, 216},
{116, 252, 214, 166, 243, 67, 41, 154, 58, 78, 234, 85, 188, 76, 65, 246, 139, 100, 138, 7, 54, 163, 87, 118, 142, 205, 17, 26, 98, 95, 39, 242},
{144, 255, 15, 174, 25, 182, 1, 220, 175, 11, 41, 141, 69, 69, 174, 46, 120, 208, 177, 158, 130, 66, 119, 3, 235, 143, 255, 5, 149, 42, 138, 165},
{112, 233, 174, 39, 48, 209, 136, 82, 207, 75, 221, 255, 118, 18, 27, 139, 84, 186, 104, 189, 22, 174, 14, 176, 131, 31, 106, 243, 139, 173, 146, 244},
{250, 254, 133, 76, 108, 59, 53, 147, 15, 200, 135, 17, 138, 131, 147, 99, 199, 233, 75, 71, 240, 26, 199, 232, 60, 27, 173, 69, 39, 246, 92, 48},
{119, 210, 114, 33, 191, 38, 98, 22, 244, 162, 249, 182, 30, 234, 188, 43, 61, 164, 212, 142, 73, 23, 155, 62, 96, 128, 111, 104, 167, 146, 203, 65},
{167, 152, 96, 47, 165, 177, 203, 192, 142, 135, 92, 7, 61, 156, 32, 137, 220, 99, 13, 180, 186, 52, 77, 237, 74, 147, 251, 15, 108, 122, 59, 28},
{249, 181, 52, 222, 139, 244, 215, 224, 198, 114, 40, 243, 200, 5, 79, 102, 209, 44, 203, 56, 200, 23, 44, 99, 183, 250, 162, 206, 231, 158, 210, 112},
{195, 177, 63, 246, 116, 210, 113, 123, 248, 17, 244, 174, 232, 40, 110, 74, 27, 54, 182, 31, 213, 70, 119, 148, 22, 77, 62, 118, 73, 8, 4, 227},
{174, 223, 121, 235, 197, 225, 150, 67, 127, 80, 219, 62, 36, 51, 65, 225, 209, 187, 13, 144, 188, 232, 86, 67, 248, 61, 152, 24, 198, 30, 51, 118},
{169, 227, 202, 33, 181, 210, 194, 212, 51, 158, 125, 246, 64, 200, 59, 83, 94, 208, 5, 226, 181, 74, 53, 92, 39, 155, 121, 119, 196, 28, 160, 34},
{124, 154, 149, 143, 36, 8, 222, 81, 182, 28, 217, 58, 223, 4, 195, 230, 121, 181, 17, 97, 174, 39, 223, 245, 163, 115, 72, 29, 9, 250, 221, 93},
{94, 129, 132, 118, 175, 123, 131, 87, 207, 57, 77, 136, 126, 101, 29, 176, 73, 215, 180, 68, 41, 126, 101, 135, 219, 224, 57, 29, 241, 38, 251, 65},
{167, 177, 51, 217, 242, 161, 150, 33, 207, 188, 199, 179, 3, 252, 175, 40, 71, 23, 126, 212, 112, 84, 36, 19, 243, 40, 155, 89, 25, 44, 143, 44},
{142, 157, 145, 41, 142, 233, 158, 158, 64, 158, 34, 89, 115, 91, 16, 81, 46, 212, 130, 142, 166, 58, 205, 243, 193, 255, 109, 107, 83, 94, 185, 217},
{103, 181, 101, 82, 232, 131, 3, 160, 69, 31, 133, 57, 115, 220, 168, 30, 207, 218, 190, 44, 102, 244, 113, 203, 36, 224, 195, 195, 212, 238, 52, 182},
{104, 138, 170, 151, 231, 202, 217, 205, 81, 42, 246, 108, 123, 2, 40, 96, 196, 95, 144, 98, 49, 43, 23, 184, 181, 141, 105, 31, 176, 224, 164, 81},
{138, 159, 183, 96, 66, 36, 16, 145, 131, 178, 48, 236, 226, 217, 221, 117, 86, 187, 22, 179, 167, 17, 14, 54, 180, 217, 218, 74, 69, 245, 169, 120},
{91, 206, 220, 176, 108, 243, 52, 201, 226, 28, 70, 196, 123, 18, 54, 236, 230, 47, 81, 109, 168, 137, 192, 19, 127, 143, 11, 161, 226, 230, 31, 19},
{24, 207, 128, 6, 155, 134, 151, 169, 43, 105, 35, 121, 125, 93, 184, 219, 76, 180, 221, 129, 248, 201, 38, 206, 237, 34, 227, 219, 92, 238, 20, 4},
{76, 30, 37, 108, 85, 239, 66, 35, 18, 15, 80, 26, 63, 117, 31, 162, 172, 3, 140, 4, 250, 168, 31, 250, 208, 3, 41, 58, 66, 122, 214, 223},
{13, 114, 121, 124, 89, 22, 163, 146, 144, 123, 191, 224, 85, 156, 229, 6, 254, 190, 77, 25, 36, 208, 94, 171, 60, 104, 191, 188, 222, 202, 52, 249},
{61, 6, 137, 137, 31, 211, 146, 84, 248, 242, 181, 113, 192, 186, 69, 59, 7, 72, 9, 101, 14, 204, 101, 246, 140, 62, 12, 151, 191, 78, 125, 45},
{157, 136, 146, 168, 3, 25, 221, 183, 210, 160, 37, 98, 46, 154, 200, 51, 233, 78, 211, 136, 192, 80, 238, 133, 173, 53, 176, 141, 252, 127, 213, 208},
{236, 37, 13, 175, 18, 251, 87, 187, 224, 204, 128, 5, 91, 147, 115, 122, 151, 89, 90, 163, 65, 38, 17, 122, 231, 248, 212, 192, 124, 138, 107, 170},
{145, 19, 150, 65, 190, 97, 199, 178, 76, 115, 138, 198, 136, 18, 180, 253, 248, 247, 187, 179, 194, 161, 221, 246, 94, 254, 64, 61, 91, 186, 104, 69},
{235, 120, 75, 39, 150, 196, 72, 209, 145, 27, 180, 77, 11, 2, 154, 155, 125, 233, 102, 2, 252, 239, 45, 119, 156, 67, 142, 249, 160, 160, 43, 116},
{143, 165, 24, 101, 222, 187, 133, 80, 114, 98, 164, 11, 16, 227, 43, 133, 145, 213, 75, 107, 148, 34, 89, 73, 28, 136, 140, 131, 231, 105, 2, 209},
{255, 184, 148, 30, 2, 59, 196, 206, 224, 101, 11, 205, 173, 175, 148, 17, 245, 251, 207, 74, 117, 102, 216, 250, 162, 252, 162, 141, 19, 22, 115, 134},
{31, 58, 43, 194, 88, 106, 56, 41, 88, 34, 189, 211, 128, 188, 100, 228, 149, 6, 140, 214, 89, 223, 4, 232, 12, 183, 1, 187, 28, 146, 97, 11},
{173, 159, 50, 163, 30, 151, 172, 58, 118, 11, 139, 20, 227, 150, 135, 213, 107, 120, 100, 176, 68, 197, 190, 248, 82, 15, 225, 61, 55, 196, 240, 250},
{8, 42, 165, 143, 186, 179, 64, 44, 100, 15, 30, 158, 136, 10, 240, 220, 181, 174, 221, 223, 195, 144, 160, 79, 89, 146, 43, 90, 157, 51, 97, 249},
{61, 3, 209, 85, 236, 48, 55, 183, 70, 6, 193, 208, 190, 103, 211, 46, 221, 3, 25, 245, 200, 43, 37, 8, 104, 91, 246, 3, 89, 13, 132, 120},
{91, 121, 64, 214, 89, 93, 32, 238, 196, 217, 242, 53, 71, 78, 136, 226, 189, 164, 233, 98, 238, 230, 250, 56, 65, 83, 137, 141, 171, 250, 231, 62},
{133, 122, 163, 187, 119, 181, 55, 152, 138, 23, 49, 26, 211, 59, 150, 19, 144, 166, 205, 184, 82, 209, 107, 20, 157, 165, 177, 216, 27, 103, 147, 81},
{138, 114, 71, 105, 110, 180, 111, 127, 214, 105, 13, 43, 148, 113, 228, 203, 37, 239, 239, 238, 125, 114, 244, 74, 24, 241, 242, 146, 130, 94, 46, 79},
{85, 108, 150, 69, 191, 198, 106, 86, 228, 219, 78, 42, 73, 10, 92, 242, 16, 3, 18, 27, 228, 216, 36, 149, 19, 179, 28, 6, 226, 38, 163, 82},
{191, 46, 144, 17, 234, 91, 79, 85, 44, 28, 26, 143, 24, 237, 106, 132, 165, 28, 88, 162, 186, 248, 45, 92, 31, 189, 164, 172, 255, 51, 125, 111},
{15, 105, 201, 161, 101, 197, 235, 191, 127, 28, 238, 232, 231, 198, 234, 172, 192, 193, 60, 42, 87, 165, 80, 226, 245, 151, 8, 214, 96, 118, 19, 23},
{84, 157, 205, 255, 217, 251, 101, 194, 230, 208, 26, 232, 23, 201, 46, 29, 123, 221, 11, 53, 196, 102, 220, 130, 2, 70, 240, 1, 178, 74, 188, 195},
{244, 120, 86, 42, 110, 203, 209, 158, 119, 115, 207, 5, 104, 140, 138, 113, 25, 153, 59, 171, 105, 67, 136, 70, 30, 10, 203, 80, 13, 200, 172, 216},
{116, 64, 52, 174, 54, 126, 16, 194, 162, 33, 33, 157, 176, 197, 225, 12, 59, 55, 253, 228, 148, 47, 179, 185, 24, 138, 253, 20, 142, 55, 172, 88}};
auto rs = std::make_unique<ReedSolomon>(nroots, minpoly, prim, fcr, pad, genpoly_coeff, genmatrix);
std::vector<uint8_t> encoded_output = rs->encode_with_generator_matrix(input);
EXPECT_TRUE(encoded_output == expected_output);
}
TEST(ReedSolomonTest, DecodeNoErrors)
{
const std::vector<uint8_t> expected_output = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
std::vector<uint8_t> encoded_input = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
auto rs = std::make_unique<ReedSolomon>();
int result = rs->decode(encoded_input);
EXPECT_TRUE(result == 0);
std::vector<uint8_t> decoded(encoded_input.begin(), encoded_input.begin() + 32);
EXPECT_TRUE(expected_output == decoded);
}
TEST(ReedSolomonTest, Decode1Error)
{
const std::vector<uint8_t> expected_output = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
std::vector<uint8_t> encoded_input = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
// Introduce 1 error:
encoded_input[23] = 0;
auto rs = std::make_unique<ReedSolomon>();
int result = rs->decode(encoded_input);
EXPECT_TRUE(result == 1);
std::vector<uint8_t> decoded(encoded_input.begin(), encoded_input.begin() + 32);
EXPECT_TRUE(expected_output == decoded);
}
TEST(ReedSolomonTest, Decode2Errors)
{
const std::vector<uint8_t> expected_output = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
std::vector<uint8_t> encoded_input = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
// Introduce 2 errors:
encoded_input[23] = 0;
encoded_input[55] = 0;
auto rs = std::make_unique<ReedSolomon>();
int result = rs->decode(encoded_input);
EXPECT_TRUE(result == 2);
std::vector<uint8_t> decoded(encoded_input.begin(), encoded_input.begin() + 32);
EXPECT_TRUE(expected_output == decoded);
}
TEST(ReedSolomonTest, Decode16Errors)
{
const std::vector<uint8_t> expected_output = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
std::vector<uint8_t> encoded_input = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
// Introduce 16 errors:
encoded_input[3] = 0;
encoded_input[23] = 0;
encoded_input[55] = 0;
encoded_input[72] = 0;
encoded_input[85] = 0;
encoded_input[105] = 0;
encoded_input[123] = 0;
encoded_input[147] = 0;
encoded_input[174] = 0;
encoded_input[188] = 0;
encoded_input[195] = 0;
encoded_input[203] = 0;
encoded_input[208] = 0;
encoded_input[222] = 0;
encoded_input[233] = 0;
encoded_input[244] = 0;
auto rs = std::make_unique<ReedSolomon>();
int result = rs->decode(encoded_input);
EXPECT_TRUE(result == 16);
std::vector<uint8_t> decoded(encoded_input.begin(), encoded_input.begin() + 32);
EXPECT_TRUE(expected_output == decoded);
}
TEST(ReedSolomonTest, Decode111Errors)
{
const std::vector<uint8_t> expected_output = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
std::vector<uint8_t> encoded_input = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
// Introduce t = (n-k)/2 = 111 errors:
for (int i = 0; i < 222; i += 2)
{
encoded_input[i] = 0;
}
auto rs = std::make_unique<ReedSolomon>();
int result = rs->decode(encoded_input);
EXPECT_TRUE(result == 111);
std::vector<uint8_t> decoded(encoded_input.begin(), encoded_input.begin() + 32);
EXPECT_TRUE(expected_output == decoded);
}
TEST(ReedSolomonTest, Decode111ErrorsCustomConstructor)
{
const std::vector<uint8_t> expected_output = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
std::vector<uint8_t> encoded_input = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
// Introduce t = (n-k)/2 = 111 errors:
for (int i = 0; i < 222; i += 2)
{
encoded_input[i] = 0;
}
int nroots = 223;
int minpoly = 29;
int prim = 1;
int fcr = 1;
auto rs = std::make_unique<ReedSolomon>(nroots, minpoly, prim, fcr);
int result = rs->decode(encoded_input);
EXPECT_TRUE(result == 111);
std::vector<uint8_t> decoded(encoded_input.begin(), encoded_input.begin() + 32);
EXPECT_TRUE(expected_output == decoded);
}
TEST(ReedSolomonTest, Decode112Errors)
{
std::vector<uint8_t> encoded_input = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
// Introduce 112 errors (this should make the decoder fail):
for (int i = 0; i < 224; i += 2)
{
encoded_input[i] = 0;
}
auto rs = std::make_unique<ReedSolomon>();
int result = rs->decode(encoded_input);
EXPECT_TRUE(result == -1);
}
TEST(ReedSolomonTest, Decode113ErrorsWithErasure)
{
std::vector<uint8_t> expected_output = {71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179};
std::vector<uint8_t> encoded_input = {
71, 12, 25, 210, 178, 81, 243, 9, 112, 98, 196, 203, 48, 125, 114, 165, 181, 193, 71, 174, 168, 42, 31, 128, 245, 87, 150, 58, 192, 66, 130, 179, 133, 210, 122, 224, 75, 138, 20, 205, 14, 245, 209, 187, 246, 228, 12, 39, 244, 238, 223, 217, 84, 233, 137, 168, 153, 8, 94, 26, 99, 169, 149, 203, 115, 69, 211, 43, 70, 96, 70, 38, 160, 1, 232, 153, 223, 165, 93, 205, 101, 170, 60, 188, 198, 82, 168, 79, 95, 23, 118, 215, 187, 136, 24, 99, 252, 3, 144, 166, 117, 45, 168, 239, 77, 42, 246, 33, 122, 97, 242, 236, 13, 217, 96, 186, 71, 250, 242, 177, 125, 87, 27, 13, 118, 181, 178, 12, 27, 66, 31, 74, 127, 46, 112, 127, 116, 122, 190, 71, 240, 95, 78, 194, 113, 80, 46, 126, 74, 136, 118, 133, 105, 176, 47, 230, 162, 195, 93, 157, 72, 119, 13, 232, 151, 200, 191, 143, 75, 161, 111, 29, 158, 16, 181, 165, 92, 39, 17, 218, 228, 58, 176, 233, 55, 211, 195, 73, 37, 137, 232, 241, 150, 236, 152, 153, 53, 74, 81, 91, 160, 244, 21, 95, 176, 179, 141, 39, 61, 136, 16, 58, 160, 51, 210, 31, 134, 63, 203, 96, 219, 44, 231, 61, 220, 0, 241, 220, 207, 17, 52, 150, 117, 54, 222, 128, 101, 213, 164, 234, 74, 224, 57, 246, 70, 27, 202, 229, 4, 243, 128, 211, 158, 199, 4};
// Introduce 113 errors:
for (int i = 0; i < 226; i += 2)
{
encoded_input[i] = 0;
}
std::vector<int> erasure_positions{2, 4, 16, 18, 22, 54};
auto rs = std::make_unique<ReedSolomon>();
int result = rs->decode(encoded_input, erasure_positions);
EXPECT_TRUE(result == 113);
std::vector<uint8_t> decoded(encoded_input.begin(), encoded_input.begin() + 32);
EXPECT_TRUE(expected_output == decoded);
}