336 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C#
		
	
	
		
		
			
		
	
	
			336 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C#
		
	
	
| 
								 | 
							
								using System;
							 | 
						||
| 
								 | 
							
								using System.Linq;
							 | 
						||
| 
								 | 
							
								using System.Text;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace fec
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    public class Matrix
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								     * The number of rows in the matrix.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    private readonly int rows;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * The number of columns in the matrix.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    private readonly int columns;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * The data in the matrix, in row major form.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * To get element (r, c): data[r][c]
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * Because this this is computer science, and not math,
							 | 
						||
| 
								 | 
							
								     * the indices for both the row and column start at 0.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    private readonly byte [] [] data;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Initialize a matrix of zeros.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param initRows The number of rows in the matrix.
							 | 
						||
| 
								 | 
							
								     * @param initColumns The number of columns in the matrix.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public Matrix(int initRows, int initColumns) {
							 | 
						||
| 
								 | 
							
								        rows = initRows;
							 | 
						||
| 
								 | 
							
								        columns = initColumns;
							 | 
						||
| 
								 | 
							
								        data = new byte [rows] [];
							 | 
						||
| 
								 | 
							
								        for (int r = 0; r < rows; r++) {
							 | 
						||
| 
								 | 
							
								            data[r] = new byte [columns];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Initializes a matrix with the given row-major data.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public Matrix(byte [] [] initData) {
							 | 
						||
| 
								 | 
							
								        rows = initData.Length;
							 | 
						||
| 
								 | 
							
								        columns = initData[0].Length;
							 | 
						||
| 
								 | 
							
								        data = new byte [rows] [];
							 | 
						||
| 
								 | 
							
								        for (int r = 0; r < rows; r++) {
							 | 
						||
| 
								 | 
							
								            if (initData[r].Length != columns) {
							 | 
						||
| 
								 | 
							
								                throw new Exception("Not all rows have the same number of columns");
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            data[r] = new byte[columns];
							 | 
						||
| 
								 | 
							
								            for (int c = 0; c < columns; c++) {
							 | 
						||
| 
								 | 
							
								                data[r][c] = initData[r][c];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns an identity matrix of the given size.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public static Matrix identity(int size) {
							 | 
						||
| 
								 | 
							
								        Matrix result = new Matrix(size, size);
							 | 
						||
| 
								 | 
							
								        for (int i = 0; i < size; i++) {
							 | 
						||
| 
								 | 
							
								            result.set(i, i, 1);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns a human-readable string of the matrix contents.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * Example: [[1, 2], [3, 4]]
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public string toString() {
							 | 
						||
| 
								 | 
							
								        StringBuilder result = new StringBuilder();
							 | 
						||
| 
								 | 
							
								        result.Append('[');
							 | 
						||
| 
								 | 
							
								        for (int r = 0; r < rows; r++) {
							 | 
						||
| 
								 | 
							
								            if (r != 0) {
							 | 
						||
| 
								 | 
							
								                result.Append(", ");
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            result.Append('[');
							 | 
						||
| 
								 | 
							
								            for (int c = 0; c < columns; c++) {
							 | 
						||
| 
								 | 
							
								                if (c != 0) {
							 | 
						||
| 
								 | 
							
								                    result.Append(", ");
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                result.Append(data[r][c] & 0xFF);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            result.Append(']');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        result.Append(']');
							 | 
						||
| 
								 | 
							
								        return result.ToString();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns a human-readable string of the matrix contents.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * Example:
							 | 
						||
| 
								 | 
							
								     *    00 01 02
							 | 
						||
| 
								 | 
							
								     *    03 04 05
							 | 
						||
| 
								 | 
							
								     *    06 07 08
							 | 
						||
| 
								 | 
							
								     *    09 0a 0b
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public String toBigString() {
							 | 
						||
| 
								 | 
							
								        StringBuilder result = new StringBuilder();
							 | 
						||
| 
								 | 
							
								        for (int r = 0; r < rows; r++) {
							 | 
						||
| 
								 | 
							
								            for (int c = 0; c < columns; c++) {
							 | 
						||
| 
								 | 
							
								                int value = get(r, c);
							 | 
						||
| 
								 | 
							
								                if (value < 0) {
							 | 
						||
| 
								 | 
							
								                    value += 256;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                result.Append(String.Format("%02x ", value));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            result.Append("\n");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return result.ToString();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns the number of columns in this matrix.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public int getColumns() {
							 | 
						||
| 
								 | 
							
								        return columns;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns the number of rows in this matrix.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public int getRows() {
							 | 
						||
| 
								 | 
							
								        return rows;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns the value at row r, column c.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public byte get(int r, int c) {
							 | 
						||
| 
								 | 
							
								        if (r < 0 || rows <= r) {
							 | 
						||
| 
								 | 
							
								            throw new Exception("Row index out of range: " + r);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (c < 0 || columns <= c) {
							 | 
						||
| 
								 | 
							
								            throw new Exception("Column index out of range: " + c);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return data[r][c];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Sets the value at row r, column c.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public void set(int r, int c, byte value) {
							 | 
						||
| 
								 | 
							
								        if (r < 0 || rows <= r) {
							 | 
						||
| 
								 | 
							
								            throw new Exception("Row index out of range: " + r);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (c < 0 || columns <= c) {
							 | 
						||
| 
								 | 
							
								            throw new Exception("Column index out of range: " + c);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        data[r][c] = value;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns true iff this matrix is identical to the other.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public bool equals(Object other) {
							 | 
						||
| 
								 | 
							
								        if (!(other is Matrix)) {
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        for (int r = 0; r < rows; r++) {
							 | 
						||
| 
								 | 
							
								            if (!Object.Equals(data[r], ((Matrix)other).data[r])) {
							 | 
						||
| 
								 | 
							
								                return false;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Multiplies this matrix (the one on the left) by another
							 | 
						||
| 
								 | 
							
								     * matrix (the one on the right).
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public Matrix times(Matrix right) {
							 | 
						||
| 
								 | 
							
								        if (getColumns() != right.getRows()) {
							 | 
						||
| 
								 | 
							
								            throw new Exception(
							 | 
						||
| 
								 | 
							
								                    "Columns on left (" + getColumns() +") " +
							 | 
						||
| 
								 | 
							
								                    "is different than rows on right (" + right.getRows() + ")");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        Matrix result = new Matrix(getRows(), right.getColumns());
							 | 
						||
| 
								 | 
							
								        for (int r = 0; r < getRows(); r++) {
							 | 
						||
| 
								 | 
							
								            for (int c = 0; c < right.getColumns(); c++) {
							 | 
						||
| 
								 | 
							
								                byte value = 0;
							 | 
						||
| 
								 | 
							
								                for (int i = 0; i < getColumns(); i++) {
							 | 
						||
| 
								 | 
							
								                    value ^= Galois.multiply(get(r, i), right.get(i, c));
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                result.set(r, c, value);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns the concatenation of this matrix and the matrix on the right.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public Matrix augment(Matrix right) {
							 | 
						||
| 
								 | 
							
								        if (rows != right.rows) {
							 | 
						||
| 
								 | 
							
								            throw new Exception("Matrices don't have the same number of rows");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        Matrix result = new Matrix(rows, columns + right.columns);
							 | 
						||
| 
								 | 
							
								        for (int r = 0; r < rows; r++) {
							 | 
						||
| 
								 | 
							
								            for (int c = 0; c < columns; c++) {
							 | 
						||
| 
								 | 
							
								                result.data[r][c] = data[r][c];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            for (int c = 0; c < right.columns; c++) {
							 | 
						||
| 
								 | 
							
								                result.data[r][columns + c] = right.data[r][c];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns a part of this matrix.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public Matrix submatrix(int rmin, int cmin, int rmax, int cmax) {
							 | 
						||
| 
								 | 
							
								        Matrix result = new Matrix(rmax - rmin, cmax - cmin);
							 | 
						||
| 
								 | 
							
								        for (int r = rmin; r < rmax; r++) {
							 | 
						||
| 
								 | 
							
								            for (int c = cmin; c < cmax; c++) {
							 | 
						||
| 
								 | 
							
								                result.data[r - rmin][c - cmin] = data[r][c];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns one row of the matrix as a byte array.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public byte [] getRow(int row) {
							 | 
						||
| 
								 | 
							
								        byte [] result = new byte [columns];
							 | 
						||
| 
								 | 
							
								        for (int c = 0; c < columns; c++) {
							 | 
						||
| 
								 | 
							
								            result[c] = get(row, c);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Exchanges two rows in the matrix.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public void swapRows(int r1, int r2) {
							 | 
						||
| 
								 | 
							
								        if (r1 < 0 || rows <= r1 || r2 < 0 || rows <= r2) {
							 | 
						||
| 
								 | 
							
								            throw new Exception("Row index out of range");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        byte [] tmp = data[r1];
							 | 
						||
| 
								 | 
							
								        data[r1] = data[r2];
							 | 
						||
| 
								 | 
							
								        data[r2] = tmp;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Returns the inverse of this matrix.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @throws IllegalArgumentException when the matrix is singular and
							 | 
						||
| 
								 | 
							
								     * doesn't have an inverse.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public Matrix invert() {
							 | 
						||
| 
								 | 
							
								        // Sanity check.
							 | 
						||
| 
								 | 
							
								        if (rows != columns) {
							 | 
						||
| 
								 | 
							
								            throw new Exception("Only square matrices can be inverted");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Create a working matrix by augmenting this one with
							 | 
						||
| 
								 | 
							
								        // an identity matrix on the right.
							 | 
						||
| 
								 | 
							
								        Matrix work = augment(identity(rows));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Do Gaussian elimination to transform the left half into
							 | 
						||
| 
								 | 
							
								        // an identity matrix.
							 | 
						||
| 
								 | 
							
								        work.gaussianElimination();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // The right half is now the inverse.
							 | 
						||
| 
								 | 
							
								        return work.submatrix(0, rows, columns, columns * 2);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Does the work of matrix inversion.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * Assumes that this is an r by 2r matrix.
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    private void gaussianElimination() {
							 | 
						||
| 
								 | 
							
								        // Clear out the part below the main diagonal and scale the main
							 | 
						||
| 
								 | 
							
								        // diagonal to be 1.
							 | 
						||
| 
								 | 
							
								        for (int r = 0; r < rows; r++) {
							 | 
						||
| 
								 | 
							
								            // If the element on the diagonal is 0, find a row below
							 | 
						||
| 
								 | 
							
								            // that has a non-zero and swap them.
							 | 
						||
| 
								 | 
							
								            if (data[r][r] == 0) {
							 | 
						||
| 
								 | 
							
								                for (int rowBelow = r + 1; rowBelow < rows; rowBelow++) {
							 | 
						||
| 
								 | 
							
								                    if (data[rowBelow][r] != 0) {
							 | 
						||
| 
								 | 
							
								                        swapRows(r, rowBelow);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // If we couldn't find one, the matrix is singular.
							 | 
						||
| 
								 | 
							
								            if (data[r][r] == 0) {
							 | 
						||
| 
								 | 
							
								                throw new Exception("Matrix is singular");
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // Scale to 1.
							 | 
						||
| 
								 | 
							
								            if (data[r][r] != 1) {
							 | 
						||
| 
								 | 
							
								                byte scale = Galois.divide(1, data[r][r]);
							 | 
						||
| 
								 | 
							
								                for (int c = 0; c < columns; c++) {
							 | 
						||
| 
								 | 
							
								                    data[r][c] = Galois.multiply(data[r][c], scale);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // Make everything below the 1 be a 0 by subtracting
							 | 
						||
| 
								 | 
							
								            // a multiple of it.  (Subtraction and addition are
							 | 
						||
| 
								 | 
							
								            // both exclusive or in the Galois field.)
							 | 
						||
| 
								 | 
							
								            for (int rowBelow = r + 1; rowBelow < rows; rowBelow++) {
							 | 
						||
| 
								 | 
							
								                if (data[rowBelow][r] != 0) {
							 | 
						||
| 
								 | 
							
								                    byte scale = data[rowBelow][r];
							 | 
						||
| 
								 | 
							
								                    for (int c = 0; c < columns; c++) {
							 | 
						||
| 
								 | 
							
								                        data[rowBelow][c] ^= Galois.multiply(scale, data[r][c]);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // Now clear the part above the main diagonal.
							 | 
						||
| 
								 | 
							
								        for (int d = 0; d < rows; d++) {
							 | 
						||
| 
								 | 
							
								            for (int rowAbove = 0; rowAbove < d; rowAbove++) {
							 | 
						||
| 
								 | 
							
								                if (data[rowAbove][d] != 0) {
							 | 
						||
| 
								 | 
							
								                    byte scale = data[rowAbove][d];
							 | 
						||
| 
								 | 
							
								                    for (int c = 0; c < columns; c++) {
							 | 
						||
| 
								 | 
							
								                        data[rowAbove][c] ^= Galois.multiply(scale, data[d][c]);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |