//------------------------------------------------------------------------------
//    module util.cpp  version 2.0b                                           //
//                                                                            //
//    general utilities                                                       //
//                                                                            //
//    copyright (c) 1998-2003 by Lars Haendel                                 //
//    home: www.newty.de                                                      //
//                                                                            //
//    This program is free software and can be used under the terms of the    //
//    GNU licence. See header-file for further information and disclaimer.    //
//                                                                            //
//------------------------------------------------------------------------------


#include <limits>       // due to   INT_MAX
#include <math>         //          sqrt()
#include <stdio>        //          sprintf()
#include <values>       //          MAXFLOAT

#include "util.h"
#include "defines.h"


//----------------------------------------------------------------------------------------------------------------------
// write time stamp to static variable
const char* TimeStamp()
{
   static char szText[256];

   time_t _tstamp = time(NULL);
   tm* tstamp = localtime(&_tstamp);
   sprintf(szText, "%.2d.%.2d.%d %.2d:%.2d", tstamp->tm_mday, tstamp->tm_mon+1, tstamp->tm_year+1900
            , tstamp->tm_hour, tstamp->tm_min);

   return szText;
}


//----------------------------------------------------------------------------------------------------------------------
// write given value to 'text' using the specified precision counted from the most signifcant digit. Reduce precision
// if diplayed digits at the end would be "0". Example with _prec=1: 0.002545 -> "0.0025", 0.00203 -> "0.002"
void ValueToText(const float value, char* text, const int width /*=0*/, const int _prec /*=0*/)
{
   int prec=_prec;                           // ini

   if(value == MAXFLOAT)
      sprintf(text, "%*s", width, "oo");     // 1. 'oo' for infinity
   else
   {
      if(value==0)                           // 2. set precision to 0 for zero
         prec=0;

      // 3. all other cases ...
      else
      {
         // a) add precision depending on most significant digit
         if(value < (float) 0.01)
            prec+=3;
         else
            if(value < (float) 0.1)
               prec+=2;
            else
               if(value < (float) 1.0)
                  prec+=1;


         // b) reduce precision if displayed values would be "0"
         for(int i=0;i<_prec;i++)
         {
            int fac1 = pow(10, prec-1);                      // pre-calculate to save some time
            int fac2 = fac1*10;

            float r2 = floor(0.5+value*fac2)/fac2;           // round to actual precision and ...
            float r1 = floor(0.5+value*fac1)/fac1;           // ... to actual precision minus one
            if(r1==r2)  // if next                           // reduce precision by one if rounded values are equal
               prec--;
            else
               break;
         }
      }

      // c) write text using sprintf()
      sprintf(text, "%*.*f", width, prec, value);
   }
}


//----------------------------------------------------------------------------------------------------------------------
// write given value static variable 1 and return pointer to it
const char* ValueToText1(const float value, const int width /*=0*/, const int prec /*=0*/
                           , const bool& f_SpecialMinusOne/*=false*/)
{
   static char szText[STS];
   if(f_SpecialMinusOne && value==-1)     // if enabled: treat value of '-1' as unknown
      strcpy(szText, "NA");
   else
      ValueToText(value, szText, width, prec);
   return szText;
}


//----------------------------------------------------------------------------------------------------------------------
// write given value to static variable 2 and return pointer to it
const char* ValueToText2(const float value, const int width /*=0*/, const int prec /*=0*/
                           , const bool& f_SpecialMinusOne)
{
   static char szText[STS];
   if(f_SpecialMinusOne && value==-1)     // if enabled: treat value of '-1' as unknown
      strcpy(szText, "NA");
   else
      ValueToText(value, szText, width, prec);
   return szText;
}


//----------------------------------------------------------------------------------------------------------------------
// write given value (using a fixed floating point precision) to static variable and return pointer to it
const char* ValueToTextFP(const float value, const int width /*=0*/, const int prec /*=0*/)
{
   static char szText[STS];
   sprintf(szText, "%*.*f", width, prec, value);
   return szText;
}


