#include <fxp.h>
#include <artd_acu.h>
#include <math.h>
#include <stdlib.h>
//#include <iostream.h>

//added for divide
#define DvLen 15
#define DdLen 31
#define QLen 16
#define HiDdMin 16
typedef Int<DvLen+1> DV;
typedef Int<DdLen+1> DD;
typedef Int<(DdLen - HiDdMin) + 1> DM;
typedef Int<QLen> Q;
typedef Uint<5> U5;

//enum States {WAIT_INIT, DIVIDE, OUTPUT, WAIT_NOTGO};

//void segment(Int<8> , Int<9> [] );
void acClip(Int<2> , Int<9> );
Int<9>& acClipZero(Int<2> );
Int<24>& autocorr(const Int<9>, const Int<9> , int );
Int<8>& clip (Fix<8,3> );
void durbin(Int<24> , Fix<16,14> , Int<24> &);
void findmax(Fix<8,3> , int &, Fix<8,3>& );
void findmax1(Int<9> , int &, Int<9>& );
void fir (Fix<8,7> , Int<9> , Fix<8,3> );
void lpcalgo(const Int<9>, const Int<9>, Int<24> , Fix<16,14>, Int<24>&);
//void silence(Int<9> , Int<24> &, int&);
void divide(const DD ,const DV, Q &);

const Fix<8,7> b[24] = {0.00640759401170,  0.01984381523851,  0.03340912325104,  0.03814450893268, 
                        0.02110739335607, -0.01432194494667, -0.04996842537222, -0.05398868561241, 
                       -0.00461817662976,  0.09424799942780,  0.20602916485687,  0.28023108146123,
                        0.28023108146123,  0.20602916485687,  0.09424799942780, -0.00461817662976,
                       -0.05398868561241, -0.04996842537222, -0.01432194494667,  0.02110739335607,
                        0.03814450893268,  0.03340912325104,  0.01984381523851,  0.00640759401170 };
                        
const Fix<8,7> window[240] = {0.08, 
0.080159, 
0.0806357, 
0.0814299, 
0.082541, 
0.0839683, 
0.0857107, 
0.0877671, 
0.0901361, 
0.0928159, 
0.0958048, 
0.0991006, 
0.102701, 
0.106604, 
0.110806, 
0.115305, 
0.120098, 
0.12518, 
0.130549, 
0.136202, 
0.142133, 
0.148339, 
0.154816, 
0.161559, 
0.168564, 
0.175826, 
0.183339, 
0.191098, 
0.199099, 
0.207335, 
0.215801, 
0.224492, 
0.2334, 
0.24252, 
0.251846, 
0.261371, 
0.271088, 
0.280991, 
0.291074, 
0.301328, 
0.311747, 
0.322324, 
0.333052, 
0.343922, 
0.354928, 
0.366062, 
0.377316, 
0.388683, 
0.400154, 
0.411722, 
0.423378, 
0.435115, 
0.446925, 
0.458798, 
0.470728, 
0.482706, 
0.494724, 
0.506772, 
0.518844, 
0.53093, 
0.543023, 
0.555113, 
0.567193, 
0.579254, 
0.591288, 
0.603287, 
0.615242, 
0.627145, 
0.638988, 
0.650762, 
0.66246, 
0.674073, 
0.685593, 
0.697013, 
0.708324, 
0.719519, 
0.73059, 
0.741529, 
0.752329, 
0.762982, 
0.773481, 
0.783819, 
0.793988, 
0.803982, 
0.813793, 
0.823415, 
0.832841, 
0.842065, 
0.85108, 
0.85988, 
0.868459, 
0.876811, 
0.88493, 
0.892811, 
0.900448, 
0.907836, 
0.91497, 
0.921844, 
0.928455, 
0.934797, 
0.940867, 
0.946659, 
0.95217, 
0.957397, 
0.962335, 
0.966981, 
0.971332, 
0.975385, 
0.979137, 
0.982585, 
0.985728, 
0.988562, 
0.991087, 
0.9933, 
0.9952, 
0.996785, 
0.998054, 
0.999007, 
0.999642, 
0.99996, 
0.99996, 
0.999642, 
0.999007, 
0.998054, 
0.996785, 
0.9952, 
0.9933, 
0.991088, 
0.988563, 
0.985728, 
0.982586, 
0.979137, 
0.975385, 
0.971333, 
0.966982, 
0.962336, 
0.957398, 
0.952171, 
0.94666, 
0.940868, 
0.934799, 
0.928456, 
0.921846, 
0.914971, 
0.907838, 
0.90045, 
0.892813, 
0.884932, 
0.876813, 
0.868461, 
0.859882, 
0.851082, 
0.842067, 
0.832843, 
0.823417, 
0.813795, 
0.803984, 
0.79399, 
0.783821, 
0.773484, 
0.762984, 
0.752331, 
0.741531, 
0.730592, 
0.719521, 
0.708326, 
0.697015, 
0.685595, 
0.674075, 
0.662462, 
0.650764, 
0.63899, 
0.627147, 
0.615244, 
0.603289, 
0.591291, 
0.579257, 
0.567196, 
0.555116, 
0.543025, 
0.530933, 
0.518846, 
0.506775, 
0.494726, 
0.482709, 
0.470731, 
0.458801, 
0.446927, 
0.435117, 
0.42338, 
0.411724, 
0.400156, 
0.388685, 
0.377319, 
0.366064, 
0.354931, 
0.343925, 
0.333054, 
0.322327, 
0.311749, 
0.30133, 
0.291076, 
0.280993, 
0.27109, 
0.261373, 
0.251848, 
0.242522, 
0.233402, 
0.224493, 
0.215803, 
0.207337, 
0.1991, 
0.1911, 
0.18334, 
0.175827, 
0.168566, 
0.161561, 
0.154818, 
0.148341, 
0.142134, 
0.136203, 
0.130551, 
0.125181, 
0.120099, 
0.115306, 
0.110807, 
0.106605, 
0.102702, 
0.0991013, 
0.0958054, 
0.0928165, 
0.0901366, 
0.0877676, 
0.0857111, 
0.0839686, 
0.0825413, 
0.0814301, 
0.0806358, 
0.080159, 
0.08};
                        

