/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.timeseries.date.localdate;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.ABS_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.ADD_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.AVERAGE_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.DIVIDE_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.FIRST_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.LOG10_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.LOG_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.MAXIMUM_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.MINIMUM_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.MULTIPLY_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.NEGATE_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.NO_INTERSECTION_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.POWER_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.RECIPROCAL_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.SECOND_OPERATOR;
import static com.opengamma.timeseries.DoubleTimeSeriesOperators.SUBTRACT_OPERATOR;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import org.threeten.bp.LocalDate;
import com.opengamma.timeseries.DoubleTimeSeries;
import com.opengamma.timeseries.DoubleTimeSeriesOperators.BinaryOperator;
import com.opengamma.timeseries.date.AbstractDateDoubleTimeSeries;
import com.opengamma.timeseries.date.DateDoubleTimeSeries;
/**
* Abstract implementation of {@code LocalDateDoubleTimeSeries}.
*/
abstract class AbstractLocalDateDoubleTimeSeries
extends AbstractDateDoubleTimeSeries<LocalDate>
implements LocalDateDoubleTimeSeries {
/**
* Creates an instance.
*/
public AbstractLocalDateDoubleTimeSeries() {
}
//-------------------------------------------------------------------------
static int[] convertToIntArray(Collection<LocalDate> times) {
int[] timesArray = new int[times.size()];
int i = 0;
for (LocalDate time : times) {
timesArray[i++] = LocalDateToIntConverter.convertToInt(time);
}
return timesArray;
}
static int[] convertToIntArray(LocalDate[] dates) {
int[] timesArray = new int[dates.length];
for (int i = 0; i < timesArray.length; i++) {
timesArray[i] = LocalDateToIntConverter.convertToInt(dates[i]);
}
return timesArray;
}
static double[] convertToDoubleArray(Collection<Double> values) {
double[] valuesArray = new double[values.size()];
int i = 0;
for (Double value : values) {
valuesArray[i++] = value;
}
return valuesArray;
}
static double[] convertToDoubleArray(Double[] values) {
double[] valuesArray = new double[values.length];
for (int i = 0; i < valuesArray.length; i++) {
valuesArray[i] = values[i];
}
return valuesArray;
}
static Entry<LocalDate, Double> makeMapEntry(LocalDate key, Double value) {
return new SimpleImmutableEntry<LocalDate, Double>(key, value);
}
//-------------------------------------------------------------------------
@Override
protected int convertToInt(LocalDate date) {
return LocalDateToIntConverter.convertToInt(date);
}
@Override
protected LocalDate convertFromInt(int date) {
return LocalDateToIntConverter.convertToLocalDate(date);
}
@Override
protected LocalDate[] createArray(int size) {
return new LocalDate[size];
}
//-------------------------------------------------------------------------
/**
* Gets the internal storage array without cloning.
*
* @return the array, not null
*/
abstract int[] timesArrayFast0();
/**
* Gets the internal storage array without cloning.
*
* @return the array, not null
*/
abstract double[] valuesArrayFast0();
/**
* Creates a new instance without cloning.
*
* @param times the times array, not null
* @param values the values array, not null
* @return the new instance, not null
*/
abstract LocalDateDoubleTimeSeries newInstanceFast(int[] times, double[] values);
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleEntryIterator iterator() {
return new LocalDateDoubleEntryIterator() {
private int _index = -1;
@Override
public boolean hasNext() {
return (_index + 1) < size();
}
@Override
public Entry<LocalDate, Double> next() {
if (hasNext() == false) {
throw new NoSuchElementException("No more elements in the iteration");
}
_index++;
int date = AbstractLocalDateDoubleTimeSeries.this.getTimeAtIndexFast(_index);
Double value = AbstractLocalDateDoubleTimeSeries.this.getValueAtIndex(_index);
return makeMapEntry(AbstractLocalDateDoubleTimeSeries.this.convertFromInt(date), value);
}
@Override
public int nextTimeFast() {
if (hasNext() == false) {
throw new NoSuchElementException("No more elements in the iteration");
}
_index++;
return AbstractLocalDateDoubleTimeSeries.this.getTimeAtIndexFast(_index);
}
@Override
public LocalDate nextTime() {
return AbstractLocalDateDoubleTimeSeries.this.convertFromInt(nextTimeFast());
}
@Override
public int currentTimeFast() {
if (_index < 0) {
throw new IllegalStateException("Iterator has not yet been started");
}
return AbstractLocalDateDoubleTimeSeries.this.getTimeAtIndexFast(_index);
}
@Override
public LocalDate currentTime() {
return AbstractLocalDateDoubleTimeSeries.this.convertFromInt(currentTimeFast());
}
@Override
public Double currentValue() {
if (_index < 0) {
throw new IllegalStateException("Iterator has not yet been started");
}
return AbstractLocalDateDoubleTimeSeries.this.getValueAtIndex(_index);
}
@Override
public double currentValueFast() {
if (_index < 0) {
throw new IllegalStateException("Iterator has not yet been started");
}
return AbstractLocalDateDoubleTimeSeries.this.getValueAtIndexFast(_index);
}
@Override
public int currentIndex() {
return _index;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Immutable iterator");
}
};
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries subSeries(LocalDate startTime, LocalDate endTime) {
return subSeriesFast(convertToInt(startTime), true, convertToInt(endTime), false);
}
@Override
public LocalDateDoubleTimeSeries subSeries(LocalDate startTime, boolean includeStart, LocalDate endTime, boolean includeEnd) {
return subSeriesFast(convertToInt(startTime), includeStart, convertToInt(endTime), includeEnd);
}
@Override
public LocalDateDoubleTimeSeries subSeriesFast(int startTime, int endTime) {
return subSeriesFast(startTime, true, endTime, false);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries lag(int days) {
int[] times = timesArrayFast0();
double[] values = valuesArrayFast0();
if (days == 0) {
return newInstanceFast(times, values);
} else if (days < 0) {
if (-days < times.length) {
int[] resultTimes = new int[times.length + days]; // remember days is -ve
System.arraycopy(times, 0, resultTimes, 0, times.length + days);
double[] resultValues = new double[times.length + days];
System.arraycopy(values, -days, resultValues, 0, times.length + days);
return newInstanceFast(resultTimes, resultValues);
} else {
return newInstanceFast(new int[0], new double[0]);
}
} else { // if (days > 0) {
if (days < times.length) {
int[] resultTimes = new int[times.length - days]; // remember days is +ve
System.arraycopy(times, days, resultTimes, 0, times.length - days);
double[] resultValues = new double[times.length - days];
System.arraycopy(values, 0, resultValues, 0, times.length - days);
return newInstanceFast(resultTimes, resultValues);
} else {
return newInstanceFast(new int[0], new double[0]);
}
}
}
//-------------------------------------------------------------------------
private LocalDateDoubleTimeSeries operate(DoubleTimeSeries<?> other, BinaryOperator operator) {
if (other instanceof DateDoubleTimeSeries) {
return operate((DateDoubleTimeSeries<?>) other, operator);
}
throw new UnsupportedOperationException("Can only operate on a DateDoubleTimeSeries");
}
public LocalDateDoubleTimeSeries operate(DateDoubleTimeSeries<?> other, BinaryOperator operator) {
int[] aTimes = timesArrayFast0();
// if the series share a common set of times use the common series
if (other instanceof AbstractLocalDateDoubleTimeSeries &&
aTimes == ((AbstractLocalDateDoubleTimeSeries) other).timesArrayFast0()) {
return operateWithSameTimes(other, operator);
}
double[] aValues = valuesArrayFast0();
int aCount = 0;
int[] bTimes = other.timesArrayFast();
double[] bValues = other.valuesArrayFast();
int bCount = 0;
int[] resTimes = new int[Math.min(aTimes.length, bTimes.length)];
double[] resValues = new double[resTimes.length];
int resCount = 0;
while (aCount < aTimes.length && bCount < bTimes.length) {
if (aTimes[aCount] == bTimes[bCount]) {
resTimes[resCount] = aTimes[aCount];
resValues[resCount] = operator.operate(aValues[aCount], bValues[bCount]);
resCount++;
aCount++;
bCount++;
} else if (aTimes[aCount] < bTimes[bCount]) {
aCount++;
} else { // if (aTimes[aCount] > bTimes[bCount]) {
bCount++;
}
}
int[] trimmedTimes = new int[resCount];
double[] trimmedValues = new double[resCount];
System.arraycopy(resTimes, 0, trimmedTimes, 0, resCount);
System.arraycopy(resValues, 0, trimmedValues, 0, resCount);
return newInstanceFast(trimmedTimes, trimmedValues);
}
private LocalDateDoubleTimeSeries unionOperate(DoubleTimeSeries<?> other, BinaryOperator operator) {
if (other instanceof DateDoubleTimeSeries) {
return unionOperate((DateDoubleTimeSeries<?>) other, operator);
}
throw new UnsupportedOperationException("Can only operate on a DateDoubleTimeSeries");
}
public LocalDateDoubleTimeSeries unionOperate(DateDoubleTimeSeries<?> other, BinaryOperator operator) {
int[] aTimes = timesArrayFast0();
// if the series share a common set of times use the common series
if (other instanceof AbstractLocalDateDoubleTimeSeries &&
aTimes == ((AbstractLocalDateDoubleTimeSeries) other).timesArrayFast0()) {
return operateWithSameTimes(other, operator);
}
double[] aValues = valuesArrayFast0();
int aCount = 0;
int[] bTimes = other.timesArrayFast();
double[] bValues = other.valuesArrayFast();
int bCount = 0;
int[] resTimes = new int[aTimes.length + bTimes.length];
double[] resValues = new double[resTimes.length];
int resCount = 0;
while (aCount < aTimes.length || bCount < bTimes.length) {
if (aCount >= aTimes.length) {
int bRemaining = bTimes.length - bCount;
System.arraycopy(bTimes, bCount, resTimes, resCount, bRemaining);
System.arraycopy(bValues, bCount, resValues, resCount, bRemaining);
resCount += bRemaining;
break;
} else if (bCount >= bTimes.length) {
int aRemaining = aTimes.length - aCount;
System.arraycopy(aTimes, aCount, resTimes, resCount, aRemaining);
System.arraycopy(aValues, aCount, resValues, resCount, aRemaining);
resCount += aRemaining;
break;
} else if (aTimes[aCount] == bTimes[bCount]) {
resTimes[resCount] = aTimes[aCount];
resValues[resCount] = operator.operate(aValues[aCount], bValues[bCount]);
resCount++;
aCount++;
bCount++;
} else if (aTimes[aCount] < bTimes[bCount]) {
resTimes[resCount] = aTimes[aCount];
resValues[resCount] = aValues[aCount];
resCount++;
aCount++;
} else { // if (aTimes[aCount] > bTimes[bCount]) {
resTimes[resCount] = bTimes[bCount];
resValues[resCount] = bValues[bCount];
resCount++;
bCount++;
}
}
int[] trimmedTimes = new int[resCount];
double[] trimmedValues = new double[resCount];
System.arraycopy(resTimes, 0, trimmedTimes, 0, resCount);
System.arraycopy(resValues, 0, trimmedValues, 0, resCount);
return newInstanceFast(trimmedTimes, trimmedValues);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries add(double amountToAdd) {
return operate(amountToAdd, ADD_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries add(DoubleTimeSeries<?> other) {
return operate(other, ADD_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries unionAdd(DoubleTimeSeries<?> other) {
return unionOperate(other, ADD_OPERATOR);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries subtract(double amountToSubtract) {
return operate(amountToSubtract, SUBTRACT_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries subtract(DoubleTimeSeries<?> other) {
return operate(other, SUBTRACT_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries unionSubtract(DoubleTimeSeries<?> other) {
return unionOperate(other, SUBTRACT_OPERATOR);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries multiply(double amountToMultiplyBy) {
return operate(amountToMultiplyBy, MULTIPLY_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries multiply(DoubleTimeSeries<?> other) {
return operate(other, MULTIPLY_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries unionMultiply(DoubleTimeSeries<?> other) {
return unionOperate(other, MULTIPLY_OPERATOR);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries divide(double amountToDivideBy) {
return operate(amountToDivideBy, DIVIDE_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries divide(DoubleTimeSeries<?> other) {
return operate(other, DIVIDE_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries unionDivide(DoubleTimeSeries<?> other) {
return unionOperate(other, DIVIDE_OPERATOR);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries power(double power) {
return operate(power, POWER_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries power(DoubleTimeSeries<?> other) {
return operate(other, POWER_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries unionPower(DoubleTimeSeries<?> other) {
return unionOperate(other, POWER_OPERATOR);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries minimum(double minValue) {
return operate(minValue, MINIMUM_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries minimum(DoubleTimeSeries<?> other) {
return operate(other, MINIMUM_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries unionMinimum(DoubleTimeSeries<?> other) {
return unionOperate(other, MINIMUM_OPERATOR);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries maximum(double maxValue) {
return operate(maxValue, MAXIMUM_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries maximum(DoubleTimeSeries<?> other) {
return operate(other, MAXIMUM_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries unionMaximum(DoubleTimeSeries<?> other) {
return unionOperate(other, MAXIMUM_OPERATOR);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries average(double value) {
return operate(value, AVERAGE_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries average(DoubleTimeSeries<?> other) {
return operate(other, AVERAGE_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries unionAverage(DoubleTimeSeries<?> other) {
return unionOperate(other, AVERAGE_OPERATOR);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries intersectionFirstValue(DoubleTimeSeries<?> other) {
// optimize PLAT-1590
if (other instanceof AbstractLocalDateDoubleTimeSeries) {
int[] aTimes = timesArrayFast0();
double[] aValues = valuesArrayFast0();
int aCount = 0;
int[] bTimes = ((AbstractLocalDateDoubleTimeSeries) other).timesArrayFast0();
int bCount = 0;
int[] resTimes = new int[Math.min(aTimes.length, bTimes.length)];
double[] resValues = new double[resTimes.length];
int resCount = 0;
while (aCount < aTimes.length && bCount < bTimes.length) {
if (aTimes[aCount] == bTimes[bCount]) {
resTimes[resCount] = aTimes[aCount];
resValues[resCount] = aValues[aCount];
resCount++;
aCount++;
bCount++;
} else if (aTimes[aCount] < bTimes[bCount]) {
aCount++;
} else { // if (aTimes[aCount] > bTimes[bCount]) {
bCount++;
}
}
int[] trimmedTimes = new int[resCount];
double[] trimmedValues = new double[resCount];
System.arraycopy(resTimes, 0, trimmedTimes, 0, resCount);
System.arraycopy(resValues, 0, trimmedValues, 0, resCount);
return newInstanceFast(trimmedTimes, trimmedValues);
}
return operate(other, FIRST_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries intersectionSecondValue(DoubleTimeSeries<?> other) {
return operate(other, SECOND_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries noIntersectionOperation(DoubleTimeSeries<?> other) {
return unionOperate(other, NO_INTERSECTION_OPERATOR);
}
//-------------------------------------------------------------------------
@Override
public LocalDateDoubleTimeSeries negate() {
return operate(NEGATE_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries reciprocal() {
return operate(RECIPROCAL_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries log() {
return operate(LOG_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries log10() {
return operate(LOG10_OPERATOR);
}
@Override
public LocalDateDoubleTimeSeries abs() {
return operate(ABS_OPERATOR);
}
//-------------------------------------------------------------------------
@Override
public double maxValue() {
if (isEmpty()) {
throw new NoSuchElementException("Time-series is empty");
}
double max = Double.MIN_VALUE;
for (double value : valuesArrayFast0()) {
max = Math.max(max, value);
}
return max;
}
@Override
public double minValue() throws NoSuchElementException {
if (isEmpty()) {
throw new NoSuchElementException("Time-series is empty");
}
double min = Double.MAX_VALUE;
for (double value : valuesArrayFast0()) {
min = Math.min(min, value);
}
return min;
}
//-------------------------------------------------------------------------
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof AbstractLocalDateDoubleTimeSeries) {
AbstractLocalDateDoubleTimeSeries other = (AbstractLocalDateDoubleTimeSeries) obj;
return Arrays.equals(timesArrayFast0(), other.timesArrayFast0()) &&
Arrays.equals(valuesArrayFast0(), other.valuesArrayFast0());
}
if (obj instanceof DateDoubleTimeSeries) {
DateDoubleTimeSeries<?> other = (DateDoubleTimeSeries<?>) obj;
return Arrays.equals(timesArrayFast0(), other.timesArrayFast()) &&
Arrays.equals(valuesArrayFast0(), other.valuesArrayFast());
}
return false;
}
@Override
public int hashCode() {
return Arrays.hashCode(timesArrayFast0()) ^ Arrays.hashCode(valuesArrayFast0());
}
private LocalDateDoubleTimeSeries operateWithSameTimes(DateDoubleTimeSeries<?> other, BinaryOperator operator) {
// series share a common set of times so use the common series
int[] aTimes = timesArrayFast0();
double[] aValues = valuesArrayFast0();
double[] resValues = new double[aTimes.length];
for (int i = 0; i < aTimes.length; i++) {
resValues[i] = operator.operate(aValues[i], other.getValueAtIndexFast(i));
}
return newInstanceFast(aTimes, resValues);
}
}