Added unit tests for array data and minor improvements.
authorTeemu Murtola <teemu.murtola@cbr.su.se>
Sun, 13 Nov 2011 16:34:41 +0000 (18:34 +0200)
committerTeemu Murtola <teemu.murtola@gmail.com>
Tue, 21 Feb 2012 20:03:33 +0000 (22:03 +0200)
Added unit tests for AnalysisArrayData and some non-trivial methods in
AbstractAverageHistogram.  The analysis data test framework now contains
a method for filling an array data object from test input data.

Also fixed some issues in AbstractAnalysisArrayData, made read-only
accessors in public, added a few more methods to extract information,
and made the class use std::vector internally for memory management
instead of snew/sfree.

Part of issue #823.

Change-Id: I1d768e2763861f326b4e5a7cb94f5f82f0d56a9e

src/gromacs/analysisdata/arraydata.cpp
src/gromacs/analysisdata/arraydata.h
src/gromacs/analysisdata/tests/CMakeLists.txt
src/gromacs/analysisdata/tests/arraydata.cpp [new file with mode: 0644]
src/gromacs/analysisdata/tests/datatest.h
src/gromacs/analysisdata/tests/histogram.cpp
src/gromacs/analysisdata/tests/refdata/AbstractAverageHistogramTest_ResamplesAtDoubleBinWidth.xml [new file with mode: 0644]
src/gromacs/analysisdata/tests/refdata/AbstractAverageHistogramTest_ResamplesAtDoubleBinWidthWithIntegerBins.xml [new file with mode: 0644]

index f222fecc1ed60bc05d1f7e25e8b572755e8405b6..003c8601cacc6953ed2c38148c0c2471d357d7b6 100644 (file)
@@ -37,8 +37,7 @@
  */
 #include "gromacs/analysisdata/arraydata.h"
 
-// Legacy header.
-#include "smalloc.h"
+#include <algorithm>
 
 #include "gromacs/fatalerror/exceptions.h"
 #include "gromacs/fatalerror/gmxassert.h"
