/* JAI-Ext - OpenSource Java Advanced Image Extensions Library * http://www.geo-solutions.it/ * Copyright 2014 GeoSolutions * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package it.geosolutions.jaiext.stats; import static org.junit.Assert.*; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import it.geosolutions.jaiext.range.Range; import it.geosolutions.jaiext.range.RangeFactory; import it.geosolutions.jaiext.stats.Statistics.StatsType; import it.geosolutions.jaiext.testclasses.TestBase; import org.junit.BeforeClass; import org.junit.Test; /** * This test-class is used for evaluating if the SimpleStatsOpImage class computes correctly the requested statistics. For this purpose 6 methods are * created, each one testing a particular case: * <ul> * <li>Calculation without ROI and No Data.</li> * <li>Calculation with ROI but without No Data.</li> * <li>Calculation with ROI, using a RasterAccessor, but without No Data.</li> * <li>Calculation with NoData but without ROI.</li> * <li>Calculation with ROI and No Data.</li> * <li>Calculation with ROI, using a RasterAccessor, and No Data.</li> * </ul> * * All of these cases are tested on all the JAI data types (Byte,Short, Unsigned Short, Integer, Float, Double), and on both 1 and 2 bands. The * correct statistics are calculated before the test executions, and then are compared inside all the tests. Every single test call the * testStatistics() method which calls the SimpleStatsDescriptor.create() method for returning a new instance of the SimpleStatsOpImage, then * calculates the statistics and finally compares them with the initial precalculated values. */ public class CompleteStatsTest extends TestBase { /** Tolerance value used for comparison between double */ private final static double TOLERANCE = 0.1d; /** Horizontal subsampling parameter */ private static int xPeriod; /** Vertical subsampling parameter */ private static int yPeriod; /** ROI object used for selecting the active area of the image */ private static ROI roi; /** No Data value for Byte */ private static byte noDataB; /** No Data value for UShort */ private static short noDataU; /** No Data value for Short */ private static short noDataS; /** No Data value for Integer */ private static int noDataI; /** No Data value for Float */ private static float noDataF; /** No Data value for Double */ private static double noDataD; /** No Data Range for Byte */ private static Range noDataByte; /** No Data Range for UShort */ private static Range noDataUShort; /** No Data Range for Short */ private static Range noDataShort; /** No Data Range for Integer */ private static Range noDataInt; /** No Data Range for Float */ private static Range noDataFloat; /** No Data Range for Double */ private static Range noDataDouble; /** Array indicating the statistics to calculate */ private static StatsType[] simpleStats; /** Array indicating the statistics to calculate */ private static StatsType[] complexStats; /** Array with only one band index */ private static int[] band1; /** Array with 2 band indexes */ private static int[] band2; /** Source Image array */ private static RenderedImage[] sourceIMG; /** * 2-D array containing the results of all the selected statistics calculated manually, the second dimension takes in account if NO DATA or ROI * are used */ private static double[][] calculations; /** Array containing the number of all samples, every entry indicates takes in account if ROI or No Data */ private static long[] numSamples; /** 2-D array containing the histogram results for each kind of evaluation (With ROI, With NoData, With/Without ROI and NoData) */ private static double[][] hist; /** Array indicating the minimum bounds for each band */ private static double[] minBound; /** Array indicating the maximum bounds for each band */ private static double[] maxBound; /** Array indicating the size of one bin for each band */ private static double[] binInterval; /** Array indicating the number of bins for each band */ private static int[] numBins; /** Array containing the histogram range for each band */ private static Range[] interval; /** List array containing the pixel values lists used for calculating the median for each kind of evaluation */ private static List<Double>[] listData; // Initial static method for preparing all the test data @BeforeClass public static void initialSetup() { // Subsampling params definitions xPeriod = 1; yPeriod = 1; // Definition of the Histogram, Mode and Median parameters minBound = new double[] { -3, -3, -3 }; maxBound = new double[] { 3, 3, 3 }; numBins = new int[] { 4, 4, 4 }; double binInterval0 = ((maxBound[0] - minBound[0]) / numBins[0]); binInterval = new double[] { binInterval0, binInterval0, binInterval0 }; interval = new Range[3]; interval[0] = RangeFactory.create(minBound[0], true, maxBound[0], false, false); interval[1] = RangeFactory.create(minBound[1], true, maxBound[1], false, false); interval[2] = RangeFactory.create(minBound[2], true, maxBound[2], false, false); hist = new double[numBins[0]][4]; listData = new List[4]; for (int i = 0; i < listData.length; i++) { listData[i] = new ArrayList<Double>(); } // ROI creation Rectangle roiBounds = new Rectangle(5, 5, DEFAULT_WIDTH / 4, DEFAULT_HEIGHT / 4); roi = new ROIShape(roiBounds); // No Data Range creation // No Data values noDataB = 50; noDataU = 50; noDataS = 50; noDataI = 50; noDataF = 50; noDataD = 50; boolean minIncluded = true; boolean maxIncluded = true; noDataByte = RangeFactory.create(noDataB, minIncluded, noDataB, maxIncluded); noDataUShort = RangeFactory.createU(noDataU, minIncluded, noDataU, maxIncluded); noDataShort = RangeFactory.create(noDataS, minIncluded, noDataS, maxIncluded); noDataInt = RangeFactory.create(noDataI, minIncluded, noDataI, maxIncluded); noDataFloat = RangeFactory.create(noDataF, minIncluded, noDataF, maxIncluded, true); noDataDouble = RangeFactory.create(noDataD, minIncluded, noDataD, maxIncluded, true); // Statistics types simpleStats = new StatsType[] { StatsType.MEAN, StatsType.SUM, StatsType.MAX, StatsType.MIN, StatsType.EXTREMA, StatsType.VARIANCE, StatsType.DEV_STD }; complexStats = new StatsType[] { StatsType.HISTOGRAM, StatsType.MODE, StatsType.MEDIAN }; // Band array creation int[] array1Band = { 0 }; int[] array2Band = { 0, 2 }; band1 = array1Band; band2 = array2Band; // Image creations sourceIMG = new RenderedImage[6]; sourceIMG[0] = createTestImage(DataBuffer.TYPE_BYTE, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataB, false); sourceIMG[1] = createTestImage(DataBuffer.TYPE_USHORT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataU, false); sourceIMG[2] = createTestImage(DataBuffer.TYPE_SHORT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataS, false); sourceIMG[3] = createTestImage(DataBuffer.TYPE_INT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataI, false); sourceIMG[4] = createTestImage(DataBuffer.TYPE_FLOAT, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataF, false); sourceIMG[5] = createTestImage(DataBuffer.TYPE_DOUBLE, DEFAULT_WIDTH, DEFAULT_HEIGHT, noDataD, false); // Statistical calculation numSamples = new long[4]; calculations = new double[9][4]; int minTileX = sourceIMG[0].getMinTileX(); int minTileY = sourceIMG[0].getMinTileY(); int maxTileX = minTileX + sourceIMG[0].getNumXTiles(); int maxTileY = minTileY + sourceIMG[0].getNumYTiles(); calculations[2][0] = Double.NEGATIVE_INFINITY; calculations[2][1] = Double.NEGATIVE_INFINITY; calculations[2][2] = Double.NEGATIVE_INFINITY; calculations[2][3] = Double.NEGATIVE_INFINITY; calculations[3][0] = Double.POSITIVE_INFINITY; calculations[3][1] = Double.POSITIVE_INFINITY; calculations[3][2] = Double.POSITIVE_INFINITY; calculations[3][3] = Double.POSITIVE_INFINITY; // Cycle on all pixels of the 6 images for (int i = minTileX; i < maxTileX; i++) { for (int j = minTileY; j < maxTileY; j++) { // Selection of a Raster Raster arrayRas = sourceIMG[0].getTile(i, j); int minX = arrayRas.getMinX(); int minY = arrayRas.getMinY(); int maxX = minX + arrayRas.getWidth(); int maxY = minY + arrayRas.getHeight(); // Cycle on the Raster pixels for (int x = minX; x < maxX; x++) { for (int y = minY; y < maxY; y++) { byte value = (byte) arrayRas.getSample(x, y, 0); // Cycle for all the 4 cases: No roi and No NoData, only roi, only NoData, both roi and NoData for (int z = 0; z < 4; z++) { switch (z) { case 0: // update of the number of samples numSamples[z]++; // update of the sum of samples calculations[1][z] += value; // update of the squared sum of samples calculations[4][z] += value * value; // update of the minimum and maximum values if (value > calculations[2][z]) { calculations[2][z] = value; } if (value < calculations[3][z]) { calculations[3][z] = value; } // update of histogram, mode and median containers if (interval[0].contains((double) value)) { int index = getIndex(value); hist[index][z]++; listData[z].add((double) value); } break; case 1: if (roi.contains(x, y)) { numSamples[z]++; calculations[1][z] += value; calculations[4][z] += value * value; if (value > calculations[2][z]) { calculations[2][z] = value; } if (value < calculations[3][z]) { calculations[3][z] = value; } if (interval[0].contains((double) value)) { int index = getIndex(value); hist[index][z]++; listData[z].add((double) value); } } break; case 2: if (!noDataByte.contains(value)) { numSamples[z]++; calculations[1][z] += value; calculations[4][z] += value * value; if (value > calculations[2][z]) { calculations[2][z] = value; } if (value < calculations[3][z]) { calculations[3][z] = value; } if (interval[0].contains((double) value)) { int index = getIndex(value); hist[index][z]++; listData[z].add((double) value); } } break; case 3: if (!noDataByte.contains(value) && roi.contains(x, y)) { numSamples[z]++; calculations[1][z] += value; calculations[4][z] += value * value; if (value > calculations[2][z]) { calculations[2][z] = value; } if (value < calculations[3][z]) { calculations[3][z] = value; } if (interval[0].contains((double) value)) { int index = getIndex(value); hist[index][z]++; listData[z].add((double) value); } } break; } } } } } } // Final calculation for (int h = 0; h < 6; h++) { for (int z = 0; z < 4; z++) { // calculation of the mean if(numSamples[z]==1){ calculations[0][z] = calculations[1][z] / (numSamples[z]); }else{ calculations[0][z] = calculations[1][z] / (numSamples[z] - 1); } // Calculation of the variance calculations[5][z] = (calculations[4][z] - (calculations[1][z] * calculations[1][z]) / (numSamples[z])) / (numSamples[z] - 1); // Calculation of the standard deviation calculations[6][z] = Math.sqrt(calculations[5][z]); // Calculation of the median Collections.sort(listData[z]); int listSize = listData[z].size(); if (listSize == 0) { calculations[8][z] = Double.NaN; } else if (listSize == 1) { calculations[8][z] = listData[z].get(0); } else { int halfSize = listSize / 2; double halfValue = listData[z].get(halfSize); if (listData[z].size() % 2 == 1) { calculations[8][z] = halfValue; } else { calculations[8][z] = (halfValue + listData[z].get(halfSize + 1)) / 2; } } // Calculation of the mode double max = Double.NEGATIVE_INFINITY; double indexMax = 0; for (int i = 0; i < numBins[0]; i++) { if (hist[i][z] > max) { max = hist[i][z]; indexMax = i; } } if (max != 0) { indexMax = indexMax + minBound[0]; } calculations[7][z] = indexMax; } } } // This test checks if the statistics are correct in absence of No Data and ROI @Test public void testNoRangeNoRoi() { boolean roiUsed = false; boolean noDataRangeUsed = false; boolean useROIAccessor = false; // Byte data Type testStatistics(sourceIMG[0], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Byte data Type testStatistics(sourceIMG[0], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band2, roiUsed, noDataRangeUsed, useROIAccessor); } // This test checks if the statistics are correct in absence of No Data but with ROI (ROI RasterAccessor not used) @Test public void testRoiBounds() { boolean roiUsed = true; boolean noDataRangeUsed = false; boolean useROIAccessor = false; // Byte data Type testStatistics(sourceIMG[0], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Byte data Type testStatistics(sourceIMG[0], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band2, roiUsed, noDataRangeUsed, useROIAccessor); } // This test checks if the statistics are correct in absence of No Data but with ROI (ROI RasterAccessor used) @Test public void testRoiAccessor() { boolean roiUsed = true; boolean noDataRangeUsed = false; boolean useROIAccessor = true; // Byte data Type testStatistics(sourceIMG[0], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Byte data Type testStatistics(sourceIMG[0], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band2, roiUsed, noDataRangeUsed, useROIAccessor); } // This test checks if the statistics are correct in absence of ROI but with No Data @Test public void testNoData() { boolean roiUsed = false; boolean noDataRangeUsed = true; boolean useROIAccessor = false; // Byte data Type testStatistics(sourceIMG[0], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Byte data Type testStatistics(sourceIMG[0], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band2, roiUsed, noDataRangeUsed, useROIAccessor); } // This test checks if the statistics are correct in presence of No Data and ROI (ROI RasterAccessor not used) @Test public void testRoiNoData() { boolean roiUsed = true; boolean noDataRangeUsed = true; boolean useROIAccessor = false; // Byte data Type testStatistics(sourceIMG[0], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Byte data Type testStatistics(sourceIMG[0], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band2, roiUsed, noDataRangeUsed, useROIAccessor); } // This test checks if the statistics are correct in presence of No Data and ROI (ROI RasterAccessor used) @Test public void testRoiAccessorNoData() { boolean roiUsed = true; boolean noDataRangeUsed = true; boolean useROIAccessor = true; // Byte data Type testStatistics(sourceIMG[0], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band1, roiUsed, noDataRangeUsed, useROIAccessor); // Byte data Type testStatistics(sourceIMG[0], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Ushort data Type testStatistics(sourceIMG[1], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Short data Type testStatistics(sourceIMG[2], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Integer data Type testStatistics(sourceIMG[3], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Float data Type testStatistics(sourceIMG[4], band2, roiUsed, noDataRangeUsed, useROIAccessor); // Double data Type testStatistics(sourceIMG[5], band2, roiUsed, noDataRangeUsed, useROIAccessor); } // This method calculates the statistics with the SimpleStatsOpImage and then compares them with the already calculated values. public void testStatistics(RenderedImage source, int[] bands, boolean roiUsed, boolean noDataRangeUsed, boolean useRoiAccessor) { // The precalculated roi is used, if selected by the related boolean. ROI roiData; if (roiUsed) { roiData = roi; } else { roiData = null; } // The precalculated NoData Range is used, if selected by the related boolean. Range noDataRange; if (noDataRangeUsed) { int dataType = source.getSampleModel().getDataType(); switch (dataType) { case DataBuffer.TYPE_BYTE: noDataRange = noDataByte; break; case DataBuffer.TYPE_USHORT: noDataRange = noDataUShort; break; case DataBuffer.TYPE_SHORT: noDataRange = noDataShort; break; case DataBuffer.TYPE_INT: noDataRange = noDataInt; break; case DataBuffer.TYPE_FLOAT: noDataRange = noDataFloat; break; case DataBuffer.TYPE_DOUBLE: noDataRange = noDataDouble; break; default: throw new IllegalArgumentException("Wrong data type"); } } else { noDataRange = null; } // Index used for indicating which index of the "calculation" array must be taken for reading the precalculated statistic values int statsIndex = 0; if (roiUsed && noDataRangeUsed) { statsIndex = 3; } else if (roiUsed) { statsIndex = 1; } else if (noDataRangeUsed) { statsIndex = 2; } // Simple statistics RenderedImage dstSimple = StatisticsDescriptor.create(source, xPeriod, yPeriod, roiData, noDataRange, useRoiAccessor, bands, simpleStats, null); // Statistic calculation Statistics[][] resultSimple = (Statistics[][]) dstSimple .getProperty(Statistics.STATS_PROPERTY); // Complex statistics RenderedImage dstComplex = StatisticsDescriptor .create(source, xPeriod, yPeriod, roiData, noDataRange, useRoiAccessor, bands, complexStats, minBound, maxBound, numBins, null); // Statistic calculation Statistics[][] resultComplex = (Statistics[][]) dstComplex .getProperty(Statistics.STATS_PROPERTY); // Control only band 0 Statistics[] stats0 = resultSimple[0]; // Control only band 0 Statistics[] stats02 = resultComplex[0]; // SIMPLE STATISTICS // Test if the calculated values are equal with a tolerance value for (int i = 0; i < stats0.length; i++) { Statistics stat = stats0[i]; switch (i) { case 0: case 1: case 2: case 3: case 5: case 6: double value = (Double) stat.getResult(); assertEquals(calculations[i][statsIndex], value, TOLERANCE); break; case 4: double[] extrema = (double[]) stat.getResult(); double max = extrema[1]; double min = extrema[0]; assertEquals(calculations[2][statsIndex], max, TOLERANCE); assertEquals(calculations[3][statsIndex], min, TOLERANCE); break; } } // COMPLEX STATISTICS // Test if the calculated values are equal with a tolerance value for (int i = 0; i < stats02.length; i++) { Statistics stat = stats02[i]; switch (i + 7) { case 7: double[] hist2 = (double[]) (stat.getResult()); for (int j = 0; j < numBins[0]; j++) { assertEquals(hist[i][statsIndex], hist2[i], TOLERANCE); } break; case 8: case 9: double value = (Double) stat.getResult(); assertEquals(calculations[i + 6][statsIndex], value, TOLERANCE); break; } } } // Private method for calculating the bin/index related to the sample private static int getIndex(double sample) { int index = (int) ((sample - minBound[0]) / binInterval[0]); return index; } }