From b7217033397a1c84fc8daf09085b7a756b297745 Mon Sep 17 00:00:00 2001 From: iucgnaw Date: Wed, 10 Mar 2021 20:51:00 +0800 Subject: [PATCH 01/23] Fixed wrong SignalSource.implementation check. --- src/core/receiver/control_thread.cc | 36 ++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/core/receiver/control_thread.cc b/src/core/receiver/control_thread.cc index fb62f9d86..19c1348f9 100644 --- a/src/core/receiver/control_thread.cc +++ b/src/core/receiver/control_thread.cc @@ -92,23 +92,37 @@ ControlThread::ControlThread() const std::string empty_implementation; std::string src_impl = configuration_->property("SignalSource.implementation", empty_implementation); int src_count = configuration_->property("Receiver.sources_count", 1); - if (src_impl.empty() && (src_count != 1)) + if (src_impl.empty()) { - int num_src = 0; - for (int i = 0; i < src_count; i++) + if ((src_count == 1)) { - std::string src_impl_multiple = configuration_->property("SignalSource" + std::to_string(i) + ".implementation", empty_implementation); - if (!src_impl_multiple.empty()) + conf_has_signal_sources_ = false; + } + else + { + int num_src = 0; + for (int i = 0; i < src_count; i++) { - num_src++; + std::string src_impl_multiple = configuration_->property("SignalSource" + std::to_string(i) + ".implementation", empty_implementation); + if (!src_impl_multiple.empty()) + { + num_src++; + } + } + if (num_src == src_count) + { + conf_has_signal_sources_ = true; + } + else + { + conf_has_signal_sources_ = false; } } - if (num_src != src_count) - { - src_impl = std::string(""); - } } - conf_has_signal_sources_ = !src_impl.empty(); + else + { + conf_has_signal_sources_ = true; + } std::string pvt_impl = configuration_->property("PVT.implementation", empty_implementation); conf_has_pvt_ = !pvt_impl.empty(); From 4141164fac446c256e7d06cbc6cd1a199231bc4e Mon Sep 17 00:00:00 2001 From: Marc Majoral Date: Fri, 12 Mar 2021 18:01:25 +0100 Subject: [PATCH 02/23] fixed FPGA dual-band post-processing mode. --- .../adapters/ad9361_fpga_signal_source.cc | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc b/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc index 224aa6e5b..cc13a0667 100644 --- a/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc +++ b/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc @@ -103,22 +103,6 @@ Ad9361FpgaSignalSource::Ad9361FpgaSignalSource(const ConfigurationInterface *con const int l1_band = configuration->property("Channels_1C.count", 0) + configuration->property("Channels_1B.count", 0); - // by default the DMA transfers samples corresponding to two frequency bands to the FPGA - num_freq_bands_ = 2; - dma_buff_offset_pos_ = 0; - - // if only one input file is specified in the configuration file then: - // if there is at least one channel assigned to frequency band 1 then the DMA transfers the samples to the L1 frequency band channels - // otherwise the DMA transfers the samples to the L2/L5 frequency band channels - if (filename1.empty()) - { - num_freq_bands_ = 1; - if (l1_band != 0) - { - dma_buff_offset_pos_ = 2; - } - } - const double default_seconds_to_skip = 0.0; const std::string empty_string; @@ -140,6 +124,27 @@ Ad9361FpgaSignalSource::Ad9361FpgaSignalSource(const ConfigurationInterface *con filename1 = configuration->property(role + ".filename1", empty_string); } + // by default the DMA transfers samples corresponding to two frequency bands to the FPGA + num_freq_bands_ = 2; + dma_buff_offset_pos_ = 0; + + // if only one input file is specified in the configuration file then: + // if there is at least one channel assigned to frequency band 1 then the DMA transfers the samples to the L1 frequency band channels + // otherwise the DMA transfers the samples to the L2/L5 frequency band channels + // if more than one input file are specified then the DMA transfer the samples to both the L1 and the L2/L5 frequency channels. + if (filename1.empty()) + { + num_freq_bands_ = 1; + if (l1_band != 0) + { + dma_buff_offset_pos_ = 2; + } + } + else + { + dma_buff_offset_pos_ = 2; + } + samples_ = configuration->property(role + ".samples", static_cast(0)); const double seconds_to_skip = configuration->property(role + ".seconds_to_skip", default_seconds_to_skip); @@ -649,6 +654,7 @@ void Ad9361FpgaSignalSource::run_DMA_process(const std::string &filename0, const // run the DMA while (run_DMA) { + dma_index = 0; if (nbytes_remaining < read_buffer_size) { read_buffer_size = nbytes_remaining; @@ -684,26 +690,26 @@ void Ad9361FpgaSignalSource::run_DMA_process(const std::string &filename0, const } // read filename 1 (if enabled) - dma_index = 0; if (num_freq_bands_ > 1) { + dma_index = 0; try { - infile1.read(reinterpret_cast(input_samples.data()), read_buffer_size); + infile2.read(reinterpret_cast(input_samples.data()), read_buffer_size); } catch (const std::ifstream::failure &e) { std::cerr << "Exception reading file " << filename1 << '\n'; break; } - if (infile1) + if (infile2) { nread_elements = read_buffer_size; } else { // FLAG AS ERROR !! IT SHOULD NEVER HAPPEN - nread_elements = infile1.gcount(); + nread_elements = infile2.gcount(); } for (int index0 = 0; index0 < (nread_elements); index0 += 2) From 9417e713c3e0e4d7f220db24d5a8ef1860ecff93 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sat, 13 Mar 2021 21:35:55 +0100 Subject: [PATCH 03/23] Replace Receiver.sources_count configuration parameter name by GNSS-SDR.num_sources. The former parameter name is still read to ensure backward compatibility --- conf/gnss-sdr_BDS_B3I_GPS_L1_CA_ibyte.conf | 2 +- ...nss-sdr_GLONASS_L1_CA_GPS_L1_CA_ibyte.conf | 6 ++-- .../gnss-sdr_GLONASS_L1_CA_GPS_L2C_ibyte.conf | 2 +- ...nss-sdr_GLONASS_L2_CA_GPS_L1_CA_ibyte.conf | 4 +-- .../gnss-sdr_GLONASS_L2_CA_GPS_L2C_ibyte.conf | 2 +- conf/gnss-sdr_multisource_Hybrid_ishort.conf | 2 +- conf/gnss-sdr_multisource_Hybrid_nsr.conf | 2 +- src/core/receiver/control_thread.cc | 31 +++++-------------- src/core/receiver/file_configuration.cc | 3 +- src/core/receiver/gnss_flowgraph.cc | 15 ++++++--- 10 files changed, 30 insertions(+), 39 deletions(-) diff --git a/conf/gnss-sdr_BDS_B3I_GPS_L1_CA_ibyte.conf b/conf/gnss-sdr_BDS_B3I_GPS_L1_CA_ibyte.conf index cd9e0c1a3..f6df123ee 100644 --- a/conf/gnss-sdr_BDS_B3I_GPS_L1_CA_ibyte.conf +++ b/conf/gnss-sdr_BDS_B3I_GPS_L1_CA_ibyte.conf @@ -12,7 +12,7 @@ ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=30000000 -Receiver.sources_count=2 +GNSS-SDR.num_sources=2 ;######### CONTROL_THREAD CONFIG ############ ControlThread.wait_for_flowgraph=false diff --git a/conf/gnss-sdr_GLONASS_L1_CA_GPS_L1_CA_ibyte.conf b/conf/gnss-sdr_GLONASS_L1_CA_GPS_L1_CA_ibyte.conf index 8ab657656..021b12ae8 100644 --- a/conf/gnss-sdr_GLONASS_L1_CA_GPS_L1_CA_ibyte.conf +++ b/conf/gnss-sdr_GLONASS_L1_CA_GPS_L1_CA_ibyte.conf @@ -7,11 +7,11 @@ ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 -Receiver.sources_count=2 +GNSS-SDR.num_sources=2 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource0.implementation=File_Signal_Source -SignalSource0.filename=/archive/NT1065_L1_20160923_fs6625e6_if60e3_schar.bin ; <- PUT YOUR FILE HERE +SignalSource0.filename=../data/NT1065_L1_20160923_fs6625e6_if60e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource0.item_type=ibyte SignalSource0.sampling_frequency=6625000 SignalSource0.samples=0 @@ -19,7 +19,7 @@ SignalSource0.dump=false; SignalSource0.dump_filename=/archive/signal_glonass.bin SignalSource1.implementation=File_Signal_Source -SignalSource1.filename=/archive/NT1065_GLONASS_L1_20160923_fs6625e6_if0e3_schar.bin ; <- PUT YOUR FILE HERE +SignalSource1.filename=../data/NT1065_GLONASS_L1_20160923_fs6625e6_if0e3_schar.bin ; <- PUT YOUR FILE HERE SignalSource1.item_type=ibyte SignalSource1.sampling_frequency=6625000 SignalSource1.samples=0 diff --git a/conf/gnss-sdr_GLONASS_L1_CA_GPS_L2C_ibyte.conf b/conf/gnss-sdr_GLONASS_L1_CA_GPS_L2C_ibyte.conf index 0e1f86f56..8d37347c8 100644 --- a/conf/gnss-sdr_GLONASS_L1_CA_GPS_L2C_ibyte.conf +++ b/conf/gnss-sdr_GLONASS_L1_CA_GPS_L2C_ibyte.conf @@ -7,7 +7,7 @@ ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 -Receiver.sources_count=2 +GNSS-SDR.num_sources=2 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource0.implementation=File_Signal_Source diff --git a/conf/gnss-sdr_GLONASS_L2_CA_GPS_L1_CA_ibyte.conf b/conf/gnss-sdr_GLONASS_L2_CA_GPS_L1_CA_ibyte.conf index 21e282f52..e4f7a4488 100644 --- a/conf/gnss-sdr_GLONASS_L2_CA_GPS_L1_CA_ibyte.conf +++ b/conf/gnss-sdr_GLONASS_L2_CA_GPS_L1_CA_ibyte.conf @@ -1,4 +1,4 @@ -; This is a GNSS-SDR configuration file +GNSS-SDR.num_sources; This is a GNSS-SDR configuration file ; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ ; SPDX-License-Identifier: GPL-3.0-or-later ; SPDX-FileCopyrightText: (C) 2010-2020 (see AUTHORS file for a list of contributors) @@ -7,7 +7,7 @@ ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 -Receiver.sources_count=2 +GNSS-SDR.num_sources=2 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource0.implementation=File_Signal_Source diff --git a/conf/gnss-sdr_GLONASS_L2_CA_GPS_L2C_ibyte.conf b/conf/gnss-sdr_GLONASS_L2_CA_GPS_L2C_ibyte.conf index 4ff0a65d2..79585a6ae 100644 --- a/conf/gnss-sdr_GLONASS_L2_CA_GPS_L2C_ibyte.conf +++ b/conf/gnss-sdr_GLONASS_L2_CA_GPS_L2C_ibyte.conf @@ -7,7 +7,7 @@ ;######### GLOBAL OPTIONS ################## GNSS-SDR.internal_fs_sps=6625000 -Receiver.sources_count=2 +GNSS-SDR.num_sources=2 ;######### SIGNAL_SOURCE CONFIG ############ SignalSource0.implementation=File_Signal_Source diff --git a/conf/gnss-sdr_multisource_Hybrid_ishort.conf b/conf/gnss-sdr_multisource_Hybrid_ishort.conf index 1de85f0a6..5838a3b88 100644 --- a/conf/gnss-sdr_multisource_Hybrid_ishort.conf +++ b/conf/gnss-sdr_multisource_Hybrid_ishort.conf @@ -13,7 +13,7 @@ ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. GNSS-SDR.internal_fs_sps=4000000 -Receiver.sources_count=2 +GNSS-SDR.num_sources=2 SignalSource.enable_throttle_control=false SignalSource.repeat=false diff --git a/conf/gnss-sdr_multisource_Hybrid_nsr.conf b/conf/gnss-sdr_multisource_Hybrid_nsr.conf index 44bc9d716..c5c5e9134 100644 --- a/conf/gnss-sdr_multisource_Hybrid_nsr.conf +++ b/conf/gnss-sdr_multisource_Hybrid_nsr.conf @@ -9,7 +9,7 @@ [GNSS-SDR] -Receiver.sources_count=2 +GNSS-SDR.num_sources=2 ;######### GLOBAL OPTIONS ################## ;internal_fs_sps: Internal signal sampling frequency after the signal conditioning stage [samples per second]. diff --git a/src/core/receiver/control_thread.cc b/src/core/receiver/control_thread.cc index 19c1348f9..59baf3475 100644 --- a/src/core/receiver/control_thread.cc +++ b/src/core/receiver/control_thread.cc @@ -91,33 +91,18 @@ ControlThread::ControlThread() const std::string empty_implementation; std::string src_impl = configuration_->property("SignalSource.implementation", empty_implementation); - int src_count = configuration_->property("Receiver.sources_count", 1); + int src_count_deprecated = configuration_->property("Receiver.sources_count", 1); + int src_count = configuration_->property("GNSS-SDR.num_sources", src_count_deprecated); if (src_impl.empty()) { - if ((src_count == 1)) + src_impl = std::string(""); + int num_src = 0; + for (auto i = 0; i < src_count; i++) { - conf_has_signal_sources_ = false; - } - else - { - int num_src = 0; - for (int i = 0; i < src_count; i++) - { - std::string src_impl_multiple = configuration_->property("SignalSource" + std::to_string(i) + ".implementation", empty_implementation); - if (!src_impl_multiple.empty()) - { - num_src++; - } - } - if (num_src == src_count) - { - conf_has_signal_sources_ = true; - } - else - { - conf_has_signal_sources_ = false; - } + auto src_impl_multiple = configuration_->property("SignalSource" + std::to_string(i) + ".implementation", empty_implementation); + num_src += !src_impl_multiple.empty(); } + conf_has_signal_sources_ = (num_src == src_count); } else { diff --git a/src/core/receiver/file_configuration.cc b/src/core/receiver/file_configuration.cc index 53700a21c..9828a332d 100644 --- a/src/core/receiver/file_configuration.cc +++ b/src/core/receiver/file_configuration.cc @@ -22,6 +22,7 @@ #include "file_configuration.h" #include "gnss_sdr_make_unique.h" #include +#include #include @@ -55,7 +56,7 @@ void FileConfiguration::init() } else { - LOG(WARNING) << "Unable to open configuration file " << filename_; + std::cerr << "Unable to open configuration file " << filename_ << '\n'; } } diff --git a/src/core/receiver/gnss_flowgraph.cc b/src/core/receiver/gnss_flowgraph.cc index 516d65b5d..fdb2d43a1 100644 --- a/src/core/receiver/gnss_flowgraph.cc +++ b/src/core/receiver/gnss_flowgraph.cc @@ -100,7 +100,8 @@ void GNSSFlowgraph::init() channels_status_ = channel_status_msg_receiver_make(); // 1. read the number of RF front-ends available (one file_source per RF front-end) - sources_count_ = configuration_->property("Receiver.sources_count", 1); + int sources_count_deprecated = configuration_->property("Receiver.sources_count", 1); + sources_count_ = configuration_->property("GNSS-SDR.num_sources", sources_count_deprecated); int signal_conditioner_ID = 0; @@ -112,7 +113,10 @@ void GNSSFlowgraph::init() { auto& src = sig_source_.back(); auto RF_Channels = src->getRfChannels(); - std::cout << "RF Channels: " << RF_Channels << '\n'; + if (sources_count_ == 1) + { + std::cout << "RF Channels: " << RF_Channels << '\n'; + } for (auto j = 0U; j < RF_Channels; ++j) { sig_conditioner_.push_back(block_factory->GetSignalConditioner(configuration_.get(), signal_conditioner_ID)); @@ -120,6 +124,10 @@ void GNSSFlowgraph::init() } } } + if (sources_count_ != 1 && !enable_fpga_offloading_) + { + std::cout << "RF Channels: " << sources_count_ << '\n'; + } if (!sig_conditioner_.empty()) { signal_conditioner_connected_ = std::vector(sig_conditioner_.size(), false); @@ -522,7 +530,6 @@ int GNSSFlowgraph::disconnect_desktop_flowgraph() int GNSSFlowgraph::connect_fpga_flowgraph() { // Check that the Signal Source has been instantiated successfully - for (auto& src : sig_source_) { if (src == nullptr) @@ -541,7 +548,6 @@ int GNSSFlowgraph::connect_fpga_flowgraph() } // Connect blocks to the top_block - if (connect_channels() != 0) { return 1; @@ -560,7 +566,6 @@ int GNSSFlowgraph::connect_fpga_flowgraph() DLOG(INFO) << "Blocks connected internally to the top_block"; // Connect the counter - if (connect_fpga_sample_counter() != 0) { return 1; From 60a7a6d51db36931a622cd49a401f00b6ce2930d Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sat, 13 Mar 2021 22:30:23 +0100 Subject: [PATCH 04/23] Update Armadillo and Protocol Buffers local versions --- CMakeLists.txt | 4 ++-- README.md | 12 ++++++------ docs/changelog.md | 3 +++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 604c9b47a..5e1c7e37d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,13 +324,13 @@ set(GNSSSDR_PROTOBUF_MIN_VERSION "3.0.0") ################################################################################ set(GNSSSDR_GFLAGS_LOCAL_VERSION "2.2.2") set(GNSSSDR_GLOG_LOCAL_VERSION "0.4.0") -set(GNSSSDR_ARMADILLO_LOCAL_VERSION "10.2.x") +set(GNSSSDR_ARMADILLO_LOCAL_VERSION "10.3.x") set(GNSSSDR_GTEST_LOCAL_VERSION "1.10.0") set(GNSSSDR_GNSS_SIM_LOCAL_VERSION "master") set(GNSSSDR_GPSTK_LOCAL_VERSION "8.0.0") set(GNSSSDR_MATIO_LOCAL_VERSION "1.5.19") set(GNSSSDR_PUGIXML_LOCAL_VERSION "1.11.4") -set(GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION "3.15.5") +set(GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION "3.15.6") set(GNSSSDR_BENCHMARK_LOCAL_VERSION "1.5.2") set(GNSSSDR_MATHJAX_EXTERNAL_VERSION "2.7.7") diff --git a/README.md b/README.md index d0402a3c9..94fe9b7c6 100644 --- a/README.md +++ b/README.md @@ -292,9 +292,9 @@ $ sudo apt-get install libblas-dev liblapack-dev # For Debian/Ubuntu/Linux $ sudo yum install lapack-devel blas-devel # For Fedora/CentOS/RHEL $ sudo zypper install lapack-devel blas-devel # For OpenSUSE $ sudo pacman -S blas lapack # For Arch Linux -$ wget http://sourceforge.net/projects/arma/files/armadillo-10.2.1.tar.xz -$ tar xvfz armadillo-10.2.1.tar.xz -$ cd armadillo-10.2.1 +$ wget http://sourceforge.net/projects/arma/files/armadillo-10.3.0.tar.xz +$ tar xvfz armadillo-10.3.0.tar.xz +$ cd armadillo-10.3.0 $ cmake . $ make $ sudo make install @@ -395,9 +395,9 @@ $ sudo apt-get install autoconf automake libtool curl make g++ unzip and then: ``` -$ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.15.5/protobuf-cpp-3.15.5.tar.gz -$ tar xvfz protobuf-cpp-3.15.5.tar.gz -$ cd protobuf-3.15.5 +$ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.15.6/protobuf-cpp-3.15.6.tar.gz +$ tar xvfz protobuf-cpp-3.15.6.tar.gz +$ cd protobuf-3.15.6 $ ./autogen.sh $ ./configure $ make diff --git a/docs/changelog.md b/docs/changelog.md index 544575c3e..51afcdc85 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -64,6 +64,9 @@ SPDX-FileCopyrightText: 2011-2021 Carles Fernandez-Prades Date: Sun, 14 Mar 2021 20:00:18 +0100 Subject: [PATCH 05/23] Add multiple RF output capability to LabSat 3 Wideband format --- docs/changelog.md | 3 +- .../adapters/labsat_signal_source.cc | 148 +++- .../adapters/labsat_signal_source.h | 15 +- .../gnuradio_blocks/labsat23_source.cc | 799 ++++++++++-------- .../gnuradio_blocks/labsat23_source.h | 20 +- 5 files changed, 565 insertions(+), 420 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 51afcdc85..c7ec3c833 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -63,7 +63,8 @@ SPDX-FileCopyrightText: 2011-2021 Carles Fernandez-Prades +#include +#include using namespace std::string_literals; @@ -37,7 +39,25 @@ LabsatSignalSource::LabsatSignalSource(const ConfigurationInterface* configurati const int64_t sampling_frequency_deprecated = configuration->property(role + ".sampling_frequency", static_cast(16368000)); const int64_t throttle_frequency_sps = configuration->property(role + ".throttle_frequency_sps", static_cast(sampling_frequency_deprecated)); - const int channel_selector = configuration->property(role + ".selected_channel", 1); + std::string channels_to_read = configuration->property(role + ".selected_channel", default_item_type); + std::stringstream ss(channels_to_read); + int found; + while (ss.good()) + { + std::string substr; + getline(ss, substr, ','); + if (std::stringstream(substr) >> found) + { + if (found >= 1 && found <= 3) + { + channels_selector_vec_.push_back(found); + } + } + } + if (channels_selector_vec_.empty()) + { + channels_selector_vec_.push_back(1); + } const std::string default_filename("./example_capture.LS3"); filename_ = configuration->property(role + ".filename", default_filename); @@ -47,7 +67,7 @@ LabsatSignalSource::LabsatSignalSource(const ConfigurationInterface* configurati if (item_type_ == "gr_complex") { item_size_ = sizeof(gr_complex); - labsat23_source_ = labsat23_make_source_sptr(filename_.c_str(), channel_selector, queue, digital_io_enabled); + labsat23_source_ = labsat23_make_source_sptr(filename_.c_str(), channels_selector_vec_, queue, digital_io_enabled); DLOG(INFO) << "Item size " << item_size_; DLOG(INFO) << "labsat23_source_(" << labsat23_source_->unique_id() << ")"; } @@ -58,49 +78,95 @@ LabsatSignalSource::LabsatSignalSource(const ConfigurationInterface* configurati } if (dump_) { - DLOG(INFO) << "Dumping output into file " << dump_filename_; - DLOG(INFO) << "file_sink(" << file_sink_->unique_id() << ")"; - file_sink_ = gr::blocks::file_sink::make(item_size_, dump_filename_.c_str()); + std::vector dump_filename; + file_sink_.reserve(channels_selector_vec_.size()); + for (int i : channels_selector_vec_) + { + if (channels_selector_vec_.size() == 1) + { + dump_filename.push_back(dump_filename_); + } + else + { + std::string aux(dump_filename_.substr(0, dump_filename_.length() - 4)); + std::string extension(dump_filename_.substr(dump_filename_.length() - 3, dump_filename_.length())); + if (i == 1) + { + aux += "_chA."s; + } + if (i == 2) + { + aux += "_chB."s; + } + if (i == 3) + { + aux += "_chC."s; + } + dump_filename.push_back(aux + extension); + } + std::cout << "Dumping output into file " << dump_filename.back() << '\n'; + file_sink_.push_back(gr::blocks::file_sink::make(item_size_, dump_filename.back().c_str())); + DLOG(INFO) << "file_sink(" << file_sink_.back()->unique_id() << ")"; + } } if (enable_throttle_control_) { - throttle_ = gr::blocks::throttle::make(item_size_, throttle_frequency_sps); + for (auto it = channels_selector_vec_.begin(); it != channels_selector_vec_.end(); ++it) + { + throttle_.push_back(gr::blocks::throttle::make(item_size_, throttle_frequency_sps)); + } } if (in_stream_ > 0) { LOG(ERROR) << "A signal source does not have an input stream"; } - if (out_stream_ > 1) + if (out_stream_ > 3) { - LOG(ERROR) << "This implementation only supports one output stream"; + LOG(ERROR) << "This implementation supports up to 3 output streams"; } } +size_t LabsatSignalSource::getRfChannels() const +{ + return channels_selector_vec_.size(); +} + + void LabsatSignalSource::connect(gr::top_block_sptr top_block) { if (enable_throttle_control_ == true) { - top_block->connect(labsat23_source_, 0, throttle_, 0); - DLOG(INFO) << "connected labsat23_source_ to throttle"; - if (dump_) + int rf_chan = 0; + for (const auto& th : throttle_) { - top_block->connect(labsat23_source_, 0, file_sink_, 0); - DLOG(INFO) << "connected labsat23_source_to sink"; + top_block->connect(labsat23_source_, rf_chan, th, 0); + DLOG(INFO) << "connected labsat23_source_ to throttle"; + if (dump_) + { + top_block->connect(labsat23_source_, rf_chan, file_sink_[rf_chan], 0); + DLOG(INFO) << "connected labsat23_source_to sink"; + } + rf_chan++; } } else { - if (dump_) + int rf_chan = 0; + for (auto it = channels_selector_vec_.begin(); it != channels_selector_vec_.end(); ++it) { - top_block->connect(labsat23_source_, 0, file_sink_, 0); - DLOG(INFO) << "connected labsat23_source_ to sink"; - } - else - { - DLOG(INFO) << "nothing to connect internally"; + if (dump_) + { + top_block->connect(labsat23_source_, 0, file_sink_[rf_chan], 0); + DLOG(INFO) << "connected labsat23_source_ to sink"; + } + else + { + DLOG(INFO) << "nothing to connect internally"; + } + rf_chan++; } } } @@ -110,20 +176,30 @@ void LabsatSignalSource::disconnect(gr::top_block_sptr top_block) { if (enable_throttle_control_ == true) { - top_block->disconnect(labsat23_source_, 0, throttle_, 0); - DLOG(INFO) << "disconnected labsat23_source_ to throttle"; - if (dump_) + int rf_chan = 0; + for (const auto& th : throttle_) { - top_block->disconnect(labsat23_source_, 0, file_sink_, 0); - DLOG(INFO) << "disconnected labsat23_source_ to sink"; + top_block->disconnect(labsat23_source_, rf_chan, th, 0); + DLOG(INFO) << "disconnected labsat23_source_ to throttle"; + if (dump_) + { + top_block->disconnect(labsat23_source_, rf_chan, file_sink_[rf_chan], 0); + DLOG(INFO) << "disconnected labsat23_source_ to sink"; + } + rf_chan++; } } else { - if (dump_) + int rf_chan = 0; + for (auto it = channels_selector_vec_.begin(); it != channels_selector_vec_.end(); ++it) { - top_block->disconnect(labsat23_source_, 0, file_sink_, 0); - DLOG(INFO) << "disconnected labsat23_source_ to sink"; + if (dump_) + { + top_block->disconnect(labsat23_source_, rf_chan, file_sink_[rf_chan], 0); + DLOG(INFO) << "disconnected labsat23_source_ to sink"; + } + rf_chan++; } } } @@ -140,7 +216,17 @@ gr::basic_block_sptr LabsatSignalSource::get_right_block() { if (enable_throttle_control_ == true) { - return throttle_; + return throttle_[0]; + } + return labsat23_source_; +} + + +gr::basic_block_sptr LabsatSignalSource::get_right_block(int i) +{ + if (enable_throttle_control_ == true) + { + return throttle_[i]; } return labsat23_source_; } diff --git a/src/algorithms/signal_source/adapters/labsat_signal_source.h b/src/algorithms/signal_source/adapters/labsat_signal_source.h index ec613698f..50424ebf7 100644 --- a/src/algorithms/signal_source/adapters/labsat_signal_source.h +++ b/src/algorithms/signal_source/adapters/labsat_signal_source.h @@ -1,6 +1,6 @@ /*! * \file labsat_signal_source.h - * \brief Labsat 2 and 3 front-end signal sampler driver + * \brief LabSat version 2, 3, and 3 Wideband format reader * \author Javier Arribas, jarribas(at)cttc.es * * ----------------------------------------------------------------------------- @@ -8,7 +8,7 @@ * 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 * * ----------------------------------------------------------------------------- @@ -27,6 +27,7 @@ #include #include #include +#include /** \addtogroup Signal_Source * \{ */ @@ -37,7 +38,8 @@ class ConfigurationInterface; /*! - * \brief This class reads samples stored by a LabSat 2 or LabSat 3 device + * \brief This class reads samples stored in LabSat version 2, 3, and 3 Wideband + * format. */ class LabsatSignalSource : public SignalSourceBase { @@ -53,15 +55,18 @@ public: return item_size_; } + size_t getRfChannels() const override; void connect(gr::top_block_sptr top_block) override; void disconnect(gr::top_block_sptr top_block) override; gr::basic_block_sptr get_left_block() override; gr::basic_block_sptr get_right_block() override; + gr::basic_block_sptr get_right_block(int i) override; private: gr::block_sptr labsat23_source_; - gr::blocks::file_sink::sptr file_sink_; - gr::blocks::throttle::sptr throttle_; + std::vector file_sink_; + std::vector throttle_; + std::vector channels_selector_vec_; std::string item_type_; std::string filename_; diff --git a/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.cc b/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.cc index 2bc98b970..d9cf1ff3c 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.cc +++ b/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.cc @@ -1,7 +1,7 @@ /*! * \file labsat23_source.cc * - * \brief Unpacks capture files in the Labsat 2 (ls2), Labsat 3 (ls3), or Labsat + * \brief Unpacks capture files in the LabSat 2 (ls2), LabSat 3 (ls3), or LabSat * 3 Wideband (LS3W) formats. * \author Javier Arribas jarribas (at) cttc.es * @@ -23,35 +23,29 @@ #include "gnss_sdr_make_unique.h" #include #include +#include #include #include -#include #include #include #include #include -#include -labsat23_source_sptr labsat23_make_source_sptr(const char *signal_file_basename, int channel_selector, Concurrent_Queue *queue, bool digital_io_enabled) +labsat23_source_sptr labsat23_make_source_sptr(const char *signal_file_basename, const std::vector &channel_selector, Concurrent_Queue *queue, bool digital_io_enabled) { return labsat23_source_sptr(new labsat23_source(signal_file_basename, channel_selector, queue, digital_io_enabled)); } labsat23_source::labsat23_source(const char *signal_file_basename, - int channel_selector, + const std::vector &channel_selector, Concurrent_Queue *queue, bool digital_io_enabled) : gr::block("labsat23_source", gr::io_signature::make(0, 0, 0), - gr::io_signature::make(1, 1, sizeof(gr_complex))), + gr::io_signature::make(1, 3, sizeof(gr_complex))), d_queue(queue) { - if (channel_selector < 1 or channel_selector > 2) - { - std::cout << "Labsat source config error: channel selection out of bounds, check gnss-sdr config file\n"; - exit(1); - } d_channel_selector_config = channel_selector; d_header_parsed = false; d_bits_per_sample = 0; @@ -69,7 +63,7 @@ labsat23_source::labsat23_source(const char *signal_file_basename, if (d_is_ls3w) { d_labsat_version = 3; - std::cout << "Labsat file version 3 wideband detected.\n"; + std::cout << "LabSat file version 3 Wideband detected.\n"; // Read ini file std::string ini_file = signal_file.substr(0, signal_file.length() - 4) + std::string("ini"); if (read_ls3w_ini(ini_file) != 0) @@ -82,11 +76,11 @@ labsat23_source::labsat23_source(const char *signal_file_basename, if (binary_input_file.is_open()) { - std::cout << "Labsat file source is reading samples from " << signal_file << '\n'; + std::cout << "LabSat file source is reading samples from " << signal_file << '\n'; } else { - std::cout << "Labsat file " << signal_file << " could not be opened!\n"; + std::cout << "LabSat file " << signal_file << " could not be opened!\n"; exit(1); } } @@ -140,6 +134,229 @@ int labsat23_source::getBit(uint8_t byte, int position) } +int labsat23_source::parse_header() +{ + if (binary_input_file.eof() == false) + { + std::array memblock{}; + binary_input_file.read(memblock.data(), 1024); + // parse Labsat header + // check preamble + int byte_counter = 0; + bool preamble_ok = true; + for (int i = 0; i < 8; i++) + { + if (memblock[byte_counter] != 0x00) + { + preamble_ok = false; + } + byte_counter++; + } + + if (preamble_ok == false) + { + std::cout << "LabSat source do not detect the preamble in the selected file\n"; + return -1; + } + + // check Labsat version + if (memblock[byte_counter] == 0x4C and memblock[byte_counter + 1] == 0x53 and memblock[byte_counter + 2] == 0x32) + { + d_labsat_version = 2; + std::cout << "LabSat file version 2 detected\n"; + } + + if (memblock[byte_counter] == 0x4C and memblock[byte_counter + 1] == 0x53 and memblock[byte_counter + 2] == 0x33) + { + d_labsat_version = 3; + std::cout << "LabSat file version 3 detected\n"; + } + + if (d_labsat_version == 0) + { + std::cout << "LabSat source do not detect the version number in the file header\n"; + return -1; + } + + byte_counter += 3; + + int sub_version = static_cast(memblock[byte_counter]); + + std::cout << "LabSat file sub version " << sub_version << '\n'; + + byte_counter++; + + int header_bytes = 0; + header_bytes += memblock[byte_counter] | (memblock[byte_counter + 1] << 8) | (memblock[byte_counter + 2] << 16) | (memblock[byte_counter + 3] << 24); + + byte_counter += 4; + + // read first section + // section ID (little-endian) + uint8_t section_id = static_cast(memblock[byte_counter]) + static_cast(memblock[byte_counter + 1]) * 256; + byte_counter += 2; + + // uint8_t section_lenght_bytes = 0; + // section_lenght_bytes += memblock[byte_counter] | (memblock[byte_counter + 1] << 8) | (memblock[byte_counter + 2] << 16) | (memblock[byte_counter + 3] << 24); + + byte_counter += 4; + if (section_id == 2) + { + d_ref_clock = static_cast(memblock[byte_counter]); + switch (d_ref_clock) + { + case 0: + std::cout << "LabSat reference clock: internal OCXO\n"; + break; + case 1: + std::cout << "LabSat reference clock: internal TCXO\n"; + break; + case 2: + std::cout << "LabSat reference clock: external 10 MHz\n"; + break; + case 3: + std::cout << "LabSat reference clock: external 16.386 MHz\n"; + break; + default: + std::cout << "LabSat Unknown reference clock ID " << static_cast(d_ref_clock) << '\n'; + } + byte_counter++; + d_bits_per_sample = static_cast(memblock[byte_counter]); + switch (d_bits_per_sample) + { + case 2: + std::cout << "LabSat is using 2 bits per sample\n"; + break; + case 4: + std::cout << "LabSat is using 4 bits per sample\n"; + break; + default: + std::cout << "LabSat Unknown bits per sample ID " << static_cast(d_bits_per_sample) << '\n'; + return -1; + } + + byte_counter++; + d_channel_selector = static_cast(memblock[byte_counter]); + switch (d_channel_selector) + { + case 0: + std::cout << "Available channels: Channel A + B, 1 bit quantisation (I & Q)\n"; + break; + case 1: + std::cout << "Available channels: Channel A, 1 bit quantisation (I & Q)\n"; + break; + case 2: + std::cout << "Available channels: Channel B, 1 bit quantisation (I & Q)\n"; + break; + case 3: + std::cout << "Available channels: Channel A, 2 bit quantisation (I & Q)\n"; + break; + case 4: + std::cout << "Available channels: Channel B, 2 bit quantisation (I & Q)\n"; + break; + default: + std::cout << "Unknown channel selection ID " << static_cast(d_channel_selector) << '\n'; + return -1; + } + + // check if the selected channel in config file match the file encoding + if (d_channel_selector_config[0] == 2 and d_channel_selector != 0) + { + std::cout << "LabSat source channel config inconsistency: channel 2 is selected but the file has only one channel.\n"; + return -1; + } + + // todo: Add support for dual channel files + if (d_channel_selector == 0) + { + std::cout << "ERROR: LabSat file contains more than one channel and this is not currently supported for LabSat version " << d_labsat_version << ".\n"; + return -1; + } + byte_counter++; + auto quantization = static_cast(memblock[byte_counter]); + switch (quantization) + { + case 0: + break; + case 1: + std::cout << "1 bit per sample\n"; + break; + case 2: + std::cout << "2 bit per sample\n"; + break; + default: + std::cout << "Unknown quantization ID " << static_cast(quantization) << '\n'; + } + byte_counter++; + auto channel_a_constellation = static_cast(memblock[byte_counter]); + switch (channel_a_constellation) + { + case 0: + std::cout << "LabSat Channel A is GPS\n"; + break; + case 1: + std::cout << "LabSat Channel A is GLONASS\n"; + break; + case 2: + std::cout << "LabSat Channel A is BDS\n"; + break; + default: + std::cout << "Unknown channel A constellation ID " << static_cast(channel_a_constellation) << '\n'; + } + byte_counter++; + auto channel_b_constellation = static_cast(memblock[byte_counter]); + switch (channel_b_constellation) + { + case 0: + std::cout << "LabSat Channel B is GPS\n"; + break; + case 1: + std::cout << "LabSat Channel B is GLONASS\n"; + break; + case 2: + std::cout << "LabSat Channel B is BDS\n"; + break; + case 255: + // No channel B + break; + default: + std::cout << "Unknown channel B constellation ID " << static_cast(channel_b_constellation) << '\n'; + } + + // end of header + d_header_parsed = true; + // seek file to the first signal sample + binary_input_file.clear(); + binary_input_file.seekg(header_bytes, binary_input_file.beg); + if (d_channel_selector_config.size() > 1) + { + std::cerr << "Multiple RF source is not implemented for LabSat version " << d_labsat_version << "files.\n"; + std::cerr << "The Multiple RF source feature is only available for LabSat 3 Wideband format files.\n"; + std::cerr << "Selecting channel"; + if (d_channel_selector_config[0] == 1) + { + std::cerr << " A."; + } + if (d_channel_selector_config[0] == 2) + { + std::cerr << " B."; + } + if (d_channel_selector_config[0] == 3) + { + std::cerr << " C."; + } + std::cerr << '\n'; + } + return 0; + } + std::cout << "LabSat file header error: section 2 is not available.\n"; + return -1; + } + std::cout << "LabSat file read error: file is empty.\n"; + return -1; +} + + void labsat23_source::decode_samples_one_channel(int16_t input_short, gr_complex *out, int type) { std::bitset<16> bs(input_short); @@ -223,12 +440,12 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) if (error_ > 0) { - std::cerr << "Warning: Labsat ini file " << filename + std::cerr << "Warning: LabSat ini file " << filename << " contains a syntax error in line " << error_ << ", continuing anyway.\n"; } if (error_ < 0) { - std::cerr << "Error: Labsat ini file " << filename << " cannot be opened.\n"; + std::cerr << "Error: LabSat ini file " << filename << " cannot be opened.\n"; return 1; } const std::string empty_string(""); @@ -238,18 +455,18 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) d_ls3w_OSC = ini_reader->Get("config", "OSC", empty_string); if (d_ls3w_OSC.empty()) { - std::cerr << "Labsat reference clock: not found.\n"; + std::cerr << "LabSat reference clock: not found.\n"; } else { // Sanity check if ((d_ls3w_OSC != "OCXO") and (d_ls3w_OSC != "TCXO") and (d_ls3w_OSC != "EXT")) { - std::cerr << "Labsat reference clock is unknown.\n"; + std::cerr << "LabSat reference clock is unknown.\n"; } else { - std::cout << "Labsat reference clock: " << d_ls3w_OSC << '\n'; + std::cout << "LabSat reference clock: " << d_ls3w_OSC << '\n'; } } @@ -259,7 +476,7 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) { std::stringstream smp_ss(ls3w_SMP_aux); smp_ss >> d_ls3w_SMP; - std::cout << "Labsat sample rate: " << d_ls3w_SMP << " Sps\n"; + std::cout << "LabSat sample rate: " << d_ls3w_SMP << " Sps\n"; } // Quantization @@ -272,12 +489,12 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) // Sanity check if (d_ls3w_QUA > 3) { - std::cerr << "Labsat sample quantization of " << d_ls3w_QUA << " bits is not supported.\n"; + std::cerr << "LabSat sample quantization of " << d_ls3w_QUA << " bits is not supported.\n"; d_ls3w_QUA = 0; } else { - std::cout << "Labsat sample quantization: " << d_ls3w_QUA << " bits for I + " << d_ls3w_QUA << " bits for Q.\n"; + std::cout << "LabSat sample quantization: " << d_ls3w_QUA << " bits for I + " << d_ls3w_QUA << " bits for Q.\n"; } } @@ -291,12 +508,12 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) // Sanity check if (d_ls3w_CHN > 3) { - std::cerr << "Labsat files with " << d_ls3w_CHN << " RF channels are not supported.\n"; + std::cerr << "LabSat files with " << d_ls3w_CHN << " RF channels are not supported.\n"; d_ls3w_CHN = 0; } else { - std::cout << "Labsat data file contains " << d_ls3w_CHN << " RF channels.\n"; + std::cout << "LabSat data file contains " << d_ls3w_CHN << " RF channels.\n"; } } @@ -324,7 +541,7 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) { std::stringstream cfa_ss(ls3w_CFA_aux); cfa_ss >> d_ls3w_CFA; - std::cout << "Labsat center frequency for RF channel A: " << d_ls3w_CFA << " Hz\n"; + std::cout << "LabSat center frequency for RF channel A: " << d_ls3w_CFA << " Hz\n"; } std::string ls3w_BWA_aux = ini_reader->Get("channel A", "BWA", empty_string); @@ -332,7 +549,7 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) { std::stringstream bwa_ss(ls3w_BWA_aux); bwa_ss >> d_ls3w_BWA; - std::cout << "Labsat RF filter bandwidth for RF channel A: " << d_ls3w_BWA << " Hz\n"; + std::cout << "LabSat RF filter bandwidth for RF channel A: " << d_ls3w_BWA << " Hz\n"; } } @@ -344,7 +561,7 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) { std::stringstream cfb_ss(ls3w_CFB_aux); cfb_ss >> d_ls3w_CFB; - std::cout << "Labsat center frequency for RF channel B: " << d_ls3w_CFB << " Hz\n"; + std::cout << "LabSat center frequency for RF channel B: " << d_ls3w_CFB << " Hz\n"; } std::string ls3w_BWB_aux = ini_reader->Get("channel B", "BWB", empty_string); @@ -352,7 +569,7 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) { std::stringstream bwb_ss(ls3w_BWB_aux); bwb_ss >> d_ls3w_BWB; - std::cout << "Labsat RF filter bandwidth for RF channel B: " << d_ls3w_BWB << " Hz\n"; + std::cout << "LabSat RF filter bandwidth for RF channel B: " << d_ls3w_BWB << " Hz\n"; } } @@ -364,7 +581,7 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) { std::stringstream cfc_ss(ls3w_CFC_aux); cfc_ss >> d_ls3w_CFC; - std::cout << "Labsat center frequency for RF channel C: " << d_ls3w_CFC << " Hz\n"; + std::cout << "LabSat center frequency for RF channel C: " << d_ls3w_CFC << " Hz\n"; } std::string ls3w_BWC_aux = ini_reader->Get("channel C", "BWC", empty_string); @@ -372,42 +589,63 @@ int labsat23_source::read_ls3w_ini(const std::string &filename) { std::stringstream bwc_ss(ls3w_BWC_aux); bwc_ss >> d_ls3w_BWC; - std::cout << "Labsat RF filter bandwidth for RF channel C: " << d_ls3w_BWC << " Hz\n"; + std::cout << "LabSat RF filter bandwidth for RF channel C: " << d_ls3w_BWC << " Hz\n"; } } - std::cout << "Labsat selected channel"; - if (d_channel_selector_config == 1) + std::cout << "LabSat selected channel" << ((d_channel_selector_config.size() > 1) ? "s" : "") << ": "; + if (std::find(d_channel_selector_config.begin(), d_channel_selector_config.end(), 1) != d_channel_selector_config.end()) { - std::cout << ": A\n"; + std::cout << "A"; } - if (d_channel_selector_config == 2) + if (std::find(d_channel_selector_config.begin(), d_channel_selector_config.end(), 2) != d_channel_selector_config.end()) { if (d_ls3w_CHN > 1) { - std::cout << ": B\n"; + if (d_channel_selector_config.size() == 1) + { + std::cout << "B"; + } + else + { + std::cout << ", B"; + } } else { - std::cout << " is not found.\n"; + std::cerr << "\nConfiguration error: RF channel B is selected but not found in data file.\n"; + std::cerr << "Exiting the program.\n"; + return -1; } } - if (d_channel_selector_config == 3) + if (std::find(d_channel_selector_config.begin(), d_channel_selector_config.end(), 3) != d_channel_selector_config.end()) { if (d_ls3w_CHN > 2) { - std::cout << ": C\n"; + if (d_channel_selector_config.size() == 1) + { + std::cout << "C"; + } + else + { + std::cout << ", C"; + } } else { - std::cout << " is not found.\n"; + std::cerr << "\nConfiguration error: RF channel C is selected but not found in data file.\n"; + std::cerr << "Exiting the program.\n"; + return -1; } } + std::cout << '\n'; d_ls3w_samples_per_register = this->number_of_samples_per_ls3w_register(); d_ls3w_spare_bits = 64 - d_ls3w_samples_per_register * d_ls3w_QUA * 2; - d_ls3w_selected_channel_offset = (d_channel_selector_config - 1) * d_ls3w_QUA * 2; - + for (auto ch_select : d_channel_selector_config) + { + d_ls3w_selected_channel_offset.push_back((ch_select - 1) * d_ls3w_QUA * 2); + } return 0; } @@ -495,7 +733,7 @@ int labsat23_source::number_of_samples_per_ls3w_register() const } -void labsat23_source::decode_ls3w_register_one_channel(uint64_t input, gr_complex *out) const +void labsat23_source::decode_ls3w_register(uint64_t input, std::vector &out, size_t output_pointer) const { std::bitset<64> bs(input); @@ -507,173 +745,179 @@ void labsat23_source::decode_ls3w_register_one_channel(uint64_t input, gr_comple bs[64 - i - 1] = t; } - for (int i = 0; i < d_ls3w_samples_per_register; i++) + int output_chan = 0; + for (auto channel_offset : d_ls3w_selected_channel_offset) { - float sampleI = 0.0; - float sampleQ = 0.0; - - const int bit_offset = d_ls3w_spare_bits + i * d_ls3w_SFT + d_ls3w_selected_channel_offset; - switch (d_ls3w_QUA) + for (int i = 0; i < d_ls3w_samples_per_register; i++) { - case 1: - sampleI = bs[bit_offset] ? -1.0 : 1.0; - sampleQ = bs[bit_offset + 1] ? -1.0 : 1.0; - break; - case 2: - if (bs[bit_offset]) - { - if (bs[bit_offset + 1]) // 11 - { - sampleI = -0.5; - } - else // 10 - { - sampleI = -1.0; - } - } - else - { - if (bs[bit_offset + 1]) // 01 - { - sampleI = 1.0; - } - else // 00 - { - sampleI = 0.5; - } - } - if (bs[bit_offset + 2]) - { - if (bs[bit_offset + 3]) // 11 - { - sampleQ = -0.5; - } - else // 10 - { - sampleQ = -1.0; - } - } - else - { - if (bs[bit_offset + 3]) // 01 - { - sampleQ = 1.0; - } - else // 00 - { - sampleQ = 0.5; - } - } - break; + float sampleI = 0.0; + float sampleQ = 0.0; - case 3: - if (bs[bit_offset]) + const int bit_offset = d_ls3w_spare_bits + i * d_ls3w_SFT + channel_offset; + switch (d_ls3w_QUA) { - if (bs[bit_offset + 1]) + case 1: + sampleI = bs[bit_offset] ? -1.0 : 1.0; + sampleQ = bs[bit_offset + 1] ? -1.0 : 1.0; + break; + case 2: + if (bs[bit_offset]) { - if (bs[bit_offset + 2]) // 111 - { - sampleI = -0.25; - } - else // 110 + if (bs[bit_offset + 1]) // 11 { sampleI = -0.5; } - } - else - { - if (bs[bit_offset + 2]) // 101 - { - sampleI = -0.75; - } - else // 100 + else // 10 { sampleI = -1.0; } } - } - else - { - if (bs[bit_offset + 1]) - { - if (bs[bit_offset + 2]) // 011 - { - sampleI = 1; - } - else // 010 - { - sampleI = 0.75; - } - } else { - if (bs[bit_offset + 2]) // 001 + if (bs[bit_offset + 1]) // 01 + { + sampleI = 1.0; + } + else // 00 { sampleI = 0.5; } - else // 000 - { - sampleI = 0.25; - } } - } - if (bs[bit_offset + 3]) - { - if (bs[bit_offset + 4]) + if (bs[bit_offset + 2]) { - if (bs[bit_offset + 5]) // 111 - { - sampleQ = -0.25; - } - else // 110 + if (bs[bit_offset + 3]) // 11 { sampleQ = -0.5; } - } - else - { - if (bs[bit_offset + 5]) // 101 - { - sampleQ = -0.75; - } - else // 100 + else // 10 { sampleQ = -1.0; } } - } - else - { - if (bs[bit_offset + 4]) + else { - if (bs[bit_offset + 5]) // 011 + if (bs[bit_offset + 3]) // 01 { - sampleQ = 1; + sampleQ = 1.0; } - else // 010 + else // 00 { - sampleQ = 0.75; + sampleQ = 0.5; + } + } + break; + + case 3: + if (bs[bit_offset]) + { + if (bs[bit_offset + 1]) + { + if (bs[bit_offset + 2]) // 111 + { + sampleI = -0.25; + } + else // 110 + { + sampleI = -0.5; + } + } + else + { + if (bs[bit_offset + 2]) // 101 + { + sampleI = -0.75; + } + else // 100 + { + sampleI = -1.0; + } } } else { - if (bs[bit_offset + 5]) // 001 + if (bs[bit_offset + 1]) { - sampleQ = 0.5; + if (bs[bit_offset + 2]) // 011 + { + sampleI = 1; + } + else // 010 + { + sampleI = 0.75; + } } - else // 000 + else { - sampleQ = 0.25; + if (bs[bit_offset + 2]) // 001 + { + sampleI = 0.5; + } + else // 000 + { + sampleI = 0.25; + } } } + if (bs[bit_offset + 3]) + { + if (bs[bit_offset + 4]) + { + if (bs[bit_offset + 5]) // 111 + { + sampleQ = -0.25; + } + else // 110 + { + sampleQ = -0.5; + } + } + else + { + if (bs[bit_offset + 5]) // 101 + { + sampleQ = -0.75; + } + else // 100 + { + sampleQ = -1.0; + } + } + } + else + { + if (bs[bit_offset + 4]) + { + if (bs[bit_offset + 5]) // 011 + { + sampleQ = 1; + } + else // 010 + { + sampleQ = 0.75; + } + } + else + { + if (bs[bit_offset + 5]) // 001 + { + sampleQ = 0.5; + } + else // 000 + { + sampleQ = 0.25; + } + } + } + + break; + + default: + break; } - - break; - - default: - break; + gr_complex *aux = out[output_chan]; + aux[output_pointer + i] = gr_complex(sampleI, sampleQ); } - out[i] = gr_complex(sampleI, sampleQ); + output_chan++; } } @@ -683,211 +927,16 @@ int labsat23_source::general_work(int noutput_items, __attribute__((unused)) gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { - auto *out = reinterpret_cast(output_items[0]); - + std::vector out; + for (auto & output_item : output_items) + { + out.push_back(reinterpret_cast(output_item)); + } if (!d_is_ls3w) { if (d_header_parsed == false) { - if (binary_input_file.eof() == false) - { - std::array memblock{}; - binary_input_file.read(memblock.data(), 1024); - // parse Labsat header - // check preamble - int byte_counter = 0; - bool preamble_ok = true; - for (int i = 0; i < 8; i++) - { - if (memblock[byte_counter] != 0x00) - { - preamble_ok = false; - } - byte_counter++; - } - - if (preamble_ok == false) - { - std::cout << "Labsat source do not detect the preamble in the selected file\n"; - return -1; - } - - // check Labsat version - if (memblock[byte_counter] == 0x4C and memblock[byte_counter + 1] == 0x53 and memblock[byte_counter + 2] == 0x32) - { - d_labsat_version = 2; - std::cout << "Labsat file version 2 detected\n"; - } - - if (memblock[byte_counter] == 0x4C and memblock[byte_counter + 1] == 0x53 and memblock[byte_counter + 2] == 0x33) - { - d_labsat_version = 3; - std::cout << "Labsat file version 3 detected\n"; - } - - if (d_labsat_version == 0) - { - std::cout << "Labsat source do not detect the labsat version in file header\n"; - return -1; - } - - byte_counter += 3; - - int sub_version = static_cast(memblock[byte_counter]); - - std::cout << "Labsat file sub version " << sub_version << '\n'; - - byte_counter++; - - int header_bytes = 0; - header_bytes += memblock[byte_counter] | (memblock[byte_counter + 1] << 8) | (memblock[byte_counter + 2] << 16) | (memblock[byte_counter + 3] << 24); - - byte_counter += 4; - - // read first section - // section ID (little-endian) - uint8_t section_id = static_cast(memblock[byte_counter]) + static_cast(memblock[byte_counter + 1]) * 256; - byte_counter += 2; - - // uint8_t section_lenght_bytes = 0; - // section_lenght_bytes += memblock[byte_counter] | (memblock[byte_counter + 1] << 8) | (memblock[byte_counter + 2] << 16) | (memblock[byte_counter + 3] << 24); - - byte_counter += 4; - if (section_id == 2) - { - d_ref_clock = static_cast(memblock[byte_counter]); - switch (d_ref_clock) - { - case 0: - std::cout << "Labsat reference clock: internal OCXO\n"; - break; - case 1: - std::cout << "Labsat reference clock: internal TCXO\n"; - break; - case 2: - std::cout << "Labsat reference clock: external 10 MHz\n"; - break; - case 3: - std::cout << "Labsat reference clock: external 16.386 MHz\n"; - break; - default: - std::cout << "Labsat Unknown reference clock ID " << static_cast(d_ref_clock) << '\n'; - } - byte_counter++; - d_bits_per_sample = static_cast(memblock[byte_counter]); - switch (d_bits_per_sample) - { - case 2: - std::cout << "Labsat is using 2 bits per sample\n"; - break; - case 4: - std::cout << "Labsat is using 4 bits per sample\n"; - break; - default: - std::cout << "Labsat Unknown bits per sample ID " << static_cast(d_bits_per_sample) << '\n'; - return -1; - } - - byte_counter++; - d_channel_selector = static_cast(memblock[byte_counter]); - switch (d_channel_selector) - { - case 0: - std::cout << "Available channels: Channel A + B, 1 bit quantisation (I & Q)\n"; - break; - case 1: - std::cout << "Available channels: Channel A, 1 bit quantisation (I & Q)\n"; - break; - case 2: - std::cout << "Available channels: Channel B, 1 bit quantisation (I & Q)\n"; - break; - case 3: - std::cout << "Available channels: Channel A, 2 bit quantisation (I & Q)\n"; - break; - case 4: - std::cout << "Available channels: Channel B, 2 bit quantisation (I & Q)\n"; - break; - default: - std::cout << "Unknown channel selection ID " << static_cast(d_channel_selector) << '\n'; - return -1; - } - - // check if the selected channel in config file match the file encoding - if (d_channel_selector_config == 2 and d_channel_selector != 0) - { - std::cout << "Labsat source channel config inconsistency: channel 2 is selected but the file has only one channel\n"; - return -1; - } - - // todo: Add support for dual channel files - if (d_channel_selector == 0) - { - std::cout << "ERROR: Labsat file contains more than one channel and it is not currently supported by Labsat signal source.\n"; - return -1; - } - byte_counter++; - auto quantization = static_cast(memblock[byte_counter]); - switch (quantization) - { - case 0: - break; - case 1: - std::cout << "1 bit per sample\n"; - break; - case 2: - std::cout << "2 bit per sample\n"; - break; - default: - std::cout << "Unknown quantization ID " << static_cast(quantization) << '\n'; - } - byte_counter++; - auto channel_a_constellation = static_cast(memblock[byte_counter]); - switch (channel_a_constellation) - { - case 0: - std::cout << "Labsat Channel A is GPS\n"; - break; - case 1: - std::cout << "Labsat Channel A is GLONASS\n"; - break; - case 2: - std::cout << "Labsat Channel A is BDS\n"; - break; - default: - std::cout << "Unknown channel A constellation ID " << static_cast(channel_a_constellation) << '\n'; - } - byte_counter++; - auto channel_b_constellation = static_cast(memblock[byte_counter]); - switch (channel_b_constellation) - { - case 0: - std::cout << "Labsat Channel B is GPS\n"; - break; - case 1: - std::cout << "Labsat Channel B is GLONASS\n"; - break; - case 2: - std::cout << "Labsat Channel B is BDS\n"; - break; - case 255: - // No channel B - break; - default: - std::cout << "Unknown channel B constellation ID " << static_cast(channel_b_constellation) << '\n'; - } - - // end of header - d_header_parsed = true; - // seek file to the first signal sample - binary_input_file.clear(); - binary_input_file.seekg(header_bytes, binary_input_file.beg); - return 0; - } - std::cout << "Labsat file header error: section 2 is not available.\n"; - return -1; - } - std::cout << "Labsat file read error: file is empty.\n"; - return -1; + return parse_header(); } // ready to start reading samples @@ -913,7 +962,7 @@ int labsat23_source::general_work(int noutput_items, int output_pointer = 0; for (int i = 0; i < n_int16_to_read; i++) { - decode_samples_one_channel(memblock[i], &out[output_pointer], d_bits_per_sample); + decode_samples_one_channel(memblock[i], out[0] + output_pointer, d_bits_per_sample); output_pointer += 8; } return output_pointer; @@ -923,13 +972,13 @@ int labsat23_source::general_work(int noutput_items, d_current_file_number++; if (d_labsat_version == 3) { - std::cout << "End of current file, reading the next Labsat file in sequence: " << generate_filename() << '\n'; + std::cout << "End of current file, reading the next LabSat file in sequence: " << generate_filename() << '\n'; } binary_input_file.close(); binary_input_file.open(generate_filename().c_str(), std::ios::in | std::ios::binary); if (binary_input_file.is_open()) { - std::cout << "Labsat file source is reading samples from " << generate_filename() << '\n'; + std::cout << "LabSat file source is reading samples from " << generate_filename() << '\n'; return 0; } @@ -971,7 +1020,7 @@ int labsat23_source::general_work(int noutput_items, int output_pointer = 0; for (int i = 0; i < n_int16_to_read; i++) { - decode_samples_one_channel(memblock[i], &out[output_pointer], d_bits_per_sample); + decode_samples_one_channel(memblock[i], out[0] + output_pointer, d_bits_per_sample); output_pointer += 4; } return output_pointer; @@ -1022,7 +1071,7 @@ int labsat23_source::general_work(int noutput_items, { return 0; } - int output_pointer = 0; + std::size_t output_pointer = 0; for (int i = 0; i < registers_to_read; i++) { std::array memory_block{}; @@ -1036,7 +1085,7 @@ int labsat23_source::general_work(int noutput_items, if (binary_input_file.gcount() == 8) { - decode_ls3w_register_one_channel(read_register, &out[output_pointer]); + decode_ls3w_register(read_register, out, output_pointer); output_pointer += d_ls3w_samples_per_register; } else diff --git a/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.h b/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.h index 8983cc5a7..f7d047d9b 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.h +++ b/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.h @@ -1,7 +1,7 @@ /*! * \file labsat23_source.h * - * \brief Unpacks capture files in the Labsat 2 (ls2), Labsat 3 (ls3), or Labsat + * \brief Unpacks capture files in the LabSat 2 (ls2), LabSat 3 (ls3), or LabSat * 3 Wideband (LS3W) formats. * \author Javier Arribas jarribas (at) cttc.es * @@ -23,9 +23,11 @@ #include "gnss_block_interface.h" #include #include +#include #include #include #include +#include /** \addtogroup Signal_Source * \{ */ @@ -39,12 +41,13 @@ using labsat23_source_sptr = gnss_shared_ptr; labsat23_source_sptr labsat23_make_source_sptr( const char *signal_file_basename, - int channel_selector, + const std::vector &channel_selector, Concurrent_Queue *queue, bool digital_io_enabled); /*! - * \brief This class implements conversion between Labsat2 and 3 format byte packet samples to gr_complex + * \brief This class implements conversion between Labsat 2, 3 and 3 Wideband + * formats to gr_complex */ class labsat23_source : public gr::block { @@ -59,28 +62,29 @@ public: private: friend labsat23_source_sptr labsat23_make_source_sptr( const char *signal_file_basename, - int channel_selector, + const std::vector &channel_selector, Concurrent_Queue *queue, bool digital_io_enabled); labsat23_source(const char *signal_file_basename, - int channel_selector, + const std::vector &channel_selector, Concurrent_Queue *queue, bool digital_io_enabled); std::string generate_filename(); + int parse_header(); int getBit(uint8_t byte, int position); int read_ls3w_ini(const std::string &filename); int number_of_samples_per_ls3w_register() const; void decode_samples_one_channel(int16_t input_short, gr_complex *out, int type); - void decode_ls3w_register_one_channel(uint64_t input, gr_complex *out) const; + void decode_ls3w_register(uint64_t input, std::vector &out, std::size_t output_pointer) const; std::ifstream binary_input_file; std::string d_signal_file_basename; Concurrent_Queue *d_queue; - int d_channel_selector_config; + std::vector d_channel_selector_config; int d_current_file_number; uint8_t d_labsat_version; uint8_t d_channel_selector; @@ -90,6 +94,7 @@ private: // Data members for Labsat 3 Wideband std::string d_ls3w_OSC; + std::vector d_ls3w_selected_channel_offset; int64_t d_ls3w_SMP{}; int32_t d_ls3w_QUA{}; int32_t d_ls3w_CHN{}; @@ -101,7 +106,6 @@ private: int32_t d_ls3w_BWB{}; int32_t d_ls3w_BWC{}; int d_ls3w_spare_bits{}; - int d_ls3w_selected_channel_offset{}; int d_ls3w_samples_per_register{}; bool d_is_ls3w = false; bool d_ls3w_digital_io_enabled = false; From ac6d7feeabe5a6bf4069bb131eed7b847b2e357c Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 14 Mar 2021 22:17:22 +0100 Subject: [PATCH 06/23] Fix clang-format job --- src/algorithms/signal_source/gnuradio_blocks/labsat23_source.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.cc b/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.cc index d9cf1ff3c..386d617b2 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.cc +++ b/src/algorithms/signal_source/gnuradio_blocks/labsat23_source.cc @@ -928,7 +928,7 @@ int labsat23_source::general_work(int noutput_items, gr_vector_void_star &output_items) { std::vector out; - for (auto & output_item : output_items) + for (auto &output_item : output_items) { out.push_back(reinterpret_cast(output_item)); } From aeb6a0cd84116b278b9dfcb3a55b25c377d324a2 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Thu, 25 Mar 2021 14:53:18 +0100 Subject: [PATCH 07/23] Add Reed Solomon encoder and decoder class --- src/core/system_parameters/CMakeLists.txt | 2 + src/core/system_parameters/reed_solomon.cc | 834 ++++++++++++++++++ src/core/system_parameters/reed_solomon.h | 135 +++ src/tests/test_main.cc | 1 + .../galileo_e6b_reed_solomon_test.cc | 495 +++++++++++ 5 files changed, 1467 insertions(+) create mode 100644 src/core/system_parameters/reed_solomon.cc create mode 100644 src/core/system_parameters/reed_solomon.h create mode 100644 src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc diff --git a/src/core/system_parameters/CMakeLists.txt b/src/core/system_parameters/CMakeLists.txt index 224dbe361..c885892cc 100644 --- a/src/core/system_parameters/CMakeLists.txt +++ b/src/core/system_parameters/CMakeLists.txt @@ -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) diff --git a/src/core/system_parameters/reed_solomon.cc b/src/core/system_parameters/reed_solomon.cc new file mode 100644 index 000000000..d7d59d111 --- /dev/null +++ b/src/core/system_parameters/reed_solomon.cc @@ -0,0 +1,834 @@ +/*! + * \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 +#include + +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(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& genpoly_coeff, + const std::vector>& 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(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(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>(d_symbols_per_block, std::vector(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 = log_table[a]; + uint8_t y = log_table[b]; + uint8_t log_mult = (x + y) % d_symbols_per_block; + + return antilog[log_mult]; +} + + +void ReedSolomon::init_log_tables() +{ + 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++) + { + log_table[x] = i; + 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 ReedSolomon::encode_with_generator_matrix(const std::vector& data_to_encode) const +{ + std::vector 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 ReedSolomon::encode_with_generator_poly(const std::vector& data_to_encode) const +{ + std::vector 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& data_to_decode) const +{ + if (data_to_decode.size() != d_symbols_per_block) + { + std::cerr << "Reed Solomon error: bad input length\n"; + return -1; + } + std::vector decoded_data(d_data_in_block, 0); + int result = decode_rs_8(data_to_decode.data(), nullptr, 0, d_pad); + return result; +} + + +int ReedSolomon::decode_rs_8(uint8_t* data, int* eras_pos, int no_eras, int pad) const +{ + if (pad < 0 || pad > 222) + { + return -1; + } + int retval; + 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 + count = 0; + if (eras_pos != nullptr) + { + for (i = 0; i < count; i++) + { + eras_pos[i] = loc[i]; + } + } + retval = count; + return retval; + } + + 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(®[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 + count = -1; + if (eras_pos != nullptr) + { + for (i = 0; i < count; i++) + { + eras_pos[i] = loc[i]; + } + } + retval = count; + return retval; + } + + // 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])]; + } + } + + if (eras_pos != nullptr) + { + for (i = 0; i < count; i++) + { + eras_pos[i] = loc[i]; + } + } + retval = count; + + return retval; +} diff --git a/src/core/system_parameters/reed_solomon.h b/src/core/system_parameters/reed_solomon.h new file mode 100644 index 000000000..5dface4bd --- /dev/null +++ b/src/core/system_parameters/reed_solomon.h @@ -0,0 +1,135 @@ +/*! + * \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 +#include +#include +#include + + +/** \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& genpoly_coeff = std::vector{}, + const std::vector>& gen_matrix = std::vector>{}); + + /*! + * \brief Decode an encoded block. The decoded symbols are at the first + * 255-nroots elements of the input vector. Returns the number of corrected + * errors or -1 if decoding failed. + */ + int decode(std::vector& data_to_decode) const; + + /*! + * \brief Encode data with the generator matrix (for testing purposes) + */ + std::vector encode_with_generator_matrix(const std::vector& data_to_encode) const; + + /*! + * \brief Encode data with the generator polynomial (for testing purposes) + */ + std::vector encode_with_generator_poly(const std::vector& data_to_encode) const; + +private: + static const int d_symbols_per_block = 255; // the total number of symbols in a RS block. + + int decode_rs_8(uint8_t* data, int* eras_pos, int no_eras, int pad) const; + int mod255(int x) const; + int rs_min(int a, int b) 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(); + void init_alpha_tables(); + + std::array log_table{}; + std::array antilog{}; + std::array d_alpha_to{}; + std::array d_index_of{}; + std::vector> d_genmatrix; + std::vector d_genpoly_coeff; + std::vector d_genpoly_index; + + size_t d_data_in_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; + uint8_t d_a0; +}; + +/** \} */ +/** \} */ +#endif // GNSS_SDR_REED_SOLOMON_H diff --git a/src/tests/test_main.cc b/src/tests/test_main.cc index 8201dfdd6..724b08bf8 100644 --- a/src/tests/test_main.cc +++ b/src/tests/test_main.cc @@ -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" diff --git a/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc b/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc new file mode 100644 index 000000000..fa1824edf --- /dev/null +++ b/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc @@ -0,0 +1,495 @@ +/*! + * \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 + + +TEST(ReedSolomonTest, EncodeWithGenMatrix) +{ + // input vector as defined in Galileo HAS ICD v1.2, Annex D + const std::vector 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 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(); + std::vector 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 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 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(); + std::vector 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 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 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 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(nroots, minpoly, prim, fcr, pad, genpoly_coeff); + + std::vector 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 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 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 genpoly_coeff; + const std::vector> 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(nroots, minpoly, prim, fcr, pad, genpoly_coeff, genmatrix); + + std::vector encoded_output = rs->encode_with_generator_matrix(input); + + EXPECT_TRUE(encoded_output == expected_output); +} + + +TEST(ReedSolomonTest, DecodeNoErrors) +{ + const std::vector 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 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(); + + int result = rs->decode(encoded_input); + + EXPECT_TRUE(result == 0); + std::vector decoded(encoded_input.begin(), encoded_input.begin() + 32); + EXPECT_TRUE(expected_output == decoded); +} + + +TEST(ReedSolomonTest, Decode1Error) +{ + const std::vector 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 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; + int result = rs->decode(encoded_input); + + auto rs = std::make_unique(); + + EXPECT_TRUE(result == 1); + std::vector decoded(encoded_input.begin(), encoded_input.begin() + 32); + EXPECT_TRUE(expected_output == decoded); +} + + +TEST(ReedSolomonTest, Decode2Errors) +{ + const std::vector 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 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(); + + int result = rs->decode(encoded_input); + EXPECT_TRUE(result == 2); + std::vector decoded(encoded_input.begin(), encoded_input.begin() + 32); + EXPECT_TRUE(expected_output == decoded); +} + + +TEST(ReedSolomonTest, Decode16Errors) +{ + const std::vector 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 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(); + + int result = rs->decode(encoded_input); + EXPECT_TRUE(result == 16); + std::vector decoded(encoded_input.begin(), encoded_input.begin() + 32); + EXPECT_TRUE(expected_output == decoded); +} + + +TEST(ReedSolomonTest, Decode111Errors) +{ + const std::vector 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 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(); + + int result = rs->decode(encoded_input); + EXPECT_TRUE(result == 111); + std::vector decoded(encoded_input.begin(), encoded_input.begin() + 32); + EXPECT_TRUE(expected_output == decoded); +} + + +TEST(ReedSolomonTest, Decode111ErrorsCustomConstructor) +{ + const std::vector 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 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(nroots, minpoly, prim, fcr); + + int result = rs->decode(encoded_input); + + EXPECT_TRUE(result == 111); + std::vector decoded(encoded_input.begin(), encoded_input.begin() + 32); + EXPECT_TRUE(expected_output == decoded); +} From 4c51037b1429e23eeb7aa73636f0913abaab58f4 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Thu, 25 Mar 2021 19:12:05 +0100 Subject: [PATCH 08/23] Fix test --- .../system-parameters/galileo_e6b_reed_solomon_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc b/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc index fa1824edf..9520174fb 100644 --- a/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc +++ b/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc @@ -382,10 +382,10 @@ TEST(ReedSolomonTest, Decode1Error) // Introduce 1 error: encoded_input[23] = 0; - int result = rs->decode(encoded_input); auto rs = std::make_unique(); + int result = rs->decode(encoded_input); EXPECT_TRUE(result == 1); std::vector decoded(encoded_input.begin(), encoded_input.begin() + 32); EXPECT_TRUE(expected_output == decoded); From c67ac1247f378a0988d9df2e6bb3f4792bf31b76 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Fri, 26 Mar 2021 12:40:35 +0100 Subject: [PATCH 09/23] Add optional erasure positions to RS decoder --- src/core/system_parameters/reed_solomon.cc | 41 +++--------------- src/core/system_parameters/reed_solomon.h | 17 +++++--- .../galileo_e6b_reed_solomon_test.cc | 42 +++++++++++++++++++ 3 files changed, 60 insertions(+), 40 deletions(-) diff --git a/src/core/system_parameters/reed_solomon.cc b/src/core/system_parameters/reed_solomon.cc index d7d59d111..9324c6d68 100644 --- a/src/core/system_parameters/reed_solomon.cc +++ b/src/core/system_parameters/reed_solomon.cc @@ -540,26 +540,24 @@ void ReedSolomon::encode_rs_8(const uint8_t* data, uint8_t* parity) const } -int ReedSolomon::decode(std::vector& data_to_decode) const +int ReedSolomon::decode(std::vector& data_to_decode, const std::vector& erasure_positions) const { if (data_to_decode.size() != d_symbols_per_block) { std::cerr << "Reed Solomon error: bad input length\n"; return -1; } - std::vector decoded_data(d_data_in_block, 0); - int result = decode_rs_8(data_to_decode.data(), nullptr, 0, d_pad); + 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, int* eras_pos, int no_eras, int pad) const +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 retval; int deg_lambda; int el; int deg_omega; @@ -621,16 +619,7 @@ int ReedSolomon::decode_rs_8(uint8_t* data, int* eras_pos, int no_eras, int pad) { // if syndrome is zero, data[] is a codeword and there are no // errors to correct. So return data[] unmodified - count = 0; - if (eras_pos != nullptr) - { - for (i = 0; i < count; i++) - { - eras_pos[i] = loc[i]; - } - } - retval = count; - return retval; + return 0; } memset(&lambda[1], 0, d_nroots * sizeof(lambda[0])); @@ -761,16 +750,7 @@ int ReedSolomon::decode_rs_8(uint8_t* data, int* eras_pos, int no_eras, int pad) { // deg(lambda) unequal to number of roots => uncorrectable // error detected - count = -1; - if (eras_pos != nullptr) - { - for (i = 0; i < count; i++) - { - eras_pos[i] = loc[i]; - } - } - retval = count; - return retval; + return -1; } // Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo @@ -821,14 +801,5 @@ int ReedSolomon::decode_rs_8(uint8_t* data, int* eras_pos, int no_eras, int pad) } } - if (eras_pos != nullptr) - { - for (i = 0; i < count; i++) - { - eras_pos[i] = loc[i]; - } - } - retval = count; - - return retval; + return count; } diff --git a/src/core/system_parameters/reed_solomon.h b/src/core/system_parameters/reed_solomon.h index 5dface4bd..2b1d01dae 100644 --- a/src/core/system_parameters/reed_solomon.h +++ b/src/core/system_parameters/reed_solomon.h @@ -79,11 +79,18 @@ public: const std::vector>& gen_matrix = std::vector>{}); /*! - * \brief Decode an encoded block. The decoded symbols are at the first - * 255-nroots elements of the input vector. Returns the number of corrected - * errors or -1 if decoding failed. + * \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. + * + * Returns the number of corrected errors or -1 if decoding failed. */ - int decode(std::vector& data_to_decode) const; + int decode(std::vector& data_to_decode, + const std::vector& erasure_positions = std::vector{}) const; /*! * \brief Encode data with the generator matrix (for testing purposes) @@ -98,7 +105,7 @@ public: private: static const int d_symbols_per_block = 255; // the total number of symbols in a RS block. - int decode_rs_8(uint8_t* data, int* eras_pos, int no_eras, int pad) const; + int decode_rs_8(uint8_t* data, const int* eras_pos, int no_eras, int pad) const; int mod255(int x) const; int rs_min(int a, int b) const; diff --git a/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc b/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc index 9520174fb..a6ecad1ba 100644 --- a/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc +++ b/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc @@ -493,3 +493,45 @@ TEST(ReedSolomonTest, Decode111ErrorsCustomConstructor) std::vector decoded(encoded_input.begin(), encoded_input.begin() + 32); EXPECT_TRUE(expected_output == decoded); } + + +TEST(ReedSolomonTest, Decode112Errors) +{ + std::vector 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(); + + int result = rs->decode(encoded_input); + EXPECT_TRUE(result == -1); +} + + +TEST(ReedSolomonTest, Decode113ErrorsWithErasure) +{ + std::vector 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 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 erasure_positions{2, 4, 16, 18, 22, 54}; + + auto rs = std::make_unique(); + + int result = rs->decode(encoded_input, erasure_positions); + EXPECT_TRUE(result == 113); + std::vector decoded(encoded_input.begin(), encoded_input.begin() + 32); + EXPECT_TRUE(expected_output == decoded); +} From e262dfc31573b68b121d7bbda2e133677bbba1e8 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 28 Mar 2021 13:07:50 +0200 Subject: [PATCH 10/23] Implement HAS message page decoding Signed-off-by: Carles Fernandez --- .../PVT/gnuradio_blocks/rtklib_pvt_gs.cc | 6 + .../PVT/gnuradio_blocks/rtklib_pvt_gs.h | 1 + .../galileo_telemetry_decoder_gs.cc | 13 +- src/core/system_parameters/Galileo_CNAV.h | 8 +- .../system_parameters/galileo_cnav_message.cc | 139 ++++++++++++++---- .../system_parameters/galileo_cnav_message.h | 15 +- src/core/system_parameters/reed_solomon.cc | 12 +- src/core/system_parameters/reed_solomon.h | 33 +++-- 8 files changed, 162 insertions(+), 65 deletions(-) diff --git a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc index a89dda829..9914c0f28 100644 --- a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc +++ b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.cc @@ -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).hash_code(); d_galileo_almanac_helper_sptr_type_hash_code = typeid(std::shared_ptr).hash_code(); d_galileo_almanac_sptr_type_hash_code = typeid(std::shared_ptr).hash_code(); + d_galileo_has_message_sptr_type_hash_code = typeid(std::shared_ptr).hash_code(); d_glonass_gnav_ephemeris_sptr_type_hash_code = typeid(std::shared_ptr).hash_code(); d_glonass_gnav_utc_model_sptr_type_hash_code = typeid(std::shared_ptr).hash_code(); d_glonass_gnav_almanac_sptr_type_hash_code = typeid(std::shared_ptr).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) diff --git a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h index ae8a73b3f..4e6fd3144 100644 --- a/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h +++ b/src/algorithms/PVT/gnuradio_blocks/rtklib_pvt_gs.h @@ -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; diff --git a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc index ad6161b21..667cc0f8e 100644 --- a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc +++ b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc @@ -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 tmp_obj = std::make_shared(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 } } diff --git a/src/core/system_parameters/Galileo_CNAV.h b/src/core/system_parameters/Galileo_CNAV.h index 2a5480977..52110f2a1 100644 --- a/src/core/system_parameters/Galileo_CNAV.h +++ b/src/core/system_parameters/Galileo_CNAV.h @@ -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"; diff --git a/src/core/system_parameters/galileo_cnav_message.cc b/src/core/system_parameters/galileo_cnav_message.cc index f1005a411..8a5abf3c5 100644 --- a/src/core/system_parameters/galileo_cnav_message.cc +++ b/src/core/system_parameters/galileo_cnav_message.cc @@ -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 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(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(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 erasure_positions; + erasure_positions.reserve(GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK - d_list_pid.size()); + for (uint8_t mpid = 1; mpid <= GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK; mpid++) + { + if (std::find(d_list_pid.begin(), d_list_pid.end(), mpid) == d_list_pid.end()) + { + erasure_positions.push_back(mpid - 1); + } + else + { + d_list_pid.remove(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 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(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)}; + d_M_matrix = {GALILEO_CNAV_INFORMATION_VECTOR_LENGTH, std::vector(GALILEO_CNAV_OCTETS_IN_SUBPAGE)}; + return -1; + } + + std::vector 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(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)}; + d_M_matrix = {GALILEO_CNAV_INFORMATION_VECTOR_LENGTH, std::vector(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 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) diff --git a/src/core/system_parameters/galileo_cnav_message.h b/src/core/system_parameters/galileo_cnav_message.h index 5913b3b6d..c557185f2 100644 --- a/src/core/system_parameters/galileo_cnav_message.h +++ b/src/core/system_parameters/galileo_cnav_message.h @@ -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 #include #include #include +#include /** \addtogroup Core * \{ */ @@ -68,23 +70,24 @@ private: bool CRC_test(std::bitset 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 bits, const std::pair& parameter) const; uint8_t read_has_message_header_parameter_uint8(std::bitset bits, const std::pair& parameter) const; uint16_t read_has_message_header_parameter_uint16(std::bitset bits, const std::pair& parameter) const; bool read_has_message_header_parameter_bool(std::bitset bits, const std::pair& 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> d_C_matrix{GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK, std::vector(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)}; // 255 x 53 + std::vector> d_M_matrix{GALILEO_CNAV_INFORMATION_VECTOR_LENGTH, std::vector(GALILEO_CNAV_OCTETS_IN_SUBPAGE, 0)}; // 32 x 53 std::list d_list_pid; uint8_t d_has_page_status{}; diff --git a/src/core/system_parameters/reed_solomon.cc b/src/core/system_parameters/reed_solomon.cc index 9324c6d68..c75c5baf0 100644 --- a/src/core/system_parameters/reed_solomon.cc +++ b/src/core/system_parameters/reed_solomon.cc @@ -409,21 +409,21 @@ uint8_t ReedSolomon::galois_mul_table(uint8_t a, uint8_t b) const return 0; } - uint8_t x = log_table[a]; - uint8_t y = log_table[b]; + 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 antilog[log_mult]; + return d_antilog[log_mult]; } void ReedSolomon::init_log_tables() { - log_table[0] = 0; // dummy value + 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++) { - log_table[x] = i; - antilog[i] = x; + d_log_table[x] = i; + d_antilog[i] = x; } } diff --git a/src/core/system_parameters/reed_solomon.h b/src/core/system_parameters/reed_solomon.h index 2b1d01dae..24a4f6ad3 100644 --- a/src/core/system_parameters/reed_solomon.h +++ b/src/core/system_parameters/reed_solomon.h @@ -85,7 +85,7 @@ public: * of the input vector. * * The second parameter is optional, and contains a vector of erasure - * positions to be passed to the decoding algorithm. + * positions to be passed to the decoding algorithm. Defaults to empty. * * Returns the number of corrected errors or -1 if decoding failed. */ @@ -105,36 +105,37 @@ public: private: static const int d_symbols_per_block = 255; // the total number of symbols in a RS block. - int decode_rs_8(uint8_t* data, const int* eras_pos, int no_eras, int pad) const; 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(); - void init_alpha_tables(); + void init_log_tables(); // initialize d_log_table and d_antilog + void init_alpha_tables(); // initialize d_alpha_to, d_index_of - std::array log_table{}; - std::array antilog{}; - std::array d_alpha_to{}; - std::array d_index_of{}; - std::vector> d_genmatrix; - std::vector d_genpoly_coeff; - std::vector d_genpoly_index; + std::array d_alpha_to{}; // used for decoding + std::array d_index_of{}; // used for decoding + std::array d_log_table{}; // used for encoding + std::array d_antilog{}; // used for encoding - size_t d_data_in_block; + std::vector> d_genmatrix; // used for encoding + std::vector d_genpoly_coeff; // used for encoding + std::vector 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_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; - uint8_t d_a0; + uint8_t d_min_poly; // primitive polynomial + uint8_t d_a0; // auxiliar variable }; /** \} */ From 59e8049fe6a332cc90732c4a9e6b66f39c6d8e46 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 28 Mar 2021 18:15:54 +0200 Subject: [PATCH 11/23] Update Contributor Covenant badge --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94fe9b7c6..fe445da66 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ SPDX-License-Identifier: GPL-3.0-or-later ) [comment]: # ( -SPDX-FileCopyrightText: 2011-2020 Carles Fernandez-Prades +SPDX-FileCopyrightText: 2011-2021 Carles Fernandez-Prades ) @@ -12,7 +12,7 @@ SPDX-FileCopyrightText: 2011-2020 Carles Fernandez-Prades Date: Sun, 28 Mar 2021 18:16:29 +0200 Subject: [PATCH 12/23] Update local Matio version to 1.5.21 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e1c7e37d..2b1ccd5b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -328,7 +328,7 @@ set(GNSSSDR_ARMADILLO_LOCAL_VERSION "10.3.x") set(GNSSSDR_GTEST_LOCAL_VERSION "1.10.0") set(GNSSSDR_GNSS_SIM_LOCAL_VERSION "master") set(GNSSSDR_GPSTK_LOCAL_VERSION "8.0.0") -set(GNSSSDR_MATIO_LOCAL_VERSION "1.5.19") +set(GNSSSDR_MATIO_LOCAL_VERSION "1.5.21") set(GNSSSDR_PUGIXML_LOCAL_VERSION "1.11.4") set(GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION "3.15.6") set(GNSSSDR_BENCHMARK_LOCAL_VERSION "1.5.2") From 846fc0b15a60e435ce417119ff2cc65b7ff901dc Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 28 Mar 2021 19:16:11 +0200 Subject: [PATCH 13/23] Always initialize members --- src/core/system_parameters/reed_solomon.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/system_parameters/reed_solomon.h b/src/core/system_parameters/reed_solomon.h index 24a4f6ad3..977ea53e3 100644 --- a/src/core/system_parameters/reed_solomon.h +++ b/src/core/system_parameters/reed_solomon.h @@ -126,16 +126,16 @@ private: std::vector d_genpoly_coeff; // used for encoding std::vector d_genpoly_index; // used for encoding - size_t d_data_in_block; // number of information symbols in a block + 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 + 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 + uint8_t d_min_poly{}; // primitive polynomial + uint8_t d_a0{}; // auxiliar variable }; /** \} */ From 455178954de8b7298ea7329a5a780f756e352ac8 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 28 Mar 2021 19:18:05 +0200 Subject: [PATCH 14/23] Fix loop variable size --- src/core/system_parameters/galileo_cnav_message.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/system_parameters/galileo_cnav_message.cc b/src/core/system_parameters/galileo_cnav_message.cc index 8a5abf3c5..5d869a0d5 100644 --- a/src/core/system_parameters/galileo_cnav_message.cc +++ b/src/core/system_parameters/galileo_cnav_message.cc @@ -195,15 +195,15 @@ int Galileo_Cnav_Message::decode_message_type1() std::vector erasure_positions; erasure_positions.reserve(GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK - d_list_pid.size()); - for (uint8_t mpid = 1; mpid <= GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK; mpid++) + for (int mpid = 1; mpid <= GALILEO_CNAV_MAX_NUMBER_SYMBOLS_ENCODED_BLOCK; mpid++) { - if (std::find(d_list_pid.begin(), d_list_pid.end(), mpid) == d_list_pid.end()) + if (std::find(d_list_pid.begin(), d_list_pid.end(), static_cast(mpid)) == d_list_pid.end()) { - erasure_positions.push_back(mpid - 1); + erasure_positions.push_back(static_cast(mpid - 1)); } else { - d_list_pid.remove(mpid); + d_list_pid.remove(static_cast(mpid)); } } From 9ee57dca5913a807d2fdc84cdacdc0605b2ec1a4 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 28 Mar 2021 19:38:21 +0200 Subject: [PATCH 15/23] CI: Fix build-macos job --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9ad93776a..cf35260a0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,7 +26,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: install dependencies - run: brew update && brew install ninja pkg-config hdf5 libtool automake armadillo lapack gflags glog gnuradio log4cpp pugixml protobuf && pip3 install mako + run: brew update && brew install ninja pkg-config hdf5 automake armadillo lapack gflags glog gnuradio log4cpp pugixml protobuf && pip3 install mako - name: configure run: cd build && cmake -GNinja .. - name: build From eb68c41074af04ab4461b8cecd0f5b6e54f350a1 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 28 Mar 2021 23:37:29 +0200 Subject: [PATCH 16/23] Fix parameters in example, remove irrelevant test --- .../galileo_e6b_reed_solomon_test.cc | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc b/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc index a6ecad1ba..9dd5e7955 100644 --- a/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc +++ b/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc @@ -63,8 +63,8 @@ TEST(ReedSolomonTest, EncodeWithCustomPoly) int nroots = 223; int minpoly = 29; - int prim = 127; - int fcr = 209; + int prim = 1; + int fcr = 1; int pad = 0; const std::vector 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}; @@ -392,26 +392,6 @@ TEST(ReedSolomonTest, Decode1Error) } -TEST(ReedSolomonTest, Decode2Errors) -{ - const std::vector 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 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(); - - int result = rs->decode(encoded_input); - EXPECT_TRUE(result == 2); - std::vector decoded(encoded_input.begin(), encoded_input.begin() + 32); - EXPECT_TRUE(expected_output == decoded); -} - - TEST(ReedSolomonTest, Decode16Errors) { const std::vector 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}; From 149c26b971070110725a68ecb2f5cd34defbc5c3 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Thu, 1 Apr 2021 10:22:24 +0200 Subject: [PATCH 17/23] Expand ReedSolomon class to shortened codes. Add conf parameters for Galileo E1B RS encoding/decoding and the corresponding unit tests --- src/core/system_parameters/reed_solomon.cc | 823 +++++++++++------- src/core/system_parameters/reed_solomon.h | 61 +- src/tests/test_main.cc | 1 + .../galileo_e1b_reed_solomon_test.cc | 310 +++++++ .../galileo_e6b_reed_solomon_test.cc | 54 +- 5 files changed, 900 insertions(+), 349 deletions(-) create mode 100644 src/tests/unit-tests/system-parameters/galileo_e1b_reed_solomon_test.cc diff --git a/src/core/system_parameters/reed_solomon.cc b/src/core/system_parameters/reed_solomon.cc index c75c5baf0..db5cffffa 100644 --- a/src/core/system_parameters/reed_solomon.cc +++ b/src/core/system_parameters/reed_solomon.cc @@ -1,6 +1,7 @@ /*! * \file reed_solomon.cc - * \brief Class implementing a Reed-Solomon encoder/decoder + * \brief Class implementing a Reed-Solomon encoder/decoder for blocks of 255 + * symbols and symbols of 8 bits. * \author Carles Fernandez, 2021. cfernandez(at)cttc.es * * ----------------------------------------------------------------------------- @@ -15,278 +16,438 @@ */ #include "reed_solomon.h" +#include #include #include -ReedSolomon::ReedSolomon() +ReedSolomon::ReedSolomon(const std::string& gnss_signal) { - 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. + if (gnss_signal.empty() or gnss_signal == "E6B") + { + 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_shortening = 0; // shortening parameter + d_rows_G = 255; // rows of generator matrix + d_columns_G = 32; // columns of generator matrix + + 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}; + } + + if (gnss_signal == "E1B") + { + d_nroots = 60; + d_min_poly = 29; + d_prim = 1; + d_fcr = 195; + d_pad = 0; + d_shortening = 137; + d_rows_G = 118; + d_columns_G = 58; + + 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0}, + {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, 0}, + {0, 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}, + {0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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}, + {193, 146, 156, 183, 62, 35, 90, 25, 89, 208, 43, 148, 117, 199, 92, 112, 4, 70, 74, 183, 37, 100, 118, 44, 238, 107, 186, 200, 179, 63, 140, 194, 176, 131, 38, 110, 33, 178, 43, 173, 98, 96, 105, 115, 90, 76, 133, 194, 133, 182, 99, 20, 183, 90, 84, 131, 26, 139}, + {91, 221, 65, 59, 102, 79, 66, 118, 29, 43, 164, 253, 13, 228, 108, 106, 252, 230, 45, 237, 12, 149, 152, 235, 143, 178, 102, 199, 227, 65, 230, 178, 140, 133, 93, 213, 89, 41, 198, 197, 215, 94, 122, 58, 18, 251, 179, 187, 61, 1, 239, 229, 179, 214, 244, 97, 202, 42}, + {190, 173, 125, 234, 225, 164, 122, 176, 130, 152, 32, 205, 41, 44, 78, 230, 128, 101, 92, 134, 176, 227, 128, 184, 180, 130, 142, 165, 134, 141, 110, 153, 50, 65, 25, 14, 98, 172, 34, 94, 181, 210, 38, 49, 15, 55, 169, 204, 233, 34, 197, 138, 78, 134, 181, 57, 82, 206}, + {154, 48, 226, 109, 5, 179, 159, 79, 237, 32, 199, 98, 33, 127, 219, 8, 110, 36, 68, 76, 24, 100, 105, 49, 245, 131, 53, 59, 61, 75, 216, 146, 103, 132, 185, 132, 24, 216, 243, 97, 70, 233, 161, 6, 10, 226, 77, 85, 182, 219, 24, 87, 154, 117, 124, 3, 106, 233}, + {101, 12, 238, 213, 120, 140, 44, 51, 196, 68, 201, 121, 36, 108, 200, 161, 56, 97, 123, 115, 237, 146, 54, 164, 141, 59, 232, 60, 60, 36, 85, 169, 129, 61, 49, 75, 21, 19, 49, 124, 195, 252, 63, 207, 153, 125, 144, 60, 39, 141, 117, 232, 96, 5, 162, 38, 187, 80}, + {58, 136, 130, 2, 173, 144, 135, 249, 194, 161, 38, 205, 85, 247, 142, 247, 228, 245, 99, 151, 114, 158, 68, 235, 185, 203, 78, 14, 149, 18, 163, 149, 250, 51, 198, 100, 15, 234, 113, 242, 230, 245, 22, 250, 196, 114, 38, 80, 103, 157, 65, 57, 4, 107, 109, 16, 9, 131}, + {231, 109, 52, 176, 87, 22, 48, 253, 54, 106, 47, 175, 119, 108, 137, 133, 99, 143, 63, 81, 242, 135, 75, 49, 22, 130, 125, 185, 168, 229, 196, 43, 92, 226, 57, 76, 149, 140, 100, 86, 217, 135, 132, 114, 90, 208, 180, 174, 150, 112, 147, 191, 11, 164, 32, 117, 5, 36}, + {197, 18, 114, 255, 50, 100, 185, 124, 218, 88, 190, 215, 41, 191, 206, 138, 120, 5, 243, 244, 111, 223, 137, 234, 221, 169, 43, 39, 143, 82, 41, 137, 149, 2, 84, 3, 143, 219, 88, 107, 118, 9, 200, 15, 221, 43, 131, 249, 253, 37, 40, 189, 116, 164, 225, 126, 177, 188}, + {152, 246, 146, 92, 78, 104, 171, 40, 30, 39, 246, 202, 204, 246, 55, 63, 240, 204, 63, 221, 233, 17, 105, 240, 166, 128, 153, 215, 115, 174, 39, 28, 112, 195, 178, 227, 100, 75, 117, 74, 82, 114, 105, 148, 192, 160, 58, 182, 232, 67, 140, 167, 147, 187, 216, 183, 66, 19}, + {88, 233, 3, 20, 252, 51, 135, 113, 130, 146, 84, 13, 81, 30, 147, 99, 56, 10, 63, 185, 42, 166, 207, 88, 220, 165, 199, 7, 86, 27, 71, 58, 216, 213, 113, 126, 19, 45, 56, 108, 143, 26, 252, 120, 123, 185, 143, 39, 153, 166, 78, 151, 33, 124, 208, 125, 40, 233}, + {73, 120, 195, 119, 81, 197, 85, 70, 175, 109, 204, 25, 136, 117, 21, 195, 222, 74, 162, 75, 237, 9, 155, 148, 242, 157, 99, 129, 206, 123, 255, 182, 75, 94, 57, 229, 151, 178, 115, 53, 34, 17, 139, 20, 30, 190, 82, 126, 204, 133, 128, 61, 227, 71, 16, 86, 4, 201}, + {62, 195, 117, 83, 179, 169, 59, 213, 201, 116, 240, 164, 198, 84, 105, 89, 127, 51, 126, 50, 81, 215, 167, 203, 139, 96, 251, 23, 173, 37, 188, 176, 235, 36, 53, 56, 67, 148, 47, 18, 237, 164, 221, 182, 234, 200, 51, 29, 243, 115, 114, 246, 173, 29, 62, 127, 167, 14}, + {169, 67, 240, 110, 142, 45, 20, 35, 20, 206, 92, 117, 106, 151, 17, 35, 2, 94, 255, 101, 84, 49, 101, 168, 17, 122, 73, 59, 87, 175, 103, 105, 140, 56, 62, 64, 5, 220, 188, 80, 138, 214, 246, 235, 11, 222, 227, 230, 54, 55, 52, 88, 237, 16, 238, 237, 27, 194}, + {88, 216, 182, 118, 206, 243, 194, 206, 137, 152, 189, 167, 238, 184, 242, 69, 36, 248, 173, 121, 146, 27, 239, 84, 132, 18, 61, 215, 186, 63, 70, 122, 173, 41, 138, 242, 176, 76, 175, 165, 149, 194, 88, 231, 4, 114, 241, 254, 201, 120, 58, 47, 222, 2, 123, 75, 114, 176}, + {188, 19, 183, 35, 242, 4, 50, 148, 5, 83, 98, 226, 132, 70, 109, 207, 93, 173, 89, 56, 167, 215, 50, 7, 10, 227, 169, 183, 90, 56, 48, 240, 253, 128, 253, 243, 52, 59, 182, 102, 244, 200, 169, 111, 38, 177, 75, 71, 199, 90, 47, 66, 186, 31, 231, 86, 23, 111}, + {23, 55, 128, 7, 36, 174, 140, 216, 161, 135, 112, 142, 248, 244, 169, 173, 126, 229, 219, 233, 3, 40, 112, 29, 130, 246, 246, 170, 182, 54, 85, 154, 76, 227, 6, 219, 121, 94, 24, 81, 142, 202, 226, 212, 231, 55, 200, 225, 62, 28, 217, 96, 242, 50, 143, 249, 1, 118}, + {36, 111, 216, 59, 50, 180, 160, 75, 113, 134, 211, 91, 182, 143, 169, 101, 207, 67, 126, 96, 42, 56, 183, 225, 15, 184, 125, 128, 115, 21, 76, 214, 228, 55, 135, 200, 122, 54, 10, 195, 57, 215, 193, 137, 218, 47, 31, 75, 201, 19, 226, 46, 219, 252, 171, 244, 153, 190}, + {202, 21, 206, 182, 17, 61, 151, 229, 64, 62, 73, 178, 215, 44, 252, 93, 5, 209, 253, 16, 63, 35, 156, 48, 132, 142, 110, 111, 142, 65, 41, 174, 240, 80, 64, 126, 247, 108, 249, 9, 154, 80, 102, 29, 170, 52, 203, 253, 175, 191, 82, 31, 64, 248, 79, 31, 153, 237}, + {63, 144, 241, 65, 93, 237, 185, 69, 251, 58, 85, 58, 152, 193, 129, 101, 152, 224, 102, 114, 197, 80, 147, 168, 161, 35, 37, 176, 37, 19, 150, 140, 126, 239, 31, 84, 110, 203, 7, 62, 79, 48, 27, 101, 153, 52, 2, 110, 203, 86, 143, 164, 144, 196, 194, 80, 87, 17}, + {20, 217, 37, 96, 55, 13, 235, 165, 222, 135, 30, 148, 34, 171, 224, 35, 95, 56, 14, 247, 5, 181, 213, 16, 162, 2, 124, 197, 27, 211, 78, 31, 186, 240, 5, 6, 25, 69, 239, 23, 105, 5, 142, 36, 99, 80, 157, 139, 199, 218, 129, 93, 53, 150, 145, 76, 209, 173}, + {102, 239, 33, 51, 4, 178, 28, 177, 128, 137, 105, 141, 208, 44, 210, 248, 152, 72, 255, 24, 26, 112, 197, 180, 35, 51, 35, 150, 104, 152, 253, 28, 134, 112, 41, 200, 80, 83, 171, 151, 10, 167, 71, 162, 53, 204, 242, 207, 41, 54, 32, 236, 75, 36, 132, 91, 34, 112}, + {230, 225, 186, 12, 73, 187, 104, 52, 239, 137, 241, 251, 194, 35, 227, 12, 21, 251, 1, 210, 178, 186, 187, 212, 199, 45, 72, 126, 86, 110, 94, 159, 88, 238, 94, 182, 143, 236, 43, 42, 34, 71, 81, 29, 120, 105, 177, 144, 178, 120, 255, 94, 193, 145, 44, 236, 247, 141}, + {131, 247, 106, 160, 99, 127, 254, 236, 225, 79, 24, 221, 242, 124, 91, 71, 209, 121, 237, 27, 217, 152, 35, 103, 27, 7, 61, 28, 185, 73, 182, 77, 200, 106, 117, 51, 96, 56, 125, 37, 61, 213, 103, 101, 88, 83, 102, 162, 159, 216, 31, 113, 68, 132, 78, 30, 248, 98}, + {141, 134, 43, 202, 72, 109, 204, 33, 132, 193, 51, 182, 43, 212, 100, 221, 126, 205, 46, 77, 190, 130, 181, 189, 175, 208, 165, 139, 133, 24, 113, 224, 15, 96, 20, 206, 175, 176, 68, 217, 213, 95, 140, 58, 214, 164, 80, 48, 161, 118, 97, 194, 209, 247, 238, 230, 26, 34}, + {214, 148, 137, 44, 243, 55, 191, 63, 77, 214, 201, 75, 217, 156, 103, 212, 104, 128, 241, 41, 83, 85, 83, 182, 214, 56, 127, 110, 57, 214, 249, 25, 236, 146, 192, 92, 101, 119, 184, 14, 83, 139, 28, 130, 232, 139, 88, 56, 204, 204, 150, 58, 197, 3, 51, 115, 171, 240}, + {45, 176, 137, 213, 83, 71, 180, 128, 251, 170, 151, 33, 125, 122, 145, 152, 32, 29, 244, 173, 19, 175, 36, 161, 46, 108, 88, 154, 198, 123, 60, 147, 246, 64, 239, 179, 146, 240, 54, 156, 124, 91, 75, 164, 1, 18, 169, 50, 26, 173, 131, 149, 102, 70, 251, 159, 11, 242}, + {101, 187, 110, 190, 192, 218, 216, 24, 11, 82, 67, 41, 103, 48, 205, 235, 168, 47, 66, 195, 12, 153, 253, 233, 29, 224, 7, 81, 157, 223, 101, 77, 128, 172, 245, 29, 34, 153, 25, 185, 62, 198, 141, 37, 59, 118, 96, 216, 64, 33, 3, 115, 162, 249, 145, 161, 39, 49}, + {94, 206, 2, 170, 252, 165, 52, 243, 243, 102, 47, 226, 182, 51, 212, 93, 231, 98, 241, 134, 172, 160, 3, 137, 198, 160, 51, 66, 153, 220, 86, 62, 128, 24, 198, 71, 126, 233, 228, 243, 31, 158, 125, 24, 203, 239, 228, 59, 74, 135, 132, 63, 183, 76, 5, 9, 143, 167}, + {62, 212, 195, 146, 110, 4, 91, 180, 124, 40, 251, 71, 61, 106, 47, 152, 225, 10, 86, 97, 156, 150, 14, 83, 150, 84, 198, 71, 110, 114, 27, 25, 99, 239, 115, 199, 225, 125, 116, 133, 43, 153, 82, 64, 230, 29, 98, 171, 182, 245, 112, 242, 175, 73, 53, 106, 248, 133}, + {65, 208, 229, 79, 178, 119, 149, 48, 150, 242, 149, 110, 57, 7, 128, 153, 202, 211, 206, 218, 3, 45, 91, 82, 253, 98, 180, 185, 153, 212, 22, 233, 116, 66, 62, 79, 247, 165, 192, 32, 79, 200, 68, 87, 209, 89, 71, 144, 241, 160, 165, 119, 126, 62, 7, 20, 178, 125}, + {66, 194, 199, 72, 30, 167, 104, 8, 188, 230, 72, 45, 18, 64, 35, 84, 64, 224, 143, 99, 244, 77, 194, 171, 115, 86, 200, 40, 205, 185, 128, 199, 14, 197, 255, 61, 184, 242, 31, 99, 85, 216, 129, 3, 72, 182, 211, 150, 26, 45, 164, 63, 218, 97, 181, 182, 26, 172}, + {46, 73, 249, 186, 70, 166, 170, 161, 98, 165, 160, 141, 25, 242, 131, 190, 43, 45, 12, 242, 21, 247, 30, 156, 188, 150, 124, 206, 42, 21, 99, 201, 162, 50, 212, 179, 52, 108, 180, 204, 174, 41, 140, 123, 14, 106, 68, 154, 100, 177, 54, 58, 66, 215, 92, 137, 24, 216}, + {131, 63, 194, 227, 213, 112, 227, 46, 116, 194, 52, 140, 132, 167, 138, 39, 99, 71, 59, 22, 249, 63, 110, 194, 83, 124, 134, 40, 9, 53, 205, 112, 158, 144, 169, 185, 101, 131, 253, 186, 219, 89, 9, 184, 62, 37, 101, 87, 149, 14, 214, 184, 32, 7, 8, 110, 157, 141}, + {42, 239, 135, 195, 81, 125, 136, 127, 58, 211, 123, 157, 75, 107, 154, 100, 161, 46, 157, 58, 123, 179, 20, 189, 215, 98, 69, 18, 175, 20, 175, 11, 27, 127, 17, 177, 82, 77, 58, 140, 53, 23, 43, 11, 64, 33, 1, 163, 115, 59, 46, 114, 185, 216, 43, 233, 30, 109}, + {187, 107, 37, 79, 138, 135, 199, 125, 100, 160, 209, 101, 79, 175, 142, 182, 14, 16, 33, 85, 179, 136, 192, 221, 110, 59, 128, 190, 176, 114, 107, 181, 8, 123, 87, 3, 82, 100, 79, 166, 32, 172, 206, 166, 177, 16, 30, 27, 156, 47, 3, 241, 186, 3, 233, 75, 189, 170}, + {9, 165, 153, 194, 5, 174, 10, 63, 16, 164, 181, 18, 107, 155, 255, 189, 32, 70, 255, 198, 172, 116, 102, 163, 87, 238, 208, 19, 207, 81, 226, 130, 36, 223, 98, 234, 108, 136, 113, 176, 188, 113, 103, 29, 43, 131, 26, 247, 17, 208, 158, 247, 22, 55, 98, 62, 83, 91}, + {122, 218, 243, 230, 181, 240, 78, 230, 125, 90, 141, 63, 181, 72, 34, 163, 211, 215, 3, 128, 106, 59, 113, 131, 158, 208, 182, 227, 2, 45, 162, 58, 49, 199, 209, 30, 40, 232, 161, 64, 126, 69, 193, 153, 253, 55, 57, 194, 77, 251, 139, 85, 136, 246, 82, 129, 112, 108}, + {3, 23, 252, 210, 151, 185, 126, 184, 72, 131, 93, 160, 61, 246, 134, 64, 40, 203, 79, 34, 204, 149, 25, 221, 12, 193, 154, 85, 73, 152, 29, 129, 176, 161, 171, 238, 217, 105, 239, 86, 255, 10, 209, 131, 23, 37, 231, 26, 18, 135, 175, 22, 116, 6, 51, 194, 87, 235}, + {19, 239, 7, 48, 105, 219, 196, 193, 243, 116, 95, 221, 73, 66, 231, 53, 8, 176, 139, 131, 2, 10, 74, 187, 90, 36, 199, 11, 209, 224, 181, 56, 51, 115, 183, 217, 134, 79, 181, 170, 252, 113, 6, 84, 254, 59, 138, 194, 181, 204, 63, 218, 218, 9, 135, 240, 75, 4}, + {118, 105, 33, 252, 158, 172, 57, 215, 1, 102, 65, 97, 114, 123, 228, 46, 77, 159, 175, 112, 2, 78, 135, 37, 128, 188, 73, 95, 136, 97, 211, 225, 153, 170, 208, 55, 32, 27, 122, 127, 162, 200, 171, 237, 182, 165, 230, 222, 31, 80, 218, 186, 33, 56, 95, 30, 193, 34}, + {6, 172, 37, 99, 30, 134, 173, 200, 150, 224, 104, 27, 101, 244, 250, 32, 37, 125, 178, 237, 232, 225, 10, 194, 38, 62, 40, 146, 22, 161, 1, 149, 232, 164, 114, 174, 164, 162, 21, 137, 28, 74, 253, 47, 236, 27, 24, 160, 99, 150, 248, 253, 248, 32, 175, 98, 175, 234}, + {154, 136, 227, 53, 140, 76, 189, 152, 149, 52, 191, 42, 247, 51, 3, 188, 168, 129, 92, 162, 115, 60, 107, 187, 143, 17, 137, 157, 10, 219, 244, 253, 107, 94, 92, 239, 184, 30, 253, 86, 145, 64, 57, 221, 20, 1, 97, 228, 218, 81, 172, 106, 237, 195, 218, 25, 49, 20}, + {14, 142, 223, 89, 178, 180, 186, 230, 37, 21, 217, 61, 213, 111, 83, 33, 88, 216, 192, 230, 12, 2, 136, 41, 188, 132, 163, 107, 195, 180, 59, 177, 232, 241, 187, 138, 165, 223, 243, 87, 177, 4, 57, 80, 43, 195, 13, 36, 232, 89, 143, 255, 208, 27, 118, 64, 9, 126}, + {193, 156, 18, 104, 103, 145, 238, 163, 191, 245, 62, 77, 72, 18, 51, 35, 37, 30, 146, 119, 195, 104, 116, 164, 199, 215, 62, 107, 216, 252, 56, 249, 1, 107, 215, 213, 171, 23, 244, 94, 53, 209, 109, 74, 10, 103, 70, 207, 161, 94, 58, 155, 72, 138, 79, 245, 90, 130}, + {79, 59, 250, 36, 207, 70, 246, 222, 60, 177, 165, 41, 204, 234, 152, 167, 149, 103, 155, 164, 187, 3, 17, 106, 13, 56, 133, 163, 235, 220, 120, 143, 129, 186, 95, 61, 175, 136, 71, 10, 115, 67, 117, 1, 45, 98, 49, 241, 153, 52, 208, 110, 173, 47, 113, 244, 61, 144}, + {251, 48, 41, 161, 98, 4, 23, 58, 118, 137, 248, 53, 112, 216, 84, 215, 230, 30, 47, 192, 128, 172, 163, 224, 153, 32, 247, 171, 185, 250, 215, 122, 108, 176, 103, 100, 88, 226, 193, 41, 242, 37, 192, 195, 80, 138, 188, 51, 47, 149, 155, 136, 53, 252, 19, 64, 193, 142}, + {124, 242, 42, 20, 52, 143, 229, 10, 57, 221, 174, 40, 150, 213, 224, 204, 178, 33, 118, 18, 244, 244, 237, 3, 222, 160, 236, 31, 243, 114, 105, 73, 192, 178, 102, 101, 53, 94, 197, 3, 132, 227, 160, 186, 34, 225, 141, 34, 52, 76, 102, 119, 181, 212, 14, 205, 191, 213}, + {18, 64, 11, 249, 128, 124, 136, 8, 208, 164, 247, 53, 52, 35, 117, 134, 253, 34, 226, 165, 253, 103, 53, 43, 10, 195, 107, 215, 253, 44, 79, 166, 118, 115, 128, 1, 187, 156, 116, 38, 55, 38, 104, 27, 189, 70, 245, 66, 54, 172, 51, 147, 164, 178, 22, 189, 253, 110}, + {186, 131, 99, 220, 159, 82, 188, 47, 130, 235, 80, 242, 18, 26, 119, 140, 149, 68, 174, 53, 227, 91, 10, 152, 118, 211, 236, 58, 19, 86, 67, 191, 40, 102, 127, 135, 84, 178, 104, 78, 20, 130, 120, 219, 219, 165, 194, 5, 198, 44, 83, 108, 68, 100, 192, 6, 80, 203}, + {244, 1, 47, 157, 50, 104, 143, 121, 171, 22, 185, 89, 161, 84, 117, 207, 80, 102, 206, 80, 112, 208, 81, 221, 226, 101, 99, 138, 24, 202, 173, 238, 196, 243, 122, 135, 30, 65, 224, 10, 207, 251, 255, 153, 6, 227, 204, 111, 108, 15, 154, 216, 146, 153, 174, 27, 154, 46}, + {166, 77, 140, 145, 232, 144, 89, 158, 148, 141, 37, 165, 157, 162, 192, 200, 9, 9, 104, 112, 87, 52, 200, 120, 142, 45, 51, 235, 242, 210, 120, 215, 74, 253, 40, 204, 70, 217, 114, 147, 235, 77, 87, 158, 168, 173, 127, 182, 243, 109, 81, 99, 102, 163, 156, 151, 214, 50}, + {235, 88, 105, 58, 29, 99, 50, 220, 211, 227, 31, 24, 23, 181, 195, 94, 74, 2, 153, 222, 56, 121, 105, 55, 131, 212, 174, 111, 223, 208, 196, 124, 12, 40, 90, 94, 6, 220, 75, 174, 126, 71, 86, 38, 60, 251, 12, 123, 23, 235, 46, 225, 213, 196, 219, 254, 253, 173}, + {167, 130, 60, 200, 96, 187, 40, 113, 160, 84, 38, 24, 41, 222, 144, 171, 225, 27, 143, 56, 22, 41, 127, 36, 234, 121, 79, 140, 113, 99, 114, 84, 85, 69, 215, 249, 41, 254, 25, 158, 209, 208, 108, 9, 109, 223, 220, 156, 92, 80, 114, 87, 64, 158, 130, 146, 138, 215}, + {108, 47, 174, 236, 151, 205, 169, 124, 151, 201, 168, 91, 80, 176, 57, 217, 13, 166, 171, 95, 96, 91, 149, 209, 18, 164, 249, 213, 250, 137, 237, 234, 214, 216, 233, 152, 7, 248, 2, 105, 38, 58, 205, 209, 27, 40, 167, 68, 228, 43, 79, 118, 135, 82, 40, 15, 50, 86}, + {41, 109, 177, 142, 47, 51, 187, 195, 198, 206, 119, 44, 158, 252, 67, 181, 212, 88, 228, 139, 126, 213, 82, 234, 75, 124, 215, 142, 248, 125, 35, 8, 11, 167, 53, 206, 180, 110, 70, 132, 89, 158, 220, 141, 167, 220, 220, 66, 176, 128, 95, 118, 86, 241, 187, 89, 169, 89}, + {19, 197, 125, 125, 53, 99, 78, 4, 136, 250, 18, 247, 197, 225, 237, 240, 253, 76, 24, 40, 171, 184, 10, 240, 109, 99, 122, 70, 10, 81, 80, 6, 186, 200, 177, 71, 166, 34, 178, 3, 46, 215, 146, 89, 240, 139, 115, 249, 237, 110, 56, 42, 186, 43, 112, 120, 208, 250}, + {76, 132, 133, 106, 171, 24, 138, 136, 53, 120, 173, 40, 116, 36, 165, 27, 205, 167, 81, 15, 168, 148, 227, 184, 214, 205, 123, 4, 108, 148, 229, 196, 244, 145, 144, 100, 250, 196, 117, 187, 145, 44, 231, 188, 176, 64, 13, 231, 127, 178, 180, 241, 61, 83, 21, 91, 102, 129}, + {48, 210, 222, 175, 41, 107, 16, 81, 186, 110, 8, 71, 8, 48, 88, 203, 67, 80, 210, 123, 187, 227, 142, 241, 160, 79, 25, 237, 118, 57, 179, 239, 140, 1, 31, 71, 136, 158, 180, 190, 132, 130, 153, 179, 180, 177, 193, 7, 102, 67, 155, 145, 219, 53, 159, 224, 186, 35}, + {42, 92, 106, 223, 29, 129, 147, 140, 69, 29, 215, 161, 128, 231, 13, 182, 77, 14, 138, 211, 22, 241, 200, 93, 228, 145, 118, 141, 106, 107, 163, 117, 132, 109, 128, 7, 172, 160, 39, 197, 49, 72, 240, 155, 75, 171, 149, 7, 35, 200, 99, 63, 144, 35, 25, 126, 144, 74}, + {208, 233, 31, 47, 4, 122, 82, 145, 161, 246, 27, 245, 202, 177, 213, 121, 8, 131, 31, 207, 85, 30, 100, 142, 53, 205, 170, 102, 118, 16, 234, 141, 112, 36, 21, 182, 63, 246, 166, 158, 44, 135, 62, 122, 72, 187, 234, 187, 70, 199, 128, 31, 122, 67, 112, 185, 130, 81}}; + + d_genpoly_coeff = {1, 208, 42, 48, 76, 19, 41, 108, 167, 235, 166, 244, 186, 18, 124, 251, 79, 193, 14, 154, 6, 118, 19, 3, 122, 9, 187, 42, 131, 46, 66, 65, 62, 94, 101, 45, 214, 141, 131, 230, 102, 20, 63, 202, 36, 23, 188, 88, 169, 62, 73, 88, 152, 197, 231, 58, 101, 154, 190, 91, 193}; + } + + if (!gnss_signal.empty() && ((gnss_signal != "E6B") && gnss_signal != "E1B")) + { + std::cerr << "Unknown Reead Solomon configuration. Setting basic defaults for Galileo E6B.\n"; + // Set E6B parameters by default + 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_shortening = 0; // shortening parameter + d_rows_G = 0; + d_columns_G = 0; + 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}; + } + d_data_in_block = d_symbols_per_block - d_nroots; + d_info_symbols_shortened = d_data_in_block - d_shortening; + d_data_symbols_shortened = d_symbols_per_block - d_shortening; + d_a0 = static_cast(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(); } @@ -297,29 +458,37 @@ ReedSolomon::ReedSolomon(int nroots, int prim, int fcr, int pad, + int shortening, const std::vector& genpoly_coeff, const std::vector>& gen_matrix) { - if (fcr < 0 || fcr >= (1 << 8)) + if (fcr < 0 || fcr >= (1 << d_symsize)) { - std::cerr << "Reed Solomon bad configuration: fcr must be 0 < frc <= 255\n"; + std::cerr << "Reed Solomon wrong configuration: fcr must be 0 < frc <= 255\n"; return; } - if (prim <= 0 || prim >= (1 << 8)) + if (prim <= 0 || prim >= (1 << d_symsize)) { - std::cerr << "Reed Solomon bad configuration: prim must be 0 <= prim <= 255\n"; + std::cerr << "Reed Solomon wrong configuration: prim must be 0 <= prim <= 255\n"; return; } - if (nroots < 0 || nroots >= (1 << 8)) + if (nroots < 0 || nroots >= (1 << d_symsize)) { // Can't have more roots than symbol values! - std::cerr << "Reed Solomon bad configuration: nroots must be 0 < nroots <= 255\n"; + std::cerr << "Reed Solomon wrong configuration: nroots must be 0 < nroots <= 255\n"; return; } - if (pad < 0 || pad >= ((1 << 8) - 1 - nroots)) + if (pad < 0 || pad >= ((1 << d_symsize) - 1 - nroots)) { // Too much padding - std::cerr << "Reed Solomon bad configuration: pad must be 0 < pad <= 255-nroots\n"; + std::cerr << "Reed Solomon wrong configuration: pad must be 0 < pad <= 255-nroots\n"; + return; + } + + if (shortening < 0 || shortening >= (d_symbols_per_block - nroots)) + { + // Too much shortening + std::cerr << "Reed Solomon wrong configuration: shortening must be 0 < shortening <= 255-nroots\n"; return; } @@ -330,6 +499,9 @@ ReedSolomon::ReedSolomon(int nroots, d_pad = pad; d_a0 = static_cast(d_symbols_per_block); d_fcr = fcr; + d_shortening = shortening; + d_info_symbols_shortened = d_data_in_block - d_shortening; + d_data_symbols_shortened = d_symbols_per_block - d_shortening; if (!genpoly_coeff.empty() && (int(genpoly_coeff.size()) == d_nroots + 1)) { @@ -337,18 +509,30 @@ ReedSolomon::ReedSolomon(int nroots, } else { - d_genpoly_coeff = std::vector(d_nroots + 1, 0); + if (!genpoly_coeff.empty()) + { + std::cerr << "Reed Solomon wrong configuration: genpoly_coeff should be of size " + << d_nroots + 1 << ", but it of size " << genpoly_coeff.size() << '\n'; + } + d_genpoly_coeff = std::vector(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))) + if (rows_G != 0) { - d_genmatrix = gen_matrix; - } - else - { - d_genmatrix = std::vector>(d_symbols_per_block, std::vector(d_data_in_block, 0)); + size_t columns_G = gen_matrix[0].size(); + if ((rows_G != d_data_symbols_shortened) || (columns_G != d_info_symbols_shortened)) + { + std::cerr << "Reed Solomon wrong configuration: gen_matrix should be of size (" + << d_data_symbols_shortened << "x" << d_info_symbols_shortened << ")" + << ", but it is (" << rows_G << "x" << columns_G << ")" << '\n'; + } + else + { + d_rows_G = rows_G; + d_columns_G = columns_G; + d_genmatrix = gen_matrix; + } } init_log_tables(); @@ -361,7 +545,7 @@ 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); + x = (x >> d_symsize) + (x & d_symbols_per_block); } return x; } @@ -378,7 +562,7 @@ 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++) + for (i = 0; i < d_symsize; i++) { if (b & 1) { @@ -430,7 +614,6 @@ void ReedSolomon::init_log_tables() 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; @@ -440,7 +623,7 @@ void ReedSolomon::init_alpha_tables() d_index_of[sr] = i; d_alpha_to[i] = sr; sr <<= 1; - if (sr & (1 << symsize)) + if (sr & (1 << d_symsize)) { sr ^= d_min_poly; } @@ -449,7 +632,7 @@ void ReedSolomon::init_alpha_tables() if (sr != 1) { - std::cerr << "Field generator polynomial is not primitive!\n"; + std::cerr << "Reed Solomon wrong configuration: Field generator polynomial is not primitive!\n"; return; } @@ -471,15 +654,22 @@ void ReedSolomon::init_alpha_tables() std::vector ReedSolomon::encode_with_generator_matrix(const std::vector& data_to_encode) const { - std::vector encoded_output(d_symbols_per_block, 0); - if (data_to_encode.size() != d_data_in_block) + std::vector encoded_output(d_data_symbols_shortened, 0); + if (d_rows_G == 0) { + std::cerr << "Reed Solomon usage problem: Generator matrix is not defined.\n"; + return encoded_output; + } + if (data_to_encode.size() != d_columns_G) + { + std::cerr << "Reed Solomon usage problem: wrong vector input size in encode_with_generator_matrix method.\n"; return encoded_output; } - for (int i = 0; i < d_symbols_per_block; i++) + // Matrix-vector product in the Galoise field + for (size_t i = 0; i < d_rows_G; i++) { - for (size_t k = 0; k < d_data_in_block; k++) + for (size_t k = 0; k < d_columns_G; k++) { encoded_output[i] = galois_add(encoded_output[i], galois_mul_table(d_genmatrix[i][k], data_to_encode[k])); } @@ -490,18 +680,27 @@ std::vector ReedSolomon::encode_with_generator_matrix(const std::vector std::vector ReedSolomon::encode_with_generator_poly(const std::vector& data_to_encode) const { - std::vector encoded_output(d_symbols_per_block, 0); + std::vector encoded_output(d_data_symbols_shortened, 0); - if (data_to_encode.size() != d_data_in_block) + if (data_to_encode.size() != d_info_symbols_shortened) { + std::cerr << "Reed Solomon usage problem: wrong vector input size in encode_with_generator_poly method.\n"; return encoded_output; } - for (size_t k = 0; k < d_data_in_block; k++) + + std::copy(data_to_encode.begin(), data_to_encode.end(), encoded_output.begin()); + + if (d_shortening == 0) { - encoded_output[k] = data_to_encode[k]; + encode_rs_8(encoded_output.data(), encoded_output.data() + d_data_in_block); + } + else + { + std::vector unshortened_encoded_output(d_symbols_per_block, 0); + std::copy(data_to_encode.begin(), data_to_encode.end(), unshortened_encoded_output.begin()); + encode_rs_8(unshortened_encoded_output.data(), unshortened_encoded_output.data() + d_data_in_block); + std::copy(unshortened_encoded_output.begin() + d_data_in_block, unshortened_encoded_output.end(), encoded_output.begin() + d_info_symbols_shortened); } - int parity_offset = d_symbols_per_block - d_nroots; - encode_rs_8(encoded_output.data(), encoded_output.data() + parity_offset); return encoded_output; } @@ -512,8 +711,6 @@ void ReedSolomon::encode_rs_8(const uint8_t* data, uint8_t* parity) const 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]]; @@ -542,12 +739,32 @@ void ReedSolomon::encode_rs_8(const uint8_t* data, uint8_t* parity) const int ReedSolomon::decode(std::vector& data_to_decode, const std::vector& erasure_positions) const { - if (data_to_decode.size() != d_symbols_per_block) + int result = -1; + if (data_to_decode.size() != d_data_symbols_shortened) { - std::cerr << "Reed Solomon error: bad input length\n"; - return -1; + std::cerr << "Reed Solomon usage error: wrong vector input size in decode method.\n"; + return result; + } + + if (d_shortening == 0) + { + result = decode_rs_8(data_to_decode.data(), erasure_positions.data(), erasure_positions.size(), d_pad); + } + else + { + // Create unshortened code vector + std::vector unshortened_code_vector(d_symbols_per_block, 0); + std::copy(data_to_decode.begin(), data_to_decode.begin() + d_info_symbols_shortened, unshortened_code_vector.begin()); + std::copy(data_to_decode.begin() + d_info_symbols_shortened, data_to_decode.begin() + d_data_symbols_shortened, unshortened_code_vector.begin() + d_data_in_block); + + result = decode_rs_8(unshortened_code_vector.data(), erasure_positions.data(), erasure_positions.size(), d_pad); + if (result >= 0) + { + // Store decoded result into the shortened code vector + std::copy(unshortened_code_vector.begin(), unshortened_code_vector.begin() + d_info_symbols_shortened, data_to_decode.begin()); + std::copy(unshortened_code_vector.begin() + d_data_in_block, unshortened_code_vector.begin() + d_symbols_per_block, data_to_decode.begin() + d_info_symbols_shortened); + } } - int result = decode_rs_8(data_to_decode.data(), erasure_positions.data(), erasure_positions.size(), d_pad); return result; } diff --git a/src/core/system_parameters/reed_solomon.h b/src/core/system_parameters/reed_solomon.h index 977ea53e3..ce0e68af8 100644 --- a/src/core/system_parameters/reed_solomon.h +++ b/src/core/system_parameters/reed_solomon.h @@ -1,6 +1,7 @@ /*! * \file reed_solomon.h - * \brief Class implementing a Reed-Solomon encoder/decoder + * \brief Class implementing a Reed-Solomon encoder/decoder for blocks of 255 + * symbols and symbols of 8 bits. * \author Carles Fernandez, 2021. cfernandez(at)cttc.es * * ----------------------------------------------------------------------------- @@ -21,6 +22,7 @@ #include #include #include +#include #include @@ -33,19 +35,21 @@ * \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. + * Hamming distance, with symbols of 8 bits. It allows shortened RS codes. */ 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 + * Constructs a Reed Solomon object. The encode_with_generator_poly * and encode_with_generator_matrix methods are available for testing * purposes. + * + * gnss_signal: empty or "E6B" sets the Galileo E6B RS parameters. + * "E1B" sets the Galileo E1B (INAV) RS parameters. */ - ReedSolomon(); + explicit ReedSolomon(const std::string& gnss_signal = std::string()); /*! * \brief Custom constructor for RS(255, 255-nroots, nroots+1). Parameters: @@ -59,51 +63,63 @@ public: * * fcr - first consecutive root of the Reed-Solomon generator polynomial. * - * pad - the number of pad symbols in a block. Defaults to 0. + * pad - the number of pad symbols in a block. If not defined, it defaults + * to 0. * - * genpoly_coeff - a vector of nroots+1 elements containing the generator + * shortening - value of the shortening parameter. Defaults to 0. + * If different to 0, it defines a shortened RS encoder/decoder. + * + * 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. - * + * gen_matrix - a (255-shortening)x(255-nroots-shortening) 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, + int shortening = 0, const std::vector& genpoly_coeff = std::vector{}, const std::vector>& gen_matrix = std::vector>{}); /*! * \brief Decode an encoded block. * - * The decoded symbols are at the first 255-nroots elements - * of the input vector. + * The decoded symbols are at the first 255-nroots-shortening elements + * of the data_to_decode 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. + * Returns the number of corrected errors, or -1 if decoding failed. */ int decode(std::vector& data_to_decode, const std::vector& erasure_positions = std::vector{}) const; /*! * \brief Encode data with the generator matrix (for testing purposes) + * + * Returns the encoded vector. It is set to all zeros if the generator + * matrix is not defined. */ std::vector encode_with_generator_matrix(const std::vector& data_to_encode) const; /*! * \brief Encode data with the generator polynomial (for testing purposes) + * + * Returns the encoded vector. It is set to all zeros if the generator + * polynomial is not defined. */ std::vector encode_with_generator_poly(const std::vector& data_to_encode) const; private: static const int d_symbols_per_block = 255; // the total number of symbols in a RS block. + static const int d_symsize = 8; // symbol size, in bits. int mod255(int x) const; int rs_min(int a, int b) const; @@ -126,13 +142,18 @@ private: std::vector d_genpoly_coeff; // used for encoding std::vector d_genpoly_index; // used for encoding - size_t d_data_in_block{}; // number of information symbols in a block + size_t d_data_in_block{}; // number of information symbols in a block + size_t d_rows_G{}; // number of rows of the generator matrix + size_t d_columns_G{}; // number of rows of the generator matrix + size_t d_info_symbols_shortened{}; // number of info symbols in the shortened code + size_t d_data_symbols_shortened{}; // number of data symbols in the shortened code - 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 + 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 + int d_shortening{}; // shortening parameter uint8_t d_min_poly{}; // primitive polynomial uint8_t d_a0{}; // auxiliar variable diff --git a/src/tests/test_main.cc b/src/tests/test_main.cc index 724b08bf8..2a7444506 100644 --- a/src/tests/test_main.cc +++ b/src/tests/test_main.cc @@ -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_e1b_reed_solomon_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" diff --git a/src/tests/unit-tests/system-parameters/galileo_e1b_reed_solomon_test.cc b/src/tests/unit-tests/system-parameters/galileo_e1b_reed_solomon_test.cc new file mode 100644 index 000000000..6f856bf99 --- /dev/null +++ b/src/tests/unit-tests/system-parameters/galileo_e1b_reed_solomon_test.cc @@ -0,0 +1,310 @@ +/*! + * \file galileo_e1b_reed_solomon_test.cc + * \brief Tests for Reed Solomon E1B 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 + + +TEST(ReedSolomonE1BTest, EncodeWithPoly) +{ + const std::vector information_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162}; + const std::vector code_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162, 238, 77, 12, 72, 235, 21, 148, 213, 230, 54, 183, 82, 49, 104, 12, 228, 150, 157, 220, 112, 236, 187, 63, 31, 175, 47, 210, 164, 17, 104, 98, 46, 252, 165, 194, 57, 26, 213, 14, 133, 176, 148, 34, 9, 167, 43, 204, 198, 25, 164, 233, 55, 153, 31, 237, 84, 212, 76, 137, 242}; + + auto rs = std::make_unique("E1B"); + + std::vector encoded_output = rs->encode_with_generator_poly(information_vector); + + EXPECT_TRUE(encoded_output == code_vector); +} + + +TEST(ReedSolomonE1BTest, EncodeWithCustomPoly) +{ + // ICD 2.0 Annex F + const std::vector information_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162}; + const std::vector code_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162, 238, 77, 12, 72, 235, 21, 148, 213, 230, 54, 183, 82, 49, 104, 12, 228, 150, 157, 220, 112, 236, 187, 63, 31, 175, 47, 210, 164, 17, 104, 98, 46, 252, 165, 194, 57, 26, 213, 14, 133, 176, 148, 34, 9, 167, 43, 204, 198, 25, 164, 233, 55, 153, 31, 237, 84, 212, 76, 137, 242}; + + // Instantiate a ReedSolomon object with Galileo E1 (INAV) parameters + const int nroots = 60; + const int minpoly = 29; + const int pad = 0; + const int prim = 1; + const int fcr = 195; + const int shortening = 137; + const std::vector genpoly_coeff = {1, 208, 42, 48, 76, 19, 41, 108, 167, 235, 166, 244, 186, 18, 124, 251, 79, 193, 14, 154, 6, 118, 19, 3, 122, 9, 187, 42, 131, 46, 66, 65, 62, 94, 101, 45, 214, 141, 131, 230, 102, 20, 63, 202, 36, 23, 188, 88, 169, 62, 73, 88, 152, 197, 231, 58, 101, 154, 190, 91, 193}; + + auto rs = std::make_unique(nroots, minpoly, prim, fcr, pad, shortening, genpoly_coeff); + + std::vector encoded_output = rs->encode_with_generator_poly(information_vector); + + EXPECT_TRUE(encoded_output == code_vector); +} + + +TEST(ReedSolomonE1BTest, EncodeWithMatrix) +{ + // ICD 2.0 Annex F + const std::vector information_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162}; + const std::vector code_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162, 238, 77, 12, 72, 235, 21, 148, 213, 230, 54, 183, 82, 49, 104, 12, 228, 150, 157, 220, 112, 236, 187, 63, 31, 175, 47, 210, 164, 17, 104, 98, 46, 252, 165, 194, 57, 26, 213, 14, 133, 176, 148, 34, 9, 167, 43, 204, 198, 25, 164, 233, 55, 153, 31, 237, 84, 212, 76, 137, 242}; + + auto rs = std::make_unique("E1B"); + + std::vector encoded_output = rs->encode_with_generator_matrix(information_vector); + + EXPECT_TRUE(code_vector == encoded_output); +} + + +TEST(ReedSolomonE1BTest, EncodeWithCustomMatrix) +{ + // ICD 2.0 Annex F + const std::vector information_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162}; + const std::vector code_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162, 238, 77, 12, 72, 235, 21, 148, 213, 230, 54, 183, 82, 49, 104, 12, 228, 150, 157, 220, 112, 236, 187, 63, 31, 175, 47, 210, 164, 17, 104, 98, 46, 252, 165, 194, 57, 26, 213, 14, 133, 176, 148, 34, 9, 167, 43, 204, 198, 25, 164, 233, 55, 153, 31, 237, 84, 212, 76, 137, 242}; + + const int nroots = 60; + const int minpoly = 29; + const int prim = 1; + const int fcr = 1; + const int pad = 0; + const int shortening = 137; + const std::vector genpoly_coeff; + const std::vector> 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0}, + {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, 0}, + {0, 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}, + {0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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, 0, 0, 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}, + {193, 146, 156, 183, 62, 35, 90, 25, 89, 208, 43, 148, 117, 199, 92, 112, 4, 70, 74, 183, 37, 100, 118, 44, 238, 107, 186, 200, 179, 63, 140, 194, 176, 131, 38, 110, 33, 178, 43, 173, 98, 96, 105, 115, 90, 76, 133, 194, 133, 182, 99, 20, 183, 90, 84, 131, 26, 139}, + {91, 221, 65, 59, 102, 79, 66, 118, 29, 43, 164, 253, 13, 228, 108, 106, 252, 230, 45, 237, 12, 149, 152, 235, 143, 178, 102, 199, 227, 65, 230, 178, 140, 133, 93, 213, 89, 41, 198, 197, 215, 94, 122, 58, 18, 251, 179, 187, 61, 1, 239, 229, 179, 214, 244, 97, 202, 42}, + {190, 173, 125, 234, 225, 164, 122, 176, 130, 152, 32, 205, 41, 44, 78, 230, 128, 101, 92, 134, 176, 227, 128, 184, 180, 130, 142, 165, 134, 141, 110, 153, 50, 65, 25, 14, 98, 172, 34, 94, 181, 210, 38, 49, 15, 55, 169, 204, 233, 34, 197, 138, 78, 134, 181, 57, 82, 206}, + {154, 48, 226, 109, 5, 179, 159, 79, 237, 32, 199, 98, 33, 127, 219, 8, 110, 36, 68, 76, 24, 100, 105, 49, 245, 131, 53, 59, 61, 75, 216, 146, 103, 132, 185, 132, 24, 216, 243, 97, 70, 233, 161, 6, 10, 226, 77, 85, 182, 219, 24, 87, 154, 117, 124, 3, 106, 233}, + {101, 12, 238, 213, 120, 140, 44, 51, 196, 68, 201, 121, 36, 108, 200, 161, 56, 97, 123, 115, 237, 146, 54, 164, 141, 59, 232, 60, 60, 36, 85, 169, 129, 61, 49, 75, 21, 19, 49, 124, 195, 252, 63, 207, 153, 125, 144, 60, 39, 141, 117, 232, 96, 5, 162, 38, 187, 80}, + {58, 136, 130, 2, 173, 144, 135, 249, 194, 161, 38, 205, 85, 247, 142, 247, 228, 245, 99, 151, 114, 158, 68, 235, 185, 203, 78, 14, 149, 18, 163, 149, 250, 51, 198, 100, 15, 234, 113, 242, 230, 245, 22, 250, 196, 114, 38, 80, 103, 157, 65, 57, 4, 107, 109, 16, 9, 131}, + {231, 109, 52, 176, 87, 22, 48, 253, 54, 106, 47, 175, 119, 108, 137, 133, 99, 143, 63, 81, 242, 135, 75, 49, 22, 130, 125, 185, 168, 229, 196, 43, 92, 226, 57, 76, 149, 140, 100, 86, 217, 135, 132, 114, 90, 208, 180, 174, 150, 112, 147, 191, 11, 164, 32, 117, 5, 36}, + {197, 18, 114, 255, 50, 100, 185, 124, 218, 88, 190, 215, 41, 191, 206, 138, 120, 5, 243, 244, 111, 223, 137, 234, 221, 169, 43, 39, 143, 82, 41, 137, 149, 2, 84, 3, 143, 219, 88, 107, 118, 9, 200, 15, 221, 43, 131, 249, 253, 37, 40, 189, 116, 164, 225, 126, 177, 188}, + {152, 246, 146, 92, 78, 104, 171, 40, 30, 39, 246, 202, 204, 246, 55, 63, 240, 204, 63, 221, 233, 17, 105, 240, 166, 128, 153, 215, 115, 174, 39, 28, 112, 195, 178, 227, 100, 75, 117, 74, 82, 114, 105, 148, 192, 160, 58, 182, 232, 67, 140, 167, 147, 187, 216, 183, 66, 19}, + {88, 233, 3, 20, 252, 51, 135, 113, 130, 146, 84, 13, 81, 30, 147, 99, 56, 10, 63, 185, 42, 166, 207, 88, 220, 165, 199, 7, 86, 27, 71, 58, 216, 213, 113, 126, 19, 45, 56, 108, 143, 26, 252, 120, 123, 185, 143, 39, 153, 166, 78, 151, 33, 124, 208, 125, 40, 233}, + {73, 120, 195, 119, 81, 197, 85, 70, 175, 109, 204, 25, 136, 117, 21, 195, 222, 74, 162, 75, 237, 9, 155, 148, 242, 157, 99, 129, 206, 123, 255, 182, 75, 94, 57, 229, 151, 178, 115, 53, 34, 17, 139, 20, 30, 190, 82, 126, 204, 133, 128, 61, 227, 71, 16, 86, 4, 201}, + {62, 195, 117, 83, 179, 169, 59, 213, 201, 116, 240, 164, 198, 84, 105, 89, 127, 51, 126, 50, 81, 215, 167, 203, 139, 96, 251, 23, 173, 37, 188, 176, 235, 36, 53, 56, 67, 148, 47, 18, 237, 164, 221, 182, 234, 200, 51, 29, 243, 115, 114, 246, 173, 29, 62, 127, 167, 14}, + {169, 67, 240, 110, 142, 45, 20, 35, 20, 206, 92, 117, 106, 151, 17, 35, 2, 94, 255, 101, 84, 49, 101, 168, 17, 122, 73, 59, 87, 175, 103, 105, 140, 56, 62, 64, 5, 220, 188, 80, 138, 214, 246, 235, 11, 222, 227, 230, 54, 55, 52, 88, 237, 16, 238, 237, 27, 194}, + {88, 216, 182, 118, 206, 243, 194, 206, 137, 152, 189, 167, 238, 184, 242, 69, 36, 248, 173, 121, 146, 27, 239, 84, 132, 18, 61, 215, 186, 63, 70, 122, 173, 41, 138, 242, 176, 76, 175, 165, 149, 194, 88, 231, 4, 114, 241, 254, 201, 120, 58, 47, 222, 2, 123, 75, 114, 176}, + {188, 19, 183, 35, 242, 4, 50, 148, 5, 83, 98, 226, 132, 70, 109, 207, 93, 173, 89, 56, 167, 215, 50, 7, 10, 227, 169, 183, 90, 56, 48, 240, 253, 128, 253, 243, 52, 59, 182, 102, 244, 200, 169, 111, 38, 177, 75, 71, 199, 90, 47, 66, 186, 31, 231, 86, 23, 111}, + {23, 55, 128, 7, 36, 174, 140, 216, 161, 135, 112, 142, 248, 244, 169, 173, 126, 229, 219, 233, 3, 40, 112, 29, 130, 246, 246, 170, 182, 54, 85, 154, 76, 227, 6, 219, 121, 94, 24, 81, 142, 202, 226, 212, 231, 55, 200, 225, 62, 28, 217, 96, 242, 50, 143, 249, 1, 118}, + {36, 111, 216, 59, 50, 180, 160, 75, 113, 134, 211, 91, 182, 143, 169, 101, 207, 67, 126, 96, 42, 56, 183, 225, 15, 184, 125, 128, 115, 21, 76, 214, 228, 55, 135, 200, 122, 54, 10, 195, 57, 215, 193, 137, 218, 47, 31, 75, 201, 19, 226, 46, 219, 252, 171, 244, 153, 190}, + {202, 21, 206, 182, 17, 61, 151, 229, 64, 62, 73, 178, 215, 44, 252, 93, 5, 209, 253, 16, 63, 35, 156, 48, 132, 142, 110, 111, 142, 65, 41, 174, 240, 80, 64, 126, 247, 108, 249, 9, 154, 80, 102, 29, 170, 52, 203, 253, 175, 191, 82, 31, 64, 248, 79, 31, 153, 237}, + {63, 144, 241, 65, 93, 237, 185, 69, 251, 58, 85, 58, 152, 193, 129, 101, 152, 224, 102, 114, 197, 80, 147, 168, 161, 35, 37, 176, 37, 19, 150, 140, 126, 239, 31, 84, 110, 203, 7, 62, 79, 48, 27, 101, 153, 52, 2, 110, 203, 86, 143, 164, 144, 196, 194, 80, 87, 17}, + {20, 217, 37, 96, 55, 13, 235, 165, 222, 135, 30, 148, 34, 171, 224, 35, 95, 56, 14, 247, 5, 181, 213, 16, 162, 2, 124, 197, 27, 211, 78, 31, 186, 240, 5, 6, 25, 69, 239, 23, 105, 5, 142, 36, 99, 80, 157, 139, 199, 218, 129, 93, 53, 150, 145, 76, 209, 173}, + {102, 239, 33, 51, 4, 178, 28, 177, 128, 137, 105, 141, 208, 44, 210, 248, 152, 72, 255, 24, 26, 112, 197, 180, 35, 51, 35, 150, 104, 152, 253, 28, 134, 112, 41, 200, 80, 83, 171, 151, 10, 167, 71, 162, 53, 204, 242, 207, 41, 54, 32, 236, 75, 36, 132, 91, 34, 112}, + {230, 225, 186, 12, 73, 187, 104, 52, 239, 137, 241, 251, 194, 35, 227, 12, 21, 251, 1, 210, 178, 186, 187, 212, 199, 45, 72, 126, 86, 110, 94, 159, 88, 238, 94, 182, 143, 236, 43, 42, 34, 71, 81, 29, 120, 105, 177, 144, 178, 120, 255, 94, 193, 145, 44, 236, 247, 141}, + {131, 247, 106, 160, 99, 127, 254, 236, 225, 79, 24, 221, 242, 124, 91, 71, 209, 121, 237, 27, 217, 152, 35, 103, 27, 7, 61, 28, 185, 73, 182, 77, 200, 106, 117, 51, 96, 56, 125, 37, 61, 213, 103, 101, 88, 83, 102, 162, 159, 216, 31, 113, 68, 132, 78, 30, 248, 98}, + {141, 134, 43, 202, 72, 109, 204, 33, 132, 193, 51, 182, 43, 212, 100, 221, 126, 205, 46, 77, 190, 130, 181, 189, 175, 208, 165, 139, 133, 24, 113, 224, 15, 96, 20, 206, 175, 176, 68, 217, 213, 95, 140, 58, 214, 164, 80, 48, 161, 118, 97, 194, 209, 247, 238, 230, 26, 34}, + {214, 148, 137, 44, 243, 55, 191, 63, 77, 214, 201, 75, 217, 156, 103, 212, 104, 128, 241, 41, 83, 85, 83, 182, 214, 56, 127, 110, 57, 214, 249, 25, 236, 146, 192, 92, 101, 119, 184, 14, 83, 139, 28, 130, 232, 139, 88, 56, 204, 204, 150, 58, 197, 3, 51, 115, 171, 240}, + {45, 176, 137, 213, 83, 71, 180, 128, 251, 170, 151, 33, 125, 122, 145, 152, 32, 29, 244, 173, 19, 175, 36, 161, 46, 108, 88, 154, 198, 123, 60, 147, 246, 64, 239, 179, 146, 240, 54, 156, 124, 91, 75, 164, 1, 18, 169, 50, 26, 173, 131, 149, 102, 70, 251, 159, 11, 242}, + {101, 187, 110, 190, 192, 218, 216, 24, 11, 82, 67, 41, 103, 48, 205, 235, 168, 47, 66, 195, 12, 153, 253, 233, 29, 224, 7, 81, 157, 223, 101, 77, 128, 172, 245, 29, 34, 153, 25, 185, 62, 198, 141, 37, 59, 118, 96, 216, 64, 33, 3, 115, 162, 249, 145, 161, 39, 49}, + {94, 206, 2, 170, 252, 165, 52, 243, 243, 102, 47, 226, 182, 51, 212, 93, 231, 98, 241, 134, 172, 160, 3, 137, 198, 160, 51, 66, 153, 220, 86, 62, 128, 24, 198, 71, 126, 233, 228, 243, 31, 158, 125, 24, 203, 239, 228, 59, 74, 135, 132, 63, 183, 76, 5, 9, 143, 167}, + {62, 212, 195, 146, 110, 4, 91, 180, 124, 40, 251, 71, 61, 106, 47, 152, 225, 10, 86, 97, 156, 150, 14, 83, 150, 84, 198, 71, 110, 114, 27, 25, 99, 239, 115, 199, 225, 125, 116, 133, 43, 153, 82, 64, 230, 29, 98, 171, 182, 245, 112, 242, 175, 73, 53, 106, 248, 133}, + {65, 208, 229, 79, 178, 119, 149, 48, 150, 242, 149, 110, 57, 7, 128, 153, 202, 211, 206, 218, 3, 45, 91, 82, 253, 98, 180, 185, 153, 212, 22, 233, 116, 66, 62, 79, 247, 165, 192, 32, 79, 200, 68, 87, 209, 89, 71, 144, 241, 160, 165, 119, 126, 62, 7, 20, 178, 125}, + {66, 194, 199, 72, 30, 167, 104, 8, 188, 230, 72, 45, 18, 64, 35, 84, 64, 224, 143, 99, 244, 77, 194, 171, 115, 86, 200, 40, 205, 185, 128, 199, 14, 197, 255, 61, 184, 242, 31, 99, 85, 216, 129, 3, 72, 182, 211, 150, 26, 45, 164, 63, 218, 97, 181, 182, 26, 172}, + {46, 73, 249, 186, 70, 166, 170, 161, 98, 165, 160, 141, 25, 242, 131, 190, 43, 45, 12, 242, 21, 247, 30, 156, 188, 150, 124, 206, 42, 21, 99, 201, 162, 50, 212, 179, 52, 108, 180, 204, 174, 41, 140, 123, 14, 106, 68, 154, 100, 177, 54, 58, 66, 215, 92, 137, 24, 216}, + {131, 63, 194, 227, 213, 112, 227, 46, 116, 194, 52, 140, 132, 167, 138, 39, 99, 71, 59, 22, 249, 63, 110, 194, 83, 124, 134, 40, 9, 53, 205, 112, 158, 144, 169, 185, 101, 131, 253, 186, 219, 89, 9, 184, 62, 37, 101, 87, 149, 14, 214, 184, 32, 7, 8, 110, 157, 141}, + {42, 239, 135, 195, 81, 125, 136, 127, 58, 211, 123, 157, 75, 107, 154, 100, 161, 46, 157, 58, 123, 179, 20, 189, 215, 98, 69, 18, 175, 20, 175, 11, 27, 127, 17, 177, 82, 77, 58, 140, 53, 23, 43, 11, 64, 33, 1, 163, 115, 59, 46, 114, 185, 216, 43, 233, 30, 109}, + {187, 107, 37, 79, 138, 135, 199, 125, 100, 160, 209, 101, 79, 175, 142, 182, 14, 16, 33, 85, 179, 136, 192, 221, 110, 59, 128, 190, 176, 114, 107, 181, 8, 123, 87, 3, 82, 100, 79, 166, 32, 172, 206, 166, 177, 16, 30, 27, 156, 47, 3, 241, 186, 3, 233, 75, 189, 170}, + {9, 165, 153, 194, 5, 174, 10, 63, 16, 164, 181, 18, 107, 155, 255, 189, 32, 70, 255, 198, 172, 116, 102, 163, 87, 238, 208, 19, 207, 81, 226, 130, 36, 223, 98, 234, 108, 136, 113, 176, 188, 113, 103, 29, 43, 131, 26, 247, 17, 208, 158, 247, 22, 55, 98, 62, 83, 91}, + {122, 218, 243, 230, 181, 240, 78, 230, 125, 90, 141, 63, 181, 72, 34, 163, 211, 215, 3, 128, 106, 59, 113, 131, 158, 208, 182, 227, 2, 45, 162, 58, 49, 199, 209, 30, 40, 232, 161, 64, 126, 69, 193, 153, 253, 55, 57, 194, 77, 251, 139, 85, 136, 246, 82, 129, 112, 108}, + {3, 23, 252, 210, 151, 185, 126, 184, 72, 131, 93, 160, 61, 246, 134, 64, 40, 203, 79, 34, 204, 149, 25, 221, 12, 193, 154, 85, 73, 152, 29, 129, 176, 161, 171, 238, 217, 105, 239, 86, 255, 10, 209, 131, 23, 37, 231, 26, 18, 135, 175, 22, 116, 6, 51, 194, 87, 235}, + {19, 239, 7, 48, 105, 219, 196, 193, 243, 116, 95, 221, 73, 66, 231, 53, 8, 176, 139, 131, 2, 10, 74, 187, 90, 36, 199, 11, 209, 224, 181, 56, 51, 115, 183, 217, 134, 79, 181, 170, 252, 113, 6, 84, 254, 59, 138, 194, 181, 204, 63, 218, 218, 9, 135, 240, 75, 4}, + {118, 105, 33, 252, 158, 172, 57, 215, 1, 102, 65, 97, 114, 123, 228, 46, 77, 159, 175, 112, 2, 78, 135, 37, 128, 188, 73, 95, 136, 97, 211, 225, 153, 170, 208, 55, 32, 27, 122, 127, 162, 200, 171, 237, 182, 165, 230, 222, 31, 80, 218, 186, 33, 56, 95, 30, 193, 34}, + {6, 172, 37, 99, 30, 134, 173, 200, 150, 224, 104, 27, 101, 244, 250, 32, 37, 125, 178, 237, 232, 225, 10, 194, 38, 62, 40, 146, 22, 161, 1, 149, 232, 164, 114, 174, 164, 162, 21, 137, 28, 74, 253, 47, 236, 27, 24, 160, 99, 150, 248, 253, 248, 32, 175, 98, 175, 234}, + {154, 136, 227, 53, 140, 76, 189, 152, 149, 52, 191, 42, 247, 51, 3, 188, 168, 129, 92, 162, 115, 60, 107, 187, 143, 17, 137, 157, 10, 219, 244, 253, 107, 94, 92, 239, 184, 30, 253, 86, 145, 64, 57, 221, 20, 1, 97, 228, 218, 81, 172, 106, 237, 195, 218, 25, 49, 20}, + {14, 142, 223, 89, 178, 180, 186, 230, 37, 21, 217, 61, 213, 111, 83, 33, 88, 216, 192, 230, 12, 2, 136, 41, 188, 132, 163, 107, 195, 180, 59, 177, 232, 241, 187, 138, 165, 223, 243, 87, 177, 4, 57, 80, 43, 195, 13, 36, 232, 89, 143, 255, 208, 27, 118, 64, 9, 126}, + {193, 156, 18, 104, 103, 145, 238, 163, 191, 245, 62, 77, 72, 18, 51, 35, 37, 30, 146, 119, 195, 104, 116, 164, 199, 215, 62, 107, 216, 252, 56, 249, 1, 107, 215, 213, 171, 23, 244, 94, 53, 209, 109, 74, 10, 103, 70, 207, 161, 94, 58, 155, 72, 138, 79, 245, 90, 130}, + {79, 59, 250, 36, 207, 70, 246, 222, 60, 177, 165, 41, 204, 234, 152, 167, 149, 103, 155, 164, 187, 3, 17, 106, 13, 56, 133, 163, 235, 220, 120, 143, 129, 186, 95, 61, 175, 136, 71, 10, 115, 67, 117, 1, 45, 98, 49, 241, 153, 52, 208, 110, 173, 47, 113, 244, 61, 144}, + {251, 48, 41, 161, 98, 4, 23, 58, 118, 137, 248, 53, 112, 216, 84, 215, 230, 30, 47, 192, 128, 172, 163, 224, 153, 32, 247, 171, 185, 250, 215, 122, 108, 176, 103, 100, 88, 226, 193, 41, 242, 37, 192, 195, 80, 138, 188, 51, 47, 149, 155, 136, 53, 252, 19, 64, 193, 142}, + {124, 242, 42, 20, 52, 143, 229, 10, 57, 221, 174, 40, 150, 213, 224, 204, 178, 33, 118, 18, 244, 244, 237, 3, 222, 160, 236, 31, 243, 114, 105, 73, 192, 178, 102, 101, 53, 94, 197, 3, 132, 227, 160, 186, 34, 225, 141, 34, 52, 76, 102, 119, 181, 212, 14, 205, 191, 213}, + {18, 64, 11, 249, 128, 124, 136, 8, 208, 164, 247, 53, 52, 35, 117, 134, 253, 34, 226, 165, 253, 103, 53, 43, 10, 195, 107, 215, 253, 44, 79, 166, 118, 115, 128, 1, 187, 156, 116, 38, 55, 38, 104, 27, 189, 70, 245, 66, 54, 172, 51, 147, 164, 178, 22, 189, 253, 110}, + {186, 131, 99, 220, 159, 82, 188, 47, 130, 235, 80, 242, 18, 26, 119, 140, 149, 68, 174, 53, 227, 91, 10, 152, 118, 211, 236, 58, 19, 86, 67, 191, 40, 102, 127, 135, 84, 178, 104, 78, 20, 130, 120, 219, 219, 165, 194, 5, 198, 44, 83, 108, 68, 100, 192, 6, 80, 203}, + {244, 1, 47, 157, 50, 104, 143, 121, 171, 22, 185, 89, 161, 84, 117, 207, 80, 102, 206, 80, 112, 208, 81, 221, 226, 101, 99, 138, 24, 202, 173, 238, 196, 243, 122, 135, 30, 65, 224, 10, 207, 251, 255, 153, 6, 227, 204, 111, 108, 15, 154, 216, 146, 153, 174, 27, 154, 46}, + {166, 77, 140, 145, 232, 144, 89, 158, 148, 141, 37, 165, 157, 162, 192, 200, 9, 9, 104, 112, 87, 52, 200, 120, 142, 45, 51, 235, 242, 210, 120, 215, 74, 253, 40, 204, 70, 217, 114, 147, 235, 77, 87, 158, 168, 173, 127, 182, 243, 109, 81, 99, 102, 163, 156, 151, 214, 50}, + {235, 88, 105, 58, 29, 99, 50, 220, 211, 227, 31, 24, 23, 181, 195, 94, 74, 2, 153, 222, 56, 121, 105, 55, 131, 212, 174, 111, 223, 208, 196, 124, 12, 40, 90, 94, 6, 220, 75, 174, 126, 71, 86, 38, 60, 251, 12, 123, 23, 235, 46, 225, 213, 196, 219, 254, 253, 173}, + {167, 130, 60, 200, 96, 187, 40, 113, 160, 84, 38, 24, 41, 222, 144, 171, 225, 27, 143, 56, 22, 41, 127, 36, 234, 121, 79, 140, 113, 99, 114, 84, 85, 69, 215, 249, 41, 254, 25, 158, 209, 208, 108, 9, 109, 223, 220, 156, 92, 80, 114, 87, 64, 158, 130, 146, 138, 215}, + {108, 47, 174, 236, 151, 205, 169, 124, 151, 201, 168, 91, 80, 176, 57, 217, 13, 166, 171, 95, 96, 91, 149, 209, 18, 164, 249, 213, 250, 137, 237, 234, 214, 216, 233, 152, 7, 248, 2, 105, 38, 58, 205, 209, 27, 40, 167, 68, 228, 43, 79, 118, 135, 82, 40, 15, 50, 86}, + {41, 109, 177, 142, 47, 51, 187, 195, 198, 206, 119, 44, 158, 252, 67, 181, 212, 88, 228, 139, 126, 213, 82, 234, 75, 124, 215, 142, 248, 125, 35, 8, 11, 167, 53, 206, 180, 110, 70, 132, 89, 158, 220, 141, 167, 220, 220, 66, 176, 128, 95, 118, 86, 241, 187, 89, 169, 89}, + {19, 197, 125, 125, 53, 99, 78, 4, 136, 250, 18, 247, 197, 225, 237, 240, 253, 76, 24, 40, 171, 184, 10, 240, 109, 99, 122, 70, 10, 81, 80, 6, 186, 200, 177, 71, 166, 34, 178, 3, 46, 215, 146, 89, 240, 139, 115, 249, 237, 110, 56, 42, 186, 43, 112, 120, 208, 250}, + {76, 132, 133, 106, 171, 24, 138, 136, 53, 120, 173, 40, 116, 36, 165, 27, 205, 167, 81, 15, 168, 148, 227, 184, 214, 205, 123, 4, 108, 148, 229, 196, 244, 145, 144, 100, 250, 196, 117, 187, 145, 44, 231, 188, 176, 64, 13, 231, 127, 178, 180, 241, 61, 83, 21, 91, 102, 129}, + {48, 210, 222, 175, 41, 107, 16, 81, 186, 110, 8, 71, 8, 48, 88, 203, 67, 80, 210, 123, 187, 227, 142, 241, 160, 79, 25, 237, 118, 57, 179, 239, 140, 1, 31, 71, 136, 158, 180, 190, 132, 130, 153, 179, 180, 177, 193, 7, 102, 67, 155, 145, 219, 53, 159, 224, 186, 35}, + {42, 92, 106, 223, 29, 129, 147, 140, 69, 29, 215, 161, 128, 231, 13, 182, 77, 14, 138, 211, 22, 241, 200, 93, 228, 145, 118, 141, 106, 107, 163, 117, 132, 109, 128, 7, 172, 160, 39, 197, 49, 72, 240, 155, 75, 171, 149, 7, 35, 200, 99, 63, 144, 35, 25, 126, 144, 74}, + {208, 233, 31, 47, 4, 122, 82, 145, 161, 246, 27, 245, 202, 177, 213, 121, 8, 131, 31, 207, 85, 30, 100, 142, 53, 205, 170, 102, 118, 16, 234, 141, 112, 36, 21, 182, 63, 246, 166, 158, 44, 135, 62, 122, 72, 187, 234, 187, 70, 199, 128, 31, 122, 67, 112, 185, 130, 81}}; + + auto rs = std::make_unique(nroots, minpoly, prim, fcr, pad, shortening, genpoly_coeff, genmatrix); + + std::vector encoded_output = rs->encode_with_generator_matrix(information_vector); + + EXPECT_TRUE(code_vector == encoded_output); +} + + +TEST(ReedSolomonE1BTest, DecodeNoErrors) +{ + // ICD 2.0 Annex F + const std::vector information_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162}; + + std::vector code_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162, 238, 77, 12, 72, 235, 21, 148, 213, 230, 54, 183, 82, 49, 104, 12, 228, 150, 157, 220, 112, 236, 187, 63, 31, 175, 47, 210, 164, 17, 104, 98, 46, 252, 165, 194, 57, 26, 213, 14, 133, 176, 148, 34, 9, 167, 43, 204, 198, 25, 164, 233, 55, 153, 31, 237, 84, 212, 76, 137, 242}; + + // Instantiate a ReedSolomon object with Galileo E1 (INAV) parameters + const int nroots = 60; + const int minpoly = 29; + const int prim = 1; + const int fcr = 195; + const int pad = 0; + const int shortening = 137; + auto rs = std::make_unique(nroots, minpoly, prim, fcr, pad, shortening); + + int result = rs->decode(code_vector); + + EXPECT_TRUE(result == 0); + + std::vector decoded(code_vector.begin(), code_vector.begin() + 58); + + EXPECT_TRUE(information_vector == decoded); +} + + +TEST(ReedSolomonE1BTest, Decode30Errors) +{ + // ICD 2.0 Annex F + const std::vector information_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162}; + + std::vector code_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162, 238, 77, 12, 72, 235, 21, 148, 213, 230, 54, 183, 82, 49, 104, 12, 228, 150, 157, 220, 112, 236, 187, 63, 31, 175, 47, 210, 164, 17, 104, 98, 46, 252, 165, 194, 57, 26, 213, 14, 133, 176, 148, 34, 9, 167, 43, 204, 198, 25, 164, 233, 55, 153, 31, 237, 84, 212, 76, 137, 242}; + + // Introduce 30 non-consecutive errors + for (int i = 0; i < 60; i += 2) + { + code_vector[i] = 0; + } + + auto rs = std::make_unique("E1B"); + + int result = rs->decode(code_vector); + + EXPECT_TRUE(result == 30); + + std::vector decoded(code_vector.begin(), code_vector.begin() + 58); + + EXPECT_TRUE(information_vector == decoded); +} + + +TEST(ReedSolomonE1BTest, Decode31Errors) +{ + // ICD 2.0 Annex F + const std::vector information_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162}; + + std::vector code_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162, 238, 77, 12, 72, 235, 21, 148, 213, 230, 54, 183, 82, 49, 104, 12, 228, 150, 157, 220, 112, 236, 187, 63, 31, 175, 47, 210, 164, 17, 104, 98, 46, 252, 165, 194, 57, 26, 213, 14, 133, 176, 148, 34, 9, 167, 43, 204, 198, 25, 164, 233, 55, 153, 31, 237, 84, 212, 76, 137, 242}; + + // Introduce 31 errors (this should make the decoder to fail) + for (int i = 0; i < 62; i += 2) + { + code_vector[i] = 0; + } + + auto rs = std::make_unique("E1B"); + + int result = rs->decode(code_vector); + + EXPECT_TRUE(result == -1); +} + + +TEST(ReedSolomonE1BTest, Decode33ErrorsWithErasure) +{ + // ICD 2.0 Annex F + const std::vector information_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162}; + + std::vector code_vector = {147, 109, 66, 23, 234, 140, 74, 234, 49, 89, 241, 253, 169, 161, 89, 93, 75, 142, 83, 102, 98, 218, 14, 197, 155, 151, 43, 181, 9, 163, 142, 111, 8, 118, 21, 47, 135, 139, 108, 215, 51, 147, 185, 52, 17, 151, 97, 102, 238, 71, 83, 114, 47, 80, 67, 199, 215, 162, 238, 77, 12, 72, 235, 21, 148, 213, 230, 54, 183, 82, 49, 104, 12, 228, 150, 157, 220, 112, 236, 187, 63, 31, 175, 47, 210, 164, 17, 104, 98, 46, 252, 165, 194, 57, 26, 213, 14, 133, 176, 148, 34, 9, 167, 43, 204, 198, 25, 164, 233, 55, 153, 31, 237, 84, 212, 76, 137, 242}; + + // Introduce 33 errors + for (int i = 0; i < 66; i += 2) + { + code_vector[i] = 0; + } + + std::vector erasure_positions{2, 4, 16, 18, 22, 54}; + + auto rs = std::make_unique("E1B"); + + int result = rs->decode(code_vector, erasure_positions); + + EXPECT_TRUE(result == 33); + + std::vector decoded(code_vector.begin(), code_vector.begin() + 58); + + EXPECT_TRUE(information_vector == decoded); +} diff --git a/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc b/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc index 9dd5e7955..3775a1d7a 100644 --- a/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc +++ b/src/tests/unit-tests/system-parameters/galileo_e6b_reed_solomon_test.cc @@ -1,6 +1,6 @@ /*! * \file galileo_e6b_reed_solomon_test.cc - * \brief Tests for Reed Solomon encoder and decoder + * \brief Tests for Reed Solomon E6B encoder and decoder * \author Carles Fernandez-Prades, 2021. cfernandez(at)cttc.es * * @@ -20,7 +20,7 @@ #include -TEST(ReedSolomonTest, EncodeWithGenMatrix) +TEST(ReedSolomonE6BTest, EncodeWithGenMatrix) { // input vector as defined in Galileo HAS ICD v1.2, Annex D const std::vector 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}; @@ -36,7 +36,7 @@ TEST(ReedSolomonTest, EncodeWithGenMatrix) } -TEST(ReedSolomonTest, EncodeWithGenPoly) +TEST(ReedSolomonE6BTest, EncodeWithGenPoly) { // input vector as defined in Galileo HAS ICD v1.2, Annex D const std::vector 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}; @@ -52,7 +52,7 @@ TEST(ReedSolomonTest, EncodeWithGenPoly) } -TEST(ReedSolomonTest, EncodeWithCustomPoly) +TEST(ReedSolomonE6BTest, EncodeWithCustomPoly) { // input vector as defined in Galileo HAS ICD v1.2, Annex D const std::vector 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}; @@ -61,14 +61,15 @@ TEST(ReedSolomonTest, EncodeWithCustomPoly) const std::vector 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; + const int nroots = 223; + const int minpoly = 29; + const int prim = 1; + const int fcr = 1; + const int pad = 0; + const int shortening = 0; const std::vector 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(nroots, minpoly, prim, fcr, pad, genpoly_coeff); + auto rs = std::make_unique(nroots, minpoly, prim, fcr, pad, shortening, genpoly_coeff); std::vector encoded_output = rs->encode_with_generator_poly(input); @@ -76,7 +77,7 @@ TEST(ReedSolomonTest, EncodeWithCustomPoly) } -TEST(ReedSolomonTest, EncodeWithCustomMatrix) +TEST(ReedSolomonE6BTest, EncodeWithCustomMatrix) { // input vector as defined in Galileo HAS ICD v1.2, Annex D const std::vector 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}; @@ -85,12 +86,13 @@ TEST(ReedSolomonTest, EncodeWithCustomMatrix) const std::vector 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 genpoly_coeff; + const int nroots = 223; + const int minpoly = 29; + const int prim = 1; + const int fcr = 1; + const int pad = 0; + const int shortening = 0; + const std::vector genpoly_coeff; const std::vector> 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}, @@ -348,7 +350,7 @@ TEST(ReedSolomonTest, EncodeWithCustomMatrix) {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(nroots, minpoly, prim, fcr, pad, genpoly_coeff, genmatrix); + auto rs = std::make_unique(nroots, minpoly, prim, fcr, pad, shortening, genpoly_coeff, genmatrix); std::vector encoded_output = rs->encode_with_generator_matrix(input); @@ -356,7 +358,7 @@ TEST(ReedSolomonTest, EncodeWithCustomMatrix) } -TEST(ReedSolomonTest, DecodeNoErrors) +TEST(ReedSolomonE6BTest, DecodeNoErrors) { const std::vector 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}; @@ -373,7 +375,7 @@ TEST(ReedSolomonTest, DecodeNoErrors) } -TEST(ReedSolomonTest, Decode1Error) +TEST(ReedSolomonE6BTest, Decode1Error) { const std::vector 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}; @@ -392,7 +394,7 @@ TEST(ReedSolomonTest, Decode1Error) } -TEST(ReedSolomonTest, Decode16Errors) +TEST(ReedSolomonE6BTest, Decode16Errors) { const std::vector 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}; @@ -426,7 +428,7 @@ TEST(ReedSolomonTest, Decode16Errors) } -TEST(ReedSolomonTest, Decode111Errors) +TEST(ReedSolomonE6BTest, Decode111Errors) { const std::vector 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}; @@ -448,7 +450,7 @@ TEST(ReedSolomonTest, Decode111Errors) } -TEST(ReedSolomonTest, Decode111ErrorsCustomConstructor) +TEST(ReedSolomonE6BTest, Decode111ErrorsCustomConstructor) { const std::vector 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}; @@ -475,7 +477,7 @@ TEST(ReedSolomonTest, Decode111ErrorsCustomConstructor) } -TEST(ReedSolomonTest, Decode112Errors) +TEST(ReedSolomonE6BTest, Decode112Errors) { std::vector 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}; @@ -493,9 +495,9 @@ TEST(ReedSolomonTest, Decode112Errors) } -TEST(ReedSolomonTest, Decode113ErrorsWithErasure) +TEST(ReedSolomonE6BTest, Decode113ErrorsWithErasure) { - std::vector 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}; + const std::vector 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 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}; From a32ea2ab9c8bc34bb143f74a74a7cf456ba4099b Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Thu, 1 Apr 2021 12:53:28 +0200 Subject: [PATCH 18/23] Remove redundant check --- src/core/system_parameters/reed_solomon.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/core/system_parameters/reed_solomon.cc b/src/core/system_parameters/reed_solomon.cc index db5cffffa..1118ef1ae 100644 --- a/src/core/system_parameters/reed_solomon.cc +++ b/src/core/system_parameters/reed_solomon.cc @@ -771,10 +771,6 @@ int ReedSolomon::decode(std::vector& data_to_decode, const std::vector< 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; From 5cd1955dc03ae8bb1d279ba02db862bea28c510a Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Thu, 1 Apr 2021 13:21:11 +0200 Subject: [PATCH 19/23] Add check of number of erasures, remove unused parameter in private method --- src/core/system_parameters/reed_solomon.cc | 11 ++++++++--- src/core/system_parameters/reed_solomon.h | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/core/system_parameters/reed_solomon.cc b/src/core/system_parameters/reed_solomon.cc index 1118ef1ae..45d97a55c 100644 --- a/src/core/system_parameters/reed_solomon.cc +++ b/src/core/system_parameters/reed_solomon.cc @@ -745,10 +745,15 @@ int ReedSolomon::decode(std::vector& data_to_decode, const std::vector< std::cerr << "Reed Solomon usage error: wrong vector input size in decode method.\n"; return result; } + if (erasure_positions.size() > std::size_t(d_nroots)) + { + std::cerr << "Reed Solomon usage error: too much erasure positions.\n"; + return result; + } if (d_shortening == 0) { - result = decode_rs_8(data_to_decode.data(), erasure_positions.data(), erasure_positions.size(), d_pad); + result = decode_rs_8(data_to_decode.data(), erasure_positions.data(), erasure_positions.size()); } else { @@ -757,7 +762,7 @@ int ReedSolomon::decode(std::vector& data_to_decode, const std::vector< std::copy(data_to_decode.begin(), data_to_decode.begin() + d_info_symbols_shortened, unshortened_code_vector.begin()); std::copy(data_to_decode.begin() + d_info_symbols_shortened, data_to_decode.begin() + d_data_symbols_shortened, unshortened_code_vector.begin() + d_data_in_block); - result = decode_rs_8(unshortened_code_vector.data(), erasure_positions.data(), erasure_positions.size(), d_pad); + result = decode_rs_8(unshortened_code_vector.data(), erasure_positions.data(), erasure_positions.size()); if (result >= 0) { // Store decoded result into the shortened code vector @@ -769,7 +774,7 @@ int ReedSolomon::decode(std::vector& data_to_decode, const std::vector< } -int ReedSolomon::decode_rs_8(uint8_t* data, const int* eras_pos, int no_eras, int pad) const +int ReedSolomon::decode_rs_8(uint8_t* data, const int* eras_pos, int no_eras) const { int deg_lambda; int el; diff --git a/src/core/system_parameters/reed_solomon.h b/src/core/system_parameters/reed_solomon.h index ce0e68af8..049091ae1 100644 --- a/src/core/system_parameters/reed_solomon.h +++ b/src/core/system_parameters/reed_solomon.h @@ -123,7 +123,7 @@ private: 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; + int decode_rs_8(uint8_t* data, const int* eras_pos, int no_eras) const; uint8_t galois_mul(uint8_t a, uint8_t b) const; uint8_t galois_add(uint8_t a, uint8_t b) const; From a209bcc97932ce4b062633e434490e106ce5d77c Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Thu, 1 Apr 2021 14:29:46 +0200 Subject: [PATCH 20/23] Clean the constructor --- src/core/system_parameters/reed_solomon.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/system_parameters/reed_solomon.cc b/src/core/system_parameters/reed_solomon.cc index 45d97a55c..e78259be9 100644 --- a/src/core/system_parameters/reed_solomon.cc +++ b/src/core/system_parameters/reed_solomon.cc @@ -20,9 +20,10 @@ #include #include + ReedSolomon::ReedSolomon(const std::string& gnss_signal) { - if (gnss_signal.empty() or gnss_signal == "E6B") + if (gnss_signal.empty() || gnss_signal == "E6B") { d_nroots = 223; // number of parity symbols in a block d_min_poly = 29; // minimal poly @@ -292,8 +293,7 @@ ReedSolomon::ReedSolomon(const std::string& gnss_signal) 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}; } - - if (gnss_signal == "E1B") + else if (gnss_signal == "E1B") { d_nroots = 60; d_min_poly = 29; @@ -426,8 +426,7 @@ ReedSolomon::ReedSolomon(const std::string& gnss_signal) d_genpoly_coeff = {1, 208, 42, 48, 76, 19, 41, 108, 167, 235, 166, 244, 186, 18, 124, 251, 79, 193, 14, 154, 6, 118, 19, 3, 122, 9, 187, 42, 131, 46, 66, 65, 62, 94, 101, 45, 214, 141, 131, 230, 102, 20, 63, 202, 36, 23, 188, 88, 169, 62, 73, 88, 152, 197, 231, 58, 101, 154, 190, 91, 193}; } - - if (!gnss_signal.empty() && ((gnss_signal != "E6B") && gnss_signal != "E1B")) + else { std::cerr << "Unknown Reead Solomon configuration. Setting basic defaults for Galileo E6B.\n"; // Set E6B parameters by default From e95352492f5caf950c686c3d89b6371b6c498216 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 4 Apr 2021 10:29:59 +0200 Subject: [PATCH 21/23] Update local version of Protocol Buffers to 3.15.7 --- CMakeLists.txt | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b1ccd5b0..ba6a67356 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -330,7 +330,7 @@ set(GNSSSDR_GNSS_SIM_LOCAL_VERSION "master") set(GNSSSDR_GPSTK_LOCAL_VERSION "8.0.0") set(GNSSSDR_MATIO_LOCAL_VERSION "1.5.21") set(GNSSSDR_PUGIXML_LOCAL_VERSION "1.11.4") -set(GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION "3.15.6") +set(GNSSSDR_PROTOCOLBUFFERS_LOCAL_VERSION "3.15.7") set(GNSSSDR_BENCHMARK_LOCAL_VERSION "1.5.2") set(GNSSSDR_MATHJAX_EXTERNAL_VERSION "2.7.7") diff --git a/README.md b/README.md index fe445da66..8f6d16285 100644 --- a/README.md +++ b/README.md @@ -395,9 +395,9 @@ $ sudo apt-get install autoconf automake libtool curl make g++ unzip and then: ``` -$ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.15.6/protobuf-cpp-3.15.6.tar.gz -$ tar xvfz protobuf-cpp-3.15.6.tar.gz -$ cd protobuf-3.15.6 +$ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.15.7/protobuf-cpp-3.15.7.tar.gz +$ tar xvfz protobuf-cpp-3.15.7.tar.gz +$ cd protobuf-3.15.7 $ ./autogen.sh $ ./configure $ make From 13a83092da95ebe4f655d2a22426f425d2772d68 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 4 Apr 2021 11:01:14 +0200 Subject: [PATCH 22/23] Fix warning -Wunused-private-field raised by clang --- .../signal_source/adapters/fmcomms2_signal_source.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/algorithms/signal_source/adapters/fmcomms2_signal_source.cc b/src/algorithms/signal_source/adapters/fmcomms2_signal_source.cc index 9a7b9213f..de447a0e3 100644 --- a/src/algorithms/signal_source/adapters/fmcomms2_signal_source.cc +++ b/src/algorithms/signal_source/adapters/fmcomms2_signal_source.cc @@ -331,6 +331,15 @@ Fmcomms2SignalSource::Fmcomms2SignalSource(const ConfigurationInterface *configu file_sink_ = gr::blocks::file_sink::make(item_size_, dump_filename_.c_str()); DLOG(INFO) << "file_sink(" << file_sink_->unique_id() << ")"; } + + if (in_stream_ > 0) + { + LOG(ERROR) << "A signal source does not have an input stream"; + } + if (out_stream_ > 1) + { + LOG(ERROR) << "This implementation only supports one output stream"; + } } From 09b4c8f7a32c044d1eb1701acef6d1b5aba3a080 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Sun, 4 Apr 2021 11:24:55 +0200 Subject: [PATCH 23/23] Fix bug in searching for gr-iio when CMake is re-run several times (fixes #491) --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba6a67356..f85d03a81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2901,6 +2901,10 @@ if(GNURADIO_IIO_FOUND) set(GR_IIO_INCLUDE_HAS_GNURADIO TRUE) set(GNURADIO_API_IIO TRUE) else() + # Force to always search for gr-iio + unset(GRIIO_FOUND CACHE) + unset(IIO_INCLUDE_DIRS CACHE) + unset(IIO_LIBRARIES CACHE) find_package(GRIIO) set_package_properties(GRIIO PROPERTIES PURPOSE "Used for communication with PlutoSDR and FMCOMMS devices."