int main(const Int<8> IN[240], Int<8> OUT)
{
	#pragma OUT OUT
	
        Uint<4> p = 10;
        Int<24> G2;
        Fix<16,14> alpha[10];
        Fix<8,3> seg_lp;
        Int<8> pitch;
        Int<9> seg[240];
        Int<9> seg1[240];
        Int<24> R0;
        int S;
        int j;



//        segment(IN, seg);
//*****************************************************************************
//void segment(Int<8> signal[240], Int<9> w[240])
//{
//        #ifdef __SYNTHESIS__
//        #pragma OUT w
//        #endif
        
        Uint<8> sp_size = 160;
        Uint<9> win_size = 240;
        Uint<9> win_size1 = 239;
//        Int<12> tmp4;
//        Fix<10,6> tmp5;
//        Fix<8,7>  tmp6;
//        Fix<8,7>  tmp7;
        Int<8>  tmp3;
        Int<9>  tmp2;
        Int<8> state;
        int k;

//        for (n=0; n<239; n++)
//      {
//                tmp4 = 2*3.14159*n;
//                tmp5 = tmp4/win_size1;
//              divide(tmp4, (win_size1), tmp5, 1, done);
//                tmp6 = cos(tmp5);
//                tmp7 = 0.46 * tmp6;
//                window[n] = 0.54 - tmp7;
//        }

        state = 0;

        for (k=0; k<win_size; k++)
        {
                tmp3 = 0.98 * state;
                tmp2 = IN[k] - tmp3;
                seg[k] = window[k] * tmp2;
                seg1[k] = window[k] * tmp2;
                state = IN[k];
        }

//        return;
//}
//*****************************************************************************



//        silence(seg, R0, S);
//*****************************************************************************
//void silence(Int<9> seg[240], Int<24>& R0, int& S)
//{
//        #ifdef __SYNTHESIS__
//        #pragma OUT R0 S
//        #endif
        
        
//        int S;
        Int<24> back_ground = 1000;

        R0 = Int<24> (autocorr(seg, seg1, 0));

        if (R0 <= back_ground)
                S = 1;
        else  S = 0;

//        return ;
//}
//*****************************************************************************

        if (S == 1)
        {
           G2 = 0;
           for (j=0; j<p; j++)
              alpha[j] = 0;
           pitch = 0;
        }
        else
        {
           lpcalgo(seg, seg1, R0, alpha, G2);
//           pitch = 0;
           fir(b, seg, seg_lp);
           pitch = clip(seg_lp);
        }


        OUT = pitch;
        return 0;
}

