// Simple Sudoku solver
// Copyright 2005 Steinar H. Gunderson. GPLv2.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <queue>
#include <set>

#define WIDTH  9
#define HEIGHT 9
#define XGRAN  3
#define YGRAN  3
#define NUM    (XGRAN*YGRAN)

bool board[WIDTH * HEIGHT][NUM];
int passes = 0;

char *current_pass;
int num_current_pass;

void print_board()
{
	printf("+");
	for (unsigned x = 0; x < WIDTH; ++x) {
		for (unsigned xx = 0; xx < XGRAN; ++xx) {
			printf("-");
		}
		printf("+");
	}
	printf("\n");

	for (unsigned y = 0; y < HEIGHT; ++y) {
		for (unsigned yy = 0; yy < YGRAN; ++yy) {
			printf("|");
			for (unsigned x = 0; x < WIDTH; ++x) {
				for (unsigned xx = 0; xx < XGRAN; ++xx) {
					if (board[y * WIDTH + x][yy * YGRAN + xx]) {
						printf("%x", yy * YGRAN + xx + 1);
					} else {
						printf(" ");
					}
				}
				printf("|");
			}
			printf("\n");
		}
		printf("+");
		for (unsigned x = 0; x < WIDTH; ++x) {
			for (unsigned xx = 0; xx < XGRAN; ++xx) {
				printf("-");
			}
			printf("+");
		}
		printf("\n");
	}
	
	printf("\n");
}

bool print_sol()
{
	bool complete = true;
	
	for (unsigned y = 0; y < HEIGHT; ++y) {
		for (unsigned x = 0; x < WIDTH; ++x) {
			unsigned n = 0, k = 0;
			for (unsigned i = 0; i < NUM; ++i) {
				if (board[y * WIDTH + x][i]) {
					++n;
					k = i;
				}
			}

			if (n == 1) {
				printf("%x", k + 1);
			} else {
				printf("-");
				complete = false;
			}
		}
		printf("\n");
	}

	return complete;
}

// are the opportunities in cell 2 a subset of cell 1? (doesn't have to
// be a true subset, equality is fine)
bool cell_is_subset(unsigned x1, unsigned y1, unsigned x2, unsigned y2)
{
	for (unsigned i = 0; i < NUM; ++i) {
		if (!board[y1 * WIDTH + x1][i] && board[y2 * WIDTH + x2][i])
			return false;
	}
	return true;
}

void start_pass(char *name)
{
	current_pass = name;
	num_current_pass = 0;
}

void impossible(unsigned x, unsigned y, unsigned i)
{
	if (board[y * WIDTH + x][i]) {
		++num_current_pass;
	}
	board[y * WIDTH + x][i] = false;
}

bool end_pass()
{
	if (num_current_pass > 0) {
		printf("'%s' eliminated %u\n", current_pass, num_current_pass);
		//print_board();
		return true;
	}

	return false;
}

void block_propagation()
{
	// block propagation
	for (unsigned yb = 0; yb < HEIGHT/YGRAN; ++yb) {
		for (unsigned xb = 0; xb < WIDTH/XGRAN; ++xb) {
			for (unsigned y = 0; y < YGRAN; ++y) {
				for (unsigned x = 0; x < XGRAN; ++x) {
					unsigned yy = yb*YGRAN + y;
					unsigned xx = xb*XGRAN + x;
					
					for (unsigned i = 0; i < NUM; ++i) {
						if (!board[yy * WIDTH + xx][i])
							continue;
						
						// if somebody can't be anything but I, we can't
						for (unsigned y2 = 0; y2 < YGRAN; ++y2) {
							for (unsigned x2 = 0; x2 < XGRAN; ++x2) {
								unsigned yy2 = yb*YGRAN + y2;
								unsigned xx2 = xb*XGRAN + x2;

								if (yy2 == yy && xx2 == xx)
									continue;
								
								for (unsigned j = 0; j < NUM; ++j) {
									if (i == j && !board[yy2 * WIDTH + xx2][j])
										goto flexible;
									if (i != j && board[yy2 * WIDTH + xx2][j])
										goto flexible;
								}
								impossible(xx, yy, i);
								goto next;
flexible:						
								1;
							}
						}
next:
						1;
					}
				}	
			}
		} 
	}
}

