#include "Wavelet.h"
#include "DataGrid3D.h"
#include "DataGrid2D.h"
#include "DataGrid1D.h"
#include "DataGrid.h"
#include "wt.h"
#include "Common.h"

extern bool verbose;
extern bool parallel;
extern bool master;
extern char * slave;

Wavelet::Wavelet()
{
	remote = new RemoteProcessor();
}

Wavelet::~Wavelet()
{
}

void Wavelet::inverseTransform(DataGrid * data) {
	
	switch (data->getDimension()) {
		case 1:
			this->inverseTransform1D(data);
			break;
		case 2:
			this->inverseTransform2D(data);	
			break;
		case 3:
			this->inverseTransform3D(data);	
			break;	
	}
}

void Wavelet::transform(DataGrid * data) {

	switch (data->getDimension()) {
		case 1:
			this->transform1D(data);
			break;
		case 2:
			this->transform2D(data);	
			break;
		case 3:
			this->transform3D(data);	
			break;	
	}
	
	data->stripZeros(limit);
}

void Wavelet::transform1D(DataGrid * data) {
		
	int size = data->getX();
	
	double ** arrays = new double*[this->nsteps + 1];
	int * steps = new int[this->nsteps];

	// get the input data into a double array 
	// this is wasted copying because of our dataGrid class
	double * input = new double[size];
	bzero(input,size);
	for (int k = 0; k  < size; k++) {
		input[k] = data->getData(k,0,0); 	
	}

	// for each decomposition level
	for (int step = 0; step < this->nsteps; ++step) {
			
		//int outsize = (size >> 1) + 1;
		//if (size % 2 == 1) { outsize++; }
		int outsize = (int) floor((size + this->dec_len - 1) / 2.);

		//cout << "out size x " << outsize << " size: " << size << endl;
		
		// create approx and detail vectors
		double * approx = new double[outsize];
		double * detail = new double[outsize];
		
		bzero(approx,outsize);
		bzero(detail,outsize);
			
		// get the approximation data
		if (parallel && master) {
			remote->remoteProcess(input, size, approx, detail, outsize, MODE_ZEROPAD, this->mode);
		}
		else {
			d_dec_a(input, size, this, approx, outsize, MODE_ZEROPAD);
		    d_dec_d(input, size, this, detail, outsize, MODE_ZEROPAD);
		}
		
		size = outsize;
		input = approx;
		
		// save the detail and then approx. next level the detail 
		// will overwrite this approx, which is what we want to happen
		
		arrays[step] = detail;
		arrays[step+1] = approx;
		steps[step] = outsize;
		if (verbose) cout << "Level " << step << " is size: " << outsize << endl << endl;
	}
	
	int length = 0;
	for (int k = 0; k < this->nsteps; k++) {
		length += steps[k];
		if (verbose) cout << "\t" << steps[k] << endl;
	}
	length += steps[this->nsteps-1];
	
	// save teh structure of the decomposition
	data->levels = this->nsteps;
	data->setStructure(steps);
	
	data->resize(length,0,0,false);
	
	// put the data back into the data grid
	int index = 0;
	for (int k = 0; k < this->nsteps+1; k++) {
		double * array = arrays[k];
		int array_size = steps[k];
		if (k == this->nsteps) { array_size = steps[k-1]; }

		for (int j = 0; j < array_size; j++) {
			data->setData(index,0,0,array[j]); 
			index++;
		}
	}
}

