package se.l4.vibe.probes;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
/**
* Probes for creating average values.
*
* @author Andreas Holstenson
*
*/
public class Average
{
private Average()
{
}
/**
* Create an average for the entire time series.
*
* @param series
* @return
*/
public static <T extends Number> Probe<Double> forSampler(Sampler<T> series)
{
return SamplerProbes.forSampler(series, Average.<T>newOperation());
}
/**
* Create an average for the entire time series.
*
* @param series
* @param reader
* @return
*/
public static <I, T extends Number> Probe<Double> forSampler(Sampler<I> series, ValueReader<I, T> reader)
{
return SamplerProbes.forSampler(series, Average.<I, T>newOperation(reader));
}
/**
* Create an average for a time series that will keep an average for the
* specified duration.
*
* @param series
* @param duration
* @param unit
* @return
*/
public static <T extends Number> Probe<Double> forSampler(
Sampler<T> series,
long duration,
TimeUnit unit)
{
return SamplerProbes.forSampler(series, duration, unit, Average.<T>newOperation());
}
/**
* Create an average for a time series that will keep an average for the
* specified duration.
*
* @param series
* @param duration
* @param unit
* @return
*/
public static <I, T extends Number> Probe<Double> forSampler(
Sampler<I> series,
ValueReader<I, T> reader,
long duration,
TimeUnit unit)
{
return SamplerProbes.forSampler(series, duration, unit, Average.<I, T>newOperation(reader));
}
/**
* Create a probe that will average another sampled probe. This type of
* probe should be used as part of a {@link Sampler time series}.
*
* @param probe
* @return
*/
public static <T extends Number> SampledProbe<Double> forProbe(
SampledProbe<T> probe)
{
return new AveragingProbeProbe<T>(probe);
}
/**
* Create a probe that averages numeric values.
*
* @return
*/
public static AveragingProbe create()
{
return new AveragingProbe();
}
/**
* Create an operation that will calculate an average.
*
* @return
*/
public static <T extends Number> SampleOperation<T, Double> newOperation()
{
return new AverageOperation<T, T>(ValueReaders.<T>same());
}
/**
* Create an operation that will calculate an average.
*
* @return
*/
public static <I, T extends Number> SampleOperation<I, Double> newOperation(ValueReader<I, T> reader)
{
return new AverageOperation<I, T>(reader);
}
/**
* Operation that will calculate the average.
*
* @author Andreas Holstenson
*
* @param <T>
*/
private static class AverageOperation<I, O extends Number>
implements SampleOperation<I, Double>
{
private final ValueReader<I, O> reader;
private double totalSum;
private double totalEntries;
public AverageOperation(ValueReader<I, O> reader)
{
this.reader = reader;
}
@Override
public void add(I value, Collection<Sampler.Entry<I>> entries)
{
totalSum += reader.read(value).doubleValue();
totalEntries += 1;
}
@Override
public void remove(I value, Collection<Sampler.Entry<I>> entries)
{
totalSum -= reader.read(value).doubleValue();
totalEntries -= 1;
}
@Override
public Double get()
{
return totalSum / totalEntries;
}
}
/**
* Probe that calculates the average of another probe.
*
* @author Andreas Holstenson
*
* @param <T>
*/
private static class AveragingProbeProbe<T extends Number>
extends AbstractSampledProbe<Double>
{
private long accumulated;
private long samples;
private final SampledProbe<T> probe;
public AveragingProbeProbe(SampledProbe<T> probe)
{
this.probe = probe;
}
@Override
public Double peek()
{
long diff = probe.peek().longValue();
return (accumulated + diff) / (samples + 1.0);
}
@Override
protected Double sample0()
{
accumulated += probe.read().longValue();
samples++;
return accumulated / (double) samples;
}
}
}