//Author: Yusuf Sahillioglu
//EM algorithm (for isometric coarse correspondence) and coarse-to-fine (C2F) algorithm correspondence (for isometric coarse and dense correspondence) as parts of my cool PhD thesis

//selected algorithm set by the 4th cmd line argument
int theAlgo;
#define EM_ALGO						1 //use EM algo introuduced in the following journal:
									  //Y. Sahillioglu, Y. Yemez, Minimum-Distortion Isometric Shape Correspondence Using EM Algorithm, PAMI, 2011
									  //also used during EM algo: greedy optimization in: Y. Sahillioglu, Y. Yemez, 3D shape correspondence by isometry-driven greedy optimization, CVPR'10 
#define C2F_ALGO					2 //use C2F algo from:
									  //Y. Sahillioglu, Y. Yemez, Coarse-to-Fine Combinatorial Matching for Dense Isometric Shape Correspondence, SGP, 2011.

//x64 release mode is damn fast^
//even in dbg mode, i see the benefit of x64: 83 dijkstra on 20K dancing takes 25, 10, and 7 secs on x86-dbg, x64-dbg, x64-release, respectively; 285 dijkstra on dancing takes 91, 39, and 30 secs on x86-dbg, x64-dbg, x64-release, respectively
//50 min coarseToFineMatching() (= EM_ALGO undefined) in x86 debug mode w/ prints, takes just 1.5min (not even 2) here; incredible!!!!!!!!!!!!!!!!!!!!!!!!!
//^rarely x64 release mode not reach its optimum performance; if you feel it is not that fast, just delete \ipch and \x64 folders in ShapeCorresponderV2_x64.sln folder and build-run (ctrl+f5, or f7 just to build)

//for slightly faster output, play w/ Mesh.executionTimeOnly boolean to disable file printing of the computed correspondeces, and do your build for a brand new ShapeCorresponder_x64.exe (copy new ShapeCorresponder_x64.exe from x64\Release\ two parents up, i.e. to ShapeCorresponder_x64.sln's folder)
//for exhaustive printing to see almost every computational step, define DBG_DETAIL in Mesh.h, and do your build for a brand new ShapeCorresponder_x64.exe (copy new ShapeCorresponder_x64.exe from x64\Release\ two parents up, i.e. to ShapeCorresponder_x64.sln's folder)

#include "Model.h" //a screen object

#include <windows.h> //for get_time just-below
inline double get_time()
{
	LARGE_INTEGER t, frequency;
	QueryPerformanceCounter(&t);
	QueryPerformanceFrequency(&frequency);
	return (double)t.QuadPart/(double)frequency.QuadPart;
}

int main(int argc, char ** argv)
{
	//get 5 cmd line arguments
	if (argc != 8)
	{
		cout << "where're my 7 arguments?\n\n\n\n" <<
			    "ShapeCorresponder_x64.exe $obj1$ $obj2$ $EM-algo$ $shortcut-edges$ $grd-available$ $max-level$ $nInitialSamples$\n\n" << 
				"$obj1$: source mesh in obj format (1-based triangles)\n\n" <<
				"$obj2$: targetmesh in obj format (1-based triangles)\n\n" <<
				"$EM-algo$: 1 for EM algo, otherwise C2F algo\n\n" <<
				"$shortcut-edges$: 1 to enable shortcut edges for more accurate shortest paths, otherwise disable them\n\n" <<
				"$grd-available$: 1 means ground-truth correspondence known as s_i to t_i where i is in [0, # of vertices] to print ground-truth distortion D_grd\n\n" <<
				"$max-level$: starting from 0, max level to be reached in C2F mode (not used for EM algo); use -1 for auto stop (at the finest level)\n\n" <<
				"$nInitialSamples$: -1 not to force one-to-one mapping at the first level, and otherwise initial 1-to-1 map of size nInitialSamples is forced (more accurate) (not used for EM algo)\n\n";
		exit(0);
	}

	char* sourceMeshPath = argv[1], * targetMeshPath = argv[2]; //[0] is the name of the program
	theAlgo = (atoi(argv[3]) == 1 ? EM_ALGO : C2F_ALGO);

	Model* jModel = new Model("Model # 1", sourceMeshPath, atoi(argv[4]) == 1, atoi(argv[5]) == 1, atoi(argv[6]), atoi(argv[7]));
	jModel->init();
	Model* jModel2 = new Model("Model # 2", targetMeshPath, atoi(argv[4]) == 1, atoi(argv[5]) == 1, atoi(argv[6]), atoi(argv[7]));
	jModel2->init();

	double start_time = get_time(), geodesics;
	if (theAlgo == EM_ALGO)
	{
		jModel->getMesh()->C2Fmode = jModel2->getMesh()->C2Fmode = false;
		/*bool coarseCorresp = true;
		if (coarseCorresp)*/
		{
			int k = jModel->getMesh()->initialSpectralMatching( jModel2->getMesh() );
			//cout << get_time()-start_time << " secs for geodesics computations (plus spectral corresp which is negligible for nBases = ~80)\n"; printing here takes extra 0.1secs
			geodesics = get_time()-start_time;
			jModel->getMesh()->greedyOpt(jModel2->getMesh(), true, k); //just skip here to see initial corresp
			jModel->getMesh()->finalizeMatching(jModel2->getMesh(), k, true);
		}
		/*else //denseCorresp (this interpolation-based dense correspondence is just experimental and not as robust as C2F algo; so, not in use)
			jModel->getMesh()->denseMatching( jModel2->getMesh() );*/
	}
	else if (theAlgo == C2F_ALGO)
	{
		jModel->getMesh()->C2Fmode = jModel2->getMesh()->C2Fmode = true;
		jModel->getMesh()->coarseToFineMatching( jModel2->getMesh() );
	}
	else
	{
		cout << "undefined algorithm choice\n";
		exit(0);
	}
	if (theAlgo == EM_ALGO)
		cout << geodesics << " secs for geodesics computations (plus spectral corresp which is negligible for nBases = ~80)\n";
	cout << get_time()-start_time << " secs for all computations (excluding load time of 2 input meshes)\n";

	return 0;
}