void Wavelet::transform2D(DataGrid * data) {
		
	int xsize = data->getX();
	int ysize = data->getY();

	int arrayindex = 0, stepindex = 0;	
	DataGrid ** arrays = new DataGrid*[3*this->nsteps+1];
	int * steps = new int[this->nsteps*2];
	DataGrid * inputGrid = data;
	
	int lengthx = 0, lengthy = 0;
	
	for (int step = 0; step < this->nsteps; ++step) {
			
		int outsizex = (int) floor((xsize + this->dec_len - 1) / 2.);
		int outsizey = (int) floor((ysize + this->dec_len - 1) / 2.);
		
		DataGrid * iGrid1 = new DataGrid2D(xsize,outsizey,0);
		DataGrid * iGrid2 = new DataGrid2D(xsize,outsizey,0);
		
		DataGrid * tempGrid1 = new DataGrid2D(outsizex,outsizey,0);
		DataGrid * tempGrid2 = new DataGrid2D(outsizex,outsizey,0);
		DataGrid * tempGrid3 = new DataGrid2D(outsizex,outsizey,0);
		DataGrid * tempGrid4 = new DataGrid2D(outsizex,outsizey,0);
		
		double * approx = new double[outsizey];
		double * detail = new double[outsizey];
		
		bzero(approx,outsizey);
		bzero(detail,outsizey);
		
		// go through each row and get the approx and detail
		for (int j = 0; j < xsize; j++) {
			double * input = new double[ysize];
			bzero(input,ysize);
			
			// copy whole row
			for (int k = 0; k  < ysize; k++) {
				input[k] = inputGrid->getData(j,k,0); 	
			}
			
			if (parallel && master) {
				remote->remoteProcess(input, ysize, approx, detail, outsizey, MODE_ZEROPAD, this->mode);
			}
			else {
				d_dec_a(input, ysize, this, approx, outsizey, MODE_ZEROPAD);
				d_dec_d(input, ysize, this, detail, outsizey, MODE_ZEROPAD);				
			}
			
			for (int index = 0; index < outsizey; index++) {
				iGrid1->setData(j,index,0,approx[index]);	
				iGrid2->setData(j,index,0,detail[index]);
			}
			
			bzero(approx,outsizey);
			bzero(detail,outsizey);
		}

		double * approx1 = new double[outsizex];
		double * detail1 = new double[outsizex];
		
		bzero(approx1,outsizex);
		bzero(detail1,outsizex);

		double * approx2 = new double[outsizex];
		double * detail2 = new double[outsizex];
		
		bzero(approx2,outsizex);
		bzero(detail2,outsizex);
		
		// now go through the columns of the approx and the detail
		// and get their approx and detail (4 vectors now) and
		// store them
		for (int j = 0; j < outsizey; j++) {
			
			double * input1 = new double[xsize];
			bzero(input1,xsize);
			
			double * input2 = new double[xsize];
			bzero(input2,xsize);
			
			// copy whole col
			for (int k = 0; k  < xsize; k++) {
				input1[k] = iGrid1->getData(k,j,0); 	
				input2[k] = iGrid2->getData(k,j,0); 	
			}
			
			if (parallel && master) {
				remote->remoteProcess(input1, xsize, approx1, detail1, outsizex, MODE_ZEROPAD, this->mode);
			}
			else {
				d_dec_a(input1, xsize, this, approx1, outsizex, MODE_ZEROPAD);
				d_dec_d(input1, xsize, this, detail1, outsizex, MODE_ZEROPAD);
			}
			
			if (parallel && master) {
				remote->remoteProcess(input2, xsize, approx2, detail2, outsizex, MODE_ZEROPAD, this->mode);
			}
			else {
				d_dec_a(input2, xsize, this, approx2, outsizex, MODE_ZEROPAD);
				d_dec_d(input2, xsize, this, detail2, outsizex, MODE_ZEROPAD);
			}
			
			for (int index = 0; index < outsizex; index++) {
				tempGrid1->setData(index,j,0,approx1[index]);	
				tempGrid2->setData(index,j,0,detail1[index]);				
				tempGrid3->setData(index,j,0,approx2[index]);	
				tempGrid4->setData(index,j,0,detail2[index]);
			}
		}
			   
		xsize = outsizex;
		ysize = outsizey;
		inputGrid = tempGrid1;

		// save the details, and the approx. the approx
		// gets overwritten in the next level of deconstruction
		arrays[arrayindex] = tempGrid4;
		arrays[arrayindex+1] = tempGrid3;
		arrays[arrayindex+2] = tempGrid2;
		arrays[arrayindex+3] = tempGrid1;
		arrayindex += 3;
				
		lengthx += outsizex;
		lengthy += outsizey;

		if (verbose) cout << "Level " << step << ":  X size: " << outsizey << 
			"and Y size: " << outsizey << endl;
		
		// store the structure of the decomposition
		steps[stepindex++] = outsizex;
		steps[stepindex++] = outsizey;
	}
	
	lengthx += xsize;
	lengthy += ysize;
	
	data->levels = this->nsteps;
	data->setStructure(steps);
	
	data->resize(lengthx,lengthy,0,false);
	
	int indexx = 0;
	int indexy = 0;
	int oldx = indexx;
	int oldy = indexy;
	int count = 0;
	
	// we need some fancy footwork here to store
	// the data properly in a recursive manner and
	// in a way that can be reformed later.
	for (int k = 0; k < this->nsteps; k++) {
		
		oldx = indexx;
		oldy = indexy;
		
		DataGrid * tmp1 = arrays[k*3];
		DataGrid * tmp2 = arrays[k*3+1];
		DataGrid * tmp3 = arrays[k*3+2];
		//DataGrid * tmp4 = arrays[k*3+3];
		
		int lastx = 0, lasty = 0;

		for (int x = 0; x < tmp1->getX(); x++) {
			for (int y = 0; y < tmp1->getY(); y++, indexy++) {
				data->setData(indexx,indexy,0,tmp1->getData(x,y,0)); 
				count++;
			}
			lasty = indexy;
			indexy = oldy;
			indexx++;
		}
		lastx = indexx;
		
		indexy = lasty;
		indexx = oldx;

		for (int x = 0; x < tmp2->getX(); x++) {
			for (int y = 0; y < tmp2->getY(); y++,indexy++) {
				data->setData(indexx,indexy,0,tmp2->getData(x,y,0)); 
				count++;
			}
			indexy = lasty;
			indexx++;
		}

		indexx = lastx;
		indexy = oldy;
		for (int x = 0; x < tmp3->getX(); x++) {
			for (int y = 0; y < tmp3->getY(); y++, indexy++) {
				data->setData(indexx,indexy,0,tmp3->getData(x,y,0)); 	
				count++;
			}
			indexy = oldy;
			indexx++;
		}
		indexx = lastx;
		indexy = lasty;	
	}
	
	oldy = indexy;
	
	// get last approx vector
	DataGrid * tmp1 = arrays[this->nsteps*3];
	for (int x = 0; x < tmp1->getX(); x++) {
		for (int y = 0; y < tmp1->getY(); y++,indexy++) {
			data->setData(indexx,indexy,0,tmp1->getData(x,y,0)); 
			count++;
		}
		indexx++;
		indexy = oldy;
	}
}

