BlockRealMatrix.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 java.util.Arrays;

  24. import org.hipparchus.exception.LocalizedCoreFormats;
  25. import org.hipparchus.exception.MathIllegalArgumentException;
  26. import org.hipparchus.exception.NullArgumentException;
  27. import org.hipparchus.util.FastMath;
  28. import org.hipparchus.util.MathUtils;

  29. /**
  30.  * Cache-friendly implementation of RealMatrix using a flat arrays to store
  31.  * square blocks of the matrix.
  32.  * <p>
  33.  * This implementation is specially designed to be cache-friendly. Square blocks are
  34.  * stored as small arrays and allow efficient traversal of data both in row major direction
  35.  * and columns major direction, one block at a time. This greatly increases performances
  36.  * for algorithms that use crossed directions loops like multiplication or transposition.
  37.  * </p>
  38.  * <p>
  39.  * The size of square blocks is a static parameter. It may be tuned according to the cache
  40.  * size of the target computer processor. As a rule of thumbs, it should be the largest
  41.  * value that allows three blocks to be simultaneously cached (this is necessary for example
  42.  * for matrix multiplication). The default value is to use 52x52 blocks which is well suited
  43.  * for processors with 64k L1 cache (one block holds 2704 values or 21632 bytes). This value
  44.  * could be lowered to 36x36 for processors with 32k L1 cache.
  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 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 52x52, a 100x60 matrix would be stored in 4 blocks.
  55.  * Block 0 would be a {@code double[2704]} array holding the upper left 52x52 square, block 1
  56.  * would be a {@code double[416]} array holding the upper right 52x8 rectangle, block 2 would
  57.  * be a {@code double[2496]} array holding the lower left 48x52 rectangle and block 3 would
  58.  * be a {@code double[384]} array holding the lower right 48x8 rectangle.
  59.  * </p>
  60.  * <p>
  61.  * The layout complexity overhead versus simple mapping of matrices to java
  62.  * arrays is negligible for small matrices (about 1%). The gain from cache efficiency leads
  63.  * to up to 3-fold improvements for matrices of moderate to large size.
  64.  * </p>
  65.  */
  66. public class BlockRealMatrix extends AbstractRealMatrix implements Serializable {
  67.     /** Block size. */
  68.     public static final int BLOCK_SIZE = 52;
  69.     /** Serializable version identifier */
  70.     private static final long serialVersionUID = 4991895511313664478L;
  71.     /** Blocks of matrix entries. */
  72.     private final double[][] blocks;
  73.     /** Number of rows of the matrix. */
  74.     private final int rows;
  75.     /** Number of columns of the matrix. */
  76.     private final int columns;
  77.     /** Number of block rows of the matrix. */
  78.     private final int blockRows;
  79.     /** Number of block columns of the matrix. */
  80.     private final int blockColumns;

  81.     /**
  82.      * Create a new matrix with the supplied row and column dimensions.
  83.      *
  84.      * @param rows  the number of rows in the new matrix
  85.      * @param columns  the number of columns in the new matrix
  86.      * @throws MathIllegalArgumentException if row or column dimension is not
  87.      * positive.
  88.      */
  89.     public BlockRealMatrix(final int rows, final int columns)
  90.         throws MathIllegalArgumentException {
  91.         super(rows, columns);
  92.         this.rows = rows;
  93.         this.columns = columns;

  94.         // number of blocks
  95.         blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE;
  96.         blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;

  97.         // allocate storage blocks, taking care of smaller ones at right and bottom
  98.         blocks = createBlocksLayout(rows, columns);
  99.     }

  100.     /**
  101.      * Create a new dense matrix copying entries from raw layout data.
  102.      * <p>The input array <em>must</em> already be in raw layout.</p>
  103.      * <p>Calling this constructor is equivalent to call:</p>
  104.      * <pre>matrix = new BlockRealMatrix(rawData.length, rawData[0].length,
  105.      *                                   toBlocksLayout(rawData), false);</pre>
  106.      *
  107.      * @param rawData data for new matrix, in raw layout
  108.      * @throws MathIllegalArgumentException if the shape of {@code blockData} is
  109.      * inconsistent with block layout.
  110.      * @throws MathIllegalArgumentException if row or column dimension is not
  111.      * positive.
  112.      * @see #BlockRealMatrix(int, int, double[][], boolean)
  113.      */
  114.     public BlockRealMatrix(final double[][] rawData)
  115.         throws MathIllegalArgumentException {
  116.         this(rawData.length, rawData[0].length, toBlocksLayout(rawData), false);
  117.     }

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

  140.         // number of blocks
  141.         blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE;
  142.         blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;

  143.         if (copyArray) {
  144.             // allocate storage blocks, taking care of smaller ones at right and bottom
  145.             blocks = new double[blockRows * blockColumns][];
  146.         } else {
  147.             // reference existing array
  148.             blocks = blockData; // NOPMD - array copy is taken care of by parameter
  149.         }

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

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

  192.         // safety checks
  193.         for (double[] rawDatum : rawData) {
  194.             final int length = rawDatum.length;
  195.             if (length != columns) {
  196.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  197.                         columns, length);
  198.             }
  199.         }

  200.         // convert array
  201.         final double[][] blocks = new double[blockRows * blockColumns][];
  202.         int blockIndex = 0;
  203.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  204.             final int pStart = iBlock * BLOCK_SIZE;
  205.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  206.             final int iHeight = pEnd - pStart;
  207.             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  208.                 final int qStart = jBlock * BLOCK_SIZE;
  209.                 final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  210.                 final int jWidth = qEnd - qStart;

  211.                 // allocate new block
  212.                 final double[] block = new double[iHeight * jWidth];
  213.                 blocks[blockIndex] = block;

  214.                 // copy data
  215.                 int index = 0;
  216.                 for (int p = pStart; p < pEnd; ++p) {
  217.                     System.arraycopy(rawData[p], qStart, block, index, jWidth);
  218.                     index += jWidth;
  219.                 }
  220.                 ++blockIndex;
  221.             }
  222.         }

  223.         return blocks;
  224.     }

  225.     /**
  226.      * Create a data array in blocks layout.
  227.      * <p>
  228.      * This method can be used to create the array argument of the {@link
  229.      * #BlockRealMatrix(int, int, double[][], boolean)} constructor.
  230.      * </p>
  231.      * @param rows Number of rows in the new matrix.
  232.      * @param columns Number of columns in the new matrix.
  233.      * @return a new data array in blocks layout.
  234.      * @see #toBlocksLayout(double[][])
  235.      * @see #BlockRealMatrix(int, int, double[][], boolean)
  236.      */
  237.     public static double[][] createBlocksLayout(final int rows, final int columns) {
  238.         final int blockRows = (rows    + BLOCK_SIZE - 1) / BLOCK_SIZE;
  239.         final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;

  240.         final double[][] blocks = new double[blockRows * blockColumns][];
  241.         int blockIndex = 0;
  242.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  243.             final int pStart = iBlock * BLOCK_SIZE;
  244.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  245.             final int iHeight = pEnd - pStart;
  246.             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  247.                 final int qStart = jBlock * BLOCK_SIZE;
  248.                 final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  249.                 final int jWidth = qEnd - qStart;
  250.                 blocks[blockIndex] = new double[iHeight * jWidth];
  251.                 ++blockIndex;
  252.             }
  253.         }

  254.         return blocks;
  255.     }

  256.     /** {@inheritDoc} */
  257.     @Override
  258.     public BlockRealMatrix createMatrix(final int rowDimension,
  259.                                         final int columnDimension)
  260.         throws MathIllegalArgumentException {
  261.         return new BlockRealMatrix(rowDimension, columnDimension);
  262.     }

  263.     /** {@inheritDoc} */
  264.     @Override
  265.     public BlockRealMatrix copy() {
  266.         // create an empty matrix
  267.         BlockRealMatrix copied = new BlockRealMatrix(rows, columns);

  268.         // copy the blocks
  269.         for (int i = 0; i < blocks.length; ++i) {
  270.             System.arraycopy(blocks[i], 0, copied.blocks[i], 0, blocks[i].length);
  271.         }

  272.         return copied;
  273.     }

  274.     /** {@inheritDoc} */
  275.     @Override
  276.     public BlockRealMatrix add(final RealMatrix m)
  277.         throws MathIllegalArgumentException {
  278.         if (m instanceof BlockRealMatrix) {
  279.             return add((BlockRealMatrix) m);
  280.         } else {
  281.             // safety check
  282.             MatrixUtils.checkAdditionCompatible(this, m);

  283.             final BlockRealMatrix out = new BlockRealMatrix(rows, columns);

  284.             // perform addition block-wise, to ensure good cache behavior
  285.             int blockIndex = 0;
  286.             for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
  287.                 final int pStart = iBlock * BLOCK_SIZE;
  288.                 final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  289.                 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {

  290.                     // perform addition on the current block
  291.                     final double[] outBlock = out.blocks[blockIndex];
  292.                     final double[] tBlock   = blocks[blockIndex];
  293.                     final int qStart = jBlock * BLOCK_SIZE;
  294.                     final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  295.                     int k = 0;
  296.                     for (int p = pStart; p < pEnd; ++p) {
  297.                         for (int q = qStart; q < qEnd; ++q) {
  298.                             outBlock[k] = tBlock[k] + m.getEntry(p, q);
  299.                             ++k;
  300.                         }
  301.                     }
  302.                     // go to next block
  303.                     ++blockIndex;
  304.                 }
  305.             }

  306.             return out;
  307.         }
  308.     }

  309.     /**
  310.      * Compute the sum of this matrix and {@code m}.
  311.      *
  312.      * @param m Matrix to be added.
  313.      * @return {@code this} + m.
  314.      * @throws MathIllegalArgumentException if {@code m} is not the same
  315.      * size as this matrix.
  316.      */
  317.     public BlockRealMatrix add(final BlockRealMatrix m)
  318.         throws MathIllegalArgumentException {
  319.         // safety check
  320.         MatrixUtils.checkAdditionCompatible(this, m);

  321.         final BlockRealMatrix out = new BlockRealMatrix(rows, columns);

  322.         // perform addition block-wise, to ensure good cache behavior
  323.         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
  324.             final double[] outBlock = out.blocks[blockIndex];
  325.             final double[] tBlock = blocks[blockIndex];
  326.             final double[] mBlock = m.blocks[blockIndex];
  327.             for (int k = 0; k < outBlock.length; ++k) {
  328.                 outBlock[k] = tBlock[k] + mBlock[k];
  329.             }
  330.         }

  331.         return out;
  332.     }

  333.     /** {@inheritDoc} */
  334.     @Override
  335.     public BlockRealMatrix subtract(final RealMatrix m)
  336.         throws MathIllegalArgumentException {
  337.         if (m instanceof BlockRealMatrix) {
  338.             return subtract((BlockRealMatrix) m);
  339.         } else {
  340.             // safety check
  341.             MatrixUtils.checkSubtractionCompatible(this, m);

  342.             final BlockRealMatrix out = new BlockRealMatrix(rows, columns);

  343.             // perform subtraction block-wise, to ensure good cache behavior
  344.             int blockIndex = 0;
  345.             for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
  346.                 final int pStart = iBlock * BLOCK_SIZE;
  347.                 final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  348.                 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {

  349.                     // perform subtraction on the current block
  350.                     final double[] outBlock = out.blocks[blockIndex];
  351.                     final double[] tBlock = blocks[blockIndex];
  352.                     final int qStart = jBlock * BLOCK_SIZE;
  353.                     final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  354.                     int k = 0;
  355.                     for (int p = pStart; p < pEnd; ++p) {
  356.                         for (int q = qStart; q < qEnd; ++q) {
  357.                             outBlock[k] = tBlock[k] - m.getEntry(p, q);
  358.                             ++k;
  359.                         }
  360.                     }
  361.                     // go to next block
  362.                     ++blockIndex;
  363.                 }
  364.             }

  365.             return out;
  366.         }
  367.     }

  368.     /**
  369.      * Subtract {@code m} from this matrix.
  370.      *
  371.      * @param m Matrix to be subtracted.
  372.      * @return {@code this} - m.
  373.      * @throws MathIllegalArgumentException if {@code m} is not the
  374.      * same size as this matrix.
  375.      */
  376.     public BlockRealMatrix subtract(final BlockRealMatrix m)
  377.         throws MathIllegalArgumentException {
  378.         // safety check
  379.         MatrixUtils.checkSubtractionCompatible(this, m);

  380.         final BlockRealMatrix out = new BlockRealMatrix(rows, columns);

  381.         // perform subtraction block-wise, to ensure good cache behavior
  382.         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
  383.             final double[] outBlock = out.blocks[blockIndex];
  384.             final double[] tBlock = blocks[blockIndex];
  385.             final double[] mBlock = m.blocks[blockIndex];
  386.             for (int k = 0; k < outBlock.length; ++k) {
  387.                 outBlock[k] = tBlock[k] - mBlock[k];
  388.             }
  389.         }

  390.         return out;
  391.     }

  392.     /** {@inheritDoc} */
  393.     @Override
  394.     public BlockRealMatrix scalarAdd(final double d) {

  395.         final BlockRealMatrix out = new BlockRealMatrix(rows, columns);

  396.         // perform subtraction block-wise, to ensure good cache behavior
  397.         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
  398.             final double[] outBlock = out.blocks[blockIndex];
  399.             final double[] tBlock = blocks[blockIndex];
  400.             for (int k = 0; k < outBlock.length; ++k) {
  401.                 outBlock[k] = tBlock[k] + d;
  402.             }
  403.         }

  404.         return out;
  405.     }

  406.     /** {@inheritDoc} */
  407.     @Override
  408.     public RealMatrix scalarMultiply(final double d) {
  409.         final BlockRealMatrix out = new BlockRealMatrix(rows, columns);

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

  418.         return out;
  419.     }

  420.     /** {@inheritDoc} */
  421.     @Override
  422.     public BlockRealMatrix multiply(final RealMatrix m)
  423.         throws MathIllegalArgumentException {
  424.         if (m instanceof BlockRealMatrix) {
  425.             return multiply((BlockRealMatrix) m);
  426.         } else {
  427.             // safety check
  428.             MatrixUtils.checkMultiplicationCompatible(this, m);

  429.             final BlockRealMatrix out = new BlockRealMatrix(rows, m.getColumnDimension());

  430.             // perform multiplication block-wise, to ensure good cache behavior
  431.             int blockIndex = 0;
  432.             for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
  433.                 final int pStart = iBlock * BLOCK_SIZE;
  434.                 final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);

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

  438.                     // select current block
  439.                     final double[] outBlock = out.blocks[blockIndex];

  440.                     // perform multiplication on current block
  441.                     for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
  442.                         final int kWidth = blockWidth(kBlock);
  443.                         final double[] tBlock = blocks[iBlock * blockColumns + kBlock];
  444.                         final int rStart = kBlock * BLOCK_SIZE;
  445.                         int k = 0;
  446.                         for (int p = pStart; p < pEnd; ++p) {
  447.                             final int lStart = (p - pStart) * kWidth;
  448.                             final int lEnd = lStart + kWidth;
  449.                             for (int q = qStart; q < qEnd; ++q) {
  450.                                 double sum = 0;
  451.                                 int r = rStart;
  452.                                 for (int l = lStart; l < lEnd; ++l) {
  453.                                     sum += tBlock[l] * m.getEntry(r++, q);
  454.                                 }
  455.                                 outBlock[k] += sum;
  456.                                 ++k;
  457.                             }
  458.                         }
  459.                     }
  460.                     // go to next block
  461.                     ++blockIndex;
  462.                 }
  463.             }

  464.             return out;
  465.         }
  466.     }

  467.     /**
  468.      * Returns the result of postmultiplying this by {@code m}.
  469.      *
  470.      * @param m Matrix to postmultiply by.
  471.      * @return {@code this} * m.
  472.      * @throws MathIllegalArgumentException if the matrices are not compatible.
  473.      */
  474.     public BlockRealMatrix multiply(BlockRealMatrix m)
  475.         throws MathIllegalArgumentException {
  476.         // safety check
  477.         MatrixUtils.checkMultiplicationCompatible(this, m);

  478.         final BlockRealMatrix out = new BlockRealMatrix(rows, m.columns);

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

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

  484.             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
  485.                 final int jWidth = out.blockWidth(jBlock);
  486.                 final int jWidth2 = jWidth  + jWidth;
  487.                 final int jWidth3 = jWidth2 + jWidth;
  488.                 final int jWidth4 = jWidth3 + jWidth;

  489.                 // select current block
  490.                 final double[] outBlock = out.blocks[blockIndex];

  491.                 // perform multiplication on current block
  492.                 for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
  493.                     final int kWidth = blockWidth(kBlock);
  494.                     final double[] tBlock = blocks[iBlock * blockColumns + kBlock];
  495.                     final double[] mBlock = m.blocks[kBlock * m.blockColumns + jBlock];
  496.                     int k = 0;
  497.                     for (int p = pStart; p < pEnd; ++p) {
  498.                         final int lStart = (p - pStart) * kWidth;
  499.                         final int lEnd   = lStart + kWidth;
  500.                         for (int nStart = 0; nStart < jWidth; ++nStart) {
  501.                             double sum = 0;
  502.                             int l = lStart;
  503.                             int n = nStart;
  504.                             while (l < lEnd - 3) {
  505.                                 sum += tBlock[l] * mBlock[n] +
  506.                                        tBlock[l + 1] * mBlock[n + jWidth] +
  507.                                        tBlock[l + 2] * mBlock[n + jWidth2] +
  508.                                        tBlock[l + 3] * mBlock[n + jWidth3];
  509.                                 l += 4;
  510.                                 n += jWidth4;
  511.                             }
  512.                             while (l < lEnd) {
  513.                                 sum += tBlock[l++] * mBlock[n];
  514.                                 n += jWidth;
  515.                             }
  516.                             outBlock[k] += sum;
  517.                             ++k;
  518.                         }
  519.                     }
  520.                 }
  521.                 // go to next block
  522.                 ++blockIndex;
  523.             }
  524.         }

  525.         return out;
  526.     }

  527.     /**
  528.      * Returns the result of postmultiplying {@code this} by {@code m^T}.
  529.      * @param m matrix to first transpose and second postmultiply by
  530.      * @return {@code this * m^T}
  531.      * @throws MathIllegalArgumentException if
  532.      * {@code columnDimension(this) != columnDimension(m)}
  533.      * @since 1.3
  534.      */
  535.     public BlockRealMatrix multiplyTransposed(BlockRealMatrix m)
  536.         throws MathIllegalArgumentException {
  537.         // safety check
  538.         MatrixUtils.checkSameColumnDimension(this, m);

  539.         final BlockRealMatrix out = new BlockRealMatrix(rows, m.rows);

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

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

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

  547.                 // select current block
  548.                 final double[] outBlock = out.blocks[blockIndex];

  549.                 // perform multiplication on current block
  550.                 for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
  551.                     final int kWidth = blockWidth(kBlock);
  552.                     final double[] tBlock = blocks[iBlock * blockColumns + kBlock];
  553.                     final double[] mBlock = m.blocks[jBlock * m.blockColumns + kBlock];
  554.                     int k = 0;
  555.                     for (int p = pStart; p < pEnd; ++p) {
  556.                         final int lStart = (p - pStart) * kWidth;
  557.                         final int lEnd   = lStart + kWidth;
  558.                         for (int nStart = 0; nStart < jWidth * kWidth; nStart += kWidth) {
  559.                             double sum = 0;
  560.                             int l = lStart;
  561.                             int n = nStart;
  562.                             while (l < lEnd - 3) {
  563.                                 sum += tBlock[l]     * mBlock[n]     +
  564.                                        tBlock[l + 1] * mBlock[n + 1] +
  565.                                        tBlock[l + 2] * mBlock[n + 2] +
  566.                                        tBlock[l + 3] * mBlock[n + 3];
  567.                                 l += 4;
  568.                                 n += 4;
  569.                             }
  570.                             while (l < lEnd) {
  571.                                 sum += tBlock[l++] * mBlock[n++];
  572.                             }
  573.                             outBlock[k] += sum;
  574.                             ++k;
  575.                         }
  576.                     }
  577.                 }
  578.                 // go to next block
  579.                 ++blockIndex;
  580.             }
  581.         }

  582.         return out;
  583.     }

  584.     /** {@inheritDoc} */
  585.     @Override
  586.     public BlockRealMatrix multiplyTransposed(final RealMatrix m)
  587.         throws MathIllegalArgumentException {
  588.         if (m instanceof BlockRealMatrix) {
  589.             return multiplyTransposed((BlockRealMatrix) m);
  590.         } else {
  591.             // safety check
  592.             MatrixUtils.checkSameColumnDimension(this, m);

  593.             final BlockRealMatrix out = new BlockRealMatrix(rows, m.getRowDimension());

  594.             // perform multiplication block-wise, to ensure good cache behavior
  595.             int blockIndex = 0;
  596.             for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
  597.                 final int pStart = iBlock * BLOCK_SIZE;
  598.                 final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);

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

  602.                     // select current block
  603.                     final double[] outBlock = out.blocks[blockIndex];

  604.                     // perform multiplication on current block
  605.                     for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
  606.                         final int kWidth = blockWidth(kBlock);
  607.                         final double[] tBlock = blocks[iBlock * blockColumns + kBlock];
  608.                         final int rStart = kBlock * BLOCK_SIZE;
  609.                         int k = 0;
  610.                         for (int p = pStart; p < pEnd; ++p) {
  611.                             final int lStart = (p - pStart) * kWidth;
  612.                             final int lEnd = lStart + kWidth;
  613.                             for (int q = qStart; q < qEnd; ++q) {
  614.                                 double sum = 0;
  615.                                 int r = rStart;
  616.                                 for (int l = lStart; l < lEnd; ++l) {
  617.                                     sum += tBlock[l] * m.getEntry(q, r++);
  618.                                 }
  619.                                 outBlock[k] += sum;
  620.                                 ++k;
  621.                             }
  622.                         }
  623.                     }
  624.                     // go to next block
  625.                     ++blockIndex;
  626.                 }
  627.             }

  628.             return out;
  629.         }
  630.     }

  631.     /**
  632.      * Returns the result of postmultiplying {@code this^T} by {@code m}.
  633.      * @param m matrix to postmultiply by
  634.      * @return {@code this^T * m}
  635.      * @throws MathIllegalArgumentException if
  636.      * {@code columnDimension(this) != columnDimension(m)}
  637.      * @since 1.3
  638.      */
  639.     public BlockRealMatrix transposeMultiply(final BlockRealMatrix m)
  640.         throws MathIllegalArgumentException {
  641.         // safety check
  642.         MatrixUtils.checkSameRowDimension(this, m);

  643.         final BlockRealMatrix out = new BlockRealMatrix(columns, m.columns);

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

  647.             final int iHeight  = out.blockHeight(iBlock);
  648.             final int iHeight2 = iHeight  + iHeight;
  649.             final int iHeight3 = iHeight2 + iHeight;
  650.             final int iHeight4 = iHeight3 + iHeight;
  651.             final int pStart   = iBlock * BLOCK_SIZE;
  652.             final int pEnd     = FastMath.min(pStart + BLOCK_SIZE, columns);

  653.             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
  654.                 final int jWidth  = out.blockWidth(jBlock);
  655.                 final int jWidth2 = jWidth  + jWidth;
  656.                 final int jWidth3 = jWidth2 + jWidth;
  657.                 final int jWidth4 = jWidth3 + jWidth;

  658.                 // select current block
  659.                 final double[] outBlock = out.blocks[blockIndex];

  660.                 // perform multiplication on current block
  661.                 for (int kBlock = 0; kBlock < blockRows; ++kBlock) {
  662.                     final int      kHeight = blockHeight(kBlock);
  663.                     final double[] tBlock  = blocks[kBlock * blockColumns + iBlock];
  664.                     final double[] mBlock  = m.blocks[kBlock * m.blockColumns + jBlock];
  665.                     int k = 0;
  666.                     for (int p = pStart; p < pEnd; ++p) {
  667.                         final int lStart = p - pStart;
  668.                         final int lEnd   = lStart + iHeight * kHeight;
  669.                         for (int nStart = 0; nStart < jWidth; ++nStart) {
  670.                             double sum = 0;
  671.                             int l = lStart;
  672.                             int n = nStart;
  673.                             while (l < lEnd - iHeight3) {
  674.                                 sum += tBlock[l]            * mBlock[n] +
  675.                                        tBlock[l + iHeight]  * mBlock[n + jWidth] +
  676.                                        tBlock[l + iHeight2] * mBlock[n + jWidth2] +
  677.                                        tBlock[l + iHeight3] * mBlock[n + jWidth3];
  678.                                 l += iHeight4;
  679.                                 n += jWidth4;
  680.                             }
  681.                             while (l < lEnd) {
  682.                                 sum += tBlock[l] * mBlock[n];
  683.                                 l += iHeight;
  684.                                 n += jWidth;
  685.                             }
  686.                             outBlock[k] += sum;
  687.                             ++k;
  688.                         }
  689.                     }
  690.                 }
  691.                 // go to next block
  692.                 ++blockIndex;
  693.             }
  694.         }

  695.         return out;
  696.     }

  697.     /** {@inheritDoc} */
  698.     @Override
  699.     public BlockRealMatrix transposeMultiply(final RealMatrix m)
  700.         throws MathIllegalArgumentException {
  701.         if (m instanceof BlockRealMatrix) {
  702.             return transposeMultiply((BlockRealMatrix) m);
  703.         } else {
  704.             // safety check
  705.             MatrixUtils.checkSameRowDimension(this, m);

  706.             final BlockRealMatrix out = new BlockRealMatrix(columns, m.getColumnDimension());

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

  710.                 final int iHeight = out.blockHeight(iBlock);
  711.                 final int pStart  = iBlock * BLOCK_SIZE;
  712.                 final int pEnd    = FastMath.min(pStart + BLOCK_SIZE, columns);

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

  716.                     // select current block
  717.                     final double[] outBlock = out.blocks[blockIndex];

  718.                     // perform multiplication on current block
  719.                     for (int kBlock = 0; kBlock < blockRows; ++kBlock) {
  720.                         final int      kHeight = blockHeight(kBlock);
  721.                         final double[] tBlock  = blocks[kBlock * blockColumns + iBlock];
  722.                         final int      rStart  = kBlock * BLOCK_SIZE;
  723.                         int k = 0;
  724.                         for (int p = pStart; p < pEnd; ++p) {
  725.                             final int lStart = p - pStart;
  726.                             final int lEnd   = lStart + iHeight * kHeight;
  727.                             for (int q = qStart; q < qEnd; ++q) {
  728.                                 double sum = 0;
  729.                                 int r = rStart;
  730.                                 for (int l = lStart; l < lEnd; l += iHeight) {
  731.                                     sum += tBlock[l] * m.getEntry(r++, q);
  732.                                 }
  733.                                 outBlock[k] += sum;
  734.                                 ++k;
  735.                             }
  736.                         }
  737.                     }
  738.                     // go to next block
  739.                     ++blockIndex;
  740.                 }
  741.             }

  742.             return out;
  743.         }

  744.     }

  745.     /** {@inheritDoc} */
  746.     @Override
  747.     public double[][] getData() {
  748.         final double[][] data = new double[getRowDimension()][getColumnDimension()];
  749.         final int lastColumns = columns - (blockColumns - 1) * BLOCK_SIZE;

  750.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  751.             final int pStart = iBlock * BLOCK_SIZE;
  752.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  753.             int regularPos = 0;
  754.             int lastPos = 0;
  755.             for (int p = pStart; p < pEnd; ++p) {
  756.                 final double[] dataP = data[p];
  757.                 int blockIndex = iBlock * blockColumns;
  758.                 int dataPos = 0;
  759.                 for (int jBlock = 0; jBlock < blockColumns - 1; ++jBlock) {
  760.                     System.arraycopy(blocks[blockIndex++], regularPos, dataP, dataPos, BLOCK_SIZE);
  761.                     dataPos += BLOCK_SIZE;
  762.                 }
  763.                 System.arraycopy(blocks[blockIndex], lastPos, dataP, dataPos, lastColumns);
  764.                 regularPos += BLOCK_SIZE;
  765.                 lastPos    += lastColumns;
  766.             }
  767.         }

  768.         return data;
  769.     }

  770.     /** {@inheritDoc} */
  771.     @Override
  772.     public double getNorm1() {
  773.         final double[] colSums = new double[BLOCK_SIZE];
  774.         double maxColSum = 0;
  775.         for (int jBlock = 0; jBlock < blockColumns; jBlock++) {
  776.             final int jWidth = blockWidth(jBlock);
  777.             Arrays.fill(colSums, 0, jWidth, 0.0);
  778.             for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  779.                 final int iHeight = blockHeight(iBlock);
  780.                 final double[] block = blocks[iBlock * blockColumns + jBlock];
  781.                 for (int j = 0; j < jWidth; ++j) {
  782.                     double sum = 0;
  783.                     for (int i = 0; i < iHeight; ++i) {
  784.                         sum += FastMath.abs(block[i * jWidth + j]);
  785.                     }
  786.                     colSums[j] += sum;
  787.                 }
  788.             }
  789.             for (int j = 0; j < jWidth; ++j) {
  790.                 maxColSum = FastMath.max(maxColSum, colSums[j]);
  791.             }
  792.         }
  793.         return maxColSum;
  794.     }

  795.     /** {@inheritDoc} */
  796.     @Override
  797.     public double getNormInfty() {
  798.         final double[] rowSums = new double[BLOCK_SIZE];
  799.         double maxRowSum = 0;
  800.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  801.             final int iHeight = blockHeight(iBlock);
  802.             Arrays.fill(rowSums, 0, iHeight, 0.0);
  803.             for (int jBlock = 0; jBlock < blockColumns; jBlock++) {
  804.                 final int jWidth = blockWidth(jBlock);
  805.                 final double[] block = blocks[iBlock * blockColumns + jBlock];
  806.                 for (int i = 0; i < iHeight; ++i) {
  807.                     double sum = 0;
  808.                     for (int j = 0; j < jWidth; ++j) {
  809.                         sum += FastMath.abs(block[i * jWidth + j]);
  810.                     }
  811.                     rowSums[i] += sum;
  812.                 }
  813.             }
  814.             for (int i = 0; i < iHeight; ++i) {
  815.                 maxRowSum = FastMath.max(maxRowSum, rowSums[i]);
  816.             }
  817.         }
  818.         return maxRowSum;
  819.     }

  820.     /** {@inheritDoc} */
  821.     @Override
  822.     public double getFrobeniusNorm() {
  823.         double sum2 = 0;
  824.         for (double[] block : blocks) {
  825.             for (final double entry : block) {
  826.                 sum2 += entry * entry;
  827.             }
  828.         }
  829.         return FastMath.sqrt(sum2);
  830.     }

  831.     /** {@inheritDoc} */
  832.     @Override
  833.     public BlockRealMatrix getSubMatrix(final int startRow, final int endRow,
  834.                                         final int startColumn,
  835.                                         final int endColumn)
  836.         throws MathIllegalArgumentException {
  837.         // safety checks
  838.         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);

  839.         // create the output matrix
  840.         final BlockRealMatrix out =
  841.             new BlockRealMatrix(endRow - startRow + 1, endColumn - startColumn + 1);

  842.         // compute blocks shifts
  843.         final int blockStartRow = startRow / BLOCK_SIZE;
  844.         final int rowsShift = startRow % BLOCK_SIZE;
  845.         final int blockStartColumn = startColumn / BLOCK_SIZE;
  846.         final int columnsShift = startColumn % BLOCK_SIZE;

  847.         // perform extraction block-wise, to ensure good cache behavior
  848.         int pBlock = blockStartRow;
  849.         for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
  850.             final int iHeight = out.blockHeight(iBlock);
  851.             int qBlock = blockStartColumn;
  852.             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
  853.                 final int jWidth = out.blockWidth(jBlock);

  854.                 // handle one block of the output matrix
  855.                 final int outIndex = iBlock * out.blockColumns + jBlock;
  856.                 final double[] outBlock = out.blocks[outIndex];
  857.                 final int index = pBlock * blockColumns + qBlock;
  858.                 final int width = blockWidth(qBlock);

  859.                 final int heightExcess = iHeight + rowsShift - BLOCK_SIZE;
  860.                 final int widthExcess = jWidth + columnsShift - BLOCK_SIZE;
  861.                 if (heightExcess > 0) {
  862.                     // the submatrix block spans on two blocks rows from the original matrix
  863.                     if (widthExcess > 0) {
  864.                         // the submatrix block spans on two blocks columns from the original matrix
  865.                         final int width2 = blockWidth(qBlock + 1);
  866.                         copyBlockPart(blocks[index], width,
  867.                                       rowsShift, BLOCK_SIZE,
  868.                                       columnsShift, BLOCK_SIZE,
  869.                                       outBlock, jWidth, 0, 0);
  870.                         copyBlockPart(blocks[index + 1], width2,
  871.                                       rowsShift, BLOCK_SIZE,
  872.                                       0, widthExcess,
  873.                                       outBlock, jWidth, 0, jWidth - widthExcess);
  874.                         copyBlockPart(blocks[index + blockColumns], width,
  875.                                       0, heightExcess,
  876.                                       columnsShift, BLOCK_SIZE,
  877.                                       outBlock, jWidth, iHeight - heightExcess, 0);
  878.                         copyBlockPart(blocks[index + blockColumns + 1], width2,
  879.                                       0, heightExcess,
  880.                                       0, widthExcess,
  881.                                       outBlock, jWidth, iHeight - heightExcess, jWidth - widthExcess);
  882.                     } else {
  883.                         // the submatrix block spans on one block column from the original matrix
  884.                         copyBlockPart(blocks[index], width,
  885.                                       rowsShift, BLOCK_SIZE,
  886.                                       columnsShift, jWidth + columnsShift,
  887.                                       outBlock, jWidth, 0, 0);
  888.                         copyBlockPart(blocks[index + blockColumns], width,
  889.                                       0, heightExcess,
  890.                                       columnsShift, jWidth + columnsShift,
  891.                                       outBlock, jWidth, iHeight - heightExcess, 0);
  892.                     }
  893.                 } else {
  894.                     // the submatrix block spans on one block row from the original matrix
  895.                     if (widthExcess > 0) {
  896.                         // the submatrix block spans on two blocks columns from the original matrix
  897.                         final int width2 = blockWidth(qBlock + 1);
  898.                         copyBlockPart(blocks[index], width,
  899.                                       rowsShift, iHeight + rowsShift,
  900.                                       columnsShift, BLOCK_SIZE,
  901.                                       outBlock, jWidth, 0, 0);
  902.                         copyBlockPart(blocks[index + 1], width2,
  903.                                       rowsShift, iHeight + rowsShift,
  904.                                       0, widthExcess,
  905.                                       outBlock, jWidth, 0, jWidth - widthExcess);
  906.                     } else {
  907.                         // the submatrix block spans on one block column from the original matrix
  908.                         copyBlockPart(blocks[index], width,
  909.                                       rowsShift, iHeight + rowsShift,
  910.                                       columnsShift, jWidth + columnsShift,
  911.                                       outBlock, jWidth, 0, 0);
  912.                     }
  913.                }
  914.                 ++qBlock;
  915.             }
  916.             ++pBlock;
  917.         }

  918.         return out;
  919.     }

  920.     /**
  921.      * Copy a part of a block into another one
  922.      * <p>This method can be called only when the specified part fits in both
  923.      * blocks, no verification is done here.</p>
  924.      * @param srcBlock source block
  925.      * @param srcWidth source block width ({@link #BLOCK_SIZE} or smaller)
  926.      * @param srcStartRow start row in the source block
  927.      * @param srcEndRow end row (exclusive) in the source block
  928.      * @param srcStartColumn start column in the source block
  929.      * @param srcEndColumn end column (exclusive) in the source block
  930.      * @param dstBlock destination block
  931.      * @param dstWidth destination block width ({@link #BLOCK_SIZE} or smaller)
  932.      * @param dstStartRow start row in the destination block
  933.      * @param dstStartColumn start column in the destination block
  934.      */
  935.     private void copyBlockPart(final double[] srcBlock, final int srcWidth,
  936.                                final int srcStartRow, final int srcEndRow,
  937.                                final int srcStartColumn, final int srcEndColumn,
  938.                                final double[] dstBlock, final int dstWidth,
  939.                                final int dstStartRow, final int dstStartColumn) {
  940.         final int length = srcEndColumn - srcStartColumn;
  941.         int srcPos = srcStartRow * srcWidth + srcStartColumn;
  942.         int dstPos = dstStartRow * dstWidth + dstStartColumn;
  943.         for (int srcRow = srcStartRow; srcRow < srcEndRow; ++srcRow) {
  944.             System.arraycopy(srcBlock, srcPos, dstBlock, dstPos, length);
  945.             srcPos += srcWidth;
  946.             dstPos += dstWidth;
  947.         }
  948.     }

  949.     /** {@inheritDoc} */
  950.     @Override
  951.     public void setSubMatrix(final double[][] subMatrix, final int row,
  952.                              final int column)
  953.         throws MathIllegalArgumentException, NullArgumentException {
  954.         // safety checks
  955.         MathUtils.checkNotNull(subMatrix);
  956.         final int refLength = subMatrix[0].length;
  957.         if (refLength == 0) {
  958.             throw new MathIllegalArgumentException(LocalizedCoreFormats.AT_LEAST_ONE_COLUMN);
  959.         }
  960.         final int endRow = row + subMatrix.length - 1;
  961.         final int endColumn = column + refLength - 1;
  962.         MatrixUtils.checkSubMatrixIndex(this, row, endRow, column, endColumn);
  963.         for (final double[] subRow : subMatrix) {
  964.             if (subRow.length != refLength) {
  965.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  966.                                                        refLength, subRow.length);
  967.             }
  968.         }

  969.         // compute blocks bounds
  970.         final int blockStartRow = row / BLOCK_SIZE;
  971.         final int blockEndRow = (endRow + BLOCK_SIZE) / BLOCK_SIZE;
  972.         final int blockStartColumn = column / BLOCK_SIZE;
  973.         final int blockEndColumn = (endColumn + BLOCK_SIZE) / BLOCK_SIZE;

  974.         // perform copy block-wise, to ensure good cache behavior
  975.         for (int iBlock = blockStartRow; iBlock < blockEndRow; ++iBlock) {
  976.             final int iHeight = blockHeight(iBlock);
  977.             final int firstRow = iBlock * BLOCK_SIZE;
  978.             final int iStart = FastMath.max(row,    firstRow);
  979.             final int iEnd = FastMath.min(endRow + 1, firstRow + iHeight);

  980.             for (int jBlock = blockStartColumn; jBlock < blockEndColumn; ++jBlock) {
  981.                 final int jWidth = blockWidth(jBlock);
  982.                 final int firstColumn = jBlock * BLOCK_SIZE;
  983.                 final int jStart = FastMath.max(column,    firstColumn);
  984.                 final int jEnd = FastMath.min(endColumn + 1, firstColumn + jWidth);
  985.                 final int jLength = jEnd - jStart;

  986.                 // handle one block, row by row
  987.                 final double[] block = blocks[iBlock * blockColumns + jBlock];
  988.                 for (int i = iStart; i < iEnd; ++i) {
  989.                     System.arraycopy(subMatrix[i - row], jStart - column,
  990.                                      block, (i - firstRow) * jWidth + (jStart - firstColumn),
  991.                                      jLength);
  992.                 }

  993.             }
  994.         }
  995.     }

  996.     /** {@inheritDoc} */
  997.     @Override
  998.     public BlockRealMatrix getRowMatrix(final int row)
  999.         throws MathIllegalArgumentException {
  1000.         MatrixUtils.checkRowIndex(this, row);
  1001.         final BlockRealMatrix out = new BlockRealMatrix(1, columns);

  1002.         // perform copy block-wise, to ensure good cache behavior
  1003.         final int iBlock = row / BLOCK_SIZE;
  1004.         final int iRow = row - iBlock * BLOCK_SIZE;
  1005.         int outBlockIndex = 0;
  1006.         int outIndex = 0;
  1007.         double[] outBlock = out.blocks[outBlockIndex];
  1008.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1009.             final int jWidth = blockWidth(jBlock);
  1010.             final double[] block = blocks[iBlock * blockColumns + jBlock];
  1011.             final int available = outBlock.length - outIndex;
  1012.             if (jWidth > available) {
  1013.                 System.arraycopy(block, iRow * jWidth, outBlock, outIndex, available);
  1014.                 outBlock = out.blocks[++outBlockIndex];
  1015.                 System.arraycopy(block, iRow * jWidth, outBlock, 0, jWidth - available);
  1016.                 outIndex = jWidth - available;
  1017.             } else {
  1018.                 System.arraycopy(block, iRow * jWidth, outBlock, outIndex, jWidth);
  1019.                 outIndex += jWidth;
  1020.             }
  1021.         }

  1022.         return out;
  1023.     }

  1024.     /** {@inheritDoc} */
  1025.     @Override
  1026.     public void setRowMatrix(final int row, final RealMatrix matrix)
  1027.         throws MathIllegalArgumentException {
  1028.         if (matrix instanceof BlockRealMatrix) {
  1029.             setRowMatrix(row, (BlockRealMatrix) matrix);
  1030.         } else {
  1031.             super.setRowMatrix(row, matrix);
  1032.         }
  1033.     }

  1034.     /**
  1035.      * Sets the entries in row number <code>row</code>
  1036.      * as a row matrix.  Row indices start at 0.
  1037.      *
  1038.      * @param row the row to be set
  1039.      * @param matrix row matrix (must have one row and the same number of columns
  1040.      * as the instance)
  1041.      * @throws MathIllegalArgumentException if the specified row index is invalid.
  1042.      * @throws MathIllegalArgumentException if the matrix dimensions do
  1043.      * not match one instance row.
  1044.      */
  1045.     public void setRowMatrix(final int row, final BlockRealMatrix matrix)
  1046.         throws MathIllegalArgumentException {
  1047.         MatrixUtils.checkRowIndex(this, row);
  1048.         final int nCols = getColumnDimension();
  1049.         if ((matrix.getRowDimension() != 1) ||
  1050.             (matrix.getColumnDimension() != nCols)) {
  1051.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH_2x2,
  1052.                                                    matrix.getRowDimension(), matrix.getColumnDimension(),
  1053.                                                    1, nCols);
  1054.         }

  1055.         // perform copy block-wise, to ensure good cache behavior
  1056.         final int iBlock = row / BLOCK_SIZE;
  1057.         final int iRow = row - iBlock * BLOCK_SIZE;
  1058.         int mBlockIndex = 0;
  1059.         int mIndex = 0;
  1060.         double[] mBlock = matrix.blocks[mBlockIndex];
  1061.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1062.             final int jWidth = blockWidth(jBlock);
  1063.             final double[] block = blocks[iBlock * blockColumns + jBlock];
  1064.             final int available  = mBlock.length - mIndex;
  1065.             if (jWidth > available) {
  1066.                 System.arraycopy(mBlock, mIndex, block, iRow * jWidth, available);
  1067.                 mBlock = matrix.blocks[++mBlockIndex];
  1068.                 System.arraycopy(mBlock, 0, block, iRow * jWidth, jWidth - available);
  1069.                 mIndex = jWidth - available;
  1070.             } else {
  1071.                 System.arraycopy(mBlock, mIndex, block, iRow * jWidth, jWidth);
  1072.                 mIndex += jWidth;
  1073.            }
  1074.         }
  1075.     }

  1076.     /** {@inheritDoc} */
  1077.     @Override
  1078.     public BlockRealMatrix getColumnMatrix(final int column)
  1079.         throws MathIllegalArgumentException {
  1080.         MatrixUtils.checkColumnIndex(this, column);
  1081.         final BlockRealMatrix out = new BlockRealMatrix(rows, 1);

  1082.         // perform copy block-wise, to ensure good cache behavior
  1083.         final int jBlock = column / BLOCK_SIZE;
  1084.         final int jColumn = column - jBlock * BLOCK_SIZE;
  1085.         final int jWidth = blockWidth(jBlock);
  1086.         int outBlockIndex = 0;
  1087.         int outIndex = 0;
  1088.         double[] outBlock = out.blocks[outBlockIndex];
  1089.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1090.             final int iHeight = blockHeight(iBlock);
  1091.             final double[] block = blocks[iBlock * blockColumns + jBlock];
  1092.             for (int i = 0; i < iHeight; ++i) {
  1093.                 if (outIndex >= outBlock.length) {
  1094.                     outBlock = out.blocks[++outBlockIndex];
  1095.                     outIndex = 0;
  1096.                 }
  1097.                 outBlock[outIndex++] = block[i * jWidth + jColumn];
  1098.             }
  1099.         }

  1100.         return out;
  1101.     }

  1102.     /** {@inheritDoc} */
  1103.     @Override
  1104.     public void setColumnMatrix(final int column, final RealMatrix matrix)
  1105.         throws MathIllegalArgumentException {
  1106.         if (matrix instanceof BlockRealMatrix) {
  1107.             setColumnMatrix(column, (BlockRealMatrix) matrix);
  1108.         } else {
  1109.             super.setColumnMatrix(column, matrix);
  1110.         }
  1111.     }

  1112.     /**
  1113.      * Sets the entries in column number <code>column</code>
  1114.      * as a column matrix.  Column indices start at 0.
  1115.      *
  1116.      * @param column the column to be set
  1117.      * @param matrix column matrix (must have one column and the same number of rows
  1118.      * as the instance)
  1119.      * @throws MathIllegalArgumentException if the specified column index is invalid.
  1120.      * @throws MathIllegalArgumentException if the matrix dimensions do
  1121.      * not match one instance column.
  1122.      */
  1123.     void setColumnMatrix(final int column, final BlockRealMatrix matrix)
  1124.         throws MathIllegalArgumentException {
  1125.         MatrixUtils.checkColumnIndex(this, column);
  1126.         final int nRows = getRowDimension();
  1127.         if ((matrix.getRowDimension() != nRows) ||
  1128.             (matrix.getColumnDimension() != 1)) {
  1129.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH_2x2,
  1130.                                                    matrix.getRowDimension(), matrix.getColumnDimension(),
  1131.                                                    nRows, 1);
  1132.         }

  1133.         // perform copy block-wise, to ensure good cache behavior
  1134.         final int jBlock = column / BLOCK_SIZE;
  1135.         final int jColumn = column - jBlock * BLOCK_SIZE;
  1136.         final int jWidth = blockWidth(jBlock);
  1137.         int mBlockIndex = 0;
  1138.         int mIndex = 0;
  1139.         double[] mBlock = matrix.blocks[mBlockIndex];
  1140.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1141.             final int iHeight = blockHeight(iBlock);
  1142.             final double[] block = blocks[iBlock * blockColumns + jBlock];
  1143.             for (int i = 0; i < iHeight; ++i) {
  1144.                 if (mIndex >= mBlock.length) {
  1145.                     mBlock = matrix.blocks[++mBlockIndex];
  1146.                     mIndex = 0;
  1147.                 }
  1148.                 block[i * jWidth + jColumn] = mBlock[mIndex++];
  1149.             }
  1150.         }
  1151.     }

  1152.     /** {@inheritDoc} */
  1153.     @Override
  1154.     public RealVector getRowVector(final int row)
  1155.         throws MathIllegalArgumentException {
  1156.         MatrixUtils.checkRowIndex(this, row);
  1157.         final double[] outData = new double[columns];

  1158.         // perform copy block-wise, to ensure good cache behavior
  1159.         final int iBlock = row / BLOCK_SIZE;
  1160.         final int iRow = row - iBlock * BLOCK_SIZE;
  1161.         int outIndex = 0;
  1162.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1163.             final int jWidth = blockWidth(jBlock);
  1164.             final double[] block = blocks[iBlock * blockColumns + jBlock];
  1165.             System.arraycopy(block, iRow * jWidth, outData, outIndex, jWidth);
  1166.             outIndex += jWidth;
  1167.         }

  1168.         return new ArrayRealVector(outData, false);
  1169.     }

  1170.     /** {@inheritDoc} */
  1171.     @Override
  1172.     public void setRowVector(final int row, final RealVector vector)
  1173.         throws MathIllegalArgumentException {
  1174.         if (vector instanceof ArrayRealVector) {
  1175.             setRow(row, ((ArrayRealVector) vector).getDataRef());
  1176.         } else {
  1177.             super.setRowVector(row, vector);
  1178.         }
  1179.     }

  1180.     /** {@inheritDoc} */
  1181.     @Override
  1182.     public RealVector getColumnVector(final int column)
  1183.         throws MathIllegalArgumentException {
  1184.         MatrixUtils.checkColumnIndex(this, column);
  1185.         final double[] outData = new double[rows];

  1186.         // perform copy block-wise, to ensure good cache behavior
  1187.         final int jBlock = column / BLOCK_SIZE;
  1188.         final int jColumn = column - jBlock * BLOCK_SIZE;
  1189.         final int jWidth = blockWidth(jBlock);
  1190.         int outIndex = 0;
  1191.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1192.             final int iHeight = blockHeight(iBlock);
  1193.             final double[] block = blocks[iBlock * blockColumns + jBlock];
  1194.             for (int i = 0; i < iHeight; ++i) {
  1195.                 outData[outIndex++] = block[i * jWidth + jColumn];
  1196.             }
  1197.         }

  1198.         return new ArrayRealVector(outData, false);
  1199.     }

  1200.     /** {@inheritDoc} */
  1201.     @Override
  1202.     public void setColumnVector(final int column, final RealVector vector)
  1203.         throws MathIllegalArgumentException {
  1204.         if (vector instanceof ArrayRealVector) {
  1205.             setColumn(column, ((ArrayRealVector) vector).getDataRef());
  1206.         } else {
  1207.             super.setColumnVector(column, vector);
  1208.         }
  1209.     }

  1210.     /** {@inheritDoc} */
  1211.     @Override
  1212.     public double[] getRow(final int row) throws MathIllegalArgumentException {
  1213.         MatrixUtils.checkRowIndex(this, row);
  1214.         final double[] out = new double[columns];

  1215.         // perform copy block-wise, to ensure good cache behavior
  1216.         final int iBlock = row / BLOCK_SIZE;
  1217.         final int iRow = row - iBlock * BLOCK_SIZE;
  1218.         int outIndex = 0;
  1219.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1220.             final int jWidth     = blockWidth(jBlock);
  1221.             final double[] block = blocks[iBlock * blockColumns + jBlock];
  1222.             System.arraycopy(block, iRow * jWidth, out, outIndex, jWidth);
  1223.             outIndex += jWidth;
  1224.         }

  1225.         return out;
  1226.     }

  1227.     /** {@inheritDoc} */
  1228.     @Override
  1229.     public void setRow(final int row, final double[] array)
  1230.         throws MathIllegalArgumentException {
  1231.         MatrixUtils.checkRowIndex(this, row);
  1232.         final int nCols = getColumnDimension();
  1233.         if (array.length != nCols) {
  1234.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH_2x2,
  1235.                                                    1, array.length,
  1236.                                                    1, nCols);
  1237.         }

  1238.         // perform copy block-wise, to ensure good cache behavior
  1239.         final int iBlock = row / BLOCK_SIZE;
  1240.         final int iRow = row - iBlock * BLOCK_SIZE;
  1241.         int outIndex = 0;
  1242.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1243.             final int jWidth     = blockWidth(jBlock);
  1244.             final double[] block = blocks[iBlock * blockColumns + jBlock];
  1245.             System.arraycopy(array, outIndex, block, iRow * jWidth, jWidth);
  1246.             outIndex += jWidth;
  1247.         }
  1248.     }

  1249.     /** {@inheritDoc} */
  1250.     @Override
  1251.     public double[] getColumn(final int column) throws MathIllegalArgumentException {
  1252.         MatrixUtils.checkColumnIndex(this, column);
  1253.         final double[] out = new double[rows];

  1254.         // perform copy block-wise, to ensure good cache behavior
  1255.         final int jBlock  = column / BLOCK_SIZE;
  1256.         final int jColumn = column - jBlock * BLOCK_SIZE;
  1257.         final int jWidth  = blockWidth(jBlock);
  1258.         int outIndex = 0;
  1259.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1260.             final int iHeight = blockHeight(iBlock);
  1261.             final double[] block = blocks[iBlock * blockColumns + jBlock];
  1262.             for (int i = 0; i < iHeight; ++i) {
  1263.                 out[outIndex++] = block[i * jWidth + jColumn];
  1264.             }
  1265.         }

  1266.         return out;
  1267.     }

  1268.     /** {@inheritDoc} */
  1269.     @Override
  1270.     public void setColumn(final int column, final double[] array)
  1271.         throws MathIllegalArgumentException {
  1272.         MatrixUtils.checkColumnIndex(this, column);
  1273.         final int nRows = getRowDimension();
  1274.         if (array.length != nRows) {
  1275.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH_2x2,
  1276.                                                    array.length, 1,
  1277.                                                    nRows, 1);
  1278.         }

  1279.         // perform copy block-wise, to ensure good cache behavior
  1280.         final int jBlock  = column / BLOCK_SIZE;
  1281.         final int jColumn = column - jBlock * BLOCK_SIZE;
  1282.         final int jWidth = blockWidth(jBlock);
  1283.         int outIndex = 0;
  1284.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1285.             final int iHeight = blockHeight(iBlock);
  1286.             final double[] block = blocks[iBlock * blockColumns + jBlock];
  1287.             for (int i = 0; i < iHeight; ++i) {
  1288.                 block[i * jWidth + jColumn] = array[outIndex++];
  1289.             }
  1290.         }
  1291.     }

  1292.     /** {@inheritDoc} */
  1293.     @Override
  1294.     public double getEntry(final int row, final int column)
  1295.         throws MathIllegalArgumentException {
  1296.         MatrixUtils.checkMatrixIndex(this, row, column);
  1297.         final int iBlock = row / BLOCK_SIZE;
  1298.         final int jBlock = column / BLOCK_SIZE;
  1299.         final int k = (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
  1300.             (column - jBlock * BLOCK_SIZE);
  1301.         return blocks[iBlock * blockColumns + jBlock][k];
  1302.     }

  1303.     /** {@inheritDoc} */
  1304.     @Override
  1305.     public void setEntry(final int row, final int column, final double value)
  1306.         throws MathIllegalArgumentException {
  1307.         MatrixUtils.checkMatrixIndex(this, row, column);
  1308.         final int iBlock = row / BLOCK_SIZE;
  1309.         final int jBlock = column / BLOCK_SIZE;
  1310.         final int k = (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
  1311.             (column - jBlock * BLOCK_SIZE);
  1312.         blocks[iBlock * blockColumns + jBlock][k] = value;
  1313.     }

  1314.     /** {@inheritDoc} */
  1315.     @Override
  1316.     public void addToEntry(final int row, final int column,
  1317.                            final double increment)
  1318.         throws MathIllegalArgumentException {
  1319.         MatrixUtils.checkMatrixIndex(this, row, column);
  1320.         final int iBlock = row    / BLOCK_SIZE;
  1321.         final int jBlock = column / BLOCK_SIZE;
  1322.         final int k = (row    - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
  1323.             (column - jBlock * BLOCK_SIZE);
  1324.         blocks[iBlock * blockColumns + jBlock][k] += increment;
  1325.     }

  1326.     /** {@inheritDoc} */
  1327.     @Override
  1328.     public void multiplyEntry(final int row, final int column,
  1329.                               final double factor)
  1330.         throws MathIllegalArgumentException {
  1331.         MatrixUtils.checkMatrixIndex(this, row, column);
  1332.         final int iBlock = row / BLOCK_SIZE;
  1333.         final int jBlock = column / BLOCK_SIZE;
  1334.         final int k = (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
  1335.             (column - jBlock * BLOCK_SIZE);
  1336.         blocks[iBlock * blockColumns + jBlock][k] *= factor;
  1337.     }

  1338.     /** {@inheritDoc} */
  1339.     @Override
  1340.     public BlockRealMatrix transpose() {
  1341.         final int nRows = getRowDimension();
  1342.         final int nCols = getColumnDimension();
  1343.         final BlockRealMatrix out = new BlockRealMatrix(nCols, nRows);

  1344.         // perform transpose block-wise, to ensure good cache behavior
  1345.         int blockIndex = 0;
  1346.         for (int iBlock = 0; iBlock < blockColumns; ++iBlock) {
  1347.             final int pStart = iBlock * BLOCK_SIZE;
  1348.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, columns);
  1349.             for (int jBlock = 0; jBlock < blockRows; ++jBlock) {
  1350.                 // transpose current block
  1351.                 final double[] outBlock = out.blocks[blockIndex];
  1352.                 final double[] tBlock = blocks[jBlock * blockColumns + iBlock];
  1353.                 final int qStart = jBlock * BLOCK_SIZE;
  1354.                 final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, rows);
  1355.                 int k = 0;
  1356.                 for (int p = pStart; p < pEnd; ++p) {
  1357.                     final int lInc = pEnd - pStart;
  1358.                     int l = p - pStart;
  1359.                     for (int q = qStart; q < qEnd; ++q) {
  1360.                         outBlock[k] = tBlock[l];
  1361.                         ++k;
  1362.                         l+= lInc;
  1363.                     }
  1364.                 }
  1365.                 // go to next block
  1366.                 ++blockIndex;
  1367.             }
  1368.         }

  1369.         return out;
  1370.     }

  1371.     /** {@inheritDoc} */
  1372.     @Override
  1373.     public int getRowDimension() {
  1374.         return rows;
  1375.     }

  1376.     /** {@inheritDoc} */
  1377.     @Override
  1378.     public int getColumnDimension() {
  1379.         return columns;
  1380.     }

  1381.     /** {@inheritDoc} */
  1382.     @Override
  1383.     public double[] operate(final double[] v)
  1384.         throws MathIllegalArgumentException {
  1385.         if (v.length != columns) {
  1386.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  1387.                                                    v.length, columns);
  1388.         }
  1389.         final double[] out = new double[rows];

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

  1417.         return out;
  1418.     }

  1419.     /** {@inheritDoc} */
  1420.     @Override
  1421.     public double[] preMultiply(final double[] v)
  1422.         throws MathIllegalArgumentException {
  1423.         if (v.length != rows) {
  1424.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  1425.                                                    v.length, rows);
  1426.         }
  1427.         final double[] out = new double[columns];

  1428.         // perform multiplication block-wise, to ensure good cache behavior
  1429.         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1430.             final int jWidth  = blockWidth(jBlock);
  1431.             final int jWidth2 = jWidth  + jWidth;
  1432.             final int jWidth3 = jWidth2 + jWidth;
  1433.             final int jWidth4 = jWidth3 + jWidth;
  1434.             final int qStart = jBlock * BLOCK_SIZE;
  1435.             final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1436.             for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1437.                 final double[] block  = blocks[iBlock * blockColumns + jBlock];
  1438.                 final int pStart = iBlock * BLOCK_SIZE;
  1439.                 final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1440.                 for (int q = qStart; q < qEnd; ++q) {
  1441.                     int k = q - qStart;
  1442.                     double sum = 0;
  1443.                     int p = pStart;
  1444.                     while (p < pEnd - 3) {
  1445.                         sum += block[k]           * v[p]     +
  1446.                                block[k + jWidth]  * v[p + 1] +
  1447.                                block[k + jWidth2] * v[p + 2] +
  1448.                                block[k + jWidth3] * v[p + 3];
  1449.                         k += jWidth4;
  1450.                         p += 4;
  1451.                     }
  1452.                     while (p < pEnd) {
  1453.                         sum += block[k] * v[p++];
  1454.                         k += jWidth;
  1455.                     }
  1456.                     out[q] += sum;
  1457.                 }
  1458.             }
  1459.         }

  1460.         return out;
  1461.     }

  1462.     /** {@inheritDoc} */
  1463.     @Override
  1464.     public double walkInRowOrder(final RealMatrixChangingVisitor visitor) {
  1465.         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
  1466.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1467.             final int pStart = iBlock * BLOCK_SIZE;
  1468.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1469.             for (int p = pStart; p < pEnd; ++p) {
  1470.                 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1471.                     final int jWidth = blockWidth(jBlock);
  1472.                     final int qStart = jBlock * BLOCK_SIZE;
  1473.                     final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1474.                     final double[] block = blocks[iBlock * blockColumns + jBlock];
  1475.                     int k = (p - pStart) * jWidth;
  1476.                     for (int q = qStart; q < qEnd; ++q) {
  1477.                         block[k] = visitor.visit(p, q, block[k]);
  1478.                         ++k;
  1479.                     }
  1480.                 }
  1481.              }
  1482.         }
  1483.         return visitor.end();
  1484.     }

  1485.     /** {@inheritDoc} */
  1486.     @Override
  1487.     public double walkInRowOrder(final RealMatrixPreservingVisitor visitor) {
  1488.         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
  1489.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1490.             final int pStart = iBlock * BLOCK_SIZE;
  1491.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1492.             for (int p = pStart; p < pEnd; ++p) {
  1493.                 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1494.                     final int jWidth = blockWidth(jBlock);
  1495.                     final int qStart = jBlock * BLOCK_SIZE;
  1496.                     final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1497.                     final double[] block = blocks[iBlock * blockColumns + jBlock];
  1498.                     int k = (p - pStart) * jWidth;
  1499.                     for (int q = qStart; q < qEnd; ++q) {
  1500.                         visitor.visit(p, q, block[k]);
  1501.                         ++k;
  1502.                     }
  1503.                 }
  1504.              }
  1505.         }
  1506.         return visitor.end();
  1507.     }

  1508.     /** {@inheritDoc} */
  1509.     @Override
  1510.     public double walkInRowOrder(final RealMatrixChangingVisitor visitor,
  1511.                                  final int startRow, final int endRow,
  1512.                                  final int startColumn, final int endColumn)
  1513.         throws MathIllegalArgumentException {
  1514.         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
  1515.         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
  1516.         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
  1517.             final int p0     = iBlock * BLOCK_SIZE;
  1518.             final int pStart = FastMath.max(startRow, p0);
  1519.             final int pEnd   = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
  1520.             for (int p = pStart; p < pEnd; ++p) {
  1521.                 for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
  1522.                     final int jWidth = blockWidth(jBlock);
  1523.                     final int q0     = jBlock * BLOCK_SIZE;
  1524.                     final int qStart = FastMath.max(startColumn, q0);
  1525.                     final int qEnd   = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
  1526.                     final double[] block = blocks[iBlock * blockColumns + jBlock];
  1527.                     int k = (p - p0) * jWidth + qStart - q0;
  1528.                     for (int q = qStart; q < qEnd; ++q) {
  1529.                         block[k] = visitor.visit(p, q, block[k]);
  1530.                         ++k;
  1531.                     }
  1532.                 }
  1533.              }
  1534.         }
  1535.         return visitor.end();
  1536.     }

  1537.     /** {@inheritDoc} */
  1538.     @Override
  1539.     public double walkInRowOrder(final RealMatrixPreservingVisitor visitor,
  1540.                                  final int startRow, final int endRow,
  1541.                                  final int startColumn, final int endColumn)
  1542.         throws MathIllegalArgumentException {
  1543.         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
  1544.         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
  1545.         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
  1546.             final int p0     = iBlock * BLOCK_SIZE;
  1547.             final int pStart = FastMath.max(startRow, p0);
  1548.             final int pEnd   = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
  1549.             for (int p = pStart; p < pEnd; ++p) {
  1550.                 for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
  1551.                     final int jWidth = blockWidth(jBlock);
  1552.                     final int q0     = jBlock * BLOCK_SIZE;
  1553.                     final int qStart = FastMath.max(startColumn, q0);
  1554.                     final int qEnd   = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
  1555.                     final double[] block = blocks[iBlock * blockColumns + jBlock];
  1556.                     int k = (p - p0) * jWidth + qStart - q0;
  1557.                     for (int q = qStart; q < qEnd; ++q) {
  1558.                         visitor.visit(p, q, block[k]);
  1559.                         ++k;
  1560.                     }
  1561.                 }
  1562.              }
  1563.         }
  1564.         return visitor.end();
  1565.     }

  1566.     /** {@inheritDoc} */
  1567.     @Override
  1568.     public double walkInOptimizedOrder(final RealMatrixChangingVisitor visitor) {
  1569.         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
  1570.         int blockIndex = 0;
  1571.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1572.             final int pStart = iBlock * BLOCK_SIZE;
  1573.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1574.             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1575.                 final int qStart = jBlock * BLOCK_SIZE;
  1576.                 final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1577.                 final double[] block = blocks[blockIndex];
  1578.                 int k = 0;
  1579.                 for (int p = pStart; p < pEnd; ++p) {
  1580.                     for (int q = qStart; q < qEnd; ++q) {
  1581.                         block[k] = visitor.visit(p, q, block[k]);
  1582.                         ++k;
  1583.                     }
  1584.                 }
  1585.                 ++blockIndex;
  1586.             }
  1587.         }
  1588.         return visitor.end();
  1589.     }

  1590.     /** {@inheritDoc} */
  1591.     @Override
  1592.     public double walkInOptimizedOrder(final RealMatrixPreservingVisitor visitor) {
  1593.         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
  1594.         int blockIndex = 0;
  1595.         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
  1596.             final int pStart = iBlock * BLOCK_SIZE;
  1597.             final int pEnd   = FastMath.min(pStart + BLOCK_SIZE, rows);
  1598.             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
  1599.                 final int qStart = jBlock * BLOCK_SIZE;
  1600.                 final int qEnd   = FastMath.min(qStart + BLOCK_SIZE, columns);
  1601.                 final double[] block = blocks[blockIndex];
  1602.                 int k = 0;
  1603.                 for (int p = pStart; p < pEnd; ++p) {
  1604.                     for (int q = qStart; q < qEnd; ++q) {
  1605.                         visitor.visit(p, q, block[k]);
  1606.                         ++k;
  1607.                     }
  1608.                 }
  1609.                 ++blockIndex;
  1610.             }
  1611.         }
  1612.         return visitor.end();
  1613.     }

  1614.     /** {@inheritDoc} */
  1615.     @Override
  1616.     public double walkInOptimizedOrder(final RealMatrixChangingVisitor visitor,
  1617.                                        final int startRow, final int endRow,
  1618.                                        final int startColumn,
  1619.                                        final int endColumn)
  1620.         throws MathIllegalArgumentException {
  1621.         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
  1622.         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
  1623.         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
  1624.             final int p0     = iBlock * BLOCK_SIZE;
  1625.             final int pStart = FastMath.max(startRow, p0);
  1626.             final int pEnd   = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
  1627.             for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
  1628.                 final int jWidth = blockWidth(jBlock);
  1629.                 final int q0     = jBlock * BLOCK_SIZE;
  1630.                 final int qStart = FastMath.max(startColumn, q0);
  1631.                 final int qEnd   = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
  1632.                 final double[] block = blocks[iBlock * blockColumns + jBlock];
  1633.                 for (int p = pStart; p < pEnd; ++p) {
  1634.                     int k = (p - p0) * jWidth + qStart - q0;
  1635.                     for (int q = qStart; q < qEnd; ++q) {
  1636.                         block[k] = visitor.visit(p, q, block[k]);
  1637.                         ++k;
  1638.                     }
  1639.                 }
  1640.             }
  1641.         }
  1642.         return visitor.end();
  1643.     }

  1644.     /** {@inheritDoc} */
  1645.     @Override
  1646.     public double walkInOptimizedOrder(final RealMatrixPreservingVisitor visitor,
  1647.                                        final int startRow, final int endRow,
  1648.                                        final int startColumn,
  1649.                                        final int endColumn)
  1650.         throws MathIllegalArgumentException {
  1651.         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
  1652.         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
  1653.         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
  1654.             final int p0     = iBlock * BLOCK_SIZE;
  1655.             final int pStart = FastMath.max(startRow, p0);
  1656.             final int pEnd   = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
  1657.             for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
  1658.                 final int jWidth = blockWidth(jBlock);
  1659.                 final int q0     = jBlock * BLOCK_SIZE;
  1660.                 final int qStart = FastMath.max(startColumn, q0);
  1661.                 final int qEnd   = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
  1662.                 final double[] block = blocks[iBlock * blockColumns + jBlock];
  1663.                 for (int p = pStart; p < pEnd; ++p) {
  1664.                     int k = (p - p0) * jWidth + qStart - q0;
  1665.                     for (int q = qStart; q < qEnd; ++q) {
  1666.                         visitor.visit(p, q, block[k]);
  1667.                         ++k;
  1668.                     }
  1669.                 }
  1670.             }
  1671.         }
  1672.         return visitor.end();
  1673.     }

  1674.     /**
  1675.      * Get the height of a block.
  1676.      * @param blockRow row index (in block sense) of the block
  1677.      * @return height (number of rows) of the block
  1678.      */
  1679.     private int blockHeight(final int blockRow) {
  1680.         return (blockRow == blockRows - 1) ? rows - blockRow * BLOCK_SIZE : BLOCK_SIZE;
  1681.     }

  1682.     /**
  1683.      * Get the width of a block.
  1684.      * @param blockColumn column index (in block sense) of the block
  1685.      * @return width (number of columns) of the block
  1686.      */
  1687.     private int blockWidth(final int blockColumn) {
  1688.         return (blockColumn == blockColumns - 1) ? columns - blockColumn * BLOCK_SIZE : BLOCK_SIZE;
  1689.     }

  1690. }