Skip to content
Snippets Groups Projects
TimeSeriesPropertyTest.h 81.8 KiB
Newer Older
#ifndef TIMESERIESPROPERTYTEST_H_
#define TIMESERIESPROPERTYTEST_H_

#include <cxxtest/TestSuite.h>
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/Exception.h"
#include "MantidKernel/make_unique.h"
#include "MantidKernel/PropertyWithValue.h"
#include "MantidKernel/TimeSplitter.h"
#include <cmath>
#include <boost/make_shared.hpp>
#include <boost/scoped_ptr.hpp>

using namespace Mantid::Kernel;

class TimeSeriesPropertyTest : public CxxTest::TestSuite {
  // Create a small TSP<double>. Callee owns the returned object.
  TimeSeriesProperty<double> *createDoubleTSP() {
    TimeSeriesProperty<double> *p =
        new TimeSeriesProperty<double>("doubleProp");
    TS_ASSERT_THROWS_NOTHING(p->addValue("2007-11-30T16:17:00", 9.99));
    TS_ASSERT_THROWS_NOTHING(p->addValue("2007-11-30T16:17:10", 7.55));
    TS_ASSERT_THROWS_NOTHING(p->addValue("2007-11-30T16:17:20", 5.55));
    TS_ASSERT_THROWS_NOTHING(p->addValue("2007-11-30T16:17:30", 10.55));
    return p;
  }