void Wavelet::transform3D(DataGrid * data) {
	int xsize = data->getX();
	int ysize = data->getY();
	int zsize = data->getZ();

	int arrayindex = 0, stepindex = 0;	
	DataGrid ** arrays = new DataGrid*[7*this->nsteps+1];
	int * steps = new int[this->nsteps*3];
	DataGrid * inputGrid = data;
	
		int lengthx = 0, lengthy = 0, lengthz = 0;
	
	for (int step = 0; step < this->nsteps; ++step) {
			
		int outsizex = (int) floor((xsize + this->dec_len - 1) / 2.);
		int outsizey = (int) floor((ysize + this->dec_len - 1) / 2.);
		int outsizez = (int) floor((zsize + this->dec_len - 1) / 2.);
		
		if (verbose) cout << " step " << step << "x: " << outsizex << " y: " << outsizey
			<< " z: " << outsizez << endl;
		
		DataGrid * l1Grid1 = new DataGrid3D(xsize,ysize,outsizez);
		DataGrid * l1Grid2 = new DataGrid3D(xsize,ysize,outsizez);
		
		DataGrid * l2Grid1 = new DataGrid3D(xsize,outsizey,outsizez);
		DataGrid * l2Grid2 = new DataGrid3D(xsize,outsizey,outsizez);
		DataGrid * l2Grid3 = new DataGrid3D(xsize,outsizey,outsizez);
		DataGrid * l2Grid4 = new DataGrid3D(xsize,outsizey,outsizez);
	
		DataGrid * l3Grid1 = new DataGrid3D(outsizex,outsizey,outsizez);
		DataGrid * l3Grid2 = new DataGrid3D(outsizex,outsizey,outsizez);
		DataGrid * l3Grid3 = new DataGrid3D(outsizex,outsizey,outsizez);
		DataGrid * l3Grid4 = new DataGrid3D(outsizex,outsizey,outsizez);
		DataGrid * l3Grid5 = new DataGrid3D(outsizex,outsizey,outsizez);
		DataGrid * l3Grid6 = new DataGrid3D(outsizex,outsizey,outsizez);
		DataGrid * l3Grid7 = new DataGrid3D(outsizex,outsizey,outsizez);
		DataGrid * l3Grid8 = new DataGrid3D(outsizex,outsizey,outsizez);
	
		
		double * approx = new double[outsizez];
		double * detail = new double[outsizez];
		
		bzero(approx,outsizez);
		bzero(detail,outsizez);
		
		// go through each row and get the approx and detail
		for (int j = 0; j < xsize; j++) {
			for (int k = 0; k < ysize; k++) {
				double * input = new double[zsize];
				bzero(input,zsize);
				
				// copy whole row
				for (int i = 0; i  < zsize; i++) {
					input[i] = inputGrid->getData(j,k,i); 	
				}
				
				if (parallel && master) {
					remote->remoteProcess(input, zsize, approx, detail, outsizez, MODE_ZEROPAD, this->mode);
				}
				else {
					d_dec_a(input, zsize, this, approx, outsizez, MODE_ZEROPAD);
					d_dec_d(input, zsize, this, detail, outsizez, MODE_ZEROPAD);				
				}
				
				for (int index = 0; index < outsizez; index++) {
					l1Grid1->setData(j,k,index,approx[index]);	
					l1Grid2->setData(j,k,index,detail[index]);
				}
				
				bzero(approx,outsizez);
				bzero(detail,outsizez);
			}
		}

		
		delete approx;
		delete detail;

		double * approx1 = new double[outsizey];
		double * detail1 = new double[outsizey];
		
		bzero(approx1,outsizey);
		bzero(detail1,outsizey);

		double * approx2 = new double[outsizey];
		double * detail2 = new double[outsizey];
		
		bzero(approx2,outsizey);
		bzero(detail2,outsizey);
		
		// now go through the columns of the approx and the detail
		// and get their approx and detail (4 vectors now) and
		// store them
		for (int k = 0; k < xsize; k++) {
			
			double * input1 = new double[ysize];
			bzero(input1,ysize);
			
			double * input2 = new double[ysize];
			bzero(input2,ysize);
			
			// copy whole col
			for (int j = 0; j  < outsizez; j++) {
				for (int i = 0; i < ysize; i++) {
					input1[i] = l1Grid1->getData(k,i,j); 	
					input2[i] = l1Grid2->getData(k,i,j); 
				}	
			
				if (parallel && master) {
					remote->remoteProcess(input1, ysize, approx1, detail1, outsizey, MODE_ZEROPAD, this->mode);
				}
				else {
					d_dec_a(input1, ysize, this, approx1, outsizey, MODE_ZEROPAD);
					d_dec_d(input1, ysize, this, detail1, outsizey, MODE_ZEROPAD);
				}
				
				if (parallel && master) {
					remote->remoteProcess(input2, ysize, approx2, detail2, outsizey, MODE_ZEROPAD, this->mode);
				}
				else {
					d_dec_a(input2, ysize, this, approx2, outsizey, MODE_ZEROPAD);
					d_dec_d(input2, ysize, this, detail2, outsizey, MODE_ZEROPAD);
				}
				
				for (int index = 0; index < outsizey; index++) {
					l2Grid1->setData(k,index,j,approx1[index]);	
					l2Grid2->setData(k,index,j,detail1[index]);				
					l2Grid3->setData(k,index,j,approx2[index]);	
					l2Grid4->setData(k,index,j,detail2[index]);
				}
			}
		}
		
		delete approx1;
		delete approx2;
		delete detail1;
		delete detail2;
			
		// NOW GO THROUGH AND DECIMATE THE FINAL
		// ROW - THE X ROW	
			   
		approx1 = new double[outsizex];
		detail1 = new double[outsizex];
		approx2 = new double[outsizex];
		detail2 = new double[outsizex];
		double * approx3 = new double[outsizex];
		double * detail3 = new double[outsizex];		
		double * approx4 = new double[outsizex];
		double * detail4 = new double[outsizex];
		
		bzero(approx1,outsizex);
		bzero(detail1,outsizex);
		bzero(approx2,outsizex);
		bzero(detail2,outsizex);
		bzero(approx3,outsizex);
		bzero(detail3,outsizex);
		bzero(approx4,outsizex);
		bzero(detail4,outsizex);
		
		// now go through the columns of the approx and the detail
		// and get their approx and detail (4 vectors now) and
		// store them
		for (int k = 0; k < outsizey; k++) {
			
			double * input1 = new double[xsize];
			double * input2 = new double[xsize];
			double * input3 = new double[xsize];
			double * input4 = new double[xsize];
			bzero(input1,xsize);
			bzero(input2,xsize);
			bzero(input3,xsize);
			bzero(input4,xsize);
			
			// copy whole col
			for (int j = 0; j < outsizez; j++) {
				for (int i = 0; i < xsize; i++) {
					input1[i] = l2Grid1->getData(i,k,j); 	
					input2[i] = l2Grid2->getData(i,k,j); 
					input3[i] = l2Grid3->getData(i,k,j); 
					input4[i] = l2Grid4->getData(i,k,j); 
				}	
			
			
				if (parallel && master) {
					remote->remoteProcess(input1, xsize, approx1, detail1, outsizex, MODE_ZEROPAD, this->mode);
				}
				else {
					d_dec_a(input1, xsize, this, approx1, outsizex, MODE_ZEROPAD);
					d_dec_d(input1, xsize, this, detail1, outsizex, MODE_ZEROPAD);
				}
				
				if (parallel && master) {
					remote->remoteProcess(input2, xsize, approx2, detail2, outsizex, MODE_ZEROPAD, this->mode);
				}
				else {
					d_dec_a(input2, xsize, this, approx2, outsizex, MODE_ZEROPAD);
					d_dec_d(input2, xsize, this, detail2, outsizex, MODE_ZEROPAD);
				}
				
				if (parallel && master) {
					remote->remoteProcess(input3, xsize, approx3, detail3, outsizex, MODE_ZEROPAD, this->mode);
				}
				else {
					d_dec_a(input3, xsize, this, approx3, outsizex, MODE_ZEROPAD);
					d_dec_d(input3, xsize, this, detail3, outsizex, MODE_ZEROPAD);
				}
				
				if (parallel && master) {
					remote->remoteProcess(input4, xsize, approx4, detail4, outsizex, MODE_ZEROPAD, this->mode);
				}
				else {
					d_dec_a(input4, xsize, this, approx4, outsizex, MODE_ZEROPAD);
					d_dec_d(input4, xsize, this, detail4, outsizex, MODE_ZEROPAD);
				}
				
				for (int index = 0; index < outsizex; index++) {
					l3Grid1->setData(index,k,j,approx1[index]);	
					l3Grid2->setData(index,k,j,detail1[index]);				
					l3Grid3->setData(index,k,j,approx2[index]);	
					l3Grid4->setData(index,k,j,detail2[index]);
					l3Grid5->setData(index,k,j,approx3[index]);	
					l3Grid6->setData(index,k,j,detail3[index]);				
					l3Grid7->setData(index,k,j,approx4[index]);	
					l3Grid8->setData(index,k,j,detail4[index]);
				}
			}
		}	   
		
		delete approx1;
		delete approx2;
		delete approx3;
		delete approx4;
		delete detail1;
		delete detail2;
		delete detail3;
		delete detail4;
		
		xsize = outsizex;
		ysize = outsizey;
		zsize = outsizez;
		inputGrid = l3Grid1;
		
		// save the details, and the approx. the approx
		// gets overwritten in the next level of deconstruction
		arrays[arrayindex] = l3Grid8;
		arrays[arrayindex+1] = l3Grid7;
		arrays[arrayindex+2] = l3Grid6;
		arrays[arrayindex+3] = l3Grid5;
		arrays[arrayindex+4] = l3Grid4;
		arrays[arrayindex+5] = l3Grid3;
		arrays[arrayindex+6] = l3Grid2;
		arrays[arrayindex+7] = l3Grid1;
		arrayindex += 7;
				
		lengthx += outsizex;
		lengthy += outsizey;
		lengthz += outsizez;

		if (verbose) cout << "Level " << step << ":  X size: " << outsizey << 
			"and Y size: " << outsizey << " and Z size: " << outsizez << endl;
		
		// store the structure of the decomposition
		steps[stepindex++] = outsizex;
		steps[stepindex++] = outsizey;
		steps[stepindex++] = outsizez;
	}
	
	lengthx += xsize;
	lengthy += ysize;
	lengthz += zsize;
	
	data->levels = this->nsteps;
	data->setStructure(steps);
	
	data->resize(lengthx,lengthy,lengthz,false);
	
	int indexx = 0;
	int indexy = 0;
	int indexz = 0;
	int oldx = indexx;
	int oldy = indexy;
	int oldz = indexz;
	int count = 0;
	
	// we need some fancy footwork here to store
	// the data properly in a recursive manner and
	// in a way that can be reformed later.
	for (int k = 0; k < this->nsteps; k++) {
		oldx = indexx;
		oldy = indexy;
		oldz = indexz;
		
		//cout << indexx << " " << indexy << " " << indexz << endl;
				
		int lastx = 0, lasty = 0, lastz = 0;

		for (int g = 0; g < 7; g++) {
			DataGrid * tmp = arrays[k*7+g];

			for (int x = 0; x < tmp->getX(); x++, indexx++) {
				for (int y = 0; y < tmp->getY(); y++, indexy++) {
					for (int z = 0; z < tmp->getZ(); z++, indexz++) {
						data->setData(indexx,indexy,indexz,tmp->getData(x,y,z)); 
//							if (g == 0) { cout << "DDD " << 
//								indexx << " " << indexy << " " << indexz <<  " " << tmp->getData(x,y,z) << endl; }
					}
					if (g == 0) { lastz = indexz; indexz = oldz; }
					else if (g == 1) { indexz = oldz; }
					else if (g == 2) { indexz = lastz; }
					else if (g == 3) { indexz = lastz; } 
					else if (g == 4) { indexz = oldz; } 
					else if (g == 5) { indexz = oldz; } 
					else if (g == 6) { indexz = lastz; } 
				}
				if (g < 4) { lasty = indexy; indexy = oldy; }
				else { indexy = lasty; }
			}
			
			if (g == 0) { lastx = indexx;  indexz = oldz; }
			else if (g == 1) { indexx = oldx;  indexz = lastz; }
			else if (g == 2) { indexx = lastx; indexz = lastz; }
			else if (g == 3) { indexx = oldx;  indexz = oldz; indexy = lasty; }
			else if (g == 4) { indexx = lastx; indexz = oldz;  }
			else if (g == 5) { indexx = oldx; indexz = lastz; }
			else if (g == 6) { indexx = lastx; indexz = lastz; }			
		}

		indexx = lastx;
		indexy = lasty;
		indexz = lastz;		
	}
	
	oldz = indexz;
	oldy = indexy;
	oldx = indexx;
	
	// get last approx vector
	DataGrid * tmp1 = arrays[this->nsteps*7];
	for (int x = 0; x < tmp1->getX(); x++, indexx++) {
		for (int y = 0; y < tmp1->getY(); y++,indexy++) {
			for (int z = 0; z < tmp1->getZ(); z++, indexz++) {
				data->setData(indexx,indexy,indexz,tmp1->getData(x,y,z)); 
			}
			indexz = oldz;
		}
		indexy = oldy;
	}
}
		

