File Coverage

blib/lib/Math/PRBS.pm
Criterion Covered Total %
statement 126 126 100.0
branch 74 74 100.0
condition 30 30 100.0
subroutine 21 21 100.0
pod 16 16 100.0
total 267 267 100.0


line stmt bran cond sub pod time code
1             =head1 NAME
2            
3             Math::PRBS - Generate Pseudorandom Binary Sequences using an Iterator-based Linear Feedback Shift Register
4            
5             =cut
6             package Math::PRBS;
7 7     7   115746 use warnings;
  7         12  
  7         248  
8 7     7   32 use strict;
  7         10  
  7         186  
9            
10 7     7   3508 use version 0.77; our $VERSION = version->declare('0.002');
  7         12414  
  7         42  
11            
12             =head1 SYNOPSIS
13            
14             use Math::PRBS;
15             my $x3x2 = Math::PRBS->new( taps => [3,2] );
16             my $prbs7 = Math::PRBS->new( prbs => 7 );
17             my ($i, $value) = $x3x2t->next();
18             my @p7 = $prbs7->generate_all();
19            
20             =head1 DESCRIPTION
21            
22             This module will generate various Pseudorandom Binary Sequences (PRBS). This module creates a iterator object, and you can use that object to generate the sequence one value at a time, or I.
23            
24             The generated sequence is a series of 0s and 1s which appears random for a certain length, and then repeats thereafter.
25            
26             It is implemented using an XOR-based Linear Feedback Shift Register (LFSR), which is described using a feedback polynomial (or reciprocal characteristic polynomial). The terms that appear in the polynomial are called the 'taps', because you tap off of that bit of the shift register for generating the feedback for the next value in the sequence.
27            
28             =head1 FUNCTIONS AND METHODS
29            
30             =head2 Initialization
31            
32             =over
33            
34             =item C<$seq = Math::PRBS::new( I value> )>
35            
36             Creates the sequence iterator C<$seq> using one of the C value> pairs described below.
37            
38             =cut
39            
40             sub new {
41 25     25 1 3918 my $class = shift;
42 25         111 my $self = bless { lfsr => 0, start => 0, i => 0, period => undef, taps => [] }, $class;
43 25         27 my %pairs;
44 25 100       94 %pairs = @_%2 ? () : @_;
45            
46             =over
47            
48             =item C I>
49            
50             C needs an integer I to indicate one of the "standard" PRBS polynomials.
51            
52             # example: PRBS7 = x**7 + x**6 + 1
53             $seq = Math::PRBS::new( ptbs => 7 );
54            
55             The "standard" PRBS polynomials implemented are
56            
57             polynomial | prbs | taps | poly (string)
58             ------------------+------------+-----------------+---------------
59             x**7 + x**6 + 1 | prbs => 7 | taps => [7,6] | poly => '1100000'
60             x**15 + x**14 + 1 | prbs => 15 | taps => [15,14] | poly => '110000000000000'
61             x**23 + x**18 + 1 | prbs => 23 | taps => [23,18] | poly => '10000100000000000000000'
62             x**31 + x**28 + 1 | prbs => 31 | taps => [31,28] | poly => '1001000000000000000000000000000'
63            
64             =cut
65            
66 25 100       75 if( exists $pairs{prbs} )
    100          
    100          
67             {
68 5         27 my %prbs = (
69             7 => [7,6] ,
70             15 => [15,14],
71             23 => [23,18],
72             31 => [31,28],
73             );
74 5 100       26 die __PACKAGE__."::new(prbs => '$pairs{prbs}'): standard PRBS include 7, 15, 23, 31" unless exists $prbs{ $pairs{prbs} };
75 4         5 $self->{taps} = [ @{ $prbs{ $pairs{prbs} } } ];
  4         19  
76             }
77            
78             =item C [ I, I, ... ]>
79            
80             C needs an array reference containing the powers in the polynomial that you tap off for creating the feedback. Do I include the C<0> for the C in the polynomial; that's automatically included.
81            
82             # example: x**3 + x**2 + 1
83             # 3 and 2 are taps, 1 is not tapped, 0 is implied feedback
84             $seq = Math::PRBS::new( taps => [3,2] );
85            
86             =cut
87            
88             elsif( exists $pairs{taps} )
89             {
90 9 100       41 die __PACKAGE__."::new(taps => $pairs{taps}): argument should be an array reference" unless 'ARRAY' eq ref($pairs{taps});
91 8         12 $self->{taps} = [ sort {$b <=> $a} @{ $pairs{taps} } ]; # taps in descending order
  9         40  
  8         41  
92 8 100       11 die __PACKAGE__."::new(taps => [@{$pairs{taps}}]): need at least one tap" unless @{ $pairs{taps} };
  1         8  
  8         31  
93             }
94            
95             =item C '...'>
96            
97             C needs a string for the bits C downto C, with a 1 indicating the power is included in the list, and a 0 indicating it is not.
98            
99             # example: x**3 + x**2 + 1
100             # 3 and 2 are taps, 1 is not tapped, 0 is implied feedback
101             $seq = Math::PRBS::new( poly => '110' );
102            
103             =cut
104            
105             elsif( exists $pairs{poly} )
106             {
107 9         11 local $_ = $pairs{poly}; # used for implicit matching in die-unless and while-condition
108 9 100       49 die __PACKAGE__."::new(poly => '$pairs{poly}'): argument should be an binary string" unless /^[01]*$/;
109 7         11 my @taps = ();
110 7         7 my $l = length;
111 7         23 while( m/([01])/g ) {
112 21 100       73 push @taps, $l - pos() + 1 if $1;
113             }
114 7         18 $self->{taps} = [ reverse sort {$a <=> $b} @taps ];
  6         19  
115 7 100       28 die __PACKAGE__."::new(poly => '$pairs{poly}'): need at least one tap" unless @taps;
116             } else {
117 2         22 die __PACKAGE__."::new(".join(',',@_)."): unknown arguments";
118             }
119            
120 17         70 $self->{lfsr} = oct('0b1' . '0'x($self->{taps}[0] - 1));
121 17         25 $self->{start} = $self->{lfsr};
122            
123 17         50 return $self;
124             }
125            
126             =back
127            
128             =item C<$seq-Ereset()>
129            
130             Reinitializes the sequence: resets the sequence back to the starting state. The next call to C will be the initial C<$i,$value> again.
131            
132             =cut
133            
134             sub reset {
135 14     14 1 23 my $self = shift;
136 14         32 $self->{lfsr} = $self->{start};
137 14         21 $self->{i} = 0;
138 14         19 return $self;
139             }
140            
141             =back
142            
143             =head2 Iteration
144            
145             =over
146            
147             =item C<$value = $seq-Enext()>
148            
149             =item C<($i, $value) = $seq-Enext()>
150            
151             Computes the next value in the sequence. (Optionally, in list context, also returns the current value of the i for the sequence.)
152            
153             =cut
154            
155             sub next {
156 771085     771085 1 502222 my $self = shift;
157 771085         446680 my $newbit = 0;
158 771085         849870 my $mask = oct( '0b' . '1'x($self->{taps}[0]) );
159 771085         517027 my $i = $self->{i};
160 771085         471046 ++ $self->{i};
161            
162 771085         434629 $newbit ^= ( $self->{lfsr} >> ($_-1) ) & 1 for @{ $self->{taps} };
  771085         1481574  
163            
164 771085         640207 $self->{lfsr} = (($self->{lfsr} << 1) | $newbit) & $mask;
165            
166 771085 100 100     2591422 $self->{period} = $i+1 if $i && !defined($self->{period}) && ($self->{lfsr} eq $self->{start});
      100        
167            
168 771085 100       1531761 return wantarray ? ( $i , $newbit ) : $newbit;
169             }
170            
171             =item C<$seq-Erewind()>
172            
173             Rewinds the sequence back to the starting state. The subsequent call to C will be the initial C<$i,$value> again.
174             (This is actually an alias for C).
175            
176             =cut
177            
178 7     7   5545 BEGIN { *rewind = \&reset; } # alias
179            
180             =item C<$i = $seq-Etell_i()>
181            
182             Return the current C position. The subsequent call to C will return this C.
183            
184             =cut
185            
186             sub tell_i {
187 27     27 1 4344 return $_[0]->{i};
188             }
189            
190             =item C<$state = $seq-Etell_state()>
191            
192             Return the current internal state of the feedback register. Useful for debug, or plugging into C<-Eseek_to_state($state)> to get back to this state at some future point in the program.
193            
194             =cut
195            
196             sub tell_state {
197 21     21 1 87 return $_[0]->{lfsr};
198             }
199            
200             =item C<$seq-Eseek_to_i( $n )>
201            
202             =item C<$seq-Eith( $n )>
203            
204             Moves forward in the sequence until C reaches C<$n>. If C $n> already, will internally C first. If C<$n E period>, it will stop at the end of the period, instead.
205            
206             =cut
207            
208             sub seek_to_i {
209 10     10 1 38 my $self = shift;
210 10         16 my $n = shift;
211            
212 10 100       55 $self->rewind() if $self->{i} > $n;
213 10   100     144 $self->next() while(($self->{i} < $n) && !(defined($self->{period}) && ($self->{i} >= $self->{period})));
      100        
214             }
215            
216 7     7   12736 BEGIN { *ith = \&seek_to_i; } # alias
217            
218             =item C<$seq-Eseek_to_state( $lfsr )>
219            
220             Moves forward in the sequence until the internal LFSR state reaches C<$lfsr>. It will wrap around, if necessary, but will stop once the internal state returns to the starting point.
221            
222             =cut
223            
224             sub seek_to_state {
225 3     3 1 6 my $self = shift;
226 3         5 my $lfsr = shift;
227 3         6 my $state = $self->{lfsr};
228            
229             #local $\ = "\n";
230             #local $, = "\t";
231             #print STDERR __LINE__, "seek_to_state($lfsr):", $self->{i}, $self->{lfsr};
232 3 100       13 $self->next() unless $state == $lfsr;
233             #print STDERR __LINE__, "seek_to_state($lfsr):", $self->{i}, $self->{lfsr};
234 3   100     27 $self->next() while ($self->{lfsr} != $lfsr) && ($self->{lfsr} != $state); # Devel::Cover = the coverage hole is because I am testing for a condition that shouldn't be possible: getting back to initial state without ever having
235             #print STDERR __LINE__, "seek_to_state($lfsr):", $self->{i}, $self->{lfsr};
236             }
237            
238             =item C<$seq-Eseek_forward_n( $n )>
239            
240             Moves forward in the sequence C<$n> steps.
241            
242             =cut
243            
244             sub seek_forward_n {
245 4     4 1 11 my $self = shift;
246 4         5 my $n = shift;
247            
248 4         20 $self->next() for 1 .. $n;
249             }
250            
251             =item C<$seq-Eseek_to_end()>
252            
253             =item C<$seq-Eseek_to_end( limit =E $n )>
254            
255             Moves forward until it's reached the end of the the period. (Will start in the first period using C.)
256            
257             If C $n> is used, will not seek beyond C.
258            
259             =cut
260            
261             sub seek_to_end {
262 7     7 1 12 my $self = shift;
263            
264 7 100       37 die __PACKAGE__."::generate_to_end(@_) requires even number of arguments, expecting name=>value pairs" unless 0 == @_ % 2;
265            
266 6         27 my %opts = map lc, @_; # lowercase name,value pairs for canonical
267 6 100       18 my $limit = exists $opts{limit} ? $opts{limit} : 65535;
268 6 100       24 $limit = (2 ** $self->{taps}[0] - 1) if lc($limit) eq 'max';
269 6 100 100     39 $self->{i} %= $self->{period} if defined $self->{period} && $self->{i} > $self->{period};
270 6         22 while( $self->{i} % $limit ) {
271 245837         253385 $self->next();
272 245837 100 100     772766 $limit = $self->{period} if defined $self->{period} && $self->{period} < $limit; # pick PERIOD if PERIOD smaller than LIMIT
273             }
274             }
275            
276             =item C<@all = $seq-Egenerate( I )>
277            
278             Generates the next I values in the sequence, wrapping around if it reaches the end. In list context, returns the values as a list; in scalar context, returns the string concatenating that list.
279            
280             =cut
281            
282             sub generate {
283 4     4 1 1572 my ($self, $n) = @_;
284 4         6 my @ret = ();
285 4         10 foreach( 1 .. $n ) {
286 318         299 push @ret, scalar $self->next(); # need to force the scalar version to not push (i,value)
287             }
288 4 100       55 return wantarray ? @ret : join '', @ret;
289             }
290            
291             =item C<@all = $seq-Egenerate_all( )>
292            
293             =item C<@all = $seq-Egenerate_all( I $max_i> )>
294            
295             Returns the whole sequence, from the beginning, up to the end of the sequence; in list context, returns the list of values; in scalar context, returns the string concatenating that list. If the sequence is longer than the default limit of 65535, or the limit given by C<$max_i> if the optional C $max_i> is provided, then it will stop before the end of the sequence.
296            
297             =item C<@all = $seq-Egenerate_to_end( )>
298            
299             =item C<@all = $seq-Egenerate_to_end( I $max_i> )>
300            
301             Returns the remaining sequence, from whatever state the list is currently at, up to the end of the sequence; in list context, returns the list of values; in scalar context, returns the string concatenating that list. The limits work just as with C.
302            
303             =cut
304            
305             sub generate_to_end {
306 18     18 1 496 my $self = shift;
307            
308 18 100       64 die __PACKAGE__."::generate_to_end(@_) requires even number of arguments, expecting name=>value pairs" unless 0 == @_ % 2;
309            
310 17         57 my %opts = map lc, @_; # lowercase name,value pairs for canonical
311 17 100       43 my $limit = exists $opts{limit} ? $opts{limit} : 65535;
312 17 100       43 $limit = (2 ** $self->{taps}[0] - 1) if lc($limit) eq 'max';
313             # originally, the next block was "$self->rewind() if ($self->{i} && $opts{rewind});", but Devel::Cover complained that not all conditions were tested
314             # so I broke it into the following, which made Devel::Cover happy: note, it doesn't matter whether the i comes before or after rewind
315 17 100       35 if ($self->{i}) {
316 7 100       18 if( $opts{rewind} ) {
317 2         6 $self->rewind();
318             }
319             }
320 17 100 100     68 $self->{i} %= $self->{period} if defined $self->{period} && $self->{i} > $self->{period};
321 17         14 my $ret = '';
322 17         36 while( $self->{i} < $limit ) {
323 278986         266842 $ret .= scalar $self->next(); # need to force the scalar version to not push (i,value)
324 278986 100 100     711454 $limit = $self->{period} if defined $self->{period} && $self->{period} < $limit; # pick PERIOD if PERIOD smaller than LIMIT
325             }
326 17 100       325 return wantarray ? split(//, $ret) : $ret;
327             }
328            
329             sub generate_all {
330 7     7 1 22 my $self = shift;
331            
332 7 100       30 die __PACKAGE__."::generate_all(@_) requires even number of arguments, expecting name=>value pairs" unless 0 == @_ % 2;
333            
334 6         37 my %opts = @_;
335 6         17 $opts{rewind} = 1; # override existing rewind value
336 6         19 return generate_to_end($self, %opts);
337             }
338            
339             =back
340            
341             =head2 Information
342            
343             =over
344            
345             =item C<$i = $seq-Edescription>
346            
347             Returns a string describing the sequence in terms of the polynomial.
348            
349             $prbs7->description # "PRBS from polynomial x**7 + x**6 + 1"
350            
351             =cut
352            
353             sub description {
354 10     10 1 746 my $self = shift;
355 10         15 my $p = '';
356 10         8 foreach ( reverse sort {$a <=> $b} @{ $self->{taps} } ) {
  11         25  
  10         35  
357 21 100       42 $p .= ' + ' if $p;
358 21         36 $p .= "x**$_";
359             }
360 10         49 return "PRBS from polynomial $p + 1";
361             }
362            
363             =item C<$i = $seq-Etaps>
364            
365             Returns an array-reference containing the list of tap identifiers, which could then be passed to C<-Enew(taps =E ...)>.
366            
367             my $old_prbs = ...;
368             my $new_prbs = Math::PRBS->new( taps => $old_prbs->taps() );
369            
370             =cut
371            
372             sub taps {
373 11     11 1 22 my @taps = @{ $_[0]->{taps} };
  11         32  
374 11         64 return [@taps];
375             }
376            
377             =item C<$i = $seq-Eperiod( I 'estimate' | $n | 'max'> )>
378            
379             Returns the period of the sequence.
380            
381             Without any arguments, will return undef if the period hasn't been determined yet (ie, haven't travelled far enough in the sequence):
382            
383             $i = $seq->period(); # unknown => undef
384            
385             If I is set to 'estimate', will return C if the period hasn't been determined yet:
386            
387             $i = $seq->period(force => 'estimate'); # unknown => 2**k - 1
388            
389             If I is set to an integer C<$n>, it will try to generate the whole sequence (up to C= $n>), and return the period if found, or undef if not found.
390            
391             $i = $seq->period(force => $n); # look until $n; undef if sequence period still not found
392            
393             If I is set 'max', it will loop thru the entire sequence (up to C), and return the period that was found. It will still return undef if still not found, but all sequences B find the period within C<2**k-1>. If you find a sequence that doesn't, feel free to file a bug report, including the Cnew()> command listing the taps array or poly string; if C is greater than C<32>, please include a code that fixes the bug in the bug report, as development resources may not allow for debug of issues when C 32>.
394            
395             $i = $seq->period(force => 'max'); # look until 2**k - 1; undef if sequence period still not found
396            
397            
398             =cut
399            
400             sub period {
401 17     17 1 1089 my $self = shift;
402 17 100       70 return $self->{period} if defined $self->{period}; # if period's already computed, use it
403            
404 7 100       29 die __PACKAGE__."::period(@_) requires even number of arguments, expecting name=>value pairs" unless 0 == @_ % 2;
405            
406 6         26 my %opts = map lc, @_; # lowercase the arguments and make them into a canonical hash
407 6 100       16 my $force = exists($opts{force}) ? $opts{force} : 'not';
408 6 100       43 return $self->{period} if 'not' eq $force; # if not forced, return the undefined period
409            
410 4         10 my $max = 2**$self->{taps}[0] - 1; # if forced, max is 2**k-1
411 4 100       12 return $max if $force eq 'estimate'; # if estimate, guess that period is maximal
412            
413 3 100       11 $max = $force unless $force =~ /[^\d]/; # don't change max if force is a string (or negative, or floatingpoint)
414            
415             # ... loop thru all, until limit is reached or period is defined
416 3   100     21 $self->next while $self->{i}<$max && !defined $self->{period};
417            
418 3         31 return $self->{period};
419             }
420            
421             =item C<$i = $seq-Eoeis_anum>
422            
423             For known polynomials, return the L "A" number. For example, you can go to L to look at the sequence A011686.
424            
425             Not all maximum-length PRBS sequences (binary m-sequences) are in OEIS. Of the four "standard" PRBS (7, 15, 23, 31) mentioned above, only PRBS7 is there, as L. If you have the A-number for other m-sequences that aren't included below, please let the module maintainer know.
426            
427             Polynomial | Taps | OEIS
428             ----------------------------------------------+-----------------------+---------
429             x**2 + x**1 + 1 | [ 2, 1 ] | A011655
430             x**3 + x**2 + 1 | [ 3, 2 ] | A011656
431             x**3 + x**1 + 1 | [ 3, 1 ] | A011657
432             x**4 + x**3 + x**2 + x**1 + 1 | [ 4, 3, 2, 1 ] | A011658
433             x**4 + x**1 + 1 | [ 4, 1 ] | A011659
434             x**5 + x**4 + x**2 + x**1 + 1 | [ 5, 4, 2, 1 ] | A011660
435             x**5 + x**3 + x**2 + x**1 + 1 | [ 5, 3, 2, 1 ] | A011661
436             x**5 + x**2 + 1 | [ 5, 2 ] | A011662
437             x**5 + x**4 + x**3 + x**1 + 1 | [ 5, 4, 3, 1 ] | A011663
438             x**5 + x**3 + 1 | [ 5, 3 ] | A011664
439             x**5 + x**4 + x**3 + x**2 + 1 | [ 5, 4, 3, 2 ] | A011665
440             x**6 + x**5 + x**4 + x**1 + 1 | [ 6, 5, 4, 1 ] | A011666
441             x**6 + x**5 + x**3 + x**2 + 1 | [ 6, 5, 3, 2 ] | A011667
442             x**6 + x**5 + x**2 + x**1 + 1 | [ 6, 5, 2, 1 ] | A011668
443             x**6 + x**1 + 1 | [ 6, 1 ] | A011669
444             x**6 + x**4 + x**3 + x**1 + 1 | [ 6, 4, 3, 1 ] | A011670
445             x**6 + x**5 + x**4 + x**2 + 1 | [ 6, 5, 4, 2 ] | A011671
446             x**6 + x**3 + 1 | [ 6, 3 ] | A011672
447             x**6 + x**5 + 1 | [ 6, 5 ] | A011673
448             x**7 + x**6 + x**5 + x**4 + x**3 + x**2 + 1 | [ 7, 6, 5, 4, 3, 2 ] | A011674
449             x**7 + x**4 + 1 | [ 7, 4 ] | A011675
450             x**7 + x**6 + x**4 + x**2 + 1 | [ 7, 6, 4, 2 ] | A011676
451             x**7 + x**5 + x**2 + x**1 + 1 | [ 7, 5, 2, 1 ] | A011677
452             x**7 + x**5 + x**3 + x**1 + 1 | [ 7, 5, 3, 1 ] | A011678
453             x**7 + x**6 + x**4 + x**1 + 1 | [ 7, 6, 4, 1 ] | A011679
454             x**7 + x**6 + x**5 + x**4 + x**2 + x**1 + 1 | [ 7, 6, 5, 4, 2, 1 ] | A011680
455             x**7 + x**6 + x**5 + x**3 + x**2 + x**1 + 1 | [ 7, 6, 5, 3, 2, 1 ] | A011681
456             x**7 + x**1 + 1 | [ 7, 1 ] | A011682
457             x**7 + x**5 + x**4 + x**3 + x**2 + x**1 + 1 | [ 7, 5, 4, 3, 2, 1 ] | A011683
458             x**7 + x**4 + x**3 + x**2 + 1 | [ 7, 4, 3, 2 ] | A011684
459             x**7 + x**6 + x**3 + x**1 + 1 | [ 7, 6, 3, 1 ] | A011685
460             x**7 + x**6 + 1 | [ 7, 6 ] | A011686
461             x**7 + x**6 + x**5 + x**4 + 1 | [ 7, 6, 5, 4 ] | A011687
462             x**7 + x**5 + x**4 + x**3 + 1 | [ 7, 5, 4, 3 ] | A011688
463             x**7 + x**3 + x**2 + x**1 + 1 | [ 7, 3, 2, 1 ] | A011689
464             x**7 + x**3 + 1 | [ 7, 3 ] | A011690
465             x**7 + x**6 + x**5 + x**2 + 1 | [ 7, 6, 5, 2 ] | A011691
466             x**8 + x**6 + x**4 + x**3 + x**2 + x**1 + 1 | [ 8, 6, 4, 3, 2, 1 ] | A011692
467             x**8 + x**5 + x**4 + x**3 + 1 | [ 8, 5, 4, 3 ] | A011693
468             x**8 + x**7 + x**5 + x**3 + 1 | [ 8, 7, 5, 3 ] | A011694
469             x**8 + x**7 + x**6 + x**5 + x**4 + x**2 + 1 | [ 8, 7, 6, 5, 4, 2 ] | A011695
470             x**8 + x**7 + x**6 + x**5 + x**4 + x**3 + 1 | [ 8, 7, 6, 5, 4, 3 ] | A011696
471             x**8 + x**4 + x**3 + x**2 + 1 | [ 8, 4, 3, 2 ] | A011697
472             x**8 + x**6 + x**5 + x**4 + x**2 + x**1 + 1 | [ 8, 6, 5, 4, 2, 1 ] | A011698
473             x**8 + x**7 + x**5 + x**1 + 1 | [ 8, 7, 5, 1 ] | A011699
474             x**8 + x**7 + x**3 + x**1 + 1 | [ 8, 7, 3, 1 ] | A011700
475             x**8 + x**5 + x**4 + x**3 + x**2 + x**1 + 1 | [ 8, 5, 4, 3, 2, 1 ] | A011701
476             x**8 + x**7 + x**5 + x**4 + x**3 + x**2 + 1 | [ 8, 7, 5, 4, 3, 2 ] | A011702
477             x**8 + x**7 + x**6 + x**4 + x**3 + x**2 + 1 | [ 8, 7, 6, 4, 3, 2 ] | A011703
478             x**8 + x**6 + x**3 + x**2 + 1 | [ 8, 6, 3, 2 ] | A011704
479             x**8 + x**7 + x**3 + x**2 + 1 | [ 8, 7, 3, 2 ] | A011705
480             x**8 + x**6 + x**5 + x**2 + 1 | [ 8, 6, 5, 2 ] | A011706
481             x**8 + x**7 + x**6 + x**4 + x**2 + x**1 + 1 | [ 8, 7, 6, 4, 2, 1 ] | A011707
482             x**8 + x**7 + x**6 + x**3 + x**2 + x**1 + 1 | [ 8, 7, 6, 3, 2, 1 ] | A011708
483             x**8 + x**7 + x**2 + x**1 + 1 | [ 8, 7, 2, 1 ] | A011709
484             x**8 + x**7 + x**6 + x**1 + 1 | [ 8, 7, 6, 1 ] | A011710
485             x**8 + x**7 + x**6 + x**5 + x**2 + x**1 + 1 | [ 8, 7, 6, 5, 2, 1 ] | A011711
486             x**8 + x**7 + x**5 + x**4 + 1 | [ 8, 7, 5, 4 ] | A011712
487             x**8 + x**6 + x**5 + x**1 + 1 | [ 8, 6, 5, 1 ] | A011713
488             x**8 + x**4 + x**3 + x**1 + 1 | [ 8, 4, 3, 1 ] | A011714
489             x**8 + x**6 + x**5 + x**4 + 1 | [ 8, 6, 5, 4 ] | A011715
490             x**8 + x**7 + x**6 + x**5 + x**4 + x**1 + 1 | [ 8, 7, 6, 5, 4, 1 ] | A011716
491             x**8 + x**5 + x**3 + x**2 + 1 | [ 8, 5, 3, 2 ] | A011717
492             x**8 + x**6 + x**5 + x**4 + x**3 + x**1 + 1 | [ 8, 6, 5, 4, 3, 1 ] | A011718
493             x**8 + x**5 + x**3 + x**1 + 1 | [ 8, 5, 3, 1 ] | A011719
494             x**8 + x**7 + x**4 + x**3 + x**2 + x**1 + 1 | [ 8, 7, 4, 3, 2, 1 ] | A011720
495             x**8 + x**6 + x**5 + x**3 + 1 | [ 8, 6, 5, 3 ] | A011721
496             x**9 + x**4 + 1 | [ 9, 4 ] | A011722
497             x**10 + x**3 + 1 | [ 10, 3 ] | A011723
498             x**11 + x**2 + 1 | [ 11, 2 ] | A011724
499             x**12 + x**7 + x**4 + x**3 + 1 | [ 12, 7, 4, 3 ] | A011725
500             x**13 + x**4 + x**3 + x**1 + 1 | [ 13, 4, 3, 1 ] | A011726
501             x**14 + x**12 + x**11 + x**1 + 1 | [ 14, 12, 11, 1 ] | A011727
502             x**15 + x**1 + 1 | [ 15, 1 ] | A011728
503             x**16 + x**5 + x**3 + x**2 + 1 | [ 16, 5, 3, 2 ] | A011729
504             x**17 + x**3 + 1 | [ 17, 3 ] | A011730
505             x**18 + x**7 + 1 | [ 18, 7 ] | A011731
506             x**19 + x**6 + x**5 + x**1 + 1 | [ 19, 6, 5, 1 ] | A011732
507             x**20 + x**3 + 1 | [ 20, 3 ] | A011733
508             x**21 + x**2 + 1 | [ 21, 2 ] | A011734
509             x**22 + x**1 + 1 | [ 22, 1 ] | A011735
510             x**23 + x**5 + 1 | [ 23, 5 ] | A011736
511             x**24 + x**4 + x**3 + x**1 + 1 | [ 24, 4, 3, 1 ] | A011737
512             x**25 + x**3 + 1 | [ 25, 3 ] | A011738
513             x**26 + x**8 + x**7 + x**1 + 1 | [ 26, 8, 7, 1 ] | A011739
514             x**27 + x**8 + x**7 + x**1 + 1 | [ 27, 8, 7, 1 ] | A011740
515             x**28 + x**3 + 1 | [ 28, 3 ] | A011741
516             x**29 + x**2 + 1 | [ 29, 2 ] | A011742
517             x**30 + x**16 + x**15 + x**1 + 1 | [ 30, 16, 15, 1 ] | A011743
518             x**31 + x**3 + 1 | [ 31, 3 ] | A011744
519             x**32 + x**28 + x**27 + x**1 + 1 | [ 32, 28, 27, 1 ] | A011745
520            
521             =cut
522            
523             my %OEIS = (
524             join(';', @{ [ 2, 1 ] } ) => 'A011655',
525             join(';', @{ [ 3, 2 ] } ) => 'A011656',
526             join(';', @{ [ 3, 1 ] } ) => 'A011657',
527             join(';', @{ [ 4, 3, 2, 1 ] } ) => 'A011658',
528             join(';', @{ [ 4, 1 ] } ) => 'A011659',
529             join(';', @{ [ 5, 4, 2, 1 ] } ) => 'A011660',
530             join(';', @{ [ 5, 3, 2, 1 ] } ) => 'A011661',
531             join(';', @{ [ 5, 2 ] } ) => 'A011662',
532             join(';', @{ [ 5, 4, 3, 1 ] } ) => 'A011663',
533             join(';', @{ [ 5, 3 ] } ) => 'A011664',
534             join(';', @{ [ 5, 4, 3, 2 ] } ) => 'A011665',
535             join(';', @{ [ 6, 5, 4, 1 ] } ) => 'A011666',
536             join(';', @{ [ 6, 5, 3, 2 ] } ) => 'A011667',
537             join(';', @{ [ 6, 5, 2, 1 ] } ) => 'A011668',
538             join(';', @{ [ 6, 1 ] } ) => 'A011669',
539             join(';', @{ [ 6, 4, 3, 1 ] } ) => 'A011670',
540             join(';', @{ [ 6, 5, 4, 2 ] } ) => 'A011671',
541             join(';', @{ [ 6, 3 ] } ) => 'A011672',
542             join(';', @{ [ 6, 5 ] } ) => 'A011673',
543             join(';', @{ [ 7, 6, 5, 4, 3, 2 ] } ) => 'A011674',
544             join(';', @{ [ 7, 4 ] } ) => 'A011675',
545             join(';', @{ [ 7, 6, 4, 2 ] } ) => 'A011676',
546             join(';', @{ [ 7, 5, 2, 1 ] } ) => 'A011677',
547             join(';', @{ [ 7, 5, 3, 1 ] } ) => 'A011678',
548             join(';', @{ [ 7, 6, 4, 1 ] } ) => 'A011679',
549             join(';', @{ [ 7, 6, 5, 4, 2, 1 ] } ) => 'A011680',
550             join(';', @{ [ 7, 6, 5, 3, 2, 1 ] } ) => 'A011681',
551             join(';', @{ [ 7, 1 ] } ) => 'A011682',
552             join(';', @{ [ 7, 5, 4, 3, 2, 1 ] } ) => 'A011683',
553             join(';', @{ [ 7, 4, 3, 2 ] } ) => 'A011684',
554             join(';', @{ [ 7, 6, 3, 1 ] } ) => 'A011685',
555             join(';', @{ [ 7, 6 ] } ) => 'A011686',
556             join(';', @{ [ 7, 6, 5, 4 ] } ) => 'A011687',
557             join(';', @{ [ 7, 5, 4, 3 ] } ) => 'A011688',
558             join(';', @{ [ 7, 3, 2, 1 ] } ) => 'A011689',
559             join(';', @{ [ 7, 3 ] } ) => 'A011690',
560             join(';', @{ [ 7, 6, 5, 2 ] } ) => 'A011691',
561             join(';', @{ [ 8, 6, 4, 3, 2, 1 ] } ) => 'A011692',
562             join(';', @{ [ 8, 5, 4, 3 ] } ) => 'A011693',
563             join(';', @{ [ 8, 7, 5, 3 ] } ) => 'A011694',
564             join(';', @{ [ 8, 7, 6, 5, 4, 2 ] } ) => 'A011695',
565             join(';', @{ [ 8, 7, 6, 5, 4, 3 ] } ) => 'A011696',
566             join(';', @{ [ 8, 4, 3, 2 ] } ) => 'A011697',
567             join(';', @{ [ 8, 6, 5, 4, 2, 1 ] } ) => 'A011698',
568             join(';', @{ [ 8, 7, 5, 1 ] } ) => 'A011699',
569             join(';', @{ [ 8, 7, 3, 1 ] } ) => 'A011700',
570             join(';', @{ [ 8, 5, 4, 3, 2, 1 ] } ) => 'A011701',
571             join(';', @{ [ 8, 7, 5, 4, 3, 2 ] } ) => 'A011702',
572             join(';', @{ [ 8, 7, 6, 4, 3, 2 ] } ) => 'A011703',
573             join(';', @{ [ 8, 6, 3, 2 ] } ) => 'A011704',
574             join(';', @{ [ 8, 7, 3, 2 ] } ) => 'A011705',
575             join(';', @{ [ 8, 6, 5, 2 ] } ) => 'A011706',
576             join(';', @{ [ 8, 7, 6, 4, 2, 1 ] } ) => 'A011707',
577             join(';', @{ [ 8, 7, 6, 3, 2, 1 ] } ) => 'A011708',
578             join(';', @{ [ 8, 7, 2, 1 ] } ) => 'A011709',
579             join(';', @{ [ 8, 7, 6, 1 ] } ) => 'A011710',
580             join(';', @{ [ 8, 7, 6, 5, 2, 1 ] } ) => 'A011711',
581             join(';', @{ [ 8, 7, 5, 4 ] } ) => 'A011712',
582             join(';', @{ [ 8, 6, 5, 1 ] } ) => 'A011713',
583             join(';', @{ [ 8, 4, 3, 1 ] } ) => 'A011714',
584             join(';', @{ [ 8, 6, 5, 4 ] } ) => 'A011715',
585             join(';', @{ [ 8, 7, 6, 5, 4, 1 ] } ) => 'A011716',
586             join(';', @{ [ 8, 5, 3, 2 ] } ) => 'A011717',
587             join(';', @{ [ 8, 6, 5, 4, 3, 1 ] } ) => 'A011718',
588             join(';', @{ [ 8, 5, 3, 1 ] } ) => 'A011719',
589             join(';', @{ [ 8, 7, 4, 3, 2, 1 ] } ) => 'A011720',
590             join(';', @{ [ 8, 6, 5, 3 ] } ) => 'A011721',
591             join(';', @{ [ 9, 4 ] } ) => 'A011722',
592             join(';', @{ [ 10, 3 ] } ) => 'A011723',
593             join(';', @{ [ 11, 2 ] } ) => 'A011724',
594             join(';', @{ [ 12, 7, 4, 3 ] } ) => 'A011725',
595             join(';', @{ [ 13, 4, 3, 1 ] } ) => 'A011726',
596             join(';', @{ [ 14, 12, 11, 1 ] } ) => 'A011727',
597             join(';', @{ [ 15, 1 ] } ) => 'A011728',
598             join(';', @{ [ 16, 5, 3, 2 ] } ) => 'A011729',
599             join(';', @{ [ 17, 3 ] } ) => 'A011730',
600             join(';', @{ [ 18, 7 ] } ) => 'A011731',
601             join(';', @{ [ 19, 6, 5, 1 ] } ) => 'A011732',
602             join(';', @{ [ 20, 3 ] } ) => 'A011733',
603             join(';', @{ [ 21, 2 ] } ) => 'A011734',
604             join(';', @{ [ 22, 1 ] } ) => 'A011735',
605             join(';', @{ [ 23, 5 ] } ) => 'A011736',
606             join(';', @{ [ 24, 4, 3, 1 ] } ) => 'A011737',
607             join(';', @{ [ 25, 3 ] } ) => 'A011738',
608             join(';', @{ [ 26, 8, 7, 1 ] } ) => 'A011739',
609             join(';', @{ [ 27, 8, 7, 1 ] } ) => 'A011740',
610             join(';', @{ [ 28, 3 ] } ) => 'A011741',
611             join(';', @{ [ 29, 2 ] } ) => 'A011742',
612             join(';', @{ [ 30, 16, 15, 1 ] } ) => 'A011743',
613             join(';', @{ [ 31, 3 ] } ) => 'A011744',
614             join(';', @{ [ 32, 28, 27, 1 ] } ) => 'A011745',
615             );
616             sub oeis_anum {
617 7     7 1 12 my $taps = join ';', @{ $_[0]->{taps} };
  7         19  
618 7 100       43 return exists $OEIS{$taps} ? $OEIS{$taps} : undef;
619             }
620            
621            
622             =back
623            
624             =head1 THEORY
625            
626             A pseudorandom binary sequence (PRBS) is the sequence of N unique bits, in this case generated from an LFSR. Once it generates the N bits, it loops around and repeats that seqence. While still within the unique N bits, the sequence of N bits shares some properties with a truly random sequence of the same length. The benefit of this sequence is that, while it shares statistical properites with a random sequence, it is actually deterministic, so is often used to deterministically test hardware or software that requires a data stream that needs pseudorandom properties.
627            
628             In an LFSR, the polynomial description (like C) indicates which bits are "tapped" to create the feedback bit: the taps are the powers of x in the polynomial (3 and 2). The C<1> is really the C term, and isn't a "tap", in the sense that it isn't used for generating the feedback; instead, that is the location where the new feedback bit comes back into the shift register; the C<1> is in all characteristic polynomials, and is implied when creating a new instance of B.
629            
630             If the largest power of the polynomial is C, there are C bits in the register (one for each of the powers C and one for the C's feedback bit). For any given C, the largest sequence that can be produced is C, and that sequence is called a maximum length sequence or m-sequence; there can be more than one m-sequence for a given C. One useful feature of an m-sequence is that if you divide it into every possible partial sequence that's C bits long (wraping from N-1 to 0 to make the last few partial sequences also C bits), you will generate every possible combination of C bits (*), except for C zeroes in a row. For example,
631            
632             # x**3 + x**2 + 1 = "1011100"
633             "_101_1100 " -> 101
634             "1_011_100 " -> 011
635             "10_111_00 " -> 111
636             "101_110_0 " -> 110
637             "1011_100_ " -> 100
638             "1_0111_00 " -> 001 (requires wrap to get three digits: 00 from the end, and 1 from the beginning)
639             "10_1110_0 " -> 010 (requires wrap to get three digits: 0 from the end, and 10 from the beginning)
640            
641             The Wikipedia:LFSR article (see L) lists some polynomials that create m-sequence for various register sizes, and links to Philip Koopman's complete list up to C.
642            
643             If you want to create try own polynonial to find a long m-sequence, here are some things to consider: 1) the number of taps for the feedback (remembering not to count the feedback bit as a tap) must be even; 2) the entire set of taps must be relatively prime; 3) those two conditions are necesssary, but not sufficient, so you may have to try multiple polynomials to find an m-sequence; 4) keep in mind that the time to compute the period (and thus determine if it's an m-sequence) doubles every time C increases by 1; as the time increases, it makes more sense to look at the complete list up to C), and pure-perl is probably tpp wrong language for searching C64>.
644            
645             (*) Since a maximum length sequence contains every k-bit combination (except all zeroes), it can be used for verifying that software or hardware behaves properly for every possible sequence of k-bits.
646            
647             =head1 REFERENCES
648            
649             =over
650            
651             =item * Wikipedia:Linear-feedback Shift Register (LFSR) at L
652            
653             =over
654            
655             =item * Article includes a list of some L
656            
657             =item * Article links to Philip Koopman's complete list of maximum length polynomials, up to C at L
658            
659             =back
660            
661             =item * Wikipedia:Pseudorandom Binary Sequence (PRBS) at L
662            
663             =over
664            
665             =item * The underlying algorithm in B is based on the C code in this article's L<"Practical Implementation"|https://en.wikipedia.org/w/index.php?title=Pseudorandom_binary_sequence&oldid=700999060#Practical_implementation>
666            
667             =back
668            
669             =item * Wikipedia:Maximum Length Sequence (m-sequence) at L
670            
671             =over
672            
673             =item * Article describes some of the properties of m-sequences
674            
675             =back
676            
677             =back
678            
679             =head1 AUTHOR
680            
681             Peter C. Jones Cpetercj AT cpan DOT orgE>
682            
683             Please report any bugs or feature requests thru the web interface at
684             L
685            
686             =head1 COPYRIGHT
687            
688             Copyright (C) 2016 Peter C. Jones
689            
690             =head1 LICENCE
691            
692             This program is free software; you can redistribute it and/or modify it
693             under the terms of either: the GNU General Public License as published
694             by the Free Software Foundation; or the Artistic License.
695            
696             See L for more information.
697            
698            
699             =cut
700            
701             1;