#include <new>
#include <cmath>
#include <ctime>
#include <cstdlib>
#include <fstream>
#include <iostream>

using namespace std;

//image class-------------------------------------------------------------------------------
class Image
{
	int **image;
	int iSize;

	int ***iBit;
	int ****sBit;
	int ***result;
public:
	Image(char *fname, int s);
	Image(char *fn1, char *fn2, int s);
	~Image() { FreeImage(); }
	
	void Allocate();
	void FreeImage();
	void SetValue(int num, int plane, int x, int y, int type);	

	//encoder
	void Decompose();  //image -> iBit
	void VSS22();  //iBit -> sBit
	void Combine();  //sBit -> result

	//decoder
	void IDecompose();  //result -> sBit
	void IVSS22();  //sBit -> iBit
	void ICombine(); //iBit -> image

	//change and rotate
	void ChangeArea(int image, int plane, int area1, int area2);
	void RotateRight(int image, int plane);
	void RotateLeft(int image, int plane);

	void SaveImage(char *fname);
	void SaveResult(char *fn1, char *fn2);
};

int GetRand(int min, int max)
{
	int r = rand() % (max-min+1);
	return r + min;
}

//image class functions---------------------------------------------------------------------------
Image::Image(char *fname, int s)
{
	iSize = s;
	srand(time(0));
	Allocate();

	cout << "read image [" << fname << "].....................";

	//read image
	ifstream in(fname, ios::in | ios::binary);
	if (!in)
	{
		cout << "Open file [" << fname << "] Error!\n";
		exit(1);
	}
	for(int x=0; x<iSize; x++)
		for(int y=0; y<iSize; y++)
			image[x][y] = in.get();

	in.close();

	cout << "OK\n";
}

Image::Image(char *fn1, char *fn2, int s)
{	
	iSize = s / 2;
	srand(time(0));
	Allocate();

	cout << "read images [" << fn1 << " and " << fn2 << "].....................";

	//read images
	ifstream in1(fn1, ios::in | ios::binary);
	ifstream in2(fn2, ios::in | ios::binary);
	if (!in1)
	{
		cout << "Open file [" << fn1 << "] Error!\n";
		exit(1);
	}
	if (!in2)
	{
		cout << "Open file [" << fn2 << "] Error!\n";
		exit(1);
	}
	for(int x=0; x<iSize*2; x++)
		for(int y=0; y<iSize*2; y++)
		{
			result[0][x][y] = in1.get();
			result[1][x][y] = in2.get();
		}

	in1.close();
	in2.close();

	cout << "OK\n";
}

void Image::Allocate()
{
	//allocate memory space
	try {
		image = new int *[iSize];
		for(int i=0; i<iSize; i++)
			image[i] = new int [iSize];

		iBit = new int **[8];
		for(int i=0; i<8; i++)
		{
			iBit[i] = new int *[iSize];
			for(int j=0; j<iSize; j++)
				iBit[i][j] = new int [iSize];
		}
		
		sBit = new int ***[2];
		for(int i=0; i<2; i++)
		{
			sBit[i] = new int **[8];
			for(int j=0; j<8; j++)
			{
				sBit[i][j] = new int *[iSize*2];
				for(int k=0; k<iSize*2; k++)
					sBit[i][j][k] = new int [iSize*2];
			}
		}

		result = new int **[2];
		for(int i=0; i<2; i++)
		{
			result[i] = new int *[iSize*2];
			for(int j=0; j<iSize*2; j++)
				result[i][j] = new int [iSize*2];
		}
	}
	catch(bad_alloc)
	{
		cout << "Allocation Error!\n";
		exit(1);
	}
}

void Image::FreeImage()
{
	//free memory space
	for(int i=0; i<iSize; i++)
		delete [] image[i];
	delete [] image;

	for(int i=0; i<8; i++)
	{
		for(int j=0; j<iSize; j++)
			delete [] iBit[i][j];
		delete [] iBit[i];
	}
	delete [] iBit;
		
	for(int i=0; i<2; i++)
	{
		for(int j=0; j<8; j++)
		{
			for(int k=0; k<iSize*2; k++)
				delete [] sBit[i][j][k];
			delete [] sBit[i][j];
		}
		delete [] sBit[i];
	}
	delete [] sBit;

	for(int i=0; i<2; i++)
	{
		for(int j=0; j<iSize*2; j++)
			delete [] result[i][j];
		delete [] result[i];
	}
	delete [] result;
}

