#include <cstdlib>
#include <iostream>
#include <math.h>
#include <vector>
#include <strstream>
using namespace std;

void shift();
int getLetterVal(char);
void setLetterVal(char, int);
int getValueOfLine(char*);
bool isSolutionFound();
void setOrder();
bool alreadyOrdered(char);
void fillInOrder();
bool charIsUsed(int);
void printPuzzle();
void printPuzzle2();
bool checkIt(int, int);
void checkRules(); 
bool isNumberAvailable(int);
void removePossibleVal(char, int);
void removeOtherPossibleVal(char, int);
void nailPossibility(char, int);
void init(char *argv[]);

//char *top, *mid, *low;
char top[10] = ".";
char mid[10] = ".";
char low[10] = ".";
int topLength=0, midLength=0, lowLength=0, maxLength=0, minLength=0;
int letterVals[26];
int solutions[100][26];
int *t;
vector <int> possibleVals[26];
vector <char> order;
vector <char> charsUsed;
vector <int> firstSolutionFound;
int numberChecked = 0, numberFound = 0;
bool solutionFound = false;


// Returns if a character is used or not (function not currently called)
bool charIsUsed(int A) {
	int j;
	for(j=0; j<charsUsed.size(); j++)
		if(charsUsed[j] == (char)(A+65))
			return true;
	return false;
}

// Checks current letter values to determine if they offer a solution
bool isSolutionFound() {
	int j,i;
	for(j=0; j<charsUsed.size(); j++)
		for(i=0; i<charsUsed.size(); i++)
			if(t[charsUsed[j]-65] == t[charsUsed[i]-65] && j!=i)
				return false;
	if(getValueOfLine(top) + getValueOfLine(mid) != getValueOfLine(low))
		return false;
	return true;
}

// Returns if the far 3 digits add up
bool checkFirst() {
	return ((t[top[maxLength-1]-65] + t[mid[maxLength-1]-65])%10 == t[low[maxLength-1]-65]);
}

// Returns if Ith order number is available. True = good number | False = try again
bool isNumberAvailable(int I) {
	int j;
	if(I > charsUsed.size()-1)
		return true;
	for(j=0; j<I && j<charsUsed.size()-1; j++)
		if(t[order[I]-65] == t[order[j]-65])
			return false;
	return true;
}


// Checks to see if the current temporary values meet criteria
// Returns true if: changing a variable that doesn't matter
//				  : first numbers add up (only for I=0)
//				  : if numberIsAvailable 
bool checkIt(int I, int J) {
	if(I == 0)
		return checkFirst();

	if(!isNumberAvailable(J))
		return false;

	if(I == 1 && minLength > 1)
	{
		int cin = (int)(t[top[maxLength-I]-65] + t[mid[maxLength-I]-65])/10;
		if((t[top[maxLength-1-I]-65] + t[mid[maxLength-1-I]-65] + cin)%10 != t[low[maxLength-1-I]-65])
			return false;
	}

	if(I == 2 && minLength > 2)
	{
		I = 1;
		int cin = (int)((t[top[maxLength-I]-65] + t[mid[maxLength-I]-65])/10);
		int cin2 =(int)((t[top[maxLength-I-1]-65]+t[mid[maxLength-I-1]-65] + cin)/10);
		if((t[top[maxLength-2-I]-65] + t[mid[maxLength-2-I]-65] + cin2)%10 != t[low[maxLength-2-I]-65])
			return false;
	}

	if(I == 3 && minLength > 3)
	{
		I = 1;
		int cin = (int)((t[top[maxLength-I]-65] + t[mid[maxLength-I]-65])/10);
		int cin2 =(int)((t[top[maxLength-I-1]-65]+t[mid[maxLength-I-1]-65] + cin)/10);
		int cin3 =(int)((t[top[maxLength-I-2]-65]+t[mid[maxLength-I-2]-65] + cin2)/10);
		if((t[top[maxLength-3-I]-65] + t[mid[maxLength-3-I]-65] + cin3)%10 != t[low[maxLength-3-I]-65])
			return false;
	}

	return true;
}

