/*
* Copyright (c) 2009-2015
* IT-Consulting Stephan Schloepke (http://www.schloepke.de/)
* klemm software consulting Mirko Klemm (http://www.klemm-scs.com/)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jbasics.math;
import org.jbasics.arrays.unstable.ArrayIterator;
import org.jbasics.checker.ContractCheck;
import org.jbasics.pattern.container.TabularData;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class BigDecimalMatrix implements TabularData<BigDecimal>, Iterable<Collection<BigDecimal>> /* , Comparable<BigDecimalMatrix> */ {
private final int rows, columns;
private final BigDecimal[][] matrix;
private transient Collection<BigDecimal>[] iterables;
public BigDecimalMatrix(final int rows, final int columns, final Number... values) {
this.rows = ContractCheck.mustBeInRange(rows, 1, Integer.MAX_VALUE, "rows"); //$NON-NLS-1$
this.columns = ContractCheck.mustBeInRange(columns, 1, Integer.MAX_VALUE, "columns"); //$NON-NLS-1$
this.matrix = new BigDecimal[rows][columns];
int i = 0, j = 0;
if (values != null) {
for (final Number value : values) {
this.matrix[i][j] = value == null ? BigDecimal.ZERO : NumberConverter.toBigDecimal(value);
if (++j >= columns) {
j = 0;
if (++i >= rows) {
return;
}
}
}
}
// fill the possible rest with zero
for (; j < columns; j++) {
this.matrix[i][j] = BigDecimal.ZERO;
}
for (; i < rows; i++) {
for (j = 0; j < columns; j++) {
this.matrix[i][j] = BigDecimal.ZERO;
}
}
}
public BigDecimalMatrix(final List<List<Number>> values) {
this(ContractCheck.mustNotBeNullOrEmpty(values, "values").size(), BigDecimalMatrix.determinColumnSize(values), values); //$NON-NLS-1$
}
public BigDecimalMatrix(final int rows, final int columns, final List<List<Number>> values) {
ContractCheck.mustNotBeNullOrEmpty(values, "values"); //$NON-NLS-1$
this.rows = ContractCheck.mustBeInRange(rows, 1, Integer.MAX_VALUE, "rows"); //$NON-NLS-1$
this.columns = ContractCheck.mustBeInRange(columns, 1, Integer.MAX_VALUE, "columns"); //$NON-NLS-1$
this.matrix = new BigDecimal[this.rows][this.columns];
for (int i = 0; i < this.rows; i++) {
final List<? extends Number> colData = values.size() <= i ? null : values.get(i);
for (int j = 0; j < this.columns; j++) {
final Number v = colData == null || colData.size() <= j ? null : colData.get(j);
this.matrix[i][j] = v == null ? BigDecimal.ZERO : NumberConverter.toBigDecimal(v);
}
}
}
private static int determinColumnSize(final List<List<Number>> values) {
assert values != null;
int maxColumns = 0;
for (final List<Number> column : values) {
maxColumns = Math.max(maxColumns, column != null ? column.size() : 0);
}
return maxColumns;
}
public static BigDecimalMatrixBuilder create() {
return new BigDecimalMatrixBuilder();
}
public static BigDecimalMatrix createColumnVector(final Number... values) {
return new BigDecimalMatrix(ContractCheck.mustNotBeNullOrEmpty(values, "values").length, 1, values);
}
public static BigDecimalMatrix createRowVector(final Number... values) {
return new BigDecimalMatrix(1, ContractCheck.mustNotBeNullOrEmpty(values, "values").length, values);
}
public static BigDecimalMatrix createIdentityMatrix(final int size) {
final BigDecimalMatrix result = new BigDecimalMatrix(size, size);
for (int i = 0; i < size; i++) {
result.matrix[i][i] = BigDecimal.ONE;
}
return result;
}
public static BigDecimalMatrix createRandomColumnVector(final int size) {
return BigDecimalMatrix.createRandomMatrix(ContractCheck.mustBeInRange(size, 0, Integer.MAX_VALUE, "size"), 1); //$NON-NLS-1$
}
public static BigDecimalMatrix createRandomMatrix(final int rows, final int columns) {
return BigDecimalMatrix.createRandomMatrix(rows, columns, new JavaRandomNumberSequence());
}
public static BigDecimalMatrix createRandomMatrix(final int rows, final int columns, final RandomNumberSequence<? extends Number> r) {
final BigDecimalMatrix result = new BigDecimalMatrix(rows, columns);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
result.matrix[i][j] = NumberConverter.toBigDecimal(r.nextRandomNumber());
}
}
return result;
}
public static BigDecimalMatrix createRandomRowVector(final int size) {
return BigDecimalMatrix.createRandomMatrix(1, ContractCheck.mustBeInRange(size, 0, Integer.MAX_VALUE, "size")); //$NON-NLS-1$
}
public static BigDecimalMatrix createRandomColumnVector(final int size, final RandomNumberSequence<? extends Number> r) {
return BigDecimalMatrix.createRandomMatrix(ContractCheck.mustBeInRange(size, 0, Integer.MAX_VALUE, "size"), 1, r); //$NON-NLS-1$
}
public static BigDecimalMatrix createRandomRowVector(final int size, final RandomNumberSequence<? extends Number> r) {
return BigDecimalMatrix.createRandomMatrix(1, ContractCheck.mustBeInRange(size, 0, Integer.MAX_VALUE, "size"), r); //$NON-NLS-1$
}
public BigDecimal get(final int row, final int column) {
return this.matrix[row][column];
}
@Override
public BigDecimal getValueAtRowAndColumn(int row, int column) {
return this.matrix[row][column];
}
public int rowSize() {
return this.matrix.length;
}
public int columnSize() {
return this.matrix[0].length;
}
public BigDecimalMatrix transpose() {
final BigDecimalMatrix result = new BigDecimalMatrix(this.columns, this.rows);
for (int i = 0; i < this.rows; i++) {
for (int j = 0; j < this.columns; j++) {
result.matrix[j][i] = this.matrix[i][j];
}
}
return result;
}
public BigDecimalMatrix add(final BigDecimalMatrix summant) {
return add(summant, MathContext.UNLIMITED);
}
public BigDecimalMatrix add(final BigDecimalMatrix summant, final MathContext mc) {
if (ContractCheck.mustNotBeNull(summant, "summant").rows != this.rows || summant.columns != this.columns) {
throw new IllegalArgumentException("Matrix summation can only be done with matrices of the exact same dimensions");
}
final BigDecimalMatrix result = new BigDecimalMatrix(this.rows, this.columns);
for (int i = 0; i < this.matrix.length; i++) {
for (int j = 0; j < this.matrix[i].length; j++) {
result.matrix[i][j] = this.matrix[i][j].add(summant.matrix[i][j], mc);
}
}
return result;
}
public BigDecimalMatrix subtract(final BigDecimalMatrix summant) {
return subtract(summant, MathContext.UNLIMITED);
}
public BigDecimalMatrix subtract(final BigDecimalMatrix summant, final MathContext mc) {
if (ContractCheck.mustNotBeNull(summant, "summant").rows != this.rows || summant.columns != this.columns) {
throw new IllegalArgumentException("Matrix summation can only be done with matrices of the exact same dimensions");
}
final BigDecimalMatrix result = new BigDecimalMatrix(this.rows, this.columns);
for (int i = 0; i < this.matrix.length; i++) {
for (int j = 0; j < this.matrix[i].length; j++) {
result.matrix[i][j] = this.matrix[i][j].subtract(summant.matrix[i][j], mc);
}
}
return result;
}
public BigDecimalMatrix multiply(final long scalar) {
return multiply(BigDecimal.valueOf(scalar), MathContext.UNLIMITED);
}
public BigDecimalMatrix multiply(final BigDecimal scalar, final MathContext mc) {
final BigDecimalMatrix result = new BigDecimalMatrix(this.rows, this.columns);
for (int i = 0; i < this.matrix.length; i++) {
for (int j = 0; j < this.matrix[i].length; j++) {
result.matrix[i][j] = this.matrix[i][j].multiply(scalar, mc);
}
}
return result;
}
public BigDecimalMatrix multiply(final double scalar) {
return multiply(BigDecimal.valueOf(scalar), MathContext.UNLIMITED);
}
public BigDecimalMatrix multiply(final Number scalar) {
return multiply(NumberConverter.toBigDecimal(scalar), MathContext.UNLIMITED);
}
public BigDecimalMatrix multiply(final Number scalar, final MathContext mc) {
return multiply(NumberConverter.toBigDecimal(scalar), mc);
}
public BigDecimalMatrix multiply(final BigDecimal scalar) {
return multiply(scalar, MathContext.UNLIMITED);
}
public BigDecimalMatrix multiply(final BigDecimalMatrix factor) {
return multiply(factor, MathContext.UNLIMITED);
}
public BigDecimalMatrix multiply(final BigDecimalMatrix factor, final MathContext mc) {
if (this.columns != factor.rows) {
throw new IllegalArgumentException("The rows of the matrix factor must be equal to the columns of this matrix"); //$NON-NLS-1$
}
final BigDecimalMatrix result = new BigDecimalMatrix(this.rows, factor.columns);
for (int i = 0; i < result.columns; i++) {
for (int j = 0; j < result.rows; j++) {
BigDecimal temp = BigDecimal.ZERO;
for (int k = 0; k < this.columns; k++) {
temp = temp.add(this.matrix[j][k].multiply(factor.matrix[k][i]));
}
result.matrix[j][i] = temp.round(mc);
}
}
return result;
}
@Override
@SuppressWarnings("unchecked")
public Iterator<Collection<BigDecimal>> iterator() {
if (this.iterables == null) {
this.iterables = new Collection[this.matrix.length];
for (int i = 0; i < this.matrix.length; i++) {
// this.iterables[i] = new ArrayCollection<BigDecimal>(this.matrix[i]);
this.iterables[i] = Arrays.asList(this.matrix[i]);
}
}
return new ArrayIterator<Collection<BigDecimal>>(this.iterables);
}
@Override
public int hashCode() {
final int prime = 17;
int result = 1;
for (final BigDecimal[] row : this.matrix) {
for (final BigDecimal value : row) {
result = result * prime + (value == null ? BigDecimal.ZERO.hashCode() : value.hashCode());
}
}
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
} else {
final BigDecimal[][] m = ((BigDecimalMatrix) obj).matrix;
if (m.length != this.matrix.length) {
return false;
}
for (int i = 0; i < this.matrix.length; i++) {
if (m[i].length != this.matrix[i].length) {
return false;
}
for (int j = 0; j < this.matrix[i].length; j++) {
BigDecimal left = this.matrix[i][j];
final BigDecimal right = m[i][j];
if (left == null) {
left = BigDecimal.ZERO;
}
if (right == null) {
left = BigDecimal.ZERO;
}
if (!left.equals(right)) {
return false;
}
}
}
return true;
}
}
@Override
@SuppressWarnings("nls")
public String toString() {
final StringBuilder t = new StringBuilder();
for (final BigDecimal[] element : this.matrix) {
t.append(t.length() == 0 ? "[ " : " | ");
for (int j = 0; j < element.length; j++) {
if (j > 0) {
t.append(" ");
}
t.append(element[j]);
}
}
return t.append(" ]").toString();
}
}