void Image::SetValue(int num, int plane, int x, int y, int type)
{
	switch(type)
	{
		case 0:
			sBit[num][plane][x][y] = 0;
			sBit[num][plane][x][y+1] = 0;
			sBit[num][plane][x+1][y] = 1;
			sBit[num][plane][x+1][y+1] = 1;
			break;
		case 1:
			sBit[num][plane][x][y] = 0;
			sBit[num][plane][x][y+1] = 1;
			sBit[num][plane][x+1][y] = 0;
			sBit[num][plane][x+1][y+1] = 1;
			break;
		case 2:
			sBit[num][plane][x][y] = 0;
			sBit[num][plane][x][y+1] = 1;
			sBit[num][plane][x+1][y] = 1;
			sBit[num][plane][x+1][y+1] = 0;
			break;
		case 3:
			sBit[num][plane][x][y] = 1;
			sBit[num][plane][x][y+1] = 1;
			sBit[num][plane][x+1][y] = 0;
			sBit[num][plane][x+1][y+1] = 0;
			break;
		case 4:
			sBit[num][plane][x][y] = 1;
			sBit[num][plane][x][y+1] = 0;
			sBit[num][plane][x+1][y] = 1;
			sBit[num][plane][x+1][y+1] = 0;
			break;
		case 5:
			sBit[num][plane][x][y] = 1;
			sBit[num][plane][x][y+1] = 0;
			sBit[num][plane][x+1][y] = 0;
			sBit[num][plane][x+1][y+1] = 1;
			break;
	}
}

void Image::Decompose()  //image -> iBit
{
	cout << "Decompose.....................";

	for(int x=0; x<iSize; x++)
		for(int y=0; y<iSize; y++)
		{
			int index = 7;
			for(int w=128; w; w/=2)
				if (image[x][y] & w) iBit[index--][x][y] = 1;
				else iBit[index--][x][y] = 0;
		}

	cout << "OK\n";
}

void Image::VSS22()  //iBit -> sBit
{
	cout << "VSS22.....................";

	for(int n=0; n<8; n++)
		for(int x=0; x<iSize; x++)
			for(int y=0; y<iSize; y++)
			{
				int p1=GetRand(0,5), p2;

				if (iBit[n][x][y] == 1)  
				{
					int tmp = (p1+3) % 6;
					do {
						p2 = GetRand(0, 5);
					} while(p2 == tmp);
				}
				else  
				{
					p2 = (p1 + 3) % 6;
				}

				SetValue(0, n, x*2, y*2, p1);
				SetValue(1, n, x*2, y*2, p2);
			}

	cout << "OK\n";
}

void Image::Combine()  //sBit -> result
{
	cout << "Combine.....................";

	for(int n=0; n<2; n++)
		for(int x=0; x<iSize*2; x++)
			for(int y=0; y<iSize*2; y++)
			{
				int index = 7, sum = 0;
				for(int w=128; w; w/=2)
					sum += w * sBit[n][index--][x][y];
				result[n][x][y] = sum;
			}

	cout << "OK\n";
}

void Image::IDecompose()  //result -> sBit
{
	cout << "IDecompose.....................";

	for(int x=0; x<iSize*2; x++)
		for(int y=0; y<iSize*2; y++)
		{
			int index = 7;
			for(int w=128; w; w/=2)
			{
				if (result[0][x][y] & w)
					sBit[0][index][x][y] = 1;
				else
					sBit[0][index][x][y] = 0;

				if (result[1][x][y] & w)
					sBit[1][index][x][y] = 1;
				else
					sBit[1][index][x][y] = 0;

				index --;
			}
		}

	cout << "OK\n";
}

