Linear Feedback Shift Register (Intermediate)

Dated Jan 22, 2018; last modified on Mon, 22 Jan 2018

Description

Link to original problem

In computing, a linear-feedback shift register (LFSR) is a shift register whose input bit is a linear function of its previous state. The most commonly used linear function of single bits is exclusive-or (XOR). Thus, an LFSR is most often a shift register whose input bit is driven by the XOR of some bits of the overall shift register value.

The initial value of the LFSR is called the seed, and because the operation of the register is deterministic, the stream of values produced by the register is completely determined by its current (or previous) state. Likewise, because the register has a finite number of possible states, it must eventually enter a repeating cycle.

Your challenge today is to implement an LFSR in software.

Example Input

You’ll be given a LFSR input on one line specifying the tap positions (0-indexed), the feedback function (XOR or XNOR), the initial value with leading 0s as needed to show you the bit width, and the number of clock steps to output. Example:

[0,2] XOR 001 7

Example Output

Your program should emit the clock step and the registers (with leading 0s) for the input LFSR. From our above example:

0 001
1 100
2 110
3 111
4 011
5 101
6 010
7 001

Challenge Input

[1,2] XOR 001 7
[0,2] XNOR 001 7
[1,2,3,7] XOR 00000001 16
[1,5,6,31] XOR 00000000000000000000000000000001 16

Challenge Output

(Only showing the first two for brevity’s sake.)

0 001
1 100
2 010
3 101
4 110
5 111
6 011
7 001
0 001
1 000
2 100
3 010
4 101
5 110
6 011
7 001

My Solution

import java.util.LinkedList;
import java.util.BitSet;
import java.lang.StringBuilder;

public class LFSR347 {
    
    private LinkedList<BitSet> lfsr;
    private int[] tapPositions;
    
    public LFSR347(int[] initialValues, int[] tapPositions) {
        this.lfsr = new LinkedList<BitSet>();
        this.tapPositions = tapPositions;
        for (int n: initialValues) {
            this.lfsr.add(getBit(n));
        }
    }
    
    public BitSet getBit(int a) {
        BitSet b = new BitSet(1);
        if (a == 1) b.set(1);
        else if (a != 0) throw new IllegalArgumentException();
        return b;
    }
    
    public String toString() {
        Object[] array = this.lfsr.toArray();
        StringBuilder sb = new StringBuilder();
        for (Object n: array) sb.append(n);
        return sb.toString();
    }
    
    public String step(String operation) {
        
        BitSet result = this.lfsr.get(this.tapPositions[0]);
        for (int i = 1; i < this.tapPositions.length; i++) {
            // System.out.print(" " + result);
            result.xor(this.lfsr.get(this.tapPositions[i]));
            if (operation == "XNOR") result.flip(0);
            else if (operation != "XOR") throw new IllegalArgumentException();
        }
        
        this.lfsr.removeLast();
        this.lfsr.addFirst(result);
        return toString();
    }
    
    public int xor(int a, int b) {
        return a ^ b;
    }
    
    public static void main(String[] args) {
        int[] initialValues = {0, 0, 1};
        int[] tapPositions = {0, 2};
        LFSR347 testLFSR = new LFSR347(initialValues, tapPositions);
        System.out.println("0 " + testLFSR.toString() + "\n");
        for (int i = 1; i < 8; i++) {
            System.out.println("\n" + i + " " + testLFSR.step("XOR") + "\n");
        }
    }
    
    
} 

Further Reading

Bonus

Write a function that detects the periodicity of the LFSR configuration.

Post-Mortem

I wrote this solution 20 months ago; a post-mortem is in order:

  • I left print statements inside functions.
  • I didn’t close my java.io.BufferedReader
  • I couldn’t get java.util.BitSet working.