@Override
    public Matrix kernel(Matrix matrix) {
        isValid(matrix);

        Matrix kernelMatrix = new Matrix(matrix);
        kernelMatrix.transpose();
        kernelMatrix = gauss(kernelMatrix);

        //prepare result matrix
        Matrix result = new Matrix(matrix.getRows() - matrix.getColumns(), matrix.getRows());
        for (int x = 0; x < result.getRows(); x++) {
            result.setElement(x, result.getColumns() - x - 1, 1);
        }

        HashSet rowsToDelete = new HashSet();

        int index = result.getRows();
        int position = 0;
        for (int counter = result.getRows() - 1; counter >= 0; counter--) {

            index--;
            position++;

            //set kernel vector for every row
            for (int row = kernelMatrix.getRows() - 1; row >= 0; row--) {

                long value = 0;
                //loop used to find a good value in current row
                for (int col = row + 1; col <= kernelMatrix.getColumns() - 1; col++) {
                    value = galoisField.add(value, galoisField.multiply(kernelMatrix.getElement(row, col),
                            result.getElement(index, col)));
                }

                if (row == kernelMatrix.getRows() - 1) {
                    long numerator = kernelMatrix.getElement(row, (kernelMatrix.getRows() - 1) + position);
                    long denumerator = kernelMatrix.getElement(row, row);
                    if (denumerator != 0) {
                        result.setElement(index, row, galoisField.divide(numerator, denumerator));
                    } else {
                        if (numerator == 0) {
                            result.setElement(index, row, 0);
                        } else {
                            rowsToDelete.add(counter);
                        }
                    }

                } else {
                    if (kernelMatrix.getElement(row, row) != 0) {
                        result.setElement(index, row, galoisField.divide(value, kernelMatrix.getElement(row, row)));
                    } else {
                        if (value == 0) {
                            result.setElement(index, row, 0);
                        } else {
                            rowsToDelete.add(counter);
                        }
                    }
                }
            }
        }

        //For some matrices, kernel cannot be found for every parameter
        //deleteRows is called to delete this rows from result matrix
        return deleteRows(result, rowsToDelete);
    }