// Brute force with recurring checks to eliminate branches
int bruteForceIt() {
	int i;
	t = (int*)malloc(sizeof(int)*26);
	int *j = (int*)malloc(sizeof(int)*26);
	int *J = (int*)malloc(sizeof(int)*26);

//	cout << "\nPrinting all answers. Each row of 26 digits is an answer." << endl << endl;

//	for(i=0; i<26; i++)
//		cout << (char)(i+65);
//	cout << endl;

	for(j[0]=1, t[order[0]-65]=possibleVals[order[0]-65][0]; j[0] <= possibleVals[order[0]-65].size(); t[order[0]-65]=possibleVals[order[0]-65][j[0]++])
	for(j[1]=1, t[order[1]-65]=possibleVals[order[1]-65][0]; j[1] <= possibleVals[order[1]-65].size(); t[order[1]-65]=possibleVals[order[1]-65][j[1]++])
		if(isNumberAvailable(1))
	for(j[2]=1, t[order[2]-65]=possibleVals[order[2]-65][0]; j[2] <= possibleVals[order[2]-65].size(); t[order[2]-65]=possibleVals[order[2]-65][j[2]++])
		if(checkIt(0,2))
	for(j[3]=1, t[order[3]-65]=possibleVals[order[3]-65][0]; j[3] <= possibleVals[order[3]-65].size(); t[order[3]-65]=possibleVals[order[3]-65][j[3]++])
		if(isNumberAvailable(3))
	for(j[4]=1, t[order[4]-65]=possibleVals[order[4]-65][0]; j[4] <= possibleVals[order[4]-65].size(); t[order[4]-65]=possibleVals[order[4]-65][j[4]++])
		if(isNumberAvailable(4))
	for(j[5]=1, t[order[5]-65]=possibleVals[order[5]-65][0]; j[5] <= possibleVals[order[5]-65].size(); t[order[5]-65]=possibleVals[order[5]-65][j[5]++])
		if(checkIt(1,5))
	for(j[6]=1, t[order[6]-65]=possibleVals[order[6]-65][0]; j[6] <= possibleVals[order[6]-65].size(); t[order[6]-65]=possibleVals[order[6]-65][j[6]++])
		if(isNumberAvailable(6))
	for(j[7]=1, t[order[7]-65]=possibleVals[order[7]-65][0]; j[7] <= possibleVals[order[7]-65].size(); t[order[7]-65]=possibleVals[order[7]-65][j[7]++])
		if(isNumberAvailable(7))
	for(j[8]=1, t[order[8]-65]=possibleVals[order[8]-65][0]; j[8] <= possibleVals[order[8]-65].size(); t[order[8]-65]=possibleVals[order[8]-65][j[8]++])
		if(checkIt(2,8))	
	for(j[9]=1, t[order[9]-65]=possibleVals[order[9]-65][0]; j[9] <= possibleVals[order[9]-65].size(); t[order[9]-65]=possibleVals[order[9]-65][j[9]++])
		if(checkIt(3,9))	
		if(isNumberAvailable(9))
	{
		numberChecked++;

		if(isSolutionFound())
		{
			for(i=0; i<10; i++)
				letterVals[order[i]-65] = t[order[i]-65];
			for(i=0; i<26; i++)
				cout << letterVals[i];
			cout << "\n";


			if(numberFound < 100)
			{
				for(i=0; i<26; i++)
					solutions[numberFound][i] = (char)letterVals[i];
			}
			numberFound++;
		}	
	}
	if(numberFound == 0)
		return 0;
	return 1;
}

// Returns the value of letter c
int getLetterVal(char C) {
	return t[(int)C - 65];
}

// Sets the value of letter c (never called)
void setLetterVal(char C, int i) {
	t[(int)C - 65] = i;
}

// Returns the value of one of the lines
int getValueOfLine(char* A) {
	int j, result=0;
	for(j=maxLength; j>=0; j--)
	{
		if(A[j] != NULL && A[j] != ' ')
			result += getLetterVal(A[j])*(int)(pow((float)10,(float)maxLength-j-1));
	}
	return result;
}

void die()
{
	exit(0);
}

int main(int argc, char* argv[]) {


	if(argc < 4)
		die();

	init(argv);

	bruteForceIt();
	return 0;
}

// Initialization: Gets input, shifts it to align right. 
//		Sets the order of characters. Checks for general reduction rules.
//		Sets possible values for each char
void init(char *argv[]) {
	int j;

	for(j=0; j<26; j++)
	{
		letterVals[j]=NULL;
		possibleVals[j].push_back(0);
	}

	j=0;
	while(argv[1][j] != NULL)
	{
		top[j] = argv[1][j++];
		topLength++;
	}
	j=0;
	while(argv[2][j] != NULL)
	{
		mid[j] = argv[2][j++];
		midLength++;
	}
	j=0;
	while(argv[3][j] != NULL)
	{
		low[j] = argv[3][j++];
		lowLength++;
	}
	

	shift();
	setOrder();
	checkRules();

	for(j=0; j<26; j++)
		letterVals[j] = 0;
}

void checkRules() {
	if(lowLength > topLength && lowLength > midLength)
	{
		possibleVals[low[0]-65].clear();
		possibleVals[low[0]-65].push_back(1);

		if(topLength < midLength)
		{
			nailPossibility(mid[maxLength-1 - topLength], 9);
			nailPossibility(low[maxLength-1 - topLength], 0);
		}
		else if(midLength < topLength)
		{
			nailPossibility(top[maxLength-1 - topLength], 9);
			nailPossibility(low[maxLength-1 - topLength], 0);
		}
	}

	else if(topLength == midLength && midLength == lowLength)
		removePossibleVal(low[0], 0);

	if(low[maxLength-1] == top[maxLength-1])
		nailPossibility(mid[maxLength-1], 0);
	if(low[maxLength-1] == mid[maxLength-1])
		nailPossibility(top[maxLength-1], 0);

	if(low[maxLength-1] != mid[maxLength-1])
		if(low[maxLength-1] != top[maxLength-1])
			if(mid[maxLength-1] != top[maxLength-1])
			{
				removePossibleVal(top[maxLength-1], 0);
				removePossibleVal(mid[maxLength-1], 0);
			}
	removePossibleVal(top[maxLength-topLength], 0);
	removePossibleVal(mid[maxLength-midLength], 0);
	removePossibleVal(low[maxLength-lowLength], 0);
}

