| package org.bouncycastle.pqc.math.linearalgebra; |
| |
| import java.security.SecureRandom; |
| |
| /** |
| * This class describes some operations with matrices over finite field GF(2) |
| * and is used in ecc and MQ-PKC (also has some specific methods and |
| * implementation) |
| */ |
| public class GF2Matrix |
| extends Matrix |
| { |
| |
| /** |
| * For the matrix representation the array of type int[][] is used, thus one |
| * element of the array keeps 32 elements of the matrix (from one row and 32 |
| * columns) |
| */ |
| private int[][] matrix; |
| |
| /** |
| * the length of each array representing a row of this matrix, computed as |
| * <tt>(numColumns + 31) / 32</tt> |
| */ |
| private int length; |
| |
| /** |
| * Create the matrix from encoded form. |
| * |
| * @param enc the encoded matrix |
| */ |
| public GF2Matrix(byte[] enc) |
| { |
| if (enc.length < 9) |
| { |
| throw new ArithmeticException( |
| "given array is not an encoded matrix over GF(2)"); |
| } |
| |
| numRows = LittleEndianConversions.OS2IP(enc, 0); |
| numColumns = LittleEndianConversions.OS2IP(enc, 4); |
| |
| int n = ((numColumns + 7) >>> 3) * numRows; |
| |
| if ((numRows <= 0) || (n != (enc.length - 8))) |
| { |
| throw new ArithmeticException( |
| "given array is not an encoded matrix over GF(2)"); |
| } |
| |
| length = (numColumns + 31) >>> 5; |
| matrix = new int[numRows][length]; |
| |
| // number of "full" integer |
| int q = numColumns >> 5; |
| // number of bits in non-full integer |
| int r = numColumns & 0x1f; |
| |
| int count = 8; |
| for (int i = 0; i < numRows; i++) |
| { |
| for (int j = 0; j < q; j++, count += 4) |
| { |
| matrix[i][j] = LittleEndianConversions.OS2IP(enc, count); |
| } |
| for (int j = 0; j < r; j += 8) |
| { |
| matrix[i][q] ^= (enc[count++] & 0xff) << j; |
| } |
| } |
| } |
| |
| /** |
| * Create the matrix with the contents of the given array. The matrix is not |
| * copied. Unused coefficients are masked out. |
| * |
| * @param numColumns the number of columns |
| * @param matrix the element array |
| */ |
| public GF2Matrix(int numColumns, int[][] matrix) |
| { |
| if (matrix[0].length != (numColumns + 31) >> 5) |
| { |
| throw new ArithmeticException( |
| "Int array does not match given number of columns."); |
| } |
| this.numColumns = numColumns; |
| numRows = matrix.length; |
| length = matrix[0].length; |
| int rest = numColumns & 0x1f; |
| int bitMask; |
| if (rest == 0) |
| { |
| bitMask = 0xffffffff; |
| } |
| else |
| { |
| bitMask = (1 << rest) - 1; |
| } |
| for (int i = 0; i < numRows; i++) |
| { |
| matrix[i][length - 1] &= bitMask; |
| } |
| this.matrix = matrix; |
| } |
| |
| /** |
| * Create an nxn matrix of the given type. |
| * |
| * @param n the number of rows (and columns) |
| * @param typeOfMatrix the martix type (see {@link Matrix} for predefined |
| * constants) |
| */ |
| public GF2Matrix(int n, char typeOfMatrix) |
| { |
| this(n, typeOfMatrix, new java.security.SecureRandom()); |
| } |
| |
| /** |
| * Create an nxn matrix of the given type. |
| * |
| * @param n the matrix size |
| * @param typeOfMatrix the matrix type |
| * @param sr the source of randomness |
| */ |
| public GF2Matrix(int n, char typeOfMatrix, SecureRandom sr) |
| { |
| if (n <= 0) |
| { |
| throw new ArithmeticException("Size of matrix is non-positive."); |
| } |
| |
| switch (typeOfMatrix) |
| { |
| |
| case Matrix.MATRIX_TYPE_ZERO: |
| assignZeroMatrix(n, n); |
| break; |
| |
| case Matrix.MATRIX_TYPE_UNIT: |
| assignUnitMatrix(n); |
| break; |
| |
| case Matrix.MATRIX_TYPE_RANDOM_LT: |
| assignRandomLowerTriangularMatrix(n, sr); |
| break; |
| |
| case Matrix.MATRIX_TYPE_RANDOM_UT: |
| assignRandomUpperTriangularMatrix(n, sr); |
| break; |
| |
| case Matrix.MATRIX_TYPE_RANDOM_REGULAR: |
| assignRandomRegularMatrix(n, sr); |
| break; |
| |
| default: |
| throw new ArithmeticException("Unknown matrix type."); |
| } |
| } |
| |
| /** |
| * Copy constructor. |
| * |
| * @param a another {@link GF2Matrix} |
| */ |
| public GF2Matrix(GF2Matrix a) |
| { |
| numColumns = a.getNumColumns(); |
| numRows = a.getNumRows(); |
| length = a.length; |
| matrix = new int[a.matrix.length][]; |
| for (int i = 0; i < matrix.length; i++) |
| { |
| matrix[i] = IntUtils.clone(a.matrix[i]); |
| } |
| |
| } |
| |
| /** |
| * create the mxn zero matrix |
| */ |
| private GF2Matrix(int m, int n) |
| { |
| if ((n <= 0) || (m <= 0)) |
| { |
| throw new ArithmeticException("size of matrix is non-positive"); |
| } |
| |
| assignZeroMatrix(m, n); |
| } |
| |
| /** |
| * Create the mxn zero matrix. |
| * |
| * @param m number of rows |
| * @param n number of columns |
| */ |
| private void assignZeroMatrix(int m, int n) |
| { |
| numRows = m; |
| numColumns = n; |
| length = (n + 31) >>> 5; |
| matrix = new int[numRows][length]; |
| for (int i = 0; i < numRows; i++) |
| { |
| for (int j = 0; j < length; j++) |
| { |
| matrix[i][j] = 0; |
| } |
| } |
| } |
| |
| /** |
| * Create the mxn unit matrix. |
| * |
| * @param n number of rows (and columns) |
| */ |
| private void assignUnitMatrix(int n) |
| { |
| numRows = n; |
| numColumns = n; |
| length = (n + 31) >>> 5; |
| matrix = new int[numRows][length]; |
| for (int i = 0; i < numRows; i++) |
| { |
| for (int j = 0; j < length; j++) |
| { |
| matrix[i][j] = 0; |
| } |
| } |
| for (int i = 0; i < numRows; i++) |
| { |
| int rest = i & 0x1f; |
| matrix[i][i >>> 5] = 1 << rest; |
| } |
| } |
| |
| /** |
| * Create a nxn random lower triangular matrix. |
| * |
| * @param n number of rows (and columns) |
| * @param sr source of randomness |
| */ |
| private void assignRandomLowerTriangularMatrix(int n, SecureRandom sr) |
| { |
| numRows = n; |
| numColumns = n; |
| length = (n + 31) >>> 5; |
| matrix = new int[numRows][length]; |
| for (int i = 0; i < numRows; i++) |
| { |
| int q = i >>> 5; |
| int r = i & 0x1f; |
| int s = 31 - r; |
| r = 1 << r; |
| for (int j = 0; j < q; j++) |
| { |
| matrix[i][j] = sr.nextInt(); |
| } |
| matrix[i][q] = (sr.nextInt() >>> s) | r; |
| for (int j = q + 1; j < length; j++) |
| { |
| matrix[i][j] = 0; |
| } |
| |
| } |
| |
| } |
| |
| /** |
| * Create a nxn random upper triangular matrix. |
| * |
| * @param n number of rows (and columns) |
| * @param sr source of randomness |
| */ |
| private void assignRandomUpperTriangularMatrix(int n, SecureRandom sr) |
| { |
| numRows = n; |
| numColumns = n; |
| length = (n + 31) >>> 5; |
| matrix = new int[numRows][length]; |
| int rest = n & 0x1f; |
| int help; |
| if (rest == 0) |
| { |
| help = 0xffffffff; |
| } |
| else |
| { |
| help = (1 << rest) - 1; |
| } |
| for (int i = 0; i < numRows; i++) |
| { |
| int q = i >>> 5; |
| int r = i & 0x1f; |
| int s = r; |
| r = 1 << r; |
| for (int j = 0; j < q; j++) |
| { |
| matrix[i][j] = 0; |
| } |
| matrix[i][q] = (sr.nextInt() << s) | r; |
| for (int j = q + 1; j < length; j++) |
| { |
| matrix[i][j] = sr.nextInt(); |
| } |
| matrix[i][length - 1] &= help; |
| } |
| |
| } |
| |
| /** |
| * Create an nxn random regular matrix. |
| * |
| * @param n number of rows (and columns) |
| * @param sr source of randomness |
| */ |
| private void assignRandomRegularMatrix(int n, SecureRandom sr) |
| { |
| numRows = n; |
| numColumns = n; |
| length = (n + 31) >>> 5; |
| matrix = new int[numRows][length]; |
| GF2Matrix lm = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_LT, sr); |
| GF2Matrix um = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_UT, sr); |
| GF2Matrix rm = (GF2Matrix)lm.rightMultiply(um); |
| Permutation perm = new Permutation(n, sr); |
| int[] p = perm.getVector(); |
| for (int i = 0; i < n; i++) |
| { |
| System.arraycopy(rm.matrix[i], 0, matrix[p[i]], 0, length); |
| } |
| } |
| |
| /** |
| * Create a nxn random regular matrix and its inverse. |
| * |
| * @param n number of rows (and columns) |
| * @param sr source of randomness |
| * @return the created random regular matrix and its inverse |
| */ |
| public static GF2Matrix[] createRandomRegularMatrixAndItsInverse(int n, |
| SecureRandom sr) |
| { |
| |
| GF2Matrix[] result = new GF2Matrix[2]; |
| |
| // ------------------------------------ |
| // First part: create regular matrix |
| // ------------------------------------ |
| |
| // ------ |
| int length = (n + 31) >> 5; |
| GF2Matrix lm = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_LT, sr); |
| GF2Matrix um = new GF2Matrix(n, Matrix.MATRIX_TYPE_RANDOM_UT, sr); |
| GF2Matrix rm = (GF2Matrix)lm.rightMultiply(um); |
| Permutation p = new Permutation(n, sr); |
| int[] pVec = p.getVector(); |
| |
| int[][] matrix = new int[n][length]; |
| for (int i = 0; i < n; i++) |
| { |
| System.arraycopy(rm.matrix[pVec[i]], 0, matrix[i], 0, length); |
| } |
| |
| result[0] = new GF2Matrix(n, matrix); |
| |
| // ------------------------------------ |
| // Second part: create inverse matrix |
| // ------------------------------------ |
| |
| // inverse to lm |
| GF2Matrix invLm = new GF2Matrix(n, Matrix.MATRIX_TYPE_UNIT); |
| for (int i = 0; i < n; i++) |
| { |
| int rest = i & 0x1f; |
| int q = i >>> 5; |
| int r = 1 << rest; |
| for (int j = i + 1; j < n; j++) |
| { |
| int b = (lm.matrix[j][q]) & r; |
| if (b != 0) |
| { |
| for (int k = 0; k <= q; k++) |
| { |
| invLm.matrix[j][k] ^= invLm.matrix[i][k]; |
| } |
| } |
| } |
| } |
| // inverse to um |
| GF2Matrix invUm = new GF2Matrix(n, Matrix.MATRIX_TYPE_UNIT); |
| for (int i = n - 1; i >= 0; i--) |
| { |
| int rest = i & 0x1f; |
| int q = i >>> 5; |
| int r = 1 << rest; |
| for (int j = i - 1; j >= 0; j--) |
| { |
| int b = (um.matrix[j][q]) & r; |
| if (b != 0) |
| { |
| for (int k = q; k < length; k++) |
| { |
| invUm.matrix[j][k] ^= invUm.matrix[i][k]; |
| } |
| } |
| } |
| } |
| |
| // inverse matrix |
| result[1] = (GF2Matrix)invUm.rightMultiply(invLm.rightMultiply(p)); |
| |
| return result; |
| } |
| |
| /** |
| * @return the array keeping the matrix elements |
| */ |
| public int[][] getIntArray() |
| { |
| return matrix; |
| } |
| |
| /** |
| * @return the length of each array representing a row of this matrix |
| */ |
| public int getLength() |
| { |
| return length; |
| } |
| |
| /** |
| * Return the row of this matrix with the given index. |
| * |
| * @param index the index |
| * @return the row of this matrix with the given index |
| */ |
| public int[] getRow(int index) |
| { |
| return matrix[index]; |
| } |
| |
| /** |
| * Returns encoded matrix, i.e., this matrix in byte array form |
| * |
| * @return the encoded matrix |
| */ |
| public byte[] getEncoded() |
| { |
| int n = (numColumns + 7) >>> 3; |
| n *= numRows; |
| n += 8; |
| byte[] enc = new byte[n]; |
| |
| LittleEndianConversions.I2OSP(numRows, enc, 0); |
| LittleEndianConversions.I2OSP(numColumns, enc, 4); |
| |
| // number of "full" integer |
| int q = numColumns >>> 5; |
| // number of bits in non-full integer |
| int r = numColumns & 0x1f; |
| |
| int count = 8; |
| for (int i = 0; i < numRows; i++) |
| { |
| for (int j = 0; j < q; j++, count += 4) |
| { |
| LittleEndianConversions.I2OSP(matrix[i][j], enc, count); |
| } |
| for (int j = 0; j < r; j += 8) |
| { |
| enc[count++] = (byte)((matrix[i][q] >>> j) & 0xff); |
| } |
| |
| } |
| return enc; |
| } |
| |
| |
| /** |
| * Returns the percentage of the number of "ones" in this matrix. |
| * |
| * @return the Hamming weight of this matrix (as a ratio). |
| */ |
| public double getHammingWeight() |
| { |
| double counter = 0.0; |
| double elementCounter = 0.0; |
| int rest = numColumns & 0x1f; |
| int d; |
| if (rest == 0) |
| { |
| d = length; |
| } |
| else |
| { |
| d = length - 1; |
| } |
| |
| for (int i = 0; i < numRows; i++) |
| { |
| |
| for (int j = 0; j < d; j++) |
| { |
| int a = matrix[i][j]; |
| for (int k = 0; k < 32; k++) |
| { |
| int b = (a >>> k) & 1; |
| counter = counter + b; |
| elementCounter = elementCounter + 1; |
| } |
| } |
| int a = matrix[i][length - 1]; |
| for (int k = 0; k < rest; k++) |
| { |
| int b = (a >>> k) & 1; |
| counter = counter + b; |
| elementCounter = elementCounter + 1; |
| } |
| } |
| |
| return counter / elementCounter; |
| } |
| |
| /** |
| * 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 < length; j++) |
| { |
| if (matrix[i][j] != 0) |
| { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Get the quadratic submatrix of this matrix consisting of the leftmost |
| * <tt>numRows</tt> columns. |
| * |
| * @return the <tt>(numRows x numRows)</tt> submatrix |
| */ |
| public GF2Matrix getLeftSubMatrix() |
| { |
| if (numColumns <= numRows) |
| { |
| throw new ArithmeticException("empty submatrix"); |
| } |
| int length = (numRows + 31) >> 5; |
| int[][] result = new int[numRows][length]; |
| int bitMask = (1 << (numRows & 0x1f)) - 1; |
| if (bitMask == 0) |
| { |
| bitMask = -1; |
| } |
| for (int i = numRows - 1; i >= 0; i--) |
| { |
| System.arraycopy(matrix[i], 0, result[i], 0, length); |
| result[i][length - 1] &= bitMask; |
| } |
| return new GF2Matrix(numRows, result); |
| } |
| |
| /** |
| * Compute the full form matrix <tt>(this | Id)</tt> from this matrix in |
| * left compact form, where <tt>Id</tt> is the <tt>k x k</tt> identity |
| * matrix and <tt>k</tt> is the number of rows of this matrix. |
| * |
| * @return <tt>(this | Id)</tt> |
| */ |
| public GF2Matrix extendLeftCompactForm() |
| { |
| int newNumColumns = numColumns + numRows; |
| GF2Matrix result = new GF2Matrix(numRows, newNumColumns); |
| |
| int ind = numRows - 1 + numColumns; |
| for (int i = numRows - 1; i >= 0; i--, ind--) |
| { |
| // copy this matrix to first columns |
| System.arraycopy(matrix[i], 0, result.matrix[i], 0, length); |
| // store the identity in last columns |
| result.matrix[i][ind >> 5] |= 1 << (ind & 0x1f); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Get the submatrix of this matrix consisting of the rightmost |
| * <tt>numColumns-numRows</tt> columns. |
| * |
| * @return the <tt>(numRows x (numColumns-numRows))</tt> submatrix |
| */ |
| public GF2Matrix getRightSubMatrix() |
| { |
| if (numColumns <= numRows) |
| { |
| throw new ArithmeticException("empty submatrix"); |
| } |
| |
| int q = numRows >> 5; |
| int r = numRows & 0x1f; |
| |
| GF2Matrix result = new GF2Matrix(numRows, numColumns - numRows); |
| |
| for (int i = numRows - 1; i >= 0; i--) |
| { |
| // if words have to be shifted |
| if (r != 0) |
| { |
| int ind = q; |
| // process all but last word |
| for (int j = 0; j < result.length - 1; j++) |
| { |
| // shift to correct position |
| result.matrix[i][j] = (matrix[i][ind++] >>> r) |
| | (matrix[i][ind] << (32 - r)); |
| } |
| // process last word |
| result.matrix[i][result.length - 1] = matrix[i][ind++] >>> r; |
| if (ind < length) |
| { |
| result.matrix[i][result.length - 1] |= matrix[i][ind] << (32 - r); |
| } |
| } |
| else |
| { |
| // no shifting necessary |
| System.arraycopy(matrix[i], q, result.matrix[i], 0, |
| result.length); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Compute the full form matrix <tt>(Id | this)</tt> from this matrix in |
| * right compact form, where <tt>Id</tt> is the <tt>k x k</tt> identity |
| * matrix and <tt>k</tt> is the number of rows of this matrix. |
| * |
| * @return <tt>(Id | this)</tt> |
| */ |
| public GF2Matrix extendRightCompactForm() |
| { |
| GF2Matrix result = new GF2Matrix(numRows, numRows + numColumns); |
| |
| int q = numRows >> 5; |
| int r = numRows & 0x1f; |
| |
| for (int i = numRows - 1; i >= 0; i--) |
| { |
| // store the identity in first columns |
| result.matrix[i][i >> 5] |= 1 << (i & 0x1f); |
| |
| // copy this matrix to last columns |
| |
| // if words have to be shifted |
| if (r != 0) |
| { |
| int ind = q; |
| // process all but last word |
| for (int j = 0; j < length - 1; j++) |
| { |
| // obtain matrix word |
| int mw = matrix[i][j]; |
| // shift to correct position |
| result.matrix[i][ind++] |= mw << r; |
| result.matrix[i][ind] |= mw >>> (32 - r); |
| } |
| // process last word |
| int mw = matrix[i][length - 1]; |
| result.matrix[i][ind++] |= mw << r; |
| if (ind < result.length) |
| { |
| result.matrix[i][ind] |= mw >>> (32 - r); |
| } |
| } |
| else |
| { |
| // no shifting necessary |
| System.arraycopy(matrix[i], 0, result.matrix[i], q, length); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Compute the transpose of this matrix. |
| * |
| * @return <tt>(this)<sup>T</sup></tt> |
| */ |
| public Matrix computeTranspose() |
| { |
| int[][] result = new int[numColumns][(numRows + 31) >>> 5]; |
| for (int i = 0; i < numRows; i++) |
| { |
| for (int j = 0; j < numColumns; j++) |
| { |
| int qs = j >>> 5; |
| int rs = j & 0x1f; |
| int b = (matrix[i][qs] >>> rs) & 1; |
| int qt = i >>> 5; |
| int rt = i & 0x1f; |
| if (b == 1) |
| { |
| result[j][qt] |= 1 << rt; |
| } |
| } |
| } |
| |
| return new GF2Matrix(numRows, result); |
| } |
| |
| /** |
| * Compute the inverse of this matrix. |
| * |
| * @return the inverse of this matrix (newly created). |
| * @throws ArithmeticException if this matrix is not invertible. |
| */ |
| public Matrix computeInverse() |
| { |
| if (numRows != numColumns) |
| { |
| throw new ArithmeticException("Matrix is not invertible."); |
| } |
| |
| // clone this matrix |
| int[][] tmpMatrix = new int[numRows][length]; |
| 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][length]; |
| for (int i = numRows - 1; i >= 0; i--) |
| { |
| int q = i >> 5; |
| int r = i & 0x1f; |
| invMatrix[i][q] = 1 << r; |
| } |
| |
| // simultaneously compute Gaussian reduction of tmpMatrix and unit |
| // matrix |
| for (int i = 0; i < numRows; i++) |
| { |
| // i = q * 32 + (i mod 32) |
| int q = i >> 5; |
| int bitMask = 1 << (i & 0x1f); |
| // if diagonal element is zero |
| if ((tmpMatrix[i][q] & bitMask) == 0) |
| { |
| boolean foundNonZero = false; |
| // find a non-zero element in the same column |
| for (int j = i + 1; j < numRows; j++) |
| { |
| if ((tmpMatrix[j][q] & bitMask) != 0) |
| { |
| // found it, swap rows ... |
| foundNonZero = true; |
| swapRows(tmpMatrix, i, j); |
| swapRows(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 all but i-th row |
| for (int j = numRows - 1; j >= 0; j--) |
| { |
| if ((j != i) && ((tmpMatrix[j][q] & bitMask) != 0)) |
| { |
| addToRow(tmpMatrix[i], tmpMatrix[j], q); |
| addToRow(invMatrix[i], invMatrix[j], 0); |
| } |
| } |
| } |
| |
| return new GF2Matrix(numColumns, invMatrix); |
| } |
| |
| /** |
| * Compute the product of a permutation matrix (which is generated from an |
| * n-permutation) and this matrix. |
| * |
| * @param p the permutation |
| * @return {@link GF2Matrix} <tt>P*this</tt> |
| */ |
| public Matrix leftMultiply(Permutation p) |
| { |
| int[] pVec = p.getVector(); |
| if (pVec.length != numRows) |
| { |
| throw new ArithmeticException("length mismatch"); |
| } |
| |
| int[][] result = new int[numRows][]; |
| |
| for (int i = numRows - 1; i >= 0; i--) |
| { |
| result[i] = IntUtils.clone(matrix[pVec[i]]); |
| } |
| |
| return new GF2Matrix(numRows, result); |
| } |
| |
| /** |
| * compute product a row vector and this matrix |
| * |
| * @param vec a vector over GF(2) |
| * @return Vector product a*matrix |
| */ |
| public Vector leftMultiply(Vector vec) |
| { |
| |
| if (!(vec instanceof GF2Vector)) |
| { |
| throw new ArithmeticException("vector is not defined over GF(2)"); |
| } |
| |
| if (vec.length != numRows) |
| { |
| throw new ArithmeticException("length mismatch"); |
| } |
| |
| int[] v = ((GF2Vector)vec).getVecArray(); |
| int[] res = new int[length]; |
| |
| int q = numRows >> 5; |
| int r = 1 << (numRows & 0x1f); |
| |
| // compute scalar products with full words of vector |
| int row = 0; |
| for (int i = 0; i < q; i++) |
| { |
| int bitMask = 1; |
| do |
| { |
| int b = v[i] & bitMask; |
| if (b != 0) |
| { |
| for (int j = 0; j < length; j++) |
| { |
| res[j] ^= matrix[row][j]; |
| } |
| } |
| row++; |
| bitMask <<= 1; |
| } |
| while (bitMask != 0); |
| } |
| |
| // compute scalar products with last word of vector |
| int bitMask = 1; |
| while (bitMask != r) |
| { |
| int b = v[q] & bitMask; |
| if (b != 0) |
| { |
| for (int j = 0; j < length; j++) |
| { |
| res[j] ^= matrix[row][j]; |
| } |
| } |
| row++; |
| bitMask <<= 1; |
| } |
| |
| return new GF2Vector(res, numColumns); |
| } |
| |
| /** |
| * Compute the product of the matrix <tt>(this | Id)</tt> and a column |
| * vector, where <tt>Id</tt> is a <tt>(numRows x numRows)</tt> unit |
| * matrix. |
| * |
| * @param vec the vector over GF(2) |
| * @return <tt>(this | Id)*vector</tt> |
| */ |
| public Vector leftMultiplyLeftCompactForm(Vector vec) |
| { |
| if (!(vec instanceof GF2Vector)) |
| { |
| throw new ArithmeticException("vector is not defined over GF(2)"); |
| } |
| |
| if (vec.length != numRows) |
| { |
| throw new ArithmeticException("length mismatch"); |
| } |
| |
| int[] v = ((GF2Vector)vec).getVecArray(); |
| int[] res = new int[(numRows + numColumns + 31) >>> 5]; |
| |
| // process full words of vector |
| int words = numRows >>> 5; |
| int row = 0; |
| for (int i = 0; i < words; i++) |
| { |
| int bitMask = 1; |
| do |
| { |
| int b = v[i] & bitMask; |
| if (b != 0) |
| { |
| // compute scalar product part |
| for (int j = 0; j < length; j++) |
| { |
| res[j] ^= matrix[row][j]; |
| } |
| // set last bit |
| int q = (numColumns + row) >>> 5; |
| int r = (numColumns + row) & 0x1f; |
| res[q] |= 1 << r; |
| } |
| row++; |
| bitMask <<= 1; |
| } |
| while (bitMask != 0); |
| } |
| |
| // process last word of vector |
| int rem = 1 << (numRows & 0x1f); |
| int bitMask = 1; |
| while (bitMask != rem) |
| { |
| int b = v[words] & bitMask; |
| if (b != 0) |
| { |
| // compute scalar product part |
| for (int j = 0; j < length; j++) |
| { |
| res[j] ^= matrix[row][j]; |
| } |
| // set last bit |
| int q = (numColumns + row) >>> 5; |
| int r = (numColumns + row) & 0x1f; |
| res[q] |= 1 << r; |
| } |
| row++; |
| bitMask <<= 1; |
| } |
| |
| return new GF2Vector(res, numRows + numColumns); |
| } |
| |
| /** |
| * Compute the product of this matrix and a matrix A over GF(2). |
| * |
| * @param mat a matrix A over GF(2) |
| * @return matrix product <tt>this*matrixA</tt> |
| */ |
| public Matrix rightMultiply(Matrix mat) |
| { |
| if (!(mat instanceof GF2Matrix)) |
| { |
| throw new ArithmeticException("matrix is not defined over GF(2)"); |
| } |
| |
| if (mat.numRows != numColumns) |
| { |
| throw new ArithmeticException("length mismatch"); |
| } |
| |
| GF2Matrix a = (GF2Matrix)mat; |
| GF2Matrix result = new GF2Matrix(numRows, mat.numColumns); |
| |
| int d; |
| int rest = numColumns & 0x1f; |
| if (rest == 0) |
| { |
| d = length; |
| } |
| else |
| { |
| d = length - 1; |
| } |
| for (int i = 0; i < numRows; i++) |
| { |
| int count = 0; |
| for (int j = 0; j < d; j++) |
| { |
| int e = matrix[i][j]; |
| for (int h = 0; h < 32; h++) |
| { |
| int b = e & (1 << h); |
| if (b != 0) |
| { |
| for (int g = 0; g < a.length; g++) |
| { |
| result.matrix[i][g] ^= a.matrix[count][g]; |
| } |
| } |
| count++; |
| } |
| } |
| int e = matrix[i][length - 1]; |
| for (int h = 0; h < rest; h++) |
| { |
| int b = e & (1 << h); |
| if (b != 0) |
| { |
| for (int g = 0; g < a.length; g++) |
| { |
| result.matrix[i][g] ^= a.matrix[count][g]; |
| } |
| } |
| count++; |
| } |
| |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Compute the product of this matrix and a permutation matrix which is |
| * generated from an n-permutation. |
| * |
| * @param p the permutation |
| * @return {@link GF2Matrix} <tt>this*P</tt> |
| */ |
| public Matrix rightMultiply(Permutation p) |
| { |
| |
| int[] pVec = p.getVector(); |
| if (pVec.length != numColumns) |
| { |
| throw new ArithmeticException("length mismatch"); |
| } |
| |
| GF2Matrix result = new GF2Matrix(numRows, numColumns); |
| |
| for (int i = numColumns - 1; i >= 0; i--) |
| { |
| int q = i >>> 5; |
| int r = i & 0x1f; |
| int pq = pVec[i] >>> 5; |
| int pr = pVec[i] & 0x1f; |
| for (int j = numRows - 1; j >= 0; j--) |
| { |
| result.matrix[j][q] |= ((matrix[j][pq] >>> pr) & 1) << r; |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Compute the product of this matrix and the given column vector. |
| * |
| * @param vec the vector over GF(2) |
| * @return <tt>this*vector</tt> |
| */ |
| public Vector rightMultiply(Vector vec) |
| { |
| if (!(vec instanceof GF2Vector)) |
| { |
| throw new ArithmeticException("vector is not defined over GF(2)"); |
| } |
| |
| if (vec.length != numColumns) |
| { |
| throw new ArithmeticException("length mismatch"); |
| } |
| |
| int[] v = ((GF2Vector)vec).getVecArray(); |
| int[] res = new int[(numRows + 31) >>> 5]; |
| |
| for (int i = 0; i < numRows; i++) |
| { |
| // compute full word scalar products |
| int help = 0; |
| for (int j = 0; j < length; j++) |
| { |
| help ^= matrix[i][j] & v[j]; |
| } |
| // compute single word scalar product |
| int bitValue = 0; |
| for (int j = 0; j < 32; j++) |
| { |
| bitValue ^= (help >>> j) & 1; |
| } |
| // set result bit |
| if (bitValue == 1) |
| { |
| res[i >>> 5] |= 1 << (i & 0x1f); |
| } |
| } |
| |
| return new GF2Vector(res, numRows); |
| } |
| |
| /** |
| * Compute the product of the matrix <tt>(Id | this)</tt> and a column |
| * vector, where <tt>Id</tt> is a <tt>(numRows x numRows)</tt> unit |
| * matrix. |
| * |
| * @param vec the vector over GF(2) |
| * @return <tt>(Id | this)*vector</tt> |
| */ |
| public Vector rightMultiplyRightCompactForm(Vector vec) |
| { |
| if (!(vec instanceof GF2Vector)) |
| { |
| throw new ArithmeticException("vector is not defined over GF(2)"); |
| } |
| |
| if (vec.length != numColumns + numRows) |
| { |
| throw new ArithmeticException("length mismatch"); |
| } |
| |
| int[] v = ((GF2Vector)vec).getVecArray(); |
| int[] res = new int[(numRows + 31) >>> 5]; |
| |
| int q = numRows >> 5; |
| int r = numRows & 0x1f; |
| |
| // for all rows |
| for (int i = 0; i < numRows; i++) |
| { |
| // get vector bit |
| int help = (v[i >> 5] >>> (i & 0x1f)) & 1; |
| |
| // compute full word scalar products |
| int vInd = q; |
| // if words have to be shifted |
| if (r != 0) |
| { |
| int vw = 0; |
| // process all but last word |
| for (int j = 0; j < length - 1; j++) |
| { |
| // shift to correct position |
| vw = (v[vInd++] >>> r) | (v[vInd] << (32 - r)); |
| help ^= matrix[i][j] & vw; |
| } |
| // process last word |
| vw = v[vInd++] >>> r; |
| if (vInd < v.length) |
| { |
| vw |= v[vInd] << (32 - r); |
| } |
| help ^= matrix[i][length - 1] & vw; |
| } |
| else |
| { |
| // no shifting necessary |
| for (int j = 0; j < length; j++) |
| { |
| help ^= matrix[i][j] & v[vInd++]; |
| } |
| } |
| |
| // compute single word scalar product |
| int bitValue = 0; |
| for (int j = 0; j < 32; j++) |
| { |
| bitValue ^= help & 1; |
| help >>>= 1; |
| } |
| |
| // set result bit |
| if (bitValue == 1) |
| { |
| res[i >> 5] |= 1 << (i & 0x1f); |
| } |
| } |
| |
| return new GF2Vector(res, numRows); |
| } |
| |
| /** |
| * Compare this matrix with another object. |
| * |
| * @param other another object |
| * @return the result of the comparison |
| */ |
| public boolean equals(Object other) |
| { |
| |
| if (!(other instanceof GF2Matrix)) |
| { |
| return false; |
| } |
| GF2Matrix otherMatrix = (GF2Matrix)other; |
| |
| if ((numRows != otherMatrix.numRows) |
| || (numColumns != otherMatrix.numColumns) |
| || (length != otherMatrix.length)) |
| { |
| return false; |
| } |
| |
| for (int i = 0; i < numRows; i++) |
| { |
| if (!IntUtils.equals(matrix[i], otherMatrix.matrix[i])) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * @return the hash code of this matrix |
| */ |
| public int hashCode() |
| { |
| int hash = (numRows * 31 + numColumns) * 31 + length; |
| for (int i = 0; i < numRows; i++) |
| { |
| hash = hash * 31 + matrix[i].hashCode(); |
| } |
| return hash; |
| } |
| |
| /** |
| * @return a human readable form of the matrix |
| */ |
| public String toString() |
| { |
| int rest = numColumns & 0x1f; |
| int d; |
| if (rest == 0) |
| { |
| d = length; |
| } |
| else |
| { |
| d = length - 1; |
| } |
| |
| StringBuffer buf = new StringBuffer(); |
| for (int i = 0; i < numRows; i++) |
| { |
| buf.append(i + ": "); |
| for (int j = 0; j < d; j++) |
| { |
| int a = matrix[i][j]; |
| for (int k = 0; k < 32; k++) |
| { |
| int b = (a >>> k) & 1; |
| if (b == 0) |
| { |
| buf.append('0'); |
| } |
| else |
| { |
| buf.append('1'); |
| } |
| } |
| buf.append(' '); |
| } |
| int a = matrix[i][length - 1]; |
| for (int k = 0; k < rest; k++) |
| { |
| int b = (a >>> k) & 1; |
| if (b == 0) |
| { |
| buf.append('0'); |
| } |
| else |
| { |
| buf.append('1'); |
| } |
| } |
| buf.append('\n'); |
| } |
| |
| return buf.toString(); |
| } |
| |
| /** |
| * Swap two rows of the given matrix. |
| * |
| * @param matrix the matrix |
| * @param first the index of the first row |
| * @param second the index of the second row |
| */ |
| private static void swapRows(int[][] matrix, int first, int second) |
| { |
| int[] tmp = matrix[first]; |
| matrix[first] = matrix[second]; |
| matrix[second] = tmp; |
| } |
| |
| /** |
| * Partially add one row to another. |
| * |
| * @param fromRow the addend |
| * @param toRow the row to add to |
| * @param startIndex the array index to start from |
| */ |
| private static void addToRow(int[] fromRow, int[] toRow, int startIndex) |
| { |
| for (int i = toRow.length - 1; i >= startIndex; i--) |
| { |
| toRow[i] = fromRow[i] ^ toRow[i]; |
| } |
| } |
| |
| } |