@@ -47,13 +46,12 @@ namespace gmx
 {
 
 AbstractAnalysisArrayData::AbstractAnalysisArrayData()
-    : _nrows(0), _value(NULL), _xstart(0.0), _xstep(1.0), _bReady(false)
+    : _nrows(0), _xstart(0.0), _xstep(1.0), _bReady(false)
 {
 }
 
 AbstractAnalysisArrayData::~AbstractAnalysisArrayData()
 {
-    sfree(_value);
 }
 
 
@@ -64,7 +62,7 @@ AbstractAnalysisArrayData::getDataWErr(int index, real *x, real *dx,
 {
     if (index < 0)
     {
-        index += _nrows;
+        index += frameCount() - 1;
         if (index < 0)
         {
             return false;
@@ -76,7 +74,7 @@ AbstractAnalysisArrayData::getDataWErr(int index, real *x, real *dx,
     }
     if (x != NULL)
     {
-        *x = _xstart + index * _xstep;
+        *x = xvalue(index);
     }
     if (dx != NULL)
     {
@@ -84,7 +82,7 @@ AbstractAnalysisArrayData::getDataWErr(int index, real *x, real *dx,
     }
     if (y != NULL)
     {
-        *y = _value + (index * columnCount());
+        *y = &_value[index * columnCount()];
     }
     if (dy != NULL)
     {
@@ -110,7 +108,7 @@ AbstractAnalysisArrayData::requestStorage(int /*nframes*/)
 void
 AbstractAnalysisArrayData::setColumnCount(int ncols)
 {
-    GMX_RELEASE_ASSERT(!_value,
+    GMX_RELEASE_ASSERT(!isAllocated(),
                        "Cannot change column count after data has been allocated");
     AbstractAnalysisData::setColumnCount(ncols);
 }
@@ -120,7 +118,7 @@ void
 AbstractAnalysisArrayData::setRowCount(int nrows)
 {
     GMX_RELEASE_ASSERT(nrows > 0, "Invalid number of rows");
-    GMX_RELEASE_ASSERT(!_value,
+    GMX_RELEASE_ASSERT(!isAllocated(),
                        "Cannot change row count after data has been allocated");
     _nrows = nrows;
 }
@@ -129,10 +127,10 @@ AbstractAnalysisArrayData::setRowCount(int nrows)
 void
 AbstractAnalysisArrayData::allocateValues()
 {
-    GMX_RELEASE_ASSERT(_value == NULL, "Can only allocate values once");
+    GMX_RELEASE_ASSERT(!isAllocated(), "Can only allocate values once");
     GMX_RELEASE_ASSERT(rowCount() > 0 && columnCount() > 0,
                        "Row and column counts must be set before allocating values");
-    snew(_value, rowCount() * columnCount());
+    _value.resize(rowCount() * columnCount());
 }
 
 
@@ -148,8 +146,7 @@ AbstractAnalysisArrayData::setXAxis(real start, real step)
 void
 AbstractAnalysisArrayData::valuesReady()
 {
-    GMX_RELEASE_ASSERT(columnCount() > 0 && _nrows > 0 && _value,
-                       "There must be some data");
+    GMX_RELEASE_ASSERT(isAllocated(), "There must be some data");
     if (_bReady)
     {
         return;
@@ -157,10 +154,10 @@ AbstractAnalysisArrayData::valuesReady()
     _bReady = true;
 
     notifyDataStart();
-    for (int i = 0; i < _nrows; ++i)
+    for (int i = 0; i < rowCount(); ++i)
     {
-        notifyFrameStart(_xstart + i * _xstep, 0);
-        notifyPointsAdd(0, columnCount(), _value + (i * columnCount()),
+        notifyFrameStart(xvalue(i), 0);
+        notifyPointsAdd(0, columnCount(), &_value[i * columnCount()],
                         NULL, NULL);
         notifyFrameFinish();
     }
@@ -172,17 +169,14 @@ void
 AbstractAnalysisArrayData::copyContents(const AbstractAnalysisArrayData *src,
                                         AbstractAnalysisArrayData *dest)
 {
-    GMX_RELEASE_ASSERT(src->columnCount() > 0 && src->_nrows > 0 && src->_value,
-                       "Source data must not be empty");
-    GMX_RELEASE_ASSERT(!dest->_value, "Destination data must not be allocated");
+    GMX_RELEASE_ASSERT(src->isAllocated(), "Source data must not be empty");
+    GMX_RELEASE_ASSERT(!dest->isAllocated(),
+                       "Destination data must not be allocated");
     dest->setColumnCount(src->columnCount());
-    dest->setRowCount(src->_nrows);
+    dest->setRowCount(src->rowCount());
     dest->allocateValues();
-    dest->setXAxis(src->_xstart, src->_xstep);
-    for (int i = 0; i < src->_nrows * src->columnCount(); ++i)
-    {
-        dest->_value[i] = src->_value[i];
-    }
+    dest->setXAxis(src->xstart(), src->xstep());
+    std::copy(src->_value.begin(), src->_value.end(), dest->_value.begin());
 }
 
 } // namespace gmx
index 3e90afd8aa81a9f69073430b100c339651542ce4..4712a2fdac591d8a859b28a0dfad7021214ce0c7 100644 (file)
@@ -41,6 +41,8 @@
 
 #include <cstddef>
 
+#include <vector>
+
 #include "../fatalerror/gmxassert.h"
 
 #include "abstractdata.h"
@@ -69,9 +71,6 @@ class AbstractAnalysisArrayData : public AbstractAnalysisData
                                  const bool **present = 0) const;
         virtual bool requestStorage(int nframes = -1);
 
-    protected:
-        AbstractAnalysisArrayData();
-
         /*! \brief
          * Returns the number of rows in the data array.
          *
@@ -79,6 +78,30 @@ class AbstractAnalysisArrayData : public AbstractAnalysisData
          * returns 0 before valuesReady() has been called.
          */
         int rowCount() const { return _nrows; }
+        //! Returns true if values have been allocated.
+        bool isAllocated() const { return !_value.empty(); }
+        //! Returns the x value of the first frame.
+        real xstart() const { return _xstart; }
+        //! Returns the step between frame x values.
+        real xstep() const { return _xstep; }
+        //! Returns the x value of a row.
+        real xvalue(int row) const
+        {
+            GMX_ASSERT(row >= 0 && row < rowCount(), "Row index out of range");
+            return xstart() + row * xstep();
+        }
+        //! Returns a given array element.
+        real value(int row, int col) const
+        {
+            GMX_ASSERT(row >= 0 && row < rowCount(), "Row index out of range");
+            GMX_ASSERT(col >= 0 && col < columnCount(), "Column index out of range");
+            GMX_ASSERT(isAllocated(), "Data array not allocated");
+            return _value[row * columnCount() + col];
+        }
+
+    protected:
+        AbstractAnalysisArrayData();
+
         /*! \brief
          * Sets the number of columns in the data array.
          *
@@ -97,10 +120,6 @@ class AbstractAnalysisArrayData : public AbstractAnalysisData
          * setColumnCount() and setRowCount() must have been called.
          */
         void allocateValues();
-        //! Returns the x value of the first frame.
-        real xstart() const { return _xstart; }
-        //! Returns the step between frame x values.
-        real xstep() const { return _xstep; }
         /*! \brief
          * Sets the values reported as x values for frames.
          */
@@ -108,17 +127,9 @@ class AbstractAnalysisArrayData : public AbstractAnalysisData
         //! Returns a reference to a given array element.
         real &value(int row, int col)
         {
-            GMX_ASSERT(row >= 0 && row < _nrows, "Row index out of range");
-            GMX_ASSERT(col >= 0 && col < columnCount(), "Column index out of range");
-            GMX_ASSERT(_value != NULL, "Data array not allocated");
-            return _value[row * columnCount() + col];
-        }
-        //! Returns a given array element.
-        const real &value(int row, int col) const
-        {
-            GMX_ASSERT(row >= 0 && row < _nrows, "Row index out of range");
+            GMX_ASSERT(row >= 0 && row < rowCount(), "Row index out of range");
             GMX_ASSERT(col >= 0 && col < columnCount(), "Column index out of range");
-            GMX_ASSERT(_value != NULL, "Data array not allocated");
+            GMX_ASSERT(isAllocated(), "Data array not allocated");
             return _value[row * columnCount() + col];
         }
         /*! \brief
@@ -150,7 +161,7 @@ class AbstractAnalysisArrayData : public AbstractAnalysisData
 
     private:
         int                  _nrows;
-        real                *_value;
+        std::vector<real>    _value;
         real                 _xstart;
         real                 _xstep;
         bool                 _bReady;
@@ -175,12 +186,9 @@ class AnalysisArrayData : public AbstractAnalysisArrayData
 
         // TODO: These statements cause Doxygen to generate confusing
         // documentation.
-        using AbstractAnalysisArrayData::rowCount;
         using AbstractAnalysisArrayData::setColumnCount;
         using AbstractAnalysisArrayData::setRowCount;
         using AbstractAnalysisArrayData::allocateValues;
-        using AbstractAnalysisArrayData::xstart;
-        using AbstractAnalysisArrayData::xstep;
         using AbstractAnalysisArrayData::setXAxis;
         using AbstractAnalysisArrayData::value;
         using AbstractAnalysisArrayData::setValue;
index b9816a34fb4324698802f914fb2201ea03250ccf..0bbaba429ae3627b3da7bd55bda19b727b2b5ff3 100644 (file)
@@ -1,5 +1,6 @@
 if (TESTUTILS_HAVE_REFDATA)
     add_gmock_test(AnalysisDataUnitTests analysisdata-test
-                   analysisdata.cpp average.cpp datatest.cpp histogram.cpp
+                   analysisdata.cpp arraydata.cpp
+                   average.cpp datatest.cpp histogram.cpp
                    mock_module.cpp)
 endif ()
diff --git a/src/gromacs/analysisdata/tests/arraydata.cpp b/src/gromacs/analysisdata/tests/arraydata.cpp
new file mode 100644 (file)
index 0000000..c74d3a0
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Tests for gmx::AnalysisArrayData functionality.
+ *
+ * These tests check the functionality of gmx::AnalysisArrayData and its base
+ * class gmx::AbstractAnalysisArrayData.
+ * Checking is done using gmx::test::AnalysisDataTestFixture and mock
+ * modules that implement gmx::AnalysisDataModuleInterface.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_analysisdata
+ */
+#include <memory>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/analysisdata/arraydata.h"
+
+#include "datatest.h"
+
+namespace
+{
+
+/********************************************************************
+ * Tests for gmx::AnalysisArrayData.
+ */
+
+typedef gmx::test::AnalysisDataTestFixture AnalysisArrayDataTest;
+
+using gmx::test::END_OF_DATA;
+using gmx::test::END_OF_FRAME;
+static const real inputdata[] = {
+    1.0,  0.0, 1.0, 2.0, END_OF_FRAME,
+    2.0,  1.0, 1.0, 1.0, END_OF_FRAME,
+    3.0,  2.0, 0.0, 0.0, END_OF_FRAME,
+    4.0,  3.0, 2.0, 1.0, END_OF_FRAME,
+    END_OF_DATA
+};
+
+TEST_F(AnalysisArrayDataTest, CallsModuleCorrectly)
+{
+    gmx::test::AnalysisDataTestInput input(inputdata);
+    gmx::AnalysisArrayData data;
+    data.setXAxis(1.0, 1.0);
+    setupArrayData(input, &data);
+
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW(data.valuesReady());
+}
+
+TEST_F(AnalysisArrayDataTest, StorageWorks)
+{
+    gmx::test::AnalysisDataTestInput input(inputdata);
+    gmx::AnalysisArrayData data;
+    data.setXAxis(1.0, 1.0);
+    setupArrayData(input, &data);
+
+    ASSERT_NO_THROW(addStaticStorageCheckerModule(input, -1, &data));
+    ASSERT_NO_THROW(data.valuesReady());
+}
+
+} // namespace
index 7081ce442d162e408d91b36c2fe560310ff5e051..bb67be237bfa5dbaf9a323ca0cb3ff6c6f0a014b 100644 (file)
@@ -197,6 +197,9 @@ class AnalysisDataTestInput
  * presentAllData() and presentDataFrame() are provided to push data from an
  * AnalysisDataTestInput into an AnalysisData object.  In typical tests, most
  * checks are done during the these methods, by the added mock modules.
+ * setupArrayData() performs the same function for classes derived from
+ * AbstractAnalysisArrayData.  In that case, the test should separately ensure
+ * that AbstractAnalysisArrayData::valuesReady() gets called.
  *
  * \todo
  * Support for errors and for arbitrary multipoint data.
@@ -220,6 +223,24 @@ class AnalysisDataTestFixture : public ::testing::Test
          */
         static void presentDataFrame(const AnalysisDataTestInput &input, int row,
                                      AnalysisDataHandle *handle);
+        /*! \brief
+         * Initializes an array data object from AnalysisDataTestInput.
+         *
+         * \tparam ArrayData  Class derived from AbstractAnalysisArrayData.
+         *
+         * The ArrayData class should expose the setter methods
+         * (setColumnCount(), setRowCount(), allocateValues(), setValue())
+         * publicly or declare the fixture class as a friend.
+         * The X axis in \p data must be configured to match \p input before
+         * calling this method.
+         *
+         * Does not call AbstractAnalysisArrayData::valuesReady().
+         * The test must ensure that this method gets called, otherwise the
+         * mock modules never get called.
+         */
+        template <class ArrayData>
+        static void setupArrayData(const AnalysisDataTestInput &input,
+                                   ArrayData *data);
 
         /*! \brief
          * Adds a mock module that verifies output against
@@ -317,6 +338,32 @@ class AnalysisDataTestFixture : public ::testing::Test
         gmx::test::TestReferenceData  data_;
 };
 
+
+template <class ArrayData>
+void AnalysisDataTestFixture::setupArrayData(const AnalysisDataTestInput &input,
+                                             ArrayData *data)
+{
+    GMX_RELEASE_ASSERT(!input.isMultipoint(),
+                       "Array data cannot be initialized from multipoint data");
+    GMX_RELEASE_ASSERT(data->columnCount() == 0 || data->columnCount() == input.columnCount(),
+                       "Mismatching input and target data");
+    GMX_RELEASE_ASSERT(data->rowCount() == 0 || data->rowCount() == input.frameCount(),
+                       "Mismatching input and target data");
+    data->setColumnCount(input.columnCount());
+    data->setRowCount(input.frameCount());
+    data->allocateValues();
+    for (int row = 0; row < input.frameCount(); ++row)
+    {
+        const AnalysisDataTestInputFrame &frame = input.frame(row);
+        EXPECT_FLOAT_EQ(frame.x(), data->xvalue(row));
+        const AnalysisDataTestInputPointSet &points = frame.points();
+        for (int column = 0; column < input.columnCount(); ++column)
+        {
+            data->setValue(row, column, points.y(column));
+        }
+    }
+}
+
 } // namespace test
 
 } // namespace gmx
index 97a60829c19b6befac12ae95ce6932ffeabed228..be12dd55b88564c74889722e5b85db6bdebac1cb 100644 (file)
@@ -42,6 +42,8 @@
  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
  * \ingroup module_analysisdata
  */
+#include <memory>
+
 #include <gtest/gtest.h>
 
 #include "gromacs/analysisdata/analysisdata.h"
@@ -287,4 +289,100 @@ TEST_F(BinAverageModuleTest, ComputesCorrectlyWithAll)
     ASSERT_NO_THROW(presentAllData(input, &data));
 }
 
+
+/********************************************************************
+ * Tests for gmx::AbstractAverageHistogram.
+ *
+ * This class derives from gmx::AbstractAnalysisArrayData, and is tested using
+ * corresponding facilities in gmx::test::AnalysisDataTestFixture.
+ */
+
+typedef gmx::test::AnalysisDataTestFixture AbstractAverageHistogramTest;
+
+// Input data for average histogram tests.
+static const real averageinputdata[] = {
+    1.0, 2.0, 1.0, END_OF_FRAME,
+    1.5, 1.0, 1.0, END_OF_FRAME,
+    2.0, 3.0, 2.0, END_OF_FRAME,
+    2.5, 4.0, 2.0, END_OF_FRAME,
+    3.0, 2.0, 1.0, END_OF_FRAME,
+    3.5, 0.0, 3.0, END_OF_FRAME,
+    4.0, 1.0, 3.0, END_OF_FRAME,
+    END_OF_DATA
+};
+
+/*! \internal \brief
+ * Mock object for testing gmx::AbstractAverageHistogram.
+ *
+ * Exposes necessary methods from gmx::AbstractAverageHistogram to use with
+ * gmx::test::AnalysisDataTestFixture::setupArrayData().
+ */
+class MockAverageHistogram : public gmx::AbstractAverageHistogram
+{
+    public:
+        MockAverageHistogram() {}
+        explicit MockAverageHistogram(const gmx::AnalysisHistogramSettings &settings)
+            : AbstractAverageHistogram(settings)
+        {
+        }
+
+        using AbstractAverageHistogram::init;
+        using AbstractAverageHistogram::setColumnCount;
+        using AbstractAverageHistogram::setRowCount;
+        using AbstractAverageHistogram::allocateValues;
+        using AbstractAverageHistogram::setValue;
+};
+
+
+TEST_F(AbstractAverageHistogramTest, ClonesCorrectly)
+{
+    gmx::test::AnalysisDataTestInput input(averageinputdata);
+    MockAverageHistogram data(
+            gmx::histogramFromBins(1.0, input.frameCount(), 0.5).integerBins());
+    setupArrayData(input, &data);
+
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    std::auto_ptr<gmx::AbstractAverageHistogram> copy(data.clone());
+    ASSERT_NO_THROW(addStaticCheckerModule(input, copy.get()));
+    ASSERT_NO_THROW(copy->done());
+    ASSERT_NO_THROW(data.done());
+    std::auto_ptr<gmx::AbstractAverageHistogram> copy2(data.clone());
+    ASSERT_NO_THROW(addStaticCheckerModule(input, copy2.get()));
+    ASSERT_NO_THROW(copy2->done());
+}
+
+
+TEST_F(AbstractAverageHistogramTest, ResamplesAtDoubleBinWidth)
+{
+    gmx::test::AnalysisDataTestInput input(averageinputdata);
+    MockAverageHistogram data(
+            gmx::histogramFromBins(1.0, input.frameCount(), 0.5).integerBins());
+    setupArrayData(input, &data);
+
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW(addReferenceCheckerModule("InputData", &data));
+    std::auto_ptr<gmx::AbstractAverageHistogram> resampled(
+            data.resampleDoubleBinWidth(false));
+    ASSERT_NO_THROW(addReferenceCheckerModule("ResampledHistogram", resampled.get()));
+    ASSERT_NO_THROW(data.done());
+    ASSERT_NO_THROW(resampled->done());
+}
+
+
+TEST_F(AbstractAverageHistogramTest, ResamplesAtDoubleBinWidthWithIntegerBins)
+{
+    gmx::test::AnalysisDataTestInput input(averageinputdata);
+    MockAverageHistogram data(
+            gmx::histogramFromBins(1.0, input.frameCount(), 0.5).integerBins());
+    setupArrayData(input, &data);
+
+    ASSERT_NO_THROW(addStaticCheckerModule(input, &data));
+    ASSERT_NO_THROW(addReferenceCheckerModule("InputData", &data));
+    std::auto_ptr<gmx::AbstractAverageHistogram> resampled(
+            data.resampleDoubleBinWidth(true));
+    ASSERT_NO_THROW(addReferenceCheckerModule("ResampledHistogram", resampled.get()));
+    ASSERT_NO_THROW(data.done());
+    ASSERT_NO_THROW(resampled->done());
+}
+
 } // namespace
diff --git a/src/gromacs/analysisdata/tests/refdata/AbstractAverageHistogramTest_ResamplesAtDoubleBinWidth.xml b/src/gromacs/analysisdata/tests/refdata/AbstractAverageHistogramTest_ResamplesAtDoubleBinWidth.xml
new file mode 100644 (file)
index 0000000..b0566f1
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<ReferenceData>
+  <Compound Name="InputData" Subtype="AnalysisData">
+    <Compound Name="Frame0" Subtype="DataFrame">
+      <Real Name="X">1.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>2.000000</Real>
+        <Real>1.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame1" Subtype="DataFrame">
+      <Real Name="X">1.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>1.000000</Real>
+        <Real>1.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame2" Subtype="DataFrame">
+      <Real Name="X">2.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>3.000000</Real>
+        <Real>2.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame3" Subtype="DataFrame">
+      <Real Name="X">2.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>4.000000</Real>
+        <Real>2.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame4" Subtype="DataFrame">
+      <Real Name="X">3.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>2.000000</Real>
+        <Real>1.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame5" Subtype="DataFrame">
+      <Real Name="X">3.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>0.000000</Real>
+        <Real>3.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame6" Subtype="DataFrame">
+      <Real Name="X">4.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>1.000000</Real>
+        <Real>3.000000</Real>
+      </Compound>
+    </Compound>
+  </Compound>
+  <Compound Name="ResampledHistogram" Subtype="AnalysisData">
+    <Compound Name="Frame0" Subtype="DataFrame">
+      <Real Name="X">1.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>3.000000</Real>
+        <Real>1.414214</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame1" Subtype="DataFrame">
+      <Real Name="X">2.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>7.000000</Real>
+        <Real>2.828427</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame2" Subtype="DataFrame">
+      <Real Name="X">3.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>2.000000</Real>
+        <Real>3.162278</Real>
+      </Compound>
+    </Compound>
+  </Compound>
+</ReferenceData>
diff --git a/src/gromacs/analysisdata/tests/refdata/AbstractAverageHistogramTest_ResamplesAtDoubleBinWidthWithIntegerBins.xml b/src/gromacs/analysisdata/tests/refdata/AbstractAverageHistogramTest_ResamplesAtDoubleBinWidthWithIntegerBins.xml
new file mode 100644 (file)
index 0000000..c21a28b
--- /dev/null
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<ReferenceData>
+  <Compound Name="InputData" Subtype="AnalysisData">
+    <Compound Name="Frame0" Subtype="DataFrame">
+      <Real Name="X">1.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>2.000000</Real>
+        <Real>1.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame1" Subtype="DataFrame">
+      <Real Name="X">1.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>1.000000</Real>
+        <Real>1.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame2" Subtype="DataFrame">
+      <Real Name="X">2.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>3.000000</Real>
+        <Real>2.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame3" Subtype="DataFrame">
+      <Real Name="X">2.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>4.000000</Real>
+        <Real>2.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame4" Subtype="DataFrame">
+      <Real Name="X">3.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>2.000000</Real>
+        <Real>1.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame5" Subtype="DataFrame">
+      <Real Name="X">3.500000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>0.000000</Real>
+        <Real>3.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame6" Subtype="DataFrame">
+      <Real Name="X">4.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>1.000000</Real>
+        <Real>3.000000</Real>
+      </Compound>
+    </Compound>
+  </Compound>
+  <Compound Name="ResampledHistogram" Subtype="AnalysisData">
+    <Compound Name="Frame0" Subtype="DataFrame">
+      <Real Name="X">1.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>2.000000</Real>
+        <Real>1.000000</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame1" Subtype="DataFrame">
+      <Real Name="X">2.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>4.000000</Real>
+        <Real>2.236068</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame2" Subtype="DataFrame">
+      <Real Name="X">3.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>6.000000</Real>
+        <Real>2.236068</Real>
+      </Compound>
+    </Compound>
+    <Compound Name="Frame3" Subtype="DataFrame">
+      <Real Name="X">4.000000</Real>
+      <Compound Name="Y" Subtype="SequenceReal">
+        <Int Name="Length">2</Int>
+        <Real>1.000000</Real>
+        <Real>4.242640</Real>
+      </Compound>
+    </Compound>
+  </Compound>
+</ReferenceData>