/*************************************************************************
  FILE:  BddSdPubfunc.cpp
  DESCR: Containing some utility functions used in the program
  AUTH:  Raoguang Song, Yu Wang
  DATE:  (C) Jan, 2006, 2009
*************************************************************************/
#include <string>
#include <iostream>
#include "BddSdType.h"
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include "BddSdPubfunc.h"
#include <cassert>

using namespace std;

namespace BDDSD
{

int giNumofBddNodes = 0;

int iVerbLevel = 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:   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 BDDSD

