/* 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);
}


