#include "Mesh.h"

int Mesh::addVertex(float x, float y, float z)
{
	float v[3] = {x, y, z};
	return addVertex(v);
}

int Mesh::addVertex(float* v)
{
	//add v to verts, if not duplicated

	bool duplicateExists = false;
	int i = (int) verts.size();

	//duplicate prevention
	for (i = (int) verts.size() - 1; i >= 0; i--)
	{
		if (fabs(verts[i]->coords[0] - v[0]) < ROUNDING_ERR && //since i scale stuff initially w/ SCALE_FACTOR = 1000 just in case, ROUNDING_ERR = 0.01f should be good here
			fabs(verts[i]->coords[1] - v[1]) < ROUNDING_ERR &&
			fabs(verts[i]->coords[2] - v[2]) < ROUNDING_ERR)
		{
			duplicateExists = true;
			break;	//more than efficiency: return the correct i by this break
		}
	}
	if (! duplicateExists)
	{
		int idx = (int) verts.size();
		verts.push_back(new Vertex(idx, v));
		return idx; //pointer to this new item (last index)
	}

	//this duplicate vert v is ith element of verts
	return i;
}

void Mesh::addVertexND(float* c)
{
	//fastly add verts w/ coords c to mesh; ND: no duplication check

	int vSize = (int) verts.size(); //size before push_back just-below
	verts.push_back(new Vertex(vSize, c));
}

void Mesh::addTriangle(int v1i, int v2i, int v3i, bool temporary0Area)
{
	//add tri v1i-v2i-v3i to the triInd'th element of tris

	int triInd = (int) tris.size();

	tris.push_back( new Triangle(triInd, v1i, v2i, v3i) );

	//split() of an edge shared by temporary 0-area tri'll validly make newcomers temporary 0-area
	if (! temporary0Area){ float area = calcTriArea(triInd); assert(area >= MIN_TRIANGLE_AREA_ALLOWED); }

	//update each vertex with its new tri and vert neighbors
	verts[v1i]->triNeighbors.push_back(triInd);
	//addVertNeighbor returns true if verts[v1i] is not neighbor of [v2i]; so they can form a new edge
	if (verts[v1i]->addVertNeighbor(v2i))	addEdge(v1i, v2i);
	if (verts[v1i]->addVertNeighbor(v3i))	addEdge(v1i, v3i);
	//in other words, a 'false' returned by addVertNeighbor prevents adding the same edge if it already exists

	verts[v2i]->triNeighbors.push_back(triInd);
	verts[v2i]->addVertNeighbor(v1i);	//v1i v2i edge is (probably) added above; do not add v2i v1i edge!!!
	if (verts[v2i]->addVertNeighbor(v3i))	addEdge(v2i, v3i);	//v3i v2i is not added above, so add this if ok

	verts[v3i]->triNeighbors.push_back(triInd);
	verts[v3i]->addVertNeighbor(v1i);	//no addEdge risk, so no if control
	verts[v3i]->addVertNeighbor(v2i);
}

int Mesh::getEdgeSharedBy(int vi, int vj)
{
	//return idx of edge[vi, vj]

	for (unsigned int vie = 0; vie < verts[vi]->edgeList.size(); vie++)
		for (unsigned int vje = 0; vje < verts[vj]->edgeList.size(); vje++)
			if (verts[vj]->edgeList[vje] == verts[vi]->edgeList[vie])
				return verts[vj]->edgeList[vje];

	//edge not exist
	return -99;
}

void Mesh::updateNormals()
{
	//update normals of tris and verts in mesh

	//triangles
	for (int t = 0; t < (int) tris.size(); t++)
	{
		int v1i = tris[t]->v1i, v2i = tris[t]->v2i, v3i = tris[t]->v3i;

		tris[t]->setNormal(verts[v1i]->coords, verts[v2i]->coords, verts[v3i]->coords);
		tris[t]->setArea(verts[v1i]->coords, verts[v2i]->coords, verts[v3i]->coords);
	}

	//vertices
	for (unsigned int v = 0; v < verts.size(); v++)
	{
		float* normalTotal = new float[3];
		normalTotal[0] = normalTotal[1] = normalTotal[2] = 0.0f;
		int howManyTriNeighb = 0;
		for (unsigned int tneighb = 0; tneighb < verts[v]->triNeighbors.size(); tneighb++)		
			if (tris[ verts[v]->triNeighbors[tneighb] ]->area >= MIN_TRIANGLE_AREA_ALLOWED)	//ignore temporary 0-area tris,
																							//which occur by i.e. split
			{
				for (int i = 0; i < 3; i++)
					normalTotal[i] += tris[ verts[v]->triNeighbors[tneighb] ]->normal[i]; //maby a better way is to weight tri-normals w/ their areas for v.normal computation
				howManyTriNeighb++;
			}
		if (howManyTriNeighb != 0) //if 0, go on w/ the ex-normal
		{
			for (int i = 0; i < 3; i++)
				normalTotal[i] /= (float) howManyTriNeighb;
			verts[v]->setNormal(normalTotal);
		}
	}
}

inline float distanceBetween(float* v1, float* v2)
{
	//In fact, the keyword inline is not necessary. If the function is defined with its body directly and the function
	//has a smaller block of code, it will be automatically treated as inline by the compiler
	return (float) sqrt( (v2[0]-v1[0])*(v2[0]-v1[0]) + (v2[1]-v1[1])*(v2[1]-v1[1]) + (v2[2]-v1[2])*(v2[2]-v1[2]));
}
inline float distanceBetween2(float* v1, float* v2)
{
	//squared distance which skips sqrt part of distanceBetween() for efficiency; e.g. use this for comparison-based distances
	return (float) (v2[0]-v1[0])*(v2[0]-v1[0]) +  (v2[1]-v1[1])*(v2[1]-v1[1]) + (v2[2]-v1[2])*(v2[2]-v1[2]);
}
inline float distanceBetweenKD(float* v1, float* v2, int k)
{
	//L2 distance b/w two k-D pnts
	float ss = 0;
	for (int i = 0; i < k; i++)
		ss += ((v2[i]-v1[i])*(v2[i]-v1[i]));
	return (float) sqrt(ss);
}

inline float Mesh::calcTriArea(int t)
{
	float* v1 = verts[ tris[t]->v1i ]->coords,
		 * v2 = verts[ tris[t]->v2i ]->coords,
		 * v3 = verts[ tris[t]->v3i ]->coords;

	float* p_r = new float[3];
	for (int i = 0; i < 3; i++)
		p_r[i] = v2[i] - v1[i];

	float* q_r = new float[3];
	for (int i = 0; i < 3; i++)
		q_r[i] = v3[i] - v1[i];

	float* cross = new float[3];
	cross[0] = p_r[1]*q_r[2] - p_r[2]*q_r[1];
	cross[1] = p_r[2]*q_r[0] - p_r[0]*q_r[2];
	cross[2] = p_r[0]*q_r[1] - p_r[1]*q_r[0];

	//length of this vector
	float length = sqrt(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2]);

	//When it comes time to de-allocate them, I had them all in a single delete[] statement: delete[] a, b, c, d, e; this appears to only be de-allocating a and completely ignoring b, c, d, and e
	delete p_r;
	delete q_r;
	delete cross;

	return 0.5f * length;
}

void Mesh::addEdge(int v1i, int v2i)
{
	int initial = (int) edges.size();
	Edge* newcomer = new Edge(initial, v1i, v2i, distanceBetween(verts[v1i]->coords, verts[v2i]->coords));
	edgeMultiSet.insert(MultiSetContent(newcomer->idx, newcomer->length));

	//update the list of edges of which verts[v1i] is a member
	verts[v1i]->edgeList.push_back(newcomer->idx);
	verts[v2i]->edgeList.push_back(newcomer->idx); //do same thing for [v2i]

	edges.push_back(newcomer);

	//information
	if (newcomer->length < minEdgeLen)
		minEdgeLen = newcomer->length;
	if (newcomer->length > maxEdgeLen)
		maxEdgeLen = newcomer->length;
	edgeLenTotal += newcomer->length;
}

void Mesh::prepareForGeodesicComputation()
{
	//prepares the mesh to upcoming geodesic computation process

	//1) fine distribution of verts needed; so, use split to resample verts until all edge lengths < threshold
//	edgeSplit();
//	updateNormals(); //newcomer tris/verts should learn their normals

	//2) edges should be isotropic, i.e. uniform in all directions, for good geodesic approximation
	genShortcutEdges();
}

bool Mesh::vertsCollinear(float* v1, float* v2, float* v3)
{
	//3 points may be on the same line which makes area of tri 0; for convexity check, 4th vert
	//projected onto the plane (no plane actually) of such 3 collinear verts always yield a convex shape

	// If a triangle is defined by 3 points, say p, q and r, then
	// its area is 0.5 * length of ((p - r) cross (q - r)) [See Real-Time Rendering book, Appendix A]
	/*SbVec3f p_r = v2 - v1;
	SbVec3f q_r = v3 - v1;
	SbVec3f cross = p_r.cross(q_r);*/
	float* p_r = new float[3];
	for (int i = 0; i < 3; i++)
		p_r[i] = v2[i] - v1[i];

	float* q_r = new float[3];
	for (int i = 0; i < 3; i++)
		q_r[i] = v3[i] - v1[i];

	float* cross = new float[3];
	cross[0] = p_r[1]*q_r[2] - p_r[2]*q_r[1];
	cross[1] = p_r[2]*q_r[0] - p_r[0]*q_r[2];
	cross[2] = p_r[0]*q_r[1] - p_r[1]*q_r[0];

	//length of this vector
	float length = sqrt(cross[0]*cross[0] + cross[1]*cross[1] + cross[2]*cross[2]), area = 0.5f * length;

	//delete a, b, c; ignores b and c
	delete p_r;
	delete q_r;
	delete cross;

	return (area == 0.0f); //area ~ 0 means that verts are collinear

/*	if (theInput != JUMPING && theInput != DANCING && area == 0.0f)
		return true;
	else if (theInput != JUMPING && theInput != DANCING && area != 0.0f)
		return false;
	else //not hybrid run, so hybridModelsToSameCoorFrame() not scaled coords and triangle areas not violated
		return (area < MIN_TRIANGLE_AREA_ALLOWED ? true : false);	//.01 margin for variable precision
//it was area < 0.01f when i got paper results but this is now a better programming and more consistent*/
}

float* Mesh::projectVertOntoPlane(int toBeProjected, float* planeUnormal, float distOfPlaneFromOrigin)
{
	//using the projectiveMatrix from www.euclideanspace.com/maths/geometry/elements/plane/

	float* prjctdVert = new float[3], * v = verts[toBeProjected]->coords;

	//using the projectiveMatrix from www.euclideanspace.com/maths/geometry/elements/plane/
	prjctdVert[0] = (planeUnormal[1]*planeUnormal[1] + planeUnormal[2]*planeUnormal[2])*v[0] - planeUnormal[0]*planeUnormal[1]*v[1] - planeUnormal[0]*planeUnormal[2]*v[2];
	prjctdVert[1] = -planeUnormal[1]*planeUnormal[0]*v[0] + (planeUnormal[0]*planeUnormal[0] + planeUnormal[2]*planeUnormal[2])*v[1] - planeUnormal[1]*planeUnormal[2]*v[2];
	prjctdVert[2] = -planeUnormal[2]*planeUnormal[0]*v[0] - planeUnormal[2]*planeUnormal[1]*v[1] + (planeUnormal[0]*planeUnormal[0] + planeUnormal[1]*planeUnormal[1])*v[2];

	//this crucial part is added after my visualization; thanx to my test friendly style!!!
	//result just above is projected onto plane passing through origin; move this point to the
	//actual plane by moving it along the plane planeUnormal direction with an amount of D
	for (int i = 0; i < 3; i++)
		prjctdVert[i] += distOfPlaneFromOrigin*planeUnormal[i];

	return prjctdVert;
}

bool lineSegmentsIntersect(float* s1p1, float* s1p2, float* s2p1, float* s2p2)
{
	//returns true if segment1 (s1) defined by 2 pnts (p1, p2) intersects segment2; z-coords are ignored, having the
	//projection effect on x-y plane (safe 'cos s1 & s2 are guaranteed to be on same plane, not necessarily x-y tough)

	//algo from local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
	float denom = (s2p2[1]-s2p1[1])*(s1p2[0]-s1p1[0]) - (s2p2[0]-s2p1[0])*(s1p2[1]-s1p1[1]);
	if (denom == 0.0f)
		return false; //parallel lines
	float numer1 = (s2p2[0]-s2p1[0])*(s1p1[1]-s2p1[1]) - (s2p2[1]-s2p1[1])*(s1p1[0]-s2p1[0]),
		  numer2 = (s1p2[0]-s1p1[0])*(s1p1[1]-s2p1[1]) - (s1p2[1]-s1p1[1])*(s1p1[0]-s2p1[0]),
		  u1 = numer1 / denom, u2 = numer2 / denom;
	return (u1 >= 0.0f && u1 <= 1.0f		&&		u2 >= 0.0f && u2 <= 1.0f);
}

void Mesh::genShortcutEdges()
{
	//introduces additional edges so that resulting mesh has edges that are uniform in all directions

	if (! shortcutEdges)
		return; //no shortcut 'cos it slows down the base computation as well as dijkstra (but shortcut'ed more accurate; so, do it; for x64 release, it crashes at weird places for david_victoria (x86 version never crash)

	cout << "shortcut edges being generated....\n";

	minSedgeLen = INF;
	maxSedgeLen = -INF;
	sedgeLenTotal = 0.0f;
	for (unsigned int t = 0; t < tris.size(); t++)
	{
		//if (theInput == HORSE || theInput == DOG_WOLF)
		//	break; //for dog-wolf and horse run, getTrianglesSharedBy() exits program due to border edges; so, no shortcut stuff there; horse1-3 result worsens w/ shortcuts (weird)

		//get the 3 triangles adjacent to t (edge-adjacency)
		int v1i = tris[t]->v1i, v2i = tris[t]->v2i, v3i = tris[t]->v3i;
		float* v1ic = verts[v1i]->coords, * v2ic = verts[v2i]->coords, * v3ic = verts[v3i]->coords;
		if ( vertsCollinear(v1ic, v2ic, v3ic) )
			continue; //no plane defined for this degenarate tri

		int e1 = getEdgeSharedBy(v1i, v3i), e2 = getEdgeSharedBy(v1i, v2i), e3 = getEdgeSharedBy(v2i, v3i);
		int* shrdTris = getTrianglesSharedBy(edges[e1]);
		int t1 = (shrdTris[0] == t ? shrdTris[1] : shrdTris[0]);
//		delete [] shrdTris;
		shrdTris = getTrianglesSharedBy(edges[e2]);
		int t2 = (shrdTris[0] == t ? shrdTris[1] : shrdTris[0]);
//		delete [] shrdTris;
		shrdTris = getTrianglesSharedBy(edges[e3]);
		int t3 = (shrdTris[0] == t ? shrdTris[1] : shrdTris[0]);
//		delete [] shrdTris;

		if (t1 == -1 || t2 == -1 || t3 == -1) //border edge on (non-closed) mesh makes one triangle neighbor to an endge; so, the 2nd tri may become -1
			continue;
		int u1, u2, u3; //u1 is the uncommon vert of the 1st adj tri (t1), and so on
		if (! (tris[t1]->v1i == v1i || tris[t1]->v1i == v3i)) //v2i ignored 'cos t1 is created via v1i-v3i edge
			u1 = tris[t1]->v1i;
		else if (! (tris[t1]->v2i == v1i || tris[t1]->v2i == v3i))
			u1 = tris[t1]->v2i;
		else
			u1 = tris[t1]->v3i;

		if (! (tris[t3]->v1i == v3i || tris[t3]->v1i == v2i))
			u2 = tris[t3]->v1i;
		else if (! (tris[t3]->v2i == v3i || tris[t3]->v2i == v2i))
			u2 = tris[t3]->v2i;
		else
			u2 = tris[t3]->v3i;
		
		if (! (tris[t2]->v1i == v2i || tris[t2]->v1i == v1i))
			u3 = tris[t2]->v1i;
		else if (! (tris[t2]->v2i == v2i || tris[t2]->v2i == v1i))
			u3 = tris[t2]->v2i;
		else
			u3 = tris[t2]->v3i;

		//now i've v1i, u1, v2i, u2, v3i, u3; need to project u's to the plane of v1i-v2i-v3i first
		float distOfPlaneFromOrigin = 0; //tris[t]->unormal.dot(v1ic);
		for (int i = 0; i < 3; i++)
			distOfPlaneFromOrigin += tris[t]->unormal[i]*v1ic[i];
		//u1 projected to the plane of t, and projected coord is kept in u1c
		float*  u1c = projectVertOntoPlane(u1, tris[t]->unormal, distOfPlaneFromOrigin),
				//project u2 as u2c
			 *	u2c = projectVertOntoPlane(u2, tris[t]->unormal, distOfPlaneFromOrigin),
				//project u3 as u3c		
			 *	u3c = projectVertOntoPlane(u3, tris[t]->unormal, distOfPlaneFromOrigin);

		//now i've v1i, u1c, v2i, u2c, v3i, u3c; create desired shortcut edges b/w corresponding pairs
		//test 1 corresponding pair: v1i ~ u2c (v1i ~ u2c line segment is related to edge e3 segment)
		if ( lineSegmentsIntersect(v1ic, u2c,	v2ic, v3ic) )
			addShortcutEdge(v1i, u2, v1ic, u2c);
		//test other corresponding pairs in each if-statement
		if ( lineSegmentsIntersect(v2ic, u1c,	v1ic, v3ic) )
			addShortcutEdge(v2i, u1, v2ic, u1c);
		if ( lineSegmentsIntersect(v3ic, u3c,	v1ic, v2ic) )
			addShortcutEdge(v3i, u3, v3ic, u3c);
		//shortcut edges from u1
		if ( lineSegmentsIntersect(u1c,	u3c,	v1ic, v3ic) )
			addShortcutEdge(u1, u3, u1c, u3c);
		if ( lineSegmentsIntersect(u1c,	u2c,	v1ic, v3ic) )
			addShortcutEdge(u1, u2, u1c, u2c);
		//shortcut edges from u2 (note that, u1-u2 already checked above, so no u2-u1 here
		if ( lineSegmentsIntersect(u2c,	u3c,	v2ic, v3ic) )
			addShortcutEdge(u2, u3, u2c, u3c);

		//delete a, b, c; ignores b and c
		delete u1c;
		delete u2c;
		delete u3c;
	}

	if (((int) sedges.size()) != 0)
		cout << "shortcutEdges: min <= avg <= maxSedgeLen: " << minSedgeLen << " <= " << sedgeLenTotal/((int) sedges.size()) << " <= " << maxSedgeLen << "\n";
	cout << "Done! # of generated shortcut edges: " << (int) sedges.size() << ", for " << (int) verts.size() << " verts\n\n";
}

inline void Mesh::addShortcutEdge(int v1i, int v2i, float* v1, float* v2Prjctd)
{
	//shortcut edge b/w v1i & v2i added if it has not been previously generated

	//check if virtual connection already exists
	for (unsigned int se = 0; se < verts[v1i]->sedgeList.size(); se++)
	{
		Edge* sedge = sedges[ verts[v1i]->sedgeList[se] ];

		int otherVert = (sedge->v1i == v1i ? sedge->v2i : sedge->v1i);
		if (otherVert == v2i) //virtual connection already exists
			return;
	}

	//add the shortcut edge for the first time b/w these 2 verts, where edge length is from projected domain
	float plen = distanceBetween(v1, v2Prjctd);
	int sSize = (int) sedges.size(); //size before push_back just-below
	sedges.push_back(new Edge(sSize, v1i, v2i, plen));
	verts[v1i]->sedgeList.push_back(sSize);
	verts[v2i]->sedgeList.push_back(sSize); //do same thing for [v2i]
	if (plen < minSedgeLen)
		minSedgeLen = plen;
	if (plen > maxSedgeLen)
		maxSedgeLen = plen;
	sedgeLenTotal += plen;
}

int* Mesh::getTrianglesSharedBy(Edge* bigEdge, int v1i, int v2i)
{
	//tri t is shared by bigEdge iff it exists in the triNeigbors of verts[v1i] & [v2i]
	if (v1i == -1) //learn v1i and v2i from bigEdge
	{
		v1i = bigEdge->v1i;
		v2i = bigEdge->v2i;
	}
	int a = 0, tr1, tr2;
	int* shTris = new int[2];	//portable version (memo-friendly as well 'cos i delete shrdTris in split/flip())
	//seeing this took my 6 hours!!! prevent removed triangles from being selected as shared!
	for (unsigned int t1 = 0; t1 < verts[v1i]->triNeighbors.size(); t1++)
	{
		tr1 = verts[v1i]->triNeighbors[t1];
		for (unsigned int t2 = 0; t2 < verts[v2i]->triNeighbors.size(); t2++)
		{
			tr2 = verts[v2i]->triNeighbors[t2];
			if  (tris[tr2] != 0 && (tr1 == tr2))
				shTris[a++] = tr1; //or tr2
		}
	}
	//if called from split/flip, a is certainly incremented by 1; but it remains at 1 (no more ++) 
	//if bigEdge is a border edge (not in my case 'cos my mesh is closed); take precautions anyway;
	if (a == 1) shTris[1] = -1;	//number -1 must not change 'cos used by somewhere else
//if (shTris[1] == -1) cout << v1i << "," << v2i << " is a border edge found in my closed-mesh :(\n\n"; //exit(0);}

//if called from contract, a may remain 0, which tells me to remove that edgesAdded[cea] neighbor to 2 removed tris
if (a == 0)	shTris[0] = -1;	//since that part of contract() is inactive, don't care here

assert(shTris[0] != shTris[1]);
	return shTris;
}

void mtrxMult(float** result, int m1Row, int m1Col, int m2Row, int m2Col, float** m1, float** m2)
{
	//result = m1 * m2
	
	//init result to 0
	for (int r = 0; r < m1Row; r++)
		for (int c = 0; c < m2Col; c++)
			result[r][c] = 0.0f;

	/*pseudocode:
	for each row i in matrix1 do:
            for each column j in matrix 2 do:
                for each column k in matrix1 do:
                    increase  matrix3[i,j] by matrix1[i,k]*matrix2[k,j] */
	for (int m1r = 0; m1r < m1Row; m1r++)
		for (int m2c = 0; m2c < m2Col; m2c++)
			for (int m1c = 0; m1c < m1Col; m1c++)
				result[m1r][m2c] += m1[m1r][m1c] * m2[m1c][m2c]; //call-by-ref to result
}

void Mesh::computeBaseGeodesicAffinityMtrx(int k, bool toFile)
{
	//computes 180x180 geodesic affinity mtrx b/w bases and sends it to k-D spectral domain via PCAonAffinity()

	cout << "base geodesic affinity mtrx (b/w pairs of base vertices only) being computed..\n";
	int n = (int) baseVerts.size();

	//use Gaussian kernel'ed version of geodesic distances to normalize mtrx elements in (0, 1), i.e. scale invariant,
	//and to decrease the importance of far-away vertices
	float twoSigmaSquare = 2.0f; //kernel(geoDist(p, q)) = e^{- geoDist(p, q) / twoSigmaSquare}
//Jain's paper says sigma = maxGeoDist; since maxGeoDist is always 1 after normalization below, set twoSigmaSquare = 2 directly//twoSigmaSquare = 0.3f; //so that far away pnts, i.e. big geodesics, map to 0.04; if twoSigmaSquare = 2.0f, they map to as low as 0.61; closest pnts, i.e. geoDist = 0, always map to 

	FILE* fPtr;
	if (toFile)
	{
		char fName[250];
		sprintf(fName, "..\\..\\tools\\specCorr\\specCorr\\meshes\\A_%d %d.dat", frameID, (int) baseVerts.size());
		if (! (fPtr = fopen(fName, "w")))
		{
			cout << "can't create file " << fName << endl;
			exit(0);
		}
	}
	//ready to use normalized dSpanning/geoDist values to compute baseGeoAffinity mtrx
	baseGeoAffinity = new float*[n];
	float sigma = maxGeoDistG / 2.0f, a = 2.0f / (sqrt(2*PI) * sigma); //true 0-mean gaussian kernel parameters that are explained below
	for (int i = 0; i < n; i++)
	{
		baseGeoAffinity[i] = new float[n];
		for (int j = 0; j < n; j++)
		{
			if (j < i) //save time
				baseGeoAffinity[i][j] = baseGeoAffinity[j][i]; //symmetric mtrx for undirected graph (copy precomputed result)
			else
			{
				float geoDist = verts[ baseVerts[i] ]->dSpanning[ baseVerts[j] ], geoDistUnn = verts[ baseVerts[i] ]->dSpanningUnn[ baseVerts[j] ];
								//verts[ baseVerts[i] ]->getGeodesic(baseVerts[j]);
assert(geoDist >= 0.0f && geoDist <= 1.0f);
				//baseGeoAffinity[i][j] = geoDist; //no kernel [sucks]
				//baseGeoAffinity[i][j] = pow(EXP, -(geoDist*geoDist) / twoSigmaSquare); //Gaussian kernel'ed (applyGaussian.m uses this; no!!!!, they use unnormalized geodists and use sigma = maxGeoDistG)
				if (useKernel1)
					baseGeoAffinity[i][j] = pow(EXP, -(geoDistUnn*geoDistUnn) / (maxGeoDistG*maxGeoDistG)); //Gaussian kernel'ed (applyGaussian.m uses this) [kernel1]
				else
					//unnormalized geo dists range from 0 to say 666; i want to map [0,666] -> [0,1] where values close to 0 map close to 1 and close to 666 map to 0: 0 -> 1, 666 -> ~0
					//this effect is obtained via gaussian kernel; mu is the value i want to favor the most: 0; sigma^2 (variance) is the width of the kernel; values after 3sigma is regarded as 0; so,
					//3sigma = 666 --> sigma = 666/3=222, making variance = 222^2 = 49284; recall 0-mean gausian kernel: (1 / (sqrt(2pi) x sigma)) x e^(-x^2 / (2 x sigma^2)); since my values are +ve,
					//i need the right half of this gaussian and to make the integral 1 in this interval, use 2 x above, i.e. to make the summation of weights 1, use 2 x above; so, kernel works like this:
					baseGeoAffinity[i][j] = a * pow(EXP, -(geoDistUnn*geoDistUnn) / (2 * sigma * sigma)); //[kernel2] [PCAonAffinity() can use the 1st eigvec as well; no, still can't use 'cos i still use a big gaussian width (sigma) and therefore 1st eigvec is still a bad consant vector]
			}

			if (toFile)
				fprintf(fPtr, "%f\t", verts[ baseVerts[i] ]->dSpanningUnn[ baseVerts[j] ]); //unnormalized geodesic dist to file
		} //end of j
		if (toFile)
			fprintf(fPtr, "\n");
	} //end of i
	if (toFile)
		fclose(fPtr);
	cout << "Done!\n\n";
	
	PCAonAffinity(k);
}

/*void Mesh::readAffinityFromFile(char* fileAddr)
{
	//fast replacement of computeBaseGeodesicAffinityMtrx() which simply reads precomputed nxn info from fileAddr

	FILE* fPtr = fopen(fileAddr, "r");
	int n;
	fscanf(fPtr, "%d\n", &n);
	baseGeoAffinity = new float*[n];
	for (int i = 0; i < n; i++)
	{
		baseGeoAffinity[i] = new float[n];
		for (int j = 0; j < n; j++)
		{
			float geoDist;
			fscanf(fPtr, "%f\t", &geoDist);
			baseGeoAffinity[i][j] = geoDist;
		}
	}
	fclose(fPtr);

	//go to the spectral domain w/ the eigvectors of this baseGeoAffinity[][]
	PCAonAffinity(3); //or 4-d
}*/

//////////// quicksort /////////////////
int partition(float* a, int* vIdxs, int p, int r) { //using float a[], int vIdxs[] again does call-by-ref to arrays
  float x = a[r];
  int xv = vIdxs[r];
  int j = p - 1;
  for (int i = p; i < r; i++) {

    if (x <= a[i]) {
      j = j + 1;
	  //exchange a[i] & a[j]
      float temp = a[j];
      a[j] = a[i];
      a[i] = temp;
	  //exchange corresponding indices vIdxs[i] & vIdxs[j]
      int tmp = vIdxs[j];
      vIdxs[j] = vIdxs[i];
      vIdxs[i] = tmp;
    }
  }
  //exchange a[j+1] & a[r]
  a[r] = a[j + 1];
  a[j + 1] = x;
  //exchange corresponding indices vIdxs[j+1] & vIdxs[r]
  vIdxs[r] = vIdxs[j + 1];
  vIdxs[j + 1] = xv;

  return (j + 1);
}
void quickSort(float* a, int* vIdxs, int p, int r) {
	//quicksort from 24bytes.com/Quick-Sort.html
  if (p < r) {
    int q = partition(a, vIdxs, p, r);
    quickSort(a, vIdxs, p, q - 1);
    quickSort(a, vIdxs, q + 1, r);
  }
}
//////////// quicksort ends /////////////////

float Mesh::setRadius(float wholeSurfaceArea, bool initialBasesByMu)
{
	//sets the radius that gives ~5 bases according to the input; our formula for ~5 is generic but i may need little perturbations for different inputs

	if (initialBasesByMu) //called only if computeMu=true, i.e. bases spread over the whole surface by mu values
	{
		//manually adjusted r values for 8x8 start
		if (theInput == BALLERINA && (frameID == 5 || frameID == 11 || frameID == 6 || frameID == 7 || frameID == 12 || frameID == 13)) return 1.35f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start
		if (theInput == BALLERINA && frameID > 60 && frameID <= 80) return 1.31f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start, airplane class
		if (theInput == BALLERINA && frameID > 80 && frameID <= 100) return 2.2f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start, ant class
		if (theInput == BALLERINA && frameID > 100 && frameID <= 120) return (frameID == 102 ? 1.53f : 1.63f) * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start, chair class
		if (theInput == BALLERINA && (frameID > 160 && frameID <= 180) || (frameID > 220 && frameID <= 240)) return 1.2f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start, teddy or fish class
		if (theInput == BALLERINA && frameID > 180 && frameID <= 200) return (frameID == 186 || frameID == 199 || frameID == 187 || frameID == 200 ? 1.36f : 1.5f) * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start, hand class
		if (theInput == BALLERINA && frameID == 252 || frameID == 254) return 1.17f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start, bird class
		if (theInput == BALLERINA && frameID > 240 && frameID <= 260) return (frameID == 241 || frameID == 256 ? 1.31f : 1.1f) * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start, bird class
		if (theInput == BALLERINA && frameID > 280 && frameID <= 300) return (frameID == 283 ? 1.4f : 1.25f) * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start, armadillo class
		if (theInput == BALLERINA && frameID == 397 || frameID == 390) return 1.58f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start, 4-legged class
		if (theInput == BALLERINA && frameID > 381 && frameID <= 400) return (frameID == 388 || frameID == 399 ? 1.28f : 1.4f) * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start, 4-legged class
		if (theInput == SHREC11 && (frameID == 35 || frameID == 45)) return (frameID == 35 ? 1.1f : 1.4f) * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start
		if (theInput == SHREC11 && frameID == 31) return 1.3f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start
		if (theInput == SHREC11 && frameID > 30 && frameID <= 35) return (frameID == 33 ? 1.18f : 1.13f) * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start
		if (theInput == SHREC11 && frameID > 30 && frameID <= 35) return 1.25f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start
		if (theInput == SHREC11 && (frameID == 64 || frameID == 74 || frameID == 75)) return 1.9f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start
		if (theInput == SHREC11 && frameID == 73) return 2.2f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start
		if (theInput == SHREC11 && frameID > 60 && frameID <= 75) return (frameID == 63 ? 1.7f : 1.6f) * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start
		if (theInput == BALLERINA || theInput == SCAPE || theInput == SHREC11 || theInput == TOSCA) return 1.5f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start
		
		if (theInput == DOG_WOLF || theInput == DAVID_VICTORIA || theInput == CAT || theInput == DOG || theInput == CAT_DOG || theInput == DAVID || theInput == VICTORIA || theInput == MICHEAL || theInput == MICHAEL_VICTORIA || theInput == DAVID_MICHAEL || theInput == LION || theInput == CAT_WOLF || theInput == WOLF_HORSE2)
			return 1.5f * 0.6f * sqrt(wholeSurfaceArea / PI);
		if (theInput == JUMPING)
			return (targetMesh ? 1.5f : 1.8f) * 0.6f * sqrt(wholeSurfaceArea / PI);
		if (theInput == BALLERINA || theInput == HORSE)
			return 2.0f * 0.6f * sqrt(wholeSurfaceArea / PI); //7-7 start for horse
			//return 1.5f * 0.6f * sqrt(wholeSurfaceArea / PI); //8-8 start for horse
			//return 2.5f * 0.6f * sqrt(wholeSurfaceArea / PI); //5-5 start for horse
		if (theInput == BALLERINA_JUMPING)
			return (targetMesh ? 1.45f : 1.5f) * 0.6f * sqrt(wholeSurfaceArea / PI);
			//return 1.3f * 0.6f * sqrt(wholeSurfaceArea / PI); //100014-505
		if (theInput == WOLF_HORSE || theInput == DOG_HORSE)
			return 1.8f * 0.6f * sqrt(wholeSurfaceArea / PI);
	}
	else
	{
		if (theInput == BALLERINA)
			return 2 * 0.6f * sqrt(wholeSurfaceArea / PI); //our original formula to give ~5 bases during c2f algo; not good here w/ 0.6 'cos wholeSurfaceArea here is a closed mesh unlike maxAreaBV1
		if (theInput == HORSE) //our original formula gives 5 for horse init, but due to tail, i need 6 for horse; so, use a smaller value
			return 1.8f * 0.6f * sqrt(wholeSurfaceArea / PI);
		if (theInput == DOG_WOLF)
			return (targetMesh ? 2.0f : 1.5f) * 0.6f * sqrt(wholeSurfaceArea / PI);
		if (theInput == JUMPING)
			return 1.5f * 0.6f * sqrt(wholeSurfaceArea / PI);
	}
	
	cout << "undefined input\n";
	exit(0);
}

float Mesh::computeBaseVerts(bool dijkstraFromBases, bool COES, bool computeMu)
{
	//uses our COES sampling to decide baseVerts; for that, modified Dijkstra's Algo, from Topology Matching by Hilaga et al,
	//distributes bases uniformly over the surface; i pick next-base from the sorted verts in gcOrder[] to make it COES (arbitrary pick makes it Hilaga's algo)

	//common/real-life case where baseFromFile = false
	float wholeSurfaceArea = 0.0f, r;
	for (int t = 0; t < (int) tris.size(); t++)
		wholeSurfaceArea += tris[t]->area;
	r = (float) sqrt(wholeSurfaceArea*0.005f); //gives roughly 150 baseVerts

	if (C2Fmode)
	{
		if (computeMu) //temporary ~40 bases to compute Mu with
			//r *= (theInput == JUMPING || theInput == DANCING || theInput == BALLERINA || theInput == BALLERINA_JUMPING ? 3 : 2); //~40 bases will be used to compute Mu values, based on which the initial ~5 bases will be computed, i.e. muOrder replaces the gcOrder; for big inputs like JUMPING use ~20 bases for efficiency
			r *= 3;
		else //no Mu at all
			r = setRadius(wholeSurfaceArea, false);
	}
	else //for PAMI (= EM_ALGO) scrshots of ~80 bases
	{
		//if (theInput == JUMPING) r *= 1.3f; else if (theInput == HORSE) r *= 1.4f; else if (theInput == BALLERINA) r *= 1.35f; else if (theInput == BALLERINA_JUMPING) r *= (targetMesh ? 1.28f : 1.4f); else if (theInput == DOG_WOLF) r *= 1.2f/*1.3f*/; else r *= 1.33f; //for ~80
		r *= 1.35f;
	}

	computeBaseVerts(r, COES);

	if (dijkstraFromBases) //fill the dSpanning[] entries by launching original O((E+V) lgV) dijkstra from each base
	{
		//compute min/max around whole surface, not around a patch
		minGeoDistG = INF;
		maxGeoDistG = -INF;
		cout << "original Dijkstra from each baseVert being computed....\n";
		for (int i = 0; i < (int) baseVerts.size(); i++)
			dijkstraShortestPaths(baseVerts[i]); //dSpanning[]'s now are bad 'cos i used bad min/maxGeoDistG (+-INF)
		cout << "Done!\n\n";

		//normalization
		for (int i = 0; i < (int) baseVerts.size(); i++)
			for (int v = 0; v < (int) verts.size(); v++)
			{
				if (verts[ baseVerts[i] ]->dSpanningUnn[ v ] < minGeoDistG)
					minGeoDistG = verts[ baseVerts[i] ]->dSpanningUnn[ v ];
				if (verts[ baseVerts[i] ]->dSpanningUnn[ v ] > maxGeoDistG)
					maxGeoDistG = verts[ baseVerts[i] ]->dSpanningUnn[ v ];
			}
		//correct normalized dSpanning[] values w/ correct min/maxGeoDistG
		for (int i = 0; i < (int) baseVerts.size(); i++)
			for (int v = 0; v < (int) verts.size(); v++)
				verts[ baseVerts[i] ]->dSpanning[ v ] = (verts[ baseVerts[i] ]->dSpanningUnn[ v ] - minGeoDistG) / (maxGeoDistG - minGeoDistG); //dSpanning normalized now
		//upcoming dSpanning[]s will be normalized correctly inside dijkstraShortestPaths() 'cos min/maxGeoDistG are set accurately now

		cout << "min/max geodesic distance around the whole surface: " << minGeoDistG << " / " << maxGeoDistG << endl;

		if (maxGeoDistG == INF)
		{
			cout << "meshes w/ disconnected verts have INF maxGeoDistG's which is not good for scale normalization, so fix the mesh into 1 component\n";
			exit(0);
		}

		//some more info related to baseVerts, e.g. baseVert.patchIdx, patchArea, ..
		for (int i = 0; i < (int) baseVerts.size(); i++)
		{
			int baseVertIdx = baseVerts[i];
			verts[baseVertIdx]->patchArea = computePatchArea(baseVertIdx, r);
			verts[baseVertIdx]->patchIdx = verts[baseVertIdx]->idxInBaseVerts;
		}
	}

	if (computeMu)
	{
assert(dijkstraFromBases); //so that dSpanning is ready now
		//use baseVerts to determine the v.Mu values via v.Mu = SUM_i [g(v, bi) * bi.area] for each vert v
		cout << "Mu (sum of geodesic from v to all verts) being computed....\n";
		for (int v = 0; v < (int) verts.size(); v++)
		{
			for (int i = 0; i < (int) baseVerts.size(); i++)
				verts[v]->Mu += verts[ baseVerts[i] ]->dSpanning[v] * verts[ baseVerts[i] ]->patchArea; //patchArea weight
			verts[v]->baseCandidate = true; //prepare for the computeBaseVerts() to be called again below
		}
			//verts[v]->Mu /= (int) baseVerts.size(); //for mesh1 and mesh2, # of bases may differ; so, use avg value; redundant actually 'cos Mu's won't be compared b/w 2 meshes; Mu is just for base selection in source and target mesh separately; therefore no normalized Mun kept either
		cout << "done!\n\n";

		//recompute baseVerts but now w.r.t. muOrder, not gcOrder; this effect achieved by simply making the 2nd param false in computeBaseVerts() call below
		for (int i = 0; i < (int) baseVerts.size(); i++)
		{
			verts[ baseVerts[i] ]->base = false;
			delete verts[ baseVerts[i] ]->dSpanning; //'cos these dSpanning were normalized w.r.t. min/maxGeoDistG above; clear here so that if it needs to be used by c2f algo, it'll recalculate its dSpanning[]
			delete verts[ baseVerts[i] ]->dSpanningUnn;
			verts[ baseVerts[i] ]->dSpanning = NULL;
			verts[ baseVerts[i] ]->patchIdx = -1;
		}
		baseVerts.clear(); //'cos new baseVerts'll be created below w.r.t. sorted mu values
		sortMus();

		if (nInitialSamples != -1)
		{
			if (nInitialSamples < 5 || nInitialSamples > 10)
			{
				cout << "initial # of samples suggested to be in [6, 10], promoting 8 to be more resistant to initial symmetric flips\n";
				exit(0);
			}

			int nDesiredInitialBases = nInitialSamples; //# of bases to be produced to perform initial nDesiredInitialBases x nDesiredInitialBases 1-to-1 mapping with
			//auto search will start from this generic value just-below and change r until nDesiredInitialBases x nDesiredInitialBases start found
			float f = 1.5f, sapi06 = 0.6f * sqrt(wholeSurfaceArea / PI);
			r = f * sapi06; //8-8 start

			computeBaseVerts(r, false);

			//for batchRun mode, i'm not in front of the computer; so, it needs to find nInitialBases x nInitialBases automatically to make the initial match, e.g. nDesiredInitialBases = 8
			int nTry = 0, nMaxTry = 100;
			float deltaInc = 0.5f, deltaDec = -deltaInc; //scaled to be compatible w/ f
			while ((int) baseVerts.size() != nDesiredInitialBases && nTry++ < nMaxTry)
			{
				//clear existing base stuff as i'll recompute all bases below hopefully in the amount of nInitialBases
				for (int v = 0; v < (int) verts.size(); v++)
				{
					verts[v]->base = false;
					verts[v]->baseCandidate = true;
					verts[v]->idxInBaseVerts = verts[v]->patchIdx = -1;
				}

				//recompute all bases w/ new r
				if ((int) baseVerts.size() > nDesiredInitialBases) //we overhit the target, so decrease # bases by increasing r
				{
					f += deltaInc;
					deltaDec /= 2.0f; //make the decrements (not increments) smaller (see we're at 5 comment below)
				}
				else //we underhit the target, so increase # bases by decreasing r
				{
					//we're at 5, want to reach 5.5, deltaInc=1; so, we do 5 to 6 here; now, we overhit, so do 6 to 5 above, and inf loop; soln: when we hit 6 here, we decrease deltaInc so that if we enter overhit above, we make 5 to 5.5, not 6;
					f += deltaDec;
					deltaInc /= 2.0f; //make the decrements (not increments) smaller
				}
				baseVerts.clear();
				r = f * sapi06;//this radius hopefully gives exactly 8 bases w/ which initial 8x8 will be performed; if not, try again
				computeBaseVerts(r, false);
			}
		}
		else //no forcing 1-to-1; so, # of bases allowed to be different
		{
			r = setRadius(wholeSurfaceArea, true);
			computeBaseVerts(r, false);
		}

		/////////// copy from if (dijkstraFromBase) block /////////////
		//compute min/max around whole surface, not around a patch, w.r.t. new mu-based bases
		minGeoDistG = INF;
		maxGeoDistG = -INF;
		cout << "original Dijkstra from each mu-based baseVert being computed....\n";
		for (int i = 0; i < (int) baseVerts.size(); i++) //w/ new base w.r.t muOrder[]
			dijkstraShortestPaths(baseVerts[i]); //dSpanning[]'s now are bad 'cos i used bad min/maxGeoDistG (+-INF)
		cout << "Done!\n\n";

		//normalization
		for (int i = 0; i < (int) baseVerts.size(); i++)
			for (int v = 0; v < (int) verts.size(); v++)
			{
				if (verts[ baseVerts[i] ]->dSpanningUnn[ v ] < minGeoDistG)
					minGeoDistG = verts[ baseVerts[i] ]->dSpanningUnn[ v ];
				if (verts[ baseVerts[i] ]->dSpanningUnn[ v ] > maxGeoDistG)
					maxGeoDistG = verts[ baseVerts[i] ]->dSpanningUnn[ v ];
			}
		if (maxGeoDistG == INF)
		{
			cout << "meshes w/ disconnected verts have INF maxGeoDistG's which is not good for scale normalization, so fix the mesh into 1 component\n";
			exit(0);
		}
		//correct normalized dSpanning[] values w/ correct min/maxGeoDistG
		for (int i = 0; i < (int) baseVerts.size(); i++)
			for (int v = 0; v < (int) verts.size(); v++)
				verts[ baseVerts[i] ]->dSpanning[ v ] = (verts[ baseVerts[i] ]->dSpanningUnn[ v ] - minGeoDistG) / (maxGeoDistG - minGeoDistG); //dSpanning normalized now
		//upcoming dSpanning[]s will be normalized correctly inside dijkstraShortestPaths() 'cos min/maxGeoDistG are set accurately now

		cout << "min/max geodesic (by mu-based bases) around the whole surface: " << minGeoDistG << " / " << maxGeoDistG << endl;

		//some more info related to baseVerts, e.g. baseVert.patchIdx, patchArea, ..
		for (int i = 0; i < (int) baseVerts.size(); i++)
		{
			int baseVertIdx = baseVerts[i];
			verts[baseVertIdx]->patchArea = computePatchArea(baseVertIdx, r);
			verts[baseVertIdx]->patchIdx = verts[baseVertIdx]->idxInBaseVerts;
		}
		/////////// copy from if (dijkstraFromBase) block ends /////////////
	} //end of computeMu

	return r;
}

void Mesh::computeBaseVerts(float r, bool COES)
{
	//cont'd computeBaseVerts() above for baseVerts computation

	cout << frameID << "'s base vertices being computed (r = " << r << ")....\n";

	if (COES)
	{
		computeGaussianCurvatures(); //descriptor; in use only for COES sampling
		sortCurvatures();
	}

	int baseVertIdx = getBaseVertIdx(COES);
	vector< int >* vlist = new vector<int>; //first a baseVert enters this list, and then verts in the area/patch of that baseVert

//FILE* fPtr = fopen("base idxs, f___.dat", "w");
	//output dependent: O(k * V), where k = # of baseVerts generated, and V is # of verts
	while (baseVertIdx != -1) //there is still a baseVert whose area to be expanded below
	{
		/////// shortcut Dijkstra w/ slow arrays to create 1 baseVert ///////
		//step 1: initialization
		for (int v = 0; v < (int) verts.size(); v++)
			verts[v]->d = INF;

		//step 2: select a base vert
		verts[baseVertIdx]->base = true;
		verts[baseVertIdx]->d = 0.0f; //so that this baseVert is initially selected from vlist in the loop below
		vlist->push_back(baseVertIdx);		//used below
		baseVerts.push_back(baseVertIdx);	//used later; whole aim is to fill this vector indeed
//fprintf(fPtr, "%d\n", baseVertIdx);
		verts[baseVertIdx]->baseCandidate = false;
		verts[baseVertIdx]->idxInBaseVerts = (int) baseVerts.size()-1;
		if (verts[baseVertIdx]->baseNghbhood)
		{
			delete verts[baseVertIdx]->baseNghbhood;
			verts[baseVertIdx]->baseNghbhood = NULL;
		}
		verts[baseVertIdx]->baseNghbhood = new vector<int>;
		while (! vlist->empty())
		{
			//step 3: extract vert w/ min d value
			float minD = INF;
			int minDidx, v;
			for (int i = 0; i < (int) vlist->size(); i++)
				if (verts[ (*vlist)[i] ]->d < minD) //dereferencing
				{
					v = (*vlist)[i];
					minD = verts[v]->d;
					minDidx = i;
				}
			//erase the element w/ min d value
			vlist->erase(vlist->begin() + minDidx); //ve.erase(begin + 4) erases ve[4]

			//step 4: relaxation loop that updates d values and expands baseVert.area as long as d is small enough
			for (unsigned int ve = 0; ve < verts[v]->edgeList.size(); ve++)
			{
				int e = verts[v]->edgeList[ve];
				int va = (edges[e]->v1i == v ? edges[e]->v2i : edges[e]->v1i);
				if (verts[v]->d + edges[e]->length < verts[va]->d)
				{
					verts[va]->d = verts[v]->d + edges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->baseCandidate = false;
						verts[baseVertIdx]->baseNghbhood->push_back(va);
					}
				}
			}
			for (unsigned int ve = 0; ve < verts[v]->sedgeList.size(); ve++)
			{
				int e = verts[v]->sedgeList[ve];
				int va = (sedges[e]->v1i == v ? sedges[e]->v2i : sedges[e]->v1i);
				if (verts[v]->d + sedges[e]->length < verts[va]->d)
				{
					verts[va]->d = verts[v]->d + sedges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->baseCandidate = false;
						verts[baseVertIdx]->baseNghbhood->push_back(va);
					}
				}
			}
		} //end of vlist.empty()
		/////// shortcut Dijkstra w/ slow arrays to create 1 baseVert ends ///////
#ifdef DBG_DETAIL
		cout << "base" << baseVerts.size()-1 << ": " << baseVertIdx << endl;
#endif
//cout << "base" << baseVerts.size()-1 << ": " << baseVertIdx << "\t" << verts[baseVertIdx]->vertNeighborsSet.size() << endl;

		//baseVertIdx = computeFrontier(secondCall, baseVertIdx); //this is mandatory for matchByFrontiers() algo!!!!
		baseVertIdx = getBaseVertIdx(COES);
	} //end of baseVertIdx
//fclose(fPtr);

	delete vlist;

	cout << "Done! # of base vertices: " << (int) baseVerts.size() << ", for " << (int) verts.size() << " verts\n\n";
}

int Mesh::getBaseVertIdx(bool COES)
{
	//returns the idx of the first vertex that's eligible to be base according to our COES scheme

	for (int v = 0; v < (int) verts.size(); v++)
//info: cpu speculates the if-condition is true and then roll backs the if-block if not true; since COES=true will mostly be true, put this in if-part for efficiency
		if (COES)
		{
			if (verts[ gcOrder[v] ]->baseCandidate) //test the next which comes from gcOrder
				return gcOrder[v];
		}
		else //not COES, so pick the next from muOrder
		{
			//if (verts[v]->baseCandidate) return v; //this old one picks the next arbitrarily, i.e. not from gcOrder or muOrder
			if (verts[ muOrder[v] ]->baseCandidate) //test the next which comes from muOrder
				return muOrder[v];
		}

	return -1; //noone can be base, i.e. termination of computeBaseVerts()
}

void Mesh::initRoots()
{
/*	//initializes roots[] that will be used in rootBasedSymSoln

	roots.clear(); //clear possible push_back(s) above

	//manual root selection (though can be less-accurately autmatized by using critical verts)
	if (theInput == JUMPING)
		roots.push_back(9092); //right hand
	else if (theInput == DANCING)
		roots.push_back(12); //right hand (2117 is right toe)
	else if (theInput == HORSE)
		roots.push_back(2022); //left front foot
	else if (theInput == DANCING_JUMPING)
	{
		//right hand for dancing and jumping man hybrid run
		if (targetMesh) roots.push_back(9092);
		else roots.push_back(12);
	}
	else if (theInput == DOG_WOLF)
	{
		if (targetMesh) roots.push_back(0);
		else roots.push_back(1156);
	}
	else if (theInput == BALLERINA)
	{
		if (frameID == 100015) roots.push_back(4330);
		else if (frameID == 10004) roots.push_back(4471);
		else if (frameID == 10003) roots.push_back(4437);
		else	roots.push_back(0); //just made up 0 to prevent crash w/ a small idx; may not be good as a root
	}
	else if (theInput == WOLF_DANCING)
	{
		if (targetMesh) roots.push_back(2117);
		else roots.push_back(12);
	}
	else
		roots.push_back(0);//just made up 0 to prevent crash w/ a small idx; may not be good as a root

assert((int) roots.size() == 1);
	//cout << "first root of " << (targetMesh ? "target" : "source") << " mesh: " << roots[0] << "\n\n";*/
}

void Mesh::hybridModelsToSameCoordFrame(Mesh* mesh2)
{
	//puts hybrid models, e.g. ballerina, to the same coord frame so that i see them properly during visualization; not required for
	//the correspondence algo at all 'cos it is transformation invariant but required for cool visualization of 2 meshes at the same time
	cout << "hybrid models being scaled..\n"; //translation & rotation are just for display purposes

	//translation and scaling will be applied to put them in the same coordinate frame
	moveCOMtoOrigin();
	mesh2->moveCOMtoOrigin();

	//initial scaling (s) that makes mean variance from origin 1, i.e. scale each model to an appropriate size via avgDistToOrigin
	float avgDistToOrigin = 0.0f, triFactor = 100.0f, * zero = new float[3];
	zero[0] = zero[1] = zero[2] = 0.0f;
	for (int i = 0; i < (int) verts.size(); i++)
		//taking pow2 below makes sum of means from origin to be 1; w/o pow2 it makes sum of distances from origin 1, which i want
		avgDistToOrigin += distanceBetween(verts[i]->coords, zero);
	for (int i = 0; i < (int) verts.size(); i++)
		for (int ii = 0; ii < 3; ii++)
			verts[i]->coords[ii] /= avgDistToOrigin;
	MIN_TRIANGLE_AREA_ALLOWED /= (avgDistToOrigin*triFactor); //w/ shrunk coords, areas will be small as well; be prepared (note that, original mesh passed assert(area >= MIN_TRIANGLE_AREA_ALLOWED); at addTriangle() for all tris); *100 'cos tris not scale linearly to verts
	//same stuff for mesh2
	avgDistToOrigin = 0.0f;
	for (int i = 0; i < (int) mesh2->verts.size(); i++)
		avgDistToOrigin += distanceBetween(mesh2->verts[i]->coords, zero);
	for (int i = 0; i < (int) mesh2->verts.size(); i++)
		for (int ii = 0; ii < 3; ii++)
			mesh2->verts[i]->coords[ii] /= avgDistToOrigin;
	delete zero;
	mesh2->MIN_TRIANGLE_AREA_ALLOWED /= (avgDistToOrigin*triFactor);
	//now 2 models are optimally scale-aligned by Horn; m1 & m2 cannot change after scaling above; so, no redundant update here

/*	//initial rotation for better visualization of jumping man w.r.t. dancing man; rotate him -90 degree in x-axis
	float y, z, //rotated coords
			theta = (theInput == WOLF_DANCING ? 180.0f : -90.0f) * (PI / 180.0f); //radian rotation angle
	if (theInput == BALLERINA || theInput == DOG_WOLF || theInput == TOSCA || theInput == BALLERINA_JUMPING || theInput == CAT || theInput == DOG || theInput == LION || theInput == HORSE2 || theInput == CAT_DOG || theInput == CAT_WOLF || theInput == WOLF_HORSE2 || theInput == DAVID || theInput == VICTORIA || theInput == MICHEAL || theInput == DAVID_VICTORIA || theInput == MICHAEL_VICTORIA || theInput == DAVID_MICHAEL) //no rotation at all
		theta = 0.0f;
	for (int i = 0; i < (int) mesh2->verts.size(); i++) //jumping man is mesh2
	{
		y = mesh2->verts[i]->coords[1]*cos(theta) - mesh2->verts[i]->coords[2]*sin(theta);
		z = mesh2->verts[i]->coords[1]*sin(theta) + mesh2->verts[i]->coords[2]*cos(theta);
		mesh2->verts[i]->coords[1] = y;
		mesh2->verts[i]->coords[2] = z;
	}
	//this rotation slightly ruins the ultimate results only for 10004-10004 case (same input just to see if the result is perfect); so don't bother w/ this weird happening; or move this to Painter just to be safe anyway
	if ((theInput == BALLERINA && frameID == 10004) || //for ballerina, i also need an additional y-rotation
		 theInput == BALLERINA_JUMPING)
	{
		theta = 130.0f * (PI / 180.0f); //radian rotation angle [ballerina]
		for (int i = 0; i < (int) mesh2->verts.size(); i++)
		{
			float x = mesh2->verts[i]->coords[2]*sin(theta) + mesh2->verts[i]->coords[0]*cos(theta);
			z = mesh2->verts[i]->coords[2]*cos(theta) - mesh2->verts[i]->coords[0]*sin(theta);
			mesh2->verts[i]->coords[0] = x;
			mesh2->verts[i]->coords[2] = z;
		}
	}
	if (! (theInput == DOG_WOLF || theInput == BALLERINA || theInput == TOSCA || theInput == DOG || theInput == CAT || theInput == LION || theInput == HORSE2 || theInput == CAT_DOG || theInput == CAT_WOLF || theInput == WOLF_HORSE2 || theInput == DAVID || theInput == VICTORIA || theInput == MICHEAL || theInput == DAVID_VICTORIA || theInput == MICHAEL_VICTORIA || theInput == DAVID_MICHAEL)) //enlarge mesh1 for better visualization, which is skipped for dog-wolf, ballerina, ..
	{
		float ss = 1.3f;
		for (int i = 0; i < (int) verts.size(); i++)
			for (int ii = 0; ii < 3; ii++)
				verts[i]->coords[ii] *= ss;
		MIN_TRIANGLE_AREA_ALLOWED *= (ss*triFactor); //each scale to coords must impose a scale to this value
	}
	if (theInput == BALLERINA_JUMPING)
	{
		//jumping needs enlargement
		float ss = 4.0f;
		for (int i = 0; i < (int) mesh2->verts.size(); i++)
			for (int ii = 0; ii < 3; ii++)
				mesh2->verts[i]->coords[ii] *= ss;
		MIN_TRIANGLE_AREA_ALLOWED = mesh2->MIN_TRIANGLE_AREA_ALLOWED = 0.0f; //never mind this MIN_TRIANGLE_AREA_ALLOWED stuff 'cos i know that they don't have 0-area degenerate trianlges
	}*/

	//r computation needs triangle areas
	updateNormals();
	mesh2->updateNormals();
	//edge lengths also need to be updated since all endpnts have been sclaed above
	updateEdgeLengths();
	mesh2->updateEdgeLengths();
	cout << "Done!\n\n";
}

void Mesh::updateEdgeLengths()
{
	//called after lengths of edges change, but connectivity stays the same, e.g. after scale of hybridModelsToSameCoordFrame()

	edgeMultiSet.clear(); //re-fill this w/ new edge lengths (although order not change after scale, i want to maintain latest length values in edgeMultiSet (for no reason)
	edgeLenTotal = 0.0f;
	minEdgeLen = INF;
	maxEdgeLen = -INF;
	for (unsigned int e = 0; e < edges.size(); e++)
	{
		edges[e]->length = distanceBetween(verts[edges[e]->v1i]->coords, verts[edges[e]->v2i]->coords);
		edgeMultiSet.insert(MultiSetContent(edges[e]->idx, edges[e]->length));
		if (edges[e]->length < minEdgeLen)
			minEdgeLen = edges[e]->length;
		if (edges[e]->length > maxEdgeLen)
			maxEdgeLen = edges[e]->length;
		edgeLenTotal += edges[e]->length;
	}
	cout << "after updateEdgeLengths(): min <= avg <= maxEdgeLen: " << minEdgeLen << " <= " << edgeLenTotal/((int) edges.size()) << " <= " << maxEdgeLen << "\n";
}

void Mesh::computeGeodesicDistsToRoot(bool print)
{
	//computes avgGd2Roots values for all vertices, which is the geodesic distance to root for each vertex; if roots.size > 1, then i have
	//2+ roots and for each vertex i compute the avgGd2Roots, average geodesic distance to all roots

	if (print) cout << "geodesic distance from " << (float) roots.size() << " root(s) to all verts being computed for each root to fill avgGd2Roots..\n";
	for (int r = 0; r < (int) roots.size(); r++)
		dijkstraShortestPaths(roots[r]); //fill root.dSpanning array

	float minVal = INF, maxVal = -INF; //for normalization
	for (int v = 0; v < (int) verts.size(); v++)
	{
		//set avgGd2Roots
		verts[v]->avgGd2Roots = 0.0f;
		for (int r = 0; r < (int) roots.size(); r++)
			verts[v]->avgGd2Roots += //verts[roots[r]]->getGeodesic(v); //should i use normalized considering hybrid runs? hybrids are scaled so shouldn't be a problem but anyway
									  verts[ roots[r] ]->dSpanning[v];
		verts[v]->avgGd2Roots /= (float) roots.size();
		//and some stuff for normalization below [since dSpanning in [0,1], avgGd2Roots / roots.size must be in [0,1] and hence normalization below is redundant but anyway]
		if (verts[v]->avgGd2Roots < minVal)
			minVal = verts[v]->avgGd2Roots;
		if (verts[v]->avgGd2Roots > maxVal)
			maxVal = verts[v]->avgGd2Roots;
	}
	float rangeVal = maxVal - minVal;
	rangeVal = (rangeVal > 0.001f ? rangeVal : maxVal); //prevent divide by zero and small values in denom that exaggerates error
	//normalize avgGd2Roots, e.g. to make it invariant to scaling
	for (int v = 0; v < (int) verts.size(); v++)
		verts[v]->avgGd2Roots = (verts[v]->avgGd2Roots - minVal) / rangeVal; //in [0,1]
	//now each vertex has normalized avgGd2Roots; for baseVerts, i use avg avgGd2Roots of its patch to make it a better representation effect
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int bv = baseVerts[i];
		float total = 0.0f;
		if ((int) verts[bv]->baseNghbhood->size() == 0) //when all verts are baseVerts, baseNghbhood.size=0, and prevent making weight 0 
			continue;
		for (int j = 0; j < (int) verts[bv]->baseNghbhood->size(); j++)
		{
			int v = (*verts[bv]->baseNghbhood)[j];
			total += verts[v]->avgGd2Roots;
		}
		verts[bv]->avgGd2Roots = total / ((int) verts[bv]->baseNghbhood->size());
	}

	if (print) cout << "Done!\n\n";
}

float* gd2r1, * gd2r2; //geodesic distances to root for mesh1 and mesh2 to be filled by matchBySpectralBases() (or avg of geo. dists to roots if roots.size > 1)
float Mesh::computeMSEKD(float** pntSet1, float** pntSet2, int n1, int n2, int k, bool div)
{
	//computes mean squared error (mse) b/w 2 k-d pnts sets of size n1 & n2, that is sum of squared distances of closest pairs

	//before computing mse, i need closest-pnt correspondence b/w mesh1 baseVerts & mesh2 baseVerts
	int* closestPntInSet1 = new int[n2]; //closest pnt in pntSet1 for given pntSet2 vertex (therefore n2 elements)
	float dist; //closest w.r.t. spatial coords
	for (int bv2 = 0; bv2 < n2; bv2++) 
	{
		float minDist = INF;
		for (int bv1 = 0; bv1 < n1; bv1++)
		{
			dist = distanceBetweenKD(pntSet2[bv2], pntSet1[bv1], k);
			if (dist < minDist)
			{
				closestPntInSet1[bv2] = bv1; //closest pnt in pntSet1 for pntSet2 vertex bv2 is bv1
				minDist = dist;
			}
		}
	}
	float mse = 0.0f; //now i can compute this
	for (int bv2 = 0; bv2 < n2; bv2++)
	{
		//squared distance b/w (target) mesh2.bv and its correspondence in mesh1 for this iteration (closestBaseInM1)
		dist = pow(pntSet2[bv2][0] - pntSet1[ closestPntInSet1[bv2] ][0], 2.0f) +
			   pow(pntSet2[bv2][1] - pntSet1[ closestPntInSet1[bv2] ][1], 2.0f) +
			   pow(pntSet2[bv2][2] - pntSet1[ closestPntInSet1[bv2] ][2], 2.0f) + 
			   pow(pntSet2[bv2][3] - pntSet1[ closestPntInSet1[bv2] ][3], 2.0f);
		if (k == 5)
			dist += pow(pntSet2[bv2][4] - pntSet1[ closestPntInSet1[bv2] ][4], 2.0f);
		else if (k == 6)
			dist += (pow(pntSet2[bv2][4] - pntSet1[ closestPntInSet1[bv2] ][4], 2.0f) + pow(pntSet2[bv2][5] - pntSet1[ closestPntInSet1[bv2] ][5], 2.0f));
		if (! rootBasedSymSoln)
			mse += dist; //no sqrt() above, hence dist = squaredDist indeed
		else
			mse += pow(gd2r2[bv2] - gd2r1[ closestPntInSet1[bv2] ], 2.0f); //replace w/ += dist above for symmetry-problem solution
	}
	if (div) //save time by skipping division when unnecessary
		mse /= n2;

	delete [] closestPntInSet1;
	return mse;
}

inline float Mesh::computePatchArea(int bv, float r)
{
	//computes the area represented by baseVert bv, by summing up the tri areas of desired tris implied by its baseNghbhood verts

	for (int t = 0; t < (int) tris.size(); t++)
		tris[t]->usedInPatchAreaComputation = false;
	float area = 0.0f;
	for (int i = 0; i < (int) verts[bv]->baseNghbhood->size(); i++)
	{
		int v = (*verts[bv]->baseNghbhood)[i]; //only nonremoved verts can be added to baseNghbhood, and hence no v.removed check
//		if (//bv == 1010 || bv == 5664 || bv == 3011 || bv == 8165 || bv == 12082 || bv == 14063) //for jumping.431, COES bases
//			bv == 12494 || bv == 8382) //draw this patch only (fails if adjustBaseVerts() called)
//			verts[v]->patchIdx = verts[bv]->idxInBaseVerts;

		for (int tn = 0; tn < (int) verts[v]->triNeighbors.size(); tn++)
		{
			int t = verts[v]->triNeighbors[tn];

			if (! tris[t]->usedInPatchAreaComputation)
			{
				//find 2 verts, v1 & v2, of t other than v
				int v1 = (tris[t]->v1i == v ? tris[t]->v2i : tris[t]->v1i), v2 = tris[t]->v1i;
				if (v2 == v1 || v2 == v)
					v2 = ((tris[t]->v2i != v1 && tris[t]->v2i != v) ? tris[t]->v2i : tris[t]->v3i);
				
				//v is close to base for sure ('cos it's in baseNghbhood); add this triNeigh of v to patchArea if other 2 verts are also close to base
				//if (verts[v1]->d < r && verts[v2]->d < r) old version; wrong 'cos i change d values after computeBaseVerts() done w/ here, e.g. they become # edges for BFS after computeBaseNghbs() done
				//if (resumeLevel || (verts[bv]->dSpanningUnn[v1] <= r && verts[bv]->dSpanningUnn[v2] <= r)) //when resumeFromLevel() is called, dSpanningUnn's are uninited and hence skipped
				if (verts[bv]->dSpanningUnn[v1] <= r && verts[bv]->dSpanningUnn[v2] <= r) //resumeFromLevel() not in use anymore
				{
					area += tris[t]->area;
					tris[t]->usedInPatchAreaComputation = true; //not add it again to area due to another v neighbor to this t
				}
			}
		}
	}

	return area;
}

void Mesh::dijkstraShortestPaths(int sourceVert, float far)
{
	//computes shortest paths from sourceVert to all other verts that are at distance far or less; only the desired values kept in global geodesics and dSpanning[] is deallocated in the end for memo efficiency

	//aim of this function is to fill dSpanning/Unn[] values so that i can use them somewhere else

	//after memo efficient vector<> geodesics tactic failed to clean memo, rolling back to old code w/ dSpanning[]
	if (verts[sourceVert]->dSpanning) //alloc memo and run dijkstra only if memo not already allocated for sourceVert (nonzero, that is non-NULL, values make if (true)), that is, only if dijkstra never been called for sourceVert before
		return;
	verts[sourceVert]->dSpanning = new float[ (int) verts.size() ];
	verts[sourceVert]->dSpanningUnn = new float[ (int) verts.size() ];

	//use Fibonacci heap to get O(VlgV + e); minHeap takes O(VlgV + elgV), simple array takes O(V^2)
	FibHeap* h = new FibHeap();

	/////// original Dijkstra to compute shortest paths from sourceVert to all others ///////
	//initialization
	for (int v = 0; v < (int) verts.size(); v++)
	{
		verts[v]->dSpanned = (v == sourceVert ? 0.0f : INF);
		FibHeapNode* hn = new FibHeapNode(verts[v]->dSpanned, v); //v is a handle from headNode to verts,
		verts[v]->heapNode = hn;								  //and hn is an handle from verts to heapNode
		h->Insert(hn);
		//no delete hn; here 'cos it'll be extracted, used, and deleted below
	}

	while (true)
	{
		//extract vert w/ min dSpanned value
//not doing the brute force O(V) algo to extract min (useful for dense graph, i.e. simple array usage, which is not my case)
		FibHeapNode* minNode = h->ExtractMin(); //fast O(lgV) priority queue algo
		if (minNode == NULL)
			break; //no more verts w/ unknown shortest dist; so, we're done
		int minDidx = minNode->element;
		float minD = minNode->key;
		verts[sourceVert]->dSpanningUnn[minDidx] = minD; //remember original value before normalization in the end of this function
		delete minNode; //new hn above is deleted here to save memo [info: delete minNode; automatically calls minNode->~FibHeapNode(), i.e. its destructor]

		//minD is an ascending value; so, when i reach far enough verts, i can safely skip the rest since all verts visited so far have finalized their shortest paths,
		//which means any vertex that i visit after this pnt will be have dist >= minD to sourceVert
		if (minD > far)  //far enough vert reached
			break;

		//relax each edge, including shortcuts, incident to minDidx
		for (unsigned int ve = 0; ve < verts[minDidx]->edgeList.size(); ve++)
		{
			int e = verts[minDidx]->edgeList[ve];
			int va = (edges[e]->v1i == minDidx ? edges[e]->v2i : edges[e]->v1i);
			if (verts[minDidx]->dSpanned + edges[e]->length < verts[va]->dSpanned)
			{
				verts[va]->dSpanned = verts[minDidx]->dSpanned + edges[e]->length; //relaxation
				h->DecreaseKey(verts[va]->heapNode, verts[va]->dSpanned); //worst O(lgV), amortized O(1)
			}
		}
		for (unsigned int ve = 0; ve < verts[minDidx]->sedgeList.size(); ve++)
		{
			int e = verts[minDidx]->sedgeList[ve];
			int va = (sedges[e]->v1i == minDidx ? sedges[e]->v2i : sedges[e]->v1i);
			if (verts[minDidx]->dSpanned + sedges[e]->length < verts[va]->dSpanned)
			{
				verts[va]->dSpanned = verts[minDidx]->dSpanned + sedges[e]->length; //relaxation
				h->DecreaseKey(verts[va]->heapNode, verts[va]->dSpanned); //worst: O(lgV), amortized O(1)
			}
		}//*/
	} //end of while (true)
	/////// original Dijkstra to compute shortest paths from sourceVert to all others ///////

	//normalized dSpanning[] from dSpanningUnn[]
	float denom = maxGeoDistG - minGeoDistG; //use the global min/maxGeoDistG that spans the whole surface, not a patch
	for (int i = 0; i < (int) verts.size(); i++)
		verts[sourceVert]->dSpanning[ i ] = (verts[sourceVert]->dSpanningUnn[i] - minGeoDistG) / denom; //don't forget to change minGeoDist to minGeoDistG if you change denom

//this memo leakage took weeks to see :( i insert all verts to heap above, and delete only some of them above if far != INF; so, delete the rest here
//handling it via if (dSpanningUnn > far) continue; before the Insert() above; but can't do that easily 'cos dSpanningUnn'll be set here; alternatively, keep ptrs to
//hn's by making FibHeapNode** hns = new FibHeapNode*[verts.size()], and hns[i] = hn stuff; then delete hns[*] w/ a loop here but again complex; so, force far == INF
assert(far == INF);

	delete h;
}

void Mesh::computeCorrespondenceError(Mesh* mesh2)
{
	//computes the correspondence error on this model using the info available in corresponding pre-computed file

	cout << "correspondence error on this model (" << frameID << ") being computed....\n";

	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 /*&& theInput != SCAPE*/ && theInput != TOSCA) //= if (hybridRun)
		hybridModelsToSameCoordFrame(mesh2);

	//error = sum of geodesic distances, e.g. g(0, corresp(0)) + g(1, corresp(1)); note that, this sum is 0 if corresp(i) = i since
	//i know the ground-truth values as corresp(i) = i for the jumping man data (ground-truth unknown for lowReso models)
	int k = 6;
	prepareForGeodesicComputation(); //shortcut edge existence slows down algo due to O((E+V)lgV) dijkstra, e.g. 2min faster w/o shortcuts for dancing and for jumping; but i saw that shortcut'ed output is better; so, keep it
	float rForBase1 = computeBaseVerts(true, true, false); //make the 3rd  param true for ~10 bases
	computeBaseGeodesicAffinityMtrx(k);

	//same stuff for mesh2
	mesh2->prepareForGeodesicComputation();
	float rForBase2 = mesh2->computeBaseVerts(true, true, false);
	mesh2->computeBaseGeodesicAffinityMtrx(k);

	//bigger of normalized r1 and normalized r2 (not a big deal 'cos both values are close anyway; use the bigger to see smaller costs, both for me and for Zhang)
	normalizedR1 = rForBase1 / maxGeoDistG; //div isometryCost (in [0,1]) by this to get it in terms of r; if r = maxGeoDistG (which can't happen but anyway), normalizedR is 1

	FILE* fPtr;
	char fName[200];
	sprintf(fName, "..\\..\\tools\\specCorr\\specCorr\\meshes\\C %d-%d.dat", frameID, mesh2->frameID);
	if (! (fPtr = fopen(fName, "r"))) //for Zhang's results
	{
		cout << fName << " not found\n";
		exit(0);
	}
	
	//these move spatial (i.e. non-spectral) center-of-mass to origin which provides a better viewing
	moveCOMtoOrigin();
	mesh2->moveCOMtoOrigin();
	
	int n1, oneToMany = 0;
	fscanf(fPtr, "%d\n", &n1); //baseVerts.size for mesh1
//due to 1-to-1 nature of my alternating results, if a base remains unmatched in the end it is not written to file; so, n1 = 81 & baseVerts.size = 82 may happen; so, no assert
#ifdef DBG_DETAIL
	cout << n1 << " = " << (int) baseVerts.size() << "\tnormalized r1 = " << normalizedR1 << endl;
#endif
assert(n1 == (int) baseVerts.size()); //so that file and this run is consistent; necessary but not sufficient condition
	
interestingMatches.clear();
	while (! feof(fPtr))
	{
		int i, ci; //i and corresp(i)
		fscanf(fPtr, "%d\t%d\n", &i, &ci);
		i = baseVerts[i-1]; //C*.dat for Zhang has idx to baseVerts[], starting from 1, not 0, and now i becomes idx to verts[]
		ci = mesh2->baseVerts[ci-1]; //being careful w/ -1 here for Zhang's case
		
#ifdef DBG_DETAIL
		cout << i << "\t" << ci << "\n";
#endif
		if (mesh2->verts[ci]->matchIdx != -1)
			oneToMany++;
		verts[i]->matchIdx = ci;
		mesh2->verts[ci]->matchIdx = i; //redundant dual op
interestingMatches.push_back(i);interestingMatches.push_back(ci);
assert(verts[i]->base && mesh2->verts[ci]->base);
	}
	fclose(fPtr);
#ifdef DBG_DETAIL
	cout << "at most " << oneToMany << " mesh2 bases has many matches in mesh1, i.e. 1-to-many looking from mesh1 to mesh2\n";
#endif
	//set correspondence list, safeCorresps, using matchIdx's above
	refillSafeCorresp(mesh2);
	setCorrespondenceErrs(mesh2, rForBase1, -2);
	setExtremeCorrespondences(rForBase1, true);
cout << "when worst match is at a 1-to-many vertex, its final matchCostI may be overwritten to a smaller value (of another good match); so, worst cost not reliable for zhang's results\n";
}

void Mesh::computeCorrespondenceErrorC2F(Mesh* mesh2, bool compareWithZhang, bool justDisplay)
{
	//similar to computeCorrespondenceError() but no spectral embedding here and also no computeBaseVerts() here 'cos bases are not computed via computeBaseVerts() in c2f, i.e. used computeTmpBaseVerts2() was in use

	cout << "correspondence error on this model (" << frameID << ") being computed....\n";

	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 /*&& theInput != SCAPE*/ && theInput != TOSCA) //= if (hybridRun)
		hybridModelsToSameCoordFrame(mesh2);

	//error = sum of geodesic distances, e.g. g(0, corresp(0)) + g(1, corresp(1)); note that, this sum is 0 if corresp(i) = i since
	//i know the ground-truth values as corresp(i) = i for the jumping man data (ground-truth unknown for lowReso models)
	if (! justDisplay)
	{
		prepareForGeodesicComputation(); //shortcut edge existence slows down algo due to O((E+V)lgV) dijkstra, e.g. 2min faster w/o shortcuts for dancing and for jumping; but i saw that shortcut'ed output is better; so, keep it
		mesh2->prepareForGeodesicComputation();
	}

	FILE* fPtr, * fPtrBases1, * fPtrBases2, * fEvalPtr;
	char fName[300], fNameBases1[300], fNameBases2[300], fEvalName[300], * type = (theInput == JUMPING ? "jumping" : "hybrid");
	vector< int > bases1, bases2; //from file for zhang, i.e. match 7-3 in Zhang's file translates to bases1[6]-bases2[2] here
	if (compareWithZhang)
	{
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\%s\\subdivide_at_all_times\\%d-%d\\C %d-%d level5.dat", type, frameID, mesh2->frameID, frameID, mesh2->frameID);
		sprintf(fNameBases1, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\%s\\subdivide_at_all_times\\%d-%d\\bases_%d (level5).dat", type, frameID, mesh2->frameID, frameID);
		sprintf(fNameBases2, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\%s\\subdivide_at_all_times\\%d-%d\\bases_%d (level5).dat", type, frameID, mesh2->frameID, mesh2->frameID);
	}
	else
	{
		//sprintf(fName, "output\\dense\\%s\\subdivide_at_all_times\\%d-%d\\%d-%d level12.dat", type, frameID, mesh2->frameID, frameID, mesh2->frameID);
		if (theInput == SCAPE)
		{
			sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SCAPE\\%s%d-%d level%d.dat", (collapsed ? "collapsed\\" : ""), frameID, mesh2->frameID, levelToEvaluate);
			sprintf(fEvalName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SCAPE\\%sresults.dat", (collapsed ? "collapsed\\" : ""));
		}
		else if (theInput == BALLERINA)
		{
			sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\Watertight\\%s%d-%d level%d.dat", (collapsed ? "collapsed\\" : ""), frameID, mesh2->frameID, levelToEvaluate);
			sprintf(fEvalName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\Watertight\\%sresults.dat", (collapsed ? "collapsed\\" : ""));
		}
		else if (theInput == TOSCA)
		{
			sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\%s%d-%d level%d.dat", (collapsed ? "collapsed\\" : ""), frameID, mesh2->frameID, levelToEvaluate); //*interpolated.dat full-dense map output is just for visualization; here, load the biggest level available and use at most 5000 matches on it for evaluation
			sprintf(fEvalName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\%sresults across humans.dat", (collapsed ? "collapsed\\" : ""));
		}
		else if (theInput == SHREC11)
		{
			sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\%s%d-%d level%d.dat", (collapsed ? "collapsed\\" : ""), frameID, mesh2->frameID, levelToEvaluate); //*interpolated.dat full-dense map output is just for visualization; here, load the biggest level available and use at most 5000 matches on it for evaluation
			sprintf(fEvalName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\%sresults.dat", (collapsed ? "collapsed\\" : ""));
		}
		else
			exit(0);
	}
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}
	if (compareWithZhang && ! (fPtrBases1 = fopen(fNameBases1, "r")))
	{
		cout << fNameBases1 << " not found\n";
		exit(0);
	}
	if (compareWithZhang && ! (fPtrBases2 = fopen(fNameBases2, "r")))
	{
		cout << fNameBases2 << " not found\n";
		exit(0);
	}
	fEvalPtr = fopen(fEvalName, "a"); //append this current evaluation to the result file

	//these move spatial (i.e. non-spectral) center-of-mass to origin which provides a better viewing
	moveCOMtoOrigin();
	mesh2->moveCOMtoOrigin();

	int level, nBases1 = 0;
	if (compareWithZhang)
	{
		fscanf(fPtrBases1, "%f\t%f\t%f\t%d\n", &r1, &minGeoDistG, &maxGeoDistG, &level);
		fscanf(fPtrBases2, "%f\t%f\t%f\t%d\n", &r2, &mesh2->minGeoDistG, &mesh2->maxGeoDistG, &level);
		while (! feof(fPtrBases1)) //fill bases1
		{
			int bv;
			fscanf(fPtrBases1, "%d\n", &bv);
			bases1.push_back(bv);
		}
		while (! feof(fPtrBases2)) //fill bases2
		{
			int bv;
			fscanf(fPtrBases2, "%d\n", &bv);
			bases2.push_back(bv);
		}
		//done w/ bases*.dat file
		fclose(fPtrBases1);
		fclose(fPtrBases2);
		cout << (int) bases1.size() << " vs. " << (int) bases2.size() << " will be matched by Zhang\n";
		//fetch the # of bases in the first line of Zhang's corresp output
		fscanf(fPtr, "%d\n", &nBases1);
assert(nBases1 == (int) bases1.size()); //Zhang's file and my bases file consistent
	}
	else
	{
		fscanf(fPtr, "%f\t%f\t%d\n", &r1, &r2, &level); //radius on 2 meshes and level
		fscanf(fPtr, "%f\t%f\t%f\t%f\n", &minGeoDistG, &maxGeoDistG, &mesh2->minGeoDistG, &mesh2->maxGeoDistG);
	}
	cout << "min/max geodesic distance around the whole surface [source]: " << minGeoDistG << " / " << maxGeoDistG <<
			"\nmin/max geodesic distance around the whole surface [target]: " << mesh2->minGeoDistG << " / " << mesh2->maxGeoDistG << "\nradii:\t" << r1 << " & " << r2 << endl;

	//bigger of normalized r1 and normalized r2 (not a big deal 'cos both values are close anyway; use the bigger to see smaller costs, both for me and for Zhang)
	normalizedR1 = r1 / maxGeoDistG; //div isometryCost (in [0,1]) by this to get it in terms of r of current (level's) patches; if r = maxGeoDistG (which can't happen but anyway), normalizedR is 1

interestingMatches.clear();
	int earlyBreak = (collapsed ? -1 : 5000); //evaluate first 5000 matches for efficiency (-1 ignores earlyBreak stuff and makes the whole most accurate but slow run)
	char* breakStr = (collapsed ? ": no earlyBreak for collapseds" : "");
	while (! feof(fPtr))
	{
		int i, ci; //i and corresp(i)
		fscanf(fPtr, "%d\t", &i);

		if (! compareWithZhang && i == -2)
			break;
		
		fscanf(fPtr, "%d\n", &ci);

		if (compareWithZhang)
		{
			i = bases1[i-1];
			ci = bases2[ci-1]; //convert 3-7 in Zhang to bases1[3]-bases2[7] here
		}
#ifdef DBG_DETAIL
		if ((int) safeCorresp.size() % 100 == 0)
			cout << i << "\t" << ci << "\n";
#endif
		safeCorresp.push_back(i);
		safeCorresp.push_back(ci); //getMatchingLinesSepDense() & setCorrespondenceErrsTmp() traverses safeCorresp[]
interestingMatches.push_back(i);interestingMatches.push_back(ci); //for spheres

		if (! justDisplay) //correspondence error will be printed as well; so, prepare bases and dSpannings
		{			
			dijkstraShortestPaths(i); //dSpanning[]'s now are good 'cos min/maxGeoDistG fread above
			mesh2->dijkstraShortestPaths(ci);
			nBases1++;
			if (nBases1 % 2500 == 1)
				cout << nBases1 << "*2 th dijkstra completed (earlyBreak = " << earlyBreak << breakStr << ")\n";
		}

		//fill baseVerts just for counting w/o duplicates in order to be able to say n vs. m mapping in the end
		bool exists = false;
		for (int j = 0; j < (int) baseVerts.size(); j++)
		{
			if (baseVerts[j] == i)
			{
				exists = true;
				break;
			}
		}
		if (! exists)
			baseVerts.push_back(i);
		exists = false;
		for (int j = 0; j < (int) mesh2->baseVerts.size(); j++) //actually duplicates in mesh2.baseVerts not hurt at all; so skip here later for speed up (if any); i guess only levelInfoToFile gets effected w/ duplicates but it is handleable
		{
			if (mesh2->baseVerts[j] == ci)
			{
				exists = true;
				break;
			}
		}
		if (! exists)
			mesh2->baseVerts.push_back(ci);

		if (nBases1 == earlyBreak) //to speed up evaluations of uncollapsed/original meshes, use at most 12500 matches, i.e. geodesic distances
			break;
	} //end of feof

	if (! justDisplay) //correspondence error will be printed as well
	{
		setCorrespondenceErrsTmp(mesh2, true);
		setExtremeCorrespondences(r1, true, true);
	}
	else
	{
		worstMatchI = worstMatchG = -1; //actually, i have this info in the file as well; so this else is totally redundant
	}

	cout << (int) baseVerts.size() << " vs. " << mesh2->baseVerts.size() << " mapping\n";
	char str[250];
	sprintf(str, (earlyBreak == -1 ? "" : "\tfirst %d matches used"), earlyBreak);
	fprintf(fEvalPtr, "======== %d-%d (level %d%s) ========\n%d vs. %d mapping\n", frameID, mesh2->frameID, levelToEvaluate, str, (int) baseVerts.size(), mesh2->baseVerts.size());

/*SCAPE output files not have ground-truth info; so, freading them will ruin the computed ground-truh stuff above (by setCorrespondenceErrsTmp())
	//errors from file
	fscanf(fPtr, "%f\t%f\n", &groundTruthCost, &isometryCost); //avg
	int tmp;
	fscanf(fPtr, "%f\t\t%d\t%d\n", &maxCostGg, &worstMatchG, &tmp); //worst ground truth and the related match it is attached to
	if (worstMatchG != -1)
		verts[worstMatchG]->worstGin2 = tmp;
	fscanf(fPtr, "%f\t\t%d\t%d\n", &maxCostIg, &worstMatchI, &tmp); //worst isometry and the related match it is attached to
	if (worstMatchI != -1)
		verts[worstMatchI]->worstIin2 = tmp;
*/
	fclose(fPtr);
	cout << "avg costs:\n"; //r: given in terms of current patch radius, n: normalized by max geo dist, none: given as is
	if (! collapsed && (theInput == JUMPING || theInput == DANCING || theInput == HORSE ||
		theInput == SCAPE || theInput == TOSCA || theInput == SHREC11)) //we've gound-truth info as well
	{
		cout << "ground-truth cost: " << groundTruthCost << " (= " << groundTruthCost/r1 << "r, = " << groundTruthCost/maxGeoDistG << "n)\nisometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n"; //ground-truth costs not in [0,1], so use unnormalized r
		fprintf(fEvalPtr, "avg iso, ground\t=\t%f, %fn\n", isometryCost, groundTruthCost/maxGeoDistG);
	}
	else
	{
		cout << "loaded isometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n";
		fprintf(fEvalPtr, "avg iso\t=\t%f\n", isometryCost);
	}
	

	cout << "worst costs:\n";
	if (! collapsed && (theInput == JUMPING || theInput == DANCING || theInput == HORSE ||
		theInput == SCAPE || theInput == TOSCA || theInput == SHREC11))
	{
		cout << "ground-truth: " << maxCostGg << /*"/" << minCostG << */"\t" << maxCostGg/r1 << "r"/*/" << minCostG/rForBase1 << "r*/"\t" << maxCostGg/maxGeoDistG << "n"/*/ << minCostG/maxGeoDistG << "n*/"\t" << worstMatchG << "->" << verts[worstMatchG]->worstGin2 << "\n" //ground-truth costs not in [0,1], so use unnormalized r
				<< "isometry: " << maxCostIg << /*"/" << minCostI << */"\t" << maxCostIg/normalizedR1 << "r"/*/" << minCostI/normalizedR1 << "r*/"\t\t" << worstMatchI << "->" << verts[worstMatchI]->worstIin2 << "\n\n"; //isometry costs in [0,1], so use normalized r
		fprintf(fEvalPtr, "worst iso, ground\t=\t%f, %fn\n", maxCostIg, maxCostGg/maxGeoDistG);
		fprintf(fEvalPtr, "worst iso, ground\t=\t%d->%d, %d->%d\n", worstMatchI, verts[worstMatchI]->worstIin2, worstMatchG, verts[worstMatchG]->worstGin2);
	}
	else
	{
		cout << "isometry: " << maxCostIg << /*"/" << minCostI << */"\t" << maxCostIg/normalizedR1 << "r"/*/" << minCostI/normalizedR1 << "r*/"\t\t" << worstMatchI << "->" << verts[worstMatchI]->worstIin2 << "\n\n"; //ground-truth n/a in hybrid run
		fprintf(fEvalPtr, "worst iso\t=\t%f\n", maxCostIg);
		fprintf(fEvalPtr, "worst iso\t=\t%d->%d\n", worstMatchI, verts[worstMatchI]->worstIin2);
	}
	fclose(fEvalPtr);

	cout << "done!\n\n";
}
void Mesh::computeCorrespondenceErrorBIM(Mesh* mesh2, bool justDisplay)
{
	//same as computeCorrespondenceErrorC2F() except this blended intrinsic maps output comes in number-per-line format; in our C2F output, 2 number per line representing 1 match; here, 2 consecutive lines make 1 match

	cout << "correspondence error on this model (" << frameID << ") being computed....\n";

	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 /*&& theInput != SCAPE*/ && theInput != TOSCA) //= if (hybridRun)
		hybridModelsToSameCoordFrame(mesh2);

	//error = sum of geodesic distances, e.g. g(0, corresp(0)) + g(1, corresp(1)); note that, this sum is 0 if corresp(i) = i since
	//i know the ground-truth values as corresp(i) = i for the jumping man data (ground-truth unknown for lowReso models)
	if (! justDisplay)
	{
		prepareForGeodesicComputation(); //shortcut edge existence slows down algo due to O((E+V)lgV) dijkstra, e.g. 2min faster w/o shortcuts for dancing and for jumping; but i saw that shortcut'ed output is better; so, keep it
		mesh2->prepareForGeodesicComputation();
	}

	FILE* fPtr, * fEvalPtr, * fC2Fptr; //open C2F output file as well to learn maxGeoDist stuff
	char fName[300], fEvalName[300], fC2Fname[300];
	if (theInput == SCAPE)
	{
		sprintf(fC2Fname, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SCAPE\\%d-%d level%d.dat", frameID, mesh2->frameID, levelToEvaluate);
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SCAPE\\BIM\\mesh%s%d\\Blended_DenseMap_mesh%s%d_to_mesh%s%d.dense.gaps.map", (frameID < 10 ? "00" : "0"), frameID, (frameID < 10 ? "00" : "0"), frameID, (mesh2->frameID < 10 ? "00" : "0"), mesh2->frameID);
		sprintf(fEvalName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SCAPE\\BIM\\resultsBIM.dat");
	}
	else if (theInput == BALLERINA)
	{
		sprintf(fC2Fname, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\Watertight\\%d-%d level%d.dat", frameID, mesh2->frameID, levelToEvaluate);
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\Watertight\\BIM\\%d\\Blended_DenseMap_%d_to_%d.dense.gaps.map", frameID, frameID, mesh2->frameID);
		sprintf(fEvalName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\Watertight\\BIM\\resultsBIM.dat");
	}
	else if (theInput == TOSCA)
	{
		sprintf(fC2Fname, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\%d-%d level%d.dat", frameID, mesh2->frameID, levelToEvaluate);
		char* obj = "cat", * obj2 = ""; //obj2 required only for across humans
		int frameID1 = frameID, frameID2 = mesh2->frameID; //frameID and mesh2->frameID values normalized to blended input ids
		if (frameID >= 11 && frameID <= 17)
		{
			obj = "david";
			if (frameID == 11) frameID1 = 0;	if (frameID == 12) frameID1 = 1;	if (frameID == 13) frameID1 = 6;	if (frameID == 14) frameID1 = 10;	if (frameID == 15) frameID1 = 11;	if (frameID == 16) frameID1 = 12;	if (frameID == 17) frameID1 = 13;
			if (mesh2->frameID == 11) frameID2 = 0;	if (mesh2->frameID == 12) frameID2 = 1;	if (mesh2->frameID == 13) frameID2 = 6;	if (mesh2->frameID == 14) frameID2 = 10;	if (mesh2->frameID == 15) frameID2 = 11;	if (mesh2->frameID == 16) frameID2 = 12;	if (mesh2->frameID == 17) frameID2 = 13;
		}
		else if (frameID >= 27 && frameID <= 34)
		{
			obj = "horse";
			if (frameID == 27) frameID1 = 0;	if (frameID == 34) frameID1 = 17;	if (frameID == 28) frameID1 = 5;	if (frameID == 29) frameID1 = 6;	if (frameID == 30) frameID1 = 7;	if (frameID == 31) frameID1 = 10;	if (frameID == 33) frameID1 = 15;
			if (mesh2->frameID == 27) frameID2 = 0;	if (mesh2->frameID == 34) frameID2 = 17;	if (mesh2->frameID == 28) frameID2 = 5;	if (mesh2->frameID == 29) frameID2 = 6;	if (mesh2->frameID == 30) frameID2 = 7;	if (mesh2->frameID == 31) frameID2 = 10;	if (mesh2->frameID == 33) frameID2 = 15;
		}
		else if (frameID >= 35 && frameID <= 54)
		{
			obj = "michael";
			frameID1 = frameID - 35;
			frameID2 = mesh2->frameID - 35;
		}
		else if (frameID >= 55 && frameID <= 66)
		{
			obj = "victoria";
			if (frameID == 55) frameID1 = 0;	if (frameID == 56) frameID1 = 1;	if (frameID == 57) frameID1 = 2;	if (frameID == 58) frameID1 = 4;	if (frameID == 66) frameID1 = 25;	if (frameID == 59) frameID1 = 7;	if (frameID == 60) frameID1 = 10;	if (frameID == 61) frameID1 = 12;	if (frameID == 62) frameID1 = 17;	if (frameID == 63) frameID1 = 21;	if (frameID == 64) frameID1 = 23;	if (frameID == 65) frameID1 = 24;
			if (mesh2->frameID == 55) frameID2 = 0;	if (mesh2->frameID == 56) frameID2 = 1;	if (mesh2->frameID == 57) frameID2 = 2;	if (mesh2->frameID == 58) frameID2 = 4;	if (mesh2->frameID == 66) frameID2 = 25;	if (mesh2->frameID == 59) frameID2 = 7;	if (mesh2->frameID == 60) frameID2 = 10;	if (mesh2->frameID == 61) frameID2 = 12;	if (mesh2->frameID == 62) frameID2 = 17;	if (mesh2->frameID == 63) frameID2 = 21;	if (mesh2->frameID == 64) frameID2 = 23;	if (mesh2->frameID == 65) frameID2 = 24;
		}
		obj2 = obj;

//enable this block only if across humans in action which can only have victoria as target
if (mesh2->frameID >= 55 && mesh2->frameID <= 66) //and if across humans
{
	obj2 = "victoria"; //mesh1 values already set above
	if (mesh2->frameID == 55) frameID2 = 0;	if (mesh2->frameID == 56) frameID2 = 1;	if (mesh2->frameID == 57) frameID2 = 2;	if (mesh2->frameID == 58) frameID2 = 4;	if (mesh2->frameID == 66) frameID2 = 25;	if (mesh2->frameID == 59) frameID2 = 7;	if (mesh2->frameID == 60) frameID2 = 10;	if (mesh2->frameID == 61) frameID2 = 12;	if (mesh2->frameID == 62) frameID2 = 17;	if (mesh2->frameID == 63) frameID2 = 21;	if (mesh2->frameID == 64) frameID2 = 23;	if (mesh2->frameID == 65) frameID2 = 24;
}//*/

		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\BIM\\across humans\\%s%d\\Blended_DenseMap_%s%d_to_%s%d.dense.gaps.map", obj, frameID1, obj, frameID1, obj2, frameID2);
		sprintf(fEvalName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\BIM\\across humans\\resultsBIM across humans.dat");
	}
	else if (theInput == SHREC11)
	{
		sprintf(fC2Fname, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\%d-%d level%d.dat", frameID, mesh2->frameID, levelToEvaluate);
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\BIM\\%d\\Blended_DenseMap_%d_to_%d.dense.gaps.map", frameID, frameID, mesh2->frameID);
		sprintf(fEvalName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\BIM\\resultsBIM.dat");
	}
	else
		exit(0);

	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}
	if (! (fC2Fptr = fopen(fC2Fname, "r")))
	{
		cout << fC2Fname << " not found\n";
		exit(0);
	}
	fEvalPtr = fopen(fEvalName, "a"); //append this current evaluation to the result file

	//these move spatial (i.e. non-spectral) center-of-mass to origin which provides a better viewing
	moveCOMtoOrigin();
	mesh2->moveCOMtoOrigin();

	//C2F output file to learn maxGeoDist stuff
	int level, nBases1 = 0;
	fscanf(fC2Fptr, "%f\t%f\t%d\n", &r1, &r2, &level); //radius on 2 meshes and level
	fscanf(fC2Fptr, "%f\t%f\t%f\t%f\n", &minGeoDistG, &maxGeoDistG, &mesh2->minGeoDistG, &mesh2->maxGeoDistG);

	cout << "min/max geodesic distance around the whole surface [source]: " << minGeoDistG << " / " << maxGeoDistG <<
			"\nmin/max geodesic distance around the whole surface [target]: " << mesh2->minGeoDistG << " / " << mesh2->maxGeoDistG << "\nradii:\t" << r1 << " & " << r2 << endl;

	//bigger of normalized r1 and normalized r2 (not a big deal 'cos both values are close anyway; use the bigger to see smaller costs, both for me and for Zhang)
	normalizedR1 = r1 / maxGeoDistG; //div isometryCost (in [0,1]) by this to get it in terms of r of current (level's) patches; if r = maxGeoDistG (which can't happen but anyway), normalizedR is 1

interestingMatches.clear();
	int earlyBreak = 5000; //evaluate first 5000 matches for efficiency
	while (! feof(fPtr))
	{
		int i, ci; //i and corresp(i)
		fscanf(fPtr, "%d\n", &i);
		fscanf(fPtr, "%d\n", &ci); //2 consecutive lines make 1 match

#ifdef DBG_DETAIL
		if ((int) safeCorresp.size() % 100 == 0)
			cout << i << "\t" << ci << "\n";
#endif
		safeCorresp.push_back(i);
		safeCorresp.push_back(ci); //getMatchingLinesSepDense() & setCorrespondenceErrsTmp() traverses safeCorresp[]
interestingMatches.push_back(i);interestingMatches.push_back(ci); //for spheres

		if (! justDisplay) //correspondence error will be printed as well; so, prepare bases and dSpannings
		{			
			dijkstraShortestPaths(i); //dSpanning[]'s now are good 'cos min/maxGeoDistG fread above
			mesh2->dijkstraShortestPaths(ci);
			nBases1++;
			if (nBases1 % 2500 == 1)
				cout << nBases1 << "*2 th dijkstra completed (earlyBreak = " << earlyBreak << ")\n";
		}

		//fill baseVerts just for counting w/o duplicates in order to be able to say n vs. m mapping in the end
		bool exists = false;
		for (int j = 0; j < (int) baseVerts.size(); j++)
		{
			if (baseVerts[j] == i)
			{
				exists = true;
				break;
			}
		}
		if (! exists)
			baseVerts.push_back(i);
		exists = false;
		for (int j = 0; j < (int) mesh2->baseVerts.size(); j++) //actually duplicates in mesh2.baseVerts not hurt at all; so skip here later for speed up (if any); i guess only levelInfoToFile gets effected w/ duplicates but it is handleable
		{
			if (mesh2->baseVerts[j] == ci)
			{
				exists = true;
				break;
			}
		}
		if (! exists)
			mesh2->baseVerts.push_back(ci);

		if (nBases1 == earlyBreak) //to speed up evaluations, use at most 12500 matches, i.e. geodesic distances
			break;
	} //end of feof

	if (! justDisplay) //correspondence error will be printed as well
	{
		setCorrespondenceErrsTmp(mesh2, true);
		setExtremeCorrespondences(r1, true, true);
	}
	else
	{
		worstMatchI = worstMatchG = -1; //actually, i have this info in the file as well; so this else is totally redundant
	}

	cout << (int) baseVerts.size() << " vs. " << mesh2->baseVerts.size() << " mapping\n";	
	fprintf(fEvalPtr, "======== %d-%d ========\n%d vs. %d mapping\n", frameID, mesh2->frameID, (int) baseVerts.size(), mesh2->baseVerts.size());

/*SCAPE output files not have ground-truth info; so, freading them will ruin the computed ground-truh stuff above (by setCorrespondenceErrsTmp())
	//errors from file
	fscanf(fPtr, "%f\t%f\n", &groundTruthCost, &isometryCost); //avg
	int tmp;
	fscanf(fPtr, "%f\t\t%d\t%d\n", &maxCostGg, &worstMatchG, &tmp); //worst ground truth and the related match it is attached to
	if (worstMatchG != -1)
		verts[worstMatchG]->worstGin2 = tmp;
	fscanf(fPtr, "%f\t\t%d\t%d\n", &maxCostIg, &worstMatchI, &tmp); //worst isometry and the related match it is attached to
	if (worstMatchI != -1)
		verts[worstMatchI]->worstIin2 = tmp;
*/
	fclose(fPtr);
	cout << "avg costs:\n"; //r: given in terms of current patch radius, n: normalized by max geo dist, none: given as is
	if (theInput == JUMPING || theInput == DANCING || theInput == HORSE ||
		theInput == SCAPE || theInput == TOSCA || theInput == SHREC11) //we've gound-truth info as well
	{
		cout << "ground-truth cost: " << groundTruthCost << " (= " << groundTruthCost/r1 << "r, = " << groundTruthCost/maxGeoDistG << "n)\nisometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n"; //ground-truth costs not in [0,1], so use unnormalized r
		fprintf(fEvalPtr, "avg iso, ground\t=\t%f, %fn\n", isometryCost, groundTruthCost/maxGeoDistG);
	}
	else
	{
		cout << "loaded isometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n";
		fprintf(fEvalPtr, "avg iso\t=\t%f\n", isometryCost);
	}

	cout << "worst costs:\n";
	if (theInput == JUMPING || theInput == DANCING || theInput == HORSE ||
		theInput == SCAPE || theInput == TOSCA || theInput == SHREC11)
	{
		cout << "ground-truth: " << maxCostGg << /*"/" << minCostG << */"\t" << maxCostGg/r1 << "r"/*/" << minCostG/rForBase1 << "r*/"\t" << maxCostGg/maxGeoDistG << "n"/*/ << minCostG/maxGeoDistG << "n*/"\t" << worstMatchG << "->" << verts[worstMatchG]->worstGin2 << "\n" //ground-truth costs not in [0,1], so use unnormalized r
			 << "isometry: " << maxCostIg << /*"/" << minCostI << */"\t" << maxCostIg/normalizedR1 << "r"/*/" << minCostI/normalizedR1 << "r*/"\t\t" << worstMatchI << "->" << verts[worstMatchI]->worstIin2 << "\n\n"; //isometry costs in [0,1], so use normalized r
		fprintf(fEvalPtr, "worst iso, ground\t=\t%f, %fn\n", maxCostIg, maxCostGg/maxGeoDistG);
		fprintf(fEvalPtr, "worst iso, ground\t=\t%d->%d, %d->%d\n", worstMatchI, verts[worstMatchI]->worstIin2, worstMatchG, verts[worstMatchG]->worstGin2);
	}
	else
	{
		cout << "isometry: " << maxCostIg << /*"/" << minCostI << */"\t" << maxCostIg/normalizedR1 << "r"/*/" << minCostI/normalizedR1 << "r*/"\t\t" << worstMatchI << "->" << verts[worstMatchI]->worstIin2 << "\n\n"; //ground-truth n/a in hybrid run
		fprintf(fEvalPtr, "worst iso\t=\t%f\n", maxCostIg);
		fprintf(fEvalPtr, "worst iso\t=\t%d->%d\n", worstMatchI, verts[worstMatchI]->worstIin2);
	}	
	fclose(fEvalPtr);

	cout << "done!\n\n";
}
void Mesh::computeCorrespondenceErrorGMDS(Mesh* mesh2, bool interpolateCoarseMap)
{
	//evaluates the correspondence computed by GMDS

	cout << "gmds correspondence error on this model (" << frameID << ") being computed....\n";
if (! collapsed) {cout << "GMDS has worked on collapsed meshes; so, use collapsed input here\n"; exit(0);}

	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 && theInput != TOSCA) //= if (hybridRun)
		hybridModelsToSameCoordFrame(mesh2);

	prepareForGeodesicComputation(); //shortcut edge existence slows down algo due to O((E+V)lgV) dijkstra, e.g. 2min faster w/o shortcuts for dancing and for jumping; but i saw that shortcut'ed output is better; so, keep it
	mesh2->prepareForGeodesicComputation();		

	char fName[250];
	if (theInput == TOSCA)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\GMDS\\david%d-david%d\\david%d-david%d.gmds.dat", frameID, mesh2->frameID, frameID, mesh2->frameID); //tosca collapsed
	else if (theInput == SCAPE)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SCAPE\\GMDS\\mesh%s%d-mesh%s%d\\mesh%s%d-mesh%s%d.gmds.dat", (frameID < 10 ? "00" : "0"), frameID, (mesh2->frameID < 10 ? "00" : "0"), mesh2->frameID, (frameID < 10 ? "00" : "0"), frameID, (mesh2->frameID < 10 ? "00" : "0"), mesh2->frameID);
	else if (theInput == BALLERINA)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\Watertight\\GMDS\\%d-%d\\%d-%d.gmds.dat", frameID, mesh2->frameID, frameID, mesh2->frameID);
	else if (theInput == SHREC11)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\GMDS\\%d-%d\\%d-%d.gmds.dat", frameID, mesh2->frameID, frameID, mesh2->frameID);
	else
		exit(0);
	FILE* fPtr;
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}

	//these move spatial (i.e. non-spectral) center-of-mass to origin which provides a better viewing
	moveCOMtoOrigin();
	mesh2->moveCOMtoOrigin();

	fscanf(fPtr, "%f\t%f\t%f\t%f\n", &minGeoDistG, &maxGeoDistG, &mesh2->minGeoDistG, &mesh2->maxGeoDistG);
	cout << "min/max geodesic distance around the whole surface [source]: " << minGeoDistG << " / " << maxGeoDistG <<
			"\nmin/max geodesic distance around the whole surface [target]: " << mesh2->minGeoDistG << " / " << mesh2->maxGeoDistG << "\n\n";
if (minGeoDistG != 0.0f) {cout << "cannot find min/max geo dist line at the top of the *.gmds.dat file\n"; exit(0); }//make sure i added the min/max geo dist line to the top of the file

	while (! feof(fPtr))
	{
		int i, ci; //i and corresp(i)
		fscanf(fPtr, "%d\t%d\n", &i, &ci);
		i--;
		ci--; //-1 'cos verts in *.gmds.dat file start from 1 whereas verts[] here starts from 0
		safeCorresp.push_back(i);
		safeCorresp.push_back(ci); //getMatchingLinesSepDense() & setCorrespondenceErrsTmp() traverses safeCorresp[]
		verts[i]->matchIdx = ci; mesh2->verts[ci]->matchIdx = i; //line drawing needs matchIdx values (for many-to-many this is overwritten arbitrarily but anyway)
		verts[i]->base = mesh2->verts[ci]->base = true; //initially/default false base made true here and will be used belw for efficiency only

		//if (! justDisplay) //correspondence error will be printed as well; so, prepare bases and dSpannings
		{			
			dijkstraShortestPaths(i); //dSpanning[]'s now are good 'cos min/maxGeoDistG fread above
			mesh2->dijkstraShortestPaths(ci);
		}

		//fill baseVerts just for counting w/o duplicates in order to be able to say n vs. m mapping in the end
		bool exists = false;
		for (int j = 0; j < (int) baseVerts.size(); j++)
		{
			if (baseVerts[j] == i)
			{
				exists = true;
				break;
			}
		}
		if (! exists)
			baseVerts.push_back(i);
		exists = false;
		for (int j = 0; j < (int) mesh2->baseVerts.size(); j++) //actually duplicates in mesh2.baseVerts not hurt at all; so skip here later for speed up (if any); i guess only levelInfoToFile gets effected w/ duplicates but it is handleable
		{
			if (mesh2->baseVerts[j] == ci)
			{
				exists = true;
				break;
			}
		}
		if (! exists)
			mesh2->baseVerts.push_back(ci);
	} //end of feof

	if (interpolateCoarseMap) //interpolate coarse map in safeCorresp to dense map
	{		
		////////////////////////////////////////// interpolation of coarseCorresp //////////////////////////////////////////
//block copied from interpolateCoarseCorresp()
		cout << "interpolating coarse corresp into dense one of size ~" << (int) verts.size() << "..\n";
		int m = (int) safeCorresp.size() / 2;
		//for each vert v, create the m-size geo2coarses[] list (m = # of coarse correspoondence verts) where v.geo2coarses[4] gives the geodesic dist from v to 5th coarse corresp matched vert
		for (int v = 0; v < (int) verts.size(); v++)
			for (int s = 0; s < m; s++)
				verts[v]->geo2coarses.push_back(verts[ safeCorresp[2*s] ]->dSpanning[v]); //2s is even and therefore on mesh1; use 2s+1 for correspondence in mesh2
		//fill geo2coarses[] for mesh2 as well to compare them below for dense interpolated matching
		for (int v = 0; v < (int) mesh2->verts.size(); v++)
			for (int s = 0; s < m; s++)
				mesh2->verts[v]->geo2coarses.push_back(mesh2->verts[ safeCorresp[2*s+1] ]->dSpanning[v]); //2s+1 is odd and therefore on mesh2 as the correspondence of 2s in mesh1

		bool mesh1Full = false; //true makes all mesh1 verts get a match, i.e. no unmatched in mesh1, hence many-to-1; false makes all mesh2 verts get a match, hence 1-to-many; use false for my dense coloring scheme compatibilty
		if (mesh1Full)
			//each mesh1 vert v1 is matched to v2 where v2.geo2coarses is closest to v1.geo2coarses; so, many-to-1 is possible
			for (int v1 = 0; v1 < (int) verts.size(); v1++)
			{
				if (verts[v1]->base)
					continue; //already placed in safeCorresp w/ its non-interpolated match
				float minDist = INF;
				int bestV2 = -1;
				for (int v2 = 0; v2 < (int) mesh2->verts.size(); v2++)
				{
					float dist = 0.0f; //dist b/w v1.geo2coarses & v2.geo2coarses vectors
					for (int i = 0; i < m; i++)
						dist += fabs(verts[v1]->geo2coarses[i] - mesh2->verts[v2]->geo2coarses[i]); //i'th entries are compatible as they're geodesic dists to i'th coarse match
					if (dist < minDist)
					{
						bestV2 = v2;
						minDist = dist;
					}
				}

				safeCorresp.push_back(v1);
				safeCorresp.push_back(bestV2);
//interestingMatches.push_back(v1);interestingMatches.push_back(bestV2); //no spheres for this newcomer interpolated matches; i want to spheres of the generating coarse corresp, which is already added to interestingMatches above

				if (v1 % 10 == 0)
					cout << v1 << " / " << (int) verts.size() << "th interpolation completed\n";
			}
		else //almost same except loop headers swap
			//each mesh2 vert v2 is matched to v1 where v1.geo2coarses is closest to v2.geo2coarses; so, 1-to-many is possible
			for (int v2 = 0; v2 < (int) mesh2->verts.size(); v2++)
			{
				if (mesh2->verts[v2]->base)
					continue; //already placed in safeCorresp w/ its non-interpolated match
				float minDist = INF;
				int bestV1 = -1;
				for (int v1 = 0; v1 < (int) verts.size(); v1++)
				{
					float dist = 0.0f; //dist b/w v1.geo2coarses & v2.geo2coarses vectors
					for (int i = 0; i < m; i++)
						dist += fabs(verts[v1]->geo2coarses[i] - mesh2->verts[v2]->geo2coarses[i]); //i'th entries are compatible as they're geodesic dists to i'th coarse match
					if (dist < minDist)
					{
						bestV1 = v1;
						minDist = dist;
					}
				}

				safeCorresp.push_back(bestV1);
				safeCorresp.push_back(v2);
//interestingMatches.push_back(v1);interestingMatches.push_back(bestV2); //no spheres for this newcomer interpolated matches; i want to spheres of the generating coarse corresp, which is already added to interestingMatches above

				if (v2 % 2500 == 0)
					cout << v2 << " / " << (int) mesh2->verts.size() << "th interpolation completed\n";
			}
		cout << "interpolated dense corresp is of size " << (int) safeCorresp.size() / 2 << "\ndone!\n\n";

		//fwrite safeCorresp in the format of *level*.dat files but simpler, i.e. no worst stuff and baseNghbhood stuff, that is nothing after '-2' in levelInfoToFile()
		char fOutName[250];
		if (theInput == TOSCA)
			sprintf(fOutName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\GMDS\\horse%d-horse%d\\horse%d-horse%d.gmds.interpolated.dat", frameID, mesh2->frameID, frameID, mesh2->frameID); //tosca collapsed
		else if (theInput == SCAPE)
			sprintf(fOutName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SCAPE\\GMDS\\mesh%s%d-mesh%s%d\\mesh%s%d-mesh%s%d.gmds.interpolated.dat", (frameID < 10 ? "00" : "0"), frameID, (mesh2->frameID < 10 ? "00" : "0"), mesh2->frameID, (frameID < 10 ? "00" : "0"), frameID, (mesh2->frameID < 10 ? "00" : "0"), mesh2->frameID);
		else if (theInput == BALLERINA)
			sprintf(fOutName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\Watertight\\GMDS\\%d-%d\\%d-%d.gmds.interpolated.dat", frameID, mesh2->frameID, frameID, mesh2->frameID);
		else if (theInput == SHREC11)
			sprintf(fOutName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\GMDS\\%d-%d\\%d-%d.gmds.interpolated.dat", frameID, mesh2->frameID, frameID, mesh2->frameID);
		else
			exit(0);
		FILE* fOutPtr = fopen(fOutName, "w");

		//first, stuff to be loaded/fread as r1 and r2 for the next level, followed by level whose info'll be fprinted in this file
		fprintf(fOutPtr, "%f\t%f\t%d\n", -9.0f, -9.0f, 999/*level*/); //no level info 'cos this is an interpolated output
		fprintf(fOutPtr, "%f\t%f\t%f\t%f\n", minGeoDistG, maxGeoDistG, mesh2->minGeoDistG, mesh2->maxGeoDistG); //may be needed when evaluating this output

		//second, correspondences to be loaded/fread into safeCorresp for a later time
		for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
			fprintf(fOutPtr, "%d\t%d\n", safeCorresp[2*s], safeCorresp[2*s + 1]);
		fprintf(fOutPtr, "-2\n"); //section separator is -2
		fclose(fOutPtr);
		////////////////////////////////////////// interpolation of coarseCorresp ends //////////////////////////////////////////

		m = (int) safeCorresp.size() / 2;
		cout << "geodesics for each vert of " << m << "-size match list..\n"; 
		for (int s = 0; s < m; s++) //make geodesics ready to be used in corresp error computations below
		{
			dijkstraShortestPaths(safeCorresp[2*s]);
			mesh2->dijkstraShortestPaths(safeCorresp[2*s + 1]);
			if (s % 1000 == 0)
				cout << s << " / " << m << "\n";
		}
		cout << "done!\n\n";
	}

	//if (! justDisplay) //correspondence error will be printed as well
	{
		setCorrespondenceErrsTmp(mesh2, true);
		setExtremeCorrespondences(r1, true, true);
	}
/*	else
	{
		worstMatchI = worstMatchG = -1; //actually, i have this info in the file as well; so this else is totally redundant
		decideTop10Mu(true); //even if muOrder not ready, i have manual picks
	}*/

	cout << "# corresps: " << (int) safeCorresp.size() / 2 << endl;

	fclose(fPtr);

	FILE* fEvalPtr;
	char fEvalName[100];
	if (theInput == TOSCA)
		sprintf(fEvalName, "output\\dense\\TOSCA\\GMDS\\resultsGMDS within humans.dat");
	else if (theInput == SCAPE)
		sprintf(fEvalName, "output\\dense\\SCAPE\\GMDS\\resultsGMDS.dat");
	else if (theInput == BALLERINA)
		sprintf(fEvalName, "output\\dense\\Watertight\\GMDS\\resultsGMDS.dat");
	else if (theInput == SHREC11)
		sprintf(fEvalName, "output\\dense\\SHREC11\\linux\\GMDS\\resultsGMDS.dat");
	else
		exit(0);

	//append evaluation to the file
	fEvalPtr = fopen(fEvalName, "a"); //append this current evaluation to the result file
	fprintf(fEvalPtr, "======== %d-%d ========\ninterpolation of %d vs. %d mapping\n", frameID, mesh2->frameID, (int) baseVerts.size(), mesh2->baseVerts.size());

	cout << "avg costs:\n"; //r: given in terms of current patch radius, n: normalized by max geo dist, none: given as is
	cout << "isometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n"; //no ground-truth for sure 'cos gmds works on collapsed meshes (of sizes 5K at most)
	fprintf(fEvalPtr, "avg iso\t=\t%f\n", isometryCost);

	cout << "worst costs:\n";
	cout << "isometry: " << maxCostIg << /*"/" << minCostI << */"\t" << maxCostIg/normalizedR1 << "r"/*/" << minCostI/normalizedR1 << "r*/"\t\t" << worstMatchI << "->" << verts[worstMatchI]->worstIin2 << "\n\n"; //ground-truth n/a in hybrid run
	fprintf(fEvalPtr, "worst iso\t=\t%f\n", maxCostIg);
	fprintf(fEvalPtr, "worst iso\t=\t%d->%d\n", worstMatchI, verts[worstMatchI]->worstIin2);
	fclose(fEvalPtr);

	cout << "done!\n\n";
}

void Mesh::computeInputQuality(Mesh* mesh2)
{
	//computes the deviation of isometry for this input pair

	cout << "isometry deviation b/w 2 models (" << frameID << " & " << mesh2->frameID << ") being computed....\n";

	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 /*&& theInput != SCAPE*/ && theInput != TOSCA) //= if (hybridRun)
		hybridModelsToSameCoordFrame(mesh2);

	//for hybridRuns, i.e. no fixed connectivity, i need manual correspondences selected on critical parts of the object; ~5 is enough for articulated objects; for fixed-connectivity runs, i know all corresps trivially but can't use them 'cos i
	//need dSpanning[] to be computed for each of them, which is infeasible for 20K; selected baseVerts will be considered during setCorrespondenceErrs() (bad naming for here) that computes isometry deviation via safeList composed of all baseVerts
	int s = 0; //current size of baseVerts being expanded
	if (theInput == BALLERINA)
	{
		baseVerts.push_back(3898); verts[ baseVerts[s++] ]->matchIdx = 3912; //correspondence manually found
		baseVerts.push_back(4471); verts[ baseVerts[s++] ]->matchIdx = 4484; 
		baseVerts.push_back(4029); verts[ baseVerts[s++] ]->matchIdx = 4043; 
		baseVerts.push_back(4658); verts[ baseVerts[s++] ]->matchIdx = 4670; 
		baseVerts.push_back(4757); verts[ baseVerts[s++] ]->matchIdx = 4769; 
	}
	else if (theInput == DOG_WOLF)
	{
		baseVerts.push_back(1); verts[ baseVerts[s++] ]->matchIdx = 1726; //correspondence manually found
		baseVerts.push_back(2424); verts[ baseVerts[s++] ]->matchIdx = 3352;
		baseVerts.push_back(1156); verts[ baseVerts[s++] ]->matchIdx = 879;
		baseVerts.push_back(220); verts[ baseVerts[s++] ]->matchIdx = 900;
		baseVerts.push_back(3332); verts[ baseVerts[s++] ]->matchIdx = 2904;
		baseVerts.push_back(2034); verts[ baseVerts[s++] ]->matchIdx = 2484;
	}
	else if (theInput == DANCING_JUMPING)
	{
		baseVerts.push_back(2201); verts[ baseVerts[s++] ]->matchIdx = 15206; //correspondence manually found
		baseVerts.push_back(9062); verts[ baseVerts[s++] ]->matchIdx = 487;
		baseVerts.push_back(1835); verts[ baseVerts[s++] ]->matchIdx = 15795;
		baseVerts.push_back(556); verts[ baseVerts[s++] ]->matchIdx = 8382;
		baseVerts.push_back(314); verts[ baseVerts[s++] ]->matchIdx = 9082;
	}
	else if (theInput == DANCING)
	{
		baseVerts.push_back(2201); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++; //i-i correspondence is known thanks to fixed connectivity [very weirdly, baseVerts[s++] crashes; so, do s++ separately]
		baseVerts.push_back(9062); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
		baseVerts.push_back(1835); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
		baseVerts.push_back(556); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
		baseVerts.push_back(314); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
	}
	else if (theInput == JUMPING)
	{
		baseVerts.push_back(487); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++; //i-i correspondence is known thanks to fixed connectivity
		baseVerts.push_back(8382); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
		baseVerts.push_back(9082); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
		baseVerts.push_back(15206); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
		baseVerts.push_back(15795); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
	}
	else if (theInput == HORSE)
	{
		baseVerts.push_back(3717); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++; //i-i correspondence is known thanks to fixed connectivity
		baseVerts.push_back(2036); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
		baseVerts.push_back(6408); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
		baseVerts.push_back(2704); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
		baseVerts.push_back(8428); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
		baseVerts.push_back(7548); verts[ baseVerts[s] ]->matchIdx = baseVerts[s]; s++;
	}
	else if (theInput == CAT)
	{
		baseVerts.push_back(621); verts[ baseVerts[s++] ]->matchIdx = 399; //correspondence manually found
		baseVerts.push_back(671); verts[ baseVerts[s++] ]->matchIdx = 1002;
		baseVerts.push_back(1602); verts[ baseVerts[s++] ]->matchIdx = 1728;
		baseVerts.push_back(2614); verts[ baseVerts[s++] ]->matchIdx = 2637;
		baseVerts.push_back(2865); verts[ baseVerts[s++] ]->matchIdx = 2457;
	}
	else if (theInput == DOG)
	{
		baseVerts.push_back(172); verts[ baseVerts[s++] ]->matchIdx = 1055; //correspondence manually found
		baseVerts.push_back(306); verts[ baseVerts[s++] ]->matchIdx = 333;
		baseVerts.push_back(1601); verts[ baseVerts[s++] ]->matchIdx = 3134;
		baseVerts.push_back(2318); verts[ baseVerts[s++] ]->matchIdx = 2048;
		baseVerts.push_back(3095); verts[ baseVerts[s++] ]->matchIdx = 2505;
	}
	else if (theInput == VICTORIA)
	{
		baseVerts.push_back(105); verts[ baseVerts[s++] ]->matchIdx = 2997; //correspondence manually found
		baseVerts.push_back(983); verts[ baseVerts[s++] ]->matchIdx = 3367;
		baseVerts.push_back(1320); verts[ baseVerts[s++] ]->matchIdx = 39;
		baseVerts.push_back(1466); verts[ baseVerts[s++] ]->matchIdx = 1743;
		baseVerts.push_back(3307); verts[ baseVerts[s++] ]->matchIdx = 2901;
	}
	else if (theInput == DAVID_VICTORIA)
	{
		baseVerts.push_back(480); verts[ baseVerts[s++] ]->matchIdx = 2997; //correspondence manually found
		baseVerts.push_back(1330); verts[ baseVerts[s++] ]->matchIdx = 1743;
		baseVerts.push_back(1429); verts[ baseVerts[s++] ]->matchIdx = 39;
		baseVerts.push_back(2054); verts[ baseVerts[s++] ]->matchIdx = 2901;
		baseVerts.push_back(3266); verts[ baseVerts[s++] ]->matchIdx = 3367;
	}

	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		verts[ baseVerts[i] ]->base = mesh2->verts[ verts[ baseVerts[i] ]->matchIdx ]->base = true;
		cout << baseVerts[i] << " " << verts[ baseVerts[i] ]->matchIdx << endl;
	}

	//error = sum of geodesic distances, e.g. g(0, corresp(0)) + g(1, corresp(1)); note that, this sum is 0 if corresp(i) = i since
	//i know the ground-truth values as corresp(i) = i for the jumping man data (ground-truth unknown for lowReso models)
	int k = 6;
	prepareForGeodesicComputation(); //shortcut edge existence slows down algo due to O((E+V)lgV) dijkstra, e.g. 2min faster w/o shortcuts for dancing and for jumping; but i saw that shortcut'ed output is better; so, keep it
	mesh2->prepareForGeodesicComputation();
	//fill the dSpanning[] entries by launching original O((E+V) lgV) dijkstra from each base
	minGeoDistG = mesh2->minGeoDistG = INF;
	maxGeoDistG = mesh2->maxGeoDistG = -INF;
	cout << "original Dijkstra from each baseVert being computed for mesh1 & mesh2....\n";
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		dijkstraShortestPaths(baseVerts[i]); //dSpanning[]'s now are bad 'cos i used bad min/maxGeoDistG
		mesh2->dijkstraShortestPaths(verts[ baseVerts[i] ]->matchIdx); //dSpanning[]'s now are bad 'cos i used bad min/maxGeoDistG
	}
	cout << "Done!\n\n";
	
	//min/maxGeoDistG being set for mesh1
	for (int i = 0; i < (int) baseVerts.size(); i++)
		for (int v = 0; v < (int) verts.size(); v++)
		{
			if (verts[ baseVerts[i] ]->dSpanningUnn[ v ] < minGeoDistG)
				minGeoDistG = verts[ baseVerts[i] ]->dSpanningUnn[ v ];
			if (verts[ baseVerts[i] ]->dSpanningUnn[ v ] > maxGeoDistG)
				maxGeoDistG = verts[ baseVerts[i] ]->dSpanningUnn[ v ];
		}
	//min/maxGeoDistG being set for mesh2
	for (int i = 0; i < (int) baseVerts.size(); i++) //i go to mesh2 baseVerts through mesh1.baseVerts' matchIdx values set above
		for (int v = 0; v < (int) mesh2->verts.size(); v++)
		{
			int bv2 = verts[ baseVerts[i] ]->matchIdx;
			if (mesh2->verts[ bv2 ]->dSpanningUnn[ v ] < mesh2->minGeoDistG)
				mesh2->minGeoDistG = mesh2->verts[ bv2 ]->dSpanningUnn[ v ];
			if (mesh2->verts[ bv2 ]->dSpanningUnn[ v ] > mesh2->maxGeoDistG)
				mesh2->maxGeoDistG = mesh2->verts[ bv2 ]->dSpanningUnn[ v ];
		}
	cout << "min/maxGeoDistG for mesh1 & mesh2: " << minGeoDistG << " " << maxGeoDistG << "  &  " << mesh2->minGeoDistG << " " << mesh2->maxGeoDistG << endl;
	//correct normalized dSpanning[] values w/ correct min/maxGeoDistG
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		//for mesh1
		for (int v = 0; v < (int) verts.size(); v++)
			verts[ baseVerts[i] ]->dSpanning[ v ] = (verts[ baseVerts[i] ]->dSpanningUnn[ v ] - minGeoDistG) / (maxGeoDistG - minGeoDistG); //dSpanning normalized now
		//for mesh2
		for (int v = 0; v < (int) mesh2->verts.size(); v++)
		{
			int bv2 = verts[ baseVerts[i] ]->matchIdx;
			mesh2->verts[ bv2 ]->dSpanning[ v ] = (mesh2->verts[ bv2 ]->dSpanningUnn[ v ] - mesh2->minGeoDistG) / (mesh2->maxGeoDistG - mesh2->minGeoDistG); //mesh2->dSpanning normalized now
		}
	}

	//these move spatial (i.e. non-spectral) center-of-mass to origin which provides a better viewing
	moveCOMtoOrigin();
	mesh2->moveCOMtoOrigin();

	//set correspondence list, safeCorresps, using matchIdx's above
	refillSafeCorresp(mesh2);
	cout << "\nisometry deviation of this input: " << setCorrespondenceErrs(mesh2, 1.0f, -9) << endl;
}

////////////// eigenvector/value finder ////////////////
//adapted from Tina4 (tina-vision.net/tina4/doxygen/html/mat__eigen_8c-source.html)
#define SIGN(a,b) ((b)<0 ? -fabs(a) : fabs(a))
static void tred2(int n, float **a, float *d, float *e)
{
	int l, k, j, i;
	float scale, hh, h, g, f;

	//start arrays at 0 not 1
	for (i = 0; i < n; ++i)
		(a[i])--;
	a--;
	d--;
	e--;
	
	for (i = n; i >= 2; i--)
	{
		l = i - 1;
		h = scale = 0.0;
		if (l > 1)
		{
			for (k = 1; k <= l; k++)
				scale += fabs(a[i][k]);
			if (scale == 0.0)
				e[i] = a[i][l];
			else
			{
				for (k = 1; k <= l; k++)
				{
					a[i][k] /= scale;
					h += a[i][k] * a[i][k];
				}
				f = a[i][l];
				g = f > 0 ? -sqrt(h) : sqrt(h);
				e[i] = scale * g;
				h -= f * g;
				a[i][l] = f - g;
				f = 0.0;
				for (j = 1; j <= l; j++)
				{
					a[j][i] = a[i][j] / h;
					g = 0.0;
					for (k = 1; k <= j; k++)
						g += a[j][k] * a[i][k];
					for (k = j + 1; k <= l; k++)
						g += a[k][j] * a[i][k];
					e[j] = g / h;
					f += e[j] * a[i][j];
				}
				hh = f / (h + h);
				for (j = 1; j <= l; j++)
				{
					f = a[i][j];
					e[j] = g = e[j] - hh * f;
					for (k = 1; k <= j; k++)
						a[j][k] -= (f * e[k] + g * a[i][k]);
				}
			}
		}
		else
			e[i] = a[i][l];
		d[i] = h;
	}
	d[1] = 0.0;
	e[1] = 0.0;
	for (i = 1; i <= n; i++)
	{
		l = i - 1;
		if (d[i])
		{
			for (j = 1; j <= l; j++)
			{
					g = 0.0;
				for (k = 1; k <= l; k++)
					g += a[i][k] * a[k][j];
				for (k = 1; k <= l; k++)
					a[k][j] -= g * a[k][i];
			}
		}
		d[i] = a[i][i];
		a[i][i] = 1.0;
		for (j = 1; j <= l; j++)
			a[j][i] = a[i][j] = 0.0;
	}

	a++;
	d++;
	e++;
	for (i = 0; i < n; ++i)
		(a[i])++;
}
static int tqli(int n, float **z, float *d, float *e)
{
	int m, l, iter, i, k;
	float s, r, p, g, f, dd, c, b;

	//start arrays at 0 not 1
	for (i = 0; i < n; ++i)
		(z[i])--;
	z--;
	d--;
	e--;

	for (i = 2; i <= n; i++)
		e[i - 1] = e[i];
	e[n] = 0.0;
	for (l = 1; l <= n; l++)
	{
		iter = 0;
		do
		{
			for (m = l; m <= n - 1; m++)
			{
				dd = fabs(d[m]) + fabs(d[m + 1]);
				if (fabs(e[m]) + dd == dd)
					break;
			}
			if (m != l)
			{
				if (iter++ == 30)
					return (0);
				g = (d[l + 1] - d[l]) / (2.0f * e[l]);
				r = sqrt((g * g) + 1.0f);
				g = d[m] - d[l] + e[l] / (g + SIGN(r, g));
				s = c = 1.0;
				p = 0.0;
				for (i = m - 1; i >= l; i--)
				{
					f = s * e[i];
					b = c * e[i];
					if (fabs(f) >= fabs(g))
					{
						c = g / f;
						r = sqrt((c * c) + 1.0f);
						e[i + 1] = f * r;
						c *= (s = 1.0f / r);
					}
					else
					{
						s = f / g;
						r = sqrt((s * s) + 1.0f);
						e[i + 1] = g * r;
						s *= (c = 1.0f / r);
					}
					g = d[i + 1] - p;
					r = (d[i] - g) * s + 2.0f * c * b;
					p = s * r;
					d[i + 1] = g + p;
					g = c * r - b;
					for (k = 1; k <= n; k++)
					{
						f = z[k][i + 1];
						z[k][i + 1] = s * z[k][i] + c * f;
						z[k][i] = c * z[k][i] - s * f;
					}
				}
				d[l] = d[l] - p;
				e[l] = g;
				e[m] = 0.0;
			}
		}
		while (m != l);
	}

	z++;
	d++;
	e++;
	for (i = 0; i < n; ++i)
		(z[i])++;

	return (1);
}
static int eigen(int n, float **a, float **evec, float *eval)
{
	//Find eigenvalues and eigenvectors of symmetric n by n matrix a

	int i, j, k, success;
	float *d = new float[n]; //tvector_alloc(0, n, float);
	float *e = new float[n]; //tvector_alloc(0, n, float);

	for(i = 0; i < n; i++)
		for(j = 0; j < n; j++)
			evec[i][j] = a[i][j];
	
	tred2(n, evec, eval, e);
	success = tqli(n, evec, eval, e);

	if(!success)
	{
		delete [] d; //tvector_free(d, 0, float);
		delete [] e; //tvector_free(e, 0, float);
		return(success);
	}

	for (j = 0; j < n - 1; j++)
	{
		float emax = eval[j];
		int jmax = j;
		for (k = j + 1; k < n; k++)
		if (emax < eval[k])
		{
	          emax = eval[k];
			jmax = k;
		}
        if (jmax != j)
		{
			float tmp = eval[j]; eval[j] = eval[jmax]; eval[jmax] = tmp; //SWAP(float, eval[j], eval[jmax]);
			for(i = 0; i < n; i++)
			{
              float tmp = evec[i][j]; evec[i][j] = evec[i][jmax]; evec[i][jmax] = tmp; //SWAP(float, evec[i][j], evec[i][jmax]);
			}
		}
	}
	delete [] d; //tvector_free(d, 0, float);
	delete [] e; //tvector_free(e, 0, float);
	return (success);
}
////////////// eigenvector/value finder ends ////////////////

void Mesh::PCAonAffinity(int k, bool pseudoinverseTranspose)
{
	//principal component analysis on the baseGeoAffinity^^ matrix (to embed it into another (spectral) domain of k-dimension); to do
	//this, it computes the eigenvals/vecs of baseGeoAffinity matrix, and then selects the k best eigvecs (corresponding to k-largest eigvals); hence,
	//k-dimensional spectral domain will be used to represent n-dimensional baseGeoAffinity we have here
	//^^if geoAff = true, then work on baseGeoAffinity; otherwise, work on baseMahAffinity, i.e. Mahalanobis dists

	cout << "PCA on geodesic affinity matrix (of base vertices) starts to get spectral coords..\n";

//info: eigenvectors corresponding to symmetric positive definite matrices are orthogonal; so, they can be used as basis-vectors of our new k-D space

	// solve for eigenvalues and eigenvectors.
	// eigen values are sorted in descending order, 
	// eigen vectors are sorted in accordance.
	int n = (int) baseVerts.size();

	eigen_values = new float[n];
	eigen_vectors = new float*[n]; //n eigenvectors w/ n components each, i.e. n-dimensions, each (in PCA, i'll choose the best/largest k of them)
	for (int i = 0; i < n; i++)
		eigen_vectors[i] = new float[n];
	eigen(n, baseGeoAffinity, eigen_vectors, eigen_values); //call-by-reference effect to fill eigen_* (eigvectors come normalized, i.e. length = 1)

//eigen_values in descending order as we go from [0] to [n]; eigen_vectors.column1 = eigvector1 (corresponding to largest eigval), col2 = eigvec2, and so on..

	//compute 2 matrices that need to be multiplied to reach spectralCoords
	float** diagK = new float*[k]; //kxk
	for (int i = 1; i <= k; i++) //use 2nd, 3rd, 4th eigvecs as suggested by paper (for 3-D); do it if kernel1 in use in computeBaseGeodesicAffinityMtrx(); do it for kernel2 as well 'cos although 1st eigval is not that big, 1 eigvec is still a bad constant vector due to big gaussian width
	{
		diagK[i-1] = new float[k];
		for (int j = 1; j <= k; j++)
		{
			diagK[i-1][j-1] = (i == j ? sqrt(eigen_values[i]) : 0.0f); //use sqrt(eigval) as suggested by literature, i.e. for better eigval scaling
			if (pseudoinverseTranspose && i == j)
				diagK[i-1][j-1] = 1.0f / sqrt(eigen_values[i]); //this scale factor needed for denseMatching()
		}
	}
	for (int i = 0; i <= k; i++)
	{
		if (eigen_values[i] < 0.0f)
			eigen_values[i] = 0.0f;
#ifdef DBG_DETAIL
		cout << "eigval" << i << ": " << eigen_values[i] << endl; //prints in descending order
#endif
	}

//remember: paper uses 2nd, 3rd, and 4th eigvecs (for 3-D)!! ppt: First eigenvector ignored as it is almost a constant 'cos row-sums of affinity mtrx are almost constant

	float** ekt = new float*[k]; //kxn
	for (int i = 1; i <= k; i++) //use 2nd, 3rd, 4th eigvecs as suggested by paper (for 3-D)
	{
		ekt[i-1] = new float[n];
		for (int j = 0; j < n; j++)
			ekt[i-1][j] = eigen_vectors[j][i];
	}
	
	//let's get the desired spectral coords
	spectralCoords = new float*[k];
	for (int i = 0; i < k; i++) //alloc but not fill yet
		spectralCoords[i] = new float[n]; //kxn mtrx
	mtrxMult(spectralCoords, k, k, k, n, diagK, ekt); //desired result (of size kxn) filled by call-by-ref

	//recapture memo
	//delete [] eigen_values; //not here
	//for (int i = 0; i < n; i++) delete [] eigen_vectors[i]; delete [] eigen_vectors; //not here 'cos used in findB..ors()
	for (int i = 0; i < k; i++) delete [] diagK[i]; delete [] diagK;
	for (int i = 0; i < k; i++) delete [] ekt[i]; delete [] ekt;

	//set v.spectral values
	if ((int) baseVerts.size() > 0)
	{
//		FILE* fPtr = fopen("base spectral coords, f___.dat", "w");
		for (int i = 0; i < n; i++)
		{
//			fprintf(fPtr, "scoords%d:\t", i);
//			for (int j = 0; j < k; j++)
//				fprintf(fPtr, "%f\t", spectralCoords[j][i]);
			
			if (k <= 6)
			{
				verts[ baseVerts[i] ]->spectralK = new float[k];
				for (int j = 0; j < k; j++)
					verts[ baseVerts[i] ]->spectralK[j] = spectralCoords[j][i];
//note that, for k > 3, quaternion-based icp algo not work; use svd-based algo in that case (literature\others\icp\coding (svd instead of quaternions).m)
			}
			else
			{
				cout << "need some work to enumarate principal axes for >6 dimension; so, currently can use upto 6-D\n";
				exit(0);
			}

//			fprintf(fPtr, "\n");
		}
//		fclose(fPtr);
		
		//Euclidean/nonspectral/traditional 3-D coords of base vertices, just as an extra info
//		fPtr = fopen("base coords, f___.dat", "w");
//		for (int i = 0; i < (int) baseVerts.size(); i++)
//			fprintf(fPtr, "coords%d:\t%f\t%f\t%f\t\n", i, verts[ baseVerts[i] ]->coords[0], verts[ baseVerts[i] ]->coords[1], verts[ baseVerts[i] ]->coords[2]);
//		fclose(fPtr);
	}

	//translate spectral coords so that center of mass is at the origin (0,0,0) and scale them so that avg dist to origin is 1
	translateAndScaleSpectralBasesK(k);

	cout << "Done!\n\n";
}

void Mesh::translateAndScaleSpectralBasesK(int k, bool translateOnly)
{
	//translated all k-D spectral bases s.t. their center at origin (0,0,0) and scale'em s.t. bases' avg dist to origin is 1

	int n = (int) baseVerts.size();
	float* m1 = new float[k], * m2 = new float[k]; //mass ~ mean names used interchangably
	float* sum = new float[k];
	for (int i = 0; i < k; i++)
		sum[i] = 0.0f;
	for (int i = 0; i < n; i++)
		for (int c = 0; c < k; c++)
			sum[c] += verts[ baseVerts[i] ]->spectralK[c];
	for (int c = 0; c < k; c++)
		m1[c] = sum[c] / (float) n;
	//initial translation (t) that moves shape centers to (0,0,0,0,0) when k = 5
	for (int i = 0; i < n; i++)
		for (int c = 0; c < k; c++)
			verts[ baseVerts[i] ]->spectralK[c] -= m1[c];
	//now 2 models are optimally translation-aligned
	for (int c = 0; c < k; c++)
		m1[c] = m2[c] = 0.0f; //m1 and m2 moved to center after translation above
	if (! translateOnly)
	{
		//initial scaling (s) that makes mean variance from origin 1, i.e. scale each model to an appropriate size via avgDistToOrigin
		float avgDistToOrigin = 0.0f;
		for (int i = 0; i < n; i++)
			//taking pow2 below makes sum of means from origin to be 1; w/o pow2 it makes sum of distances from origin 1, which i want
			avgDistToOrigin += distanceBetweenKD(verts[ baseVerts[i] ]->spectralK, m1, k); //m1 = (0,0,0,0) when k = 4
		for (int i = 0; i < n; i++)
			for (int c = 0; c < k; c++)
				verts[ baseVerts[i] ]->spectralK[c] /= avgDistToOrigin;
	}
	//now 2 models are optimally scale-aligned by Horn; m1 & m2 cannot change after scaling above; so, no redundant update here
	delete [] m1; delete [] m2; delete [] sum;
}

void translateAndScaleK(float** &pnts, int n, int k)
{
	//translates k-D pnts so that center of mass is at the origin (0,0,0) and scale them so that avg dist to origin is 1

	float* m1 = new float[k], * m2 = new float[k]; //mass ~ mean names used interchangably
	float* sum = new float[k];
	for (int i = 0; i < k; i++)
		sum[i] = 0.0f;
	for (int i = 0; i < n; i++)
		for (int c = 0; c < k; c++)
			sum[c] += pnts[i][c];
	for (int c = 0; c < k; c++)
		m1[c] = sum[c] / (float) n;
	//initial translation (t) that moves shape centers to (0,0,0,0,0) when k = 5
	for (int i = 0; i < n; i++)
		for (int c = 0; c < k; c++)
			pnts[i][c] -= m1[c];
	//now 2 models are optimally translation-aligned
	for (int c = 0; c < k; c++)
		m1[c] = m2[c] = 0.0f; //m1 and m2 moved to center after translation above
	//initial scaling (s) that makes mean variance from origin 1, i.e. scale each model to an appropriate size via avgDistToOrigin
	float avgDistToOrigin = 0.0f;
	for (int i = 0; i < n; i++)
		//taking pow2 below makes sum of means from origin to be 1; w/o pow2 it makes sum of distances from origin 1, which i want
		avgDistToOrigin += distanceBetweenKD(pnts[i], m1, k); //m1 = (0,0,0,0) when k = 4
	for (int i = 0; i < n; i++)
		for (int c = 0; c < k; c++)
			pnts[i][c] /= avgDistToOrigin;
	//now 2 models are optimally scale-aligned by Horn; m1 & m2 cannot change after scaling above; so, no redundant update here
	delete [] m1; delete [] m2; delete [] sum;
}
int factorial(int k)
{
	if (k > 10)
		return -1; //don't bother big numbers
	int res = 1;
	for (int i = k; i > 0; i--)
		res *= i;
	return res;
}
//weights on each of 2 terms for spectral-domain cost (convex combination, i.e. sums to 1); rootBasedSymSoln handles these weights
float w1 = 1.0f, w2 = 0.0f; //pure L2 cost effect w/ w1=1; w2 is for avgGd2Roots of rootBasedSymSoln
void negateK(float* a, int k) { for (int i = 0; i < k; i++) a[i] = -a[i]; }
void Mesh::findBestAffinityEigvectors(Mesh* mesh2, int k, bool pseudoinverseTranspose)
{
	//eigvecs of geodesic affinity matrices may suffer from reordering and sign flips; heal them here by selecting the best
	//ordering and signing of mesh2.affinityEigvecs w.r.t. fixed mesh1.affinityEigvecs

	cout << "affinity eigvecs' reordering and sign flips..\n";

	int n1 = (int) baseVerts.size(), n2 = (int) mesh2->baseVerts.size();

	if (rootBasedSymSoln)
	{
		initRoots();
		mesh2->initRoots();
		computeGeodesicDistsToRoot(true); //for gd2r1
		mesh2->computeGeodesicDistsToRoot(true); //for gd2r2
		gd2r1 = new float[n1];
		gd2r2 = new float[n2];
		for (int i = 0; i < n1; i++)
			gd2r1[i] = verts[ baseVerts[i] ]->avgGd2Roots;
		for (int i = 0; i < n2; i++)
			gd2r2[i] = mesh2->verts[ mesh2->baseVerts[i] ]->avgGd2Roots;
		roots.clear();
		mesh2->roots.clear();
		w1 = 0.5f, w2 = 0.5f; //L2 + avgGd2Roots
	}

	//spectral coords and hence initial pntSet(K)1/2 are already centered at origin and scaled so that avg dist to origin is 1
	float** pntSetK1 = new float*[n1]; //for mesh1's (this) spectralCoords (of baseVerts)
	for (int i = 0; i < n1; i++)
	{
		pntSetK1[i] = new float[k];
		for (int c = 0; c < k; c++)
			pntSetK1[i][c] = verts[ baseVerts[i] ]->spectralK[c]; //fixed; so fill it now and update never
	}
	float** pntSetK2 = new float*[n2];
	for (int i = 0; i < n2; i++)
		pntSetK2[i] = new float[k];

//eigen_values in descending order as we go from [0] to [n]; eigen_vectors.column1 = eigvector1 (corresponding to largest eigval), col2 = eigvec2, and so on..
//so, eigen_vectors[1..7][i] is 1st 7 components, e.g. x,y,z,.., of ith eigenvector
	float** eigvecs1 = new float*[k]; //k leading eigvecs (each of which has n components) of mesh1.affinityMtrx; each row is an eigvec
	for (int i = 1; i <= k; i++) //i=0 is 1st eigvec w/ huge eigval and therefore ignored
	{
		eigvecs1[i-1] = new float[n1]; //kxn1 mtrx
		for (int j = 0; j < n1; j++)
			eigvecs1[i-1][j] = eigen_vectors[j][i]; //eigen_vectors[1..n1][0] is ignored 'cos it is constant corresponding to the largest eigvalue
	}
	float* eigvals1 = new float[k]; //corresponding eigvalues
	//fixed eigvecs and eigvals for mesh1 filled once and never updated as opposed to mesh2 stuff below
	for (int i = 1; i <= k; i++)
		eigvals1[i-1] = eigen_values[i]; //eigen_values[0] will never be accessed (huge eigval)
	//eigvecs/vals for mesh2 to be updated inside the loop
	float** eigvecs2 = new float*[k]; //k leading eigvecs of mesh2.affinityMtrx (each row is an eigvec)
	for (int i = 1; i <= k; i++)
	{
		eigvecs2[i-1] = new float[n2]; //kxn2 mtrx
		for (int j = 0; j < n2; j++) //init here in case i skip reordering below
			eigvecs2[i-1][j] = mesh2->eigen_vectors[j][i];
	}
	float* eigvals2 = new float[k]; //corresponding eigvalues to be filled and refilled inside the loop below
	for (int i = 1; i <= k; i++) //init here in case i skip reordering below
		eigvals2[i-1] = mesh2->eigen_values[i];
	//auxiliary arrays to get the new mesh2.spectrals
	float** spectralCoords2 = new float*[k]; //desired spectrals
	for (int i = 0; i < k; i++) //alloc but not fill yet
		spectralCoords2[i] = new float[n2]; //kxn2 mtrx
	float** diagK2 = new float*[k]; //kxk
	for (int i = 1; i <= k; i++) //use 2nd, 3rd, 4th eigvecs as suggested by paper (for 3-D)
		diagK2[i-1] = new float[k];

	float minAlignErr = INF, alignErr = INF;
	int maxR = factorial(k);
	int* perms = new int[k]; //perms[] at r=0 call = 6 5 4 3 2 1, r=717 call = 1 6 5 2 4 3, and so on, for k = 6 (6!.txt) case
	char fName[150]; sprintf(fName, "data\\permutations\\%d!.txt", k);
	FILE* fPtr;
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}
//	for (int r = 0; r < maxR; r++) //k! reordering (just cancel lines from here to eigvals2[i] = .. (inclusive) to skip this reordering stuff)
	{
/*		//refill eigvecs and eigvals according to the current reordering (one of k! permutations)
		//one permutation of 6 elements (1..6) is 1 line of 6!.txt; get this from file
		for (int p = 0; p < k; p++)
			fscanf(fPtr, "%d ", &perms[p]);
		for (int i = 0; i < k; i++)
			for (int j = 0; j < n2; j++)
				eigvecs2[i][j] = mesh2->eigen_vectors[j][perms[i]]; //perms[] start at idx 1, which is good 'cos eigen_vectors[0] is constant and discarded
		for (int i = 0; i < k; i++)
			eigvals2[i] = mesh2->eigen_values[perms[i]];//*/

		//arbitrary sign flip handling
		int jj, maxJ = (int) pow(2.0f, k); //= 16 for k = 4, 32 for k = 5
		for (int j = 0; j < maxJ && alignErr > 0.0f; j++) //try each orientation of mesh2.eigen_vectors
		{
			///////// +- eigvector handling ////////////
			//one of 2^k permutations
			if (j == 1 || j == 9 || j == 17 || j == 25 || j == 33 || j == 41 || j == 49 || j == 57) //do nothing for j = 0
				negateK(eigvecs2[2], n2); //negate effect, i.e. eigvecs2[2] = -eigvecs2[2]; ++-++- for j = 33, ++-+-- for j = 49, ++---- for j = 57
			else if (j == 2 || j == 10 || j == 18 || j == 26 || j == 34 || j == 42 || j == 50 || j == 58) //+-++++ for j = 2 || +-+-++ for j = 10 || +-++-+ for j = 18 || +-+--+ for j = 26 || +-+++- for j = 34 || +-+-+- for j = 42 || +-++-- for j = 50 || +-+--- for j = 58
			{
				negateK(eigvecs2[1], n2);
				negateK(eigvecs2[2], n2);
			}
			else if (j == 3 || j == 11 || j == 19 || j == 27 || j == 35 || j == 43 || j == 51 || j == 59) //+--+++ || +---++ || +--+-+ || +----+ || +--++- || +---+- || +--+-- || +-----
				negateK(eigvecs2[2], n2);
			else if (j == 4 || j == 12 || j == 20 || j == 28 || j == 36 || j == 44 || j == 52 || j == 60) //-+++++ || -++-++ || -+++-+ || -++--+ || -++++- || -++-+- || -+++-- || -++---
			{
				negateK(eigvecs2[0], n2);
				negateK(eigvecs2[1], n2);
				negateK(eigvecs2[2], n2);
			}
			else if (j == 5 || j == 13 || j == 21 || j == 29 || j == 37 || j == 45 || j == 53 || j == 61) //-+-+++ || -+--++ || -+-+-+ || -+---+ || -+-++- || -+--+- || -+-+-- || -+----
				negateK(eigvecs2[2], n2);
			else if (j == 6 || j == 14 || j == 22 || j == 30 || j == 38 || j == 46 || j == 54 || j == 62) //--++++ || --+-++ || --++-+ || --+--+ || --+++- || --+-+- || --++-- || --+---
			{
				negateK(eigvecs2[1], n2);
				negateK(eigvecs2[2], n2);
			}
			else if (j == 7 || j == 15 || j == 23 || j == 31 || j == 39 || j == 47 || j == 55 || j == 63) //---+++ || ----++ || ---+-+ || -----+ || ---++- || ----+- || ---+-- || ------
				negateK(eigvecs2[2], n2);
			else if (j == 8 || j == 16 || j == 24 || j == 32 || j == 40 || j == 48 || j == 56) //+++-++ || ++++-+ || +++--+ || +++++- || +++-+- || ++++-- || +++---
			{
				negateK(eigvecs2[0], n2);
				negateK(eigvecs2[1], n2);
				negateK(eigvecs2[2], n2); //reset to original +++ for first 3 axes
				negateK(eigvecs2[3], n2); //and flip 4th axes for the next 7 iters
				if (j == 16 || j == 32 || j == 48) //if we enter here, it means k = 5 (or 6), therefore we have 5 (or 6) principal axes
					negateK(eigvecs2[4], n2); //and flip the 5th axes for the next 16 iters
				if (j == 32) //if we enter here, it means k = 6, therefore we have 6 principal axes
					negateK(eigvecs2[5], n2); //and flip the 6th axes for the next 16 iters
			}
			///////// +- eigvector handling ////////////

			//obtain new mesh2.spectralCoords w/ these orders and signs of eigvectors
			//compute 2 matrices that need to be multiplied to reach spectralCoords
			for (int i = 0; i < k; i++) //use 2nd, 3rd, 4th eigvecs as suggested by paper (for 3-D)
				for (int j = 0; j < k; j++)
				{
					diagK2[i][j] = (i == j ? sqrt(eigvals2[i]) : 0.0f); //use sqrt(eigval) as suggested by literature, i.e. for better eigval scaling
					if (pseudoinverseTranspose && i == j)
						diagK2[i][j] = 1.0f / sqrt(eigvals2[i]); //this scale factor needed for denseMatching()
				}

			//desired result (of size kxn2) filled by call-by-ref
			mtrxMult(spectralCoords2, k, k, k, n2, diagK2, eigvecs2); //eigvecs2 is the mtrx whose rows are eigvecs; so, same as the one referred as ekt in PCAonAffinity()
			
			for (int i = 0; i < n2; i++)
				for (int c = 0; c < k; c++)
					pntSetK2[i][c] = spectralCoords2[c][i];
			//since pntSet1 is translated to origin and scaled so that avg dist to origin is 1 during PCAonAffinity(), do same for pntSet2 for a good comparison
			translateAndScaleK(pntSetK2, n2, k);
			alignErr = computeMSEKD(pntSetK1, pntSetK2, n1, n2, k, false);

			//see mean-squared alignment err b/w fixed mesh1.spectrals and this current/new mesh2.spectrals
			if (alignErr < minAlignErr)
			{
				jj = j;
				minAlignErr = alignErr;
				//this is the best spectral coords for target spectral w.r.t. fixed source spectral coords
				for (int i = 0; i < n2; i++)
					for (int c = 0; c < k; c++)
						mesh2->verts[ mesh2->baseVerts[i] ]->spectralK[c] = pntSetK2[i][c];

				//remember the untranslated and unscaled spectralCoords2[][] to be used in mesh2.distanceBasedTriangulation()
				for (int i = 0; i < n2; i++)
					for (int c = 0; c < k; c++)
						mesh2->spectralCoords[c][i] = spectralCoords2[c][i]; //spectralCoords[][] allocated by PCAonAffinity()
			}
//if (j == 4) break; //see 5th permutation which is not necassarily the best, for scrshots purposes (disable if (alignErr < minAlignErr) above)
		} //end of j
#ifdef DBG_DETAIL
		cout << "j'th axes (out of [0, " << maxJ-1 << "]) selected: " << jj << " (alignErr = " << (minAlignErr/n2) << ")\n\n";
#endif
	} //end of r
	fclose(fPtr);

	//re-capture memo
	delete [] eigen_values;
	delete [] mesh2->eigen_values;
	for (int i = 0; i < n1; i++) delete [] eigen_vectors[i]; delete [] eigen_vectors;
	for (int i = 0; i < n2; i++) delete [] mesh2->eigen_vectors[i]; delete [] mesh2->eigen_vectors;
	for (int i = 0; i < k; i++) delete [] eigvecs1[i]; delete [] eigvecs1;
	for (int i = 0; i < k; i++) delete [] eigvecs2[i]; delete [] eigvecs2;
	delete [] eigvals1;	delete [] eigvals2;
	delete [] perms;
	for (int i = 0; i < k; i++) delete [] spectralCoords2[i]; delete [] spectralCoords2;
	for (int i = 0; i < k; i++) delete [] diagK2[i]; delete [] diagK2;
	for (int i = 0; i < n1; i++) delete [] pntSetK1[i];	delete [] pntSetK1;
	for (int i = 0; i < n2; i++) delete [] pntSetK2[i];	delete [] pntSetK2;
	
	cout << "Done!!\n\n";
}

float** R1bst = new float*[3], ** R2bst = new float*[3]; //the best of all rotations to be used by denseMatching()
void Mesh::PCAalignmentK(float** &pntSet1, float** &pntSet2, int n1, int n2, int k)
{
	//employs PCA based pose normalization to align n1-size pntSet1 w/ target n2-size pntSet2; to do so, both are transformed to the common x, y, z axes; pntSet1/2 updated via call-by-ref

	cout << "covariance eigvecs' reordering and sign flips..\n";

	//pntSet1/2 are already translated so that centers at origin (0,0,0) and scaled so that avg dist to origin is 1; all done by PCAonAffinity()
	//center of masses (or means)
	float* m1 = new float[k], * m2 = new float[k]; //mass ~ mean names used interchangably
	for (int c = 0; c < k; c++)
		m1[c] = m2[c] = 0.0f; //m1 and m2 moved to center after translation above

	float alignErr = 100.0f; //alignErr stays big if startByPrincipalAxAlignment = true
	//principal axes for mesh1
	float** ms = new float*[k]; //mean-shifted kxn matrix for the mesh1
	for (int i = 0; i < k; i++) //for all k rows
	{
		ms[i] = new float[n1];
		for (int j = 0; j < n1; j++) //fill n1 columns
			ms[i][j] = pntSet1[j][i] - m1[i];
	}
	float** mst = new float*[n1]; //transpose of ms
	for (int i = 0; i < n1; i++)
	{
		mst[i] = new float[k];
		for (int j = 0; j < k; j++)
			mst[i][j] = ms[j][i];
	}
	float** cov = new float*[k]; //covariance mtrx of mesh1
	for (int i = 0; i < k; i++)
		cov[i] = new float[k];
	mtrxMult(cov, k, n1, n1, k, ms, mst); //call-by-ref to cov (info: since resulting cov is always real symmetric, its eigvals are nonnegative real numbers)
//see covarianceInfo.png for info on cov, e.g. [0][0] is the variance b/w x-coords of all pnts, [0][1] is the covariance b/w x & y-coords of all pnts, ..
	float* eig_vals = new float[k]; //eigen values
	float** principal_axes = new float*[k]; //eigenvectors in k-D
	for (int i = 0; i < k; i++)
		principal_axes[i] = new float[k];
	eigen(k, cov, principal_axes, eig_vals); //call-by-reference effect to last 2 parameters
//eig_vals in descending order as we go from [0] to [n]; principal_axes.column1 = eigvector1 (corresponding to largest eigval), col2 = eigvec2, and so on..
	float** principalAxes1 = new float*[k]; //eigvecs returned are already normalized (k-D cannot be drawn to scr, so not use a global version)
	for (int j = 0; j < k; j++)
	{
		principalAxes1[j] = new float[k];
		for (int c = 0; c < k; c++)
			principalAxes1[j][c] = principal_axes[c][j];
	}
	//aligning rotation applied to mean-shifted coords (ms[i][j] = pntSet1[i] - m1[i], ms = kxn); so, remember ms[][] values
	float** p1Shifted = new float*[n1];
	for (int i = 0; i < n1; i++)
	{
		p1Shifted[i] = new float[k];
		for (int c = 0; c < k; c++)
			p1Shifted[i][c] = ms[c][i];
	}

	//principal axes for mesh2 (overwrite to same variables, e.g. cov, above to save space)
	float** ms2 = new float*[k]; //cannot overwrite to ms 'cos n1 may not be equal to n2
	for (int i = 0; i < k; i++) //for all k rows
	{
		ms2[i] = new float[n2];
		for (int j = 0; j < n2; j++)
			ms2[i][j] = pntSet2[j][i] - m2[i];
	}
	float** mst2 = new float*[n2]; //transpose of ms2
	for (int i = 0; i < n2; i++)
	{
		mst2[i] = new float[k];
		for (int j = 0; j < k; j++)
			mst2[i][j] = ms2[j][i];
	}
	mtrxMult(cov, k, n2, n2, k, ms2, mst2); //call-by-ref to cov
	eigen(k, cov, principal_axes, eig_vals); //call-by-reference to last 2 parameters
	float** principalAxes2 = new float*[k];
	for (int j = 0; j < k; j++)
	{
		principalAxes2[j] = new float[k];
		for (int c = 0; c < k; c++)
			principalAxes2[j][c] = principal_axes[c][j];
	}
	float** p2Shifted = new float*[n2];
	for (int i = 0; i < n2; i++)
	{
		p2Shifted[i] = new float[k];
		for (int c = 0; c < k; c++)
			p2Shifted[i][c] = ms2[c][i];
	}

	//align each principal axes to traditional xyz-axes via R1 & R2
	float** R1 = new float*[k], ** R2 = new float*[k], //kxk rotation mtrx to be applied to mesh1 & mesh2, respectively (won't be a direct mtrx multiplication though)
		 ** R1best = new float*[k], ** R2best = new float*[k]; //end the best of all rotations
	for (int i = 0; i < k; i++)
	{
		R1[i] = new float[k];		R2[i] = new float[k];
		R1best[i] = new float[k];	R2best[i] = new float[k];
	}
	int* closestPntInP1 = new int[n2]; //filled for pntSet2 entries; closestPntInP1[3] = 7 means pntSet2[3] and pntSet1[7] are closest
	float minAlignErr = INF; //axes leading to minimum alignErr are the desired ones
	int maxR = factorial(k); //k = 3 calls here only
	int* perms = new int[k]; //perms[] at r=0 call = 6 5 4 3 2 1, at r=717 call = 1 6 5 2 4 3, and so on, for k = 6 (6!.txt) case
//	char fName[150]; sprintf(fName, "..\\ShapeCorresponder\\input\\permutations\\%d!.txt", k);
//	FILE* fPtr;
//	if (! (fPtr = fopen(fName, "r")))
//	{
//		cout << fName << " not found\n";
//		exit(0);
//	}
//	for (int r = 0; r < maxR; r++) //k! reordering (just cancel lines from here to principalAxes2[j][c] = .. (inclusive) to skip this reordering stuff)
	{
/*		//refill principalAxes2 according to the current reordering (one of k! permutations)
		//one permutation of 6 elements (1..6) is 1 line of 6!.txt; get this from file
		for (int p = 0; p < k; p++)
			fscanf(fPtr, "%d ", &perms[p]);
		for (int j = 0; j < k; j++)
			for (int c = 0; c < k; c++)
				principalAxes2[j][c] = principal_axes[c][perms[j]-1]; //*/

		//sign flip (it is enough to keep pax1 fixed, and try 8 pax2 possibilities; so, canceling this pax1 outer loop (similar to alignWith()))
		int jj, maxJ = (int) pow(2.0f, k); //= 16 for k = 4, 32 for k = 5
		for (int j = 0; j < maxJ && alignErr > 0.0f; j++) //try each orientation of pax2
		{
			///////// +- eigvector handling ////////////
			if (j == 1 || j == 9 || j == 17 || j == 25 || j == 33 || j == 41 || j == 49 || j == 57) //do nothing for j = 0
				negateK(principalAxes2[(k == 2 ? 0 : 2)], k); //negate effect, i.e. principalAxes2[2] = -principalAxes2[2]; ++-++- for j = 33, ++-+-- for j = 49, ++---- for j = 57
			else if (j == 2 || j == 10 || j == 18 || j == 26 || j == 34 || j == 42 || j == 50 || j == 58) //+-++++ for j = 2 || +-+-++ for j = 10 || +-++-+ for j = 18 || +-+--+ for j = 26 || +-+++- for j = 34 || +-+-+- for j = 42 || +-++-- for j = 50 || +-+--- for j = 58
			{
				if (k != 2)
				{
					negateK(principalAxes2[1], k);
					negateK(principalAxes2[2], k);
				}
				else
					negateK(principalAxes2[1], k);
			}
			else if (j == 3 || j == 11 || j == 19 || j == 27 || j == 35 || j == 43 || j == 51 || j == 59) //+--+++ || +---++ || +--+-+ || +----+ || +--++- || +---+- || +--+-- || +-----
				negateK(principalAxes2[(k == 2 ? 0 : 2)], k);
			else if (j == 4 || j == 12 || j == 20 || j == 28 || j == 36 || j == 44 || j == 52 || j == 60) //-+++++ || -++-++ || -+++-+ || -++--+ || -++++- || -++-+- || -+++-- || -++---
			{
				negateK(principalAxes2[0], k);
				negateK(principalAxes2[1], k);
				negateK(principalAxes2[2], k);
			}
			else if (j == 5 || j == 13 || j == 21 || j == 29 || j == 37 || j == 45 || j == 53 || j == 61) //-+-+++ || -+--++ || -+-+-+ || -+---+ || -+-++- || -+--+- || -+-+-- || -+----
				negateK(principalAxes2[2], k);
			else if (j == 6 || j == 14 || j == 22 || j == 30 || j == 38 || j == 46 || j == 54 || j == 62) //--++++ || --+-++ || --++-+ || --+--+ || --+++- || --+-+- || --++-- || --+---
			{
				negateK(principalAxes2[1], k);
				negateK(principalAxes2[2], k);
			}
			else if (j == 7 || j == 15 || j == 23 || j == 31 || j == 39 || j == 47 || j == 55 || j == 63) //---+++ || ----++ || ---+-+ || -----+ || ---++- || ----+- || ---+-- || ------
				negateK(principalAxes2[2], k);
			else if (j == 8 || j == 16 || j == 24 || j == 32 || j == 40 || j == 48 || j == 56) //+++-++ || ++++-+ || +++--+ || +++++- || +++-+- || ++++-- || +++---
			{
				negateK(principalAxes2[0], k);
				negateK(principalAxes2[1], k);
				negateK(principalAxes2[2], k); //reset to original +++ for first 3 axes
				negateK(principalAxes2[3], k); //and flip 4th axes for the next 7 iters
				if (j == 16 || j == 32 || j == 48) //if we enter here, it means k = 5 (or 6), therefore we have 5 (or 6) principal axes
					negateK(principalAxes2[4], k); //and flip the 5th axes for the next 16 iters
				if (j == 32) //if we enter here, it means k = 6, therefore we have 6 principal axes
					negateK(principalAxes2[5], k); //and flip the 6th axes for the next 16 iters
			}
			///////// +- eigvector handling ////////////

			//try rotation w/ current/new principalAxes
			for (int a = 0; a < k; a++)
				for (int b = 0; b < k; b++)
				{
					//transform unit vectors principalAxes[] onto traditional xyz-axes
					R1[a][b] = principalAxes1[a][b];
					//transform unit vectors principalAxes2[] onto traditional xyz-axes
					R2[a][b] = principalAxes2[a][b];
				}

			//apply R1 & R2 rotation coming from alignPrincipalAxes() to each spectral point (icp input)
			for (int bv = 0; bv < n1; bv++)
			{
//note that, pntSet1 changes in each iteration, but not to worry 'cos p1Shifted constant and hence new pntSet1 are all valid
				//aligning rotation applied to mean-shifted coords (works)
				if (k != 2)
				{
					pntSet1[bv][0] = R1[0][0]*p1Shifted[bv][0] + R1[0][1]*p1Shifted[bv][1] + R1[0][2]*p1Shifted[bv][2] + R1[0][3]*p1Shifted[bv][3] + (k == 5 ? R1[0][4]*p1Shifted[bv][4] : 0.0f)
										+ (k == 6 ? R1[0][4]*p1Shifted[bv][4] + R1[0][5]*p1Shifted[bv][5] : 0.0f);
					pntSet1[bv][1] = R1[1][0]*p1Shifted[bv][0] + R1[1][1]*p1Shifted[bv][1] + R1[1][2]*p1Shifted[bv][2] + R1[1][3]*p1Shifted[bv][3] + (k == 5 ? R1[1][4]*p1Shifted[bv][4] : 0.0f)
										+ (k == 6 ? R1[1][4]*p1Shifted[bv][4] + R1[1][5]*p1Shifted[bv][5] : 0.0f);
					pntSet1[bv][2] = R1[2][0]*p1Shifted[bv][0] + R1[2][1]*p1Shifted[bv][1] + R1[2][2]*p1Shifted[bv][2] + R1[2][3]*p1Shifted[bv][3] + (k == 5 ? R1[2][4]*p1Shifted[bv][4] : 0.0f)
										+ (k == 6 ? R1[2][4]*p1Shifted[bv][4] + R1[2][5]*p1Shifted[bv][5] : 0.0f);
					pntSet1[bv][3] = R1[3][0]*p1Shifted[bv][0] + R1[3][1]*p1Shifted[bv][1] + R1[3][2]*p1Shifted[bv][2] + R1[3][3]*p1Shifted[bv][3] + (k == 5 ? R1[3][4]*p1Shifted[bv][4] : 0.0f)
										+ (k == 6 ? R1[3][4]*p1Shifted[bv][4] + R1[3][5]*p1Shifted[bv][5] : 0.0f);
				}
				else
				{
					pntSet1[bv][0] = R1[0][0]*p1Shifted[bv][0] + R1[0][1]*p1Shifted[bv][1];
					pntSet1[bv][1] = R1[1][0]*p1Shifted[bv][0] + R1[1][1]*p1Shifted[bv][1];
				}
			}
			for (int bv = 0; bv < n2; bv++)
			{
				if (k != 2)
				{
					pntSet2[bv][0] = R2[0][0]*p2Shifted[bv][0] + R2[0][1]*p2Shifted[bv][1] + R2[0][2]*p2Shifted[bv][2] + R2[0][3]*p2Shifted[bv][3] + (k == 5 ? R2[0][4]*p2Shifted[bv][4] : 0.0f)
										+ (k == 6 ? R2[0][4]*p2Shifted[bv][4] + R2[0][5]*p2Shifted[bv][5] : 0.0f);
					pntSet2[bv][1] = R2[1][0]*p2Shifted[bv][0] + R2[1][1]*p2Shifted[bv][1] + R2[1][2]*p2Shifted[bv][2] + R2[1][3]*p2Shifted[bv][3] + (k == 5 ? R2[1][4]*p2Shifted[bv][4] : 0.0f)
										+ (k == 6 ? R2[1][4]*p2Shifted[bv][4] + R2[1][5]*p2Shifted[bv][5] : 0.0f);
					pntSet2[bv][2] = R2[2][0]*p2Shifted[bv][0] + R2[2][1]*p2Shifted[bv][1] + R2[2][2]*p2Shifted[bv][2] + R2[2][3]*p2Shifted[bv][3] + (k == 5 ? R2[2][4]*p2Shifted[bv][4] : 0.0f)
										+ (k == 6 ? R2[2][4]*p2Shifted[bv][4] + R2[2][5]*p2Shifted[bv][5] : 0.0f);
					pntSet2[bv][3] = R2[3][0]*p2Shifted[bv][0] + R2[3][1]*p2Shifted[bv][1] + R2[3][2]*p2Shifted[bv][2] + R2[3][3]*p2Shifted[bv][3] + (k == 5 ? R2[3][4]*p2Shifted[bv][4] : 0.0f)
										+ (k == 6 ? R2[3][4]*p2Shifted[bv][4] + R2[3][5]*p2Shifted[bv][5] : 0.0f);
				}
				else
				{
					pntSet2[bv][0] = R2[0][0]*p2Shifted[bv][0] + R2[0][1]*p2Shifted[bv][1];
					pntSet2[bv][1] = R2[1][0]*p2Shifted[bv][0] + R2[1][1]*p2Shifted[bv][1];
				}
			}

			//////////////// error of current alignment ////////////////
			//before error computation, i need closest-pnt correspondence b/w pntSet1 & pntSet2
			float dist, minDista = INF, maxDista = -INF; //for dist normalization
			for (int i2 = 0; i2 < n2; i2++)
			{
				float minDist = INF; //INF's inside the loop 'cos i fill closestPntInP1[] per element
				for (int i1 = 0; i1 < n1; i1++)
				{
					dist = distanceBetweenKD(pntSet2[i2], pntSet1[i1], k);
					if (dist < minDist)
					{
						closestPntInP1[i2] = i1; //analogous to mesh2->verts[ mesh2->baseVerts[i2] ]->closestBaseInM1 = baseVerts[i1];
						minDist = dist;
					}
					if (dist < minDista) //global min
						minDista = dist;
					if (dist > maxDista)
						maxDista = dist;
				}
			} //end of i2
			//compute new alignment error as the sum of squared distances b/w closest pnts (or squared avgGd2Roots differences)
			alignErr = 0.0f;
			for (int bv2 = 0; bv2 < n2; bv2++)
			{
				//distance b/w (target) mesh2.bv and its correspondence in mesh1 for this iteration (closestBaseInP1)
				if (k != 2)
					dist = sqrt(pow(pntSet2[bv2][0] - pntSet1[ closestPntInP1[bv2] ][0], 2.0f) +										 
								pow(pntSet2[bv2][1] - pntSet1[ closestPntInP1[bv2] ][1], 2.0f) +
								pow(pntSet2[bv2][2] - pntSet1[ closestPntInP1[bv2] ][2], 2.0f) +
								pow(pntSet2[bv2][3] - pntSet1[ closestPntInP1[bv2] ][3], 2.0f) + 
								(k == 5 ? pow(pntSet2[bv2][4] - pntSet1[ closestPntInP1[bv2] ][4], 2.0f) : 0.0f) +
								(k == 6 ? pow(pntSet2[bv2][4] - pntSet1[ closestPntInP1[bv2] ][4], 2.0f) + pow(pntSet2[bv2][5] - pntSet1[ closestPntInP1[bv2] ][5], 2.0f) : 0.0f));
				else
					dist = sqrt(pow(pntSet2[bv2][0] - pntSet1[ closestPntInP1[bv2] ][0], 2.0f) +										 
								pow(pntSet2[bv2][1] - pntSet1[ closestPntInP1[bv2] ][1], 2.0f));
				//only L2 (no need to normalize dist, therefore no sqrt)
				if (! rootBasedSymSoln)
					alignErr += dist; //do sqrt() above 'cos dist needs to be normalized just-below and min/maxDista is sqrt'ed values
				else
					alignErr += pow(gd2r2[bv2] - gd2r1[ closestPntInP1[bv2] ], 2.0f); //replace w/ += dist above for symmetry-problem solution
			}
			if (alignErr < minAlignErr)
			{
				minAlignErr = alignErr;
				jj = j;
				//R1best = R1;	R2best = R2; this fails, i.e. R1best changes whenever R1 changes in a nonrelated iteration
				for (int a = 0; a < k; a++)
					for (int b = 0; b < k; b++)
					{
						R1best[a][b] = R1[a][b];
						R2best[a][b] = R2[a][b];
						R1bst[a][b] = R1best[a][b];
						R2bst[a][b] = R2best[a][b];
					}
			}
			//////////////// error of current alignment ends ////////////////
		} //end of j
#ifdef DBG_DETAIL
		cout << "j'th axes (out of [0, " << maxJ-1 << "]) selected: " << jj << " (alignErr = " << (minAlignErr/n2) << ")\n";
#endif
	} //end of r
	//apply R1best & R2best rotation coming from alignPrincipalAxes() to each spectral point (icp input)
	for (int bv = 0; bv < n1; bv++)
	{
		//aligning rotation applied to mean-shifted coords (works)
		if (k != 2)
		{
			pntSet1[bv][0] = R1best[0][0]*p1Shifted[bv][0] + R1best[0][1]*p1Shifted[bv][1] + R1best[0][2]*p1Shifted[bv][2] + R1best[0][3]*p1Shifted[bv][3] + (k == 5 ? R1best[0][4]*p1Shifted[bv][4] : 0.0f)
								+ (k == 6 ? R1best[0][4]*p1Shifted[bv][4] + R1best[0][5]*p1Shifted[bv][5] : 0.0f);
			pntSet1[bv][1] = R1best[1][0]*p1Shifted[bv][0] + R1best[1][1]*p1Shifted[bv][1] + R1best[1][2]*p1Shifted[bv][2] + R1best[1][3]*p1Shifted[bv][3] + (k == 5 ? R1best[1][4]*p1Shifted[bv][4] : 0.0f)
								+ (k == 6 ? R1best[1][4]*p1Shifted[bv][4] + R1best[1][5]*p1Shifted[bv][5] : 0.0f);
			pntSet1[bv][2] = R1best[2][0]*p1Shifted[bv][0] + R1best[2][1]*p1Shifted[bv][1] + R1best[2][2]*p1Shifted[bv][2] + R1best[2][3]*p1Shifted[bv][3] + (k == 5 ? R1best[2][4]*p1Shifted[bv][4] : 0.0f)
								+ (k == 6 ? R1best[2][4]*p1Shifted[bv][4] + R1best[2][5]*p1Shifted[bv][5] : 0.0f);
			pntSet1[bv][3] = R1best[3][0]*p1Shifted[bv][0] + R1best[3][1]*p1Shifted[bv][1] + R1best[3][2]*p1Shifted[bv][2] + R1best[3][3]*p1Shifted[bv][3] + (k == 5 ? R1best[3][4]*p1Shifted[bv][4] : 0.0f)
								+ (k == 6 ? R1best[3][4]*p1Shifted[bv][4] + R1best[3][5]*p1Shifted[bv][5] : 0.0f);
		}
		else
		{
			pntSet1[bv][0] = R1best[0][0]*p1Shifted[bv][0] + R1best[0][1]*p1Shifted[bv][1];
			pntSet1[bv][1] = R1best[1][0]*p1Shifted[bv][0] + R1best[1][1]*p1Shifted[bv][1];
		}
	}
	for (int bv = 0; bv < n2; bv++)
	{
		//same stuff for mesh2
		if (k != 2)
		{
			pntSet2[bv][0] = R2best[0][0]*p2Shifted[bv][0] + R2best[0][1]*p2Shifted[bv][1] + R2best[0][2]*p2Shifted[bv][2] + R2best[0][3]*p2Shifted[bv][3] + (k == 5 ? R2best[0][4]*p2Shifted[bv][4] : 0.0f)
								+ (k == 6 ? R2best[0][4]*p2Shifted[bv][4] + R2best[0][5]*p2Shifted[bv][5] : 0.0f);
			pntSet2[bv][1] = R2best[1][0]*p2Shifted[bv][0] + R2best[1][1]*p2Shifted[bv][1] + R2best[1][2]*p2Shifted[bv][2] + R2best[1][3]*p2Shifted[bv][3] + (k == 5 ? R2best[1][4]*p2Shifted[bv][4] : 0.0f)
								+ (k == 6 ? R2best[1][4]*p2Shifted[bv][4] + R2best[1][5]*p2Shifted[bv][5] : 0.0f);
			pntSet2[bv][2] = R2best[2][0]*p2Shifted[bv][0] + R2best[2][1]*p2Shifted[bv][1] + R2best[2][2]*p2Shifted[bv][2] + R2best[2][3]*p2Shifted[bv][3] + (k == 5 ? R2best[2][4]*p2Shifted[bv][4] : 0.0f)
								+ (k == 6 ? R2best[2][4]*p2Shifted[bv][4] + R2best[2][5]*p2Shifted[bv][5] : 0.0f);
			pntSet2[bv][3] = R2best[3][0]*p2Shifted[bv][0] + R2best[3][1]*p2Shifted[bv][1] + R2best[3][2]*p2Shifted[bv][2] + R2best[3][3]*p2Shifted[bv][3] + (k == 5 ? R2best[3][4]*p2Shifted[bv][4] : 0.0f)
								+ (k == 6 ? R2best[3][4]*p2Shifted[bv][4] + R2best[3][5]*p2Shifted[bv][5] : 0.0f);
		}
		else
		{
			pntSet2[bv][0] = R2best[0][0]*p2Shifted[bv][0] + R2best[0][1]*p2Shifted[bv][1];
			pntSet2[bv][1] = R2best[1][0]*p2Shifted[bv][0] + R2best[1][1]*p2Shifted[bv][1];
		}
	}

	//memo re-capture
	for (int i = 0; i < k; i++) delete [] ms[i]; delete [] ms;
	for (int i = 0; i < k; i++) delete [] mst[i]; delete [] mst;
	for (int i = 0; i < k; i++) delete [] ms2[i]; delete [] ms2;
	for (int i = 0; i < k; i++) delete [] mst2[i]; delete [] mst2;
	for (int i = 0; i < k; i++) delete [] cov[i]; delete [] cov;
	delete [] eig_vals;
	for (int i = 0; i < k; i++) delete [] principal_axes[i]; delete [] principal_axes;
	delete [] p1Shifted;	delete [] p2Shifted;
	delete [] principalAxes1;	delete [] principalAxes2;
	delete [] closestPntInP1;
	delete [] perms;
	for (int i = 0; i < k; i++) delete [] R1[i]; delete [] R1;
	for (int i = 0; i < k; i++) delete [] R2[i]; delete [] R2;
	for (int i = 0; i < k; i++) delete [] R1best[i]; delete [] R1best;
	for (int i = 0; i < k; i++) delete [] R2best[i]; delete [] R2best;

	cout << "Done!\n\n";
}

float minDist = INF, maxDist = -INF; //normalizers for L2 distance in hybridCost()
void Mesh::distsAmongKDSets(int* idxSet1, int* idxSet2, int n1, int n2, int k, Mesh* mesh2)
{
	//sets min & max L2 distance b/w two k-D pnt sets of spectral coords, for later normalization purposes
	
	float dist;
	for (int i = 0; i < n1; i++)
		for (int j = 0; j < n2; j++)
		{
			dist = distanceBetweenKD(verts[ idxSet1[i] ]->spectralK, mesh2->verts[ idxSet2[j] ]->spectralK, k);
			if (dist > maxDist)
				maxDist = dist;
			if (dist < minDist)
				minDist = dist;
		}	
}
inline void distsAmongSetsKD(float** pntSet1, float** pntSet2, int n1, int n2, int k)
{
	//sets min & max L2 distance b/w two k-D pnt sets, for later normalization purposes
	
	for (int bv = 0; bv < n1; bv++)
		for (int bv2 = 0; bv2 < n2; bv2++)
		{
			float dist = distanceBetweenKD(pntSet1[bv], pntSet2[bv2], k);
			if (dist > maxDist)
				maxDist = dist;
			if (dist < minDist)
				minDist = dist;
		}
}

inline float Mesh::hybridCost(float* v1Aligned, float* v2Aligned, int k, Vertex* v1, Vertex* v2)
{
	//instead of pure L2 in costs[][], use hybrid cost of L2+avgGd2Roots, where each term is normalized
	//in [0,1], e.g. maxHybridCost would be 2, but due to weights that sum up to 1, maxHybridCost'll be 1

	//avgGd2Root, Mun, nv already normalized in [0,1]; normalize L2 here
	//L2 distance b/w two k-D pnts
	float dist = distanceBetweenKD(v1Aligned, v2Aligned, k);
	dist = (dist - minDist) / (maxDist - minDist); //(sample - min) / range to map it to [0,1] interval

	//since each term in [0,1], fabs() below will be in [0,1] as well
//if (dist < 0 || dist > 1) exit(0); assert(v1->base && v2->base);

//even if w1 = 1 and others = 0, descriptor values that were divided by 0, e.g. Mun, returns -1.#INDR and fails Hungarian; took 3 hrs to see this :(
	return w1*dist + w2*fabs(v1->avgGd2Roots - v2->avgGd2Roots);
}

int ii1; //remember the very first sizes for memo re-captures only
int Mesh::initialSpectralMatching(Mesh* mesh2)
{
	//computes baseVerts of 2 meshes, moves them to spectral domain where they are aligned for the initial correspondence

	int k = 6; //k-D spectral coords (maby later: to draw 6-D, use first 3 coords as x-y-z, and remaining 3 as an index to a color map)

	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 && theInput != TOSCA) //= if (hybridRun)
		hybridModelsToSameCoordFrame(mesh2);

	//compute baseVerts for mesh1 (this)
	prepareForGeodesicComputation(); //shortcut edge existence slows down algo due to O((E+V)lgV) dijkstra, e.g. 2min faster w/o shortcuts for dancing and for jumping; but i saw that shortcut'ed output is better; so, keep it
	r1 = computeBaseVerts(true, true);
	computeBaseNeighbs(r1); //base neighbs of each base for greedy optimization
	//computes geodesic distance matrix between pairs of all base verts, i.e. |nBaseVerts1|x|nBaseVerts1| mtrx (b/w pairs of all verts (16Kx16K) is infeasible)
	computeBaseGeodesicAffinityMtrx(k); //do it last 'cos this updates/normalizes some dSpanning[] values [PCAonAffinity() is also called there]
//return;

//int vv=3644, v2=4471; cout << verts[vv]->dSpanningUnn[v2] << " " << verts[vv]->dSpanning[v2] << endl; exit(0);

	//same stuff for mesh2
	mesh2->prepareForGeodesicComputation();
	r2 = mesh2->computeBaseVerts(true, true);
	mesh2->computeBaseNeighbs(r2);
	mesh2->computeBaseGeodesicAffinityMtrx(k);
//return;

	//bigger of normalized r1 and normalized r2 (not a big deal 'cos both values are close anyway; use the bigger to see smaller costs, both for me and for Zhang)
	normalizedR1 = r1 / maxGeoDistG; //div isometryCost (in [0,1]) by this to get it in terms of r; if r = maxGeoDistG (which can't happen but anyway), normalizedR is 1
#ifdef DBG_DETAIL
	cout << "normalized r1 = " << normalizedR1 << endl;
#endif

	//align spectral coords considering arbitrary sign flips and reordering of eigvecs of geodesic affinity matrices
	findBestAffinityEigvectors(mesh2, k); //either use this and not PCAalignment(K)() in loop below or vice versa

	moveCOMtoOrigin();
	initRoots(); //moved here from above for automatic 1st root (even if firstRootAuto = false, this has no harm to manual 1st root based execution)
	mesh2->moveCOMtoOrigin();
	mesh2->initRoots();

	float minMSE = INF, discourage = 1.0f; //using INF for the virtual entries of costs[][] fails Hungarian.solve(); again discourage them, but w/ a smaller number; recall, costs[][] entries here are normalized in [0,1] for the very 1st hungarian; so, 1 is good for that; in general, use the largest possible value in costs[][]
#ifndef HUNGARIAN_INSTEAD_OF_BLOSSOM //if not defined; so, blossom in use
	discourage = INF; //hungarian code rarely acts differently for discourage = 1 and = INF; keep it this way; it must be a big number anyway
#endif
	/////// memo allocations ///////
	int n1 = (int) baseVerts.size(), n2 = (int) mesh2->baseVerts.size();
	ii1 = n1; //remember the very first sizes for memo re-captures only
	float** pntSetK1, ** pntSetK2, ** pntSetK1c, ** pntSetK2c; //last 2 are copies corresponding to the best candidates
	int* vIdx2; //if r,c = 3,7 is a match in costs[r][c], i.e. [3][7]=0, then baseVerts[3].matchIdx = vIdx2[7], hence vIdx2[7] is an idx to verts, unlike c which is an idx to baseVerts[]
	gd2r1 = new float[n1]; //although defined for all verts, PCAalignmentK works on baseVerts only; so, keep only baseVerts entries w/ these globals
	gd2r2 = new float[n2];
	pntSetK1 = new float*[n1]; //for mesh1's (this) spectralCoords (of baseVerts)
	for (int i = 0; i < n1; i++) pntSetK1[i] = new float[k];
	pntSetK2 = new float*[n2]; //same stuff for mesh2
	for (int i = 0; i < n2; i++) pntSetK2[i] = new float[k];
	//copies
	pntSetK1c = new float*[n1]; for (int i = 0; i < n1; i++) pntSetK1c[i] = new float[k];
	pntSetK2c = new float*[n2]; for (int i = 0; i < n2; i++) pntSetK2c[i] = new float[k];
	//remember the related idx to verts[] for matchIdx decision
	vIdx2 = new int[n2];
	int nRows = (n1 > n2 ? n1 : n2), nCols = nRows; //square mtrx
	float** costs = new float*[nRows]; //[3][8]: costs of assigning mesh1.baseVerts3 to mesh2.baseVerts8, [3][4]: cost of .. mesh1.baseVerts3 to mesh2.baseVerts4
	for (int r = 0; r < nRows; r++)
		costs[r] = new float[nCols];
	/////// memo allocations end ///////

	//now that PCA on the baseGeoAffinity matrix (to embed it into k-D spectral (best k-eigvecs) domain) is done, play w/ spectral coords		
	for (int i = 0; i < n1; i++)
	{
		for (int c = 0; c < k; c++)
			pntSetK1[i][c] = verts[ baseVerts[i] ]->spectralK[c]; //refilling them in each iter w/ same values is inefficient; move this outside the rc1-loop later
		if (rootBasedSymSoln)
			gd2r1[i] = verts[ baseVerts[i] ]->avgGd2Roots;
	}
	for (int i = 0; i < n2; i++)
	{
		for (int c = 0; c < k; c++)
			pntSetK2[i][c] = mesh2->verts[ mesh2->baseVerts[i] ]->spectralK[c];
		vIdx2[i] = mesh2->baseVerts[i];
		if (rootBasedSymSoln)
			gd2r2[i] = mesh2->verts[ mesh2->baseVerts[i] ]->avgGd2Roots;
	}

	//PCAalignmentK(pntSetK1, pntSetK2, n1, n2, k); //pntSetK1/2 updated via call-by-ref [not in use thanks to the effect of findBestAffinityEigvectors() above]
	float mse = computeMSEKD(pntSetK1, pntSetK2, n1, n2, k);
#ifdef DBG_DETAIL
	cout << "mean squared error after findBestAffinityEigvectors (or PCAalignment): " << mse << endl;
#endif
	//normalizers for L2 distance that goes into hybrid costs[][]
	minDist = INF; maxDist = -INF; //normalizers for L2 distance in hybridCost() [init again to clear effects of previous iteration]
	distsAmongSetsKD(pntSetK1, pntSetK2, n1, n2, k);
//cout << "min & max L2 dist difference among 2 sets: " << minDist << " & " << maxDist << endl;
						
	//compute Hungarian assignment on this alignment of source and target spectral coords
	cout << "\nInitial Blossom V assignment in the spectral domain..\n";
//if this is taking too long, check costs[] 'cos -1.INDR entries there is causing that wait; took 3 hrs to see :(
	//common to all k values (except distanceBetween() & distanceBetween4D())
	//now that 2 base sets are PCAaligned, i can pair them up w/ hybrid costs including Euclidean dist and more; use Hungarian (Munkres) algo
	for (int r = 0; r < nRows; r++)
		for (int c = 0; c < nCols; c++)
			costs[r][c] = ((r < n1 && c < n2) ? hybridCost(pntSetK1[r], pntSetK2[c], k, verts[ baseVerts[r] ], mesh2->verts[ mesh2->baseVerts[c] ]) : discourage); //use hybrid cost instead
//since pntSetK1/2 is different from v.spectralK (i.e. aligned), i need to send them to hybridCost() just-above separately

#ifdef PROBABILISTIC_COST
	//costs[][] in [0,1] for sure thanks to hybridCost; so, convert them into probabilities of matching i w/ j for [i][j]: prob = 1 - cost;; to obey pdf, sum of each row (excluding virtuals) must be 1 as well
	//prob[i][j] = 1 - cost[i][j] effect
	float* rowSum = new float[n1]; //rowSum[i] = sum of probability entries in i'th row
	for (int r = 0; r < n1; r++)
		rowSum[r] = 0.0f;
	for (int r = 0; r < nRows; r++)
		for (int c = 0; c < nCols; c++)
			if (r < n1 && c < n2) //ignore virtuals
			{
				costs[r][c] = 1.0f - costs[r][c]; //cost to prob conversion
				rowSum[r] += costs[r][c]; //sum of this row in terms of probabilities
			}
	//obey pdf effect (by making rowSum = 1, i.e. by dividing each entries of i'th row by rowSum[i]; virtuals = discourage stay as is and no problem w/ that)
	for (int r = 0; r < nRows; r++)
		for (int c = 0; c < nCols; c++)
			if (r < n1 && c < n2) //ignore virtuals
				costs[r][c] /= rowSum[r]; //make sum of probs in each row 1 (excluding virtuals, i.e. consider them as 0 probs so that matching a vert to virtual has 0 probability)
	delete [] rowSum;
	//take the log of probs for EM framework; since hungarian/blossom sucks on -ve edges, take the
	//abs of log; this also helps for min-weight perfect matching, i.e. smaller costs map to smaller prob entries and hence min-weight can be done; example given in setCorrespondenceErrs() for this comment
	float maxC = -INF, minC = INF;
	for (int r = 0; r < nRows; r++)
		for (int c = 0; c < nCols; c++)
			if (r < n1 && c < n2)
			{
				costs[r][c] = (costs[r][c] != 0.0f ? fabs( log10(costs[r][c]) ) : discourage); //log10(0) must be prevented 'cos it's undefined; log10(0) happens when isometryCost is 1 (= bad), so, corresponding entry must be discourage to prevent its selection
				//prob in [0,1] but when abs-log taken, we have >1 values, e.g. abs( log(0.01) ) = 2; so, normalization needed
				if (costs[r][c] > maxC && costs[r][c] != discourage)
					maxC = costs[r][c];
				if (costs[r][c] < minC)
					minC = costs[r][c];
			}
	//normalization to put values in [0,1]
	for (int r = 0; r < nRows; r++)
		for (int c = 0; c < nCols; c++)
			costs[r][c] = ((r < n1 && c < n2) ? (costs[r][c] - minC) / (maxC - minC)  : discourage);
#endif

#ifdef HUNGARIAN_INSTEAD_OF_BLOSSOM
	//if mse of PCA-normalization is bad, then costs[][] would be bad, which makes Hungarian result unreliable; so, use minMSE-based best candidates
	Hungarian* h = new Hungarian(nRows, nCols); //could have been replaced by blossom but anyway
	h->solve(costs); //result of Hungarian is written to costs mtrx (hence the weird naming; interpret it as assignments from now on)
//h->sanityCheck(); //not necessary
	delete h;

	for (int r = 0; r < nRows; r++)
		for (int c = 0; c < nCols; c++)
			if (r < n1 && costs[r][c] == 0) //0 means that column c is selected for this row r
			{
				verts[ baseVerts[r] ]->matchIdx = (c < n2 ? vIdx2[c] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1
				if (verts[ baseVerts[r] ]->matchIdx != -1)
				{
assert(mesh2->verts[ vIdx2[c] ]->base); //baseVerts[r].matchIdx must be a base
					mesh2->verts[ vIdx2[c] ]->matchIdx = baseVerts[r];
					mesh2->verts[ vIdx2[c] ]->nMatches = 1; //1-to-1 mapping of Hungarian guarantees 1 match per m2.v
				}
			}
#else //blossom
	//////////// blossom input ///////////////
	int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e = 0, * edges = new int[2*nEdgeNum];
	double* weightsD = new double[nEdgeNum];
	for (int i = 0; i < nRows; i++)
		for (int j = 0; j < nCols; j++)
		{
			edges[2*e] = i;
			edges[2*e+1] = nRows+j;
			weightsD[e++] = (double) costs[i][j];
		}
	//////////// blossom input ends ///////////////

	Blossom* blossom = new Blossom();
	int* matches = new int[nVertNum]; //see this as lines of matches where 2 consecutive items make a matching line, e.g. 369 694
	blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches); //matches update by call-by-ref
	delete blossom;
	delete [] edges;
	delete [] weightsD;

	//transform data in matches[] to matchIdx values consistently; heavily adapted from isometryBasedGraphMatching()
	for (int m = 0; m < nRows; m++)
	{
		int m1 = matches[2*m]; //left side of the (m+1)'th matching line
		if (m1 < n1)
		{
			int m2 = matches[2*m + 1] - nRows; //mathches[m1 + 1], right side of the line, is in [nRows..2*nRows-1], hence nRows entry at mesh2
			verts[ baseVerts[m1] ]->matchIdx = (m2 < n2 ? vIdx2[m2] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1; not setting mesh2->matchIdx, which is not important anyway
			if (verts[ baseVerts[m1] ]->matchIdx != -1)
			{
assert(mesh2->verts[ vIdx2[m2] ]->base); //baseVerts[m1].matchIdx must be a base
				mesh2->verts[ vIdx2[m2] ]->matchIdx = baseVerts[m1];
				mesh2->verts[ vIdx2[m2] ]->nMatches = 1; //1-to-1 mapping of blossom guarantees 1 match per m2.v
			}
		}
	} //end of m
	delete [] matches;
#endif
	cout << "Done!\n";

	refillSafeCorresp(mesh2, 1); //w.r.t. matchIdx values set by this iteration's Hungarian (safeCorresp is used in setCorrespondenceErrs())
	setCorrespondenceErrs(mesh2, r1, 1);
	setExtremeCorrespondences(r1, true); //extreme results of 1st Hungarian
	worstMatchI = bestMatchI = worstMatchG = bestMatchG = -1; //init them in case i return early

	for (int i = 0; i < n1; i++)
		for (int c = 0; c < k; c++)
			verts[ baseVerts[i] ]->spectralK[c] = pntSetK1[i][c]; //spectralK has the aligned coords from now on
	for (int i = 0; i < n2; i++)
		for (int c = 0; c < k; c++)
			mesh2->verts[ mesh2->baseVerts[i] ]->spectralK[c] = pntSetK2[i][c];

//	fetchSpectralInfo(k);
//	mesh2->fetchSpectralInfo(k);

	char fName2[250];
	if (theInput == JUMPING)
		sprintf(fName2, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\jumping\\errs%d-%d %d.dat", frameID, mesh2->frameID, (int) baseVerts.size());
	else if (theInput == HORSE)
		sprintf(fName2, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\horse\\errs%d-%d %d.dat", frameID, mesh2->frameID, (int) baseVerts.size());
	else
		sprintf(fName2, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\%s\\errs%d-%d %d.dat", (theInput != DANCING ? "hybrid" : "dancing"), frameID, mesh2->frameID, (int) baseVerts.size());
	errfPtr = fopen(fName2, "w");
	fprintf(errfPtr, "%d vs. %d\nafter initialization: gr = %fr, iso = %fr\t\tworsts: %fr, %fr\n", (int) baseVerts.size(), (int) mesh2->baseVerts.size(), groundTruthCost/r1, isometryCost/normalizedR1, maxCostGg/r1, maxCostIg/normalizedR1);

	delete [] pntSetK1; delete [] pntSetK2;
	delete [] pntSetK1c; delete [] pntSetK2c;
	delete [] vIdx2;
	delete [] gd2r1;
	delete [] gd2r2;
	for (int i = 0; i < nRows; i++) delete [] costs[i]; delete [] costs;
	
	return k;
}

int Mesh::initialEuclideanMatching(Mesh* mesh2)
{
	//computes ~5 baseVerts of 2 meshes and matches them for the initial correspondence using combinations/permutations

//if (theInput != JUMPING && theInput != DANCING && theInput != HORSE) //= if (hybridRun)
		hybridModelsToSameCoordFrame(mesh2); //i can't choose input now, so call here at all times

	//put the initial ~5 bases to the extremities of mesh1/2; curvature in gcOrder[] does this effect mostly, i.e. for all data except dog_wolf, but this mu-based scheme should be more safe
	bool initialBaseByMu = true;

	//compute baseVerts for mesh1 (this)
	prepareForGeodesicComputation(); //shortcut edge existence slows down algo due to O((E+V)lgV) dijkstra, e.g. 2min faster w/o shortcuts for dancing and for jumping; but i saw that shortcut'ed output is better; so, keep it
	r1 = computeBaseVerts(true, true, initialBaseByMu);

	//same stuff for mesh2
	mesh2->prepareForGeodesicComputation();
	r2 = mesh2->computeBaseVerts(true, true, initialBaseByMu);

	if (nInitialSamples != -1 && (int) baseVerts.size() != (int) mesh2->baseVerts.size())
	{
		cout << "\n\n# samples not equal which leaves some unmatched samples in forced 1-to-1 initial mapping; so, either cancel forcing 1-to-1 at level 0 or re-run w/ different r values\n";
		exit(0);
	}

	//bigger of normalized r1 and normalized r2 (not a big deal 'cos both values are close anyway; use the bigger to see smaller costs, both for me and for Zhang)
	normalizedR1 = r1 / maxGeoDistG; //div isometryCost (in [0,1]) by this to get it in terms of r; if r = maxGeoDistG (which can't happen but anyway), normalizedR is 1
#ifdef DBG_DETAIL
	cout << "normalized r1 = " << normalizedR1 << endl;
#endif

	//just for better visual
	moveCOMtoOrigin();
	mesh2->moveCOMtoOrigin();

	//////////// combinations/permutations for initial matching ////////////////
	//prepare tmpBaseVerts set to be used matchSmallTmpBases()
	for (int i = 0; i < (int) baseVerts.size(); i++)
		tmpBaseVerts.push_back(baseVerts[i]);
	for (int i = 0; i < (int) mesh2->baseVerts.size(); i++)
		mesh2->tmpBaseVerts.push_back(mesh2->baseVerts[i]);
assert((int) tmpBaseVerts.size() > 2 && (int) mesh2->tmpBaseVerts.size() > 2); //actually they're ~5 but be sure the top of matchSmallTmpBases(), i.e. == 1 || == 2 part, not executed (although there's no harm to execute there anyway)

	fillPermutationMtrxs(); //to be used just-below
	matchSmallTmpBases(mesh2); //forPostProcessing=true here is a bad naming but does what needs to be done, e.g. useCurrentAndParentForTraversalList=false effect and no patches[] info maintanence in vain

	//use matchIdxs[] to create the fresh safeCorresp list which will be traversed in the next level of iteration		
	refillSafeCorrespTmp(mesh2, 0);

	//baseVerts/baseNghbhood already made ready by computeBaseVerts()
interestingMatches.clear();
	initialCorresp = new vector<int>;
	for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
	{
		int bv1 = safeCorresp[2*ss], bv2 = safeCorresp[2*ss + 1];
		initialCorresp->push_back(bv1);
		initialCorresp->push_back(bv2);

assert(verts[bv1]->matchIdxs.size() == 2 && mesh2->verts[bv2]->matchIdxs.size() == 2); //1-to-1 guarantee
interestingMatches.push_back(bv1);interestingMatches.push_back(bv2);
	} //end of ss

	//matchIdxs[] and matchIndexes[] need to be empty for upcoming iterations in coarseToFineMatching()
	clearVects2(); 
	mesh2->clearVects2();

	//select the best 5 matches out of ~10 initial matches
	initialCorrespC10_5(mesh2);

	levelInfoToFile(0, mesh2);
	//////////// combinations/permutations for initial matching ends ////////////////

	return -1; //unused
}
float Mesh::initialEuclideanMatchingFromFile(Mesh* mesh2, int nthMapping, bool firstCall, bool toFile)
{
	//freads mxm mappings in file (m ~ 8); one of 8! mappings (<< 8! 'cos i don't fwrite too bad mappings) in that file is hopefully/quite probably healing the symmetry problem

	int m = 8, mv; //among mxm initial mapping candidates, fread the nthMapping

	if (firstCall)
		hybridModelsToSameCoordFrame(mesh2);

	char fsName[300]; sprintf(fsName, "%s\\symHeal%d-%d_%dx%dtop10.dat", outFolder, frameID, mesh2->frameID, m, m); //the ones that make to top10
	FILE* fsPtr = fopen(fsName, "r");
	if (! fsPtr)
	{
		cout << "symFlipCare file: " << fsName << " not found\n";
		exit(0);
	}

	if (firstCall)
	{
		//baseVerts to be loaded from file (which already computed them w/ radii); but first radii
		prepareForGeodesicComputation(); //shortcut edge existence slows down algo due to O((E+V)lgV) dijkstra, e.g. 2min faster w/o shortcuts for dancing and for jumping; but i saw that shortcut'ed output is better; so, keep it	
		mesh2->prepareForGeodesicComputation();
	}
	fscanf(fsPtr, "%d\t\t%f\t%f\t\t%f\t%f\n\n", &mv, &maxGeoDistG, &mesh2->maxGeoDistG, &r1, &r2); //maxGeoDistG's for dSpanning[] normalization
assert(mv == m); //verify
	minGeoDistG = mesh2->minGeoDistG = 0.0f;
//cout << maxGeoDistG << " " << mesh2->maxGeoDistG << " " << r1 << " " << r2 << "\n\n";

	//bigger of normalized r1 and normalized r2 (not a big deal 'cos both values are close anyway; use the bigger to see smaller costs, both for me and for Zhang)
	normalizedR1 = r1 / maxGeoDistG; //div isometryCost (in [0,1]) by this to get it in terms of r; if r = maxGeoDistG (which can't happen but anyway), normalizedR is 1
#ifdef DBG_DETAIL
	cout << "normalized r1 = " << normalizedR1 << endl;
#endif

	if (firstCall)
	{
		//just for better visual
		moveCOMtoOrigin();
		mesh2->moveCOMtoOrigin();
	}

	//each mapping comes w/ m+1 lines (isometry cost + m lines of mapping); skip the nthMapping-1 preceding mapping
	float isoCost, isoCostSave;
	int v1, v2;
	bool noMoreMapping = false; //no more mapping to process
	for (int map = 1; map < nthMapping; map++)
	{
		fscanf(fsPtr, "%f\n", &isoCost); //dummy
		if (isoCost == -2.0f)
		{
			noMoreMapping = true;
			break;
		}
		for (int i = 0; i < m; i++)
			fscanf(fsPtr, "%d\t%d\n", &v1, &v2); //dummy
	}
	if (noMoreMapping)
		return isoCost; //= -2
	//desired mapping
	fscanf(fsPtr, "%f\n", &isoCost);
	if (isoCost == -2.0f)
		return isoCost; //= -2
	isoCostSave = isoCost;
	baseVerts.clear(); mesh2->baseVerts.clear(); //clearVects2() already clear'ed matchIdxs values
	for (int i = 0; i < m; i++)
	{
		fscanf(fsPtr, "%d\t%d\n", &v1, &v2);
		verts[v1]->matchIdxs.push_back((float) v2);
		verts[v1]->matchIdxs.push_back(0.0f); //0.0f is dummy 'cos odd indices (matchCostI) will be skipped in refillSafeCorrespTmp() anyway
		mesh2->verts[v2]->matchIdxs.push_back((float) v1); //duality
		mesh2->verts[v2]->matchIdxs.push_back(0.0f);
		baseVerts.push_back(v1);
		mesh2->baseVerts.push_back(v2);
		verts[v1]->tmpBaseLevel = mesh2->verts[v2]->tmpBaseLevel = 0; //refillSafeCorrespTmp() uses this info
		dijkstraShortestPaths(v1); //dSpanning[]'s now are good 'cos i used good min/maxGeoDistG
		mesh2->dijkstraShortestPaths(v2);
//cout << v1 << "\t" << v2 << endl;
	}

	//skip following mappings to reach the baseNghbhood section
	fscanf(fsPtr, "%f\n", &isoCost); //dummy
	while (isoCost != -2.0f) //we've a mapping to skip
	{
		for (int i = 0; i < m; i++)
			fscanf(fsPtr, "%d\t%d\n", &v1, &v2); //dummy
		fscanf(fsPtr, "%f\n", &isoCost); //dummy
	}
	
//if (firstCall) //don't empty (by delete'ing) and refill the baseNghbhood w/ same info by preventing below for calls other than the 1st one; no!! baseNghbhood gets shrunk as levels proceed; so, refill to fresh initial baseNghbhood from file
//{
		//get baseNghbhood info as well
		fscanf(fsPtr, "%d\n", &v1); //first line for the base id whose baseNghbhood'll be fread from the subsequent line
		while (v1 != -2)
		{
			if (verts[v1]->baseNghbhood)
				delete verts[v1]->baseNghbhood;				
			verts[v1]->baseNghbhood = new vector<int>;
			while (true)
			{
				int v;
				fscanf(fsPtr, "%d ", &v);
				if (v == -3)
					break;
				verts[v1]->baseNghbhood->push_back(v);
			}
			//get next base
			fscanf(fsPtr, "%d\n", &v1);
		}
		fscanf(fsPtr, "%d\n", &v2); //same stuff for mesh2
		while (v2 != -2)
		{
			if (mesh2->verts[v2]->baseNghbhood)
				delete mesh2->verts[v2]->baseNghbhood;
			mesh2->verts[v2]->baseNghbhood = new vector<int>;
			while (true)
			{
				int v;
				fscanf(fsPtr, "%d ", &v);
				if (v == -3)
					break;
				mesh2->verts[v2]->baseNghbhood->push_back(v);
			}
			//get next base
			fscanf(fsPtr, "%d\n", &v2);
		}
//}
	cout << m << " x " << m << " initial mapping fread\n";

	if (firstCall)
	{
		//for upcoming level's COES sampling
		computeGaussianCurvatures();
		sortCurvatures();
		mesh2->computeGaussianCurvatures();
		mesh2->sortCurvatures();
	}

	//prepare tmpBaseVerts
	tmpBaseVerts.clear(); mesh2->tmpBaseVerts.clear();
	for (int i = 0; i < (int) baseVerts.size(); i++)
		tmpBaseVerts.push_back(baseVerts[i]);
	for (int i = 0; i < (int) mesh2->baseVerts.size(); i++)
		mesh2->tmpBaseVerts.push_back(mesh2->baseVerts[i]);

	if (firstCall)
		fillPermutationMtrxs(); //to be used in later matchSmallTmpBases() calls
		//matchSmallTmpBases(mesh2); no need 'cos i set matchIdxs above; the rest is same as initialEuclideanMatching()

	//use matchIdxs[] to create the fresh safeCorresp list which will be traversed in the next level of iteration		
	refillSafeCorrespTmp(mesh2, 0);

	//baseVerts/baseNghbhood already made ready by computeBaseVerts()
interestingMatches.clear();
	initialCorresp = new vector<int>;
	for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
	{
		int bv1 = safeCorresp[2*ss], bv2 = safeCorresp[2*ss + 1];
		initialCorresp->push_back(bv1);
		initialCorresp->push_back(bv2);

assert(verts[bv1]->matchIdxs.size() == 2 && mesh2->verts[bv2]->matchIdxs.size() == 2); //1-to-1 guarantee
interestingMatches.push_back(bv1);interestingMatches.push_back(bv2);
	} //end of ss

	//matchIdxs[] and matchIndexes[] need to be empty for upcoming iterations in coarseToFineMatching()
	clearVects2(); 
	mesh2->clearVects2();

	//select the best 5 matches out of ~10 initial matches
	initialCorrespC10_5(mesh2); //when parent size is not big enough, i consult to initialCorresp

	if (toFile)
		levelInfoToFile(0, mesh2); //no need during sym heal files creations

	return isoCostSave;
}

int Mesh::greedyOpt(Mesh* mesh2, bool alternatingOpt, int k)
{
	//greedy optimization that minimizes isometryCost; slightly modified version of our CVPR'10; alternating optimization further creates 1-to-1 mapping via isometryCost'ed blossoms

	//remember the related idx to verts[] for matchIdx decision (copied from initialSpectralMatching())
	int* vIdx2 = new int[ (int) mesh2->baseVerts.size() ]; //if r,c = 3,7 is a match in costs[r][c], i.e. [3][7]=0, then baseVerts[3].matchIdx = vIdx2[7], hence vIdx2[7] is an idx to verts, unlike c which is an idx to baseVerts[]
	for (int i = 0; i < (int) mesh2->baseVerts.size(); i++)
		vIdx2[i] = mesh2->baseVerts[i];

//for (int v = 0; v < (int) verts.size(); v++) verts[v]->bam2 = verts[v]->matchIdx; //save initial spectral results just to count # of correspondence updates made by alternating-optimization (isometryBasedGraphMatching())

	bool ultimateIn1to1 = true; //true returns the ultimate output 1-to-1, i.e. do isometryBasedGraphMatching() for the very last iter as well

	//since the initial baseVerts of this (mesh1) and mesh2 are not the ground-truth values, after initialSpectralMatching(), we matched
	//the mesh2.baseVert to best mesh1.baseVert available; refine these pairs by updating mesh2.bases accordingly
	float prevIsoCost;
	int iterOuter = 0, lastIter = 5;
	do
	{
		cout << "\n\n\n******************** " << iterOuter << " ********************\n";
		int iter = 1; //for greedyForBaseUpdate()
		do
		{
			prevIsoCost = isometryCost;
			greedyForBaseUpdate(mesh2, r1, iter++); //updates global isometryCost as well
			if (iter == 2)
			{
				setExtremeCorrespondences(r1, true);
				fprintf(errfPtr, "[outerIter%d] after 1st greedy: gr = %fr, iso = %fr\t\tworsts: %fr, %fr\n", iterOuter, groundTruthCost/r1, isometryCost/normalizedR1, maxCostGg/r1, maxCostIg/normalizedR1);
			}
		} while (prevIsoCost > isometryCost && iter < 10); //stop when isometryCost stops to decrease; last condition for efficiency as well as to prevent infinite loops
		setExtremeCorrespondences(r1, true);
		fprintf(errfPtr, "[outerIter%d] after all greedy: gr = %fr, iso = %fr\t\tworsts: %fr, %fr\n", iterOuter, groundTruthCost/r1, isometryCost/normalizedR1, maxCostGg/r1, maxCostIg/normalizedR1); //ground-truth costs not in [0,1], so use unnormalized r

		if (! alternatingOpt)
		{
			iterOuter = 1000000;
			break; //see the result of greedy optimization only, i.e. no alternating optimization
		}

//int i2g=0;for (int v = 0; v < (int) verts.size(); v++) {if (verts[v]->matchIdx != verts[v]->bam2)i2g++; verts[v]->bam = verts[v]->matchIdx;}  //just to count # of correspondence updates made by alternating-optimization (isometryBasedGraphMatching())

		///////////////// hungarian/blossom after greedy /////////////////////
		if (! ultimateIn1to1 && iterOuter == lastIter)
			; //ultimateIn1to1 not wanted; so, don't do 1-1 isometryBasedGraphMatching() in the very lastIter
		else
		{
			prevIsoCost = isometryCost; //isometryCost'll be updated by isometryBasedGraphMatching(), and if new isometryCost not improve, i.e. same as before (converged), then outer do-loop breaks
			//runs an Hungarian w.r.t. improved matchCostI, thnks to greedy optimization in greedyForBaseUpdate() computes the initial correspondences
			isometryBasedGraphMatching(mesh2, r1, vIdx2, 1, k); //updates global isometryCost as well
			//adjustBaseVerts() still not done 'cos unmatched verts might want to match w/ someone in an Hungarian; now that Hungarians over, adjust bases; no!!! 'cos Hungarians not over yet, i.e. upcoming in next iteration; so this block should be removed outside this loop
		}

//int g2a=0,i2a=0;for (int v = 0; v < (int) verts.size(); v++) {if (verts[v]->matchIdx != verts[v]->bam) g2a++;if (verts[v]->matchIdx != verts[v]->bam2) i2a++;} cout<<"# of correspondence updates as a result of passing from greedy / initialSpectral to alternating optimization: " << g2a << " / " << i2a << "; also from initial to greedy: " << i2g << endl;

		setExtremeCorrespondences(r1, true);
		fprintf(errfPtr, "[outerIter%d] after blossom: gr = %fr, iso = %fr\t\tworsts: %fr, %fr\n\n", iterOuter, groundTruthCost/r1, isometryCost/normalizedR1, maxCostGg/r1, maxCostIg/normalizedR1);
		///////////////// hungarian/blossom after greedy ends /////////////////////
	} while (prevIsoCost != isometryCost && ++iterOuter <= lastIter); //stop when isometryCost stops to decrease; indeed, go 5-10 times, and pick the best (later) 'cos i may have local-minima on the road; e.g. 10 iters of dog-wolf: 0.574, 0.571, 0.575, 0.577, 0.560, 0.586, 0.577, 0.560, 0.586, 0.577, 0.560

	//memo re-capture
	delete [] vIdx2;
	return k;
}

void Mesh::finalizeMatching(Mesh* mesh2, int k, bool detailsToFile)
{
	//decides ultimate correspondence after discarding the bases that are not safe; creates unsorted and then sorted C_*.dat

	//can safely cancel bases that are not safe, i.e. not in safeCorresp (= have matchIdx=-1), at this time
	int initialSize1 = adjustBaseVerts(),
		initialSize2 = mesh2->adjustBaseVerts(); //these sizes may not be equal to initials 'cos unmatched verts, the ones matching w/ virtuals, are gone at this time
	cout << "mesh1.baseVerts size reduced from " << initialSize1 << " to " << baseVerts.size() << "\nmesh2.baseVerts size reduced from " << initialSize2 << " to " << mesh2->baseVerts.size() << endl;
	fprintf(errfPtr, "%d vs. %d\n", (int) baseVerts.size(), (int) mesh2->baseVerts.size());
	fclose(errfPtr);

	//fill safeCorresp which will be used to set the vertex-based and average correspondence errors
	refillSafeCorresp(mesh2, 1); //bv2 can't be -1 in refillSafeCorresp() due to removals by adjustBaseVerts(); so, 2nd param not important here
	setCorrespondenceErrs(mesh2, r1, -2);
	setExtremeCorrespondences(r1, false); //these 3 lines are redundant 'cos already done, e.g. matchCostI/G set, in the end of greedyOpt() whether alternatingOpt is true or not

	//fprint the results in unsorted order
	char fName[250];
	if (theInput == JUMPING)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\jumping\\C_%d-%d %d.dat", frameID, mesh2->frameID, (int) baseVerts.size());
	else if (theInput == HORSE)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\horse\\C_%d-%d %d.dat", frameID, mesh2->frameID, (int) baseVerts.size());
	else
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\%s\\C_%d-%d %d.dat", (theInput != DANCING ? "hybrid" : "dancing"), frameID, mesh2->frameID, (int) baseVerts.size());
	FILE* fPtr = fopen(fName, "w");
	fprintf(fPtr, "%d\t\t%f\t%f\t%f\t%f\t%f\t%f\n", (int) baseVerts.size(), r1, r2, minGeoDistG, maxGeoDistG, mesh2->minGeoDistG, mesh2->maxGeoDistG); //# of correspondences to be written below and r values and min/maxGeoDistG
interestingMatches.clear();
	initialCorresp = new vector<int>;
	for (int b = 0; b < (int) baseVerts.size(); b++)
	{
		//now that bases are updated, i can pair up elements of corresponding patches (denseCorresp); bases in this file not sorted and no baseNghbhood info here
		int corresp = verts[ baseVerts[b] ]->matchIdx;
assert(corresp != -1); //adjustBaseVerts() should already have deleted such bases

		fprintf(fPtr, "%d\t%d\t%f\t%f\n", baseVerts[b], corresp, verts[ baseVerts[b] ]->matchCostI, ((theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 /*&& theInput != SCAPE*/ && theInput != TOSCA) ? -0.9f : verts[ baseVerts[b] ]->matchCostG)); //ground-truth n/a for hybridRun, so use -0.9
		nCorresps++;
		mesh2->nCorresps++;
		initialCorresp->push_back(baseVerts[b]);
		initialCorresp->push_back(corresp);
interestingMatches.push_back(baseVerts[b]);interestingMatches.push_back(corresp);
	}
	fclose(fPtr);

	if (detailsToFile)
		//create necessary file to be used later for dense correspondence; bases sorted w.r.t. matchCostI in this file and duplicate-free baseNghbhoods/patches also here
		duplicateFreePatchesToFile(mesh2, fName); //overwrite to fName above

	//remember them to draw w/ a sphere endpoint during visualization
	if (worstMatchG != -1)
	{
		interestingMatches.push_back(worstMatchG);interestingMatches.push_back(verts[worstMatchG]->matchIdx);
	}
	if (worstMatchI != -1)
	{
		interestingMatches.push_back(worstMatchI);interestingMatches.push_back(verts[worstMatchI]->matchIdx);
	}

	//select the best 5 matches out of ~10 initial matches
	initialCorrespC10_5(mesh2);

	if (baseGeoAffinity) //becomes non-null only if initialSpectralMatching() gets called
	{
		//memo re-capture
		for (int i = 0; i < ii1; i++) delete [] baseGeoAffinity[i]; delete [] baseGeoAffinity;
		for (int i = 0; i < (int) mesh2->baseVerts.size(); i++) delete [] mesh2->baseGeoAffinity[i]; delete [] mesh2->baseGeoAffinity;
		for (int i = 0; i < k; i++) delete [] spectralCoords[i]; delete [] spectralCoords;
	}

	cout << nCorresps << " correspondences computed\nDone!\n\n";
}

void Mesh::initialCorrespC10_5(Mesh* mesh2)
{
	//converts ~10 size spectral correspondence list into size 5 by picking the best 5-tuple combination out of C(10,5) combinations
	
	int m = (int) initialCorresp->size() / 2; //this naming indeed sucks for initialEuclideanMatching(); think it as initialCorresp
assert(m >= 5); //typically m is 10 but it can't be < 5 'cos then 5-nested-fors below crash; e.g., when m=5, there is only C(5,5)=1 combination to try

	if (m > 20)
		return; //don't do below in vain 'cos EM_ALGO probably defined and therefore i started w/ 40 or 180 bases and not need the best 5

	//choose 5 from m, hence 5-nested loops to try all combinations
	float minIsometryCost = INF;
	vector< int >* bestCorresp = NULL;
	for (int a = 0; a < m; a++)
		for (int b = a+1; b < m; b++)
			for (int c = b+1; c < m; c++)
				for (int d = c+1; d < m; d++)
					for (int e = d+1; e < m; e++)
					{
						//current 5-combination is copied from appropriate slots of initialCorresp to initialCorrespTry
						vector< int >* initialCorrespTry = new vector<int>;
						//corresp # 1
						initialCorrespTry->push_back((*initialCorresp)[2*a]);
						initialCorrespTry->push_back((*initialCorresp)[2*a + 1]);
						//corresp # 2
						initialCorrespTry->push_back((*initialCorresp)[2*b]);
						initialCorrespTry->push_back((*initialCorresp)[2*b + 1]);
						//corresp # 3
						initialCorrespTry->push_back((*initialCorresp)[2*c]);
						initialCorrespTry->push_back((*initialCorresp)[2*c + 1]);
						//corresp # 4
						initialCorrespTry->push_back((*initialCorresp)[2*d]);
						initialCorrespTry->push_back((*initialCorresp)[2*d + 1]);
						//corresp # 5
						initialCorrespTry->push_back((*initialCorresp)[2*e]);
						initialCorrespTry->push_back((*initialCorresp)[2*e + 1]);

						float isometryCostTry = getErrOfCorrespList(mesh2, *initialCorrespTry, false);
						if (isometryCostTry < minIsometryCost)
						{
							minIsometryCost = isometryCostTry;
							if (bestCorresp) //clear previous effects
								delete bestCorresp; //bestCorresp = NULL not made 'cos bestCorresp'll be non-NULL in line just-below anyway
							bestCorresp = new vector<int>; //start a brand new bestCorresp
							for (int i = 0; i < (int) initialCorrespTry->size(); i++) //bestCorresp = initialCorrespTry effect by explicit copy
								bestCorresp->push_back((*initialCorrespTry)[i]);
						}
						//memo recapture
						delete initialCorrespTry;
					} //end of e

	//initialCorresp is of size 5 now, the best 5-tuple fetched from the initial ~10 matches
	initialCorresp = bestCorresp;
assert((int) initialCorresp->size() == 10); //10 means 5x2, hence 5 matches
}

void Mesh::safeCorrespC10_5(Mesh* mesh2)
{
	//converts ~10 size safeCorresp list into size 5 by picking the best 5-tuple combination out of C(10,5) combinations
	
	int m = (int) safeCorresp.size() / 2;
assert(m >= 5); //typically m is 10 but it can't be < 5 'cos then 5-nested-fors below crash; e.g., when m=5, there is only C(5,5)=1 combination to try

	//choose 5 from m, hence 5-nested loops to try all combinations
	float minIsometryCost = INF;
	vector< int > bestCorresp;
	for (int a = 0; a < m; a++)
		for (int b = a+1; b < m; b++)
			for (int c = b+1; c < m; c++)
				for (int d = c+1; d < m; d++)
					for (int e = d+1; e < m; e++)
					{
						//current 5-combination is copied from appropriate slots of initialCorresp to initialCorrespTry
						vector< int > safeCorrespTry;
						//corresp # 1
						safeCorrespTry.push_back(safeCorresp[2*a]);
						safeCorrespTry.push_back(safeCorresp[2*a + 1]);
						//corresp # 2
						safeCorrespTry.push_back(safeCorresp[2*b]);
						safeCorrespTry.push_back(safeCorresp[2*b + 1]);
						//corresp # 3
						safeCorrespTry.push_back(safeCorresp[2*c]);
						safeCorrespTry.push_back(safeCorresp[2*c + 1]);
						//corresp # 4
						safeCorrespTry.push_back(safeCorresp[2*d]);
						safeCorrespTry.push_back(safeCorresp[2*d + 1]);
						//corresp # 5
						safeCorrespTry.push_back(safeCorresp[2*e]);
						safeCorrespTry.push_back(safeCorresp[2*e + 1]);

						float isometryCostTry = getErrOfCorrespList(mesh2, safeCorrespTry, false);
						if (isometryCostTry < minIsometryCost)
						{
							minIsometryCost = isometryCostTry;
							bestCorresp = safeCorrespTry;
						}
					} //end of e

	//initialCorresp is of size 5 now, the best 5-tuple fetched from the initial ~10 matches
	safeCorresp = bestCorresp;
assert((int) safeCorresp.size() == 10); //10 means 5x2, hence 5 matches
}

/*void Mesh::fetchSpectralInfo(int k)
{
	//obtain some interesting info b/w aligned spectral base verts

	//farhest (most widely separated) pair of spectral bases
	float minDist = INF, maxDist = -INF, totalDist = 0.0f;
	int maxDistBase1, maxDistBase2;
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int bv1 = baseVerts[i];
		for (int j = i + 1; j < (int) baseVerts.size(); j++)
		{
			int bv2 = baseVerts[j];
			float dist = distanceBetweenKD(verts[bv1]->spectralK, verts[bv2]->spectralK, k);
			if (dist < minDist)	minDist = dist;
			if (dist > maxDist)	{ maxDist = dist; maxDistBase1 = bv1; maxDistBase2 = bv2; }
		}
	}
	cout << "\nfarhest pair of spectralBases " << maxDistBase1 << " " << maxDistBase2 << " w/ dist " << maxDist << "; minDist " << minDist << endl; 

	//avg L2 distance b/w nghboring spectral bases
	int nDists = 0; //# of dist values added to totalDist for avging later
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int bv = baseVerts[i];
		for (int bn = 0; bn < (int) verts[bv]->baseNghbs.size(); bn++)
		{
			float dist = distanceBetweenKD(verts[bv]->spectralK, verts[ verts[bv]->baseNghbs[bn] ]->spectralK, k);
			totalDist += dist;
			nDists++;
		}
	}
	totalDist /= nDists;
	cout << "avg length b/w nghboring spectral bases: " << totalDist << "\n";
	
	float closeThrshld = totalDist / 2.0f; //bases that are too close to a given sepBase b go to the fallens list of b; too close means dist < closeThrshld
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int bv = baseVerts[i];
		bool separationSufficient = true;
		for (int sb = 0; sb < (int) sepBases.size(); sb++)
			if (distanceBetweenKD(verts[bv]->spectralK, verts[ sepBases[sb] ]->spectralK, k) < closeThrshld)
			{
				separationSufficient = false;
				verts[ sepBases[sb] ]->fallens.push_back(bv); //remember bv as a base falling on sepBases[sb] to access it later
cout << sepBases[sb] << " <- " << bv << endl;
				break;
			}
		if (separationSufficient)
			sepBases.push_back(bv);
	}
	cout << "# of sufficiently separated bases: " << (int) sepBases.size() << " (original = " << (int) baseVerts.size() << ", thrshld = " << closeThrshld << ")\n";
}*/

void Mesh::moreBasesForDrawing()
{
	//sets 1-ring neighborhood verts of a base as bases in order to make blue bases span bigger areas for better visualization

	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int bv = baseVerts[i];
		//look at each adjacent vertices of bv and make them base for visualization purposes
		set< int, std::less< int > >::const_iterator neighbVertIdx;
		for (neighbVertIdx = verts[bv]->vertNeighborsSet.begin(); neighbVertIdx != verts[bv]->vertNeighborsSet.end(); neighbVertIdx++)
			verts[(*neighbVertIdx)]->base = true;
	}
}

void Mesh::fillBaseGeoAffinity()
{
	//same as computeBaseGeodesicAffinityMtrx() but this not call PCAonAffinity() 'cos it's unnecessary here

	cout << "baseGeoAffinity[][] being filled..\n";

	int n = (int) baseVerts.size();
	for (int i = 0; i < n; i++)
		dijkstraShortestPaths(baseVerts[i]); //geodesics[] (or dSpanning[]) ready

	float twoSigmaSquare = 2.0f; //kernel(geoDist(p, q)) = e^{- geoDist(p, q) / twoSigmaSquare}
	baseGeoAffinity = new float*[n];
	for (int i = 0; i < n; i++) //and fill the new one
	{
		baseGeoAffinity[i] = new float[n];
		for (int j = 0; j < n; j++)
		{
			if (j < i) //save time
				baseGeoAffinity[i][j] = baseGeoAffinity[j][i]; //symmetric mtrx for undirected graph (copy precomputed result)
			else
			{
				float geoDist = //verts[ baseVerts[i] ]->getGeodesic(baseVerts[j]);
								verts[ baseVerts[i] ]->dSpanning[baseVerts[j]];
				//baseGeoAffinity[i][j] = geoDist; //no kernel
				baseGeoAffinity[i][j] = pow(EXP, -(geoDist*geoDist) / twoSigmaSquare); //Gaussian kernel'ed (applyGaussian.m uses this)
//true Gaussian is scaled by 1/sqrt(2pi): (1.0f/sqrt(2.0f*PI)) * pow(EXP, -(geoDist*geoDist) / twoSigmaSquare); eigvals change but result same when this in use
			}
		} //end of j
	} //end of i

	cout << "baseGeoAffinity[][] ready\n";
}

void Mesh::distanceBasedTriangulation(int k)
{
	//embeds all verts to k-dimensional space via distance-based triangulation

	//for each vertex v, compute its embedding
	int n = (int) baseVerts.size();
	float* delta_a = new float[n], //nx1 column vector holding squared distances between the point a and the baseVert_i in its ith row
		 * delta_mu = new float[n], //nx1 column vector: (delta_1 + delta_2 + .. + delta_n) / n, where delta_i is the vector of squared distances from bv_i to all the bases
		 * c = new float[k], //store embedded coords temporarily for k > 3
		 twoSigmaSquare = 2.0f;
	//fixed delta_mu to be used below
	for (int r = 0; r < n; r++)
		delta_mu[r] = 0.0f;
	for (int bv = 0; bv < n; bv++)
		//for this bv, compute its distance to r'th baseVert and save it at delta_mu[r]; not save actually, accumulate to tell the truth
		for (int r = 0; r < n; r++)
		{
			float geoDist = verts[ baseVerts[bv] ]->dSpanning[ baseVerts[r] ];
			delta_mu[r] //+= (geoDist * geoDist); //squared geodesic dist kernel (use the same normalization as in fillBaseGeoAffinity(); no!! this normalization sucks eigvecs of baseGeoAffinity[], but use the same thing below)
					    += pow(EXP, -(geoDist*geoDist) / twoSigmaSquare); //same normalization
		}
	for (int r = 0; r < n; r++)
		delta_mu[r] /= n;
	//embedded pnt x_a = -1/2 * lkPseudoinvTranspose * (delta_a - delta_mu), where x_a stored in v.spectral/spectralK,
	//and lkPseudoinvTranspose is already ready as spectralCoords[] from PCAonAffinity()
	for (int v = 0; v < (int) verts.size(); v++)
	{	
		//fill delta_a
		for (int r = 0; r < n; r++)
		{
			float geoDist = verts[ baseVerts[r] ]->dSpanning[v];
			delta_a[r] //= (geoDist * geoDist); //squared geodesic dist kernel (use the same normalization as above and as in fillBaseGeoAffinity(); no!! this normalization sucks eigvecs of baseGeoAffinity[], but use the same thing above)
					   = pow(EXP, -(geoDist*geoDist) / twoSigmaSquare); //same normalization
			//do delta_a = delta_a - delta_mu
			delta_a[r] -= delta_mu[r];
		}
		//all terms to compute embedded pnt x_a ready; so, here we go
		float c1 = 0.0f, c2 = 0.0f, c3 = 0.0f; //for k = 3
		for (int cc = 0; cc < k; cc++)
			c[cc] = 0.0f; //initialization for k > 3
		for (int i = 0; i < n; i++)
		{
			if (k == 3)
			{
				c1 += spectralCoords[0][i] * delta_a[i]; //verts[ baseVerts[i] ]->spectral[0] * delta_a[i]; sucks due to translateAndScale() on spectral[]
				c2 += spectralCoords[1][i] * delta_a[i];
				c3 += spectralCoords[2][i] * delta_a[i];
//spectralCoords[cc][i] vs. verts[ baseVerts[i] ]->spectral[cc] not same 'cos translateAndScale() applied to bv.spectral but not to spectralCoords[]
			}
			else if (k <= 6)
			{
				for (int cc = 0; cc < k; cc++)
					c[cc] += spectralCoords[cc][i] * delta_a[i];
			}
		} //end of i

		if (! verts[v]->base) //PCAonAffinity() already allocated memo for bases
			verts[v]->spectralK = new float[k];
		for (int j = 0; j < k; j++)
			verts[v]->spectralK[j] = -0.5f * c[j];
	} //end of v

	//memo re-capture
	delete delta_a; delete delta_mu; delete c;
}

void Mesh::correspFromFile(Mesh* mesh2)
{
	//freads corresponding bases correspondence; their nonbase patch vertices is also fread so no need for computeBaseVerts() at all

	char fName[50];
	if (theInput == JUMPING)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\jumping\\C_%d-%d %d.dat", frameID, mesh2->frameID, 46666666666);
	else
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\%s\\C_%d-%d %d.dat", (theInput != DANCING ? "hybrid" : "dancing"), frameID, mesh2->frameID, 46);
	FILE* fPtr;
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}
	int correspSize;
	float maxGroundCost = -INF;
	fscanf(fPtr, "%d\t\t%f\t%f\t%f\t%f\t%f\t%f\n", &correspSize, &r1, &r2, &minGeoDistG, &maxGeoDistG, &mesh2->minGeoDistG, &mesh2->maxGeoDistG);
	mesh2->r1 = r2;
	mesh2->r2 = r1; //dijkstraShortestPaths() needs radius of mesh2 (which is at mesh2.r1) for earlyBreak
	worstMatchI = bestMatchI = worstMatchG = bestMatchG = -1;
	for (int i = 0; i < correspSize; i++) //read each correspondence line of form '7563		7311	0.022194	3568.131592'
	{
		int bv1, bv2;
		float isometryCost, groundCost;
		fscanf(fPtr, "%d\t%d\t%f\t%f", &bv1, &bv2, &isometryCost, &groundCost);
cout << bv1 << " " << bv2 << endl;
		//mesh1
		verts[bv1]->base = mesh2->verts[bv2]->base = true; //note that, bases come in the ascending order of isometry costs s.t. safeCorresp is already sorted as desired

		if (i < 10) //for efficiency, use only top10 elements of file for safeCorresp list; even for accuracy since the quality of matches decrease as we go in this list and keeping only the good ones is good for costs[][]
//disable line just-above to cancel this top10 efficiency trick
		{
			safeCorresp.push_back(bv1); //next safe correspondence goes to following 2 slots in safeCorresp
			safeCorresp.push_back(bv2);
			verts[bv1]->safe = mesh2->verts[bv2]->safe = true;
		}
		//keep all matches from coarseStage to process them in pairUpPatches(); use top10 for isometryCosts of costs[][] however
		safeCorrespAll.push_back(bv1);
		safeCorrespAll.push_back(bv2);
		verts[bv1]->safe = mesh2->verts[bv2]->safe = true;

		verts[bv1]->matchIdx = bv2;
		mesh2->verts[bv2]->matchIdx = bv1;
		verts[bv1]->matchCostI = mesh2->verts[bv2]->matchCostI = isometryCost;
		verts[bv1]->matchCostG = mesh2->verts[bv2]->matchCostG = groundCost;
		verts[bv1]->idxInBaseVerts = verts[bv1]->patchIdx = mesh2->verts[bv2]->idxInBaseVerts = mesh2->verts[bv2]->patchIdx = (int) baseVerts.size();
		mesh2->baseVerts.push_back(bv2);
		baseVerts.push_back(bv1); //baseNghbhood to be fread below; baseNghbs, dSpanning to be computed below
		if (groundCost > maxGroundCost)
		{
			maxGroundCost = groundCost;
			worstMatchG = bv1;
			mesh2->worstMatchG = bv2;
		}
		worstMatchI = bv1; mesh2->worstMatchI = bv2; //the last line is the worst isometryCost line 'cos file is already sorted

		verts[bv1]->hasBadIsometryCost = mesh2->verts[bv2]->hasBadIsometryCost = (i >= correspSize - 9); //bottom 9 in file regarded as bad
	} //end of i
assert(correspSize == (int) baseVerts.size() && correspSize == (int) mesh2->baseVerts.size());

	int baseCounter = 0;
	nOverlaps = new int[(int) verts.size()];
	mesh2->nOverlaps = new int[(int) mesh2->verts.size()];
	for (int i = 0; i < (int) verts.size(); i++)		nOverlaps[i] = 0;
	for (int i = 0; i < (int) mesh2->verts.size(); i++)	mesh2->nOverlaps[i] = 0;
	while (baseCounter < correspSize*2) //read each base\t\tbaseNghbhood line that ends w/ a -1
	{
		int vIdx, bv = baseCounter / 2;
		fscanf(fPtr, "%d\t\t", &vIdx); //skip the base idx bv in the beginning and put the upcomings to the baseNghbs of bv
		if (baseCounter % 2 == 0) //even numbered lines for mesh1, odds for mesh2
		{
			assert(vIdx == baseVerts[bv]);
			nOverlaps[vIdx]++; //baseVerts'll have 1 in the end and others 1+
			//alloc memo for the patch of mesh1 baseVert
			if (verts[ baseVerts[bv] ]->baseNghbhood)
				delete verts[ baseVerts[bv] ]->baseNghbhood;
			verts[ baseVerts[bv] ]->baseNghbhood = new vector<int>;
		}
		else
		{
			assert(vIdx == mesh2->baseVerts[bv]);
			mesh2->nOverlaps[vIdx]++; //baseVerts'll have 1 in the end and others 1+
			if (mesh2->verts[ mesh2->baseVerts[bv] ]->baseNghbhood)
				delete mesh2->verts[ mesh2->baseVerts[bv] ]->baseNghbhood;
			mesh2->verts[ mesh2->baseVerts[bv] ]->baseNghbhood = new vector<int>;
		}
		
		while (true)
		{
			fscanf(fPtr, "%d", &vIdx);
			if (vIdx == -1)
				break;
			if (baseCounter % 2 == 0) //even numbered lines for mesh1
			{
				verts[ baseVerts[bv] ]->baseNghbhood->push_back(vIdx);
//if (baseVerts[bv] == 7563 || baseVerts[bv] == 6571 || baseVerts[bv] == 9804 || baseVerts[bv] == 9422 || baseVerts[bv] == 9407)
				verts[vIdx]->patchIdx = verts[ baseVerts[bv] ]->patchIdx;
				nOverlaps[vIdx]++;
			}
			else //odds for mesh2
			{
				mesh2->verts[ mesh2->baseVerts[bv] ]->baseNghbhood->push_back(vIdx);
//if (bv==0)//so that only baseVerts[0].patch is visible, i.e., the base w/ the min isometryCost 
				mesh2->verts[vIdx]->patchIdx = mesh2->verts[ mesh2->baseVerts[bv] ]->patchIdx;
				mesh2->nOverlaps[vIdx]++;
			}
		}
		baseCounter++;
	} //end of feof

	fclose(fPtr);
}

void Mesh::denseMatching(Mesh* mesh2)
{
	//dense matching b/w all verts using interpolation-based or graph matching based tactics, where
	//interpolation-based: employ LMDS to obtain 2-d patches on which Shepard interpolation is done
	//graph matching based: does blossom on costs[i][j] that is filled w/ isometryCost of matching i to j

	cout << "dense matching being computed via distance-based triangulation..\n";

	correspFromFile(mesh2);
//return; //see the correctness of the loaded correspondences [you may wanna call moveCOMtoOrigin() as well]

	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 /*&& theInput != SCAPE*/ && theInput != TOSCA) //= if (hybridRun)
		hybridModelsToSameCoordFrame(mesh2);

	//shortcuts needed by Dijkstra for more accurate dSpanning[]
	prepareForGeodesicComputation();
	//fill baseGeoAffinity[][] and prepare dSpanning[]
	fillBaseGeoAffinity();
	
	//same stuff for mesh2
	mesh2->prepareForGeodesicComputation();
	mesh2->fillBaseGeoAffinity();

	//these move spatial (i.e. non-spectral) center-of-mass to origin which provides a better viewing
	moveCOMtoOrigin();
	mesh2->moveCOMtoOrigin();

	//get sizes
	int n1 = (int) baseVerts.size(), n2 = (int) mesh2->baseVerts.size();

	//Landmark MDS (LMDS), i.e., distance-based triangulation of ``Sparse multidimensional scaling using landmark points'' of Silva and Tenenbaum needed for interpolation-based matching
	if (! denseMatchByGraphMatching) //interpolation-based dense matching
	{
		//make lkPseudoinvTranspose ready in the bv.spectral/spectralK attribute
		int k = 2;
		PCAonAffinity(k, true);
		mesh2->PCAonAffinity(k, true);

		/*//////////affinity eigenvector based alignment///////////
		findBestAffinityEigvectors(mesh2, k, true); //either use this and not PCAalignment(K)() below or vice versa
		//now the spectral bases are aligned; use these aligned bases (in spectralCoords[][] mtrx) in distanceBasedTriangulation() computation
		//////////affinity eigenvector based alignment ends///////////*/

		//use lkPseudoinvTranspose to get the desired k-D embedding of all vertices, bases & nonbases
		distanceBasedTriangulation(k);
		mesh2->distanceBasedTriangulation(k);

		//////////PCA pose normalization based alignment////////////////
		//rigid alignment required for the best orientation of the two embedded pnt sets; use my PCA normalization as suggested by above paper
		R1bst = new float*[k]; R2bst = new float*[k]; //the best kxk rotation mtrx to be applied to mesh1 & mesh2, respectively (won't be a direct mtrx multiplication though)
		for (int i = 0; i < k; i++)
		{
			R1bst[i] = new float[k];	R2bst[i] = new float[k];
		}
		//pca alignment call below need origin-translated coords (after PCAonAffinity(), coords are origin-tranlated, and after distanceBasedTriangulation(), they remain so; so, 2 calls below are redundant)
		translateAndScaleSpectralBasesK(k, true); //when scaled, base spectrals seen as small w.r.t. nonbase spectrals
		mesh2->translateAndScaleSpectralBasesK(k, true);
		//pca alignment
		float** pntSetK1 = new float*[n1];
		for (int i = 0; i < n1; i++)
		{
			pntSetK1[i] = new float[k];
			for (int c = 0; c < k; c++)
				pntSetK1[i][c] = verts[ baseVerts[i] ]->spectralK[c];
		}
		float** pntSetK2 = new float*[n2];
		for (int i = 0; i < n2; i++)
		{
			pntSetK2[i] = new float[k];
			for (int c = 0; c < k; c++)
				pntSetK2[i][c] = mesh2->verts[ mesh2->baseVerts[i] ]->spectralK[c];
		}
		PCAalignmentK(pntSetK1, pntSetK2, n1, n2, k); //R1/2bst made available, pntSetK1/2 updated via call-by-ref although they're not used further here

		//desired alignnment by applying the best rotation (computed just-above using only the bases for efficiency) to all spectral vertices
		float c1, c2, c3, c4;
		for (int i = 0; i < (int) verts.size(); i++)
		{
			//aligning rotation applied to mean-shifted coords (works)
			if (k != 2)
			{
				c1 = R1bst[0][0]*verts[i]->spectralK[0] + R1bst[0][1]*verts[i]->spectralK[1] + R1bst[0][2]*verts[i]->spectralK[2] + R1bst[0][3]*verts[i]->spectralK[3] + (k == 5 ? R1bst[0][4]*verts[i]->spectralK[4] : 0.0f)
						   + (k == 6 ? R1bst[0][4]*verts[i]->spectralK[4] + R1bst[0][5]*verts[i]->spectralK[5] : 0.0f);
				c2 = R1bst[1][0]*verts[i]->spectralK[0] + R1bst[1][1]*verts[i]->spectralK[1] + R1bst[1][2]*verts[i]->spectralK[2] + R1bst[1][3]*verts[i]->spectralK[3] + (k == 5 ? R1bst[1][4]*verts[i]->spectralK[4] : 0.0f)
						   + (k == 6 ? R1bst[1][4]*verts[i]->spectralK[4] + R1bst[1][5]*verts[i]->spectralK[5] : 0.0f);
				c3 = R1bst[2][0]*verts[i]->spectralK[0] + R1bst[2][1]*verts[i]->spectralK[1] + R1bst[2][2]*verts[i]->spectralK[2] + R1bst[2][3]*verts[i]->spectralK[3] + (k == 5 ? R1bst[2][4]*verts[i]->spectralK[4] : 0.0f)
						   + (k == 6 ? R1bst[2][4]*verts[i]->spectralK[4] + R1bst[2][5]*verts[i]->spectralK[5] : 0.0f);
				c4 = R1bst[3][0]*verts[i]->spectralK[0] + R1bst[3][1]*verts[i]->spectralK[1] + R1bst[3][2]*verts[i]->spectralK[2] + R1bst[3][3]*verts[i]->spectralK[3] + (k == 5 ? R1bst[3][4]*verts[i]->spectralK[4] : 0.0f)
						   + (k == 6 ? R1bst[3][4]*verts[i]->spectralK[4] + R1bst[3][5]*verts[i]->spectralK[5] : 0.0f);
			}
			else
			{
				c1 = R1bst[0][0]*verts[i]->spectralK[0] + R1bst[0][1]*verts[i]->spectralK[1];
				c2 = R1bst[1][0]*verts[i]->spectralK[0] + R1bst[1][1]*verts[i]->spectralK[1];
				c3 = 0.0f; //2d coords visualized at z = 0 plane
			}
			verts[i]->spectralK[0] = c1;	verts[i]->spectralK[1] = c2;	verts[i]->spectralK[2] = c3;	if (k != 2) verts[i]->spectralK[3] = c4;
			if (k == 5 || k == 6)
			{
				float c5 = R1bst[4][0]*verts[i]->spectralK[0] + R1bst[4][1]*verts[i]->spectralK[1] + R1bst[4][2]*verts[i]->spectralK[2] + R1bst[4][3]*verts[i]->spectralK[3] + (k == 5 ? R1bst[4][4]*verts[i]->spectralK[4] : 0.0f)
					   + (k == 6 ? R1bst[4][4]*verts[i]->spectralK[4] + R1bst[4][5]*verts[i]->spectralK[5] : 0.0f);
				verts[i]->spectralK[4] = c5;
				if (k == 6)
				{
					float c6 = R1bst[5][0]*verts[i]->spectralK[0] + R1bst[5][1]*verts[i]->spectralK[1] + R1bst[5][2]*verts[i]->spectralK[2] + R1bst[5][3]*verts[i]->spectralK[3] + (k == 5 ? R1bst[5][4]*verts[i]->spectralK[4] : 0.0f)
					   + (k == 6 ? R1bst[5][4]*verts[i]->spectralK[4] + R1bst[5][5]*verts[i]->spectralK[5] : 0.0f);
					verts[i]->spectralK[5] = c6;
				}
			}
		} //end of i
		for (int i = 0; i < (int) mesh2->verts.size(); i++)
		{
			//same stuff for mesh2
			if (k != 2)
			{
				c1 = R2bst[0][0]*mesh2->verts[i]->spectralK[0] + R2bst[0][1]*mesh2->verts[i]->spectralK[1] + R2bst[0][2]*mesh2->verts[i]->spectralK[2] + R2bst[0][3]*mesh2->verts[i]->spectralK[3] + (k == 5 ? R2bst[0][4]*mesh2->verts[i]->spectralK[4] : 0.0f)
					+ (k == 6 ? R2bst[0][4]*mesh2->verts[i]->spectralK[4] + R2bst[0][5]*mesh2->verts[i]->spectralK[5] : 0.0f);
				c2 = R2bst[1][0]*mesh2->verts[i]->spectralK[0] + R2bst[1][1]*mesh2->verts[i]->spectralK[1] + R2bst[1][2]*mesh2->verts[i]->spectralK[2] + R2bst[1][3]*mesh2->verts[i]->spectralK[3] + (k == 5 ? R2bst[1][4]*mesh2->verts[i]->spectralK[4] : 0.0f)
					+ (k == 6 ? R2bst[1][4]*mesh2->verts[i]->spectralK[4] + R2bst[1][5]*mesh2->verts[i]->spectralK[5] : 0.0f);
				c3 = R2bst[2][0]*mesh2->verts[i]->spectralK[0] + R2bst[2][1]*mesh2->verts[i]->spectralK[1] + R2bst[2][2]*mesh2->verts[i]->spectralK[2] + R2bst[2][3]*mesh2->verts[i]->spectralK[3] + (k == 5 ? R2bst[2][4]*mesh2->verts[i]->spectralK[4] : 0.0f)
					+ (k == 6 ? R2bst[2][4]*mesh2->verts[i]->spectralK[4] + R2bst[2][5]*mesh2->verts[i]->spectralK[5] : 0.0f);
				c4 = R2bst[3][0]*mesh2->verts[i]->spectralK[0] + R2bst[3][1]*mesh2->verts[i]->spectralK[1] + R2bst[3][2]*mesh2->verts[i]->spectralK[2] + R2bst[3][3]*mesh2->verts[i]->spectralK[3] + (k == 5 ? R2bst[3][4]*mesh2->verts[i]->spectralK[4] : 0.0f)
					+ (k == 6 ? R2bst[3][4]*mesh2->verts[i]->spectralK[4] + R2bst[3][5]*mesh2->verts[i]->spectralK[5] : 0.0f);
			}
			else
			{
				c1 = R2bst[0][0]*mesh2->verts[i]->spectralK[0] + R2bst[0][1]*mesh2->verts[i]->spectralK[1];
				c2 = R2bst[1][0]*mesh2->verts[i]->spectralK[0] + R2bst[1][1]*mesh2->verts[i]->spectralK[1];
				c3 = 0.0f;
			}
			mesh2->verts[i]->spectralK[0] = c1;	mesh2->verts[i]->spectralK[1] = c2;	mesh2->verts[i]->spectralK[2] = c3;	if (k != 2) mesh2->verts[i]->spectralK[3] = c4;
			if (k == 5 || k == 6)
			{
				float c5 = R2bst[4][0]*mesh2->verts[i]->spectralK[0] + R2bst[4][1]*mesh2->verts[i]->spectralK[1] + R2bst[4][2]*mesh2->verts[i]->spectralK[2] + R2bst[4][3]*mesh2->verts[i]->spectralK[3] + (k == 5 ? R2bst[4][4]*mesh2->verts[i]->spectralK[4] : 0.0f)
					   + (k == 6 ? R2bst[4][4]*mesh2->verts[i]->spectralK[4] + R2bst[4][5]*mesh2->verts[i]->spectralK[5] : 0.0f);
				mesh2->verts[i]->spectralK[4] = c5;
				if (k == 6)
				{
					float c6 = R2bst[5][0]*mesh2->verts[i]->spectralK[0] + R2bst[5][1]*mesh2->verts[i]->spectralK[1] + R2bst[5][2]*mesh2->verts[i]->spectralK[2] + R2bst[5][3]*mesh2->verts[i]->spectralK[3] + (k == 5 ? R2bst[5][4]*mesh2->verts[i]->spectralK[4] : 0.0f)
					   + (k == 6 ? R2bst[5][4]*mesh2->verts[i]->spectralK[4] + R2bst[5][5]*mesh2->verts[i]->spectralK[5] : 0.0f);
					mesh2->verts[i]->spectralK[5] = c6;
				}
			}
		} //end of i

		//memo re-capture
		for (int i = 0; i < k; i++) delete [] R1bst[i]; delete [] R1bst;
		for (int i = 0; i < k; i++) delete [] R2bst[i]; delete [] R2bst;
		//////////PCA pose normalization based alignment ends////////////////*/

		//baseNghbs needed by interpolation
		computeBaseNeighbs(r1);
		mesh2->computeBaseNeighbs(r2);
	}
	else //graph matching based dense corresp
	{
		//tmpBase creation is w/ COES sampling, which needs sorted curvatures
		computeGaussianCurvatures();
		mesh2->computeGaussianCurvatures();
		sortCurvatures();
		mesh2->sortCurvatures();
	}

	//pair the patches up
	pairUpPatches(mesh2);

	cout << nCorresps << " correspondences computed\nDone!\n";
}

void Mesh::duplicateFreePatchesToFile(Mesh* mesh2, char* fName)
{
	//fprints patch info, as the indices of member verts, w/ the duplicates discarded

	//start by refilling safeCorresp in the ascending order of isometryCosts
	sortSafeCorresp(mesh2, fName); //so fName to be appended below now has the rearranged/sorted correspondence info

	FILE* fPtr3 = fopen(fName, "a"); //append the duplicate-free patch vertices of each matched base to the file that holds the correspondences
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
	{
		int bv1 = safeCorresp[2*s], bv2 = safeCorresp[2*s + 1];
		
		//use non-duplicates in baseNghbhood (= patch) of each base (in pntSet*) for efficiency and accuracy (tried to keep baseNghbhood duplicate-free at all times but that slowed down baseVert generation)
		fprintf(fPtr3, "%d\t\t", bv1); //duplicate-free nonbases in patch of bv1 fprinted below
		for (int pv = 0; pv < (int) verts[bv1]->baseNghbhood->size(); pv++) //~5K baseNghbhood reduces to ~1K after duplicate removal for 15K dancing man runs
		{
			int pvIdx = (*verts[bv1]->baseNghbhood)[pv];
			bool toFile = true; //allowed to be written to file
			for (int processed = pv-1; processed >= 0; processed--) //start from where pv left off for duplicate check
				if (pvIdx == (*verts[bv1]->baseNghbhood)[processed]) //then baseNghbhood[pv] (= pvIdx) is a duplicate; remove it (by not adding it again to pntSet1)
				{
					toFile = false; //duplicate exists; so, no chance of getting into file
					break;
				}
			if (toFile)
				fprintf(fPtr3, "%d\t", pvIdx);
		}
		fprintf(fPtr3, "-1\n%d\t\t", bv2); //duplicate-free nonbases in patch of bv2 fprinted below
		for (int pv = 0; pv < (int) mesh2->verts[bv2]->baseNghbhood->size(); pv++) //same stuff for mesh2 patch
		{
			int pvIdx = (*mesh2->verts[bv2]->baseNghbhood)[pv];
			bool toFile = true;
			for (int processed = pv-1; processed >= 0; processed--)
				if (pvIdx == (*mesh2->verts[bv2]->baseNghbhood)[processed])
				{
					toFile = false;
					break;
				}
			if (toFile)
				fprintf(fPtr3, "%d\t", pvIdx);
		}
		fprintf(fPtr3, "-1\n");
	}
	fclose(fPtr3);
}

void insertionSort(float* A, int s, int* vIdxs)
{
	//does insertion sort to sort array A of size s in descending order; call-by-ref to A, hence array is sorted in the caller when this returns

	for (int p = 1; p < s; p++)
	{
		float tmp = A[p];
		int j, tmpIdx = vIdxs[p];
		for (j = p; j > 0 && tmp > A[j-1]; j--) //just replace > w/ < to make ascending order
		{
			A[j] = A[j-1];
			vIdxs[j] = vIdxs[j-1];
		}
		A[j] = tmp;
		vIdxs[j] = tmpIdx;
	}
}
void Mesh::sortCurvatures()
{
	//creates and fills gcOrder[] w/ vertex idxs corresponding to the descending values of vertex curvatures

	cout << "sorting verts w.r.t. curvature values..\n";
	gcOrder = new int[ (int) verts.size() ];

	//efficient nlogn sorting, n = # of verts
	float* gcVals = new float[ (int) verts.size() ];
	int* vIdxs = new int[ (int) verts.size() ], count = 0;
	for (int i = 0; i < (int) verts.size(); i++)
	{
		gcVals[i] = verts[i]->gc;
		vIdxs[i] = i;
	}
	/////////////// x64 run must not use quickSort() to prevent run-time crash ////////////
	//quickSort(gcVals, vIdxs, 0, (int) verts.size()); //call by ref to gcVals (redundant) and vIdxs (output to be used below) [makes the program crash; so, use easier insertionSort() which has a safer code]
	insertionSort(gcVals, (int) verts.size(), vIdxs); //call by ref to gcVals (redundant) and vIdxs (output to be used below)
	/////////////// x64 run must not use quickSort() to prevent run-time crash ends ////////////
	for (int i = 0; i < (int) verts.size(); i++)
	{
		gcOrder[count] = vIdxs[i];
//fprintf(fPtr, "%d\t%f\n", gcOrder[count], verts[ gcOrder[count] ]->gc);
		count++;
	}
	delete [] gcVals;
	delete [] vIdxs;
	cout << "done!\n\n";
}

void Mesh::sortMus()
{
	//creates and fills muOrder[] w/ vertex idxs corresponding to the descending values of vertex mu values

	cout << "sorting verts w.r.t. mu values..\n";
	muOrder = new int[ (int) verts.size() ];
	//efficient nlogn sorting, n = # of verts
	float* muVals = new float[ (int) verts.size() ];
	int* vIdxs = new int[ (int) verts.size() ];
	for (int i = 0; i < (int) verts.size(); i++)
	{
		muVals[i] = verts[i]->Mu;
		vIdxs[i] = i;
	}
	/////////////// x64 run must not use quickSort() to prevent run-time crash ////////////
	//quickSort(muVals, vIdxs, 0, (int) verts.size()); //call by ref to muVals (redundant) and vIdxs (output to be used below) [makes the program crash; so, use easier insertionSort() which has a safer code]
	insertionSort(muVals, (int) verts.size(), vIdxs); //call by ref to gcVals (redundant) and vIdxs (output to be used below)
	/////////////// x64 run must not use quickSort() to prevent run-time crash ends ////////////

	for (int i = 0; i < (int) verts.size(); i++)
		muOrder[i] = vIdxs[i];
	delete [] muVals;
	delete [] vIdxs;
	cout << "done!\n\n";
}

void Mesh::moveCOMtoOrigin()
{
	//moves all verts so that mesh's center-of-mess is at the origin (0,0,0), speaking of spatial coords, not spectral

	float* m3 = new float[3], //mean/center-of-mass
		 * sum = new float[3];
	sum[0] = sum[1] = sum[2] = 0.0f;
	for (int i = 0; i < (int) verts.size(); i++)
		for (int ii = 0; ii < 3; ii++)
			sum[ii] += verts[i]->coords[ii];
	for (int ii = 0; ii < 3; ii++)
		m3[ii] = sum[ii] / (float) verts.size();
	//initial translation (t) that moves shape centers to (0,0,0)
	for (int i = 0; i < (int) verts.size(); i++)
		for (int ii = 0; ii < 3; ii++)
			verts[i]->coords[ii] -= m3[ii];
}

inline float angleBetween(float* v1, float* v2)
{
	//returns smallest radian angle b/w normalized v1 & v2 via v1 DOT v2 = v1Len * v2Len * cosTheta (len = 1 for normalized, hence acos(dot) is the theta)

	float cosTheta = 0; //v1.dot(v2);
	for (int i = 0; i < 3; i++)
		cosTheta += v1[i]*v2[i];
	if (cosTheta < -1.0f)	cosTheta = -1.0f;
	if (cosTheta > 1.0f)	cosTheta = 1.0f;
	return acos(cosTheta); //* (180/PI); no radian to float to save time (besides computeGaussianCurvature() expects radian output)
}

void Mesh::computeGaussianCurvatures()
{
	//look at 1-ring neighbhood of v to compute its Gaussian curvature value, gc, via way1 = [Surface Curvature Extraction for 3D Image Analysis or Surface Rendering] talk,
	//or w/ a more sophisticated yet similar method from way2 = [Discrete Differential-Geometry Operators for Triangulated 2-Manifolds]; way2 in use
//note that, this [descriptor] is translation and rotation invariant, but not scale invariant; no problem for now since i compare 2 meshes w/ almost same bbox dimensions

	float minVal = INF, maxVal = -INF, twoPI = 2*PI, PIo2 = PI/2.0f;
	for (int v = 0; v < (int) verts.size(); v++)
	{
		float angleSum = 0.0f, vf = 1.0f/8.0f, a1, a2, a3,
			  areaSum = 0.0f, areaSumMixed = 0.0f; //last two sums are for way1 and way2, respectively
//both way1 & way2 give the ~same result; use either one
		for (int tn = 0; tn < (int) verts[v]->triNeighbors.size(); tn++)
		{
			int t = verts[v]->triNeighbors[tn];
			areaSum += tris[t]->area;

			//find 2 verts, v1 & v2, of t other than v, for angleSum
			int v1 = (tris[t]->v1i == v ? tris[t]->v2i : tris[t]->v1i), v2 = tris[t]->v1i;
			if (v2 == v1 || v2 == v)
				v2 = ((tris[t]->v2i != v1 && tris[t]->v2i != v) ? tris[t]->v2i : tris[t]->v3i);
			float* nl1 = new float[3], len1;
			for (int i = 0; i < 3; i++)
				nl1[i] = verts[v1]->coords[i] - verts[v]->coords[i]; //from v to neighbor vert v1
			float* nl2 = new float[3], len2;
			for (int i = 0; i < 3; i++)
				nl2[i] = verts[v2]->coords[i] - verts[v]->coords[i]; //from v to neighbor vert v2
			float* nl3 = new float[3], len3;
			for (int i = 0; i < 3; i++)
				nl3[i] = verts[v2]->coords[i] - verts[v1]->coords[i]; //from v1 to v2
			//nl1.normalize(); effect
			len1 = sqrt(nl1[0]*nl1[0] + nl1[1]*nl1[1] + nl1[2]*nl1[2]);
			for (int i = 0; i < 3; i++)
				nl1[i] /= (len1 != 0.0f ? len1 : 1.0f);
			//nl2.normalize(); effect
			len2 = sqrt(nl2[0]*nl2[0] + nl2[1]*nl2[1] + nl2[2]*nl2[2]);
			for (int i = 0; i < 3; i++)
				nl2[i] /= (len2 != 0.0f ? len2 : 1.0f);
			a1 = angleBetween(nl1, nl2); //first angle of t, i.e. the one at vertex v
			angleSum += a1;
			
			//below is for way2
			//nl3.normalize(); effect
			len3 = sqrt(nl3[0]*nl3[0] + nl3[1]*nl3[1] + nl3[2]*nl3[2]);
			for (int i = 0; i < 3; i++)
				nl3[i] /= (len3 != 0.0f ? len3 : 1.0f);
			a2 = angleBetween(nl2, nl3); //2nd angle of t, i.e. the one at vertex v2
			a3 = angleBetween(nl1, nl3); //3rd angle of t, i.e. the one at vertex v1
			if (a1 >= PIo2 && a1 <= PI) //angle of t at v is obtuse, i.e. between 90 and 180 degree
				areaSumMixed += tris[t]->area/2.0f;
			else if ((a2 >= PIo2 && a2 <= PI) || (a3 >= PIo2 && a3 <= PI)) //t is obtuse, i.e. has an angle b/w 90 and 180 degree, but a1 is not that angle
				areaSumMixed += tris[t]->area/4.0f;
			else //none of the angles between 90 & 180 degree, making t non-obtuse
				//add Voronoi region of v in t to areaSumMixed
				areaSumMixed += vf * (distanceBetween2(verts[v]->coords, verts[v1]->coords)*(1.0f/tan(a2)) + distanceBetween2(verts[v]->coords, verts[v2]->coords)*(1.0f/tan(a3)));
		} //end of tn
		//desired curvature value
assert(areaSum != 0.0f && areaSumMixed != 0.0f);
		//verts[v]->gc = fabs(3 * (twoPI - angleSum)/areaSum); //way1 (just replace line below w/ this for easy way1 effect)
		verts[v]->gc = fabs((twoPI - angleSum)/areaSumMixed); //way2

		//normalizers
		if (verts[v]->gc < minVal)
			minVal = verts[v]->gc;
		if (verts[v]->gc > maxVal)
			maxVal = verts[v]->gc;
	}

	float rangeVal = maxVal - minVal;
	rangeVal = (rangeVal > 0.001f ? rangeVal : maxVal); //prevent divide by zero and small denoms that exaggerate error
	//normalize gc into [0, 1] interval: (sample - mean) / variance, where if deviation ~ 0, use denominator = maxVal
	for (int v = 0; v < (int) verts.size(); v++)
		verts[v]->gc = (verts[v]->gc - minVal) / rangeVal; //in [0,1]
		//verts[v]->gc = pow(EXP, -(verts[v]->gc*verts[v]->gc) / 0.3f); //alternative Gaussian normalization
	//now each vertex has normalized gc; for baseVerts, i use avg gc of its patch to make it a representation effect larger than 1-ring neighborhood
	//no!! 'cos at this time, curvatures are computed w/o knowing bases and therefore baseNghbhoods; so, i've no info for avging
}

void Mesh::computeBaseNeighbs(float r)
{
	//computes base neighbors, i.e. surrounding bases, of each base vert
//not applicable 'cos i now keep base to base^^ geodesic dist in geodesics[] and needed base to all verts v below (^^ though it's easy to get base to all dists by playng w/ saveGeoDist)

	cout << "\nsurrounding base neighbs for each base being computed....\n";

	if (r == TINY) //this special flag says that all verts are baseVerts; so, v.baseNghb will be v.vertNeighborsSet, i.e. 1-ring neighbhood
	{
		for (int v = 0; v < (int) verts.size(); v++)
		{
			//look at each adjacent vertices of v
			set< int, std::less< int > >::const_iterator neighbVertIdx;
			for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++)
				verts[v]->baseNghbs.push_back((*neighbVertIdx));
		}
		cout << "Done!\n\n";
		return;
	}

	//run BFS from i'th baseVert until first baseVert with >> r distance is found (this means we hit a nonneighb baseVert
	//and hence no need to go further); all encountered baseVerts, i.e. at ~r distance, are recognized as baseNghbs
	float thrshold = 2.0f*r; //just a shortcut threshold; if i reach v that is 2r far from source, then BFS has certainly processed all potential baseNeighbs; so, quit early
	//bigger the thrshold, the more distant bases selected as nghbs

	int maxExtra = 4, * extras = new int[maxExtra]; //at most maxExtra not-too-far bases will be stored in case they are needed as base neighbors
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		for (int v = 0; v < (int) verts.size(); v++) //prepare for BFS
			verts[v]->d = INF; //clear effect(s) of previous call(s)

		//BFS distances not care edge lengths; so, no need to go through shortcut-edges neighbhood; use existing dSpanning[] values for r comparisons
		int source = baseVerts[i];
		bool baseNghbsSet = false;
		queue< int > q;
		q.push(source);
		verts[source]->d = 0; //length of unweighted shortest path from source to source is 0
		int v, e = 0, minBaseNghbs = 4; //assign at least minBaseNghbs base neighbs to each base
		while (! q.empty() && ! baseNghbsSet)
		{
			v = q.front();
			q.pop();

			baseNghbsSet = false;
			//look at each adjacent vertices of v
			set< int, std::less< int > >::const_iterator neighbVertIdx;
			for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++)
			{
				if (verts[(*neighbVertIdx)]->d != INF)
					continue;
				verts[(*neighbVertIdx)]->d = verts[v]->d + 1; //update d of this adjacent
				if (verts[(*neighbVertIdx)]->base)
				{
					//since source is base, dSpanningUnn[] is defined for it for sure; use it to measure geoDist between source and current neighbVertIdx
					if (verts[source]->dSpanningUnn[(*neighbVertIdx)] > thrshold) //thresholded comparison to be safe
					{
						baseNghbsSet = true; //done for this baseVert/source
						break;
					}
					else if (verts[source]->dSpanningUnn[(*neighbVertIdx)] <= r + maxEdgeLen) //close enough to source; so, it must be a base neighbor (=r dist cannot be base 'cos they're in source.baseNghbhood; therefore +maxEdgeLen)
//actually, baseNghb may not come from 1-ring neighborhood of the source.patchFrontier, so, maxEdgeLen is not very decisive indeed; not to worry 'cos the ones i miss here added later due to extras[]
						verts[source]->baseNghbs.push_back((*neighbVertIdx));
					else if (e < maxExtra)
						//r+maxEdgeLen < dist <= thrshold falls here
						extras[e++] = (*neighbVertIdx); //will be used in case baseNghbs has nnot been populated well by +maxEdgeLen tolerance
				}
				q.push((*neighbVertIdx));
			} //end of neighbVertIdx
		}
		int f = 0;
		while ((int) verts[source]->baseNghbs.size() < minBaseNghbs && f < e)
			verts[source]->baseNghbs.push_back(extras[f++]); //populate deficient base neighbors via not-too-far bases
#ifdef DBG_DETAIL
		cout << "# of baseNeighbs for base v" << source << ": " << (int) verts[source]->baseNghbs.size() << "\t";
		for (int j = 0; j < (int) verts[source]->baseNghbs.size(); j++)
			cout << verts[source]->baseNghbs[j] << "\t";
		cout << endl;
#endif
	} //end of i

	delete [] extras;

	cout << "Done!\n\n";
}

int Mesh::adjustBaseVerts()
{
	//adjusts baseVerts w.r.t. safe value; discard baseVerts that are not safe, e.g. for clearer display (and maby for more serious tasks)

	int initialSize = (int) baseVerts.size();
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		if (! verts[baseVerts[i]]->safe) //this to-be-removed base is not safe
		{
			verts[baseVerts[i]]->base = false; //not base anymore (although it is homogenous, it doesn't have a match; hence, not worth being base)
			verts[baseVerts[i]]->baseCandidate = true;
//////			verts[baseVerts[i]]->baseNghbhood.clear(); not clear (or delete now) 'cos this ex-base may become base again in greedyForBaseUpdate() and it's inefficient/cumbersome to recompute baseNghbhood
			verts[baseVerts[i]]->idxInBaseVerts = verts[baseVerts[i]]->patchIdx = verts[baseVerts[i]]->matchIdx = -1;
			baseVerts.erase(baseVerts.begin() + i); //erase(begin + 4) erases [4]
			i--; //handle vector shifting
		}
	}
	for (int i = 0; i < (int) verts.size(); i++)
		verts[i]->patchIdx = -1;
	for (int i = 0; i < (int) baseVerts.size(); i++) //go through the new baseVerts to update idxInBaseVerts values as well as patchIdx
	{
		verts[baseVerts[i]]->idxInBaseVerts = verts[baseVerts[i]]->patchIdx = i;
		for (int j = 0; j < (int) verts[ baseVerts[i] ]->baseNghbhood->size(); j++)
			verts[ (*verts[ baseVerts[i] ]->baseNghbhood)[j] ]->patchIdx = i;
//note that, vertices that are not touched here remain patchIdx = -1 thnx to for-loop above
	}

	return initialSize;
}

float Mesh::setCorrespondenceErrs(Mesh* mesh2, float r, int when)
{
	//set vertex-based correspondence costs matchCostI/G of m1/m2.baseVerts as well as average costs correspErrG/isometryCost; matchIdx's also set

#ifdef DBG_DETAIL
	if (when == 1)
		cout << "\nerrors after 1st Blossom V:\n";
	else if (when == 3)
		cout << "\nerrors after greedyForBaseUpdate():\n";
	else if (when == 4)
		cout << "\nerrors after isometryBasedGraphMatching():\n";
	else if (when == -2)	//called in the end very end by finalizeMatching()
		cout << "\nultimate errors:\n";
#endif

	isometryCost = groundTruthCost = 0.0f;
	int scSize = (int) safeCorresp.size() / 2; //final size of safeCorresp to be used for averaging; div by 2 'cos every 2 item counts 1 'cos they're related
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
	{
assert(verts[safeCorresp[2*s]]->base && mesh2->verts[safeCorresp[2*s + 1]]->base);
		//matchIdx
		int bv1 = safeCorresp[2*s], bv2 = safeCorresp[2*s + 1];
		verts[bv1]->matchIdx = bv2;
		mesh2->verts[bv2]->matchIdx = bv1; //dual op (for one-to-many matching, m2.matchIdx holds the most recent match only; not important 'cos m1.matchIdx values are always correct)
		//matchCostI
		verts[bv1]->matchCostI = 0.0f;
		for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
		{
			int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
			if (s1 == bv1)
				continue; //even if i go below, s1.dSpanning[bv1]'d be 0 so no effect; besides, assume s1-s2 is a good match being tested and s1(=tbv1)-tbv2 is a bad match in safeList; if i canceled continue just-below, then good s1-s2 would get a higher cost wrongly

			//use normalized dSpanning[] for isometryCost of whole correspondence (dSpanning[] are already normalized into [0,1] after comp..BaseGeoAff..Mtrx())
			float bv1s1 = verts[bv1]->dSpanning[s1], bv2s2 = mesh2->verts[bv2]->dSpanning[s2];
assert(bv1s1 >= 0 && bv1s1 <= 1 && bv2s2 >= 0 && bv2s2 <= 1);
			//if bv1 vs. bv2 is a good correspondence, then geoDist(bv1, s1) = geoDist(bv2, s2) for all s1 & s2 in safeCorresp
			float diffForNormalized = bv1s1 - bv2s2; //use normalized dSpanning values to make this value comparable w/ any input meshes
			verts[bv1]->matchCostI += fabs(diffForNormalized); //diffForNormalized small if bv1 vs. bv2 is a good correspondence (must use this for isometryCost below)
										//diffForNormalized*diffForNormalized; //squared difference behaves better than fabs difference in addMoreRoots; but here fabs acted better, e.g. for 5-221
		}
		verts[bv1]->matchCostI /= (scSize-1); //fabs for matchCostI in [0,1], making matchCostI / scSize in [0,1]
		mesh2->verts[bv2]->matchCostI = verts[bv1]->matchCostI; //m2.matchCostI set but for one-to-many case it'll hold only the most recent info which is not fatal 'cos m1 values are robust
		//global isometry cost (difference from correspErrI is that, isometryCost is normalized, hence comparable among different inputs)
		isometryCost += verts[bv1]->matchCostI;
#ifdef PROBABILISTIC_COST //isometryCost has nothing to do w/ probabilistic cost 'cos it gives the deviation from isometry of the current correspondence, in safeCorresp[] list; so, update vertex-based costs only to be used in greedy part
			//0.0000000001f is for log(0) undefined case; perturb a little to use a defined value in this very rare case
			verts[bv1]->matchProbI = mesh2->verts[bv2]->matchProbI = fabs( log10( (verts[bv1]->matchCostI != 1.0f ? 1.0f - verts[bv1]->matchCostI : 0.0000000001f) ) ); //taking abs makes cost and prob linearly proportional (not reversely) so that cost = 0.9 and 0.99 maps to 0.10 and 0.100 probs and they map to -1 and -2 log s and they to 1 and 2 abs-logs
#endif
		//matchCostG
		if (theInput == JUMPING || theInput == DANCING || theInput == HORSE) //ground-truth available only for these three
		{
			//matchCostG & groundTruthCost
			float geoDist = verts[bv1]->dSpanningUnn[bv2];
			verts[bv1]->matchCostG = mesh2->verts[bv2]->matchCostG = geoDist;
			groundTruthCost += geoDist;
		}
	}
	groundTruthCost /= scSize;
	//global isometry cost
	isometryCost /= scSize; //scSize amount of += [0,1] made above; so, div it by scSize to make isometryCost in [0,1]
	//whole correspondence cost
assert(isometryCost >= 0 && isometryCost <= 1);

	if (when == -2 || when == 1 || when == 5) //r: given in terms of current patch radius, n: normalized by max geo dist, none: given as is
	{
		if (theInput == JUMPING || theInput == DANCING || theInput == HORSE) //we've gound-truth info as well
			cout << "ground-truth cost: " << groundTruthCost << " (= " << groundTruthCost/r << "r, = " << groundTruthCost/maxGeoDistG << "n)\nisometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n"; //ground-truth costs not in [0,1], so use unnormalized r
		else
		{
			cout << "isometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n";
			groundTruthCost = -0.9f * r1; //an invalid # to fprint just to say ground-truth n/a
		}
	}

	return isometryCost;
}

void Mesh::sortSafeCorresp(Mesh* mesh2, char* fName)
{
	//clears and then refills safeCorresp w/ current baseVerts entries in the ascending order of isometryCosts

	//empty it
	int initialSize = (int) safeCorresp.size();
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
		verts[ safeCorresp[2*s] ]->safe = mesh2->verts[ safeCorresp[2*s + 1] ]->safe = false;
	safeCorresp.clear();
	//refill it in sorted order
	for (int j = 0; j < (int) baseVerts.size(); j++)
		verts[ baseVerts[j] ]->processed = false;
	FILE* fPtr = fopen(fName, "w");
	fprintf(fPtr, "%d\t\t%f\t%f\t%f\t%f\t%f\t%f\n", (int) baseVerts.size(), r1, r2, minGeoDistG, maxGeoDistG, mesh2->minGeoDistG, mesh2->maxGeoDistG); //# of correspondences to be written below and r values and min/maxGeoDists
	for (int s = 0; 2*s+1 < initialSize; s++)
	{
		int nextMinCost; //next vert whose matchCostI is current minimum (idx to verts[])
		float mc = INF;
		for (int j = 0; j < (int) baseVerts.size(); j++)
			if (! verts[ baseVerts[j] ]->processed && verts[ baseVerts[j] ]->matchCostI < mc)
			{
				mc = verts[ baseVerts[j] ]->matchCostI; //matchCostI and matchProbI are straightly (not reversely) related; so, sorting w/ matchProbI has the same effect, i.e. could've used = matchProbI as well here
				nextMinCost = baseVerts[j];
			}

		safeCorresp.push_back(nextMinCost); //next safe correspondence goes to following 2 slots in safeCorresp
		safeCorresp.push_back(verts[nextMinCost]->matchIdx); //bv2
		verts[nextMinCost]->safe = mesh2->verts[ verts[nextMinCost]->matchIdx ]->safe = true;
		verts[nextMinCost]->processed = true;
		fprintf(fPtr, "%d\t%d\t%f\t%f\n", nextMinCost, verts[nextMinCost]->matchIdx, verts[nextMinCost]->matchCostI, ((theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 /*&& theInput != SCAPE*/ && theInput != TOSCA) ? -0.9f : verts[nextMinCost]->matchCostG)); //ground-truth n/a for hybridRun
		verts[nextMinCost]->top10 = ((int) safeCorresp.size() < 20); //true if in top10
	}
	fclose(fPtr);
}

void Mesh::decideTop10Iso()
{
	//similar to sortSafeCorresp1to1() but this can't sort safeCorresp exactly 'cos matchIdx represents only 1 match although v may have 2+ matches in this many-to-1 safeCorresp; so, decide just top10 w.r.t. matchCostI

	int initialSize = (int) safeCorresp.size();
	vector< int > topIso10;
	for (int j = 0; j < (int) baseVerts.size(); j++)
		verts[ baseVerts[j] ]->processed = false;
	for (int s = 0; 2*s+1 < initialSize; s++)
	{
		int nextMinCost; //next vert whose matchCostI is current minimum (idx to verts[])
		float mc = INF;
		for (int j = 0; j < (int) baseVerts.size(); j++)
			if (! verts[ baseVerts[j] ]->processed && verts[ baseVerts[j] ]->matchCostI < mc)
			{
				mc = verts[ baseVerts[j] ]->matchCostI; //matchCostI and matchProbI are straightly (not reversely) related; so, sorting w/ matchProbI has the same effect, i.e. could've used = matchProbI as well here
				nextMinCost = baseVerts[j];
			}

		topIso10.push_back(nextMinCost);
		topIso10.push_back(verts[nextMinCost]->matchIdx); //bv2
		verts[nextMinCost]->processed = true;
		verts[nextMinCost]->top10 = ((int) topIso10.size() <= 12);//22); //22: top10, 12: top5
	}
}
void Mesh::decideTop10Curv()
{
	//similar to decideTop10Iso() but this decided top10 w.r.t. gc (curvatures)

	computeGaussianCurvatures(); //gc's ready for sure
	int initialSize = (int) safeCorresp.size();
	vector< int > topCurv10;
	for (int j = 0; j < (int) baseVerts.size(); j++)
		verts[ baseVerts[j] ]->processed = false;
	for (int s = 0; 2*s+1 < initialSize; s++)
	{
		int nextMaxCurv;
		float mc = -INF;
		for (int j = 0; j < (int) baseVerts.size(); j++)
			if (! verts[ baseVerts[j] ]->processed && verts[ baseVerts[j] ]->gc > mc)
			{
				mc = verts[ baseVerts[j] ]->gc;
				nextMaxCurv = baseVerts[j];
			}

		topCurv10.push_back(nextMaxCurv);
		topCurv10.push_back(verts[nextMaxCurv]->matchIdx);
		verts[nextMaxCurv]->processed = true;
		if (! (C2Fmode && theInput == DOG_WOLF))
			verts[nextMaxCurv]->topCurv10 = ((int) topCurv10.size() <= 12);//22); //22: top10, 12: top5
	}
if (theInput == DOG_WOLF) { verts[2145]->topCurv10 = false; verts[2346/**/]->topCurv10 = verts[1003]->topCurv10 = verts[1005]->topCurv10 = verts[220]->topCurv10 = verts[525]->topCurv10 = verts[2424]->topCurv10 = verts[2034]->topCurv10 = verts[2562]->topCurv10 = verts[1156]->topCurv10 = verts[3332]->topCurv10 = verts[0]->topCurv10 = verts[1]->topCurv10 = true; }//cancel some high curvatures from face and add feet for clear visual
}
void Mesh::decideTop10Mu(bool evenlySpaced)
{
	//similar to decideTop10Iso() but this decided top10 w.r.t. mu (integral geodesic distance); besides, a chance to make top10 evenlySpaced by sacrifying the true high mu verts just because they're too close

	//muOrder[0] is the vert w/ max mu, [1] is the second max, and so on
	if (! muOrder) //muOrder not filled, so pick top10 manually
	{
		if (theInput == BALLERINA) verts[4656]->topMu10 = verts[4762]->topMu10 = verts[4482]->topMu10 = verts[4134]->topMu10 = verts[4935]->topMu10 = verts[5332]->topMu10 = verts[3897]->topMu10 = verts[5409]->topMu10 = verts[5002]->topMu10 = verts[934]->topMu10 = true;
		if (theInput == DOG_WOLF) verts[1]->topMu10 = verts[525]->topMu10 = verts[3332]->topMu10 = verts[1005]->topMu10 = verts[2562]->topMu10 = verts[1315]->topMu10 = verts[457]->topMu10 = verts[3130]->topMu10 = verts[2199]->topMu10 = verts[2209]->topMu10 = true;
		if (theInput == HORSE) {verts[8297]->topMu10 = verts[7553]->topMu10 = verts[6525]->topMu10 = verts[2794]->topMu10 = verts[8039]->topMu10 = verts[2079]->topMu10 = verts[5249]->topMu10 = verts[5394]->topMu10 = verts[7010]->topMu10 = verts[4005]->topMu10 = true;
								verts[/*284*/715]->top10 = verts[715]->top10 = verts[1000]->top10 /*= verts[4727]->top10*/ = true; }//set top10 iso as well just for a better visual
		if (theInput == DOG_HORSE) {verts[1]->topMu10 = verts[215]->topMu10 = verts[525]->topMu10 = verts[530]->topMu10 = verts[1228]->topMu10 = verts[1504]->topMu10 = verts[2034]->topMu10 = verts[2562]->topMu10 = verts[3094]->topMu10 = verts[3332]->topMu10 = true;
								verts[1658]->top10 = verts[1763]->top10 = verts[2008]->top10 = verts[2042]->top10 = true; }//set top10 iso as well just for a better visual
		if (theInput == WOLF_HORSE) verts[364]->topMu10 = verts[299]->topMu10 = /*verts[988]->topMu10 =*/ verts[1117]->topMu10 = verts[1583]->topMu10 = verts[1632]->topMu10 = verts[2821]->topMu10 = verts[2873]->topMu10 = true;
									//verts[1649]->top10 = /*verts[1803]->top10 = verts[1807]->top10 = verts[1866]->top10 = verts[1902]->top10 = verts[2090]->top10 =*/ true; }//set top10 iso as well just for a better visual

		return;
	}
	if (evenlySpaced)
	{
		if (radii.size() < 3) //can't access radii[2] for evenly-spaced sampling distance below
			return;
		for (int i = 0; i < (int) verts.size(); i++)
			verts[i]->processed = false;
	}
	int count = 0;
	for (int i = 0; i < (int) verts.size(); i++) //size of muOrder = verts.size()
		for (int j = 0; j < (int) baseVerts.size(); j++) //check whether muOrder[i] is an active/matched vert of this level; if so, put it to top10 w.r.t. (of course if top10 not filled yet)
			if (muOrder[i] == baseVerts[j])
			{
				if (! evenlySpaced)
				{
					if (count++ <= 10) //< for top10
						verts[ baseVerts[j] ]->topMu10 = true;
					else
						return;
				}
				else
				{
					if (! verts[ baseVerts[j] ]->processed && count++ < 10)
					{
						verts[ baseVerts[j] ]->topMu10 = true;
//cout << baseVerts[j] << "\t";
						//mark all verts around baseVerts[j] by making processed = true for them
						for (int v = 0; v < (int) verts.size(); v++)
							if (verts[ baseVerts[j] ]->dSpanningUnn[v] <= radii[2]) //use radius of x level as the evenlySpaced sampling distance
								verts[v]->processed = true;
					}
					else if (count == 10) //some if rejections are due to processed=true
						return;
				}
			}
//if (theInput == DOG_WOLF) { verts[2145]->topCurv10 = false; verts[2346/**/]->topCurv10 = verts[1003]->topCurv10 = verts[1005]->topCurv10 = verts[220]->topCurv10 = verts[525]->topCurv10 = verts[2424]->topCurv10 = verts[2034]->topCurv10 = verts[2562]->topCurv10 = verts[1156]->topCurv10 = verts[3332]->topCurv10 = verts[0]->topCurv10 = verts[1]->topCurv10 = true; }//cancel some high curvatures from face and add feet for clear visual
}


//#define USE_VOTING //pick 1 candidate c2 and compare bv1-bv2 vs. bv2-c2; c2 centers baseNghbsIn2DueTo1
void Mesh::greedyForBaseUpdate(Mesh* mesh2, float r, int iter)
{
	//refines results of 2nd Hungarian further by establishing base-neighbor isometry, i.e., if 2 bases are base-neighbors, then their correspondences must be base-neighbors as well

	cout << "\nmatches refined via greedy optimization of isometry cost....\n";

	int n1 = (int) baseVerts.size();
	//m2.baseVerts.nMatches become 1 if they're currently matched w/ some m1.baseVerts (for iter = 1), and 1+ for iter > 1 ('cos 1-to-many is allowed here, and nowhere else before here)
	for (int j = 0; j < (int) mesh2->baseVerts.size(); j++)
		mesh2->verts[ mesh2->baseVerts[j] ]->nMatches = 0; //init
	for (int j = 0; j < n1; j++)
		if (baseVerts[j] != -1 && //flagged as a extra repetition line; so, not process it again (not sure i still have this repetition thing but do this for safety)
			verts[ baseVerts[j] ]->matchIdx != -1)
		{
			mesh2->verts[ verts[baseVerts[j]]->matchIdx ]->nMatches++;
if (iter == 1) assert(mesh2->verts[ verts[baseVerts[j]]->matchIdx ]->nMatches == 1); //all correspondences so far is 1-to-1 so that nMatches cannot exceed 1 for any m2.base
//assertion above is wrong for call from matchAfterZhang() 'cos his correspondence that initializes here (iter = 1) may be 1-to-many
		}
	int nUpd = 0, nTie = 0; //# of change of existing correspondences, i.e. updates, and # of times a tie is resolved to decide c2
	for (int i = 0; i < n1; i++)
	{
		//get the (i, c(i)) pair
		int bv1 = baseVerts[i];
		if (bv1 == -1)
			continue; //flagged as an extra repetition line; so, not process it again
		int bv2 = verts[bv1]->matchIdx;
		if (bv2 == -1) //1-to-many mappings may leave unmatched bv1's
			continue;
assert(verts[bv1]->base && mesh2->verts[bv2]->base);

//cout << "\n[[" << bv1 << ", " << bv2 << "]] in test\n";

		bool updatePerformed = false; //to reshape safeCorresp after each update
		//bv1.baseNghbs have correspondences in mesh2, i.e. baseNghbsIn2DueTo1; assuming baseNghbsIn2DueTo1 vs. bv1.baseNghbs have been paired up accurately, i'll test the accuracy of bv1 vs. bv2 pairing; to do so,
		//see what baseNghbsIn2DueTo1 has in their center; let c2 be that centering base, which is computed as c2 = most frequent vertex among the union of baseNghbsIn2DueTo1[i].baseNghbs, called allVotes[], for i = 1..m
		//fill baseNghbsIn2DueTo1 list
		vector< int > baseNghbsIn2DueTo1;
		for (int k = 0; k < (int) verts[bv1]->baseNghbs.size(); k++)
			baseNghbsIn2DueTo1.push_back(verts[	verts[bv1]->baseNghbs[k] ]->matchIdx);
#ifdef USE_VOTING
		//no PROBABILISTIC_COST implemented  for USE_VOTING!!!!!!!!!!!!
		bool weightedVoting = true; //weights each vote w/ the quality of the match of the voter, where quality is the matchCostI
		
		vector< int > c2Candids; //tie breaker
		int votesSize = 0, c2; //c2 is in mesh2
		if (! weightedVoting)
		{
			int* allVotes = new int[n1*n1]; //sum of # of baseNeighs of each baseNghb cannot exceed, say, n1^2 (very generous); also, m2.baseVerts size is same as m1.baseVerts size so that n1 or n2 not matter here
//if (bv1 == 19367) cout << bv1 << " vs. " << bv2 << " in test\n";
			//use baseNghbsIn2DueTo1 to decide c2
			for (int bn = 0; bn < (int) baseNghbsIn2DueTo1.size(); bn++) //for baseNghbsIn2DueTo1
			{
				int bnIdx = baseNghbsIn2DueTo1[bn];
//if (bv1 == 19367) cout << mesh2->verts[bnIdx]->matchCostI << " == " << verts[ mesh2->verts[bnIdx]->matchIdx ]->matchCostI << "\t\t" << bnIdx << "pp\t";
				if (bnIdx != -1) //some m1.bases may have no match now; since baseNghbs is still old, they come here; add nothing to allVotes if they're seen
					for (int bnn = 0; bnn < (int) mesh2->verts[bnIdx]->baseNghbs.size(); bnn++) //for baseNghbsIn2DueTo1[bn].baseNghbs
					{
						allVotes[votesSize++] = mesh2->verts[bnIdx]->baseNghbs[bnn];
//if (bv1 == 19367) cout << mesh2->verts[bnIdx]->baseNghbs[bnn] << "p\t";
					}
//if (bv1 == 19367) cout << endl;
			}
			//sort allVotes[] to easily decide the most frequent integer in it
			insertionSort(allVotes, votesSize);
			//find the most frequent integer, which is the voted/suggested pair, c2, for bv1
			int prev = allVotes[0], maxCount = -1, count = 0;
			vector< int > c2Candidates; //there may be 2+ multisets that have the max cardinality; remember all as c2 candidates and pick the best as c2 later
										//format: i <- elementRepresentingMultiset, i+1 <- cardinality; e.g. 5,3 means allVotes[] has have a 5,5,5 triplet
			for (int j = 0; j < votesSize; j++)
			{
				if (allVotes[j] == prev)
				{
					if (++count >= maxCount) //increment count and do this test in case last item in allVotes belongs to most-frequent multiset
					{
						//c2 = prev;
						c2Candidates.push_back(allVotes[j]); c2Candidates.push_back(count);
//if (bv1 == 19367) cout << allVotes[j] << "\t" << count << ".\n";
						maxCount = count;
						for (int c = 1; c < (int) c2Candidates.size(); c += 2)
							if (c2Candidates[c] < count)
								c2Candidates[c-1] = -1; //refer to comments of else-block below which entirely applies here as well
					}
				}
				else
				{
					count = 1; //reset it for this new item that is different from prev
					if (count >= maxCount)
					{
						//c2 = prev; //replace this w/ below to remember all candidates in case there're 2+ multisets w/ max cardinality
						c2Candidates.push_back(allVotes[j]); c2Candidates.push_back(count); //insert element and then its cardinality
						maxCount = count;
//if (bv1 == 19367) cout << allVotes[j] << "\t" << count << "!\n";
						//clear all entries that has a count lower than this current count
						for (int c = 1; c < (int) c2Candidates.size(); c += 2)
							if (c2Candidates[c] < count) //then associated candid, i.e. c2Candidates[c-1], should be discarded/cleared
								c2Candidates[c-1] = -1; //clear effect to this element
					}
				}
				prev = allVotes[j]; //previous item to be used in next iteration
			}
			//extract necessary info from c2Candidates to c2Candids using special format of c2Candidates
			int maxCardinality;
			for (int c = (int) c2Candidates.size()-1; c >= 1; c -= 2)
			{
				int currentElement = c2Candidates[c-1], currentCardinality = c2Candidates[c];
				if (currentElement != -1)
				{
					c2Candids.push_back(currentElement);
					maxCardinality = currentCardinality;
				}
				else //ensure that -1 1  -1 1  -1 1  -1 2  8 3  17 3, that is, all cardinalities < 3 has -1 on their left for this example sequence
					assert(currentCardinality < maxCardinality);
					//break; //save time instead of assertion
			}
			delete [] allVotes;
		}
		else //weighted voting
		{
			int m = (int) baseNghbsIn2DueTo1.size();
			float* weights = new float[m], //voting weight of each baseNghbsIn2DueTo1
				   weightSum = 0.0f; //normalization
			for (int bn = 0; bn < m; bn++)
			{
				int bnIdx = baseNghbsIn2DueTo1[bn];
				weights[bn] = (bnIdx == -1 ? -1.0f : 1.0f - mesh2->verts[bnIdx]->matchCostI); //the lower the matchCostI, the more effective the weight is
				weightSum += (bnIdx == -1 ? 0.0f : weights[bn]); //keep -1's in weights for idx consistency b/w weights and baseNghbsIn2DueTo1
			}
			//assert(weightSum != 0.0f); //for very weird cases this may be 0 indeed, i.e. when bv1.baseNghbs don't have matches on mesh2 side, all baseNghbsIn2DueTo1 is -1
			votesSize = (int) weightSum; //just to handle the if condition below; replaces assert just above 'cos when weightSum = 0, if condition in swap part below fails and nothing happens
			for (int bn = 0; bn < m; bn++)
			{
				weights[bn] /= weightSum;
//if (bv1 == 19367) cout << baseNghbsIn2DueTo1[bn] << " --weight-->" << weights[bn] << "\n";
			}
			float* votes = new float[(int) mesh2->verts.size()]; //not so efficient but easy; # of votes i'th vertex gets, where # is not integer but weighted float
			int* nVotes = new int[(int) mesh2->verts.size()]; //normalize votes[i] by votes[i] /= nVotes[i] where the latter keeps the # of appearances of i'th vertex during whole voting
			for (int v = 0; v < (int) mesh2->verts.size(); v++)
			{
				votes[v] = 0.0f;
				nVotes[v] = 0;
			}
			for (int bn = 0; bn < m; bn++) //for baseNghbsIn2DueTo1
			{
				int bnIdx = baseNghbsIn2DueTo1[bn];
				if (bnIdx != -1)
					for (int bnn = 0; bnn < (int) mesh2->verts[bnIdx]->baseNghbs.size(); bnn++) //for baseNghbsIn2DueTo1[bn].baseNghbs
					{
						votes[ mesh2->verts[bnIdx]->baseNghbs[bnn] ] += weights[bn]; //baseNghbs cannot be -1, so safe here
						nVotes[ mesh2->verts[bnIdx]->baseNghbs[bnn] ]++;
					}
			}
			for (int v = 0; v < (int) mesh2->verts.size(); v++) //normalize votes
				if (nVotes[v] != 0)
				{
					if (nVotes[v] == 1) //if this has only 1 vote, then it's deceiptful 'cos i divide by 1 and therefore it remains big; so, make candidates w/ 1 vote unselectable
						votes[v] = 0.0f;
					else
						votes[v] /= nVotes[v];
//if (bv1 == 19367) cout << " vote for mesh2." << v << " is " << votes[v] << endl;
				}
			float maxVote = -INF;
			for (int v = 0; v < (int) mesh2->verts.size(); v++) //select the max normalized vote
				if (nVotes[v] != 0 && votes[v] > maxVote) //normalized version
					maxVote = votes[v];
//if (bv1 == 19367) cout << maxVote << " mv\n";
			if (maxVote == 0.0f) //candidates w/ 1 vote only are skipped above; so, if all candids've 1 vote, then there's no nonzero votes[v] and therefore no candidate for bv1's replacement
				votesSize = 0;
			else
			{
				//for tie detection, add all verts w/ maxVore to c2Candids and i'll handle ties below
				for (int v = 0; v < (int) mesh2->verts.size(); v++)
					if (votes[v] == maxVote)
						c2Candids.push_back(v);
assert(! c2Candids.empty());
			}
			delete [] weights;
			delete [] votes;
			delete [] nVotes;
		} //end of weightedVoting

		bool fromTie = false; //c2 resolved from a tie
		//in case of a tie for c2, i.e. multiple idxs appear same (max) number of times, pick the best idx as the c2
		if ((int) c2Candids.size() == 1)
			c2 = c2Candids[0]; //no tie; only 1 candidate for c2
		else //tie
		{
//if (bv1 == 19367) cout << "tie while doing " << bv1 << " " << bv2 << "\n";
			fromTie = true;
			float minOC = INF; //pick the candidate that produces the min overall cost
			int scSize = (int) safeCorresp.size() / 2;
			for (int c = 0; c < (int) c2Candids.size(); c++)
			{
				int c2c = c2Candids[c];
				//vertex-based cost computations adapted from setCorrespondenceErrs()
				float vertexBasedIsometry = 0.0f;
				for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
				{
					int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
					if (s1 == bv1) //ignore bv1 vs. existing match in safeCorresp 'cos i'm trying to see bv1 vs. c2c effect on all other safe matches
						continue;
					float diffForNormalized = verts[bv1]->dSpanning[s1] - mesh2->verts[c2c]->dSpanning[s2];
					vertexBasedIsometry += fabs(diffForNormalized); //diffForNormalized small if bv1 vs. c2c is a good correspondence (must use this for isometryCost below)
				}
				vertexBasedIsometry /= (scSize-1); //-1: s1 == bv1 ignored (indeed not so important but use this)
				//vertexBasedIsometry /= scSize; //assuming s1=bv1 & s2=c2; so, even if ignore s1=bv1 iteration, consider it as += 0 contribution for that iteration and hence no scSize-1 in denom here; use /= scSize
float oc = vertexBasedIsometry; //costsCpy[][] not inited for calls from matchAfterZhang(); wDissim=0 anyway; so, set oc = vertexBasedIsometry directly
////cout << bv1 << " vs. " << c2c << " yields: " << oc << endl;
				if (oc < minOC)
				{
					c2 = c2c;
					minOC = oc;
				}
			} //end of c
////cout << "rrrrrrrresolved: " << c2 << endl;
		}

		////////////// swap //////////////////////
		int bv11;
		bool upd1 = true, upd11 = true; //if bv1 needs a new/updated match, i.e. vertexBasedIso <= bv1.matchCostI, then this remains true; similarly for bv11
		if (votesSize > 0 && c2 != bv2) //update needed (votesSize > 0 condition is detail, to prevent here in case nothing added to allVotes)
//			&& ! fromTie) //no, should be disabled 'cos most of updates are due to ties
		{
assert(c2 != -1); //'cos it is set above via c2Candids[]
			bv11 = mesh2->verts[c2]->matchIdx; //other base in mesh1 that's currently matched w/ c2 (may match w/ bv2 below)
//if (bv1 == 19367) cout << "c2 != bv2 ==> (((((((" << bv1 << ", " << bv2 << ") & (" << bv11 << ", " << c2 << ")))))))))\n";

			//before update: (bv1,bv2) and (bv11,c2), which is wrong if we're here (heal by swapping matches); no!, don't decide the
			//wrongness immediately; compute vertexBasedIsometry of bv1 assuming update is performed; if it is worse than existing matchCostI,
			//then initial (bv1,bv2) is not that bad and therefore update is not done, i.e. existing (bv1,bv2) and (bv11,c2) stays as is
			////////////// isometry cost comparison under swap performed assumption //////////////
			//bv1 vs. c2 active assumption
			int scSize = (int) safeCorresp.size() / 2;
			float vertexBasedIso = 0.0f; //for bv1 (under the assumption that it is paired w/ c2)
			for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
			{
				int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
				if (s1 == bv1)
					continue;
				float diffForNormalized = verts[bv1]->dSpanning[s1] - mesh2->verts[c2]->dSpanning[s2]; //bv1,c2
				vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if bv1 vs. c2 is a good correspondence (must use this for isometryCost below)
			}
			vertexBasedIso /= (scSize-1);
//if (bv1 == 19367) cout << bv1 << " vs. " << c2 << " yyyyyyyyyields: " << vertexBasedIso << "\tvs. " << verts[bv1]->matchCostI  << endl;
			if (vertexBasedIso > verts[bv1]->matchCostI) //bv1 vs. c2 leads to a worse isometry cost for bv1 than the existing cost bv1.matchCostI; so, no swap
			//if (vertexBasedIso > iCostI[bv1]) //bv1 vs. c2 leads to a worse isometry cost for bv1 than the existing cost bv1.matchCostI; so, no swap
				//continue; //with the new i of the outer for-loop w/o doing lines below
				upd1 = false; //no update but go below anyway 'cos bv11 may need an updated match

			//bv11 vs. bv2 active assumption
			if (bv11 != -1)
			{
				vertexBasedIso = 0.0f; //for bv11 (under the assumption that it is paired w/ bv2)
				for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
				{
					int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
					if (s1 == bv11)
						continue;
					float diffForNormalized = verts[bv11]->dSpanning[s1] - mesh2->verts[bv2]->dSpanning[s2]; //bv11,bv2
					vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if bv11 vs. bv2 is a good correspondence (must use this for isometryCost below)
				}
				vertexBasedIso /= (scSize-1);
//if (bv1 == 19367) cout << bv11 << " vs. " << bv2 << " yieldss: " << vertexBasedIso << "\tvs. " << verts[bv11]->matchCostI  << endl;
				if (vertexBasedIso > verts[bv11]->matchCostI) //bv11 vs. bv2 leads to a worse isometry cost for bv1 than the existing cost bv11.matchCostI; so, no swap
				//if (vertexBasedIso > iCostI[bv11]) //bv11 vs. bv2 leads to a worse isometry cost for bv1 than the existing cost bv11.matchCostI; so, no swap
					//continue; //with the new i of the outer for-loop w/o doing lines below
					upd11 = false; //no update for bv11 but go below anyway 'cos bv1 may want an update, i.e. upd1 = true
			}
			else //no bv11 at all, so no update on it
				upd11 = false;
			////////////// isometry cost comparison under update performed assumption ends //////////////

			if (! upd1 && ! upd11)
				continue; //none of the pairs needs an update; so, don't go below

			if (upd1)
			{
				//update for bv1
				verts[bv1]->matchIdx = c2;
				mesh2->verts[c2]->matchIdx = bv1; //dual but not that informative: e.g. c2 has bv1111 before; now it has both bv1111 and bv11 but matchIdx = bv11 only; not to worry 'cos that info is in mesh1 side
				mesh2->verts[c2]->nMatches++; //for 1-to-many handling of mesh2 bases
				if (! verts[bv1]->base) //although this is still in baseNghbs of other bases, it may currently be nonbase (due adjustBaseVerts() called from updBases2() or from here); so, convert it to base again
				{
					baseVerts.push_back(bv1); //idxInBaseVerts = baseVerts.size()-1 not done 'cos it'll be set after after this for-loop in adjustBaseVerts() anyway
					verts[bv1]->base = true;
cout<<"never comes here 'cos baseVerts never change; bases in it may become unmatched but stay there until i call adjustBaseVerts() at the very end\n";exit(0);
				}
				if (! mesh2->verts[c2]->base)
				{
					mesh2->baseVerts.push_back(c2);
					mesh2->verts[c2]->base = true;
cout<<"never comes here 'cos baseVerts never change; bases in it may become unmatched but stay there until i call adjustBaseVerts() at the very end\n";exit(0);
assert(mesh2->verts[c2]->nMatches == 1); //since it just became base, nMatches must be 1 due to ++ above
				}

				if (--mesh2->verts[bv2]->nMatches == 0) //for 1-to-many handling of mesh2 bases; bv2 lost 1 match, namely bv1, i.e. no (bv1,c2) anymore, so that we decrease nMatches--; it'll increase again below if upd2 = true
					mesh2->verts[bv2]->matchIdx = -1; //matchIdx = -1 removes it from m2.baseVerts during m2.adjustBaseVerts(); it stays > 0 if nMatches > 0, which is good 'cos it stays in m2.baseVerts; no!!!! either way, bv2 stays at baseVerts
			}

			if (bv11 != -1)
			{
				if (upd11)
				{
					//update for bv2
					verts[bv11]->matchIdx = bv2;
					mesh2->verts[bv2]->matchIdx = bv11; //dual but not that informative: e.g. c2 has bv1111 before; now it has both bv1111 and bv11 but matchIdx = bv11 only; not to worry 'cos that info is in mesh1 side
					mesh2->verts[bv2]->nMatches++; //for 1-to-many handling of mesh2 bases

					//no if (! mesh2->verts[bv2]->base) block here 'cos bv2 is base for sure since in the very beginning of i-forLoop, we know it as bv1 vs. bv2 pair

					if (! verts[bv11]->base)
					{
						baseVerts.push_back(bv11);
						verts[bv11]->base = true;
cout<<"never comes here 'cos baseVerts never change; bases in it may become unmatched but stay there until i call adjustBaseVerts() at the very end\n";exit(0);
					} //bv2 is base for sure, so no redundant if (! bv2.base) block below

					if (--mesh2->verts[c2]->nMatches == 0) //for 1-to-many handling of mesh2 bases; c2 lost 1 match, namely bv11, i.e. no (bv11,c2) anymore, so that we decrease nMatches--
						mesh2->verts[c2]->matchIdx = -1; //matchIdx = -1 removes it from m2.baseVerts during m2.adjustBaseVerts(); it stays > 0 if nMatches > 0, which is good 'cos it stays in m2.baseVerts
				}
			}
//else if (bv11 == -1) not needed 'cos it does if --nMatches==0 then bv2.matchIdx = -1; it's already done above for sure (in non-swap block it is used in both if and else if to gurantee that it is done)

			//before update: (bv1,bv2) & (bv11,c2)
			//after update: (bv1,c2) & (bv11,bv2) for upd1 = upd11 = true, (bv1,c2) & (bv11,c2) for upd1 = true upd11 = false, (bv1,bv2) & (bv11,bv2) for upd1 = false upd11 = true case
			nUpd += ((upd1 && upd11) ? 2 : 1);
			nTie += ((int) c2Candids.size() > 1 ? 1 : 0); //just for info
			updatePerformed = true;
		} //end of c2 != bv2
		////////////// swap ends //////////////////////

		//////////// dynamic safeCorresp ///////////////
		if (updatePerformed)
		{
#ifdef DBG_DETAIL
			cout << "(" << bv1 << ", " << bv2 << ") & (" << bv11 << ", " << c2 << ") --> ";
			if (upd1 && upd11) //exact swap happened
				cout << "(" << bv1 << ", " << c2 << ") & (" << bv11 << ", " << bv2 << ") upd1 & upd11\n";
			else if (upd1 && ! upd11)
				cout << "(" << bv1 << ", " << c2 << ") & (" << bv11 << ", " << c2 << ") upd1\n";
			else if (! upd1 && upd11)
				cout << "(" << bv1 << ", " << bv2 << ") & (" << bv11 << ", " << bv2 << ") upd11\n";
#endif
			refillSafeCorresp(mesh2, 1); //reshape safeCorresp to involve new pair after update above
			setCorrespondenceErrs(mesh2, r, -1); //matchCostI changes for this new safeCorresp list
		}
		//////////// dynamic safeCorresp ends ///////////////
#else	//not use voting; rather, consider each baseNghbsIn2DueTo1 and their baseNghbs as a candidate for bv1 and do the same swap if candidate is better than the existing bv2 for bv1

		//better idea to use the 5 best spectral corresp (or alternating corresp for iter>1) 'cos it's more reliable than initial safeCorresp which is the unrefined very first ~10 spectral correspondence, not the best 5
		if ((int) safeCorresp.size() < 30) //do it only for coarse-to-fine algo init where i've few initial matches from which best 5 can be chosen efficiently; < 30 means at most 15 matches in current safeCorresp, which'll be reduced to 5 via safeCorrespC10_5()
			safeCorrespC10_5(mesh2);

		//use baseNghbsIn2DueTo1 and their baseNghbs to decide the best candidate c2 for bv1
		int scSize = (int) safeCorresp.size() / 2, minC2idx = -1;
		float minIsoForC2 = INF; //c2 will be tried as a match of bv1; it'll either be the one w/ minimum isometry deviation/cost or the one which has maximum probability to match w/ bv1 (prob = 1 - cost actually); but to make cost and prob linearly proportional, use abs-log (log for EM framework)
		//make all bases to be processed in the next for-loop unprocessed to process them once for efficiency
		for (int bn = 0; bn < (int) baseNghbsIn2DueTo1.size(); bn++) //for baseNghbsIn2DueTo1
		{
			int bnIdx = baseNghbsIn2DueTo1[bn];
			if (bnIdx != -1) //some m1.bases may have no match now; since baseNghbs is still old, they come here; add nothing to allVotes if they're seen
				for (int bnn = 0; bnn < (int) mesh2->verts[bnIdx]->baseNghbs.size(); bnn++) //for baseNghbsIn2DueTo1[bn].baseNghbs
					mesh2->verts[ mesh2->verts[bnIdx]->baseNghbs[bnn] ]->processed = false;
		}
		for (int bn = 0; bn < (int) baseNghbsIn2DueTo1.size(); bn++) //for unprocessed baseNghbsIn2DueTo1[bn].baseNghbs
		{
			int bnIdx = baseNghbsIn2DueTo1[bn];
			if (bnIdx != -1)
				for (int bnn = 0; bnn < (int) mesh2->verts[bnIdx]->baseNghbs.size(); bnn++)
				{
					int c2c = mesh2->verts[bnIdx]->baseNghbs[bnn];
					if (! mesh2->verts[c2c]->processed) //c2c cannot be -1 'cos baseNghbs never change once inited; base.matchIdx's may be -1 though
					{
						//bv1 vs. c2c active assumption
						float vertexBasedIso = 0.0f; //for bv1 (under the assumption that it is paired w/ c2c)
						for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
						{
							int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
							if (s1 == bv1)
								continue;

//current (bv1, bv2) = (stomach, leg) and c2c is stomach (so, bv1-c2c is better); is s1==bv1 considered here, then s1~bv1 dist - s2~c2c dist = 0 - nonZero would give a cost for beautiful bv1-c2c active assumption; so, skip s1=bv1
							float diffForNormalized = verts[bv1]->dSpanning[s1] - mesh2->verts[c2c]->dSpanning[s2]; //bv1,c2c
assert(verts[bv1]->dSpanning[s1] >= 0.0f && verts[bv1]->dSpanning[s1] <= 1.0f && mesh2->verts[c2c]->dSpanning[s2] >= 0.0f && mesh2->verts[c2c]->dSpanning[s2] <= 1.0f);
							vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if bv1 vs. c2 is a good correspondence (must use this for isometryCost below)
						}
						vertexBasedIso /= (scSize-1); //nonprobabilisitc cost, i.e. deviation of isometry cost
#ifdef PROBABILISTIC_COST
						//0.0000000001f is for log(0) undefined case; perturb a little to use a defined value in this very rare case
						vertexBasedIso = fabs( log10( (vertexBasedIso != 1.0f ? 1.0f - vertexBasedIso : 0.0000000001f) )); //log is for base e, log10 for base 10; reason of fabs in setCorrespondenceErrs(); no rowSum = 1 normalization (for pdf) here 'cos i'm just comparing elements and normalizing to sum=1 not change results; not have an obvious row to normalize anyway; so, we're good
#endif
						if (vertexBasedIso < minIsoForC2)
						{
							minIsoForC2 = vertexBasedIso;
							minC2idx = c2c;
						}
					}
					mesh2->verts[c2c]->processed = true;
				} //end of bnn
		} //end of bn
		for (int bn = 0; bn < (int) baseNghbsIn2DueTo1.size(); bn++) //for unprocessed baseNghbsIn2DueTo1's themselves
		{
			int c2c = baseNghbsIn2DueTo1[bn]; //c2c can be -1 'cos baseNghbs due to bv1 are based on bv1.matchIdx's which may be -1
			if (c2c != -1 && ! mesh2->verts[c2c]->processed)
			{
				//bv1 vs. c2c active assumption
				float vertexBasedIso = 0.0f; //for bv1 (under the assumption that it is paired w/ c2c)
				for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
				{
					int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
					if (s1 == bv1)
						continue;
					float diffForNormalized = verts[bv1]->dSpanning[s1] - mesh2->verts[c2c]->dSpanning[s2]; //bv1,c2c
					vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if bv1 vs. c2 is a good correspondence (must use this for isometryCost below)
				}
				vertexBasedIso /= (scSize-1); //nonprobabilisitc cost, i.e. deviation of isometry cost
#ifdef PROBABILISTIC_COST
				vertexBasedIso = fabs( log10( (vertexBasedIso != 1.0f ? 1.0f - vertexBasedIso : 0.0000000001f) ) ); //probabilisitc cost, i.e. 1 - deviation of isometry cost exposed to abs-log to be compatible w/ EM framework and the fact that lower cost leads to lower probabilistic value for min-weight perfect matching on costs[][] mtrx
#endif
				if (vertexBasedIso < minIsoForC2)
				{
					minIsoForC2 = vertexBasedIso;
					minC2idx = c2c;
				}
				mesh2->verts[c2c]->processed = true;
			}
		} //end of bn
		if (minC2idx == -1)
			continue; //rarely; in case minC2idx not set above
		//////////// swap effect above ///////////////
		//swap effect above by comparing bv1-bv2 vs. bv1-minC2idx as well as bv11-minC2idx vs. bv11-bv2
		int bv11 = mesh2->verts[minC2idx]->matchIdx; //other base in mesh1 that's currently matched w/ minC2idx (may match w/ bv2 below)
		//bv11 vs. bv2 active assumption
		bool upd11 = false, upd1 = false;
		if (bv11 != -1 && bv11 != bv1) //2nd for efficiency; bv11 = bv1 means minC2idx is bv2, so below i'd compute bv1 vs. bv2 active assumption which is already known as current verts[bv1]->matchCostI
		{
			float vertexBasedIso = 0.0f; //for bv11 (under the assumption that it is paired w/ bv2)
			for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
			{
				int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
				if (s1 == bv11)
					continue;
				float diffForNormalized = verts[bv11]->dSpanning[s1] - mesh2->verts[bv2]->dSpanning[s2]; //bv11,bv2
				vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if bv11 vs. bv2 is a good correspondence (must use this for isometryCost below)
			}
			vertexBasedIso /= (scSize-1);
#ifdef PROBABILISTIC_COST
			vertexBasedIso = fabs( log10( (vertexBasedIso != 1.0f ? 1.0f - vertexBasedIso : 0.0000000001f) ) );
			if (vertexBasedIso < verts[bv11]->matchProbI) //bv11 vs. bv2 leads to a better isometry cost for bv11 than the existing cost bv11.matchCostI; so, do swap
#else
			if (vertexBasedIso < verts[bv11]->matchCostI) //bv11 vs. bv2 leads to a better isometry cost for bv11 than the existing cost bv11.matchCostI; so, do swap
#endif
				upd11 = true; //if-condition of this statement is either due to matchProbI or matchCostI
		}
#ifdef PROBABILISTIC_COST
		if (minIsoForC2 < verts[bv1]->matchProbI) //upd1 effect above; changes bv1-bv2 to bv1-minC2idx; minIsoForC2 naming sucks 'cos it actually holds abs-log values, which are compatible w/ matchProbI, the probability, not the cost
#else
		if (minIsoForC2 < verts[bv1]->matchCostI) //upd1 effect above; changes bv1-bv2 to bv1-minC2idx
#endif
		{
			//update for bv1
			verts[bv1]->matchIdx = minC2idx;
			mesh2->verts[minC2idx]->matchIdx = bv1; //dual but not that informative: e.g. c2 has bv1111 before; now it has both bv1111 and bv11 but matchIdx = bv11 only; not to worry 'cos that info is in mesh1 side
			mesh2->verts[minC2idx]->nMatches++; //for 1-to-many handling of mesh2 bases
assert(verts[bv1]->base && mesh2->verts[minC2idx]->base);
			if (--mesh2->verts[bv2]->nMatches == 0) //for 1-to-many handling of mesh2 bases; bv2 lost 1 match, namely bv1, i.e. no (bv1,c2) anymore, so that we decrease nMatches--; it'll increase again below if upd2 = true
				mesh2->verts[bv2]->matchIdx = -1; //matchIdx = -1 removes it from m2.baseVerts during m2.adjustBaseVerts(); it stays > 0 if nMatches > 0, which is good 'cos it stays in m2.baseVerts; no!!!! either way, bv2 stays at baseVerts
			//before update: (bv1,bv2) & (bv11,minC2Idx)
			//after update: (bv1,minC2Idx) & (bv11,bv2) for upd1 = upd11 = true, (bv1,minC2Idx) & (bv11,minC2Idx) for upd1 = true upd11 = false, (bv1,bv2) & (bv11,bv2) for upd1 = false upd11 = true case
			nUpd++;
			updatePerformed = true;
			upd1 = true;
		}
		if (upd11) //upd11 effect above; changes bv11-minC2idx to bv11-bv2
		{
			//update for bv2
			verts[bv11]->matchIdx = bv2;
			mesh2->verts[bv2]->matchIdx = bv11; //dual but not that informative: e.g. c2 has bv1111 before; now it has both bv1111 and bv11 but matchIdx = bv11 only; not to worry 'cos that info is in mesh1 side
			mesh2->verts[bv2]->nMatches++; //for 1-to-many handling of mesh2 bases
assert(verts[bv11]->base);
			if (--mesh2->verts[minC2idx]->nMatches == 0) //for 1-to-many handling of mesh2 bases; c2 lost 1 match, namely bv11, i.e. no (bv11,c2) anymore, so that we decrease nMatches--
				mesh2->verts[minC2idx]->matchIdx = -1; //matchIdx = -1 removes it from m2.baseVerts during m2.adjustBaseVerts(); it stays > 0 if nMatches > 0, which is good 'cos it stays in m2.baseVerts
			//before update: (bv1,bv2) & (bv11,minC2Idx)
			//after update: (bv1,minC2Idx) & (bv11,bv2) for upd1 = upd11 = true, (bv1,minC2Idx) & (bv11,minC2Idx) for upd1 = true upd11 = false, (bv1,bv2) & (bv11,bv2) for upd1 = false upd11 = true case
			nUpd++;
			updatePerformed = true;
		}
		//////////// swap effect above ends ///////////////

		//////////// dynamic safeCorresp ///////////////
		if (updatePerformed)
		{
#ifdef DBG_DETAIL
//FILE* fPtrr = fopen("upds.dat", "a");
			cout << "(" << bv1 << ", " << bv2 << ") & (" << bv11 << ", " << minC2idx << ") --> ";
//fprintf(fPtrr, "(%d, %d) & (%d, %d) --> ", bv1, bv2, bv11, minC2idx);
			if (upd1 && upd11) //exact swap happened
			{
				cout << "(" << bv1 << ", " << minC2idx << ") & (" << bv11 << ", " << bv2 << ") upd1 & upd11\n";
//fprintf(fPtrr, "(%d, %d) & (%d, %d)\n", bv1, minC2idx, bv11, bv2);
			}
			else if (upd1 && ! upd11)
			{
				cout << "(" << bv1 << ", " << minC2idx << ") & (" << bv11 << ", " << minC2idx << ") upd1\n";
//fprintf(fPtrr, "(%d, %d) & (%d, %d)\n", bv1, minC2idx, bv11, minC2idx);
			}
			else if (! upd1 && upd11)
			{
				cout << "(" << bv1 << ", " << bv2 << ") & (" << bv11 << ", " << bv2 << ") upd11\n";
//fprintf(fPtrr, "(%d, %d) & (%d, %d)\n", bv1, bv2, bv11, bv2);
			}
//fclose(fPtrr);
#endif
			refillSafeCorresp(mesh2, 1); //reshape safeCorresp to involve new pair after update above; many-to-1's may appear in safeCorrep now since v.matchIdx can be the same for 2+ mesh1 v's
			setCorrespondenceErrs(mesh2, r, -1); //matchCostI changes for this new safeCorresp list
		}
		//////////// dynamic safeCorresp ends ///////////////
#endif
	} //end of i
	
	//fill safeCorresp which will be used to set the vertex-based and average correspondence errors
	refillSafeCorresp(mesh2);
	setCorrespondenceErrs(mesh2, r, 3); //matchCostI is updated (for next call's if (vertexBasedIso < verts[bv11]->matchCostI) test that decides upd11=true); besides, current overall cost is learnt (to decide to do the next call)

	cout << "# of updates performed: " << nUpd << "/" << n1 << "; " << nTie << " of them from tie; iter # " << iter << "\nDone!\n\n";
//assert(baseVerts.size() == mesh2->baseVerts.size()); //invalid when 1-to-many allowed
}

void Mesh::isometryBasedGraphMatching(Mesh* mesh2, float radius, int* vIdx2, int iter, int k)
{
	//runs an Hungarian/blossom w.r.t. isometry costs of matching i to j below (for that computation, i need a traversal list which is simply the output of greedyForBaseUpdate())

	cout << "\npairs refined by isometry-cost Blossom V [4]....\niter # " << iter << endl;

	//adapted from Hungarian block of matchBySpectralBases()
	int n1 = (int) baseVerts.size(), n2 = (int) mesh2->baseVerts.size(), scSize = (int) safeCorresp.size() / 2;
	int nRows = (n1 > n2 ? n1 : n2), nCols = nRows; //square mtrx
	float** costs = new float*[nRows];
	float w1 = 0.0f, w2 = 1.0f, minCost = INF, maxCost = -INF, discourage = 1.0f; //discourage value'll be the larget cost value in costs[][] for the Hungarian code i use; no, indeed let all elements in costs[][] be in [0,1] and make discourage=1 safely
#ifndef HUNGARIAN_INSTEAD_OF_BLOSSOM //if not defined; so blossom in use
	discourage = INF; //1.0f; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; i normalize costs[][] into [0,1] so we're good w/ discourage=1.0f [sucks w/ 1 for ballerina100015-4; logically, it must be very big anyway; i guess this = 1 thing was from old hungarian code requirement]
#endif

#ifdef PROBABILISTIC_COST
	float* rowSum = new float[n1]; //rowSum[i] = sum of probability entries in i'th row
	for (int r = 0; r < n1; r++)
		rowSum[r] = 0.0f;
#endif
	//fill costs[][]
	for (int r = 0; r < nRows; r++)
	{
		costs[r] = new float[nCols];
		for (int c = 0; c < nCols; c++)
		{
			float cost = discourage;
			if (r < n1 && c < n2)
			{
				//baseVerts[r] vs. mesh2->baseVerts[c] active assumption (copied from greedyForBaseUpdate().`if (votesSize > 0 && c2 != bv2)' block)
				float vertexBasedIso = 0.0f; //for baseVerts[r] (under the assumption that it is paired w/ mesh2->baseVerts[c])
				for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
				{
					int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
					if (s1 == baseVerts[r])
						continue;
					float diffForNormalized = verts[ baseVerts[r] ]->dSpanning[s1] - mesh2->verts[ mesh2->baseVerts[c] ]->dSpanning[s2]; //baseVerts[r],mesh2->baseVerts[c]
					vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if baseVerts[r] vs. mesh2->baseVerts[c] is a good correspondence
				}
				vertexBasedIso /= (scSize-1);
				cost = vertexBasedIso;
				//i above use v_iso(bv1 | bv2), that is, bv1.matchCostI given (bv1, bv2) is a correspondence; if bv1-bv2 is a good pair, then this difference is small

				if (vertexBasedIso > maxCost)
					maxCost = vertexBasedIso;
				if (vertexBasedIso < minCost)
					minCost = vertexBasedIso;
#ifdef PROBABILISTIC_COST
				costs[r][c] = 1.0f - vertexBasedIso;
				rowSum[r] += costs[r][c]; //sum of probabilities (1 - isoCost) must be 1 to obey pdf; use this row-normalizer to do that nomralization below
#endif
			}
#ifndef PROBABILISTIC_COST //not defined; so, use vertexBasedIso (or discourage) coming from above
			costs[r][c] = cost;
#endif
		} //end of c
	} //end of r
#ifdef PROBABILISTIC_COST
	//obey pdf effect (by making rowSum = 1, i.e. by dividing each entries of i'th row by rowSum[i]; virtuals = discourage stay as is and no problem w/ that)
	for (int r = 0; r < nRows; r++)
		for (int c = 0; c < nCols; c++)
			if (r < n1 && c < n2) //ignore virtuals
				costs[r][c] /= rowSum[r]; //make sum of probs in each row 1 (excluding virtuals, i.e. consider them as 0 probs so that matching a vert to virtual has 0 probability)
	delete [] rowSum;
	//take the log of probs for EM framework; since hungarian/blossom sucks on -ve edges, take the
	//abs of log; this also helps for min-weight perfect matching, i.e. smaller costs map to smaller prob entries and hence min-weight can be done; example given in setCorrespondenceErrs() for this comment
	minCost = INF;
	maxCost = -INF; //min/maxCost set above was due to isometryCost entries; reset them using abs-log'ed prob entries
	for (int r = 0; r < nRows; r++)
		for (int c = 0; c < nCols; c++)
			if (r < n1 && c < n2)
			{
				costs[r][c] = (costs[r][c] != 0.0f ? fabs( log10(costs[r][c]) ) : discourage); //log10(0) must be prevented 'cos it's undefined; log10(0) happens when isometryCost is 1 (= bad), so, corresponding entry must be discourage to prevent its selection
				//prob in [0,1] but when abs-log taken, we have >1 values, e.g. abs( log(0.01) ) = 2; so, normalization needed
				if (costs[r][c] > maxCost && costs[r][c] != discourage)
					maxCost = costs[r][c];
				if (costs[r][c] < minCost)
					minCost = costs[r][c];
			}
#endif
	//normalize costs[][], which currently holds either vertexBasedIsometryCosts or abs-log'ed probabilities (where prob = 1 -cost), into [0,1]
	for (int r = 0; r < nRows; r++)
		if (r < n1) //remaining virtual rows [n1..nRows] are already set to be discourage value (1) above so no normalization there
			for (int c = 0; c < nCols; c++)
				if (c < n2) //remaining virtual columns [n2..nCols] are already set to be discourage value (1) so no normalization there
					costs[r][c] = (costs[r][c] - minCost) / (maxCost - minCost);

	//incorporate L2 dists in LMDS space to the costs[][] which currently holds [0,1]-normalized vertexBasedIsometryCosts
	if (w1 != 0.0f) //don't do below in vain if weight for L2 dist is 0
		for (int r = 0; r < nRows; r++)
			for (int c = 0; c < nCols; c++)
				if (r < n1 && c < n2)
				{
					float dist;
					dist = (distanceBetweenKD(verts[ baseVerts[r] ]->spectralK, mesh2->verts[ mesh2->baseVerts[c] ]->spectralK, k) - minDist) / (maxDist - minDist); //(sample - min) / range to map it to [0,1] interval
assert(dist >= 0.0f && dist <= 1.0f);
					costs[r][c] = w1*dist + w2*costs[r][c]; //costs[r][c] currently holds pure vertexBasedIsometryCosts in [0,1]
				}

#ifdef HUNGARIAN_INSTEAD_OF_BLOSSOM //n^3 matching
//FILE*fPtr6=fopen("ucosts2.dat", "w");for (int x = 0; x < nRows; x++){for (int y = 0; y < nRows; y++)fprintf(fPtr6, "%f\t", costs[x][y]);fprintf(fPtr6, "\n");}fclose(fPtr6);
	//Hungarian assignment using costs[][] mtrx
	Hungarian* h = new Hungarian(nRows, nCols);
	h->solve(costs);
	delete h;
//FILE*fPtr7=fopen("ucosts2res.dat", "w");for (int x = 0; x < nRows; x++){for (int y = 0; y < nRows; y++)fprintf(fPtr7, "%f\t", costs[x][y]);fprintf(fPtr7, "\n");}fclose(fPtr7);

	//set matches according to costs[][] which is now a permutation mtrx prepared by h.solve() above
	for (int i = 0; i < n1; i++)
		verts[ baseVerts[i] ]->matchIdx = -1; //to be updated below if it has a nonvirtual match
	for (int i = 0; i < n2; i++)
		mesh2->verts[ mesh2->baseVerts[i] ]->matchIdx = -1; //to be updated below if it has a nonvirtual match
	for (int r = 0; r < nRows; r++)
		for (int c = 0; c < nCols; c++)
			if (r < n1 && costs[r][c] == 0) //0 means that column c is selected for this row r
			{
				verts[ baseVerts[r] ]->matchIdx = (c < n2 ? vIdx2[c] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1
				if (verts[ baseVerts[r] ]->matchIdx != -1)
				{
assert(mesh2->verts[ vIdx2[c] ]->base); //baseVerts[r].matchIdx must be a base
					mesh2->verts[ vIdx2[c] ]->matchIdx = baseVerts[r];
					mesh2->verts[ vIdx2[c] ]->nMatches = 1; //1-to-1 mapping of Hungarian guarantees 1 match per m2.v
				}
			}
for (int i = 0; i < nRows; i++) delete [] costs[i]; delete [] costs; //memo re-capture
#else //same result as hungarian but this is n^2lgn blossom matching, amortized cost should even be better 'cos way faster than hungarian
	//////////// blossom input ///////////////
	int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e = 0, * edges = new int[2*nEdgeNum];
//	cout << nVertNum << " verts (half at mesh1 half at mesh2) to be matched by checking " << nEdgeNum << " edges\n";
	double* weightsD = new double[nEdgeNum];
	for (int i = 0; i < nRows; i++)
		for (int j = 0; j < nCols; j++)
		{
			edges[2*e] = i;
			edges[2*e+1] = nRows+j;
			weightsD[e++] = (double) costs[i][j];
		}
	//////////// blossom input ends ///////////////

	Blossom* blossom = new Blossom();
	int* matches = new int[nVertNum]; //see this as lines of matches where 2 consecutive items make a matching line, e.g. 369 694
	blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches); //matches update by call-by-ref
	delete blossom;
	delete [] edges;
	delete [] weightsD;

	//transform data in matches[] to matchIdx values consistently
	for (int m = 0; m < nRows; m++)
	{
		//assume nRows = 693 (693x2 verts in total, including virtuals) and n1>n2, then matches[] lines:
		//0 856 \n 1 925  \n .. 323 693 \n .. 691 1198 \n 692 1380; then means m1.v[baseVerts[0]] matches w/ m2.v[vIdx2[856-693]]
		int m1 = matches[2*m]; //left side of the (m+1)'th matching line
		if (m1 < n1)
		{
			int m2 = matches[2*m + 1] - nRows; //mathches[m1 + 1], right side of the line, is in [nRows..2*nRows-1], hence nRows entry at mesh2
			verts[ baseVerts[m1] ]->matchIdx = (m2 < n2 ? vIdx2[m2] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1
			if (verts[ baseVerts[m1] ]->matchIdx != -1)
			{
assert(mesh2->verts[ vIdx2[m2] ]->base); //baseVerts[m1].matchIdx must be a base
				mesh2->verts[ vIdx2[m2] ]->matchIdx = baseVerts[m1];
				mesh2->verts[ vIdx2[m2] ]->nMatches = 1; //1-to-1 mapping of blossom guarantees 1 match per m2.v
			}
		}
	} //end of m
	delete [] matches;
#endif

	//fill safeCorresp which will be used to set the vertex-based and average correspondence errors
	refillSafeCorresp(mesh2, 1);
	setCorrespondenceErrs(mesh2, radius, 4);

	//hungarian or blossom may rarely leave an outlier match that is 5+ times worse than the average isometryCost; so, just roll-back such matches to originals, hence making 1-to-many possible even here; no!!, simply make their matchIdx=-1 to keep 1-to-1 alive
	bool outlierDetected = false;
	for (int i = 0; i < (int) baseVerts.size(); i++)
		if (verts[ baseVerts[i] ]->matchIdx != -1 && verts[ baseVerts[i] ]->matchCostI > 2.5f*isometryCost) //1st: some outliers are matched up w/ virtuals and hence no match at all; for the others, if any (hopefully none), outlierDetected becomes true
		{
cout << "outlierrrrrrrrrrr: " << baseVerts[i] << " -> " << verts[ baseVerts[i] ]->matchIdx << " w/ matchCostI = " << verts[ baseVerts[i] ]->matchCostI << ", avg isometryCost = " << isometryCost << "\n\n\n\n";
			verts[ baseVerts[i] ]->matchIdx = mesh2->verts[ verts[ baseVerts[i] ]->matchIdx ]->matchIdx = -1;
			outlierDetected = true;
		}
	if (outlierDetected)
	{
		refillSafeCorresp(mesh2, 1);
		setCorrespondenceErrs(mesh2, radius, 4);
	}

	cout << "Done!\n\n";
}

int baseCounter = 0; //print number of base in process
#define USE_BEST_MATCH_AMONGST_TOUCHING_PATCHES //if defined, vertex v is tested for all touching patches and the one w/ closest L2 dist (or isometryCost) wins to be used for matchIdx value; hence, no nonintuitive thresholding
void Mesh::pairUpPatches(Mesh* mesh2)
{
	//pairs up vertices in corresponding patches via interpolation; patches already aligned by preceding denseMatching() call




//notInUseeeeeeeeeeee: file paths not fixed!!!!!!!!!!!!

	cout << "pairing up corresponding patches....\n";

	//send each patch w/ its corresponding patch to other pairUpPatchesBy*() to match non-bases in the patches; start from the good patches and not process the already matched non-bases ('cos good patch already matched it before)
	char fName[150];
	if (theInput == JUMPING)
		sprintf(fName, "output\\dense\\jumping\\%d-%d %s.dat", frameID, mesh2->frameID, (lessBases ? "lessBases" : "moreBases"));
	else
		sprintf(fName, "output\\dense\\%s\\%d-%d %s.dat", (theInput != DANCING ? "hybrid" : "dancing"), frameID, mesh2->frameID, (lessBases ? "lessBases" : "moreBases"));
	FILE* fPtr = fopen(fName, "w"); //ultimate dense correspondence goes here
	//for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
	for (int s = 0; 2*s+1 < (int) safeCorrespAll.size(); s++) //when top10 in use, i still want to process all matches, which are in safeCorrespAll; isometryCosts for costs[][]'ll be based on top10 however
	{
		int bv1 = safeCorrespAll[2*s], bv2 = safeCorrespAll[2*s + 1]; //sorted correspondences in use

//if (bv1 != 4029 && bv1 != 4471) continue;//delLaterrrrrrrrrrrrrrrrrrrrrrrrrrrrr
//if (bv1 != 4471) continue;//delLaterrrrrrrrrrrrrrrrrrrrrrrrrrrrr

		if (denseMatchByGraphMatching)	//slow but more accurate that uses ~n^2lgn min-weight perfect matching
			//pairUpPatchesByGraphMatching(mesh2, fPtr, bv1, bv2);
			pairUpPatchesByGraphMatching2(mesh2, bv1, bv2);
		else						//fast but less accurate that uses linear time interpolation
			pairUpPatchesByInterpolation(mesh2, fPtr, bv1, bv2);
	} //end of s*/

#ifdef USE_BEST_MATCH_AMONGST_TOUCHING_PATCHES
	int force1to1 = 2; //1-to-many is natural to maintain the bests but 1-to-1 may be forced too (0: no force1to1 at all (do nothing, stay 1-to-many), 1 or 2 (better) forces 1-to-1)
	make1to1(mesh2, fPtr, force1to1);
	if (force1to1 != 0) //existing file have invalid 1-to-many entries that i made -1 in code but not in file; so, rewrite a clean file free of -1s and 1-to-manys
	{
		fclose(fPtr);
		fPtr = fopen(fName, "w");
		for (int v = 0; v < (int) verts.size(); v++)
			if (verts[v]->matchIdx != -1)
				fprintf(fPtr, "%d\t%d\n", v, verts[v]->matchIdx);
else if (verts[v]->base) fprintf(fPtr, "%d\t%d\n", v, verts[v]->matchIdxCoarse); //matchIdx = -1 and base; so, use the coarse stage match in the expense of a potential 1-to-many situation
		fclose(fPtr);
	}
#endif

	if (theInput == JUMPING)
		sprintf(fName, "output\\dense\\jumping\\%d-%dbaseAfterDense %s.dat", frameID, mesh2->frameID, (lessBases ? "lessBases" : "moreBases"));
	else
		sprintf(fName, "output\\dense\\%s\\%d-%dbaseAfterDense %s.dat", (theInput != DANCING ? "hybrid" : "dancing"), frameID, mesh2->frameID, (lessBases ? "lessBases" : "moreBases"));
	FILE* fPtr2 = fopen(fName, "w"); //ultimate correspondences of bases; bases already had coarse correspondences and they probably changed at this time; so, make a comparison later w/ this file
	fprintf(fPtr2, "%d\t\t%f\t%f\n", (int) baseVerts.size(), r1, r2); //close format as i saved Zhang's correspondences to use this file later in computeCo..Error()
	for (int bv = 0; bv < (int) baseVerts.size(); bv++)
		//last 2 params to are the costs coming from coarse corresp stage to compare them w/ the costs after dense corresp; i don't compute costs after dense here for simplicity, and will do it in computeCor..Error() of a next execution
		//fprintf(fPtr2, "%d\t%d\t%f\t%f\n", baseVerts[bv], verts[ baseVerts[bv] ]->matchIdx, verts[ baseVerts[bv] ]->matchCostI, verts[ baseVerts[bv] ]->matchCostG); //matchCostI/G values are what i loaded during correspFromFile() and hence for coarse stage
//replace 2nd %d's param just-above w/ (verts[ baseVerts[bv] ]->matchIdx == -1 ? verts[ baseVerts[bv] ]->matchIdxCoarse : verts[ baseVerts[bv] ]->matchIdx) to get rid of -1's in the expense of potential 1-to-many's, like this:
fprintf(fPtr2, "%d\t%d\t%f\t%f\n", baseVerts[bv], (verts[ baseVerts[bv] ]->matchIdx == -1 ? verts[ baseVerts[bv] ]->matchIdxCoarse : verts[ baseVerts[bv] ]->matchIdx), verts[ baseVerts[bv] ]->matchCostI, verts[ baseVerts[bv] ]->matchCostG);
	fclose(fPtr2);

	cout << "avg patch size: " << totalPatchSize / (int) baseVerts.size() << " vs. " << mesh2->totalPatchSize / (int) mesh2->baseVerts.size() << "\nDone!\n\n";
//*/

	//draw the desired matches only, preferably corresponding to segments of body parts like sign finger
	for (int i = 0; i < 27; i++)verts[4437+i]->drawMatchLine=true;for (int i = 0; i < 10; i++)verts[4470+i]->drawMatchLine=true;for (int i = 0; i < 4; i++)verts[4603+i]->drawMatchLine=true;//10004 right.sign finger
	//for (int i = 0; i < 6; i++)verts[4607+i]->drawMatchLine=true;for (int i = 0; i < 6; i++)verts[4623+i]->drawMatchLine=true;for (int i = 0; i < 6; i++)verts[4464+i]->drawMatchLine=true;for (int i = 0; i < 15; i++)verts[4374+i]->drawMatchLine=true;//10004 right.pinky finger
	//for (int i = 0; i < 25; i++)verts[4389+i]->drawMatchLine=true;for (int i = 0; i < 10; i++)verts[4495+i]->drawMatchLine=true;for (int i = 0; i < 1; i++)verts[4520+i]->drawMatchLine=true;for (int i = 0; i < 5; i++)verts[4565+i]->drawMatchLine=true;//10004 right.ring finger
	//for (int i = 0; i < 18; i++)verts[4419+i]->drawMatchLine=true;for (int i = 0; i < 15; i++)if(i!=6)verts[4480+i]->drawMatchLine=true;for (int i = 0; i < 11; i++)verts[4573+i]->drawMatchLine=true;for (int i = 0; i < 4; i++)verts[4617+i]->drawMatchLine=true;//10004 right.middle finger
	//for (int i = 0; i < 11; i++)verts[4509+i]->drawMatchLine=true;for (int i = 0; i < 11; i++)verts[4528+i]->drawMatchLine=true;for (int i = 0; i < 21; i++)verts[4544+i]->drawMatchLine=true;//10004 right.thumb finger
}

void Mesh::pairUpPatchesByGraphMatching2(Mesh* mesh2, int bv1, int bv2)
{
	//only difference from pairUpPatchesByGraphMatching2() is, this one uses tmpSafeCorresp list to create costs[][] for blossom whereas that one used top10 matches of existing safeCorresp list from coarse stage

	cout << baseCounter++ << ") pairing up patches of " << bv1 << " & " << bv2 << " w/ a patch-based safe list....\n";

	//a traversalList will be traversed by tmpBases to create a tmpBase matching during matchTmpBasesByGraph()
	bool useTopnForTraversalList = false; //traversalList creation choice for tmpBase matching; by top-n elements or by trying all combinations of 5-tuple matches and picking the best

	//i first need 10 tmpBases within patch1 sampled using our COES tactic; result written to global tmpBaseVerts
	bool discardBV = true; //first bases argumented here (bv1/2) are not made tmpBase since their match bv1-bv2 may be faulty from coarse stage and i may reselect bv1-bv2 in matchTmpBasesByGraph() if both allowed to be tmpBases
	if (! useTopnForTraversalList)
		discardBV = false; //if combinations will be used for traversalList creation (= matchTmpBasesC10_5()), then discardBV must be false, which is the natural case anyway
	computeTmpBaseVerts(r1/3.0f, bv1, discardBV);
	mesh2->computeTmpBaseVerts(r2/3.0f, bv2, discardBV);

	//get the safe correspondences b/w tmpBases of given patches; result written to global tmpSafeCorresp
	matchTmpBasesByGraph(mesh2, bv1, bv2, useTopnForTraversalList);
	
	/////////// ultimate patch-correspondence via tmpSafeCorresp //////////////
	//use tmpSafeCorresp, and not safeCorresps, to decide the isometryCost based costs[][] for blossom [heavily adapted from pairUpPatchesByGraphMatching()]
	if (verts[bv1]->baseNghbhood)
		delete verts[bv1]->baseNghbhood; //not setting to NULL 'cos will become non-NULL just-below
	verts[bv1]->baseNghbhood = new vector<int>;
	verts[bv1]->baseNghbhood->push_back(bv1);
	int tmp = (*verts[bv1]->baseNghbhood)[0]; //to swap bv1/ w/ the first entry [0] so that it is processed first (not so crucial)
	(*verts[bv1]->baseNghbhood)[0] = bv1;			(*verts[bv1]->baseNghbhood)[(int) verts[bv1]->baseNghbhood->size()-1] = tmp;				//swapped
	mesh2->verts[bv2]->baseNghbhood->push_back(bv2);
	int tmp2 = (*mesh2->verts[bv2]->baseNghbhood)[0];
	(*mesh2->verts[bv2]->baseNghbhood)[0] = bv2;	(*mesh2->verts[bv2]->baseNghbhood)[(int) mesh2->verts[bv2]->baseNghbhood->size()-1] = tmp2; //swapped
	totalPatchSize += (int) verts[bv1]->baseNghbhood->size();
	mesh2->totalPatchSize += (int) mesh2->verts[bv2]->baseNghbhood->size();
	verts[bv1]->matchIdx = mesh2->verts[bv2]->matchIdx = -1; //they'll find their matches below, probably the same bv1-bv2 match
	//Hungarian cost matrix adapted from isometryBasedGraphMatching()
	int n1 = (int) verts[bv1]->baseNghbhood->size(), n2 = (int) mesh2->verts[bv2]->baseNghbhood->size();
	for (int nb1 = 0; nb1 < (int) verts[bv1]->baseNghbhood->size(); nb1++)
		if (verts[ (*verts[bv1]->baseNghbhood)[nb1] ]->matchIdx != -1)	//previously matched; so, no need to look for a match for this one
			n1--; //exclude matched verts of this patch
	for (int nb2 = 0; nb2 < (int) mesh2->verts[bv2]->baseNghbhood->size(); nb2++)
		if (mesh2->verts[ (*mesh2->verts[bv2]->baseNghbhood)[nb2] ]->matchIdx != -1)
			n2--;
	int nRows = (n1 > n2 ? n1 : n2), nCols = nRows; //square mtrx
	int* vIdx1 = new int[n1], * vIdx2 = new int[n2]; //if r,c = 3,7 is a match in costs[r][c], i.e. [3][7]=0, then verts[ vIdx1[3] ].matchIdx = vIdx2[7], hence vIdx1/2[*] is an idx to verts
	float** costs = new float*[nRows];
	float discourage = INF; //1.0f; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; i normalize costs[][] into [0,1] so we're good w/ discourage=1.0f [sucks w/ 1 for ballerina100015-4; logically, it must be very big anyway; i guess this 1 thing was from old hungarian code requirement]
	int row = 0, scSize = (int) tmpSafeCorresp->size() / 2;
	for (int nb1 = 0; nb1 < (int) verts[bv1]->baseNghbhood->size(); nb1++)
	{
		int v1 = (*verts[bv1]->baseNghbhood)[nb1];		//baseNghbhood[] guaranteed to be duplicate-free after correspFromFile()
		if (verts[v1]->matchIdx != -1)				//previously matched; so, no need to look for a match for this one
			continue; //replace here w/ for header above for sorted/nonarbitrary processing

		//[i][*] entry of cost, i.e. cost of matching patch1Verts[i] w/ patch2Verts[*] where * runs from 0 to # of patch2Verts (so patch2Size+1 column, +1 for bv2 itself)
		costs[row] = new float[nCols]; //nCols = nRows anyway
		vIdx1[row] = v1; //row = 5, v1 = 68 means that row5 (6th row) is about vert68
		int col = 0;
		for (int nb2 = 0; nb2 < (int) mesh2->verts[bv2]->baseNghbhood->size(); nb2++)
		{
			int v2 = (*mesh2->verts[bv2]->baseNghbhood)[nb2];	//baseNghbhood[] guaranteed to be duplicate-free after correspFromFile()
			if (mesh2->verts[v2]->matchIdx != -1)			//previously matched; so, no need to look for a match for this one
				continue;

			vIdx2[col] = v2; //col = 6, v2 = 69 means that column6 is about vert69

			//vIdx1[row] vs. vIdx2[col] active assumption (copied from greedyForBaseUpdate().`if (votesSize > 0 && c2 != bv2)' block)
			float vertexBasedIso = 0.0f; //for vIdx1[r] (under the assumption that it is paired w/ vIdx2[c])
			for (int ss = 0; 2*ss+1 < (int) tmpSafeCorresp->size(); ss++)
			{
				int s1 = (*tmpSafeCorresp)[2*ss], s2 = (*tmpSafeCorresp)[2*ss + 1];
//if (row==0 && col==0)cout << s1 << " _______________ " << s2 << endl;
				if (s1 == vIdx1[row]) //must ignore s1==vIdx1[r] as explained in greedyForBaseUpdate()'s bv1 vs. c2c active assumption part
					continue;

				float diffForNormalized = //verts[ vIdx1[r] ]->dSpanning[s1] - mesh2->verts[ vIdx2[c] ]->dSpanning[s2]; //baseVerts[r],mesh2->baseVerts[c]
											verts[s1]->dSpanning[ vIdx1[row] ] - mesh2->verts[s2]->dSpanning[ vIdx2[col] ]; //vIdx1/2 is not necessarily a base and hence dSpanning not always inited; s1/2 are always bases
				vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if baseVerts[r] vs. mesh2->baseVerts[c] is a good correspondence
			}
assert(scSize > 1); //no div by zero just-below
			vertexBasedIso /= (scSize-1); //scSize # of time += fabs done above each of which in [0,1]; so, after /=, vertexBasedIso in [0,1] for sure
			costs[row][col++] = vertexBasedIso;
		} //end of nb2
		while (col < nCols) //fill the remaining virtual columns w/ infinitive costs
			costs[row][col++] = discourage;
		row++;
	} //end of nb1
	while (row < nRows) //fill the remaining virtual rows w/ infinitive costs
	{
		costs[row] = new float[nCols]; //nRows = nCols
		for (int col = 0; col < nCols; col++)
			costs[row][col] = discourage;
		row++;
	}

	//////////// blossom input ///////////////
	int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e = 0, * edges = new int[2*nEdgeNum];
	double* weightsD = new double[nEdgeNum];
	for (int i = 0; i < nRows; i++)
		for (int j = 0; j < nCols; j++)
		{
			edges[2*e] = i;
			edges[2*e+1] = nRows+j;
			weightsD[e++] = (double) costs[i][j];
		}
	//////////// blossom input ends ///////////////

	Blossom* blossom = new Blossom();
	int* matches = new int[nVertNum]; //see this as lines of matches where 2 consecutive items make a matching line, e.g. 369 694
	blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches); //matches update by call-by-ref
	delete blossom;
	delete [] edges;
	delete [] weightsD;

	//transform data in matches[] to matchIdx values consistently
	for (int m = 0; m < nRows; m++)
	{
		//assume nRows = 693 (693x2 verts in total, including virtuals) and n1>n2, then matches[] lines:
		//0 856 \n 1 925  \n .. 323 693 \n .. 691 1198 \n 692 1380; then means m1.v[vIdx1[0]] matches w/ m2.v[vIdx2[856-693]]
		int m1 = matches[2*m]; //left side of the (m+1)'th matching line
		if (m1 < n1)
		{
			int m2 = matches[2*m + 1] - nRows; //mathches[m1 + 1], right side of the line, is in [nRows..2*nRows-1], hence nRows entry at mesh2
#ifdef USE_BEST_MATCH_AMONGST_TOUCHING_PATCHES
			if (m2 < n2)
			{
				//since no matchIdx set here, upcoming touching patches will process m1 and m2 again and the best will be decided in the end
				verts[ vIdx1[m1] ]->matchIdxs.push_back((float) vIdx2[m2]);
				verts[ vIdx1[m1] ]->matchIdxs.push_back(costs[m1][m2]); //cost of this match; if this turns out to be the best cost, then verts[ vIdx1[m1] ]->matchIdx = vIdx2[m2]'ll be done in make1to1()
				mesh2->verts[ vIdx2[m2] ]->matchIdxs.push_back((float) vIdx1[m1]); //make1to1() works on mesh2.matchIdxs too for force1to1=2 case
				mesh2->verts[ vIdx2[m2] ]->matchIdxs.push_back(costs[m1][m2]);
			}

			//base needs to know its matchIdx at all times 'cos upcoming iters may want to reach it through baseNghbs[]; i certainly set bv1.matchIdx to -1 at top, so heal it
			if (vIdx1[m1] == bv1) //same as if (verts[ vIdx1[m1] ]->base)
				verts[ vIdx1[m1] ]->matchIdx = (m2 < n2 ? vIdx2[m2] : -1); //nothing needed for mesh2.matchIdx
#else
			int mi = (m2 < n2 ? vIdx2[m2] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1
			if (mi != -1 )// && costs[m1][m2] <= 0.4f) //verts w/ LMDS dists > threshold are not matched when this patch is active; hopefully, upcoming patches will make a better match
						 //	&& costsUnn[m1][m2] <= avgMinDistsFrom3) //avg of minDist values used in a match computed by pair..ion3() in 3 secs
			{
				verts[ vIdx1[m1] ]->matchIdx = mi;
				fprintf(fPtrU, "%d\t%d\n", vIdx1[m1], vIdx2[m2]);
//fprintf(fPtrU, "%f\n", costsUnn[m1][m2]); //make \t -> \n in fprint just-above
				mesh2->verts[ vIdx2[m2] ]->matchIdx = vIdx1[m1];
				nCorresps++;
				mesh2->nCorresps++;
			}
#endif
		}
	} //end of m
	delete [] matches;

	//roll-back to exmatches 'cos due to patchSize differences bv1 couldn't find chance to find a match for itself (matched w/ a virtual vertex) and i need matches of bases for next iters for interpolation
#ifdef USE_BEST_MATCH_AMONGST_TOUCHING_PATCHES
	//base needs to know its matchIdx at all times 'cos upcoming iters may want to reach it through baseNghbs[]; i certainly set bv1.matchIdx to -1 at top, so heal it; indeed it's healed above but not for bv2; so, do it here anyway for safety and bv2's assertion in piar..ion2_1()
	verts[bv1]->matchIdx = bv2;
	mesh2->verts[bv2]->matchIdx = bv1;
#else
	if (verts[bv1]->matchIdx == -1)
	{
		verts[bv1]->matchIdx = bv2;
//assert(mesh2->verts[bv2]->matchIdx != -1); //the reason bv1 left unmatched is patch1Size > patch2Size and therefore some verts in patch1 left unmatched; but all verts in patch2 matched; so, bv2 must have a match now; no!! after threshold-based canceling above, this assert not good anymore
		fprintf(fPtrU, "%d\t%d\n", bv1, bv2); //75\t46\n fprinted above; now, 48\t46\n fprinted (bv1=48, bv2=46); so, while freading, 48-46 overwrites to 75-46 which is good; no!! 48.matchIdx becomes 46 but 75.matchIdx also stays 46; make it -1 w/ a 2nd fprint
		if (mesh2->verts[bv2]->matchIdx != -1) //if the reason bv1 left unmatched is patch1Size > patch2Size (and not the thresholding), some verts in patch1 left unmatched; but all verts in patch2 matched; so, bv2 must have a match now that needs to be canceled
		{
			fprintf(fPtrU, "%d\t%d\n", mesh2->verts[bv2]->matchIdx, -1);
			verts[ mesh2->verts[bv2]->matchIdx ]->matchIdx = -1; //the patch1 vert coupling w/ bv2 no longer couples 'cos bv2 has its old partner (bv1) from now on
		}
		mesh2->verts[bv2]->matchIdx = bv1;
		//mesh2->nCorresps++; not done 'cos mesh2 lost a correspondence just-above; similarly, mesh1 just replaced a correspondence so no nCorresps++
	}
//note that, bv2 may remain bv2.matchIdx = -1 (unmatched) if patch2Size > patch1Size but not doing anything here 'cos bv2.matchIdx's not used; maby, i should have canceled base property of bv2 and made the bv1.newMatch a base vert; but base info not so crucial here so skipping
#endif
	/////////// ultimate patch-correspondence via tmpSafeCorresp ends //////////////

	//memo re-capture
	for (int i = 0; i < nRows; i++) delete [] costs[i];		delete [] costs;
	delete [] vIdx1;
	delete [] vIdx2;

	cout << "done!\n";
}

void Mesh::computeTmpBaseVerts(float r, int bv, bool discardBV)
{
	//copy of of computeBaseVerts() except this doesn't touch base properties, but works on tmpBase attributes

	cout << bv << ".patch's tmp bases being computed (r' = " << r << ")....\n";

	//start w/ fresh globals not to get effected by previous patch processing
	tmpBaseVerts.clear();
	//keep the search within the patch, not the whole mesh, using th
	for (int v = 0; v < (int) verts.size(); v++)
		verts[v]->tmpBaseCandidate = false;
	for (int nb1 = 0; nb1 < (int) verts[bv]->baseNghbhood->size(); nb1++)
	{
		verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseCandidate = true;
		//verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood.clear(); //due to overlaps or levels of coarseToFineMatching(), some verts in bv.tmpBaseNghbhood (= bv's patch) may already fill their tmpBaseNghbhood before, which needs cleaning now
		if (verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood)
			delete verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood;
		verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood = new vector<int>; //clear effect in memory efficient way
	}
	//verts[bv]->tmpBaseNghbhood.clear(); //not only bv's patch verts, but also bv itself might filled its tmpBaseNghbhood before due to coarseToFineMatching(), not due to overlap 'cos nonbases overlap only
	if (verts[bv]->tmpBaseNghbhood)
		delete verts[bv]->tmpBaseNghbhood;
	verts[bv]->tmpBaseNghbhood = new vector<int>; //clear effect in memory efficient way

	int baseVertIdx = bv; //firstBaseVert is initial base itself, which will be erased below 'cos i don't want bv in tmpBases
	vector< int >* vlist = new vector<int>; //first a baseVert enters this list, and then verts in the area/patch of that baseVert
	while (baseVertIdx != -1) //there is still a baseVert whose area to be expanded below
	{
		/////// shortcut Dijkstra w/ slow arrays to create 1 tmpBaseVert ///////
		//step 1: initialization
		for (int v = 0; v < (int) verts.size(); v++)
			verts[v]->d = INF;

		//step 2: select a tmp base vert
		verts[baseVertIdx]->d = 0.0f; //so that this baseVert is initially selected from vlist in the loop below
		vlist->push_back(baseVertIdx);			//used below
		tmpBaseVerts.push_back(baseVertIdx);	//used later; whole aim is to fill this vector indeed
		verts[baseVertIdx]->tmpBaseCandidate = false;
		while (! vlist->empty())
		{
			//step 3: extract vert w/ min d value
			float minD = INF;
			int minDidx, v;
			for (int i = 0; i < (int) vlist->size(); i++)
				if (verts[ (*vlist)[i] ]->d < minD)
				{
					v = (*vlist)[i];
					minD = verts[v]->d;
					minDidx = i;
				}
			//erase the element w/ min d value
			vlist->erase(vlist->begin() + minDidx); //ve.erase(begin + 4) erases ve[4]

			//step 4: relaxation loop that updates d values and expands baseVert.area as long as d is small enough
			for (unsigned int ve = 0; ve < verts[v]->edgeList.size(); ve++)
			{
				int e = verts[v]->edgeList[ve];
				int va = (edges[e]->v1i == v ? edges[e]->v2i : edges[e]->v1i);
				if (verts[v]->d + edges[e]->length < verts[va]->d)
				{
					verts[va]->d = verts[v]->d + edges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->tmpBaseCandidate = false;
//no duplicate prevention here as opposed to com..TmpBaseVerts2()
						verts[baseVertIdx]->tmpBaseNghbhood->push_back(va);
					}
				}
			}
			for (unsigned int ve = 0; ve < verts[v]->sedgeList.size(); ve++)
			{
				int e = verts[v]->sedgeList[ve];
				int va = (sedges[e]->v1i == v ? sedges[e]->v2i : sedges[e]->v1i);
				if (verts[v]->d + sedges[e]->length < verts[va]->d)
				{
					verts[va]->d = verts[v]->d + sedges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->tmpBaseCandidate = false;
						verts[baseVertIdx]->tmpBaseNghbhood->push_back(va);
					}
				}
			}
		} //end of vlist.empty()
		/////// shortcut Dijkstra w/ slow arrays to create 1 tmpBaseVert ends ///////
#ifdef DBG_DETAIL
		if (! (discardBV && baseVertIdx == bv))
			cout << "tmp base" << tmpBaseVerts.size()-1 << ": " << baseVertIdx << endl; //bv'll be erased below, so discard this announcement
#endif
		//baseVertIdx = getBaseVertIdx(true); effect but i need to use tmpBaseCandidate; so, just adapt that fnctn below
		baseVertIdx = -1;
		for (int v = 0; v < (int) verts.size(); v++)
			if (verts[gcOrder[v]]->tmpBaseCandidate) //test the next which comes from gcOrder
			{
				baseVertIdx = gcOrder[v];
				break;
			}
	} //end of baseVertIdx

	if (discardBV) //bv is at [0] and i want to get rid of it 'cos it's a coarse stage base, e.g. sign->pinky finger and if i let it in tmpBaseVerts then it may again make that stupid match in matchTmpBasesByGraph()
		tmpBaseVerts.erase(tmpBaseVerts.begin()); //erase(begin + 4) erases [4]

	delete vlist;
	cout << "Done! # of tmp base vertices: " << tmpBaseVerts.size() << "\n\n";
}

int adaptStrategy = 1; //1: C(10,5)'s 5 best matches of the parentPatch; 0: fixed initialCorresp of size 5 (C(10,5) combinations being tried to get this best 5 as well)
float farThrshld = 3.0f; //shortcut dijkstra when farThrshld x far distances start to show up where farThrshld > 1 (3.0 is good 'cos consider 2 pnts a & b on a circle; 1r to
						 //go from a to center and 1r to b, hence dist from a to b; but 3.0 may not be enough when a patch not subdivided and in next level(s) i need to touch everywhere on it w/ decreased r value; also, 3.0
						 //may again stay low during matchInitialTmpBases() if the safeCorrespsAdaptive has a bad match, i/e left leg to right leg, say a to b; then, mesh1.tmpBase[0] vs. mesh2.tmpBase[4] check wants mesh1.tmpBase[0] to a
						 //geodesic and mesh2.tmpBase[4] to b geodesic; since b is wrongly on right leg (recall tmpBase[4] on the left leg), the latter geodesic is too high and farThrshld fails; for such cases do dijkstra again w/ INF farThrshld
void Mesh::computeTmpBaseVerts2(float r, int bv, float rOfPrevLevel, int currentLevel)
{
	//similar to computeBaseVerts(), creates tmpBaseVerts within the patch of bv; but this shares the existing tmpBases around first, and fill the rest w/ old COES tactic from amongst the candidate=true verts

	cout << bv << ".patch's tmp bases being computed (r' = " << r << ")....\n";

	//start w/ fresh globals not to get effected by previous patch processing
	tmpBaseVerts.clear();
	//keep the search within the patch, not the whole mesh, using tmpBaseCandidate; inPatch prevents me picking newcomers from ~borders of this patch; picking close to borders bad 'cos the next bv coming this function may share such a newcomer c, where bv-c dist is ~0, i.e. uniformity sucks
	for (int v = 0; v < (int) verts.size(); v++)
		verts[v]->tmpBaseCandidate = verts[v]->inPatch = verts[v]->closeToBorder = false;
	for (int nb1 = 0; nb1 < (int) verts[bv]->baseNghbhood->size(); nb1++)
	{
//assert((*verts[bv]->baseNghbhood)[nb1] != bv); verified that bv not listed in its baseNghbhood/patch
		verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseCandidate = verts[ (*verts[bv]->baseNghbhood)[nb1] ]->inPatch = true;
		//verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood.clear(); //due to overlaps or levels of coarseToFineMatching(), some verts in bv.tmpBaseNghbhood (= bv's patch) may already fill their tmpBaseNghbhood before, which needs cleaning now
		if (verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood)
			delete verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood;
		verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood = new vector<int>; //clear effect in memory efficient way
	}
	//verts[bv]->tmpBaseNghbhood.clear(); //not only bv's patch verts, but also bv itself might filled its tmpBaseNghbhood before due to coarseToFineMatching(), not due to overlap 'cos nonbases overlap only
	if (verts[bv]->tmpBaseNghbhood)
		delete verts[bv]->tmpBaseNghbhood;
	verts[bv]->tmpBaseNghbhood = new vector<int>; //clear effect that actually clears/deallocs memory

	//dSpanning[] may be undefined so fill it from scratch (if already filled, then it returns immediately anyway)
	//dijkstraShortestPaths(bv); redundant 'cos bv is the generator base, which assures that it has been a tmpBase in the prev level and therefore its dSpanning already computed
	//dijkstraShortestPaths(bv, farThrshld * rOfPrevLevel); can't use this w/ dSpanning[] 'cos in that tactic, once bv.dSpanning[] is computed it is never refilled again; so, if i compute here w/ a shortcut now, then i can't reach far-away verts and when i need to reach them later i've an undefined value

	///////// small difference from computeTmpBaseVerts() /////////////
	//bv itself is also a candidate to be a tmpBase; indeed it must be selected so select it here and make its patch tmpBaseCandidate = false to effect shared tmpBases and normal tmpBases selections below
	verts[bv]->inPatch = true; //tmpBaseCandidate (false currently) not updated 'cos tmpBaseCandidateFalse(bv) makes it false anyway
	tmpBaseVerts.push_back(bv);
	tmpBaseCandidateFalse(bv, r);
	///////// small difference from computeTmpBaseVerts() ends /////////////

	///////// difference from computeTmpBaseVerts() [shared bases] /////////////
	//before starting the patch search of tmpBaseCandidate = true verts as usual, first look around to see if there's existing tmpBaseVerts due to an overlapping patch; if so, then make them tmpBase directly, mark their vicinity (tmpBaseNghbhood) as candid=false

	//adapted from computeBaseNeighbs(); run BFS from bv until first baseVert with ~rOfPrevLevel distance is found (this means we hit a neighboring tmpBaseVert
	//and it's impossible to find a tmpBase to share after that distance); all encountered tmpBaseVerts, i.e. at < rOfPrevLevel distance, are recognized as tmpBaseVert of bv's patch
	int source = bv, v;
	queue< int >* q = new queue<int>;
	q->push(source);
	for (int vv = 0; vv < (int) verts.size(); vv++)
		verts[vv]->d = (vv == source ? 0.0f : INF); //prepare for BFS below; length of unweighted shortest path from source to source is 0
	while (! q->empty())// && ! earlyBreak) //modified BFS
	{
		v = q->front();
		q->pop();
		//look at each adjacent vertices of v
		set< int, std::less< int > >::const_iterator neighbVertIdx;
		for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++)
		{
			if (verts[(*neighbVertIdx)]->d != INF)
				continue;
			verts[(*neighbVertIdx)]->d = verts[v]->d + 1; //update d of this adjacent

			//since source is tmpBase, getGeodesicUnn[] is defined for it for sure; use it to measure geoDist between source and current neighbVertIdx
			if (verts[source]->dSpanningUnn[(*neighbVertIdx)] >= rOfPrevLevel //verts[source]->getGeodesicUnn((*neighbVertIdx)) >= rOfPrevLevel
				|| (verts[(*neighbVertIdx)]->tmpBaseLevel != -1 && verts[(*neighbVertIdx)]->tmpBaseLevel != currentLevel))
				//is the part after || making any sense?????????????? redundant but let it stay, i guess
			{
//				earlyBreak = true; //we hit a distant vert or we hit a tmpBase of a prev level which cannot be used as a tmpBase of this level; so, done for this source
				//indeed the other neighbor(s) of source can be a close vert if edge lengths not uniform; so, consider those verts as well by converting break just-below to a continue and canceling earlyBreak just-above
				
				continue; //break; //continue'll prevent q.push_back below and eventually q'll be empty for sure
			}

			if (verts[(*neighbVertIdx)]->tmpBaseLevel == currentLevel && verts[(*neighbVertIdx)]->tmpBaseCandidate) //an existing tmpBase which has created during this level of outer iteration, so can be shared for this bv as well
			{
				tmpBaseVerts.push_back((*neighbVertIdx));	//used later; whole aim is to fill this vector indeed
				//make this tmpBaseVert and its patch (w.r.t. current r, not rOfPrevLevel) unavailable, candid=false, for the future tmpBaseVert selection below in the same old part
				tmpBaseCandidateFalse((*neighbVertIdx), r); //dijkstra already computed on this shared tmpBase when it was announced as tmpBase in its very first time entrance to this function
			}
			q->push((*neighbVertIdx));
		} //end of neighbVertIdx
	}
	delete q;

#ifdef DBG_DETAIL
	for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
		cout << "tmp base" << i << ": " << tmpBaseVerts[i] << (i == 0 ? "\n" : "\t(shared)\n");
#endif
	///////// difference from computeTmpBaseVerts() [shared bases] ends /////////////*/

	///////// another difference from computeTmpBaseVerts() /////////////
	//for adaptStrategy=1 case, i need correspondences from the related previous level (parent) patch; get them here
	if (adaptStrategy == 1)
	{
		if (adaptList)
			delete adaptList; //memo recapture [adaptList=NULL not done 'cos it'll be non-NULL just-below anyway]
		adaptList = new vector<int>; //clear to a fresh start of size 0 here
		if (currentLevel > 1) //parentPatch is n/a for level1 bases; i use initialCorresp for them by leaving adaptList empty here
		{
			//adapted from shared tmpBases above; difference is i go to as long as twoLevelBack = 2 x rOfPrevLevel since the parent is 2 level back, not one level
			float twoLevelBack = 2 * rOfPrevLevel; //for SUBDIVIDE_AT_ALL_TIMES, this 2 is always correct 'cos parent is always coming from two level back; for alternating that may not subdiv at all times, i may need >2 number here; but not so important anyway
			source = bv;
			q = new queue<int>;
			q->push(source);
			for (int vv = 0; vv < (int) verts.size(); vv++)
				verts[vv]->d = (vv == source ? 0.0f : INF); //prepare for BFS below; length of unweighted shortest path from source to source is 0
			while (! q->empty()) //modified BFS
			{
				v = q->front();
				q->pop();

				//look at each adjacent vertices of v
				set< int, std::less< int > >::const_iterator neighbVertIdx;
				for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++)
				{
					if (verts[(*neighbVertIdx)]->d != INF)
						continue;
					verts[(*neighbVertIdx)]->d = verts[v]->d + 1; //update d of this adjacent
					if (verts[source]->dSpanningUnn[(*neighbVertIdx)] >= twoLevelBack)
						continue; //continue'll prevent q.push_back below and eventually q'll be empty for sure

					if (verts[(*neighbVertIdx)]->matchIdx != -1) //an existing match which has made 2 levels before, a parent match as we can call it
					{
						adaptList->push_back((*neighbVertIdx));
						adaptList->push_back(verts[(*neighbVertIdx)]->matchIdx);
					}
					q->push((*neighbVertIdx));
				} //end of neighbVertIdx
			}
			delete q;
		} //end of currentLevel > 1
	}
	///////// another difference from computeTmpBaseVerts() ends /////////////*/

	//the rest is the similar to old COES tactic from computeTmpBaseVerts() except this not select bases close to borders for uniformity maintenance of c2f strategy
	int baseVertIdx = -1;
	for (int v = 0; v < (int) verts.size(); v++) //more accurate mechanism after shared tmpBases above played w/ tmpBaseCandidate values
		if (verts[ gcOrder[v] ]->tmpBaseCandidate && ! closeToPatchBorder(gcOrder[v], r)) //test the next which comes from gcOrder
		{
			baseVertIdx = gcOrder[v];
			break;
		}
	vector< int >* vlist = new vector<int>; //first a baseVert enters this list, and then verts in the area/patch of that baseVert
	while (baseVertIdx != -1) //there is still a baseVert whose area to be expanded below
	{
		/////// shortcut Dijkstra w/ slow arrays to create 1 tmpBaseVert ///////
		//step 1: initialization
		for (int v = 0; v < (int) verts.size(); v++)
			verts[v]->d = INF;

		//step 2: select a tmp base vert
		verts[baseVertIdx]->d = 0.0f; //so that this baseVert is initially selected from vlist in the loop below
		vlist->push_back(baseVertIdx);			//used below
		tmpBaseVerts.push_back(baseVertIdx);	//used later; whole aim is to fill this vector indeed
		verts[baseVertIdx]->tmpBaseCandidate = false;
		while (! vlist->empty())
		{
			//step 3: extract vert w/ min d value
			float minD = INF;
			int minDidx, v;
			for (int i = 0; i < (int) vlist->size(); i++)
				if (verts[ (*vlist)[i] ]->d < minD)
				{
					v = (*vlist)[i];
					minD = verts[v]->d;
					minDidx = i;
				}
			//erase the element w/ min d value
			vlist->erase(vlist->begin() + minDidx); //ve.erase(begin + 4) erases ve[4]

			//step 4: relaxation loop that updates d values and expands baseVert.area as long as d is small enough
			for (unsigned int ve = 0; ve < verts[v]->edgeList.size(); ve++)
			{
				int e = verts[v]->edgeList[ve];
				int va = (edges[e]->v1i == v ? edges[e]->v2i : edges[e]->v1i);
				if (verts[v]->d + edges[e]->length < verts[va]->d)
				{
					verts[va]->d = verts[v]->d + edges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->tmpBaseCandidate = false;
						//duplicates may arise in baseVertIdx.tmpBaseNghbhood w/o check below [or do later while counting # verts in this tmpBaseNghbhood; duplicates have no harm, i guess, other than giving big cardinality, which i may use to decided optimal r value; also, not taking serious time anyway
						bool exists = false;
						for (int tv = 0; tv < (int) verts[baseVertIdx]->tmpBaseNghbhood->size(); tv++)
							if ((*verts[baseVertIdx]->tmpBaseNghbhood)[tv] == va)
							{
								exists = true;
								break;
							}
						if (! exists)//*/
							verts[baseVertIdx]->tmpBaseNghbhood->push_back(va);
					}
				}
			}
			for (unsigned int ve = 0; ve < verts[v]->sedgeList.size(); ve++)
			{
				int e = verts[v]->sedgeList[ve];
				int va = (sedges[e]->v1i == v ? sedges[e]->v2i : sedges[e]->v1i);
				if (verts[v]->d + sedges[e]->length < verts[va]->d)
				{
					verts[va]->d = verts[v]->d + sedges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->tmpBaseCandidate = false;
						//duplicates may arise in baseVertIdx.tmpBaseNghbhood w/o check below
						bool exists = false;
						for (int tv = 0; tv < (int) verts[baseVertIdx]->tmpBaseNghbhood->size(); tv++)
							if ((*verts[baseVertIdx]->tmpBaseNghbhood)[tv] == va)
							{
								exists = true;
								break;
							}
						if (! exists)//*/
							verts[baseVertIdx]->tmpBaseNghbhood->push_back(va);
					}
				}
			}
		} //end of vlist.empty()
		/////// shortcut Dijkstra w/ slow arrays to create 1 tmpBaseVert ends ///////
#ifdef DBG_DETAIL
		cout << "tmp base" << tmpBaseVerts.size()-1 << ": " << baseVertIdx << endl;
#endif
		//select the next tmpBase in coes manner
		baseVertIdx = -1;
		for (int v = 0; v < (int) verts.size(); v++)
			if (verts[ gcOrder[v] ]->tmpBaseCandidate && ! closeToPatchBorder(gcOrder[v], r)) //test the next which comes from gcOrder
			{
				baseVertIdx = gcOrder[v];
				break;
			}
	} //end of baseVertIdx

	delete vlist;

	cout << "Done! # of tmp base vertices: " << (int) tmpBaseVerts.size() << "\n\n";
}

void Mesh::computeTmpBaseVerts4(float r, int bv, float rOfPrevLevel, int currentLevel)
{
	//similar to computeBaseVerts2(), creates tmpBaseVerts within the patch of bv; but this picks the related subset from tmpBaseVertsAll, where each newcomer tmpBase picked here comes w/ its tmpBaseNghood automatically (thnx to basesOverWholeMesh())

	//cout << bv << ".patch's tmp bases being computed (r' = " << r << ")....\n";

	//start w/ fresh globals not to get effected by previous patch processing
	tmpBaseVerts.clear();
	tmpBaseVerts.push_back(bv); //bv itself is a newcomer tmpBase generated by bv

	//look around bv to see if there's tmpBaseVerts created by preceding basesOverWholeMesh() call; if so, then make them tmpBase directly (no need to mark their vicinity (tmpBaseNghbhood) as candid=false 'cos basesOverWholeMesh() already created tmpBaseNghbhood for me)
	//adapted from computeBaseNeighbs(); run BFS from bv until first baseVert with ~rOfPrevLevel distance is found (this means we hit a neighboring tmpBaseVert
	//and it's impossible to find a tmpBase to share after that distance); all encountered tmpBaseVerts, i.e. at < rOfPrevLevel distance, are recognized as tmpBaseVert of bv's patch
	int source = bv, v;
	queue< int >* q = new queue<int>;
	q->push(source);
	for (int vv = 0; vv < (int) verts.size(); vv++)
		verts[vv]->d = (vv == source ? 0.0f : INF); //prepare for BFS below; length of unweighted shortest path from source to source is 0
	while (! q->empty())// && ! earlyBreak) //modified BFS
	{
		v = q->front();
		q->pop();
		//look at each adjacent vertices of v
		set< int, std::less< int > >::const_iterator neighbVertIdx;
		for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++)
		{
			if (verts[(*neighbVertIdx)]->d != INF)
				continue;
			verts[(*neighbVertIdx)]->d = verts[v]->d + 1; //update d of this adjacent

			//since source is tmpBase, dSpanningUnn[] is defined for it for sure; use it to measure geoDist between source and current neighbVertIdx
			//if (verts[source]->dSpanningUnn[(*neighbVertIdx)] >= rOfPrevLevel)
if (verts[source]->dSpanningUnn[(*neighbVertIdx)] >= 2*rOfPrevLevel) //continue BFS a bit more as i may arrive at a geodesically close point (dijkstra w/ shortcut edges) even if i'm not at a different pnt (BFS w/o shortcut edges)
			{
//				earlyBreak = true; //we hit a distant vert or we hit a tmpBase of a prev level which cannot be used as a tmpBase of this level; so, done for this source
				//indeed the other neighbor(s) of source can be a close vert if edge lengths not uniform; so, consider those verts as well by converting break just-below to a continue and canceling earlyBreak just-above
				
				continue; //break; //continue'll prevent q.push_back below and eventually q'll be empty for sure
//skip this continue at all to check all verts
			}

			//if (verts[(*neighbVertIdx)]->tmpBaseLevel == currentLevel) //an existing tmpBase which has created during this level of outer iteration, so can be used in the patch of this bv as well
if (verts[(*neighbVertIdx)]->tmpBaseLevel == currentLevel && verts[source]->dSpanningUnn[(*neighbVertIdx)] <= rOfPrevLevel) //i let <10rOfPrevLevel come here, so, be sure that only <rOfPrevLevel added to tmpBaseVerts as a patch base
				tmpBaseVerts.push_back((*neighbVertIdx));	//another newcomer tmpBase generated by bv; whole aim is to fill this vector indeed
			q->push((*neighbVertIdx));
		} //end of neighbVertIdx
	}
	delete q;

#ifdef DBG_DETAIL
	for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
		cout << "tmp base" << i << ": " << tmpBaseVerts[i] << "\n";
#endif

	///////// make parent ready for safeList /////////////
	//for adaptStrategy=1 case, i need correspondences from the related previous level (parent) patch; get them here
	if (adaptStrategy == 1)
	{
		if (adaptList)
			delete adaptList; //memo recapture [adaptList=NULL not done 'cos it'll be non-NULL just-below anyway]
		adaptList = new vector<int>; //clear to a fresh start of size 0 here
		if (currentLevel > 1) //parentPatch is n/a for level1 bases; i use initialCorresp for them by leaving adaptList empty here
		{
			//adapted from shared tmpBases above; difference is i go to as long as twoLevelBack = 2 x rOfPrevLevel since the parent is 2 level back, not one level
			float twoLevelBack = radii[currentLevel-2]; //2 * rOfPrevLevel; //for SUBDIVIDE_AT_ALL_TIMES, this 2 is always correct 'cos parent is always coming from two level back; for alternating that may not subdiv at all times, i may need >2 number here; but not so important anyway
			source = bv;
			q = new queue<int>;
			q->push(source);
			for (int vv = 0; vv < (int) verts.size(); vv++)
				verts[vv]->d = (vv == source ? 0.0f : INF); //prepare for BFS below; length of unweighted shortest path from source to source is 0
			while (! q->empty()) //modified BFS
			{
				v = q->front();
				q->pop();

				//look at each adjacent vertices of v
				set< int, std::less< int > >::const_iterator neighbVertIdx;
				for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++)
				{
					if (verts[(*neighbVertIdx)]->d != INF)
						continue;
					verts[(*neighbVertIdx)]->d = verts[v]->d + 1; //update d of this adjacent
					if (verts[source]->dSpanningUnn[(*neighbVertIdx)] >= twoLevelBack)
						continue; //continue'll prevent q.push_back below and eventually q'll be empty for sure

					if (verts[(*neighbVertIdx)]->matchIdx != -1) //an existing match which has made 2 levels before, a parent match as we can call it
					{
						adaptList->push_back((*neighbVertIdx));
						adaptList->push_back(verts[(*neighbVertIdx)]->matchIdx);
					}
					q->push((*neighbVertIdx));
				} //end of neighbVertIdx
			}
			delete q;
		} //end of currentLevel > 1
	}
	///////// make parent ready for safeList ends /////////////

//	cout << "Done! # of tmp base vertices: " << (int) tmpBaseVerts.size() << "\n\n";
}
void Mesh::computeTmpBaseVerts4Eff(float r, int bv, float rOfPrevLevel, int currentLevel)
{
	//same as computeTmpBaseVerts4() except this is fast; BFS stuff above is redundant (and may cause errors due to shortcut dijkstra vs. bfs inconsistency); since source.dSpanning[*] defined for all *, linearly traverse over verts list w/o linear BFS traversal (nevertheless BFS is more costly, big constant)

	//cout << bv << ".patch's tmp bases being computed (r' = " << r << ")....\n";

	//start w/ fresh globals not to get effected by previous patch processing
	tmpBaseVerts.clear();
	tmpBaseVerts.push_back(bv); //bv itself is a newcomer tmpBase generated by bv

	//look around bv to see if there's tmpBaseVerts created by preceding basesOverWholeMesh() call; if so, then make them tmpBase directly (no need to mark their vicinity (tmpBaseNghbhood) as candid=false 'cos basesOverWholeMesh() already created tmpBaseNghbhood for me)
	//all encountered tmpBaseVerts, i.e. at < rOfPrevLevel distance, are recognized as tmpBaseVert of bv's patch
	for (int v = 0; v < (int) verts.size(); v++)
		if (v != bv && verts[v]->tmpBaseLevel == currentLevel && verts[bv]->dSpanningUnn[v] <= rOfPrevLevel) //make v a tmpBase of bv (v != bv for sure, so no duplicate in tmpBaseVerts)
			tmpBaseVerts.push_back(v);	//another newcomer tmpBase generated by bv; whole aim is to fill this vector indeed

//#ifdef DBG_DETAIL
//	for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
//		cout << "tmp base" << i << ": " << tmpBaseVerts[i] << "\n";
//#endif

	///////// make parent ready for safeList /////////////
	//for adaptStrategy=1 case, i need correspondences from the related previous level (parent) patch; get them here
	if (adaptStrategy == 1)
	{
		if (adaptList)
			delete adaptList; //memo recapture [adaptList=NULL not done 'cos it'll be non-NULL just-below anyway]
		adaptList = new vector<int>; //clear to a fresh start of size 0 here
		if (currentLevel > 1) //parentPatch is n/a for level1 bases; i use initialCorresp for them by leaving adaptList empty here
		{
			//adapted from shared tmpBases above; difference is i go to as long as twoLevelBack = 2 x rOfPrevLevel since the parent is 2 level back, not one level
			float twoLevelBack = radii[currentLevel-2]; //2 * rOfPrevLevel; //for SUBDIVIDE_AT_ALL_TIMES, this 2 is always correct 'cos parent is always coming from two level back; for alternating that may not subdiv at all times, i may need >2 number here; but not so important anyway
			for (int v = 0; v < (int) verts.size(); v++)
				if (v != bv && verts[bv]->dSpanningUnn[v] < twoLevelBack && verts[v]->matchIdx != -1) //make sufficiently close v (other than bv itself) and its existing match a parent match for bv
				{
					adaptList->push_back(v);
					adaptList->push_back(verts[v]->matchIdx);
				}
		} //end of currentLevel > 1
	}
	///////// make parent ready for safeList ends /////////////

//	cout << "Done! # of tmp base vertices: " << (int) tmpBaseVerts.size() << "\n\n";
}

int Mesh::countTmpBaseVerts(float r, int bv)
{
	//similar to computeBaseVerts2(), creates tmpBaseVerts within the patch of bv; but whole aim is to count # of tmpBaseVerts generated; so, most stuff cleaned here

#ifdef DBG_DETAIL
	cout << bv << ".patch's tmp bases (r' = " << r << ") just for counting..:\t";
#endif

	//start w/ fresh globals not to get effected by previous patch processing
	tmpBaseVerts.clear();
	//keep the search within the patch, not the whole mesh, using tmpBaseCandidate
	for (int v = 0; v < (int) verts.size(); v++)
		verts[v]->tmpBaseCandidate = verts[v]->inPatch = verts[v]->closeToBorder = false; //inPatch & closeToBorder won't be used if SAMPLING_V2 defined
	for (int nb1 = 0; nb1 < (int) verts[bv]->baseNghbhood->size(); nb1++)
	{
		verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseCandidate = verts[ (*verts[bv]->baseNghbhood)[nb1] ]->inPatch = true;
		//verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood.clear(); //due to overlaps or levels of coarseToFineMatching(), some verts in bv.tmpBaseNghbhood (= bv's patch) may already fill their tmpBaseNghbhood before, which needs cleaning now
																			 //thanks to this observation, when actual computeTmpBaseVerts2() is called on this bv, tmpBaseNghbhood filled here will be cleared anyway
		if (verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood)
			delete verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood;
		verts[ (*verts[bv]->baseNghbhood)[nb1] ]->tmpBaseNghbhood = new vector<int>; //clear effect in memory efficient way
	}
	//verts[bv]->tmpBaseNghbhood.clear(); //not only bv's patch verts, but also bv itself might filled its tmpBaseNghbhood before due to coarseToFineMatching(), not due to overlap 'cos nonbases overlap only
	if (verts[bv]->tmpBaseNghbhood)
		delete verts[bv]->tmpBaseNghbhood;
	verts[bv]->tmpBaseNghbhood = new vector<int>; //clear effect that actually clears/deallocs memory

	//dijkstraShortestPaths(bv); redundant 'cos bv is the generator base, which assures that it has been a tmpBase in the prev level and therefore its dSpanning already computed

	//bv itself is also a candidate to be a tmpBase; indeed it must be selected so select it here and make its patch tmpBaseCandidate = false to effect shared tmpBases and normal tmpBases selections below
	verts[bv]->inPatch = true; //tmpBaseCandidate (false currently) not updated 'cos tmpBaseCandidateFalse(bv) makes it false anyway
	tmpBaseVerts.push_back(bv); //all tmpBaseVerts updates and
	tmpBaseCandidateFalse(bv, r); //all tmpBaseCandidate/tmpBaseNghbhood updates will be cleared when the actual computeTmpBaseVerts2() called after this function

	//the rest is the similar to old COES tactic from computeTmpBaseVerts() except this not select bases close to borders for uniformity maintenance of c2f strategy		
	int baseVertIdx = -1;
	for (int v = 0; v < (int) verts.size(); v++) //more accurate mechanism after shared tmpBases above played w/ tmpBaseCandidate values
//#ifdef SAMPLING_V2 //no!!! 'cos i need to stay away from the borders as i count the bases on an isolated patch (here) whicle giving the shared vert effect during the nonisolated patch cases that i'll encounter in the real level iterations
//		if (verts[ gcOrder[v] ]->tmpBaseCandidate)
//#else
		if (verts[ gcOrder[v] ]->tmpBaseCandidate && ! closeToPatchBorder(gcOrder[v], r))
//#endif
		{
			baseVertIdx = gcOrder[v];
			break;
		}
	vector< int >* vlist = new vector<int>; //first a baseVert enters this list, and then verts in the area/patch of that baseVert
	while (baseVertIdx != -1) //there is still a baseVert whose area to be expanded below
	{
		/////// shortcut Dijkstra w/ slow arrays to create 1 tmpBaseVert ///////
		//step 1: initialization
		for (int v = 0; v < (int) verts.size(); v++)
			verts[v]->d = INF;

		//step 2: select a tmp base vert
		verts[baseVertIdx]->d = 0.0f; //so that this baseVert is initially selected from vlist in the loop below
		vlist->push_back(baseVertIdx);			//used below
		tmpBaseVerts.push_back(baseVertIdx);	//used later; whole aim is to fill this vector indeed
		verts[baseVertIdx]->tmpBaseCandidate = false;
		while (! vlist->empty())
		{
			//step 3: extract vert w/ min d value
			float minD = INF;
			int minDidx, v;
			for (int i = 0; i < (int) vlist->size(); i++)
				if (verts[ (*vlist)[i] ]->d < minD)
				{
					v = (*vlist)[i];
					minD = verts[v]->d;
					minDidx = i;
				}
			//erase the element w/ min d value
			vlist->erase(vlist->begin() + minDidx); //ve.erase(begin + 4) erases ve[4]

			//step 4: relaxation loop that updates d values and expands baseVert.area as long as d is small enough
			for (unsigned int ve = 0; ve < verts[v]->edgeList.size(); ve++)
			{
				int e = verts[v]->edgeList[ve];
				int va = (edges[e]->v1i == v ? edges[e]->v2i : edges[e]->v1i);
				if (verts[v]->d + edges[e]->length < verts[va]->d)
				{
					verts[va]->d = verts[v]->d + edges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->tmpBaseCandidate = false;
/* duplicate-prevention redundant here 'cos i don't access tmpBaseNghbhood.size; indeed i won't even access tmpBaseNghbhood when done w/ this funciton
						//duplicates may arise in baseVertIdx.tmpBaseNghbhood w/o check below [or do later while counting # verts in this tmpBaseNghbhood; duplicates have no harm, i guess, other than giving big cardinality, which i may use to decided optimal r value; also, not taking serious time anyway
						bool exists = false;
						for (int tv = 0; tv < (int) verts[baseVertIdx]->tmpBaseNghbhood->size(); tv++)
							if ((*verts[baseVertIdx]->tmpBaseNghbhood)[tv] == va)
							{
								exists = true;
								break;
							}
						if (! exists)//*/
							verts[baseVertIdx]->tmpBaseNghbhood->push_back(va);
					}
				}
			}
			for (unsigned int ve = 0; ve < verts[v]->sedgeList.size(); ve++)
			{
				int e = verts[v]->sedgeList[ve];
				int va = (sedges[e]->v1i == v ? sedges[e]->v2i : sedges[e]->v1i);
				if (verts[v]->d + sedges[e]->length < verts[va]->d)
				{
					verts[va]->d = verts[v]->d + sedges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->tmpBaseCandidate = false;
/*						//duplicates may arise in baseVertIdx.tmpBaseNghbhood w/o check below
						bool exists = false;
						for (int tv = 0; tv < (int) verts[baseVertIdx]->tmpBaseNghbhood->size(); tv++)
							if ((*verts[baseVertIdx]->tmpBaseNghbhood)[tv] == va)
							{
								exists = true;
								break;
							}
						if (! exists)//*/
							verts[baseVertIdx]->tmpBaseNghbhood->push_back(va);
					}
				}
			}
		} //end of vlist.empty()
		/////// shortcut Dijkstra w/ slow arrays to create 1 tmpBaseVert ends ///////
//cout << "\n[counter] tmp base" << tmpBaseVerts.size()-1 << ": " << baseVertIdx << endl;

		//select the next tmpBase in coes manner
		baseVertIdx = -1;
		for (int v = 0; v < (int) verts.size(); v++)
//#ifdef SAMPLING_V2
//			if (verts[ gcOrder[v] ]->tmpBaseCandidate) //test the next which comes from gcOrder
//#else
			if (verts[ gcOrder[v] ]->tmpBaseCandidate && ! closeToPatchBorder(gcOrder[v], r)) //test the next which comes from gcOrder
//#endif
			{
				baseVertIdx = gcOrder[v];
				break;
			}
	} //end of baseVertIdx

	delete vlist;

#ifdef DBG_DETAIL
	cout << (int) tmpBaseVerts.size() << "\n";
#endif

	return (int) tmpBaseVerts.size();
}

//use either slow array/memo efficient or fast heap/memo inefficient (now also memo efficient thanks to ExtractNodeForDeletion) version (not both); slow array takes 90 secs in the beginning for 8 tmpBase ceration whereas heap takes just 40 secs for
//same tmpBases; but array is better in the long run, e.g. 9 min (array) vs. 13 min (heap) for maxLevel = 3 case 'cos heap initially insert all verts to heap (O(lgn)) and extracts just a few (O(1)) as levels proceed; so, extract benefit goes away
bool Mesh::closeToPatchBorder(int w, float r)
{
	//returns true if w lies within non-border parts of the current patch which is designated by current inPatch values set before calling here

	if (verts[w]->closeToBorder) //say patch of bv5 in process; when tmpBaseCandidate v22 enters here for the first time (in the process of v33) as w, it returned true and therefore not selected as base; v24-34-44 then selected as bases; now, for another newcomer search for patch of v33, there is no need to reconsider w=v22 here for efficiency
		return true; //not reconsider v22 since it'll produce true again for sure

	//w is close to patch border if its new patch (of radius r) stays within current patch as a whole, i.e. all new (hypothetic) patch members have inPatch = true values; below tests this (heavily-adapted from tmpBaseCandidateFalse())
if (arrayInsteadOfHeap)
{
	/////// shortcut Dijkstra w/ slow arrays to decide hypothetic patch members of w ///////
	vector< int >* vlist = new vector<int>; //first w enters this list, and then verts in the area/patch of w (hypototically)
	for (int v = 0; v < (int) verts.size(); v++)
		verts[v]->d2 = (v == w ? 0.0f : INF); //so that w is initially selected from vlist in the loop below [here ->d could've been used as well]
	vlist->push_back(w);		//used below
	while (! vlist->empty())
	{
		//step 3: extract vert w/ min d value
		float minD = INF;
		int minDidx, v;
		for (int i = 0; i < (int) vlist->size(); i++)
			if (verts[ (*vlist)[i] ]->d2 < minD) //dereferencing
			{
				v = (*vlist)[i];
				minD = verts[v]->d2;
				minDidx = i;
			}
		//erase the element w/ min d value
		vlist->erase(vlist->begin() + minDidx); //ve.erase(begin + 4) erases ve[4]

		//step 4: relaxation loop that updates d values and expands baseVert.area as long as d is small enough
		for (unsigned int ve = 0; ve < verts[v]->edgeList.size(); ve++)
		{
			int e = verts[v]->edgeList[ve];
			int va = (edges[e]->v1i == v ? edges[e]->v2i : edges[e]->v1i);
			if (verts[v]->d2 + edges[e]->length < verts[va]->d2)
			{
				verts[va]->d2 = verts[v]->d2 + edges[e]->length;
				if (verts[va]->d2 < r)
				{
					vlist->push_back(va);
					if (! verts[va]->inPatch)
					{
						verts[w]->closeToBorder = true;
						break;
					}
				}
			}
		}
		if (verts[w]->closeToBorder)
			break; //save time by not checking shortcut-edges and waiting for vlist be empty
		for (unsigned int ve = 0; ve < verts[v]->sedgeList.size(); ve++)
		{
			int e = verts[v]->sedgeList[ve];
			int va = (sedges[e]->v1i == v ? sedges[e]->v2i : sedges[e]->v1i);
			if (verts[v]->d2 + sedges[e]->length < verts[va]->d2)
			{
				verts[va]->d2 = verts[v]->d2 + sedges[e]->length;
				if (verts[va]->d2 < r)
				{
					vlist->push_back(va);
					if (! verts[va]->inPatch)
					{
						verts[w]->closeToBorder = true;
						break;
					}
				}
			}
		}
		if (verts[w]->closeToBorder)
			break; //save time by not waiting for vlist be empty
	} //end of vlist.empty()
	delete vlist;
	/////// shortcut Dijkstra w/ slow arrays to decide hypothetic patch members of w ends ///////
}
//#else
else //heap in use*/
{
	/////// shortcut Dijkstra w/ fast fib-heap to decide hypothetic patch members of w ///////
	FibHeap* h = new FibHeap();
	//initialization
	for (int v = 0; v < (int) verts.size(); v++)
	{
//		if (verts[w]->dSpanningUnn[v] > r) //can't do this 'cos w is any vertex coming from gcOrder[]; so, dSpanningUnn not defined yet
//			continue; //i delete only the extracted nodes; i extract nodes until minD <= r; so, insert as much nodes as you will extract later
		verts[v]->dSpanned = (v == w ? 0.0f : INF);
		FibHeapNode* hn = new FibHeapNode(verts[v]->dSpanned, v); //v is a handle from headNode to verts,
		verts[v]->heapNode = hn;								  //and hn is an handle from verts to heapNode
		h->Insert(hn); //inefficiency: must insert all verts since continue; above cannot be done
	}
	while (true)
	{
		//extract vert w/ min dSpanned value
		FibHeapNode* minNode = h->ExtractMin(); //fast O(lgV) priority queue algo
		if (minNode == NULL)
			break; //no more verts w/ unknown shortest dist; so, we're done
		int minDidx = minNode->element;
		float minD = minNode->key;
		delete minNode; //never be used again; save memo; hn above is deleted here indeed
		if (minD > r) //minD is an ascending value; so, when i reach far enough verts, i can safely skip the rest since all verts visited so far have finalized their shortest paths,
						//which means any vertex that i visit after this pnt will be have dist >= minD to sourceVert
			break;
		else
		{
			if (! verts[minDidx]->inPatch)
			{
				verts[w]->closeToBorder = true;
				break;
			}
		}
		//relax each edge, including shortcuts, incident to minDidx
		for (unsigned int ve = 0; ve < verts[minDidx]->edgeList.size(); ve++)
		{
			int e = verts[minDidx]->edgeList[ve];
			int va = (edges[e]->v1i == minDidx ? edges[e]->v2i : edges[e]->v1i);
			if (verts[minDidx]->dSpanned + edges[e]->length < verts[va]->dSpanned)
			{
				verts[va]->dSpanned = verts[minDidx]->dSpanned + edges[e]->length; //relaxation
				h->DecreaseKey(verts[va]->heapNode, verts[va]->dSpanned); //worst O(lgV), amortized O(1)
			}
		}
		for (unsigned int ve = 0; ve < verts[minDidx]->sedgeList.size(); ve++)
		{
			int e = verts[minDidx]->sedgeList[ve];
			int va = (sedges[e]->v1i == minDidx ? sedges[e]->v2i : sedges[e]->v1i);
			if (verts[minDidx]->dSpanned + sedges[e]->length < verts[va]->dSpanned)
			{
				verts[va]->dSpanned = verts[minDidx]->dSpanned + sedges[e]->length; //relaxation
				h->DecreaseKey(verts[va]->heapNode, verts[va]->dSpanned); //worst: O(lgV), amortized O(1)
			}
		}
	} //end of while (true)
	FibHeapNode* minNode = h->ExtractNodeForDeletion(); //my O(1) version of ExtractMin() which is used to delete the min (root), and not maintain the min-heap property; so, use just for memo dealloc
	while (minNode)
	{
		delete minNode;
		minNode = h->ExtractNodeForDeletion();
	}
	delete h;
	/////// shortcut Dijkstra w/ fast fib-heap to decide hypothetic patch members of w ///////
//#endif
}//*/

	return verts[w]->closeToBorder;
}

void Mesh::computeTmpBaseVerts3(int bv)
{
	//similar to computeBaseVerts() (the one w/o shared tmpBase and parentPatch stuff) but this not includes bv1/2 inside tmpBaseVerts in order not to change their cool matches during matchSmallTmpBases()

	cout << bv << ".patch's tmp bases being computed....\n";

	//start w/ fresh globals not to get effected by previous patch processing
	tmpBaseVerts.clear();
	//tmpBases are trivially the ones inside bv.baseNghbhood, so don't bother w/ dijkstra stuff
	for (int nb1 = 0; nb1 < (int) verts[bv]->baseNghbhood->size(); nb1++)
//assert((*verts[bv]->baseNghbhood)[nb1] != bv); //verified that bv not listed in its baseNghbhood/patch
		if (verts[ (*verts[bv]->baseNghbhood)[nb1] ]->matchIdxs.empty()) //for existing bases bv1-bv2, i added that existing corresp (of prevLevel) to the matchIdxs; never consider them again; consider only the non-bases whose matchIdxs not touched yet
			tmpBaseVerts.push_back((*verts[bv]->baseNghbhood)[nb1]);

	cout << "Done! # of tmp base vertices (not include bv = " << bv << "): " << tmpBaseVerts.size() << "\n\n";
}

void Mesh::tmpBaseCandidateFalse(int sourceVert, float far)
{
	//verts that're at most far distant from sourceVerts are visited and made tmpBaseCandidate = false; actually i don't visit them; i just use pre-computed dSpanningUnn[] values

assert( verts[sourceVert]->tmpBaseNghbhood->empty() ); //'cos i'll refill it here

	for (int v = 0; v < (int) verts.size(); v++)
	{
		if (verts[sourceVert]->dSpanningUnn[v] > far)
			continue;

		//v is a close vertex i want to insert to sourceVert.tmpBaseNghbhood
		verts[v]->tmpBaseCandidate = false; //aim of this fnctn; this includes sourceVert itself (dSpanningUnn = 0 for that case)
		verts[sourceVert]->tmpBaseNghbhood->push_back(v); //2nd aim is to prepare the tmpBaseNghbhood which was cleared before; this elements'll be necessary at next level where this vert subdivides this patch into new tmpBases
	}

/*	//below explicitly visits each vert via dijkstra-based graph search and updates tmpBaseCandidate/tmpBaseNghbhood values; redundant now thanks to fast trick above
if (arrayInsteadOfHeap)
{
	/////// shortcut Dijkstra w/ slow arrays ///////
	vector< int >* vlist = new vector<int>; //first w enters this list, and then verts in the area/patch of w (hypototically)
	for (int v = 0; v < (int) verts.size(); v++)
		verts[v]->d2 = (v == sourceVert ? 0.0f : INF); //so that w is initially selected from vlist in the loop below
	vlist->push_back(sourceVert);		//used below
	while (! vlist->empty())
	{
		//step 3: extract vert w/ min d value
		float minD = INF;
		int minDidx, v;
		for (int i = 0; i < (int) vlist->size(); i++)
			if (verts[ (*vlist)[i] ]->d2 < minD) //dereferencing
			{
				v = (*vlist)[i];
				minD = verts[v]->d2;
				minDidx = i;
			}
		if (minD > far) //minD is an ascending value; so, when i reach far enough verts, i can safely skip the rest since all verts visited so far have finalized their shortest paths,
						//which means any vertex that i visit after this pnt will be have dist >= minD to sourceVert
			break;
		else
		{
			verts[v]->tmpBaseCandidate = false; //aim of this fnctn; this includes sourceVert itself (minD = 0 for that case)
			if (v != sourceVert)
			{
				//duplicates may arise in verts[sourceVert]->tmpBaseNghbhood w/o check below 'cos va may be added 1+ times to vlist which provides entries for tmpBaseNghbhood; note that, in heap version, i just decreasy-key of the existing v so no duplicate nodes there
				bool exists = false;
				for (int tv = 0; tv < (int) verts[sourceVert]->tmpBaseNghbhood->size(); tv++)
					if ((*verts[sourceVert]->tmpBaseNghbhood)[tv] == v)
					{
						exists = true;
						break;
					}
				if (! exists)
					verts[sourceVert]->tmpBaseNghbhood->push_back(v); //2nd aim is to prepare the tmpBaseNghbhood which was cleared before; this elements'll be necessary at next level where this vert subdivides this patch into new tmpBases
			}
		}
		//erase the element w/ min d value
		vlist->erase(vlist->begin() + minDidx); //ve.erase(begin + 4) erases ve[4]

		//step 4: relaxation loop that updates d values and expands baseVert.area as long as d is small enough
		for (unsigned int ve = 0; ve < verts[v]->edgeList.size(); ve++)
		{
			int e = verts[v]->edgeList[ve];
			int va = (edges[e]->v1i == v ? edges[e]->v2i : edges[e]->v1i);
			if (verts[v]->d2 + edges[e]->length < verts[va]->d2)
			{
				verts[va]->d2 = verts[v]->d2 + edges[e]->length;
				if (verts[va]->d2 < far)
					vlist->push_back(va);
			}
		}
		for (unsigned int ve = 0; ve < verts[v]->sedgeList.size(); ve++)
		{
			int e = verts[v]->sedgeList[ve];
			int va = (sedges[e]->v1i == v ? sedges[e]->v2i : sedges[e]->v1i);
			if (verts[v]->d2 + sedges[e]->length < verts[va]->d2)
			{
				verts[va]->d2 = verts[v]->d2 + sedges[e]->length;
				if (verts[va]->d2 < far)
					vlist->push_back(va);
			}
		}
	} //end of vlist.empty()
	delete vlist;
}
	/////// shortcut Dijkstra w/ slow arrays ends /////// just disable this block and enable below for other fast but memo-inefficient (now efficient) tactic
//#else
else
{
	//using fast fib-heap instead of slow arrays not save big time here; but such a replacement speeds up 2 times in closeToPatchBorder()

	/////// shortcut Dijkstra w/ fast fib-heap ///////
//took 2 weeks to see :((((((((((, it delete's only the extractMin ones, not all verts that were inserted first; so, big memo leakage leading to crash; soln: use 
	//use Fibonacci heap to get O(VlgV + e); minHeap takes O(VlgV + elgV), simple array takes O(V^2)
	FibHeap* h = new FibHeap();

	//initialization
	for (int v = 0; v < (int) verts.size(); v++)
	{
		verts[v]->dSpanned = (v == sourceVert ? 0.0f : INF);
		FibHeapNode* hn = new FibHeapNode(verts[v]->dSpanned, v); //v is a handle from headNode to verts,
		verts[v]->heapNode = hn;								  //and hn is an handle from verts to heapNode
		h->Insert(hn); //can do if (sourceVerts.dSpanningUnn > far) continue here 'cos sourceVert knows its dijkstra already; but when i do that, null ptr exception occurs below (not occur w/ some prints on; so weird; not do and be safe)
	}
	while (true)
	{
		//extract vert w/ min dSpanned value
		FibHeapNode* minNode = h->ExtractMin(); //fast O(lgV) priority queue algo
		if (minNode == NULL)
			break; //no more verts w/ unknown shortest dist; so, we're done
		int minDidx = minNode->element;
		float minD = minNode->key;
		delete minNode; //never be used again; save memo; hn above is deleted here indeed
		if (minD > far) //minD is an ascending value; so, when i reach far enough verts, i can safely skip the rest since all verts visited so far have finalized their shortest paths,
						//which means any vertex that i visit after this pnt will be have dist >= minD to sourceVert
			break;
		else
		{
			verts[minDidx]->tmpBaseCandidate = false; //aim of this fnctn; this includes sourceVert itself (minD = 0 for that case)
			if (minDidx != sourceVert) //no duplicate risk thanks to decrease-key applied to the existing vertex (instead of pushing the same (duplicated) vertex again due to a better dSpanned value as in array case)
				verts[sourceVert]->tmpBaseNghbhood->push_back(minDidx); //2nd aim is to prepare the tmpBaseNghbhood which was cleared before; this elements'll be necessary at next level where this vert subdivides this patch into new tmpBases
		}
//cout << "ffffff\n";
		//relax each edge, including shortcuts, incident to minDidx
		for (unsigned int ve = 0; ve < verts[minDidx]->edgeList.size(); ve++)
		{
			int e = verts[minDidx]->edgeList[ve];
			int va = (edges[e]->v1i == minDidx ? edges[e]->v2i : edges[e]->v1i);
			if (verts[minDidx]->dSpanned + edges[e]->length < verts[va]->dSpanned)
			{
				verts[va]->dSpanned = verts[minDidx]->dSpanned + edges[e]->length; //relaxation
				h->DecreaseKey(verts[va]->heapNode, verts[va]->dSpanned); //worst O(lgV), amortized O(1)
			}
		}
		for (unsigned int ve = 0; ve < verts[minDidx]->sedgeList.size(); ve++)
		{
			int e = verts[minDidx]->sedgeList[ve];
			int va = (sedges[e]->v1i == minDidx ? sedges[e]->v2i : sedges[e]->v1i);
			if (verts[minDidx]->dSpanned + sedges[e]->length < verts[va]->dSpanned)
			{
				verts[va]->dSpanned = verts[minDidx]->dSpanned + sedges[e]->length; //relaxation
				h->DecreaseKey(verts[va]->heapNode, verts[va]->dSpanned); //worst: O(lgV), amortized O(1)
			}
		}
	} //end of while (true)	
	FibHeapNode* minNode = h->ExtractNodeForDeletion(); //my O(1) version of ExtractMin() which is used to delete the min (root), and not maintain the min-heap property; so, use just for memo dealloc
	while (minNode)
	{
		delete minNode;
		minNode = h->ExtractNodeForDeletion();
	}
	delete h;
	/////// shortcut Dijkstra w/ fast fib-heap ends ///////
//#endif
}//*/
}

void Mesh::matchTmpBasesByGraph(Mesh* mesh2, int bv1, int bv2, bool useTopnForTraversalList, int level)
{
	//matches current tmpBaseVerts and writes the result into tmpSafeCorresp w/ 2 alternating algo alternatives that differ only by the creation of traversalList tmpSafeCorresp2
	
	cout << "matching tmpBases b/w active patches " << bv1 << " & " << bv2 << "..\n";

	for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
	{
		verts[ tmpBaseVerts[i] ]->tmpBaseLevel = level; //means tmpBaseVerts[i] became a tmpBase at this level (it may not be matched tough)
		verts[ tmpBaseVerts[i] ]->residue = true; //e.g. for the 3 vs. 2 patch case, 2 vs. 2 match is set below via blossom, and 1 residue in patch1 gets its match from the bottom w/o blossom
	}
	//same stuff for mesh2
	for (int i = 0; i < (int) mesh2->tmpBaseVerts.size(); i++)
	{
		mesh2->verts[ mesh2->tmpBaseVerts[i] ]->tmpBaseLevel = level;
		mesh2->verts[ mesh2->tmpBaseVerts[i] ]->residue = true;
	}

	//for the loop in alternating corresp part below (step 1-2) and the ultimate patch-corresp part in return, dSpanning[]/getGeodesic() needs to be defined for tmpBases since i need geodesics from a tmpBase to another tmpBase below for new/better isometryCost computation
#ifdef DBG_DETAIL
	cout << "normalized geodesics b/w tmpBases and other tmpBases in the same patch being computed..\n";
#endif
	for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
		//dijkstraShortestPaths(tmpBaseVerts[i], farThrshld * r1); //r1 = rOfPrevLevel for sure (damn fast but not so safe 'cos .. [see farThrshld declaration comment]); i'll launch farThrshld=INF, i.e. original, dijkstra when farThrshld stays low, so we're still safe)
		dijkstraShortestPaths(tmpBaseVerts[i]); //be safe/accurate but not that efficient (i'll call this anyway in matchTmpBasesByGraph*() below if needed, i.e. getGeodesic() returns -1, so we're still safe)
	for (int j = 0; j < (int) mesh2->tmpBaseVerts.size(); j++)
		//mesh2->dijkstraShortestPaths(mesh2->tmpBaseVerts[j], farThrshld * r2);
		mesh2->dijkstraShortestPaths(mesh2->tmpBaseVerts[j]);
#ifdef DBG_DETAIL
	cout << "done!\n";
#endif

	//initial correspondence b/w tmpBases that will be refined later by matchTmpBasesTopn/C10_5() below
	matchTmpBasesInitial(mesh2, bv1, bv2, level); //result written to global tmpSafeCorresp

#ifdef SUBDIV_TO_5_BASES //don't bother w/ alternating phase (matchTmpBasesC10_5()) when SUBDIV_TO_5_BASES undefined; rely on the initial corresp above (that used parent as safeList, and if parent small it used initialCorresp) to keep things simple and clean
	//refinement of the initial corresp found by matchTmpBasesInitial()
/*	if (useTopnForTraversalList)
		matchTmpBasesTopn(mesh2, bv1, bv2);
	else*/
		matchTmpBasesC10_5(mesh2, bv1, bv2);
#endif

	cout << "done!\n\n";
}

int Mesh::refineInitialBasesByGraph(Mesh* mesh2, int k)
{
	//refines initial ~10 bases (safeCorresp) matched in the spectral domain and overwrites the result into safeCorresp w/ the alternating algo similar to matchTmpBasesC10_5()
	
	cout << "matching initial bases b/w source and target..\n";

	///////////// matchTmpBasesC10_5() effect ///////////////
	int n1 = (int) baseVerts.size(), n2 = (int) mesh2->baseVerts.size();
	int nRows = (n1 > n2 ? n1 : n2), nCols = nRows; //square mtrx
	int* vIdx1 = new int[n1], * vIdx2 = new int[n2]; //if r,c = 3,7 is a match in costs[r][c], i.e. [3][7]=0, then verts[ vIdx1[3] ].matchIdx = vIdx2[7], hence vIdx1/2[*] is an idx to verts
	float** costs = new float*[nRows];
	float discourage = INF; //1.0f; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; i normalize costs[][] into [0,1] so we're good w/ discourage=1.0f [sucks w/ 1 for ballerina100015-4; logically, it must be very big anyway; i guess this 1 thing was from old hungarian code requirement]
	//stuff for blossom
	int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e, * edges = new int[2*nEdgeNum], * matches = new int[nVertNum]; //see matches[] as lines of matches where 2 consecutive items make a matching line, e.g. 369 694
	double* weightsD = new double[nEdgeNum];

	//allocate memo only once (and overwrite it during upcoming iters) for efficiency and fill the virtuals; again for efficiency don't refill vIdx1/2 w/ the same info in all iters; do it here for once
	int row = 0;
	for (int i = 0; i < n1; i++)
	{
		costs[row] = new float[nCols]; //nCols = nRows anyway
		vIdx1[row] = baseVerts[i]; //= tmpBase1
		int col = 0;
		for (int j = 0; j < n2; j++)
			vIdx2[col++] = mesh2->baseVerts[j]; //tmpBase2
		while (col < nCols) //fill the remaining virtual columns w/ infinitive costs
			costs[row][col++] = discourage;
		row++;
	}
	while (row < nRows) //fill the remaining virtual rows w/ infinitive costs
	{
		costs[row] = new float[nCols]; //nRows = nCols
		for (int col = 0; col < nCols; col++)
			costs[row][col] = discourage;
		row++;
	}

	int m = (int) safeCorresp.size() / 2, n = 5; //choose n from m
assert(m >= 5 && m <= MAX_M_FOR_COMBINATIONS); //typically m is 10 but it can't be < 5 'cos then 5-nested-fors below crash; e.g., when m=5, there is only C(5,5)=1 combination to try
	///////// step 1-2) alternating correspondence b/w bases ////////////////
	int nAlternate = 5; //# of alternating iterations among which the best corresp'll be maintained; what's alternating/changing is the costs[][] to blossom based on changing traversalList tmpSafeCorresp2
	float minCorrespCost = INF; //not just do nAlternate iters and quit as in matchTmpBasesTopn(); remember the minCost corresp (bestTmpSafeCorresp) out of all nAlternate corresps and use/maintain it as tmpSafeCorresp for later
	vector< int >* bestSafeCorresp = NULL;
	for (int iter = 0; iter < nAlternate; iter++)
	{
		///////// step 1) traversalList (safeCorresp2) creation ////////////////
		//try all combinations of size 5 of the initial corresp (of size m = ~10) safeCorresp; best combination kept as safeCorresp2 list
		vector< int >* safeCorresp2 = NULL;
		//choose 5 from m, hence 5-nested loops to try all combinations
		int nCombinations = 0;
		float minIsometryCost = INF;
		for (int a = 0; a < m; a++)
			for (int b = a+1; b < m; b++)
				for (int c = b+1; c < m; c++)
					for (int d = c+1; d < m; d++)
						for (int e = d+1; e < m; e++)
						{
							//current 5-combination is copied from appropriate slots of safeCorresp to safeCorrespTry
							vector< int >* safeCorrespTry = new vector<int>;
							//corresp # 1
							safeCorrespTry->push_back((safeCorresp)[2*a]);
							safeCorrespTry->push_back((safeCorresp)[2*a + 1]);
							//corresp # 2
							safeCorrespTry->push_back((safeCorresp)[2*b]);
							safeCorrespTry->push_back((safeCorresp)[2*b + 1]);
							//corresp # 3
							safeCorrespTry->push_back((safeCorresp)[2*c]);
							safeCorrespTry->push_back((safeCorresp)[2*c + 1]);
							//corresp # 4
							safeCorrespTry->push_back((safeCorresp)[2*d]);
							safeCorrespTry->push_back((safeCorresp)[2*d + 1]);
							//corresp # 5
							safeCorrespTry->push_back((safeCorresp)[2*e]);
							safeCorrespTry->push_back((safeCorresp)[2*e + 1]);

							//get the overall isometryCost of this current 5-tuple combination
							float isometryCostTry = 0.0f;
							for (int s = 0; s < n; s++) //n=5 is stable 'cos we've 5-nested-fors for C(m,5) combinations
							{
								int tbv1 = (*safeCorrespTry)[2*s], tbv2 = (*safeCorrespTry)[2*s + 1];
								float vertexBasedIso = 0.0f;
								for (int ss = 0; ss < n; ss++) //n=5 is stable 'cos we've 5-nested-fors for C(m,5) combinations
								{
									int s1 = (*safeCorrespTry)[2*ss], s2 = (*safeCorrespTry)[2*ss + 1];
									if (s1 == tbv1)
										continue;
									//if tbv1 vs. tbv2 is a good correspondence, then geoDist(tbv1, s1) = geoDist(tbv2, s2) for all s1 & s2 in tmpSafeCorrespTry
									float t1s1 = //verts[tbv1]->getGeodesic(s1), t2s2 = mesh2->verts[tbv2]->getGeodesic(s2); //use normalized geodesic values to make this value comparable w/ any input meshes
												 verts[tbv1]->dSpanning[s1], t2s2 = mesh2->verts[tbv2]->dSpanning[s2]; //use normalized geodesic values to make this value comparable w/ any input meshes
									vertexBasedIso += fabs(t1s1 - t2s2); //difference (diffForNormalized) small if tbv1 vs. tbv2 is a good correspondence
assert(t1s1 >= 0 && t1s1 <= 1 && t2s2 >= 0 && t2s2 <= 1);
								}
								vertexBasedIso /= (n-1);
								isometryCostTry += vertexBasedIso;
							} //end of s
							//overall isometry cost of this safeCorrespTry combination
							//isometryCost /= n; //scSize [save time by skipping division since i only need comparison below; could've been done above for n-1 too but anyway]

							if (isometryCostTry < minIsometryCost)
							{
								minIsometryCost = isometryCostTry;
								if (safeCorresp2) //clear previous effects
									delete safeCorresp2; //not set to NULL 'cos it'll be non-NULL just-below anyway
								safeCorresp2 = new vector<int>; //start a brand new safeCorresp2
								for (int i = 0; i < (int) safeCorrespTry->size(); i++) //safeCorresp2 = safeCorrespTry effect by explicit copy
									safeCorresp2->push_back((*safeCorrespTry)[i]);
							}
							//recapture memo
							delete safeCorrespTry;
#ifdef DBG_DETAIL
							nCombinations++; //just for info
#endif
						} //end of e
		
#ifdef DBG_DETAIL
		cout << "C(" << m << ", " << n << ") = " << nCombinations << " combinations tried and best traversalList selected\n";
		cout << "traversalList to be traversed by bases at iter" << iter << ":\n"; //see the best selection
		for (int ss = 0; 2*ss+1 < (int) safeCorresp2->size(); ss++)
			cout << (*safeCorresp2)[2*ss] << "->" << (*safeCorresp2)[2*ss + 1] << endl;
#endif
		///////// step 1) traversalList (safeCorresp2) creation ends ////////////////

		///////// step 2) safeCorresp b/w bases for this iter ////////////////
		//create the ultimate safeCorresp to be used in return using the ultimate traversalList computed above
		int scSize = (int) safeCorresp2->size() / 2; //important to use the size of the safeCorresp2 list since it'll be used for isometryCost computation from now on
assert(scSize > 1); //no div by zero below
		//initial (or prev alternating iter's) corresp info is now irrelevant; clear that effect and keep the new alternating corresps in this fresh list
		//delete safeCorresp; safeCorresp = new vector<int>; //safeCorresp size=0 now
		safeCorresp.clear();
		int row = 0;
		for (int i = 0; i < n1; i++)
		{
			int base1 = baseVerts[i];

			//find the unmatched mesh2.base[j] who is the globally best match for base1; to do so globally, use blossom matching
			int col = 0;
			for (int j = 0; j < n2; j++)
			{
				int base2 = mesh2->baseVerts[j];

				//base1 (vIdx1[row]) vs. base2 (vIdx2[col]) active assumption
				float vertexBasedIso = 0.0f; //for vIdx1[r] (under the assumption that it is paired w/ vIdx2[c]
				for (int ss = 0; 2*ss+1 < (int) safeCorresp2->size(); ss++)
				{
//info: safeCorresp2 is of size n=5 whereas # of bases n1/2 is more, say ~10
					int s1 = (*safeCorresp2)[2*ss], s2 = (*safeCorresp2)[2*ss + 1];
					if (s1 == base1)
						continue;

					float s1t1 = //verts[s1]->getGeodesic(base1), s2t2 = mesh2->verts[s2]->getGeodesic(base2);
								 verts[s1]->dSpanning[base1], s2t2 = mesh2->verts[s2]->dSpanning[base2];
					vertexBasedIso += fabs(s1t1 - s2t2); //difference (diffForNormalized) small if base1 vs. base2 is a good correspondence
assert(s1t1 >= 0 && s1t1 <= 1 && s2t2 >= 0 && s2t2 <= 1);
				}
				vertexBasedIso /= (scSize-1); //scSize # of time += fabs done above each of which in [0,1]; so, after /=, vertexBasedIso in [0,1] for sure
				costs[row][col++] = vertexBasedIso; //overwrite the costs[][] for initial corresp above
			} //end of j
			//not filling the remaining virtual columns w/ infinitive costs 'cos it's already done at top
			row++;
		} //end of i
//not filling the remaining virtual rows w/ infinitive costs 'cos it's already done at top

		e = 0;
		for (int i = 0; i < nRows; i++)
			for (int j = 0; j < nCols; j++)
			{
				edges[2*e] = i;
				edges[2*e+1] = nRows+j;
				weightsD[e++] = (double) costs[i][j];
			}
		//blossom output
		Blossom* blossom = new Blossom();
		blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches); //matches update by call-by-ref
		delete blossom;

		//transform data in matches[] to safeCorresp.push_back's consistently..
		for (int m = 0; m < nRows; m++)
		{
			int m1 = matches[2*m]; //left side of the (m+1)'th matching line
			if (m1 < n1)
			{
				int m2 = matches[2*m + 1] - nRows; //mathches[m1 + 1], right side of the line, is in [nRows..2*nRows-1], hence nRows entry at mesh2
				int mi = (m2 < n2 ? vIdx2[m2] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1
				if (mi != -1)
				{
					//the correspondences
					safeCorresp.push_back(vIdx1[m1]);
					safeCorresp.push_back(mi);
#ifdef DBG_DETAIL
					cout << vIdx1[m1] << " =====> " << mi << "\t[" << iter << "]\n";
#endif
				}
			}
		} //end of m
		//..and compute its error, in order to help keep it later if it's the current best so far
		float safeCorrespErr = setCorrespondenceErrs(mesh2, r1); //replaces tmpSafeCorrespErr = computeTmpSafeCorrespErr(mesh2);
		//keep safeCorresp if it's the current best so far; by keeping i mean remembering it in bestSafeCorresp and roll-back to it after nAlternate-loop
		if (safeCorrespErr < minCorrespCost)
		{
			if (bestSafeCorresp)
				delete bestSafeCorresp;
			bestSafeCorresp = new vector<int>; //clear to a fresh start of size 0
			for (int s = 0; s < (int) safeCorresp.size(); s++) //bestSafeCorresp = safeCorresp effect to roll-back to it after nAlternate-loop
				bestSafeCorresp->push_back((safeCorresp)[s]);				
			minCorrespCost = safeCorrespErr;
		} //end of if safeCorrespErr < minCorrespCost
assert((int) safeCorresp.size() / 2 == (n1 < n2 ? n1 : n2)); //small of n1 & n2 must be matched all
		delete safeCorresp2; //memo recapture

#ifdef DBG_DETAIL
		//use value printed below for an early break of this loop [delLater for efficieny]
		cout << "overall isometryCost of safeCorresp (of initial bases): " << safeCorrespErr << "\n\n";
#endif
		///////// step 2) safeCorresp b/w bases for this iter ends ////////////////
	} //end of iter

	//roll-back to the bestSafeCorresp
	//delete safeCorresp; safeCorresp = new vector<int>; //fresh start w/ size 0 list
	safeCorresp.clear();
	int bcSize = (int) bestSafeCorresp->size() / 2;
	for (int i = 0; i < bcSize; i++)
	{
		int best1 = (*bestSafeCorresp)[2*i], best2 = (*bestSafeCorresp)[2*i + 1];
assert(best1 != -1 && best2 != -1);
		//the correspondences
		//safeCorresp.push_back(best1); safeCorresp.push_back(best2); not done 'cos refillSafeCorresp() below'll do it via matchIdx values set just-below
		verts[best1]->matchIdx = best2;
		mesh2->verts[best2]->matchIdx = best1;
#ifdef DBG_DETAIL
		cout << best1 << " ==best==> " << best2 << "\n";
#endif
	} //end of i
#ifdef DBG_DETAIL
		cout << "overall isometryCost of the best safeCorresp chosen: " << minCorrespCost << "\n\n";
#endif

	//fill safeCorresp which will be used to set the vertex-based and average correspondence errors
	refillSafeCorresp(mesh2, 1);
	setCorrespondenceErrs(mesh2, r1); //ultimate matchCostI and matchIdx values set
assert(minCorrespCost == isometryCost); //global isometryCost set just-above via setCorrespondenceErrs()

	//memo re-capture
	delete [] matches;
	delete [] edges;
	delete [] weightsD;
	for (int i = 0; i < nRows; i++) delete [] costs[i];		delete [] costs;
	delete [] vIdx1;
	delete [] vIdx2;
	///////// step 1-2) alternating correspondence b/w bases ends ////////////////
	///////////// matchTmpBasesC10_5() effect ends ///////////////
		
	cout << "done!\n\n";
	return k;
}

void Mesh::matchTmpBasesInitial(Mesh* mesh2, int bv1, int bv2, int level)
{
	//initial match of tmpBases by traversing them over safeCorrespsAdaptive list created beloww by adaptiveListForIsometryCost(); result written to global tmpSafeCorresp




//residue stuff not done here; no problem if matchTmpBasesC10_5() is called 'cos it does residue but big problem if i don't call it, i.e. in SUBDIV_TO_5_BASES defined case; so, put that part to SUBDIV_TO_5_BASES undef block here later!!!!!!!!!!!!!!!

//adaptiveListForIsometryCost() is obselete; use radii[] tactic to visit the parent (and possibly grand-grand-.. parent) until 5 matches found; adaptList used in that fnctn (and prepared in comp..erts4()) is
//obselete 'cos it not use radii[] but uses float twoLevelBack = 2 * rOfPrevLevel, which is wrong now 'cos i don't use rDenom tactic for new level radius computation!!!!!!!!!!!!!!!!!!


	//first i need 2+ mesh1.bases (and their corresps) in safeCorrespsAdaptive; initial tmpBase match'll be based on this list so it better be as accurate as possible
	bool needInitial = adaptiveListForIsometryCost(bv1, bv2, level, mesh2); //global safeCorrespsAdaptive is set
	int scSize = (int) safeCorrespsAdaptive->size() / 2;
assert(scSize > 1); //no div by zero below; see comment at top in adaptiveListForIsometryCost() to see the possible scSize values here

	if (adaptStrategy == 1)
	{
		//geodesic from safeCorrespsAdaptive to bv1/2's tmpBases will be necessarry; to make them ready call dijkstra for all safeCorrespsAdaptive verts (of size 10); recall saveGeoDist set in matchTmpBasesByGraph() before
		for (int s = 0; s < scSize; s++)
			//dijkstraShortestPaths((*safeCorrespsAdaptive)[2*s], farThrshld * r1); //r1 = rOfPrevLevel for sure
			dijkstraShortestPaths((*safeCorrespsAdaptive)[2*s]); //will be called if required so we're still safe
		for (int s = 0; s < scSize; s++)
			//mesh2->dijkstraShortestPaths((*safeCorrespsAdaptive)[2*s + 1], farThrshld * r2);
			mesh2->dijkstraShortestPaths((*safeCorrespsAdaptive)[2*s + 1]);
	}
	if (adaptStrategy == 0 || needInitial) //initialCorresp should have been renamed as initialCorresp 'cos initialEuclideanMatching() might have been used for initial correspondence creation; anyway
	{
		//geodesic from initialCorresp vertex to bv1/2's tmpBases will be necessarry; to make them ready call dijkstra for all spectral verts (~10); recall saveGeoDist set in matchTmpBasesByGraph() before
		for (int s = 0; s < (int) initialCorresp->size() / 2; s++) dijkstraShortestPaths((*initialCorresp)[2*s]); //no shortcut farThrshld 'cos spectral's may be too far from the related tmpBases due to bv1
		for (int s = 0; s < (int) initialCorresp->size() / 2; s++) mesh2->dijkstraShortestPaths((*initialCorresp)[2*s + 1]);
	}

	if (tmpSafeCorresp)
		delete tmpSafeCorresp; //tmpSafeCorresp=NULL not done 'cos it'll be non-NULL just-below anyway
	tmpSafeCorresp = new vector<int>; //clear effects of previous patch and start w/ size 0 tmpSafeCorresp
	//blossom input
	//let geodesic from mesh1.tmpBase[i] to closestBase1 be 7; then mesh1.tmpBase[i] will be matched w/ mesh2.tmpBase[j] whose dist to closestBase2 is as close to 7 as possible; to do it globally, use blossom matching
	int n1 = (int) tmpBaseVerts.size(), n2 = (int) mesh2->tmpBaseVerts.size();
	int nRows = (n1 > n2 ? n1 : n2), nCols = nRows; //square mtrx
	int* vIdx1 = new int[n1], * vIdx2 = new int[n2]; //if r,c = 3,7 is a match in costs[r][c], i.e. [3][7]=0, then verts[ vIdx1[3] ].matchIdx = vIdx2[7], hence vIdx1/2[*] is an idx to verts
	float** costs = new float*[nRows];
	float discourage = INF; //1.0f; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; i normalize costs[][] into [0,1] so we're good w/ discourage=1.0f [sucks w/ 1 for ballerina100015-4; logically, it must be very big anyway; i guess this 1 thing was from old hungarian code requirement]
	int row = 0;
	for (int i = 0; i < n1; i++)
	{
		int tmpBase1 = tmpBaseVerts[i];

		//find the unmatched mesh2.tmpBase[j] whose dist2 to closestBase2 is as close to dist1 as possible; to do so globally, use blossom matching
		costs[row] = new float[nCols]; //nCols = nRows anyway
		vIdx1[row] = tmpBase1; //row = 5, v1 = 68 means that row5 (6th row) is about vert68
		int col = 0;
		for (int j = 0; j < n2; j++)
		{
			int tmpBase2 = mesh2->tmpBaseVerts[j];
			vIdx2[col] = tmpBase2; //col = 6, v2 = 69 means that column6 is about vert69

			//tmpBase1 (vIdx1[row]) vs. tmpBase2 (vIdx2[col]) active assumption
			float vertexBasedIso = 0.0f; //for vIdx1[r] (under the assumption that it is paired w/ vIdx2[c]
			for (int ss = 0; 2*ss+1 < (int) safeCorrespsAdaptive->size(); ss++)
			{
				int s1 = (*safeCorrespsAdaptive)[2*ss], s2 = (*safeCorrespsAdaptive)[2*ss + 1];
				if (s1 == tmpBase1) //safeCorrespsAdaptive're from coarse stage bases; vIdx1/2're from tmpBases; so, == seems to never happen; but tmpBases[0] is a coarseStage base and therefore == may still happen
					continue;
				float s1t1 = verts[s1]->dSpanning[tmpBase1], s2t2 = mesh2->verts[s2]->dSpanning[tmpBase2];
				vertexBasedIso += fabs(s1t1 - s2t2); //difference (diffForNormalized) is small if tmpBase1 vs. tmpBase2 is a good correspondence
assert(s1t1 >= 0 && s1t1 <= 1 && s2t2 >= 0 && s2t2 <= 1);
			}
			vertexBasedIso /= (scSize-1); //scSize # of time += fabs done above each of which in [0,1]; so, after /=, vertexBasedIso in [0,1] for sure
			costs[row][col++] = vertexBasedIso;

			//if (useTopnForTraversalList) { now doing it for C(10, 5) too 'cos i need matchIdxs for 1-to-1 conversion in make1to1() later
			//save in this format for later use in alternating corresp top5 sorting as well as 1-to-1 conversion: for 4471->4388 w/ cost 0.019232, verts[4471]->isometryCosts[x] = 4388, [x+1] = 0.019232
			verts[tmpBase1]->isometryCosts.push_back((float) tmpBase2);
			verts[tmpBase1]->isometryCosts.push_back(vertexBasedIso);//}
//cout << tmpBase1 << " & " << tmpBase2 << ": " << vertexBasedIso << endl;
		} //end of j
		while (col < nCols) //fill the remaining virtual columns w/ infinitive costs
			costs[row][col++] = discourage;
		row++;
	}
	while (row < nRows) //fill the remaining virtual rows w/ infinitive costs
	{
		costs[row] = new float[nCols]; //nRows = nCols
		for (int col = 0; col < nCols; col++)
			costs[row][col] = discourage;
		row++;
	}
	int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e = 0, * edges = new int[2*nEdgeNum];
	double* weightsD = new double[nEdgeNum];
	for (int i = 0; i < nRows; i++)
		for (int j = 0; j < nCols; j++)
		{
			edges[2*e] = i;
			edges[2*e+1] = nRows+j;
			weightsD[e++] = (double) costs[i][j];
		}

#ifndef DBG_DETAIL //if not defined
		cout << "(initial) "; //based on safeCorrespsAdaptive)\t";
#endif
	//blossom output
	Blossom* blossom = new Blossom();
	int* matches = new int[nVertNum]; //see this as lines of matches where 2 consecutive items make a matching line, e.g. 369 694
	blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches); //matches update by call-by-ref
	delete blossom;
	delete [] edges;
	delete [] weightsD;

	//transform data in matches[] to tmpSafeCorresp.push_back's consistently
#ifdef DBG_DETAIL
	cout << "initial corresp to be refined later by alternating scheme (no alternating currently):\n";
#endif
	for (int m = 0; m < nRows; m++)
	{
		//assume nRows = 693 (693x2 verts in total, including virtuals) and n1>n2, then matches[] lines:
		//0 856 \n 1 925  \n .. 323 693 \n .. 691 1198 \n 692 1380; then means m1.v[vIdx1[0]] matches w/ m2.v[vIdx2[856-693]]
		int m1 = matches[2*m]; //left side of the (m+1)'th matching line
		if (m1 < n1)
		{
			int m2 = matches[2*m + 1] - nRows; //mathches[m1 + 1], right side of the line, is in [nRows..2*nRows-1], hence nRows entry at mesh2
			int mi = (m2 < n2 ? vIdx2[m2] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1
			if (mi != -1)
			{
				//the correspondences
				tmpSafeCorresp->push_back(vIdx1[m1]);
				tmpSafeCorresp->push_back(mi);
#ifdef DBG_DETAIL
				cout << vIdx1[m1] << " ------> " << mi << "\t"; //(useTopnForTraversalList ? "\t" : "\n");
#endif
				//if (useTopnForTraversalList) now doing it for C(10, 5) too 'cos i need matchIdxs for 1-to-1 conversion in make1to1() later
				//remember the corresponding isometryCost used above for sorting-based, e.g. top5, tmpSafeCorresp2 creation as well as 1-to-1 conversion
				for (int i = 0; 2*i+1 < (int) verts[ vIdx1[m1] ]->isometryCosts.size(); i++)
					if (verts[ vIdx1[m1] ]->isometryCosts[2*i] == mi) //got the related cost
					{
						verts[ vIdx1[m1] ]->isometryCostChosenByBlossom = verts[ vIdx1[m1] ]->isometryCosts[2*i + 1];
						//verts[ vIdx1[m1] ]->tmpMatchIdx = mi; //to get the other side of vIdx1[m1] in case it makes to top5
#ifdef DBG_DETAIL
						cout << verts[ vIdx1[m1] ]->isometryCostChosenByBlossom << "\n";
#endif

#ifndef SUBDIV_TO_5_BASES //matchTmpBasesC10_5()'ll be called when SUBDIV_TO_5_BASES defined and it'll handle the matchIdxs stuff; otherwise, we handle it here and not bother w/ matchTmpBasesC10_5() at all
						//for 1-to-1 conversion in make1to1() later
						verts[ vIdx1[m1] ]->matchIdxs.push_back((float) mi);
						verts[ vIdx1[m1] ]->matchIdxs.push_back(verts[ vIdx1[m1] ]->isometryCostChosenByBlossom); //cost of this match; if this turns out to be the best cost, then verts[best1]->matchIdx = vIdx2[m2]'ll be done in make1to1()
						mesh2->verts[mi]->matchIdxs.push_back((float) vIdx1[m1]); //make1to1() works on mesh2.matchIdxs too for force1to1=2 case
						mesh2->verts[mi]->matchIdxs.push_back(verts[ vIdx1[m1] ]->isometryCostChosenByBlossom);
#endif

						break;
					}
			} //end of mi
		} //end of m1
	} //end of m
assert((int) tmpSafeCorresp->size() / 2 == (n1 < n2 ? n1 : n2)); //small of n1 & n2 must be matched all

	//memo re-capture not now 'cos they'll be updated and used in alternating correspondence below
	delete [] matches; //no delete [] edges; and so on 'cos they're deleted in Blossom.match()
	for (int i = 0; i < nRows; i++) delete [] costs[i];		delete [] costs;
	delete [] vIdx1;
	delete [] vIdx2;
#ifdef DBG_DETAIL
	cout << "overall isometryCost of the initial tmpSafeCorresp: " << computeTmpSafeCorrespErr(mesh2) << "\n\n";
#endif
}

/*void Mesh::matchTmpBasesTopn(Mesh* mesh2, int bv1, int bv2)
{
	//let tmpSafeCorresp be the initial corresps to be refined here; algo is:
	//1) get top-n elements of tmpSafeCorresp, call it tmpSafeCorresp2^^ (n = nMatchesToTraverse / 2 below), the traversalList
	//2) tmpBases traverse this tmpSafeCorresp2 and correspondences updated (hopefully in a good manner), new corresps overwritten to tmpSafeCorresp; go to step 1)

	int n1 = (int) tmpBaseVerts.size(), n2 = (int) mesh2->tmpBaseVerts.size();
	int nRows = (n1 > n2 ? n1 : n2), nCols = nRows; //square mtrx
	int* vIdx1 = new int[n1], * vIdx2 = new int[n2]; //if r,c = 3,7 is a match in costs[r][c], i.e. [3][7]=0, then verts[ vIdx1[3] ].matchIdx = vIdx2[7], hence vIdx1/2[*] is an idx to verts
	float** costs = new float*[nRows];
	float discourage = INF; //1.0f; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; i normalize costs[][] into [0,1] so we're good w/ discourage=1.0f [sucks w/ 1 for ballerina100015-4; logically, it must be very big anyway; i guess this 1 thing was from old hungarian code requirement]
	//stuff for blossom
	int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e, * edges = new int[2*nEdgeNum], * matches = new int[nVertNum]; //see matches[] as lines of matches where 2 consecutive items make a matching line, e.g. 369 694
	double* weightsD = new double[nEdgeNum];

	//allocate memo only once (and overwrite it during upcoming iters) for efficiency and fill the virtuals; again for efficiency don't refill vIdx1/2 w/ the same info in all iters; do it here for once
	int row = 0;
	for (int i = 0; i < n1; i++)
	{
		costs[row] = new float[nCols]; //nCols = nRows anyway
		vIdx1[row] = tmpBaseVerts[i]; //= tmpBase1
		int col = 0;
		for (int j = 0; j < n2; j++)
			vIdx2[col++] = mesh2->tmpBaseVerts[j]; //tmpBase2
		while (col < nCols) //fill the remaining virtual columns w/ infinitive costs
			costs[row][col++] = discourage;
		row++;
	}
	while (row < nRows) //fill the remaining virtual rows w/ infinitive costs
	{
		costs[row] = new float[nCols]; //nRows = nCols
		for (int col = 0; col < nCols; col++)
			costs[row][col] = discourage;
		row++;
	}

	///////// step 1-2) alternating correspondence b/w tmpBases ////////////////
	int nMatchesToTraverse = 6*2, //top-n (n = nMatchesToTraverse / 2) elements from tmpSafeCorresp'll be used; use a big # to make tmpBases traverse all elements in current tmpSafeCorresp for new/better isometryCost computation
		nAlternate = 4; //# of alternating iterations; what's alternating/changing is the costs[][] to blossom based on changing traversalList tmpSafeCorresp2
//as opposed to more robust matchTmpBasesC10_5(), i don't maintain the best corresp here; i just keep the last iter's corresp, assuming it converged to minimum cost and stayed there
	for (int iter = 0; iter < nAlternate; iter++)
	{
		///////// step 1) traversalList creation ////////////////
		//use the top5 (assuming nMatchesToTraverse=5) of the initial corresp (of size say 10) above as the tmpSafeCorresp2 list and compute new/better isometryCosts w.r.t. tmpSafeCorresp2
		vector< int >* tmpSafeCorresp2 = new vector<int>; //start a brand new tmpSafeCorresp2
		//tmpBases will traverse tmpSafeCorresp2 list, which is the recently computed tmpSafeCorresp (or top5 elements of tmpSafeCorresp, currently not in use)
		if (nMatchesToTraverse >= (int) tmpSafeCorresp->size())
			//all matches in tmpSafeCorresp'll be traversed (by tmpBases) for new/better isometryCost computation; so, copy all w/o bothering sorting
			for (int i = 0; i < (int) tmpSafeCorresp->size(); i++) //tmpSafeCorresp2 = tmpSafeCorresp effect by explicit copy
				tmpSafeCorresp2->push_back((*tmpSafeCorresp)[i]);
		else //use the top nMatchesToTraverse elements of tmpSafeCorresp for new/better isometryCost computation; so, sorting required
		{
			for (int i = 0; i < n1; i++)
				verts[ tmpBaseVerts[i] ]->processed = false; //duplicate prevention in tmpSafeCorresp2; processed means added-to-tmpSafeCorresp2
			while ((int) tmpSafeCorresp2->size() < nMatchesToTraverse)
			{
				float minIsoCost = INF;
				int minIsoIdx, minIsoIdx2;
				for (int ss = 0; 2*ss+1 < (int) tmpSafeCorresp->size(); ss++)
				{
					int s1 = (*tmpSafeCorresp)[2*ss];
					if (verts[s1]->isometryCostChosenByBlossom < minIsoCost && ! verts[s1]->processed)
					{
						minIsoCost = verts[s1]->isometryCostChosenByBlossom;
						minIsoIdx = s1;
						minIsoIdx2 = (*tmpSafeCorresp)[2*ss + 1];
					}
				} //end of for ss

				//after sorting, ready to fill cleared tmpSafeCorresp2 w/ cool entries
				tmpSafeCorresp2->push_back(minIsoIdx);
				tmpSafeCorresp2->push_back(minIsoIdx2); //verts[minIsoIdx]->tmpMatchIdx); //matchIdx not set yet (it'll be set later in make1to1()); so, use tmpMatchIdx or easier minIsoIdx2
				verts[minIsoIdx]->processed = true; //added
			} //end of while nMatchesToTraverse
		}

#ifdef DBG_DETAIL
		cout << "traversalList to be traversed by tmpBases at iter" << iter << ":\n"; //see the best selection
		for (int ss = 0; 2*ss+1 < (int) tmpSafeCorresp2->size(); ss++)
			cout << (*tmpSafeCorresp2)[2*ss] << "->" << (*tmpSafeCorresp2)[2*ss + 1] << "\t" << verts[ (*tmpSafeCorresp2)[2*ss] ]->isometryCostChosenByBlossom << endl;
#endif
		///////// step 1) traversalList creation ends ////////////////

		///////// step 2) tmpSafeCorresp b/w bases for this iter ////////////////
//same as step 2) of matchTmpBasesC10_5() except isometryCosts[] part
		//create the ultimate tmpSafeCorresp to be used in return using the ultimate traversalList computed above
		int scSize = (int) tmpSafeCorresp2->size() / 2; //important to use the size of the tmpSafeCorresp2 list since it'll be used for isometryCost computation from now on
assert(scSize > 1); //no div by zero below
		//initial (or prev alternating iter's) corresp info is now irrelevant; clear that effect and keep the new alternating corresps in this fresh list
		delete tmpSafeCorresp; //no preceding if (tmpSafeCorresp) 'cos tmpSafeCorresp not NULL for sure, i.e. used above already
		tmpSafeCorresp = new vector<int>; //tmpSafeCorresp size=0 now
		int row = 0;
		for (int i = 0; i < n1; i++)
		{
			int tmpBase1 = tmpBaseVerts[i];
			verts[tmpBase1]->isometryCosts.clear(); //clear stuff from initial corresp and previous iters which (re)fills this list

			//find the unmatched mesh2.tmpBase[j] whose dist2 to closestBase2 is as close to dist1 as possible; to do so globally, use blossom matching
			int col = 0;
			for (int j = 0; j < n2; j++)
			{
				int tmpBase2 = mesh2->tmpBaseVerts[j];

				//tmpBase1 (vIdx1[row]) vs. tmpBase2 (vIdx2[col]) active assumption
				float vertexBasedIso = 0.0f; //for vIdx1[r] (under the assumption that it is paired w/ vIdx2[c]
				for (int ss = 0; 2*ss+1 < (int) tmpSafeCorresp2->size(); ss++)
				{
//info: tmpSafeCorresp2 is of size nMatchesToTraverse / 2, say 5, whereas # of tmpBases n1/2 is more, say ~10
					int s1 = (*tmpSafeCorresp2)[2*ss], s2 = (*tmpSafeCorresp2)[2*ss + 1];
					if (s1 == tmpBase1)
						continue;
					
					float s1t1 = //verts[s1]->getGeodesic(tmpBase1), s2t2 = mesh2->verts[s2]->getGeodesic(tmpBase2);
								 verts[s1]->dSpanning[tmpBase1], s2t2 = mesh2->verts[s2]->dSpanning[tmpBase2];
					vertexBasedIso += fabs(s1t1 - s2t2); //difference (diffForNormalized) is small if tmpBase1 vs. tmpBase2 is a good correspondence
				}
				vertexBasedIso /= (scSize-1); //scSize # of time += fabs done above each of which in [0,1]; so, after /=, vertexBasedIso in [0,1] for sure
assert(scSize > 1);
				costs[row][col++] = vertexBasedIso; //overwrite the costs[][] for initial corresp above
//cout << tmpBase1 << " &&&&&&& " << tmpBase2 << ": " << vertexBasedIso << endl;
				
				//update isometryCosts only for the last alternating iteration 'cos the ones before that will not yield the ultimate corresps; they're just a path to this last iteration that yields the ultimate corresps
				if (iter == nAlternate-1)
				{
					//remember in this format for use in alternating corresp top5 sorting: for 4471->4388 w/ cost 0.019232, verts[4471]->isometryCosts[x] = 4388, [x+1] = 0.019232
					verts[tmpBase1]->isometryCosts.push_back((float) tmpBase2);
					verts[tmpBase1]->isometryCosts.push_back(vertexBasedIso);
				}
			} //end of j
			//not filling the remaining virtual columns w/ infinitive costs 'cos it's already done at top
			row++;
		}
//not filling the remaining virtual rows w/ infinitive costs 'cos it's already done at top

		e = 0;
		for (int i = 0; i < nRows; i++)
			for (int j = 0; j < nCols; j++)
			{
				edges[2*e] = i;
				edges[2*e+1] = nRows+j;
				weightsD[e++] = (double) costs[i][j];
			}
		//blossom output
		Blossom* blossom = new Blossom();
		blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches); //matches update by call-by-ref
		delete blossom;

		//transform data in matches[] to tmpSafeCorresp.push_back's consistently
		for (int m = 0; m < nRows; m++)
		{
			int m1 = matches[2*m]; //left side of the (m+1)'th matching line
			if (m1 < n1)
			{
				int m2 = matches[2*m + 1] - nRows; //mathches[m1 + 1], right side of the line, is in [nRows..2*nRows-1], hence nRows entry at mesh2
				int mi = (m2 < n2 ? vIdx2[m2] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1
				if (mi != -1)
				{
					//the correspondences
					tmpSafeCorresp->push_back(vIdx1[m1]);
					tmpSafeCorresp->push_back(mi);

#ifdef DBG_DETAIL
					cout << vIdx1[m1] << " ====================> " << mi << "\t[" << iter << "]\t";
#endif
					//update isometryCosts only for the last alternating iteration 'cos the ones before that will not yield the ultimate corresps; they're just a path to this last iteration that yields the ultimate corresps
					if (iter == nAlternate-1)
						//remember the corresponding isometryCost used above for sorting-based, e.g. top5, tmpSafeCorresp2 creation
						for (int i = 0; 2*i+1 < (int) verts[ vIdx1[m1] ]->isometryCosts.size(); i++)
							if (verts[ vIdx1[m1] ]->isometryCosts[2*i] == mi) //got the related cost
							{
								verts[ vIdx1[m1] ]->isometryCostChosenByBlossom = verts[ vIdx1[m1] ]->isometryCosts[2*i + 1];
								//verts[ vIdx1[m1] ]->tmpMatchIdx = mi; //to get the other side of vIdx1[m1] in case it makes to top5
#ifdef DBG_DETAIL
								cout << verts[ vIdx1[m1] ]->isometryCostChosenByBlossom << "\n";
#endif

								//for 1-to-1 conversion in make1to1() later
								verts[ vIdx1[m1] ]->matchIdxs.push_back((float) mi);
								verts[ vIdx1[m1] ]->matchIdxs.push_back(verts[ vIdx1[m1] ]->isometryCostChosenByBlossom); //cost of this match; if this turns out to be the best cost, then verts[ vIdx1[m1] ]->matchIdx = vIdx2[m2]'ll be done in make1to1()
								mesh2->verts[mi]->matchIdxs.push_back((float) vIdx1[m1]); //make1to1() works on mesh2.matchIdxs too for force1to1=2 case
								mesh2->verts[mi]->matchIdxs.push_back(verts[ vIdx1[m1] ]->isometryCostChosenByBlossom);

								break;
							}
				}
			}
		} //end of m
assert((int) tmpSafeCorresp->size() / 2 == (n1 < n2 ? n1 : n2)); //small of n1 & n2 must be matched all
		delete tmpSafeCorresp2;
#ifdef DBG_DETAIL
		//use value printed below for an early break of this loop [delLater for efficiency]
		cout << "overall isometryCost of tmpSafeCorresp (of tmpBases): " << computeTmpSafeCorrespErr(mesh2) << "\n\n";
#endif
		///////// step 2) tmpSafeCorresp b/w bases for this iter ends ////////////////
	} //end of for iter

	//memo re-capture
	delete [] matches;
	delete [] edges;
	delete [] weightsD;
	for (int i = 0; i < nRows; i++) delete [] costs[i];		delete [] costs;
	delete [] vIdx1;
	delete [] vIdx2;
	///////// step 1-2) alternating correspondence b/w tmpBases ends ////////////////
}*/

void Mesh::matchTmpBasesC10_5(Mesh* mesh2, int bv1, int bv2)
{
	//let tmpSafeCorresp be the initial corresps to be refined here; algo is:
	//1) try C(m, n) combinations of the m-size tmpSafeCorresp matches and get the n-tuple (n=5 always, m = ~10) which gives the lowest isometry cost, call it tmpSafeCorresp2
	//2) tmpBases traverse this tmpSafeCorresp2 and correspondences updated (hopefully in a good manner), new corresps overwritten to tmpSafeCorresp; go to step 1)

#ifndef SUBDIV_TO_5_BASES
	cout << "when SUBDIV_TO_5_BASES undefined, matchTmpBasesInitial() already set the matchIdxs; so, don't go below to add more (indeed it shouldn't hurt to add more but the idea is to let matchTmpBasesInitial() handle all matchIdxs stuff)\n";
	exit(0);
#endif

	int n1 = (int) tmpBaseVerts.size(), n2 = (int) mesh2->tmpBaseVerts.size();
	int nRows = (n1 > n2 ? n1 : n2), nCols = nRows; //square mtrx
	int* vIdx1 = new int[n1], * vIdx2 = new int[n2]; //if r,c = 3,7 is a match in costs[r][c], i.e. [3][7]=0, then verts[ vIdx1[3] ].matchIdx = vIdx2[7], hence vIdx1/2[*] is an idx to verts
	float** costs = new float*[nRows];
	float discourage = INF; //1.0f; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; i normalize costs[][] into [0,1] so we're good w/ discourage=1.0f [sucks w/ 1 for ballerina100015-4; logically, it must be very big anyway; i guess this 1 thing was from old hungarian code requirement]
	//stuff for blossom
	int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e, * edges = new int[2*nEdgeNum], * matches = new int[nVertNum]; //see matches[] as lines of matches where 2 consecutive items make a matching line, e.g. 369 694
	double* weightsD = new double[nEdgeNum];

	//allocate memo only once (and overwrite it during upcoming iters) for efficiency and fill the virtuals; again for efficiency don't refill vIdx1/2 w/ the same info in all iters; do it here for once
	int row = 0;
	for (int i = 0; i < n1; i++)
	{
		costs[row] = new float[nCols]; //nCols = nRows anyway
		vIdx1[row] = tmpBaseVerts[i]; //= tmpBase1
		int col = 0;
		for (int j = 0; j < n2; j++)
			vIdx2[col++] = mesh2->tmpBaseVerts[j]; //tmpBase2
		while (col < nCols) //fill the remaining virtual columns w/ infinitive costs
			costs[row][col++] = discourage;
		row++;
	}
	while (row < nRows) //fill the remaining virtual rows w/ infinitive costs
	{
		costs[row] = new float[nCols]; //nRows = nCols
		for (int col = 0; col < nCols; col++)
			costs[row][col] = discourage;
		row++;
	}

	int m = (int) tmpSafeCorresp->size() / 2, n; //choose n from m (tmpSafeCorresp is the initial corresp list coming from matchTmpBasesInitial())
assert(m >= 5); //typically m is 10 but it can't be < 5 'cos then 5-nested-fors below crash; e.g., when m=5, there is only C(5,5)=1 combination to try
	///////// step 1-2) alternating correspondence b/w tmpBases ////////////////
	int nAlternate = 5; //# of alternating iterations among which the best corresp'll be maintained; what's alternating/changing is the costs[][] to blossom based on changing traversalList tmpSafeCorresp2
	bool reductionNeeded = (m > MAX_M_FOR_COMBINATIONS); //C(m,5) may take too long, so use only the first 15 entries [very rare, but be safe] and select the best from that 15 for traversalList of isometryCost mtrx; so, still accurate enough
	float minCorrespCost = INF; //not just do nAlternate iters and quit as in matchTmpBasesTopn(); remember the minCost corresp (bestTmpSafeCorresp) out of all nAlternate corresps and use/maintain it as tmpSafeCorresp for later
	vector< int >* bestTmpSafeCorresp = NULL;
	for (int iter = 0; iter < nAlternate; iter++)
	{
		///////// step 1) traversalList (tmpSafeCorresp2) creation ////////////////
		//////// top-n selection of traversal list ////////////
		vector< int >* tmpSafeCorrespReduced = new vector< int >; //top-MAX_M_FOR_COMBINATIONS of tmpSafeCorresp'll be put here and C(MAX_M_FOR_COMBINATIONS, 5)'ll be selected from here as traversalList tmpSafeCorresp2
		if (reductionNeeded) //reduce big m to ~10 for efficiency in combinations C(m,5) below
		{
			bool arbitraryReduction = false;
			if (arbitraryReduction)
			{
				//////// arbitrary selection of traversal list ////////////
				m = MAX_M_FOR_COMBINATIONS; //C(m,5) may take too long, so use only the first 10 entries [very rare, but be safe] and select the best from that 10 for traversalList of isometryCost computation; so, still accurate enough
				for (int i = 0; i < (int) tmpSafeCorresp->size(); i++) //tmpSafeCorrespReduced = tmpSafeCorresp effect; so, not picking the first 10 indeed; picking all of them
					tmpSafeCorrespReduced->push_back((*tmpSafeCorresp)[i]);
				//////// arbitrary selection of traversal list ends ////////////
			}
			else
			{
				//////// top-n selection of traversal list ////////////
				//pick the top-MAX_M_FOR_COMBINATIONS elements (w.r.t. isometryCostChosenByBlossom) from adaptList and overwrite the result to adaptList
				vector< int >* adaptListTmp = new vector<int>;
				for (int i = 0; i < (int) tmpSafeCorresp->size(); i++)
					if (i % 2 == 0) //mesh1 verts are at even entries
						verts[ (*tmpSafeCorresp)[i] ]->processed = false; //duplicate prevention in tmpSafeCorrespReduced; processed means added-to-tmpSafeCorrespReduced
				while ((int) tmpSafeCorrespReduced->size() < 2*MAX_M_FOR_COMBINATIONS)
				{
					float minIsoCost = INF;
					int minIsoIdx, minIsoIdx2;
					for (int ss = 0; 2*ss+1 < (int) tmpSafeCorresp->size(); ss++)
					{
						int s1 = (*tmpSafeCorresp)[2*ss];
						if (verts[s1]->isometryCostChosenByBlossom < minIsoCost && ! verts[s1]->processed)
						{
							minIsoCost = verts[s1]->isometryCostChosenByBlossom;
							minIsoIdx = s1;
							minIsoIdx2 = (*tmpSafeCorresp)[2*ss + 1]; //mesh2 side of this match
						}
					} //end of for ss

					//after sorting, ready to fill new adaptListTmp w/ cool entries
					tmpSafeCorrespReduced->push_back(minIsoIdx);
					tmpSafeCorrespReduced->push_back(minIsoIdx2); //matchIdx not set yet (it'll be set later in make1to1()); so, use minIsoIdx2
					verts[minIsoIdx]->processed = true; //added
				}
				m = (int) tmpSafeCorrespReduced->size() / 2;
assert(m == MAX_M_FOR_COMBINATIONS);
				//////// top-n selection of traversal list ends ////////////
			}
		}
		else //no reduction needed 'cos tmpSafeCorresp is already of size ~10; so, use tmpSafeCorresp itself for the creation of tmpSafeCorresp2 through C(m,5) combinations
			for (int i = 0; i < (int) tmpSafeCorresp->size(); i++) //tmpSafeCorrespReduced = tmpSafeCorresp effect
				tmpSafeCorrespReduced->push_back((*tmpSafeCorresp)[i]);
		//////// top-n selection of traversal list ends ////////////

		//try all combinations of size 5 of the initial corresp (of size m = ~10) tmpSafeCorrespReduced; best combination kept as tmpSafeCorresp2 list
		vector< int >* tmpSafeCorresp2 = NULL;
		//choose 5 from m, hence 5-nested loops to try all combinations
		int nCombinations = 0;
		float minIsometryCost = INF;
//assert(m == MAX_M_FOR_COMBINATIONS); passed above; so m is not so big for sure; do the combinations/permutations then
		if (m > 8) //m is big enough (common case), so choose 5 from m
		{
			n = 5; //n and # of for-loops below must match
			for (int a = 0; a < m; a++)
				for (int b = a+1; b < m; b++)
					for (int c = b+1; c < m; c++)
						for (int d = c+1; d < m; d++)
							for (int e = d+1; e < m; e++)
							{
								//current 5-combination is copied from appropriate slots of tmpSafeCorrespReduced to tmpSafeCorrespTry
								vector< int >* tmpSafeCorrespTry = new vector<int>;
								//corresp # 1
								tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*a]);
								tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*a + 1]);
								//corresp # 2
								tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*b]);
								tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*b + 1]);
								//corresp # 3
								tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*c]);
								tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*c + 1]);
								//corresp # 4
								tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*d]);
								tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*d + 1]);
								//corresp # 5
								tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*e]);
								tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*e + 1]);

								//get the overall isometryCost of this current 5-tuple combination
								float isometryCostTry = 0.0f;
								for (int s = 0; s < n; s++) //n=5 is stable 'cos we've 5-nested-fors for C(m,5) combinations
								{
									int tbv1 = (*tmpSafeCorrespTry)[2*s], tbv2 = (*tmpSafeCorrespTry)[2*s + 1];
									float vertexBasedIso = 0.0f;
									for (int ss = 0; ss < n; ss++) //n=5 is stable 'cos we've 5-nested-fors for C(m,5) combinations
									{
										int s1 = (*tmpSafeCorrespTry)[2*ss], s2 = (*tmpSafeCorrespTry)[2*ss + 1];
										if (s1 == tbv1)
											continue;
										//if tbv1 vs. tbv2 is a good correspondence, then geoDist(tbv1, s1) = geoDist(tbv2, s2) for all s1 & s2 in tmpSafeCorrespTry
										float t1s1 = //verts[tbv1]->getGeodesic(s1), t2s2 = mesh2->verts[tbv2]->getGeodesic(s2); //use normalized geodesic values to make this value comparable w/ any input meshes
													 verts[tbv1]->dSpanning[s1], t2s2 = mesh2->verts[tbv2]->dSpanning[s2]; //use normalized geodesic values to make this value comparable w/ any input meshes
										vertexBasedIso += fabs(t1s1 - t2s2); //difference (diffForNormalized) small if tbv1 vs. tbv2 is a good correspondence
assert(t1s1 >= 0 && t1s1 <= 1 && t2s2 >= 0 && t2s2 <= 1);
									}
									vertexBasedIso /= (n-1);
									isometryCostTry += vertexBasedIso;
								} //end of s
								//overall isometry cost of this tmpSafeCorrespTry combination
								//isometryCost /= n; //scSize [save time by skipping division since i only need comparison below; could've been done above for n-1 too but anyway]

								if (isometryCostTry < minIsometryCost)
								{
									minIsometryCost = isometryCostTry;
									if (tmpSafeCorresp2) //clear previous effects
										delete tmpSafeCorresp2; //not set to NULL 'cos it'll be non-NULL just-below anyway
									tmpSafeCorresp2 = new vector<int>; //start a brand new tmpSafeCorresp2
									for (int i = 0; i < (int) tmpSafeCorrespTry->size(); i++) //tmpSafeCorresp2 = tmpSafeCorrespTry effect by explicit copy
										tmpSafeCorresp2->push_back((*tmpSafeCorrespTry)[i]);
								}
								//recapture memo
								delete tmpSafeCorrespTry;
#ifdef DBG_DETAIL
								nCombinations++; //just for info
#endif
							} //end of e
		}
		else //m = 5/6/7/8 which makes choosing 5 erronous; for such cases choose 4 from m
		{
			n = 4; //n and # of for-loops below must match
			for (int a = 0; a < m; a++)
				for (int b = a+1; b < m; b++)
					for (int c = b+1; c < m; c++)
						for (int d = c+1; d < m; d++)
						{
							//current 4-combination is copied from appropriate slots of tmpSafeCorrespReduced to tmpSafeCorrespTry
							vector< int >* tmpSafeCorrespTry = new vector<int>;
							//corresp # 1
							tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*a]);
							tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*a + 1]);
							//corresp # 2
							tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*b]);
							tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*b + 1]);
							//corresp # 3
							tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*c]);
							tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*c + 1]);
							//corresp # 4
							tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*d]);
							tmpSafeCorrespTry->push_back((*tmpSafeCorrespReduced)[2*d + 1]);

							//get the overall isometryCost of this current 4-tuple combination
							float isometryCostTry = 0.0f;
							for (int s = 0; s < n; s++) //n=5 is stable 'cos we've 5-nested-fors for C(m,5) combinations
							{
								int tbv1 = (*tmpSafeCorrespTry)[2*s], tbv2 = (*tmpSafeCorrespTry)[2*s + 1];
								float vertexBasedIso = 0.0f;
								for (int ss = 0; ss < n; ss++) //n=5 is stable 'cos we've 5-nested-fors for C(m,5) combinations
								{
									int s1 = (*tmpSafeCorrespTry)[2*ss], s2 = (*tmpSafeCorrespTry)[2*ss + 1];
									if (s1 == tbv1)
										continue;
									//if tbv1 vs. tbv2 is a good correspondence, then geoDist(tbv1, s1) = geoDist(tbv2, s2) for all s1 & s2 in tmpSafeCorrespTry
									float t1s1 = //verts[tbv1]->getGeodesic(s1), t2s2 = mesh2->verts[tbv2]->getGeodesic(s2); //use normalized geodesic values to make this value comparable w/ any input meshes
												 verts[tbv1]->dSpanning[s1], t2s2 = mesh2->verts[tbv2]->dSpanning[s2]; //use normalized geodesic values to make this value comparable w/ any input meshes
									vertexBasedIso += fabs(t1s1 - t2s2); //difference (diffForNormalized) small if tbv1 vs. tbv2 is a good correspondence
assert(t1s1 >= 0 && t1s1 <= 1 && t2s2 >= 0 && t2s2 <= 1);
								}
								vertexBasedIso /= (n-1);
								isometryCostTry += vertexBasedIso;
							} //end of s
							//overall isometry cost of this tmpSafeCorrespTry combination
							//isometryCost /= n; //scSize [save time by skipping division since i only need comparison below; could've been done above for n-1 too but anyway]

							if (isometryCostTry < minIsometryCost)
							{
								minIsometryCost = isometryCostTry;
								if (tmpSafeCorresp2) //clear previous effects
									delete tmpSafeCorresp2; //not set to NULL 'cos it'll be non-NULL just-below anyway
								tmpSafeCorresp2 = new vector<int>; //start a brand new tmpSafeCorresp2
								for (int i = 0; i < (int) tmpSafeCorrespTry->size(); i++) //tmpSafeCorresp2 = tmpSafeCorrespTry effect by explicit copy
									tmpSafeCorresp2->push_back((*tmpSafeCorrespTry)[i]);
							}
							//recapture memo
							delete tmpSafeCorrespTry;
#ifdef DBG_DETAIL
							nCombinations++; //just for info
#endif
						} //end of d
		}
#ifdef DBG_DETAIL
		cout << "C(" << m << ", " << n << ") = " << nCombinations << " combinations tried and best traversalList selected\n";
		cout << "traversalList to be traversed by tmpBases at iter" << iter << ":\n"; //see the best selection
		for (int ss = 0; 2*ss+1 < (int) tmpSafeCorresp2->size(); ss++)
			cout << (*tmpSafeCorresp2)[2*ss] << "->" << (*tmpSafeCorresp2)[2*ss + 1] << endl;
		cout << "additions to traversalList:\n";
#endif
		//in addition to best-5 of the current/this patches' corresp, use also the parent patches' corresp in the traversal list to increase its quality
assert((int) safeCorrespsAdaptive->size() > 1); //set in matchTmpBasesInitial()
		for (int ss = 0; 2*ss+1 < (int) safeCorrespsAdaptive->size(); ss++)
		{
			int s1 = (*safeCorrespsAdaptive)[2*ss], s2 = (*safeCorrespsAdaptive)[2*ss + 1];
			tmpSafeCorresp2->push_back(s1);
			tmpSafeCorresp2->push_back(s2); //dijkstra already computed on safeCorrespsAdaptive so that dSpanning defined and can safely be used below
#ifdef DBG_DETAIL
			cout << s1 << "->" << s2 << endl;
#endif
		}
		///////// step 1) traversalList (tmpSafeCorresp2) creation ends ////////////////

		///////// step 2) tmpSafeCorresp b/w bases for this iter ////////////////
//same as step 2) of matchTmpBasesTopn()
		//create the ultimate tmpSafeCorresp to be used in return using the ultimate traversalList computed above
		int scSize = (int) tmpSafeCorresp2->size() / 2; //important to use the size of the tmpSafeCorresp2 list since it'll be used for isometryCost computation from now on
assert(scSize > 1); //no div by zero below
		//initial (or prev alternating iter's) corresp info is now irrelevant; clear that effect and keep the new alternating corresps in this fresh list
		delete tmpSafeCorresp; //no preceding if (tmpSafeCorresp) 'cos tmpSafeCorresp not NULL for sure, i.e. used above already
		delete tmpSafeCorrespReduced;
		tmpSafeCorresp = new vector<int>; //tmpSafeCorresp size=0 now
		int row = 0;
		for (int i = 0; i < n1; i++)
		{
			int tmpBase1 = tmpBaseVerts[i];
			verts[tmpBase1]->isometryCosts.clear(); //clear stuff from initial corresp and previous iters which (re)fills this list

			//find the unmatched mesh2.tmpBase[j] whose dist2 to closestBase2 is as close to dist1 as possible; to do so globally, use blossom matching
			int col = 0;
			for (int j = 0; j < n2; j++)
			{
				int tmpBase2 = mesh2->tmpBaseVerts[j];

				//tmpBase1 (vIdx1[row]) vs. tmpBase2 (vIdx2[col]) active assumption
				float vertexBasedIso = 0.0f; //for vIdx1[r] (under the assumption that it is paired w/ vIdx2[c]
				for (int ss = 0; 2*ss+1 < (int) tmpSafeCorresp2->size(); ss++)
				{
//info: tmpSafeCorresp2 is of size n=5 whereas # of tmpBases n1/2 is more, say ~10
					int s1 = (*tmpSafeCorresp2)[2*ss], s2 = (*tmpSafeCorresp2)[2*ss + 1];
					if (s1 == tmpBase1)
						continue;

					float s1t1 = verts[s1]->dSpanning[tmpBase1], s2t2 = mesh2->verts[s2]->dSpanning[tmpBase2];
					vertexBasedIso += fabs(s1t1 - s2t2); //difference (diffForNormalized) small if tmpBase1 vs. tmpBase2 is a good correspondence
assert(s1t1 >= 0 && s1t1 <= 1 && s2t2 >= 0 && s2t2 <= 1);
				}
				vertexBasedIso /= (scSize-1); //scSize # of time += fabs done above each of which in [0,1]; so, after /=, vertexBasedIso in [0,1] for sure
				costs[row][col++] = vertexBasedIso; //overwrite the costs[][] for initial corresp above
//cout << tmpBase1 << " &&&&&&& " << tmpBase2 << ": " << vertexBasedIso << endl;
				
				//update isometryCosts only for the last alternating iteration 'cos the ones before that will not yield the ultimate corresps; they're just a path to this last iteration that yields the ultimate corresps
//				if (iter == nAlternate-1) no, not only for the last iteration 'cos i need isometryCostChosenByBlossom value (set by isometryCosts[]) at intermediate iterations' sorting above
//				{
					//remember in this format for use in 1-to-1 conversion in make1to1(): for 4471->4388 w/ cost 0.019232, verts[4471]->isometryCosts[x] = 4388, [x+1] = 0.019232
					verts[tmpBase1]->isometryCosts.push_back((float) tmpBase2);
					verts[tmpBase1]->isometryCosts.push_back(vertexBasedIso);
//				}
			} //end of j
			//not filling the remaining virtual columns w/ infinitive costs 'cos it's already done at top
			row++;
		} //end of i
//not filling the remaining virtual rows w/ infinitive costs 'cos it's already done at top

		e = 0;
		for (int i = 0; i < nRows; i++)
			for (int j = 0; j < nCols; j++)
			{
				edges[2*e] = i;
				edges[2*e+1] = nRows+j;
				weightsD[e++] = (double) costs[i][j];
			}
		//blossom output
		Blossom* blossom = new Blossom();
		blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches); //matches update by call-by-ref
		delete blossom;

		//transform data in matches[] to tmpSafeCorresp.push_back's consistently..
		for (int m = 0; m < nRows; m++)
		{
			int m1 = matches[2*m]; //left side of the (m+1)'th matching line
			if (m1 < n1)
			{
				int m2 = matches[2*m + 1] - nRows; //mathches[m1 + 1], right side of the line, is in [nRows..2*nRows-1], hence nRows entry at mesh2
				int mi = (m2 < n2 ? vIdx2[m2] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1
				if (mi != -1)
				{
					//the correspondences
					tmpSafeCorresp->push_back(vIdx1[m1]);
					tmpSafeCorresp->push_back(mi);
#ifdef DBG_DETAIL
					cout << vIdx1[m1] << " =====> " << mi << "\t[" << iter << "].";
#endif
					//extraly, need to update isometryCostChosenByBlossom which'll be used in the next iter for sorting
					for (int i = 0; 2*i+1 < (int) verts[ vIdx1[m1] ]->isometryCosts.size(); i++)
						if (verts[ vIdx1[m1] ]->isometryCosts[2*i] == mi) //got the related cost
						{
							verts[ vIdx1[m1] ]->isometryCostChosenByBlossom = verts[ vIdx1[m1] ]->isometryCosts[2*i + 1];
#ifdef DBG_DETAIL
							cout << verts[ vIdx1[m1] ]->isometryCostChosenByBlossom << "\n";
#endif
							break;
						}
				}
			}
		} //end of m
		//..and compute its error, in order to help keep it later if it's the current best so far
		float tmpSafeCorrespErr = computeTmpSafeCorrespErr(mesh2);
		//keep tmpSafeCorresp if it's the current best so far; by keeping i mean remembering it in bestTmpSafeCorresp and roll-back to it after nAlternate-loop
		if (tmpSafeCorrespErr < minCorrespCost)
		{
			if (bestTmpSafeCorresp)
				delete bestTmpSafeCorresp;
			bestTmpSafeCorresp = new vector<int>; //clear to a fresh start of size 0
			for (int s = 0; s < (int) tmpSafeCorresp->size(); s++) //bestTmpSafeCorresp = tmpSafeCorresp effect to roll-back to it after nAlternate-loop
			{
				bestTmpSafeCorresp->push_back((*tmpSafeCorresp)[s]);
				if (s % 2 == 0) //access only to even entries which are mesh1 verts
					verts[(*tmpSafeCorresp)[s]]->bestIsometryCosts = verts[(*tmpSafeCorresp)[s]]->isometryCosts;
			}
			minCorrespCost = tmpSafeCorrespErr;
		} //end of if tmpSafeCorrespErr < minCorrespCost
assert((int) tmpSafeCorresp->size() / 2 == (n1 < n2 ? n1 : n2)); //small of n1 & n2 must be matched all
		delete tmpSafeCorresp2; //memo recapture

#ifdef DBG_DETAIL
		//use value printed below for an early break of this loop [delLater for efficieny]
		cout << "overall isometryCost of the intermediate tmpSafeCorresp: " << tmpSafeCorrespErr << "\n\n";
#endif
		///////// step 2) tmpSafeCorresp b/w bases for this iter ends ////////////////
	} //end of iter

	//roll-back to the bestTmpSafeCorresp
	delete tmpSafeCorresp;
	tmpSafeCorresp = new vector<int>; //fresh start w/ size 0 list
	int bcSize = (int) bestTmpSafeCorresp->size() / 2;
	for (int i = 0; i < bcSize; i++)
	{
		int best1 = (*bestTmpSafeCorresp)[2*i], best2 = (*bestTmpSafeCorresp)[2*i + 1];
assert(best1 != -1 && best2 != -1);
		//the correspondences
		tmpSafeCorresp->push_back(best1);
		tmpSafeCorresp->push_back(best2);
#ifdef DBG_DETAIL
		cout << best1 << " ==best==> " << best2 << "\t";
#endif

		//update matchIdxs w/ bestTmpSafeCorresp 'cos the ones before that will not yield the ultimate corresps; they're just a path to this best corresp that yields the ultimate corresps
		//remember the corresponding isometryCost used above for 1-to-1 conversion in make1to1() later
		for (int i = 0; 2*i+1 < (int) verts[best1]->bestIsometryCosts.size(); i++)
			if (verts[best1]->bestIsometryCosts[2*i] == best2) //got the related cost
			{
				verts[best1]->isometryCostChosenByBlossom = verts[best1]->bestIsometryCosts[2*i + 1];
				//verts[best1]->tmpMatchIdx = best2; //to get the other side ofbest1in case it makes to top5 [not used here in C(10, 5) case but anyway]
#ifdef DBG_DETAIL
				cout << verts[best1]->isometryCostChosenByBlossom << "\n";
#endif
				//for 1-to-1 conversion in make1to1() later
				verts[best1]->matchIdxs.push_back((float) best2);
				verts[best1]->matchIdxs.push_back(verts[best1]->isometryCostChosenByBlossom); //cost of this match; if this turns out to be the best cost, then verts[best1]->matchIdx = vIdx2[m2]'ll be done in make1to1()
				mesh2->verts[best2]->matchIdxs.push_back((float) best1); //make1to1() works on mesh2.matchIdxs too for force1to1=2 case
				mesh2->verts[best2]->matchIdxs.push_back(verts[best1]->isometryCostChosenByBlossom);
				verts[best1]->residue = mesh2->verts[best2]->residue = false; //set here as a result of blossom matching; the one(s) left residue=true (inited at matchTmpBasesByGraph()) will be matched below

				break;
			}
	} //end of i
assert(computeTmpSafeCorrespErr(mesh2) == minCorrespCost);
#ifdef DBG_DETAIL
		cout << "overall isometryCost of the best tmpSafeCorresp chosen: " << minCorrespCost << "\n\n";
#endif

	//memo re-capture
	delete [] matches;
	delete [] edges;
	delete [] weightsD;
	for (int i = 0; i < nRows; i++) delete [] costs[i];		delete [] costs;
	delete [] vIdx1;
	delete [] vIdx2;
	///////// step 1-2) alternating correspondence b/w tmpBases ends ////////////////

	//////////////// residue stuff ///////////////////
	//i could have done below w/o if-else using the from/toMesh but anyway; this is easier and more explanatory
	if ((int) mesh2->tmpBaseVerts.size() < (int) tmpBaseVerts.size()) //mesh2.size is less so that mesh1.vert remained unmatched here (insufficient candidates in mesh2); so, fromMesh = smaller mesh = mesh2 for sure
		for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
		{
			int tbv1 = tmpBaseVerts[i];
			if (! verts[tbv1]->residue) //tbv1 already has a match candidate coming from above, e.g. for the 3 vs. 2 patch case, 2 vs. 2 is set above via permutations, and 1 residue in patch1 is going below by escaping from this continue
				continue;

			//no match created above for this mesh1 vert (also it's not shared 'cos o/w it might have an entry from a previous patch that would have made matchIdxs nonempty here); so, create 1 good match using the good correspondence created above as the safeList
			float minMatchCostIResidue = INF;
			int bestMatchForResidue = -1;
			for (int j = 0; j < (int) mesh2->tmpBaseVerts.size(); j++) //try tbv1 vs. next tmpBaseVerts from mesh2
			{
				float vertexBasedIso = 0.0f;
				for (int s = 0; s < bcSize; s++) //to try tbv1 vs. mesh2.tmpBaseVerts[j], use bestTmpSafeCorresp as the safeList
				{
					int s1 = (*bestTmpSafeCorresp)[2*s], s2 = (*bestTmpSafeCorresp)[2*s + 1]; //even idx for mesh1, odds for mesh2
assert(s1 != tbv1); //'cos if tbv1 was in bestTmpSafeCorresp, then tbv1.matchIdxs wouldn't be empty and i'd not come here; so, tbv1 not in bestCorresp for sure
					if (s2 == mesh2->tmpBaseVerts[j]) //prevent dSpanning = 0 case not to confuse the vertexBasedIso calculation
						continue;

					//if tbv1 vs. mesh2.tmpBaseVerts[j] is a good correspondence, then geoDist(tbv1, s1) = geoDist(mesh2.tmpBaseVerts[j], s2) for all s1 & s2 in bestCorresp, where s1 ~ mesh1, s2 ~ mesh2
					float t1s1 = verts[tbv1]->dSpanning[s1], t2s2 = mesh2->verts[ mesh2->tmpBaseVerts[j] ]->dSpanning[s2];
					vertexBasedIso += fabs(t1s1 - t2s2); //normalized difference here is small if tbv1 vs. mesh2->tmpBaseVerts[j] is a good correspondence
assert(t1s1 >= 0 && t1s1 <= 1 && t2s2 >= 0 && t2s2 <= 1);
				}
				if (bcSize != 1) //no div by zero
					vertexBasedIso /= (bcSize-1); //-1 'cos s2 skipped/continue'd one time
				else
					vertexBasedIso = 0.0f; //due to symmetry problem, all costs will be the same and hence best selected arbitrarily

				if (vertexBasedIso < minMatchCostIResidue) //tbv1 vs. mesh2->tmpBaseVerts[j] is the best match so far
				{
					bestMatchForResidue = mesh2->tmpBaseVerts[j];
					minMatchCostIResidue = vertexBasedIso;
				}
			} //end of j
			verts[tbv1]->matchIdxs.push_back((float) bestMatchForResidue);
			verts[tbv1]->matchIdxs.push_back(minMatchCostIResidue);
			mesh2->verts[bestMatchForResidue]->matchIdxs.push_back((float) tbv1); //dual op
			mesh2->verts[bestMatchForResidue]->matchIdxs.push_back(minMatchCostIResidue);
		}
	else //mesh1.size is less so that mesh2.vert remained unmatched here (insufficient candidates in mesh1); so, fromMesh = smaller mesh = mesh1 for sure
		for (int i = 0; i < (int) mesh2->tmpBaseVerts.size(); i++)
		{
			int tbv2 = mesh2->tmpBaseVerts[i];
			if (! mesh2->verts[tbv2]->residue) //tbv2 already has a match candidate coming from above, e.g. for the 2 vs. 3 patch case, 2 vs. 2 is set above via permutations, and 1 residue in patch2 is going below by escaping from this continue
				continue;

			//no match created above for this mesh2 vert (also it's not shared 'cos o/w it might have an entry from a previous patch that would have made matchIdxs nonempty here); so, create 1 good match using the good correspondence created above as the safeList
			float minMatchCostIResidue = INF;
			int bestMatchForResidue = -1;
			for (int j = 0; j < (int) tmpBaseVerts.size(); j++) //try next tmpBaseVerts from mesh1 vs. tbv2
			{
				float vertexBasedIso = 0.0f;
				for (int s = 0; s < bcSize; s++) //to try next tmpBaseVerts from mesh1 (tmpBaseVerts[j]) vs. tbv2, use bestCorresp as the safeList
				{
					int s1 = (*bestTmpSafeCorresp)[2*s], s2 = (*bestTmpSafeCorresp)[2*s + 1]; //even idx for fromMesh (this/mesh1), odds for toMesh; so, s1 ~ fromMesh, s2 ~ toMesh; since fromMesh == mesh1 here for sure, s2 ~ mesh2
assert(s2 != tbv2); //'cos if tbv2 was in bestCorresp, then tbv2.matchIdxs wouldn't be empty and i'd not come here; so, tbv2 not in bestCorresp for sure
					if (s1 == tmpBaseVerts[j]) //prevent dSpanning = 0 case not to confuse the vertexBasedIso calculation
						continue;

					//if tmpBaseVerts[j] vs. tbv2 is a good correspondence, then geoDist(tmpBaseVerts[j] , s1) = geoDist(tbv2, s2) for all s1 & s2 in bestCorresp, where s1 ~ mesh1, s2 ~ mesh2
					float t1s1 = verts[ tmpBaseVerts[j] ]->dSpanning[s1], t2s2 = mesh2->verts[tbv2]->dSpanning[s2];
					vertexBasedIso += fabs(t1s1 - t2s2); //normalized difference here is small if tmpBaseVerts[j] vs. tbv2 is a good correspondence
assert(t1s1 >= 0 && t1s1 <= 1 && t2s2 >= 0 && t2s2 <= 1);
				}
				if (bcSize != 1) //no div by zero
					vertexBasedIso /= (bcSize-1); //-1 'cos s2 skipped/continue'd one time
				else
					vertexBasedIso = 0.0f; //due to symmetry problem, all costs will be the same and hence best selected arbitrarily

				if (vertexBasedIso < minMatchCostIResidue) //tmpBaseVerts[j] vs. tbv2 is the best match so far
				{
					bestMatchForResidue = tmpBaseVerts[j];
					minMatchCostIResidue = vertexBasedIso;
				}
			} //end of j
			mesh2->verts[tbv2]->matchIdxs.push_back((float) bestMatchForResidue);
			mesh2->verts[tbv2]->matchIdxs.push_back(minMatchCostIResidue);
			verts[bestMatchForResidue]->matchIdxs.push_back((float) tbv2); //dual op
			verts[bestMatchForResidue]->matchIdxs.push_back(minMatchCostIResidue);
		}
	//////////////// residue stuff ends ///////////////////
	if (bestTmpSafeCorresp)
		delete bestTmpSafeCorresp;
}

float Mesh::computeTmpSafeCorrespErr(Mesh* mesh2)
{
	//sets the overall isometryCost of tmpSafeCorresp list [heavily adapted from setCorrespondenceErrs()]

	float isometryCost = 0.0f;
	int scSize = (int) tmpSafeCorresp->size() / 2; //final size of safeCorresp to be used for averaging; div by 2 'cos every 2 item counts 1 'cos they're related
	if (scSize == 1) //prevent div by 0 below; can't compute the quality of tmpSafeCorresp that has only 1 match in it
		return isometryCost;
	for (int s = 0; 2*s+1 < (int) tmpSafeCorresp->size(); s++)
	{
		int tbv1 = (*tmpSafeCorresp)[2*s], tbv2 = (*tmpSafeCorresp)[2*s + 1];
		float vertexBasedIso = 0.0f;
		for (int ss = 0; 2*ss+1 < (int) tmpSafeCorresp->size(); ss++)
		{
			int s1 = (*tmpSafeCorresp)[2*ss], s2 = (*tmpSafeCorresp)[2*ss + 1];
			if (s1 == tbv1)
				continue;

			float t1s1 = //verts[tbv1]->getGeodesic(s1), t2s2 = mesh2->verts[tbv2]->getGeodesic(s2);
						 verts[tbv1]->dSpanning[s1], t2s2 = mesh2->verts[tbv2]->dSpanning[s2];
			vertexBasedIso += fabs(t1s1 - t2s2);
		}
		vertexBasedIso /= (scSize-1);
		isometryCost += vertexBasedIso;
	}
	//global isometry cost
	isometryCost /= scSize;

	return isometryCost;
}

bool Mesh::adaptiveListForIsometryCost(int bv1, int bv2, int level, Mesh* mesh2)
{
	//creates and returns the safeCorrespsAdaptive to be used by isometryCost computation in return, where it consists some bases w.r.t. strategy; here bv1/2 
	//are baseVerts and they just produced tmpBases within their patches; for the initial match of these tmpBases (of level k) i need an adaptiveList based on baseVerts (of level k-1)
	//strategy=0: safeCorrespsAdaptive consists of 5 best initial corresps plus bv1-2 [not in use]
	//strategy=1: safeCorrespsAdaptive consists of 5 best (C(~10,5)) matches of bv1/2.parentPatch; sometimes 4 best used if ~10 <= 8; sometimes 2/3/4 matches from parent used; sometimes 5 best initial spectral corresps used

#ifdef DBG_DETAIL
	cout << "\nadaptive safe corresp list being created to do the initial match with..\n";
#endif

	bool needInitial = false;
	if (safeCorrespsAdaptive)
		delete safeCorrespsAdaptive; //not set to NULL 'cos it'll be non-NULL just-below anyway
	safeCorrespsAdaptive = new vector<int>; //of size 0 now
	if (adaptStrategy == 1)
	{
		//grand parent patch
		if (level == 1) //no parentPatch available in level 1 (recall level 0 is the spectral matching)
			for (int i = 0; i < (int) initialCorresp->size(); i++) //safeCorrespsAdaptive = initialCorresp effect by explicit copying (do this o/w when i delete safeCorrespsAdaptive, i'd also delete initialCorresp which may be used later)
				safeCorrespsAdaptive->push_back((*initialCorresp)[i]); //(*..) is dereferencing
		else
		{
			//use adaptList refilled by computeTmpBaseVerts2() and pick the best of C(~10,5) combinations as safeCorrespsAdaptive to be used later in return
			int m = (int) adaptList->size() / 2;
			////////// transfer mesh2.adaptList to mesh1.adaptList ///////////////
			//although it seems that mesh1 and mesh2 parentPatches must be the same, it's rarely not the case; assume 75 has 1302 as parentPatch in level1; in level2 47 has 75 as a newcomer and hence 75.parentPatch = 42, which is currently unmatched (matchIdx=-1 for all verts)
			//509 of level1's 75-509 match, on the other hand, not a newcomer in level2 (yet) so its patch is still say 1444 from level1; so, 75 and 509 have 2 different parentPatches now
			if (m == 0 || m == 1) //then try the other adaptList due to bv2, i.e., corresps of bv2.parentPatch set by computeTmpBaseVerts2() before calling here
			{
				delete adaptList;
				adaptList = new vector<int>; //clear to a fresh start of size 0 here
				for (int i = 0; i < (int) mesh2->adaptList->size() / 2; i++) //adaptList ~= mesh2.adaptList effect by swapping places since mesh2.adaptList is kinda wrong, i.e. mesh2 vert precedes mesh1 vert; no problem 'cos it's not used (except here)
				{
					adaptList->push_back((*mesh2->adaptList)[2*i + 1]);
					adaptList->push_back((*mesh2->adaptList)[2*i]);
				}
				m = (int) adaptList->size() / 2;
			} //if m still 0 or 1, then else if part will execute below, which is not a problem at all (m = 2/3/4 not transfer mesh2 data above 'cos they're already big enough for a good traversalList)*/
			////////// transfer mesh2.adaptList to mesh1.adaptList ends ///////////////
			if (m >= 5) //typically m is ~10 but it can't be < 5 for C(10,5) 'cos then 5-nested-fors below crash; e.g., when m=5, there is only C(5,5)=1 combination to try
			{
				if (m > MAX_M_FOR_COMBINATIONS) //reduce big m to ~10 for efficiency in combinations C(m,5) below
				{
					bool arbitraryReduction = false;
					if (arbitraryReduction)
						//////// arbitrary selection of traversal list ////////////
						m = MAX_M_FOR_COMBINATIONS; //C(m,5) may take too long, so use only the first 15 entries [very rare, but be safe] and select the best from that 15 for traversalList of isometryCost computation; so, still accurate enough
						//////// arbitrary selection of traversal list ends ////////////
					else
					{
						//////// top-n selection of traversal list ////////////
						//pick the top-MAX_M_FOR_COMBINATIONS elements (w.r.t. isometryCostChosenByBlossom) from adaptList and overwrite the result to adaptList
						vector< int >* adaptListTmp = new vector<int>;
						for (int i = 0; i < (int) adaptList->size(); i++)
							if (i % 2 == 0) //mesh1 verts are at even entries
								verts[ (*adaptList)[i] ]->processed = false; //duplicate prevention in adaptListTmp; processed means added-to-adaptListTmp
						while ((int) adaptListTmp->size() < 2*MAX_M_FOR_COMBINATIONS)
						{
							float minIsoCost = INF;
							int minIsoIdx, minIsoIdx2;
							for (int ss = 0; 2*ss+1 < (int) adaptList->size(); ss++)
							{
								int s1 = (*adaptList)[2*ss];
								if (verts[s1]->isometryCostChosenByBlossom < minIsoCost && ! verts[s1]->processed)
								{
									minIsoCost = verts[s1]->isometryCostChosenByBlossom;
									minIsoIdx = s1;
									minIsoIdx2 = (*adaptList)[2*ss + 1]; //mesh2 side of this match
								}
							} //end of for ss

							//after sorting, ready to fill new adaptListTmp w/ cool entries
							adaptListTmp->push_back(minIsoIdx);
							adaptListTmp->push_back(minIsoIdx2); //matchIdx not set yet (it'll be set later in make1to1()); so, use minIsoIdx2
							verts[minIsoIdx]->processed = true; //added
						}
						delete adaptList; //non-NULL for sure (used above); so, delete directly
						adaptList = new vector<int>; //clear to a fresh start of size 0 here
						for (int i = 0; i < (int) adaptListTmp->size(); i++) //adaptList = adaptListTmp effect
							adaptList->push_back((*adaptListTmp)[i]);
						delete adaptListTmp;
						m = (int) adaptList->size() / 2;
assert(m == MAX_M_FOR_COMBINATIONS);
						//////// top-n selection of traversal list ends ////////////
					}
				}

				for (int i = 0; i < m; i++)
					//dijkstraShortestPaths((*adaptList)[2*i], farThrshld * r1); //r1 = rOfPrevLevel for sure (not so safe 'cos .. [see farThrshld declaration comment])
					dijkstraShortestPaths((*adaptList)[2*i]); //be safe/accurate but not that efficient (will be called if required so we're still safe)
				for (int j = 0; j < m; j++)
					//mesh2->dijkstraShortestPaths((*adaptList)[2*j + 1], farThrshld * r2);
					mesh2->dijkstraShortestPaths((*adaptList)[2*j + 1]);

				//choose 5 from m, hence 5-nested loops to try all combinations
				float minIsometryCost = INF;
				vector< int >* bestCorresp = NULL;
				if (m > 8) //m is big enough (common case), so choose 5 from m
					for (int a = 0; a < m; a++)
						for (int b = a+1; b < m; b++)
							for (int c = b+1; c < m; c++)
								for (int d = c+1; d < m; d++)
									for (int e = d+1; e < m; e++)
									{
										//current 5-combination is copied from appropriate slots of initialCorresp to initialCorrespTry
										vector< int >* adaptCorrespTry = new vector<int>;
										//corresp # 1
										adaptCorrespTry->push_back((*adaptList)[2*a]);
										adaptCorrespTry->push_back((*adaptList)[2*a + 1]);
										//corresp # 2
										adaptCorrespTry->push_back((*adaptList)[2*b]);
										adaptCorrespTry->push_back((*adaptList)[2*b + 1]);
										//corresp # 3
										adaptCorrespTry->push_back((*adaptList)[2*c]);
										adaptCorrespTry->push_back((*adaptList)[2*c + 1]);
										//corresp # 4
										adaptCorrespTry->push_back((*adaptList)[2*d]);
										adaptCorrespTry->push_back((*adaptList)[2*d + 1]);
										//corresp # 5
										adaptCorrespTry->push_back((*adaptList)[2*e]);
										adaptCorrespTry->push_back((*adaptList)[2*e + 1]);

										float isometryCostTry = getErrOfCorrespList(mesh2, *adaptCorrespTry, false);
										if (isometryCostTry < minIsometryCost)
										{
											minIsometryCost = isometryCostTry;
											if (bestCorresp) //clear previous effects
												delete bestCorresp;
											bestCorresp = new vector<int>; //start a brand new bestCorresp of size 0
											for (int i = 0; i < (int) adaptCorrespTry->size(); i++) //bestCorresp = adaptCorrespTry effect by explicit copy
												bestCorresp->push_back((*adaptCorrespTry)[i]);
										}
										//recapture memo
										delete adaptCorrespTry;
									} //end of e
				else //m = 5/6/7/8 which makes choosing 5 erronous, i.e. too much; for such cases choose 4 from m
					for (int a = 0; a < m; a++)
						for (int b = a+1; b < m; b++)
							for (int c = b+1; c < m; c++)
								for (int d = c+1; d < m; d++)
								{
									//current 4-combination is copied from appropriate slots of initialCorresp to initialCorrespTry
									vector< int >* adaptCorrespTry = new vector<int>;
									//corresp # 1
									adaptCorrespTry->push_back((*adaptList)[2*a]);
									adaptCorrespTry->push_back((*adaptList)[2*a + 1]);
									//corresp # 2
									adaptCorrespTry->push_back((*adaptList)[2*b]);
									adaptCorrespTry->push_back((*adaptList)[2*b + 1]);
									//corresp # 3
									adaptCorrespTry->push_back((*adaptList)[2*c]);
									adaptCorrespTry->push_back((*adaptList)[2*c + 1]);
									//corresp # 4
									adaptCorrespTry->push_back((*adaptList)[2*d]);
									adaptCorrespTry->push_back((*adaptList)[2*d + 1]);

									float isometryCostTry = getErrOfCorrespList(mesh2, *adaptCorrespTry, false);
									if (isometryCostTry < minIsometryCost)
									{
										minIsometryCost = isometryCostTry;
										if (bestCorresp) //clear previous effects
											delete bestCorresp;
										bestCorresp = new vector<int>; //start a brand new bestCorresp of size 0
										for (int i = 0; i < (int) adaptCorrespTry->size(); i++) //bestCorresp = adaptCorrespTry effect by explicit copy
											bestCorresp->push_back((*adaptCorrespTry)[i]);
									}
									//recapture memo
									delete adaptCorrespTry;
								} //end of d

				//safeCorrespsAdaptive is of size 5 now, the best 5-tuple fetched from the initial m matches
				safeCorrespsAdaptive = bestCorresp;
			}
			else if (m == 0 || m == 1) //added m=1 later 'cos i now divide vertexBasedIso by scSize-1 (correct way) and safeCorrespAdaptive of size 1 would make div by zero error
			{
				//it may rarely be the case that m=0; happens like this: say 754's patch (p1) at level1 not subdivided; 754.parentPatch = p1
				//is done as usual; in level2, 754.patch will again be same as p1 and i now sample ~10 verts, say V, on it (who has no matches yet (assume none of the newcomers are shared tmpBases));
				//754.parentPatch'll give p1 and on p1 there's no match to use as initial match 'cos V's not assigned yet; in that case, use initialCorresp for the initial match (of V)
				for (int i = 0; i < (int) initialCorresp->size(); i++) //safeCorrespsAdaptive = initialCorresp effect by explicit copying (do this o/w when i delete safeCorrespsAdaptive, i'd also delete initialCorresp which may be used later)
					safeCorrespsAdaptive->push_back((*initialCorresp)[i]); //(*..) is dereferencing
				needInitial = true;
			}
			else //1 < m < 5 case
				for (int i = 0; i < (int) adaptList->size(); i++) //safeCorrespsAdaptive = adaptList effect by explicit copying
					safeCorrespsAdaptive->push_back((*adaptList)[i]); //(*..) is dereferencing
		}
	}
	else if (adaptStrategy == 0)
	{
		for (int i = 0; i < (int) initialCorresp->size(); i++) //safeCorrespsAdaptive = initialCorresp effect by explicit copying (do this o/w when i delete safeCorrespsAdaptive, i'd also delete initialCorresp which may be used later)
			safeCorrespsAdaptive->push_back((*initialCorresp)[i]); //(*..) is dereferencing
		safeCorrespsAdaptive->push_back(bv1);
		safeCorrespsAdaptive->push_back(bv2);
	}
	else
	{
		cout << "undefined adaptive list creation strategy\n";
		exit(0);
	}

#ifdef DBG_DETAIL
	cout << "initial corresp of these patches'll be based on this safe list:\n";
	for (int ss = 0; 2*ss+1 < (int) safeCorrespsAdaptive->size(); ss++)
		cout << (*safeCorrespsAdaptive)[2*ss] << " -_-_-> " << (*safeCorrespsAdaptive)[2*ss + 1] << endl;
	cout << "done!\n";
#endif
	return needInitial;
}

//#define HUNGARIAN_INSTEAD_OF_BLOSSOM //if not defined, use more efficient blossom matching instead of hungarian for the min-weight perfect matching problem both in pairUpPatchesByInterpolation() and in isometryBasedGraphMatching()
void Mesh::pairUpPatchesByGraphMatching(Mesh* mesh2, FILE* fPtrU, int bv1, int bv2)
{
	//when bv1.patchSize > bv2.patchSize, nearest matching in pairUpPatchesByInterpolation() consumes bv2's good vertices by pairing them w/
	//bv1's initial vertices; if those initial verts of bv1 are actually not good, then we consumed bv2's good vertices badly; so, it is always
	//good to make matching from fewer patch to crowded patch; Hungarian or blossom does this desired effect implicitly w/o any explicit bv1.patchSize > bv2.patchSize tests

	cout << baseCounter++ << ") matching up next 2 patches of size " << (int) verts[bv1]->baseNghbhood->size()+1 << " vs. " << (int) mesh2->verts[bv2]->baseNghbhood->size()+1 << ", bases: " << bv1 << " & " << bv2 << "..\n";

	//float w1 = 0.0f, w2 = 1.0f; //weights for L2cost and vertexBasedIso, respectively, for the costs[][] (looking at top10 matches for w2)
	//no L2cost here 'cos although they could be normalized to be used together, it's not so logical 'cos one is spectral and other is Euclidean costs; here use only the Euclidean isometryCost

	//add bv1/2 to the patch of bv1/2 to be able to refine their correspondence from coarse correspondence step, i.e. currenty bv1-bv2 is a pair and it may change below
	verts[bv1]->baseNghbhood->push_back(bv1);
	mesh2->verts[bv2]->baseNghbhood->push_back(bv2);
	totalPatchSize += (int) verts[bv1]->baseNghbhood->size();
	mesh2->totalPatchSize += (int) mesh2->verts[bv2]->baseNghbhood->size();
	int tmp = (*verts[bv1]->baseNghbhood)[0], tmp2 = (*mesh2->verts[bv2]->baseNghbhood)[0]; //to swap bv1/2 w/ the first entry [0] so that it is processed first (not so crucial)
	(*verts[bv1]->baseNghbhood)[0] = bv1;	  (*verts[bv1]->baseNghbhood)[(int) verts[bv1]->baseNghbhood->size()-1] = tmp;				//swapped
	(*mesh2->verts[bv2]->baseNghbhood)[0] = bv2;	(*mesh2->verts[bv2]->baseNghbhood)[(int) mesh2->verts[bv2]->baseNghbhood->size()-1] = tmp2; //swapped
	verts[bv1]->matchIdx = mesh2->verts[bv2]->matchIdx = -1; //they'll find their matches below, probably the same bv1-bv2 match

	//Hungarian cost matrix adapted from isometryBasedGraphMatching()
	int n1 = (int) verts[bv1]->baseNghbhood->size(), n2 = (int) mesh2->verts[bv2]->baseNghbhood->size();
	for (int nb1 = 0; nb1 < (int) verts[bv1]->baseNghbhood->size(); nb1++)
		if (verts[ (*verts[bv1]->baseNghbhood)[nb1] ]->matchIdx != -1)	//previously matched; so, no need to look for a match for this one
			n1--; //exclude matched verts of this patch
	for (int nb2 = 0; nb2 < (int) mesh2->verts[bv2]->baseNghbhood->size(); nb2++)
		if (mesh2->verts[ (*mesh2->verts[bv2]->baseNghbhood)[nb2] ]->matchIdx != -1)
			n2--;
	int nRows = (n1 > n2 ? n1 : n2), nCols = nRows; //square mtrx
	int* vIdx1 = new int[n1], * vIdx2 = new int[n2]; //if r,c = 3,7 is a match in costs[r][c], i.e. [3][7]=0, then verts[ vIdx1[3] ].matchIdx = vIdx2[7], hence vIdx1/2[*] is an idx to verts
	float** costs = new float*[nRows];
	float minCost = INF, maxCost = -INF, minDistThreshold = 0.0f,
		  discourage = INF; //1.0f; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; i normalize costs[][] into [0,1] so we're good w/ discourage=1.0f [sucks w/ 1 for ballerina100015-4; logically, it must be very big anyway; i guess this 1 thing was from old hungarian code requirement]
	
	//for each nonbase in patch1, compute its isometryCost of matching it w/ j'th patch2 vert
	int row = 0, scSize = (int) safeCorresp.size() / 2;
//#ifdef USE_ADAPTIVE_SAFE_CORRESPS //safe correps list is not static, i.e. adaptive/dynamic and changes for each vertex, consisting of e.g, geodesically close bases only
	////////adaptiveListForIsometryCost(bv1, bv2, 3); //global safeCorrespsAdaptive is set


	adaptiveListForIsometryCost(bv1, bv2); //never tried w/ this version after geodesics[] introduction for memo efficiency!!!!!!!!!!!


	scSize = (int) safeCorrespsAdaptive->size() / 2;
assert(scSize > 1); //no div by zero below
//#endif
	for (int nb1 = 0; nb1 < (int) verts[bv1]->baseNghbhood->size(); nb1++)
	{
		int v1 = (*verts[bv1]->baseNghbhood)[nb1];		//baseNghbhood[] guaranteed to be duplicate-free after correspFromFile()
		if (verts[v1]->matchIdx != -1)				//previously matched; so, no need to look for a match for this one
			continue;

		//[i][*] entry of cost, i.e. cost of matching patch1Verts[i] w/ patch2Verts[*] where * runs from 0 to # of patch2Verts (so patch2Size+1 column, +1 for bv2 itself)
		costs[row] = new float[nCols]; //nCols = nRows anyway
		vIdx1[row] = v1; //row = 5, v1 = 68 means that row5 (6th row) is about vert68
		int col = 0;
		for (int nb2 = 0; nb2 < (int) mesh2->verts[bv2]->baseNghbhood->size(); nb2++)
		{
			int v2 = (*mesh2->verts[bv2]->baseNghbhood)[nb2];	//baseNghbhood[] guaranteed to be duplicate-free after correspFromFile()
			if (mesh2->verts[v2]->matchIdx != -1)			//previously matched; so, no need to look for a match for this one
				continue;

			vIdx2[col] = v2; //col = 6, v2 = 69 means that column6 is about vert69


			//vIdx1[row] vs. vIdx2[col] active assumption (copied from greedyForBaseUpdate().`if (votesSize > 0 && c2 != bv2)' block)
			float vertexBasedIso = 0.0f; //for vIdx1[r] (under the assumption that it is paired w/ vIdx2[c])
//#ifdef USE_ADAPTIVE_SAFE_CORRESPS
			for (int ss = 0; 2*ss+1 < (int) safeCorrespsAdaptive->size(); ss++)
			{
				int s1 = (*safeCorrespsAdaptive)[2*ss], s2 = (*safeCorrespsAdaptive)[2*ss + 1];
//#else
//			for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
//			{
//				int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
//#endif
//if (row==0 && col==0)cout << s1 << " _______________ " << s2 << endl;
				if (s1 == vIdx1[row]) //must ignore s1==vIdx1[r] as explained in greedyForBaseUpdate()'s bv1 vs. c2c active assumption part
					continue;

				float diffForNormalized = //verts[ vIdx1[r] ]->dSpanning[s1] - mesh2->verts[ vIdx2[c] ]->dSpanning[s2]; //baseVerts[r],mesh2->baseVerts[c]
											verts[s1]->dSpanning[ vIdx1[row] ] - mesh2->verts[s2]->dSpanning[ vIdx2[col] ]; //vIdx1/2 is not necessarily a base and hence dSpanning not always inited; s1/2 are always bases
				vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if baseVerts[r] vs. mesh2->baseVerts[c] is a good correspondence
			}
			vertexBasedIso /= (scSize-1); //scSize # of time += fabs done above each of which in [0,1]; so, after /=, vertexBasedIso in [0,1] for sure
			costs[row][col++] = vertexBasedIso;
		} //end of nb2
		while (col < nCols) //fill the remaining virtual columns w/ infinitive costs
			costs[row][col++] = discourage;
		row++;
	} //end of nb1
	while (row < nRows) //fill the remaining virtual rows w/ infinitive costs
	{
		costs[row] = new float[nCols]; //nRows = nCols
		for (int col = 0; col < nCols; col++)
			costs[row][col] = discourage;
		row++;
	}

	//costs[][] ready; solve it
#ifdef HUNGARIAN_INSTEAD_OF_BLOSSOM
#ifdef DBG_DETAIL
	cout << "Hungarian costs: " << nRows << " x " << nCols << endl;
#endif
	//solve Hungarian assignment w/ this costs[][]
	Hungarian* h = new Hungarian(nRows, nCols);
	h->solve(costs); //result of Hungarian is written to costs mtrx (hence the weird naming; interpret it as assignments from now on)
//not so fast :( for 455x455 costs mtrx, it takes 65 secs, 571x571 takes 492 secs, 427x427 120 secs, 693x693 takes 466 secs, 472x472 takes 110 secs, etc; whole run 1hr 6 mins for 15K jumping
	delete h;

int nMatchesSet = 0;
	for (int r = 0; r < nRows; r++)
		for (int c = 0; c < nCols; c++)
			if (r < n1 && costs[r][c] == 0) //0 means that column c is selected for this row r
			{
				verts[ vIdx1[r] ]->matchIdx = (c < n2 ? vIdx2[c] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1
				if (verts[ vIdx1[r] ]->matchIdx != -1)
				{
					fprintf(fPtrU, "%d\t%d\n", vIdx1[r], vIdx2[c]);
					mesh2->verts[ vIdx2[c] ]->matchIdx = vIdx1[r];
					nCorresps++;
					mesh2->nCorresps++;
nMatchesSet++;
				}
			}
assert(nMatchesSet == (n1 <= n2 ? n1 : n2)); //min(n1,n2) number of matches must have been set just-above
#else
	//////////// blossom input ///////////////
	int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e = 0, * edges = new int[2*nEdgeNum];
	double* weightsD = new double[nEdgeNum];
	for (int i = 0; i < nRows; i++)
		for (int j = 0; j < nCols; j++)
		{
			edges[2*e] = i;
			edges[2*e+1] = nRows+j;
			weightsD[e++] = (double) costs[i][j];
		}
	//////////// blossom input ends ///////////////

	Blossom* blossom = new Blossom();
	int* matches = new int[nVertNum]; //see this as lines of matches where 2 consecutive items make a matching line, e.g. 369 694
	blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches); //matches update by call-by-ref
	delete blossom;
	delete [] edges;
	delete [] weightsD;

	//transform data in matches[] to matchIdx values consistently
	for (int m = 0; m < nRows; m++)
	{
		//assume nRows = 693 (693x2 verts in total, including virtuals) and n1>n2, then matches[] lines:
		//0 856 \n 1 925  \n .. 323 693 \n .. 691 1198 \n 692 1380; then means m1.v[vIdx1[0]] matches w/ m2.v[vIdx2[856-693]]
		int m1 = matches[2*m]; //left side of the (m+1)'th matching line
		if (m1 < n1)
		{
			int m2 = matches[2*m + 1] - nRows; //mathches[m1 + 1], right side of the line, is in [nRows..2*nRows-1], hence nRows entry at mesh2
#ifdef USE_BEST_MATCH_AMONGST_TOUCHING_PATCHES			
			if (m2 < n2)
			{
				//since no matchIdx set here, upcoming touching patches will process m1 and m2 again and the best will be decided in the end
				verts[ vIdx1[m1] ]->matchIdxs.push_back((float) vIdx2[m2]);
				verts[ vIdx1[m1] ]->matchIdxs.push_back(costs[m1][m2]); //cost of this match; if this turns out to be the best cost, then verts[ vIdx1[m1] ]->matchIdx = vIdx2[m2]'ll be done in make1to1()
				mesh2->verts[ vIdx2[m2] ]->matchIdxs.push_back((float) vIdx1[m1]); //make1to1() works on mesh2.matchIdxs too for force1to1=2 case
				mesh2->verts[ vIdx2[m2] ]->matchIdxs.push_back(costs[m1][m2]);
			}

			//base needs to know its matchIdx at all times 'cos upcoming iters may want to reach it through baseNghbs[]; i certainly set bv1.matchIdx to -1 at top, so heal it
			if (vIdx1[m1] == bv1) //same as if (verts[ vIdx1[m1] ]->base)
				verts[ vIdx1[m1] ]->matchIdx = (m2 < n2 ? vIdx2[m2] : -1); //nothing needed for mesh2.matchIdx
#else
			int mi = (m2 < n2 ? vIdx2[m2] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1
			if (mi != -1 )// && costs[m1][m2] <= 0.4f) //verts w/ LMDS dists > threshold are not matched when this patch is active; hopefully, upcoming patches will make a better match
						 //	&& costsUnn[m1][m2] <= avgMinDistsFrom3) //avg of minDist values used in a match computed by pair..ion3() in 3 secs
			{
				verts[ vIdx1[m1] ]->matchIdx = mi;
				fprintf(fPtrU, "%d\t%d\n", vIdx1[m1], vIdx2[m2]);
//fprintf(fPtrU, "%f\n", costsUnn[m1][m2]); //make \t -> \n in fprint just-above
				mesh2->verts[ vIdx2[m2] ]->matchIdx = vIdx1[m1];
				nCorresps++;
				mesh2->nCorresps++;
			}
#endif
		}
	} //end of m
	delete [] matches;
#endif
//fprintf(fPtrU, "\n\n");

	//roll-back to exmatches 'cos due to patchSize differences bv1 couldn't find chance to find a match for itself (matched w/ a virtual vertex) and i need matches of bases for next iters for interpolation
#ifdef USE_BEST_MATCH_AMONGST_TOUCHING_PATCHES
	//base needs to know its matchIdx at all times 'cos upcoming iters may want to reach it through baseNghbs[]; i certainly set bv1.matchIdx to -1 at top, so heal it; indeed it's healed above but not for bv2; so, do it here anyway for safety and bv2's assertion in piar..ion2_1()
	verts[bv1]->matchIdx = bv2;
	mesh2->verts[bv2]->matchIdx = bv1;
#else
	if (verts[bv1]->matchIdx == -1)
	{
		verts[bv1]->matchIdx = bv2;
//assert(mesh2->verts[bv2]->matchIdx != -1); //the reason bv1 left unmatched is patch1Size > patch2Size and therefore some verts in patch1 left unmatched; but all verts in patch2 matched; so, bv2 must have a match now; no!! after threshold-based canceling above, this assert not good anymore
		fprintf(fPtrU, "%d\t%d\n", bv1, bv2); //75\t46\n fprinted above; now, 48\t46\n fprinted (bv1=48, bv2=46); so, while freading, 48-46 overwrites to 75-46 which is good; no!! 48.matchIdx becomes 46 but 75.matchIdx also stays 46; make it -1 w/ a 2nd fprint
		if (mesh2->verts[bv2]->matchIdx != -1) //if the reason bv1 left unmatched is patch1Size > patch2Size (and not the thresholding), some verts in patch1 left unmatched; but all verts in patch2 matched; so, bv2 must have a match now that needs to be canceled
		{
			fprintf(fPtrU, "%d\t%d\n", mesh2->verts[bv2]->matchIdx, -1);
			verts[ mesh2->verts[bv2]->matchIdx ]->matchIdx = -1; //the patch1 vert coupling w/ bv2 no longer couples 'cos bv2 has its old partner (bv1) from now on
		}
		mesh2->verts[bv2]->matchIdx = bv1;
		//mesh2->nCorresps++; not done 'cos mesh2 lost a correspondence just-above; similarly, mesh1 just replaced a correspondence so no nCorresps++
	}
//note that, bv2 may remain bv2.matchIdx = -1 (unmatched) if patch2Size > patch1Size but not doing anything here 'cos bv2.matchIdx's not used; maby, i should have canceled base property of bv2 and made the bv1.newMatch a base vert; but base info not so crucial here so skipping
#endif

	//memo re-capture
	for (int i = 0; i < nRows; i++) delete [] costs[i];		delete [] costs;
	delete [] vIdx1;
	delete [] vIdx2;

	cout << "done!\n";
}

void Mesh::pairUpPatchesByInterpolation(Mesh* mesh2, FILE* fPtrU, int bv1, int bv2)
{
	//pairs up all elements of bv1's patch1 w/ the elements of bv2's patch2 if patch1Size < patch2Size and vice versa; hence, pairing always
	//from smaller patch to bigger one so that small elements can select their cool matches from bigger set; kinda Hungarian effect but faster

	cout << baseCounter++ << ") matching up next 2 patches of size " << (int) verts[bv1]->baseNghbhood->size()+1 << " vs. " << (int) mesh2->verts[bv2]->baseNghbhood->size()+1 << ", bases: " << bv1 << " & " << bv2 << " (includes matched verts)..\n";

	//float w1 = 1.0f, w2 = 0.0f; //weights for L2cost and vertexBasedIso, respectively; no isoemtryCost here 'cos although they could be normalized to be used together, it's not so logical 'cos one is spectral and other is Euclidean costs; ; here use only the spectral L2cost

	//add bv1/2 to the patch of bv1/2 to be able to refine their correspondence from coarse correspondence step, i.e. currenty bv1-bv2 is a pair and it may change below
	verts[bv1]->baseNghbhood->push_back(bv1);
	mesh2->verts[bv2]->baseNghbhood->push_back(bv2);
	totalPatchSize += (int) verts[bv1]->baseNghbhood->size();
	mesh2->totalPatchSize += (int) mesh2->verts[bv2]->baseNghbhood->size();
	int tmp = (*verts[bv1]->baseNghbhood)[0], tmp2 = (*mesh2->verts[bv2]->baseNghbhood)[0]; //to swap bv1/2 w/ the first entry [0] so that it is processed first (not so crucial)
	(*verts[bv1]->baseNghbhood)[0] = bv1;			(*verts[bv1]->baseNghbhood)[(int) verts[bv1]->baseNghbhood->size()-1] = tmp;				//swapped
	(*mesh2->verts[bv2]->baseNghbhood)[0] = bv2;	(*mesh2->verts[bv2]->baseNghbhood)[(int) mesh2->verts[bv2]->baseNghbhood->size()-1] = tmp2; //swapped
	verts[bv1]->matchIdx = mesh2->verts[bv2]->matchIdx = -1; //they'll find their matches below, probably the same bv1-bv2 match
	int iSize1 = (int) verts[bv1]->baseNghbhood->size(), iSize2 = (int) mesh2->verts[bv2]->baseNghbhood->size(), //ultimate size of idxs1[] and idxs2[]
		decideSize1 = iSize1, decideSize2 = iSize2; //to decide which patch is the from-patch (smaller size)
	for (int nb1 = 0; nb1 < (int) verts[bv1]->baseNghbhood->size(); nb1++)
		if (verts[ (*verts[bv1]->baseNghbhood)[nb1] ]->matchIdx != -1)	//previously matched; so, no need to look for a match for this one
			decideSize1--; //exclude matched verts of this patch
	for (int nb2 = 0; nb2 < (int) mesh2->verts[bv2]->baseNghbhood->size(); nb2++)
		if (mesh2->verts[ (*mesh2->verts[bv2]->baseNghbhood)[nb2] ]->matchIdx != -1)
			decideSize2--;

	//////// nonbases selected in non-arbitrary order, i.e. w.r.t. descending nOverlaps vals ////////
	int* idxsFrom, idxsFromSize = 0, smallVal = 1, * idxsTo, idxsToSize = 0, bvFrom, bvTo; //from and to part of the mapping
	Mesh* fromMesh, * toMesh;
	bool fromOnMesh1; //true if from part of mapping, i.e. from-patch, is the one on source/mesh1, and false if it is on target/mesh2
	if (decideSize1 <= decideSize2) //patch1 is smaller; so, mapping from patch1 to patch2
	{
		//from side must be in sorted order
		idxsFrom = new int[iSize1];
		for (int nb1 = 0; nb1 < (int) verts[bv1]->baseNghbhood->size(); nb1++) //involves bv1 itself after push_back() above
			verts[ (*verts[bv1]->baseNghbhood)[nb1] ]->processed = false;
		while (idxsFromSize < iSize1) //re-traverse bv1.baseNghbhood w/ the next smallVal as long as idxsFrom[] not fully filled
		{
			for (int nb1 = 0; nb1 < (int) verts[bv1]->baseNghbhood->size(); nb1++)
			{
				int v1 = (*verts[bv1]->baseNghbhood)[nb1];	//baseNghbhood[] guaranteed to be duplicate-free after correspFromFile()
				//put the next smallest nOverlap valued nonbase into idxs1
				if (! verts[v1]->processed && nOverlaps[v1] == smallVal)
				{
					idxsFrom[idxsFromSize++] = v1;
					verts[v1]->processed = true; //processed means added to idxsFrom[] in this context
				}
			}
			smallVal++; //try the next small value for the upcoming for-loop above
		}

		//to side in arbitrary order 'cos the sorted from side will select the best from the to list
		idxsTo = new int[iSize2];
		for (int nb2 = 0; nb2 < (int) mesh2->verts[bv2]->baseNghbhood->size(); nb2++)
			idxsTo[idxsToSize++] = (*mesh2->verts[bv2]->baseNghbhood)[nb2]; //already matched ones in this list too, and will be discarded below
		fromOnMesh1 = true;
		bvFrom = bv1;
		bvTo = bv2;
		fromMesh = this;
		toMesh = mesh2;
	}
	else	//patch2 is smaller; so, mapping from patch2 to patch1
	{
		//from side must be in sorted order
		idxsFrom = new int[iSize2];
		for (int nb2 = 0; nb2 < (int) mesh2->verts[bv2]->baseNghbhood->size(); nb2++)
			mesh2->verts[ (*mesh2->verts[bv2]->baseNghbhood)[nb2] ]->processed = false;
		while (idxsFromSize < iSize2) //re-traverse bv2.baseNghbhood w/ the next smallVal as long as idxsFrom[] not fully filled
		{
			for (int nb2 = 0; nb2 < (int) mesh2->verts[bv2]->baseNghbhood->size(); nb2++)
			{
				int v2 = (*mesh2->verts[bv2]->baseNghbhood)[nb2];
				if (! mesh2->verts[v2]->processed && mesh2->nOverlaps[v2] == smallVal)
				{
					idxsFrom[idxsFromSize++] = v2;
					mesh2->verts[v2]->processed = true;
				}
			}
			smallVal++;
		}
		//to side in arbitrary order 'cos the sorted from side will select the best from the to list
		idxsTo = new int[iSize1];
		for (int nb1 = 0; nb1 < (int) verts[bv1]->baseNghbhood->size(); nb1++)
			idxsTo[idxsToSize++] = (*verts[bv1]->baseNghbhood)[nb1];
		fromOnMesh1 = false;
		bvFrom = bv2;
		bvTo = bv1;
		fromMesh = mesh2;
		toMesh = this;
	}
	//////// nonbases selected in non-arbitrary order, i.e. w.r.t. descending nOverlaps vals ends ////////

	int nFromActive = 0; //# of unmatched from-verts
	//for each nonbase in smaller patch (from part of the mapping), compute its interpolated 2d coord on larger patch using Shepard's technique; then desired match is the closest & unmatched vertex within larger patch
	for (int nbFrom = 0; nbFrom < idxsFromSize; nbFrom++)
	{
		int vFrom = idxsFrom[nbFrom];					//baseNghbhood[] guaranteed to be duplicate-free after correspFromFile()
		if (fromMesh->verts[vFrom]->matchIdx != -1)		//previously matched; so, no need to look for a match for this one
			continue;

		//(xFrom,yFrom) is the nonbase in from-patch whose correspondent will be sought below in to-patch
		float xFrom = fromMesh->verts[vFrom]->spectralK[0], yFrom = fromMesh->verts[vFrom]->spectralK[1], normalizer = 0.0f, d;

		//find alpha_i that locates (xFrom,yFrom) on source; bvFrom.baseNghbs and bvFrom tell us this location
		int aSize = (int) fromMesh->verts[bvFrom]->baseNghbs.size() + 1; //+1 is for bvFrom itself
		float* alphas = new float[aSize];
		for (int a = 0; a < aSize; a++) //for each base neighb of bv1 and bv1 itself
		{
			float xnFrom, ynFrom; //2d spectral coords of the current bvFrom.baseNeighb or bvFrom itself
			int bFrom = (a != aSize - 1 ? fromMesh->verts[bvFrom]->baseNghbs[a] : bvFrom); //last iteration is for bvFrom; bFrom is certainly a base in from-patch
			xnFrom = fromMesh->verts[bFrom]->spectralK[0];
			ynFrom = fromMesh->verts[bFrom]->spectralK[1];
			d = sqrt((xFrom-xnFrom)*(xFrom-xnFrom) + (yFrom-ynFrom)*(yFrom-ynFrom));
			if (d == 0.0f) //rare case
			{
				for (int aa = 0; aa < aSize; aa++) //make alphas[a] = 1.0f and other entries 0
					alphas[aa] = (aa == a ? 1.0f : 0.0f);
				normalizer = -1.0f; //special flag not to update alphas[] below
				break; //all alphas are ready
			}
			normalizer += (1.0f / (d*d)); //1/||x - x_j||^p and i always use p = 2 (j is bv1.baseNghbs and bv1 itself)
		} //end of a
		if (normalizer != -1.0f) //set alphas[]
			for (int a = 0; a < aSize; a++)
			{
				float xnFrom, ynFrom; //d computation copied from for-loop above
				int bFrom = (a != aSize - 1 ? fromMesh->verts[bvFrom]->baseNghbs[a] : bvFrom);
				xnFrom = fromMesh->verts[bFrom]->spectralK[0];
				ynFrom = fromMesh->verts[bFrom]->spectralK[1];
				d = sqrt((xFrom-xnFrom)*(xFrom-xnFrom) + (yFrom-ynFrom)*(yFrom-ynFrom)); //d can't be 0 here hence no div by 0 exception (normalizer != -1 guarantees this)
				alphas[a] = ((1.0f / (d*d)) / normalizer); //alpha_i = (1/||x - x_i||^p) / normalizer, and i always use p = 2
			}
		//found alpha_i that locates (xFrom,yFrom) on from-patch; use these alphas to interpolate a location in target-patch; each alphas[i] is associated w/ the known correspondences in to-patch, use this fact
		float ixTo = 0.0f, iyTo = 0.0f; //interpolated x and y coords in to-patch
		for (int a = 0; a < aSize; a++) //for each correspondence of bv1.baseNeighbs and correspondence of bv1 itself
		{
			int bFrom = (a != aSize - 1 ? fromMesh->verts[bvFrom]->baseNghbs[a] : bvFrom); //last iteration is for bv1
			int idx2 = (bFrom != bvFrom ? fromMesh->verts[bFrom]->matchIdx : bvTo); //bvFrom.matchIdx made -1 at top, so use the exmatch value which is bvTo
assert(idx2 != -1);
			//right-hand sides below are 2d spectral coords of the current bvFrom.baseNeighb's correspondence or bvFrom's correspondence in to-patch
			ixTo += alphas[a] * toMesh->verts[idx2]->spectralK[0];
			iyTo += alphas[a] * toMesh->verts[idx2]->spectralK[1];
		}
		delete [] alphas;

		//the closest (to (ix2,iy2)) unmatched point in patch2 is the desired/interpolated correspondence of v1
		float minCost = INF;
		int bestMatchInToPatch = -1;
		for (int nbTo = 0; nbTo < (int) toMesh->verts[bvTo]->baseNghbhood->size(); nbTo++)
		{
			int vTo = (*toMesh->verts[bvTo]->baseNghbhood)[nbTo];	//baseNghbhood[] guaranteed to be duplicate-free after correspFromFile()
			if (toMesh->verts[vTo]->matchIdx != -1)				//previously matched; so, no need to look for a match for this one
				continue;

			//(xTo,yTo) is the nonbase in to-patch which is to be tested w/ (ixTo,iyTo) for closeness
			float xTo = toMesh->verts[vTo]->spectralK[0], yTo = toMesh->verts[vTo]->spectralK[1];
			float dist = (ixTo-xTo)*(ixTo-xTo) + (iyTo-yTo)*(iyTo-yTo); //no sqrt for fast comparison
			if (dist < minCost)
			{
				bestMatchInToPatch = vTo;
				minCost = dist;
			}
		} //end of nbTo
assert(bestMatchInToPatch != -1); //since we're mapping from smaller to larger patch, all small-patch verts must find a match for them which makes bestMatchInToPatch different from -1 at all times
		
		nFromActive++;

#ifdef USE_BEST_MATCH_AMONGST_TOUCHING_PATCHES
		//since no matchIdx set here, upcoming touching patches will process vFrom and bestMatchInToPatch again and the best will be decided in the end
		fromMesh->verts[vFrom]->matchIdxs.push_back((float) bestMatchInToPatch);
		fromMesh->verts[vFrom]->matchIdxs.push_back(minCost); //cost of this match; if this turns out to be the best cost, mesh1's vFrom.matchIdx = mesh2's bestMatchInToPatch'll be done in make1to1()
		toMesh->verts[bestMatchInToPatch]->matchIdxs.push_back((float) vFrom);
		toMesh->verts[bestMatchInToPatch]->matchIdxs.push_back(minCost);
#else
		fromMesh->verts[vFrom]->matchIdx = bestMatchInToPatch;
		toMesh->verts[bestMatchInToPatch]->matchIdx = vFrom;
		fromMesh->nCorresps++;
		toMesh->nCorresps++;
		if (fromOnMesh1)
			fprintf(fPtrU, "%d\t%d\n", vFrom, bestMatchInToPatch); //left side source/mesh1, right side target/mesh2 in output file
		else
			fprintf(fPtrU, "%d\t%d\n", bestMatchInToPatch, vFrom);
#endif
	} //end of nbFrom
assert(nFromActive >= 1);

#ifdef USE_BEST_MATCH_AMONGST_TOUCHING_PATCHES
	//base needs to know its matchIdx at all times 'cos upcoming iters may want to reach it through baseNghbs[]; i certainly set bv1.matchIdx to -1 at top, so heal it
	fromMesh->verts[bvFrom]->matchIdx = bvTo;
	toMesh->verts[bvTo]->matchIdx = bvFrom;
#else
assert(fromMesh->verts[bvFrom]->matchIdx != -1); //all verts in smaller from-patch is matched, including the base vert bvFrom with which we called this function
	if (toMesh->verts[bvTo]->matchIdx == -1) //roll-back to exmatches 'cos due to patchSize differences bvTo (at the larger patch) couldn't find a chance to find a match for itself and i need matches of bases for next iters for interpolation
	{
		toMesh->verts[bvTo]->matchIdx = bvFrom;
assert(fromMesh->verts[bvFrom]->matchIdx != -1); //the reason bvTo left unmatched is to-patchSize > from-patchSize and therefore some verts in to-patch left unmatched; but all verts in smaller from-patch matched; so, bvFrom must have a match now
		if (fromOnMesh1)
			fprintf(fPtrU, "%d\t%d\n", bvFrom, bvTo); //75\t46\n fprinted above; now, 48\t46\n fprinted (bv1=48, bv2=46); so, while freading, 48-46 overwrites to 75-46 which is good; no!! 48.matchIdx becomes 46 but 75.matchIdx also stays 46; make it -1 w/ a 2nd fprint
		else
			fprintf(fPtrU, "%d\t%d\n", bvTo, bvFrom); //left side source/mesh1, right side target/mesh2 in output file
		fprintf(fPtrU, "%d\t%d\n", toMesh->verts[bvTo]->matchIdx, -1);
		fromMesh->verts[ toMesh->verts[bvTo]->matchIdx ]->matchIdx = -1; //the from-patch vert coupling w/ bvTo no longer couples 'cos bvTo has its old partner (bvFrom) from now on
		toMesh->verts[bvTo]->matchIdx = bvFrom;
	}
#endif

	//memo re-capture
	delete [] idxsFrom;	delete [] idxsTo;
//	delete fromMesh;	delete toMesh; //can't do this 'cos the real this (mesh1) and mesh2 is gone if these deleted

	cout << "done!\n";
}

//#define PRIVILEGED_CONVERSION //define to make verts w/ only 1 matches privileged, i.e., they're matched w/ their only candidate and such candids are locked for them, i.e. can't be used by others
void Mesh::make1to1(Mesh* mesh2, FILE* fPtrU, int force1to1, bool updateMatchCostI, bool baseFlagValid)
{
	//uses v.matchIdxs set by pairUpPatchesBy*() to decide the best touching-patch-match for v
	//force1to1=1 tries to commit the min-cost match, i.e. that match is used as matchIdx's if mesh2 side is free
	//force1to1=2 tries all matches in ascending order of match costs and commits the first available, hence more matches done than force1to1=1 and it's better
	//i generally don't update matchCostI 'cos i want the original, i.e. coarseStage, values to be written to file so that i can see the improvement in a computeCorr.rror() later

	cout << "\nmatching up verts via the best patch-matches" << (force1to1 != 0 ? " (1-to-many to 1-to-1)..\n" : "..\n");

//FILE* fPtr = fopen("f11.dat", "w");

	if (force1to1 != 2 && ! fPtrU)
	{
		cout << "file to write n/a\n"; //problem 'cos i access fPtrU below for force1to1 = 0 or 1
		exit(0);
	}

	//bases not remain matchIdx=-1 after pairUpPatchesBy*(); make them -1 for the future of this fnctn; all other verts made -1 too but it seems redundant since they already are; anyway, be safe
	for (int v = 0; v < (int) verts.size(); v++)
	{
		if (baseFlagValid && verts[v]->base) //not maintaining base attribute for coarseToFineMatching() levels; so, not bother w/ assert() below
		{
assert(verts[v]->matchIdx != -1);
			verts[v]->matchIdxCoarse = verts[v]->matchIdx; //save this to maintain the old/coarseStage values in case this base not find a match below
		}
		verts[v]->matchIdx = -1;
		verts[v]->matchCostDense = INF;
	}
	for (int v = 0; v < (int) mesh2->verts.size(); v++) //same stuff for mesh2
	{
		if (baseFlagValid && mesh2->verts[v]->base)
		{
assert(mesh2->verts[v]->matchIdx != -1);
			mesh2->verts[v]->matchIdxCoarse = mesh2->verts[v]->matchIdx; //save this to maintain the old/coarseStage values in case this base not find a match below
		}
		mesh2->verts[v]->matchIdx = -1;
		mesh2->verts[v]->matchCostDense = INF;
	}
	if (force1to1 == 1 || force1to1 == 0)
		for (int v = 0; v < (int) verts.size(); v++)
		{
			//decide the best
			float minMatchCost = INF;
			int bestMatchIdx = -1;
			for (int candid = 0; candid < (int) verts[v]->matchIdxs.size(); candid += 2)
			{
				float matchCostCandid = verts[v]->matchIdxs[candid + 1];
				if (matchCostCandid < minMatchCost)
				{
					bestMatchIdx = (int) verts[v]->matchIdxs[candid]; //preceding entry is the matchIdx associated w/ this cost
					minMatchCost = matchCostCandid;
				}
			}
			if (bestMatchIdx != -1) //matchIdxs may have -1 entries but i don't expect -1 here 'cos there should be other entries there as well
			{
				//set matchIdx according to the best; 1-to-many may happen here if force1to1 is 0
				if (force1to1 != 0 && mesh2->verts[bestMatchIdx]->matchIdx != -1) //see the existing match of this m2.v in mesh1 side
				{
					if (verts[ mesh2->verts[bestMatchIdx]->matchIdx ]->matchCostDense > minMatchCost) //mesh1 side vert is worse than this match, so unmatch it
					{
						verts[ mesh2->verts[bestMatchIdx]->matchIdx ]->matchIdx = -1;
						verts[ mesh2->verts[bestMatchIdx]->matchIdx ]->matchCostDense = INF;
						if (updateMatchCostI)
						{
							verts[ mesh2->verts[bestMatchIdx]->matchIdx ]->matchCostI = INF; //same as matchCostDense
							verts[ mesh2->verts[bestMatchIdx]->matchIdx ]->isometryCostChosenByBlossom = verts[ mesh2->verts[bestMatchIdx]->matchIdx ]->matchCostI;
						}
						nCorresps--; //lost 1 correspondence in mesh1 side; nothing changes in mesh2 'cos new match is coming for this mesh2 vert below
					}
					else //if (verts[ mesh2->verts[bestMatchIdx]->matchIdx ]->matchCostDense <= minMatchCost) //mesh1 side vert is better then this match, so keep it
						continue; //to keep the existing/previous/better match, do nothing
				}
				verts[v]->matchIdx = bestMatchIdx;
				nCorresps++;				
				if (mesh2->verts[bestMatchIdx]->matchIdx == -1)
					mesh2->nCorresps++; //do it only once even of this m2.v is matched w/ many m1.verts
				mesh2->verts[bestMatchIdx]->matchIdx = v; //keep the most recent match (m2.nCorresps incremented only once though)
				fprintf(fPtrU, "%d\t%d\n", v, bestMatchIdx);
				verts[v]->matchCostDense = mesh2->verts[bestMatchIdx]->matchCostDense = minMatchCost;
				if (updateMatchCostI)
				{
					verts[v]->matchCostI = mesh2->verts[bestMatchIdx]->matchCostI = minMatchCost; //same as matchCostDense
					verts[v]->isometryCostChosenByBlossom = verts[v]->matchCostI;
					mesh2->verts[bestMatchIdx]->isometryCostChosenByBlossom = mesh2->verts[bestMatchIdx]->matchCostI;
				}
			}
		} //end of for v
	else if (force1to1 == 2) //2nd and better way of forcing, that leaves less vertices unmatched compared to force1to1 = 1
	{
		//sort v.matchIdxs for all v w.r.t. cost values it holds on odd indices; uses insertion sort indeed
		for (int v = 0; v < (int) verts.size(); v++)
			for (int candid = 2; candid < (int) verts[v]->matchIdxs.size(); candid += 2) //sort v.matchIdxs
			{
				float key = verts[v]->matchIdxs[candid+1]; //2 consecutive items are idx-cost, where i want cost to be key, so +1
				float idxKey = verts[v]->matchIdxs[candid];
				//insert A[candid+1] into the sorted sequence A[0..candid-1], where A = verts[v]->matchIdxs for short
				int i = candid-1;
				while (i > 0 && verts[v]->matchIdxs[i] > key)
				{
					//shift the costs larger than key 1 to the right (actually 2 'cos each consecutive 2 items is 1 package here)
					verts[v]->matchIdxs[i+2] = verts[v]->matchIdxs[i];
					verts[v]->matchIdxs[i+1] = verts[v]->matchIdxs[i-1]; //and the idx part as part of package deal
					i -= 2;
				}
				//place the key to the computed spot
				verts[v]->matchIdxs[i+2] = key;
				verts[v]->matchIdxs[i+1] = idxKey; //and the idx part as part of package deal
			}
		
#ifdef PRIVILEGED_CONVERSION
//no reason to give verts w/ 1-match candidate privilege (noPriviligeIn1-1conversion.png); no!!! # of 1-1 mappings decrease significantly w/o this 'cos more verts left unmatched
		//second, handle the verts w/ only 1 match candidate; since they don't have any other chance, trivially set those matches here, i.e. update matchIdx's; if i did it later, then they may have left unmatched
		for (int v = 0; v < (int) verts.size(); v++)
			if (verts[v]->matchIdx == -1 && (int) verts[v]->matchIdxs.size() == 2) //2, not 1, 'cos idx comes w/ corresponding cost in next slot
			{
				int bestMatchIdx = (int) verts[v]->matchIdxs[0];
				float thisCost = verts[v]->matchIdxs[1];
				if (mesh2->verts[bestMatchIdx]->matchIdx != -1 && //bestMatchIdx in mesh2 already found a match; so, replace that match w/ this one only if this cost < existing match's cost
					mesh2->verts[bestMatchIdx]->matchCostDense <= thisCost)
					continue; //no replacement 'cos this cost not less than existing match's cost

				verts[v]->matchIdx = bestMatchIdx;
				nCorresps++;
				mesh2->nCorresps++;
				int existingMatch = mesh2->verts[bestMatchIdx]->matchIdx; //save before updating it
				mesh2->verts[bestMatchIdx]->matchIdx = v;
				verts[v]->matchCostDense = mesh2->verts[bestMatchIdx]->matchCostDense = thisCost;
				if (updateMatchCostI)
				{
					verts[v]->matchCostI = mesh2->verts[bestMatchIdx]->matchCostI = thisCost; //same as matchCostDense
					verts[v]->isometryCostChosenByBlossom = verts[v]->matchCostI;
					mesh2->verts[bestMatchIdx]->isometryCostChosenByBlossom = mesh2->verts[bestMatchIdx]->matchCostI;
					//matchCostG
					verts[v]->matchCostG = mesh2->verts[bestMatchIdx]->matchCostG = verts[v]->dSpanningUnn[bestMatchIdx];
				}
				//use saved match to cancel its matchIdx
				if (existingMatch != -1)
				{
					verts[existingMatch]->matchIdx = -1;
					verts[existingMatch]->matchCostDense = INF; //back to defaults since this ex-match not a match anymore
/*					if (updateMatchCostI)
					{
						verts[existingMatch]->matchCostI = INF; //same as matchCostDense
						verts[existingMatch]->isometryCostChosenByBlossom = verts[existingMatch]->matchCostI;
					}not do it if allowManyTo1() in use 'cos it checks isometryCostChosenByBlossom of an unmatched, i.e. matchIdx=-1, existingMatch vert here in order to determine if it is an outlier*/
					nCorresps--;
					mesh2->nCorresps--; //cancel ++'s above since i already counted during that previous match
				}
			}
		for (int v = 0; v < (int) mesh2->verts.size(); v++) //same stuff for mesh2
			if (mesh2->verts[v]->matchIdx == -1 && (int) mesh2->verts[v]->matchIdxs.size() == 2) //2, not 1, 'cos idx comes w/ corresponding cost in next slot
			{
				int bestMatchIdx = (int) mesh2->verts[v]->matchIdxs[0];
				float thisCost = mesh2->verts[v]->matchIdxs[1];
				if (verts[bestMatchIdx]->matchIdx != -1 && //already has a match; so, replace that match w/ this one if this cost < existing match's cost
					verts[bestMatchIdx]->matchCostDense <= thisCost)
					continue;

				mesh2->verts[v]->matchIdx = bestMatchIdx;
				mesh2->nCorresps++;
				nCorresps++;
				int existingMatch = verts[bestMatchIdx]->matchIdx; //remember before updating it
				verts[bestMatchIdx]->matchIdx = v;
				verts[bestMatchIdx]->matchCostDense = mesh2->verts[v]->matchCostDense = thisCost;
				if (updateMatchCostI)
				{
					verts[bestMatchIdx]->matchCostI = mesh2->verts[v]->matchCostI = thisCost; //same as matchCostDense
					verts[bestMatchIdx]->isometryCostChosenByBlossom = verts[bestMatchIdx]->matchCostI;
					mesh2->verts[v]->isometryCostChosenByBlossom = mesh2->verts[v]->matchCostI;
					//matchCostG
					verts[bestMatchIdx]->matchCostG = mesh2->verts[v]->matchCostG = verts[bestMatchIdx]->dSpanningUnn[v];
				}
				//use remembered match to cancel its matchIdx
				if (existingMatch != -1)
				{
					mesh2->verts[existingMatch]->matchIdx = -1;
					mesh2->verts[existingMatch]->matchCostDense = INF; //back to defaults since this ex-match not a match anymore
					if (updateMatchCostI)
					{
						mesh2->verts[existingMatch]->matchCostI = INF; //same as matchCostDense
						mesh2->verts[existingMatch]->isometryCostChosenByBlossom = mesh2->verts[existingMatch]->matchCostI;
						//matchCostG
						mesh2->verts[existingMatch]->matchCostG = INF;
					}//can do it 'cos allowManyTo1() checks outliers over mesh1.matchCostI values and allow1ToMany() has no outlier check at all
					nCorresps--;
					mesh2->nCorresps--; //cancel ++'s above since i already counted during that previous match
				}
			}

		//third, use sorted matchIdxs[] to try each as a match for bv2; say thumb-ring is the only match for thumb; when i process ring-ring (w/ lower cost), i simply skip it 'cos ring already has a match (!= -1) (due to disabled 2nd if condition (<=) below, we hit continue;)
		for (int v = 0; v < (int) verts.size(); v++)
			if (verts[v]->matchIdx == -1) //always true for PRIVILEGED_CONVERSION undefined case; for defined case, mandatory to put due to 'same stuff' part part above; e.g. say b (mesh2) has only 1 match a (mesh1) so that a-b set there; if i let a pass here (w/o if-check), then a-c can happen and i should do sth for b below, which i don't do; so, use this if-check
				for (int candid = 0; candid < (int) verts[v]->matchIdxs.size(); candid += 2)
				{
					int bestMatchIdx = (int) verts[v]->matchIdxs[candid]; //matchIdx associated w/ the cost in the next entry
					float thisCost = verts[v]->matchIdxs[candid + 1]; //the cost associated w/ the canidate idx in the prev entry
assert(bestMatchIdx != -1); //matchIdxs may have -1 entries but i don't expect -1 here 'cos there should be other entries there as well; nevertheless put assertion here

					if (mesh2->verts[bestMatchIdx]->matchIdx != -1)
//no && mesh2->verts[bestMatchIdx]->matchCostDense <= thisCost) condition 'cos it's enough that bestMatchIdx is available, i.e. unmatched, currently; in global thinking,  i would have passed this continue; for n/a (matchIdx != -1) mesh2 vert 
//to match them w/ curent v if thisCost < n/a mesh2 vert, and make the existing mesh1 match of n/a mesh2 vert equal to -1; but then i would leave that existing mesh1 vert unmatched (although it might have other candids); so, don't loose existing mesh1 vert in vain
						continue; //this candidate can't be used 'cos it already has a match (due to 'verts w/ only 1 match candidate' part above, or a prev iter in this v-for-loop); try next one in sorted order

					verts[v]->matchIdx = bestMatchIdx;
					nCorresps++;
					mesh2->nCorresps++;
					mesh2->verts[bestMatchIdx]->matchIdx = v;
					verts[v]->matchCostDense = mesh2->verts[bestMatchIdx]->matchCostDense = thisCost;
					if (updateMatchCostI)
					{
						verts[v]->matchCostI = mesh2->verts[bestMatchIdx]->matchCostI = thisCost; //same as matchCostDense
						verts[v]->isometryCostChosenByBlossom = verts[v]->matchCostI;
						mesh2->verts[bestMatchIdx]->isometryCostChosenByBlossom = mesh2->verts[bestMatchIdx]->matchCostI;
						//matchCostG
						verts[v]->matchCostG = mesh2->verts[bestMatchIdx]->matchCostG = verts[v]->dSpanningUnn[bestMatchIdx];
					}

					break; //v found its match; go for the next v; this shortcut for efficiency only 'cos upcoming thisCost = verts[v]->matchIdxs[candid + 1] cannot beat current thisCost = matchCostDense set above since matchIdxs are sorted in ascending order
				} //end of candid
#else
		//third, use sorted matchIdxs[] to try each as a match for bv2; here i can cancel an existing match (so # 1-1 decreases more); say thumb-ring is the only match for thumb; when i process ring-ring (w/ lower cost), i cancel the existingMatch (thumb) of mesh2 ring forever (due to 2nd if condition (<=) below)
#ifdef DBG_DETAIL
		cout << "linear time 1-1 conversion to match the most as good as possible..\n";
#endif

		for (int v = 0; v < (int) verts.size(); v++)
			//if (verts[v]->matchIdx == -1) //always true for PRIVILEGED_CONVERSION undefined case anyway
			for (int candid = 0; candid < (int) verts[v]->matchIdxs.size(); candid += 2)
			{
				int bestMatchIdx = (int) verts[v]->matchIdxs[candid]; //matchIdx associated w/ the cost in the next entry
				float thisCost = verts[v]->matchIdxs[candid + 1]; //the cost associated w/ the canidate idx in the prev entry
assert(bestMatchIdx != -1); //matchIdxs may have -1 entries but i don't expect -1 here 'cos there should be other entries there as well; nevertheless put assertion here

				if (mesh2->verts[bestMatchIdx]->matchIdx != -1 && mesh2->verts[bestMatchIdx]->matchCostDense <= thisCost)
					continue; //this candidate can't be used 'cos it already has a match (due to 'verts w/ only 1 match candidate' part above, or a prev iter in this v-for-loop); try next one in sorted order

				verts[v]->matchIdx = bestMatchIdx;
//fprintf(fPtr, "%d\t%d\n", v, bestMatchIdx);
				nCorresps++;
				mesh2->nCorresps++;
				int existingMatch = mesh2->verts[bestMatchIdx]->matchIdx; //save before updating it
				mesh2->verts[bestMatchIdx]->matchIdx = v;
				verts[v]->matchCostDense = mesh2->verts[bestMatchIdx]->matchCostDense = thisCost;
				if (updateMatchCostI)
				{
					verts[v]->matchCostI = mesh2->verts[bestMatchIdx]->matchCostI = thisCost; //same as matchCostDense
					verts[v]->isometryCostChosenByBlossom = verts[v]->matchCostI;
					mesh2->verts[bestMatchIdx]->isometryCostChosenByBlossom = mesh2->verts[bestMatchIdx]->matchCostI;
					//matchCostG
					verts[v]->matchCostG = mesh2->verts[bestMatchIdx]->matchCostG = verts[v]->dSpanningUnn[bestMatchIdx];
				}

				//use saved match to cancel its matchIdx
				if (existingMatch != -1)
				{
					verts[existingMatch]->matchIdx = -1;

//fprintf(fPtr, "%d deleteddddddddddd\n", existingMatch);

					verts[existingMatch]->matchCostDense = INF; //back to defaults since this ex-match not a match anymore
/*					if (updateMatchCostI)
					{
						verts[existingMatch]->matchCostI = INF; //same as matchCostDense
						verts[existingMatch]->isometryCostChosenByBlossom = verts[existingMatch]->matchCostI;
					}not do it if allowManyTo1() in use 'cos it checks isometryCostChosenByBlossom of an unmatched, i.e. matchIdx=-1, existingMatch vert here in order to determine if it is an outlier*/
					nCorresps--;
					mesh2->nCorresps--; //cancel ++'s above since i already counted during that previous match
				}

				break; //v found its match; go for the next v; this shortcut for efficiency only 'cos upcoming thisCost = verts[v]->matchIdxs[candid + 1] cannot beat current thisCost = matchCostDense set above since matchIdxs are sorted in ascending order
			} //end of candid
#ifdef DBG_DETAIL
		cout << nCorresps << " matches set after initial linear 1-1 conversion; done!\n";
#endif

		//fourth, make one more global pass to match verts that left unmatched due to if (existingMatch != -1) block above; so, if vert v w/ 2+ matches left unmatched after there, i here set the best of other 2+ candids as match of v
		vector< int >* unmatched1 = new vector<int>, * unmatched2 = new vector<int>, * allUnmatched2 = new vector<int>;
		for (int v = 0; v < (int) mesh2->verts.size(); v++)
			if (mesh2->verts[v]->matchIdx == -1 && ! mesh2->verts[v]->matchIdxs.empty()) //get rid of mesh2 verts that don't have a match at all; after this eliminaiton, it'll be more efficient to do the relevance test below
				allUnmatched2->push_back(v);
		for (int v = 0; v < (int) verts.size(); v++)
			if (verts[v]->matchIdx == -1) //an unmatched mesh1 vert; it needs to pass relevance test below to enter unmatched1 list
				for (int c = 0; c < (int) verts[v]->matchIdxs.size(); c++) //irrelevant verts that are discarded; they're either the ones that are not accessed during this ctf level (recognized by their 0-size matchIdxs) or the ones whose mesh2-matchCandidate(s) not in unmatched2, i.e. such mesh2 verts already matched w/ other mesh1 verts
				{
					bool irrelevant = true;
					for (int u2 = 0; u2 < (int) allUnmatched2->size(); u2++)
						if ((*allUnmatched2)[u2] == verts[v]->matchIdxs[c])
						{
							irrelevant = false; //this mesh1 vert can be matched w/ an unmatched vert from mesh2; so, put it in unmatched1
							//keep the relevant unmatched ones in unmatched2 unless it is already there
							bool exists = false; //duplicate-prevention; 1864-5104 & 2743-5104 where both 1864 & 2743 in unmatched1; 5104'd be added twice w/o this prevention
							for (int uu2 = 0; uu2 < (int) unmatched2->size(); uu2++)
								if ((*unmatched2)[uu2] == (*allUnmatched2)[u2])
								{
									exists = true;
									break;
								}
							if (! exists)
								unmatched2->push_back((*allUnmatched2)[u2]);
						}
					if (! irrelevant)
					{
						bool exists = false; //duplicate=prevention in unmatched1 as well; 1864-5282 & 1864-5104 where both 5282 & 5104 in unmatched2; 1856'd be added twice w/o this prevention
						for (int uu1 = 0; uu1 < (int) unmatched1->size(); uu1++)
							if ((*unmatched1)[uu1] == v)
							{
								exists = true;
								break;
							}
						if (! exists)
							unmatched1->push_back(v);
					}
				}

		int n1 = (int) unmatched1->size(), n2 = (int) unmatched2->size(), n = (n1 > n2 ? n1 : n2), nMoreMatches = 0;
//		if (0 < n && n < 500) //blossom takes too long after for big n values; skipping here if that happens (rarely 'cos above matched most of them already)
		{
#ifdef DBG_DETAIL
			if (n > 0)
				cout << "blossom v on " << n1 << " vs. " << n2 << " costs[][] to match the unmatched ones after the linear time initial 1-1 conversion above..\n";
#endif
			float discourage = INF; //below heavily adapted from initialSpectralMatching()
			//prepare costs[][] mtrx
			float** costs = new float*[n]; //[3][8]: costs of assigning mesh1.verts[unmatched1[3]] to mesh2.verts[unmatched2[8]]
			//if r,c = 3,7 is a match in costs[r][c], i.e. [3][7]=0, then verts[unmatched1[3]].matchIdx = unmatched2[7], hence unmatched2[7] is an idx to mesh2.verts, unlike c which is an idx to unmatched2[]
			for (int r = 0; r < n; r++)
			{
				costs[r] = new float[n];
				for (int c = 0; c < n; c++)
					if (r < n1 && c < n2)
					{
						//check the unmatched entries v2 in the matchIdxs list of this mesh1 vert v1
						int v1 = (*unmatched1)[r], v2 = (*unmatched2)[c];
						for (int candid = 0; candid < (int) verts[v1]->matchIdxs.size(); candid += 2)
							costs[r][c] = ((int) verts[v1]->matchIdxs[candid] == v2 ? verts[v1]->matchIdxs[candid + 1] : discourage); //+1 to put the corresponding cost into costs[][]
					}
					else
						costs[r][c] = discourage; //virtual row or column
			} //end of r

			//////////// blossom input ///////////////
			int nVertNum = n*2, nEdgeNum = n*n, e = 0, * edges = new int[2*nEdgeNum];
			double* weightsD = new double[nEdgeNum];
			for (int i = 0; i < n; i++)
				for (int j = 0; j < n; j++)
				{
					edges[2*e] = i;
					edges[2*e+1] = n+j;
					weightsD[e++] = (double) costs[i][j];
				}
			//////////// blossom input ends ///////////////

			Blossom* blossom = new Blossom();
			int* matches = new int[nVertNum]; //see this as lines of matches where 2 consecutive items make a matching line, e.g. 369 694
			blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches); //matches update by call-by-ref
			delete blossom;
			delete [] edges;
			delete [] weightsD;

			//transform data in matches[] to matchIdx values consistently
			for (int m = 0; m < n; m++)
			{
				int m1 = matches[2*m]; //left side of the (m+1)'th matching line
				if (m1 < n1)
				{
					int m2 = matches[2*m + 1] - n; //mathches[m1 + 1], right side of the line, is in [nRows..2*nRows-1], hence nRows entry at mesh2
					verts[ (*unmatched1)[m1] ]->matchIdx = (m2 < n2 ? (*unmatched2)[m2] : -1); //if virtual column(s) exists in initial costs, some rows will not have valid matches, hence -1; not setting mesh2->matchIdx, which is not important anyway
					//say we have 203-1559 & 377-1559 and for both 203 & 377, 1559 is the only match, i.e. all other costs in their row=discourage; say 377-1559 is lower, so blossom picked it; then for the 203, i select an entry (discourage) which is actually not in 203.matchIdxs (therefore it stayed discourage); detect such cases costs[][] and 203 remains unmatched
					if (costs[m1][m2] == discourage)
						verts[ (*unmatched1)[m1] ]->matchIdx = -1;
					if (verts[ (*unmatched1)[m1] ]->matchIdx != -1)
					{
assert(mesh2->verts[ (*unmatched2)[m2] ]->matchIdx == -1); //previously unmatched mesh2 vert being matched just-below

						mesh2->verts[ (*unmatched2)[m2] ]->matchIdx = (*unmatched1)[m1];
						nMoreMatches++;

//fprintf(fPtr, "%d\t%d\n", (*unmatched1)[m1], (*unmatched2)[m2]);

						verts[ (*unmatched1)[m1] ]->matchCostDense = mesh2->verts[ (*unmatched2)[m2] ]->matchCostDense = costs[m1][m2];
						if (updateMatchCostI)
						{
							verts[ (*unmatched1)[m1] ]->matchCostI = mesh2->verts[ (*unmatched2)[m2] ]->matchCostI = costs[m1][m2]; //same as matchCostDense
							verts[ (*unmatched1)[m1] ]->isometryCostChosenByBlossom = verts[ (*unmatched1)[m1] ]->matchCostI;
							mesh2->verts[ (*unmatched2)[m2] ]->isometryCostChosenByBlossom = mesh2->verts[ (*unmatched2)[m2] ]->matchCostI;
							//matchCostG
							verts[ (*unmatched1)[m1] ]->matchCostG = mesh2->verts[ (*unmatched2)[m2] ]->matchCostG = verts[ (*unmatched1)[m1] ]->dSpanningUnn[ (*unmatched2)[m2] ];
						}
					}
				}
			} //end of m
			nCorresps += nMoreMatches;
			mesh2->nCorresps += nMoreMatches;

			//memo re-capture
			delete [] matches;
			for (int i = 0; i < n; i++) delete [] costs[i]; delete [] costs;
#ifdef DBG_DETAIL
			if (n > 0)
				cout << nMoreMatches << " more matches set after blossom v of 1-1 conversion; done!\n";
#endif
		}
			
		delete unmatched1;
		delete unmatched2;
#endif
	}
	else
	{
		cout << "unrecognized force1to1 value\n";
		exit(0);
	}
	cout << (nCorresps == mesh2->nCorresps ? "1-to-1: " : "not 1-to-1: ") << nCorresps << " vs. " << mesh2->nCorresps << "\ndone!\n\n";
//	fclose(fPtrU);exit(0);

//fclose(fPtr);
}

void Mesh::refillSafeCorrespTmp(Mesh* mesh2, int currentLevel, bool isoCostPass1)
{
	//clears and then refills safeCorresp w/ current tmpBaseVerts entries at currentLevel; note that, safeCorresp list is used in the next level iteration of coarseToFineMatching()

	//tmpBaseVerts now has the most recent tmpBaseVerts, i.e. the set due to the last patch matched by matchTmpBasesByGraph() in for-s loop of coarseToFineMatching(); clear it and refill w/ all tmpBases of this level
	tmpBaseVerts.clear();
	for (int v = 0; v < (int) verts.size(); v++)
		if (verts[v]->tmpBaseLevel == currentLevel && ! verts[v]->matchIdxs.empty()) //2nd is redundant theoretically 'cos all tmpBases must have match candidates; but in practice, since shortcut edges used in basesOv..(), v77 selected
																					 //and v77.tmpBaseLevel=currentLevel happened; comp..erts4() made BFS w/o shortcuts and never picked v77 as tmp, so match*() never populated v77.matchIdxs[]
			tmpBaseVerts.push_back(v);

	//empty safeCorresp
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
		verts[ safeCorresp[2*s] ]->safe = mesh2->verts[ safeCorresp[2*s + 1] ]->safe = false;
	safeCorresp.clear();

	//refill safeCorresp using matchIdxs[] values set during matchSmallTmpBases()
	for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
	{
		int tbv1 = tmpBaseVerts[i];
		for (int j = 0; j < (int) verts[tbv1]->matchIdxs.size(); j += 2) //skip odd indices which are the matchCosts of the match in the preceding even idx
		{
			int tbv2 = (int) verts[tbv1]->matchIdxs[j];

			//79-80 is a match; due to overlap shared 79 finds again 79-80 match, so safeCorresp would have duplicates if i'm not careful
			bool pairExists = false;
			for (int s = 0; s < (int) safeCorresp.size(); s += 2)
				if (safeCorresp[s] == tbv1 && safeCorresp[s+1] == tbv2) //duplicate if 79-80 exists as a pair, i.e. 79-44 and 55-80 should not cancel entrance of 79-80 to the safeCorresp; so, don't use safe=true fact
				{
					pairExists = true;
					break;
				}
			if (! pairExists)
			{
				safeCorresp.push_back(tbv1); //next safe correspondence goes to following 2 slots in safeCorresp, which is used in the next level iteration of coarseToFineMatching()
				safeCorresp.push_back(tbv2);
				verts[tbv1]->safe = mesh2->verts[tbv2]->safe = true;
				//matchIndexes[] w/ appropriate iso cost values (computed over whole mesh) will be refilled during setCorrespondenceErrsTmp()
				verts[tbv1]->matchIndexes.clear();
				mesh2->verts[tbv2]->matchIndexes.clear();
			}
		}
	}

	//compute the overall isometry cost of these corresps that span the whole mesh; while doing so, update the corresponding matchIndexes[] as well which will be used in trimCorresps() and setExtremeCorrespondences()
	if (! executionTimeOnly) //call just-below unnecessarily increases execution time to be written to paper 'cos it is uses mapping itself as the safeList and hence yields O(N^2) algo for N bases; so, replace that to get faster times
		setCorrespondenceErrsTmp(mesh2, false);	
	else
		setCorrespondenceErrsTmpEff(mesh2, false, currentLevel);	//efficient replacement that uses evenly-spaced M bases as safeList and therefore runs in O(NM) time
	//just-above gives the D_iso of immediate matches of patch-by-patch matching in coarseToFineMatching(); i'll also optimally reduce unnecessary matches and recompute D_iso after trimming
	trimCorresps(mesh2, currentLevel, finalLevel, isoCostPass1);
}

vector< int > safeCorrespStaFixed; //save the very first evenly-spaced 100-element safeCorrespSta in this and use it for the next levels
void Mesh::trimCorresps(Mesh* mesh2, int currentLevel, bool pass1Only, bool isoCostPass1)
{
	//vertices know their v_iso (vertexBasedIso) w.r.t. safeList that span whole mesh; so, trim the matches that are too costly w/o leaving any uncovered regions and w/o using a threshold

#ifdef DBG_DETAIL
	cout << "\ntrimming many-to-many safeCorresp:\n"; //in limit i keep all 1-to-1's and reduce to 1-to-1 mapping from the many-to-many input
#endif

	bool doPass3 = true, //pass # 3 is also good but you may skip it
		 pass1ToFileForZhangComparison = false; //i normally produce 443-471 mapping for nBases = 443 vs. 471, i.e. full coverage; zhang, on the other hand, produces 443-346 mapping, i.e. uncovered regions; it is not fair for me to compare my full coverage, so save my partial coverage (pass1) for zhang comparison

	vector< int > safeCorrespReduced; //safeCorresp's cool entries will sit here and in the end i'll set safeCorresp = safeCorrespReduced
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
		verts[ safeCorresp[2*s] ]->safe = mesh2->verts[ safeCorresp[2*s + 1] ]->safe = false; //safeCorresp.clear() not done 'cos i need it below

	//pass # 1: set the min-cost matches for all mesh1 verts; hence, nothing left uncovered at mesh1 in the end; many-to-1's may be seen here, e.g. 33-55 and 44-55 is chosen since 55 is the min-cost candid for both 33 and 44
	for (int s = 0; s < (int) safeCorresp.size(); s += 2)
	{
		int bv1 = safeCorresp[s], bv2 = safeCorresp[s + 1];
		float minVertexBasedIso = INF;
		int minIdx2 = -1;
		for (int i = 0; i < (int) verts[bv1]->matchIndexes.size(); i += 2)
			if (verts[bv1]->matchIndexes[i+1] < minVertexBasedIso)
			{
				minVertexBasedIso = verts[bv1]->matchIndexes[i+1];
				minIdx2 = (int) verts[bv1]->matchIndexes[i];
			}
assert(minIdx2 != -1); //this also shows that matchIndexes[] not empty
		
		//769-2021, 764-99 are 1-to-many's in the current safeCorresp; matchIndexes[] will select 764-99 but if i don't make duplicate prevention below, then 764-99'd be added twice to safeCorrespReduced, e.g. 1 for bv1-bv2=769-2021 processing and 1 for 764-99 processing
		bool pairExists = false;
		for (int s = 0; s < (int) safeCorrespReduced.size(); s += 2)
			if (safeCorrespReduced[s] == bv1 && safeCorrespReduced[s+1] == minIdx2)
			{
				pairExists = true;
				break;
			}
		if (! pairExists)
		{
			safeCorrespReduced.push_back(bv1);
			safeCorrespReduced.push_back(minIdx2);
			verts[bv1]->safe = mesh2->verts[minIdx2]->safe = true; //to use as continue; condition below
		}
	}
	if (pass1ToFileForZhangComparison)
	{
		FILE* fPtr = fopen("trimPass1.dat", "w");
		for (int s = 0; 2*s+1 < (int) safeCorrespReduced.size(); s++)
			fprintf(fPtr, "%d\t%d\n", safeCorrespReduced[2*s], safeCorrespReduced[2*s + 1]);
		fclose(fPtr);
	}
	if (isoCostPass1) //compute isometryCostPass1; computed and used only by fileForSymProblem=1 mode
	{
		vector< int > safeCorrespSave = safeCorresp; //save original safeCorresp to roll back in the end of this block
		safeCorresp = safeCorrespReduced; //many-to-1 safeCorresp to be processed by setCorrespondenceErrsTmpManyTo1()
		setCorrespondenceErrsTmpManyTo1(mesh2); //desired isometryCostPass1 value made ready
		safeCorresp = safeCorrespSave; //roll back
	}

	if(! pass1Only) //last level does pass1 only, so skip this pass2
		//pass # 2: something, e.g. 2029, may left uncovered in mesh2 assuming 2021-2022 (iso cost = .2), 2021-2029 (cost = .5) ==> 2021-2022 selected above; also, 764-2029 (.8), 764-760 (.1) --> 764-760; so, mesh2.2029 uncovered; so, set it to best candid (2021) via 1-to-many
		for (int s = 0; s < (int) safeCorresp.size(); s += 2)
		{
			int bv1 = safeCorresp[s], bv2 = safeCorresp[s + 1];
			if (mesh2->verts[bv2]->safe)
				continue; //mesh2.v already has a match, so covered and no need to worry about it

			float minVertexBasedIso = INF;
			int minIdx1 = -1;
			for (int i = 0; i < (int) mesh2->verts[bv2]->matchIndexes.size(); i += 2)
				if (mesh2->verts[bv2]->matchIndexes[i+1] < minVertexBasedIso)
				{
					minVertexBasedIso = mesh2->verts[bv2]->matchIndexes[i+1];
					minIdx1 = (int) mesh2->verts[bv2]->matchIndexes[i];
				}
assert(minIdx1 != -1); //this also shows that mesh2.matchIndexes[] not empty
		
			bool pairExists = false;
			for (int s = 0; s < (int) safeCorrespReduced.size(); s += 2)
				if (safeCorrespReduced[s] == minIdx1 && safeCorrespReduced[s+1] == bv2)
				{
					pairExists = true;
					break;
				}
			if (! pairExists)
			{
				safeCorrespReduced.push_back(minIdx1);
				safeCorrespReduced.push_back(bv2);
				verts[minIdx1]->safe = mesh2->verts[bv2]->safe = true; //to use as continue; condition above
			}
		}
	//convert the many-to-many safeCorresp to the optimally reduced possibly 1-to-1 one
	safeCorresp = safeCorrespReduced;
	if(! pass1Only) //last level does pass1 only, so skip this pass2
		for (int s = 0; s < (int) safeCorresp.size(); s += 2)
		{
			//matchIndexes[] w/ appropriate iso cost values (computed over whole mesh) will be refilled during setCorrespondenceErrsTmp()
			verts[ safeCorresp[s] ]->matchIndexes.clear();
			mesh2->verts[ safeCorresp[s + 1] ]->matchIndexes.clear();
			//verts[ safeCorresp[s] ]->matchIdx = safeCorresp[s + 1]; //required for adaptList creation in computeTmpBaseVerts4() and for parentList creation in matchSmallTmpBases(); some info may overwritten due to 1-to-many cases but not so important
			//mesh2->verts[ safeCorresp[s + 1] ]->matchIdx = safeCorresp[s]; //dual op required since mesh2.computeTmpBaseVerts4()'ll access matchIDx values as well
			//matchIdx values will be set by setCorrespondenceErrsTmp( , true) to prevent overwriting for 1-to-many's; i'll keep the min-cost matchIdx since i've access to vertexBasedIso there; for comparisons, i need matchCostI initialized
			verts[ safeCorresp[s] ]->matchCostI = INF; //mesh2.matchCostI not used in setCorrespondenceErrsTmp() so skipped
		}
	//compute the overall isometry cost of these optimally reduced, possibly 1-1, corresps that span the whole mesh; while doing so, update the corresponding matchIndexes[] as well which will be used in setExtremeCorrespondences()
	setCorrespondenceErrsTmp(mesh2, true);
	
	if (! pass1Only && doPass3 && currentLevel > 0) //don't do pass # 3 in vain for the initial euclidean match; i may rarely spoil it w/ this redundant pass # 3
	{
		//pass # 3: i learnt mesh1.matchCostI values and avg isometryCost (D_iso) w.r.t. whole mesh just-above; cancel bad matches w/ matchCostI > 2*D_iso and for such a cancelled match v-u, assign best v-w and t-u in order not to leave them unmatched (uncovered region prevention)
assert((int) safeCorresp.size() == 2 * (int) safeCorrespCosts.size());
		//one of 2 just-below will be the traversal list (\S^{'} in c2f paper) to be used below
		vector< int > safeCorrespDyn, //as i produce good v-w and t-u out of bad v-u, i make the replacement dynamically in that scc-iteration below, and use the dynamically changed/better safeCorrespDyn for the scc+1 iteration; initially safeCorrespDyn = safeCorresp for sure
					  safeCorrespSta; //the static safeList that the t/wCandidates will traverse; it consists of good elements of all safeCorresp
		float bigCost = 1.5f * isometryCost; //2 is too much 'cos bads start earlier than that

//bigCost = 2.5f * isometryCost; enable only at last level??????????????

		bool useSubset = true; //instead of using safeCorrespDyn as safeList that gets updated everytime v-u is converted to v-w and t-u and that holds all matches over the surface, use only subset of matches over the surface, that have matchCostI < avg isometryCost
							   //this subset usage is mandatory especially for high levels of big inputs, e.g. level7+ of HORSE, 'cos safeCorrespDyn.size() traversal becomes intractable, e.g. ~40min for level7; use a fixed & low size safeCorrespSta.size() traversal for efficiency (besides it should be more accurate since safeCorrespSta has the cool entries)
		//always pick the best 100 elements from safeCorrespCosts to keep it small yet accurate
		int topN = 100*2; //*2 is 'cos safeCorrespSta will have 2 elements for 1 cost in safeCorrespCosts; so, topN/2 matches will be in safeCorrespSta in the end
//topN = 1000*2; //keep it big enough so that local effects
		if ((int) safeCorrespCosts.size() <= topN / 2)
			useSubset = false; //use safeCorrespDyn code since it modifies safeCorrespDyn within the loop and so better than the fixed safeCorrespSta filled (for the small inputs fixed safeCorrespSta is bad 'cos i take all matches into safeCorrespSta which includes bad matches that should have been updated within loop, as in safeCorrespDyn)


//useSubset = false; //don't bother topN evenly spaced verts as the usage whole set is more accurate (but way slower); accuracy increment doesn't worth time increment


		if (useSubset)
		{
			//traversal list
			/* this gives safeCorrespSta of size 2721 for HORSE level7 (and increases as levels advance), which is still too big
			for (int scc = 0; scc < (int) safeCorrespCosts.size(); scc++)
				if (safeCorrespCosts[scc] < isometryCost) //use < 0.8*isometryCost to keep safeCorrespSta even smaller
				{
					safeCorrespSta.push_back(safeCorresp[2*scc]);
					safeCorrespSta.push_back(safeCorresp[2*scc + 1]);
				}*/
			if (! safeCorrespStaFixed.empty()) //this global list is filled and ready to be used from now on as the good evenly-spaced safeList
				safeCorrespSta = safeCorrespStaFixed;
			else if ((int) safeCorrespCosts.size() > topN / 2)
			{
				for (int scc = 0; scc < (int) safeCorrespCosts.size(); scc++)
					verts[ safeCorresp[2*scc] ]->processed = false; //=true means added to safeCorrespSta; note that, 1-to-many's also avoided in safeCorrespSta naturally by this processed effect, which is not a big deal but anyway
				while ((int) safeCorrespSta.size() < topN)
				{
					float minIsoCost = INF;
					int minIsoIdx1 = -1, minIsoIdx2;
					for (int scc = 0; scc < (int) safeCorrespCosts.size(); scc++)
						if (safeCorrespCosts[scc] < minIsoCost && ! verts[ safeCorresp[2*scc] ]->processed)
						{
							minIsoCost = safeCorrespCosts[scc];
							minIsoIdx1 = safeCorresp[2*scc];
							minIsoIdx2 = safeCorresp[2*scc + 1];
						}
					if (minIsoIdx1 == -1) //all elements are processed (due to 1-to-many's 1+ candidates get canceled as a result of 1 v.processed effect; besides, forcing evenly-spaced may provide less minIsoIdx1/2 than expected); so, quit early before reaching topN elements
						break;
					safeCorrespSta.push_back(minIsoIdx1);
					safeCorrespSta.push_back(minIsoIdx2);
					verts[minIsoIdx1]->processed = true; //added to safeCorrespSta; this has the side effect of not adding any other minIsoIdx1-* match (1-to-many's) to safeCorrespSta even if they are in the top100

					/////////// evenly-spaced top100 //////////////
					//using top100 as is may suck 'cos all 100 may cluster at the left leg, which makes geodesics from thumb to safeList elements = geodesics from head to safeList elements, and hence may select thumb-head match; so, distribute top100 uniformly, by simply marking the r-nghbhood of minIsoIdx1
					for (int scc = 0; scc < (int) safeCorrespCosts.size(); scc++) //related elements that will be touched in the upcoming iteration of this while loop
						if (verts[minIsoIdx1]->dSpanningUnn[ safeCorresp[2*scc] ] <= radii[currentLevel-2]) //[level] is the current radius (= sample1), [level-1] is the rofPrevLevel (= r1), [level-2] is 2*rOfPrevLevel if i had divided r /= 2 in the end of all iters (i'm setting r w.r.t. biggest-patch which roughly has r /= 2 effect))
							verts[ safeCorresp[2*scc] ]->processed = true; //mark it whick prevents this from being added to safeCorrespSta in the upcoming iter					
					/////////// evenly-spaced top100 ends //////////////
				}
				//evenly-spaced safeCorrespSta is prepared for the very first time; save it and use this saved list for the upcoming levels (not bother creating a new 100-size safeCorrespSta 'cos radii[level-2] for the next level will be small so that all 100 may be clustered at the left leg again; so, use this safely evenly-spaced list from now on)
				if ((int) safeCorrespSta.size() == topN) //remove this condition to use a safeCorrespStaFixed of <100 matches (quite possible but not for sure)
					safeCorrespStaFixed = safeCorrespSta;
			}
			else //current level provides a small safeCorrespCosts; so, use it as is w/o bothering w/ picking the topN elements from it as safe list
				for (int scc = 0; scc < (int) safeCorrespCosts.size(); scc++)
				{
cout << "cannot come here for sure 'cos useSubset made false above\n"; exit(0);
					safeCorrespSta.push_back(safeCorresp[2*scc]);
					safeCorrespSta.push_back(safeCorresp[2*scc + 1]);
				}
#ifdef DBG_DETAIL
			cout << "subset of matches as traversal list has size: " << (int) safeCorrespSta.size() / 2 << endl;
#endif
			//and action [heavily adapted from !useSubset part; see comments there if necessary]
			vector< int > safeCorrespReduced; //safeCorresp's cool entries will sit here and in the end i'll set safeCorresp = safeCorrespReduced; keep this separate list 'cos i need safeCorresp intact during iters 'cos safeCorrespCosts is consistent w/ intact safeCorresp
			//traversal list here is safeCorrespSta; safeCorrespReduced'll include only cool matches in the end so that i do safeCorresp = safeCorrespReduced; later
			safeCorrespReduced = safeCorresp;
			int nOutliers = 0, nUpdates = 0; //# of ~outlier matches in original safeCorresp (output of pass # 2) that gets updated (and # of updates)
			for (int scc = 0; scc < (int) safeCorrespCosts.size(); scc++)
			{
				if (safeCorrespCosts[scc] <= bigCost)
					continue; //corresponding safeCorresp[2*scc - 2]-safeCorresp[2*scc - 1] is not a bad match; so, not enters pass # 3 at all

				int v = safeCorresp[2*scc], u = safeCorresp[2*scc + 1], w, t; //v-u is the bad match which will be replaced w/ v-w and t-u; get the v-u from safeCorresp, and not safeCorrespReduced/Sta, 'cos it's consistent w/ safeCorrespCosts
#ifdef DBG_DETAIL
				cout << safeCorrespCosts[scc] << " > " << bigCost << " for " << v << " " << u << "\t[subset]\n";
#endif
				nOutliers++;

				//v-w creation
				float minIso = INF;
				for (int a = 0; a < (int) safeCorresp.size(); a += 2) //candidates to test
				{
					int nAdds = 0, wCandidate = safeCorresp[a+1]; //w candidate
					//v vs. wCandidate active assumption
					float vertexBasedIso = 0.0f;
					for (int s = 0; s < (int) safeCorrespSta.size(); s += 2) //traversal
					{
						int s1 = safeCorrespSta[s], s2 = safeCorrespSta[s + 1];
						if (s1 == v || s2 == wCandidate)
							continue;
						float diffForNormalized = verts[v]->dSpanning[s1] - mesh2->verts[wCandidate]->dSpanning[s2]; //v,wCandidate
						vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if v vs. wCandidate is a good correspondence
						nAdds++;
					}
					vertexBasedIso /= nAdds;
					if (vertexBasedIso < minIso)
					{
						minIso = vertexBasedIso;
						w = wCandidate;
					}
//cout << v << "\t" << wCandidate << ": " << vertexBasedIso << "\t\t";
				}
#ifdef DBG_DETAIL
				cout << "v-w " << v << " " << w << "\t" << minIso << "\n\n";
#endif
				//t-u creation
				minIso = INF;
				for (int a = 0; a < (int) safeCorresp.size(); a += 2) //candidates to test
				{
					int nAdds = 0, tCandidate = safeCorresp[a]; //w candidate
					//tCandidate vs. u active assumption
					float vertexBasedIso = 0.0f;
					for (int s = 0; s < (int) safeCorrespSta.size(); s += 2) //traversal
					{
						int s1 = safeCorrespSta[s], s2 = safeCorrespSta[s + 1];
						if (s1 == v || s1 == tCandidate || s2 == u)
							continue;
						float diffForNormalized = verts[tCandidate]->dSpanning[s1] - mesh2->verts[u]->dSpanning[s2]; //tCandidate,u
						vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if tCandidate vs. u is a good correspondence
						nAdds++;
					}
					vertexBasedIso /= nAdds;
					if (vertexBasedIso < minIso)
					{
						minIso = vertexBasedIso;
						t = tCandidate;
					}
				}
#ifdef DBG_DETAIL
				cout << "t-u " << t << " " << u << "\t" << minIso << "\n\n\n\n";
#endif
				//replace v-u in safeCorrespReduced w/ v-w and t-u (if duplicates not occur) to keep safeCorrespDyn dynamic and better (to be used in the next scc iter)
				bool vwExists = false; //duplicate prevention
				for (int a = 0; a < (int) safeCorrespReduced.size(); a += 2) //!useSubset part acts on safeCorrespDyn which is eventually set to safeCorresp
					if (safeCorrespReduced[a] == v && safeCorrespReduced[a+1] == w) //pair v-w detected
					{
						vwExists = true;
						break;
					}
				for (int a = 0; a < (int) safeCorrespReduced.size(); a += 2)
					if (safeCorrespReduced[a] == v && safeCorrespReduced[a+1] == u) //pair v-u is detected, i.e. the region to modify
					{
						if (! vwExists)
							safeCorrespReduced[a+1] = w; //v-w replaces v-u (leave v as is at idx [a])
						else
						{
							//v-w already in safeCorrespDyn; so, just get rid of bad v-u by erasing [a] & [a+1]
							safeCorrespReduced.erase(safeCorrespReduced.begin() + a); //list.erase(begin + 4) erases list[4]
							safeCorrespReduced.erase(safeCorrespReduced.begin() + a); //[a+1] mentioned above is the [a] of the newly-shrunk safeCorrespDyn
							//loop controller safeCorrespReduced.size() changes after these erases but no worries 'cos i'm break'ing anyway
						}

						break;
					}
				bool tuExists = false; //duplicate prevention
				for (int a = 0; a < (int) safeCorrespReduced.size(); a += 2)
					if (safeCorrespReduced[a] == t && safeCorrespReduced[a+1] == u)
					{
						tuExists = true;
						break;
					}
				if (! tuExists) //if t-u is already there then no need to insert its duplicate to safeCorrespDyn
				{
					safeCorrespReduced.push_back(t);
					safeCorrespReduced.push_back(u);
				}
				if (w != u)
					nUpdates++;
				if (t != v)
					nUpdates++;
			} //end of scc
			//safeCorrespReduced is ready and it does not involve the bad outlier matches that safeCorresp did; so, set safeCorresp = safeCorrespReduced and recompute its quality
			safeCorresp = safeCorrespReduced;
#ifdef DBG_DETAIL
			cout << nUpdates << "/" << (int) safeCorrespCosts.size() << " updates by pass # 3 [subset]\n";
#endif
//FILE* fPtr = fopen("nOutliers.dat", "a"); if (currentLevel==1)fprintf(fPtr,"===== %d-%d =====\n", frameID, mesh2->frameID);fprintf(fPtr, "%d\t%d / %d updates by pass # 3 [subset]\tnOutliers: %d\n", currentLevel, nUpdates, (int) safeCorrespCosts.size(), nOutliers); fclose(fPtr);
		} //end of useSubset
		else //!useSubset
		{
			//traversal list
			safeCorrespDyn = safeCorresp;
			//and action
			int nOutliers = 0, nUpdates = 0; //# of ~outlier matches in original safeCorresp (output of pass # 2) that gets updated (and # of updates)
			for (int scc = 0; scc < (int) safeCorrespCosts.size(); scc++)
			{
				if (safeCorrespCosts[scc] <= bigCost)
					continue; //corresponding safeCorresp[2*scc - 2]-safeCorresp[2*scc - 1] is not a bad match; so, not enters pass # 3 at all

				int v = safeCorresp[2*scc], u = safeCorresp[2*scc + 1], w, t; //v-u is the bad match which will be replaced w/ v-w and t-u; get the v-u from safeCorresp, and not safeCorrespDyn, 'cos it's consistent w/ safeCorrespCosts
#ifdef DBG_DETAIL
				cout << safeCorrespCosts[scc] << " > " << bigCost << " for " << v << " " << u << "\t[dynamic]\n";
#endif
				nOutliers++;

				//v-w creation
				float minIso = INF;
				for (int a = 0; a < (int) safeCorrespDyn.size(); a += 2) //candidates to test
				{
					int nAdds = 0, wCandidate = safeCorrespDyn[a+1]; //w candidate
					//v vs. wCandidate active assumption
					float vertexBasedIso = 0.0f;
					for (int s = 0; s < (int) safeCorrespDyn.size(); s += 2) //traversal
					{
						int s1 = safeCorrespDyn[s], s2 = safeCorrespDyn[s + 1];
						if (s1 == v || s2 == wCandidate) //safeList is the current safeCorrep - {(v-*)} (so, bad v-u is excluded); 2nd: prevent wCandidate.dSpanning[s2]=0 case below
							continue;
						float diffForNormalized = verts[v]->dSpanning[s1] - mesh2->verts[wCandidate]->dSpanning[s2]; //v,wCandidate
						vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if v vs. wCandidate is a good correspondence
						nAdds++;
					}
					vertexBasedIso /= nAdds;
					if (vertexBasedIso < minIso)
					{
						minIso = vertexBasedIso;
						w = wCandidate;
					}
//cout << v << "\t" << wCandidate << ": " << vertexBasedIso << "\t\t";
				}
#ifdef DBG_DETAIL
				cout << "v-w" << v << " " << w << "\t" << minIso << "\n\n";
#endif
				//t-u creation
				minIso = INF;
				for (int a = 0; a < (int) safeCorrespDyn.size(); a += 2) //candidates to test
				{
					int nAdds = 0, tCandidate = safeCorrespDyn[a]; //w candidate
					//tCandidate vs. u active assumption
					float vertexBasedIso = 0.0f;
					for (int s = 0; s < (int) safeCorrespDyn.size(); s += 2) //traversal
					{
						int s1 = safeCorrespDyn[s], s2 = safeCorrespDyn[s + 1];
						if (s1 == v || s1 == tCandidate || s2 == u) //safeList is the current safeCorrep - {(v-*)} (so, bad v-u is excluded) minus {(tCandidate-*)} (so, tCandidate.dSpanning[s1] = 0 case is prevented) minus {(*-u)} (to prevent u.dSpanning[s2]=0 below)
							continue;
						float diffForNormalized = verts[tCandidate]->dSpanning[s1] - mesh2->verts[u]->dSpanning[s2]; //tCandidate,u
						vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if tCandidate vs. u is a good correspondence
						nAdds++;
					}
					vertexBasedIso /= nAdds;
					if (vertexBasedIso < minIso)
					{
						minIso = vertexBasedIso;
						t = tCandidate;
					}
//cout << tCandidate << "\t" << u << ": " << vertexBasedIso << "\t\t";
				}
#ifdef DBG_DETAIL
				cout << "t-u" << t << " " << u << "\t" << minIso << "\n\n\n\n";
#endif
				//replace v-u in safeCorrespDyn w/ v-w and t-u (if duplicates not occur) to keep safeCorrespDyn dynamic and better (to be used in the next scc iter)
				bool vwExists = false; //duplicate prevention
				for (int a = 0; a < (int) safeCorrespDyn.size(); a += 2)
					if (safeCorrespDyn[a] == v && safeCorrespDyn[a+1] == w)
					{
						vwExists = true;
						break;
					}
				for (int a = 0; a < (int) safeCorrespDyn.size(); a += 2)
					if (safeCorrespDyn[a] == v && safeCorrespDyn[a+1] == u) //pair v-u is detected
					{
						if (! vwExists)
							safeCorrespDyn[a+1] = w; //v-w replaces v-u (leave v as is at idx [a])
						else
						{
							//v-w already in safeCorrespDyn; so, just get rid of bad v-u by erasing [a] & [a+1]
							safeCorrespDyn.erase(safeCorrespDyn.begin() + a); //list.erase(begin + 4) erases list[4]
							safeCorrespDyn.erase(safeCorrespDyn.begin() + a); //[a+1] mentioned above is the [a] of the newly-shrunk safeCorrespDyn
							//loop controller safeCorrespDyn.size() changes after these erases but no worries 'cos i'm break'ing anyway
						}

						break;
					}
				bool tuExists = false; //duplicate prevention
				for (int a = 0; a < (int) safeCorrespDyn.size(); a += 2)
					if (safeCorrespDyn[a] == t && safeCorrespDyn[a+1] == u)
					{
						tuExists = true;
						break;
					}
				if (! tuExists) //if t-u is already there then no need to insert its duplicate to safeCorrespDyn
				{
					safeCorrespDyn.push_back(t);
					safeCorrespDyn.push_back(u);
				}
				if (w != u)
					nUpdates++;
				if (t != v)
					nUpdates++;
			} //end of scc
			//safeCorrespDyn is ready and it does not involve the bad outlier matches that safeCorresp did; so, set safeCorresp = safeCorrespDyn and recompute its quality
			safeCorresp = safeCorrespDyn;
#ifdef DBG_DETAIL
			cout << nUpdates << "/" << (int) safeCorrespCosts.size() << " updates by pass # 3 [dynamic]\n";
#endif
//FILE* fPtr = fopen("nOutliers.dat", "a"); if (currentLevel==1)fprintf(fPtr,"===== %d-%d =====\n", frameID, mesh2->frameID);fprintf(fPtr, "%d\t%d / %d updates by pass # 3 [subset]\tnOutliers: %d\n", currentLevel, nUpdates, (int) safeCorrespCosts.size(), nOutliers); fclose(fPtr);
		} //end of !useSubset
	
		//safeCorresp is ready and it does not involve the bad outlier matches that safeCorresp before pass # 3 did; so, recompute its quality
		for (int s = 0; s < (int) safeCorresp.size(); s += 2)
		{
			verts[ safeCorresp[s] ]->matchIndexes.clear();
			mesh2->verts[ safeCorresp[s + 1] ]->matchIndexes.clear();
			verts[ safeCorresp[s] ]->matchCostI = INF;
		}
		//compute the overall isometry cost of these optimally reduced, possibly 1-1, corresps that span the whole mesh; while doing so, update the corresponding matchIndexes[] as well which will be used in setExtremeCorrespondences()
		if (! executionTimeOnly) //call just-below unnecessarily increases execution time to be written to paper 'cos it is just to learn the isometric distortion; so, skip that info to get faster times
			setCorrespondenceErrsTmp(mesh2, true); //safeCorrespCosts will again be refilled but won't be used
	}

	//the best and worst vertex-based correspondences for drawing purposes
	if (! executionTimeOnly) //call just-below unnecessarily increases execution time to be written to paper 'cos it is just to learn the isometric distortion; so, skip that info to get faster times
		setExtremeCorrespondences(r1, true, true);

	cout << "done!\n\n";
}

void Mesh::setCorrespondenceErrsTmp(Mesh* mesh2, bool reducedCorresp)
{
	//similar to setCorrespondenceErrs() but this works on many-to-many safeCorresp[] filled by refillSafeCorrespTmp() for the 1st call, and its optimally reduced possibly 1-1 version for the 2nd call

#ifdef DBG_DETAIL
	if (reducedCorresp)
		cout << "\nD_iso(optimally reduced safeCorreep):\n"; //2nd call
	else
		cout << "\nD_iso(many-to-many safeCorreep):\n"; //1st call
#endif

	//for this, i need dSpanning for all active base verts; for efficiency, use top20 of this whole list, compute dijkstra just for them later; or don't bother 'cos this machine can handle it anyway

	if (reducedCorresp)
		safeCorrespCosts.clear(); //corresponding matchCostI of each match will be saved, i.e. safeCorrespCosts[i] holds the matchCostI of safeCorresp[2i]-safeCorresp[2i+1]
	isometryCost = groundTruthCost = 0.0f;
	int scSize = (int) safeCorresp.size() / 2; //final size of safeCorresp to be used for averaging; div by 2 'cos every 2 item counts 1 'cos they're related
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
	{
		int bv1 = safeCorresp[2*s], bv2 = safeCorresp[2*s + 1];
		verts[bv1]->matchIndexes.push_back((float) bv2); //[i]: match, [i+1]: iso cost of i'th match computed this big safeCorresp that spans the whole surface
		mesh2->verts[bv2]->matchIndexes.push_back((float) bv1);

		float vertexBasedIso = 0.0f;
		int nAdds = 0; //for many-to-many case, # of continue'd matches in safeList not always 1; so, maintain te denominator explicitly
		for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
		{
			int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
			if (s1 == bv1 || s2 == bv2) //assume s1-s2 is a good match being tested and s1(=tbv1)-tbv2 is a bad match in safeList; if i canceled continue just-below, then good s1-s2 would get a higher cost wrongly
				continue; //idea is to prevent dSpanning=0's below; so, bv1-s1 and bv2-s2 prevented if bv1=s1 or bv2=s2

			//use normalized dSpanning[] for isometryCost of whole correspondence (dSpanning[] are already normalized into [0,1] after comp..BaseGeoAff..Mtrx())
			float bv1s1 = verts[bv1]->dSpanning[s1], bv2s2 = mesh2->verts[bv2]->dSpanning[s2];
//if (! (bv1s1 >= 0 && bv1s1 <= 1 && bv2s2 >= 0 && bv2s2 <= 1)) cout << bv1s1 << " && " << bv2s2 << "\t" << bv1 << " " << bv2 << " " << s1 << " " << s2 << endl; //may happen in c2f 'cos maxGeoDist is based on initial 6 bases; in latest levels, a geoDist may (slightly) exceed that max, e.g. heel to head (initial) vs. toe to head (later)
			//if bv1 vs. bv2 is a good correspondence, then geoDist(bv1, s1) = geoDist(bv2, s2) for all s1 & s2 in safeCorresp
			float diffForNormalized = bv1s1 - bv2s2; //use normalized dSpanning values to make this value comparable w/ any input meshes
			vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if bv1 vs. bv2 is a good correspondence (must use this for isometryCost below)
			nAdds++;
		}
		vertexBasedIso /= nAdds; //fabs for matchCostI in [0,1], making matchCostI / nAdds in [0,1] 'cos nAdds amount of += [0,1] made above
		//global isometry cost (D_iso in papers)
		isometryCost += vertexBasedIso;

		verts[bv1]->matchIndexes.push_back(vertexBasedIso); //this [i+1] is the iso cost of match i [i]
		mesh2->verts[bv2]->matchIndexes.push_back(vertexBasedIso);
		//matchCostG
/*		if (theInput == JUMPING || theInput == DANCING || theInput == HORSE ||
			theInput == SCAPE || theInput == TOSCA || theInput == SHREC11) //ground-truth available only for these three
			groundTruthCost += verts[bv1]->dSpanningUnn[bv2];*/

		//matchIdx values required for adaptList creation in computeTmpBaseVerts4() and for parentList creation in matchSmallTmpBases()
		if (reducedCorresp && vertexBasedIso < verts[bv1]->matchCostI) //work on optimal/reduced safeCorresp only 'cos i can trust their better iso costs
		{
			verts[bv1]->matchIdx = bv2;
			mesh2->verts[bv2]->matchIdx = bv1;
			verts[bv1]->matchCostI = vertexBasedIso;
		}
		//safeCorrespCosts required for the pass # 3 of trimCorresps()
		if (reducedCorresp)
			safeCorrespCosts.push_back(vertexBasedIso);

//#ifdef DBG_DETAIL
//		cout << bv1 << "==\t==" << bv2 << "\t" << vertexBasedIso << endl; //matches in safeCorresp whose D_iso is being computed
//#endif
	}
	groundTruthCost /= scSize;
	//global isometry cost
	isometryCost /= scSize; //scSize amount of += [0,1] made above; so, div it by scSize to make isometryCost in [0,1]
	//whole correspondence cost
assert(isometryCost >= 0 && isometryCost <= 1);

/*	if (theInput == JUMPING || theInput == DANCING || theInput == HORSE ||
		theInput == SCAPE || theInput == TOSCA || theInput == SHREC11) //we've gound-truth info as well
		cout << "ground-truth cost: " << groundTruthCost << " (= " << groundTruthCost/r1 << "r, = " << groundTruthCost/maxGeoDistG << "n)\nisometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n"; //ground-truth costs not in [0,1], so use unnormalized r
	else*/
	{
		cout << "isometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n";
		groundTruthCost = -0.9f * r1; //an invalid # to fprint just to say ground-truth n/a
	}
}
void Mesh::setCorrespondenceErrsTmpEff(Mesh* mesh2, bool reducedCorresp, int currentLevel)
{
	//similar to setCorrespondenceErrsTmp() but this uses a safelist of size M (evenly-spaced bases) and hence O(NM) time whereas setCorrespondenceErrsTmp() is O(N^2) 'cos it uses the mapping (safeCorresp[]) itself as safelist

#ifdef DBG_DETAIL
	if (reducedCorresp)
		cout << "\nD_iso(optimally reduced safeCorreep):\n"; //2nd call
	else
		cout << "\nD_iso(many-to-many safeCorreep):\n"; //1st call
#endif

	//for this, i need dSpanning for all active base verts; for efficiency, use top20 of this whole list, compute dijkstra just for them later; or don't bother 'cos this machine can handle it anyway

	/////////////////////// safeList preparation ////////////////////////////
	//traversal list (\S^{'} in c2f paper) of size M (evenly-spaced bases) to be used below
	vector< int > safeCorrespSta; //the static safeList that the elements of safeCorresp will traverse
	int topN = 100*2; //*2 is 'cos safeCorrespSta will have 2 elements for 1 cost in safeCorrespCosts; so, topN/2 matches will be in safeCorrespSta in the end; M above is equivalent to topN here
	//decide safeCorrespSta
	if ((int) safeCorresp.size() <= topN) //current safeCorresp is small already, so use it directly as safeList, i.e. safeList of safeCorresp is safeCorresp itself
	{
		safeCorrespSta = safeCorresp;
		cout << "traversal list is safeCorresp itself w/ size " << (int) safeCorrespSta.size() << endl;
	}
	else if (! safeCorrespStaFixed.empty()) //this global list is filled and ready to be used from now on as the good evenly-spaced safeList
	{
		safeCorrespSta = safeCorrespStaFixed;
		cout << "traversal list is precomputed global list w/ size " << (int) safeCorrespSta.size() << endl;
	}
	else //if ((int) safeCorrespCosts.size() > topN / 2)
	{
		for (int scc = 0; scc < (int) safeCorrespCosts.size(); scc++)
			verts[ safeCorresp[2*scc] ]->processed = false; //=true means added to safeCorrespSta; note that, 1-to-many's also avoided in safeCorrespSta naturally by this processed effect, which is not a big deal but anyway
		while ((int) safeCorrespSta.size() < topN)
		{
			float minIsoCost = INF;
			int minIsoIdx1 = -1, minIsoIdx2;
			for (int scc = 0; scc < (int) safeCorrespCosts.size(); scc++)
				if (safeCorrespCosts[scc] < minIsoCost && ! verts[ safeCorresp[2*scc] ]->processed)
				{
					minIsoCost = safeCorrespCosts[scc];
					minIsoIdx1 = safeCorresp[2*scc];
					minIsoIdx2 = safeCorresp[2*scc + 1];
				}
			if (minIsoIdx1 == -1) //all elements are processed (due to 1-to-many's 1+ candidates get canceled as a result of 1 v.processed effect; besides, forcing evenly-spaced may provide less minIsoIdx1/2 than expected); so, quit early before reaching topN elements
				break;
			safeCorrespSta.push_back(minIsoIdx1);
			safeCorrespSta.push_back(minIsoIdx2);
			verts[minIsoIdx1]->processed = true; //added to safeCorrespSta; this has the side effect of not adding any other minIsoIdx1-* match (1-to-many's) to safeCorrespSta even if they are in the top100

			/////////// evenly-spaced top100 //////////////
			//using top100 as is may suck 'cos all 100 may cluster at the left leg, which makes geodesics from thumb to safeList elements = geodesics from head to safeList elements, and hence may select thumb-head match; so, distribute top100 uniformly, by simply marking the r-nghbhood of minIsoIdx1
			for (int scc = 0; scc < (int) safeCorrespCosts.size(); scc++) //related elements that will be touched in the upcoming iteration of this while loop
				if (verts[minIsoIdx1]->dSpanningUnn[ safeCorresp[2*scc] ] <= radii[currentLevel-2]) //[level] is the current radius (= sample1), [level-1] is the rofPrevLevel (= r1), [level-2] is 2*rOfPrevLevel if i had divided r /= 2 in the end of all iters (i'm setting r w.r.t. biggest-patch which roughly has r /= 2 effect))
					verts[ safeCorresp[2*scc] ]->processed = true; //mark it whick prevents this from being added to safeCorrespSta in the upcoming iter					
			/////////// evenly-spaced top100 ends //////////////
		}
		//evenly-spaced safeCorrespSta is prepared for the very first time; save it and use this saved list for the upcoming levels (not bother creating a new 100-size safeCorrespSta 'cos radii[level-2] for the next level will be small so that all 100 may be clustered at the left leg again; so, use this safely evenly-spaced list from now on)
		if ((int) safeCorrespSta.size() == topN) //remove this condition to use a safeCorrespStaFixed of <100 matches (quite possible but not for sure)
			safeCorrespStaFixed = safeCorrespSta;
		cout << "traversal list is just computed now w/ size " << (int) safeCorrespSta.size() << endl;
	}
	/////////////////////// safeList preparation ends ////////////////////////////

	//the rest is same as setCorrespondenceErrsTmp() except traversal lits, i.e. 2nd for loop, is safeCorrespSta[] instead of safeCorresp[] itself
	if (reducedCorresp)
		safeCorrespCosts.clear(); //corresponding matchCostI of each match will be saved, i.e. safeCorrespCosts[i] holds the matchCostI of safeCorresp[2i]-safeCorresp[2i+1]
	isometryCost = groundTruthCost = 0.0f;
	int scSize = (int) safeCorresp.size() / 2; //final size of safeCorresp to be used for averaging; div by 2 'cos every 2 item counts 1 'cos they're related; this is # of += vertexBasedIso executed and therefore used for averaging
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
	{
		int bv1 = safeCorresp[2*s], bv2 = safeCorresp[2*s + 1];
		verts[bv1]->matchIndexes.push_back((float) bv2); //[i]: match, [i+1]: iso cost of i'th match computed this big safeCorresp that spans the whole surface
		mesh2->verts[bv2]->matchIndexes.push_back((float) bv1);

		float vertexBasedIso = 0.0f;
		int nAdds = 0; //for many-to-many case, # of continue'd matches in safeList not always 1; so, maintain te denominator explicitly
		//for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
		for (int ss = 0; 2*ss+1 < (int) safeCorrespSta.size(); ss++)
		{
			int s1 = safeCorrespSta[2*ss], s2 = safeCorrespSta[2*ss + 1];
			if (s1 == bv1 || s2 == bv2) //assume s1-s2 is a good match being tested and s1(=tbv1)-tbv2 is a bad match in safeList; if i canceled continue just-below, then good s1-s2 would get a higher cost wrongly
				continue; //idea is to prevent dSpanning=0's below; so, bv1-s1 and bv2-s2 prevented if bv1=s1 or bv2=s2

			//use normalized dSpanning[] for isometryCost of whole correspondence (dSpanning[] are already normalized into [0,1] after comp..BaseGeoAff..Mtrx())
			float bv1s1 = verts[bv1]->dSpanning[s1], bv2s2 = mesh2->verts[bv2]->dSpanning[s2];
//if (! (bv1s1 >= 0 && bv1s1 <= 1 && bv2s2 >= 0 && bv2s2 <= 1)) cout << bv1s1 << " && " << bv2s2 << "\t" << bv1 << " " << bv2 << " " << s1 << " " << s2 << endl; //may happen in c2f 'cos maxGeoDist is based on initial 6 bases; in latest levels, a geoDist may (slightly) exceed that max, e.g. heel to head (initial) vs. toe to head (later)
			//if bv1 vs. bv2 is a good correspondence, then geoDist(bv1, s1) = geoDist(bv2, s2) for all s1 & s2 in safeCorresp
			float diffForNormalized = bv1s1 - bv2s2; //use normalized dSpanning values to make this value comparable w/ any input meshes
			vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if bv1 vs. bv2 is a good correspondence (must use this for isometryCost below)
			nAdds++;
		}
		vertexBasedIso /= nAdds; //fabs for matchCostI in [0,1], making matchCostI / nAdds in [0,1] 'cos nAdds amount of += [0,1] made above
		//global isometry cost (D_iso in papers)
		isometryCost += vertexBasedIso;

		verts[bv1]->matchIndexes.push_back(vertexBasedIso); //this [i+1] is the iso cost of match i [i]
		mesh2->verts[bv2]->matchIndexes.push_back(vertexBasedIso);
		//matchCostG
//		if (theInput == JUMPING || theInput == DANCING || theInput == HORSE) //ground-truth available only for these three
//			groundTruthCost += verts[bv1]->dSpanningUnn[bv2];

		//matchIdx values required for adaptList creation in computeTmpBaseVerts4() and for parentList creation in matchSmallTmpBases()
		if (reducedCorresp && vertexBasedIso < verts[bv1]->matchCostI) //work on optimal/reduced safeCorresp only 'cos i can trust their better iso costs
		{
			verts[bv1]->matchIdx = bv2;
			mesh2->verts[bv2]->matchIdx = bv1;
			verts[bv1]->matchCostI = vertexBasedIso;
		}
		//safeCorrespCosts required for the pass # 3 of trimCorresps()
		if (reducedCorresp)
			safeCorrespCosts.push_back(vertexBasedIso);

//#ifdef DBG_DETAIL
//		cout << bv1 << "==\t==" << bv2 << "\t" << vertexBasedIso << endl; //matches in safeCorresp whose D_iso is being computed
//#endif
	}
	groundTruthCost /= scSize;
	//global isometry cost
	isometryCost /= scSize; //scSize amount of += [0,1] made above; so, div it by scSize to make isometryCost in [0,1]
	//whole correspondence cost
assert(isometryCost >= 0 && isometryCost <= 1);

/*	if (theInput == JUMPING || theInput == DANCING || theInput == HORSE) //we've gound-truth info as well
		cout << "ground-truth cost: " << groundTruthCost << " (= " << groundTruthCost/r1 << "r, = " << groundTruthCost/maxGeoDistG << "n)\nisometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n"; //ground-truth costs not in [0,1], so use unnormalized r
	else*/
	{
		cout << "isometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n";
		groundTruthCost = -0.9f * r1; //an invalid # to fprint just to say ground-truth n/a
	}
}
void Mesh::setCorrespondenceErrsTmpManyTo1(Mesh* mesh2)
{
	//similar to setCorrespondenceErrs() but this works on many-to-one safeCorresp[] and uses the best matches for each candidate match of a given source vert; cost is averaged over these bests

	//reset matchIdx and matchCostI values so that they will start frsh for the assignments below
	for (int v = 0; v < (int) mesh2->verts.size(); v++)
	{
		mesh2->verts[v]->matchCostICpy = INF; //*Cpy thing is a bit paranoid but it ensures that the originals (w/o Cpy suffixes) never get updated here
		mesh2->verts[v]->matchIdxCpy = -1;
	}
	//decide the best selection of matches
	int scSize = (int) safeCorresp.size() / 2; //final size of safeCorresp to be used for averaging; div by 2 'cos every 2 item counts 1 'cos they're related
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
	{
		int bv1 = safeCorresp[2*s], bv2 = safeCorresp[2*s + 1];

		float vertexBasedIso = 0.0f;
		int nAdds = 0; //for many-to-many case, # of continue'd matches in safeList not always 1; so, maintain te denominator explicitly
		for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
		{
			int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
			if (s1 == bv1 || s2 == bv2) //assume s1-s2 is a good match being tested and s1(=tbv1)-tbv2 is a bad match in safeList; if i canceled continue just-below, then good s1-s2 would get a higher cost wrongly
				continue; //idea is to prevent dSpanning=0's below; so, bv1-s1 and bv2-s2 prevented if bv1=s1 or bv2=s2

			//use normalized dSpanning[] for isometryCost of whole correspondence (dSpanning[] are already normalized into [0,1] after comp..BaseGeoAff..Mtrx())
			float bv1s1 = verts[bv1]->dSpanning[s1], bv2s2 = mesh2->verts[bv2]->dSpanning[s2];
//if (! (bv1s1 >= 0 && bv1s1 <= 1 && bv2s2 >= 0 && bv2s2 <= 1)) cout << bv1s1 << " && " << bv2s2 << "\t" << bv1 << " " << bv2 << " " << s1 << " " << s2 << endl; //may happen in c2f 'cos maxGeoDist is based on initial 6 bases; in latest levels, a geoDist may (slightly) exceed that max, e.g. heel to head (initial) vs. toe to head (later)
			//if bv1 vs. bv2 is a good correspondence, then geoDist(bv1, s1) = geoDist(bv2, s2) for all s1 & s2 in safeCorresp
			float diffForNormalized = bv1s1 - bv2s2; //use normalized dSpanning values to make this value comparable w/ any input meshes
			vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if bv1 vs. bv2 is a good correspondence (must use this for isometryCost below)
			nAdds++;
		}
assert(nAdds > 0);
		vertexBasedIso /= nAdds; //fabs for matchCostI in [0,1], making matchCostI / nAdds in [0,1] 'cos nAdds amount of += [0,1] made above
		if (vertexBasedIso < mesh2->verts[bv2]->matchCostICpy) //best match (so far) selection
		{
			//mesh1 vert indices are unique yet mesh2 indices have duplicates, i.e. many-to-1; so, among duplicate mesh2 indices, maintain the best
			mesh2->verts[bv2]->matchCostICpy = verts[bv1]->matchCostI = vertexBasedIso;
			mesh2->verts[bv2]->matchIdxCpy = bv1; verts[bv1]->matchIdxCpy = bv2;
		}
	}
	//refill safeCorrep[] w/ the best matches
	safeCorresp.clear();
interestingMatches.clear();
	for (int v = 0; v < (int) mesh2->verts.size(); v++)
		if (mesh2->verts[v]->matchIdxCpy != -1)
		{
			safeCorresp.push_back(mesh2->verts[v]->matchIdxCpy); //next safe correspondence goes to following 2 slots in safeCorresp
			safeCorresp.push_back(v);
interestingMatches.push_back(mesh2->verts[v]->matchIdxCpy); //for visual
interestingMatches.push_back(v);
		}

	//given the light (quite possibly) one-to-one safeCorresp, compute global isometry cost over it
	isometryCost = groundTruthCost = 0.0f;
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
	{
		int bv1 = safeCorresp[2*s], bv2 = safeCorresp[2*s + 1];

		float vertexBasedIso = 0.0f;
		int nAdds = 0; //for many-to-many case, # of continue'd matches in safeList not always 1; so, maintain te denominator explicitly
		for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
		{
			int s1 = safeCorresp[2*ss], s2 = safeCorresp[2*ss + 1];
			if (s1 == bv1 || s2 == bv2) //assume s1-s2 is a good match being tested and s1(=tbv1)-tbv2 is a bad match in safeList; if i canceled continue just-below, then good s1-s2 would get a higher cost wrongly
				continue; //idea is to prevent dSpanning=0's below; so, bv1-s1 and bv2-s2 prevented if bv1=s1 or bv2=s2

			//use normalized dSpanning[] for isometryCost of whole correspondence (dSpanning[] are already normalized into [0,1] after comp..BaseGeoAff..Mtrx())
			float bv1s1 = verts[bv1]->dSpanning[s1], bv2s2 = mesh2->verts[bv2]->dSpanning[s2];
//if (! (bv1s1 >= 0 && bv1s1 <= 1 && bv2s2 >= 0 && bv2s2 <= 1)) cout << bv1s1 << " && " << bv2s2 << "\t" << bv1 << " " << bv2 << " " << s1 << " " << s2 << endl; //may happen in c2f 'cos maxGeoDist is based on initial 6 bases; in latest levels, a geoDist may (slightly) exceed that max, e.g. heel to head (initial) vs. toe to head (later)
			//if bv1 vs. bv2 is a good correspondence, then geoDist(bv1, s1) = geoDist(bv2, s2) for all s1 & s2 in safeCorresp
			float diffForNormalized = bv1s1 - bv2s2; //use normalized dSpanning values to make this value comparable w/ any input meshes
			vertexBasedIso += fabs(diffForNormalized); //diffForNormalized small if bv1 vs. bv2 is a good correspondence (must use this for isometryCost below)
			nAdds++;
		}
assert(nAdds > 0);
		vertexBasedIso /= nAdds; //fabs for matchCostI in [0,1], making matchCostI / nAdds in [0,1] 'cos nAdds amount of += [0,1] made above
		//global isometry cost (D_iso in papers)
		isometryCost += vertexBasedIso;
		mesh2->verts[bv2]->matchCostI = verts[bv1]->matchCostI = vertexBasedIso;

		//matchCostG
/*		if (theInput == JUMPING || theInput == DANCING || theInput == HORSE) //ground-truth available only for these three
		{
			groundTruthCost += verts[bv1]->dSpanningUnn[bv2];
			verts[bv1]->matchCostG = mesh2->verts[bv2]->matchCostG = verts[bv1]->dSpanningUnn[bv2];
		}*/
	}
	groundTruthCost /= scSize;
	//global isometry cost
	isometryCost /= scSize; //scSize amount of += [0,1] made above; so, div it by scSize to make isometryCost in [0,1]
	isometryCostPass1 = isometryCost;
//cout<<isometryCost << " ////// " << scSize << endl;
	//whole correspondence cost
//assert(isometryCost >= 0 && isometryCost <= 1);

/*information below is for call from computeCorrespondenceErrorC2F()
	if (theInput == JUMPING || theInput == DANCING || theInput == HORSE) //we've gound-truth info as well
		cout << "ground-truth cost: " << groundTruthCost << " (= " << groundTruthCost/r1 << "r, = " << groundTruthCost/maxGeoDistG << "n)\nisometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n"; //ground-truth costs not in [0,1], so use unnormalized r
	else
	{
		cout << "isometry cost: " << isometryCost << " (= " << isometryCost/normalizedR1 << "r)\n\n";
		groundTruthCost = -0.9f * r1; //an invalid # to fprint just to say ground-truth n/a
	}

	//see the variation in matchCostI so that you can ignore worst match if it is too isolated
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
		cout << verts[ safeCorresp[2*s] ]->matchCostI << "\t";*/
}

void Mesh::refillSafeCorresp(Mesh* mesh2, int when)
{
	//clears and then refills safeCorresp w/ current baseVerts entries

	//empty it
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
		verts[ safeCorresp[2*s] ]->safe = mesh2->verts[ safeCorresp[2*s + 1] ]->safe = false;
	safeCorresp.clear();
	//refill it
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int bv1 = baseVerts[i], bv2 = verts[bv1]->matchIdx;
assert(bv1 != -1);
		if (when == 1 && bv2 == -1) //for the call after an Hungarian/Blossom (when = 1), bv2 may be -1, i.e. virtual vertex
		{
			verts[bv1]->matchCostI = verts[bv1]->matchCostG = INF; //just for readability; this bv1'll be removed from baseVerts in adjustBaseVerts() anyway
			continue;
		}
		else if (bv2 == -1) //for calls other than Hungarian, bv1 may have other matches, e.g. after greedy, so not set bv1.costs = inf here, but not go further either
			continue;

		safeCorresp.push_back(bv1); //next safe correspondence goes to following 2 slots in safeCorresp
		safeCorresp.push_back(bv2);
		verts[bv1]->safe = mesh2->verts[bv2]->safe = true;
	}
}

void Mesh::setExtremeCorrespondences(float rForBase1, bool lightPrint, bool manyToMany)
{
	//sets best and worst vertex-based correspondence for drawing purposes

	float maxCostG = -INF, minCostG = INF, maxCostI = -INF, minCostI = INF;
	int worstIin2 = -1, worstGin2 = -1; //mesh2 side of the worstMatchI/worstMathG
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
	{
		int bv1 = safeCorresp[2*s];
		if (manyToMany) //mapping in safeCorresp[] may not be 1-to-1, which happens if i call here from refillSafeCorrespTmp()
		{
			float costI = -INF, costG = -INF;
			int tmpI2 = -1, tmpG2 = -1;
			for (int i = 0; i < (int) verts[bv1]->matchIndexes.size(); i += 2)
			{
				//isometry cost
				if (verts[bv1]->matchIndexes[i+1] > costI)
				{
					costI = verts[bv1]->matchIndexes[i+1]; //worst match among all match candidates of bv1
					tmpI2 = (int) verts[bv1]->matchIndexes[i];
				}
/*				//ground-truth cost
				if ((theInput == JUMPING || theInput == DANCING || theInput == HORSE ||
					theInput == SCAPE || theInput == TOSCA || theInput == SHREC11) &&
					 verts[bv1]->dSpanningUnn[ (int) verts[bv1]->matchIndexes[i] ] > costG) //recall, matchCostG = verts[ bv1 ]->dSpanningUnn[ verts[ bv1 ]->matchIdx ];
				{
					costG = verts[bv1]->dSpanningUnn[ (int) verts[bv1]->matchIndexes[i] ]; //worst match among all match candidates of bv1
					tmpG2 = (int) verts[bv1]->matchIndexes[i];
				}*/
			}
			//isometry cost
			if (costI > maxCostI) { maxCostI = costI; worstMatchI = bv1; worstIin2 = tmpI2;}
			//ground-truth cost
			if (costG > maxCostG) { maxCostG = costG; worstMatchG = bv1; worstGin2 = tmpG2;}
		}
		else //1-to-1 for sure case, mapping in safeCorresp[] is 1-1 set by greedyOpt() or finalizeMatching()
		{
			float costI = verts[bv1]->matchCostI, costG = verts[bv1]->matchCostG; //recall, matchCostG = verts[ bv1 ]->dSpanningUnn[ verts[ bv1 ]->matchIdx ];
			if (costG > maxCostG) { maxCostG = costG; worstMatchG = bv1; }
			if (costG < minCostG) { minCostG = costG; bestMatchG = bv1; }
			if (costI > maxCostI) { maxCostI = costI; worstMatchI = bv1; }
			if (costI < minCostI) { minCostI = costI; bestMatchI = bv1; }
		}
	}
//	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 && theInput != SCAPE && theInput != TOSCA)
		bestMatchG = worstMatchG = -1;
	if (manyToMany)
	{
		//since i don't have matchIdx values available for many-to-many case, i.e. c2f algo, save some special matches explicitly for drawing purposes
		verts[worstMatchI]->worstIin2 = worstIin2;
		if (worstMatchG != -1)
			verts[worstMatchG]->worstGin2 = worstGin2;
	}
	else
	{
		verts[worstMatchI]->worstIin2 = verts[worstMatchI]->matchIdx;
		if (worstMatchG != -1)
			verts[worstMatchG]->worstGin2 = verts[worstMatchG]->matchIdx; //matchIdx vals are available; Painter works on worstI/Gin2, so set them here
	}

//	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 && theInput != SCAPE && theInput != TOSCA)
		bestMatchG = worstMatchG = -1; //'cos ground-truth info of dancing and jumping man may not match; hence, not draw these lines
	if (! lightPrint) //r: given in terms of current patch radius, n: normalized by max geo dist, none: given as is
	{
		if (worstMatchI != -1) cout << worstMatchI << " -red-> " << verts[worstMatchI]->worstIin2 << " is the worst (" << maxCostI << ") (= " << maxCostI/normalizedR1 << "r) w.r.t. isometry cost\n";
		if (bestMatchI != -1) cout << bestMatchI << " -cyan-> " << verts[bestMatchI]->matchIdx << " is the best (" << minCostI << ") (= " << minCostI/normalizedR1 << "r) w.r.t. isometry cost\n";
		if (worstMatchG != -1) cout << worstMatchG << " -green-> " << verts[worstMatchG]->worstGin2 << " is the worst (" << maxCostG << ") (= " << maxCostG/rForBase1 << "r, " << maxCostG/maxGeoDistG << "n) w.r.t. ground-truth geodesic cost\n";
		if (bestMatchG != -1) cout << bestMatchG << " -magenta-> " << verts[bestMatchG]->matchIdx << " is the best (" << minCostG << ") (= " << minCostG/rForBase1 << "r, " << minCostG/maxGeoDistG << "n) w.r.t. ground-truth geodesic cost\n";
	}
	else
	{
		cout << "worst costs:\n"; //r: given in terms of current patch radius, n: normalized by max geo dist, none: given as is
//		if (theInput == JUMPING || theInput == DANCING || theInput == HORSE ||
//			theInput == SCAPE || theInput == TOSCA || theInput == SHREC11)
//			cout << "ground-truth: " << maxCostG << /*"/" << minCostG << */"\t" << maxCostG/rForBase1 << "r"/*/" << minCostG/rForBase1 << "r*/"\t" << maxCostG/maxGeoDistG << "n"/*/ << minCostG/maxGeoDistG << "n*/"\t" << worstMatchG << "->" << verts[worstMatchG]->worstGin2 << "\n" //ground-truth costs not in [0,1], so use unnormalized r
//				 << "isometry: " << maxCostI << /*"/" << minCostI << */"\t" << maxCostI/normalizedR1 << "r"/*/" << minCostI/normalizedR1 << "r*/"\t\t" << worstMatchI << "->" << verts[worstMatchI]->worstIin2 << "\n\n"; //isometry costs in [0,1], so use normalized r
//		else
		{
			cout << "isometry: " << maxCostI << /*"/" << minCostI << */"\t" << maxCostI/normalizedR1 << "r"/*/" << minCostI/normalizedR1 << "r*/"\t\t" << worstMatchI << "->" << verts[worstMatchI]->worstIin2 << "\n\n"; //ground-truth n/a in hybrid run
			maxCostG = -0.9f * rForBase1; //an invalid # to fprint just to say ground-truth n/a
		}
	}
	//globals for later printing
	maxCostIg = maxCostI; maxCostGg = maxCostG;
}

void Mesh::clearVects2()
{
	//clears matchIdxs, matchIndexes, and tmpBaseNghbhood
	
	for (int v = 0; v < (int) verts.size(); v++)
	{
		verts[v]->matchIdxs.clear();
		verts[v]->matchIndexes.clear();

		if (verts[v]->tmpBaseNghbhood) //clearing baseNghbhood crashes 'cos they'll be accessed in the next iter; tmpBaseNghbhood's are, on the other hand, useless now 'cos they've been transferred to the baseNghbhood list in coarseToFineMatching()
		{
			delete verts[v]->tmpBaseNghbhood; //clear effect
			verts[v]->tmpBaseNghbhood = NULL;
		}
	} //end of v
}

//for symHeal: 1) fileForSymProblem = 0 to learn threshold (for && isometryCostTry < threshold * 2 in matchSmallTmpBases()) as isometryCost of level 0 mapping [currently not in use]
//			   2) fileForSymProblem = -1 to create symHeal*_8x8top10.dat, i.e. the good initial mappings to be tracked until level 4 or 5
//			   3) fileForSymProblem = 1 to track mappings in symHeal*_8x8top10.dat, results put in symHeal*_8x8out.dat
//			   4) optional, fileForSymProblem = 2 and pick nthMapping according to the result in symHeal*_8x8out.dat and go as far as you want, i.e. like SGP runs
//int fileForSymProblem = -1; //0: no file stuff at all (normal SGP paper algo), -1: fprint 8! initial mappings (not all, the ones w/ iso cost < threshold for efficiency, i.e. ignore very bad ones from being fread later; also, fprint min top10 to a different file for easy access later),
	  					      //												   1: fprint level4 isometryCosts of 8! mappings (not all, the ones that make to min topN), 2: fread one of 8! (not all, ..) initial mappings and continue w/ that
void Mesh::coarseToFineMatching(Mesh* mesh2)
{
	//c2f matching: starting from 8 bases/patches, match the patches, refine the match, and then subdivide each patch to more few (5) patches and repeat through levels of iterations; symFlipCare: track top10 good maps until level trackTillLevel and then continue w/ the min-distortion map of level trackTillLevel

	//although i say level4 below in comments, the number is actually decided by user-defined trackTillLevel, which is typically 4 anyway
	FILE* ofPtr; //level4 isometry cost obtained by starting w/ the nthMapping'th initial mapping from file; ofPtr keeps these level4 values
	int minCost4 = 1; //id of the minimum cost level4 mapping, from which fileForSymProblem = 2 mode starts computations until dense correspondence reached; level-0 version of minCost4 mapping will be used of course to complete it into dense correspondence after 10+ levels
	sprintf(outFolder, "output\\symFlipCare");
	if (fileForSymProblem == 1) //15min to process file of 72 initial mappings on x86 dbg mode
	{
		char fsName[300]; sprintf(fsName, "%s\\symHeal%d-%d_8x8out track till levelX.dat", outFolder, frameID, mesh2->frameID); //where X is typically 4
		ofPtr = fopen(fsName, "w");
	}
	else if (fileForSymProblem == 2)
	{
		char fsName[300]; sprintf(fsName, "%s\\symHeal%d-%d_8x8out track till levelX.dat", outFolder, frameID, mesh2->frameID);
		FILE* ifPtr = fopen(fsName, "r");
		if (! ifPtr)
		{
			cout << "symFlipCare file: " << fsName << " not found\n";
			exit(0);
		}
		//reach minCost4 at the bottom of the file, i.e. the map advertised by the preceding tracking till level 4
		fscanf(ifPtr, "mapID\tlevel0 distortion\tlevelX distortion\tlevelX_pass1 distortion\n"); //skip header
		int dummi;
		float dummf;
		char dummc[30];
		for (int i = 0; i < 10; i++) //skip id and distortions of each maps
			fscanf(ifPtr, "%d\t%f\t\t%f\t\t%f\t%s\t%s\n", &dummi, &dummf, &dummf, &dummf, &dummc, &dummc);
		fscanf(ifPtr, "winner of the race advertised: %d\n", &minCost4); //desired value fetched
		fclose(ifPtr);
	}

	int nthMapping = (fileForSymProblem == 1 ? 1 : minCost4), //for fileForSymProblem = 2 mode, select minCost4 = ID of the min cost level4 mapping from the *out.dat file; fileForSymProblem = 1 mode uses nthMapping = 1 always
		advertisedMapID = -1; //winner of the tracked maps, i.e. the one that leads to the least isometric distortion at level4
	float minIsoCostSoFar = INF, minIsoCostSoFarPass1 = INF;
	bool firstCall = true; //of initialEuclideanMatchingFromFile(); prevent doing calls like computeGaussianCurvatures(), hybridModelsToSameCoordFrame() 2+ times redundantly; 'cos firstCall already sets them
	do //for ( ; level < maxLevel=5; level++) will be executed consecutively to handle symmetry problem if fileForSymProblem = 1 (i.e. level4'll be reached for each initial mapping fread and the mapping w/ min level4 cost will be maintained, hoping that it kills the sym problem)
	{
		int level, startLevel = 0, exitLevel = 28, maxLevel = trackTillLevel+1/*7*/;
//maxLevel = 5 takes 13 secs (# matched pnts = 270) in fileForSymProblem=1 mode when there are exactly 10 mappings to test, i.e. all top10's are there; = 6 takes 25 secs (# = 600), = 7 takes 90 secs (# = 1400)
		if (fileForSymProblem == 0)
			maxLevel = 1; //SGP C2F algo w/o bothering w/ all this symmetry healing stuff; just write the max level you want to reach
		else if (fileForSymProblem == -1)
			maxLevel = 1; //don't waste time in vain 'cos fileForSymProblem = -1 mode aims to create/fill symHeal*_8x8top10.dat file only; that file keeps isometryCosts of all good initial level0 8x8 mappings, so no need to go further in vain (good meaning not all 8! mappings, but the ones w/ < threshold); similarly, fileForSymProblem = 2
		else if (fileForSymProblem == 2)
		{
			maxLevel = maxLevelSuggested;
			if (maxLevel == -1 || maxLevel > 13)
			{
				maxLevel = 13; //similar to SGP C2F algo start, except this freads the desired nthMapping mapping and goes until the desired level
				if ((int) verts.size() > 45000 && (int) mesh2->verts.size() > 45000) //SHREC11 and TOSCA 45K+ inputs
					maxLevel = 11; //go until level10 for big meshes due to memory leakage problem
			}
		}
		float initialIsoCost = -1.0f;
		cout << "initial match for coarse-to-fine in action....\n";
//#ifdef SUBDIV_TO_5_BASES
		if (fileForSymProblem == 1 || fileForSymProblem == 2) //1: initial start candidates are in file; pick one and go 4 levels w/ it; then pick the next one, and so on; fprint level4 results; 2: pick one (manually the one w/ the min cost, not manually anymore as i automate the computation of minCost4) and go ~10 level w/ it 'till convergence since it's the best/advertised selection
			initialIsoCost = initialEuclideanMatchingFromFile(mesh2, nthMapping, firstCall, fileForSymProblem == 2);
		else
			initialEuclideanMatching(mesh2); //actually do this init even when SUBDIV_TO_5_BASES undefined, i.e. blossom case, to keep everything in the euclidean space		
			
		if (initialIsoCost == -2.0f) //no more initial mapping left in file; so, quit the outer while-loop
			break;
/*#else
		//for the very first level (0) of whole matching iteartions, i need an initial match which comes from initialSpectralMatching()
		if (refinement == 0)
			finalizeMatching(mesh2, initialSpectralMatching(mesh2, true), false); //not refining the initial spectral match since it only has ~10 widely-separated bases, L2 costs in spectral domain cannot confuse and hence the matching is already good [no, greedy needed for dog-wolf for instance]
		else if (refinement == 1)
			//initial spectral matching refined by greedy optimization
			finalizeMatching(mesh2, greedyOpt(mesh2, true, initialSpectralMatching(mesh2, true)), false ); //do greedy refinement as well
		else
			//initial spectral matching refined by alternating optimization
			finalizeMatching(mesh2, refineInitialBasesByGraph(mesh2, initialSpectralMatching(mesh2, true)), false ); //do alternating refinement as well
#endif*/
		cout << "done\n";
		
		cout << "\n\n\n\ncoarse-to-fine matching in action......\n";
		//fillPermutationMtrxs(); already called during initialEuclideanMatching() above
		//divide r1/2 w/ this rDenom as the levels increase (to get ~10 bases at each level)
		float rDenom = 3.0f;
#ifdef SUBDIV_TO_5_BASES
		rDenom = 2.0f;
#endif
		level = startLevel;
		arrayInsteadOfHeap = mesh2->arrayInsteadOfHeap = (level != 1); //level 1 uses heap and then i switch back to arrays for efficiency reasons
	
#ifdef DBG_DETAIL
		FILE* fPtrExc = fopen("excessives.dat", "w");
#endif
		//radius used at level 0
		radii.push_back(r1);
		mesh2->radii.push_back(r2);
		for ( ; level < maxLevel; level++) //level = 0 bases already created above
		{
			//after matching bases/patches of the prev level, subdivide corresponding patches and compute their matches
			
			cout << "***************** coarse-to-fine level # " << level << " *****************..\n";
//finalLevel = (level == maxLevel-1); //true if this is the very last level [this decision is kinda wrong 'cos maxLevel can be arbitrarily large; do it based on tmpBaseVErts.size vs. baseVerts.size later]
			float sample1 = r1 / rDenom, sample2 = r2 / rDenom;
//#ifdef SUBDIV_TO_5_BASES actually do this optimal r selection even when SUBDIV_TO_5_BASES undefined 'cos it is good to bound r from above for all cases, including blossom case
			//discard sample1/2 value as the current patch radius; rather, use a radius value which puts ~5 verts on the biggest-area patch (so that no patch will exceed 5 patches)
			//first learn the patch w/ the biggest area 'cos current radius will be based on it, i.e. radius will be s.t. biggest-area patch has ~5 newcomer bases on it when subdivided
			float maxArea1 = -INF, maxArea2 = -INF;
			int maxAreaBV1 = -1, maxAreaBV2 = -1;
			for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++) //for each corresponding patch, match the tmpBases COES'ly sampled over them
			{
				int bv1 = safeCorresp[2*s], bv2 = safeCorresp[2*s + 1];
				float area1 = computePatchArea(bv1, r1), area2 = mesh2->computePatchArea(bv2, r2);
				if (area1 > maxArea1)
				{
					maxArea1 = area1;
					maxAreaBV1 = bv1;
				}
				if (area2 > maxArea2)
				{
					maxArea2 = area2;
					maxAreaBV2 = bv2;
				}
			}
#ifdef DBG_DETAIL
			cout << "\nmax area patch in mesh1 & 2: " << maxAreaBV1 << " (" << maxArea1 << ") & " << maxAreaBV2 << " (" << maxArea2 << ")\n\n";
#endif
			//second (and last), come up w/ a radius that puts ~10 bases on maxAreaBV1/2
			int nTmpBases = -1, nIters = 0, mi = 9, ma = 11; //min/max acceptable nTmpBases
#ifdef SUBDIV_TO_5_BASES
#ifdef OPTIMAL_R_BY_FORMULA
			sample1 = 0.6f * sqrt(maxArea1 / PI); //0.6f is the estimation i computed on paper
			sample2 = 0.6f * sqrt(maxArea2 / PI); //fast yet accurate estimation that gives ~5 bases on the biggest area patch (for smaller patches, it shouldn't exceed ~5 since biggest has ~5)
#else
			mi = 4;
			ma = 6; //come up w/ a radius that puts ~5 bases on maxAreaBV1/2
#endif
#endif
#ifndef OPTIMAL_R_BY_FORMULA //not defined; so, find optimal r by binary search w/ trial and error on changing r (= sample1/2) values
			float inc = (r1 - sample1) / 10; //divide r1 to sample1 interval into ~10 equal pieces
			do
			{
				nTmpBases = countTmpBaseVerts(sample1, maxAreaBV1);
				if (nTmpBases > ma)
					sample1 += inc; //increase radius to decrease nTmpBases of the next iter
				else if (nTmpBases < mi)
				{
					inc /= 2.0f; //make the search more sensitive by decreasing increment amount
					sample1 -= inc; //decrease radius to increase nTmpBases of the next iter
				}
				else
					break; //cardinality of mi=9, 10, or ma=11 allowed; for others, re-iterate the do-while loop at most 9 times
			} while (++nIters < 9); //prevent infinite loop which rarely occurs due to periodical/altered change b/w >ma and <mi
			//same stuff for mesh2
			nIters = 0;
			inc = (r2 - sample2) / 10;
			do
			{
				nTmpBases = mesh2->countTmpBaseVerts(sample2, maxAreaBV2);
				if (nTmpBases > ma)
					sample2 += inc;
				else if (nTmpBases < mi)
				{
					inc /= 2.0f;
					sample2 -= inc;
				}
				else
					break; //cardinality of mi, 5 (or 10), or ma allowed; for others, re-iterate the do-while loop at most 9 times
			} while (++nIters < 9);
#endif
//#endif //of SUBDIV_TO_5_BASES

#ifdef SAMPLING_V2
			basesOverWholeMesh(sample1, level);
			mesh2->basesOverWholeMesh(sample2, level);
#endif

			//for each corresponding patch, match the tmpBases COES'ly sampled over them
			for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
			{
				int bv1 = safeCorresp[2*s], bv2 = safeCorresp[2*s + 1];
	//			cout << "\n------------ [" << s+1 << "/" << ((int) safeCorresp.size() / 2) << "] patches being handled: " << bv1 << " & " << bv2 << "\tsizes: " << verts[bv1]->baseNghbhood->size() << " & " << mesh2->verts[bv2]->baseNghbhood->size() << " ------------\n\n";

#ifdef SAMPLING_V2
				//computeTmpBaseVerts4(sample1, bv1, r1, level); mesh2->computeTmpBaseVerts4(sample2, bv2, r2, level); use the efficient replacements just-below
				computeTmpBaseVerts4Eff(sample1, bv1, r1, level);
				mesh2->computeTmpBaseVerts4Eff(sample2, bv2, r2, level);
#else
				computeTmpBaseVerts2(sample1, bv1, r1, level);
				mesh2->computeTmpBaseVerts2(sample2, bv2, r2, level);
#endif

#ifdef SUBDIV_TO_5_BASES
				if ((int) tmpBaseVerts.size() > 8 && (int) mesh2->tmpBaseVerts.size() > 8)
				{
//cout << (int) tmpBaseVerts.size() << " vs. " << (int) mesh2->tmpBaseVerts.size() << " to blossom matching\n";
					//get the safe correspondences b/w tmpBases of given patches via blossom graph; result written to global tmpSafeCorresp (unused) and more importantly matchIdxs set which'll be reduced to 1-to-1 via make1to1()
					matchTmpBasesByGraph(mesh2, bv1, bv2, false, level);
#ifdef DBG_DETAIL
					fprintf(fPtrExc, "level%d\t\t%d\t%d\t%d\t%d\n", level, bv1, bv2, (int) tmpBaseVerts.size(), (int) mesh2->tmpBaseVerts.size()); //only 6 lines printed when maxLevel=8; biggest is 10 vs. 8, most are 8 vs. 8
					for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
						fprintf(fPtrExc, "%d\n", tmpBaseVerts[i]);
					fprintf(fPtrExc, "\n");
					for (int i = 0; i < (int) mesh2->tmpBaseVerts.size(); i++)
						fprintf(fPtrExc, "%d\n", mesh2->tmpBaseVerts[i]);
					fprintf(fPtrExc, "\n");
#endif
				}
				else
					//get the safe correspondences b/w tmpBases of given patches via combinations/permutations thnx to low patch sizes; result written to global tmpSafeCorresp (unused) and more importantly matchIdxs set which'll be reduced to 1-to-1 via make1to1()
					matchSmallTmpBases(mesh2, false, bv1, bv2, level); //this will be called more frequently for SUBDIV_TO_5_BASES case
#else
				//get the safe correspondences b/w tmpBases of given patches via blossom graph; result written to global tmpSafeCorresp (unused) and more importantly matchIdxs set which'll be reduced to 1-to-1 via make1to1()
				matchTmpBasesByGraph(mesh2, bv1, bv2, false, level);
#endif
			} //end of s

			//interpret setCorrespondenceErrs() and setExtremeCorrespondences() results (better) in terms of current patch radius
			normalizedR1 = sample1 / maxGeoDistG; //div isometryCost (in [0,1]) by this to get it in terms of r of current patches; if r = maxGeoDistG (which can't happen but anyway), normalizedR is 1

			//tmpBases'll be sampled within new patches, not within whole mesh; so, if we consider the whole mesh as 1 patch at the beginning, then for next level we need to reduce the sampling rate r we're dealing with
			r1 = sample1; //since sample1 (= smaller version of r1) used above, that small sample1 becomes the rOfPrevLevel for the upcoming level
			r2 = sample2;
			//radius used at this level, i.e. r1 at level=5 goes to radii[5], recall radii[0] is the radius for level0, i.e. initialization w/ ~5 bases over whole surface
			radii.push_back(r1);
			mesh2->radii.push_back(r2);

			//use matchIdxs[] to create the fresh safeCorresp list which will be traversed in the next level of iteration		
			refillSafeCorrespTmp(mesh2, level, (fileForSymProblem == 1) && (level == maxLevel-1));

			//convert tmpBases to bases; a base in existing baseVerts may have a new match now; so, when i add it to baseVerts, there'll be duplicates; to prevent this, clear the existing baseVerts
interestingMatches.clear();
			baseVerts.clear(); //must clear the previous level's baseVerts to fill it fresh below
			mesh2->baseVerts.clear();
			for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
			{
				int bv1 = safeCorresp[2*ss], bv2 = safeCorresp[2*ss + 1];
				//keep baseVerts up to date 'cos they're used to re-initialize baseNghbhood's (i.e. verts[ baseVerts[bv] ]->baseNghbhood = new .. above) in the upcoming level
				bool exists = false;
				for (int i = 0; i < (int) baseVerts.size(); i++) //actually duplicates in mesh1.baseVerts not hurt at all; so skip here later for speed up (if any); i guess only levelInfoToFile gets effected w/ duplicates but it is handleable
				{
					if (baseVerts[i] == bv1)
					{
						exists = true;
						break;
					}
				}
				if (! exists)
					baseVerts.push_back(bv1); //normalized dSpanning[]'s made ready for bv1/2 while they were tmpBases; so, they still have them; actually, 2+ levels distant bases don't need dSpanning[]'s anymore, hence need to be cleaned for good coding but anyway
				//same stuff for mesh2
				exists = false;
				for (int i = 0; i < (int) mesh2->baseVerts.size(); i++) //actually duplicates in mesh2.baseVerts not hurt at all; so skip here later for speed up (if any); i guess only levelInfoToFile gets effected w/ duplicates but it is handleable
				{
					if (mesh2->baseVerts[i] == bv2)
					{
						exists = true;
						break;
					}
				}
				if (! exists)
					mesh2->baseVerts.push_back(bv2); //took 2 days to see mesh2-> is forgotten in the beginning of this line :(
				//many-to-1's are handled here automatically, i.e. 33-44, 22-44, 10-44 all seen in safeCorresp and therefore 33,22,10 all go to baseVerts and 44 goes to mesh2.baseVerts too (only 1 copy of 44 goes thanks to duplicate check); similarly, 1-to-many also handled: 20-9, 20-12, 20-18

				//baseNghbhood must be ready for the computeTmpBaseVerts() since that function needs a patch info to get restricted to (always subdivide patches centered by bv1 and bv2 regardless of # tmpBases generated; this way i prevent unsubdivided patches eventually being subdivided to ~30 tmpBases)
				if (verts[bv1]->baseNghbhood)
					delete verts[bv1]->baseNghbhood; //delete existing bigger patch, and use a smaller patch below for the upcoming level
				verts[bv1]->baseNghbhood = new vector<int>;
				for (int i = 0; i < (int) verts[bv1]->tmpBaseNghbhood->size(); i++) //verts[bv1]->baseNghbhood = verts[bv1]->tmpBaseNghbhood effect
					verts[bv1]->baseNghbhood->push_back((*verts[bv1]->tmpBaseNghbhood)[i]);

				if (mesh2->verts[bv2]->baseNghbhood)
					delete mesh2->verts[bv2]->baseNghbhood;
				mesh2->verts[bv2]->baseNghbhood = new vector<int>;
				for (int i = 0; i < (int) mesh2->verts[bv2]->tmpBaseNghbhood->size(); i++) //mesh2->verts[bv2]->baseNghbhood = mesh2->verts[bv2]->tmpBaseNghbhood effect
					mesh2->verts[bv2]->baseNghbhood->push_back((*mesh2->verts[bv2]->tmpBaseNghbhood)[i]);
interestingMatches.push_back(bv1);interestingMatches.push_back(bv2);
			} //end of ss
//if ((int) baseVerts.size() != (int) tmpBaseVertsAll.size()) cout << (int) baseVerts.size() << " < " << (int) tmpBaseVertsAll.size() << " [m111111111]\n"; //exit(0); }
//if ((int) mesh2->baseVerts.size() != (int) mesh2->tmpBaseVertsAll.size()) cout << (int) mesh2->baseVerts.size() << " < " << (int) mesh2->tmpBaseVertsAll.size() << " [m22222222]\n"; //exit(0);}
//assert((int) baseVerts.size() == (int) tmpBaseVertsAll.size() && (int) mesh2->baseVerts.size() == (int) mesh2->tmpBaseVertsAll.size()); //be sure that bases over whole mesh mave matches, i.e. no uncovered regions; not assert 'cos i pick tmpBases to be matched in computeTmpBaseVerts4() via BFS wheras basesOverWholeMesh() uses dijkstra; so, rarely, tmpBases stay less than basesOverWholeMesh; but again, very rarely

			//new bases set above may have degenarate baseNghbhood info which fails the upcoming level; why? tmpBases 73-939 was a pair; then 73 becomes a shared tmpBase and paired w/ 946; suppose first 73-939 was better
			//and therefore selected during 1-to-1 conversion in make1to1(); tmpBaseNghbhood for 73 is the most recent one (from 73-946) w/ possibly different adaptive r value, hence a different patch; besides, 939
			//later became candid=false by another tmpBase and hence its tmpBaseNghbhood is cleared; when i want to access 73-939 in the next level, i've an empty 939.patch; so, do this crucial tmpBaseNghbhood update
//			updatePatchesForNextLevel(); mesh2->updatePatchesForNextLevel(); no!!!!!!! i don't have an adaptive r anymore and 'candid=false by another tmpBase and hence its tmpBaseNghbhood is cleared' makes no sense anyway

			//matchIdxs[] and matchIndexes[] are used in refillSafeCorrespTmp() above to decide the best safeCorresp[] for this level; fresh matchIdxs/Indexes[] needed for the next level; dealloc more memo there too
			clearVects2();
			mesh2->clearVects2();

			//fprint currentLevel info and quit so that i can resume from it in the next execution of program (can't do it all in 1 run due to cursed memory problem for which i spent 3 full days, did my best, but couldn't fix :()
			if (! executionTimeOnly) //call just-below unnecessarily increases execution time to be written to paper 'cos it is just to learn the isometric distortion; so, skip that info to get faster times			
			{
				if (fileForSymProblem == 0 || fileForSymProblem == 2) //don't fprint to file in vain for symHeal mode ('cos i'll execute 4 levels in that mode and only interested in the isometryCost of the final level)
					levelInfoToFile(level, mesh2); //skip here for full efficiency, but of course then i won't be able to see the computed results
			}

			arrayInsteadOfHeap = mesh2->arrayInsteadOfHeap = true; //switch to arrays after first level

			cout << /*(int) baseVerts.size() << " vs. " << (int) mesh2->baseVerts.size() << " mapping\t# corresp: " << (int) safeCorresp.size() / 2 << "\n*/"done!\n\n";

			if ((int) tmpBaseVertsAll.size() == (int) verts.size() || (int) mesh2->tmpBaseVertsAll.size() == (int) mesh2->verts.size())
			{
				level++; //make level-1 value correct even if i shortcut from here
				break; //quit levels, as all verts of one side are used as tmpBaseVerts in this level/iter and hence no need to go further
			}
			//if (earlyBreak) { cout << "early breaking\n"; break; }
		} //end of level
#ifdef DBG_DETAIL
		fclose(fPtrExc);
#endif

		//patches, after all levels above, are so small that they couldn't be subdivided into new patches in the last level above; so, match these patches now
		//if (earlyBreak)  matchSmallPatches(mesh2, level); //loop above broken due to early break which says all patches are so small they can't be subdivided further

		//top10 iso and top 10 curvature and mu values pnts decided automatically for a better visual
		if (! executionTimeOnly) //call just-below unnecessarily increases execution time to be written to paper 'cos it is just to learn the isometric distortion; so, skip that info to get faster times			
		{
			/*decideTop10Iso();
			decideTop10Curv();
			decideTop10Mu(true);
			//no visual here, so just print the related idxs to scr	
			cout << "top10 isometry cost:\n";	for (int j = 0; j < (int) verts.size(); j++)	if (verts[j]->top10) cout << j << "\t";
			cout << "\n\ntop10 curvature:\n";	for (int j = 0; j < (int) verts.size(); j++)	if (verts[j]->topCurv10) cout << j << "\t";
			cout << "\n\ntop10 mu:\n";			for (int j = 0; j < (int) verts.size(); j++)	if (verts[j]->topMu10) cout << j << "\t";
			cout << "\n";*/
			if (fileForSymProblem == 0) //don't decide top10s in vain for symHeal mode ('cos i'll execute 4 levels in that mode and only interested in the isometryCost of the final level)
			{
				//make top10 w.r.t. matchCostI and mu ready for drawing purposes
				decideTop10Iso();
				decideTop10Mu(true);

				//boring debugging
				printUnmatched();
				mesh2->printUnmatched();
			}
		}

		cout << "final level = " << level-1 << " w/ " << (int) baseVerts.size() << " vs. " << (int) mesh2->baseVerts.size() << " mapping\t# corresp: " << (int) safeCorresp.size() / 2 << "\n";
		if (fileForSymProblem == 1)
			cout << nthMapping << "th initial mapping led to this level" << level-1 << " mapping\tDone!!!!\n\n";
		else
			cout << "Done!!!!\n\n";

		//fprint geodesic b/w bases to feed them in Zhang's algo in matlab for comparison later
		//geodesicsBetweenBasesToFile(r1, level-1);
		//mesh2->geodesicsBetweenBasesToFile(r2, level-1);

		//boring debugging
		//printUnmatched();
		//mesh2->printUnmatched();
	
		char* str = "", * strPass1 = "";
		if (isometryCost < minIsoCostSoFar)
		{
			minIsoCostSoFar = isometryCost;
			str = "minSoFar";
			if (nthMapping < 6)
				advertisedMapID = nthMapping;
		}
		if (isometryCostPass1 < minIsoCostSoFarPass1)
		{
			minIsoCostSoFarPass1 = isometryCostPass1;
			strPass1 = "minSoFarPass1";
		}
		if (fileForSymProblem == 1)
		{
			if (firstCall)
				fprintf(ofPtr, "mapID\tlevel0 distortion\tlevelX distortion\tlevelX_pass1 distortion\n");
			fprintf(ofPtr, "%d\t%f\t\t%f\t\t%f\t%s\t%s\n", nthMapping++, initialIsoCost, isometryCost, isometryCostPass1, str, strPass1);
			firstCall = false;
			radii.clear(); mesh2->radii.clear(); //for upcoming big while-iteration
			for (int v = 0; v < (int) verts.size(); v++) //most of resets below are probably redundant but no harm to do them; my aim is to reach the defaults in the constructor for the upcoming sequential run
			{
				verts[v]->base = verts[v]->safe = verts[v]->top10 = verts[v]->topCurv10 = verts[v]->topMu10 = false;
				verts[v]->matchIdx = verts[v]->tmpBaseLevel = -1;
				verts[v]->matchCostI = verts[v]->matchCostG = INF;
			}
			for (int v = 0; v < (int) mesh2->verts.size(); v++) //same stuff for mesh2
			{
				mesh2->verts[v]->base = mesh2->verts[v]->safe = mesh2->verts[v]->top10 = mesh2->verts[v]->topCurv10 = mesh2->verts[v]->topMu10 = false;
				mesh2->verts[v]->matchIdx = mesh2->verts[v]->tmpBaseLevel = -1;
				mesh2->verts[v]->matchCostI = mesh2->verts[v]->matchCostG = INF;
			}
		}
	} while (fileForSymProblem == 1); //will be break'ed when initial mappings finish (and -2 returned) in file

	if (fileForSymProblem == 1)
	{
		fprintf(ofPtr, "winner of the race advertised: %d\n", advertisedMapID);
		fprintf(ofPtr, "# of matched pnts in 2 meshes: %d/%d & %d/%d\n", (int) baseVerts.size(), (int) tmpBaseVertsAll.size(), (int) mesh2->baseVerts.size(), (int) mesh2->tmpBaseVertsAll.size());
		fprintf(ofPtr, "notice in the sorted 2nd column the jump after which candidate maps are discarded\n");
		fclose(ofPtr);
	}
}

void Mesh::geodesicsBetweenBasesToFile(float r, int finalLevel)
{
	//for Zhang comparisons, i fprint nxn matrix of geodesics (n=baseVerts.size) b/w bases and feed it to Zhang's algo in matlab

	cout << "unnormalized geodesic b/w bases being fprinted..\n";

	//cout << "min/max geodesic distance around the whole surface: " << minGeoDistG << " / " << maxGeoDistG << endl; //not used 'cos i fprint unnormalized geodesics below
	int n = (int) baseVerts.size();
	for (int i = 0; i < n; i++)
		dijkstraShortestPaths(baseVerts[i]); //if dSpanning[] ready already, it returns immediately
	char fName[250];
	sprintf(fName, "%sA_%d %d level%d.dat", (targetMesh ? "t" : "s"), frameID, n, finalLevel);
	FILE* fPtr = fopen(fName, "w");
	sprintf(fName, "%sbases_%d level%d.dat", (targetMesh ? "t" : "s"), frameID, finalLevel); //also fprint bases 'cos zhang returns 7-3 as a match, which translates to baseVerts[6]-mesh2.baseVerts[2]; so, i need bases in the order they sit in the baseVerts
	FILE* fPtr2 = fopen(fName, "w");
	fprintf(fPtr2, "%f\t%f\t%f\t%d\n", r, minGeoDistG, maxGeoDistG, finalLevel);
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
			fprintf(fPtr, "%f\t", verts[ baseVerts[i] ]->dSpanningUnn[ baseVerts[j] ]);
		fprintf(fPtr, "\n");
		fprintf(fPtr2, "%d\n", baseVerts[i]);
	}
	fclose(fPtr);
	fclose(fPtr2);

	cout << "done!\n";
}

void Mesh::printUnmatched()
{
	//prints degenerate verts that left unmatched even though they exist in tmpBaseVertsAll[] to be matched

	cout << "unmatcheds for " << targetMesh << endl;
	for (int j = 0; j < (int) tmpBaseVertsAll.size(); j++)
	{
		bool matched = false; //matched if exists in baseVerts[]
		for (int i = 0; i < (int) baseVerts.size(); i++)		
			if (tmpBaseVertsAll[j] == baseVerts[i])
			{
				matched = true;
				break;
			}
		if (! matched)
			cout << tmpBaseVertsAll[j] << "\t";
	}
	cout << endl;
}

void Mesh::basesOverWholeMesh(float r, int level)
{
	//spreads bases w/ patches of radii r over whole mesh

	tmpBaseVertsAll.clear(); //aim is to refill this one w/ bases that are at the centers patches of radii r

	//keep the search within the whole mesh
	for (int v = 0; v < (int) verts.size(); v++)
		verts[v]->tmpBaseCandidate = true;

	//first take the existing ones (they have a patch w/ radius >r; below, they'll have a patch w/ radius =r)
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int bv = baseVerts[i];
		if (verts[bv]->tmpBaseNghbhood)
			delete verts[bv]->tmpBaseNghbhood;
		verts[bv]->tmpBaseNghbhood = new vector<int>; //clear effect in memory efficient way

		//dSpanning[] may be undefined so fill it from scratch (if already filled, then it returns immediately anyway)
		//dijkstraShortestPaths(bv); redundant 'cos bv is the generator base, which assures that it has been a tmpBase in the prev level and therefore its dSpanning already computed
		//dijkstraShortestPaths(bv, farThrshld * rOfPrevLevel); can't use this w/ dSpanning[] 'cos in that tactic, once bv.dSpanning[] is computed it is never refilled again; so, if i compute here w/ a shortcut now, then i can't reach far-away verts and when i need to reach them later i've an undefined value

		tmpBaseVertsAll.push_back(bv); //baseVerts are duplicate-free although it doesn't hurt to have duplicates here
		tmpBaseCandidateFalse(bv, r); //tmpBaseCandidate & tmpBaseNghbhood updated
		verts[bv]->tmpBaseLevel = level; //so that BFS in computeTmpBaseVerts4() takes this as a related vert
	}
#ifdef DBG_DETAIL
	cout << "# tmp bases from prev level: " << tmpBaseVertsAll.size() << endl;
#endif

	//second, create more bases w/ COES that falls into centers of patches of radii r
	int baseVertIdx = -1;
	for (int v = 0; v < (int) verts.size(); v++) //more accurate mechanism after shared tmpBases above played w/ tmpBaseCandidate values
		if (verts[ gcOrder[v] ]->tmpBaseCandidate && (int) verts[ gcOrder[v] ]->vertNeighborsSet.size() > 2) //test the next which comes from gcOrder; 2nd condition to ignore isolated/disconnected verts
		{
			baseVertIdx = gcOrder[v];
			break;
		}
	vector< int >* vlist = new vector<int>; //first a baseVert enters this list, and then verts in the area/patch of that baseVert
	while (baseVertIdx != -1) //there is still a baseVert whose area to be expanded below
	{
		/////// shortcut Dijkstra w/ slow arrays to create 1 tmpBaseVert ///////
		//step 1: initialization
		for (int v = 0; v < (int) verts.size(); v++)
			verts[v]->d = INF;

		//step 2: select a tmp base vert
		verts[baseVertIdx]->d = 0.0f; //so that this baseVert is initially selected from vlist in the loop below
		vlist->push_back(baseVertIdx);			//used below
		tmpBaseVertsAll.push_back(baseVertIdx);	//used later; whole aim is to fill this vector indeed
		verts[baseVertIdx]->tmpBaseCandidate = false;
		verts[baseVertIdx]->tmpBaseLevel = level; //so that BFS in computeTmpBaseVerts4() takes this as a related vert
		if (verts[baseVertIdx]->tmpBaseNghbhood)
			delete verts[baseVertIdx]->tmpBaseNghbhood;
		verts[baseVertIdx]->tmpBaseNghbhood = new vector<int>;
		while (! vlist->empty())
		{
			//step 3: extract vert w/ min d value
			float minD = INF;
			int minDidx, v;
			for (int i = 0; i < (int) vlist->size(); i++)
				if (verts[ (*vlist)[i] ]->d < minD)
				{
					v = (*vlist)[i];
					minD = verts[v]->d;
					minDidx = i;
				}
			//erase the element w/ min d value
			vlist->erase(vlist->begin() + minDidx); //ve.erase(begin + 4) erases ve[4]

			//step 4: relaxation loop that updates d values and expands baseVert.area as long as d is small enough
			for (unsigned int ve = 0; ve < verts[v]->edgeList.size(); ve++)
			{
				int e = verts[v]->edgeList[ve];
				int va = (edges[e]->v1i == v ? edges[e]->v2i : edges[e]->v1i);
				if (verts[v]->d + edges[e]->length < verts[va]->d)
				{
					verts[va]->d = verts[v]->d + edges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->tmpBaseCandidate = false;
						//duplicates may arise in baseVertIdx.tmpBaseNghbhood w/o check below [or do later while counting # verts in this tmpBaseNghbhood; duplicates have no harm, i guess, other than giving big cardinality, which i may use to decided optimal r value; also, not taking serious time anyway]
						bool exists = false;
						for (int tv = 0; tv < (int) verts[baseVertIdx]->tmpBaseNghbhood->size(); tv++)
							if ((*verts[baseVertIdx]->tmpBaseNghbhood)[tv] == va)
							{
								exists = true;
								break;
							}
						if (! exists)//*/
							verts[baseVertIdx]->tmpBaseNghbhood->push_back(va);
					}
				}
			}
			for (unsigned int ve = 0; ve < verts[v]->sedgeList.size(); ve++)
			{
				int e = verts[v]->sedgeList[ve];
				int va = (sedges[e]->v1i == v ? sedges[e]->v2i : sedges[e]->v1i);
				if (verts[v]->d + sedges[e]->length < verts[va]->d)
				{
					verts[va]->d = verts[v]->d + sedges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->tmpBaseCandidate = false;
						//duplicates may arise in baseVertIdx.tmpBaseNghbhood w/o check below
						bool exists = false;
						for (int tv = 0; tv < (int) verts[baseVertIdx]->tmpBaseNghbhood->size(); tv++)
							if ((*verts[baseVertIdx]->tmpBaseNghbhood)[tv] == va)
							{
								exists = true;
								break;
							}
						if (! exists)//*/
							verts[baseVertIdx]->tmpBaseNghbhood->push_back(va);
					}
				}
			}
		} //end of vlist.empty()
		/////// shortcut Dijkstra w/ slow arrays to create 1 tmpBaseVert ends ///////
//cout << "tmp base" << tmpBaseVertsAll.size()-1 << ": " << baseVertIdx << endl;

		//select the next tmpBase in coes manner
		baseVertIdx = -1;
		for (int v = 0; v < (int) verts.size(); v++)
			if (verts[ gcOrder[v] ]->tmpBaseCandidate && (int) verts[ gcOrder[v] ]->vertNeighborsSet.size() > 2) //test the next which comes from gcOrder; 2nd condition to ignore isolated/disconnected verts
			{
				baseVertIdx = gcOrder[v];
				break;
			}
	} //end of baseVertIdx
	delete vlist;

//for (int i =0;i<tmpBaseVertsAll.size();i++)cout<<tmpBaseVertsAll[i]<<"\t";
	cout << "# of all tmp bases to be spread over the whole surface: " << (int) tmpBaseVertsAll.size() << "\n\n";
}

void Mesh::matchSmallPatches(Mesh* mesh2, int level)
{
	//try all match permutations and pick the smallest isometryCost valued one to match each patch, each of which are of size <9 for sure

	cout << "post-processing for coarse-to-fine in action to dense-match small patches....\n";

	int scSize = (int) safeCorresp.size() / 2;
	//base bv1 (bv2) won't be in tmpBaseVerts (mesh2.tmpBaseVerts) 'cos i don't want to change their existing cool matches coming from prev levels; so, load that matches into matchIdxs so that make1to1() converts them to matchIdx values later
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++) //for each corresponding patch, match the tmpBases COES'ly sampled over them
	{
		int bv1 = safeCorresp[2*s], bv2 = safeCorresp[2*s + 1];
assert(verts[bv1]->matchIdxs.empty() && mesh2->verts[bv2]->matchIdxs.empty());
		verts[bv1]->matchIdxs.push_back((float) bv2);
		verts[bv1]->matchIdxs.push_back(0.0f); //make it selected during 1-to-1 conversion (actually there won't be any candidates but anyway)
		mesh2->verts[bv2]->matchIdxs.push_back((float) bv1); //duality
		mesh2->verts[bv2]->matchIdxs.push_back(0.0f);
	} //end of s
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++) //for each corresponding patch, match the tmpBases COES'ly sampled over them
	{
		int bv1 = safeCorresp[2*s], bv2 = safeCorresp[2*s + 1];
		cout << "\n------------ [" << s+1 << "/" << scSize << "] patches being handled: " << bv1 << " & " << bv2 << "\tsizes: " << verts[bv1]->baseNghbhood->size() << " & " << mesh2->verts[bv2]->baseNghbhood->size() << " ------------\n\n";

		computeTmpBaseVerts3(bv1);
		mesh2->computeTmpBaseVerts3(bv2);

		//match these small amount of tmpBases
		matchSmallTmpBases(mesh2);
		/////matchSmallTmpBases(mesh2, true, bv1, bv2); non -1 bv1/2 to be able to use parentPatch corresp as traversalList; not so important
	} //end of s

	//matchTmpBasesByGraph() above not set matchIdx values; it rather prepares matchIdxs logically and i now convert them to matchIdx values (1-to-1 or 1-to-many, according to force1to1)
	nCorresps = mesh2->nCorresps = 0;
//no #ifdef USE_BEST_MATCH_AMONGST_TOUCHING_PATCHES here 'cos must call make1to1() at all times since matchTmpBasesByGraph*() acted as USE_BEST_MATCH_AMONGST_TOUCHING_PATCHES defined
	make1to1(mesh2, NULL, 2, true, false); //0: no force1to1 at all (do nothing, stay 1-to-many), 1 or 2 (better) forces 1-to-1; 4th param: true sets matchCostI which is needed by adaptiveListForIsometryCost()

	//use matchIdx values to create the baseVerts (e.g. fpr fprint in return) as well as the safeCorresp (e.g. for setExtremeCorrespondences())
	safeCorresp.clear();
	baseVerts.clear();
	mesh2->baseVerts.clear();
	for (int v = 0; v < (int) verts.size(); v++)
		verts[v]->safe = verts[v]->base = false;
	for (int v = 0; v < (int) mesh2->verts.size(); v++)
		mesh2->verts[v]->safe = mesh2->verts[v]->base = false;
	for (int v = 0; v < (int) verts.size(); v++)
		if (verts[v]->matchIdx != -1)
		{
			safeCorresp.push_back(v);
			safeCorresp.push_back(verts[v]->matchIdx);
			verts[v]->safe = mesh2->verts[ verts[v]->matchIdx ]->safe = true;
		}
	for (int ss = 0; 2*ss+1 < (int) safeCorresp.size(); ss++)
	{
		int bv1 = safeCorresp[2*ss], bv2 = safeCorresp[2*ss + 1];
		baseVerts.push_back(bv1);
		mesh2->baseVerts.push_back(bv2);
		verts[bv1]->base = mesh2->verts[bv2]->base = true;
	} //end of ss

	//set the best and worst vertex-based correspondence for drawing purposes
	setExtremeCorrespondences(r1, false);

	cout << "Done!!!!\n\n";
}

inline float Mesh::getErrOfCorrespList(Mesh* mesh2, vector< int > tmpSafeCorrespTry, bool saveIsoCosts, bool smoothnessTest)
{
	//returns the isometryCost of the correspondence lying in the tmpSafeCorrespTry list using tmpSafeCorrespTry itself as the traversalList; so, symmetry problems like
	//a		c'	cannot be healed here; if i use parent patches corresps as the traversalList then this can be fixed (getErrOfCorrespListCP())
	//b		b'
	//c		a'

	float isometryCostTry = 0.0f, worstCost = -INF;
	int n = (int) tmpSafeCorrespTry.size() / 2, worstCostIdx = -1; //idx in tmpSafeCorrespTry[] to see the worstCost later
	for (int s = 0; s < n; s++)
	{
		int tbv1 = tmpSafeCorrespTry[2*s], tbv2 = tmpSafeCorrespTry[2*s + 1];
		float vertexBasedIso = 0.0f;
		if (saveIsoCosts) //to assign these saved cost in the end in case this tmpSafeCorrespTry selected as bestCorresp in matchSmallTmpBases()
			verts[tbv1]->isometryCosts.clear();
		for (int ss = 0; ss < n; ss++)
		{
			int s1 = tmpSafeCorrespTry[2*ss], s2 = tmpSafeCorrespTry[2*ss + 1];
			if (s1 == tbv1)
				continue;

			//if tbv1 vs. tbv2 is a good correspondence, then geoDist(tbv1, s1) = geoDist(tbv2, s2) for all s1 & s2 in tmpSafeCorrespTry
			float t1s1 = verts[tbv1]->dSpanning[s1], t2s2 = mesh2->verts[tbv2]->dSpanning[s2];
			vertexBasedIso += fabs(t1s1 - t2s2); //normalized difference here is small if tbv1 vs. tbv2 is a good correspondence
assert(t1s1 >= 0 && t1s1 <= 1 && t2s2 >= 0 && t2s2 <= 1);
		}
		if (n != 1) //no div by zero
			vertexBasedIso /= (n-1);
		else
			vertexBasedIso = 0.0f; //due to symmetry problem, all costs will be the same and hence best selected arbitrarily
		isometryCostTry += vertexBasedIso;
		if (saveIsoCosts)
		{
			verts[tbv1]->isometryCosts.push_back((float) tbv2);
			verts[tbv1]->isometryCosts.push_back(vertexBasedIso);
		}
#ifdef ENFORCE_SMOOTH_MAPPING
		if (vertexBasedIso > worstCost)
		{
			worstCost = vertexBasedIso; //remember the worst match so that i discourage this mapping (tmpSafeCorrespTry) to be selected in case worst is very bad, i.e. > 2isometryCostTry
			worstCostIdx = 2*s;
		}
#endif
	} //end of s
	//overall isometry cost of this tmpSafeCorrespTry combination [all combinations coming here are of same size (n) and therefore below skipped]
	isometryCostTry /= n; //[save time by skipping division since i only use return value for comparison; could've been done above too but anyway]

#ifdef ENFORCE_SMOOTH_MAPPING
	if (smoothnessTest && worstCost > 2*isometryCostTry)
	{		
#ifdef DBG_DETAIL
		cout << worstCost << " > " << 2*isometryCostTry << " and therefore following discouraged:\n";
		for (int s = 0; s < n; s++)
			cout << tmpSafeCorrespTry[2*s] << " rrrrrrrrr " << tmpSafeCorrespTry[2*s + 1] << (2*s == worstCostIdx ? "\t(worst)" : "") << (s == n-1 ? "\n\n" : "\n");
#endif
		//discouragement effect
		isometryCostTry = INF; //prevent tmpSafeCorrespTry being selected as best in return even if original isometryCostTry is the min (preventing 'cos worst match is very bad, i.e. no smooth mapping); new unoriginal isometryCostTry is INF for discouragement
	}
#endif

	return isometryCostTry;
}

inline float Mesh::getErrOfCorrespListCP(Mesh* mesh2, vector< int > tmpSafeCorrespTry, vector< int > cpCorresp, bool saveIsoCosts)
{
	//returns the isometryCost of the correspondence lying in the tmpSafeCorrespTry list using itself (current) and cpCorresp (parent) as the traversalList, hence the name CP

	float isometryCostTry = 0.0f;
	int n = (int) tmpSafeCorrespTry.size() / 2, pcSize = (int) cpCorresp.size() / 2;
	for (int s = 0; s < n; s++)
	{
		int tbv1 = tmpSafeCorrespTry[2*s], tbv2 = tmpSafeCorrespTry[2*s + 1];
		float vertexBasedIso = 0.0f;
		if (saveIsoCosts) //to assign these saved cost in the end in case this tmpSafeCorrespTry selected as bestCorresp in matchSmallTmpBases()
			verts[tbv1]->isometryCosts.clear();
		for (int ss = 0; ss < pcSize; ss++) //traversalList
		{
			int s1 = cpCorresp[2*ss], s2 = cpCorresp[2*ss + 1];
			if (s1 == tbv1) //assume s1-s2 is a good match being tested and s1(=tbv1)-tbv2 is a bad match in safeList; if i canceled continue just-below, then good s1-s2 would get a higher cost wrongly
				continue;

			float s1t1 = verts[s1]->dSpanning[tbv1], s2t2 = mesh2->verts[s2]->dSpanning[tbv2]; //dijkstraShortestPaths() already called for cpCorresp elements just-before calling here; so, we're good
			vertexBasedIso += fabs(s1t1 - s2t2); //difference (diffForNormalized) is small if tbv1 vs. tbv2 is a good correspondence
assert(s1t1 >= 0 && s1t1 <= 1 && s2t2 >= 0 && s2t2 <= 1);
		} //end of ss
		vertexBasedIso /= (pcSize-1); //vertexBasedIso in [0,1]; pcSize can't be 1 'cos traversalList here is good, e.g. involves parents; so, since pcSize-1 amount of += [0,1] made above, this division makes vertexBasedIso in [0,1]
		isometryCostTry += vertexBasedIso;
//#ifdef ENFORCE_SMOOTH_MAPPING if (vertexBasedIso > worstCost) worstCost = vertexBasedIso; #endif not here 'cos parent corresps used here in the computation of vertexBasedIso; for smoothness check of tmpSafeCorrespTry, i need to stay within tmpSafeCorrespTry only; so, do it below
		if (saveIsoCosts)
		{
			verts[tbv1]->isometryCosts.push_back((float) tbv2);
			verts[tbv1]->isometryCosts.push_back(vertexBasedIso);
		}
	} //end of s
	//overall isometry cost of this tmpSafeCorrespTry combination [all combinations coming here are of same size (n) and therefore below skipped]
	isometryCostTry /= n; //[save time by skipping division since i only use return value for comparison; could've been done above too but anyway] [if ENFORCE_SMOOTH_MAPPING defined, then /=n is mandatory]

#ifdef ENFORCE_SMOOTH_MAPPING
	if (getErrOfCorrespList(mesh2, tmpSafeCorrespTry, false, true) == INF) //isometryCostWithoutParent=-1 is a special flag that tells tmpSafeCorrespTry has a worst match that is too bad, and hence this tmpSafeCorrespTry is discouraged
		return INF;
#endif

	return isometryCostTry;
}

void Mesh::matchSmallTmpBases(Mesh* mesh2, bool forPostprocessing, int bv1, int bv2, int level) //last 4 params not used during post-processing
{
	//matches small amount of tmpBaseVerts during normal levels or post-processing using combinatorics

	//if (! forPostprocessing) //tmpBaseVerts involves baseVert (bv1/2) who generated these tmpBases; set rhs of 'tmpBaseVerts.size() ==' accordingly, i.e. size > 0 for sure
	if (forPostprocessing) //tmpBaseVerts not involve baseVert during post-processing who generated these tmpBases; set rhs of == accordingly
	{
		if ((int) tmpBaseVerts.size() == 0 || (int) mesh2->tmpBaseVerts.size() == 0 || //at least 1 side has nothing to match; so, no need to go further
			(int) tmpBaseVerts.size() == 1 || (int) mesh2->tmpBaseVerts.size() == 1) //at least 1 side has only 1 tmpBase (say v7) and getErrOfCorrespList() always return 0 for 1-size corresp list; so, i can't find a match for v7 and hence no need to go further
			return;
	}

//	cout << "matching small amount of tmpBases b/w active patches..\n";

	for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
	{
		verts[ tmpBaseVerts[i] ]->tmpBaseLevel = level; //means tmpBaseVerts[i] became a tmpBase at this level
		verts[ tmpBaseVerts[i] ]->residue = true; //e.g. for the 3 vs. 2 patch case, 2 vs. 2 match is set below via permutations, and 1 residue in patch1 gets its match from the bottom w/o permutations
	}
	//same stuff for mesh2
	for (int i = 0; i < (int) mesh2->tmpBaseVerts.size(); i++)
	{
		mesh2->verts[ mesh2->tmpBaseVerts[i] ]->tmpBaseLevel = level;
		mesh2->verts[ mesh2->tmpBaseVerts[i] ]->residue = true;
	}

#ifdef DBG_DETAIL
	cout << "normalized geodesics b/w tmpBases and other tmpBases in the same patch being computed..\n";
#endif
	for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
		//dijkstraShortestPaths(tmpBaseVerts[i], farThrshld * r1); //r1 = rOfPrevLevel for sure (not so safe 'cos .. [see farThrshld declaration comment])
		dijkstraShortestPaths(tmpBaseVerts[i]); //be safe/accurate but not efficient (will be called if required so we're still safe)
	for (int j = 0; j < (int) mesh2->tmpBaseVerts.size(); j++)
		//mesh2->dijkstraShortestPaths(mesh2->tmpBaseVerts[j], farThrshld * r2);
		mesh2->dijkstraShortestPaths(mesh2->tmpBaseVerts[j]);
#ifdef DBG_DETAIL
	cout << "done!\n";
#endif


//if (level==10)cout<<(int) tmpBaseVerts.size()<<"\t\t"<<(int) mesh2->tmpBaseVerts.size()<<endl;

	//traversalList to decide isometryCost of current tmpSafeCorrespTry will be either current tmpSafeCorrespTry itself (useCurrentAndParentForTraversalList=false), or corresps in the current tmpSafeCorrespTry plus corresps in the parentPatch of bv1 (true); latter better due to symmetry errs in former as explained by bPrime stuff above
	bool useCurrentAndParentForTraversalList = true;
	vector< int >* parentList = NULL;
	if (forPostprocessing)
		useCurrentAndParentForTraversalList = false; //can't do this 'cos bv1/2 = -1 for forPostprocessing=true cases; you can make bv1/2 non -1 from the caller but anyway
	if (useCurrentAndParentForTraversalList) //initialEuclideanMatching() not enter here() (after forPostprocessing=true of a very bad naming)
	{
		//below heavily-adapted from computeTmpBaseVerts2()
		parentList = new vector<int>; //clear to a fresh start of size 0 here
		int minParentListSizeAllowed = 6; //at least 3 matches (hence 3*2=6) is necessary to prevent symetry errors; note that, these 3 will be populated more w/ the current correp in permutation test, i.e. my current+parent tactic
		if (level > 1) //parentPatch is n/a for level1 bases; i use initialCorresp for them by leaving parentList empty here
		{
			//copied from computeTmpBaseVerts2(); going to as long as twoLevelBack = 2 x rOfPrevLevel since the parent is 2 level back, not one level
			for (int v1 = 0; v1 < (int) verts.size(); v1++)
				verts[v1]->d = INF; //prepare for BFS below
			float twoLevelBack = //2 * r1; where r1 = rOfPrevLevel for sure; (for SUBDIVIDE_AT_ALL_TIMES w/ rDenom, this 2 is always correct 'cos parent is always coming from two level back; for alternating that may not subdiv at all times, i may need >2 number here; but not so important anyway)
								 radii[level-2]; //[level] is the current radius (= sample1), [level-1] is the rofPrevLevel (= r1), [level-2] is 2*rOfPrevLevel if i had divided r /= 2 in the end of all iters (i'm setting r w.r.t. biggest-patch which roughly has r /= 2 effect)
			int source = bv1, v;
			queue< int >* q = new queue<int>;
			q->push(source);
			verts[source]->d = 0; //length of unweighted shortest path from source to source is 0
			parentList->push_back(bv1);
			parentList->push_back(bv2); //bv1 is in parentPatch of bv1 itself; so, add that trivial match
			while (! q->empty()) //modified BFS
			{
				v = q->front();
				q->pop();

				//look at each adjacent vertices of v
				set< int, std::less< int > >::const_iterator neighbVertIdx;
				for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++)
				{
					if (verts[(*neighbVertIdx)]->d != INF)
						continue;
					verts[(*neighbVertIdx)]->d = verts[v]->d + 1; //update d of this adjacent
					if (verts[source]->dSpanningUnn[(*neighbVertIdx)] >= twoLevelBack)
						continue; //continue'll prevent q.push_back below and eventually q'll be empty for sure
					if (verts[(*neighbVertIdx)]->matchIdx != -1) //an existing match which has made 2 levels before, a parent match as we can call it
					{
						parentList->push_back((*neighbVertIdx));
						parentList->push_back(verts[(*neighbVertIdx)]->matchIdx);
					}
					q->push((*neighbVertIdx));
				} //end of neighbVertIdx
			}
			delete q;
		} //end of level > 1
		if ((int) parentList->size() < minParentListSizeAllowed) //i want at least 3 matches, hence size 6, to make traversalList better; so, if size not big, try 1 of 2 options
		{
			delete parentList;
			parentList = new vector<int>; //clear to a fresh start of size 0 here

			if (level == 1) //cancel if condition here and all the else-block to use option # 1 at all times, which is simple but not that accurate especially for advancing levels where initialCorresp remains too far, i.e. not that local
				//option # 1: use initialCorresp which is guaranteed to be big enough; note that, for level=1, i enter here for sure (level=0 is the initial matching which not enters here at all)
				for (int i = 0; i < (int) initialCorresp->size(); i++) //parentList = initialCorresp effect by explicit copying (do this o/w when i delete safeCorrespsAdaptive, i'd also delete initialCorresp which may be used later)
					parentList->push_back((*initialCorresp)[i]); //initialCorresp name sucks for SUBDIV_TO_5_BASES run as it holds 5-5 corresps due to initialEuclideanMatching() not initialSpectralMatching() but anyway; no problem
			else
			{
				//option # 2: use more than parent, i.e. grand-grand-.. parent, until 3 matches found by heavily-adapting BFS above
				for (int v1 = 0; v1 < (int) verts.size(); v1++)
					verts[v1]->d = INF; //prepare for BFS below
				int source = bv1, v;
				queue< int >* q = new queue<int>;
				q->push(source);
				verts[source]->d = 0; //length of unweighted shortest path from source to source is 0
				parentList->push_back(bv1);
				parentList->push_back(bv2); //bv1 is in parentPatch of bv1 itself; so, add that trivial match
				while (true) //modified BFS
				{
					v = q->front();
					q->pop();
					//look at each adjacent vertices of v
					set< int, std::less< int > >::const_iterator neighbVertIdx;
					for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++)
					{
						if (verts[(*neighbVertIdx)]->d != INF)
							continue;
						verts[(*neighbVertIdx)]->d = verts[v]->d + 1; //update d of this adjacent
						if (verts[(*neighbVertIdx)]->matchIdx != -1) //an existing match which has made 2 levels before, a parent match as we can call it
						{
							parentList->push_back((*neighbVertIdx));
							parentList->push_back(verts[(*neighbVertIdx)]->matchIdx);
						}
						q->push((*neighbVertIdx));
					} //end of neighbVertIdx
					//stop when minParentListSizeAllowed is reached or exceeded
					if ((int) parentList->size() >= minParentListSizeAllowed)
						break; //must come here eventually; o/w there'll be nothing to get from q->front() and get a null ptr exception
				}
				delete q;
			}//*/
		}

		//prepare geodesics/dSpanning for parentList which'll be used as the traversalList below
		int m = (int) parentList->size() / 2;
		for (int i = 0; i < m; i++)
			//dijkstraShortestPaths((*parentList)[2*i], farThrshld * r1); //r1 = rOfPrevLevel for sure (not so safe 'cos .. [see farThrshld declaration comment])
			dijkstraShortestPaths((*parentList)[2*i]); //be safe/accurate but not that efficient (will be called if required so we're still safe)
		for (int j = 0; j < m; j++)
			//mesh2->dijkstraShortestPaths((*parentList)[2*j + 1], farThrshld * r2);
			mesh2->dijkstraShortestPaths((*parentList)[2*j + 1]);
	}

#ifdef DBG_DETAIL	
	if (useCurrentAndParentForTraversalList)
	{
		cout << "traversalList (due to parent) to be traversed by small amount of tmpBases [current list being refined'll be added to this traversalList as well]:\n"; 
		for (int i = 0; i < (int) parentList->size() / 2; i++)
			cout << (*parentList)[2*i] << "->" << (*parentList)[2*i + 1] << endl;
	}
#endif

	/////////////////// best matching via combination & permutation ///////////////
	Mesh* fromMesh, * toMesh;
	if ((int) tmpBaseVerts.size() < (int) mesh2->tmpBaseVerts.size()) //patch1 is smaller; so, mapping from patch1 to patch2
	{
		fromMesh = this;
		toMesh = mesh2;
	}
	else	//patch2 is smaller; so, mapping from patch2 to patch1
	{
		fromMesh = mesh2;
		toMesh = this;

		if (useCurrentAndParentForTraversalList)
		{
			//parentList[2i] is from mesh1, [2i+1] from mesh2; below, i'll call fromMesh->getErrOfCorrespListCP(), where fromMesh is mesh2 from now on; parentList in that function (w/ parentCorresp name)
			//wants to access [2i] as mesh2 vertex (since getErrOfCorrespListCP() called over fromMesh=mesh2) and [2i+1] as mesh1 vertex, which is currently not the case; so, rearrange parentList by swaps
			for (int i = 0; i < (int) parentList->size() / 2; i++) //swap [2i] w/ [2i+1]
			{
				int tmp = (*parentList)[2*i];
				(*parentList)[2*i] = (*parentList)[2*i + 1];
				(*parentList)[2*i + 1] = tmp;
			}
		}
	}

	////////////////////// symmetry healing ///////////////////
	FILE* fsPtr;
	bool symHealFprint = (fileForSymProblem == -1 && forPostprocessing); //fprint initial (level 0) matchings w/ very low isometryCostTry values (to be used later for symmetry healing)
	int fromSize = (int) fromMesh->tmpBaseVerts.size();
	char fsName[300]; sprintf(fsName, "%s\\symHeal%d-%d_%dx%d.dat", outFolder, frameID, mesh2->frameID, fromSize, fromSize);
	if (symHealFprint)
	{		
		fsPtr = fopen(fsName, "w");
		fprintf(fsPtr, "%d\t\t%f\t%f\t\t%f\t%f\n\n", fromSize, maxGeoDistG, mesh2->maxGeoDistG, r1, r2); //1st var: fromSize, minGeoDistG's are 0 anyway
	}
	float totalIsoTry = 0.0f, nTry = 0.0f;
	vector< float > isoVals;
	vector< int > isoIdxs; //to transfer topN mappings of all mappings in fsPtr file to another (tmpPtr) file
	////////////////////// symmetry healing ///////////////////

	//toMesh is the bigger one (if sizes different); so, some toMesh.tmpBases'll remain unmatched; to decide that use combinations;
	//say toMesh.size = m = 5, fromMesh.size = 3; then, try C(5,3) toMesh combinations and for each combination try 3! permutations to match up w/ 3 fromMesh.tmpBases
	int m = (int) toMesh->tmpBaseVerts.size();//, fromSize = (int) fromMesh->tmpBaseVerts.size(); prepared above anyway
	float minIsometryCost = INF;
	vector< int > bestCorresp;
	if (fromSize == 2) //use C(m,2) combinations to decide the 2 unmatched verts in to-side as good as possible
	{
		int twoR = 2*fromSize, factR = factorial(fromSize), nCombs = 0, //store useful results for not to recompute them during iters below
			tooMany = (factR > 0 ? factR * 1000 : 1000); //1000 combinations allowed each to be tested w/ factR = 2! permutations, hence factR*1000
		for (int a = 0; a < m; a++)
			for (int b = a+1; b < m; b++)		
			{
				//current 2-combination is copied from appropriate slots of toMesh.tmpBaseVerts
				vector< int > tmp; //holds the current triple of all C(m,3) triples
				tmp.push_back(toMesh->tmpBaseVerts[a]);
				tmp.push_back(toMesh->tmpBaseVerts[b]);

				//try 2! permutations of tmp w/ stable fromMesh.tmpBaseVerts of size 2 (for the easy-to-understand version that ignore permutations)
				for (int r = 0; r < factR; r++) //k! reordering [cancel this line only to save time by using the 1st permutation only instead of all k! permutations]
				{
					//refill tmpSafeCorrespTry according to the current reordering (one of k! permutations)

					//get the overall isometryCost of this current 2-tuple combination matched w/ stable 2-tuple of fromMesh
					vector< int > tmpSafeCorrespTry;
					int idx1 = 0, idx2 = 0;
					//stable 2-tuple of fromMesh goes to even indices: 0 & 2, and permuted toMesh tmpBases goes to odd slots: 1 & 3
					for (int y = 0; y < twoR; y++)
						if (y % 2 ==0)
							tmpSafeCorrespTry.push_back(fromMesh->tmpBaseVerts[idx1++]); //stable from fromMesh side
						else
							tmpSafeCorrespTry.push_back(tmp[ perms2[r][idx2++] ]);	//perms2[0] = {0,1}, perms2[1] = {1,0}

					//compute isometry cost of this 2-2 mapping and ..
					float isometryCostTry;
					vector< int > tmpSafeCorrespTryAndParent;
					if (useCurrentAndParentForTraversalList) //better be true for fromSize = 2 case
					{
						//tmpSafeCorrespTryAndParent = current tmpSafeCorrespTry + fixed parentList
						tmpSafeCorrespTryAndParent = tmpSafeCorrespTry;
						for (int p = 0; p < (int) parentList->size(); p++)
							tmpSafeCorrespTryAndParent.push_back((*parentList)[p]);
//simply use *parentList instead of tmpSafeCorrespTryAndParent just-below to remove tmpSafeCorrespTry from the traversalList, i.e. use only the parent patch, not current + parent
						isometryCostTry = fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent);
					}
					else //should not execute here due to symmetry problem explained below
						isometryCostTry = fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, false);
					//..save it if it's the best so far
					if (isometryCostTry < minIsometryCost)
					{
						minIsometryCost = isometryCostTry;
						bestCorresp = tmpSafeCorrespTry;

						//save costs that lead to this so-far-best isometryCostTry, to assign them in the end in case this tmpSafeCorrespTry selected as bestCorresp below
						if (useCurrentAndParentForTraversalList) //better be true for fromSize = 2 case
							fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent, true);
						else
							fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, true);
					}
					//2 vs. 2+ will yield a 2-by-2 match whose quality can't be computed by getErrOfCorrespList() above, i.e. w/o parentList, due to symmetry:
					//a---------a'		or		a_		_b'
					//b---------b'				b _\___/_a'		traverse first match (a-a') w/ b-b'; both left and right cases of or yield the same isometryCostTry due to symmetry

#ifndef SUBDIV_TO_5_BASES //when SUBDIV_TO_5_BASES defined, i deal w/ small m & fromSize since i pick radius accordingly (w.r.t. biggest-area patch); so, don't bother w/ this efficiency shortcut
					if (nCombs++ > tooMany)
						a = b = m + 1; //breaks this (b-loop, not this r-loop) and all outer loops
#endif
				} //end of r
			}
	}
	else if (fromSize == 3) //use C(m,3) combinations to decide the 3 unmatched verts in to-side as good as possible
	{
		int twoR = 2*fromSize, factR = factorial(fromSize), nCombs = 0, //store useful results for not to recompute them during iters below
			tooMany = (factR > 0 ? factR * 750 : 750); //750 combinations allowed each to be tested w/ factR permutations
		for (int a = 0; a < m; a++)
			for (int b = a+1; b < m; b++)
				for (int c = b+1; c < m; c++)
				{
					//current 3-combination is copied from appropriate slots of toMesh.tmpBaseVerts
					vector< int > tmp; //holds the current triple of all C(m,3) triples
					tmp.push_back(toMesh->tmpBaseVerts[a]);
					tmp.push_back(toMesh->tmpBaseVerts[b]);
					tmp.push_back(toMesh->tmpBaseVerts[c]);

					//try 3! permutations of tmp w/ stable fromMesh.tmpBaseVerts of size 3 (for the easy-to-understand version that ignore permutations)
					for (int r = 0; r < factR; r++) //k! reordering [cancel this line only to save time by using the 1st permutation only instead of all k! permutations]
					{		
						//refill tmpSafeCorrespTry according to the current reordering (one of k! permutations)
						//i'th permutation of 3 elements (0..2) is 1 row, namely perms3[i][], of perms3[][]

						//get the overall isometryCost of this current 2-tuple combination matched w/ stable 2-tuple of fromMesh
						vector< int > tmpSafeCorrespTry;
						int idx1 = 0, idx2 = 0;
						//stable 2-tuple of fromMesh goes to even indices: 0 & 2, and permuted toMesh tmpBases goes to odd slots: 1 & 3
						for (int y = 0; y < twoR; y++)
							if (y % 2 ==0)
								tmpSafeCorrespTry.push_back(fromMesh->tmpBaseVerts[idx1++]); //stable from fromMesh side
							else
								tmpSafeCorrespTry.push_back(tmp[ perms3[r][idx2++] ]);	//permutations from toMesh side

						//compute isometry cost of this 3-3 mapping and ..
						float isometryCostTry;
						vector< int > tmpSafeCorrespTryAndParent;
						if (useCurrentAndParentForTraversalList)
						{
							//tmpSafeCorrespTryAndParent = current tmpSafeCorrespTry + fixed parentList
							tmpSafeCorrespTryAndParent = tmpSafeCorrespTry;
							for (int p = 0; p < (int) parentList->size(); p++)
								tmpSafeCorrespTryAndParent.push_back((*parentList)[p]);
							isometryCostTry = fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent);
						}
						else
							isometryCostTry = fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, false);
						//..save it if it's the best so far
						if (isometryCostTry < minIsometryCost)
						{
							minIsometryCost = isometryCostTry;
							bestCorresp = tmpSafeCorrespTry;
							//save costs that lead to this so-far-best isometryCostTry, to assign them in the end in case this tmpSafeCorrespTry selected as bestCorresp below
							if (useCurrentAndParentForTraversalList) //better be true for fromSize = 2 case
								fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent, true);
							else
								fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, true);
						}
#ifndef SUBDIV_TO_5_BASES
						if (nCombs++ > tooMany)
							a = b = c = m + 1; //breaks this (c-loop, not this r-loop) and all outer loops
#endif
					} //end of r
				}
	}
	else if (fromSize == 4) //use C(m,4) combinations to decide the 4 unmatched verts in to-side as good as possible
	{
		int twoR = 2*fromSize, factR = factorial(fromSize), nCombs = 0, //store useful results for not to recompute them during iters below
			tooMany = (factR > 0 ? factR * 200 : 200); //200 combinations allowed each to be tested w/ factR permutations; recall, C(m=20, r=4) = 4845, and >5000's are quite time wasting; so aim is to make factR * 200 close to 5000, therefore 200
		for (int a = 0; a < m; a++)
			for (int b = a+1; b < m; b++)
				for (int c = b+1; c < m; c++)
					for (int d = c+1; d < m; d++)
					{
						//current 4-combination is copied from appropriate slots of toMesh.tmpBaseVerts
						vector< int > tmp; //holds the current triple of all C(m,4)
						tmp.push_back(toMesh->tmpBaseVerts[a]);
						tmp.push_back(toMesh->tmpBaseVerts[b]);
						tmp.push_back(toMesh->tmpBaseVerts[c]);
						tmp.push_back(toMesh->tmpBaseVerts[d]);

						for (int r = 0; r < factR; r++) //k! reordering [cancel this line only to save time by using the 1st permutation only instead of all k! permutations]
						{		
							//refill tmpSafeCorrespTry according to the current reordering (one of k! permutations)

							//get the overall isometryCost of this current 2-tuple combination matched w/ stable 2-tuple of fromMesh
							vector< int > tmpSafeCorrespTry;
							int idx1 = 0, idx2 = 0;
							//stable 2-tuple of fromMesh goes to even indices: 0 & 2, and permuted toMesh tmpBases goes to odd slots: 1 & 3
							for (int y = 0; y < twoR; y++)
								if (y % 2 ==0)
									tmpSafeCorrespTry.push_back(fromMesh->tmpBaseVerts[idx1++]); //stable from fromMesh side
								else
									tmpSafeCorrespTry.push_back(tmp[ perms4[r][idx2++] ]);	//permutations from toMesh side
						
							//compute isometry cost of this 4-4 mapping and ..
							float isometryCostTry;
							vector< int > tmpSafeCorrespTryAndParent;
							if (useCurrentAndParentForTraversalList)
							{
								//tmpSafeCorrespTryAndParent = current tmpSafeCorrespTry + fixed parentList
								tmpSafeCorrespTryAndParent = tmpSafeCorrespTry;
								for (int p = 0; p < (int) parentList->size(); p++)
									tmpSafeCorrespTryAndParent.push_back((*parentList)[p]);
								isometryCostTry = fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent);
							}
							else
								isometryCostTry = fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, false);
							//..save it if it's the best so far
							if (isometryCostTry < minIsometryCost)
							{
								minIsometryCost = isometryCostTry;
								bestCorresp = tmpSafeCorrespTry;
								//save costs that lead to this so-far-best isometryCostTry, to assign them in the end in case this tmpSafeCorrespTry selected as bestCorresp below
								if (useCurrentAndParentForTraversalList) //better be true for fromSize = 2 case
									fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent, true);
								else
									fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, true);
							}
#ifndef SUBDIV_TO_5_BASES
							if (nCombs++ > tooMany)
								a = b = c = d = m + 1; //breaks this (d-loop, not this r-loop) and all outer loops
#endif
						} //end of r
					}
	}
	else if (fromSize == 5) //use C(m,5) combinations to decide the 5 unmatched verts in to-side as good as possible
	{
		int twoR = 2*fromSize, factR = factorial(fromSize), nCombs = 0, //store useful results for not to recompute them during iters below
			tooMany = (factR > 0 ? factR * 40 : 40); //40 combinations allowed each to be tested w/ factR permutations
		for (int a = 0; a < m; a++)
			for (int b = a+1; b < m; b++)
				for (int c = b+1; c < m; c++)
					for (int d = c+1; d < m; d++)
						for (int e = d+1; e < m; e++)
						{
							//current 5-combination is copied from appropriate slots of toMesh.tmpBaseVerts
							vector< int > tmp; //holds the current triple of all C(m,5)
							tmp.push_back(toMesh->tmpBaseVerts[a]);
							tmp.push_back(toMesh->tmpBaseVerts[b]);
							tmp.push_back(toMesh->tmpBaseVerts[c]);
							tmp.push_back(toMesh->tmpBaseVerts[d]);
							tmp.push_back(toMesh->tmpBaseVerts[e]);

							for (int r = 0; r < factR; r++) //k! reordering [cancel this line only to save time by using the 1st permutation only instead of all k! permutations]
							{		
								//refill tmpSafeCorrespTry according to the current reordering (one of k! permutations)

								//get the overall isometryCost of this current 2-tuple combination matched w/ stable 2-tuple of fromMesh
								vector< int > tmpSafeCorrespTry;
								int idx1 = 0, idx2 = 0;
								//stable 2-tuple of fromMesh goes to even indices: 0 & 2, and permuted toMesh tmpBases goes to odd slots: 1 & 3
								for (int y = 0; y < twoR; y++)
									if (y % 2 ==0)
										tmpSafeCorrespTry.push_back(fromMesh->tmpBaseVerts[idx1++]); //stable from fromMesh side
									else
										tmpSafeCorrespTry.push_back(tmp[ perms5[r][idx2++] ]);	//permutations from toMesh side
							
								//compute isometry cost of this 5-5 mapping and ..
								float isometryCostTry;
								vector< int > tmpSafeCorrespTryAndParent;
								if (useCurrentAndParentForTraversalList)
								{
									//tmpSafeCorrespTryAndParent = current tmpSafeCorrespTry + fixed parentList
									tmpSafeCorrespTryAndParent = tmpSafeCorrespTry;
									for (int p = 0; p < (int) parentList->size(); p++)
										tmpSafeCorrespTryAndParent.push_back((*parentList)[p]);
									isometryCostTry = fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent);
								}
								else
									isometryCostTry = fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, false);
								//..save it if it's the best so far
								if (isometryCostTry < minIsometryCost)
								{
									minIsometryCost = isometryCostTry;
									bestCorresp = tmpSafeCorrespTry;
									//save costs that lead to this so-far-best isometryCostTry, to assign them in the end in case this tmpSafeCorrespTry selected as bestCorresp below
									if (useCurrentAndParentForTraversalList) //better be true for fromSize = 2 case
										fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent, true);
									else
										fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, true);
								}
#ifndef SUBDIV_TO_5_BASES
								if (nCombs++ > tooMany)
									a = b = c = d = e = m + 1; //breaks this (e-loop, not this r-loop) and all outer loops
#endif
							} //end of r
						}
	}
	else if (fromSize == 6) //use C(m,6) combinations to decide the 6 unmatched verts in to-side as good as possible
	{
		int twoR = 2*fromSize, factR = factorial(fromSize), nCombs = 0, //store useful results for not to recompute them during iters below
			tooMany = (factR > 0 ? factR * 7 : 7); //7 combinations allowed each to be tested w/ factR permutations
		for (int a = 0; a < m; a++)
			for (int b = a+1; b < m; b++)
				for (int c = b+1; c < m; c++)
					for (int d = c+1; d < m; d++)
						for (int e = d+1; e < m; e++)
							for (int f = e+1; f < m; f++)
							{
								//current 6-combination is copied from appropriate slots of toMesh.tmpBaseVerts
								vector< int > tmp; //holds the current triple of all C(m,6)
								tmp.push_back(toMesh->tmpBaseVerts[a]);
								tmp.push_back(toMesh->tmpBaseVerts[b]);
								tmp.push_back(toMesh->tmpBaseVerts[c]);
								tmp.push_back(toMesh->tmpBaseVerts[d]);
								tmp.push_back(toMesh->tmpBaseVerts[e]);
								tmp.push_back(toMesh->tmpBaseVerts[f]);
								for (int r = 0; r < factR; r++) //k! reordering [cancel this line only to save time by using the 1st permutation only instead of all k! permutations]
								{		
									//refill tmpSafeCorrespTry according to the current reordering (one of k! permutations)

									//get the overall isometryCost of this current 2-tuple combination matched w/ stable 2-tuple of fromMesh
									vector< int > tmpSafeCorrespTry;
									int idx1 = 0, idx2 = 0;
									//stable 2-tuple of fromMesh goes to even indices: 0 & 2, and permuted toMesh tmpBases goes to odd slots: 1 & 3
									for (int y = 0; y < twoR; y++)
										if (y % 2 ==0)
											tmpSafeCorrespTry.push_back(fromMesh->tmpBaseVerts[idx1++]); //stable from fromMesh side
										else
											tmpSafeCorrespTry.push_back(tmp[ perms6[r][idx2++] ]);	//permutations from toMesh side

									//compute isometry cost of this 6-6 mapping and ..
									float isometryCostTry;
									vector< int > tmpSafeCorrespTryAndParent;
									if (useCurrentAndParentForTraversalList)
									{
										//tmpSafeCorrespTryAndParent = current tmpSafeCorrespTry + fixed parentList
										tmpSafeCorrespTryAndParent = tmpSafeCorrespTry;
										for (int p = 0; p < (int) parentList->size(); p++)
											tmpSafeCorrespTryAndParent.push_back((*parentList)[p]);
										isometryCostTry = fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent);
									}
									else
										isometryCostTry = fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, false);
									//..save it if it's the best so far
									if (isometryCostTry < minIsometryCost)
									{
										minIsometryCost = isometryCostTry;
										bestCorresp = tmpSafeCorrespTry;
										//save costs that lead to this so-far-best isometryCostTry, to assign them in the end in case this tmpSafeCorrespTry selected as bestCorresp below
										if (useCurrentAndParentForTraversalList) //better be true for fromSize = 2 case
											fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent, true);
										else
											fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, true);
									}
#ifndef SUBDIV_TO_5_BASES
									if (nCombs++ > tooMany)
										a = b = c = d = e= f = m + 1; //breaks this (f-loop, not this r-loop) and all outer loops
#endif

									////////////////////// symmetry healing ///////////////////
									if (symHealFprint
&& isometryCostTry < 0.038f * 2) //level0 min isometryCost
									{
										fprintf(fsPtr, "%f\n", isometryCostTry);
										//fromMesh = mesh2; toMesh = this; always 'cos for the initial (level 0) matching, tmpBaseVerts.size() = mesh2.tmpBaseVerts.size() for sure for test above
										for (int i = 0; 2*i + 1 < (int) tmpSafeCorrespTry.size(); i++)
											fprintf(fsPtr, "%d\t%d\n", tmpSafeCorrespTry[2*i + 1], tmpSafeCorrespTry[2*i]); //order reversed 'cos fromMesh=mehs2 for sure and i want mesh2 verts fprinted to the left of mesh1 verts
										fprintf(fsPtr, "\n");
										totalIsoTry += isometryCostTry;
										nTry++;
										isoVals.push_back(isometryCostTry);
										isoIdxs.push_back((int) nTry);
									}											
									////////////////////// symmetry healing ///////////////////
								} //end of r
							}
	}
	else if (fromSize == 7) //use C(m,7) combinations to decide the 7 unmatched verts in to-side as good as possible
	{
		//efficiency: 9 vs. 7 tries C(9, 7) = 36 sets of size 7 (7-uples) where each set is tried 7! = 5040 times; hence 36x5040 = 181440 calls to getErrOfCorrespListCP() and all takes 15 secs on x32 8-core workstation; acceptable but slows down significantly since other patches matched in just a sec
		int twoR = 2*fromSize, factR = factorial(fromSize), nCombs = 0, //store useful results for not to recompute them during iters below
			tooMany = (factR > 0 ? factR * 1 : 1); //1 combinations allowed each to be tested w/ factR (= 5040) permutations; so, 5040 call to getErrOfCorrespListCP()
		for (int a = 0; a < m; a++)
			for (int b = a+1; b < m; b++)
				for (int c = b+1; c < m; c++)
					for (int d = c+1; d < m; d++)
						for (int e = d+1; e < m; e++)
							for (int f = e+1; f < m; f++)
								for (int g = f+1; g < m; g++)
								{
									//current 7-combination is copied from appropriate slots of toMesh.tmpBaseVerts
									vector< int > tmp; //holds the current triple of all C(7,6)
									tmp.push_back(toMesh->tmpBaseVerts[a]);
									tmp.push_back(toMesh->tmpBaseVerts[b]);
									tmp.push_back(toMesh->tmpBaseVerts[c]);
									tmp.push_back(toMesh->tmpBaseVerts[d]);
									tmp.push_back(toMesh->tmpBaseVerts[e]);
									tmp.push_back(toMesh->tmpBaseVerts[f]);
									tmp.push_back(toMesh->tmpBaseVerts[g]);

//for (int i =0; i<tmp.size();i++)cout<<tmp[i]<<"\t";cout<<factR<<" permuts of this set'll be tried w/ fromMesh of size " << fromMesh->tmpBaseVerts.size()<<endl;
									for (int r = 0; r < factR; r++) //k! reordering [cancel this line only to save time by using the 1st permutation only instead of all k! permutations]
									{		
										//refill tmpSafeCorrespTry according to the current reordering (one of k! permutations)

										//get the overall isometryCost of this current 2-tuple combination matched w/ stable 2-tuple of fromMesh
										vector< int > tmpSafeCorrespTry;
										int idx1 = 0, idx2 = 0;
										//stable 2-tuple of fromMesh goes to even indices: 0 & 2, and permuted toMesh tmpBases goes to odd slots: 1 & 3
										for (int y = 0; y < twoR; y++)
											if (y % 2 ==0)
												tmpSafeCorrespTry.push_back(fromMesh->tmpBaseVerts[idx1++]); //stable from fromMesh side
											else
												tmpSafeCorrespTry.push_back(tmp[ perms7[r][idx2++] ]);	//permutations from toMesh side

										//compute isometry cost of this 6-6 mapping and ..
										float isometryCostTry;
										vector< int > tmpSafeCorrespTryAndParent;
										if (useCurrentAndParentForTraversalList)
										{
											//tmpSafeCorrespTryAndParent = current tmpSafeCorrespTry + fixed parentList
											tmpSafeCorrespTryAndParent = tmpSafeCorrespTry;
											for (int p = 0; p < (int) parentList->size(); p++)
												tmpSafeCorrespTryAndParent.push_back((*parentList)[p]);
											isometryCostTry = fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent);
										}
										else
											isometryCostTry = fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, false);
										//..save it if it's the best so far
										if (isometryCostTry < minIsometryCost)
										{
											minIsometryCost = isometryCostTry;
											bestCorresp = tmpSafeCorrespTry;
											//save costs that lead to this so-far-best isometryCostTry, to assign them in the end in case this tmpSafeCorrespTry selected as bestCorresp below
											if (useCurrentAndParentForTraversalList) //better be true for fromSize = 2 case
												fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent, true);
											else
												fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, true);
										}
#ifndef SUBDIV_TO_5_BASES
										if (nCombs++ > tooMany)
											a = b = c = d = e = f = g = m + 1; //breaks this (g-loop, not this r-loop) and all outer loops
#endif

										////////////////////// symmetry healing ///////////////////
										if (symHealFprint
&& isometryCostTry < 0.038f * 2) //level0 min isometryCost * 2
										{
											fprintf(fsPtr, "%f\n", isometryCostTry);
											//fromMesh = mesh2; toMesh = this; always 'cos for the initial (level 0) matching, tmpBaseVerts.size() = mesh2.tmpBaseVerts.size() for sure for test above
											for (int i = 0; 2*i + 1 < (int) tmpSafeCorrespTry.size(); i++)
												fprintf(fsPtr, "%d\t%d\n", tmpSafeCorrespTry[2*i + 1], tmpSafeCorrespTry[2*i]); //order reversed 'cos fromMesh=mehs2 for sure and i want mesh2 verts fprinted to the left of mesh1 verts
											fprintf(fsPtr, "\n");
											totalIsoTry += isometryCostTry;
											nTry++;
											isoVals.push_back(isometryCostTry);
											isoIdxs.push_back((int) nTry);
										}											
										////////////////////// symmetry healing ///////////////////
									} //end of r
								}
//cout << nCombs << " vs.    " << tooMany << endl;
	}
	else if (fromSize == 8) //use C(m,8) combinations to decide the 8 unmatched verts in to-side as good as possible
	{
//assert(level == 0); //i can enter here only for the very first level, i.e. call from initialEuclideanMatching()
        int twoR = 2*fromSize, factR = factorial(fromSize), nCombs = 0, //store useful results for not to recompute them during iters below
            tooMany = (factR > 0 ? factR * 1 : 1); //1 combinations allowed each to be tested w/ factR (= 5040) permutations; so, 5040 call to getErrOfCorrespListCP()

//if (m > 12) {FILE* ffPtr=fopen("tooBig.dat","a");fprintf(ffPtr, "%d\t%d\n", frameID, mesh2->frameID);fclose(ffPtr);exit(0);} //becomes too slow; shouldn't happen anyway 'cos i divide patches nicely; maby very different resolution may cause this but still less possible
//cout << "C(" << m << ", " << fromSize << ")............";
        //8! permutations in perms8[][]
        char fName[250];
        sprintf(fName, "data\\permutations\\0-start\\%d!.txt", fromSize);
        FILE* fPtr;
        if (! (fPtr = fopen(fName, "r")))
        {
            cout << fName << " not found\n";
            exit(0);
        }
        int** perms8 = new int*[factR];
        for (int i = 0; i < factR; i++) //r! reorderings/permutations
        {
            perms8[i] = new int[fromSize];
            for (int j = 0; j < fromSize; j++) //each of size r
                fscanf(fPtr, "%d ", &perms8[i][j]);
        }
        fclose(fPtr);
        for (int a = 0; a < m; a++)
            for (int b = a+1; b < m; b++)
                for (int c = b+1; c < m; c++)
                    for (int d = c+1; d < m; d++)
                        for (int e = d+1; e < m; e++)
                            for (int f = e+1; f < m; f++)
                                for (int g = f+1; g < m; g++)
                                    for (int h = g+1; h < m; h++)
                                    {
                                        vector< int > tmp;
                                        tmp.push_back(toMesh->tmpBaseVerts[a]);
                                        tmp.push_back(toMesh->tmpBaseVerts[b]);
                                        tmp.push_back(toMesh->tmpBaseVerts[c]);
                                        tmp.push_back(toMesh->tmpBaseVerts[d]);
                                        tmp.push_back(toMesh->tmpBaseVerts[e]);
                                        tmp.push_back(toMesh->tmpBaseVerts[f]);
                                        tmp.push_back(toMesh->tmpBaseVerts[g]);
                                        tmp.push_back(toMesh->tmpBaseVerts[h]);

                                        for (int r = 0; r < factR; r++) //k! reordering [cancel this line only to save time by using the 1st permutation only instead of all k! permutations]
                                        {       
                                            //refill tmpSafeCorrespTry according to the current reordering (one of k! permutations)

                                            //get the overall isometryCost of this current 2-tuple combination matched w/ stable 2-tuple of fromMesh
                                            vector< int > tmpSafeCorrespTry;
                                            int idx1 = 0, idx2 = 0;
                                            //stable 2-tuple of fromMesh goes to even indices: 0 & 2, and permuted toMesh tmpBases goes to odd slots: 1 & 3
                                            for (int y = 0; y < twoR; y++)
                                                if (y % 2 ==0)
                                                    tmpSafeCorrespTry.push_back(fromMesh->tmpBaseVerts[idx1++]); //stable from fromMesh side
                                                else
                                                    tmpSafeCorrespTry.push_back(tmp[ perms8[r][idx2++] ]);    //permutations from toMesh side

                                            //compute isometry cost of this 8-8 mapping and ..
                                            float isometryCostTry;
                                            vector< int > tmpSafeCorrespTryAndParent;
                                            if (useCurrentAndParentForTraversalList)
                                            {
                                                //tmpSafeCorrespTryAndParent = current tmpSafeCorrespTry + fixed parentList
                                                tmpSafeCorrespTryAndParent = tmpSafeCorrespTry;
                                                for (int p = 0; p < (int) parentList->size(); p++)
                                                    tmpSafeCorrespTryAndParent.push_back((*parentList)[p]);
                                                isometryCostTry = fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent);
                                            }
                                            else
                                                isometryCostTry = fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, false);
                                            //..save it if it's the best so far
                                            if (isometryCostTry < minIsometryCost)
                                            {
                                                minIsometryCost = isometryCostTry;
                                                bestCorresp = tmpSafeCorrespTry;
                                                //save costs that lead to this so-far-best isometryCostTry, to assign them in the end in case this tmpSafeCorrespTry selected as bestCorresp below
                                                if (useCurrentAndParentForTraversalList) //better be true for fromSize = 2 case
                                                    fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent, true);
                                                else
                                                    fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, true);
                                            }
#ifndef SUBDIV_TO_5_BASES
                                            if (nCombs++ > tooMany)
                                                a = b = c = d = e = f = g = h = m + 1; //breaks this (g-loop, not this r-loop) and all outer loops
#endif

											////////////////////// symmetry healing ///////////////////
											if (symHealFprint
&& isometryCostTry < 0.077f * 2) //level0 min isometryCost * 2
											{
												fprintf(fsPtr, "%f\n", isometryCostTry);
												//fromMesh = mesh2; toMesh = this; always 'cos for the initial (level 0) matching, tmpBaseVerts.size() = mesh2.tmpBaseVerts.size() for sure for test above
												for (int i = 0; 2*i + 1 < (int) tmpSafeCorrespTry.size(); i++)
													fprintf(fsPtr, "%d\t%d\n", tmpSafeCorrespTry[2*i + 1], tmpSafeCorrespTry[2*i]); //order reversed 'cos fromMesh=mehs2 for sure and i want mesh2 verts fprinted to the left of mesh1 verts
												fprintf(fsPtr, "\n");
												totalIsoTry += isometryCostTry;
												nTry++;
												isoVals.push_back(isometryCostTry);
												isoIdxs.push_back((int) nTry);
											}											
											////////////////////// symmetry healing ///////////////////
                                        } //end of r
                                    }
        for (int i = 0; i < factR; i++) delete [] perms8[i]; delete [] perms8;
//cout << "fffffffffffff\n";
    }
	else if (fromSize == 1) //use C(m,1)=m combinations to decide the 1 unmatched verts in to-side as good as possible
	{
		for (int a = 0; a < m; a++)
		{
			//current 1-combination is copied from appropriate slots of toMesh.tmpBaseVerts
			//vector< int > tmp; tmp.push_back(toMesh->tmpBaseVerts[a]);

			//try 1! permutations of tmp w/ stable fromMesh.tmpBaseVerts of size 1 (for the easy-to-understand version that ignore permutations); 1! permutation is tmp itself so no for-loops below
			//refill tmpSafeCorrespTry according to the current reordering (one of k! permutations)
			//get the overall isometryCost of this current 2-tuple combination matched w/ stable 2-tuple of fromMesh
			vector< int > tmpSafeCorrespTry;
			tmpSafeCorrespTry.push_back(fromMesh->tmpBaseVerts[0]); //don't have any other tmpBase anyway (fromSize = 1)
			tmpSafeCorrespTry.push_back(toMesh->tmpBaseVerts[a]);	//don't have any other tmpBase anyway (tmp.push_back() done only one time above (comment-out'ed indeed))

			//compute isometry cost of this 1-1 mapping and ..
			float isometryCostTry;
			vector< int > tmpSafeCorrespTryAndParent;
			if (useCurrentAndParentForTraversalList) //better be true for fromSize = 1 case
			{
				//tmpSafeCorrespTryAndParent = current tmpSafeCorrespTry + fixed parentList
				tmpSafeCorrespTryAndParent = tmpSafeCorrespTry;
				for (int p = 0; p < (int) parentList->size(); p++)
					tmpSafeCorrespTryAndParent.push_back((*parentList)[p]);
//simply use *parentList instead of tmpSafeCorrespTryAndParent just-below to remove tmpSafeCorrespTry from the traversalList, i.e. use only the parent patch, not current + parent
				isometryCostTry = fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent);
			}
			else //should not execute here due to symmetry problem explained below
				isometryCostTry = fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, false);
			//..save it if it's the best so far
			if (isometryCostTry < minIsometryCost)
			{
				minIsometryCost = isometryCostTry;
				bestCorresp = tmpSafeCorrespTry;
				//save costs that lead to this so-far-best isometryCostTry, to assign them in the end in case this tmpSafeCorrespTry selected as bestCorresp below
				if (useCurrentAndParentForTraversalList) //better be true for fromSize = 2 case
					fromMesh->getErrOfCorrespListCP(toMesh, tmpSafeCorrespTry, tmpSafeCorrespTryAndParent, true);
				else
					fromMesh->getErrOfCorrespList(toMesh, tmpSafeCorrespTry, true);
			}
		}
	}
	else
	{
		cout << "smaller patch shouldn't be that big: " << fromSize << endl; //bestCorresp.size remains 0 and nothing happens below
		//for some forced/manual entrance to this function, i may come this else part, in which case just do nothing (recall existing bv1-bv2 corresp already maintained in matchSmallPatches()); similarly, if i
		//shortcut prev level due to out of memo and resume this level from that incomplete prev level data, then bigger patches may appear here; so, no exit(0);
exit(0);
	}

#ifdef DBG_DETAIL
	cout << "bestCorresp for these tmpBases:\n";
#endif

	////////////////////// symmetry healing ///////////////////
	if (symHealFprint)
	{
		//fprint patch/baseNghbhood info as well since it'll be required for reloading later		
		fprintf(fsPtr, "-2\n\n"); //terminator of mapping section above
		for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
		{
			fprintf(fsPtr, "%d\n", tmpBaseVerts[i]);
			for (int bn = 0; bn < (int) verts[ tmpBaseVerts[i] ]->baseNghbhood->size(); bn++)
				fprintf(fsPtr, "%d\t", (*verts[ tmpBaseVerts[i] ]->baseNghbhood)[bn]);
			fprintf(fsPtr, "-3\n"); //terminator of this patch info
		}
		fprintf(fsPtr, "-2\n"); //terminator of mesh1 patches
		for (int i = 0; i < (int) mesh2->tmpBaseVerts.size(); i++)
		{
			fprintf(fsPtr, "%d\n", mesh2->tmpBaseVerts[i]);
			for (int bn = 0; bn < (int) mesh2->verts[ mesh2->tmpBaseVerts[i] ]->baseNghbhood->size(); bn++)
				fprintf(fsPtr, "%d\t", (*mesh2->verts[ mesh2->tmpBaseVerts[i] ]->baseNghbhood)[bn]);
			fprintf(fsPtr, "-3\n"); //terminator of this patch info
		}
		//avg and min isometry costs to finish w/
		fprintf(fsPtr, "-2\n\navg & min isometric distortions of all initial maps above: %f & %f\n", totalIsoTry / nTry, minIsometryCost); //-2 terminates mesh2 patches
		fclose(fsPtr);
		cout << ">>>>>>>>> " << nTry << " initial mapping candidates fwritten to file\n\n";

		//transfer top10 min isometry cost mappings of all mappings in fsPtr to tmpPtr (assuming topN = 10)
		int topN = 10, sortedIsoIdxs = 0; //aim is to make sortedIsoIdxs = topN
		fsPtr = fopen(fsName, "r"); //read this time to transfer top10 data from here to tmpPtr
		sprintf(fsName, "%s\\symHeal%d-%d_%dx%dtop%d.dat", outFolder, frameID, mesh2->frameID, fromSize, fromSize, topN);
		FILE* tmpPtr = fopen(fsName, "w");
		int a; float b, c, d, e;
		fscanf(fsPtr, "%d\t\t%f\t%f\t\t%f\t%f\n\n", &a, &b, &c, &d, &e); //from existing unsorted file
		fprintf(tmpPtr, "%d\t\t%f\t%f\t\t%f\t%f\n\n", a, b, c, d, e); //to new sorted file in creation
		while (sortedIsoIdxs < topN)
		{
			float minIso = INF;
			int minIsoIdx = -1, idxToRemove;
			for (int i = 0; i < (int) isoVals.size(); i++)
				if (isoVals[i] < minIso)
				{
					minIso = isoVals[i];
					minIsoIdx = isoIdxs[i];
					idxToRemove = i;
				}
			//i got the min of current isoVals; add it to the sortedIsoIdxs and remove from isoVals & isoIdxs
			if (minIsoIdx == -1) //should never happen
				break;
			sortedIsoIdxs++; //1 more element from min topN is fwritten below
			isoVals.erase(isoVals.begin() + idxToRemove); //v.erase(begin + 4) erases v[4]
			isoIdxs.erase(isoIdxs.begin() + idxToRemove);

			//file stuff from appropriate/non-dummy slots of fsPtr to tmpPtr (whose head line is fprint'ed above)
			rewind(fsPtr); //go to the beginning of the file
			fscanf(fsPtr, "%d\t\t%f\t%f\t\t%f\t%f\n\n", &a, &b, &c, &d, &e); //dummies
			//each mapping comes w/ m+1 lines (isometry cost + m lines of mapping); skip the nthMapping-1 preceding mapping
			float isoCost;
			int v1, v2;
			for (int map = 1; map < minIsoIdx; map++)
			{
				fscanf(fsPtr, "%f\n", &isoCost); //dummy
				for (int i = 0; i < m; i++)
					fscanf(fsPtr, "%d\t%d\n", &v1, &v2); //dummy
			}
			//desired mapping
			fscanf(fsPtr, "%f\n", &isoCost); //from existing unsorted file
			fprintf(tmpPtr, "%f\n", isoCost); //to new sorted file in creation
			for (int i = 0; i < m; i++)
			{
				fscanf(fsPtr, "%d\t%d\n", &v1, &v2); //from
				fprintf(tmpPtr, "%d\t%d\n", v1, v2); //to
			}
			fprintf(tmpPtr, "\n");
		} //end of topN
		fclose(fsPtr);
		fprintf(tmpPtr, "-2\n\n"); //separator before baseNghbhood info
		//baseNghbhood info
		for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
		{
			fprintf(tmpPtr, "%d\n", tmpBaseVerts[i]);
			for (int bn = 0; bn < (int) verts[ tmpBaseVerts[i] ]->baseNghbhood->size(); bn++)
				fprintf(tmpPtr, "%d\t", (*verts[ tmpBaseVerts[i] ]->baseNghbhood)[bn]);
			fprintf(tmpPtr, "-3\n"); //terminator of this patch info
		}
		fprintf(tmpPtr, "-2\n"); //terminator of mesh1 patches
		for (int i = 0; i < (int) mesh2->tmpBaseVerts.size(); i++)
		{
			fprintf(tmpPtr, "%d\n", mesh2->tmpBaseVerts[i]);
			for (int bn = 0; bn < (int) mesh2->verts[ mesh2->tmpBaseVerts[i] ]->baseNghbhood->size(); bn++)
				fprintf(tmpPtr, "%d\t", (*mesh2->verts[ mesh2->tmpBaseVerts[i] ]->baseNghbhood)[bn]);
			fprintf(tmpPtr, "-3\n"); //terminator of this patch info
		}
		fprintf(tmpPtr, "-2\n");
		fclose(tmpPtr);
		cout << ">>>>>>>>> " << sortedIsoIdxs << " topN initial mapping candidates fwritten to file\n\n";
	}
	////////////////////// symmetry healing ///////////////////

	//now that bestCorresp is ready, fill the matchIdxs appropriately
	//go over bestCorresp again to decide the isometryCosts to be used by 1-to-1 conversion in make1to1()
	int n = (int) bestCorresp.size() / 2;
	for (int s = 0; s < n; s++)
	{
		int tbv1 = bestCorresp[2*s], tbv2 = bestCorresp[2*s + 1]; //even idx for fromMesh, odds for toMesh; so, tbv1 ~ fromMesh, tbv2 ~ toMesh
assert(fromMesh->verts[tbv1]->isometryCosts.size() == 2 &&  //1 vert + its vertexBasedIso set in getErrOfCorrespList/CP(); so, size must be 2 at all times
	   fromMesh->verts[tbv1]->isometryCosts[0] == tbv2); //tbv1-tbv2 is a pair in bestCorresp; so, isometryCosts better be consistent w/ it

		float vertexBasedIso = fromMesh->verts[tbv1]->isometryCosts[1]; //don't compute vertexBasedIso here using bestCorresp as safeList 'cos above i've used a different safeList that involved parent
#ifdef DBG_DETAIL
		if (fromMesh == this)
			cout << tbv1 << " --=--=> " << tbv2 << "\t((" << vertexBasedIso << "))\n";
		else
			cout << tbv2 << " --=--=> " << tbv1 << "\t((" << vertexBasedIso << "))\n";
#endif
		fromMesh->verts[tbv1]->matchIdxs.push_back((float) tbv2);
		fromMesh->verts[tbv1]->matchIdxs.push_back(vertexBasedIso); //cost of this match; if this turns out to be the best cost, then verts[tbv1]->matchIdx = tbv2'll be done in make1to1()
		toMesh->verts[tbv2]->matchIdxs.push_back((float) tbv1); //duality
		toMesh->verts[tbv2]->matchIdxs.push_back(vertexBasedIso);
		fromMesh->verts[tbv1]->isometryCostChosenByBlossom = toMesh->verts[tbv2]->isometryCostChosenByBlossom = vertexBasedIso; //no blossom occured but update this value anyway for consistency
		fromMesh->verts[tbv1]->residue = toMesh->verts[tbv2]->residue = false; //set here as a result of permutation-based matching; the one(s) left residue=true will be matched below
	} //end of s

	//finally, update mesh1's residual tmpBaseVertices's matchIdxs[] values (non-residue's matchIdxs already updated above via bestCorresp
//i could have done below w/o if-else using the from/toMesh but anyway; this is easier and more explanatory
	if (! forPostprocessing && fromMesh == mesh2) //mesh2.size is less so that mesh1.vert remained unmatched here (insufficient candidates in mesh2); so, fromMesh = smaller mesh = mesh2 for sure
		for (int i = 0; i < (int) tmpBaseVerts.size(); i++)
		{
			int tbv1 = tmpBaseVerts[i];
			if (! verts[tbv1]->residue) //tbv1 already has a match candidate coming from above, e.g. for the 3 vs. 2 patch case, 2 vs. 2 is set above via permutations, and 1 residue in patch1 is going below by escaping from this continue
				continue;

			//no match created above for this mesh1 vert (also it's not shared 'cos o/w it might have an entry from a previous patch that would have made matchIdxs nonempty here); so, create 1 good match using the good correspondence created above as the safeList
			float minMatchCostIResidue = INF;
			int bestMatchForResidue = -1;
			for (int j = 0; j < (int) mesh2->tmpBaseVerts.size(); j++) //try tbv1 vs. next tmpBaseVerts from mesh2
			{
				float vertexBasedIso = 0.0f;
				for (int s = 0; s < n; s++) //to try tbv1 vs. mesh2.tmpBaseVerts[j], use bestCorresp as the safeList
				{
					int s2 = bestCorresp[2*s], s1 = bestCorresp[2*s + 1]; //even idx for fromMesh, odds for toMesh; so, s2 ~ fromMesh, s1 ~ toMesh; since fromMesh == mesh2 here for sure, s2 ~ mesh2
assert(s1 != tbv1); //'cos if tbv1 was in bestCorresp, then tbv1.matchIdxs wouldn't be empty and i'd not come here; so, tbv1 not in bestCorresp for sure
					if (s2 == mesh2->tmpBaseVerts[j]) //prevent dSpanning = 0 case not to confuse the vertexBasedIso calculation
						continue;

					//if tbv1 vs. mesh2.tmpBaseVerts[j] is a good correspondence, then geoDist(tbv1, s1) = geoDist(mesh2.tmpBaseVerts[j], s2) for all s1 & s2 in bestCorresp, where s1 ~ mesh1, s2 ~ mesh2
					float t1s1 = verts[tbv1]->dSpanning[s1], t2s2 = mesh2->verts[ mesh2->tmpBaseVerts[j] ]->dSpanning[s2];
					vertexBasedIso += fabs(t1s1 - t2s2); //normalized difference here is small if tbv1 vs. mesh2->tmpBaseVerts[j] is a good correspondence
assert(t1s1 >= 0 && t1s1 <= 1 && t2s2 >= 0 && t2s2 <= 1);
				}
				if (n != 1) //no div by zero
					vertexBasedIso /= (n-1); //-1 'cos s2 skipped/continue'd one time
				else
					vertexBasedIso = 0.0f; //due to symmetry problem, all costs will be the same and hence best selected arbitrarily

				if (vertexBasedIso < minMatchCostIResidue) //tbv1 vs. mesh2->tmpBaseVerts[j] is the best match so far
				{
					bestMatchForResidue = mesh2->tmpBaseVerts[j];
					minMatchCostIResidue = vertexBasedIso;
				}
			} //end of j
			verts[tbv1]->matchIdxs.push_back((float) bestMatchForResidue);
			verts[tbv1]->matchIdxs.push_back(minMatchCostIResidue);
			mesh2->verts[bestMatchForResidue]->matchIdxs.push_back((float) tbv1); //dual op
			mesh2->verts[bestMatchForResidue]->matchIdxs.push_back(minMatchCostIResidue);
		}
	else if (! forPostprocessing && fromMesh == this) //mesh1.size is less so that mesh2.vert remained unmatched here (insufficient candidates in mesh1); so, fromMesh = smaller mesh = mesh1 for sure
		for (int i = 0; i < (int) mesh2->tmpBaseVerts.size(); i++)
		{
			int tbv2 = mesh2->tmpBaseVerts[i];
			if (! mesh2->verts[tbv2]->residue) //tbv2 already has a match candidate coming from above, e.g. for the 2 vs. 3 patch case, 2 vs. 2 is set above via permutations, and 1 residue in patch2 is going below by escaping from this continue
				continue;

			//no match created above for this mesh2 vert (also it's not shared 'cos o/w it might have an entry from a previous patch that would have made matchIdxs nonempty here); so, create 1 good match using the good correspondence created above as the safeList
			float minMatchCostIResidue = INF;
			int bestMatchForResidue = -1;
			for (int j = 0; j < (int) tmpBaseVerts.size(); j++) //try next tmpBaseVerts from mesh1 vs. tbv2
			{
				float vertexBasedIso = 0.0f;
				for (int s = 0; s < n; s++) //to try next tmpBaseVerts from mesh1 (tmpBaseVerts[j]) vs. tbv2, use bestCorresp as the safeList
				{
					int s1 = bestCorresp[2*s], s2 = bestCorresp[2*s + 1]; //even idx for fromMesh (this/mesh1), odds for toMesh; so, s1 ~ fromMesh, s2 ~ toMesh; since fromMesh == mesh1 here for sure, s2 ~ mesh2
assert(s2 != tbv2); //'cos if tbv2 was in bestCorresp, then tbv2.matchIdxs wouldn't be empty and i'd not come here; so, tbv2 not in bestCorresp for sure
					if (s1 == tmpBaseVerts[j]) //prevent dSpanning = 0 case not to confuse the vertexBasedIso calculation
						continue;

					//if tmpBaseVerts[j] vs. tbv2 is a good correspondence, then geoDist(tmpBaseVerts[j] , s1) = geoDist(tbv2, s2) for all s1 & s2 in bestCorresp, where s1 ~ mesh1, s2 ~ mesh2
					float t1s1 = verts[ tmpBaseVerts[j] ]->dSpanning[s1], t2s2 = mesh2->verts[tbv2]->dSpanning[s2];
					vertexBasedIso += fabs(t1s1 - t2s2); //normalized difference here is small if tmpBaseVerts[j] vs. tbv2 is a good correspondence
assert(t1s1 >= 0 && t1s1 <= 1 && t2s2 >= 0 && t2s2 <= 1);
				}
				if (n != 1) //no div by zero
					vertexBasedIso /= (n-1); //-1 'cos s2 skipped/continue'd one time
				else
					vertexBasedIso = 0.0f; //due to symmetry problem, all costs will be the same and hence best selected arbitrarily

				if (vertexBasedIso < minMatchCostIResidue) //tmpBaseVerts[j] vs. tbv2 is the best match so far
				{
					bestMatchForResidue = tmpBaseVerts[j];
					minMatchCostIResidue = vertexBasedIso;
				}
			} //end of j
			mesh2->verts[tbv2]->matchIdxs.push_back((float) bestMatchForResidue);
			mesh2->verts[tbv2]->matchIdxs.push_back(minMatchCostIResidue);
			verts[bestMatchForResidue]->matchIdxs.push_back((float) tbv2); //dual op
			verts[bestMatchForResidue]->matchIdxs.push_back(minMatchCostIResidue);
		}
	/////////////////// best matching via combination & permutation ends ///////////////

	if (parentList)
		delete parentList;
//	cout << "done!\n\n";
}

void Mesh::levelInfoToFile(int currentLevel, Mesh* mesh2)
{
	//fprints info of currentLevel and exits so that i can load it and start from there for the next level

	cout << "level info being fprinted..\n";

	char fName[150];
/*	sprintf(folder, "subdivide_at_all_times");
	if (theInput == JUMPING)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\jumping\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == CAT)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\cat\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == DOG)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\dog\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == LION)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\lion\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == HORSE2)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\horse2\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == CAT_DOG)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\cat-dog\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == CAT_WOLF)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\cat-wolf\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == WOLF_HORSE2)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\wolf-horse2\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == DAVID)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\david\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == VICTORIA)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\victoria\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == MICHEAL)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\michael\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == DAVID_VICTORIA)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\dav-vic\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == MICHAEL_VICTORIA)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\mic-vic\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == DAVID_MICHAEL)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\dav-mic\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, currentLevel);
	else if (theInput == BALLERINA)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\Watertight\\%s%d-%d level%d.dat", (collapsed ? "collapsed\\" : ""), frameID, mesh2->frameID, currentLevel);
	else if (theInput == SCAPE)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SCAPE\\%s%d-%d level%d.dat", (collapsed ? "collapsed\\" : ""), frameID, mesh2->frameID, currentLevel); //extra collapsed\ folder
	else if (theInput == SHREC11)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\%s%d-%d level%d.dat", (collapsed ? "collapsed\\" : ""), frameID, mesh2->frameID, currentLevel);
	else if (theInput == TOSCA)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\%s%d-%d level%d.dat", (collapsed ? "collapsed\\" : ""), frameID, mesh2->frameID, currentLevel);
	else
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\%s\\%s\\%d-%d level%d.dat", (theInput != DANCING ? "hybrid" : "dancing"), folder, frameID, mesh2->frameID, currentLevel);	*/
	sprintf(fName, "output\\%d-%d level%d.dat", frameID, mesh2->frameID, currentLevel); //generic input/output for this publicly shared code
	FILE* fPtr = fopen(fName, "w");

	//first, stuff to be loaded/fread as r1 and r2 for the next level, followed by level whose info'll be fprinted in this file
	fprintf(fPtr, "%f\t%f\t%d\n", r1, r2, currentLevel); //no separator needed 'cos we're done
	fprintf(fPtr, "%f\t%f\t%f\t%f\n", minGeoDistG, maxGeoDistG, mesh2->minGeoDistG, mesh2->maxGeoDistG); //no separator needed 'cos we're done

	//second, correspondences to be loaded/fread into safeCorresp for the next level
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
		fprintf(fPtr, "%d\t%d\n", safeCorresp[2*s], safeCorresp[2*s + 1]);
	fprintf(fPtr, "-2\n"); //section separator is -2

	//third, avg and worst ground-truth and isometry costs of the match produced at this level; given r1 and maxGeoDistG above, 0.312r or 0.1234n values can be calculated during loading (groundTruthCost = -0.9*r1, a nonsense value, for hybrid runs)
	//fprintf(fPtr, "%f\t%f\n", groundTruthCost, isometryCost); //avg
	fprintf(fPtr, "avg: %f\n", isometryCost); //no grd-truth assumption in this publicly shared generic mode
	//fprintf(fPtr, "%f\t\t%d\t%d\n", maxCostGg, worstMatchG, (worstMatchG == -1 ? -1 : verts[worstMatchG]->worstGin2)); //worst ground truth and the related match it is attached to
	fprintf(fPtr, "worst: %f\t\t%d\t%d\n", maxCostIg, worstMatchI, verts[worstMatchI]->worstIin2); //worst isometry and the related match it is attached to
	fprintf(fPtr, "-2\n"); //section separator is -2

	//fourth, computeTmpBaseVerts2/matchSmallTmpBases() for next level needs baseNghbhood info to mark elements as tmpBaseCandidate=true; write this info as duplicate free for less file size
	for (int bv = 0; bv < (int) baseVerts.size(); bv++)
	{
assert(verts[ baseVerts[bv] ]->safe); //must be in safeCorresp[] list, i.e. fprinted above
		fprintf(fPtr, "%d\n", baseVerts[bv]); //first line for the base id whose baseNghbhood'll be fprinted into subsequent line
		for (int i = 0; i < (int) verts.size(); i++)
			verts[i]->processed = false; //duplicate prevention; processed means fprinted in the current line
		for (int v = 0; v < (int) verts[ baseVerts[bv] ]->baseNghbhood->size(); v++)
			if (! verts[ (*verts[ baseVerts[bv] ]->baseNghbhood)[v] ]->processed)
			{
				fprintf(fPtr, "%d ", (*verts[ baseVerts[bv] ]->baseNghbhood)[v]);
				verts[ (*verts[ baseVerts[bv] ]->baseNghbhood)[v] ]->processed = true;
			}
		fprintf(fPtr, "-3\n"); //end of this line where this line is the duplicate-free baseNghbhood of baseVerts[bv]
	}
	fprintf(fPtr, "-2\n"); //section separator is -2

	for (int bv = 0; bv < (int) mesh2->baseVerts.size(); bv++) //same stuff for mesh2
	{
assert(mesh2->verts[ mesh2->baseVerts[bv] ]->safe); //must be in safeCorresp[] list, i.e. fprinted above
		fprintf(fPtr, "%d\n", mesh2->baseVerts[bv]); //first line for the base id whose baseNghbhood'll be fprinted into subsequent line
		for (int i = 0; i < (int) mesh2->verts.size(); i++)
			mesh2->verts[i]->processed = false;
		for (int v = 0; v < (int) mesh2->verts[ mesh2->baseVerts[bv] ]->baseNghbhood->size(); v++)
			if (! mesh2->verts[ (*mesh2->verts[ mesh2->baseVerts[bv] ]->baseNghbhood)[v] ]->processed)
			{
				fprintf(fPtr, "%d ", (*mesh2->verts[ mesh2->baseVerts[bv] ]->baseNghbhood)[v]);
				mesh2->verts[ (*mesh2->verts[ mesh2->baseVerts[bv] ]->baseNghbhood)[v] ]->processed = true;
			}
		fprintf(fPtr, "-3\n"); //end of this line where this line is the duplicate-free baseNghbhood of baseVerts[bv]
	}
	fprintf(fPtr, "-2\n"); //section separator is -2

/*	//finally, initialCorresp since adaptiveListForIsometryCost() needs it for m=0 case
assert((int) initialCorresp->size() == 10); //10 means 5x2, hence 5 matches after initialCorrespC10_5()
	fprintf(fPtr, "5\n"); //section separator is -2
	for (int s = 0; s < 5; s++)
		fprintf(fPtr, "%d\t%d\n", (*initialCorresp)[2*s], (*initialCorresp)[2*s + 1]);*/

	//aim is to handle memory so quit now, and continue w/ a fresh memo later using this file
	fclose(fPtr);
	cout << "done!\n";
}

void Mesh::resumeFromLevel(int levelToLoad, Mesh* mesh2)
{
	//freads info of levelToLoad and resumes correspondence algo from that level (to handle memory errors do it like this)
//level*.dat format changed!!!!!!! so, this fnctn needs update if to be used again

	///////// some initialization common to previous levels /////////
	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 /*&& theInput != SCAPE*/ && theInput != TOSCA) //= if (hybridRun)
		hybridModelsToSameCoordFrame(mesh2);
	prepareForGeodesicComputation();
	computeGaussianCurvatures();
	sortCurvatures();
	moveCOMtoOrigin();
	mesh2->prepareForGeodesicComputation();
	mesh2->computeGaussianCurvatures();
	mesh2->sortCurvatures();
	mesh2->moveCOMtoOrigin();
	///////// some initialization common to previous levels ends /////////

	cout << "level info being fread: " << levelToLoad << "..\n";

	char fName[150], folder[50];
	sprintf(folder, "subdivide_at_all_times");
	if (theInput == JUMPING)
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\jumping\\%s\\%d-%d level%d.dat", folder, frameID, mesh2->frameID, levelToLoad);
	else
		sprintf(fName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\%s\\%s\\%d-%d level%d.dat", (theInput != DANCING ? "hybrid" : "dancing"), folder, frameID, mesh2->frameID, levelToLoad);
	FILE* fPtr;
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " file not found\n";
		exit(0);
	}

	//first, load/fread r1 and r2, followed by level whose info'is being fread in this file
	int prevLevel;
	fscanf(fPtr, "%f\t%f\t%d\n", &r1, &r2, &prevLevel);
assert(prevLevel == levelToLoad); //be sure to access to the correct file
	//next line is to get min/maxGeoDistG for dSpanningUnn[] normalization
	fscanf(fPtr, "%f\t%f\t%f\t%f\n", &minGeoDistG, &maxGeoDistG, &mesh2->minGeoDistG, &mesh2->maxGeoDistG);

	//second, correspondences to be loaded/fread into safeCorresp for the next level; last two are tmpBaseLevels to be used in computeTmpBaseVerts2()'s shared tmpBase detection (not fprinted 'cos they're always same as currentLevel)
	for (int i = 0; i < (int) verts.size(); i++)
		verts[i]->processed = false; //true means already added to baseVerts
	for (int i = 0; i < (int) mesh2->verts.size(); i++)
		mesh2->verts[i]->processed = false; //true means already added to mesh2.baseVerts
	while (true)
	{
		int bv1, bv2;
		fscanf(fPtr, "%d ", &bv1);
		if (bv1 == -2)
			break; //separator that says corresp info ends
		fscanf(fPtr, "%d\n", &bv2);
		verts[bv1]->tmpBaseLevel = mesh2->verts[bv2]->tmpBaseLevel = levelToLoad;
		verts[bv1]->matchIdx = bv2;
		mesh2->verts[bv2]->matchIdx = bv1;
		verts[bv1]->base = mesh2->verts[bv2]->base = verts[bv1]->safe = mesh2->verts[bv2]->safe = true;
		if (! verts[bv1]->processed) //no duplicates in baseVerts
			baseVerts.push_back(bv1);
		if (! mesh2->verts[bv2]->processed)
			mesh2->baseVerts.push_back(bv2);
		if (! verts[bv1]->processed)
//safeCorresp may involve 5299-1758 & 5302-1758 (many-to-1) but can't involve 1280-555 & 1280-5656 (1-to-many); for 1-to-many, think sth else!!!!!
		{
			safeCorresp.push_back(bv1);
			safeCorresp.push_back(bv2);
		}
		verts[bv1]->processed = true;
		mesh2->verts[bv2]->processed = true;
	}

	//third, get baseNghbhood info
	int bv;
	fscanf(fPtr, "%d\n", &bv); //first line for the base id whose baseNghbhood'll be fread from the subsequent line
	while (bv != -2)
	{
		verts[bv]->baseNghbhood = new vector<int>;
		while (true)
		{
			int v;
			fscanf(fPtr, "%d ", &v);
			if (v == -3)
				break;
			verts[bv]->baseNghbhood->push_back(v);
		}
		//get next base
		fscanf(fPtr, "%d\n", &bv);
	}
	fscanf(fPtr, "%d\n", &bv); //same stuff for mesh2
	while (bv != -2)
	{
		mesh2->verts[bv]->baseNghbhood = new vector<int>;
		while (true)
		{
			int v;
			fscanf(fPtr, "%d ", &v);
			if (v == -3)
				break;
			mesh2->verts[bv]->baseNghbhood->push_back(v);
		}
		//get next base
		fscanf(fPtr, "%d\n", &bv);
	}

	//finally, get initialCorresp
	fscanf(fPtr, "%d\n", &bv); //must be 5, the size of initialCorresp (best 5 chosen by initialCorrespC10_5())
assert(bv == 5);
	initialCorresp = new vector<int>;
	for (int s = 0; s < bv; s++)
	{
		int bv1, bv2;
		fscanf(fPtr, "%d\t%d\n", &bv1, &bv2);
		initialCorresp->push_back(bv1);
		initialCorresp->push_back(bv2);
	}
	fclose(fPtr);
	cout << "done!\n";
}

void Mesh::fillPermutationMtrxs()
{
	//fills permutation mtrxs perms2/3/4/5/6, e.g. perms3[1] = {2,0,1} and it has 3! = 6 such rows/arrays of size 3

	if (perms2 != NULL) //info: if (! perms2) shortcut sucks in x64; so, be explicit
		return; //already called here; so, not refill again to save memo

	//permutations to be read from file to perms*[][] mtrxs
	FILE* fPtr;
	char fName[150];
	//2! permutations in perms2[][]
	int r = 2, factR = factorial(r);
	sprintf(fName, "data\\permutations\\0-start\\%d!.txt", r);
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}
	perms2 = new int*[factR];
	for (int i = 0; i < factR; i++) //r! reorderings/permutations
	{
		perms2[i] = new int[r];
		for (int j = 0; j < r; j++) //each of size r
			fscanf(fPtr, "%d ", &perms2[i][j]);
	}
	fclose(fPtr);

	//3! permutations in perms3[][]
	r = 3;
	factR = factorial(r);
	sprintf(fName, "data\\permutations\\0-start\\%d!.txt", r);
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}
	perms3 = new int*[factR];
	for (int i = 0; i < factR; i++) //r! reorderings/permutations
	{
		perms3[i] = new int[r];
		for (int j = 0; j < r; j++) //each of size r
			fscanf(fPtr, "%d ", &perms3[i][j]);
	}
	fclose(fPtr);

	//4! permutations in perms4[][]
	r = 4;
	factR = factorial(r);
	sprintf(fName, "data\\permutations\\0-start\\%d!.txt", r);
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}
	perms4 = new int*[factR];
	for (int i = 0; i < factR; i++) //r! reorderings/permutations
	{
		perms4[i] = new int[r];
		for (int j = 0; j < r; j++) //each of size r
			fscanf(fPtr, "%d ", &perms4[i][j]);
	}
	fclose(fPtr);

	//5! permutations in perms5[][]
	r = 5;
	factR = factorial(r);
	sprintf(fName, "data\\permutations\\0-start\\%d!.txt", r);
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}
	perms5 = new int*[factR];
	for (int i = 0; i < factR; i++) //r! reorderings/permutations
	{
		perms5[i] = new int[r];
		for (int j = 0; j < r; j++) //each of size r
			fscanf(fPtr, "%d ", &perms5[i][j]);
	}
	fclose(fPtr);

	//6! permutations in perms6[][]
	r = 6;
	factR = factorial(r);
	sprintf(fName, "data\\permutations\\0-start\\%d!.txt", r);
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}
	perms6 = new int*[factR];
	for (int i = 0; i < factR; i++) //r! reorderings/permutations
	{
		perms6[i] = new int[r];
		for (int j = 0; j < r; j++) //each of size r
			fscanf(fPtr, "%d ", &perms6[i][j]);
	}
	fclose(fPtr);

	//7! permutations in perms7[][]
	r = 7;
	factR = factorial(r);
	sprintf(fName, "data\\permutations\\0-start\\%d!.txt", r);
	if (! (fPtr = fopen(fName, "r")))
	{
		cout << fName << " not found\n";
		exit(0);
	}
	perms7 = new int*[factR];
	for (int i = 0; i < factR; i++) //r! reorderings/permutations
	{
		perms7[i] = new int[r];
		for (int j = 0; j < r; j++) //each of size r
			fscanf(fPtr, "%d ", &perms7[i][j]);
	}
	fclose(fPtr);
}

void Mesh::interpolateCoarseCorresp(Mesh* mesh2, bool loadInterpolatedFile)
{
	//interpolate level4's 127 to 134 coarse mapping to 45K to 45K full dense mapping and fwrites it and also appends it to safeCorresp[]; or GMDS's 50 to 50 coarse mapping to 45K to 45K full dense mapping

//	if (theInput != JUMPING && theInput != DANCING && theInput != HORSE && theInput != SHREC11 && theInput != TOSCA) //= if (hybridRun)
		hybridModelsToSameCoordFrame(mesh2); //this call enable-disable affects the result, weirdly

	prepareForGeodesicComputation(); //shortcut edge existence slows down algo due to O((E+V)lgV) dijkstra, e.g. 2min faster w/o shortcuts for dancing and for jumping; but i saw that shortcut'ed output is better; so, keep it
	mesh2->prepareForGeodesicComputation();

	//these move spatial (i.e. non-spectral) center-of-mass to origin which provides a better viewing
	moveCOMtoOrigin();
	mesh2->moveCOMtoOrigin(); //this pair of commands enable-disable not affect the result

	if (loadInterpolatedFile) //don't bother w/ interpolation computations below; just fread the precomputed result (possibly computed on x64 due to its speed)
	{
		cout << "evaluation module never tested and prepared, i.e. file path stuff, for this publicly shared code as the purpose here is to generate correspondences, not to evaluate them; you may easily fix it though if my evaluation fits to yours\n";
		exit(0);

		//fread safeCorresp
		char fInterpName[250];
		if (theInput == TOSCA)
			sprintf(fInterpName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\%d-%d interpolated.dat", frameID, mesh2->frameID);
		else //SHREC11
			sprintf(fInterpName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\%d-%d interpolated.dat", frameID, mesh2->frameID);
		FILE* fInterpPtr = fopen(fInterpName, "r");
		if (! fInterpPtr)
		{
			cout << fInterpName << " not found\n";
			exit(0);
		}

		//C2F output file to learn maxGeoDist stuff
		int level, nBases1 = 0;
		fscanf(fInterpPtr, "%f\t%f\t%d\n", &r1, &r2, &level); //radius on 2 meshes and level
		fscanf(fInterpPtr, "%f\t%f\t%f\t%f\n", &minGeoDistG, &maxGeoDistG, &mesh2->minGeoDistG, &mesh2->maxGeoDistG);

		cout << "min/max geodesic distance around the whole surface [source]: " << minGeoDistG << " / " << maxGeoDistG <<
				"\nmin/max geodesic distance around the whole surface [target]: " << mesh2->minGeoDistG << " / " << mesh2->maxGeoDistG << "\nradii:\t" << r1 << " & " << r2 << endl;

		//bigger of normalized r1 and normalized r2 (not a big deal 'cos both values are close anyway; use the bigger to see smaller costs, both for me and for Zhang)
		normalizedR1 = r1 / maxGeoDistG; //div isometryCost (in [0,1]) by this to get it in terms of r of current (level's) patches; if r = maxGeoDistG (which can't happen but anyway), normalizedR is 1

interestingMatches.clear();
		while (! feof(fInterpPtr))
		{
			int i, ci; //i and corresp(i)
			fscanf(fInterpPtr, "%d\n", &i);
			if (i == -2)
				break;
			fscanf(fInterpPtr, "%d\n", &ci); //2 consecutive lines make 1 match

			safeCorresp.push_back(i);
			safeCorresp.push_back(ci); //getMatchingLinesSepDense() & setCorrespondenceErrsTmp() traverses safeCorresp[]
//interestingMatches.push_back(i);interestingMatches.push_back(ci); //for spheres (top entries are coarse mapping that get interpolated; so see the top ~65 if you want (top 8 if level0 used in interpolation))

			verts[i]->matchIdx = ci; mesh2->verts[ci]->matchIdx = i; //line drawing needs matchIdx values (for many-to-many this is overwritten arbitrarily but anyway)
		
			//correspondence error will be printed as well; so, prepare bases and dSpannings
			dijkstraShortestPaths(i); //dSpanning[]'s now are good 'cos min/maxGeoDistG fread above
			mesh2->dijkstraShortestPaths(ci);
			nBases1++;
//			if (nBases1 % 31 == 0)
//				cout << nBases1-1 << "*2 th dijkstra completed\n";
		} //end of feof	

		return; //no interpolation computations below 'cos i already fread interpolation-based correspondences
	}

	////////////////////////////////////////// fread coarseCorresp //////////////////////////////////////////
	bool c2fCoarseMapping = true; //true: c2f algo's level4's 127 to 134 coarse mapping gets interpolated; false: GMDS's 50 to 50 coarse mapping gets interpolated into full dense map covering all vertices
	char fCoarseName[250];
	int level, nBases1= 0;
	FILE* fCoarsePtr;
	if (c2fCoarseMapping)
	{
/*		if (theInput == TOSCA)
			sprintf(fCoarseName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\%d-%d level3.dat", frameID, mesh2->frameID); //update output file name below if dataset is updated here
		else //SHREC11
			sprintf(fCoarseName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\%d-%d level3.dat", frameID, mesh2->frameID);*/
		sprintf(fCoarseName, "output\\%d-%d level%d.dat", frameID, mesh2->frameID, levelToInterpolate);

//x86 execution time
//level2 = ~30 coarse matched verts on mesh1; so, 30 mesh1 dijkstra, 30 mesh2 dijkstra; and then 30-size vectors compared N^2 times (N = # verts in mesh1 & 2) to match 1 mesh1 vertex; for N = 45K voctoria, 1 dijkstra = 0.3 secs; 1 mesh1 vertex finds its match in 0.3 secs, so all 45K needs 13500 secs = 225 mins = 3.8 hrs; so, 30 ~tractable; try level1
//level3 = ~65 coarse matched verts on mesh1; so, 65 mesh1 dijkstra, 65 mesh2 dijkstra; and then 65-size vectors compared N^2 times (N = # verts in mesh1 & 2) to match 1 mesh1 vertex; for N = 45K voctoria, 1 dijkstra = 0.3 secs; 1 mesh1 vertex finds its match in 1 secs, so all 45K needs 45K secs = 750 mins = 12.5 hrs; so, 65 intractable; try level2
//here x64 execution time
//level2 = ~30 coarse matched verts on mesh1; so, 30 mesh1 dijkstra, 30 mesh2 dijkstra; and then 30-size vectors compared N^2 times (N = # verts in mesh1 & 2) to match 1 mesh1 vertex; for N = 45K voctoria, 1 dijkstra = 0.05 secs (6x faster than x86); 1 mesh1 vertex finds its match in 0.005 (60x faster) secs, so all 45K needs 217 secs = 3.6 mins (60x faster)
//level3 = ~65 coarse matched verts on mesh1; so, 65 mesh1 dijkstra, 65 mesh2 dijkstra; and then 65-size vectors compared N^2 times (N = # verts in mesh1 & 2) to match 1 mesh1 vertex; for N = 45K voctoria, 1 dijkstra = 0.05 secs (6x faster than x86); 1 mesh1 vertex finds its match in 0.007 (142x faster) secs, so all 45K needs 320 secs = 5.3 mins (142x faster) [inUse]

		if (! (fCoarsePtr = fopen(fCoarseName, "r")))
		{
			cout << fCoarseName << " not found to perform interpolation on\n";
			exit(0);
		}
		
		fscanf(fCoarsePtr, "%f\t%f\t%d\n", &r1, &r2, &level); //radius on 2 meshes and level
		fscanf(fCoarsePtr, "%f\t%f\t%f\t%f\n", &minGeoDistG, &maxGeoDistG, &mesh2->minGeoDistG, &mesh2->maxGeoDistG);
		cout << "min/max geodesic distance around the whole surface [source]: " << minGeoDistG << " / " << maxGeoDistG <<
				"\nmin/max geodesic distance around the whole surface [target]: " << mesh2->minGeoDistG << " / " << mesh2->maxGeoDistG << "\nradii:\t" << r1 << " & " << r2 << endl;

		//bigger of normalized r1 and normalized r2 (not a big deal 'cos both values are close anyway; use the bigger to see smaller costs, both for me and for Zhang)
		normalizedR1 = r1 / maxGeoDistG; //div isometryCost (in [0,1]) by this to get it in terms of r of current (level's) patches; if r = maxGeoDistG (which can't happen but anyway), normalizedR is 1

		cout << "coarse corresp being fread into safeCorresp[] to get interpolated.." << endl;
		while (! feof(fCoarsePtr))
		{
			int i, ci; //i and corresp(i)
			fscanf(fCoarsePtr, "%d\t", &i);

			if (i == -2)
				break;
		
			fscanf(fCoarsePtr, "%d\n", &ci);

			safeCorresp.push_back(i);
			safeCorresp.push_back(ci); //getMatchingLinesSepDense() & setCorrespondenceErrsTmp() traverses safeCorresp[]
			verts[i]->base = mesh2->verts[ci]->base = true; //initially/default false base made true here and will be used belw for efficiency only

			//for each source vert, geodesics to all coarse correspondence pnts needed; running dijkstra from each coarse correspondence pnt does this
			dijkstraShortestPaths(i); //dSpanning[]'s now are good 'cos min/maxGeoDistG fread above
			mesh2->dijkstraShortestPaths(ci);
			nBases1++;
//			if (nBases1 % 31 == 0)
//				cout << nBases1-1 << "*2 th dijkstra completed\n";
		} //end of feof
	}
	else
	{
exit(0); //GMDS coarse mapping comes here
	}
	worstMatchI = worstMatchG = -1; //actually, i have this info in the file as well; so this else is totally redundant	
	fclose(fCoarsePtr);
	cout << "mesh1 side of loaded coarseCorresp is of size " << nBases1 << "\ndone!\n\n";
	////////////////////////////////////////// fread coarseCorresp ends //////////////////////////////////////////

	////////////////////////////////////////// interpolation of coarseCorresp //////////////////////////////////////////
	cout << "interpolating coarse corresp into dense one of size ~" << (int) verts.size() << "..\n";
	int m = (int) safeCorresp.size() / 2;
	//for each vert v, create the m-size geo2coarses[] list (m = # of coarse correspoondence verts) where v.geo2coarses[4] gives the geodesic dist from v to 5th coarse corresp matched vert
	for (int v = 0; v < (int) verts.size(); v++)
		for (int s = 0; s < m; s++)
			verts[v]->geo2coarses.push_back(verts[ safeCorresp[2*s] ]->dSpanning[v]); //2s is even and therefore on mesh1; use 2s+1 for correspondence in mesh2
	//fill geo2coarses[] for mesh2 as well to compare them below for dense interpolated matching
	for (int v = 0; v < (int) mesh2->verts.size(); v++)
		for (int s = 0; s < m; s++)
			mesh2->verts[v]->geo2coarses.push_back(mesh2->verts[ safeCorresp[2*s+1] ]->dSpanning[v]); //2s+1 is odd and therefore on mesh2 as the correspondence of 2s in mesh1

	bool mesh1Full = false; //true makes all mesh1 verts get a match, i.e. no unmatched in mesh1, hence many-to-1; false makes all mesh2 verts get a match, hence 1-to-many; use false for my dense coloring scheme compatibilty
	if (mesh1Full)
		//each mesh1 vert v1 is matched to v2 where v2.geo2coarses is closest to v1.geo2coarses; so, many-to-1 is possible
		for (int v1 = 0; v1 < (int) verts.size(); v1++)
		{
			if (verts[v1]->base)
				continue; //already placed in safeCorresp w/ its non-interpolated match
			float minDist = INF;
			int bestV2 = -1;
			for (int v2 = 0; v2 < (int) mesh2->verts.size(); v2++)
			{
				float dist = 0.0f; //dist b/w v1.geo2coarses & v2.geo2coarses vectors
				for (int i = 0; i < m; i++)
					dist += fabs(verts[v1]->geo2coarses[i] - mesh2->verts[v2]->geo2coarses[i]); //i'th entries are compatible as they're geodesic dists to i'th coarse match
				if (dist < minDist)
				{
					bestV2 = v2;
					minDist = dist;
				}
			}

			safeCorresp.push_back(v1);
			safeCorresp.push_back(bestV2);

			if (v1 % 3100 == 0)
				cout << v1 << " / " << (int) verts.size() << "th interpolation completed\n";
		}
	else //almost same except loop headers swap
		//each mesh2 vert v2 is matched to v1 where v1.geo2coarses is closest to v2.geo2coarses; so, 1-to-many is possible
		for (int v2 = 0; v2 < (int) mesh2->verts.size(); v2++)
		{
			if (mesh2->verts[v2]->base)
				continue; //already placed in safeCorresp w/ its non-interpolated match
			float minDist = INF;
			int bestV1 = -1;
			for (int v1 = 0; v1 < (int) verts.size(); v1++)
			{
				float dist = 0.0f; //dist b/w v1.geo2coarses & v2.geo2coarses vectors
				for (int i = 0; i < m; i++)
					dist += fabs(verts[v1]->geo2coarses[i] - mesh2->verts[v2]->geo2coarses[i]); //i'th entries are compatible as they're geodesic dists to i'th coarse match
				if (dist < minDist)
				{
					bestV1 = v1;
					minDist = dist;
				}
			}

			safeCorresp.push_back(bestV1);
			safeCorresp.push_back(v2);

//			if (v2 % 3100 == 0)
//				cout << v2 << " / " << (int) mesh2->verts.size() << "th interpolation completed\n";
		}
	cout << "interpolated dense corresp is of size " << (int) safeCorresp.size() / 2 << "\ndone!\n\n";

	//fwrite safeCorresp in the format of *level*.dat files but simpler, i.e. no worst stuff and baseNghbhood stuff, that is nothing after '-2' in levelInfoToFile()
	char fOutName[250];
/*	if (theInput == TOSCA)
		sprintf(fOutName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\TOSCA\\%d-%d interpolated.dat", frameID, mesh2->frameID);
	else //SHREC11
		sprintf(fOutName, "..\\..\\..\\..\\code\\output\\ShapeCorresponderV2_x64\\dense\\SHREC11\\linux\\%d-%d interpolated.dat", frameID, mesh2->frameID);*/
	sprintf(fOutName, "output\\%d-%d interpolated.dat", frameID, mesh2->frameID);
	FILE* fOutPtr = fopen(fOutName, "w");

	//first, stuff to be loaded/fread as r1 and r2 for the next level, followed by level whose info'll be fprinted in this file
	fprintf(fOutPtr, "%f\t%f\t%d\n", r1, r2, 999/*level*/); //no level info 'cos this is an interpolated output
	fprintf(fOutPtr, "%f\t%f\t%f\t%f\n", minGeoDistG, maxGeoDistG, mesh2->minGeoDistG, mesh2->maxGeoDistG); //may be needed when evaluating this output

	//second, correspondences to be loaded/fread into safeCorresp for a later time
	for (int s = 0; 2*s+1 < (int) safeCorresp.size(); s++)
		fprintf(fOutPtr, "%d\t%d\n", safeCorresp[2*s], safeCorresp[2*s + 1]);
	fprintf(fOutPtr, "-2\n"); //section separator is -2
	////////////////////////////////////////// interpolation of coarseCorresp ends //////////////////////////////////////////
}