void acClip(Int<2> x[240], Int<9> r[240])
{
        #ifdef __SYNTHESIS__
        #pragma OUT r
        #endif

  Int<8> N = 240;
  Uint<5> min = 22;
  Uint<7> max = 100;
  int i, k;


  for (i=0; i<N; i++)
    r[i]=0;
  for (k=(min-1); k<max; k++)
    {
      for (i=0; i<N-k; i++)
        {
          if ( (x[i] == 0) || (x[i+k] == 0))
            r[k] = r[k];
          else
            if (x[i] == x[i+k])
              r[k]++;
            else
              r[k]--;
        } //for (i)
    } //for (k)
  return;
}

Int<9>& acClipZero(Int<2> x[240])
{

        #ifdef __SYNTHESIS__
        #pragma OUT r
        #endif

  Uint<8> N = 240;
  Int<9> r = 0;
  int i;

  for (i=0; i<N; i++)
    {
      if (x[i]==0)
        r=r;
      else
        r++;
    }
  return r;
}

Int<24> autocorr(const Int<9> X[240], const Int<9> X1[240], int k)
{
        #ifdef __SYNTHESIS__
        #pragma OUT R
        #endif

        int m;
        Int<24> R;
        Int<24> tmp1;
        Int<9> tmp2;
        Int<9> tmp3;
        Int<24> tmp4;

        loop_auto: for (m=0; m<(240-k); m++)
        {
                auto_acu: tmp2 = X[m];
                auto_acu2: tmp3=X1[m+k];
                auot_acu3: tmp1 = tmp2 * tmp3;
                //tmp1 = X[m] * X1[m+k];
                tmp4 = R;
                R = tmp4 + tmp1;
                
                //R = R + (X[m] * X1[m+k]);
        }

        return R;
}

Int<8>& clip (Fix<8,3> frame[240])
{
        #ifdef __SYNTHESIS__
        #pragma OUT F0
        #endif


  Uint<7> min_f = 80;
  Uint<9> max_f = 350;
  Uint<13> Fs = 8000;
  int i, n;
  Fix<8,3> CL;
  Uint<8> N = 240;
  Int<2> f_clipped[240];
  Int<9> r[240];
  Int<9> z;
  Int<9> m;
  Int<8> F0;
  int dummy;


  //divide(Fs, max_f, min_l, go, done);
  //divide(Fs, min_f, max_l, go, done);
  //min_l = Fs/Max_f;
  //max_l = Fs/min_f;

  findmax(frame, dummy, CL);
  CL = Fix<8,3> (CL * 0.3);

  for (i=0; i<240; i++)
    f_clipped[i]=0;
  for (n=0; n<240; n++)
    {
      if (frame[n] > CL)
        f_clipped[i]=1;
      else
        if (frame[n] < -CL)
          f_clipped[n] = -1;
        else
          f_clipped[n]=0;
    }

  acClip(f_clipped, r);
  z = acClipZero(f_clipped);

  findmax1(r, i, m);

  if (m >= (z * 0.3))
    F0=i; // **** BE CAREFUL: THIS IS THE INDEX NUMBER IN C ARRAY
          // **** WHICH STARTS FROM 0 AND ENDS AT 239
  else
    F0=0;

  return F0;
}

void durbin(Int<24> R[11], Fix<16,14> alpha[10], Int<24> &E)
{

        #ifdef __SYNTHESIS__
        #pragma OUT alpha E
        #endif


  int i, j;
  Fix<16,14> k;
  Fix<16,14> alpha_old[10];
  Int<24> tmp1;
  Int<24> tmp4;
  Fix<16,14> tmp3;
  Fix<16,14> tmp2;
  //bool go=1;
  //bool done; 
  Int<38> tmp5;
  Int<16> tmp6;

  E=R[0];

  
  for(i=0; i<10; i++)
    {
      tmp1=R[i+1];
      if (i>=1)
      {
      for(j=0; j<i-1; j++)
        {
          tmp4=Int<24> (alpha[j]*R[i-j+1]);
          tmp1=Int<24> (tmp1-tmp4);
        }
      }
      //divide(tmp1, E, k, go, done);
      //Int<38> tmp5 = tmp1 << 15;
      //Fix<16,14> k= tmp1/E;
      //Int<16> tmp6 = tmp5/E;
      //Fix<16,14> k = tmp6 >> 15; 
      
      tmp5 = tmp1 << 15;
      divide(tmp5, E, tmp6);
      k = tmp6 >> 15;
      
      
      
      for (j=0; j<10; j++)
        alpha_old[j] = alpha[j];

      alpha[i]=k;

      if (i>=1)
      {

      for (j=0; j<i-1; j++)
        {
          tmp3=Fix<16,4> (k * alpha_old[i-j]);
          alpha[j]=Fix<16,4> (alpha_old[j]-tmp3);
        }
      }
      tmp2=Fix<16,14> (k*k);
      E=Int<24> ((1-tmp2)*E);
    }
  return;
}

