<commit_before>#define RUN_ME_WITH_SH /*
g++ -Wall -std=c++14 -Ofast -DIL_STD -DVERBOSE -o ${0%.*} ${0}          \
	-I/opt/ibm/ILOG/CPLEX_Studio1261/cplex/include                       \
	-I/opt/ibm/ILOG/CPLEX_Studio1261/concert/include                     \
	-L/opt/ibm/ILOG/CPLEX_Studio1261/cplex/lib/x86-64_linux/static_pic   \
	-L/opt/ibm/ILOG/CPLEX_Studio1261/concert/lib/x86-64_linux/static_pic \
	-lilocplex     \
	-lcplex        \
	-lcplexdistmip \
	-lconcert      \
	-lpthread      \
	-lm
exit
*/
#include <ilcplex/ilocplex.h>
#include <cstdlib>
#include <vector>
#include <sstream>
#include <fstream>
#include <string>
#include <queue>

using namespace std;

int minNumCross = 3;
int maxRectSize = 12;

vector< IloConstraint > constraints;

int numCross;
int numBlank;

typedef pair< vector< int >, pair< int, int > > Base;

Base initialBase;

int numNodesX, numNodesY, numNodes;
vector< int > const * baseSet;

Base ConstructBase( vector< int > const & base, int width, int height ) {
	return make_pair( base, make_pair( width, height ) );
}

void LoadBase( char const * fileName ) {
	FILE * fin = fopen( fileName, "rb" );
	int a, b, c, d;
	fscanf( fin, "%d %d %d %d\n", &a, &b, &c, &d );
	char rc;
	for ( int i = 0; i < (b+1)*a; ++i ) {
		fscanf( fin, "%c", &rc );
		if ( rc == '\n' ) continue;
		if ( rc == 'H' ) {   initialBase.first.push_back( 1 );   ++numCross;   }
		else             {   initialBase.first.push_back( 0 );   ++numBlank;   }
	}
	initialBase.second.first  = b;
	initialBase.second.second = a;
	minNumCross = c;
	maxRectSize = d;

	printf( "Blank: %d, total: %d, ratio: %lf\n", numCross, numCross+numBlank, numCross/(double)(numCross+numBlank) );
}

vector< int > CopySubMatrix( vector< int > const & base, int baseW, int x, int y, int w, int h ) {
	vector< int > res;
	for ( int i = x; i < x+w; ++i ) {
		for ( int j = y; j < y+h; ++j ) {
			res.push_back( base[i + baseW*j] );
		}
	}
	return res;
}

vector< Base > DivideBase( Base const & baseSet, int numX, int numY ) {
	int w = baseSet.second.first;
	int h = baseSet.second.second;
	int wD = w / numX;
	int hD = h / numY;

	vector< Base > bases;
	for ( int i = 0; i < numX; ++i ) {
		for ( int j = 0; j < numY; ++j ) {
			int curX = i*wD, curY = j*hD;
			int curW, curH;
			if ( i == numX-1 ) {
				if ( j == numY-1 )     { curW = w-(i*wD);   curH = h-(j*hD);   }
				else                   { curW = w-(i*wD);   curH =      hD;    }
			} else if ( j == numY-1 ) { curW =      wD;    curH = h-(j*hD);   }
			else                      { curW =      wD;    curH =      hD;    }
			bases.push_back( ConstructBase( CopySubMatrix( baseSet.first, w, curX, curY, curW, curH ), curW, curH ) );
		}
	}
	return bases;
}

void AddConstraint( IloModel & model, IloConstraint const & constraint ) {
	model.add            ( constraint );
	constraints.push_back( constraint );
}

IloBoolVar AddMultConstraints( IloEnv & env, IloModel & model, IloBoolVar const & v0, IloBoolVar const & v1 ) {
	IloBoolVar res{ env };
	AddConstraint( model, res <= v0 );
	AddConstraint( model, res <= v1 );
	AddConstraint( model, res >= v0 + v1 - 1 );
	return res;
}

