/*
 * DctDirect.java
 * A straightforward implementation of DCT and IDCT for the purpose of 
 * learning and testing.
 * Floating-point arithmetic is used.  Such an implementation should 
 * not be used in practical applications.
 */
 
class DctDirect {
 
  private static double PI = 3.141592653589;

  //input: f, N; output: F
  static int dct_direct( int N, double [][] f, double [][] F )
  {
    double [] a = new double[32];
    double sum, coef;
    int i, j, u, v;

    if ( N > 32 || N <= 0 ) {
      System.out.printf ("\ninappropriate N\n");
      return -1;
    }
    a[0] = Math.sqrt ( 1.0 / N );
    for ( i = 1; i < N; ++i ) {
      a[i] = Math.sqrt ( 2.0 / N );
    } 
    for ( u = 0; u < N; ++u ) {
      for ( v = 0; v < N; ++v ) {
        sum = 0.0;
        for ( i = 0; i < N; ++i ) {
          for ( j = 0; j < N; ++j ) {
	    coef =  Math.cos ((2*i+1)*u*PI / 
                      (2*N)) * Math.cos ((2*j+1)*v*PI/(2*N));
	    sum += f[i][j] * coef;
	  } //for j
          F[u][v] = a[u] * a[v] * sum;
        } //for i
      } //for u
    } //for v
  
    return 1;
  }

  //input: N, F; output: f
  static int idct_direct( int N, double [][] F, double [][] f )
  {
    double [] a = new double[32];
    double  sum, coef;
    short i, j, u, v;

    if ( N > 32 || N <= 0 ) {
      System.out.printf ("\ninappropriate N\n");
      return -1;
    }
    a[0] = Math.sqrt ( 1.0 / N );
    for ( i = 1; i < N; ++i ) 
      a[i] = Math.sqrt ( 2.0 / N );
    
    for ( i = 0; i < N; ++i ) {
      for ( j = 0; j < N; ++j ) {
        sum = 0.0;
        for ( u = 0; u < N; ++u ) {
          for ( v = 0; v < N; ++v ) {
	    coef =  Math.cos ((2*j+1)*v*PI/(2*N)) * 
                                Math.cos ((2*i+1)*u*PI / (2*N));
	    sum += a[u] * a[v] * F[u][v] * coef;
	  } //for j
          f[i][j] = sum;
        } //for i
      } //for u
    } //for v
    return 1;
  }

  //  change values from int to double and vice versa.
  static int dct ( int N, int [][] f, int [][] F )
  {
    double [][] tempx = new double[32][32];
    double [][] tempy = new double[32][32];
    int  i, j;
 
    if ( N > 32 || N <= 0 ) {
      System.out.printf ("\ninappropriate N\n");
      return -1;
    }
    for ( i = 0; i < N; ++i )
      for ( j = 0; j < N; ++j ) 
        tempx[i][j] = (double) f[i][j];
  
    dct_direct ( N, tempx, tempy );       //DCT operation
    for ( i = 0; i < N; ++i ) 
      for ( j = 0; j < N; ++j )
        F[i][j] = (int ) ( Math.floor (tempy[i][j]+0.5) );  //rounding
    
    return 1; 
  }


  //  change values from int to doulbe, and vice versa.
  static int idct ( int N, int [][] F, int [][] f )
  {
    double [][] tempx = new double[32][32];
    double [][] tempy = new double[32][32];
    int  i, j;

    if ( N > 32 || N <= 0 ) {
      System.out.printf ("\ninappropriate N\n");
      return -1;
    }
    for ( i = 0; i < N; ++i ) 
      for ( j = 0; j < N; ++j )
        tempy[i][j] = (double) F[i][j];

    idct_direct ( N, tempy, tempx );  //IDCT operation
    for ( i = 0; i < N; ++i )
      for ( j = 0; j < N; ++j )
        f[i][j] = (int) Math.floor (tempx[i][j]+0.5);  //rounding

    return 1;
  }

  static void print_elements ( int N,  int [][] f )
  {
    int i, j;

    for ( i = 0; i < N; ++i ){
      System.out.printf("\n");
      for ( j = 0; j < N; ++j ) {
        System.out.printf ("%4d, ", f[i][j] );
      }
    }
  }

  public static void main(String[] args) throws InterruptedException 
  {
    int [][] f = new int[8][8], F = new int[8][8];
    int i, j, N;
    byte [][] temp = new byte[8][8];
    N = 8;

    //try some values for testing
    for ( i = 0; i < N; ++i ) {
      for ( j = 0; j < N; ++j ) {
        f[i][j] = i + j;
      }
    }
  
    System.out.printf("\nOriginal sample values");
    print_elements ( N, f );
    System.out.printf("\n--------------------\n");
  
    dct ( N, f, F );       //performing DCT
    System.out.printf("\nCoefficients of DCT:"); 
    print_elements ( N, F );
    System.out.printf("\n--------------------\n");

    idct ( N, F, f );      //performing IDCT
    System.out.printf("\nValues recovered by IDCT:");
    print_elements ( N, f );
    System.out.printf("\n");
  }
} 