void Image::IVSS22()  //sBit -> iBit
{
	cout << "IVSS22.....................";

	for(int n=0; n<8; n++)
		for(int x=0; x<iSize; x++)
			for(int y=0; y<iSize; y++)
			{
				int wCnt = 0;
				for(int i=0; i<2; i++)
					for(int j=0; j<2; j++)
						if (sBit[0][n][x*2+i][y*2+j] == 1 && sBit[1][n][x*2+i][y*2+j] == 1)
							wCnt ++;

				if (wCnt > 0)
					iBit[n][x][y] = 1;  
				else
					iBit[n][x][y] = 0;  
			}

	cout << "OK\n";
}

void Image::ICombine() //iBit -> image
{
	cout << "ICombine.....................";

	for(int x=0; x<iSize; x++)
		for(int y=0; y<iSize; y++)
		{
			int index = 7, sum = 0;
			for(int w=128; w; w/=2)
				sum += w * iBit[index--][x][y];
			image[x][y] = sum;
		}

	cout << "OK\n";
}

void Image::ChangeArea(int image, int plane, int area1, int area2)
{
	cout << "Change Area " << area1 << " and " << area2 << ".....................";

	int h = iSize / 2;
	int *temp1 = new int [4 * (h * (h+1) / 2)];
	int *temp2 = new int [4 * (h * (h+1) / 2)];

	int row, col;

	int index = 0;
	switch(area1)
	{
		case 1:
			row = h;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp1[index++] = sBit[image][plane][2*(row-j)+dx][2*(col+i)+dy];
			break;

		case 2:
			row = 0;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp1[index++] = sBit[image][plane][2*(row+i)+dx][2*(col+j)+dy];
			break;

		case 3:
			row = 0;
			col = h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp1[index++] = sBit[image][plane][2*(row+i)+dx][2*(col+j)+dy];
			break;

		case 4:
			row = 0;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp1[index++] = sBit[image][plane][2*(row+j)+dx][2*(col-i)+dy];
			break;

		case 5:
			row = h;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp1[index++] = sBit[image][plane][2*(row+j)+dx][2*(col-i)+dy];
			break;

		case 6:
			row = 2 * h;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp1[index++] = sBit[image][plane][2*(row-i)+dx][2*(col-j)+dy];
			break;

		case 7:
			row = 2 * h;
			col = h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp1[index++] = sBit[image][plane][2*(row-i)+dx][2*(col-j)+dy];
			break;

		case 8:
			row = 2 * h;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp1[index++] = sBit[image][plane][2*(row-j)+dx][2*(col+i)+dy];
			break;
	}

	index = 0;
	switch(area2)
	{
		case 1:
			row = h;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp2[index++] = sBit[image][plane][2*(row-j)+dx][2*(col+i)+dy];
			break;

		case 2:
			row = 0;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp2[index++] = sBit[image][plane][2*(row+i)+dx][2*(col+j)+dy];
			break;

		case 3:
			row = 0;
			col = h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp2[index++] = sBit[image][plane][2*(row+i)+dx][2*(col+j)+dy];
			break;

		case 4:
			row = 0;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp2[index++] = sBit[image][plane][2*(row+j)+dx][2*(col-i)+dy];
			break;

		case 5:
			row = h;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp2[index++] = sBit[image][plane][2*(row+j)+dx][2*(col-i)+dy];
			break;

		case 6:
			row = 2 * h;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp2[index++] = sBit[image][plane][2*(row-i)+dx][2*(col-j)+dy];
			break;

		case 7:
			row = 2 * h;
			col = h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp2[index++] = sBit[image][plane][2*(row-i)+dx][2*(col-j)+dy];
			break;

		case 8:
			row = 2 * h;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							temp2[index++] = sBit[image][plane][2*(row-j)+dx][2*(col+i)+dy];
			break;
	}

	index = 0;
	switch(area2)
	{
		case 1:
			row = h;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row-j)+dx][2*(col+i)+dy] = temp1[index++];
			break;

		case 2:
			row = 0;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row+i)+dx][2*(col+j)+dy] = temp1[index++];
			break;

		case 3:
			row = 0;
			col = h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row+i)+dx][2*(col+j)+dy] = temp1[index++];
			break;

		case 4:
			row = 0;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row+j)+dx][2*(col-i)+dy] = temp1[index++];
			break;

		case 5:
			row = h;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row+j)+dx][2*(col-i)+dy] = temp1[index++];
			break;

		case 6:
			row = 2 * h;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row-i)+dx][2*(col-j)+dy] = temp1[index++];
			break;

		case 7:
			row = 2 * h;
			col = h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row-i)+dx][2*(col-j)+dy] = temp1[index++];
			break;

		case 8:
			row = 2 * h;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row-j)+dx][2*(col+i)+dy] = temp1[index++];
			break;
	}

	index = 0;
	switch(area1)
	{
		case 1:
			row = h;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row-j)+dx][2*(col+i)+dy] = temp2[index++];
			break;

		case 2:
			row = 0;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row+i)+dx][2*(col+j)+dy] = temp2[index++];
			break;

		case 3:
			row = 0;
			col = h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row+i)+dx][2*(col+j)+dy] = temp2[index++];
			break;

		case 4:
			row = 0;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row+j)+dx][2*(col-i)+dy] = temp2[index++];
			break;

		case 5:
			row = h;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row+j)+dx][2*(col-i)+dy] = temp2[index++];
			break;

		case 6:
			row = 2 * h;
			col = 2 * h;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row-i)+dx][2*(col-j)+dy] = temp2[index++];
			break;

		case 7:
			row = 2 * h;
			col = h;
			for(int i=0; i<h; i++)
				for(int j=0; j<h-i; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row-i)+dx][2*(col-j)+dy] = temp2[index++];
			break;

		case 8:
			row = 2 * h;
			col = 0;
			for(int i=0; i<h; i++)
				for(int j=i; j<h; j++)
					for(int dx=0; dx<2; dx++)
						for(int dy=0; dy<2; dy++)
							sBit[image][plane][2*(row-j)+dx][2*(col+i)+dy] = temp2[index++];
			break;
	}

	cout << "OK\n";
}

