/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.timeseries.precise.instant; 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.Instant; import com.opengamma.timeseries.DoubleTimeSeries; import com.opengamma.timeseries.DoubleTimeSeriesOperators.BinaryOperator; import com.opengamma.timeseries.precise.AbstractPreciseDoubleTimeSeries; import com.opengamma.timeseries.precise.PreciseDoubleTimeSeries; /** * Abstract implementation of {@code InstantDoubleTimeSeries}. */ abstract class AbstractInstantDoubleTimeSeries extends AbstractPreciseDoubleTimeSeries<Instant> implements InstantDoubleTimeSeries { /** * Creates an instance. */ public AbstractInstantDoubleTimeSeries() { } //------------------------------------------------------------------------- static long[] convertToLongArray(Collection<Instant> instants) { long[] timesArray = new long[instants.size()]; int i = 0; for (Instant time : instants) { timesArray[i++] = InstantToLongConverter.convertToLong(time); } return timesArray; } static long[] convertToLongArray(Instant[] instants) { long[] timesArray = new long[instants.length]; for (int i = 0; i < timesArray.length; i++) { timesArray[i] = InstantToLongConverter.convertToLong(instants[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<Instant, Double> makeMapEntry(Instant key, Double value) { return new SimpleImmutableEntry<Instant, Double>(key, value); } //------------------------------------------------------------------------- @Override protected long convertToLong(Instant instant) { return InstantToLongConverter.convertToLong(instant); } @Override protected Instant convertFromLong(long instant) { return InstantToLongConverter.convertToInstant(instant); } @Override protected Instant[] createArray(int size) { return new Instant[size]; } //------------------------------------------------------------------------- /** * Gets the internal storage array without cloning. * * @return the array, not null */ abstract long[] timesArrayFast0(); /** * Gets the internal storage array without cloning. * * @return the array, not null */ abstract double[] valuesArrayFast0(); /** * Creates a new instance without cloning. * * @param instant the times array, not null * @param values the values array, not null * @return the new instance, not null */ abstract InstantDoubleTimeSeries newInstanceFast(long[] instant, double[] values); //------------------------------------------------------------------------- @Override public InstantDoubleEntryIterator iterator() { return new InstantDoubleEntryIterator() { private int _index = -1; @Override public boolean hasNext() { return (_index + 1) < size(); } @Override public Entry<Instant, Double> next() { if (hasNext() == false) { throw new NoSuchElementException("No more elements in the iteration"); } _index++; long time = AbstractInstantDoubleTimeSeries.this.getTimeAtIndexFast(_index); Double value = AbstractInstantDoubleTimeSeries.this.getValueAtIndex(_index); return makeMapEntry(AbstractInstantDoubleTimeSeries.this.convertFromLong(time), value); } @Override public long nextTimeFast() { if (hasNext() == false) { throw new NoSuchElementException("No more elements in the iteration"); } _index++; return AbstractInstantDoubleTimeSeries.this.getTimeAtIndexFast(_index); } @Override public Instant nextTime() { return AbstractInstantDoubleTimeSeries.this.convertFromLong(nextTimeFast()); } @Override public long currentTimeFast() { if (_index < 0) { throw new IllegalStateException("Iterator has not yet been started"); } return AbstractInstantDoubleTimeSeries.this.getTimeAtIndexFast(_index); } @Override public Instant currentTime() { return AbstractInstantDoubleTimeSeries.this.convertFromLong(currentTimeFast()); } @Override public Double currentValue() { if (_index < 0) { throw new IllegalStateException("Iterator has not yet been started"); } return AbstractInstantDoubleTimeSeries.this.getValueAtIndex(_index); } @Override public double currentValueFast() { if (_index < 0) { throw new IllegalStateException("Iterator has not yet been started"); } return AbstractInstantDoubleTimeSeries.this.getValueAtIndexFast(_index); } @Override public int currentIndex() { return _index; } @Override public void remove() { throw new UnsupportedOperationException("Immutable iterator"); } }; } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries subSeries(Instant startInstant, Instant endInstant) { return subSeriesFast(convertToLong(startInstant), true, convertToLong(endInstant), false); } @Override public InstantDoubleTimeSeries subSeries(Instant startInstant, boolean includeStart, Instant endInstant, boolean includeEnd) { return subSeriesFast(convertToLong(startInstant), includeStart, convertToLong(endInstant), includeEnd); } @Override public InstantDoubleTimeSeries subSeriesFast(long startInstant, long endInstant) { return subSeriesFast(startInstant, true, endInstant, false); } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries lag(int days) { long[] times = timesArrayFast0(); double[] values = valuesArrayFast0(); if (days == 0) { return newInstanceFast(times, values); } else if (days < 0) { if (-days < times.length) { long[] resultTimes = new long[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 long[0], new double[0]); } } else { // if (days > 0) { if (days < times.length) { long[] resultTimes = new long[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 long[0], new double[0]); } } } //------------------------------------------------------------------------- private InstantDoubleTimeSeries operate(DoubleTimeSeries<?> other, BinaryOperator operator) { if (other instanceof PreciseDoubleTimeSeries) { return operate((PreciseDoubleTimeSeries<?>) other, operator); } throw new UnsupportedOperationException("Can only operate on a PreciseDoubleTimeSeries"); } public InstantDoubleTimeSeries operate(PreciseDoubleTimeSeries<?> other, BinaryOperator operator) { long[] aTimes = timesArrayFast0(); double[] aValues = valuesArrayFast0(); int aCount = 0; long[] bTimes = other.timesArrayFast(); double[] bValues = other.valuesArrayFast(); int bCount = 0; long[] resTimes = new long[Math.max(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++; } } long[] trimmedTimes = new long[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 InstantDoubleTimeSeries unionOperate(DoubleTimeSeries<?> other, BinaryOperator operator) { if (other instanceof PreciseDoubleTimeSeries) { return unionOperate((PreciseDoubleTimeSeries<?>) other, operator); } throw new UnsupportedOperationException("Can only operate on a PreciseDoubleTimeSeries"); } public InstantDoubleTimeSeries unionOperate(PreciseDoubleTimeSeries<?> other, BinaryOperator operator) { long[] aTimes = timesArrayFast0(); double[] aValues = valuesArrayFast0(); int aCount = 0; long[] bTimes = other.timesArrayFast(); double[] bValues = other.valuesArrayFast(); int bCount = 0; long[] resTimes = new long[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++; } } long[] trimmedTimes = new long[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 InstantDoubleTimeSeries add(double amountToAdd) { return operate(amountToAdd, ADD_OPERATOR); } @Override public InstantDoubleTimeSeries add(DoubleTimeSeries<?> other) { return operate(other, ADD_OPERATOR); } @Override public InstantDoubleTimeSeries unionAdd(DoubleTimeSeries<?> other) { return unionOperate(other, ADD_OPERATOR); } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries subtract(double amountToSubtract) { return operate(amountToSubtract, SUBTRACT_OPERATOR); } @Override public InstantDoubleTimeSeries subtract(DoubleTimeSeries<?> other) { return operate(other, SUBTRACT_OPERATOR); } @Override public InstantDoubleTimeSeries unionSubtract(DoubleTimeSeries<?> other) { return unionOperate(other, SUBTRACT_OPERATOR); } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries multiply(double amountToMultiplyBy) { return operate(amountToMultiplyBy, MULTIPLY_OPERATOR); } @Override public InstantDoubleTimeSeries multiply(DoubleTimeSeries<?> other) { return operate(other, MULTIPLY_OPERATOR); } @Override public InstantDoubleTimeSeries unionMultiply(DoubleTimeSeries<?> other) { return unionOperate(other, MULTIPLY_OPERATOR); } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries divide(double amountToDivideBy) { return operate(amountToDivideBy, DIVIDE_OPERATOR); } @Override public InstantDoubleTimeSeries divide(DoubleTimeSeries<?> other) { return operate(other, DIVIDE_OPERATOR); } @Override public InstantDoubleTimeSeries unionDivide(DoubleTimeSeries<?> other) { return unionOperate(other, DIVIDE_OPERATOR); } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries power(double power) { return operate(power, POWER_OPERATOR); } @Override public InstantDoubleTimeSeries power(DoubleTimeSeries<?> other) { return operate(other, POWER_OPERATOR); } @Override public InstantDoubleTimeSeries unionPower(DoubleTimeSeries<?> other) { return unionOperate(other, POWER_OPERATOR); } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries minimum(double minValue) { return operate(minValue, MINIMUM_OPERATOR); } @Override public InstantDoubleTimeSeries minimum(DoubleTimeSeries<?> other) { return operate(other, MINIMUM_OPERATOR); } @Override public InstantDoubleTimeSeries unionMinimum(DoubleTimeSeries<?> other) { return unionOperate(other, MINIMUM_OPERATOR); } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries maximum(double maxValue) { return operate(maxValue, MAXIMUM_OPERATOR); } @Override public InstantDoubleTimeSeries maximum(DoubleTimeSeries<?> other) { return operate(other, MAXIMUM_OPERATOR); } @Override public InstantDoubleTimeSeries unionMaximum(DoubleTimeSeries<?> other) { return unionOperate(other, MAXIMUM_OPERATOR); } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries average(double value) { return operate(value, AVERAGE_OPERATOR); } @Override public InstantDoubleTimeSeries average(DoubleTimeSeries<?> other) { return operate(other, AVERAGE_OPERATOR); } @Override public InstantDoubleTimeSeries unionAverage(DoubleTimeSeries<?> other) { return unionOperate(other, AVERAGE_OPERATOR); } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries intersectionFirstValue(DoubleTimeSeries<?> other) { // optimize PLAT-1590 if (other instanceof AbstractInstantDoubleTimeSeries) { long[] aTimes = timesArrayFast0(); double[] aValues = valuesArrayFast0(); int aCount = 0; long[] bTimes = ((AbstractInstantDoubleTimeSeries) other).timesArrayFast0(); int bCount = 0; long[] resTimes = new long[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++; } } long[] trimmedTimes = new long[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 InstantDoubleTimeSeries intersectionSecondValue(DoubleTimeSeries<?> other) { return operate(other, SECOND_OPERATOR); } @Override public InstantDoubleTimeSeries noIntersectionOperation(DoubleTimeSeries<?> other) { return unionOperate(other, NO_INTERSECTION_OPERATOR); } //------------------------------------------------------------------------- @Override public InstantDoubleTimeSeries negate() { return operate(NEGATE_OPERATOR); } @Override public InstantDoubleTimeSeries reciprocal() { return operate(RECIPROCAL_OPERATOR); } @Override public InstantDoubleTimeSeries log() { return operate(LOG_OPERATOR); } @Override public InstantDoubleTimeSeries log10() { return operate(LOG10_OPERATOR); } @Override public InstantDoubleTimeSeries 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 AbstractInstantDoubleTimeSeries) { AbstractInstantDoubleTimeSeries other = (AbstractInstantDoubleTimeSeries) obj; return Arrays.equals(timesArrayFast0(), other.timesArrayFast0()) && Arrays.equals(valuesArrayFast0(), other.valuesArrayFast0()); } if (obj instanceof PreciseDoubleTimeSeries) { PreciseDoubleTimeSeries<?> other = (PreciseDoubleTimeSeries<?>) 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()); } }