/*
 * Copyright 2014 OpenRQ Team
 * 
 * Licensed under the Apache License, Version 2 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright 2011-2014, by Vladimir Kostyukov and Contributors.
 * 
 * This file is part of la4j project (http://la4j.org)
 * 
 * Licensed under the Apache License, Version 2 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * Contributor(s): Evgenia Krivova
 * Jakob Moellers
 * Maxim Samoylov
 * Anveshi Charuvaka
 * Todd Brunhoff
 * Catherine da Graca
 */
package net.fec.openrq.util.linearalgebra.matrix;


import static net.fec.openrq.util.math.OctetOps.aTimesB;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;

import net.fec.openrq.util.linearalgebra.factory.Factory;
import net.fec.openrq.util.linearalgebra.io.ByteVectorIterator;
import net.fec.openrq.util.linearalgebra.matrix.functor.MatrixAccumulator;
import net.fec.openrq.util.linearalgebra.matrix.functor.MatrixFunction;
import net.fec.openrq.util.linearalgebra.matrix.functor.MatrixProcedure;
import net.fec.openrq.util.linearalgebra.serialize.DeserializationException;
import net.fec.openrq.util.linearalgebra.vector.ByteVector;

import org.junit.Test;


public abstract class AbstractByteMatrixTest {

    public abstract Factory factory();