void block_propagation2()
{
	for (unsigned yb = 0; yb < HEIGHT/YGRAN; ++yb) {
		for (unsigned xb = 0; xb < WIDTH/XGRAN; ++xb) {
			for (unsigned y = 0; y < YGRAN; ++y) {
				for (unsigned x = 0; x < XGRAN; ++x) {
					unsigned yy = yb*YGRAN + y;
					unsigned xx = xb*XGRAN + x;
					
					for (unsigned i = 0; i < NUM; ++i) {
						if (!board[yy * WIDTH + xx][i])
							continue;
						
						// if nobody else can be I, we can't be anything else
						for (unsigned y2 = 0; y2 < YGRAN; ++y2) {
							for (unsigned x2 = 0; x2 < XGRAN; ++x2) {
								unsigned yy2 = yb*YGRAN + y2;
								unsigned xx2 = xb*XGRAN + x2;

								if (yy2 == yy && xx2 == xx)
									continue;
								
								if (board[yy2 * WIDTH + xx2][i])
									goto no_elim;
							}
						}

						for (unsigned j = 0; j < NUM; ++j) {
							if (i == j || !board[yy * WIDTH + xx][j])
								continue;
							impossible(xx, yy, j);
						} 
no_elim:						
						1;
					}
				}	
			}
		} 
	}
}

void block_elimination()
{
	for (unsigned y = 0; y < WIDTH; ++y) {
		for (unsigned x = 0; x < WIDTH; ++x) {
			// find cells in the same block matching this one exactly
			unsigned nmatch = 0, ncnt = 0;
			
			for (unsigned i = 0; i < NUM; ++i) {
				if (board[y * WIDTH + x][i])
					++ncnt;
			}

			if (ncnt == 1)
				continue;  // taken by other logic

			unsigned yb = y/YGRAN;
			unsigned xb = x/XGRAN;
			
			for (unsigned y2 = 0; y2 < YGRAN; ++y2) {
				for (unsigned x2 = 0; x2 < XGRAN; ++x2) {
					if (cell_is_subset(x, y, xb*XGRAN + x2, yb*YGRAN + y2)) {
						++nmatch;
					}
				}
			}

			if (nmatch != ncnt)
				continue;
			
			for (unsigned i = 0; i < NUM; ++i) {
				if (!board[y * WIDTH + x][i])
					continue;
				
				for (unsigned y2 = 0; y2 < YGRAN; ++y2) {
					for (unsigned x2 = 0; x2 < XGRAN; ++x2) {
						if (!board[(yb*YGRAN + y2) * WIDTH + (xb*XGRAN + x2)][i])
							continue;
						if (cell_is_subset(x, y, xb*XGRAN + x2, yb*YGRAN + y2)) 
							continue;
					
						impossible(xb*XGRAN + x2, yb*YGRAN + y2, i);
					}
				}
			}
		} 
	}
}

void horizontal_propagation()
{
	for (unsigned y = 0; y < HEIGHT; ++y) {
		for (unsigned x = 0; x < WIDTH; ++x) {
			unsigned tyb = y/YGRAN;
			unsigned txb = x/XGRAN;

			for (unsigned i = 0; i < NUM; ++i) {
				if (!board[y * WIDTH + x][i])
					continue;
				
				for (unsigned xb = 0; xb < WIDTH/XGRAN; ++xb) {
					if (xb == txb)
						continue;

					// check if anything not on this line can be I
					for (unsigned yy = 0; yy < YGRAN; ++yy) {
						unsigned y2 = tyb * YGRAN + yy;
						if (y2 == y)
							continue;

						for (unsigned xx = 0; xx < XGRAN; ++xx) {
							unsigned x2 = xb * XGRAN + xx;
							if (board[y2 * WIDTH + x2][i])
								goto no_go;
						}
					}

					impossible(x, y, i);
no_go:
					1;
				}
			}
		}
	}
}

void horizontal_propagation2()
{
	for (unsigned y = 0; y < WIDTH; ++y) {
		for (unsigned x = 0; x < WIDTH; ++x) {
			for (unsigned i = 0; i < NUM; ++i) {
				if (!board[y * WIDTH + x][i])
					continue;
						
				// if nobody else can be I, we can't be anything else
				for (unsigned x2 = 0; x2 < WIDTH; ++x2) {
					if (x2 == x)
						continue;
								
					if (board[y * WIDTH + x2][i])
						goto no_elim_h;
				}

				for (unsigned j = 0; j < NUM; ++j) {
					if (i == j || !board[y * WIDTH + x][j])
						continue;
					impossible(x, y, j);
				} 
no_elim_h:						
				1;
			}
		} 
	}
}

