1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package org.apache.commons.math.stat.descriptive;
18
19import org.apache.commons.math.MathRuntimeException;
20import org.apache.commons.math.exception.DimensionMismatchException;
21import org.apache.commons.math.exception.NotPositiveException;
22import org.apache.commons.math.exception.NullArgumentException;
23import org.apache.commons.math.exception.util.LocalizedFormats;
24
25/**
26 * Abstract base class for all implementations of the
27 * {@link UnivariateStatistic} interface.
28 * <p>
29 * Provides a default implementation of <code>evaluate(double[]),</code>
30 * delegating to <code>evaluate(double[], int, int)</code> in the natural way.
31 * </p>
32 * <p>
33 * Also includes a <code>test</code> method that performs generic parameter
34 * validation for the <code>evaluate</code> methods.</p>
35 *
36 * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
37 */
38public abstract class AbstractUnivariateStatistic
39    implements UnivariateStatistic {
40
41    /** Stored data. */
42    private double[] storedData;
43
44    /**
45     * Set the data array.
46     * <p>
47     * The stored value is a copy of the parameter array, not the array itself
48     * </p>
49     * @param values data array to store (may be null to remove stored data)
50     * @see #evaluate()
51     */
52    public void setData(final double[] values) {
53        storedData = (values == null) ? null : values.clone();
54    }
55
56    /**
57     * Get a copy of the stored data array.
58     * @return copy of the stored data array (may be null)
59     */
60    public double[] getData() {
61        return (storedData == null) ? null : storedData.clone();
62    }
63
64    /**
65     * Get a reference to the stored data array.
66     * @return reference to the stored data array (may be null)
67     */
68    protected double[] getDataRef() {
69        return storedData;
70    }
71
72    /**
73     * Set the data array.
74     * @param values data array to store
75     * @param begin the index of the first element to include
76     * @param length the number of elements to include
77     * @see #evaluate()
78     */
79    public void setData(final double[] values, final int begin, final int length) {
80        storedData = new double[length];
81        System.arraycopy(values, begin, storedData, 0, length);
82    }
83
84    /**
85     * Returns the result of evaluating the statistic over the stored data.
86     * <p>
87     * The stored array is the one which was set by previous calls to
88     * </p>
89     * @return the value of the statistic applied to the stored data
90     */
91    public double evaluate() {
92        return evaluate(storedData);
93    }
94
95    /**
96     * {@inheritDoc}
97     */
98    public double evaluate(final double[] values) {
99        test(values, 0, 0);
100        return evaluate(values, 0, values.length);
101    }
102
103    /**
104     * {@inheritDoc}
105     */
106    public abstract double evaluate(final double[] values, final int begin, final int length);
107
108    /**
109     * {@inheritDoc}
110     */
111    public abstract UnivariateStatistic copy();
112
113    /**
114     * This method is used by <code>evaluate(double[], int, int)</code> methods
115     * to verify that the input parameters designate a subarray of positive length.
116     * <p>
117     * <ul>
118     * <li>returns <code>true</code> iff the parameters designate a subarray of
119     * positive length</li>
120     * <li>throws <code>IllegalArgumentException</code> if the array is null or
121     * or the indices are invalid</li>
122     * <li>returns <code>false</li> if the array is non-null, but
123     * <code>length</code> is 0.
124     * </ul></p>
125     *
126     * @param values the input array
127     * @param begin index of the first array element to include
128     * @param length the number of elements to include
129     * @return true if the parameters are valid and designate a subarray of positive length
130     * @throws IllegalArgumentException if the indices are invalid or the array is null
131     */
132    protected boolean test(
133        final double[] values,
134        final int begin,
135        final int length) {
136
137        if (values == null) {
138            throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
139        }
140
141        if (begin < 0) {
142            throw new NotPositiveException(LocalizedFormats.START_POSITION, begin);
143        }
144
145        if (length < 0) {
146            throw new NotPositiveException(LocalizedFormats.LENGTH, length);
147        }
148
149        if (begin + length > values.length) {
150            throw MathRuntimeException.createIllegalArgumentException(
151                  LocalizedFormats.SUBARRAY_ENDS_AFTER_ARRAY_END);
152        }
153
154        if (length == 0) {
155            return false;
156        }
157
158        return true;
159
160    }
161
162    /**
163     * This method is used by <code>evaluate(double[], double[], int, int)</code> methods
164     * to verify that the begin and length parameters designate a subarray of positive length
165     * and the weights are all non-negative, non-NaN, finite, and not all zero.
166     * <p>
167     * <ul>
168     * <li>returns <code>true</code> iff the parameters designate a subarray of
169     * positive length and the weights array contains legitimate values.</li>
170     * <li>throws <code>IllegalArgumentException</code> if any of the following are true:
171     * <ul><li>the values array is null</li>
172     *     <li>the weights array is null</li>
173     *     <li>the weights array does not have the same length as the values array</li>
174     *     <li>the weights array contains one or more infinite values</li>
175     *     <li>the weights array contains one or more NaN values</li>
176     *     <li>the weights array contains negative values</li>
177     *     <li>the start and length arguments do not determine a valid array</li></ul>
178     * </li>
179     * <li>returns <code>false</li> if the array is non-null, but
180     * <code>length</code> is 0.
181     * </ul></p>
182     *
183     * @param values the input array
184     * @param weights the weights array
185     * @param begin index of the first array element to include
186     * @param length the number of elements to include
187     * @return true if the parameters are valid and designate a subarray of positive length
188     * @throws IllegalArgumentException if the indices are invalid or the array is null
189     * @since 2.1
190     */
191    protected boolean test(
192        final double[] values,
193        final double[] weights,
194        final int begin,
195        final int length) {
196
197        if (weights == null) {
198            throw new NullArgumentException(LocalizedFormats.INPUT_ARRAY);
199        }
200
201        if (weights.length != values.length) {
202            throw new DimensionMismatchException(weights.length, values.length);
203        }
204
205        boolean containsPositiveWeight = false;
206        for (int i = begin; i < begin + length; i++) {
207            if (Double.isNaN(weights[i])) {
208                throw MathRuntimeException.createIllegalArgumentException(
209                        LocalizedFormats.NAN_ELEMENT_AT_INDEX, i);
210            }
211            if (Double.isInfinite(weights[i])) {
212                throw MathRuntimeException.createIllegalArgumentException(
213                        LocalizedFormats.INFINITE_ARRAY_ELEMENT, weights[i], i);
214            }
215            if (weights[i] < 0) {
216                throw MathRuntimeException.createIllegalArgumentException(
217                      LocalizedFormats.NEGATIVE_ELEMENT_AT_INDEX, i, weights[i]);
218            }
219            if (!containsPositiveWeight && weights[i] > 0.0) {
220                containsPositiveWeight = true;
221            }
222        }
223
224        if (!containsPositiveWeight) {
225            throw MathRuntimeException.createIllegalArgumentException(
226                    LocalizedFormats.WEIGHT_AT_LEAST_ONE_NON_ZERO);
227        }
228
229        return test(values, begin, length);
230    }
231}
232
233