  // Create a small TSP<int>. Callee owns the returned object.
  TimeSeriesProperty<int> *createIntegerTSP(int numberOfValues) {
    TimeSeriesProperty<int> *log = new TimeSeriesProperty<int>("intProp");
    DateAndTime startTime("2007-11-30T16:17:00");
    for (int value = 0; value < numberOfValues; ++value) {
      DateAndTime time = startTime + value * 10.0;
      TS_ASSERT_THROWS_NOTHING(log->addValue(time, value + 1));
  void setUp() override {
    iProp = new TimeSeriesProperty<int>("intProp");
    dProp = new TimeSeriesProperty<double>("doubleProp");
    sProp = new TimeSeriesProperty<std::string>("stringProp");
  }
  void tearDown() override {
  void test_Constructor() {
    // Test that all the base class member variables are correctly assigned to
    TS_ASSERT(!iProp->name().compare("intProp"));
    TS_ASSERT(!iProp->documentation().compare(""));
    TS_ASSERT(typeid(std::vector<TimeValueUnit<int>>) == *iProp->type_info());
    TS_ASSERT(!iProp->isDefault())

    TS_ASSERT(!dProp->name().compare("doubleProp"));
    TS_ASSERT(!dProp->documentation().compare(""));
    TS_ASSERT(typeid(std::vector<TimeValueUnit<double>>) ==
              *dProp->type_info());
    TS_ASSERT(!dProp->isDefault())

    TS_ASSERT(!sProp->name().compare("stringProp"));
    TS_ASSERT(!sProp->documentation().compare(""));
    TS_ASSERT(typeid(std::vector<TimeValueUnit<std::string>>) ==
              *sProp->type_info());
    TS_ASSERT(!sProp->isDefault())
    TS_ASSERT_EQUALS(sProp->isValid(), "");
  void test_SetValue() {
    TS_ASSERT_THROWS(iProp->setValue("1"), Exception::NotImplementedError);
    TS_ASSERT_THROWS(dProp->setValue("5.5"), Exception::NotImplementedError);
    TS_ASSERT_THROWS(sProp->setValue("aValue"), Exception::NotImplementedError);
  void test_AddValue() {
    const std::string tester("2007-11-30T16:17:00");
    int sizepre = iProp->size();
    TS_ASSERT_THROWS_NOTHING(iProp->addValue(tester, 1));
    TS_ASSERT_THROWS_NOTHING(iProp->addValue("2007-11-30T16:17:10", 1));
    TS_ASSERT_EQUALS(iProp->size(), sizepre + 2);
    sizepre = dProp->size();
    TS_ASSERT_THROWS_NOTHING(dProp->addValue("2007-11-30T16:17:00", 9.99));
    TS_ASSERT_THROWS_NOTHING(dProp->addValue("2007-11-30T16:17:10", 5.55));
    TS_ASSERT_EQUALS(dProp->size(), sizepre + 2);
    sizepre = sProp->size();
    TS_ASSERT_THROWS_NOTHING(sProp->addValue("2007-11-30T16:17:00", "test"));
    TS_ASSERT_THROWS_NOTHING(sProp->addValue("2007-11-30T16:17:10", "test2"));
    TS_ASSERT_EQUALS(sProp->size(), sizepre + 2);
    // Now try the other overloads
    TimeSeriesProperty<int> otherProp("otherProp");
    TS_ASSERT_THROWS_NOTHING(
        otherProp.addValue(static_cast<std::time_t>(123), 1));
    TS_ASSERT_THROWS_NOTHING(
        otherProp.addValue(boost::posix_time::second_clock::local_time(), 1));
    const std::string dString = dProp->value();
    TS_ASSERT_EQUALS(dString.substr(0, 27), "2007-Nov-30 16:17:00  9.99\n");
    const std::string iString = iProp->value();
    TS_ASSERT_EQUALS(iString.substr(0, 24), "2007-Nov-30 16:17:00  1\n");
    const std::string sString = sProp->value();
    TS_ASSERT_EQUALS(sString.substr(0, 27), "2007-Nov-30 16:17:00  test\n");

    // Test the internal toggling of the 'sorted' flag works
    auto twoVals = dProp->valuesAsVector();
    double newVal = 2.22;
    dProp->addValue("2007-11-30T16:17:05", newVal);
    // Calling this method sorts the vector by time, so long as the internal
    // flag says it isn't sorted
    auto threeVals = dProp->valuesAsVector();
    TS_ASSERT_EQUALS(threeVals.size(), 3);
    TS_ASSERT_EQUALS(twoVals[0], threeVals[0]);
    TS_ASSERT_EQUALS(twoVals[1], threeVals[2]);
    TS_ASSERT_EQUALS(newVal, threeVals[1]);
  void test_GetDerivative() {
    dProp->addValue("2007-11-30T16:17:10", 10);
    dProp->addValue("2007-11-30T16:17:12", 12);
    dProp->addValue("2007-11-30T16:17:01", 01);
    dProp->addValue("2007-11-30T16:17:05", 05);

    auto derProp = dProp->getDerivative();
    TS_ASSERT(dynamic_cast<TimeSeriesProperty<double> *>(derProp.get()))
    TS_ASSERT_EQUALS(derProp->size(), 3);
    auto derValues = derProp->valuesAsVector();

    TS_ASSERT_EQUALS(derValues[0], 1);
    TS_ASSERT_EQUALS(derValues[1], 1);
    TS_ASSERT_EQUALS(derValues[2], 1);
    TSM_ASSERT_THROWS("derivative undefined for string property",
                      sProp->getDerivative(), std::runtime_error);
    iProp->addValue("2007-11-30T16:17:10", 10);
    TSM_ASSERT_THROWS(
        "derivative undefined for property with less then 2 values",
        iProp->getDerivative(), std::runtime_error);
    iProp->addValue("2007-11-30T16:17:12", 12);

    derProp = iProp->getDerivative();
    TS_ASSERT_EQUALS(derProp->size(), 1);
    derValues = derProp->valuesAsVector();
    TS_ASSERT_EQUALS(derValues[0], 1);
  void test_timesAsVector() {
    TimeSeriesProperty<double> *p =
        new TimeSeriesProperty<double>("doubleProp");
    TS_ASSERT_THROWS_NOTHING(p->addValue("2007-11-30T16:17:20", 5.55));
    TS_ASSERT_THROWS_NOTHING(p->addValue("2007-11-30T16:17:00", 9.99));
    TS_ASSERT_THROWS_NOTHING(p->addValue("2007-11-30T16:17:10", 5.55));
    TS_ASSERT_THROWS_NOTHING(p->addValue("2007-11-30T16:17:30", 5.55));
    std::vector<double> timeSec;
    timeSec = p->timesAsVectorSeconds();
    TS_ASSERT_DELTA(timeSec[0], 0.0, 1e-6);
    TS_ASSERT_DELTA(timeSec[1], 10.0, 1e-6);
    TS_ASSERT_DELTA(timeSec[2], 20.0, 1e-6);
    TS_ASSERT_DELTA(timeSec[3], 30.0, 1e-6);
    std::vector<DateAndTime> time;
    time = p->timesAsVector();
    TS_ASSERT_EQUALS(time[0], DateAndTime("2007-11-30T16:17:00"));
    TS_ASSERT_EQUALS(time[1], DateAndTime("2007-11-30T16:17:10"));
    TS_ASSERT_EQUALS(time[2], DateAndTime("2007-11-30T16:17:20"));
    TS_ASSERT_EQUALS(time[3], DateAndTime("2007-11-30T16:17:30"));
  void test_replaceValues() {
    // Arrange
    size_t num = 1000;
    DateAndTime first("2007-11-30T16:17:10");
    std::vector<DateAndTime> times;

    std::vector<double> values;
    std::vector<double> replacementValues;
    double offset = 100.0;
    for (size_t i = 0; i < num; i++) {
      times.push_back(first + double(i));
      values.push_back(double(i));
      replacementValues.push_back(double(i) + offset);
    }
    TimeSeriesProperty<double> tsp("test");
    tsp.addValues(times, values);
    TS_ASSERT_EQUALS(tsp.size(), 1000);
    TS_ASSERT_EQUALS(tsp.nthValue(3), 3.0);

    // Act
    tsp.replaceValues(times, replacementValues);

    // Assert
    TSM_ASSERT_EQUALS("Should have 1000 entries", tsp.size(), 1000);
    TSM_ASSERT_EQUALS("Should be 3 plus the offset of 100", tsp.nthValue(3),
                      103.0);
  void test_addValues() {
    size_t num = 1000;
    DateAndTime first("2007-11-30T16:17:10");
    std::vector<DateAndTime> times;

    std::vector<double> values;
    for (size_t i = 0; i < num; i++) {
      times.push_back(first + double(i));
      values.push_back(double(i));
    }
    TimeSeriesProperty<double> tsp("test");
    tsp.addValues(times, values);
    TS_ASSERT_EQUALS(tsp.size(), 1000);
    TS_ASSERT_EQUALS(tsp.nthValue(3), 3.0);
  void test_Casting() {
    TS_ASSERT_DIFFERS(dynamic_cast<Property *>(iProp),
                      static_cast<Property *>(0));
    TS_ASSERT_DIFFERS(dynamic_cast<Property *>(dProp),
                      static_cast<Property *>(0));
    TS_ASSERT_DIFFERS(dynamic_cast<Property *>(sProp),
                      static_cast<Property *>(0));
    TS_ASSERT_DIFFERS(dynamic_cast<ITimeSeriesProperty *>(iProp),
                      static_cast<ITimeSeriesProperty *>(0));
  //----------------------------------------------------------------------------
  void test_AdditionOperator() {
    TimeSeriesProperty<int> *log = new TimeSeriesProperty<int>("MyIntLog");
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:00", 1));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:19:10", 2));
    TimeSeriesProperty<int> *log2 = new TimeSeriesProperty<int>("MyIntLog2");
    TS_ASSERT_THROWS_NOTHING(log2->addValue("2007-11-30T16:18:00", 3));
    TS_ASSERT_THROWS_NOTHING(log2->addValue("2007-11-30T16:18:10", 4));
    TS_ASSERT_THROWS_NOTHING(log2->addValue("2007-11-30T16:18:11", 5));
    TS_ASSERT_EQUALS(log->size(), 2);
    // Concatenate the lists
    TS_ASSERT_EQUALS(log->size(), 5);
    DateAndTime t0 = log->firstTime();
    DateAndTime tf = log->lastTime();

    TS_ASSERT_EQUALS(t0, DateAndTime("2007-11-30T16:17:00"));
    TS_ASSERT_EQUALS(tf, DateAndTime("2007-11-30T16:19:10"));
    delete log;
    delete log2;
  //----------------------------------------------------------------------------
  /// Ticket 2097: This caused an infinite loop
  void test_AdditionOperatorOnYourself() {
    TimeSeriesProperty<int> *log = createIntegerTSP(2);

    (*log) += log;
    // There is now a check and trying to do this does nothing.
    TS_ASSERT_EQUALS(log->size(), 2);
  void test_ComparisonOperator() {
    // Setup two logs and two filters so that logs have different sizes but are
    // the same size after applying the filter

    TimeSeriesProperty<int> *log1 = new TimeSeriesProperty<int>("count_rate");
    log1->addValue("2016-03-17T00:00:00", 1);
    log1->addValue("2016-03-17T00:30:00", 2);
    log1->addValue("2016-03-17T01:00:00", 3);
    log1->addValue("2016-03-17T01:30:00", 4);
    log1->addValue("2016-03-17T02:00:00", 5);
    TimeSeriesProperty<bool> *filter1 = new TimeSeriesProperty<bool>("filter");
    filter1->addValue("2016-Mar-17T00:00:00", 1);
    filter1->addValue("2016-Mar-17T01:00:00", 0);
    log1->filterWith(filter1);

    TimeSeriesProperty<int> *log2 = new TimeSeriesProperty<int>("count_rate");
    log2->addValue("2016-03-17T03:00:00", 1);
    log2->addValue("2016-03-17T04:00:00", 2);
    log2->addValue("2016-03-17T05:00:00", 3);
    log2->addValue("2016-03-17T06:00:0", 4);
    TimeSeriesProperty<bool> *filter2 = new TimeSeriesProperty<bool>("filter");
    filter2->addValue("2016-Mar-17T03:00:00", 1);
    filter2->addValue("2016-Mar-17T05:00:00", 0);
    log2->filterWith(filter2);


    delete log1;
    delete log2;
    delete filter1;
    delete filter2;
  }

  //----------------------------------------------------------------------------
  void test_filterByTime() {
    TimeSeriesProperty<int> *log = createIntegerTSP(6);
    TS_ASSERT_EQUALS(log->realSize(), 6);
    DateAndTime start = DateAndTime("2007-11-30T16:17:10");
    DateAndTime stop = DateAndTime("2007-11-30T16:17:40");
    // Since the filter is < stop, the last one is not counted, so there are  3
    // taken out.
    TS_ASSERT_EQUALS(log->realSize(), 3);
  //-------------------------------------------------------------------------------
  void test_filterByTimes1() {
    TimeSeriesProperty<int> *log = createIntegerTSP(6);
    TS_ASSERT_EQUALS(log->realSize(), 6);
    Mantid::Kernel::SplittingInterval interval0(
        DateAndTime("2007-11-30T16:17:10"), DateAndTime("2007-11-30T16:17:40"),
        0);

    Mantid::Kernel::TimeSplitterType splitters;
    splitters.push_back(interval0);

    // Since the filter is < stop, the last one is not counted, so there are  3
    // taken out.

    log->filterByTimes(splitters);

    TS_ASSERT_EQUALS(log->realSize(), 3);
  void test_filterByTimesN() {
    TimeSeriesProperty<int> *log = createIntegerTSP(10);
    TS_ASSERT_EQUALS(log->realSize(), 10);
    Mantid::Kernel::SplittingInterval interval0(
        DateAndTime("2007-11-30T16:17:10"), DateAndTime("2007-11-30T16:17:40"),
        0);
    Mantid::Kernel::SplittingInterval interval1(
        DateAndTime("2007-11-30T16:18:05"), DateAndTime("2007-11-30T16:18:25"),
        0);

    Mantid::Kernel::TimeSplitterType splitters;
    splitters.push_back(interval0);
    splitters.push_back(interval1);

    // Since the filter is < stop, the last one is not counted, so there are  3
    // taken out.

    log->filterByTimes(splitters);

    TS_ASSERT_EQUALS(log->realSize(), 6);
  //----------------------------------------------------------------------------
  /// Ticket #2591
  void test_filterByTime_ifOnlyOneValue_assumes_constant_instead() {
    TimeSeriesProperty<int> *log = createIntegerTSP(1);
    TS_ASSERT_EQUALS(log->realSize(), 1);

    DateAndTime start = DateAndTime("2007-11-30T16:17:10");
    DateAndTime stop = DateAndTime("2007-11-30T16:17:40");
    log->filterByTime(start, stop);
    TS_ASSERT_EQUALS(log->realSize(), 1);
  }

  //----------------------------------------------------------------------------
  /// Ticket #2591
  void test_filterByTime_ifOnlyOneValue_assumes_constant_instead_2() {
    TimeSeriesProperty<int> *log = new TimeSeriesProperty<int>("MyIntLog");
    TS_ASSERT_THROWS_NOTHING(log->addValue("1990-01-01T00:00:00", 1));
    TS_ASSERT_EQUALS(log->realSize(), 1);

    DateAndTime start = DateAndTime("2007-11-30T16:17:10");
    DateAndTime stop = DateAndTime("2007-11-30T16:17:40");
    log->filterByTime(start, stop);
    TS_ASSERT_EQUALS(log->realSize(), 1);
  //----------------------------------------------------------------------------
  void test_makeFilterByValue() {
    TimeSeriesProperty<double> *log =
        new TimeSeriesProperty<double>("MyIntLog");
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:00", 1));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:10", 2));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:20", 3));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:30", 2.0));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:40", 2.01));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:50", 6));

    TS_ASSERT_EQUALS(log->realSize(), 6);
    // Test centred log value boundaries
    log->makeFilterByValue(splitter, 1.8, 2.2, 1.0, true);
    TS_ASSERT_EQUALS(splitter.size(), 2);
    TS_ASSERT_DELTA(s.start(), t, 1e-3);
    TS_ASSERT_DELTA(s.stop(), t, 1e-3);
    TS_ASSERT_DELTA(s.start(), t, 1e-3);
    TS_ASSERT_DELTA(s.stop(), t, 1e-3);

    // Now test with left-aligned log value boundaries
    log->makeFilterByValue(splitter, 1.8, 2.2, 1.0);
    TS_ASSERT_EQUALS(splitter.size(), 2);

    s = splitter[0];
    t = DateAndTime("2007-11-30T16:17:10");
    TS_ASSERT_DELTA(s.start(), t, 1e-3);
    t = DateAndTime("2007-11-30T16:17:20");
    TS_ASSERT_DELTA(s.stop(), t, 1e-3);

    s = splitter[1];
    t = DateAndTime("2007-11-30T16:17:30");
    TS_ASSERT_DELTA(s.start(), t, 1e-3);
    t = DateAndTime("2007-11-30T16:17:50");
    TS_ASSERT_DELTA(s.stop(), t, 1e-3);
    TS_ASSERT_THROWS(log->makeFilterByValue(splitter, 2.0, 1.0, 0.0, true),
                     std::invalid_argument);
  void test_makeFilterByValue_throws_for_string_property() {
    TimeSeriesProperty<std::string> log("StringTSP");
    TimeSplitterType splitter;
    TS_ASSERT_THROWS(log.makeFilterByValue(splitter, 0.0, 0.0, 0.0, true),
                     Exception::NotImplementedError);
  void test_expandFilterToRange() {
    TimeSeriesProperty<int> log("MyIntLog");
    TS_ASSERT_THROWS_NOTHING(log.addValue("2007-11-30T16:17:00", 1));
    TS_ASSERT_THROWS_NOTHING(log.addValue("2007-11-30T16:17:10", 2));
    TS_ASSERT_THROWS_NOTHING(log.addValue("2007-11-30T16:17:20", 3));
    TS_ASSERT_THROWS_NOTHING(log.addValue("2007-11-30T16:17:30", 4));
    TS_ASSERT_THROWS_NOTHING(log.addValue("2007-11-30T16:17:40", 6));
    TS_ASSERT_THROWS_NOTHING(log.addValue("2007-11-30T16:17:50", 2));

    // Create a TimeInterval that's wider than this log
    TimeInterval interval(DateAndTime("2007-11-30T16:16:00"),
                          DateAndTime("2007-11-30T16:18:50"));

    TimeSplitterType splitter;
    // Test good at both ends
    log.makeFilterByValue(splitter, 1.0, 2.2, 1.0, false);
    log.expandFilterToRange(splitter, 1.0, 2.2, interval);
    TS_ASSERT_EQUALS(splitter.size(), 2);
    TS_ASSERT_DELTA(splitter[0].start(), DateAndTime("2007-11-30T16:16:00"),
                    1e-3);
    TS_ASSERT_DELTA(splitter[0].stop(), DateAndTime("2007-11-30T16:17:20"),
                    1e-3);
    TS_ASSERT_DELTA(splitter[1].start(), DateAndTime("2007-11-30T16:17:50"),
                    1e-3);
    TS_ASSERT_DELTA(splitter[1].stop(), DateAndTime("2007-11-30T16:18:50"),
                    1e-3);

    // Test bad at both ends
    log.makeFilterByValue(splitter, 2.5, 10.0, 0.0, false);
    log.expandFilterToRange(splitter, 2.5, 10.0, interval);
    TS_ASSERT_EQUALS(splitter.size(), 1);
    TS_ASSERT_DELTA(splitter[0].start(), DateAndTime("2007-11-30T16:17:20"),
                    1e-3);
    TS_ASSERT_DELTA(splitter[0].stop(), DateAndTime("2007-11-30T16:17:50"),
                    1e-3);

    // Test good at start, bad at end
    log.makeFilterByValue(splitter, -1.0, 1.5, 0.0, false);
    log.expandFilterToRange(splitter, -1.0, 1.5, interval);
    TS_ASSERT_EQUALS(splitter.size(), 1);
    TS_ASSERT_DELTA(splitter[0].start(), DateAndTime("2007-11-30T16:16:00"),
                    1e-3);
    TS_ASSERT_DELTA(splitter[0].stop(), DateAndTime("2007-11-30T16:17:10"),
                    1e-3);

    // Test good at end, bad at start
    log.makeFilterByValue(splitter, 1.99, 2.5, 1.0, false);
    log.expandFilterToRange(splitter, 1.99, 2.5, interval);
    TS_ASSERT_EQUALS(splitter.size(), 2);
    TS_ASSERT_DELTA(splitter[0].start(), DateAndTime("2007-11-30T16:17:10"),
                    1e-3);
    TS_ASSERT_DELTA(splitter[0].stop(), DateAndTime("2007-11-30T16:17:20"),
                    1e-3);
    TS_ASSERT_DELTA(splitter[1].start(), DateAndTime("2007-11-30T16:17:50"),
                    1e-3);
    TS_ASSERT_DELTA(splitter[1].stop(), DateAndTime("2007-11-30T16:18:50"),
                    1e-3);
    TS_ASSERT_THROWS(log.expandFilterToRange(splitter, 2.0, 1.0, interval),
                     std::invalid_argument);

    // Test good at both ends, but interval narrower than log range
    TimeInterval narrowinterval(DateAndTime("2007-11-30T16:17:15"),
                                DateAndTime("2007-11-30T16:17:41"));
    log.makeFilterByValue(splitter, 0.0, 10.0, 0.0, false);
    log.expandFilterToRange(splitter, 0.0, 10.0, narrowinterval);
    TS_ASSERT_EQUALS(splitter.size(), 1);
    TS_ASSERT_DELTA(splitter[0].start(), DateAndTime("2007-11-30T16:17:00"),
                    1e-3);
    TS_ASSERT_DELTA(splitter[0].stop(), DateAndTime("2007-11-30T16:17:50"),
                    1e-3);
  void test_expandFilterToRange_throws_for_string_property() {
    TimeSeriesProperty<std::string> log("StringTSP");
    TimeSplitterType splitter;
    TS_ASSERT_THROWS(
        log.expandFilterToRange(splitter, 0.0, 0.0, TimeInterval()),
        Exception::NotImplementedError);
  void test_averageValueInFilter() {
    auto dblLog = createDoubleTSP();
    auto intLog = createIntegerTSP(5);

    // Test a filter that's fully within the range of both properties
    TimeSplitterType filter;
    filter.push_back(SplittingInterval(DateAndTime("2007-11-30T16:17:05"),
                                       DateAndTime("2007-11-30T16:17:29")));
    TS_ASSERT_DELTA(dblLog->averageValueInFilter(filter), 7.308, 0.001);
    TS_ASSERT_DELTA(intLog->averageValueInFilter(filter), 2.167, 0.001);

    // Test a filter that starts before the log start time
    filter[0] = SplittingInterval(DateAndTime("2007-11-30T16:16:30"),
                                  DateAndTime("2007-11-30T16:17:13"));
    TS_ASSERT_DELTA(dblLog->averageValueInFilter(filter), 9.820, 0.001);
    TS_ASSERT_DELTA(intLog->averageValueInFilter(filter), 1.070, 0.001);

    // How about one that's entirely outside the log range (should just take the
    // last value)
    filter[0] = SplittingInterval(DateAndTime("2013-01-01T00:00:00"),
                                  DateAndTime("2013-01-01T01:00:00"));
    TS_ASSERT_DELTA(dblLog->averageValueInFilter(filter), 10.55, 0.001);
    TS_ASSERT_DELTA(intLog->averageValueInFilter(filter), 5.0, 0.001);

    // Test a filter with two separate ranges, one of which goes past the end of
    // the log
    filter[0] = SplittingInterval(DateAndTime("2007-11-30T16:17:05"),
                                  DateAndTime("2007-11-30T16:17:15"));
    filter.push_back(SplittingInterval(DateAndTime("2007-11-30T16:17:25"),
                                       DateAndTime("2007-11-30T16:17:45")));
    TS_ASSERT_DELTA(dblLog->averageValueInFilter(filter), 9.123, 0.001);
    TS_ASSERT_DELTA(intLog->averageValueInFilter(filter), 3.167, 0.001);

    // Test a filter with two out of order ranges (the second one coming before
    // the first)
    // It should work fine.
    filter[0] = filter[1];
    filter[0] = SplittingInterval(DateAndTime("2007-11-30T16:17:05"),
                                  DateAndTime("2007-11-30T16:17:15"));
    TS_ASSERT_DELTA(dblLog->averageValueInFilter(filter), 9.123, 0.001);
    TS_ASSERT_DELTA(intLog->averageValueInFilter(filter), 3.167, 0.001);

    // What about an overlap between the filters? It's odd, but it's allowed.
    filter[0] = SplittingInterval(DateAndTime("2007-11-30T16:17:05"),
                                  DateAndTime("2007-11-30T16:17:15"));
    filter[1] = SplittingInterval(DateAndTime("2007-11-30T16:17:10"),
                                  DateAndTime("2007-11-30T16:17:20"));
    TS_ASSERT_DELTA(dblLog->averageValueInFilter(filter), 8.16, 0.001);
    TS_ASSERT_DELTA(intLog->averageValueInFilter(filter), 1.75, 0.001);

    // Check the correct behaviour of empty of single value logs.
    TS_ASSERT(std::isnan(dProp->averageValueInFilter(filter)));
    iProp->addValue(DateAndTime("2010-11-30T16:17:25"), 99);
    TS_ASSERT_EQUALS(iProp->averageValueInFilter(filter), 99.0);
  void test_timeAverageValue() {
    auto dblLog = createDoubleTSP();
    auto intLog = createIntegerTSP(5);

    TS_ASSERT_DELTA(dblLog->timeAverageValue(), 7.6966, .0001);
    TS_ASSERT_DELTA(intLog->timeAverageValue(), 2.5, .0001);
  void test_averageValueInFilter_throws_for_string_property() {
    TS_ASSERT_THROWS(sProp->averageValueInFilter(splitter),
                     Exception::NotImplementedError);
  //----------------------------------------------------------------------------
  void test_splitByTime_and_getTotalValue() {
    TimeSeriesProperty<int> *log = createIntegerTSP(12);
    // Make the outputs
    std::vector<Property *> outputs;
    for (std::size_t i = 0; i < 5; i++) {
      TimeSeriesProperty<int> *newlog = new TimeSeriesProperty<int>("MyIntLog");
      outputs.push_back(newlog);
    }

    // Make a splitter
    TimeSplitterType splitter;
    start = DateAndTime("2007-11-30T16:17:10");
    stop = DateAndTime("2007-11-30T16:17:40");
    splitter.push_back(SplittingInterval(start, stop, 0));
    start = DateAndTime("2007-11-30T16:17:55");
    stop = DateAndTime("2007-11-30T16:17:56");
    splitter.push_back(SplittingInterval(start, stop, 1));
    start = DateAndTime("2007-11-30T16:17:56");
    stop = DateAndTime("2007-11-30T16:18:01");
    splitter.push_back(SplittingInterval(start, stop, 2)); // just one entry
    start = DateAndTime("2007-11-30T16:18:09");
    stop = DateAndTime("2007-11-30T16:18:21");
    splitter.push_back(SplittingInterval(start, stop, 3));
    start = DateAndTime("2007-11-30T16:18:45");
    stop = DateAndTime("2007-11-30T16:22:50");
    splitter.push_back(SplittingInterval(start, stop, 4));
    log->splitByTime(splitter, outputs, false);
    TS_ASSERT_EQUALS(
        dynamic_cast<TimeSeriesProperty<int> *>(outputs[0])->realSize(), 3);
    TS_ASSERT_EQUALS(
        dynamic_cast<TimeSeriesProperty<int> *>(outputs[1])->realSize(), 1);
    TS_ASSERT_EQUALS(
        dynamic_cast<TimeSeriesProperty<int> *>(outputs[2])->realSize(), 2);
    TS_ASSERT_EQUALS(
        dynamic_cast<TimeSeriesProperty<int> *>(outputs[3])->realSize(), 3);
    TS_ASSERT_EQUALS(
        dynamic_cast<TimeSeriesProperty<int> *>(outputs[4])->realSize(), 2);
    delete log;
    delete outputs[0];
    delete outputs[1];
    delete outputs[2];
    delete outputs[3];
    delete outputs[4];
  //----------------------------------------------------------------------------
  void test_splitByTime_withOverlap() {
    TimeSeriesProperty<int> *log = createIntegerTSP(12);
    // Make the outputs
    std::vector<Property *> outputs;
    for (std::size_t i = 0; i < 1; i++) {
      TimeSeriesProperty<int> *newlog = new TimeSeriesProperty<int>("MyIntLog");
    // Make a splitter
    start = DateAndTime("2007-11-30T16:17:10");
    stop = DateAndTime("2007-11-30T16:17:40");
    splitter.push_back(SplittingInterval(start, stop, 0));
    start = DateAndTime("2007-11-30T16:17:35");
    stop = DateAndTime("2007-11-30T16:17:59");
    splitter.push_back(SplittingInterval(start, stop, 0));
    log->splitByTime(splitter, outputs, false);
    TS_ASSERT_EQUALS(
        dynamic_cast<TimeSeriesProperty<int> *>(outputs[0])->realSize(), 5);
    delete log;
    delete outputs[0];
  //----------------------------------------------------------------------------
  /**
   * otuput 0 has entries: 3
   * otuput 1 has entries: 5
   * otuput 2 has entries: 2
   * otuput 3 has entries: 7
   * @brief test_splitByTimeVector
   */
  void test_splitByTimeVector() {
    // create the splitters
    std::vector<DateAndTime> split_time_vec;
    split_time_vec.push_back(DateAndTime("2007-11-30T16:17:10"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:17:40"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:17:55"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:17:56"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:18:09"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:18:45"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:22:50"));

    std::vector<int> split_target_vec;
    split_target_vec.push_back(1);
    split_target_vec.push_back(0);
    split_target_vec.push_back(2);
    split_target_vec.push_back(0);
    split_target_vec.push_back(1);
    split_target_vec.push_back(3);

    TimeSeriesProperty<int> log("test log");
    log.addValue(DateAndTime("2007-11-30T16:17:00"), 1);
    log.addValue(DateAndTime("2007-11-30T16:17:30"), 2);
    log.addValue(DateAndTime("2007-11-30T16:18:00"), 3);
    log.addValue(DateAndTime("2007-11-30T16:18:30"), 4);
    log.addValue(DateAndTime("2007-11-30T16:19:00"), 5);
    log.addValue(DateAndTime("2007-11-30T16:19:30"), 6);
    log.addValue(DateAndTime("2007-11-30T16:20:00"), 7);
    log.addValue(DateAndTime("2007-11-30T16:20:30"), 8);
    log.addValue(DateAndTime("2007-11-30T16:21:00"), 9);
    log.addValue(DateAndTime("2007-11-30T16:21:30"), 10);

    std::vector<TimeSeriesProperty<int> *> outputs;
    for (int itarget = 0; itarget < 4; ++itarget) {
      TimeSeriesProperty<int> *tsp = new TimeSeriesProperty<int>("target");
      outputs.push_back(tsp);
    }

    log.splitByTimeVector(split_time_vec, split_target_vec, outputs);

    // Exam the split entries
    TimeSeriesProperty<int> *out_0 = outputs[0];
    // FIXME - Check whether out_0 is correct!
    TS_ASSERT_EQUALS(out_0->size(), 3);
    TS_ASSERT_EQUALS(out_0->nthValue(0), 2);
    TS_ASSERT_EQUALS(out_0->nthValue(1), 3);
    TS_ASSERT_EQUALS(out_0->nthValue(2), 4);
    TimeSeriesProperty<int> *out_1 = outputs[1];
    TS_ASSERT_EQUALS(out_1->size(), 5);
    TS_ASSERT_EQUALS(out_1->nthValue(0), 1);
    TS_ASSERT_EQUALS(out_1->nthValue(1), 2);
    TS_ASSERT_EQUALS(out_1->nthValue(2), 3);
    TS_ASSERT_EQUALS(out_1->nthValue(3), 4);
    TS_ASSERT_EQUALS(out_1->nthValue(4), 5);
    TimeSeriesProperty<int> *out_2 = outputs[2];
    TS_ASSERT_EQUALS(out_2->size(), 2);
    TS_ASSERT_EQUALS(out_2->nthValue(0), 2);
    TS_ASSERT_EQUALS(out_2->nthValue(1), 3);

    TimeSeriesProperty<int> *out_3 = outputs[3];
    TS_ASSERT_EQUALS(out_3->size(), 7);
    // out[3] should have entries: 4, 5, 6, 7, 8, 9, 10
    for (int j = 0; j < out_3->size(); ++j) {
      TS_ASSERT_EQUALS(out_3->nthValue(j), j + 4);
  }

  //----------------------------------------------------------------------------
  /** last splitter is before first entry
   * @brief test_splitByTimeVectorEarlySplitter
   */
  void test_splitByTimeVectorEarlySplitter() {
    // create the splitters
    std::vector<DateAndTime> split_time_vec;
    split_time_vec.push_back(DateAndTime("2007-11-30T16:00:10"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:00:40"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:07:55"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:07:56"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:08:09"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:08:45"));
    split_time_vec.push_back(DateAndTime("2007-11-30T16:12:50"));

    std::vector<int> split_target_vec;
    split_target_vec.push_back(1);
    split_target_vec.push_back(0);
    split_target_vec.push_back(2);
    split_target_vec.push_back(0);
    split_target_vec.push_back(1);
    split_target_vec.push_back(3);

    TimeSeriesProperty<int> log("test log");
    log.addValue(DateAndTime("2007-11-30T16:17:00"), 1);
    log.addValue(DateAndTime("2007-11-30T16:17:30"), 2);
    log.addValue(DateAndTime("2007-11-30T16:18:00"), 3);
    log.addValue(DateAndTime("2007-11-30T16:18:30"), 4);
    log.addValue(DateAndTime("2007-11-30T16:19:00"), 5);
    log.addValue(DateAndTime("2007-11-30T16:19:30"), 6);
    log.addValue(DateAndTime("2007-11-30T16:20:00"), 7);
    log.addValue(DateAndTime("2007-11-30T16:20:30"), 8);
    log.addValue(DateAndTime("2007-11-30T16:21:00"), 9);
    log.addValue(DateAndTime("2007-11-30T16:21:30"), 10);

    // Initialze the 4 splitters
    std::vector<TimeSeriesProperty<int> *> outputs;
    for (int itarget = 0; itarget < 4; ++itarget) {
      TimeSeriesProperty<int> *tsp = new TimeSeriesProperty<int>("target");
      outputs.push_back(tsp);
    }

    log.splitByTimeVector(split_time_vec, split_target_vec, outputs);

    // check
    for (int i = 0; i < 4; ++i) {
      TimeSeriesProperty<int> *out_i = outputs[i];
      TS_ASSERT_EQUALS(out_i->size(), 0);
    }
  }

  //----------------------------------------------------------------------------
  /** first splitter is after last entry
   * @brief test_splitByTimeVectorLaterSplitter
   */
  void test_splitByTimeVectorLaterSplitter() {
    // create the splitters
    std::vector<DateAndTime> split_time_vec;
    split_time_vec.push_back(DateAndTime("2007-12-30T16:00:10"));
    split_time_vec.push_back(DateAndTime("2007-12-30T16:00:40"));
    split_time_vec.push_back(DateAndTime("2007-12-30T16:07:55"));
    split_time_vec.push_back(DateAndTime("2007-12-30T16:07:56"));
    split_time_vec.push_back(DateAndTime("2007-12-30T16:08:09"));
    split_time_vec.push_back(DateAndTime("2007-12-30T16:08:45"));
    split_time_vec.push_back(DateAndTime("2007-12-30T16:12:50"));

    std::vector<int> split_target_vec;
    split_target_vec.push_back(1);
    split_target_vec.push_back(0);
    split_target_vec.push_back(2);
    split_target_vec.push_back(0);
    split_target_vec.push_back(1);
    split_target_vec.push_back(3);

    // create test log
    TimeSeriesProperty<int> log("test log");
    log.addValue(DateAndTime("2007-11-30T16:17:00"), 1);
    log.addValue(DateAndTime("2007-11-30T16:17:30"), 2);
    log.addValue(DateAndTime("2007-11-30T16:18:00"), 3);
    log.addValue(DateAndTime("2007-11-30T16:18:30"), 4);
    log.addValue(DateAndTime("2007-11-30T16:19:00"), 5);
    log.addValue(DateAndTime("2007-11-30T16:19:30"), 6);
    log.addValue(DateAndTime("2007-11-30T16:20:00"), 7);
    log.addValue(DateAndTime("2007-11-30T16:20:30"), 8);
    log.addValue(DateAndTime("2007-11-30T16:21:00"), 9);
    log.addValue(DateAndTime("2007-11-30T16:21:30"), 10);

    // Initialze the 4 splitters
    std::vector<TimeSeriesProperty<int> *> outputs;
    for (int itarget = 0; itarget < 4; ++itarget) {
      TimeSeriesProperty<int> *tsp = new TimeSeriesProperty<int>("target");
      outputs.push_back(tsp);
    }

    log.splitByTimeVector(split_time_vec, split_target_vec, outputs);

    // check
    for (int i = 0; i < 4; ++i) {
      TimeSeriesProperty<int> *out_i = outputs[i];
      TS_ASSERT_EQUALS(out_i->size(), 0);
    }
  }

  //----------------------------------------------------------------------------
  /** high-frequency splitters splits a slow change log
   * @brief test_splitByTimeVectorFastLogSplitter
   */
  void test_splitByTimeVectorFastLogSplitter() {
    // create test log
    TimeSeriesProperty<int> log("test log");
    log.addValue(DateAndTime("2007-11-30T16:17:00"), 1);
    log.addValue(DateAndTime("2007-11-30T16:17:30"), 2);
    log.addValue(DateAndTime("2007-11-30T16:18:00"), 3);
    log.addValue(DateAndTime("2007-11-30T16:18:30"), 4);
    log.addValue(DateAndTime("2007-11-30T16:19:00"), 5);
    log.addValue(DateAndTime("2007-11-30T16:19:30"), 6);
    log.addValue(DateAndTime("2007-11-30T16:20:00"), 7);
    log.addValue(DateAndTime("2007-11-30T16:20:30"), 8);
    log.addValue(DateAndTime("2007-11-30T16:21:00"), 9);
    log.addValue(DateAndTime("2007-11-30T16:21:30"), 10);

    // create a high frequency splitter
    DateAndTime split_time("2007-11-30T16:17:00");
    int64_t dt = 100 * 1000;

    std::vector<DateAndTime> vec_split_times;
    std::vector<int> vec_split_target;

    for (int i = 0; i < 10; ++i) {
      for (int j = 0; j < 10; ++j) {
        vec_split_times.push_back(split_time);
        split_time += dt;
        vec_split_target.push_back(j);
      }
    }

    // push back last split-time (split stop)
    vec_split_times.push_back(split_time);

    // Initialze the 10 splitters
    std::vector<TimeSeriesProperty<int> *> outputs;
    for (int itarget = 0; itarget < 10; ++itarget) {
      TimeSeriesProperty<int> *tsp = new TimeSeriesProperty<int>("target");
      outputs.push_back(tsp);
    }

    size_t num_splits = vec_split_target.size();
    for (size_t i = 0; i < num_splits; ++i) {
      std::cout << "s[" << i << "]  start = " << vec_split_times[i]
                << ", stop = " << vec_split_times[i + 1]
                << ":  target = " << vec_split_target[i] << "\n";
    // split time series property
    log.splitByTimeVector(vec_split_times, vec_split_target, outputs);
    // TODO/FIXME/ - continue to debug from here!
    TimeSeriesProperty<int> *out0 = outputs[0];
    for (int i = 0; i < out0->size(); ++i) {
      std::cout << i << "-th: " << out0->nthTime(i) << ", " << out0->nthValue(i)
                << "\n";
    for (size_t i = 0; i < 10; ++i) {
      TS_ASSERT_EQUALS(outputs[i]->size(), 2);
  }

  //----------------------------------------------------------------------------
  void test_statistics() {
    TimeSeriesProperty<double> *log =
        new TimeSeriesProperty<double>("MydoubleLog");
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:00", 1));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:10", 2));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:20", 3));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:30", 4));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:40", 5));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:17:50", 6));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:18:00", 7));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:18:10", 8));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:18:20", 9));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:18:30", 10));
    TS_ASSERT_THROWS_NOTHING(log->addValue("2007-11-30T16:18:40", 11));
    TS_ASSERT_EQUALS(log->realSize(), 11);
    TimeSeriesPropertyStatistics stats = log->getStatistics();
    TS_ASSERT_DELTA(stats.minimum, 1.0, 1e-3);
    TS_ASSERT_DELTA(stats.maximum, 11.0, 1e-3);
    TS_ASSERT_DELTA(stats.median, 6.0, 1e-3);
    TS_ASSERT_DELTA(stats.mean, 6.0, 1e-3);
    TS_ASSERT_DELTA(stats.duration, 100.0, 1e-3);
    TS_ASSERT_DELTA(stats.standard_deviation, 3.1622, 1e-3);
    TS_ASSERT_DELTA(log->timeAverageValue(), 5.5, 1e-3);
  void test_empty_statistics() {
    TimeSeriesProperty<double> *log =
        new TimeSeriesProperty<double>("MydoubleLog");
    TimeSeriesPropertyStatistics stats = log->getStatistics();