/*
* Copyright (C) 2008-2015 by Holger Arndt
*
* This file is part of the Java Data Mining Package (JDMP).
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* JDMP 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.
*
* JDMP 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 JDMP; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package org.ujmp.core.util;
import java.util.Arrays;
import java.util.Iterator;
import org.ujmp.core.Matrix;
import org.ujmp.core.doublematrix.stub.AbstractSparseDoubleMatrix2D;
public class DefaultSparseDoubleVector1D extends AbstractSparseDoubleMatrix2D {
private static final long serialVersionUID = -2990811989700739834L;
private static final int initialCapacity = 8;
private static final double growFactor = 1.5;
private long[] indices;
private double[] values;
private int capacity;
private int valueCount;
private boolean transposed;
public DefaultSparseDoubleVector1D(DefaultSparseDoubleVector1D source) {
super(source.getRowCount(), source.getColumnCount());
this.valueCount = source.valueCount;
this.capacity = source.capacity;
this.indices = Arrays.copyOf(source.indices, source.indices.length);
this.values = Arrays.copyOf(source.values, source.values.length);
this.transposed = getColumnCount() > getRowCount();
}
public DefaultSparseDoubleVector1D(long rows, long columns) {
super(rows, columns);
VerifyUtil.verifyTrue(rows == 1 || columns == 1, "not a vector");
this.valueCount = 0;
this.capacity = initialCapacity;
this.indices = new long[initialCapacity];
this.values = new double[initialCapacity];
this.transposed = columns > rows;
}
public final void clear() {
valueCount = 0;
}
public double getDouble(long row, long column) {
long pos;
if (transposed) {
VerifyUtil.verifyEquals(row, 0, "row must be 0");
pos = column;
} else {
VerifyUtil.verifyEquals(column, 0, "column must be 0");
pos = row;
}
int index = MathUtil.search(indices, 0, valueCount, pos);
if (index >= 0) {
return values[index];
} else {
return 0.0;
}
}
public void setDouble(double value, long row, long column) {
long pos;
if (transposed) {
VerifyUtil.verifyEquals(row, 0, "row must be 0");
pos = column;
} else {
VerifyUtil.verifyEquals(column, 0, "column must be 0");
pos = row;
}
synchronized (indices) {
int index = MathUtil.search(indices, 0, valueCount, pos);
if (index >= 0) {
// value exists
if (value == 0.0) {
// delete old value
System.arraycopy(indices, index + 1, indices, index, valueCount - index - 1);
System.arraycopy(values, index + 1, values, index, valueCount - index - 1);
valueCount--;
} else {
// update existing value
values[index] = value;
}
} else {
// value does not exist
if (value != 0.0) {
if (capacity == valueCount) {
// expand arrays if necessary
capacity = (int) (capacity * growFactor);
indices = Arrays.copyOf(indices, capacity);
values = Arrays.copyOf(values, capacity);
}
index = findInsertPosition(indices, 0, valueCount, pos);
if (index != capacity) {
// make space in the middle
System.arraycopy(indices, index, indices, index + 1, valueCount - index);
System.arraycopy(values, index, values, index + 1, valueCount - index);
}
indices[index] = pos;
values[index] = value;
valueCount++;
}
}
}
}
public static final int findInsertPosition(final long[] values, int fromIndex, int toIndex,
final long key) {
toIndex--;
while (fromIndex <= toIndex) {
int mid = (fromIndex + toIndex) >>> 1;
long midVal = values[mid];
if (midVal < key) {
fromIndex = mid + 1;
} else if (midVal > key) {
toIndex = mid - 1;
} else {
return mid;
}
}
return fromIndex;
}
public double getDouble(int row, int column) {
return getDouble((long) row, (long) column);
}
public void setDouble(double value, int row, int column) {
setDouble(value, (long) row, (long) column);
}
public boolean containsCoordinates(long... coordinates) {
return getDouble(coordinates) != 0.0;
}
public Matrix divide(double value) {
DefaultSparseDoubleVector1D result = new DefaultSparseDoubleVector1D(this);
final double[] resultValues = result.values;
for (int i = resultValues.length; --i != -1;) {
resultValues[i] /= value;
}
return result;
}
public Matrix times(double value) {
DefaultSparseDoubleVector1D result = new DefaultSparseDoubleVector1D(this);
final double[] resultValues = result.values;
for (int i = resultValues.length; --i != -1;) {
resultValues[i] *= value;
}
return result;
}
public Matrix times(Matrix matrix) {
DefaultSparseDoubleVector1D result = new DefaultSparseDoubleVector1D(this);
final double[] resultValues = result.values;
for (int i = resultValues.length; --i != -1;) {
resultValues[i] *= matrix.getAsDouble(indices[i], 0);
}
return result;
}
public Matrix divide(Matrix matrix) {
DefaultSparseDoubleVector1D result = new DefaultSparseDoubleVector1D(this);
final double[] resultValues = result.values;
for (int i = resultValues.length; --i != -1;) {
resultValues[i] /= matrix.getAsDouble(indices[i], 0);
}
return result;
}
public Iterable<long[]> availableCoordinates() {
return new NonZeroIterable(indices, valueCount);
}
}
class NonZeroIterable implements Iterable<long[]> {
private final long[] indices;
private final int valueCount;
public NonZeroIterable(long[] indices, int valueCount) {
this.indices = indices;
this.valueCount = valueCount;
}
public Iterator<long[]> iterator() {
return new NonZeroIterator(indices, valueCount);
}
}
class NonZeroIterator implements Iterator<long[]> {
private final long[] indices;
private final int valueCount;
private final long[] coordinates = new long[] { -1, 0 };
private int currentPos = -1;
public NonZeroIterator(long[] indices, int valueCount) {
this.indices = indices;
this.valueCount = valueCount;
if (valueCount > 0) {
currentPos = 0;
}
}
public boolean hasNext() {
return currentPos >= 0 && currentPos < valueCount;
}
public long[] next() {
coordinates[Matrix.ROW] = indices[currentPos];
currentPos++;
return coordinates;
}
public void remove() {
throw new RuntimeException("cannot modify matrix");
}
}