package io.sigpipe.sing.stat;

import org.apache.commons.math3.util.FastMath;

public class RunningStatisticsND {

    private long n;

    private double[] mean;
    private double[] m2;
    private double[] min;
    private double[] max;

    private double[] ss;

    public RunningStatisticsND(int size) {
        this.mean = new double[size];
        this.m2 = new double[size];
        this.min = new double[size];
        this.max = new double[size];

        for (int d = 0; d < size; ++d) {
            this.min[d] = Double.MAX_VALUE;
            this.max[d] = Double.MIN_VALUE;
        }

        ss = new double[size * (size - 1) / 2];
    }

    /**
     * Add a new sample to the running statistics.
     */
    public void put(double... samples) {
        if (samples.length != this.dimensions()) {
            throw new IllegalArgumentException("Input dimension mismatch: "
                    + samples.length + " =/= " + this.dimensions());
        }

        n++;

        for (int i = 0; i < this.dimensions() - 1; ++i) {
            for (int j = i + 1; j < this.dimensions(); ++j) {
                double dx = samples[i] - mean[i];
                double dy = samples[j] - mean[j];
                int index = index1D(i, j);
                ss[index] += dx * dy * n() / (n() + 1);
            }
        }

        for (int d = 0; d < this.dimensions(); ++d) {
            double delta = samples[d] - mean[d];
            mean[d] = mean[d] + delta / n;
            m2[d] = m2[d] + delta * (samples[d] - mean[d]);

            min[d] = FastMath.min(min[d], samples[d]);
            max[d] = FastMath.max(max[d], samples[d]);
        }
    }

    private int index1D(int i, int j) {
        int dims = this.dimensions();
        return (dims * (dims - 1) / 2)
            - (dims - i) * ((dims - i) - 1) / 2 + j - i - 1;
    }

    public int dimensions() {
        return mean.length;
    }

    public long n() {
        return this.n;
    }

}

@@ -24,16 +24,45 @@
 .stat;%0A%0A
+import java.io.IOException;%0A%0A
 import o
@@ -101,16 +101,199 @@
 tMath;%0A%0A
+import io.sigpipe.sing.serialization.ByteSerializable;%0Aimport io.sigpipe.sing.serialization.SerializationInputStream;%0Aimport io.sigpipe.sing.serialization.SerializationOutputStream;%0A%0A
 public c
@@ -317,16 +317,44 @@
 sticsND 
+implements ByteSerializable 
 %7B%0A%0A    p
@@ -2196,10 +2196,1221 @@
 %0A    %7D%0A%0A
+    @Deserialize%0A    public RunningStatisticsND(SerializationInputStream in)%0A    throws IOException %7B%0A        int dimensions = in.readInt();%0A        this.mean = new double%5Bdimensions%5D;%0A        this.m2 = new double%5Bdimensions%5D;%0A        this.min = new double%5Bdimensions%5D;%0A        this.max = new double%5Bdimensions%5D;%0A        this.ss = new double%5Bdimensions * (dimensions - 1) / 2%5D;%0A%0A        this.n = in.readLong();%0A%0A        for (int i = 0; i %3C dimensions; ++i) %7B%0A            mean%5Bi%5D = in.readDouble();%0A            m2%5Bi%5D = in.readDouble();%0A            min%5Bi%5D = in.readDouble();%0A            max%5Bi%5D = in.readDouble();%0A        %7D%0A%0A        for (int i = 0; i %3C this.ss.length; ++i) %7B%0A            ss%5Bi%5D = in.readDouble();%0A        %7D%0A    %7D%0A%0A    @Override%0A    public void serialize(SerializationOutputStream out)%0A    throws IOException %7B%0A        out.writeInt(this.dimensions());%0A%0A        out.writeLong(this.n());%0A%0A        for (int i = 0; i %3C this.dimensions(); ++i) %7B%0A            out.writeDouble(mean%5Bi%5D);%0A            out.writeDouble(m2%5Bi%5D);%0A            out.writeDouble(min%5Bi%5D);%0A            out.writeDouble(max%5Bi%5D);%0A        %7D%0A%0A        for (int i = 0; i %3C ss.length; ++i) %7B%0A            out.writeDouble(ss%5Bi%5D);%0A        %7D%0A    %7D%0A%0A
 %7D%0A

Implement serialization