// logic.cpp
//  Solve logical systems
//  (c) 2005 Ryan Patterson
//  Distributed under the zlib/libpng license

#include <getopt.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <list>
#include <map>
using namespace std;

#define MAX(a, b) ((a) > (b) ? (a) : (b))

unsigned int niter = 0;
unsigned int nsets = 0;
unsigned int nmootsets = 0;

bool verbose = false;
bool htmlout = false;
ostream* html = &cout;

// Value in a variable
class Value
{
	public: // Public variables
		string name;
		class Variable* parent;
		int val;
		
	public: // Public functions
		Value(): parent(NULL), val(0)
		{ }
		Value(const string& name, class Variable* parent): name(name), parent(parent), val(0)
		{ }
		Value(const Value& other): name(other.name), parent(other.parent), val(other.val)
		{ }
		friend ostream& operator <<(ostream& lhs, const Value& rhs)
		{ lhs << rhs.name; return lhs; }
		bool operator ==(const Value& other) const
		{ return parent == other.parent && val == other.val; }
		bool operator !=(const Value& other) const
		{ return parent != other.parent || val != other.val; }
};

// Defines a variable
class Variable
{
	public: // Public static variables
		static list<Variable> vars;
		
	public: // Public static functions
		static Variable* GetVariableByName(const string& name)
		{
			for(list<Variable>::iterator i = vars.begin(); i != vars.end(); i++)
			{
				if(i->name == name)
					return &*i;
			}
			return NULL;
		}
		
	public: // Public variables
		string name;
		int nextnum;
		vector<Value> children;
		
	public: // Public functions
		Variable(): nextnum(1)
		{ }
		Variable(const Variable& other): name(other.name), nextnum(other.nextnum), children(other.children)
		{ }
		friend ostream& operator <<(ostream& lhs, const Variable& rhs)
		{ lhs << rhs.name; return lhs; }
		friend bool operator <(const Value& lhs, const Value& rhs)
		{
			if(lhs.parent != rhs.parent)
				return lhs.parent->name < rhs.parent->name;
			return lhs.val < rhs.val;
		}
		Value& operator [](int ref)
		{
			if(ref < 1 || ref > children.size())
				return children.back();
			return children[ref - 1];
		}
		bool operator ==(const Variable& rhs) const
		{ return name == rhs.name; }
		bool operator !=(const Variable& rhs) const
		{ return name != rhs.name; }
		bool operator <(const Variable& rhs) const
		{ return name < rhs.name; }
		
		int Length()
		{
			int l = name.length();
			for(vector<Value>::iterator i = children.begin(); i != children.end(); i++)
				if(i->name.length() > l) l = i->name.length();
			return l;
		}
		
		int GetValueByName(const string& name)
		{
			for(vector<Value>::iterator i = children.begin(); i != children.end(); ++i)
				if(i->name == name)
					return i->val;
			return -1;
		}
		
		void AddChild(Value& v)
		{
			v.val = nextnum++;
			v.parent = this;
			children.push_back(v);
		}
		
		bool Contains(const Value& v)
		{
			for(vector<Value>::iterator i = children.begin(); i != children.end(); ++i)
				if(*i == v)
					return true;
			return false;
		}
		class Reference GetFullReference();
};

list<Variable> Variable::vars;

// Reference to a value
class Reference
{
	public: // Public variables
		class Variable* parent;
		vector<Value> possibilities;
		
	public: // Public functions
		Reference(): parent(NULL)
		{ }
		Reference(const Reference& other): parent(other.parent), possibilities(other.possibilities)
		{ }
		Reference(const Value& other): parent(other.parent)
		{ possibilities.push_back(other); }
		