IloBoolVar AddProductConstraints( IloEnv & env, IloModel & model
                                , IloBoolVarArray const & baseVars, int x, int y
                                , int w, int h
                                ) {
	queue< IloBoolVar > mulQueue;
	for ( int i = 0; i < w; ++i ) {
		for ( int j = 0; j < h; ++j ) {
			mulQueue.push( baseVars[x+i + numNodesX*(y+j)] );
		}
	}

	while ( mulQueue.size() > 1 ) {
		IloBoolVar v0 = mulQueue.front();
		mulQueue.pop();
		IloBoolVar v1 = mulQueue.front();
		mulQueue.pop();

		IloBoolVar mult = AddMultConstraints( env, model, v0, v1 );
		mulQueue.push( mult );
	}

	IloBoolVar last = mulQueue.front();
	mulQueue.pop();
	return last;
}

int ComputeSum( int x, int y, int w, int h ) {
	int sum = 0;
	for ( int i = 0; i < w; ++i ) {
		for ( int j = 0; j < h; ++j ) {
			if ( (*baseSet)[x+i + numNodesX*(y+j)] ) {
				++sum;
			}
		}
	}
	return sum;
}

IloBoolVarArray AddRectangle( IloEnv & env, IloModel & model, IloBoolVarArray const & baseVars, int w, int h ) {
	cout << " " << w << "x" << h;
	int matrixW = (numNodesX-w+1);
	int matrixH = (numNodesY-h+1);
	int matrixSize = matrixW*matrixH;
	IloBoolVarArray rect{ env, matrixSize };
	for ( int i = 0; i < matrixSize; ++i ) {
		char buf[1024];
		snprintf( buf, 1024, "%dx%d_{%d,%d}", w, h, i % matrixW, i / matrixW );
		rect[i].setName( buf );
	}

	for ( int i = 0; i < matrixW; ++i ) {
		for ( int j = 0; j < matrixH; ++j ) {
			IloBoolVar product = AddProductConstraints( env, model, baseVars, i, j, w, h );
			IloConstraint c0 = rect[i + matrixW*j] <= product;
			model.add            ( c0 );
			constraints.push_back( c0 );

			IloConstraint c1 = rect[i + matrixW*j] <= (ComputeSum( i, j, w, h ) >= minNumCross ? 1 : 0);
			model.add            ( c1 );
			constraints.push_back( c1 );
		}
	}

	return rect;
}

IloExpr ConstructOverlapExpr( IloEnv & env, IloModel & model, int x, int y
                            , IloBoolVarArray const & v0, int w0, int h0
                            ) {
	IloExpr expr{ env };
	for ( int i = 0; i < w0; ++i ) {
		for ( int j = 0; j < h0; ++j ) {
			if ( x-i >= 0 && x-i < numNodesX-w0+1 && y-j >= 0 && y-j < numNodesY-h0+1 ) {
				expr += v0[x-i + (numNodesX-w0+1)*(y-j)];
			}
		}
	}
	return expr;
}

IloExpr ConstructInclusionConstraintsAndExpr( IloEnv & env, IloModel & model
                                            , IloBoolVarArray const & baseVars, int x, int y
                                            , IloBoolVarArray const & rects, int w, int h
                                            ) {
	IloExpr expr{ env };
	for ( int i = 0; i < w; ++i ) {
		for ( int j = 0; j < h; ++j ) {
			if ( x-i >= 0 && x-i < numNodesX-w+1 && y-j >= 0 && y-j < numNodesY-h+1 ) {
				model.add            ( baseVars[x + numNodesX*y] >= rects[x-i + (numNodesX-w+1)*(y-j)] );
				constraints.push_back( baseVars[x + numNodesX*y] >= rects[x-i + (numNodesX-w+1)*(y-j)] );
				expr += rects[x-i + (numNodesX-w+1)*(y-j)];
			}
		}
	}
	return expr;
}

typedef pair< IloBoolVarArray, pair< int, int > > RectVarsType;
typedef vector< RectVarsType > RectVarsArrayType;

RectVarsType ConstructVarsArrays( IloEnv & env, IloModel & model, IloBoolVarArray const & baseVars, int w, int h ) {
	auto res = make_pair( AddRectangle( env, model, baseVars, w, h ), make_pair( w, h ) );
	return res;
}


