Skip to content

Commit 7b337b4

Browse files
committed
[math] LFSR: allow setting output typename, default to uchar
also remove const qualifiers from signature since they are copied by value and apply clang-formatting
1 parent e777007 commit 7b337b4

File tree

3 files changed

+106
-100
lines changed

3 files changed

+106
-100
lines changed

math/mathcore/inc/Math/LFSR.h

Lines changed: 93 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -45,109 +45,115 @@ namespace ROOT::Math::LFSR {
4545
* @throw an exception is thrown if taps are out of the range [1, k]
4646
* @see https://en.wikipedia.org/wiki/Monic_polynomial, https://en.wikipedia.org/wiki/Linear-feedback_shift_register, https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence
4747
*/
48-
template <size_t k, size_t nTaps>
49-
static bool
50-
NextLFSR(std::bitset<k>& lfsr, const std::array<uint16_t, nTaps> taps, const bool left = true)
51-
{
52-
static_assert(k <= 32, "For the moment, only supported until k == 32.");
53-
static_assert(k > 0, "Non-zero degree is needed for the LFSR.");
54-
static_assert(nTaps > 0, "At least one tap is needed for the LFSR.");
55-
static_assert(nTaps <= k, "Cannot use more taps than polynomial order");
56-
57-
// First, calculate the XOR (^) of all selected bits (marked by the taps)
58-
bool newBit = lfsr[taps.at(0) - 1]; // the exponent E of the polynomial correspond to index E - 1 in the bitset
59-
for(uint16_t j = 1; j < nTaps ; ++j)
60-
{
61-
newBit ^= lfsr[taps.at(j) - 1];
62-
}
63-
64-
//Apply the shift to the register in the right direction, and overwrite the empty one with newBit
65-
if(left)
66-
{
67-
lfsr <<= 1;
68-
lfsr[0] = newBit;
69-
}
70-
else
71-
{
72-
lfsr >>= 1;
73-
lfsr[k-1] = newBit;
74-
}
48+
template <size_t k, size_t nTaps>
49+
static bool NextLFSR(std::bitset<k> &lfsr, std::array<uint16_t, nTaps> taps, bool left = true)
50+
{
51+
static_assert(k <= 32, "For the moment, only supported until k == 32.");
52+
static_assert(k > 0, "Non-zero degree is needed for the LFSR.");
53+
static_assert(nTaps > 0, "At least one tap is needed for the LFSR.");
54+
static_assert(nTaps <= k, "Cannot use more taps than polynomial order");
55+
56+
// First, calculate the XOR (^) of all selected bits (marked by the taps)
57+
bool newBit = lfsr[taps[0] - 1]; // the exponent E of the polynomial correspond to index E - 1 in the bitset
58+
for (uint16_t j = 1; j < nTaps; ++j) {
59+
newBit ^= lfsr[taps[j] - 1];
60+
}
7561

76-
return newBit;
62+
// Apply the shift to the register in the right direction, and overwrite the empty one with newBit
63+
if (left) {
64+
lfsr <<= 1;
65+
lfsr[0] = newBit;
66+
} else {
67+
lfsr >>= 1;
68+
lfsr[k - 1] = newBit;
7769
}
7870

79-
/**
80-
* @brief Generation of a sequence of pseudo-random bits using a linear feedback shift register (LFSR), until a register value is repeated (or maxPeriod is reached)
81-
* @tparam k the length of the LFSR, usually also the order of the monic polynomial PRBS-k (last exponent)
82-
* @tparam nTaps the number of taps
83-
* @param start the start value (seed) of the LFSR
84-
* @param taps the taps that will be XOR-ed to calculate the new bit. They are the exponents of the monic polynomial. Ordering is unimportant. Note that an exponent E in the polynom maps to bit index E-1 in the LFSR.
85-
* @param left if true, the direction of the register shift is to the left <<, the newBit is set on lfsr at bit position 0 (right). If false, shift is to the right and the newBit is stored at bit position (k-1)
86-
* @param wrapping if true, allow repetition of values in the LFSRhistory, until maxPeriod is reached or the repeated value == start. Enabling this option saves memory as no history is kept
87-
* @param oppositeBit if true, use the high/low bit of the LFSR to store output (for left=true/false, respectively) instead of the newBit returned by ::NextLFSR
88-
* @return the array of pseudo random bits, or an empty array if input was incorrect
89-
* @see https://en.wikipedia.org/wiki/Monic_polynomial, https://en.wikipedia.org/wiki/Linear-feedback_shift_register, https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence
90-
*/
91-
template <size_t k, size_t nTaps>
92-
static std::vector<bool>
93-
GenerateSequence(const std::bitset<k> start, const std::array<uint16_t, nTaps> taps, const bool left = true, const bool wrapping = false, const bool oppositeBit = false)
94-
{
95-
std::vector<bool> result; // Store result here
96-
97-
//Sanity-checks
98-
static_assert(k <= 32, "For the moment, only supported until k == 32.");
99-
static_assert(k > 0, "Non-zero degree is needed for the LFSR.");
100-
static_assert(nTaps >= 2, "At least two taps are needed for a proper sequence");
101-
static_assert(nTaps <= k, "Cannot use more taps than polynomial order");
102-
for(auto tap : taps) {
103-
if(tap > k || tap == 0) {
104-
Error("ROOT::Math::LFSR", "Tap %u is out of range [1,%lu]", tap, k);
105-
return result;
106-
}
107-
}
108-
if(start.none()) {
109-
Error("ROOT::Math::LFSR", "A non-zero start value is needed");
71+
return newBit;
72+
}
73+
74+
/**
75+
* @brief Generation of a sequence of pseudo-random bits using a linear feedback shift register (LFSR), until a
76+
* register value is repeated (or maxPeriod is reached)
77+
* @tparam k the length of the LFSR, usually also the order of the monic polynomial PRBS-k (last exponent)
78+
* @tparam nTaps the number of taps
79+
* @tparam Output the type of the container where the bit result (0 or 1) is stored (e.g. char, bool). It's unsigned
80+
* char by default, use bool instead if you want to save memory
81+
* @param start the start value (seed) of the LFSR
82+
* @param taps the taps that will be XOR-ed to calculate the new bit. They are the exponents of the monic polynomial.
83+
* Ordering is unimportant. Note that an exponent E in the polynom maps to bit index E-1 in the LFSR.
84+
* @param[out] iterator container storing the array of pseudo random bits (iterator is left untouched if input
85+
* parameters were incorrect) Pass for example an iterator to std::vector<unsigned char> (or to bool if memory saving
86+
* is important)
87+
* @param left if true, the direction of the register shift is to the left <<, the newBit is set on lfsr at bit
88+
* position 0 (right). If false, shift is to the right and the newBit is stored at bit position (k-1)
89+
* @param wrapping if true, allow repetition of values in the LFSRhistory, until maxPeriod is reached or the repeated
90+
* value == start. Enabling this option saves memory as no history is kept
91+
* @param oppositeBit if true, use the high/low bit of the LFSR to store output (for left=true/false, respectively)
92+
* instead of the newBit returned by ::NextLFSR
93+
* @return the array of pseudo random bits, or an empty array if input was incorrect
94+
* @see https://en.wikipedia.org/wiki/Monic_polynomial, https://en.wikipedia.org/wiki/Linear-feedback_shift_register,
95+
* https://en.wikipedia.org/wiki/Pseudorandom_binary_sequence
96+
*/
97+
template <size_t k, size_t nTaps, typename Output = unsigned char>
98+
static std::vector<Output> GenerateSequence(std::bitset<k> start, std::array<uint16_t, nTaps> taps, bool left = true,
99+
bool wrapping = false, bool oppositeBit = false)
100+
{
101+
std::vector<bool> result; // Store result here
102+
103+
// Sanity-checks
104+
static_assert(k <= 32, "For the moment, only supported until k == 32.");
105+
static_assert(k > 0, "Non-zero degree is needed for the LFSR.");
106+
static_assert(nTaps >= 2, "At least two taps are needed for a proper sequence");
107+
static_assert(nTaps <= k, "Cannot use more taps than polynomial order");
108+
for (auto tap : taps) {
109+
if (tap > k || tap == 0) {
110+
Error("ROOT::Math::LFSR", "Tap %u is out of range [1,%lu]", tap, k);
110111
return result;
111112
}
113+
}
114+
if (start.none()) {
115+
Error("ROOT::Math::LFSR", "A non-zero start value is needed");
116+
return result;
117+
}
112118

113-
// Calculate maximum period and pre-allocate space in result
114-
const uint32_t maxPeriod = pow(2,k) - 1;
115-
result.reserve(maxPeriod);
119+
// Calculate maximum period and pre-allocate space in result
120+
const uint32_t maxPeriod = pow(2, k) - 1;
121+
result.reserve(maxPeriod);
116122

117-
std::set<uint32_t> lfsrHistory; // a placeholder to store the history of all different values of the LFSR
118-
std::bitset<k> lfsr(start); // a variable storing the current value of the LFSR
119-
uint32_t i = 0; // a loop counter
120-
if(oppositeBit) // if oppositeBit enabled, first value is already started with the seed
121-
result.emplace_back(left ? lfsr[k-1] : lfsr[0]);
123+
std::set<uint32_t> lfsrHistory; // a placeholder to store the history of all different values of the LFSR
124+
std::bitset<k> lfsr(start); // a variable storing the current value of the LFSR
125+
uint32_t i = 0; // a loop counter
126+
if (oppositeBit) // if oppositeBit enabled, first value is already started with the seed
127+
result.emplace_back(left ? lfsr[k - 1] : lfsr[0]);
122128

123-
//Loop now until maxPeriod or a lfsr value is repeated. If wrapping enabled, allow repeated values if not equal to seed
124-
do {
125-
bool newBit = NextLFSR(lfsr, taps, left);
129+
// Loop now until maxPeriod or a lfsr value is repeated. If wrapping enabled, allow repeated values if not equal
130+
// to seed
131+
do {
132+
bool newBit = NextLFSR(lfsr, taps, left);
126133

127-
if(!oppositeBit)
128-
result.emplace_back(newBit);
129-
else
130-
result.emplace_back(left ? lfsr[k-1] : lfsr[0]);
134+
if (!oppositeBit)
135+
result.emplace_back(newBit);
136+
else
137+
result.emplace_back(left ? lfsr[k - 1] : lfsr[0]);
131138

132-
++i;
139+
++i;
133140

134-
if(!wrapping) // If wrapping not allowed, break the loop once a repeated value is encountered
135-
{
136-
if(lfsrHistory.count(lfsr.to_ulong()))
137-
break;
141+
if (!wrapping) // If wrapping not allowed, break the loop once a repeated value is encountered
142+
{
143+
if (lfsrHistory.count(lfsr.to_ulong()))
144+
break;
138145

139-
lfsrHistory.insert(lfsr.to_ulong()); // Add to the history
140-
}
146+
lfsrHistory.insert(lfsr.to_ulong()); // Add to the history
141147
}
142-
while(lfsr != start && i < maxPeriod);
148+
} while (lfsr != start && i < maxPeriod);
143149

144-
if(oppositeBit)
145-
result.pop_back();// remove last element, as we already pushed the one from the seed above the while loop
150+
if (oppositeBit)
151+
result.pop_back(); // remove last element, as we already pushed the one from the seed above the while loop
146152

147-
result.shrink_to_fit();//only some special taps will lead to the maxPeriod, others will stop earlier
153+
result.shrink_to_fit(); // only some special taps will lead to the maxPeriod, others will stop earlier
148154

149-
return result;
150-
}
155+
return;
156+
}
151157
}
152158

153159
#endif

math/mathcore/test/testLFSR.cxx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
TEST(LFSR, GenerateSequence)
66
{
77
// PRBS3
8-
std::array<uint16_t, 2> taps3 = {2, 3}; // Exponents of the monic polynomial
9-
auto prbs3 = ROOT::Math::LFSR::GenerateSequence(std::bitset<3>().flip(), taps3); // Start value all high
8+
std::array<uint16_t, 2> taps3 = {2, 3}; // Exponents of the monic polynomial
9+
auto prbs3 = ROOT::Math::LFSR::GenerateSequence<3, 2, bool>(std::bitset<3>().flip(), taps3); // Start value all high
1010
EXPECT_EQ(prbs3, std::vector<bool>({false, false, true, false, true, true, true}));
1111

1212
// PRBS4
13-
std::array<uint16_t, 2> taps4 = {3, 4}; // Exponents of the monic polynomial
14-
auto prbs4 = ROOT::Math::LFSR::GenerateSequence(std::bitset<4>().flip(), taps4); // Start value all high
13+
std::array<uint16_t, 2> taps4 = {3, 4}; // Exponents of the monic polynomial
14+
auto prbs4 = ROOT::Math::LFSR::GenerateSequence<4, 2, bool>(std::bitset<4>().flip(), taps4); // Start value all high
1515
EXPECT_EQ(prbs4, std::vector<bool>({false, false, false, true, false, false, true, true, false, true, false, true,
1616
true, true, true}));
1717

1818
// PRBS7
19-
std::array<uint16_t, 2> taps5 = {5, 3}; // Exponents of the monic polynomial
20-
auto prbs5 = ROOT::Math::LFSR::GenerateSequence(std::bitset<5>().flip(), taps5); // Start value all high
19+
std::array<uint16_t, 2> taps5 = {5, 3}; // Exponents of the monic polynomial
20+
auto prbs5 = ROOT::Math::LFSR::GenerateSequence<5, 2, bool>(std::bitset<5>().flip(), taps5); // Start value all high
2121
EXPECT_EQ(prbs5, std::vector<bool>({false, false, false, true, true, false, true, true, true, false, true,
2222
false, true, false, false, false, false, true, false, false, true, false,
2323
true, true, false, false, true, true, true, true, true}));

tutorials/math/PRBS.C

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,21 @@ void PRBS()
2727
printf("==========================\n");
2828

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

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

3737
// PRBS7
38-
std::array<uint16_t, 2> taps5 = {5, 3}; // Exponents of the monic polynomial
39-
std::vector<bool> prbs5 = ROOT::Math::LFSR::GenerateSequence(std::bitset<5>().flip(), taps5); // Start value all high
38+
std::array<uint16_t, 2> taps5 = {5, 3}; // Exponents of the monic polynomial
39+
auto prbs5 = ROOT::Math::LFSR::GenerateSequence(std::bitset<5>().flip(), taps5); // Start value all high
4040

4141
for (auto prbs : {prbs3, prbs4, prbs5}) {
4242
std::cout << "PRBS period " << prbs.size() << ":\t";
4343
for (auto p : prbs) {
44-
std::cout << p << " ";
44+
std::cout << static_cast<unsigned short>(p) << " ";
4545
}
4646
std::cout << std::endl;
4747
}

0 commit comments

Comments
 (0)