/*************************************************************************
  FILE:  BddHiscPubfunc.cpp
  DESCR: Containing some utility functions used in the program
  AUTH:  Raoguang Song
  Supervisor: Dr. Ryan Leduc
  DATE:  (C) Jan, 2006
*************************************************************************/
#include <string>
#include <iostream>
#include "BddHiscType.h"
#include <stdio.h>
#include "BddHiscPubfunc.h"
#include <cassert>
#include <ctime>
#include <cstdlib>

using namespace std;

namespace BDDHISC
{
    int giNumofBddNodes = 0;

    /**
     * DESCR:   Trim away all the prefix and suffix spaces or tabs of a string
     * PARA:    str: a string (input)
     * RETURN:  trimmed string
     * */
    string str_trim(const string &str)
    {
        string sTmp("");
        unsigned int i = 0;

        //trim off the prefix spaces
        for (i = 0; i < str.length(); i++)
        {
            if (str[i] != 32 && str[i] != 9)
                break;
        }
        if (i < str.length())
        {
            sTmp = str.substr(i);
        }
        else
        {
            return sTmp;
        }

        //trim off the suffix spaces
        int j = 0;
        for (j = sTmp.length() - 1; j >= 0; j--)
        {
            if (sTmp[j] != 32 && sTmp[j] != 9)
                break;
        }
        if (j >= 0)
        {
            sTmp = sTmp.substr(0, j + 1);
        }
        else
        {
            sTmp.clear();
        }

        return sTmp;
    }

    /**
     * DESCR:   convert all the letters in a string to uppercase
     * PARA:    str: a string (input)
     * RETURN:  converted string
     * */
    string str_upper(const string &str)
    {
        unsigned int i = 0;
        string sTmp(str);

        for (i = 0; i < str.length(); i++)
        {
            if (sTmp[i] >= 'a' && sTmp[i] <= 'z')
            {
                sTmp[i] = sTmp[i] - 32;
            }
        }
        return sTmp;
    }

    /**
     * DESCR:   convert all the letters in a string to lowercase
     * PARA:    str: a string (input)
     * RETURN:  converted string
     * */
    string str_lower(const string &str)
    {
        unsigned int i = 0;
        string sTmp(str);

        for (i = 0; i < str.length(); i++)
        {
            if (sTmp[i] >= 'A' && sTmp[i] <= 'Z')
            {
                sTmp[i] = sTmp[i] + 32;
            }
        }
        return sTmp;
    }

    /**
     * DESCR:   convert an integer to a string
     * PARA:    iInt: an integer
     * RETURN:  converted string
     * */
    string str_itos(int iInt)
    {
        char scTmp[65];
        string str;
        sprintf(scTmp, "%d", iInt);
        str = scTmp;

        return str;
    }

    /**
     * DESCR:   convert a long integer to a string
     * PARA:    iInt: a long integer
     * RETURN:  converted string
     * */
    string str_ltos(long long lLong)
    {
        char scTmp[65];
        string str;
        sprintf(scTmp, "%lld", lLong);
        str = scTmp;

        return str;
    }

    /**
     * DESCR:   trim off all the characters after a COMMENT_CHAR
     * PARA:    str : a string
     * RETURN:  processed string
     * */
    string str_nocomment(const string & str)
    {
        int i;
        int iLen = str.length();

        for (i = 0; i < iLen; i++)
        {
            if (str[i] == COMMENT_CHAR)
                break;
        }
        if (i < iLen)
            return str.substr(0, i);
        else
            return str;
    }

    /**
     * DESCR:   Get sub name or des name from a full path file name
     *          with extension ".sub"/".hsc"
     *          ex: vsFile = "/home/roger/m1.sub" will return "m1"
     * PARA:    vsFile: file name with path
     * RETURN:  sub name or des name
     * */
    string GetNameFromFile(const string & vsFile)
    {
        assert(vsFile.length() > 4);
        assert(vsFile.substr(vsFile.length() - 4) == ".sub" ||
               vsFile.substr(vsFile.length() - 4) == ".hsc");

        unsigned int iPos = vsFile.find_last_of('/');

        if ( iPos == string::npos)
        {
            return vsFile.substr(0, vsFile.length() - 4);
        }
        else
        {
            return vsFile.substr(iPos + 1, vsFile.length() - 4 - (iPos + 1));
        }
    }

    /**
     * DESCR:   Convert 'H', 'R', 'A', 'L', 'D' to corresponding EVENTSUB value
     * PARA:    cEventSub ('H', 'R', 'A', 'L', 'D')
     * RETURN:  corresponding EVENTSUB value
     *
     * Updated to support low data events --H.I.
     **/
    EVENTSUB SubLetterToValue(char cEventSub)
    {
        assert(cEventSub == 'H' || cEventSub == 'R' ||
                cEventSub == 'A' || cEventSub == 'L' ||
                cEventSub == 'D');
        switch (cEventSub)
        {
        case 'H':
            return H_EVENT;
            break;
        case 'R':
            return R_EVENT;
            break;
        case 'A':
            return A_EVENT;
            break;
        case 'L':
            return L_EVENT;
            break;
        case 'D':
            return LD_EVENT;
            break;
        }
        return H_EVENT;
    }