		friend ostream& operator <<(ostream& lhs, const Reference& rhs)
		{
			if(rhs.possibilities.size() == 1)
				lhs << rhs.possibilities[0];
			else
			{
				lhs << "{ ";
				bool comma = false;
				for(vector<Value>::const_iterator i = rhs.possibilities.begin(); i != rhs.possibilities.end(); i++)
				{
					if(comma) lhs << ", ";
					comma = true;
					lhs << *i;
				}
				if(comma) lhs << " ";
				lhs << "}";
			}
		}
		bool operator <(const Reference& rhs) const
		{
			return parent->name < rhs.parent->name;
		}
		Reference& operator =(const Value& other)
		{
			if(!parent || parent == other.parent)
			{
				parent = other.parent;
				possibilities.clear();
				possibilities.push_back(other);
			}
			return *this;
		}
		Reference& operator +=(const Value& other)
		{
			if(!parent || parent == other.parent)
			{
				parent = other.parent;
				for(vector<Value>::const_iterator i = possibilities.begin(); i != possibilities.end(); ++i)
				{
					if(*i == other)
						return *this;
				}
				possibilities.push_back(other);
			}
			return *this;
		}
		Reference& operator -=(const Value& other)
		{
			if(parent == other.parent)
			{
				parent = other.parent;
				for(vector<Value>::iterator i = possibilities.begin(); i != possibilities.end(); ++i)
				{
					if(*i == other)
					{
						possibilities.erase(i);
						return *this;;
					}
				}
			}
			return *this;
		}
		bool operator ==(const Reference& other) const
		{
			if(possibilities.size() != other.possibilities.size())
				return false;
			vector<Value> op = other.possibilities;
			vector<Value> p = possibilities;
			sort(op.begin(), op.end());
			sort(p.begin(), p.end());
			for(int i = 0; i < p.size(); ++i)
			{
				if(p[i] != other.possibilities[i])
					return false;
			}
			return true;
		}
		bool operator !=(const Reference& other) const
		{
			if(possibilities.size() != other.possibilities.size())
				return true;
			vector<Value> op = other.possibilities;
			vector<Value> p = possibilities;
			sort(op.begin(), op.end());
			sort(p.begin(), p.end());
			for(int i = 0; i < p.size(); ++i)
			{
				if(possibilities[i] != other.possibilities[i])
					return true;
			}
			return false;
		}
		Value& operator *()
		{
			return possibilities.front();
		}
		
		bool Certain() const
		{ return possibilities.size() == 1; }
		void Intersect(const Reference& other)
		{
			if(*parent != *other.parent)
			{
				cout << "Conflict: Intersection of two unrelated References (" << *parent << " and " << *other.parent << ")" << endl;
				if(htmlout) (*html) << "<b>Conflict:</b> Intersection of two unrelated References (" << *parent << " and " << *other.parent << ")<br/>" << endl;
				return;
			}
			vector<Value> op = other.possibilities;
			vector<Value> p = possibilities;
			possibilities.clear();
			possibilities.resize(MAX(op.size(), p.size()));
			sort(op.begin(), op.end());
			sort(p.begin(), p.end());
			vector<Value>::iterator i = set_intersection(p.begin(), p.end(), op.begin(), op.end(), possibilities.begin());
			possibilities.erase(i, possibilities.end());
		}
		bool Has(const Value& other) const
		{
			if(*parent != *other.parent)
				return false;
			for(vector<Value>::const_iterator i = possibilities.begin(); i != possibilities.end(); ++i)
			{
				if(*i == other)
					return true;
			}
			return false;
		}
};

Reference Variable::GetFullReference()
{
	Reference r;
	for(vector<Value>::iterator i = children.begin(); i != children.end(); i++)
	{
		r += *i;
	}
	return r;
}

enum ERelationshipType
{
	Equals,
	NotEquals
};

// Defines a relationship
class Relationship
{
	public: // Public static variables
		static vector<Relationship> relationships;
		
	public: // Public static functions
		static vector<Relationship> GetRelationshipsRegarding(const Value& v)
		{
			vector<Relationship> stack;
			for(vector<Relationship>::iterator i = relationships.begin(); i != relationships.end(); ++i)
			{
				if(i->var1->Contains(v) || i->var2->Contains(v))
					stack.push_back(*i);
			}
			return stack;
		}
		
	public: // Public variables
		// Variables affecting
		Variable* var1, *var2;
		// Values affected
		int val1, val2;
		// Relationship type
		ERelationshipType type;
		
