/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; If not, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.math.statistics.parametric.aggregates; import xxl.core.math.functions.AggregationFunction; /** * Computes the average of the n last seen * {@link java.lang.Number numbers} without any error control. * <br> * <p><b>Objects of this type are recommended for use with aggregator cursors!</b></p> * <br> * Each aggregation function must support a function call of the following type:<br> * <tt>agg_n = f (agg_n-1, next)</tt>, <br> * where <tt>agg_n</tt> denotes the computed aggregation value after <tt>n</tt> steps, * <tt>f</tt> the aggregation function, * <tt>agg_n-1</tt> the computed aggregation value after <tt>n-1</tt> steps * and <tt>next</tt> the next object to use for computation. * An aggregation function delivers only <tt>null</tt> as aggregation result as long as the aggregation * function has not yet fully initialized, meaning for this class the first (n-1)-th delivered * objects are <tt>null</tt>! * * Consider the following example: * <code><pre> * Aggregator agg = new Aggregator( new DiscreteRandomNumber(new JavaDiscreteRandomWrapper(100), 50), // input-Cursor new LastNthAverage(10) // aggregate function ); * <\code><\pre> * <br> * * @see xxl.core.cursors.mappers.Aggregator * @see xxl.core.functions.Function * @see xxl.core.math.statistics.parametric.aggregates.StatefulAverage */ public class LastNthAverage extends AggregationFunction<Number,Number> { // /** stores the number of already processed objects */ // protected int count; /** number of objects to compute the average */ protected int n; /** position of the number in the internally stored array to substitute next */ protected int pos; /** internally stored numbers */ protected double[] store; /** internally stored sum of the n last seen numbers */ protected double sum; /** indicates whether the average has been initialized or not */ protected boolean init; /** Constructs a new object of this type. * * @param n number of objects to compute the average from * @throws IllegalArgumentException if a number less or equal 0 is given */ public LastNthAverage(int n) throws IllegalArgumentException { if (n < 1) throw new IllegalArgumentException( "Can't compute the average of the last " + n + " numbers! There has to be given a number n >= 1!"); this.n = n; pos = 0; // count = 0; sum = 0.0; init = false; store = new double[n]; } /** Two-figured function call for supporting aggregation by this function. * Each aggregation function must support a function call like <tt>agg_n = f (agg_n-1, next)</tt>, * where <tt>agg_n</tt> denotes the computed aggregation value after <tt>n</tt> steps, <tt>f</tt> * the aggregation function, <tt>agg_n-1</tt> the computed aggregation value after <tt>n-1</tt> steps * and <tt>next</tt> the next object to use for computation. * This method delivers only <tt>null</tt> as aggregation result as long as the aggregation * has not yet initialized. * * @param average result of the aggregation function of the last n seen elements * numbers in the previous computation step * @param next next number used for computation * @return actual valid aggregation value */ public Number invoke(Number average, Number next) { // // reinit if a previous aggregation value == null is given // // and the aggregation function has already been initialized // if ((average == null) && (init)) { // pos = 0; // count = -1; // sum = 0.0; // init = false; // store = new double[n]; // } // count++; // if (!init) { // store[count] = ((Number) next).doubleValue(); // if (count == n - 1) { // for (int i = 0; i < store.length; i++) // sum += store[i]; // init = true; // } else { // return null; // } // } else { // sum = sum - store[pos] + ((Number) next).doubleValue(); // store[pos] = ((Number) next).doubleValue(); // pos++; // if (pos == n) // pos = 0; // } // return new Double(sum / n); if (next == null) return average; else { // reinit if a previous aggregation value == null is given // and the aggregation function has already been initialized if ((average == null) && (init)) { pos = 0; sum = 0.0; init = false; store = new double[n]; } if (init) sum -= store[pos]; sum += (store[pos++] = next.doubleValue()); if (pos == n) { init = true; pos = 0; } return init ? new Double(sum / n) : null; } } }