void findmax(Fix<8,3> seg[240], int &pos, Fix<8,3>& max)
{

        #ifdef __SYNTHESIS__
        #pragma OUT max pos
        #endif

  pos=0;
  //Fix<8,3> max;
  int i;

  max=seg[0];
  for (i=0; i<240; i++)
    {
      if (seg[i]>max)
        {
          pos=i;
          max=seg[i];
        }
    }
  return;
}



findmax1(Int<9> seg[240], int &pos, Int<9>& max)
{

        #ifdef __SYNTHESIS__
        #pragma OUT max pos
        #endif

  pos=0;
  //Int<9> max;
  int i;

  max=seg[0];
  for (i=0; i<240; i++)
    {
      if (seg[i]>max)
        {
          pos=i;
          max=seg[i];
        }
    }
  return;
}

void fir (Fix<8,7> b[24], Int<9> seg[240], Fix<8,3> out[240])
{

        #ifdef __SYNTHESIS__
        #pragma OUT out
        #endif

//  int lengthb;
  int i, j;
  Int<9> state[23];

//  lengthb=24;
  for (i=0; i<23; i++) state[i]=0;

  for (i=0; i<240; i++)
    {
     out[i] = b[0] * seg[i];
     for (j=1; j<24; j++)
       out[i]=out[i] + Fix<8,3> (b[j]*state[j-1]);
    }

  for (j=23; j>0; j--)
    state[j]=state[j-1];
  state[0]=seg[i];

  return;
}

void lpcalgo(const Int<9> seg[240], const Int<9> seg1[240], Int<24> R0, Fix<16,14> alpha[10], Int<24>& G2)
{

        #ifdef __SYNTHESIS__
        #pragma OUT G2 alpha
        #endif

        Int<24> R[11];
        //Int<24> G2;
        int i;

        R[0] = R0;
        for (i=0; i<10; i++)
                R[i+1] = autocorr(seg, seg1, i);

        durbin(R, alpha, G2);
        return;
}