void Wavelet::inverseTransform1D(DataGrid * data) {
	
  	  	
	int * steps = new int[this->nsteps];

	int size = data->getX();

	double * input = new double[size];
	bzero(input,size);
	for (int k = 0; k  < size; k++) {
		input[k] = data->getData(k,0,0); 	
	}

	int stepsize = data->levelStructure[this->nsteps - 1]; 
	double * a_data = new double[stepsize];
	bzero(a_data,stepsize);
	
	for (int index = 0, k = size-(stepsize); k < size; k++, index++) {
		a_data[index] = input[k]; 
	}
	
	
	for (int step = this->nsteps-1; step >= 0; --step) {
		
		stepsize = data->levelStructure[step]; 
		
		int start = 0;
		for (int j = 0; j < step; j++) {
			start += data->levelStructure[j];
		}
		
	 	double * d_data = new double[stepsize];
		bzero(d_data,stepsize);
		
		if (verbose) cout << "Reconstruction level: " << step << " step size : " << stepsize << "start index: " << start << endl;	
		
		for (int index = 0, k = start; index < stepsize; k++, index++) {
			d_data[index] = input[k];  
		}
	
		int reconstruction_len = idwt_buffer_length(stepsize, this->rec_len, MODE_ZEROPAD);
	 
	    double * output = new double[reconstruction_len];
	  
	    d_idwt(a_data, stepsize, d_data, stepsize,this, output, 
	    		reconstruction_len, MODE_ZEROPAD, 1);
	    		
	    a_data = output;
	    size = reconstruction_len;
	}
		
	for (int k = 0; k < size; k++) {
		data->setData(k,0,0,a_data[k]); 	
	}
}

