class WaksmanUtils {

  private void setControlBits(int[] perm, int[][] controlBits, int rowIdx, int colIdx) {
    int n = perm.length;

    if (n == 2) {
      // for our base case we only have one swapper gate
      // which we can set directly
      controlBits[rowIdx][colIdx] = perm[0];
      return;
    }

    int[] rowI = new int[n / 2];
    int[] rowO = new int[n / 2];
    for (int j = 0; j < rowO.length; j++) {
      rowI[j] = -1;
      rowO[j] = -1;
    }

    int[] topPerm = new int[n / 2];
    int[] bottomPerm = new int[n / 2];

    int[] permInv = invert(perm);

    // loop until we have set all swappers at this level of the network
    while (hasNextUnsetSwapper(rowO)) {
      // find next O swapper that doesn't have control bit set
      int rowOSwIdx = nextUnsetSwapperIdx(rowO);
      // route through bottom
      rowO[rowOSwIdx] = 0;

      int firstOElIdx = topElIndexOf(rowOSwIdx);
      int rowOElIdx = firstOElIdx;

      // route from output to input and back again
      int rowIElIdx = routeFromOutputToInput(rowOElIdx, permInv, topPerm, rowI);
      rowOElIdx = routeFromInputToOutput(rowIElIdx, perm, bottomPerm, rowO);

      // loop until we're back at the start
      while (!(rowOElIdx == firstOElIdx)) {
        rowIElIdx = routeFromOutputToInput(rowOElIdx, permInv, topPerm, rowI);
        rowOElIdx = routeFromInputToOutput(rowIElIdx, perm, bottomPerm, rowO);
      }
    }

    // we have set all input and output swappers at this level of the network so we update our
    // swapper matrix and continue setting the swappers for our two sub permutations
    int numCols = controlBits[0].length;
    for (int j = 0; j < rowI.length; j++) {
      controlBits[rowIdx + j][colIdx] = rowI[j];
      controlBits[rowIdx + j][numCols - 1 - colIdx] = rowO[j];
    }
    setControlBits(topPerm, controlBits, rowIdx, colIdx + 1);
    setControlBits(bottomPerm, controlBits, rowIdx + rowI.length / 2, colIdx + 1);
  }

}