/*
//#ifndef __SYNTHESIS__
//#include <iostream.h>
//#endif

#define DvLen 15
#define DdLen 31
#define QLen 16
#define HiDdMin 16

typedef Int<DvLen+1> DV;
typedef Int<DdLen+1> DD;
typedef Int<(DdLen - HiDdMin) + 1> DM;
typedef Int<QLen> Q;
typedef Uint<5> U5;


void divide(const DD ddInput,
            const DV dvInput,
            Q &Quotient)
            //const bool go,
            //bool &done)
{
#pragma OUT Quotient
#pragma OUT done

	static bool negDivisor;
	static bool negDivident;
	static States state = WAIT_INIT;
	static DV divisor;
	static DD divident; 
	static Q quotient;
	static U5 iterator = 0;
	DM tmp;

	switch (state){
	case WAIT_INIT:
		{
			//if(go){
				divident = ddInput;
				divisor = dvInput;
				quotient = Q(0);
				negDivisor = divisor.bit(DvLen);
				negDivident = divident.bit(DdLen);
				if(negDivisor) {divisor = -divisor;}
				if(negDivident) {divident = -divident;}
				// Go to the next state
				state = DIVIDE;
				Quotient = "dontcare";
				//done = 0;
			//}
		}
	   break;
	case DIVIDE:
		{
			quotient = quotient << Q(1);
			//cout << iterator.dec() << " " << quotient.bpBin() << " ";
			divident = divident << DV(1);
			tmp = divident.slice(DdLen, HiDdMin);
			tmp = tmp - divisor;
			divident.slice(DdLen, HiDdMin) = tmp;
			if (!divident.bit(DdLen)) {
				quotient = quotient + 1;
			} else {
				tmp = divident.slice(DdLen, HiDdMin);
				tmp = tmp + divisor;
				divident.slice(DdLen, HiDdMin) = tmp;
			}

			if(iterator == DvLen)	{
				// Go to the next state
				state = OUTPUT;
			} else { ++iterator; } // Remain in current state
			Quotient = "dontcare";
			done = 0;

		}
		break;
	case OUTPUT:
		{
			if (negDivisor != negDivident) {
					quotient = -quotient;
			}
			Quotient = quotient; // Output Value
			state = WAIT_NOTGO;
			done = 1;
			iterator = 0;
		}
		break; 
	case WAIT_NOTGO:
		{
			if (!go) { state = WAIT_INIT;}
			done = 0;
			Quotient = "dontcare";
		}
		break;
	}
}


#ifndef __SYNTHESIS__
#include <fstream.h>
void main ()
{
	DD dd = DD(10000);
	DV dv = DV(500);
	Q q;
	ofstream streamdd ("ddInput.INP");
	ofstream streamdv ("dvInput.INP");
	ofstream streamq ("Quotient.REF");
	ofstream streamgo ("go.INP");
	ofstream streamdone ("done.REF");
	
	bool go = 1, done;
	// Read divident and divisor data
	divide (dd, dv, q, go, done);
	cout<<dd.dec()<<" "<<dv.dec()<<" "<<q.dec()<<" "<<go<<" "<<done<<"\n";
	streamdd << dd.bpBin() << "\n";
	streamdv << dv.bpBin() << "\n";
	streamq << q.bpBin() << "\n";
	streamgo << go << "\n";
	streamdone << done << "\n";
	go = 0;
	// Iterate to calculate output of division
	for (int i = 0; i < DvLen + 3; ++i) {
		divide (dd, dv, q, go, done);
		cout<<dd.dec()<<" "<<dv.dec()<<" "<<q.dec()<<" "<<go<<" "<<done<<"\n";
	   streamdd << dd.bpBin() << "\n";
	   streamdv << dv.bpBin() << "\n";
	   streamq << q.bpBin() << "\n";
	   streamgo << go << "\n";
	   streamdone << done << "\n";
	}
}
#endif		
*/



void divide(const DD ddInput,
			const DV dvInput,
			Q &Quotient)
			//const bool go,
			//bool &done)
{
#pragma OUT Quotient
//#pragma OUT done

	static bool negDivisor;
	static bool negDivident;
//	static States state = WAIT_INIT;
	static DV divisor;
	static DD divident; 
	static Q quotient;
	static U5 iterator = 0;
	DM tmp;

//	switch (state){
//	case WAIT_INIT:
//		{
//			if(go){
				divident = ddInput;
				divisor = dvInput;
				quotient = Q(0);
				negDivisor = divisor.bit(DvLen);
				negDivident = divident.bit(DdLen);
				if(negDivisor) {divisor = -divisor;}
				if(negDivident) {divident = -divident;}
				// Go to the next state
//				state = DIVIDE;
//				Quotient = "dontcare";
//				done = 0;
//			}
//		}
//		break;
//	case DIVIDE:
//		{
			quotient = quotient << Q(1);
			//cout << iterator.dec() << " " << quotient.bpBin() << " ";
			divident = divident << DV(1);
			tmp = divident.slice(DdLen, HiDdMin);
			tmp = tmp - divisor;
			divident.slice(DdLen, HiDdMin) = tmp;
			if (!divident.bit(DdLen)) {
				quotient = quotient + 1;
			} else {
				tmp = divident.slice(DdLen, HiDdMin);
				tmp = tmp + divisor;
				divident.slice(DdLen, HiDdMin) = tmp;
			}

//			if(iterator == DvLen)	{
//				// Go to the next state
//				state = OUTPUT;
//			} else { ++iterator; } // Remain in current state
//			Quotient = "dontcare";
//			done = 0;
//
//		}
//		break;
//	case OUTPUT:
//		{
			if (negDivisor != negDivident) {
					quotient = -quotient;
			}
			Quotient = quotient; // Output Value
			//state = WAIT_NOTGO;
			//done = 1;
			iterator = 0;
//		}
//		break; 
//	case WAIT_NOTGO:
//		{
//			if (!go) { state = WAIT_INIT;}
//			done = 0;
//			Quotient = "dontcare";
//		}
//		break;
//	}
}