void Wavelet::inverseTransform2D(DataGrid * data) {
		  	
	int xsize = data->getX();
	int ysize = data->getY();

	int stepsizey = data->levelStructure[2*this->nsteps - 1]; 
	int stepsizex = data->levelStructure[2*this->nsteps - 2]; 
	
	DataGrid * input = data;
	
	DataGrid * a_grid = new DataGrid2D(stepsizex,stepsizey,0);
	
	if (verbose) cout << "data size : " << xsize << " step x: " << stepsizex << " step y: " << stepsizey << endl;
	
	int startx = 0, starty = 0;
	for (int j = 0; j < (this->nsteps-1)*2; j+=2) {
		startx += data->levelStructure[j];
		starty += data->levelStructure[j+1];
	}
	
	int indexx, indexy;
	for (int k = startx+stepsizex, indexx = 0; k < startx+2*stepsizex; k++, indexx++) {
		for (int j = starty+stepsizey, indexy = 0; j < starty+2*stepsizey; j++, indexy++) {
			a_grid->setData(indexx,indexy,0,data->getData(k,j,0)); 
		}
	}
	
	for (int step = this->nsteps-1; step >= 0; --step) {
		
 		stepsizex = data->levelStructure[2*step]; 
		stepsizey = data->levelStructure[2*step+1]; 
		
		startx = 0, starty = 0;
		for (int j = 0; j < step*2; j+=2) {
			startx += data->levelStructure[j];
			starty += data->levelStructure[j+1];
		}
		
		if (verbose) cout << "step: " << step << " step size x: " << stepsizex << " step y: " << stepsizey << endl;			

		DataGrid * d_grid1 = new DataGrid2D(stepsizex,stepsizey,0);
	 	for (int k = startx + stepsizex, indexx = 0; k < startx + 2*stepsizex; k++, indexx++) {
			for (int j = starty, indexy = 0; j < starty+stepsizey; j++, indexy++) {
				d_grid1->setData(indexx,indexy,0,input->getData(k,j,0)); 
			}
		}

		DataGrid * d_grid2 = new DataGrid2D(stepsizex,stepsizey,0);
	 	for (int k = startx, indexx = 0; k < startx + stepsizex; k++, indexx++) {
			for (int j = starty + stepsizey, indexy = 0; j < starty + 2*stepsizey; j++, indexy++) {
				d_grid2->setData(indexx,indexy,0,input->getData(k,j,0)); 
			}
		}

		DataGrid * d_grid3 = new DataGrid2D(stepsizex,stepsizey,0);
	 	for (int k = startx, indexx = 0; k < startx+stepsizex; k++, indexx++) {
			for (int j = starty, indexy = 0; j < starty+stepsizey; j++, indexy++) {
				d_grid3->setData(indexx,indexy,0,input->getData(k,j,0)); 
			}
		}

		int reconstruction_lenx = idwt_buffer_length(stepsizex, this->rec_len, MODE_ZEROPAD);
		int reconstruction_leny = idwt_buffer_length(stepsizey, this->rec_len, MODE_ZEROPAD);

		// take cols data, recon
		// take rows data recon,
		// recon both
	   
	   	DataGrid * o_grid1 = new DataGrid2D(reconstruction_lenx,stepsizey,0);
	   	DataGrid * o_grid2 = new DataGrid2D(reconstruction_lenx,stepsizey,0);
	   
   		for (int k = 0; k < stepsizey; k++) {
		    double output1[reconstruction_lenx];
		    double output2[reconstruction_lenx];
		    
		    double a_data[stepsizex];
		    double d_data1[stepsizex];
		    double d_data2[stepsizex];
		    double d_data3[stepsizex];
			for (int i = 0; i < stepsizex; i++) {
				a_data[i] = a_grid->getData(i,k,0);
				
				d_data1[i] = d_grid1->getData(i,k,0);
				d_data2[i] = d_grid2->getData(i,k,0);
				d_data3[i] = d_grid3->getData(i,k,0);
			}

		    d_idwt(a_data, stepsizex, d_data1, stepsizex,this, output1, 
	    		reconstruction_lenx, MODE_ZEROPAD, 1);

		    d_idwt(d_data2, stepsizex, d_data3, stepsizex,this, output2, 
	    		reconstruction_lenx, MODE_ZEROPAD, 1);
	    		
	    	for (int i = 0; i < reconstruction_lenx; i++) {
				o_grid1->setData(i,k,0,output1[i]);
				o_grid2->setData(i,k,0,output2[i]);
			}
   		}
   		
   		DataGrid * o_grid = new DataGrid2D(reconstruction_lenx,reconstruction_leny,0);
   				
   		for (int k = 0; k < reconstruction_lenx; k++) {
		    double output[reconstruction_leny];
		   		    
		    double a_data[stepsizey];
		    double d_data[stepsizey];
			for (int i = 0; i < stepsizey; i++) {
				a_data[i] = o_grid1->getData(k,i,0);
				d_data[i] = o_grid2->getData(k,i,0);
			}
	
		    d_idwt(a_data, stepsizey, d_data, stepsizey,this, output, 
	    		reconstruction_leny, MODE_ZEROPAD, 1);
	    		
	    	for (int i = 0; i < reconstruction_leny; i++) {
				o_grid->setData(k,i,0,output[i]);
			}
   		}		
   		
	    delete a_grid;
	    delete o_grid1;
	    delete o_grid2;	
	    a_grid = o_grid;
	}

	a_grid->copyTo(data);	
}


