001    /*
002     * Copyright (C) 2010 by Frode Carlsen
003     *
004     * This file is part of the Universal Java Matrix Package (UJMP).
005     * See the NOTICE file distributed with this work for additional
006     * information regarding copyright ownership and licensing.
007     *
008     * UJMP is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU Lesser General Public License as
010     * published by the Free Software Foundation; either version 2
011     * of the License, or (at your option) any later version.
012     *
013     * UJMP is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016     * GNU Lesser General Public License for more details.
017     *
018     * You should have received a copy of the GNU Lesser General Public
019     * License along with UJMP; if not, write to the
020     * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
021     * Boston, MA  02110-1301  USA
022     */
023    package org.ujmp.core.doublematrix.impl;
024    
025    import java.io.Serializable;
026    import java.util.Arrays;
027    
028    /**
029     * This class describes the layout (size, order) of a square block of data
030     * within a {@link BlockDenseDoubleMatrix2D block matrix}.
031     * 
032     * @author Frode Carlsen, Holger Arndt
033     */
034    public final class BlockMatrixLayout implements Serializable {
035            private static final long serialVersionUID = 2726685238884065594L;
036    
037            /** Enum describing the layout of a block of data. */
038            public enum BlockOrder {
039                    ROWMAJOR, COLUMNMAJOR;
040    
041                    public BlockOrder transpose() {
042                            return (this == ROWMAJOR) ? COLUMNMAJOR : ROWMAJOR;
043                    }
044            }
045    
046            /** Total size of a block (area). */
047            protected final int blockArea;
048    
049            /** Length of one side (stripe) of a square block. */
050            public final int blockStripe;
051    
052            /** Number of columns of matrix. */
053            public final int columns;
054    
055            /**
056             * Whether this block is laid out in row-major (true) or column-major
057             * (false) order.
058             */
059            public final BlockOrder blockOrder;
060    
061            /** number of blocks in this matrix */
062            final int numberOfBlocks;
063    
064            /** @see #blockOrder */
065            private final boolean rowMajor;
066    
067            /** Number of rows of matrix. */
068            public final int rows;
069    
070            /** threshold for when to stop using square blocks. */
071            private final int sqbColThreshold, sqbRowThreshold;
072    
073            BlockMatrixLayout(final int rows, final int columns, final int blockStripe,
074                            final BlockOrder blockOrder) {
075    
076                    this.blockStripe = blockStripe;
077    
078                    if (rows <= 0 || columns <= 0 || blockStripe <= 0) {
079                            throw new IllegalArgumentException(String.format(
080                                            "One or more invalid values: rows=%s, columns=%s, blockSize=%s", rows, columns,
081                                            blockStripe));
082                    }
083    
084                    this.blockArea = blockStripe * blockStripe;
085                    this.rows = rows;
086                    this.columns = columns;
087                    this.sqbColThreshold = (columns / blockStripe) * blockStripe;
088                    this.sqbRowThreshold = (rows / blockStripe) * blockStripe;
089                    this.blockOrder = blockOrder;
090                    this.rowMajor = (blockOrder == BlockOrder.ROWMAJOR);
091                    this.numberOfBlocks = (rows / blockStripe + (rows % blockStripe > 0 ? 1 : 0))
092                                    * (columns / blockStripe + (columns % blockStripe > 0 ? 1 : 0));
093            }
094    
095            /**
096             * Get the block which contains the specified row, column
097             * 
098             * @param matrix
099             *            to get block from
100             * @param row
101             * @param column
102             * @return block containing given row, column
103             */
104            final double[] getBlock(BlockDenseDoubleMatrix2D matrix, int row, int column) {
105                    return matrix.getBlockData(row, column);
106            }
107    
108            final int getBlockIndexByColumn(final int lrow, final int lcol, final int numRows,
109                            final int numCols) {
110                    return rowMajor ? (lcol * numRows + lrow) : (lrow * numCols + lcol);
111            }
112    
113            final int getBlockIndexByRow(final int lrow, final int lcol, final int numRows,
114                            final int numCols) {
115                    return rowMajor ? (lrow * numCols + lcol) : (lcol * numRows + lrow);
116            }
117    
118            final int getBlockNumber(int row, int col) {
119                    return (col / blockStripe) + (row / blockStripe)
120                                    * (columns / blockStripe + (columns % blockStripe > 0 ? 1 : 0));
121            }
122    
123            final int getIndexInBlock(int row, int col) {
124                    int lrows = getRowsInBlock(row);
125                    int lcols = getColumnsInBlock(col);
126                    return getBlockIndexByRow(row % blockStripe, col % blockStripe, lrows, lcols);
127            }
128    
129            final int getBlockSize(int row, int col) {
130                    int lrows = getRowsInBlock(row);
131                    int lcols = getColumnsInBlock(col);
132                    return lrows * lcols;
133            }
134    
135            final double[] toColMajorBlock(BlockDenseDoubleMatrix2D matrix, final int rowStart, int colStart) {
136                    double[] block = getBlock(matrix, rowStart, colStart);
137                    if (!rowMajor) {
138                            return block;
139                    }
140                    return toColMajorBlock(block, rowStart, colStart);
141            }
142    
143            final double[] toColMajorBlock(double[] block, final int rowStart, int colStart) {
144    
145                    final double[] targetBlock = new double[block.length];
146                    final int lrows = getRowsInBlock(rowStart);
147                    final int lcols = getColumnsInBlock(colStart);
148    
149                    // transpose block, swap cols and rows
150                    for (int i = 0; i < lcols; i++) {
151                            final int ilrows = i * lrows;
152                            for (int j = 0; j < lrows; j++) {
153                                    targetBlock[ilrows + j] = block[j * lcols + i];
154                            }
155                    }
156    
157                    return targetBlock;
158            }
159    
160            int getColumnsInBlock(int col) {
161                    return (col >= sqbColThreshold) ? this.columns - this.sqbColThreshold : blockStripe;
162            }
163    
164            int getRowsInBlock(final int row) {
165                    return (row >= sqbRowThreshold) ? this.rows - this.sqbRowThreshold : blockStripe;
166            }
167    
168            final double[] toRowMajorBlock(final BlockDenseDoubleMatrix2D matrix, final int rowStart,
169                            int colStart) {
170                    final double[] block = getBlock(matrix, rowStart, colStart);
171                    if (rowMajor) {
172                            return block;
173                    }
174                    return toRowMajorBlock(block, rowStart, colStart);
175            }
176    
177            final double[] toRowMajorBlock(final double[] block, final int rowStart, int colStart) {
178    
179                    final double[] targetBlock = new double[block.length];
180                    final int lrows = getRowsInBlock(rowStart);
181                    final int lcols = getColumnsInBlock(colStart);
182    
183                    // transpose block
184                    for (int i = 0; i < lrows; i++) {
185                            final int ilcols = i * lcols;
186                            for (int j = 0; j < lcols; j++) {
187                                    targetBlock[ilcols + j] = block[j * lrows + i];
188                            }
189                    }
190    
191                    return targetBlock;
192            }
193    
194            @Override
195            public String toString() {
196                    int[] rowLayout = new int[blockStripe];
197                    StringBuilder b = new StringBuilder(blockArea * 4 + 40);
198                    String msg = "\n(rows=%s, columns=%s, blockSize=%s):\n";
199                    b.append(String.format(msg, rows, columns, blockStripe));
200                    int rows = blockStripe, cols = blockStripe;
201    
202                    for (int i = 0; i < rows; i++) {
203                            for (int j = 0; j < cols; j++) {
204                                    rowLayout[j] = getBlockIndexByRow(i, j, rows, cols);
205                            }
206                            b.append(Arrays.toString(rowLayout)).append("\n");
207                    }
208    
209                    return b.toString();
210            }
211    }