RectVarsArrayType AddAllRectangles( IloEnv & env, IloModel & model, IloBoolVarArray const & baseVars ) {
	RectVarsArrayType varsArrays;
	cout << "Adding rectangles";
#if 1
	for ( int i = 1; i <= maxRectSize; ++i ) {
		for ( int j = 1; j <= maxRectSize; ++j ) {
			if ( i*j <= maxRectSize ) {
				varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, i, j ) );
			}
		}
	}
#else
	varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 1 , 12 ) );
	varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 12, 1  ) );
	varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 2 , 6  ) );
	varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 6 , 2  ) );
	varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 3 , 4  ) );
	varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 4 , 3  ) );
#endif
	cout << endl;
	return varsArrays;
}

void SetCplexParameters( IloCplex & cplex ) {
	cplex.setParam( IloCplex::Param::Parallel, IloCplex::Opportunistic );
	cplex.setParam( IloCplex::Param::Threads, cplex.getNumCores() );
	cplex.setParam( IloCplex::Param::Emphasis::MIP, IloCplex::MIPEmphasisBestBound );
}

int sumScores;

void Solve() {
	IloEnv   env;   // Default init
	IloModel model{ env };
	IloCplex cplex{ model };

	IloBoolVarArray baseVars{ env, numNodes };
	for ( int i = 0; i < numNodes; ++i ) {
		char buf[1024];
		snprintf( buf, 1024, "x_{%d,%d}", i%numNodesX, i/numNodesX );
		baseVars[i].setName( buf );
	}

	RectVarsArrayType varsArray = AddAllRectangles( env, model, baseVars );

	for ( int i = 0; i < numNodesX; ++i ) {
		for ( int j = 0; j < numNodesY; ++j ) {
			IloExpr expr( env );
			for ( int k = 0; k < (int)varsArray.size(); ++k ) {
				IloBoolVarArray const & vars = varsArray[k].first;
				int w = varsArray[k].second.first;
				int h = varsArray[k].second.second;
				expr += ConstructOverlapExpr( env, model, i, j, vars, w, h );
			}
			model.add            ( expr <= 1 );
			constraints.push_back( expr <= 1 );
		}
	}

	for ( int i = 0; i < numNodesX; ++i ) {
		for ( int j = 0; j < numNodesY; ++j ) {
			IloExpr expr{ env };
			for ( int k = 0; k < (int)varsArray.size(); ++k ) {
				IloBoolVarArray const & vars = varsArray[k].first;
				int w = varsArray[k].second.first;
				int h = varsArray[k].second.second;
				expr += ConstructInclusionConstraintsAndExpr( env, model, baseVars, i, j, vars, w, h );
			}
			model.add            ( baseVars[i + numNodesX*j] <= expr );
			constraints.push_back( baseVars[i + numNodesX*j] <= expr );
		}
	}

	IloExpr objExpr{ env };
	for ( int i = 0; i < numNodes; ++i ) {
		objExpr += baseVars[i];
	}
	model.add( IloObjective( env, objExpr, IloObjective::Maximize ) );

#if 0
	for ( int i = 0; i < (int)constraints.size(); ++i ) {
		cout << i << ": " << constraints[i] << '\n';
	}
#endif

	SetCplexParameters( cplex );
	if ( !cplex.solve() ) {
		if ( cplex.getStatus() != IloAlgorithm::Infeasible ) {
			clog << "Optimization error, CPLEX status code: " << cplex.getStatus() << endl;
		} else {
			clog << "Infeasible, CPLEX status code: " << cplex.getStatus() << endl;
		}
	} else {
		cout << "Input: \n";
		for ( int j = 0; j < numNodesY; ++j ) {
			for ( int i = 0; i < numNodesX; ++i ) {
				cout << ((*baseSet)[i + numNodesX*j] == 0 ? "-" : "•");
			}
			cout << endl;
		}
		cout << "Solution: \n";
		for ( int j = 0; j < numNodesY; ++j ) {
			for ( int i = 0; i < numNodesX; ++i ) {
				if ( cplex.getValue( baseVars[i + numNodesX*j] ) == 0 ) {
					cout << ((*baseSet)[i + numNodesX*j] == 1 ? "•" : "-");
				} else {
					cout << ((*baseSet)[i + numNodesX*j] == 1 ? "o" : "x");
				}
			}
			cout << endl;
		}

		cout << "Best objective: " << cplex.getBestObjValue() << endl;
		sumScores += cplex.getBestObjValue();
	}
}