    @Test
    public void testAccess_3x3() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 3},
                                                            {0, 5, 0},
                                                            {7, 0, 9}
        });

        a.set(0, 1, aTimesB(a.get(1, 1), (byte)2));
        assertEquals(a.get(0, 1), 10);
    }

    private ByteMatrix getRowColumnMatrix() {

        return factory().createMatrix(new byte[][] {
                                                    {8, 3, 1, 9},
                                                    {4, 9, 6, 6},
                                                    {9, 1, 1, 4},
                                                    {5, 7, 3, 0}
        });
    }

    @Test
    public void testGetRow() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {8, 3, 1, 9});
        ByteVector vecB = factory().createVector(new byte[] {4, 9, 6, 6});
        ByteVector vecC = factory().createVector(new byte[] {9, 1, 1, 4});
        ByteVector vecD = factory().createVector(new byte[] {5, 7, 3, 0});

        assertEquals(vecA, matrix.getRow(0));
        assertEquals(vecB, matrix.getRow(1));
        assertEquals(vecC, matrix.getRow(2));
        assertEquals(vecD, matrix.getRow(3));
    }

    @Test
    public void testGetRowInRangeOf_0_to_0() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {});
        ByteVector vecB = factory().createVector(new byte[] {});
        ByteVector vecC = factory().createVector(new byte[] {});
        ByteVector vecD = factory().createVector(new byte[] {});

        assertEquals(vecA, matrix.getRow(0, 0, 0));
        assertEquals(vecB, matrix.getRow(1, 0, 0));
        assertEquals(vecC, matrix.getRow(2, 0, 0));
        assertEquals(vecD, matrix.getRow(3, 0, 0));
    }

    @Test
    public void testGetRowInRangeOf_0_to_2() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {8, 3});
        ByteVector vecB = factory().createVector(new byte[] {4, 9});
        ByteVector vecC = factory().createVector(new byte[] {9, 1});
        ByteVector vecD = factory().createVector(new byte[] {5, 7});

        assertEquals(vecA, matrix.getRow(0, 0, 2));
        assertEquals(vecB, matrix.getRow(1, 0, 2));
        assertEquals(vecC, matrix.getRow(2, 0, 2));
        assertEquals(vecD, matrix.getRow(3, 0, 2));
    }

    @Test
    public void testGetRowInRangeOf_0_to_4() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {8, 3, 1, 9});
        ByteVector vecB = factory().createVector(new byte[] {4, 9, 6, 6});
        ByteVector vecC = factory().createVector(new byte[] {9, 1, 1, 4});
        ByteVector vecD = factory().createVector(new byte[] {5, 7, 3, 0});

        assertEquals(vecA, matrix.getRow(0, 0, 4));
        assertEquals(vecB, matrix.getRow(1, 0, 4));
        assertEquals(vecC, matrix.getRow(2, 0, 4));
        assertEquals(vecD, matrix.getRow(3, 0, 4));
    }

    @Test
    public void testGetRowInRangeOf_2_to_2() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {});
        ByteVector vecB = factory().createVector(new byte[] {});
        ByteVector vecC = factory().createVector(new byte[] {});
        ByteVector vecD = factory().createVector(new byte[] {});

        assertEquals(vecA, matrix.getRow(0, 2, 2));
        assertEquals(vecB, matrix.getRow(1, 2, 2));
        assertEquals(vecC, matrix.getRow(2, 2, 2));
        assertEquals(vecD, matrix.getRow(3, 2, 2));
    }

    @Test
    public void testGetRowInRangeOf_2_to_4() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {1, 9});
        ByteVector vecB = factory().createVector(new byte[] {6, 6});
        ByteVector vecC = factory().createVector(new byte[] {1, 4});
        ByteVector vecD = factory().createVector(new byte[] {3, 0});

        assertEquals(vecA, matrix.getRow(0, 2, 4));
        assertEquals(vecB, matrix.getRow(1, 2, 4));
        assertEquals(vecC, matrix.getRow(2, 2, 4));
        assertEquals(vecD, matrix.getRow(3, 2, 4));
    }

    @Test
    public void testGetRowInRangeOf_4_to_4() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {});
        ByteVector vecB = factory().createVector(new byte[] {});
        ByteVector vecC = factory().createVector(new byte[] {});
        ByteVector vecD = factory().createVector(new byte[] {});

        assertEquals(vecA, matrix.getRow(0, 4, 4));
        assertEquals(vecB, matrix.getRow(1, 4, 4));
        assertEquals(vecC, matrix.getRow(2, 4, 4));
        assertEquals(vecD, matrix.getRow(3, 4, 4));
    }

    @Test
    public void testSetRow() {

        ByteMatrix a = factory().createMatrix(new byte[][] {{0, 0, 0, 0}});
        ByteMatrix b = factory().createMatrix(new byte[][] {{1, 2, 3, 4}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setRow(0, vector);

        assertEquals(a, b);
    }

    @Test
    public void testSetRow_InRangeOf_0_to_0() {

        ByteMatrix a = factory().createMatrix(new byte[][] {{0, 0, 0, 0}});
        ByteMatrix b = factory().createMatrix(new byte[][] {{0, 0, 0, 0}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setRow(0, 0, vector, 0, 0);

        assertEquals(a, b);
    }

    @Test
    public void testSetRow_InRangeOf_0_to_2() {

        ByteMatrix a = factory().createMatrix(new byte[][] {{0, 0, 0, 0}});
        ByteMatrix b = factory().createMatrix(new byte[][] {{1, 2, 0, 0}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setRow(0, 0, vector, 0, 2);

        assertEquals(a, b);
    }

    @Test
    public void testSetRow_InRangeOf_0_to_4() {

        ByteMatrix a = factory().createMatrix(new byte[][] {{0, 0, 0, 0}});
        ByteMatrix b = factory().createMatrix(new byte[][] {{1, 2, 3, 4}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setRow(0, 0, vector, 0, 4);

        assertEquals(a, b);
    }

    @Test
    public void testSetRow_InRangeOf_2_to_2() {

        ByteMatrix a = factory().createMatrix(new byte[][] {{0, 0, 0, 0}});
        ByteMatrix b = factory().createMatrix(new byte[][] {{0, 0, 0, 0}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setRow(0, 0, vector, 2, 0); // yes, 0 is the length

        assertEquals(a, b);
    }

    @Test
    public void testSetRow_InRangeOf_2_to_4() {

        ByteMatrix a = factory().createMatrix(new byte[][] {{0, 0, 0, 0}});
        ByteMatrix b = factory().createMatrix(new byte[][] {{0, 0, 3, 4}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setRow(0, 2, vector, 2, 2); // yes, the second 2 is the length

        assertEquals(a, b);
    }

    @Test
    public void testSetRow_InRangeOf_4_to_4() {

        ByteMatrix a = factory().createMatrix(new byte[][] {{0, 0, 0, 0}});
        ByteMatrix b = factory().createMatrix(new byte[][] {{0, 0, 0, 0}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setRow(0, 4, vector, 4, 0); // yes, 0 is the length

        assertEquals(a, b);
    }

    @Test
    public void testGetColumn() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {8, 4, 9, 5});
        ByteVector vecB = factory().createVector(new byte[] {3, 9, 1, 7});
        ByteVector vecC = factory().createVector(new byte[] {1, 6, 1, 3});
        ByteVector vecD = factory().createVector(new byte[] {9, 6, 4, 0});

        assertEquals(vecA, matrix.getColumn(0));
        assertEquals(vecB, matrix.getColumn(1));
        assertEquals(vecC, matrix.getColumn(2));
        assertEquals(vecD, matrix.getColumn(3));
    }

    @Test
    public void testGetColumnInRangeOf_0_to_0() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {});
        ByteVector vecB = factory().createVector(new byte[] {});
        ByteVector vecC = factory().createVector(new byte[] {});
        ByteVector vecD = factory().createVector(new byte[] {});

        assertEquals(vecA, matrix.getColumn(0, 0, 0));
        assertEquals(vecB, matrix.getColumn(1, 0, 0));
        assertEquals(vecC, matrix.getColumn(2, 0, 0));
        assertEquals(vecD, matrix.getColumn(3, 0, 0));
    }

    @Test
    public void testGetColumnInRangeOf_0_to_2() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {8, 4});
        ByteVector vecB = factory().createVector(new byte[] {3, 9});
        ByteVector vecC = factory().createVector(new byte[] {1, 6});
        ByteVector vecD = factory().createVector(new byte[] {9, 6});

        assertEquals(vecA, matrix.getColumn(0, 0, 2));
        assertEquals(vecB, matrix.getColumn(1, 0, 2));
        assertEquals(vecC, matrix.getColumn(2, 0, 2));
        assertEquals(vecD, matrix.getColumn(3, 0, 2));
    }

    @Test
    public void testGetColumnInRangeOf_0_to_4() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {8, 4, 9, 5});
        ByteVector vecB = factory().createVector(new byte[] {3, 9, 1, 7});
        ByteVector vecC = factory().createVector(new byte[] {1, 6, 1, 3});
        ByteVector vecD = factory().createVector(new byte[] {9, 6, 4, 0});

        assertEquals(vecA, matrix.getColumn(0, 0, 4));
        assertEquals(vecB, matrix.getColumn(1, 0, 4));
        assertEquals(vecC, matrix.getColumn(2, 0, 4));
        assertEquals(vecD, matrix.getColumn(3, 0, 4));
    }

    @Test
    public void testGetColumnInRangeOf_2_to_2() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {});
        ByteVector vecB = factory().createVector(new byte[] {});
        ByteVector vecC = factory().createVector(new byte[] {});
        ByteVector vecD = factory().createVector(new byte[] {});

        assertEquals(vecA, matrix.getColumn(0, 2, 2));
        assertEquals(vecB, matrix.getColumn(1, 2, 2));
        assertEquals(vecC, matrix.getColumn(2, 2, 2));
        assertEquals(vecD, matrix.getColumn(3, 2, 2));
    }

    @Test
    public void testGetColumnInRangeOf_2_to_4() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {9, 5});
        ByteVector vecB = factory().createVector(new byte[] {1, 7});
        ByteVector vecC = factory().createVector(new byte[] {1, 3});
        ByteVector vecD = factory().createVector(new byte[] {4, 0});

        assertEquals(vecA, matrix.getColumn(0, 2, 4));
        assertEquals(vecB, matrix.getColumn(1, 2, 4));
        assertEquals(vecC, matrix.getColumn(2, 2, 4));
        assertEquals(vecD, matrix.getColumn(3, 2, 4));
    }

    @Test
    public void testGetColumnInRangeOf_4_to_4() {

        ByteMatrix matrix = getRowColumnMatrix();

        ByteVector vecA = factory().createVector(new byte[] {});
        ByteVector vecB = factory().createVector(new byte[] {});
        ByteVector vecC = factory().createVector(new byte[] {});
        ByteVector vecD = factory().createVector(new byte[] {});

        assertEquals(vecA, matrix.getColumn(0, 4, 4));
        assertEquals(vecB, matrix.getColumn(1, 4, 4));
        assertEquals(vecC, matrix.getColumn(2, 4, 4));
        assertEquals(vecD, matrix.getColumn(3, 4, 4));
    }

    @Test
    public void testSetColumn() {

        ByteMatrix a = factory().createMatrix(new byte[][] { {0}, {0}, {0}, {0}});
        ByteMatrix b = factory().createMatrix(new byte[][] { {1}, {2}, {3}, {4}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setColumn(0, vector);

        assertEquals(a, b);
    }

    @Test
    public void testSetColumn_InRangeOf_0_to_0() {

        ByteMatrix a = factory().createMatrix(new byte[][] { {0}, {0}, {0}, {0}});
        ByteMatrix b = factory().createMatrix(new byte[][] { {0}, {0}, {0}, {0}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setColumn(0, 0, vector, 0, 0);

        assertEquals(a, b);
    }

    @Test
    public void testSetColumn_InRangeOf_0_to_2() {

        ByteMatrix a = factory().createMatrix(new byte[][] { {0}, {0}, {0}, {0}});
        ByteMatrix b = factory().createMatrix(new byte[][] { {1}, {2}, {0}, {0}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setColumn(0, 0, vector, 0, 2);

        assertEquals(a, b);
    }

    @Test
    public void testSetColumn_InRangeOf_0_to_4() {

        ByteMatrix a = factory().createMatrix(new byte[][] { {0}, {0}, {0}, {0}});
        ByteMatrix b = factory().createMatrix(new byte[][] { {1}, {2}, {3}, {4}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setColumn(0, 0, vector, 0, 4);

        assertEquals(a, b);
    }

    @Test
    public void testSetColumn_InRangeOf_2_to_2() {

        ByteMatrix a = factory().createMatrix(new byte[][] { {0}, {0}, {0}, {0}});
        ByteMatrix b = factory().createMatrix(new byte[][] { {0}, {0}, {0}, {0}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setColumn(0, 0, vector, 2, 0); // yes, 0 is the length

        assertEquals(a, b);
    }

    @Test
    public void testSetColumn_InRangeOf_2_to_4() {

        ByteMatrix a = factory().createMatrix(new byte[][] { {0}, {0}, {0}, {0}});
        ByteMatrix b = factory().createMatrix(new byte[][] { {0}, {0}, {3}, {4}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setColumn(0, 2, vector, 2, 2); // yes, the second 2 is the length

        assertEquals(a, b);
    }

    @Test
    public void testSetColumn_InRangeOf_4_to_4() {

        ByteMatrix a = factory().createMatrix(new byte[][] { {0}, {0}, {0}, {0}});
        ByteMatrix b = factory().createMatrix(new byte[][] { {0}, {0}, {0}, {0}});
        ByteVector vector = factory().createVector(new byte[] {1, 2, 3, 4});

        a.setColumn(0, 4, vector, 4, 0); // yes, 0 is the length

        assertEquals(a, b);
    }

    @Test
    public void testAssign_3x3() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {5, 5, 5},
                                                            {5, 5, 5},
                                                            {5, 5, 5}
        });

        ByteMatrix b = factory().createMatrix(3, 3);

        b.assign((byte)5);

        assertEquals(a, b);
    }

    @Test
    public void testResize_3x3_to_4x4_to_2x2() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0},
                                                            {0, 5, 0},
                                                            {0, 0, 9}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0, 0},
                                                            {0, 5, 0, 0},
                                                            {0, 0, 9, 0},
                                                            {0, 0, 0, 0}
        });

        ByteMatrix c = factory().createMatrix(new byte[][] {
                                                            {1, 0},
                                                            {0, 5}
        });

        a = a.resize(a.rows() + 1, a.columns() + 1);
        assertEquals(b, a);

        a = a.resize(a.rows() - 2, a.columns() - 2);
        assertEquals(c, a);
    }

    @Test
    public void testResize_2x3_to_3x4_to_1x2() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0},
                                                            {0, 5, 0},
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0, 0},
                                                            {0, 5, 0, 0},
                                                            {0, 0, 0, 0},
        });

        ByteMatrix c = factory().createMatrix(new byte[][] {
        {1, 0},
        });

        a = a.resize(a.rows() + 1, a.columns() + 1);
        assertEquals(b, a);

        a = a.resize(a.rows() - 2, a.columns() - 2);
        assertEquals(c, a);
    }

    @Test
    public void testResize_2x3_to_2x4_to_2x1() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0},
                                                            {0, 5, 0},
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0, 0},
                                                            {0, 5, 0, 0},
        });

        ByteMatrix c = factory().createMatrix(new byte[][] {
                                                            {1},
                                                            {0}
        });

        a = a.resize(a.rows(), a.columns() + 1);
        assertEquals(b, a);

        a = a.resize(a.rows(), a.columns() - 3);
        assertEquals(c, a);
    }

    @Test
    public void testResize_3x5_to_4x5_to_2x5() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0, 0, 0},
                                                            {0, 5, 0, 0, 0},
                                                            {0, 0, 7, 0, 0}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0, 0, 0},
                                                            {0, 5, 0, 0, 0},
                                                            {0, 0, 7, 0, 0},
                                                            {0, 0, 0, 0, 0},
        });

        ByteMatrix c = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0, 0, 0},
                                                            {0, 5, 0, 0, 0},
        });

        a = a.resize(a.rows() + 1, a.columns());
        assertEquals(b, a);

        a = a.resize(a.rows() - 2, a.columns());
        assertEquals(c, a);
    }

    @Test
    public void testSlice_4x4_to_2x2() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0, 0},
                                                            {0, 5, 0, 0},
                                                            {0, 0, 9, 0},
                                                            {0, 0, 0, 15}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {5, 0},
                                                            {0, 9}
        });

        ByteMatrix c = a.slice(1, 1, 3, 3);
        assertEquals(b, c);
    }

    @Test
    public void testSlice_3x4_to_1x4() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 3, 0},
                                                            {0, 5, 0, 7},
                                                            {4, 0, 9, 0}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
        {4, 0, 9, 0}
        });

        ByteMatrix c = a.slice(2, 0, 3, 4);
        assertEquals(b, c);
    }

    @Test
    public void testSwap_3x3() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0},
                                                            {0, 5, 0},
                                                            {0, 0, 9}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {0, 0, 9},
                                                            {0, 5, 0},
                                                            {1, 0, 0}
        });

        ByteMatrix c = factory().createMatrix(new byte[][] {
                                                            {9, 0, 0},
                                                            {0, 5, 0},
                                                            {0, 0, 1}
        });

        a.swapRows(0, 2);
        assertEquals(b, a);

        b.swapColumns(0, 2);
        assertEquals(c, b);
    }

    @Test
    public void testSwap_2x4() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0, 3},
                                                            {0, 5, 4, 0}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {0, 5, 4, 0},
                                                            {1, 0, 0, 3}
        });

        ByteMatrix c = factory().createMatrix(new byte[][] {
                                                            {0, 4, 5, 0},
                                                            {1, 0, 0, 3}
        });

        a.swapRows(0, 1);
        assertEquals(b, a);

        b.swapColumns(1, 2);
        assertEquals(c, b);
    }

    @Test
    public void testSwap_5x3() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0},
                                                            {0, 5, 4},
                                                            {7, 0, 2},
                                                            {0, 8, 0},
                                                            {5, 0, 6}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0},
                                                            {0, 5, 4},
                                                            {0, 8, 0},
                                                            {7, 0, 2},
                                                            {5, 0, 6}
        });

        ByteMatrix c = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0},
                                                            {0, 4, 5},
                                                            {0, 0, 8},
                                                            {7, 2, 0},
                                                            {5, 6, 0}
        });

        a.swapRows(2, 3);
        assertEquals(b, a);

        b.swapColumns(1, 2);
        assertEquals(c, b);
    }

    @Test
    public void testSwapRows() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 2, 3, 4},
                                                            {5, 6, 7, 8}
        });
        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {5, 6, 7, 8},
                                                            {1, 2, 3, 4}
        });

        a.swapRows(0, 1);

        assertEquals(a, b);
    }

    @Test
    public void testSwapColumns() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 5},
                                                            {2, 6},
                                                            {3, 7},
                                                            {4, 8}
        });
        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {5, 1},
                                                            {6, 2},
                                                            {7, 3},
                                                            {8, 4}
        });

        a.swapColumns(0, 1);

        assertEquals(a, b);
    }

    @Test
    public void testTranspose_4x4() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {0, 14, 0, 4},
                                                            {0, 5, 10, 0},
                                                            {0, 3, 0, 2},
                                                            {11, 7, 0, 1}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {0, 0, 0, 11},
                                                            {14, 5, 3, 7},
                                                            {0, 10, 0, 0},
                                                            {4, 0, 2, 1}
        });

        ByteMatrix c = a.transpose();
        assertEquals(b, c);

        ByteMatrix d = c.transpose();
        assertEquals(a, d);
    }

    @Test
    public void testTranspose_5x3() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {0, 14, 0},
                                                            {0, 5, 10},
                                                            {0, 3, 0},
                                                            {11, 7, 0},
                                                            {12, 7, 0}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {0, 0, 0, 11, 12},
                                                            {14, 5, 3, 7, 7},
                                                            {0, 10, 0, 0, 0}
        });

        ByteMatrix c = a.transpose();
        assertEquals(b, c);

        ByteMatrix d = c.transpose();
        assertEquals(a, d);
    }

    @Test
    public void testTranspose_6x5() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {8, 3, 7, 3, 75},
                                                            {5, 86, 5, 98, 7},
                                                            {67, 21, 8, 1, 8},
                                                            {9, 9, 0, 2, 2},
                                                            {3, 4, 7, 3, 94},
                                                            {8, 6, 6, 4, 6}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {8, 5, 67, 9, 3, 8},
                                                            {3, 86, 21, 9, 4, 6},
                                                            {7, 5, 8, 0, 7, 6},
                                                            {3, 98, 1, 2, 3, 4},
                                                            {75, 7, 8, 2, 94, 6}
        });

        ByteMatrix c = a.transpose();
        assertEquals(b, c);

        ByteMatrix d = c.transpose();
        assertEquals(a, d);
    }

    @Test
    public void testAdd_3x3() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0},
                                                            {0, 5, 0},
                                                            {0, 0, 9}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {11, 10, 10},
                                                            {10, 15, 10},
                                                            {10, 10, 3}
        });

        ByteMatrix c = factory().createMatrix(new byte[][] {
                                                            {0, 0, 0},
                                                            {0, 0, 0},
                                                            {0, 0, 0}
        });

        // adds are XORs
        assertEquals(b, a.add((byte)10));
        assertEquals(c, a.add(a));
    }

    @Test
    public void testAdd_4x2() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0},
                                                            {0, 5},
                                                            {7, 0},
                                                            {0, 9}
        });

        ByteMatrix b = factory().createMatrix(new byte[][] {
                                                            {11, 10},
                                                            {10, 15},
                                                            {13, 10},
                                                            {10, 3}
        });

        ByteMatrix c = factory().createMatrix(new byte[][] {
                                                            {0, 0},
                                                            {0, 0},
                                                            {0, 0},
                                                            {0, 0}
        });

        assertEquals(b, a.add((byte)10));
        assertEquals(c, a.add(a));
    }

    @Test
    public void testSubtract_3x3() {

        ByteMatrix a = factory().createMatrix(new byte[][] {
                                                            {1, 0, 0},
                                                            {0, 5, 0},
                                                            {0, 0, 9}
        });