void horizontal_elimination()
{
	for (unsigned y = 0; y < WIDTH; ++y) {
		for (unsigned x = 0; x < WIDTH; ++x) {
			// find cells on the same row matching this one exactly
			unsigned nmatch = 0, ncnt = 0;
			
			for (unsigned i = 0; i < NUM; ++i) {
				if (board[y * WIDTH + x][i])
					++ncnt;
			}

			if (ncnt == 1)
				continue;  // taken by other logic

			for (unsigned x2 = 0; x2 < WIDTH; ++x2) {
				if (cell_is_subset(x, y, x2, y)) {
					++nmatch;
				}
			}

			if (nmatch != ncnt)
				continue;
			
			for (unsigned i = 0; i < NUM; ++i) {
				if (!board[y * WIDTH + x][i])
					continue;
				
				for (unsigned x2 = 0; x2 < WIDTH; ++x2) {
					if (!board[y * WIDTH + x2][i])
						continue;
					if (cell_is_subset(x, y, x2, y))
						continue;
					
					impossible(x2, y, i);
				}
			}
		} 
	}
}

void vertical_propagation()
{
	for (unsigned y = 0; y < HEIGHT; ++y) {
		for (unsigned x = 0; x < WIDTH; ++x) {
			unsigned tyb = y/YGRAN;
			unsigned txb = x/XGRAN;

			for (unsigned i = 0; i < NUM; ++i) {
				if (!board[y * WIDTH + x][i])
					continue;
				
				for (unsigned yb = 0; yb < HEIGHT/YGRAN; ++yb) {
					if (yb == tyb)
						continue;

					// check if anything not on this column can be I
					for (unsigned xx = 0; xx < XGRAN; ++xx) {
						unsigned x2 = txb * XGRAN + xx;
						if (x2 == x)
							continue;

						for (unsigned yy = 0; yy < YGRAN; ++yy) {
							unsigned y2 = yb * YGRAN + yy;
							if (board[y2 * WIDTH + x2][i])
								goto no_go_v;
						}
					}

					impossible(x, y, i);
no_go_v:
					1;
				}
			}
		}
	}
}

void vertical_propagation2()
{
	for (unsigned y = 0; y < WIDTH; ++y) {
		for (unsigned x = 0; x < WIDTH; ++x) {
			for (unsigned i = 0; i < NUM; ++i) {
				if (!board[y * WIDTH + x][i])
					continue;
						
				// if nobody else can be I, we can't be anything else
				for (unsigned y2 = 0; y2 < HEIGHT; ++y2) {
					if (y2 == y)
						continue;
								
					if (board[y2 * WIDTH + x][i])
						goto no_elim_v;
				}

				for (unsigned j = 0; j < NUM; ++j) {
					if (i == j || !board[y * WIDTH + x][j])
						continue;
					impossible(x, y, j);
				} 
no_elim_v:
				1;
			}
		} 
	}
}

void vertical_elimination()
{
	for (unsigned y = 0; y < WIDTH; ++y) {
		for (unsigned x = 0; x < WIDTH; ++x) {
			// find cells on the same row matching this one exactly
			unsigned nmatch = 0, ncnt = 0;
			
			for (unsigned i = 0; i < NUM; ++i) {
				if (board[y * WIDTH + x][i])
					++ncnt;
			}

			if (ncnt == 1)
				continue;  // taken by other logic

			for (unsigned y2 = 0; y2 < WIDTH; ++y2) {
				if (cell_is_subset(x, y, x, y2)) {
					++nmatch;
				}
			}

			if (nmatch != ncnt)
				continue;
			
			for (unsigned i = 0; i < NUM; ++i) {
				if (!board[y * WIDTH + x][i])
					continue;
				
				for (unsigned y2 = 0; y2 < HEIGHT; ++y2) {
					if (!board[y2 * WIDTH + x][i])
						continue;
					if (cell_is_subset(x, y, x, y2))
						continue;
					
					impossible(x, y2, i);
				}
			}
		} 
	}
}

