/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.math.matrix;
import java.io.Serializable;
import org.apache.commons.lang.Validate;
import com.opengamma.util.ArgumentChecker;
/**
* A minimal implementation of a 2D matrix of doubles.
*
*/
public class DoubleMatrix2D implements Matrix<Double>, Serializable {
private final double[][] _data;
private final int _rows;
private final int _columns;
private final int _elements;
/** * Empty 2D matrix */
public static final DoubleMatrix2D EMPTY_MATRIX = new DoubleMatrix2D(new double[0][0]);
/**
* @deprecated
* Does not copy data on constructions. Do not use.
* @param data The data
* @return A matrix
*/
@Deprecated
public static DoubleMatrix2D noCopy(final double[][] data) {
return new DoubleMatrix2D(data, false);
}
/**
* Sets up an empty matrix
* @param rows Number of rows
* @param columns Number of columns
*/
public DoubleMatrix2D(final int rows, final int columns) {
Validate.isTrue(rows > 0, "row number cannot be negative or zero");
Validate.isTrue(columns > 0, "column number cannot be negative or zero");
_rows = rows;
_columns = columns;
_data = new double[_rows][_columns];
_elements = _rows * _columns;
}
// REVIEW could do with a constructor that does NOT copy the data
/**
* @param data The data, not null. The data is expected in row-column form.
* @throws IllegalArgumentException If the matrix is not rectangular
*/
public DoubleMatrix2D(final double[][] data) {
Validate.notNull(data);
if (data.length == 0) {
_data = new double[0][0];
_elements = 0;
_rows = 0;
_columns = 0;
} else {
_rows = data.length;
_columns = data[0].length;
_data = new double[_rows][_columns];
for (int i = 0; i < _rows; i++) {
System.arraycopy(data[i], 0, _data[i], 0, data[i].length);
}
_elements = _rows * _columns;
}
}
/**
* @param data The data, not null. The data is expected in row-column form.
* @throws IllegalArgumentException If the matrix is not rectangular
*/
public DoubleMatrix2D(final Double[][] data) {
Validate.notNull(data);
if (data.length == 0) {
_data = new double[0][0];
_elements = 0;
_rows = 0;
_columns = 0;
} else {
_rows = data.length;
_columns = data[0].length;
_data = new double[_rows][_columns];
for (int i = 0; i < _rows; i++) {
for (int j = 0; j < _columns; j++) {
_data[i][j] = data[i][j];
}
}
_elements = _rows * _columns;
}
}
private DoubleMatrix2D(final double[][] data, @SuppressWarnings("unused") final boolean copy) {
_rows = data.length;
_columns = data[0].length;
_elements = _rows * _columns;
_data = data;
}
/**
* Returns a copy of the row for a particular index.
* @param index The index
* @return The row
*/
public DoubleMatrix1D getRowVector(final int index) {
return getRowVector(index, true);
}
/**
* Returns the row for a particular index.
* @param index The index
* @param copy Whether to copy existing data
* @return The row
*/
public DoubleMatrix1D getRowVector(final int index, final boolean copy) {
return new DoubleMatrix1D(_data[index], copy);
}
/**
* Returns the column for a particular index.
* @param index The index
* @return The column
*/
public DoubleMatrix1D getColumnVector(final int index) {
final double[] res = new double[_rows];
for (int i = 0; i < _rows; i++) {
res[i] = _data[i][index];
}
return new DoubleMatrix1D(res, false);
}
/**
* {@inheritDoc}
*/
@Override
public Double getEntry(final int... index) {
ArgumentChecker.notNull(index, "indices");
ArgumentChecker.isTrue(index[0] < _data.length, "x index {} is greater than length of array {}", index[0], _data.length);
ArgumentChecker.isTrue(index[1] < _data[0].length, "y index {} is greater than length of array {}", index[1], _data[0].length);
return _data[index[0]][index[1]];
}
/**
* Returns the underlying matrix data. If this is changed so is the matrix.
* @see #toArray to get a copy of data
* @return An array of arrays containing the matrix elements
*/
public double[][] getData() {
return _data;
}
/**
* Convert the matrix to an array of double arrays.
* As its elements are copied, the array is independent from the matrix data.
* @return An array of arrays containing a copy of matrix elements
*/
public double[][] toArray() {
final DoubleMatrix2D temp = new DoubleMatrix2D(_data);
return temp.getData();
}
/**
* {@inheritDoc}
*/
@Override
public int getNumberOfElements() {
return _elements;
}
/**
* @return The number of rows in this matrix
*/
public int getNumberOfRows() {
return _rows;
}
/**
* @return The number of columns in this matrix
*/
public int getNumberOfColumns() {
return _columns;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + _columns;
result = prime * result + _rows;
int count = 0;
for (int i = 0; i < _rows; i++) {
for (int j = 0; j < _columns; j++) {
result = prime * result + Double.valueOf(_data[i][j]).hashCode();
if (count == 10) {
break;
}
count++;
}
if (count == 10) {
break;
}
}
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DoubleMatrix2D other = (DoubleMatrix2D) obj;
if (_columns != other._columns) {
return false;
}
if (_rows != other._rows) {
return false;
}
for (int i = 0; i < _rows; i++) {
for (int j = 0; j < _columns; j++) {
if (Double.doubleToLongBits(_data[i][j]) != Double.doubleToLongBits(other._data[i][j])) {
return false;
}
}
}
return true;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer();
for (final double[] d : _data) {
for (int i = 0; i < d.length; i++) {
sb.append(d[i]);
sb.append(i == d.length - 1 ? "\n" : "\t");
}
}
return sb.toString();
}
}