	public: // Public functions
		Relationship(Value& v1, ERelationshipType type, Value& v2): val1(v1.val), val2(v2.val), var1(v1.parent), var2(v2.parent), type(type)
		{ }
		Relationship(): var1(NULL), var2(NULL), val1(0), val2(0)
		{ }
		Relationship(const Relationship& other): var1(other.var1), var2(other.var2), val1(other.val1), val2(other.val2), type(other.type)
		{ }
		friend ostream& operator <<(ostream& lhs, const Relationship& rhs)
		{
			lhs << *rhs.var1 << "." << (*rhs.var1)[rhs.val1] << " ";
			switch(rhs.type)
			{
				case Equals: lhs << "=="; break;
				case NotEquals: lhs << "!="; break;
			}
			lhs << " " << *rhs.var2 << "." << (*rhs.var2)[rhs.val2];
			return lhs;
		}
		friend istream& operator >>(istream& lhs, Relationship& rhs)
		{
			string l, o, r;
			lhs >> l >> o >> r;
			int pdot = l.find('.');
			if(pdot != string::npos)
			{
				string var = l.substr(0, pdot);
				string val = l.substr(pdot + 1);
				rhs.var1 = Variable::GetVariableByName(var);
				rhs.val1 = rhs.var1->GetValueByName(val);
			}
			else
			{
				rhs.val1 = -1;
				for(list<Variable>::iterator i = Variable::vars.begin(); i != Variable::vars.end(); i++)
				{
					rhs.val1 = i->GetValueByName(l);
					if(rhs.val1 != -1)
					{
						rhs.var1 = &*i;
						break;
					}
				}
			}
			pdot = r.find('.');
			if(pdot != string::npos)
			{
				string var = r.substr(0, pdot);
				string val = r.substr(pdot + 1);
				rhs.var2 = Variable::GetVariableByName(var);
				rhs.val2 = rhs.var2->GetValueByName(val);
			}
			else
			{
				rhs.val2 = -1;
				for(list<Variable>::iterator i = Variable::vars.begin(); i != Variable::vars.end(); i++)
				{
					rhs.val2 = i->GetValueByName(r);
					if(rhs.val2 != -1)
					{
						rhs.var2 = &*i;
						break;
					}
				}
			}
			if(o == "!=") rhs.type = NotEquals;
			else rhs.type = Equals;
			return lhs;
		}
};

vector<Relationship> Relationship::relationships;

// Holds a set of values
class Group
{
	public: // Public static variables
		static list<Group> groups;
	
	public: // Public variables
		vector<Reference> values;
		
	public: // Public variables
		Group()
		{ }
		Group(const Group& other): values(other.values)
		{ }
		friend ostream& operator <<(ostream& lhs, const Group& rhs)
		{
			bool comma = false;
			for(vector<Reference>::const_iterator i = rhs.values.begin(); i != rhs.values.end(); ++i)
			{
				if(comma) lhs << ", ";
				comma = true;
				lhs << *i->parent << "." << *i;
			}
			return lhs;
		}
		bool operator ==(const Group& other) const
		{
			bool good = false;
			for(vector<Reference>::const_iterator i = values.begin(); i != values.end(); ++i)
			{
				if(!i->Certain()) continue;
				for(vector<Reference>::const_iterator j = other.values.begin(); j != other.values.end(); ++j)
				{
					if(!j->Certain()) continue;
					if(i->parent == j->parent && *i != *j)
					{
						return false;
					}
					if(*i == *j)
						good = true;
				}
			}
			return good;
		}
		bool operator !=(const Group& other) const
		{
			for(vector<Reference>::const_iterator i = values.begin(); i != values.end(); ++i)
			{
				if(!i->Certain()) continue;
				for(vector<Reference>::const_iterator j = other.values.begin(); j != other.values.end(); ++j)
				{
					if(!j->Certain()) continue;
					if(i->parent == j->parent && *i != *j)
					{
						return true;
					}
				}
			}
			return false;
		}
		bool operator ==(const Value& other) const
		{
			for(vector<Reference>::const_iterator i = values.begin(); i != values.end(); ++i)
			{
				if(*i == other)
					return true;
			}
			return false;
		}
		Reference& operator [](const Variable& other)
		{
			for(vector<Reference>::iterator i = values.begin(); i != values.end(); ++i)
				if(*i->parent == other)
					return *i;
			return values.back();
		}
		const Reference& operator [](const Variable& other) const
		{
			for(vector<Reference>::const_iterator i = values.begin(); i != values.end(); ++i)
				if(*i->parent == other)
					return *i;
			return values.back();
		}
		Group& operator +=(const Reference& other)
		{
			for(vector<Reference>::iterator i = values.begin(); i != values.end(); ++i)
			{
				if(i->parent == other.parent)
					return *this;
			}
			values.push_back(other);
			return *this;
		}
		Group& operator +=(const Group& other)
		{
			if(*this != other)
			{
				cout << "Conflict: Merging unequal groups: " << *this << " and " << other << endl;
				if(htmlout) (*html) << "<b>Conflict:</b> Merging unequal groups!<br/>" << endl;
				return *this;
			}
			
			for(list<Variable>::iterator i = Variable::vars.begin(); i != Variable::vars.end(); ++i)
			{
				if(Has(*i) && other.Has(*i))
				{
					(*this)[*i].Intersect(other[*i]);
				}
				else if(other.Has(*i))
				{
					*this += other[*i];
				}
			}
			return *this;
		}
		