bool dead_end()
{
	for (unsigned y = 0; y < HEIGHT; ++y) {
		for (unsigned x = 0; x < WIDTH; ++x) {
			bool ok = false;
			for (unsigned i = 0; i < NUM; ++i) {
				if (board[y * WIDTH + x][i]) {
					ok = true;
					break;
				}
			}
			if (!ok)
				return true;
		}
	}

	return false;
}

bool solve()
{
	//print_board();
	bool done_anything = false;

	start_pass("Block propagation 1");
	block_propagation();
	done_anything |= end_pass();
	
	start_pass("Block propagation 2");
	block_propagation2();
	done_anything |= end_pass();

	start_pass("Block elimination");
	block_elimination();
	done_anything |= end_pass();

	start_pass("Horizontal propagation 1");
	horizontal_propagation();
	done_anything |= end_pass();
	
	start_pass("Horizontal propagation 2");
	horizontal_propagation2();
	done_anything |= end_pass();

	start_pass("Horizontal elimination");
	horizontal_elimination();
	done_anything |= end_pass();

	start_pass("Vertical propagation 1");
	vertical_propagation();
	done_anything |= end_pass();

	start_pass("Vertical propagation 2");
	vertical_propagation2();
	done_anything |= end_pass();

	start_pass("Vertical elimination");
	vertical_elimination();
	done_anything |= end_pass();

	++passes;

	if (dead_end())
		return false;

	if (done_anything)
		return solve();   // always on we march
	
	// oops, not done anything, and we're not in a self-
	// contradiction either. need to pick a guess?
	unsigned best_x = 0, best_y = 0, best_pos = NUM + 1;

	for (unsigned y = 0; y < HEIGHT; ++y) {
		for (unsigned x = 0; x < WIDTH; ++x) {
			unsigned pos = 0;
			for (unsigned i = 0; i < NUM; ++i) {
				if (board[y * WIDTH + x][i])
					++pos;
			}

			if (pos > 1 && pos < best_pos) {
				best_pos = pos;
				best_x = x;
				best_y = y;
			}
		}
	}

	if (best_pos == NUM + 1)
		return true;

	// uncomment to enable backtracking
#if 0
	bool board_copy[WIDTH * HEIGHT][NUM];
	memcpy(board_copy, board, (WIDTH*HEIGHT)*NUM*sizeof(bool));
	
	for (unsigned i = 0; i < NUM; ++i) {
		if (!board[best_y * WIDTH + best_x][i])
			continue;
		
		for (unsigned j = 0; j < NUM; ++j) {
			if (i == j)
				continue;
			
			board[best_y * WIDTH + best_x][j] = false;
		}
		if (solve())
			return true;
		
		memcpy(board, board_copy, (WIDTH*HEIGHT)*NUM*sizeof(bool));
	}
#endif

	return false;
}

int main()
{
	for (unsigned y = 0; y < HEIGHT; ++y) 
		for (unsigned x = 0; x < WIDTH; ++x) 
			for (unsigned i = 0; i < NUM; ++i) 
				board[y * WIDTH + x][i] = true;
			
	for (unsigned y = 0; y < HEIGHT; ++y) {
		char buf[256];
		if (fgets(buf, 256, stdin) == NULL) {
			fprintf(stderr, "premature eof\n");
			exit(1);
		}
		if (strlen(buf) != WIDTH + 1) {
			fprintf(stderr, "malformed line %u\n", y);
			exit(1);
		}
		for (unsigned x = 0; x < WIDTH; ++x) {
			char ch = buf[x];
			if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'G') {
				unsigned n;
				if (ch >= '0' && ch <= '9')
					n = (ch - '0');
				else
					n = 10 + (ch - 'A');
				
				for (unsigned i = 0; i < NUM; ++i) {
					if (i == n-1)
						continue;
					board[y * WIDTH + x][i] = false;
				}
			} else if (ch == '-') {
			} else {
				fprintf(stderr, "illegal char on line %u\n", y);
				exit(1);
			}
		}
	}
	
	bool ok = solve();
	print_sol();

	if (ok) {
		printf("\n%u passes\n", passes);
	} else {
		printf("\n%u passes (incomplete)\n", passes);
		print_board();
	}
}
