NaNTransformers.java

  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.  */
  17. package org.apache.commons.statistics.descriptive;

  18. /**
  19.  * Support for creating {@link NaNTransformer} implementations.
  20.  *
  21.  * @since 1.1
  22.  */
  23. final class NaNTransformers {

  24.     /** No instances. */
  25.     private NaNTransformers() {}

  26.     /**
  27.      * Creates a {@link NaNTransformer} based on the
  28.      * {@code nanPolicy} and data {@code copy} policy.
  29.      *
  30.      * <p>The transformer is thread-safe.
  31.      *
  32.      * @param nanPolicy NaN policy.
  33.      * @param copy Set to {@code true} to use a copy of the data.
  34.      * @return the transformer
  35.      */
  36.     static NaNTransformer createNaNTransformer(NaNPolicy nanPolicy, boolean copy) {
  37.         if (nanPolicy == NaNPolicy.INCLUDE) {
  38.             return new IncludeNaNTransformer(copy);
  39.         }
  40.         if (nanPolicy == NaNPolicy.EXCLUDE) {
  41.             return new ExcludeNaNTransformer(copy);
  42.         }
  43.         // NaNPolicy.ERROR
  44.         return new ErrorNaNTransformer(copy);
  45.     }

  46.     /**
  47.      * A NaN transformer that optionally copies the data.
  48.      * No NaN processing is done as it is assumed that downstream sorting will
  49.      * move NaN to the end of the data.
  50.      */
  51.     private static final class IncludeNaNTransformer implements NaNTransformer {
  52.         /** Set to {@code true} to use a copy of the data. */
  53.         private final boolean copy;

  54.         /**
  55.          * @param copy Set to {@code true} to use a copy of the data.
  56.          */
  57.         IncludeNaNTransformer(boolean copy) {
  58.             this.copy = copy;
  59.         }

  60.         @Override
  61.         public double[] apply(double[] data, int[] bounds) {
  62.             bounds[0] = data.length;
  63.             if (copy) {
  64.                 return data.clone();
  65.             }
  66.             return data;
  67.         }
  68.     }

  69.     /**
  70.      * A transformer that moves {@code NaN} to the upper end of the array.
  71.      */
  72.     private static final class ExcludeNaNTransformer implements NaNTransformer {
  73.         /** Set to {@code true} to use a copy of the data. */
  74.         private final boolean copy;

  75.         /**
  76.          * @param copy Set to {@code true} to use a copy of the data.
  77.          */
  78.         ExcludeNaNTransformer(boolean copy) {
  79.             this.copy = copy;
  80.         }

  81.         @Override
  82.         public double[] apply(double[] data, int[] bounds) {
  83.             // Optionally work on a copy
  84.             final double[] a = copy ? data.clone() : data;
  85.             // Move NaN to end
  86.             int end = a.length;
  87.             for (int i = end; --i >= 0;) {
  88.                 final double v = a[i];
  89.                 if (v != v) {
  90.                     a[i] = a[--end];
  91.                     a[end] = v;
  92.                 }
  93.             }
  94.             // Set the size excluding NaN
  95.             bounds[0] = end;
  96.             return a;
  97.         }
  98.     }

  99.     /**
  100.      * A transformer that errors on {@code NaN}.
  101.      */
  102.     private static final class ErrorNaNTransformer implements NaNTransformer {
  103.         /** Set to {@code true} to use a copy of the data. */
  104.         private final boolean copy;

  105.         /**
  106.          * @param copy Set to {@code true} to use a copy of the data.
  107.          */
  108.         ErrorNaNTransformer(boolean copy) {
  109.             this.copy = copy;
  110.         }

  111.         @Override
  112.         public double[] apply(double[] data, int[] bounds) {
  113.             // Delay copy until data is checked for NaN
  114.             final double[] a = data;
  115.             // Error on NaN
  116.             for (int i = a.length; --i >= 0;) {
  117.                 final double v = a[i];
  118.                 if (v != v) {
  119.                     throw new IllegalArgumentException("NaN at " + i);
  120.                 }
  121.             }
  122.             bounds[0] = a.length;
  123.             // No NaNs so copy the data if required
  124.             if (copy) {
  125.                 return data.clone();
  126.             }
  127.             return data;
  128.         }
  129.     }
  130. }