int main( int argc, char * argv[] ) {
	if ( argc != 4 ) {
		cerr << "Usage: " << argv[0] << " <file> <num subdivide x> <num subdivide y>" << endl;
		exit( 0 );
	}

	LoadBase( argv[1] );

	auto bases = DivideBase( initialBase, atoi( argv[2] ), atoi( argv[3] ) );
	int numIter = 0;
	for ( auto const & base: bases ) {
		cout << "===== Iteration number: " << ++numIter << " / " << bases.size() << " ======" << endl;
		numNodesX = base.second.first;
		numNodesY = base.second.second;
		numNodes = numNodesX * numNodesY;
		baseSet = &base.first;
		Solve();
		cout << "===== Cumulative score: " << sumScores << " ======" << endl;
		cout << "===== Potential score: "  << bases.size()/(double)numIter * sumScores << " ======\n" << endl;
	}

	cout << "Total score: " << sumScores << endl;

	return 0;
}
<commit_msg>Refactor subproblems definition<commit_after>#define RUN_ME_WITH_SH /*
g++ -Wall -std=c++14 -Ofast -DIL_STD -DVERBOSE -o ${0%.*} ${0}          \
	-I/opt/ibm/ILOG/CPLEX_Studio1261/cplex/include                       \
	-I/opt/ibm/ILOG/CPLEX_Studio1261/concert/include                     \
	-L/opt/ibm/ILOG/CPLEX_Studio1261/cplex/lib/x86-64_linux/static_pic   \
	-L/opt/ibm/ILOG/CPLEX_Studio1261/concert/lib/x86-64_linux/static_pic \
	-lilocplex     \
	-lcplex        \
	-lcplexdistmip \
	-lconcert      \
	-lpthread      \
	-lm
exit
*/
#include <ilcplex/ilocplex.h>
#include <cstdlib>
#include <vector>
#include <sstream>
#include <fstream>
#include <string>
#include <queue>

using namespace std;

int minNumCross = 3;
int maxRectSize = 12;

enum BlockType { REGULAR_TYPE, HAM_TYPE, DO_NOT_EXISTS_TYPE };

struct Base {
	Base() {   }
	Base( vector< BlockType > && b, int w, int h )
		: blocks{ move( b ) }, width{ w }, height{ h } {
	}

	vector< BlockType > blocks;
	int width, height;
	int numCross = 0;
	int numBlank = 0;
	int numDoNotExists = 0;

	int size() const {   return (int)blocks.size();   }
	int accessIndex( int i, int j ) const  {   return i + width*j;   }
	BlockType access( int i, int j ) const {   return blocks[accessIndex(i, j)];   }
};

Base LoadBaseFromFile( char const * fileName ) {
	FILE * fin = fopen( fileName, "rb" );

	Base base;
	fscanf( fin, "%d %d %d %d\n", &base.height, &base.width, &minNumCross, &maxRectSize );
	char rc;
	for ( int i = 0; i < (base.width+1)*base.height; ++i ) {
		fscanf( fin, "%c", &rc );
		if      ( rc == '\n' ) continue;
		else if ( rc == 'T'  ) {   base.blocks.push_back( REGULAR_TYPE       );   ++base.numBlank;         }
		if      ( rc == 'H'  ) {   base.blocks.push_back( HAM_TYPE           );   ++base.numCross;         }
		else if ( rc == '.'  ) {   base.blocks.push_back( DO_NOT_EXISTS_TYPE );   ++base.numDoNotExists;   }
	}
	return base;
}

class SubProblem {
	public:
		SubProblem( Base const & b )
			: env{}
			, model{ env }
			, cplex{ model }
			, base{ b } {

			InitBaseVariables();
			InitRectVariables();
			InitOverlapConstraints();
			InitObjectiveFunction();

			SetCplexParameters( cplex );
		}

