BlockFieldMatrix.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */

  21. package org.hipparchus.linear;

  22. import java.io.Serializable;

  23. import org.hipparchus.Field;
  24. import org.hipparchus.FieldElement;
  25. import org.hipparchus.exception.LocalizedCoreFormats;
  26. import org.hipparchus.exception.MathIllegalArgumentException;
  27. import org.hipparchus.exception.NullArgumentException;
  28. import org.hipparchus.util.FastMath;
  29. import org.hipparchus.util.MathArrays;
  30. import org.hipparchus.util.MathUtils;

  31. /**
  32.  * Cache-friendly implementation of FieldMatrix using a flat arrays to store
  33.  * square blocks of the matrix.
  34.  * <p>
  35.  * This implementation is specially designed to be cache-friendly. Square blocks are
  36.  * stored as small arrays and allow efficient traversal of data both in row major direction
  37.  * and columns major direction, one block at a time. This greatly increases performances
  38.  * for algorithms that use crossed directions loops like multiplication or transposition.
  39.  * </p>
  40.  * <p>
  41.  * The size of square blocks is a static parameter. It may be tuned according to the cache
  42.  * size of the target computer processor. As a rule of thumbs, it should be the largest
  43.  * value that allows three blocks to be simultaneously cached (this is necessary for example
  44.  * for matrix multiplication). The default value is to use 36x36 blocks.
  45.  * </p>
  46.  * <p>
  47.  * The regular blocks represent {@link #BLOCK_SIZE} x {@link #BLOCK_SIZE} squares. Blocks
  48.  * at right hand side and bottom side which may be smaller to fit matrix dimensions. The square
  49.  * blocks are flattened in row major order in single dimension arrays which are therefore
  50.  * {@link #BLOCK_SIZE}<sup>2</sup> elements long for regular blocks. The blocks are themselves
  51.  * organized in row major order.
  52.  * </p>
  53.  * <p>
  54.  * As an example, for a block size of 36x36, a 100x60 matrix would be stored in 6 blocks.
  55.  * Block 0 would be a Field[1296] array holding the upper left 36x36 square, block 1 would be
  56.  * a Field[1296] array holding the upper center 36x36 square, block 2 would be a Field[1008]
  57.  * array holding the upper right 36x28 rectangle, block 3 would be a Field[864] array holding
  58.  * the lower left 24x36 rectangle, block 4 would be a Field[864] array holding the lower center
  59.  * 24x36 rectangle and block 5 would be a Field[672] array holding the lower right 24x28
  60.  * rectangle.
  61.  * </p>
  62.  * <p>
  63.  * The layout complexity overhead versus simple mapping of matrices to java
  64.  * arrays is negligible for small matrices (about 1%). The gain from cache efficiency leads
  65.  * to up to 3-fold improvements for matrices of moderate to large size.
  66.  * </p>
  67.  * @param <T> the type of the field elements
  68.  */
  69. public class BlockFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T> implements Serializable {
  70.     /** Block size. */
  71.     public static final int BLOCK_SIZE = 36;
  72.     /** Serializable version identifier. */
  73.     private static final long serialVersionUID = -4602336630143123183L;
  74.     /** Blocks of matrix entries. */
  75.     private final T[][] blocks;
  76.     /** Number of rows of the matrix. */
  77.     private final int rows;
  78.     /** Number of columns of the matrix. */
  79.     private final int columns;
  80.     /** Number of block rows of the matrix. */
  81.     private final int blockRows;
  82.     /** Number of block columns of the matrix. */
  83.     private final int blockColumns;

  84.     /**
  85.      * Create a new matrix with the supplied row and column dimensions.
  86.      *
  87.      * @param field Field to which the elements belong.
  88.      * @param rows Number of rows in the new matrix.
  89.      * @param columns Number of columns in the new matrix.
  90.      * @throws MathIllegalArgumentException if row or column dimension is not
  91.      * positive.
  92.      */
  93.     public BlockFieldMatrix(final Field<T> field, final int rows,
  94.                             final int columns)
  95.         throws MathIllegalArgumentException {
  96.         super(field, rows, columns);
  97.         this.rows    = rows;
  98.         this.columns = columns;

  99.         // number of blocks
  100.         blockRows    = (rows    + BLOCK_SIZE - 1) / BLOCK_SIZE;
  101.         blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;

  102.         // allocate storage blocks, taking care of smaller ones at right and bottom
  103.         blocks = createBlocksLayout(field, rows, columns);
  104.     }

  105.     /**
  106.      * Create a new dense matrix copying entries from raw layout data.
  107.      * <p>The input array <em>must</em> already be in raw layout.</p>
  108.      * <p>Calling this constructor is equivalent to call:</p>
  109.      * <pre>matrix = new BlockFieldMatrix&lt;T&gt;(getField(), rawData.length, rawData[0].length,
  110.      *                                 toBlocksLayout(rawData), false);</pre>
  111.      *
  112.      * @param rawData Data for the new matrix, in raw layout.
  113.      * @throws MathIllegalArgumentException if the {@code blockData} shape is
  114.      * inconsistent with block layout.
  115.      * @see #BlockFieldMatrix(int, int, FieldElement[][], boolean)
  116.      */
  117.     public BlockFieldMatrix(final T[][] rawData)
  118.         throws MathIllegalArgumentException {
  119.         this(rawData.length, rawData[0].length, toBlocksLayout(rawData), false);
  120.     }

  121.     /**
  122.      * Create a new dense matrix copying entries from block layout data.
  123.      * <p>The input array <em>must</em> already be in blocks layout.</p>
  124.      * @param rows  the number of rows in the new matrix
  125.      * @param columns  the number of columns in the new matrix
  126.      * @param blockData data for new matrix
  127.      * @param copyArray if true, the input array will be copied, otherwise
  128.      * it will be referenced
  129.      *
  130.      * @throws MathIllegalArgumentException if the {@code blockData} shape is
  131.      * inconsistent with block layout.
  132.      * @throws MathIllegalArgumentException if row or column dimension is not
  133.      * positive.
  134.      * @see #createBlocksLayout(Field, int, int)
  135.      * @see #toBlocksLayout(FieldElement[][])
  136.      * @see #BlockFieldMatrix(FieldElement[][])
  137.      */
  138.     public BlockFieldMatrix(final int rows, final int columns,
  139.                             final T[][] blockData, final boolean copyArray)
  140.         throws MathIllegalArgumentException {
  141.         super(extractField(blockData), rows, columns);
  142.         this.rows    = rows;
  143.         this.columns = columns;

  144.         // number of blocks
  145.         blockRows    = (rows    + BLOCK_SIZE - 1) / BLOCK_SIZE;
  146.         blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;

  147.         if (copyArray) {
  148.             // allocate storage blocks, taking care of smaller ones at right and bottom
  149.             blocks = MathArrays.buildArray(getField(), blockRows * blockColumns, -1);
  150.         } else {
  151.             // reference existing array
  152.             blocks = blockData; // NOPMD - array copy is taken care of by parameter
  153.         }

  154.         int index = 0;
  155.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  156.             final int iHeight = blockHeight(iBlock);
  157.             for (int jBlock = 0; jBlock < blockColumns; ++jBlock, ++index) {
  158.                 if (blockData[index].length != iHeight * blockWidth(jBlock)) {
  159.                     throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  160.                                                            blockData[index].length,
  161.                                                            iHeight * blockWidth(jBlock));
  162.                 }
  163.                 if (copyArray) {
  164.                     blocks[index] = blockData[index].clone();
  165.                 }
  166.             }
  167.         }
  168.     }

  169.     /**
  170.      * Convert a data array from raw layout to blocks layout.
  171.      * <p>
  172.      * Raw layout is the straightforward layout where element at row i and
  173.      * column j is in array element <code>rawData[i][j]</code>. Blocks layout
  174.      * is the layout used in {@link BlockFieldMatrix} instances, where the matrix
  175.      * is split in square blocks (except at right and bottom side where blocks may
  176.      * be rectangular to fit matrix size) and each block is stored in a flattened
  177.      * one-dimensional array.
  178.      * </p>
  179.      * <p>
  180.      * This method creates an array in blocks layout from an input array in raw layout.
  181.      * It can be used to provide the array argument of the {@link
  182.      * #BlockFieldMatrix(int, int, FieldElement[][], boolean)}
  183.      * constructor.
  184.      * </p>
  185.      * @param <T> Type of the field elements.
  186.      * @param rawData Data array in raw layout.
  187.      * @return a new data array containing the same entries but in blocks layout
  188.      * @throws MathIllegalArgumentException if {@code rawData} is not rectangular
  189.      *  (not all rows have the same length).
  190.      * @see #createBlocksLayout(Field, int, int)
  191.      * @see #BlockFieldMatrix(int, int, FieldElement[][], boolean)
  192.      */
  193.     public static <T extends FieldElement<T>> T[][] toBlocksLayout(final T[][] rawData)
  194.         throws MathIllegalArgumentException {

  195.         final int rows         = rawData.length;
  196.         final int columns      = rawData[0].length;
  197.         final int blockRows    = (rows    + BLOCK_SIZE - 1) / BLOCK_SIZE;
  198.         final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;

  199.         // safety checks
  200.         for (T[] rawDatum : rawData) {
  201.             final int length = rawDatum.length;
  202.             if (length != columns) {
  203.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  204.                         columns, length);
  205.             }
  206.         }

  207.         // convert array
  208.         final Field<T> field = extractField(rawData);
  209.         final T[][] blocks = MathArrays.buildArray(field, blockRows * blockColumns, -1);
  210.         int blockIndex = 0;
  211.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  212.             final int pStart  = iBlock * BLOCK_SIZE;
  213.             final int pEnd    = FastMath.min(pStart + BLOCK_SIZE, rows);
  214.             final int iHeight = pEnd - pStart;
  215.             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  216.                 final int qStart = jBlock * BLOCK_SIZE;
  217.                 final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  218.                 final int jWidth = qEnd - qStart;

  219.                 // allocate new block
  220.                 final T[] block = MathArrays.buildArray(field, iHeight * jWidth);
  221.                 blocks[blockIndex] = block;

  222.                 // copy data
  223.                 int index = 0;
  224.                 for (int p = pStart; p < pEnd; ++p) {
  225.                     System.arraycopy(rawData[p], qStart, block, index, jWidth);
  226.                     index += jWidth;
  227.                 }

  228.                 ++blockIndex;
  229.             }
  230.         }

  231.         return blocks;
  232.     }

  233.     /**
  234.      * Create a data array in blocks layout.
  235.      * <p>
  236.      * This method can be used to create the array argument of the {@link
  237.      * #BlockFieldMatrix(int, int, FieldElement[][], boolean)}
  238.      * constructor.
  239.      * </p>
  240.      * @param <T> Type of the field elements.
  241.      * @param field Field to which the elements belong.
  242.      * @param rows Number of rows in the new matrix.
  243.      * @param columns Number of columns in the new matrix.
  244.      * @return a new data array in blocks layout.
  245.      * @see #toBlocksLayout(FieldElement[][])
  246.      * @see #BlockFieldMatrix(int, int, FieldElement[][], boolean)
  247.      */
  248.     public static <T extends FieldElement<T>> T[][] createBlocksLayout(final Field<T> field,
  249.                                                                        final int rows, final int columns) {
  250.         final int blockRows    = (rows    + BLOCK_SIZE - 1) / BLOCK_SIZE;
  251.         final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;

  252.         final T[][] blocks = MathArrays.buildArray(field, blockRows * blockColumns, -1);
  253.         int blockIndex = 0;
  254.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  255.             final int pStart  = iBlock * BLOCK_SIZE;
  256.             final int pEnd    = FastMath.min(pStart + BLOCK_SIZE, rows);
  257.             final int iHeight = pEnd - pStart;
  258.             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  259.                 final int qStart = jBlock * BLOCK_SIZE;
  260.                 final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  261.                 final int jWidth = qEnd - qStart;
  262.                 blocks[blockIndex] = MathArrays.buildArray(field, iHeight * jWidth);
  263.                 ++blockIndex;
  264.             }
  265.         }

  266.         return blocks;
  267.     }

  268.     /** {@inheritDoc} */
  269.     @Override
  270.     public FieldMatrix<T> createMatrix(final int rowDimension,
  271.                                        final int columnDimension)
  272.         throws MathIllegalArgumentException {
  273.         return new BlockFieldMatrix<>(getField(), rowDimension,
  274.                 columnDimension);
  275.     }

  276.     /** {@inheritDoc} */
  277.     @Override
  278.     public FieldMatrix<T> copy() {

  279.         // create an empty matrix
  280.         BlockFieldMatrix<T> copied = new BlockFieldMatrix<>(getField(), rows, columns);

  281.         // copy the blocks
  282.         for (int i = 0; i < blocks.length; ++i) {
  283.             System.arraycopy(blocks[i], 0, copied.blocks[i], 0, blocks[i].length);
  284.         }

  285.         return copied;
  286.     }

  287.     /** {@inheritDoc} */
  288.     @Override
  289.     public FieldMatrix<T> add(final FieldMatrix<T> m)
  290.         throws MathIllegalArgumentException {
  291.         if (m instanceof BlockFieldMatrix) {
  292.             return add((BlockFieldMatrix<T>) m);
  293.         } else {

  294.             // safety check
  295.             checkAdditionCompatible(m);

  296.             final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);

  297.             // perform addition block-wise, to ensure good cache behavior
  298.             int blockIndex = 0;
  299.             for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
  300.                 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {

  301.                     // perform addition on the current block
  302.                     final T[] outBlock = out.blocks[blockIndex];
  303.                     final T[] tBlock   = blocks[blockIndex];
  304.                     final int      pStart   = iBlock * BLOCK_SIZE;
  305.                     final int      pEnd     = FastMath.min(pStart + BLOCK_SIZE, rows);
  306.                     final int      qStart   = jBlock * BLOCK_SIZE;
  307.                     final int      qEnd     = FastMath.min(qStart + BLOCK_SIZE, columns);
  308.                     int k = 0;
  309.                     for (int p = pStart; p < pEnd; ++p) {
  310.                         for (int q = qStart; q < qEnd; ++q) {
  311.                             outBlock[k] = tBlock[k].add(m.getEntry(p, q));
  312.                             ++k;
  313.                         }
  314.                     }

  315.                     // go to next block
  316.                     ++blockIndex;

  317.                 }
  318.             }

  319.             return out;
  320.         }
  321.     }

  322.     /**
  323.      * Compute the sum of {@code this} and {@code m}.
  324.      *
  325.      * @param m matrix to be added
  326.      * @return {@code this + m}
  327.      * @throws MathIllegalArgumentException if {@code m} is not the same
  328.      * size as {@code this}
  329.      */
  330.     public BlockFieldMatrix<T> add(final BlockFieldMatrix<T> m)
  331.         throws MathIllegalArgumentException {

  332.         // safety check
  333.         checkAdditionCompatible(m);

  334.         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);

  335.         // perform addition block-wise, to ensure good cache behavior
  336.         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
  337.             final T[] outBlock = out.blocks[blockIndex];
  338.             final T[] tBlock   = blocks[blockIndex];
  339.             final T[] mBlock   = m.blocks[blockIndex];
  340.             for (int k = 0; k < outBlock.length; ++k) {
  341.                 outBlock[k] = tBlock[k].add(mBlock[k]);
  342.             }
  343.         }

  344.         return out;
  345.     }

  346.     /** {@inheritDoc} */
  347.     @Override
  348.     public FieldMatrix<T> subtract(final FieldMatrix<T> m)
  349.         throws MathIllegalArgumentException {
  350.         if (m instanceof BlockFieldMatrix) {
  351.             return subtract((BlockFieldMatrix<T>) m);
  352.         } else {

  353.             // safety check
  354.             checkSubtractionCompatible(m);

  355.             final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);

  356.             // perform subtraction block-wise, to ensure good cache behavior
  357.             int blockIndex = 0;
  358.             for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
  359.                 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {

  360.                     // perform subtraction on the current block
  361.                     final T[] outBlock = out.blocks[blockIndex];
  362.                     final T[] tBlock   = blocks[blockIndex];
  363.                     final int      pStart   = iBlock * BLOCK_SIZE;
  364.                     final int      pEnd     = FastMath.min(pStart + BLOCK_SIZE, rows);
  365.                     final int      qStart   = jBlock * BLOCK_SIZE;
  366.                     final int      qEnd     = FastMath.min(qStart + BLOCK_SIZE, columns);
  367.                     int k = 0;
  368.                     for (int p = pStart; p < pEnd; ++p) {
  369.                         for (int q = qStart; q < qEnd; ++q) {
  370.                             outBlock[k] = tBlock[k].subtract(m.getEntry(p, q));
  371.                             ++k;
  372.                         }
  373.                     }

  374.                     // go to next block
  375.                     ++blockIndex;

  376.                 }
  377.             }

  378.             return out;
  379.         }
  380.     }

  381.     /**
  382.      * Compute {@code this - m}.
  383.      *
  384.      * @param m matrix to be subtracted
  385.      * @return {@code this - m}
  386.      * @throws MathIllegalArgumentException if {@code m} is not the same
  387.      * size as {@code this}
  388.      */
  389.     public BlockFieldMatrix<T> subtract(final BlockFieldMatrix<T> m) throws MathIllegalArgumentException {
  390.         // safety check
  391.         checkSubtractionCompatible(m);

  392.         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);

  393.         // perform subtraction block-wise, to ensure good cache behavior
  394.         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
  395.             final T[] outBlock = out.blocks[blockIndex];
  396.             final T[] tBlock   = blocks[blockIndex];
  397.             final T[] mBlock   = m.blocks[blockIndex];
  398.             for (int k = 0; k < outBlock.length; ++k) {
  399.                 outBlock[k] = tBlock[k].subtract(mBlock[k]);
  400.             }
  401.         }

  402.         return out;
  403.     }

  404.     /** {@inheritDoc} */
  405.     @Override
  406.     public FieldMatrix<T> scalarAdd(final T d) {
  407.         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);

  408.         // perform subtraction block-wise, to ensure good cache behavior
  409.         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
  410.             final T[] outBlock = out.blocks[blockIndex];
  411.             final T[] tBlock   = blocks[blockIndex];
  412.             for (int k = 0; k < outBlock.length; ++k) {
  413.                 outBlock[k] = tBlock[k].add(d);
  414.             }
  415.         }

  416.         return out;
  417.     }

  418.     /** {@inheritDoc} */
  419.     @Override
  420.     public FieldMatrix<T> scalarMultiply(final T d) {

  421.         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);

  422.         // perform subtraction block-wise, to ensure good cache behavior
  423.         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
  424.             final T[] outBlock = out.blocks[blockIndex];
  425.             final T[] tBlock   = blocks[blockIndex];
  426.             for (int k = 0; k < outBlock.length; ++k) {
  427.                 outBlock[k] = tBlock[k].multiply(d);
  428.             }
  429.         }

  430.         return out;
  431.     }

  432.     /** {@inheritDoc} */
  433.     @Override
  434.     public FieldMatrix<T> multiply(final FieldMatrix<T> m)
  435.         throws MathIllegalArgumentException {
  436.         if (m instanceof BlockFieldMatrix) {
  437.             return multiply((BlockFieldMatrix<T>) m);
  438.         } else {

  439.             // safety check
  440.             checkMultiplicationCompatible(m);

  441.             final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, m.getColumnDimension());
  442.             final T zero = getField().getZero();

  443.             // perform multiplication block-wise, to ensure good cache behavior
  444.             int blockIndex = 0;
  445.             for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {

  446.                 final int pStart = iBlock * BLOCK_SIZE;
  447.                 final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);

  448.                 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {

  449.                     final int qStart = jBlock * BLOCK_SIZE;
  450.                     final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, m.getColumnDimension());

  451.                     // select current block
  452.                     final T[] outBlock = out.blocks[blockIndex];

  453.                     // perform multiplication on current block
  454.                     for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
  455.                         final int kWidth      = blockWidth(kBlock);
  456.                         final T[] tBlock = blocks[iBlock * blockColumns + kBlock];
  457.                         final int rStart      = kBlock * BLOCK_SIZE;
  458.                         int k = 0;
  459.                         for (int p = pStart; p < pEnd; ++p) {
  460.                             final int lStart = (p - pStart) * kWidth;
  461.                             final int lEnd   = lStart + kWidth;
  462.                             for (int q = qStart; q < qEnd; ++q) {
  463.                                 T sum = zero;
  464.                                 int r = rStart;
  465.                                 for (int l = lStart; l < lEnd; ++l) {
  466.                                     sum = sum.add(tBlock[l].multiply(m.getEntry(r, q)));
  467.                                     ++r;
  468.                                 }
  469.                                 outBlock[k] = outBlock[k].add(sum);
  470.                                 ++k;
  471.                             }
  472.                         }
  473.                     }

  474.                     // go to next block
  475.                     ++blockIndex;

  476.                 }
  477.             }

  478.             return out;
  479.         }
  480.     }

  481.     /**
  482.      * Returns the result of postmultiplying {@code this} by {@code m}.
  483.      *
  484.      * @param m matrix to postmultiply by
  485.      * @return {@code this * m}
  486.      * @throws MathIllegalArgumentException if the matrices are not compatible.
  487.      */
  488.     public BlockFieldMatrix<T> multiply(BlockFieldMatrix<T> m)
  489.         throws MathIllegalArgumentException {

  490.         // safety check
  491.         checkMultiplicationCompatible(m);

  492.         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, m.columns);
  493.         final T zero = getField().getZero();

  494.         // perform multiplication block-wise, to ensure good cache behavior
  495.         int blockIndex = 0;
  496.         for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {

  497.             final int pStart = iBlock * BLOCK_SIZE;
  498.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);

  499.             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
  500.                 final int jWidth = out.blockWidth(jBlock);
  501.                 final int jWidth2 = jWidth  + jWidth;
  502.                 final int jWidth3 = jWidth2 + jWidth;
  503.                 final int jWidth4 = jWidth3 + jWidth;

  504.                 // select current block
  505.                 final T[] outBlock = out.blocks[blockIndex];

  506.                 // perform multiplication on current block
  507.                 for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
  508.                     final int kWidth = blockWidth(kBlock);
  509.                     final T[] tBlock = blocks[iBlock * blockColumns + kBlock];
  510.                     final T[] mBlock = m.blocks[kBlock * m.blockColumns + jBlock];
  511.                     int k = 0;
  512.                     for (int p = pStart; p < pEnd; ++p) {
  513.                         final int lStart = (p - pStart) * kWidth;
  514.                         final int lEnd   = lStart + kWidth;
  515.                         for (int nStart = 0; nStart < jWidth; ++nStart) {
  516.                             T sum = zero;
  517.                             int l = lStart;
  518.                             int n = nStart;
  519.                             while (l < lEnd - 3) {
  520.                                 sum = sum.
  521.                                       add(tBlock[l].multiply(mBlock[n])).
  522.                                       add(tBlock[l + 1].multiply(mBlock[n + jWidth])).
  523.                                       add(tBlock[l + 2].multiply(mBlock[n + jWidth2])).
  524.                                       add(tBlock[l + 3].multiply(mBlock[n + jWidth3]));
  525.                                 l += 4;
  526.                                 n += jWidth4;
  527.                             }
  528.                             while (l < lEnd) {
  529.                                 sum = sum.add(tBlock[l++].multiply(mBlock[n]));
  530.                                 n += jWidth;
  531.                             }
  532.                             outBlock[k] = outBlock[k].add(sum);
  533.                             ++k;
  534.                         }
  535.                     }
  536.                 }

  537.                 // go to next block
  538.                 ++blockIndex;
  539.             }
  540.         }

  541.         return out;
  542.     }

  543.     /**
  544.      * Returns the result of postmultiplying {@code this} by {@code m^T}.
  545.      * @param m matrix to first transpose and second postmultiply by
  546.      * @return {@code this * m}
  547.      * @throws MathIllegalArgumentException if
  548.      * {@code columnDimension(this) != columnDimension(m)}
  549.      * @since 1.3
  550.      */
  551.     public BlockFieldMatrix<T> multiplyTransposed(BlockFieldMatrix<T> m)
  552.         throws MathIllegalArgumentException {
  553.         // safety check
  554.         MatrixUtils.checkSameColumnDimension(this, m);

  555.         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, m.rows);

  556.         // perform multiplication block-wise, to ensure good cache behavior
  557.         int blockIndex = 0;
  558.         for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {

  559.             final int pStart = iBlock * BLOCK_SIZE;
  560.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);

  561.             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
  562.                 final int jWidth = out.blockWidth(jBlock);

  563.                 // select current block
  564.                 final T[] outBlock = out.blocks[blockIndex];

  565.                 // perform multiplication on current block
  566.                 for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
  567.                     final int kWidth = blockWidth(kBlock);
  568.                     final T[] tBlock = blocks[iBlock * blockColumns + kBlock];
  569.                     final T[] mBlock = m.blocks[jBlock * m.blockColumns + kBlock];
  570.                     int k = 0;
  571.                     for (int p = pStart; p < pEnd; ++p) {
  572.                         final int lStart = (p - pStart) * kWidth;
  573.                         final int lEnd   = lStart + kWidth;
  574.                         for (int nStart = 0; nStart < jWidth * kWidth; nStart += kWidth) {
  575.                             T sum = getField().getZero();
  576.                             int l = lStart;
  577.                             int n = nStart;
  578.                             while (l < lEnd - 3) {
  579.                                 sum = sum.
  580.                                       add(tBlock[l].multiply(mBlock[n])).
  581.                                       add(tBlock[l + 1].multiply(mBlock[n + 1])).
  582.                                       add(tBlock[l + 2].multiply(mBlock[n + 2])).
  583.                                       add(tBlock[l + 3].multiply(mBlock[n + 3]));
  584.                                 l += 4;
  585.                                 n += 4;
  586.                             }
  587.                             while (l < lEnd) {
  588.                                 sum = sum.add(tBlock[l++].multiply(mBlock[n++]));
  589.                             }
  590.                             outBlock[k] = outBlock[k].add(sum);
  591.                             ++k;
  592.                         }
  593.                     }
  594.                 }
  595.                 // go to next block
  596.                 ++blockIndex;
  597.             }
  598.         }

  599.         return out;
  600.     }

  601.     /** {@inheritDoc} */
  602.     @Override
  603.     public BlockFieldMatrix<T> multiplyTransposed(final FieldMatrix<T> m)
  604.         throws MathIllegalArgumentException {
  605.         if (m instanceof BlockFieldMatrix) {
  606.             return multiplyTransposed((BlockFieldMatrix<T>) m);
  607.         } else {
  608.             // safety check
  609.             MatrixUtils.checkSameColumnDimension(this, m);

  610.             final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, m.getRowDimension());

  611.             // perform multiplication block-wise, to ensure good cache behavior
  612.             int blockIndex = 0;
  613.             for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
  614.                 final int pStart = iBlock * BLOCK_SIZE;
  615.                 final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);

  616.                 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
  617.                     final int qStart = jBlock * BLOCK_SIZE;
  618.                     final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, m.getRowDimension());

  619.                     // select current block
  620.                     final T[] outBlock = out.blocks[blockIndex];

  621.                     // perform multiplication on current block
  622.                     for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
  623.                         final int kWidth = blockWidth(kBlock);
  624.                         final T[] tBlock = blocks[iBlock * blockColumns + kBlock];
  625.                         final int rStart = kBlock * BLOCK_SIZE;
  626.                         int k = 0;
  627.                         for (int p = pStart; p < pEnd; ++p) {
  628.                             final int lStart = (p - pStart) * kWidth;
  629.                             final int lEnd = lStart + kWidth;
  630.                             for (int q = qStart; q < qEnd; ++q) {
  631.                                 T sum = getField().getZero();
  632.                                 int r = rStart;
  633.                                 for (int l = lStart; l < lEnd; ++l) {
  634.                                     sum = sum.add(tBlock[l].multiply(m.getEntry(q, r)));
  635.                                     ++r;
  636.                                 }
  637.                                 outBlock[k] = outBlock[k].add(sum);
  638.                                 ++k;
  639.                             }
  640.                         }
  641.                     }
  642.                     // go to next block
  643.                     ++blockIndex;
  644.                 }
  645.             }

  646.             return out;
  647.         }
  648.     }

  649.     /**
  650.      * Returns the result of postmultiplying {@code this^T} by {@code m}.
  651.      * @param m matrix to postmultiply by
  652.      * @return {@code this^T * m}
  653.      * @throws MathIllegalArgumentException if
  654.      * {@code columnDimension(this) != columnDimension(m)}
  655.      * @since 1.3
  656.      */
  657.     public BlockFieldMatrix<T> transposeMultiply(final BlockFieldMatrix<T> m)
  658.         throws MathIllegalArgumentException {
  659.         // safety check
  660.         MatrixUtils.checkSameRowDimension(this, m);

  661.         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), columns, m.columns);

  662.         // perform multiplication block-wise, to ensure good cache behavior
  663.         int blockIndex = 0;
  664.         for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {

  665.             final int iHeight  = out.blockHeight(iBlock);
  666.             final int iHeight2 = iHeight  + iHeight;
  667.             final int iHeight3 = iHeight2 + iHeight;
  668.             final int iHeight4 = iHeight3 + iHeight;
  669.             final int pStart   = iBlock * BLOCK_SIZE;
  670.             final int pEnd     = FastMath.min(pStart + BLOCK_SIZE, columns);

  671.             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
  672.                 final int jWidth  = out.blockWidth(jBlock);
  673.                 final int jWidth2 = jWidth  + jWidth;
  674.                 final int jWidth3 = jWidth2 + jWidth;
  675.                 final int jWidth4 = jWidth3 + jWidth;

  676.                 // select current block
  677.                 final T[] outBlock = out.blocks[blockIndex];

  678.                 // perform multiplication on current block
  679.                 for (int kBlock = 0; kBlock < blockRows; ++kBlock) {
  680.                     final int kHeight = blockHeight(kBlock);
  681.                     final T[] tBlock  = blocks[kBlock * blockColumns + iBlock];
  682.                     final T[] mBlock  = m.blocks[kBlock * m.blockColumns + jBlock];
  683.                     int k = 0;
  684.                     for (int p = pStart; p < pEnd; ++p) {
  685.                         final int lStart = p - pStart;
  686.                         final int lEnd   = lStart + iHeight * kHeight;
  687.                         for (int nStart = 0; nStart < jWidth; ++nStart) {
  688.                             T sum = getField().getZero();
  689.                             int l = lStart;
  690.                             int n = nStart;
  691.                             while (l < lEnd - iHeight3) {
  692.                                 sum = sum.add(tBlock[l].multiply(mBlock[n]).
  693.                                        add(tBlock[l + iHeight].multiply(mBlock[n + jWidth])).
  694.                                        add(tBlock[l + iHeight2].multiply(mBlock[n + jWidth2])).
  695.                                        add(tBlock[l + iHeight3].multiply(mBlock[n + jWidth3])));
  696.                                 l += iHeight4;
  697.                                 n += jWidth4;
  698.                             }
  699.                             while (l < lEnd) {
  700.                                 sum = sum.add(tBlock[l].multiply(mBlock[n]));
  701.                                 l += iHeight;
  702.                                 n += jWidth;
  703.                             }
  704.                             outBlock[k] = outBlock[k].add(sum);
  705.                             ++k;
  706.                         }
  707.                     }
  708.                 }
  709.                 // go to next block
  710.                 ++blockIndex;
  711.             }
  712.         }

  713.         return out;
  714.     }

  715.     /** {@inheritDoc} */
  716.     @Override
  717.     public BlockFieldMatrix<T> transposeMultiply(final FieldMatrix<T> m)
  718.         throws MathIllegalArgumentException {
  719.         if (m instanceof BlockFieldMatrix) {
  720.             return transposeMultiply((BlockFieldMatrix<T>) m);
  721.         } else {
  722.             // safety check
  723.             MatrixUtils.checkSameRowDimension(this, m);

  724.             final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), columns, m.getColumnDimension());

  725.             // perform multiplication block-wise, to ensure good cache behavior
  726.             int blockIndex = 0;
  727.             for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {

  728.                 final int iHeight = out.blockHeight(iBlock);
  729.                 final int pStart  = iBlock * BLOCK_SIZE;
  730.                 final int pEnd    = FastMath.min(pStart + BLOCK_SIZE, columns);

  731.                 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
  732.                     final int qStart = jBlock * BLOCK_SIZE;
  733.                     final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, m.getColumnDimension());

  734.                     // select current block
  735.                     final T[] outBlock = out.blocks[blockIndex];

  736.                     // perform multiplication on current block
  737.                     for (int kBlock = 0; kBlock < blockRows; ++kBlock) {
  738.                         final int kHeight = blockHeight(kBlock);
  739.                         final T[] tBlock  = blocks[kBlock * blockColumns + iBlock];
  740.                         final int rStart  = kBlock * BLOCK_SIZE;
  741.                         int k = 0;
  742.                         for (int p = pStart; p < pEnd; ++p) {
  743.                             final int lStart = p - pStart;
  744.                             final int lEnd   = lStart + iHeight * kHeight;
  745.                             for (int q = qStart; q < qEnd; ++q) {
  746.                                 T sum = getField().getZero();
  747.                                 int r = rStart;
  748.                                 for (int l = lStart; l < lEnd; l += iHeight) {
  749.                                     sum = sum.add(tBlock[l].multiply(m.getEntry(r++, q)));
  750.                                 }
  751.                                 outBlock[k] = outBlock[k].add(sum);
  752.                                 ++k;
  753.                             }
  754.                         }
  755.                     }
  756.                     // go to next block
  757.                     ++blockIndex;
  758.                 }
  759.             }

  760.             return out;
  761.         }

  762.     }

  763.     /** {@inheritDoc} */
  764.     @Override
  765.     public T[][] getData() {

  766.         final T[][] data = MathArrays.buildArray(getField(), getRowDimension(), getColumnDimension());
  767.         final int lastColumns = columns - (blockColumns - 1) * BLOCK_SIZE;

  768.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  769.             final int pStart = iBlock * BLOCK_SIZE;
  770.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  771.             int regularPos   = 0;
  772.             int lastPos      = 0;
  773.             for (int p = pStart; p < pEnd; ++p) {
  774.                 final T[] dataP = data[p];
  775.                 int blockIndex = iBlock * blockColumns;
  776.                 int dataPos    = 0;
  777.                 for (int jBlock = 0; jBlock < blockColumns - 1; ++jBlock) {
  778.                     System.arraycopy(blocks[blockIndex++], regularPos, dataP, dataPos, BLOCK_SIZE);
  779.                     dataPos += BLOCK_SIZE;
  780.                 }
  781.                 System.arraycopy(blocks[blockIndex], lastPos, dataP, dataPos, lastColumns);
  782.                 regularPos += BLOCK_SIZE;
  783.                 lastPos    += lastColumns;
  784.             }
  785.         }

  786.         return data;
  787.     }

  788.     /** {@inheritDoc} */
  789.     @Override
  790.     public FieldMatrix<T> getSubMatrix(final int startRow, final int endRow,
  791.                                        final int startColumn,
  792.                                        final int endColumn)
  793.         throws MathIllegalArgumentException {
  794.         // safety checks
  795.         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);

  796.         // create the output matrix
  797.         final BlockFieldMatrix<T> out =
  798.             new BlockFieldMatrix<>(getField(), endRow - startRow + 1, endColumn - startColumn + 1);

  799.         // compute blocks shifts
  800.         final int blockStartRow    = startRow    / BLOCK_SIZE;
  801.         final int rowsShift        = startRow    % BLOCK_SIZE;
  802.         final int blockStartColumn = startColumn / BLOCK_SIZE;
  803.         final int columnsShift     = startColumn % BLOCK_SIZE;

  804.         // perform extraction block-wise, to ensure good cache behavior
  805.         int pBlock = blockStartRow;
  806.         for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
  807.             final int iHeight = out.blockHeight(iBlock);
  808.             int qBlock = blockStartColumn;
  809.             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
  810.                 final int jWidth = out.blockWidth(jBlock);

  811.                 // handle one block of the output matrix
  812.                 final int      outIndex = iBlock * out.blockColumns + jBlock;
  813.                 final T[] outBlock = out.blocks[outIndex];
  814.                 final int      index    = pBlock * blockColumns + qBlock;
  815.                 final int      width    = blockWidth(qBlock);

  816.                 final int heightExcess = iHeight + rowsShift - BLOCK_SIZE;
  817.                 final int widthExcess  = jWidth + columnsShift - BLOCK_SIZE;
  818.                 if (heightExcess > 0) {
  819.                     // the submatrix block spans on two blocks rows from the original matrix
  820.                     if (widthExcess > 0) {
  821.                         // the submatrix block spans on two blocks columns from the original matrix
  822.                         final int width2 = blockWidth(qBlock + 1);
  823.                         copyBlockPart(blocks[index], width,
  824.                                       rowsShift, BLOCK_SIZE,
  825.                                       columnsShift, BLOCK_SIZE,
  826.                                       outBlock, jWidth, 0, 0);
  827.                         copyBlockPart(blocks[index + 1], width2,
  828.                                       rowsShift, BLOCK_SIZE,
  829.                                       0, widthExcess,
  830.                                       outBlock, jWidth, 0, jWidth - widthExcess);
  831.                         copyBlockPart(blocks[index + blockColumns], width,
  832.                                       0, heightExcess,
  833.                                       columnsShift, BLOCK_SIZE,
  834.                                       outBlock, jWidth, iHeight - heightExcess, 0);
  835.                         copyBlockPart(blocks[index + blockColumns + 1], width2,
  836.                                       0, heightExcess,
  837.                                       0, widthExcess,
  838.                                       outBlock, jWidth, iHeight - heightExcess, jWidth - widthExcess);
  839.                     } else {
  840.                         // the submatrix block spans on one block column from the original matrix
  841.                         copyBlockPart(blocks[index], width,
  842.                                       rowsShift, BLOCK_SIZE,
  843.                                       columnsShift, jWidth + columnsShift,
  844.                                       outBlock, jWidth, 0, 0);
  845.                         copyBlockPart(blocks[index + blockColumns], width,
  846.                                       0, heightExcess,
  847.                                       columnsShift, jWidth + columnsShift,
  848.                                       outBlock, jWidth, iHeight - heightExcess, 0);
  849.                     }
  850.                 } else {
  851.                     // the submatrix block spans on one block row from the original matrix
  852.                     if (widthExcess > 0) {
  853.                         // the submatrix block spans on two blocks columns from the original matrix
  854.                         final int width2 = blockWidth(qBlock + 1);
  855.                         copyBlockPart(blocks[index], width,
  856.                                       rowsShift, iHeight + rowsShift,
  857.                                       columnsShift, BLOCK_SIZE,
  858.                                       outBlock, jWidth, 0, 0);
  859.                         copyBlockPart(blocks[index + 1], width2,
  860.                                       rowsShift, iHeight + rowsShift,
  861.                                       0, widthExcess,
  862.                                       outBlock, jWidth, 0, jWidth - widthExcess);
  863.                     } else {
  864.                         // the submatrix block spans on one block column from the original matrix
  865.                         copyBlockPart(blocks[index], width,
  866.                                       rowsShift, iHeight + rowsShift,
  867.                                       columnsShift, jWidth + columnsShift,
  868.                                       outBlock, jWidth, 0, 0);
  869.                     }
  870.                }
  871.                 ++qBlock;
  872.             }
  873.             ++pBlock;
  874.         }

  875.         return out;
  876.     }

  877.     /**
  878.      * Copy a part of a block into another one
  879.      * <p>This method can be called only when the specified part fits in both
  880.      * blocks, no verification is done here.</p>
  881.      * @param srcBlock source block
  882.      * @param srcWidth source block width ({@link #BLOCK_SIZE} or smaller)
  883.      * @param srcStartRow start row in the source block
  884.      * @param srcEndRow end row (exclusive) in the source block
  885.      * @param srcStartColumn start column in the source block
  886.      * @param srcEndColumn end column (exclusive) in the source block
  887.      * @param dstBlock destination block
  888.      * @param dstWidth destination block width ({@link #BLOCK_SIZE} or smaller)
  889.      * @param dstStartRow start row in the destination block
  890.      * @param dstStartColumn start column in the destination block
  891.      */
  892.     private void copyBlockPart(final T[] srcBlock, final int srcWidth,
  893.                                final int srcStartRow, final int srcEndRow,
  894.                                final int srcStartColumn, final int srcEndColumn,
  895.                                final T[] dstBlock, final int dstWidth,
  896.                                final int dstStartRow, final int dstStartColumn) {
  897.         final int length = srcEndColumn - srcStartColumn;
  898.         int srcPos = srcStartRow * srcWidth + srcStartColumn;
  899.         int dstPos = dstStartRow * dstWidth + dstStartColumn;
  900.         for (int srcRow = srcStartRow; srcRow < srcEndRow; ++srcRow) {
  901.             System.arraycopy(srcBlock, srcPos, dstBlock, dstPos, length);
  902.             srcPos += srcWidth;
  903.             dstPos += dstWidth;
  904.         }
  905.     }

  906.     /** {@inheritDoc} */
  907.     @Override
  908.     public void setSubMatrix(final T[][] subMatrix, final int row,
  909.                              final int column)
  910.         throws MathIllegalArgumentException, NullArgumentException {
  911.         // safety checks
  912.         MathUtils.checkNotNull(subMatrix);
  913.         final int refLength = subMatrix[0].length;
  914.         if (refLength == 0) {
  915.             throw new MathIllegalArgumentException(LocalizedCoreFormats.AT_LEAST_ONE_COLUMN);
  916.         }
  917.         final int endRow    = row + subMatrix.length - 1;
  918.         final int endColumn = column + refLength - 1;
  919.         checkSubMatrixIndex(row, endRow, column, endColumn);
  920.         for (final T[] subRow : subMatrix) {
  921.             if (subRow.length != refLength) {
  922.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  923.                                                        refLength, subRow.length);
  924.             }
  925.         }

  926.         // compute blocks bounds
  927.         final int blockStartRow    = row / BLOCK_SIZE;
  928.         final int blockEndRow      = (endRow + BLOCK_SIZE) / BLOCK_SIZE;
  929.         final int blockStartColumn = column / BLOCK_SIZE;
  930.         final int blockEndColumn   = (endColumn + BLOCK_SIZE) / BLOCK_SIZE;

  931.         // perform copy block-wise, to ensure good cache behavior
  932.         for (int iBlock = blockStartRow; iBlock < blockEndRow; ++iBlock) {
  933.             final int iHeight  = blockHeight(iBlock);
  934.             final int firstRow = iBlock * BLOCK_SIZE;
  935.             final int iStart   = FastMath.max(row,    firstRow);
  936.             final int iEnd     = FastMath.min(endRow + 1, firstRow + iHeight);

  937.             for (int jBlock = blockStartColumn; jBlock < blockEndColumn; ++jBlock) {
  938.                 final int jWidth      = blockWidth(jBlock);
  939.                 final int firstColumn = jBlock * BLOCK_SIZE;
  940.                 final int jStart      = FastMath.max(column,    firstColumn);
  941.                 final int jEnd        = FastMath.min(endColumn + 1, firstColumn + jWidth);
  942.                 final int jLength     = jEnd - jStart;

  943.                 // handle one block, row by row
  944.                 final T[] block = blocks[iBlock * blockColumns + jBlock];
  945.                 for (int i = iStart; i < iEnd; ++i) {
  946.                     System.arraycopy(subMatrix[i - row], jStart - column,
  947.                                      block, (i - firstRow) * jWidth + (jStart - firstColumn),
  948.                                      jLength);
  949.                 }

  950.             }
  951.         }
  952.     }

  953.     /** {@inheritDoc} */
  954.     @Override
  955.     public FieldMatrix<T> getRowMatrix(final int row)
  956.         throws MathIllegalArgumentException {
  957.         checkRowIndex(row);
  958.         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), 1, columns);

  959.         // perform copy block-wise, to ensure good cache behavior
  960.         final int iBlock  = row / BLOCK_SIZE;
  961.         final int iRow    = row - iBlock * BLOCK_SIZE;
  962.         int outBlockIndex = 0;
  963.         int outIndex      = 0;
  964.         T[] outBlock = out.blocks[outBlockIndex];
  965.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  966.             final int jWidth     = blockWidth(jBlock);
  967.             final T[] block = blocks[iBlock * blockColumns + jBlock];
  968.             final int available  = outBlock.length - outIndex;
  969.             if (jWidth > available) {
  970.                 System.arraycopy(block, iRow * jWidth, outBlock, outIndex, available);
  971.                 outBlock = out.blocks[++outBlockIndex];
  972.                 System.arraycopy(block, iRow * jWidth, outBlock, 0, jWidth - available);
  973.                 outIndex = jWidth - available;
  974.             } else {
  975.                 System.arraycopy(block, iRow * jWidth, outBlock, outIndex, jWidth);
  976.                 outIndex += jWidth;
  977.             }
  978.         }

  979.         return out;
  980.     }

  981.     /** {@inheritDoc} */
  982.     @Override
  983.     public void setRowMatrix(final int row, final FieldMatrix<T> matrix)
  984.         throws MathIllegalArgumentException {
  985.         if (matrix instanceof BlockFieldMatrix) {
  986.             setRowMatrix(row, (BlockFieldMatrix<T>) matrix);
  987.         } else {
  988.             super.setRowMatrix(row, matrix);
  989.         }
  990.     }

  991.     /**
  992.      * Sets the entries in row number <code>row</code>
  993.      * as a row matrix.  Row indices start at 0.
  994.      *
  995.      * @param row the row to be set
  996.      * @param matrix row matrix (must have one row and the same number of columns
  997.      * as the instance)
  998.      * @throws MathIllegalArgumentException if the matrix dimensions do
  999.      * not match one instance row.
  1000.      * @throws MathIllegalArgumentException if the specified row index is invalid.
  1001.      */
  1002.     public void setRowMatrix(final int row, final BlockFieldMatrix<T> matrix)
  1003.         throws MathIllegalArgumentException {
  1004.         checkRowIndex(row);
  1005.         final int nCols = getColumnDimension();
  1006.         if ((matrix.getRowDimension() != 1) ||
  1007.             (matrix.getColumnDimension() != nCols)) {
  1008.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH_2x2,
  1009.                                                    matrix.getRowDimension(), matrix.getColumnDimension(),
  1010.                                                    1, nCols);
  1011.         }

  1012.         // perform copy block-wise, to ensure good cache behavior
  1013.         final int iBlock = row / BLOCK_SIZE;
  1014.         final int iRow   = row - iBlock * BLOCK_SIZE;
  1015.         int mBlockIndex  = 0;
  1016.         int mIndex       = 0;
  1017.         T[] mBlock  = matrix.blocks[mBlockIndex];
  1018.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1019.             final int jWidth     = blockWidth(jBlock);
  1020.             final T[] block = blocks[iBlock * blockColumns + jBlock];
  1021.             final int available  = mBlock.length - mIndex;
  1022.             if (jWidth > available) {
  1023.                 System.arraycopy(mBlock, mIndex, block, iRow * jWidth, available);
  1024.                 mBlock = matrix.blocks[++mBlockIndex];
  1025.                 System.arraycopy(mBlock, 0, block, iRow * jWidth, jWidth - available);
  1026.                 mIndex = jWidth - available;
  1027.             } else {
  1028.                 System.arraycopy(mBlock, mIndex, block, iRow * jWidth, jWidth);
  1029.                 mIndex += jWidth;
  1030.            }
  1031.         }
  1032.     }

  1033.     /** {@inheritDoc} */
  1034.     @Override
  1035.     public FieldMatrix<T> getColumnMatrix(final int column)
  1036.         throws MathIllegalArgumentException {
  1037.         checkColumnIndex(column);
  1038.         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, 1);

  1039.         // perform copy block-wise, to ensure good cache behavior
  1040.         final int jBlock  = column / BLOCK_SIZE;
  1041.         final int jColumn = column - jBlock * BLOCK_SIZE;
  1042.         final int jWidth  = blockWidth(jBlock);
  1043.         int outBlockIndex = 0;
  1044.         int outIndex      = 0;
  1045.         T[] outBlock = out.blocks[outBlockIndex];
  1046.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1047.             final int iHeight = blockHeight(iBlock);
  1048.             final T[] block = blocks[iBlock * blockColumns + jBlock];
  1049.             for (int i = 0; i < iHeight; ++i) {
  1050.                 if (outIndex >= outBlock.length) {
  1051.                     outBlock = out.blocks[++outBlockIndex];
  1052.                     outIndex = 0;
  1053.                 }
  1054.                 outBlock[outIndex++] = block[i * jWidth + jColumn];
  1055.             }
  1056.         }

  1057.         return out;
  1058.     }

  1059.     /** {@inheritDoc} */
  1060.     @Override
  1061.     public void setColumnMatrix(final int column, final FieldMatrix<T> matrix)
  1062.         throws MathIllegalArgumentException {
  1063.         if (matrix instanceof BlockFieldMatrix) {
  1064.             setColumnMatrix(column, (BlockFieldMatrix<T>) matrix);
  1065.         } else {
  1066.             super.setColumnMatrix(column, matrix);
  1067.         }
  1068.     }

  1069.     /**
  1070.      * Sets the entries in column number {@code column}
  1071.      * as a column matrix.  Column indices start at 0.
  1072.      *
  1073.      * @param column Column to be set.
  1074.      * @param matrix Column matrix (must have one column and the same number of rows
  1075.      * as the instance).
  1076.      * @throws MathIllegalArgumentException if the matrix dimensions do
  1077.      * not match one instance column.
  1078.      * @throws MathIllegalArgumentException if the specified column index is invalid.
  1079.      */
  1080.     void setColumnMatrix(final int column, final BlockFieldMatrix<T> matrix)
  1081.         throws MathIllegalArgumentException {
  1082.         checkColumnIndex(column);
  1083.         final int nRows = getRowDimension();
  1084.         if ((matrix.getRowDimension() != nRows) ||
  1085.             (matrix.getColumnDimension() != 1)) {
  1086.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH_2x2,
  1087.                                                    matrix.getRowDimension(), matrix.getColumnDimension(),
  1088.                                                    nRows, 1);
  1089.         }

  1090.         // perform copy block-wise, to ensure good cache behavior
  1091.         final int jBlock  = column / BLOCK_SIZE;
  1092.         final int jColumn = column - jBlock * BLOCK_SIZE;
  1093.         final int jWidth  = blockWidth(jBlock);
  1094.         int mBlockIndex = 0;
  1095.         int mIndex      = 0;
  1096.         T[] mBlock = matrix.blocks[mBlockIndex];
  1097.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1098.             final int iHeight = blockHeight(iBlock);
  1099.             final T[] block = blocks[iBlock * blockColumns + jBlock];
  1100.             for (int i = 0; i < iHeight; ++i) {
  1101.                 if (mIndex >= mBlock.length) {
  1102.                     mBlock = matrix.blocks[++mBlockIndex];
  1103.                     mIndex = 0;
  1104.                 }
  1105.                 block[i * jWidth + jColumn] = mBlock[mIndex++];
  1106.             }
  1107.         }
  1108.     }

  1109.     /** {@inheritDoc} */
  1110.     @Override
  1111.     public FieldVector<T> getRowVector(final int row)
  1112.         throws MathIllegalArgumentException {
  1113.         checkRowIndex(row);
  1114.         final T[] outData = MathArrays.buildArray(getField(), columns);

  1115.         // perform copy block-wise, to ensure good cache behavior
  1116.         final int iBlock  = row / BLOCK_SIZE;
  1117.         final int iRow    = row - iBlock * BLOCK_SIZE;
  1118.         int outIndex      = 0;
  1119.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1120.             final int jWidth     = blockWidth(jBlock);
  1121.             final T[] block = blocks[iBlock * blockColumns + jBlock];
  1122.             System.arraycopy(block, iRow * jWidth, outData, outIndex, jWidth);
  1123.             outIndex += jWidth;
  1124.         }

  1125.         return new ArrayFieldVector<>(getField(), outData, false);
  1126.     }

  1127.     /** {@inheritDoc} */
  1128.     @Override
  1129.     public void setRowVector(final int row, final FieldVector<T> vector)
  1130.         throws MathIllegalArgumentException {
  1131.         if (vector instanceof ArrayFieldVector) {
  1132.             setRow(row, ((ArrayFieldVector<T>) vector).getDataRef());
  1133.         } else {
  1134.             super.setRowVector(row, vector);
  1135.         }
  1136.     }

  1137.     /** {@inheritDoc} */
  1138.     @Override
  1139.     public FieldVector<T> getColumnVector(final int column)
  1140.         throws MathIllegalArgumentException {
  1141.         checkColumnIndex(column);
  1142.         final T[] outData = MathArrays.buildArray(getField(), rows);

  1143.         // perform copy block-wise, to ensure good cache behavior
  1144.         final int jBlock  = column / BLOCK_SIZE;
  1145.         final int jColumn = column - jBlock * BLOCK_SIZE;
  1146.         final int jWidth  = blockWidth(jBlock);
  1147.         int outIndex      = 0;
  1148.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1149.             final int iHeight = blockHeight(iBlock);
  1150.             final T[] block = blocks[iBlock * blockColumns + jBlock];
  1151.             for (int i = 0; i < iHeight; ++i) {
  1152.                 outData[outIndex++] = block[i * jWidth + jColumn];
  1153.             }
  1154.         }

  1155.         return new ArrayFieldVector<>(getField(), outData, false);
  1156.     }

  1157.     /** {@inheritDoc} */
  1158.     @Override
  1159.     public void setColumnVector(final int column, final FieldVector<T> vector)
  1160.         throws MathIllegalArgumentException {
  1161.         if (vector instanceof ArrayFieldVector) {
  1162.             setColumn(column, ((ArrayFieldVector<T>) vector).getDataRef());
  1163.         } else {
  1164.             super.setColumnVector(column, vector);
  1165.         }
  1166.     }

  1167.     /** {@inheritDoc} */
  1168.     @Override
  1169.     public T[] getRow(final int row) throws MathIllegalArgumentException {
  1170.         checkRowIndex(row);
  1171.         final T[] out = MathArrays.buildArray(getField(), columns);

  1172.         // perform copy block-wise, to ensure good cache behavior
  1173.         final int iBlock  = row / BLOCK_SIZE;
  1174.         final int iRow    = row - iBlock * BLOCK_SIZE;
  1175.         int outIndex      = 0;
  1176.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1177.             final int jWidth     = blockWidth(jBlock);
  1178.             final T[] block = blocks[iBlock * blockColumns + jBlock];
  1179.             System.arraycopy(block, iRow * jWidth, out, outIndex, jWidth);
  1180.             outIndex += jWidth;
  1181.         }

  1182.         return out;
  1183.     }

  1184.     /** {@inheritDoc} */
  1185.     @Override
  1186.     public void setRow(final int row, final T[] array)
  1187.         throws MathIllegalArgumentException {
  1188.         checkRowIndex(row);
  1189.         final int nCols = getColumnDimension();
  1190.         if (array.length != nCols) {
  1191.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH_2x2,
  1192.                                                    1, array.length,
  1193.                                                    1, nCols);
  1194.         }

  1195.         // perform copy block-wise, to ensure good cache behavior
  1196.         final int iBlock  = row / BLOCK_SIZE;
  1197.         final int iRow    = row - iBlock * BLOCK_SIZE;
  1198.         int outIndex      = 0;
  1199.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1200.             final int jWidth     = blockWidth(jBlock);
  1201.             final T[] block = blocks[iBlock * blockColumns + jBlock];
  1202.             System.arraycopy(array, outIndex, block, iRow * jWidth, jWidth);
  1203.             outIndex += jWidth;
  1204.         }
  1205.     }

  1206.     /** {@inheritDoc} */
  1207.     @Override
  1208.     public T[] getColumn(final int column) throws MathIllegalArgumentException {
  1209.         checkColumnIndex(column);
  1210.         final T[] out = MathArrays.buildArray(getField(), rows);

  1211.         // perform copy block-wise, to ensure good cache behavior
  1212.         final int jBlock  = column / BLOCK_SIZE;
  1213.         final int jColumn = column - jBlock * BLOCK_SIZE;
  1214.         final int jWidth  = blockWidth(jBlock);
  1215.         int outIndex      = 0;
  1216.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1217.             final int iHeight = blockHeight(iBlock);
  1218.             final T[] block = blocks[iBlock * blockColumns + jBlock];
  1219.             for (int i = 0; i < iHeight; ++i) {
  1220.                 out[outIndex++] = block[i * jWidth + jColumn];
  1221.             }
  1222.         }

  1223.         return out;
  1224.     }

  1225.     /** {@inheritDoc} */
  1226.     @Override
  1227.     public void setColumn(final int column, final T[] array)
  1228.         throws MathIllegalArgumentException {
  1229.         checkColumnIndex(column);
  1230.         final int nRows = getRowDimension();
  1231.         if (array.length != nRows) {
  1232.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH_2x2,
  1233.                                                    array.length,
  1234.                                                    1, nRows, 1);
  1235.         }

  1236.         // perform copy block-wise, to ensure good cache behavior
  1237.         final int jBlock  = column / BLOCK_SIZE;
  1238.         final int jColumn = column - jBlock * BLOCK_SIZE;
  1239.         final int jWidth  = blockWidth(jBlock);
  1240.         int outIndex      = 0;
  1241.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1242.             final int iHeight = blockHeight(iBlock);
  1243.             final T[] block = blocks[iBlock * blockColumns + jBlock];
  1244.             for (int i = 0; i < iHeight; ++i) {
  1245.                 block[i * jWidth + jColumn] = array[outIndex++];
  1246.             }
  1247.         }
  1248.     }

  1249.     /** {@inheritDoc} */
  1250.     @Override
  1251.     public T getEntry(final int row, final int column)
  1252.         throws MathIllegalArgumentException {
  1253.         checkRowIndex(row);
  1254.         checkColumnIndex(column);

  1255.         final int iBlock = row    / BLOCK_SIZE;
  1256.         final int jBlock = column / BLOCK_SIZE;
  1257.         final int k      = (row    - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
  1258.             (column - jBlock * BLOCK_SIZE);

  1259.         return blocks[iBlock * blockColumns + jBlock][k];
  1260.     }

  1261.     /** {@inheritDoc} */
  1262.     @Override
  1263.     public void setEntry(final int row, final int column, final T value)
  1264.         throws MathIllegalArgumentException {
  1265.         checkRowIndex(row);
  1266.         checkColumnIndex(column);

  1267.         final int iBlock = row    / BLOCK_SIZE;
  1268.         final int jBlock = column / BLOCK_SIZE;
  1269.         final int k      = (row    - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
  1270.             (column - jBlock * BLOCK_SIZE);

  1271.         blocks[iBlock * blockColumns + jBlock][k] = value;
  1272.     }

  1273.     /** {@inheritDoc} */
  1274.     @Override
  1275.     public void addToEntry(final int row, final int column, final T increment)
  1276.         throws MathIllegalArgumentException {
  1277.         checkRowIndex(row);
  1278.         checkColumnIndex(column);

  1279.         final int iBlock = row    / BLOCK_SIZE;
  1280.         final int jBlock = column / BLOCK_SIZE;
  1281.         final int k      = (row    - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
  1282.             (column - jBlock * BLOCK_SIZE);
  1283.         final T[] blockIJ = blocks[iBlock * blockColumns + jBlock];

  1284.         blockIJ[k] = blockIJ[k].add(increment);
  1285.     }

  1286.     /** {@inheritDoc} */
  1287.     @Override
  1288.     public void multiplyEntry(final int row, final int column, final T factor)
  1289.         throws MathIllegalArgumentException {
  1290.         checkRowIndex(row);
  1291.         checkColumnIndex(column);

  1292.         final int iBlock = row    / BLOCK_SIZE;
  1293.         final int jBlock = column / BLOCK_SIZE;
  1294.         final int k      = (row    - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
  1295.             (column - jBlock * BLOCK_SIZE);
  1296.         final T[] blockIJ = blocks[iBlock * blockColumns + jBlock];

  1297.         blockIJ[k] = blockIJ[k].multiply(factor);
  1298.     }

  1299.     /** {@inheritDoc} */
  1300.     @Override
  1301.     public FieldMatrix<T> transpose() {
  1302.         final int nRows = getRowDimension();
  1303.         final int nCols = getColumnDimension();
  1304.         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), nCols, nRows);

  1305.         // perform transpose block-wise, to ensure good cache behavior
  1306.         int blockIndex = 0;
  1307.         for (int iBlock = 0; iBlock < blockColumns; ++iBlock) {
  1308.             for (int jBlock = 0; jBlock < blockRows; ++jBlock) {

  1309.                 // transpose current block
  1310.                 final T[] outBlock = out.blocks[blockIndex];
  1311.                 final T[] tBlock   = blocks[jBlock * blockColumns + iBlock];
  1312.                 final int      pStart   = iBlock * BLOCK_SIZE;
  1313.                 final int      pEnd     = FastMath.min(pStart + BLOCK_SIZE, columns);
  1314.                 final int      qStart   = jBlock * BLOCK_SIZE;
  1315.                 final int      qEnd     = FastMath.min(qStart + BLOCK_SIZE, rows);
  1316.                 int k = 0;
  1317.                 for (int p = pStart; p < pEnd; ++p) {
  1318.                     final int lInc = pEnd - pStart;
  1319.                     int l = p - pStart;
  1320.                     for (int q = qStart; q < qEnd; ++q) {
  1321.                         outBlock[k] = tBlock[l];
  1322.                         ++k;
  1323.                         l+= lInc;
  1324.                     }
  1325.                 }

  1326.                 // go to next block
  1327.                 ++blockIndex;

  1328.             }
  1329.         }

  1330.         return out;
  1331.     }

  1332.     /** {@inheritDoc} */
  1333.     @Override
  1334.     public int getRowDimension() {
  1335.         return rows;
  1336.     }

  1337.     /** {@inheritDoc} */
  1338.     @Override
  1339.     public int getColumnDimension() {
  1340.         return columns;
  1341.     }

  1342.     /** {@inheritDoc} */
  1343.     @Override
  1344.     public T[] operate(final T[] v) throws MathIllegalArgumentException {
  1345.         if (v.length != columns) {
  1346.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  1347.                                                    v.length, columns);
  1348.         }
  1349.         final T[] out = MathArrays.buildArray(getField(), rows);
  1350.         final T zero = getField().getZero();

  1351.         // perform multiplication block-wise, to ensure good cache behavior
  1352.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1353.             final int pStart = iBlock * BLOCK_SIZE;
  1354.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1355.             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1356.                 final T[] block  = blocks[iBlock * blockColumns + jBlock];
  1357.                 final int      qStart = jBlock * BLOCK_SIZE;
  1358.                 final int      qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1359.                 int k = 0;
  1360.                 for (int p = pStart; p < pEnd; ++p) {
  1361.                     T sum = zero;
  1362.                     int q = qStart;
  1363.                     while (q < qEnd - 3) {
  1364.                         sum = sum.
  1365.                               add(block[k].multiply(v[q])).
  1366.                               add(block[k + 1].multiply(v[q + 1])).
  1367.                               add(block[k + 2].multiply(v[q + 2])).
  1368.                               add(block[k + 3].multiply(v[q + 3]));
  1369.                         k += 4;
  1370.                         q += 4;
  1371.                     }
  1372.                     while (q < qEnd) {
  1373.                         sum = sum.add(block[k++].multiply(v[q++]));
  1374.                     }
  1375.                     out[p] = out[p].add(sum);
  1376.                 }
  1377.             }
  1378.         }

  1379.         return out;
  1380.     }

  1381.     /** {@inheritDoc} */
  1382.     @Override
  1383.     public T[] preMultiply(final T[] v) throws MathIllegalArgumentException {

  1384.         if (v.length != rows) {
  1385.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  1386.                                                    v.length, rows);
  1387.         }
  1388.         final T[] out = MathArrays.buildArray(getField(), columns);
  1389.         final T zero = getField().getZero();

  1390.         // perform multiplication block-wise, to ensure good cache behavior
  1391.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1392.             final int jWidth  = blockWidth(jBlock);
  1393.             final int jWidth2 = jWidth  + jWidth;
  1394.             final int jWidth3 = jWidth2 + jWidth;
  1395.             final int jWidth4 = jWidth3 + jWidth;
  1396.             final int qStart = jBlock * BLOCK_SIZE;
  1397.             final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1398.             for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1399.                 final T[] block  = blocks[iBlock * blockColumns + jBlock];
  1400.                 final int      pStart = iBlock * BLOCK_SIZE;
  1401.                 final int      pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1402.                 for (int q = qStart; q < qEnd; ++q) {
  1403.                     int k = q - qStart;
  1404.                     T sum = zero;
  1405.                     int p = pStart;
  1406.                     while (p < pEnd - 3) {
  1407.                         sum = sum.
  1408.                               add(block[k].multiply(v[p])).
  1409.                               add(block[k + jWidth].multiply(v[p + 1])).
  1410.                               add(block[k + jWidth2].multiply(v[p + 2])).
  1411.                               add(block[k + jWidth3].multiply(v[p + 3]));
  1412.                         k += jWidth4;
  1413.                         p += 4;
  1414.                     }
  1415.                     while (p < pEnd) {
  1416.                         sum = sum.add(block[k].multiply(v[p++]));
  1417.                         k += jWidth;
  1418.                     }
  1419.                     out[q] = out[q].add(sum);
  1420.                 }
  1421.             }
  1422.         }

  1423.         return out;
  1424.     }

  1425.     /** {@inheritDoc} */
  1426.     @Override
  1427.     public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor) {
  1428.         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
  1429.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1430.             final int pStart = iBlock * BLOCK_SIZE;
  1431.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1432.             for (int p = pStart; p < pEnd; ++p) {
  1433.                 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1434.                     final int jWidth = blockWidth(jBlock);
  1435.                     final int qStart = jBlock * BLOCK_SIZE;
  1436.                     final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1437.                     final T[] block = blocks[iBlock * blockColumns + jBlock];
  1438.                     int k = (p - pStart) * jWidth;
  1439.                     for (int q = qStart; q < qEnd; ++q) {
  1440.                         block[k] = visitor.visit(p, q, block[k]);
  1441.                         ++k;
  1442.                     }
  1443.                 }
  1444.              }
  1445.         }
  1446.         return visitor.end();
  1447.     }

  1448.     /** {@inheritDoc} */
  1449.     @Override
  1450.     public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor) {
  1451.         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
  1452.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1453.             final int pStart = iBlock * BLOCK_SIZE;
  1454.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1455.             for (int p = pStart; p < pEnd; ++p) {
  1456.                 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1457.                     final int jWidth = blockWidth(jBlock);
  1458.                     final int qStart = jBlock * BLOCK_SIZE;
  1459.                     final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1460.                     final T[] block = blocks[iBlock * blockColumns + jBlock];
  1461.                     int k = (p - pStart) * jWidth;
  1462.                     for (int q = qStart; q < qEnd; ++q) {
  1463.                         visitor.visit(p, q, block[k]);
  1464.                         ++k;
  1465.                     }
  1466.                 }
  1467.              }
  1468.         }
  1469.         return visitor.end();
  1470.     }

  1471.     /** {@inheritDoc} */
  1472.     @Override
  1473.     public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor,
  1474.                             final int startRow, final int endRow,
  1475.                             final int startColumn, final int endColumn)
  1476.         throws MathIllegalArgumentException {
  1477.         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
  1478.         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
  1479.         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
  1480.             final int p0     = iBlock * BLOCK_SIZE;
  1481.             final int pStart = FastMath.max(startRow, p0);
  1482.             final int pEnd   = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
  1483.             for (int p = pStart; p < pEnd; ++p) {
  1484.                 for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
  1485.                     final int jWidth = blockWidth(jBlock);
  1486.                     final int q0     = jBlock * BLOCK_SIZE;
  1487.                     final int qStart = FastMath.max(startColumn, q0);
  1488.                     final int qEnd   = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
  1489.                     final T[] block = blocks[iBlock * blockColumns + jBlock];
  1490.                     int k = (p - p0) * jWidth + qStart - q0;
  1491.                     for (int q = qStart; q < qEnd; ++q) {
  1492.                         block[k] = visitor.visit(p, q, block[k]);
  1493.                         ++k;
  1494.                     }
  1495.                 }
  1496.              }
  1497.         }
  1498.         return visitor.end();
  1499.     }

  1500.     /** {@inheritDoc} */
  1501.     @Override
  1502.     public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor,
  1503.                             final int startRow, final int endRow,
  1504.                             final int startColumn, final int endColumn)
  1505.         throws MathIllegalArgumentException {
  1506.         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
  1507.         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
  1508.         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
  1509.             final int p0     = iBlock * BLOCK_SIZE;
  1510.             final int pStart = FastMath.max(startRow, p0);
  1511.             final int pEnd   = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
  1512.             for (int p = pStart; p < pEnd; ++p) {
  1513.                 for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
  1514.                     final int jWidth = blockWidth(jBlock);
  1515.                     final int q0     = jBlock * BLOCK_SIZE;
  1516.                     final int qStart = FastMath.max(startColumn, q0);
  1517.                     final int qEnd   = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
  1518.                     final T[] block = blocks[iBlock * blockColumns + jBlock];
  1519.                     int k = (p - p0) * jWidth + qStart - q0;
  1520.                     for (int q = qStart; q < qEnd; ++q) {
  1521.                         visitor.visit(p, q, block[k]);
  1522.                         ++k;
  1523.                     }
  1524.                 }
  1525.              }
  1526.         }
  1527.         return visitor.end();
  1528.     }

  1529.     /** {@inheritDoc} */
  1530.     @Override
  1531.     public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor) {
  1532.         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
  1533.         int blockIndex = 0;
  1534.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1535.             final int pStart = iBlock * BLOCK_SIZE;
  1536.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1537.             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1538.                 final int qStart = jBlock * BLOCK_SIZE;
  1539.                 final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1540.                 final T[] block = blocks[blockIndex];
  1541.                 int k = 0;
  1542.                 for (int p = pStart; p < pEnd; ++p) {
  1543.                     for (int q = qStart; q < qEnd; ++q) {
  1544.                         block[k] = visitor.visit(p, q, block[k]);
  1545.                         ++k;
  1546.                     }
  1547.                 }
  1548.                 ++blockIndex;
  1549.             }
  1550.         }
  1551.         return visitor.end();
  1552.     }

  1553.     /** {@inheritDoc} */
  1554.     @Override
  1555.     public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor) {
  1556.         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
  1557.         int blockIndex = 0;
  1558.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1559.             final int pStart = iBlock * BLOCK_SIZE;
  1560.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1561.             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1562.                 final int qStart = jBlock * BLOCK_SIZE;
  1563.                 final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1564.                 final T[] block = blocks[blockIndex];
  1565.                 int k = 0;
  1566.                 for (int p = pStart; p < pEnd; ++p) {
  1567.                     for (int q = qStart; q < qEnd; ++q) {
  1568.                         visitor.visit(p, q, block[k]);
  1569.                         ++k;
  1570.                     }
  1571.                 }
  1572.                 ++blockIndex;
  1573.             }
  1574.         }
  1575.         return visitor.end();
  1576.     }

  1577.     /** {@inheritDoc} */
  1578.     @Override
  1579.     public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor,
  1580.                                   final int startRow, final int endRow,
  1581.                                   final int startColumn, final int endColumn)
  1582.         throws MathIllegalArgumentException {
  1583.         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
  1584.         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
  1585.         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
  1586.             final int p0     = iBlock * BLOCK_SIZE;
  1587.             final int pStart = FastMath.max(startRow, p0);
  1588.             final int pEnd   = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
  1589.             for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
  1590.                 final int jWidth = blockWidth(jBlock);
  1591.                 final int q0     = jBlock * BLOCK_SIZE;
  1592.                 final int qStart = FastMath.max(startColumn, q0);
  1593.                 final int qEnd   = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
  1594.                 final T[] block = blocks[iBlock * blockColumns + jBlock];
  1595.                 for (int p = pStart; p < pEnd; ++p) {
  1596.                     int k = (p - p0) * jWidth + qStart - q0;
  1597.                     for (int q = qStart; q < qEnd; ++q) {
  1598.                         block[k] = visitor.visit(p, q, block[k]);
  1599.                         ++k;
  1600.                     }
  1601.                 }
  1602.             }
  1603.         }
  1604.         return visitor.end();
  1605.     }

  1606.     /** {@inheritDoc} */
  1607.     @Override
  1608.     public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor,
  1609.                                   final int startRow, final int endRow,
  1610.                                   final int startColumn, final int endColumn)
  1611.         throws MathIllegalArgumentException {
  1612.         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
  1613.         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
  1614.         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
  1615.             final int p0     = iBlock * BLOCK_SIZE;
  1616.             final int pStart = FastMath.max(startRow, p0);
  1617.             final int pEnd   = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
  1618.             for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
  1619.                 final int jWidth = blockWidth(jBlock);
  1620.                 final int q0     = jBlock * BLOCK_SIZE;
  1621.                 final int qStart = FastMath.max(startColumn, q0);
  1622.                 final int qEnd   = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
  1623.                 final T[] block = blocks[iBlock * blockColumns + jBlock];
  1624.                 for (int p = pStart; p < pEnd; ++p) {
  1625.                     int k = (p - p0) * jWidth + qStart - q0;
  1626.                     for (int q = qStart; q < qEnd; ++q) {
  1627.                         visitor.visit(p, q, block[k]);
  1628.                         ++k;
  1629.                     }
  1630.                 }
  1631.             }
  1632.         }
  1633.         return visitor.end();
  1634.     }

  1635.     /**
  1636.      * Get the height of a block.
  1637.      * @param blockRow row index (in block sense) of the block
  1638.      * @return height (number of rows) of the block
  1639.      */
  1640.     private int blockHeight(final int blockRow) {
  1641.         return (blockRow == blockRows - 1) ? rows - blockRow * BLOCK_SIZE : BLOCK_SIZE;
  1642.     }

  1643.     /**
  1644.      * Get the width of a block.
  1645.      * @param blockColumn column index (in block sense) of the block
  1646.      * @return width (number of columns) of the block
  1647.      */
  1648.     private int blockWidth(final int blockColumn) {
  1649.         return (blockColumn == blockColumns - 1) ? columns - blockColumn * BLOCK_SIZE : BLOCK_SIZE;
  1650.     }
  1651. }