package com.enterprisemath.math.statistics.observation;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.builder.ToStringBuilder;
import com.enterprisemath.math.algebra.Vector;
import com.enterprisemath.utils.ValidationUtils;
/**
* Implementation of observation provider which transforms 1 dimensional real observations
* into multidimensional vectors. Vectors are grouped according the order in line.
* Configurable is vector dimension and step size. More in the examples below.
* <p></p>
* Examples:
* <ul>
* <li>[source = [1, 2, 3, 4, 5], dimension = 2, step = 1] => [[1, 2], [2, 3], [3, 4], [4, 5]]</li>
* <li>[source = [1, 2, 3, 4, 5], dimension = 2, step = 2] => [[1, 2], [3, 4]]</li>
* <li>[source = [1, 2, 3, 4, 5], dimension = 2, step = 3] => [[1, 2], [4, 5]]</li>
* <li>[source = [1, 2, 3, 4, 5], dimension = 2, step = 4] => [[1, 2]]</li>
* <li>[source = [1, 2, 3, 4, 5], dimension = 3, step = 1] => [[1, 2, 3], [2, 3, 4], [3, 4, 5]]</li>
* <li>[source = [1, 2, 3, 4, 5], dimension = 3, step = 2] => [[1, 2, 3], [3, 4, 5]]</li>
* <li>[source = [1, 2, 3, 4, 5], dimension = 6, step = 1] => []</li>
* </ul>
*
* @author radek.hecl
*
*/
public class RealToVectorWindowObservationProvider implements ObservationProvider<Vector> {
/**
* Builder object.
*/
public static class Builder {
/**
* Source observation provider.
*/
private ObservationProvider<Double> source;
/**
* Dimension of the produced vectors.
*/
private Integer dimension;
/**
* Step size.
*/
private Integer step;
/**
* Sets the source provider.
*
* @param source source provider
* @return this instance
*/
public Builder setSource(ObservationProvider<Double> source) {
this.source = source;
return this;
}
/**
* Sets dimension of the produced vectors.
*
* @param dimension dimension of the produced vectors
* @return this instance
*/
public Builder setDimension(Integer dimension) {
this.dimension = dimension;
return this;
}
/**
* Sets step size.
*
* @param step step size
* @return this instance
*/
public Builder setStep(Integer step) {
this.step = step;
return this;
}
/**
* Builds result object.
*
* @return created object
*/
public RealToVectorWindowObservationProvider build() {
return new RealToVectorWindowObservationProvider(this);
}
}
/**
* Source observation provider.
*/
private ObservationProvider<Double> source;
/**
* Dimension of the produced vectors.
*/
private Integer dimension;
/**
* Step size.
*/
private Integer step;
/**
* Creates new instance.
*
* @param builder builder object
*/
public RealToVectorWindowObservationProvider(Builder builder) {
source = builder.source;
dimension = builder.dimension;
step = builder.step;
guardInvariants();
}
/**
* Guards this object to be consistent. Throws exception if this is not the case.
*/
private void guardInvariants() {
ValidationUtils.guardNotNull(source, "source cannot be null");
ValidationUtils.guardPositiveInt(dimension, "dimension must be positive");
ValidationUtils.guardPositiveInt(step, "step must be positive");
}
@Override
public ObservationIterator<Vector> getIterator() {
return new Iterator(source.getIterator(), dimension, step);
}
/**
* Observation iterator.
*/
private static class Iterator implements ObservationIterator<Vector> {
/**
* Source iterator.
*/
private ObservationIterator<Double> source;
/**
* Dimension.
*/
private int dimension;
/**
* Step size.
*/
private int step;
/**
* Number of iterated objects.
*/
private AtomicInteger numIterated = new AtomicInteger(0);
/**
* Next element.
*/
private Vector next = null;
/**
* Creates new instance.
*
* @param observations observations
* @param dimension dimension of the result vector
* @param step step size
*/
public Iterator(ObservationIterator<Double> source, int dimension, int step) {
this.source = source;
this.dimension = dimension;
this.step = step;
prepareFirst();
}
@Override
public boolean isNextAvailable() {
return next != null;
}
@Override
public synchronized Vector getNext() {
if (next == null) {
throw new NoSuchElementException("next observation is not available");
}
Vector res = next;
prepareNext();
numIterated.incrementAndGet();
return res;
}
@Override
public long getNumIterated() {
return numIterated.intValue();
}
/**
* Prepares first observation.
*/
private void prepareFirst() {
double[] comps = new double[dimension];
for (int i = 0; i < dimension; ++i) {
if (!source.isNextAvailable()) {
return;
}
comps[i] = source.getNext();
}
next = Vector.create(comps);
}
/**
* Prepares next observation.
*/
private void prepareNext() {
double[] comps = new double[dimension];
if (step < dimension) {
for (int i = 0; i < dimension - step; ++i) {
comps[i] = next.getComponent(step + i);
}
for (int i = dimension - step; i < dimension; ++i) {
if (!source.isNextAvailable()) {
next = null;
return;
}
comps[i] = source.getNext();
}
}
else {
for (int i = 0; i < step - dimension; ++i) {
if (!source.isNextAvailable()) {
next = null;
return;
}
source.getNext();
}
for (int i = 0; i < dimension; ++i) {
if (!source.isNextAvailable()) {
next = null;
return;
}
comps[i] = source.getNext();
}
}
next = Vector.create(comps);
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
}