		void Solve() {
			if ( !cplex.solve() ) {
				if ( cplex.getStatus() != IloAlgorithm::Infeasible ) {
					clog << "Optimization error, CPLEX status code: " << cplex.getStatus() << endl;
				} else {
					clog << "Infeasible, CPLEX status code: " << cplex.getStatus() << endl;
				}
			} else {
				OutputSolution( cout );
			}
		}

		double GetBestScore() const {
			return cplex.getBestObjValue();
		}

		void PrintConstraints( ostream & out = cout ) {
			for ( int i = 0; i < (int)constraints.size(); ++i ) {
				out << i << ": " << constraints[i] << '\n';
			}
		}

	private:
		struct RectVarsType {
			RectVarsType( IloBoolVarArray const & vars, int w, int h )
				: rectVars{ vars }, rectWidth{ w }, rectHeight{ h } {
			}
			IloBoolVarArray rectVars;
			int rectWidth, rectHeight;
		};

	private:
		IloEnv   env;
		IloModel model;
		IloCplex cplex;

		Base const & base;

		IloBoolVarArray        blocksVars;
		vector< RectVarsType > rectsVarsArray;

		vector< IloConstraint > constraints;

	private:
		void InitBaseVariables() {
			blocksVars = IloBoolVarArray{ env, base.size() };
			for ( int i = 0; i < base.size(); ++i ) {
				char buf[1024];
				snprintf( buf, 1024, "x_{%d,%d}", i%(base.width), i/base.width );
				blocksVars[i].setName( buf );

				BlockType blockType = base.blocks[i];
				if ( blockType == DO_NOT_EXISTS_TYPE ) {
					AddConstraint( model, blocksVars[i] == 0 );
				}
			}
		}