void Wavelet::inverseTransform3D(DataGrid * data) {
	int xsize = data->getX();
	int ysize = data->getY();
	int zsize = data->getZ();

	int stepsizez = data->levelStructure[3*this->nsteps - 1]; 
	int stepsizey = data->levelStructure[3*this->nsteps - 2]; 
	int stepsizex = data->levelStructure[3*this->nsteps - 3]; 
	
	DataGrid * input = data;
	
	DataGrid * aaa = new DataGrid3D(stepsizex,stepsizey,stepsizez);
	
	if (verbose) cout << "data size : " << xsize << " step x" << stepsizex 
		<< " step y: " << stepsizey << " step z: " << stepsizez << endl;
	
	int startx = 0, starty = 0, startz = 0;
	for (int j = 0; j < (this->nsteps-1)*3; j+=3) {
		startx += data->levelStructure[j];
		starty += data->levelStructure[j+1];
		startz += data->levelStructure[j+2];
	}
	
	//if (verbose) cout << "start: " << startx << " " << starty << " "  << startz << endl;			
	
	int indexx =0, indexy=0, indexz=0;
	for (int k = startx+stepsizex, indexx = 0; k < startx+2*stepsizex; k++, indexx++) {
		for (int j = starty+stepsizey, indexy = 0; j < starty+2*stepsizey; j++, indexy++) {
			for (int i = startz+stepsizez, indexz = 0; i < startz+2*stepsizez; i++, indexz++) {
				aaa->setData(indexx,indexy,indexz,data->getData(k,j,i)); 
			}
		}
	}
	
	for (int step = this->nsteps-1; step >= 0; --step) {
		
 		stepsizex = data->levelStructure[3*step]; 
		stepsizey = data->levelStructure[3*step+1]; 
		stepsizez = data->levelStructure[3*step+2];

		if (verbose) cout << "step: " << step << " step size x: " << stepsizex << " step y: " << stepsizey << " z: " << stepsizez << endl;					

		startx = 0, starty = 0, startz = 0;
		for (int j = 0; j < step*3; j+=3) {
			startx += data->levelStructure[j];
			starty += data->levelStructure[j+1];
			startz += data->levelStructure[j+2];
		}
		
		//if (verbose) cout << "start: " << startx << " " << starty << " "  << startz << endl;			

		DataGrid * aad = new DataGrid3D(stepsizex,stepsizey,stepsizez);
	 	for (int k = startx, indexx = 0; k < startx+stepsizex; k++, indexx++) {
			for (int j = starty+stepsizey, indexy = 0; j < starty+2*stepsizey; j++, indexy++) {
				for (int i = startz+stepsizez, indexz = 0; i < startz + 2*stepsizez; i++, indexz++) {
					aad->setData(indexx,indexy,indexz,input->getData(k,j,i));
				} 
			}
		}

						
		DataGrid * ada = new DataGrid3D(stepsizex,stepsizey,stepsizez);
	 	for (int k = startx+stepsizex, indexx = 0; k < startx+2*stepsizex; k++, indexx++) {
			for (int j = starty+stepsizey, indexy = 0; j < starty+2*stepsizey; j++, indexy++) {
				for (int i = startz, indexz = 0; i < startz + stepsizez; i++, indexz++) {
					ada->setData(indexx,indexy,indexz,input->getData(k,j,i));
				} 
			}
		}

		DataGrid * add = new DataGrid3D(stepsizex,stepsizey,stepsizez);
	 	for (int k = startx, indexx = 0; k < startx+stepsizex; k++, indexx++) {
			for (int j = starty+stepsizey, indexy = 0; j < starty+2*stepsizey; j++, indexy++) {
				for (int i = startz, indexz = 0; i < startz + stepsizez; i++, indexz++) {
					add->setData(indexx,indexy,indexz,input->getData(k,j,i));
				} 
			}
		}
		
		DataGrid * daa = new DataGrid3D(stepsizex,stepsizey,stepsizez);
	 	for (int k = startx+stepsizex, indexx = 0; k < startx+2*stepsizex; k++, indexx++) {
			for (int j = starty, indexy = 0; j < starty+stepsizey; j++, indexy++) {
				for (int i = startz+stepsizez, indexz = 0; i < startz + 2*stepsizez; i++, indexz++) {
					daa->setData(indexx,indexy,indexz,input->getData(k,j,i));
				} 
			}
		}
		
		DataGrid * dad = new DataGrid3D(stepsizex,stepsizey,stepsizez);
	 	for (int k = startx, indexx = 0; k < startx+stepsizex; k++, indexx++) {
			for (int j = starty, indexy = 0; j < starty+stepsizey; j++, indexy++) {
				for (int i = startz+stepsizez, indexz = 0; i < startz + 2*stepsizez; i++, indexz++) {
					dad->setData(indexx,indexy,indexz,input->getData(k,j,i));
				} 
			}
		}
		
		DataGrid * dda = new DataGrid3D(stepsizex,stepsizey,stepsizez);
	 	for (int k = startx+stepsizex, indexx = 0; k < startx+2*stepsizex; k++, indexx++) {
			for (int j = starty, indexy = 0; j < starty+stepsizey; j++, indexy++) {
				for (int i = startz, indexz = 0; i < startz + stepsizez; i++, indexz++) {
					dda->setData(indexx,indexy,indexz,input->getData(k,j,i));
				} 
			}
		}
		
		DataGrid * ddd = new DataGrid3D(stepsizex,stepsizey,stepsizez);
	 	for (int k = startx, indexx = 0; k < startx+stepsizex; k++, indexx++) {
			for (int j = starty, indexy = 0; j < starty+stepsizey; j++, indexy++) {
				for (int i = startz, indexz = 0; i < startz + stepsizez; i++, indexz++) {
					ddd->setData(indexx,indexy,indexz,input->getData(k,j,i));
				} 
			}
		}
	

		int reconstruction_lenx = idwt_buffer_length(stepsizex, this->rec_len, MODE_ZEROPAD);
		int reconstruction_leny = idwt_buffer_length(stepsizey, this->rec_len, MODE_ZEROPAD);
		int reconstruction_lenz = idwt_buffer_length(stepsizez, this->rec_len, MODE_ZEROPAD);

		// take cols data, recon
		// take rows data recon,
		// recon both
	   
	   	DataGrid * aa = new DataGrid3D(reconstruction_lenx,stepsizey,stepsizez);
	   	DataGrid * ad = new DataGrid3D(reconstruction_lenx,stepsizey,stepsizez);
	    DataGrid * da = new DataGrid3D(reconstruction_lenx,stepsizey,stepsizez);
	   	DataGrid * dd = new DataGrid3D(reconstruction_lenx,stepsizey,stepsizez);
	   
   		for (int j = 0; j < stepsizey; j++) {
   			for (int i = 0; i < stepsizez; i++) {
   				
			    double output1[reconstruction_lenx];
			    double output2[reconstruction_lenx];
			    double output3[reconstruction_lenx];
			    double output4[reconstruction_lenx];
			    
			    double aaa_data[stepsizex];
			    double aad_data[stepsizex];
			    double ada_data[stepsizex];
			    double add_data[stepsizex];
			    double daa_data[stepsizex];
			    double dad_data[stepsizex];
			    double dda_data[stepsizex];
			    double ddd_data[stepsizex];
		    	
				for (int k = 0; k < stepsizex; k++) {
					aaa_data[k] = aaa->getData(k,j,i);
					aad_data[k] = aad->getData(k,j,i);
					ada_data[k] = ada->getData(k,j,i);
					add_data[k] = add->getData(k,j,i);
					daa_data[k] = daa->getData(k,j,i);
					dad_data[k] = dad->getData(k,j,i);
					dda_data[k] = dda->getData(k,j,i);
					ddd_data[k] = ddd->getData(k,j,i);
				}		
			

			    d_idwt(aaa_data, stepsizex, aad_data, stepsizex,this, output1, 
		    		reconstruction_lenx, MODE_ZEROPAD, 1);
			    d_idwt(ada_data, stepsizex, add_data, stepsizex,this, output2, 
		    		reconstruction_lenx, MODE_ZEROPAD, 1);
		   		d_idwt(daa_data, stepsizex, dad_data, stepsizex,this, output3, 
		    		reconstruction_lenx, MODE_ZEROPAD, 1);
				d_idwt(dda_data, stepsizex, ddd_data, stepsizex,this, output4, 
		    		reconstruction_lenx, MODE_ZEROPAD, 1);
		    		
		    	for (int k = 0; k < reconstruction_lenx; k++) {
					aa->setData(k,j,i,output1[k]);
					ad->setData(k,j,i,output2[k]);
					da->setData(k,j,i,output3[k]);
					dd->setData(k,j,i,output4[k]);
				}
   			}
   		}
   		
   		DataGrid * a = new DataGrid3D(reconstruction_lenx,reconstruction_leny,stepsizez);
   		DataGrid * d = new DataGrid3D(reconstruction_lenx,reconstruction_leny,stepsizez);
   				
   		for (int k = 0; k < reconstruction_lenx; k++) {
   			for (int j = 0; j < stepsizez; j++) {
			    double output1[reconstruction_leny];
			    double output2[reconstruction_leny];
			   		    
			    double aa_data[stepsizey];
			    double ad_data[stepsizey];
			    double da_data[stepsizey];
			    double dd_data[stepsizey];
			    
				for (int i = 0; i < stepsizey; i++) {
					aa_data[i] = aa->getData(k,i,j);
					ad_data[i] = ad->getData(k,i,j);
					da_data[i] = da->getData(k,i,j);
					dd_data[i] = dd->getData(k,i,j);
				}
		
			    d_idwt(aa_data, stepsizey, ad_data, stepsizey,this, output1, 
		    		reconstruction_leny, MODE_ZEROPAD, 1);
		    	d_idwt(da_data, stepsizey, dd_data, stepsizey,this, output2, 
		    		reconstruction_leny, MODE_ZEROPAD, 1);
		    		
		    	for (int i = 0; i < reconstruction_leny; i++) {
					a->setData(k,i,j,output1[i]);
					d->setData(k,i,j,output2[i]);
				}
   			}
   		}	
   		
   		DataGrid * complete = new DataGrid3D(reconstruction_lenx,reconstruction_leny,reconstruction_lenz);
   			
   		for (int k = 0; k < reconstruction_lenx; k++) {
   			for (int j = 0; j < reconstruction_leny; j++) {	
			    double output[reconstruction_lenz];
			   		    
			    double a_data[stepsizez];
			    double d_data[stepsizez];
				for (int i = 0; i < stepsizez; i++) {
					a_data[i] = a->getData(k,j,i);
					d_data[i] = d->getData(k,j,i);
				}
		
			    d_idwt(a_data, stepsizez, d_data, stepsizez,this, output, 
		    		reconstruction_lenz, MODE_ZEROPAD, 1);
		    		
		    	for (int i = 0; i < reconstruction_lenz; i++) {
					complete->setData(k,j,i,output[i]);
				}
	   		}
   		}			
   		
  		
	    delete a;  delete d;
	    delete aa; delete ad; delete da; delete dd;		
	    delete aaa;	delete aad; delete ada; delete add;
	    delete daa; delete dad; delete dda; delete ddd;
	    aaa = complete;
	}
	aaa->copyTo(data);		
	
}


