/*
* Copyright (C) 2010 by Frode Carlsen
*
* This file is part of the Universal Java Matrix Package (UJMP).
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* UJMP is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* UJMP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with UJMP; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package org.ujmp.core.doublematrix.impl;
import java.io.Serializable;
import java.util.Arrays;
/**
* This class describes the layout (size, order) of a square block of data
* within a {@link BlockDenseDoubleMatrix2D block matrix}.
*
* @author Frode Carlsen, Holger Arndt
*/
public final class BlockMatrixLayout implements Serializable {
private static final long serialVersionUID = 2726685238884065594L;
/** Enum describing the layout of a block of data. */
public enum BlockOrder {
ROWMAJOR, COLUMNMAJOR;
public BlockOrder transpose() {
return (this == ROWMAJOR) ? COLUMNMAJOR : ROWMAJOR;
}
}
/** Total size of a block (area). */
protected final int blockArea;
/** Length of one side (stripe) of a square block. */
public final int blockStripe;
/** Number of columns of matrix. */
public final int columns;
/**
* Whether this block is laid out in row-major (true) or column-major
* (false) order.
*/
public final BlockOrder blockOrder;
/** number of blocks in this matrix */
final int numberOfBlocks;
/** @see #blockOrder */
private final boolean rowMajor;
/** Number of rows of matrix. */
public final int rows;
/** threshold for when to stop using square blocks. */
private final int sqbColThreshold, sqbRowThreshold;
BlockMatrixLayout(final int rows, final int columns, final int blockStripe,
final BlockOrder blockOrder) {
this.blockStripe = blockStripe;
if (rows <= 0 || columns <= 0 || blockStripe <= 0) {
throw new IllegalArgumentException(String.format(
"One or more invalid values: rows=%s, columns=%s, blockSize=%s", rows, columns,
blockStripe));
}
this.blockArea = blockStripe * blockStripe;
this.rows = rows;
this.columns = columns;
this.sqbColThreshold = (columns / blockStripe) * blockStripe;
this.sqbRowThreshold = (rows / blockStripe) * blockStripe;
this.blockOrder = blockOrder;
this.rowMajor = (blockOrder == BlockOrder.ROWMAJOR);
this.numberOfBlocks = (rows / blockStripe + (rows % blockStripe > 0 ? 1 : 0))
* (columns / blockStripe + (columns % blockStripe > 0 ? 1 : 0));
}
/**
* Get the block which contains the specified row, column
*
* @param matrix
* to get block from
* @param row
* @param column
* @return block containing given row, column
*/
final double[] getBlock(BlockDenseDoubleMatrix2D matrix, int row, int column) {
return matrix.getBlockData(row, column);
}
final int getBlockIndexByColumn(final int lrow, final int lcol, final int numRows,
final int numCols) {
return rowMajor ? (lcol * numRows + lrow) : (lrow * numCols + lcol);
}
final int getBlockIndexByRow(final int lrow, final int lcol, final int numRows,
final int numCols) {
return rowMajor ? (lrow * numCols + lcol) : (lcol * numRows + lrow);
}
final int getBlockNumber(int row, int col) {
return (col / blockStripe) + (row / blockStripe)
* (columns / blockStripe + (columns % blockStripe > 0 ? 1 : 0));
}
final int getIndexInBlock(int row, int col) {
int lrows = getRowsInBlock(row);
int lcols = getColumnsInBlock(col);
return getBlockIndexByRow(row % blockStripe, col % blockStripe, lrows, lcols);
}
final int getBlockSize(int row, int col) {
int lrows = getRowsInBlock(row);
int lcols = getColumnsInBlock(col);
return lrows * lcols;
}
final double[] toColMajorBlock(BlockDenseDoubleMatrix2D matrix, final int rowStart, int colStart) {
double[] block = getBlock(matrix, rowStart, colStart);
if (!rowMajor) {
return block;
}
return toColMajorBlock(block, rowStart, colStart);
}
final double[] toColMajorBlock(double[] block, final int rowStart, int colStart) {
final double[] targetBlock = new double[block.length];
final int lrows = getRowsInBlock(rowStart);
final int lcols = getColumnsInBlock(colStart);
// transpose block, swap cols and rows
for (int i = 0; i < lcols; i++) {
final int ilrows = i * lrows;
for (int j = 0; j < lrows; j++) {
targetBlock[ilrows + j] = block[j * lcols + i];
}
}
return targetBlock;
}
int getColumnsInBlock(int col) {
return (col >= sqbColThreshold) ? this.columns - this.sqbColThreshold : blockStripe;
}
int getRowsInBlock(final int row) {
return (row >= sqbRowThreshold) ? this.rows - this.sqbRowThreshold : blockStripe;
}
final double[] toRowMajorBlock(final BlockDenseDoubleMatrix2D matrix, final int rowStart,
int colStart) {
final double[] block = getBlock(matrix, rowStart, colStart);
if (rowMajor) {
return block;
}
return toRowMajorBlock(block, rowStart, colStart);
}
final double[] toRowMajorBlock(final double[] block, final int rowStart, int colStart) {
final double[] targetBlock = new double[block.length];
final int lrows = getRowsInBlock(rowStart);
final int lcols = getColumnsInBlock(colStart);
// transpose block
for (int i = 0; i < lrows; i++) {
final int ilcols = i * lcols;
for (int j = 0; j < lcols; j++) {
targetBlock[ilcols + j] = block[j * lrows + i];
}
}
return targetBlock;
}
@Override
public String toString() {
int[] rowLayout = new int[blockStripe];
StringBuilder b = new StringBuilder(blockArea * 4 + 40);
String msg = "\n(rows=%s, columns=%s, blockSize=%s):\n";
b.append(String.format(msg, rows, columns, blockStripe));
int rows = blockStripe, cols = blockStripe;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
rowLayout[j] = getBlockIndexByRow(i, j, rows, cols);
}
b.append(Arrays.toString(rowLayout)).append("\n");
}
return b.toString();
}
}