		void InitRectVariables() {
		#if 1
			for ( int i = 1; i <= maxRectSize; ++i ) {
				for ( int j = 1; j <= maxRectSize; ++j ) {
					if ( i*j <= maxRectSize ) {
						rectsVarsArray.emplace_back(
							AddRectangle( env, model, blocksVars, i, j ),
							i, j
						);
					}
				}
			}
		#else
			varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 1 , 12 ) );
			varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 12, 1  ) );
			varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 2 , 6  ) );
			varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 6 , 2  ) );
			varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 3 , 4  ) );
			varsArrays.push_back( ConstructVarsArrays( env, model, baseVars, 4 , 3  ) );
		#endif
		}

		void InitOverlapConstraints() {
			for ( int i = 0; i < base.width; ++i ) {
				for ( int j = 0; j < base.height; ++j ) {
					IloExpr overlapExpr{ env };
					for ( int k = 0; k < (int)rectsVarsArray.size(); ++k ) {
						IloBoolVarArray const & rectVars = rectsVarsArray[k].rectVars;
						int w = rectsVarsArray[k].rectWidth;
						int h = rectsVarsArray[k].rectHeight;
						overlapExpr += ConstructInclusionConstraintsAndOverlapExpr( env, model, i, j
						                                                          , rectVars, w, h
						                                                          );
					}
					AddConstraint( model, overlapExpr <= 1 );
					AddConstraint( model, blocksVars[base.accessIndex( i, j )] <= overlapExpr );
				}
			}
		}

		void InitObjectiveFunction() {
			IloExpr objExpr{ env };
			for ( int i = 0; i < base.size(); ++i ) {
				objExpr += blocksVars[i];
			}
			model.add( IloObjective( env, objExpr, IloObjective::Maximize ) );
		}

		void SetCplexParameters( IloCplex & cplex ) {
			cplex.setParam( IloCplex::Param::Parallel, IloCplex::Opportunistic );
			cplex.setParam( IloCplex::Param::Threads, cplex.getNumCores() );
			cplex.setParam( IloCplex::Param::Emphasis::MIP, IloCplex::MIPEmphasisBestBound );
		}

		void AddConstraint( IloModel & model, IloConstraint const & constraint ) {
			model.add            ( constraint );
			constraints.push_back( constraint );
		}

		IloBoolVar AddMultConstraints( IloEnv & env, IloModel & model, IloBoolVar const & v0, IloBoolVar const & v1 ) {
			IloBoolVar res{ env };
			AddConstraint( model, res <= v0 );
			AddConstraint( model, res <= v1 );
			AddConstraint( model, res >= v0 + v1 - 1 );
			return res;
		}

		IloBoolVar AddProductConstraints( IloEnv & env, IloModel & model
		                                , IloBoolVarArray const & baseVars, int x, int y
		                                , int w, int h
		                                ) {
			queue< IloBoolVar > mulQueue;
			for ( int i = 0; i < w; ++i ) {
				for ( int j = 0; j < h; ++j ) {
					mulQueue.push( baseVars[x+i + base.width*(y+j)] );
				}
			}

			while ( mulQueue.size() > 1 ) {
				IloBoolVar v0 = mulQueue.front();
				mulQueue.pop();
				IloBoolVar v1 = mulQueue.front();
				mulQueue.pop();

				IloBoolVar mult = AddMultConstraints( env, model, v0, v1 );
				mulQueue.push( mult );
			}

			IloBoolVar last = mulQueue.front();
			mulQueue.pop();
			return last;
		}

		bool IsValidRect( int x, int y, int w, int h ) const {
			int sum = 0;
			for ( int i = 0; i < w; ++i ) {
				for ( int j = 0; j < h; ++j ) {
					BlockType blockType = base.access( x+i, y+j );
					if      ( blockType == DO_NOT_EXISTS_TYPE ) {   return false;   }
					else if ( blockType == HAM_TYPE           ) {   ++sum;          }
				}
			}
			return sum >= minNumCross;
		}

		IloBoolVarArray AddRectangle( IloEnv & env, IloModel & model, IloBoolVarArray const & baseVars, int w, int h ) {
			int matrixW = base.width-w+1;
			int matrixH = base.height-h+1;
			int matrixSize = matrixW*matrixH;
			IloBoolVarArray rect{ env, matrixSize };
			for ( int i = 0; i < matrixSize; ++i ) {
				char buf[1024];
				snprintf( buf, 1024, "%dx%d_{%d,%d}", w, h, i % matrixW, i / matrixW );
				rect[i].setName( buf );
			}

			for ( int i = 0; i < matrixW; ++i ) {
				for ( int j = 0; j < matrixH; ++j ) {
					if ( !IsValidRect( i, j, w, h ) ) {
						AddConstraint( model, rect[i + matrixW*j] == 0 );
					} else {
						IloBoolVar product = AddProductConstraints( env, model, baseVars, i, j, w, h );
						AddConstraint( model, rect[i + matrixW*j] <= product );
					}
				}
			}
			return rect;
		}

		IloExpr ConstructInclusionConstraintsAndOverlapExpr( IloEnv & env, IloModel & model, int x, int y
		                                                   , IloBoolVarArray const & rects, int w, int h
		                                                   ) {
			IloExpr expr{ env };
			for ( int i = 0; i < w; ++i ) {
				for ( int j = 0; j < h; ++j ) {
					if ( x-i >= 0 && x-i < base.width-w+1 && y-j >= 0 && y-j < base.height-h+1 ) {
						AddConstraint( model, blocksVars[base.accessIndex( x, y )] >= rects[x-i + (base.width-w+1)*(y-j)] );
						expr += rects[x-i + (base.width-w+1)*(y-j)];
					}
				}
			}
			return expr;
		}

		void OutputSolution( ostream & o ) const {
			auto UnselectedGetString = [&]( int i, int j ) {
				BlockType blockType = base.access( i, j );
				switch ( blockType ) {
					case REGULAR_TYPE:       {   return "-";   }
					case HAM_TYPE:           {   return "•";   }
					case DO_NOT_EXISTS_TYPE: {   return " ";   }
				}
				return "ERROR";
			};

			auto SelectedGetString = [&]( int i, int j ) {
				BlockType blockType = base.access( i, j );
				switch ( blockType ) {
					case REGULAR_TYPE:       {   return "x";   }
					case HAM_TYPE:           {   return "o";   }
					case DO_NOT_EXISTS_TYPE: {   return " ";   }
				}
				return "ERROR";
			};

			cout << "Input: \n";
			PrintBase( [&]( int i, int j ) {
				return UnselectedGetString( i, j );
			});
			cout << "\nSolution: \n";
			PrintBase( [&]( int i, int j ) {
				if ( cplex.getValue( blocksVars[base.accessIndex( i, j )] ) == 0 ) {
					return UnselectedGetString( i, j );
				} else {
					return SelectedGetString( i, j );
				}
			});

			cout << "Best objective: " << GetBestScore() << endl;
		}

		template< typename GetElemenStringFunc >
		void PrintBase( GetElemenStringFunc && getElemenString ) const {
			cout << '+';
			for ( int i = 0; i < base.width; ++i ) {
				cout << '=';
			}
			cout << "+\n";
		
			for ( int j = 0; j < base.height; ++j ) {
				cout << '|';
				for ( int i = 0; i < base.width; ++i ) {
					cout << getElemenString( i, j );
				}
				cout << "|\n";
			}
		
			cout << '+';
			for ( int i = 0; i < base.width; ++i ) {
				cout << '=';
			}
			cout << "+\n";
		}
};