//----------------------------------------------------------------------------------------------------------------------
// write value between 0 and 1 like 0.57 as string '57.1%'
const char* WritePercentage(const float& value, const int& width/*=0*/, const int& prec/*=1*/
                              , const bool& f_PercentChar/*=false*/)
{
   static char szText[STS];
   if(value<0 || value>1)
      strcpy(szText, "NA");
   else
      if(f_PercentChar)
         sprintf(szText, "%*.*f %%", width, prec, 100*value);
      else
         sprintf(szText, "%*.*f", width, prec, 100*value);
   return szText;

}


//----------------------------------------------------------------------------------------------------------------------
// write time formatted to static variable and return pointer to it
const char* WriteTime(const clock_t& time, const bool& f_Justified)
{
   static char szText[STS];

   float t=time/((float) CLK_TCK);  // time in seconds

   if(t<0.001)     sprintf(szText, "%*s"      , 8*f_Justified, "--");                        // less than a milli second
   else if(t<1)    sprintf(szText, "%*.0fms"  , 6*f_Justified, t*1000.0);                    // less than a second
   else if(t<60)   sprintf(szText, "%*.1fs"   , 7*f_Justified, t);                           // less than a minute
   else if(t<3600) sprintf(szText, "%*dm%02ds", 4*f_Justified, ((int) t/60), div(t, 60).rem);// less than an hour
   else            sprintf(szText, "%*dh%02dm", 4*f_Justified, ((int) t/3600), div(t, 3600).rem/60);

   return szText;
}


//----------------------------------------------------------------------------------------------------------------------
// write time as hours, minutes and seconds (but no milli seconds) to static text
const char* WriteTime2(const clock_t& time)
{
   static char szText[STS];

   int t=time/((float) CLK_TCK); // time in seconds

   if(t<3600)  sprintf(szText, "%dm%02ds", t/60,   div(t, 60).rem);                             // less than an hour
   else        sprintf(szText, "%dh%02dm%02ds", t/3600, div(t, 3600).rem/60, div(t, 60).rem);   // more than an hour

   return szText;
}



   //-------------------------------------------------------------------------------------------------------------------
   //    sort/randomization functions
   //


//----------------------------------------------------------------------------------------------------------------------
// constructor
TSorter::TSorter(const int& _size)
{
   size=_size;
   vec=new TIdVec[size];
   for(int i=0;i<size;i++)
   {
      vec[i].id=i;
      vec[i].a = vec[i].b = 0;   // reset
   }
}


   //-------------------------------------------------------------------------------------------------------------------
   // comparison function - sorts in ascending(!) order
   int __cdecl IdVecCmpAsc(const void* _a, const void* _b)
   {
      // explicitly cast
      const TIdVec* c = (const TIdVec*) _a;
      const TIdVec* d = (const TIdVec*) _b;

      // compare
      if(c->a < d->a)
         return -1;                                      // c<d regarding criterion a
      else
         if(c->a == d->a)                                // c=d _-> decide by criterion b
            if(c->b < d->b)
               return -1;                                // c<d regarding criterion b
            else
               if(c->b == d->b)                          // equality -> decide by id
                  if(c->id < d->id)
                     return 1;
                  else
                     return -1;
               else
                  return 1;                              // c>d regarding criterion b
         else
            return 1;                                    // c>d regarding criterion a
   }


   //-------------------------------------------------------------------------------------------------------------------
   // comparison function - sorts in descending(!) order
   int __cdecl IdVecCmpDes(const void* _a, const void* _b)
   {
      // explicitly cast
      const TIdVec* c = (const TIdVec*) _a;
      const TIdVec* d = (const TIdVec*) _b;

      // compare
      if(c->a < d->a)
         return 1;                                       // c<d regarding criterion a
      else
         if(c->a == d->a)                                // c=d _-> decide by criterion b
            if(c->b < d->b)
               return 1;                                 // c<d regarding criterion b
            else
               if(c->b == d->b)                          // equality -> decide by id
                  if(c->id < d->id)
                     return 1;
                  else
                     return -1;
               else
                  return -1;                             // c>d regarding criterion b
         else
            return -1;                                   // c>d regarding criterion a
   }


   //-------------------------------------------------------------------------------------------------------------------
   // comparison function
   int __cdecl FloatCmpDes(const void* _a, const void* _b)
   {
      // explicitly cast
      const float* a = (const float*) _a;
      const float* b = (const float*) _b;

      // compare
      if(*a<*b)
         return 1;
      else
         return -1;
   }


   //-------------------------------------------------------------------------------------------------------------------
   // comparison function (ascending order)
   int __cdecl FloatCmpAsc(const void* _a, const void* _b)
   {
      // explicitly cast
      const float* a = (const float*) _a;
      const float* b = (const float*) _b;

      // compare
      if(*a<*b)
         return -1;
      else
         return 1;
   }