		vector<list<Group>::iterator> FindMatching() const
		{
			vector<list<Group>::iterator> matches;
			for(list<Group>::iterator i = groups.begin(); i != groups.end(); i++)
			{
				if(&*i != this && *i == *this)
					matches.push_back(i);
			}
			return matches;
		}
		void Populate()
		{
			list<Variable> vars = Variable::vars;
			for(vector<Reference>::iterator i = values.begin(); i != values.end(); ++i)
				vars.remove(*i->parent);
			for(list<Variable>::iterator i = vars.begin(); i != vars.end(); i++)
				values.push_back(i->GetFullReference());
		}
		bool Has(const Variable& other) const
		{
			for(vector<Reference>::const_iterator i = values.begin(); i != values.end(); ++i)
				if(*i->parent == other)
					return true;
			return false;
		}
};

list<Group> Group::groups;

void GroupsHTML(ostream& html)
{
	html << "<table>" << endl;
	for(list<Variable>::iterator i = Variable::vars.begin(); i != Variable::vars.end(); ++i)
	{
		html << "<tr><td><b>" << *i << "</b></td>" << endl;
		for(list<Group>::iterator j = Group::groups.begin(); j != Group::groups.end(); j++)
		{
			html << "<td valign=\"top\">";
			html << (*j)[*i];
			html << "</td>" << endl;
		}
		html << "</tr>" << endl;
	}
	html << "</table>" << endl;
}

void Merge()
{
	for(list<Group>::iterator i = Group::groups.begin(); i != Group::groups.end(); i++)
	{
		vector<list<Group>::iterator> matches = i->FindMatching();
		for(vector<list<Group>::iterator>::iterator j = matches.begin(); j != matches.end(); j++)
		{
			*i += **j;
			Group::groups.erase(*j);
		}
	}
}

