package org.jgroups.util;
import java.io.DataInput;
import java.io.DataOutput;
/**
* Maintains an approximation of an average of values. Done by keeping track of the number of samples, and computing
* the new average as (count*avg + new-sample) / ++count. We reset the count if count*avg would lead to an overflow.<p/>
* This class is not thread-safe and relies on external synchronization.
* @author Bela Ban
* @since 3.4
*/
public class Average implements Streamable {
protected double avg;
protected long count;
public <T extends Average> T add(long num) {
if(num < 0)
return (T)this;
// If the product of the average and the number of samples would be greater than Long.MAX_VALUE, we have
// to reset the count and average to prevent a long overflow. This will temporarily lose the sample history, and
// the next sample will be the new average, but with more data points, the average should become more precise.
// Note that overflow should be extremely seldom, as we usually use Average in cases where we don't have a huge
// number of sample and the average is pretty small (e.g. an RPC invocation)
if(Util.productGreaterThan(count, (long)Math.ceil(avg), Long.MAX_VALUE))
clear();
double total=count * avg;
avg=(total + num) / ++count;
return (T)this;
}
/** Merges this average with another one */
public <T extends Average> T merge(T other) {
if(Util.productGreaterThan(count, (long)Math.ceil(avg), Long.MAX_VALUE) ||
Util.productGreaterThan(other.count(), (long)Math.ceil(other.average()), Long.MAX_VALUE)) {
// the above computation is not correct as the sum of the 2 products can still lead to overflow
// a non-weighted avg
avg=avg + other.average() / 2.0;
}
else { // compute the new average weighted by count
long total_count=count + other.count();
avg=(count * avg + other.count() * other.average()) / total_count;
count=total_count/2;
}
return (T)this;
}
public double getAverage() {return avg;}
public double average() {return avg;}
public long getCount() {return count;}
public long count() {return count;}
public void clear() {
avg=0.0;
count=0;
}
public String toString() {
return String.valueOf(getAverage());
}
public void writeTo(DataOutput out) throws Exception {
out.writeDouble(avg);
Bits.writeLong(count, out);
}
public void readFrom(DataInput in) throws Exception {
avg=in.readDouble();
count=Bits.readLong(in);
}
}