//----------------------------------------------------------------------------------------------------------------------
// return integer field with random order of the values 0..N-1
int* /*cr*/ RandomId(const int& N, const unsigned int& seed/*=1*/)
{
   // a) initialize random number generator to given seed or to random state
   if(seed==1)
      ::randomize();
   else
      ::srand(seed);


   // b) create vector with id and random values
   TIdVec* vec = new TIdVec[N];
   for(int i=0;i<N;i++)
   {
      vec[i].id = i;
      vec[i].a  = vec[i].b = random(INT_MAX);
   }


   // c) sort this vector regarding the random values
   qsort((void*) vec, N, sizeof(vec[0]), IdVecCmpDes);


   // d) create and initialize vector with random id's
   int* id=new int[N];
   for(int i=0;i<N;i++)
      id[i]=vec[i].id;


   // e) release
   delete[] vec;

   return id;
}


//----------------------------------------------------------------------------------------------------------------------
// return entries of <_vec> in randomized order in a new vector, delete old one
int* /*cr*/ Randomize(int*& /*ce*/ _vec, const int& nId, const unsigned int& seed/*=1*/)
{
   // a) initialize random number generator to given seed or to random state
   if(seed==1)
      ::randomize();
   else
      ::srand(seed);

   // b) create vector with id's and random values
   TIdVec* vec = new TIdVec[nId];
   for(int i=0;i<nId;i++)
   {
      vec[i].id=_vec[i];
      vec[i].a = vec[i].b = random(INT_MAX);
   }
   delete[] _vec;


   // c) sort this vector regarding the random values
   qsort((void*) vec, nId, sizeof(vec[0]), IdVecCmpDes);


   // d) create and initialize vector with random id's
   int* id=new int[nId];
   for(int i=0;i<nId;i++)
      id[i]=vec[i].id;


   // e) release
   delete[] vec;

   return id;
}



   //-------------------------------------------------------------------------------------------------------------------
   //
   //    quantization functions
   //


//----------------------------------------------------------------------------------------------------------------------
// calculate interval width
const float CalcIntWidth(const float& max, const float& min, const float& N, const bool& f_Integer/*=false*/)
{
   if(f_Integer)
      return (max-min)/(N-1);
   else
      return (max-min)/N;
}


//----------------------------------------------------------------------------------------------------------------------
// quantize given value 'x' into one of N equally spaced intervals
int Quant(const float& x, const float& max, const float& min, const float& N, const bool& f_Integer/*=false*/)
{
   int id;                                                                          // interval id e[0..N-1]

   // return 0 if maximum equals minimu
   if(max==min)
      id = 0;
   else
   {
      const float w = CalcIntWidth(max, min, N, f_Integer);                         // interval width


      if(f_Integer)
         id = (int) ( (x-min+w/2) / (max-min+w) * N);
      else
      {
         id = (int) ((x-min)/w);                                                    // calculate interval id

         if(id>=N)   id=N-1;                                                        // check
         if(id <0)   id=0;
      }
   }

   return id;
}


//----------------------------------------------------------------------------------------------------------------------
// dequantize: return median value if interval <id>
const float DeQuant(const int& id, const float& max, const float& min, const float& N, const bool& f_Integer)
{
   const float w = CalcIntWidth(max, min, N, f_Integer);                            // interval width

   if(f_Integer)
      return min + id*w;
   else
      return min + w/2 + id*w;
}


//----------------------------------------------------------------------------------------------------------------------
// write bits stored in value to cout
void PrintBits(const int& value)
{
   for(int i=0;i<MaxBits;i++)
      if(IsBitSet(value, i))     cout << "1";
   else                          cout << "0";
}


//----------------------------------------------------------------------------------------------------------------------
// write bits stored in value to ofstream
void WriteBits(ofstream& file, const int& value, const int nBits/*=MaxBits*/)
{
   for(int i=0;i<min(MaxBits, nBits);i++)
      if(IsBitSet(value, i))  file << "1";
      else                    file << "0";
}