void SolvePuzzle()
{
	Variable::vars.sort();
	
	if(htmlout)
	{
		(*html) << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" << endl;
		(*html) << "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">" << endl;
		(*html) << "<head>" << endl;
		(*html) << "<title>Solving puzzle</title>" << endl;
		(*html) << "<style type=\"text/css\">" << endl;
		(*html) << "body {" << endl;
		(*html) << "	background: #fff;" << endl;
		(*html) << "	color: #000;" << endl;
		(*html) << "	font-family: Verdana, sans-serif" << endl;
		(*html) << "}" << endl;
		(*html) << "td {" << endl;
		(*html) << "	border: 1px solid black;" << endl;
		(*html) << "	padding: 2px;" << endl;
		(*html) << "	margin: 0px;" << endl;
		(*html) << "}" << endl;
		(*html) << "</style>" << endl;
		(*html) << "</head>" << endl;
		(*html) << "<body>" << endl;
		(*html) << "<h1>Solving puzzle</h1>" << endl;
		(*html) << "<h2>Variables given:</h2>" << endl;
		for(list<Variable>::iterator i = Variable::vars.begin(); i != Variable::vars.end(); ++i)
		{
			(*html) << "<b>" << *i << ":</b> ";
			bool comma = false;
			for(vector<Value>::iterator j = i->children.begin(); j != i->children.end(); j++)
			{
				if(comma) (*html) << ", ";
				comma = true;
				(*html) << *j;
			}
			(*html) << "<br/>" << endl;
		}
		(*html) << "<h2>Relationships:</h2>" << endl;
		for(vector<Relationship>::iterator i = Relationship::relationships.begin(); i != Relationship::relationships.end(); ++i)
		{
			(*html) << *i << "<br/>" << endl;
		}
	}
	
	// Create a group for each value
	for(list<Variable>::iterator i = Variable::vars.begin(); i != Variable::vars.end(); ++i)
	{
		for(vector<Value>::iterator j = i->children.begin(); j != i->children.end(); j++)
		{
			Group g;
			g += *j;
			g.Populate();
			Group::groups.push_back(g);
		}
	}
	
	// Create mergable groups from equality relationships
	{
		vector<Relationship>::iterator i = Relationship::relationships.begin();
		while(i != Relationship::relationships.end())
		{
			if(i->type == Equals)
			{
				Group g;
				g += (*i->var1)[i->val1];
				g += (*i->var2)[i->val2];
				Group::groups.push_back(g);
				i = Relationship::relationships.erase(i);
			}
			else
				++i;
		}
	}
	Merge();
	
	if(htmlout)
	{
		(*html) << "<h2>Merged groups:</h2>" << endl;
		(*html) << "<table>" << endl;
		for(list<Variable>::iterator i = Variable::vars.begin(); i != Variable::vars.end(); ++i)
		{
			(*html) << "<tr><td><b>" << *i << "</b></td>" << endl;
			for(list<Group>::iterator j = Group::groups.begin(); j != Group::groups.end(); j++)
			{
				sort(j->values.begin(), j->values.end());
				(*html) << "<td valign=\"top\">";
				if((*j)[*i].Certain())
					(*html) << (*j)[*i];
				else
					(*html) << "&nbsp;" << endl;
				(*html) << "</td>" << endl;
			}
			(*html) << "</tr>" << endl;
		}
		(*html) << "</table>" << endl;
	}
	
	if(htmlout && verbose) (*html) << "<h2>Solving:</h2>" << endl;
	
	// Assign relational exclusions
	{
		vector<Relationship>::iterator i = Relationship::relationships.begin();
		while(i != Relationship::relationships.end())
		{
			if(i->type == NotEquals)
			{
				{
					Group g;
					g += (*i->var1)[i->val1];
					vector<list<Group>::iterator> match = g.FindMatching();
					for(vector<list<Group>::iterator>::iterator j = match.begin(); j != match.end(); j++)
					{
						(**j)[*i->var2] -= (*i->var2)[i->val2];
					}
				}
				{
					Group g;
					g += (*i->var2)[i->val2];
					vector<list<Group>::iterator> match = g.FindMatching();
					for(vector<list<Group>::iterator>::iterator j = match.begin(); j != match.end(); j++)
					{
						(**j)[*i->var1] -= (*i->var1)[i->val1];
					}
				}
				i = Relationship::relationships.erase(i);
			}
			else
				++i;
		}
	}
	
	// Loop until no progress is made in a single run.
	bool hadPOE = true;
	while(hadPOE)
	{
begin:
		Merge();
		if(htmlout && verbose) GroupsHTML(*html);
		hadPOE = false;
		for(list<Group>::iterator i = Group::groups.begin(); i != Group::groups.end(); i++)
		{
			for(vector<Reference>::iterator j = i->values.begin(); j != i->values.end(); j++)
			{
				if(!j->Certain()) continue;
				for(vector<Reference>::iterator k = i->values.begin(); k != i->values.end(); k++)
				{
					if(j == k) continue;
					if(!k->Certain()) continue;
					for(list<Group>::iterator l = Group::groups.begin(); l != Group::groups.end(); l++)
					{
						if(i == l) continue;
						++niter;
						if(!(*l)[*j->parent].Certain() && (*l)[*k->parent].Certain())
						{
							++nsets;
							bool moot = false;
							if(!(*l)[*j->parent].Has(**j))
							{
								moot = true; 
								++nmootsets;
							}
							if(!moot)
							{
								if(htmlout && verbose)
								{
									(*html) << "If " << *k << " = " << *j << " then " << (*l)[*k->parent] << " != " << *j << "<br/>" << endl;
								}
								
								(*l)[*j->parent] -= **j;
								if((*l)[*j->parent].Certain())
								{
									if(htmlout && verbose) (*html) << "Which means that " << (*l)[*k->parent] << "'s " << *j->parent << " must be " << (*l)[*j->parent] << "<br/>" << endl;
									hadPOE = true;
									goto begin;
								}
							}
						}
						if(!(*l)[*k->parent].Certain() && (*l)[*j->parent].Certain())
						{
							++nsets;
							bool moot = false;
							if(!(*l)[*j->parent].Has(**k))
							{
								moot = true; 
								++nmootsets;
							}
							if(!moot)
							{
								if(htmlout && verbose)
								{
									(*html) << "If " << *j << " = " << *k << " then " << (*l)[*j->parent] << " != " << *k << "<br/>" << endl;
								}
							
								(*l)[*k->parent] -= **k;
								if((*l)[*k->parent].Certain())
								{
									if(htmlout && verbose) (*html) << "Which means that " << (*l)[*j->parent] << "'s " << *k->parent << " must be " << (*l)[*k->parent] << "<br/>" << endl;
									hadPOE = true;
									goto begin;
								}
							}
						}
					}
				}
			}
		}
	}
	
	cout << "Groups:" << endl;
	for(list<Group>::iterator i = Group::groups.begin(); i != Group::groups.end(); i++)
	{
		sort(i->values.begin(), i->values.end());
		cout << *i << endl;
	}
	
	if(htmlout)
	{
		(*html) << "<h2>Final groups:</h2>" << endl;
		GroupsHTML(*html);
		
		(*html) << "<br/>" << endl;
		(*html) << "Found after " << niter << " iterations and " << nsets << " sets, " << nmootsets << " being moot.<br/>" << endl;
		
		(*html) << "</body>" << endl;
		(*html) << "</html>" << endl;
	}
}

