/// \file
/// \ingroup tutorial_math
/// \notebook -nodraw
/// Tutorial illustrating the use of ROOT::Math::LFSR::GenerateSequence
/// can be run with:
///
/// ~~~{.cpp}
/// root > .x PRBS.C
/// root > .x PRBS.C+ with ACLIC
/// ~~~
///
/// These values match the PRBS sequences generated by a Keysight waveform generator
/// except for the N first bits that appear at the end rather than at the beginning
/// \see https://www.sciencedirect.com/science/article/pii/S0168900222002431#fig6
///
/// \macro_output
/// \macro_code
///
/// \author Fernando Hueso-González

#include <Math/LFSR.h>
#include <iostream>

void PRBS()
{
   printf("\nLFSR::GenerateSequence PRBS3, PRBS4 and PRBS5 tests\n");
   printf("==========================\n");

   // PRBS3
   std::array<std::uint16_t, 2> taps3 = {3, 2};                                     // Exponents of the monic polynomial
   auto prbs3 = ROOT::Math::LFSR::GenerateSequence(std::bitset<3>().flip(), taps3); // Start value all high

   // PRBS4
   std::array<std::uint16_t, 2> taps4 = {4, 3};                                     // Exponents of the monic polynomial
   auto prbs4 = ROOT::Math::LFSR::GenerateSequence(std::bitset<4>().flip(), taps4); // Start value all high

   // PRBS5
   std::array<std::uint16_t, 2> taps5 = {5, 3};                                     // Exponents of the monic polynomial
   auto prbs5 = ROOT::Math::LFSR::GenerateSequence(std::bitset<5>().flip(), taps5); // Start value all high

   for (auto prbs : {prbs3, prbs4, prbs5}) {
      std::cout << "PRBS period " << prbs.size() << ":\t";
      for (auto p : prbs) {
         std::cout << static_cast<unsigned short>(p) << " ";
      }
      std::cout << std::endl;
   }

   // Find below all exponents of the monic polynomial extracted from
   // Keysight Trueform Series Operating and Service Guide p. 284-285
   // clang-format off
   // auto prbs3  = ROOT::Math::LFSR::GenerateSequence< 3, 2, bool>(bitset<3 >().flip(), std::array<std::uint16_t, 2>{3, 2});
   // auto prbs4  = ROOT::Math::LFSR::GenerateSequence< 4, 2, bool>(bitset<4 >().flip(), std::array<std::uint16_t, 2>{4, 3});
   // auto prbs5  = ROOT::Math::LFSR::GenerateSequence< 5, 2, bool>(bitset<5 >().flip(), std::array<std::uint16_t, 2>{5, 3});
   // auto prbs6  = ROOT::Math::LFSR::GenerateSequence< 6, 2, bool>(bitset<6 >().flip(), std::array<std::uint16_t, 2>{6, 5});
   // auto prbs7  = ROOT::Math::LFSR::GenerateSequence< 7, 2, bool>(bitset<7 >().flip(), std::array<std::uint16_t, 2>{7, 6});
   // auto prbs8  = ROOT::Math::LFSR::GenerateSequence< 8, 4, bool>(bitset<8 >().flip(), std::array<std::uint16_t, 4>{8, 6, 5, 4});
   // auto prbs9  = ROOT::Math::LFSR::GenerateSequence< 9, 2, bool>(bitset<9 >().flip(), std::array<std::uint16_t, 2>{9, 5});
   // auto prbs10 = ROOT::Math::LFSR::GenerateSequence<10, 2, bool>(bitset<10>().flip(), std::array<std::uint16_t, 2>{10, 7});
   // auto prbs11 = ROOT::Math::LFSR::GenerateSequence<11, 2, bool>(bitset<11>().flip(), std::array<std::uint16_t, 2>{11, 9});
   // auto prbs12 = ROOT::Math::LFSR::GenerateSequence<12, 4, bool>(bitset<12>().flip(), std::array<std::uint16_t, 4>{12, 6, 4, 1});
   // auto prbs13 = ROOT::Math::LFSR::GenerateSequence<13, 4, bool>(bitset<13>().flip(), std::array<std::uint16_t, 4>{13, 4, 3, 1});
   // auto prbs14 = ROOT::Math::LFSR::GenerateSequence<14, 4, bool>(bitset<14>().flip(), std::array<std::uint16_t, 4>{14, 5, 3, 1});
   // auto prbs15 = ROOT::Math::LFSR::GenerateSequence<15, 2, bool>(bitset<15>().flip(), std::array<std::uint16_t, 2>{15, 14});
   // auto prbs16 = ROOT::Math::LFSR::GenerateSequence<16, 4, bool>(bitset<16>().flip(), std::array<std::uint16_t, 4>{16, 15, 13, 4});
   // auto prbs17 = ROOT::Math::LFSR::GenerateSequence<17, 2, bool>(bitset<17>().flip(), std::array<std::uint16_t, 2>{17, 14});
   // auto prbs18 = ROOT::Math::LFSR::GenerateSequence<18, 2, bool>(bitset<18>().flip(), std::array<std::uint16_t, 2>{18, 11});
   // auto prbs19 = ROOT::Math::LFSR::GenerateSequence<19, 4, bool>(bitset<19>().flip(), std::array<std::uint16_t, 4>{19, 6, 2, 1});
   // auto prbs20 = ROOT::Math::LFSR::GenerateSequence<20, 2, bool>(bitset<20>().flip(), std::array<std::uint16_t, 2>{20, 17});
   // auto prbs21 = ROOT::Math::LFSR::GenerateSequence<21, 2, bool>(bitset<21>().flip(), std::array<std::uint16_t, 2>{21, 19});
   // auto prbs22 = ROOT::Math::LFSR::GenerateSequence<22, 2, bool>(bitset<22>().flip(), std::array<std::uint16_t, 2>{22, 21});
   // auto prbs23 = ROOT::Math::LFSR::GenerateSequence<23, 2, bool>(bitset<23>().flip(), std::array<std::uint16_t, 2>{23, 18});
   // auto prbs24 = ROOT::Math::LFSR::GenerateSequence<24, 4, bool>(bitset<24>().flip(), std::array<std::uint16_t, 4>{24, 23, 22, 17}, true); // Turn allow wrapping to save memory
   // auto prbs25 = ROOT::Math::LFSR::GenerateSequence<25, 2, bool>(bitset<25>().flip(), std::array<std::uint16_t, 2>{25, 22}, true); // Turn allow wrapping to save memory
   // auto prbs26 = ROOT::Math::LFSR::GenerateSequence<26, 4, bool>(bitset<26>().flip(), std::array<std::uint16_t, 4>{26, 6, 2, 1}, true); // Turn allow wrapping to save memory
   // auto prbs27 = ROOT::Math::LFSR::GenerateSequence<27, 4, bool>(bitset<27>().flip(), std::array<std::uint16_t, 4>{27, 5, 2, 1}, true); // Turn allow wrapping to save memory
   // auto prbs28 = ROOT::Math::LFSR::GenerateSequence<28, 2, bool>(bitset<28>().flip(), std::array<std::uint16_t, 2>{28, 25}, true); // Turn allow wrapping to save memory
   // auto prbs29 = ROOT::Math::LFSR::GenerateSequence<29, 4, bool>(bitset<29>().flip(), std::array<std::uint16_t, 4>{29, 27}, true); // Turn allow wrapping to save memory
   // auto prbs30 = ROOT::Math::LFSR::GenerateSequence<30, 4, bool>(bitset<30>().flip(), std::array<std::uint16_t, 4>{30, 6, 4, 1}, true); // Turn allow wrapping to save memory
   // auto prbs31 = ROOT::Math::LFSR::GenerateSequence<31, 2, bool>(bitset<31>().flip(), std::array<std::uint16_t, 2>{31, 28}, true); // Turn allow wrapping to save memory
   // auto prbs32 = ROOT::Math::LFSR::GenerateSequence<32, 4, bool>(bitset<32>().flip(), std::array<std::uint16_t, 4>{32, 22, 2, 1}, true); // Turn allow wrapping to save memory
   // clang-format on
}