    /**
     * DESCR:   Convert EVENTSUB value to corresponding name
     * PARA:    vEventSub: EVENTSUB value
     * RETURN:  corresponding name
     *
     * Updated to support low data events --H.I.
     **/
    string SubValueToLetter(EVENTSUB vEventSub)
    {
        switch (vEventSub)
        {
        case H_EVENT:
            return "HIGH SUB";
            break;
        case R_EVENT:
            return "REQUEST";
            break;
        case A_EVENT:
            return "ANSWER";
            break;
        case L_EVENT:
            return "LOW SUB";
            break;
        case LD_EVENT:
            return "LOW DATA";
            break;
        }
        return "HIGH SUB";
    }

    /**
     * DESCR:   Test if a string could be converted to an integer
     * PARA:    str: a string
     * RETURN:  0: no 1: yes
     * */
    int IsInteger(const string &str)
    {
        if (str.length() == 0)
            return 0;
        for (unsigned int i = 0; i < str.length(); i++)
        {
            if (str[i] >= '0' && str[i] <= '9')
                continue;
            else
                return 0;
        }
        return 1;
    }

    /**
     * DESCR:   Compare two integers which are provided by two general pointers.
     *          qsort, bsearch will use this function
     * PARA:    pa, pb: general pointers pointing to two integers
     * RETURN:  1: a>b
     *          0: a=b
     *         -1: a<b
     * */
    int CompareInt(const void* pa, const void* pb)
    {
        int a = *((int *) pa);
        int b = *((int *) pb);

        if (a > b)
            return 1;
        else if (a < b)
            return -1;
        else
            return 0;
    }


    /**
     * DESCR:   To print the content of a bddStat variable.
     *          Original BDD package doesn't provide such a function.
     * PARA:    bddStat: see documents of Buddy package
     * RETURN:  None
     * */
    void bddPrintStats(const bddStat &stat)
    {
        cout << endl;
        cout << "--------------bddStat-----------------" << endl;

        cout << "Num of new produced nodes: " << stat.produced << endl;
        cout << "Num of allocated nodes: " << stat.nodenum << endl;
        cout << "Max num of user defined nodes: " << stat.maxnodenum << endl;
        cout << "Num of free nodes: " << stat.freenodes << endl;
        cout << "Min num of nodes after garbage collection: " << stat.minfreenodes
             << endl;
        cout << "Num of vars:" << stat.varnum << endl;
        cout << "Num of entries in the internal caches:" << stat.cachesize << endl;
        cout << "Num of garbage collections done until now:" << stat.gbcnum << endl;
        return;
    }

    /**
     * DESCR:   Set bddpairs based on two bdd variable sets.
     *          The original function bdd_setbddpair(...) is not
     *          as the document said.
     * PARA:    pPair: where to add bdd variable pairs
     *          bddOld: variable will be replaced
     *          bddNew: new variable
     * RETURN:  None
     * */
    void SetBddPairs(bddPair *pPair, const bdd & bddOld, const bdd & bddNew)
    {
        assert(pPair != NULL);

        int *vOld = NULL;
        int *vNew = NULL;
        int nOld = 0;
        int nNew = 0;

        bdd_scanset(bddOld, vOld, nOld);
        bdd_scanset(bddNew, vNew, nNew);

        assert(nOld == nNew);

        for (int i = 0; i < nOld; i++)
        {
            bdd_setpair(pPair, vOld[i], vNew[i]);
        }

        free(vOld);
        free(vNew);

        return;
    }

    /**
     * DESCR:   Compute the number of shared events between two DES
     * PARA:    pEventsArr_a:   Event array for DES a (global index, sorted)
     *          viNumofEvents_a: Number of events in array pEventsArr_a
     *          pEventsArr_b:   Event array for DES b (global index, sorted)
     *          viNumofEvents_b: Number of events in array pEventsArr_b
     * RETURN:  Number of shared events
     * */
    int NumofSharedEvents(const int * pEventsArr_a, const int viNumofEvents_a,
             const int * pEventsArr_b, const int viNumofEvents_b)
    {
        int iNum = 0;
        int i = 0;

        assert(pEventsArr_a != NULL);
        assert(pEventsArr_b != NULL);

        if (viNumofEvents_a <= viNumofEvents_b)
        {
            for (i = 0; i < viNumofEvents_a; i++)
            {
                if (bsearch(&(pEventsArr_a[i]), pEventsArr_b, viNumofEvents_b,
                            sizeof(int), CompareInt) != NULL)
                {
                    iNum++;
                }
            }
        }
        else
        {
            for (i = 0; i < viNumofEvents_b; i++)
            {
                if (bsearch(&(pEventsArr_b[i]), pEventsArr_a, viNumofEvents_a,
                            sizeof(int), CompareInt) != NULL)
                {
                    iNum++;
                }
            }
        }

        return iNum;
    }

    /**
     * DESCR:   Customized Garbage collection handler for this program
     * PARA:    see document of Buddy Package
     * RETURN:  None
     * */
    void my_bdd_gbchandler(int pre, bddGbcStat *s)
    {
       if (!pre)
       {
          if (s->nodes > giNumofBddNodes)
          {
              printf("Garbage collection #%d: %d nodes / %d free",
                 s->num, s->nodes, s->freenodes);
              printf(" / %.1fs / %.1fs total\n",
                 (float)s->time/(float)(CLOCKS_PER_SEC),
                 (float)s->sumtime/(float)CLOCKS_PER_SEC);
              giNumofBddNodes = s->nodes;
          }
       }
       return;
    }
} //end of namespace BDDHISC
