/* Copyright (C) 2014 InfiniDB, Inc.

   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; version 2 of
   the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

// $Id: tdriver-function.cpp 9210 2013-01-21 14:10:42Z rdempsey $
#include <iostream>
#include <list>
#include <sstream>
#include <pthread.h>
#include <iomanip>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>

#include "wsdl.h"
#include "constantdatalist.h"

#include "calpontsystemcatalog.h"
using namespace execplan;

#include "functionoperation.h"
#include <boost/any.hpp>
#include <boost/function.hpp>
#include "bytestream.h"
#include <time.h>
#include <sys/time.h>
#include <cmath>
#include <boost/any.hpp>
#include <jobstep.h>

#define DEBUG

using namespace std;
using namespace joblist;
using namespace messageqcpp;

// void (*fp) (double, double);

// Timer class used by this tdriver to output elapsed times, etc.
class Timer
{
 public:
  void start(const string& message)
  {
    if (!fHeaderDisplayed)
    {
      header();
      fHeaderDisplayed = true;
    }

    gettimeofday(&fTvStart, 0);
    cout << timestr() << "          Start " << message << endl;
  }

  void stop(const string& message)
  {
    time_t now;
    time(&now);
    string secondsElapsed;
    getTimeElapsed(secondsElapsed);
    cout << timestr() << " " << secondsElapsed << " Stop  " << message << endl;
  }

  Timer() : fHeaderDisplayed(false)
  {
  }

 private:
  struct timeval fTvStart;
  bool fHeaderDisplayed;

  double getTimeElapsed(string& seconds)
  {
    struct timeval tvStop;
    gettimeofday(&tvStop, 0);
    double secondsElapsed =
        (tvStop.tv_sec + (tvStop.tv_usec / 1000000.0)) - (fTvStart.tv_sec + (fTvStart.tv_usec / 1000000.0));
    ostringstream oss;
    oss << secondsElapsed;
    seconds = oss.str();
    seconds.resize(8, '0');
    return secondsElapsed;
  }

  string timestr()
  {
    struct tm tm;
    struct timeval tv;

    gettimeofday(&tv, 0);
    localtime_r(&tv.tv_sec, &tm);

    ostringstream oss;
    oss << setfill('0') << setw(2) << tm.tm_hour << ':' << setw(2) << tm.tm_min << ':' << setw(2) << tm.tm_sec
        << '.' << setw(6) << tv.tv_usec;
    return oss.str();
  }

  void header()
  {
    cout << endl;
    cout << "Time            Seconds  Activity" << endl;
  }
};

// Timer class used by this tdriver to output elapsed times, etc.
class MultiTimer
{
 public:
  void start(const string& message);
  void stop(const string& message);
  void finish()
  {
    // Calculate the total seconds elapsed.
    struct timeval tvStop;
    gettimeofday(&tvStop, 0);
    double totalSeconds =
        (tvStop.tv_sec + (tvStop.tv_usec / 1000000.0)) - (fTvStart.tv_sec + (fTvStart.tv_usec / 1000000.0));

    cout << endl;
    cout << "Seconds  Percentage  Calls      Description" << endl;

    // Add a last entry into the vector for total.
    ProcessStats total;
    total.fTotalSeconds = totalSeconds;
    total.fProcess = "Total";
    total.fStartCount = 1;
    fProcessStats.push_back(total);

    for (uint32_t i = 0; i < fProcessStats.size(); i++)
    {
      if (i == (fProcessStats.size() - 1))
      {
        cout << endl;
      }

      // Seconds.
      string seconds;
      ostringstream oss;
      oss << fProcessStats[i].fTotalSeconds;
      seconds = oss.str();
      seconds.resize(7, '0');
      cout << seconds << "  ";

      // Percentage.
      string percentage;
      ostringstream oss2;
      oss2 << (fProcessStats[i].fTotalSeconds / totalSeconds) * 100.0;
      percentage = oss2.str();
      percentage.resize(5, ' ');
      cout << percentage << "%      ";

      // Times Initiated.
      ostringstream oss3;
      oss3 << fProcessStats[i].fStartCount;
      string timesInitiated = oss3.str();
      timesInitiated.resize(10, ' ');
      cout << timesInitiated << " ";

      // Description.
      cout << fProcessStats[i].fProcess << endl;
    }
  }
  MultiTimer() : fStarted(false){};

 private:
  class ProcessStats
  {
   public:
    string fProcess;
    struct timeval fTvProcessStarted;
    double fTotalSeconds;
    long fStartCount;
    long fStopCount;

    ProcessStats() : fProcess(""), fTotalSeconds(0.0), fStartCount(0), fStopCount(0){};

    void processStart()
    {
      gettimeofday(&fTvProcessStarted, 0);
      fStartCount++;
    }

    void processStop()
    {
      struct timeval tvStop;
      gettimeofday(&tvStop, 0);
      fStopCount++;
      fTotalSeconds += (tvStop.tv_sec + (tvStop.tv_usec / 1000000.0)) -
                       (fTvProcessStarted.tv_sec + (fTvProcessStarted.tv_usec / 1000000.0));
    }
  };

  struct timeval fTvStart;
  vector<ProcessStats> fProcessStats;
  bool fStarted;
};

void MultiTimer::stop(const string& message)
{
  bool found = false;
  uint32_t idx = 0;

  for (uint32_t i = 0; i < fProcessStats.size(); i++)
  {
    if (fProcessStats[i].fProcess == message)
    {
      idx = i;
      found = true;
      break;
    }
  }

  if (!found)
  {
    throw std::runtime_error("MultiTimer::stop " + message + " called without calling start first.");
  }

  fProcessStats[idx].processStop();
}

void MultiTimer::start(const string& message)
{
  bool found = false;
  uint32_t idx = 0;
  ProcessStats processStats;

  if (!fStarted)
  {
    fStarted = true;
    gettimeofday(&fTvStart, 0);
  }

  for (uint32_t i = 0; i < fProcessStats.size(); i++)
  {
    if (fProcessStats[i].fProcess == message)
    {
      idx = i;
      found = true;
      break;
    }
  }

  if (!found)
  {
    fProcessStats.push_back(processStats);
    idx = fProcessStats.size() - 1;
  }

  fProcessStats[idx].fProcess = message;
  fProcessStats[idx].processStart();
}

class functionDriver : public CppUnit::TestFixture
{
  CPPUNIT_TEST_SUITE(functionDriver);
  CPPUNIT_TEST(FUNCTION_TEST);
  CPPUNIT_TEST_SUITE_END();

 private:
  ResourceManager fRm;

  void testDrdFunctions()
  {
    cout << endl;
    cout << "double f(double) functions" << endl;
    cout << "---------------------------" << endl;

    FunctionOperation* fop = FunctionOperation::instance();

    // functions to test
    list<string> functionsToTest;
    functionsToTest.push_back("abs");
    functionsToTest.push_back("acos");
    functionsToTest.push_back("asin");
    functionsToTest.push_back("atan");
    functionsToTest.push_back("ceil");
    functionsToTest.push_back("cos");
    functionsToTest.push_back("cosh");
    functionsToTest.push_back("exp");
    functionsToTest.push_back("floor");
    functionsToTest.push_back("ln");
    functionsToTest.push_back("log2");
    functionsToTest.push_back("log10");
    functionsToTest.push_back("sin");
    functionsToTest.push_back("sinh");
    functionsToTest.push_back("sqrt");
    functionsToTest.push_back("tan");
    functionsToTest.push_back("tanh");

    // type vector is the same for drd functions
    vector<FunctionDataList::FuncDataListType> types;
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);

    int numRows = 1;
    DoubleElementType el0, el1;
    list<string>::iterator it;

    for (it = functionsToTest.begin(); it != functionsToTest.end(); it++)
    {
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr(*it, types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);
      WSDL<DoubleElementType>* wsdlIn1 = new WSDL<DoubleElementType>(2, numRows, fRm);

      for (int i = 0; i < numRows; i++)
      {
        el1.first = i;
        el1.second = (0.5);
        wsdlIn1->insert(el1);
      }

      wsdlIn1->endOfInput();

      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listOut(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->doubleDl(wsdlIn1);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlIn1->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlIn1->next(id1, &el1);
        wsdlOut->next(id0, &el0);
        cout << *it << "(" << el1.second << ")=" << el0.second << endl;
      }
    }
  }

  void testDrddFunctions()
  {
    cout << endl;
    cout << "double f(double, double) functions" << endl;
    cout << "---------------------------" << endl;

    FunctionOperation* fop = FunctionOperation::instance();

    list<string> functionsToTest;
    functionsToTest.push_back("atan2");
    functionsToTest.push_back("power");
    functionsToTest.push_back("+");
    functionsToTest.push_back("-");
    functionsToTest.push_back("*");
    functionsToTest.push_back("/");

    // type vector is the same for drdd functions
    vector<FunctionDataList::FuncDataListType> types;
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);

    int numRows = 1;
    DoubleElementType el0, el1, el2;
    list<string>::iterator it;

    for (it = functionsToTest.begin(); it != functionsToTest.end(); it++)
    {
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr(*it, types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);
      WSDL<DoubleElementType>* wsdlIn1 = new WSDL<DoubleElementType>(2, numRows, fRm);
      WSDL<DoubleElementType>* wsdlIn2 = new WSDL<DoubleElementType>(2, numRows, fRm);

      for (int i = 0; i < numRows; i++)
      {
        el1.first = i;
        el1.second = (0.5);
        wsdlIn1->insert(el1);
        wsdlIn2->insert(el1);  // the data in two list is the same
      }

      wsdlIn1->endOfInput();
      wsdlIn2->endOfInput();

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->doubleDl(wsdlIn1);
      listIn2->doubleDl(wsdlIn2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlIn1->getIterator();
      int id2 = wsdlIn2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlIn1->next(id1, &el1);
        wsdlIn2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << *it << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }
  }

  void testSrssFunctions()
  {
    cout << endl;
    cout << "string f(string, string) functions" << endl;
    cout << "---------------------------" << endl;

    FunctionOperation* fop = FunctionOperation::instance();

    list<string> functionsToTest;
    functionsToTest.push_back("||");
    functionsToTest.push_back("concat");

    // type vector is the same for drdd functions
    vector<FunctionDataList::FuncDataListType> types;
    types.push_back(FunctionDataList::STRING_LISTTYPE);
    types.push_back(FunctionDataList::STRING_LISTTYPE);
    types.push_back(FunctionDataList::STRING_LISTTYPE);

    int numRows = 1;
    StringElementType el0, el1, el2;
    list<string>::iterator it;

    for (it = functionsToTest.begin(); it != functionsToTest.end(); it++)
    {
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr(*it, types);
      CPPUNIT_ASSERT(fp);
      WSDL<StringElementType>* wsdlOut = new WSDL<StringElementType>(1, numRows, fRm);
      WSDL<StringElementType>* wsdlIn1 = new WSDL<StringElementType>(2, numRows, fRm);
      WSDL<StringElementType>* wsdlIn2 = new WSDL<StringElementType>(2, numRows, fRm);

      for (int i = 0; i < numRows; i++)
      {
        el1.first = i;
        el1.second = "abc";
        wsdlIn1->insert(el1);
        wsdlIn2->insert(el1);  // the data in two list is the same
      }

      wsdlIn1->endOfInput();
      wsdlIn2->endOfInput();

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->stringDl(wsdlOut);
      listIn1->stringDl(wsdlIn1);
      listIn2->stringDl(wsdlIn2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlIn1->getIterator();
      int id2 = wsdlIn2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlIn1->next(id1, &el1);
        wsdlIn2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << *it << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }
  }

  void testSrsFunctions()
  {
    cout << endl;
    cout << "string f(string) functions" << endl;
    cout << "---------------------------" << endl;

    FunctionOperation* fop = FunctionOperation::instance();

    list<string> functionsToTest;
    functionsToTest.push_back("upper");
    functionsToTest.push_back("asciistr");
    functionsToTest.push_back("trim");
    functionsToTest.push_back("ltrim");
    functionsToTest.push_back("rtrim");

    // type vector is the same for drdd functions
    vector<FunctionDataList::FuncDataListType> types;
    types.push_back(FunctionDataList::STRING_LISTTYPE);
    types.push_back(FunctionDataList::STRING_LISTTYPE);

    int numRows = 2;
    StringElementType el0, el1, el2;
    list<string>::iterator it;

    for (it = functionsToTest.begin(); it != functionsToTest.end(); it++)
    {
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr(*it, types);
      CPPUNIT_ASSERT(fp);
      WSDL<StringElementType>* wsdlOut = new WSDL<StringElementType>(1, numRows, fRm);
      WSDL<StringElementType>* wsdlIn1 = new WSDL<StringElementType>(2, numRows, fRm);

      char abc[][10] = {"  abc  ", "second"};
      abc[0][3] = 222;  // 0xDE

      for (int i = 0; i < numRows; i++)
      {
        el1.first = i;
        el1.second = abc[i];
        wsdlIn1->insert(el1);
      }

      wsdlIn1->endOfInput();

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FDLVec parms;
      listOut->stringDl(wsdlOut);
      listIn1->stringDl(wsdlIn1);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlIn1->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlIn1->next(id1, &el1);
        wsdlOut->next(id0, &el0);
        cout << *it << "(\'" << el1.second << "\')="
             << "\'" << el0.second << "\'" << endl;
      }
    }
  }

  void testAddFunctions()
  {
    cout << endl;
    cout << "add(<S>, <T>) functions" << endl;
    cout << "---------------------------" << endl;

    FunctionOperation* fop = FunctionOperation::instance();

    // the map only have one entry for add_ddd, which is mapped to add(any)
    vector<FunctionDataList::FuncDataListType> types;
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);

    int numRows = 1;

    // double list
    WSDL<DoubleElementType>* wsdlInD1 = new WSDL<DoubleElementType>(20, numRows, fRm);
    WSDL<DoubleElementType>* wsdlInD2 = new WSDL<DoubleElementType>(20, numRows, fRm);
    // uint32_t list
    WSDL<ElementType>* wsdlInU1 = new WSDL<ElementType>(20, numRows, fRm);
    WSDL<ElementType>* wsdlInU2 = new WSDL<ElementType>(20, numRows, fRm);
    // string list
    WSDL<StringElementType>* wsdlInS1 = new WSDL<StringElementType>(20, numRows, fRm);
    WSDL<StringElementType>* wsdlInS2 = new WSDL<StringElementType>(20, numRows, fRm);
    DoubleElementType eld;
    ElementType elu;
    StringElementType els;

    for (int i = 0; i < numRows; i++)
    {
      eld.first = i;
      eld.second = (0.5);
      wsdlInD1->insert(eld);
      wsdlInD2->insert(eld);
      elu.first = i;
      elu.second = (6);
      wsdlInU1->insert(elu);
      wsdlInU2->insert(elu);
      els.first = i;
      els.second = "8.8";
      wsdlInS1->insert(els);
      wsdlInS2->insert(els);
    }

    wsdlInD1->endOfInput();
    wsdlInD2->endOfInput();
    wsdlInU1->endOfInput();
    wsdlInU2->endOfInput();
    wsdlInS1->endOfInput();
    wsdlInS2->endOfInput();

    // double add(double, double)
    {
      DoubleElementType el0, el1, el2;
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("+", types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->doubleDl(wsdlInD1);
      listIn2->doubleDl(wsdlInD2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlInD1->getIterator();
      int id2 = wsdlInD2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlInD1->next(id1, &el1);
        wsdlInD2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << "+"
             << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }

    // double add(double, uint32_t)
    {
      DoubleElementType el0, el1;
      ElementType el2;
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("+", types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->doubleDl(wsdlInD1);
      listIn2->uint64Dl(wsdlInU2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlInD1->getIterator();
      int id2 = wsdlInU2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlInD1->next(id1, &el1);
        wsdlInU2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << "+"
             << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }

    // double add(double, string)
    {
      DoubleElementType el0, el1;
      StringElementType el2;
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("+", types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->doubleDl(wsdlInD1);
      listIn2->stringDl(wsdlInS2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlInD1->getIterator();
      int id2 = wsdlInS2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlInD1->next(id1, &el1);
        wsdlInS2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << "+"
             << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }

    // double add(uint32_t, double)
    {
      DoubleElementType el0, el2;
      ElementType el1;
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("+", types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->uint64Dl(wsdlInU1);
      listIn2->doubleDl(wsdlInD2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlInU1->getIterator();
      int id2 = wsdlInD2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlInU1->next(id1, &el1);
        wsdlInD2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << "+"
             << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }

    // double add(uint32_t, uint32_t)
    {
      DoubleElementType el0;
      ElementType el1, el2;
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("+", types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->uint64Dl(wsdlInU1);
      listIn2->uint64Dl(wsdlInU2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlInU1->getIterator();
      int id2 = wsdlInU2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlInU1->next(id1, &el1);
        wsdlInU2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << "+"
             << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }

    // double add(uint32_t, string)
    {
      DoubleElementType el0;
      ElementType el1;
      StringElementType el2;
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("+", types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->uint64Dl(wsdlInU1);
      listIn2->stringDl(wsdlInS2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlInU1->getIterator();
      int id2 = wsdlInS2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlInU1->next(id1, &el1);
        wsdlInS2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << "+"
             << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }

    // double add(string, double)
    {
      DoubleElementType el0, el2;
      StringElementType el1;
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("+", types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->stringDl(wsdlInS1);
      listIn2->doubleDl(wsdlInD2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlInS1->getIterator();
      int id2 = wsdlInD2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlInS1->next(id1, &el1);
        wsdlInD2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << "+"
             << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }

    // double add(string, uint32_t)
    {
      DoubleElementType el0;
      StringElementType el1;
      ElementType el2;
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("+", types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->stringDl(wsdlInS1);
      listIn2->uint64Dl(wsdlInU2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlInS1->getIterator();
      int id2 = wsdlInU2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlInS1->next(id1, &el1);
        wsdlInU2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << "+"
             << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }

    // double add(string, string)
    {
      DoubleElementType el0;
      StringElementType el1, el2;
      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("+", types);
      CPPUNIT_ASSERT(fp);
      WSDL<DoubleElementType>* wsdlOut = new WSDL<DoubleElementType>(1, numRows, fRm);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->doubleDl(wsdlOut);
      listIn1->stringDl(wsdlInS1);
      listIn2->stringDl(wsdlInS2);
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      wsdlOut->endOfInput();

      int id0 = wsdlOut->getIterator();
      int id1 = wsdlInS1->getIterator();
      int id2 = wsdlInS2->getIterator();

      for (int i = 0; i < numRows; i++)
      {
        wsdlInS1->next(id1, &el1);
        wsdlInS2->next(id2, &el2);
        wsdlOut->next(id0, &el0);
        cout << "+"
             << "(" << el1.second << ", " << el2.second << ")=" << el0.second << endl;
      }
    }
  }

  struct dateTime
  {
    unsigned msecond : 20;
    unsigned second : 6;
    unsigned minute : 6;
    unsigned hour : 6;
    unsigned day : 6;
    unsigned month : 4;
    unsigned year : 16;

    dateTime(int y = 0xFFFF, int mn = 0xF, int d = 0x3F, int h = 0x3F, int mi = 0x3F, int s = 0x3F,
             int ms = 0xFFFFE)
    {
      year = y;
      month = mn;
      day = d;
      hour = h;
      minute = mi;
      second = s;
      msecond = ms;
    }
  };

  void testDateFunctions()
  {
    cout << endl;
    cout << "to_date function with formats" << endl;
    cout << "-----------------------------" << endl;

    FunctionOperation* fop = FunctionOperation::instance();

    vector<FunctionDataList::FuncDataListType> types;
    types.push_back(FunctionDataList::STRING_LISTTYPE);
    types.push_back(FunctionDataList::UINT64_LISTTYPE);
    types.push_back(FunctionDataList::STRING_CONST_LISTTYPE);

    // data list
    AnyDataListSPtr spdl1(new AnyDataList());
    BandedDataList* dl1 = new BandedDataList(20, fRm);
    spdl1->bandedDL(dl1);

    dateTime dt(2007, 11, 12, 16, 7, 8, 999);
    uint64_t idt = *(reinterpret_cast<uint64_t*>(&dt));
    ElementType elu(0, idt);

    int numRows = 1;

    for (int i = 0; i < numRows; i++)
    {
      elu.first = i;
      dl1->insert(elu);
    }

    dl1->endOfInput();

    char fmt[6][30] = {"YYYYMMDDHH",          "YYYYMMDDHHMISS", "YYYYMMDDHHMISSFF",
                       "YYYY-MM-DD HH:MI:SS", "MON DD, RRRR",   "MM/DD/YY HH24:MI:SS.FF"};

    for (int i = 0; i < 6; i++)
    {
      AnyDataListSPtr spdl2(new AnyDataList());
      StringElementType els(0, fmt[i]);
      ConstantDataList<StringElementType>* dl2 = new ConstantDataList<StringElementType>(els);
      spdl2->stringConstantDL(dl2);
      dl2->endOfInput();

      JobStepAssociation inJs;
      inJs.outAdd(spdl1);
      inJs.outAdd(spdl2);
      //			jobstep->outputAssociation(outJs);  // output of a job step

      AnyDataListSPtr spdlOut(new AnyDataList());
      StringDataList* dlOut = new StringDataList(1, fRm);
      spdlOut->strDataList(dlOut);
      JobStepAssociation outJs;
      outJs.outAdd(spdlOut);
      //			funJobstep->inputAssociation(inJs);
      //			funJobstep->outputAssociation(outJs);

      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("to_char", types);
      CPPUNIT_ASSERT(fp);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FunctionDataListSPtr listIn2(new FunctionDataList());
      FDLVec parms;
      listOut->stringDl(outJs.outAt(0)->stringDataList());
      listIn1->uint64Dl(inJs.outAt(0)->dataList());
      listIn2->stringDl(inJs.outAt(1)->stringDataList());
      parms.push_back(listOut);
      parms.push_back(listIn1);
      parms.push_back(listIn2);
      fop->executeFunction(fop, fp, parms);
      dlOut->endOfInput();

      int id0 = dlOut->getIterator();
      int id1 = dl1->getIterator();
      int id2 = dl2->getIterator();
      StringElementType el0;
      ElementType el1;
      StringElementType el2;

      for (int i = 0; i < numRows; i++)
      {
        dl1->next(id1, &el1);
        dl2->next(id2, &el2);
        dlOut->next(id0, &el0);
        cout << "to_char"
             << "(" << el1.second << ", \'" << el2.second << "\')=" << el0.second << endl;
      }
    }

    // default format
    {
      JobStepAssociation inJs;
      inJs.outAdd(spdl1);
      //			jobstep->outputAssociation(outJs);  // output of a job step

      AnyDataListSPtr spdlOut(new AnyDataList());
      StringDataList* dlOut = new StringDataList(1, fRm);
      spdlOut->strDataList(dlOut);
      JobStepAssociation outJs;
      outJs.outAdd(spdlOut);
      //			funJobstep->inputAssociation(inJs);
      //			funJobstep->outputAssociation(outJs);

      FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("to_char", types);
      CPPUNIT_ASSERT(fp);

      FunctionDataListSPtr listOut(new FunctionDataList());
      FunctionDataListSPtr listIn1(new FunctionDataList());
      FDLVec parms;
      listOut->stringDl(outJs.outAt(0)->stringDataList());
      listIn1->uint64Dl(inJs.outAt(0)->dataList());
      parms.push_back(listOut);
      parms.push_back(listIn1);
      fop->executeFunction(fop, fp, parms);
      dlOut->endOfInput();

      int id0 = dlOut->getIterator();
      int id1 = dl1->getIterator();
      StringElementType el0;
      ElementType el1;

      for (int i = 0; i < numRows; i++)
      {
        dl1->next(id1, &el1);
        dlOut->next(id0, &el0);
        cout << "to_char"
             << "(" << el1.second << ")=" << el0.second << endl;
      }
    }
  }

  void testToNumFunctions()
  {
    cout << endl;
    cout << "to_num function, format ignored" << endl;
    cout << "-------------------------------" << endl;

    FunctionOperation* fop = FunctionOperation::instance();

    vector<FunctionDataList::FuncDataListType> types;
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);
    types.push_back(FunctionDataList::STRING_LISTTYPE);
    types.push_back(FunctionDataList::STRING_CONST_LISTTYPE);

    AnyDataListSPtr spdl1(new AnyDataList());
    StringFifoDataList* dl1 = new StringFifoDataList(2, 100);
    spdl1->stringDL(dl1);

    const int numRows = 6;
    char num[numRows][30] = {"2007111218", "1234567.8", "-123456.78", "2,007,111,218", "1.23E3", "-1.23E-3"};

    StringRowGroup rows;

    for (int i = 0; i < numRows; i++)
    {
      StringElementType els(0, num[i]);
      rows.et[rows.count++] = els;
      //			dl1->insert(els);
    }

    dl1->insert(rows);
    dl1->endOfInput();

    // format model, ignored
    AnyDataListSPtr spdl2(new AnyDataList());
    StringElementType els(0, "dummy");
    ConstantDataList<StringElementType>* dl2 = new ConstantDataList<StringElementType>(els);
    spdl2->stringConstantDL(dl2);
    dl2->endOfInput();

    JobStepAssociation inJs;
    inJs.outAdd(spdl1);
    inJs.outAdd(spdl2);
    //			jobstep->outputAssociation(outJs);  // output of a job step

    AnyDataListSPtr spdlOut(new AnyDataList());
    FIFO<DoubleElementType>* dlOut = new FIFO<DoubleElementType>(1, 100);
    spdlOut->doubleDL(dlOut);
    JobStepAssociation outJs;
    outJs.outAdd(spdlOut);
    //			funJobstep->inputAssociation(inJs);
    //			funJobstep->outputAssociation(outJs);

    FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("to_number", types);
    CPPUNIT_ASSERT(fp);

    FunctionDataListSPtr listOut(new FunctionDataList());
    FunctionDataListSPtr listIn1(new FunctionDataList());
    FunctionDataListSPtr listIn2(new FunctionDataList());
    FDLVec parms;
    listOut->doubleDl(outJs.outAt(0)->doubleDL());
    listIn1->stringDl(inJs.outAt(0)->stringDataList());
    listIn2->stringDl(inJs.outAt(1)->stringDataList());
    parms.push_back(listOut);
    parms.push_back(listIn1);
    parms.push_back(listIn2);
    fop->executeFunction(fop, fp, parms);
    dlOut->endOfInput();

    int id1 = dl1->getIterator();
    int id2 = dl2->getIterator();
    int id0 = dlOut->getIterator();
    // 		StringElementType el1;
    StringRowGroup el1;
    StringElementType el2;
    DoubleElementType el0;

    for (int i = 0; i < numRows; i++)
    {
      dl1->next(id1, &el1);
      dl2->next(id2, &el2);
      dlOut->next(id0, &el0);
      cout << "to_number"
           << "(\'" << el1.et[i].second << "\', \'" << el2.second << "\')=" << setw(20) << setprecision(10)
           << el0.second << endl;
    }
  }

  /*
      void testDrdFunctions()
      {
              cout << endl;
              cout << "double f(double) functions" << endl;
              cout << "---------------------------" << endl;
              FunctionOperation fo;
              drdMap_t::iterator it;
              drdMap_t map = fo.getDrdMap();
              int numRows = 1;
              DoubleElementType el, el2;
              for(it = map.begin(); it!= map.end(); it++)
              {
                      CPPUNIT_ASSERT(fo.isDrdFunction(it->first));
                      WSDL<DoubleElementType> wsdlIn(2, numRows);
                      WSDL<DoubleElementType> wsdlOut(1, numRows);
                      for(int i = 0; i < numRows; i++) {
                              el.first = i;
                              el.second = (0.5);
                              wsdlIn.insert(el);
                      }
                      wsdlIn.endOfInput();
                      fo.executeDrdFunction(it->first, wsdlIn, wsdlOut);
                      wsdlOut.endOfInput();
                      int id = wsdlIn.getIterator();
                      int id2 = wsdlOut.getIterator();
                      for(int i = 0; i < numRows; i++) {
                              wsdlIn.next(id, &el);
                              wsdlOut.next(id2, &el2);
                              cout << it->first << "(" << el.second << ")=" << el2.second << endl;
                      }
              }


      }

      void testDrddFunctions()
      {
              cout << endl;
              cout << "double f(double, double) functions" << endl;
              cout << "---------------------------" << endl;
              FunctionOperation fo;
              drddMap_t::iterator it;
              drddMap_t map = fo.getDrddMap();
              int numRows = 1;
              DoubleElementType el, el2, el3;
              for(it = map.begin(); it!= map.end(); it++)
              {
                      CPPUNIT_ASSERT(fo.isDrddFunction(it->first));
                      WSDL<DoubleElementType> wsdlIn(2, numRows);
                      WSDL<DoubleElementType> wsdlIn2(2, numRows);
                      WSDL<DoubleElementType> wsdlOut(1, numRows);
                      for(int i = 0; i < numRows; i++) {
                              el.first = i;
                              el.second = (0.5);
                              wsdlIn.insert(el);
                              wsdlIn2.insert(el);
                      }
                      wsdlIn.endOfInput();
                      wsdlIn2.endOfInput();
                      fo.executeDrddFunction(it->first, wsdlIn, wsdlIn2, wsdlOut);
                      wsdlOut.endOfInput();
                      int id = wsdlIn.getIterator();
                      int id2 = wsdlIn2.getIterator();
                      int id3 = wsdlOut.getIterator();
                      for(int i = 0; i < numRows; i++) {
                              wsdlIn.next(id, &el);
                              wsdlIn2.next(id2, &el2);
                              wsdlOut.next(id3, &el3);
                              cout << it->first << "(" << el.second << ", " << el2.second << ")=" <<
     el3.second << endl;
                      }
              }

      }

      void testSrssFunctions()
      {
              cout << endl;
              cout << "string f(string, string) functions" << endl;
              cout << "---------------------------" << endl;
              FunctionOperation fo;
              srssMap_t::iterator it;
              srssMap_t map = fo.getSrssMap();
              int numRows = 1;
              StringElementType el, el2, el3;
              for(it = map.begin(); it!= map.end(); it++)
              {
                      CPPUNIT_ASSERT(fo.isSrssFunction(it->first));
                      WSDL<StringElementType> wsdlIn(2, numRows);
                      WSDL<StringElementType> wsdlIn2(2, numRows);
                      WSDL<StringElementType> wsdlOut(1, numRows);
                      for(int i = 0; i < numRows; i++) {
                              el.first = i;
                              el.second = "abc";
                              wsdlIn.insert(el);
                              wsdlIn2.insert(el);
                      }
                      wsdlIn.endOfInput();
                      wsdlIn2.endOfInput();
                      fo.executeSrssFunction(it->first, wsdlIn, wsdlIn2, wsdlOut);
                      wsdlOut.endOfInput();
                      int id = wsdlIn.getIterator();
                      int id2 = wsdlIn2.getIterator();
                      int id3 = wsdlOut.getIterator();
                      for(int i = 0; i < numRows; i++) {
                              wsdlIn.next(id, &el);
                              wsdlIn2.next(id2, &el2);
                              wsdlOut.next(id3, &el3);
                              cout << it->first << "(" << el.second << ", " << el2.second << ")=" <<
     el3.second << endl;
                      }
              }

      }

     void testSrsFunctions()
      {
          cout << endl;
          cout << "string f(string) functions" << endl;
          cout << "---------------------------" << endl;
          FunctionOperation fo;
          srsMap_t::iterator it;
          srsMap_t map = fo.getSrsMap();
          int numRows = 2;
          StringElementType elIn, elOut;
          for(it = map.begin(); it!= map.end(); it++)
          {
              CPPUNIT_ASSERT(fo.isSrsFunction(it->first));
              WSDL<StringElementType> wsdlIn(2, numRows);
              WSDL<StringElementType> wsdlOut(1, numRows);
                      char abc[][10] = {"  abc  ", "second"};
              abc[0][3] = 222; // 0xDE
              for(int i = 0; i < numRows; i++) {
                  elIn.first = i;
                  elIn.second = abc[i];
                  wsdlIn.insert(elIn);
              }
              wsdlIn.endOfInput();
              fo.executeSrsFunction(it->first, wsdlIn, wsdlOut);
              wsdlOut.endOfInput();
              int idi = wsdlIn.getIterator();
              int ido = wsdlOut.getIterator();
              for(int i = 0; i < numRows; i++) {
                  wsdlIn.next(idi, &elIn);
                  wsdlOut.next(ido, &elOut);
                  cout << it->first << "(\"" << elIn.second << "\")=" << "\"" << elOut.second << "\"" << endl;
              }
          }

      }
  */

 public:
  void FUNCTION_TEST()
  {
    testDrdFunctions();
    testDrddFunctions();
    testSrssFunctions();
    testSrsFunctions();
    testAddFunctions();
    testDateFunctions();
    testToNumFunctions();
  }

  // Executes an addition (+ function) between two large DataLists and displays timing results.
  // Asserts that the output DataList contains the correct results.
  void PERFORMANCE_TEST()
  {
    cout << endl << endl;
    cout << "Performance Test" << endl;
    cout << "----------------------------------------------------------------------------------------"
         << endl;
    Timer timer;
    int numRows = 1000 * 1000 * 2;
    typedef WSDL<DoubleElementType> DoubleWSDL;
    DoubleWSDL* wsdlOut = new DoubleWSDL(1, numRows, fRm);
    DoubleWSDL* wsdlIn1 = new DoubleWSDL(2, numRows, fRm);
    DoubleWSDL* wsdlIn2 = new DoubleWSDL(2, numRows, fRm);

    stringstream ss;
    ss << "Loading " << numRows << " DoubleElementTypes into a WSDL.";
    string message = ss.str();
    timer.start(message);

    DoubleElementType el;

    for (int i = 0; i < numRows; i++)
    {
      el.first = i;
      el.second = (i % 5);
      wsdlIn1->insert(el);
    }

    wsdlIn1->endOfInput();
    timer.stop(message);

    stringstream ss2;
    ss2 << "Loading " << numRows << " DoubleElementTypes into a second WSDL.";
    message = ss2.str();
    timer.start(message);

    for (int i = 0; i < numRows; i++)
    {
      el.first = i;
      el.second = (i % 5);
      wsdlIn2->insert(el);
    }

    wsdlIn2->endOfInput();
    timer.stop(message);

    message = "Building FunctionDataList and vector";
    timer.start(message);
    FunctionDataListSPtr listOut(new FunctionDataList());
    FunctionDataListSPtr listIn1(new FunctionDataList());
    FunctionDataListSPtr listIn2(new FunctionDataList());
    FDLVec parms;
    listOut->doubleDl(wsdlOut);
    listIn1->doubleDl(wsdlIn1);
    listIn2->doubleDl(wsdlIn2);
    parms.push_back(listOut);
    parms.push_back(listIn1);
    parms.push_back(listIn2);

    vector<FunctionDataList::FuncDataListType> types;
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);
    types.push_back(FunctionDataList::DOUBLE_LISTTYPE);
    timer.stop(message);

    message = "Executing addition and loading result into a third WSDL.";
    timer.start(message);
    FunctionOperation* fop = FunctionOperation::instance();
    FunctionOperation::Function_t* fp = fop->getFunctionObjPtr("+", types);
    CPPUNIT_ASSERT(fp);
    fop->executeFunction(fop, fp, parms);
    timer.stop(message);
    wsdlOut->endOfInput();

    message = "Iterating over results and doing asserts.";
    timer.start(message);
    DoubleElementType el0, el1, el2;
    int id0 = wsdlOut->getIterator();
    int id1 = wsdlIn1->getIterator();
    int id2 = wsdlIn2->getIterator();

    for (int i = 0; i < numRows; i++)
    {
      wsdlOut->next(id0, &el0);
      wsdlIn1->next(id1, &el1);
      wsdlIn2->next(id2, &el2);

      CPPUNIT_ASSERT(el0.first == el1.first);
      CPPUNIT_ASSERT(el0.second == (el1.second + el2.second));
    }

    timer.stop(message);
  }
  /*
      void PERFORMANCE_TEST()
      {
              cout << endl << endl;
              cout << "Performance Test" << endl;
              cout <<
     "----------------------------------------------------------------------------------------" << endl; Timer
     timer; int id; int numRows = 1000 * 1000 * 2; DoubleElementType el, el2, el3; WSDL<DoubleElementType>
     wsdlIn(2, numRows); WSDL<DoubleElementType> wsdlIn2(2, numRows); WSDL<DoubleElementType> wsdlOut(1,
     numRows);

              stringstream ss;
              ss << "Loading " << numRows << " DoubleElementTypes into a WSDL.";
              string message = ss.str();
              timer.start(message);

              for(int i = 0; i < numRows; i++) {
                      el.first = i;
                      el.second = (i%5);
                      wsdlIn.insert(el);
              }
              wsdlIn.endOfInput();
              timer.stop(message);

              stringstream ss2;
              ss2 << "Loading " << numRows << " DoubleElementTypes into a second WSDL.";
              message = ss2.str();
              timer.start(message);

              for(int i = 0; i < numRows; i++) {
                      el.first = i;
                      el.second = (i%5);
                      wsdlIn2.insert(el);
              }
              wsdlIn2.endOfInput();
              timer.stop(message);

              message = "Executing addition and loading result into a third WSDL.";
              timer.start(message);
              FunctionOperation fo;
              fo.executeDrddFunction("+", wsdlIn, wsdlIn2, wsdlOut);
              timer.stop(message);
              wsdlOut.endOfInput();

              message = "Iterating over results and doing asserts.";
              timer.start(message);
              id = wsdlIn.getIterator();
              int id2 = wsdlIn2.getIterator();
              int id3 = wsdlOut.getIterator();
              for(int i = 0; i < numRows; i++) {
                      wsdlIn.next(id, &el);
                      wsdlIn2.next(id2, &el2);
                      wsdlOut.next(id3, &el3);

                      CPPUNIT_ASSERT(el.first == el3.first);
                      CPPUNIT_ASSERT((el.second + el2.second) == el3.second);

              }
              timer.stop(message);
      }
  */
};

CPPUNIT_TEST_SUITE_REGISTRATION(functionDriver);

int main(int argc, char** argv)
{
  CppUnit::TextUi::TestRunner runner;
  CppUnit::TestFactoryRegistry& registry = CppUnit::TestFactoryRegistry::getRegistry();
  runner.addTest(registry.makeTest());
  bool wasSuccessful = runner.run("", false);
  return (wasSuccessful ? 0 : 1);
}
