    001    /*
    002     * Licensed to the Apache Software Foundation (ASF) under one or more
    003     * contributor license agreements.  See the NOTICE file distributed with
    004     * this work for additional information regarding copyright ownership.
    005     * The ASF licenses this file to You under the Apache License, Version 2.0
    006     * (the "License"); you may not use this file except in compliance with
    007     * the License.  You may obtain a copy of the License at
    008     *
    009     *      http://www.apache.org/licenses/LICENSE-2.0
    010     *
    011     * Unless required by applicable law or agreed to in writing, software
    012     * distributed under the License is distributed on an "AS IS" BASIS,
    013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014     * See the License for the specific language governing permissions and
    015     * limitations under the License.
    016     */
    017    package org.apache.commons.math.stat.descriptive;
    018
    019    import org.apache.commons.math.MathRuntimeException;
    020    import org.apache.commons.math.exception.DimensionMismatchException;
    021    import org.apache.commons.math.exception.NotPositiveException;
    022    import org.apache.commons.math.exception.NullArgumentException;
    023    import org.apache.commons.math.exception.util.LocalizedFormats;
    024
    025    /**
    026     * Abstract base class for all implementations of the
    027     * {@link UnivariateStatistic} interface.
    028     * <p>
    029     * Provides a default implementation of <code>evaluate(double[]),</code>
    030     * delegating to <code>evaluate(double[], int, int)</code> in the natural way.
    031     * </p>
    032     * <p>
    033     * Also includes a <code>test</code> method that performs generic parameter
    034     * validation for the <code>evaluate</code> methods.</p>
    035     *
    036     * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
    037     */
    038    public abstract class AbstractUnivariateStatistic
    039        implements UnivariateStatistic {
    040
    041        /** Stored data. */
    042        private double[] storedData;
    043
    044        /**
    045         * Set the data array.
    046         * <p>
    047         * The stored value is a copy of the parameter array, not the array itself
    048         * </p>
    049         * @param values data array to store (may be null to remove stored data)
    050         * @see #evaluate()
    051         */
    052        public void setData(final double[] values) {
    053            storedData = (values == null) ? null : values.clone();
    054        }
    055
    056        /**
    057         * Get a copy of the stored data array.
    058         * @return copy of the stored data array (may be null)
    059         */
    060        public double[] getData() {
    061            return (storedData == null) ? null : storedData.clone();
    062        }
    063
    064        /**
    065         * Get a reference to the stored data array.
    066         * @return reference to the stored data array (may be null)
    067         */
    068        protected double[] getDataRef() {
    069            return storedData;
    070        }
    071
    072        /**
    073         * Set the data array.
    074         * @param values data array to store
    075         * @param begin the index of the first element to include
    076         * @param length the number of elements to include
    077         * @see #evaluate()
    078         */
    079        public void setData(final double[] values, final int begin, final int length) {
    080            storedData = new double[length];
    081            System.arraycopy(values, begin, storedData, 0, length);
    082        }
    083
    084        /**
    085         * Returns the result of evaluating the statistic over the stored data.
    086         * <p>
    087         * The stored array is the one which was set by previous calls to
    088         * </p>
    089         * @return the value of the statistic applied to the stored data
    090         */
    091        public double evaluate() {
    092            return evaluate(storedData);
    093        }
    094
    095        /**
    096         * {@inheritDoc}
    097         */
    098        public double evaluate(final double[] values) {
    099            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