package etomica.data;

import etomica.api.IData;
import etomica.util.Function;

/**
 * AccumulatorAverage that maintains a fixed block size.
 * <p>
 * This accumulator accepts any type of Data
 */
public class AccumulatorAverageFixed extends AccumulatorAverage {

    /**
     * Default constructor sets block size to Default value, and sets the
     * interval for pushing the output data (pushInterval) to 100.
     */
    public AccumulatorAverageFixed() {
        this(1000);
    }
    
    public AccumulatorAverageFixed(int blockSize) {
        super(blockSize);
    }

    /**
     * Returns null (any data is good data)
     */
    public DataPipe getDataCaster(IEtomicaDataInfo incomingDataInfo) {
        return null;
    }

    /**
     * Add the given values to the sums and block sums. If any of the given data
     * values is NaN, method returns with no effect on accumulation sums.
     */
    public void addData(IData data) {
        if (data.isNaN())
            return;

        mostRecent.E(data);
        currentBlockSum.PE(data);
        sum.PE(data);
        work.E(data);
        work.TE(data);
        sumSquare.PE(work);
        if (--blockCountDown == 0) {//count down to zero to determine
                                    // completion of block
            doBlockSum();
        }
    }
    
    /**
     * Performs the block sum after <tt>blockSize</tt> calls to addData.
     */
    protected void doBlockSum() {
        count++;
        blockCountDown = blockSize;
        currentBlockSum.TE(1 / (double) blockSize);//compute block average
        work.E(currentBlockSum);
        work.TE(currentBlockSum);
        sumBlockSquare.PE(work);
        if (!mostRecentBlock.isNaN()) {
            work.E(currentBlockSum);
            work.TE(mostRecentBlock);
            correlationSum.PE(work);
        }
        else {
            firstBlock.E(currentBlockSum);
        }
        //reset blocks
        mostRecentBlock.E(currentBlockSum);
        currentBlockSum.E(0.0);
    }

    /**
     * Returns a DataGroup with Data instances holding the statistics kept by
     * this accumulator (as described in general comments for this class).
     */
    public IData getData() {
        if (sum == null)
            return null;
        if (count > 1) {
            // calculate block properties (these require 2 or more blocks)
            
            // block average (later discarded)
            average.E(sum);
            average.ME(currentBlockSum);
            average.TE(1.0 / (count*blockSize));
            work.E(average);
            work.TE(average);
            error.E(sumBlockSquare);
            error.TE(1 / (double) count);
            error.ME(work);

            // error's intermediate value is useful for calculating block correlation
            blockCorrelation.E(average);
            blockCorrelation.TE(2*count);
            blockCorrelation.ME(firstBlock);
            blockCorrelation.ME(mostRecentBlock);
            blockCorrelation.TE(average);
            blockCorrelation.ME(correlationSum);
            blockCorrelation.TE(1.0/(1-count));
            blockCorrelation.PE(work);
            blockCorrelation.DE(error);
            
            // ok, now finish up with error
            error.TE(1 / (double) (count - 1));
            error.map(Function.Sqrt.INSTANCE);
        }
        else {
            error.E(Double.NaN);
            blockCorrelation.E(Double.NaN);
        }
        
        long nTotalData = count*blockSize - (blockSize-blockCountDown);
        if (nTotalData > 0) {
            // now use *all* of the data
            average.E(sum);
            average.TE(1.0 / nTotalData);
            work.E(average);
            work.TE(average);
            standardDeviation.E(sumSquare);
            standardDeviation.TE(1.0 / nTotalData);
            standardDeviation.ME(work);
            standardDeviation.map(Function.Sqrt.INSTANCE);
        }
        else {
            average.E(Double.NaN);
            standardDeviation.E(Double.NaN);
        }
        return dataGroup;
    }

    /**
     * Resets all sums to zero. All statistics are cleared.
     */
    public void reset() {
        super.reset();
        if (sum == null) {
            return;
        }
        sum.E(0);
        sumBlockSquare.E(0);
        currentBlockSum.E(0);
        sumSquare.E(0);
        correlationSum.E(0);
        firstBlock.E(Double.NaN);
        mostRecentBlock.E(Double.NaN);
    }

    /**
     * Prepares the accumulator for input data.  Discards any previous 
     * contributions to statistics.
     * 
     * @param incomingDataInfo
     *            the DataInfo instance for the data that will be given to
     *            addData
     */
    public IEtomicaDataInfo processDataInfo(IEtomicaDataInfo incomingDataInfo) {
        sum = incomingDataInfo.makeData();
        sumBlockSquare = incomingDataInfo.makeData();
        currentBlockSum = incomingDataInfo.makeData();
        sumSquare = incomingDataInfo.makeData();
        firstBlock = incomingDataInfo.makeData();
        correlationSum = incomingDataInfo.makeData();
        mostRecentBlock = incomingDataInfo.makeData();
        work = incomingDataInfo.makeData();
        return super.processDataInfo(incomingDataInfo);
    }


    private static final long serialVersionUID = 1L;
    protected IData sum; //sum(value)
    protected IData sumBlockSquare;//sum(blockAvg^2)
    protected IData currentBlockSum;//block_sum(value)
    protected IData sumSquare;//sum(value^2)
    protected IData mostRecentBlock, correlationSum, firstBlock;
    protected IData work;
}

@@ -3494,17 +3494,17 @@
 ockSize 
--
++
  (blockS

fix typo