| package org.bouncycastle.pqc.math.linearalgebra; |
| |
| /** |
| * This class describes some operations with matrices over finite field <i>GF(2<sup>m</sup>)</i> |
| * with small <i>m</i> (1< m <32). |
| * |
| * @see Matrix |
| */ |
| public class GF2mMatrix |
| extends Matrix |
| { |
| |
| /** |
| * finite field GF(2^m) |
| */ |
| protected GF2mField field; |
| |
| /** |
| * For the matrix representation the array of type int[][] is used, thus |
| * every element of the array keeps one element of the matrix (element from |
| * finite field GF(2^m)) |
| */ |
| protected int[][] matrix; |
| |
| /** |
| * Constructor. |
| * |
| * @param field a finite field GF(2^m) |
| * @param enc byte[] matrix in byte array form |
| */ |
| public GF2mMatrix(GF2mField field, byte[] enc) |
| { |
| |
| this.field = field; |
| |
| // decode matrix |
| int d = 8; |
| int count = 1; |
| while (field.getDegree() > d) |
| { |
| count++; |
| d += 8; |
| } |
| |
| if (enc.length < 5) |
| { |
| throw new IllegalArgumentException( |
| " Error: given array is not encoded matrix over GF(2^m)"); |
| } |
| |
| this.numRows = ((enc[3] & 0xff) << 24) ^ ((enc[2] & 0xff) << 16) |
| ^ ((enc[1] & 0xff) << 8) ^ (enc[0] & 0xff); |
| |
| int n = count * this.numRows; |
| |
| if ((this.numRows <= 0) || (((enc.length - 4) % n) != 0)) |
| { |
| throw new IllegalArgumentException( |
| " Error: given array is not encoded matrix over GF(2^m)"); |
| } |
| |
| this.numColumns = (enc.length - 4) / n; |
| |
| matrix = new int[this.numRows][this.numColumns]; |
| count = 4; |
| for (int i = 0; i < this.numRows; i++) |
| { |
| for (int j = 0; j < this.numColumns; j++) |
| { |
| for (int jj = 0; jj < d; jj += 8) |
| { |
| matrix[i][j] ^= (enc[count++] & 0x000000ff) << jj; |
| } |
| if (!this.field.isElementOfThisField(matrix[i][j])) |
| { |
| throw new IllegalArgumentException( |
| " Error: given array is not encoded matrix over GF(2^m)"); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Copy constructor. |
| * |
| * @param other another {@link GF2mMatrix} |
| */ |
| public GF2mMatrix(GF2mMatrix other) |
| { |
| numRows = other.numRows; |
| numColumns = other.numColumns; |
| field = other.field; |
| matrix = new int[numRows][]; |
| for (int i = 0; i < numRows; i++) |
| { |
| matrix[i] = IntUtils.clone(other.matrix[i]); |
| } |
| } |
| |
| /** |
| * Constructor. |
| * |
| * @param field a finite field GF(2^m) |
| * @param matrix the matrix as int array. Only the reference is copied. |
| */ |
| protected GF2mMatrix(GF2mField field, int[][] matrix) |
| { |
| this.field = field; |
| this.matrix = matrix; |
| numRows = matrix.length; |
| numColumns = matrix[0].length; |
| } |
| |
| /** |
| * @return a byte array encoding of this matrix |
| */ |
| public byte[] getEncoded() |
| { |
| int d = 8; |
| int count = 1; |
| while (field.getDegree() > d) |
| { |
| count++; |
| d += 8; |
| } |
| |
| byte[] bf = new byte[this.numRows * this.numColumns * count + 4]; |
| bf[0] = (byte)(this.numRows & 0xff); |
| bf[1] = (byte)((this.numRows >>> 8) & 0xff); |
| bf[2] = (byte)((this.numRows >>> 16) & 0xff); |
| bf[3] = (byte)((this.numRows >>> 24) & 0xff); |
| |
| count = 4; |
| for (int i = 0; i < this.numRows; i++) |
| { |
| for (int j = 0; j < this.numColumns; j++) |
| { |
| for (int jj = 0; jj < d; jj += 8) |
| { |
| bf[count++] = (byte)(matrix[i][j] >>> jj); |
| } |
| } |
| } |
| |
| return bf; |
| } |
| |
| /** |
| * Check if this is the zero matrix (i.e., all entries are zero). |
| * |
| * @return <tt>true</tt> if this is the zero matrix |
| */ |
| public boolean isZero() |
| { |
| for (int i = 0; i < numRows; i++) |
| { |
| for (int j = 0; j < numColumns; j++) |
| { |
| if (matrix[i][j] != 0) |
| { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Compute the inverse of this matrix. |
| * |
| * @return the inverse of this matrix (newly created). |
| */ |
| public Matrix computeInverse() |
| { |
| if (numRows != numColumns) |
| { |
| throw new ArithmeticException("Matrix is not invertible."); |
| } |
| |
| // clone this matrix |
| int[][] tmpMatrix = new int[numRows][numRows]; |
| for (int i = numRows - 1; i >= 0; i--) |
| { |
| tmpMatrix[i] = IntUtils.clone(matrix[i]); |
| } |
| |
| // initialize inverse matrix as unit matrix |
| int[][] invMatrix = new int[numRows][numRows]; |
| for (int i = numRows - 1; i >= 0; i--) |
| { |
| invMatrix[i][i] = 1; |
| } |
| |
| // simultaneously compute Gaussian reduction of tmpMatrix and unit |
| // matrix |
| for (int i = 0; i < numRows; i++) |
| { |
| // if diagonal element is zero |
| if (tmpMatrix[i][i] == 0) |
| { |
| boolean foundNonZero = false; |
| // find a non-zero element in the same column |
| for (int j = i + 1; j < numRows; j++) |
| { |
| if (tmpMatrix[j][i] != 0) |
| { |
| // found it, swap rows ... |
| foundNonZero = true; |
| swapColumns(tmpMatrix, i, j); |
| swapColumns(invMatrix, i, j); |
| // ... and quit searching |
| j = numRows; |
| continue; |
| } |
| } |
| // if no non-zero element was found |
| if (!foundNonZero) |
| { |
| // the matrix is not invertible |
| throw new ArithmeticException("Matrix is not invertible."); |
| } |
| } |
| |
| // normalize i-th row |
| int coef = tmpMatrix[i][i]; |
| int invCoef = field.inverse(coef); |
| multRowWithElementThis(tmpMatrix[i], invCoef); |
| multRowWithElementThis(invMatrix[i], invCoef); |
| |
| // normalize all other rows |
| for (int j = 0; j < numRows; j++) |
| { |
| if (j != i) |
| { |
| coef = tmpMatrix[j][i]; |
| if (coef != 0) |
| { |
| int[] tmpRow = multRowWithElement(tmpMatrix[i], coef); |
| int[] tmpInvRow = multRowWithElement(invMatrix[i], coef); |
| addToRow(tmpRow, tmpMatrix[j]); |
| addToRow(tmpInvRow, invMatrix[j]); |
| } |
| } |
| } |
| } |
| |
| return new GF2mMatrix(field, invMatrix); |
| } |
| |
| private static void swapColumns(int[][] matrix, int first, int second) |
| { |
| int[] tmp = matrix[first]; |
| matrix[first] = matrix[second]; |
| matrix[second] = tmp; |
| } |
| |
| private void multRowWithElementThis(int[] row, int element) |
| { |
| for (int i = row.length - 1; i >= 0; i--) |
| { |
| row[i] = field.mult(row[i], element); |
| } |
| } |
| |
| private int[] multRowWithElement(int[] row, int element) |
| { |
| int[] result = new int[row.length]; |
| for (int i = row.length - 1; i >= 0; i--) |
| { |
| result[i] = field.mult(row[i], element); |
| } |
| return result; |
| } |
| |
| /** |
| * Add one row to another. |
| * |
| * @param fromRow the addend |
| * @param toRow the row to add to |
| */ |
| private void addToRow(int[] fromRow, int[] toRow) |
| { |
| for (int i = toRow.length - 1; i >= 0; i--) |
| { |
| toRow[i] = field.add(fromRow[i], toRow[i]); |
| } |
| } |
| |
| public Matrix rightMultiply(Matrix a) |
| { |
| throw new RuntimeException("Not implemented."); |
| } |
| |
| public Matrix rightMultiply(Permutation perm) |
| { |
| throw new RuntimeException("Not implemented."); |
| } |
| |
| public Vector leftMultiply(Vector vector) |
| { |
| throw new RuntimeException("Not implemented."); |
| } |
| |
| public Vector rightMultiply(Vector vector) |
| { |
| throw new RuntimeException("Not implemented."); |
| } |
| |
| /** |
| * Checks if given object is equal to this matrix. The method returns false |
| * whenever the given object is not a matrix over GF(2^m). |
| * |
| * @param other object |
| * @return true or false |
| */ |
| public boolean equals(Object other) |
| { |
| |
| if (other == null || !(other instanceof GF2mMatrix)) |
| { |
| return false; |
| } |
| |
| GF2mMatrix otherMatrix = (GF2mMatrix)other; |
| |
| if ((!this.field.equals(otherMatrix.field)) |
| || (otherMatrix.numRows != this.numColumns) |
| || (otherMatrix.numColumns != this.numColumns)) |
| { |
| return false; |
| } |
| |
| for (int i = 0; i < this.numRows; i++) |
| { |
| for (int j = 0; j < this.numColumns; j++) |
| { |
| if (this.matrix[i][j] != otherMatrix.matrix[i][j]) |
| { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| public int hashCode() |
| { |
| int hash = (this.field.hashCode() * 31 + numRows) * 31 + numColumns; |
| for (int i = 0; i < this.numRows; i++) |
| { |
| for (int j = 0; j < this.numColumns; j++) |
| { |
| hash = hash * 31 + matrix[i][j]; |
| } |
| } |
| return hash; |
| } |
| |
| public String toString() |
| { |
| String str = this.numRows + " x " + this.numColumns + " Matrix over " |
| + this.field.toString() + ": \n"; |
| |
| for (int i = 0; i < this.numRows; i++) |
| { |
| for (int j = 0; j < this.numColumns; j++) |
| { |
| str = str + this.field.elementToStr(matrix[i][j]) + " : "; |
| } |
| str = str + "\n"; |
| } |
| |
| return str; |
| } |
| |
| } |