vector< BlockType > CopySubMatrix( Base const & base, int x, int y, int w, int h ) {
	vector< BlockType > res;
	for ( int j = y; j < y+h; ++j ) {
		for ( int i = x; i < x+w; ++i ) {
			res.push_back( base.access( i, j ) );
		}
	}
/*
	for ( int i = 0; i < res.size(); ++i ) {
		if ( i % baseW == 0 ) cout << "\n";
		switch ( initialBase.first[i] ) {
			case REGULAR_TYPE: cout << "-"; break;
			case HAM_TYPE: cout << "o"; break;
			case DO_NOT_EXISTS_TYPE: cout << "."; break;
		}
	}
*/
	return res;
}

vector< Base > DivideBase( Base const & base, int numX, int numY ) {
	int w = base.width;
	int h = base.height;
	int wD = w / numX;
	int hD = h / numY;

	vector< Base > bases;
	for ( int i = 0; i < numX; ++i ) {
		for ( int j = 0; j < numY; ++j ) {
			int curX = i*wD, curY = j*hD;
			int curW, curH;
			if ( i == numX-1 ) {
				if ( j == numY-1 )     { curW = w-(i*wD);   curH = h-(j*hD);   }
				else                   { curW = w-(i*wD);   curH =      hD;    }
			} else if ( j == numY-1 ) { curW =      wD;    curH = h-(j*hD);   }
			else                      { curW =      wD;    curH =      hD;    }
			bases.emplace_back( CopySubMatrix( base, curX, curY, curW, curH ), curW, curH );
		}
	}
	return bases;
}

int main( int argc, char * argv[] ) {
	if ( argc != 5 ) {
		cerr << "Usage: " << argv[0] << " <file> <outfile> [<num subdivide x> <num subdivide y>]" << endl;
		exit( 0 );
	}

	Base initialBase = LoadBaseFromFile( argv[1] );

	int numSubdivideX = (argc > 3 ? atoi( argv[3] ) : 1);
	int numSubdivideY = (argc > 4 ? atoi( argv[4] ) : 1);
	auto bases = DivideBase( initialBase, numSubdivideX, numSubdivideY );
	vector< SubProblem > subProblems;
	for ( auto const & base: bases ) {
		subProblems.emplace_back( base );
	}
	int numIter = 0, sumScores = 0;
	for ( auto & problem: subProblems ) {
		cout << "===== Iteration number: " << ++numIter << " / " << bases.size() << " ======" << endl;
		problem.Solve();
		sumScores += problem.GetBestScore();
		cout << "===== Cumulative score: " << sumScores << " ======" << endl;
		cout << "===== Potential score: "  << subProblems.size()/(double)numIter*sumScores << " ======\n" << endl;
	}

	cout << "Total score: " << sumScores << endl;
	cout << "Total score w/ Do Not Exists: " << sumScores + initialBase.numDoNotExists << endl;

	return 0;
}

{'fraction_non_alphanumeric': 0.16464839600594858, 'fraction_numerical': 0.012279583598895262, 'mean_word_length': 2.4821719189229174, 'pattern_counts': {'":': 2, '<': 327, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5854254', 'n_tokens_mistral': 10046, 'n_tokens_neox': 8932, 'n_words': 2283}