void nailPossibility(char C, int I) {
	possibleVals[C-65].clear();
	possibleVals[C-65].push_back(I);
	removeOtherPossibleVal(C, I);
}

void removeOtherPossibleVal(char C, int I) {
	int j;
	for(j=0; j<charsUsed.size(); j++)
		if(charsUsed[j] != C)
			removePossibleVal(charsUsed[j], I);
}

void removePossibleVal(char C, int I) {
	vector<int> temp;
	int y,j;
	for(j=0; j < possibleVals[C-65].size(); j++)
	{
		y = possibleVals[C-65][j];
		if(y != I)
			temp.push_back(y);
	}
	possibleVals[C-65].clear();
	for(j=0; j < temp.size(); j++)
		possibleVals[C-65].push_back(temp[j]);
	temp.clear();
}

void shift() {
	int j,i,diff;
	maxLength = topLength;
	if(midLength > maxLength)
		maxLength = midLength;
	if(lowLength > maxLength)
		maxLength = lowLength;
	minLength = topLength;
	if(midLength > maxLength)
		minLength = midLength;

	{
		diff = maxLength-topLength;
		for(j=topLength-1; j>=0; j--)
		{
			if(top[j] >= 97 && top[j] <= 122)
				top[j] -= 32;
			top[j+diff] = top[j];
			possibleVals[(int)top[j]-65].clear();
			for(i=0; i<10; i++)
				possibleVals[(int)top[j]-65].push_back(i);
		}
		for(j=0; j<diff; j++)
			top[j] = ' ';
	}
	{
		diff = maxLength-midLength;
		for(j=midLength-1; j>=0; j--)
		{
			if(mid[j] >= 97 && mid[j] <= 122)
				mid[j] -= 32;
			mid[j+diff] = mid[j];
			possibleVals[(int)mid[j]-65].clear();
			for(i=0; i<10; i++)
				possibleVals[(int)mid[j]-65].push_back(i);
		}
		for(j=0; j<diff; j++)
			mid[j] = ' ';
	}
	{
		diff = maxLength-lowLength;
		for(j=lowLength-1; j>=0; j--)
		{
			if(low[j] >= 97 && low[j] <= 122)
				low[j] -= 32;
			low[j+diff] = low[j];
			possibleVals[(int)low[j]-65].clear();
			for(i=0; i<10; i++)
				possibleVals[(int)low[j]-65].push_back(i);
		}
		for(j=0; j<diff; j++)
			low[j] = ' ';
	}
}

void setOrder() {
	int j;
	for(j=maxLength-1; j>=0; j--)
	{
		if(top[j] != NULL && !alreadyOrdered(top[j]) && top[j] != ' ')
		{
			order.push_back(top[j]);
			charsUsed.push_back(top[j]);
		}
		if(mid[j] != NULL && !alreadyOrdered(mid[j]) && mid[j] != ' ')
		{
			order.push_back(mid[j]);
			charsUsed.push_back(mid[j]);
		}
		if(low[j] != NULL && !alreadyOrdered(low[j]) && low[j] != ' ')
		{
			order.push_back(low[j]);
			charsUsed.push_back(low[j]);
		}
	}
	fillInOrder();
}

// Adds the rest of the chars to the order
void fillInOrder() {
	int j;
	for(j=65; j<=90; j++)
		if(!alreadyOrdered((char)j))
			order.push_back((char)j);
}

// Returns true if C has already been ordered
bool alreadyOrdered(char C) {
	int j;
	for(j=0; j<order.size(); j++)
		if(order[j] == C)
			return true;
	return false;
}

// Prints puzzle
void printPuzzle() {
	int j;
	cout << endl;
	cout << "  " << top << endl;
	cout << "+ " << mid << endl;
	for(j=0; j<maxLength+2; j++)
		cout << "-";
	cout << endl;
	cout << "  " << low << endl;
	cout << endl;
}

// Prints puzzle with values of first found solution
void printPuzzle2() {
	int j;
	cout << endl;
	cout << "  " << top << "    ";
	for(j=topLength; j < maxLength; j++)
		cout << " ";
	cout << "  " << getValueOfLine(top) << endl;
	cout << "+ " << mid << "    ";
	for(j=midLength; j < maxLength; j++)
		cout << " ";
	cout << "+ " << getValueOfLine(mid) << endl;
	for(j=0; j<maxLength+2; j++)
		cout << "-";
	cout << "    ";
	for(j=0; j<maxLength+2; j++)
		cout << "-";
	cout << endl;
	cout << "  " << low << "      ";
	cout << getValueOfLine(low) << endl;
	cout << endl;
}

