1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  package org.apache.commons.math4.legacy.linear;
19  
20  import java.io.Serializable;
21  
22  import org.apache.commons.math4.legacy.core.Field;
23  import org.apache.commons.math4.legacy.core.FieldElement;
24  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
25  import org.apache.commons.math4.legacy.exception.NoDataException;
26  import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
27  import org.apache.commons.math4.legacy.exception.NullArgumentException;
28  import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
29  import org.apache.commons.math4.legacy.exception.OutOfRangeException;
30  import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
31  import org.apache.commons.math4.core.jdkmath.JdkMath;
32  import org.apache.commons.math4.legacy.core.MathArrays;
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  public class BlockFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T> implements Serializable {
74      
75      public static final int BLOCK_SIZE = 36;
76      
77      private static final long serialVersionUID = -4602336630143123183L;
78      
79      private final T[][] blocks;
80      
81      private final int rows;
82      
83      private final int columns;
84      
85      private final int blockRows;
86      
87      private final int blockColumns;
88  
89      
90  
91  
92  
93  
94  
95  
96  
97  
98      public BlockFieldMatrix(final Field<T> field, final int rows,
99                              final int columns)
100         throws NotStrictlyPositiveException {
101         super(field, rows, columns);
102         this.rows    = rows;
103         this.columns = columns;
104 
105         
106         blockRows    = (rows    + BLOCK_SIZE - 1) / BLOCK_SIZE;
107         blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
108 
109         
110         blocks = createBlocksLayout(field, rows, columns);
111     }
112 
113     
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125     public BlockFieldMatrix(final T[][] rawData)
126         throws DimensionMismatchException {
127         this(rawData.length, rawData[0].length, toBlocksLayout(rawData), false);
128     }
129 
130     
131 
132 
133 
134 
135 
136 
137 
138 
139 
140 
141 
142 
143 
144 
145 
146 
147     public BlockFieldMatrix(final int rows, final int columns,
148                             final T[][] blockData, final boolean copyArray)
149         throws DimensionMismatchException, NotStrictlyPositiveException {
150         super(extractField(blockData), rows, columns);
151         this.rows    = rows;
152         this.columns = columns;
153 
154         
155         blockRows    = (rows    + BLOCK_SIZE - 1) / BLOCK_SIZE;
156         blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
157 
158         if (copyArray) {
159             
160             blocks = MathArrays.buildArray(getField(), blockRows * blockColumns, -1);
161         } else {
162             
163             blocks = blockData;
164         }
165 
166         int index = 0;
167         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
168             final int iHeight = blockHeight(iBlock);
169             for (int jBlock = 0; jBlock < blockColumns; ++jBlock, ++index) {
170                 if (blockData[index].length != iHeight * blockWidth(jBlock)) {
171                     throw new DimensionMismatchException(blockData[index].length,
172                                                          iHeight * blockWidth(jBlock));
173                 }
174                 if (copyArray) {
175                     blocks[index] = blockData[index].clone();
176                 }
177             }
178         }
179     }
180 
181     
182 
183 
184 
185 
186 
187 
188 
189 
190 
191 
192 
193 
194 
195 
196 
197 
198 
199 
200 
201 
202 
203 
204 
205     public static <T extends FieldElement<T>> T[][] toBlocksLayout(final T[][] rawData)
206         throws DimensionMismatchException {
207 
208         final int rows         = rawData.length;
209         final int columns      = rawData[0].length;
210         final int blockRows    = (rows    + BLOCK_SIZE - 1) / BLOCK_SIZE;
211         final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
212 
213         
214         for (int i = 0; i < rawData.length; ++i) {
215             final int length = rawData[i].length;
216             if (length != columns) {
217                 throw new DimensionMismatchException(columns, length);
218             }
219         }
220 
221         
222         final Field<T> field = extractField(rawData);
223         final T[][] blocks = MathArrays.buildArray(field, blockRows * blockColumns, -1);
224         int blockIndex = 0;
225         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
226             final int pStart  = iBlock * BLOCK_SIZE;
227             final int pEnd    = JdkMath.min(pStart + BLOCK_SIZE, rows);
228             final int iHeight = pEnd - pStart;
229             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
230                 final int qStart = jBlock * BLOCK_SIZE;
231                 final int qEnd   = JdkMath.min(qStart + BLOCK_SIZE, columns);
232                 final int jWidth = qEnd - qStart;
233 
234                 
235                 final T[] block = MathArrays.buildArray(field, iHeight * jWidth);
236                 blocks[blockIndex] = block;
237 
238                 
239                 int index = 0;
240                 for (int p = pStart; p < pEnd; ++p) {
241                     System.arraycopy(rawData[p], qStart, block, index, jWidth);
242                     index += jWidth;
243                 }
244 
245                 ++blockIndex;
246             }
247         }
248 
249         return blocks;
250     }
251 
252     
253 
254 
255 
256 
257 
258 
259 
260 
261 
262 
263 
264 
265 
266 
267     public static <T extends FieldElement<T>> T[][] createBlocksLayout(final Field<T> field,
268                                                                        final int rows, final int columns) {
269         final int blockRows    = (rows    + BLOCK_SIZE - 1) / BLOCK_SIZE;
270         final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE;
271 
272         final T[][] blocks = MathArrays.buildArray(field, blockRows * blockColumns, -1);
273         int blockIndex = 0;
274         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
275             final int pStart  = iBlock * BLOCK_SIZE;
276             final int pEnd    = JdkMath.min(pStart + BLOCK_SIZE, rows);
277             final int iHeight = pEnd - pStart;
278             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
279                 final int qStart = jBlock * BLOCK_SIZE;
280                 final int qEnd   = JdkMath.min(qStart + BLOCK_SIZE, columns);
281                 final int jWidth = qEnd - qStart;
282                 blocks[blockIndex] = MathArrays.buildArray(field, iHeight * jWidth);
283                 ++blockIndex;
284             }
285         }
286 
287         return blocks;
288     }
289 
290     
291     @Override
292     public FieldMatrix<T> createMatrix(final int rowDimension,
293                                        final int columnDimension)
294         throws NotStrictlyPositiveException {
295         return new BlockFieldMatrix<>(getField(), rowDimension,
296                                        columnDimension);
297     }
298 
299     
300     @Override
301     public FieldMatrix<T> copy() {
302 
303         
304         BlockFieldMatrix<T> copied = new BlockFieldMatrix<>(getField(), rows, columns);
305 
306         
307         for (int i = 0; i < blocks.length; ++i) {
308             System.arraycopy(blocks[i], 0, copied.blocks[i], 0, blocks[i].length);
309         }
310 
311         return copied;
312     }
313 
314     
315     @Override
316     public FieldMatrix<T> add(final FieldMatrix<T> m)
317         throws MatrixDimensionMismatchException {
318         if (m instanceof BlockFieldMatrix) {
319             return add((BlockFieldMatrix<T>) m);
320         }
321 
322         
323         checkAdd(m);
324 
325         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);
326 
327         
328         int blockIndex = 0;
329         for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
330             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
331 
332                 
333                 final T[] outBlock = out.blocks[blockIndex];
334                 final T[] tBlock   = blocks[blockIndex];
335                 final int      pStart   = iBlock * BLOCK_SIZE;
336                 final int      pEnd     = JdkMath.min(pStart + BLOCK_SIZE, rows);
337                 final int      qStart   = jBlock * BLOCK_SIZE;
338                 final int      qEnd     = JdkMath.min(qStart + BLOCK_SIZE, columns);
339                 int k = 0;
340                 for (int p = pStart; p < pEnd; ++p) {
341                     for (int q = qStart; q < qEnd; ++q) {
342                         outBlock[k] = tBlock[k].add(m.getEntry(p, q));
343                         ++k;
344                     }
345                 }
346 
347                 
348                 ++blockIndex;
349             }
350         }
351 
352         return out;
353     }
354 
355     
356 
357 
358 
359 
360 
361 
362 
363     public BlockFieldMatrix<T> add(final BlockFieldMatrix<T> m)
364         throws MatrixDimensionMismatchException {
365 
366         
367         checkAdd(m);
368 
369         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);
370 
371         
372         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
373             final T[] outBlock = out.blocks[blockIndex];
374             final T[] tBlock   = blocks[blockIndex];
375             final T[] mBlock   = m.blocks[blockIndex];
376             for (int k = 0; k < outBlock.length; ++k) {
377                 outBlock[k] = tBlock[k].add(mBlock[k]);
378             }
379         }
380 
381         return out;
382     }
383 
384     
385     @Override
386     public FieldMatrix<T> subtract(final FieldMatrix<T> m)
387         throws MatrixDimensionMismatchException {
388         if (m instanceof BlockFieldMatrix) {
389             return subtract((BlockFieldMatrix<T>) m);
390         }
391 
392         
393         checkAdd(m);
394 
395         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);
396 
397         
398         int blockIndex = 0;
399         for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
400             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
401 
402                 
403                 final T[] outBlock = out.blocks[blockIndex];
404                 final T[] tBlock   = blocks[blockIndex];
405                 final int      pStart   = iBlock * BLOCK_SIZE;
406                 final int      pEnd     = JdkMath.min(pStart + BLOCK_SIZE, rows);
407                 final int      qStart   = jBlock * BLOCK_SIZE;
408                 final int      qEnd     = JdkMath.min(qStart + BLOCK_SIZE, columns);
409                 int k = 0;
410                 for (int p = pStart; p < pEnd; ++p) {
411                     for (int q = qStart; q < qEnd; ++q) {
412                         outBlock[k] = tBlock[k].subtract(m.getEntry(p, q));
413                         ++k;
414                     }
415                 }
416 
417                 
418                 ++blockIndex;
419             }
420         }
421 
422         return out;
423     }
424 
425     
426 
427 
428 
429 
430 
431 
432 
433     public BlockFieldMatrix<T> subtract(final BlockFieldMatrix<T> m) throws MatrixDimensionMismatchException {
434         
435         checkAdd(m);
436 
437         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);
438 
439         
440         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
441             final T[] outBlock = out.blocks[blockIndex];
442             final T[] tBlock   = blocks[blockIndex];
443             final T[] mBlock   = m.blocks[blockIndex];
444             for (int k = 0; k < outBlock.length; ++k) {
445                 outBlock[k] = tBlock[k].subtract(mBlock[k]);
446             }
447         }
448 
449         return out;
450     }
451 
452     
453     @Override
454     public FieldMatrix<T> scalarAdd(final T d) {
455         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);
456 
457         
458         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
459             final T[] outBlock = out.blocks[blockIndex];
460             final T[] tBlock   = blocks[blockIndex];
461             for (int k = 0; k < outBlock.length; ++k) {
462                 outBlock[k] = tBlock[k].add(d);
463             }
464         }
465 
466         return out;
467     }
468 
469     
470     @Override
471     public FieldMatrix<T> scalarMultiply(final T d) {
472 
473         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, columns);
474 
475         
476         for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) {
477             final T[] outBlock = out.blocks[blockIndex];
478             final T[] tBlock   = blocks[blockIndex];
479             for (int k = 0; k < outBlock.length; ++k) {
480                 outBlock[k] = tBlock[k].multiply(d);
481             }
482         }
483 
484         return out;
485     }
486 
487     
488     @Override
489     public FieldMatrix<T> multiply(final FieldMatrix<T> m)
490         throws DimensionMismatchException {
491         if (m instanceof BlockFieldMatrix) {
492             return multiply((BlockFieldMatrix<T>) m);
493         }
494 
495         
496         checkMultiply(m);
497 
498         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, m.getColumnDimension());
499         final T zero = getField().getZero();
500 
501         
502         int blockIndex = 0;
503         for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
504 
505             final int pStart = iBlock * BLOCK_SIZE;
506             final int pEnd   = JdkMath.min(pStart + BLOCK_SIZE, rows);
507 
508             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
509 
510                 final int qStart = jBlock * BLOCK_SIZE;
511                 final int qEnd   = JdkMath.min(qStart + BLOCK_SIZE, m.getColumnDimension());
512 
513                 
514                 final T[] outBlock = out.blocks[blockIndex];
515 
516                 
517                 for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
518                     final int kWidth      = blockWidth(kBlock);
519                     final T[] tBlock = blocks[iBlock * blockColumns + kBlock];
520                     final int rStart      = kBlock * BLOCK_SIZE;
521                     int k = 0;
522                     for (int p = pStart; p < pEnd; ++p) {
523                         final int lStart = (p - pStart) * kWidth;
524                         final int lEnd   = lStart + kWidth;
525                         for (int q = qStart; q < qEnd; ++q) {
526                             T sum = zero;
527                             int r = rStart;
528                             for (int l = lStart; l < lEnd; ++l) {
529                                 sum = sum.add(tBlock[l].multiply(m.getEntry(r, q)));
530                                 ++r;
531                             }
532                             outBlock[k] = outBlock[k].add(sum);
533                             ++k;
534                         }
535                     }
536                 }
537 
538                 
539                 ++blockIndex;
540             }
541         }
542 
543         return out;
544     }
545 
546     
547 
548 
549 
550 
551 
552 
553     public BlockFieldMatrix<T> multiply(BlockFieldMatrix<T> m)
554         throws DimensionMismatchException {
555 
556         
557         checkMultiply(m);
558 
559         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, m.columns);
560         final T zero = getField().getZero();
561 
562         
563         int blockIndex = 0;
564         for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
565 
566             final int pStart = iBlock * BLOCK_SIZE;
567             final int pEnd   = JdkMath.min(pStart + BLOCK_SIZE, rows);
568 
569             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
570                 final int jWidth = out.blockWidth(jBlock);
571                 final int jWidth2 = jWidth  + jWidth;
572                 final int jWidth3 = jWidth2 + jWidth;
573                 final int jWidth4 = jWidth3 + jWidth;
574 
575                 
576                 final T[] outBlock = out.blocks[blockIndex];
577 
578                 
579                 for (int kBlock = 0; kBlock < blockColumns; ++kBlock) {
580                     final int kWidth = blockWidth(kBlock);
581                     final T[] tBlock = blocks[iBlock * blockColumns + kBlock];
582                     final T[] mBlock = m.blocks[kBlock * m.blockColumns + jBlock];
583                     int k = 0;
584                     for (int p = pStart; p < pEnd; ++p) {
585                         final int lStart = (p - pStart) * kWidth;
586                         final int lEnd   = lStart + kWidth;
587                         for (int nStart = 0; nStart < jWidth; ++nStart) {
588                             T sum = zero;
589                             int l = lStart;
590                             int n = nStart;
591                             while (l < lEnd - 3) {
592                                 sum = sum.
593                                       add(tBlock[l].multiply(mBlock[n])).
594                                       add(tBlock[l + 1].multiply(mBlock[n + jWidth])).
595                                       add(tBlock[l + 2].multiply(mBlock[n + jWidth2])).
596                                       add(tBlock[l + 3].multiply(mBlock[n + jWidth3]));
597                                 l += 4;
598                                 n += jWidth4;
599                             }
600                             while (l < lEnd) {
601                                 sum = sum.add(tBlock[l++].multiply(mBlock[n]));
602                                 n += jWidth;
603                             }
604                             outBlock[k] = outBlock[k].add(sum);
605                             ++k;
606                         }
607                     }
608                 }
609 
610                 
611                 ++blockIndex;
612             }
613         }
614 
615         return out;
616     }
617 
618     
619     @Override
620     public T[][] getData() {
621 
622         final T[][] data = MathArrays.buildArray(getField(), getRowDimension(), getColumnDimension());
623         final int lastColumns = columns - (blockColumns - 1) * BLOCK_SIZE;
624 
625         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
626             final int pStart = iBlock * BLOCK_SIZE;
627             final int pEnd   = JdkMath.min(pStart + BLOCK_SIZE, rows);
628             int regularPos   = 0;
629             int lastPos      = 0;
630             for (int p = pStart; p < pEnd; ++p) {
631                 final T[] dataP = data[p];
632                 int blockIndex = iBlock * blockColumns;
633                 int dataPos    = 0;
634                 for (int jBlock = 0; jBlock < blockColumns - 1; ++jBlock) {
635                     System.arraycopy(blocks[blockIndex++], regularPos, dataP, dataPos, BLOCK_SIZE);
636                     dataPos += BLOCK_SIZE;
637                 }
638                 System.arraycopy(blocks[blockIndex], lastPos, dataP, dataPos, lastColumns);
639                 regularPos += BLOCK_SIZE;
640                 lastPos    += lastColumns;
641             }
642         }
643 
644         return data;
645     }
646 
647     
648     @Override
649     public FieldMatrix<T> getSubMatrix(final int startRow, final int endRow,
650                                        final int startColumn,
651                                        final int endColumn)
652         throws OutOfRangeException, NumberIsTooSmallException {
653         
654         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
655 
656         
657         final BlockFieldMatrix<T> out =
658             new BlockFieldMatrix<>(getField(), endRow - startRow + 1, endColumn - startColumn + 1);
659 
660         
661         final int blockStartRow    = startRow    / BLOCK_SIZE;
662         final int rowsShift        = startRow    % BLOCK_SIZE;
663         final int blockStartColumn = startColumn / BLOCK_SIZE;
664         final int columnsShift     = startColumn % BLOCK_SIZE;
665 
666         
667         int pBlock = blockStartRow;
668         for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) {
669             final int iHeight = out.blockHeight(iBlock);
670             int qBlock = blockStartColumn;
671             for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) {
672                 final int jWidth = out.blockWidth(jBlock);
673 
674                 
675                 final int      outIndex = iBlock * out.blockColumns + jBlock;
676                 final T[] outBlock = out.blocks[outIndex];
677                 final int      index    = pBlock * blockColumns + qBlock;
678                 final int      width    = blockWidth(qBlock);
679 
680                 final int heightExcess = iHeight + rowsShift - BLOCK_SIZE;
681                 final int widthExcess  = jWidth + columnsShift - BLOCK_SIZE;
682                 if (heightExcess > 0) {
683                     
684                     if (widthExcess > 0) {
685                         
686                         final int width2 = blockWidth(qBlock + 1);
687                         copyBlockPart(blocks[index], width,
688                                       rowsShift, BLOCK_SIZE,
689                                       columnsShift, BLOCK_SIZE,
690                                       outBlock, jWidth, 0, 0);
691                         copyBlockPart(blocks[index + 1], width2,
692                                       rowsShift, BLOCK_SIZE,
693                                       0, widthExcess,
694                                       outBlock, jWidth, 0, jWidth - widthExcess);
695                         copyBlockPart(blocks[index + blockColumns], width,
696                                       0, heightExcess,
697                                       columnsShift, BLOCK_SIZE,
698                                       outBlock, jWidth, iHeight - heightExcess, 0);
699                         copyBlockPart(blocks[index + blockColumns + 1], width2,
700                                       0, heightExcess,
701                                       0, widthExcess,
702                                       outBlock, jWidth, iHeight - heightExcess, jWidth - widthExcess);
703                     } else {
704                         
705                         copyBlockPart(blocks[index], width,
706                                       rowsShift, BLOCK_SIZE,
707                                       columnsShift, jWidth + columnsShift,
708                                       outBlock, jWidth, 0, 0);
709                         copyBlockPart(blocks[index + blockColumns], width,
710                                       0, heightExcess,
711                                       columnsShift, jWidth + columnsShift,
712                                       outBlock, jWidth, iHeight - heightExcess, 0);
713                     }
714                 } else {
715                     
716                     if (widthExcess > 0) {
717                         
718                         final int width2 = blockWidth(qBlock + 1);
719                         copyBlockPart(blocks[index], width,
720                                       rowsShift, iHeight + rowsShift,
721                                       columnsShift, BLOCK_SIZE,
722                                       outBlock, jWidth, 0, 0);
723                         copyBlockPart(blocks[index + 1], width2,
724                                       rowsShift, iHeight + rowsShift,
725                                       0, widthExcess,
726                                       outBlock, jWidth, 0, jWidth - widthExcess);
727                     } else {
728                         
729                         copyBlockPart(blocks[index], width,
730                                       rowsShift, iHeight + rowsShift,
731                                       columnsShift, jWidth + columnsShift,
732                                       outBlock, jWidth, 0, 0);
733                     }
734                }
735                 ++qBlock;
736             }
737             ++pBlock;
738         }
739 
740         return out;
741     }
742 
743     
744 
745 
746 
747 
748 
749 
750 
751 
752 
753 
754 
755 
756 
757 
758     private void copyBlockPart(final T[] srcBlock, final int srcWidth,
759                                final int srcStartRow, final int srcEndRow,
760                                final int srcStartColumn, final int srcEndColumn,
761                                final T[] dstBlock, final int dstWidth,
762                                final int dstStartRow, final int dstStartColumn) {
763         final int length = srcEndColumn - srcStartColumn;
764         int srcPos = srcStartRow * srcWidth + srcStartColumn;
765         int dstPos = dstStartRow * dstWidth + dstStartColumn;
766         for (int srcRow = srcStartRow; srcRow < srcEndRow; ++srcRow) {
767             System.arraycopy(srcBlock, srcPos, dstBlock, dstPos, length);
768             srcPos += srcWidth;
769             dstPos += dstWidth;
770         }
771     }
772 
773     
774     @Override
775     public void setSubMatrix(final T[][] subMatrix, final int row,
776                              final int column)
777         throws DimensionMismatchException, OutOfRangeException,
778         NoDataException, NullArgumentException {
779         
780         NullArgumentException.check(subMatrix);
781         final int refLength = subMatrix[0].length;
782         if (refLength == 0) {
783             throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
784         }
785         final int endRow    = row + subMatrix.length - 1;
786         final int endColumn = column + refLength - 1;
787         checkSubMatrixIndex(row, endRow, column, endColumn);
788         for (final T[] subRow : subMatrix) {
789             if (subRow.length != refLength) {
790                 throw new DimensionMismatchException(refLength, subRow.length);
791             }
792         }
793 
794         
795         final int blockStartRow    = row / BLOCK_SIZE;
796         final int blockEndRow      = (endRow + BLOCK_SIZE) / BLOCK_SIZE;
797         final int blockStartColumn = column / BLOCK_SIZE;
798         final int blockEndColumn   = (endColumn + BLOCK_SIZE) / BLOCK_SIZE;
799 
800         
801         for (int iBlock = blockStartRow; iBlock < blockEndRow; ++iBlock) {
802             final int iHeight  = blockHeight(iBlock);
803             final int firstRow = iBlock * BLOCK_SIZE;
804             final int iStart   = JdkMath.max(row,    firstRow);
805             final int iEnd     = JdkMath.min(endRow + 1, firstRow + iHeight);
806 
807             for (int jBlock = blockStartColumn; jBlock < blockEndColumn; ++jBlock) {
808                 final int jWidth      = blockWidth(jBlock);
809                 final int firstColumn = jBlock * BLOCK_SIZE;
810                 final int jStart      = JdkMath.max(column,    firstColumn);
811                 final int jEnd        = JdkMath.min(endColumn + 1, firstColumn + jWidth);
812                 final int jLength     = jEnd - jStart;
813 
814                 
815                 final T[] block = blocks[iBlock * blockColumns + jBlock];
816                 for (int i = iStart; i < iEnd; ++i) {
817                     System.arraycopy(subMatrix[i - row], jStart - column,
818                                      block, (i - firstRow) * jWidth + (jStart - firstColumn),
819                                      jLength);
820                 }
821             }
822         }
823     }
824 
825     
826     @Override
827     public FieldMatrix<T> getRowMatrix(final int row)
828         throws OutOfRangeException {
829         checkRowIndex(row);
830         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), 1, columns);
831 
832         
833         final int iBlock  = row / BLOCK_SIZE;
834         final int iRow    = row - iBlock * BLOCK_SIZE;
835         int outBlockIndex = 0;
836         int outIndex      = 0;
837         T[] outBlock = out.blocks[outBlockIndex];
838         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
839             final int jWidth     = blockWidth(jBlock);
840             final T[] block = blocks[iBlock * blockColumns + jBlock];
841             final int available  = outBlock.length - outIndex;
842             if (jWidth > available) {
843                 System.arraycopy(block, iRow * jWidth, outBlock, outIndex, available);
844                 outBlock = out.blocks[++outBlockIndex];
845                 System.arraycopy(block, iRow * jWidth, outBlock, 0, jWidth - available);
846                 outIndex = jWidth - available;
847             } else {
848                 System.arraycopy(block, iRow * jWidth, outBlock, outIndex, jWidth);
849                 outIndex += jWidth;
850             }
851         }
852 
853         return out;
854     }
855 
856     
857     @Override
858     public void setRowMatrix(final int row, final FieldMatrix<T> matrix)
859         throws MatrixDimensionMismatchException, OutOfRangeException {
860         if (matrix instanceof BlockFieldMatrix) {
861             setRowMatrix(row, (BlockFieldMatrix<T>) matrix);
862         } else {
863             super.setRowMatrix(row, matrix);
864         }
865     }
866 
867     
868 
869 
870 
871 
872 
873 
874 
875 
876 
877 
878     public void setRowMatrix(final int row, final BlockFieldMatrix<T> matrix)
879         throws MatrixDimensionMismatchException, OutOfRangeException {
880         checkRowIndex(row);
881         final int nCols = getColumnDimension();
882         if (matrix.getRowDimension() != 1 ||
883             matrix.getColumnDimension() != nCols) {
884             throw new MatrixDimensionMismatchException(matrix.getRowDimension(),
885                                                        matrix.getColumnDimension(),
886                                                        1, nCols);
887         }
888 
889         
890         final int iBlock = row / BLOCK_SIZE;
891         final int iRow   = row - iBlock * BLOCK_SIZE;
892         int mBlockIndex  = 0;
893         int mIndex       = 0;
894         T[] mBlock  = matrix.blocks[mBlockIndex];
895         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
896             final int jWidth     = blockWidth(jBlock);
897             final T[] block = blocks[iBlock * blockColumns + jBlock];
898             final int available  = mBlock.length - mIndex;
899             if (jWidth > available) {
900                 System.arraycopy(mBlock, mIndex, block, iRow * jWidth, available);
901                 mBlock = matrix.blocks[++mBlockIndex];
902                 System.arraycopy(mBlock, 0, block, iRow * jWidth, jWidth - available);
903                 mIndex = jWidth - available;
904             } else {
905                 System.arraycopy(mBlock, mIndex, block, iRow * jWidth, jWidth);
906                 mIndex += jWidth;
907            }
908         }
909     }
910 
911     
912     @Override
913     public FieldMatrix<T> getColumnMatrix(final int column)
914         throws OutOfRangeException {
915         checkColumnIndex(column);
916         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), rows, 1);
917 
918         
919         final int jBlock  = column / BLOCK_SIZE;
920         final int jColumn = column - jBlock * BLOCK_SIZE;
921         final int jWidth  = blockWidth(jBlock);
922         int outBlockIndex = 0;
923         int outIndex      = 0;
924         T[] outBlock = out.blocks[outBlockIndex];
925         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
926             final int iHeight = blockHeight(iBlock);
927             final T[] block = blocks[iBlock * blockColumns + jBlock];
928             for (int i = 0; i < iHeight; ++i) {
929                 if (outIndex >= outBlock.length) {
930                     outBlock = out.blocks[++outBlockIndex];
931                     outIndex = 0;
932                 }
933                 outBlock[outIndex++] = block[i * jWidth + jColumn];
934             }
935         }
936 
937         return out;
938     }
939 
940     
941     @Override
942     public void setColumnMatrix(final int column, final FieldMatrix<T> matrix)
943         throws MatrixDimensionMismatchException, OutOfRangeException {
944         if (matrix instanceof BlockFieldMatrix) {
945             setColumnMatrix(column, (BlockFieldMatrix<T>) matrix);
946         } else {
947             super.setColumnMatrix(column, matrix);
948         }
949     }
950 
951     
952 
953 
954 
955 
956 
957 
958 
959 
960 
961 
962     void setColumnMatrix(final int column, final BlockFieldMatrix<T> matrix)
963         throws MatrixDimensionMismatchException, OutOfRangeException {
964         checkColumnIndex(column);
965         final int nRows = getRowDimension();
966         if (matrix.getRowDimension() != nRows ||
967             matrix.getColumnDimension() != 1) {
968             throw new MatrixDimensionMismatchException(matrix.getRowDimension(),
969                                                        matrix.getColumnDimension(),
970                                                        nRows, 1);
971         }
972 
973         
974         final int jBlock  = column / BLOCK_SIZE;
975         final int jColumn = column - jBlock * BLOCK_SIZE;
976         final int jWidth  = blockWidth(jBlock);
977         int mBlockIndex = 0;
978         int mIndex      = 0;
979         T[] mBlock = matrix.blocks[mBlockIndex];
980         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
981             final int iHeight = blockHeight(iBlock);
982             final T[] block = blocks[iBlock * blockColumns + jBlock];
983             for (int i = 0; i < iHeight; ++i) {
984                 if (mIndex >= mBlock.length) {
985                     mBlock = matrix.blocks[++mBlockIndex];
986                     mIndex = 0;
987                 }
988                 block[i * jWidth + jColumn] = mBlock[mIndex++];
989             }
990         }
991     }
992 
993     
994     @Override
995     public FieldVector<T> getRowVector(final int row)
996         throws OutOfRangeException {
997         checkRowIndex(row);
998         final T[] outData = MathArrays.buildArray(getField(), columns);
999 
1000         
1001         final int iBlock  = row / BLOCK_SIZE;
1002         final int iRow    = row - iBlock * BLOCK_SIZE;
1003         int outIndex      = 0;
1004         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
1005             final int jWidth     = blockWidth(jBlock);
1006             final T[] block = blocks[iBlock * blockColumns + jBlock];
1007             System.arraycopy(block, iRow * jWidth, outData, outIndex, jWidth);
1008             outIndex += jWidth;
1009         }
1010 
1011         return new ArrayFieldVector<>(getField(), outData, false);
1012     }
1013 
1014     
1015     @Override
1016     public void setRowVector(final int row, final FieldVector<T> vector)
1017         throws MatrixDimensionMismatchException, OutOfRangeException {
1018         if (vector instanceof ArrayFieldVector) {
1019             setRow(row, ((ArrayFieldVector<T>) vector).getDataRef());
1020         } else {
1021             super.setRowVector(row, vector);
1022         }
1023     }
1024 
1025     
1026     @Override
1027     public FieldVector<T> getColumnVector(final int column)
1028         throws OutOfRangeException {
1029         checkColumnIndex(column);
1030         final T[] outData = MathArrays.buildArray(getField(), rows);
1031 
1032         
1033         final int jBlock  = column / BLOCK_SIZE;
1034         final int jColumn = column - jBlock * BLOCK_SIZE;
1035         final int jWidth  = blockWidth(jBlock);
1036         int outIndex      = 0;
1037         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
1038             final int iHeight = blockHeight(iBlock);
1039             final T[] block = blocks[iBlock * blockColumns + jBlock];
1040             for (int i = 0; i < iHeight; ++i) {
1041                 outData[outIndex++] = block[i * jWidth + jColumn];
1042             }
1043         }
1044 
1045         return new ArrayFieldVector<>(getField(), outData, false);
1046     }
1047 
1048     
1049     @Override
1050     public void setColumnVector(final int column, final FieldVector<T> vector)
1051         throws OutOfRangeException, MatrixDimensionMismatchException {
1052         if (vector instanceof ArrayFieldVector) {
1053             setColumn(column, ((ArrayFieldVector<T>) vector).getDataRef());
1054         } else {
1055             super.setColumnVector(column, vector);
1056         }
1057     }
1058 
1059     
1060     @Override
1061     public T[] getRow(final int row) throws OutOfRangeException {
1062         checkRowIndex(row);
1063         final T[] out = MathArrays.buildArray(getField(), columns);
1064 
1065         
1066         final int iBlock  = row / BLOCK_SIZE;
1067         final int iRow    = row - iBlock * BLOCK_SIZE;
1068         int outIndex      = 0;
1069         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
1070             final int jWidth     = blockWidth(jBlock);
1071             final T[] block = blocks[iBlock * blockColumns + jBlock];
1072             System.arraycopy(block, iRow * jWidth, out, outIndex, jWidth);
1073             outIndex += jWidth;
1074         }
1075 
1076         return out;
1077     }
1078 
1079     
1080     @Override
1081     public void setRow(final int row, final T[] array)
1082         throws OutOfRangeException, MatrixDimensionMismatchException {
1083         checkRowIndex(row);
1084         final int nCols = getColumnDimension();
1085         if (array.length != nCols) {
1086             throw new MatrixDimensionMismatchException(1, array.length, 1, nCols);
1087         }
1088 
1089         
1090         final int iBlock  = row / BLOCK_SIZE;
1091         final int iRow    = row - iBlock * BLOCK_SIZE;
1092         int outIndex      = 0;
1093         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
1094             final int jWidth     = blockWidth(jBlock);
1095             final T[] block = blocks[iBlock * blockColumns + jBlock];
1096             System.arraycopy(array, outIndex, block, iRow * jWidth, jWidth);
1097             outIndex += jWidth;
1098         }
1099     }
1100 
1101     
1102     @Override
1103     public T[] getColumn(final int column) throws OutOfRangeException {
1104         checkColumnIndex(column);
1105         final T[] out = MathArrays.buildArray(getField(), rows);
1106 
1107         
1108         final int jBlock  = column / BLOCK_SIZE;
1109         final int jColumn = column - jBlock * BLOCK_SIZE;
1110         final int jWidth  = blockWidth(jBlock);
1111         int outIndex      = 0;
1112         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
1113             final int iHeight = blockHeight(iBlock);
1114             final T[] block = blocks[iBlock * blockColumns + jBlock];
1115             for (int i = 0; i < iHeight; ++i) {
1116                 out[outIndex++] = block[i * jWidth + jColumn];
1117             }
1118         }
1119 
1120         return out;
1121     }
1122 
1123     
1124     @Override
1125     public void setColumn(final int column, final T[] array)
1126         throws MatrixDimensionMismatchException, OutOfRangeException {
1127         checkColumnIndex(column);
1128         final int nRows = getRowDimension();
1129         if (array.length != nRows) {
1130             throw new MatrixDimensionMismatchException(array.length, 1, nRows, 1);
1131         }
1132 
1133         
1134         final int jBlock  = column / BLOCK_SIZE;
1135         final int jColumn = column - jBlock * BLOCK_SIZE;
1136         final int jWidth  = blockWidth(jBlock);
1137         int outIndex      = 0;
1138         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
1139             final int iHeight = blockHeight(iBlock);
1140             final T[] block = blocks[iBlock * blockColumns + jBlock];
1141             for (int i = 0; i < iHeight; ++i) {
1142                 block[i * jWidth + jColumn] = array[outIndex++];
1143             }
1144         }
1145     }
1146 
1147     
1148     @Override
1149     public T getEntry(final int row, final int column)
1150         throws OutOfRangeException {
1151         checkRowIndex(row);
1152         checkColumnIndex(column);
1153 
1154         final int iBlock = row    / BLOCK_SIZE;
1155         final int jBlock = column / BLOCK_SIZE;
1156         final int k      = (row    - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
1157             (column - jBlock * BLOCK_SIZE);
1158 
1159         return blocks[iBlock * blockColumns + jBlock][k];
1160     }
1161 
1162     
1163     @Override
1164     public void setEntry(final int row, final int column, final T value)
1165         throws OutOfRangeException {
1166         checkRowIndex(row);
1167         checkColumnIndex(column);
1168 
1169         final int iBlock = row    / BLOCK_SIZE;
1170         final int jBlock = column / BLOCK_SIZE;
1171         final int k      = (row    - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
1172             (column - jBlock * BLOCK_SIZE);
1173 
1174         blocks[iBlock * blockColumns + jBlock][k] = value;
1175     }
1176 
1177     
1178     @Override
1179     public void addToEntry(final int row, final int column, final T increment)
1180         throws OutOfRangeException {
1181         checkRowIndex(row);
1182         checkColumnIndex(column);
1183 
1184         final int iBlock = row    / BLOCK_SIZE;
1185         final int jBlock = column / BLOCK_SIZE;
1186         final int k      = (row    - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
1187             (column - jBlock * BLOCK_SIZE);
1188         final T[] blockIJ = blocks[iBlock * blockColumns + jBlock];
1189 
1190         blockIJ[k] = blockIJ[k].add(increment);
1191     }
1192 
1193     
1194     @Override
1195     public void multiplyEntry(final int row, final int column, final T factor)
1196         throws OutOfRangeException {
1197         checkRowIndex(row);
1198         checkColumnIndex(column);
1199 
1200         final int iBlock = row    / BLOCK_SIZE;
1201         final int jBlock = column / BLOCK_SIZE;
1202         final int k      = (row    - iBlock * BLOCK_SIZE) * blockWidth(jBlock) +
1203             (column - jBlock * BLOCK_SIZE);
1204         final T[] blockIJ = blocks[iBlock * blockColumns + jBlock];
1205 
1206         blockIJ[k] = blockIJ[k].multiply(factor);
1207     }
1208 
1209     
1210     @Override
1211     public FieldMatrix<T> transpose() {
1212         final int nRows = getRowDimension();
1213         final int nCols = getColumnDimension();
1214         final BlockFieldMatrix<T> out = new BlockFieldMatrix<>(getField(), nCols, nRows);
1215 
1216         
1217         int blockIndex = 0;
1218         for (int iBlock = 0; iBlock < blockColumns; ++iBlock) {
1219             for (int jBlock = 0; jBlock < blockRows; ++jBlock) {
1220 
1221                 
1222                 final T[] outBlock = out.blocks[blockIndex];
1223                 final T[] tBlock   = blocks[jBlock * blockColumns + iBlock];
1224                 final int      pStart   = iBlock * BLOCK_SIZE;
1225                 final int      pEnd     = JdkMath.min(pStart + BLOCK_SIZE, columns);
1226                 final int      qStart   = jBlock * BLOCK_SIZE;
1227                 final int      qEnd     = JdkMath.min(qStart + BLOCK_SIZE, rows);
1228                 int k = 0;
1229                 for (int p = pStart; p < pEnd; ++p) {
1230                     final int lInc = pEnd - pStart;
1231                     int l = p - pStart;
1232                     for (int q = qStart; q < qEnd; ++q) {
1233                         outBlock[k] = tBlock[l];
1234                         ++k;
1235                         l+= lInc;
1236                     }
1237                 }
1238 
1239                 
1240                 ++blockIndex;
1241             }
1242         }
1243 
1244         return out;
1245     }
1246 
1247     
1248     @Override
1249     public int getRowDimension() {
1250         return rows;
1251     }
1252 
1253     
1254     @Override
1255     public int getColumnDimension() {
1256         return columns;
1257     }
1258 
1259     
1260     @Override
1261     public T[] operate(final T[] v) throws DimensionMismatchException {
1262         if (v.length != columns) {
1263             throw new DimensionMismatchException(v.length, columns);
1264         }
1265         final T[] out = MathArrays.buildArray(getField(), rows);
1266         final T zero = getField().getZero();
1267 
1268         
1269         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
1270             final int pStart = iBlock * BLOCK_SIZE;
1271             final int pEnd   = JdkMath.min(pStart + BLOCK_SIZE, rows);
1272             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
1273                 final T[] block  = blocks[iBlock * blockColumns + jBlock];
1274                 final int      qStart = jBlock * BLOCK_SIZE;
1275                 final int      qEnd   = JdkMath.min(qStart + BLOCK_SIZE, columns);
1276                 int k = 0;
1277                 for (int p = pStart; p < pEnd; ++p) {
1278                     T sum = zero;
1279                     int q = qStart;
1280                     while (q < qEnd - 3) {
1281                         sum = sum.
1282                               add(block[k].multiply(v[q])).
1283                               add(block[k + 1].multiply(v[q + 1])).
1284                               add(block[k + 2].multiply(v[q + 2])).
1285                               add(block[k + 3].multiply(v[q + 3]));
1286                         k += 4;
1287                         q += 4;
1288                     }
1289                     while (q < qEnd) {
1290                         sum = sum.add(block[k++].multiply(v[q++]));
1291                     }
1292                     out[p] = out[p].add(sum);
1293                 }
1294             }
1295         }
1296 
1297         return out;
1298     }
1299 
1300     
1301     @Override
1302     public T[] preMultiply(final T[] v) throws DimensionMismatchException {
1303 
1304         if (v.length != rows) {
1305             throw new DimensionMismatchException(v.length, rows);
1306         }
1307         final T[] out = MathArrays.buildArray(getField(), columns);
1308         final T zero = getField().getZero();
1309 
1310         
1311         for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
1312             final int jWidth  = blockWidth(jBlock);
1313             final int jWidth2 = jWidth  + jWidth;
1314             final int jWidth3 = jWidth2 + jWidth;
1315             final int jWidth4 = jWidth3 + jWidth;
1316             final int qStart = jBlock * BLOCK_SIZE;
1317             final int qEnd   = JdkMath.min(qStart + BLOCK_SIZE, columns);
1318             for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
1319                 final T[] block  = blocks[iBlock * blockColumns + jBlock];
1320                 final int      pStart = iBlock * BLOCK_SIZE;
1321                 final int      pEnd   = JdkMath.min(pStart + BLOCK_SIZE, rows);
1322                 for (int q = qStart; q < qEnd; ++q) {
1323                     int k = q - qStart;
1324                     T sum = zero;
1325                     int p = pStart;
1326                     while (p < pEnd - 3) {
1327                         sum = sum.
1328                               add(block[k].multiply(v[p])).
1329                               add(block[k + jWidth].multiply(v[p + 1])).
1330                               add(block[k + jWidth2].multiply(v[p + 2])).
1331                               add(block[k + jWidth3].multiply(v[p + 3]));
1332                         k += jWidth4;
1333                         p += 4;
1334                     }
1335                     while (p < pEnd) {
1336                         sum = sum.add(block[k].multiply(v[p++]));
1337                         k += jWidth;
1338                     }
1339                     out[q] = out[q].add(sum);
1340                 }
1341             }
1342         }
1343 
1344         return out;
1345     }
1346 
1347     
1348     @Override
1349     public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor) {
1350         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
1351         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
1352             final int pStart = iBlock * BLOCK_SIZE;
1353             final int pEnd   = JdkMath.min(pStart + BLOCK_SIZE, rows);
1354             for (int p = pStart; p < pEnd; ++p) {
1355                 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
1356                     final int jWidth = blockWidth(jBlock);
1357                     final int qStart = jBlock * BLOCK_SIZE;
1358                     final int qEnd   = JdkMath.min(qStart + BLOCK_SIZE, columns);
1359                     final T[] block = blocks[iBlock * blockColumns + jBlock];
1360                     int k = (p - pStart) * jWidth;
1361                     for (int q = qStart; q < qEnd; ++q) {
1362                         block[k] = visitor.visit(p, q, block[k]);
1363                         ++k;
1364                     }
1365                 }
1366              }
1367         }
1368         return visitor.end();
1369     }
1370 
1371     
1372     @Override
1373     public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor) {
1374         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
1375         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
1376             final int pStart = iBlock * BLOCK_SIZE;
1377             final int pEnd   = JdkMath.min(pStart + BLOCK_SIZE, rows);
1378             for (int p = pStart; p < pEnd; ++p) {
1379                 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
1380                     final int jWidth = blockWidth(jBlock);
1381                     final int qStart = jBlock * BLOCK_SIZE;
1382                     final int qEnd   = JdkMath.min(qStart + BLOCK_SIZE, columns);
1383                     final T[] block = blocks[iBlock * blockColumns + jBlock];
1384                     int k = (p - pStart) * jWidth;
1385                     for (int q = qStart; q < qEnd; ++q) {
1386                         visitor.visit(p, q, block[k]);
1387                         ++k;
1388                     }
1389                 }
1390              }
1391         }
1392         return visitor.end();
1393     }
1394 
1395     
1396     @Override
1397     public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor,
1398                             final int startRow, final int endRow,
1399                             final int startColumn, final int endColumn)
1400         throws OutOfRangeException, NumberIsTooSmallException {
1401         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
1402         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
1403         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
1404             final int p0     = iBlock * BLOCK_SIZE;
1405             final int pStart = JdkMath.max(startRow, p0);
1406             final int pEnd   = JdkMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
1407             for (int p = pStart; p < pEnd; ++p) {
1408                 for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
1409                     final int jWidth = blockWidth(jBlock);
1410                     final int q0     = jBlock * BLOCK_SIZE;
1411                     final int qStart = JdkMath.max(startColumn, q0);
1412                     final int qEnd   = JdkMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
1413                     final T[] block = blocks[iBlock * blockColumns + jBlock];
1414                     int k = (p - p0) * jWidth + qStart - q0;
1415                     for (int q = qStart; q < qEnd; ++q) {
1416                         block[k] = visitor.visit(p, q, block[k]);
1417                         ++k;
1418                     }
1419                 }
1420              }
1421         }
1422         return visitor.end();
1423     }
1424 
1425     
1426     @Override
1427     public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor,
1428                             final int startRow, final int endRow,
1429                             final int startColumn, final int endColumn)
1430         throws OutOfRangeException, NumberIsTooSmallException {
1431         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
1432         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
1433         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
1434             final int p0     = iBlock * BLOCK_SIZE;
1435             final int pStart = JdkMath.max(startRow, p0);
1436             final int pEnd   = JdkMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
1437             for (int p = pStart; p < pEnd; ++p) {
1438                 for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
1439                     final int jWidth = blockWidth(jBlock);
1440                     final int q0     = jBlock * BLOCK_SIZE;
1441                     final int qStart = JdkMath.max(startColumn, q0);
1442                     final int qEnd   = JdkMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
1443                     final T[] block = blocks[iBlock * blockColumns + jBlock];
1444                     int k = (p - p0) * jWidth + qStart - q0;
1445                     for (int q = qStart; q < qEnd; ++q) {
1446                         visitor.visit(p, q, block[k]);
1447                         ++k;
1448                     }
1449                 }
1450              }
1451         }
1452         return visitor.end();
1453     }
1454 
1455     
1456     @Override
1457     public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor) {
1458         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
1459         int blockIndex = 0;
1460         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
1461             final int pStart = iBlock * BLOCK_SIZE;
1462             final int pEnd   = JdkMath.min(pStart + BLOCK_SIZE, rows);
1463             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
1464                 final int qStart = jBlock * BLOCK_SIZE;
1465                 final int qEnd   = JdkMath.min(qStart + BLOCK_SIZE, columns);
1466                 final T[] block = blocks[blockIndex];
1467                 int k = 0;
1468                 for (int p = pStart; p < pEnd; ++p) {
1469                     for (int q = qStart; q < qEnd; ++q) {
1470                         block[k] = visitor.visit(p, q, block[k]);
1471                         ++k;
1472                     }
1473                 }
1474                 ++blockIndex;
1475             }
1476         }
1477         return visitor.end();
1478     }
1479 
1480     
1481     @Override
1482     public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor) {
1483         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
1484         int blockIndex = 0;
1485         for (int iBlock = 0; iBlock < blockRows; ++iBlock) {
1486             final int pStart = iBlock * BLOCK_SIZE;
1487             final int pEnd   = JdkMath.min(pStart + BLOCK_SIZE, rows);
1488             for (int jBlock = 0; jBlock < blockColumns; ++jBlock) {
1489                 final int qStart = jBlock * BLOCK_SIZE;
1490                 final int qEnd   = JdkMath.min(qStart + BLOCK_SIZE, columns);
1491                 final T[] block = blocks[blockIndex];
1492                 int k = 0;
1493                 for (int p = pStart; p < pEnd; ++p) {
1494                     for (int q = qStart; q < qEnd; ++q) {
1495                         visitor.visit(p, q, block[k]);
1496                         ++k;
1497                     }
1498                 }
1499                 ++blockIndex;
1500             }
1501         }
1502         return visitor.end();
1503     }
1504 
1505     
1506     @Override
1507     public T walkInOptimizedOrder(final FieldMatrixChangingVisitor<T> visitor,
1508                                   final int startRow, final int endRow,
1509                                   final int startColumn, final int endColumn)
1510         throws OutOfRangeException, NumberIsTooSmallException {
1511         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
1512         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
1513         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
1514             final int p0     = iBlock * BLOCK_SIZE;
1515             final int pStart = JdkMath.max(startRow, p0);
1516             final int pEnd   = JdkMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
1517             for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
1518                 final int jWidth = blockWidth(jBlock);
1519                 final int q0     = jBlock * BLOCK_SIZE;
1520                 final int qStart = JdkMath.max(startColumn, q0);
1521                 final int qEnd   = JdkMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
1522                 final T[] block = blocks[iBlock * blockColumns + jBlock];
1523                 for (int p = pStart; p < pEnd; ++p) {
1524                     int k = (p - p0) * jWidth + qStart - q0;
1525                     for (int q = qStart; q < qEnd; ++q) {
1526                         block[k] = visitor.visit(p, q, block[k]);
1527                         ++k;
1528                     }
1529                 }
1530             }
1531         }
1532         return visitor.end();
1533     }
1534 
1535     
1536     @Override
1537     public T walkInOptimizedOrder(final FieldMatrixPreservingVisitor<T> visitor,
1538                                   final int startRow, final int endRow,
1539                                   final int startColumn, final int endColumn)
1540         throws OutOfRangeException, NumberIsTooSmallException {
1541         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
1542         visitor.start(rows, columns, startRow, endRow, startColumn, endColumn);
1543         for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) {
1544             final int p0     = iBlock * BLOCK_SIZE;
1545             final int pStart = JdkMath.max(startRow, p0);
1546             final int pEnd   = JdkMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow);
1547             for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) {
1548                 final int jWidth = blockWidth(jBlock);
1549                 final int q0     = jBlock * BLOCK_SIZE;
1550                 final int qStart = JdkMath.max(startColumn, q0);
1551                 final int qEnd   = JdkMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn);
1552                 final T[] block = blocks[iBlock * blockColumns + jBlock];
1553                 for (int p = pStart; p < pEnd; ++p) {
1554                     int k = (p - p0) * jWidth + qStart - q0;
1555                     for (int q = qStart; q < qEnd; ++q) {
1556                         visitor.visit(p, q, block[k]);
1557                         ++k;
1558                     }
1559                 }
1560             }
1561         }
1562         return visitor.end();
1563     }
1564 
1565     
1566 
1567 
1568 
1569 
1570     private int blockHeight(final int blockRow) {
1571         return (blockRow == blockRows - 1) ? rows - blockRow * BLOCK_SIZE : BLOCK_SIZE;
1572     }
1573 
1574     
1575 
1576 
1577 
1578 
1579     private int blockWidth(final int blockColumn) {
1580         return (blockColumn == blockColumns - 1) ? columns - blockColumn * BLOCK_SIZE : BLOCK_SIZE;
1581     }
1582 }