void Image::RotateRight(int image, int plane)
{
	ChangeArea(image, plane, 1, 2);
	ChangeArea(image, plane, 1, 3);
	ChangeArea(image, plane, 1, 4);
	ChangeArea(image, plane, 1, 5);
	ChangeArea(image, plane, 1, 6);
	ChangeArea(image, plane, 1, 7);
	ChangeArea(image, plane, 1, 8);
}

void Image::RotateLeft(int image, int plane)
{
	ChangeArea(image, plane, 1, 8);
	ChangeArea(image, plane, 1, 7);
	ChangeArea(image, plane, 1, 6);
	ChangeArea(image, plane, 1, 5);
	ChangeArea(image, plane, 1, 4);
	ChangeArea(image, plane, 1, 3);
	ChangeArea(image, plane, 1, 2);
}

void Image::SaveImage(char *fname)
{
	cout << "save image [" << fname << "].....................";

	//save image
	ofstream out(fname, ios::out | ios::binary);
	if (!out)
	{
		cout << "Save file [" << fname << "] Error!\n";
		exit(1);
	}
	for(int x=0; x<iSize; x++)
		for(int y=0; y<iSize; y++)
			out.put(image[x][y]);

	out.close();

	cout << "OK\n";
}

void Image::SaveResult(char *fn1, char *fn2)
{
	cout << "save images [" << fn1 << " and " << fn2 << "].....................";

	//save images
	ofstream out1(fn1, ios::out | ios::binary);
	ofstream out2(fn2, ios::out | ios::binary);
	if (!out1)
	{
		cout << "Save file [" << fn1 << "] Error!\n";
		exit(1);
	}
	if (!out2)
	{
		cout << "Save file [" << fn2 << "] Error!\n";
		exit(1);
	}
	for(int x=0; x<iSize*2; x++)
		for(int y=0; y<iSize*2; y++)
		{
			out1.put(result[0][x][y]);
			out2.put(result[1][x][y]);
		}

	out1.close();
	out2.close();

	cout << "OK\n";
}