void ReadPuzzle(istream& cin, ostream& cout)
{
	cout << "Number variables? ";
	int nvar, nopt;
	cin >> nvar;
	cout << "Number options? ";
	cin >> nopt;
	
	for(int i = 0; i < nvar; ++i)
	{
		Variable::vars.push_back(Variable());
		Variable& var = *(--Variable::vars.end());
		cout << "Variable " << i + 1 << " name? ";
		cin >> var.name;
		for(int j = 0; j < nopt; ++j)
		{
			string name;
			cout << var.name << " value " << j + 1 << "? ";
			cin >> name;
			Value val(name, &var);
			var.AddChild(val);
		}
	}
	
	int nrel;
	cout << "Number relationships? " << endl;
	cin >> nrel;
	for(int i = 0; i < nrel; i++)
	{
		Relationship blah;
		cin >> blah;
		cout << blah << endl;
		Relationship::relationships.push_back(blah);
	}
}

void usage(int argc, char* argv[])
{
	cout << "Usage:" << endl;
	cout << "  " << argv[0] << " [options]" << endl;
	cout << endl;
	cout << "Options:" << endl;
	cout << "  -f, --file=FILE    Use FILE for reading from" << endl;
	cout << "  -o, --output=FILE  Use FILE for html output" << endl;
	cout << "  -v, --verbose      Print a detailed breakdown of the problem-solving process" << endl;
	cout << "  -?, --help         Print this message" << endl;
}

int main(int argc, char* argv[])
{
	bool deletein = false;
	istream* in = &cin;
	ostream* out = &cout;
	
	ostream* htmlout = NULL;
	
	while(1)
	{
		static struct option long_options[] = {
			{ "file", 1, 0, 'f' },
			{ "output", 1, 0, 'o' },
			{ "verbose", 0, 0, 'v' },
			{ "help", 0, 0, '?' },
			{ 0, 0, 0, 0 }
		};

		int c = getopt_long(argc, argv, "f:o:v", long_options, NULL);
		if(c == -1)
			break;

		switch (c)
		{
			case 'f':
				deletein = true;
				in = new ifstream(optarg);
				out = new ofstream;
				break;
				
			case 'o':
				htmlout = new ofstream(optarg);
				::htmlout = true;
				::html = htmlout;
				break;
				
			case 'v':
				verbose = true;
				break;
	
			case '?':
			default:
				usage(argc, argv);
				return 1;
		}
	}
	
	ReadPuzzle(*in, *out);
	
	if(deletein)
	{
		delete in;
		delete out;
	}
	
	SolvePuzzle();
	
	return 0;
}

// The end
