for (int i = L; i < M; i++) {
d[i] = i;
}
final ByteMatrix X = A.copy();
// initialize i and u parameters, for the submatrices sizes
int i = 0, u = P;
// counts how many rows have been chosen already
int chosenRowsCounter = 0;
// the number of rows that are not HDPC
// (these should be chosen first)
int nonHDPCRows = S + Kprime;
// maps the index of a row to an object Row (which stores that row's characteristics)
final Map<Integer, Row> rows = new HashMap<>(M + 1, 1.0f);
for (int row = 0; row < M; row++) {
// retrieve the number of non-zeros in the row
final int nonZeros = A.nonZerosInRow(row, 0, L - u); // exclude last u columns
// is this a HDPC row?
final boolean isHDPC = (row >= S && row < S + H);
// this is an optimization
if (nonZeros == 2 && !isHDPC) {
int originalDegree = 0;
final Set<Integer> nodes = new HashSet<>(2 + 1, 1.0f); // we already know there are only 2 non zeros
ByteVectorIterator it = A.nonZeroRowIterator(row, 0, L - u);
while (it.hasNext()) {
it.next();
originalDegree += OctetOps.UNSIGN(it.get()); // add to the degree of this row
nodes.add(it.index()); // add the column index to the nodes
}
rows.put(row, new Row(row, nonZeros, originalDegree, isHDPC, nodes));
}
else {
int originalDegree = 0;
ByteVectorIterator it = A.nonZeroRowIterator(row, 0, L - u);
while (it.hasNext()) {
it.next();
originalDegree += OctetOps.UNSIGN(it.get()); // add to the degree of this row
}
rows.put(row, new Row(row, nonZeros, originalDegree, isHDPC));
}
}
TimerUtils.markTimestamp(); // DEBUG
initNanos += TimerUtils.getEllapsedTimeLong(TimeUnit.NANOSECONDS);
// at most L steps
while (i + u != L)
{
// the degree of the 'currently chosen' row
int minDegree = 256 * L;
// number of non-zeros in the 'currently chosen' row
int r = L + 1;
// currently chosen row
Row chosenRow = null;
// decoding failure?
boolean allZeros = true;
// there is a row with exactly two ones
boolean two1s = false;
/*
* find r
*/
TimerUtils.beginTimer(); // DEBUG
for (Row row : rows.values()) {
if (row.nonZeros != 0) allZeros = false;
if (row.isHDPC && chosenRowsCounter < nonHDPCRows) continue;
// if it's an edge, then it must have exactly two 1's
if (row.nodes != null) two1s = true;
if (row.nonZeros < r && row.nonZeros > 0) {
chosenRow = row;
r = chosenRow.nonZeros;
minDegree = chosenRow.originalDegree;
}
else if (row.nonZeros == r && row.originalDegree < minDegree) {
chosenRow = row;
minDegree = chosenRow.originalDegree;
}
}
TimerUtils.markTimestamp(); // DEBUG
findRNanos += TimerUtils.getEllapsedTimeLong(TimeUnit.NANOSECONDS);
if (allZeros) {// DECODING FAILURE
throw new SingularMatrixException(
"Decoding Failure - PI Decoding @ Phase 1: All entries in V are zero.");
}
/*
* choose the row
*/
TimerUtils.beginTimer(); // DEBUG
if (r == 2 && two1s) {
/*
* create graph
*/
// allocate memory
Map<Integer, Set<Integer>> graph = new HashMap<>(L - u - i + 1, 1.0f);
// lets go through all the rows... (yet again!)
for (Row row : rows.values())
{
// is this row an edge?
if (row.nodes != null)
{
// get the nodes connected through this edge
Integer[] edge = row.nodes.toArray(new Integer[2]);
int node1 = edge[0];
int node2 = edge[1];
// node1 already in graph?
if (graph.keySet().contains(node1))
{ // it is
// then lets add node 2 to its neighbours
graph.get(node1).add(node2);
}
else
{ // it isn't
// allocate memory for its neighbours
Set<Integer> edges = new HashSet<>(L - u - i + 1, 1.0f);
// add node 2 to its neighbours
edges.add(node2);
// finally, add node 1 to the graph along with its neighbours
graph.put(node1, edges);
}
// node2 already in graph?
if (graph.keySet().contains(node2))
{ // it is
// then lets add node 1 to its neighbours
graph.get(node2).add(node1);
}
else
{ // it isn't
// allocate memory for its neighbours
Set<Integer> edges = new HashSet<>(L - u - i + 1, 1.0f);
// add node 1 to its neighbours
edges.add(node1);
// finally, add node 2 to the graph along with its neighbours
graph.put(node2, edges);
}
}
else continue;
}
/*
* the graph is complete, now we must
* find the maximum size component
*/
// set of visited nodes
Set<Integer> visited = null;
/*
* TODO Optimization: I already searched, and there are optimized algorithms to find connected
* components. Then we just find and use the best one available...
*/
// what is the size of the largest component we've already found
int maximumSize = 0;
// the maximum size component
Set<Integer> greatestComponent = null;
// which nodes have already been used (either in visited or in toVisit)
Set<Integer> used = new HashSet<>(L - u - i + 1, 1.0f);
// iterates the nodes in the graph
Iterator<Map.Entry<Integer, Set<Integer>>> it = graph.entrySet().iterator();
// let's iterate through the nodes in the graph, looking for the maximum
// size component. we will be doing a breadth first search // TODO optimize this with a better
// algorithm?
while (it.hasNext())
{
// get our initial node
Map.Entry<Integer, Set<Integer>> node = it.next();
int initialNode = node.getKey();
// we can't have used it before!
if (used.contains(initialNode)) continue;
// what are the edges of our initial node?
Integer[] edges = node.getValue().toArray(new Integer[node.getValue().size()]);
// allocate memory for the set of visited nodes
visited = new HashSet<>(L - u - i + 1, 1.0f);
// the set of nodes we must still visit
List<Integer> toVisit = new LinkedList<>();
// add the initial node to the set of used and visited nodes
visited.add(initialNode);
used.add(initialNode);
// add my edges to the set of nodes we must visit
// and also put them in the used set
for (Integer edge : edges)
{
toVisit.add(edge);
used.add(edge);
}
// start the search!
while (toVisit.size() != 0)
{
// the node we are visiting
int no = toVisit.remove(0);
// add node to visited set
visited.add(no);
// queue edges to be visited (if they haven't been already
for (Integer edge : graph.get(no))
if (!visited.contains(edge)) toVisit.add(edge);
}
// is the number of visited nodes, greater than the 'currently' largest component?
if (visited.size() > maximumSize)
{ // it is! we've found a greater component then...
// update the maximum size
maximumSize = visited.size();
// update our greatest component
greatestComponent = visited;
}
else continue;
}
/*
* we've found the maximum size connected component -- 'greatestComponent'
*/
// let's choose the row
for (Row row : rows.values())
{
// is it a node in the graph?
if (row.nodes != null)
{ // it is
// get the nodes connected through this edge
Integer[] edge = row.nodes.toArray(new Integer[2]);
int node1 = edge[0];
int node2 = edge[1];
// is this row an edge in the maximum size component?
if (greatestComponent.contains(node1) && greatestComponent.contains(node2))
{
chosenRow = row;
break;
}
else continue;
}
else continue;
}
chosenRowsCounter++;
TimerUtils.markTimestamp(); // DEBUG
chooseRowNanos += TimerUtils.getEllapsedTimeLong(TimeUnit.NANOSECONDS);
}
else {
// already chosen (in 'find r')
chosenRowsCounter++;
}
/*
* a row has been chosen! -- 'chosenRow'
*/
/*
* "After the row is chosen in this step, the first row of A that intersects V is exchanged
* with the chosen row so that the chosen row is the first row that intersects V."
*/
final int chosenRowPos = chosenRow.position;
// if the chosen row is not 'i' already
if (chosenRowPos != i) {
TimerUtils.beginTimer(); // DEBUG
// swap in A
A.swapRows(i, chosenRowPos);
// swap in X
X.swapRows(i, chosenRowPos);
// decoding process - swap in d
ArrayUtils.swapInts(d, i, chosenRowPos);
// update values in 'rows' map
Row other = rows.remove(i);
rows.put(chosenRowPos, other);
other.position = chosenRowPos;
chosenRow.position = i;
TimerUtils.markTimestamp(); // DEBUG
swapRowsNanos += TimerUtils.getEllapsedTimeLong(TimeUnit.NANOSECONDS);
}
/*
* "The columns of A among those that intersect V are reordered so that one of the r nonzeros
* in the chosen row appears in the first column of V and so that the remaining r-1 nonzeros
* appear in the last columns of V."
*/
TimerUtils.beginTimer(); // DEBUG
// an array with the positions (column indices) of the non-zeros
final int[] nonZeroPos = A.nonZeroPositionsInRow(i, i, L - u);
/*
* lets start swapping columns!
*/
// is the first column in V already the place of a non-zero?
final int firstNZpos = nonZeroPos[0]; // the chosen row always has at least one non-zero
if (i != firstNZpos) {
// no, so swap the first column in V (i) with the first non-zero column
// swap columns
A.swapColumns(i, firstNZpos);
X.swapColumns(i, firstNZpos);
// decoding process - swap in c
ArrayUtils.swapInts(c, i, firstNZpos);
}
// swap the remaining non-zeros' columns so that they're the last columns in V
for (int nzp = nonZeroPos.length - 1, currCol = L - u - 1; nzp > 0; nzp--, currCol--) {
// is the current column already the place of a non-zero?
final int currNZpos = nonZeroPos[nzp];
if (currCol != currNZpos) {
// no, so swap the current column in V with the current non-zero column
// swap columns
A.swapColumns(currCol, currNZpos);
X.swapColumns(currCol, currNZpos);
// decoding process - swap in c
ArrayUtils.swapInts(c, currCol, currNZpos);
}
}