#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*/
}

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

	//shortcut sometimes crash below on x64 release mode only; better not use them at all
	if (! shortcutEdges || verts.size() > 40000) //40K+ are dense meshes anyway; so, need for path smoothings
		return; //no shortcut 'cos it slows down the base computation as well as dijkstra (but shortcut'ed more accurate; so, do it)

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

	minSedgeLen = INF;
	maxSedgeLen = -INF;
	sedgeLenTotal = 0.0f;
	for (unsigned int t = 0; t < tris.size(); t++)
	{
		//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;
}

//////////// 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 /////////////////

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
		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;
}
inline float Mesh::computeRegionArea(vector< int > regionVertsB)
{
	//computes the area represented by regionVertsB

	for (int t = 0; t < (int) tris.size(); t++)
		tris[t]->usedInPatchAreaComputation = false;
	float area = 0.0f;
	for (int i = 0; i < (int) regionVertsB.size(); i++)
		for (int tn = 0; tn < (int) verts[ regionVertsB[i] ]->triNeighbors.size(); tn++)
			if (! tris[ verts[ regionVertsB[i] ]->triNeighbors[tn] ]->usedInPatchAreaComputation)
			{
				area += tris[ verts[ regionVertsB[i] ]->triNeighbors[tn] ]->area;
				tris[ verts[ regionVertsB[i] ]->triNeighbors[tn] ]->usedInPatchAreaComputation = true; //not add it again to area due to another v neighbor to this t
			}
	return area;
}
inline float maxOf(float a, float b) { return (a > b ? a : b); }
inline float minOf(float a, float b) { return (a < b ? a : b); }

int Mesh::computeBaseVerts(bool COESforDenseSampling, bool localMaxMinMu, bool localMaxMinCurv)
{
	//sampling of baseVerts (localMaxMinMu/Curv=true selects local maxima and minima of mu/curvature values, hence no evenly-spacing there; other cases evenly-spaced though)

	//# of bases will be decided by r
	float wholeSurfaceArea = 0.0f, r;
	for (int t = 0; t < (int) tris.size(); t++)
		wholeSurfaceArea += tris[t]->area;

	//////////////////////// intermediate ~20 evenly-spaced bases to be used in mu computation (no COES, no mu-based, just arbitrary selection in getBaseVertIdx()) ////////////////////////
	if (! COESforDenseSampling) //simply skip to the r = 0.2f * sqrt(wholeSurfaceArea / PI); part of this function if n = ~50 bases are to be sampled via COES
	{
#ifdef DBG_DETAIL
		cout << "\n~" << (localMaxMinMu ? 40 : 20) << " bases to be computed for upcoming mu-computations..\n"; //called from initial2Matches()
#endif
		r = 3.0f * (float) sqrt(wholeSurfaceArea*0.005f); //gives ~20 baseVerts for mu values; approx mu values good enough for evenly-spaced extremity detection
		if (localMaxMinMu) //~40 baseVerts for even more accurate mu values which are crucial for localMaxMinMu case
			r = 0.158f * sqrt(wholeSurfaceArea / PI) * 1.8f; //A = 40 pi r^2 ==> r = 0.1 sqrt(A/pi) gives 40 on a disk/patch; *~2 to make 40 on a closed surface (as 2 same patches connected to make the closed surface)

		//bases that are at least r apart from each other
		computeBaseVerts(r);

#ifdef DBG_DETAIL
		cout << "done\n"; //called from initial2Matches()
#endif

		//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]);
		cout << "Done!\n\n";

		//no normalization but learn the min/maxGeoDistG for printing
		for (int i = 0; i < (int) baseVerts.size(); i++)
			for (int v = 0; v < (int) verts.size(); v++)
			{
				if (verts[v]->disconnected) //disconnect verts on seahorse are ignored (detect disconnected verts initially by not connected by a path prints below; the ones that prints few verts are from small disconnected part)
					continue;

				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 (verts[ baseVerts[i] ]->dSpanningUnn[ v ] == INF){cout<<baseVerts[i]<< " " << v << " not connected by a path\n";}
			}
		cout << "min/max geodesic on the surface (not used for normalization): " << minGeoDistG << " / " << maxGeoDistG << endl;
if (maxGeoDistG == INF) {cout << "disconnected vert(s) should be canceled\n";exit(0);} //guarantees that mesh is not disconnected (especially manually cut meshes can be bad); i may not crush if bases i'll use not hit these disconnected ones but be safe and stop here

		//compute mu for extremities
		for (int i = 0; i < (int) baseVerts.size(); i++)
			verts[ baseVerts[i] ]->patchArea = computePatchArea(baseVerts[i], r); //to be used in mu computation below
		//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 geodesics from v to all verts) being computed....\n";
		avgMu = 0.0f;
		int nConnected = 0;
		for (int v = 0; v < (int) verts.size(); v++)
		{
			if (verts[v]->disconnected) //disconnect verts on seahorse are ignored
				continue;

			for (int i = 0; i < (int) baseVerts.size(); i++)
				verts[v]->Mu += verts[ baseVerts[i] ]->dSpanningUnn[v] * verts[ baseVerts[i] ]->patchArea; //patchArea weight
			//not normalizing v.Mu w/ (int) baseVerts.size() to save time 'cos Mu values across meshes not to be compared; i'll use these values just to compute local maxima/minima on this shape for which normalization is redundant
			avgMu += verts[v]->Mu;
			nConnected++;
		}
		avgMu /= nConnected;
		cout << "done!\n\n";
	}
	//////////////////////// intermediate ~20 evenly-spaced bases to be used in mu computation (no COES, no mu-based, just arbitrary selection in getBaseVertIdx()) ends ////////////////////////

	cout << "recomputing bases to be pruned and then to be matched..\n";
	if (localMaxMinMu || localMaxMinCurv)
	{
		/////////////////////////////// nonevenly spaced bases to be pruned and then to be matched ///////////////////////////////
		//recompute baseVerts but now no evenly-spaced constraint; pick the extremities (regardless of their distance to each other, e.g. may be too close); to keep this set small use only the local maximas w.r.t. mu values,
		//which is achieved by making v a baseVert iff v.mu is bigger than all w.mu for w's in v's 1-ring nghbhood
		for (int i = 0; i < (int) baseVerts.size(); i++)
		{
			verts[ baseVerts[i] ]->base = false;
			if (verts[ baseVerts[i] ]->baseNghbhood)
			{
				delete verts[ baseVerts[i] ]->baseNghbhood;
				verts[ baseVerts[i] ]->baseNghbhood = NULL;
			}

			delete verts[ baseVerts[i] ]->dSpanningUnn;
			verts[ baseVerts[i] ]->dSpanningUnn = NULL; //so that i recompute dSpanning of these bases (that taught me mu values and are exposed to pruning) on original, i.e. not smoothed (coords2), mesh if needed later
		}

		if (localMaxMinCurv)
			computeGaussianCurvatures(); //gc values to be used below ready

		baseVerts.clear(); //'cos new baseVerts'll be created below w.r.t. sorted mu values	
		float maxMaximaMu = -1.0f, minMinimaMu = avgMu*(int) verts.size()*100; //maximaMu vert w/ the max mu value (most extruded) will be remembered to be protected from pruning; similarly for minimaMu vert w/ the min mu value (most centered)
		int maxMaximaMuIdx = -1, minMinimaMuIdx = -1;
		for (int v = 0; v < (int) verts.size(); v++)
		{
			if (verts[v]->disconnected) //disconnected (seahorse) verts cannot even be considered as maxima/minima bas candidates
				continue;

			bool localMaximaMu = true, localMinimaMu = true, //localMaxima/MinimaMu = true if its mu is bigger/smaller than all adjacent verts below
				 localMaximaCurv = true, localMinimaCurv = true; //localMaxima/MinimaMu = true if its gaussian curvature is bigger/smaller than all adjacent verts below
			//look at each adjacent vertices of v
			set< int, std::less< int > >::const_iterator neighbVertIdx;
			if (localMaxMinMu) //for mu, integral geodesic distance function
			{
				for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++) //localMaxima
					if (verts[v]->Mu < verts[ (*neighbVertIdx) ]->Mu)
					{
						localMaximaMu = false;
						break;
					}
				for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++) //localMinima
					if (verts[v]->Mu > verts[ (*neighbVertIdx) ]->Mu)
					{
						localMinimaMu = false;
						break;
					}
			}
			if (localMaxMinCurv) //for gaussian curvature
			{
				for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++) //localMaxima
					if (verts[v]->gc < verts[ (*neighbVertIdx) ]->gc)
					{
						localMaximaCurv = false;
						break;
					}
				for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++) //localMinima
					if (verts[v]->gc > verts[ (*neighbVertIdx) ]->gc)
					{
						localMinimaCurv = false;
						break;
					}
			}

			if (localMaxMinMu) //for mu, integral geodesic distance function
			{
				if (localMaximaMu || localMinimaMu)
				{
					//if (localMaximaMu) //enable this to add only maxima
					//if (localMinimaMu) //enable this to add only minima (both disabled means add both local maxima and minima)
					{
						baseVerts.push_back(v);
						verts[v]->base = true;
						//below redundant but anyway			
						verts[v]->baseCandidate = false;
						verts[v]->idxInBaseVerts = (int) baseVerts.size()-1;
						//verts[v]->baseNghbhood = new vector<int>;
						dijkstraShortestPaths(v); //pruneBases2() needs dSpanningUnn
					}
					verts[v]->lMaxMinMu = (localMaximaMu ? 1 : 2); //pruneBases()'ll act different for local maxima and minima mu base sources

					//most extruded localMaximaMu vert detection
					if (verts[v]->lMaxMinMu == 1 && verts[v]->Mu > maxMaximaMu)
					{
						maxMaximaMu = verts[v]->Mu;
						maxMaximaMuIdx = v;
					}
					//most centered localMinimaMu vert detection
					if (verts[v]->lMaxMinMu == 2 && verts[v]->Mu < minMinimaMu)
					{
						minMinimaMu = verts[v]->Mu;
						minMinimaMuIdx = v;
					}
				}
			}
			if (localMaxMinCurv) //for gaussian curvature
			{
				if (localMaximaCurv || localMinimaCurv)
				{
					if (localMaximaCurv) //enable this to add only maxima
					//if (localMinimaCurv) //enable this to add only minima (both disabled means add both local maxima and minima)
					{
						if (baseVerts[ (int) baseVerts.size() - 1] != v) //localMaxMinMu already added this above, so prevent duplicates in baseVerts
						{
							baseVerts.push_back(v);
							verts[v]->base = true;
							//below redundant but anyway			
							verts[v]->baseCandidate = false;
							verts[v]->idxInBaseVerts = (int) baseVerts.size()-1;
							//verts[v]->baseNghbhood = new vector<int>;
							dijkstraShortestPaths(v); //pruneBases2() needs dSpanningUnn
						}
					}
					verts[v]->lMaxMinCurv = (localMaximaCurv ? 1 : 2); //pruneBases()'ll act different for local maxima and minima curvature base sources
				}
			}
		/////////////////////////////// nonevenly spaced bases to be pruned and then to be matched ends ///////////////////////////////
		}
		char* str = (localMaxMinMu && ! localMaxMinCurv ? "mu" : (! localMaxMinMu && localMaxMinCurv ? "curvature" : "mu & curvature"));
		cout << "# of local maxima & minima " << str << " bases w/o pruning: " << (int) baseVerts.size() << ", for " << (int) verts.size() << " verts\n\n";
		if (! scaleInvDemo)
		{
			verts[maxMaximaMuIdx]->maxMaximaMu = true;
			verts[minMinimaMuIdx]->minMinimaMu = true;

		//cluster the close ones to the best one, i.e prune out the nonbests
//			pruneBases((theInput == JUMPING || theInput == DANCING || theInput == JUMPING_DANCING || theInput == WATERTIGHT || theInput == CENTAUR_HORSE || theInput == SEAHORSE_HORSE ||
//						theInput == SEAHORSE_SHARK || theInput == CENTAUR_DAVID || theInput == CENTAUR_GIRL || (theInput == GORILLA_GIRL && targetMesh) ? 12 : 36));//pruneBases() used for results in progress report4
/*			if (theInput == HORSE_TOSCA || theInput == JUMPING || theInput == DANCING || theInput == WATERTIGHT) //SCALE_INV_DISTORTION_DEMO inputs
				pruningDistDenom = 10;
			else if (theInput == CENTAUR_HORSE)
				pruningDistDenom = (targetMesh ? 8 : (frameID == 4 ? 5 : 14)); //centaur always divided by 14 to be consistent w/ other datasets; centaur4 is an exception w/ fewer bases
			else if (theInput == CENTAUR_DAVID)
			{
				pruningDistDenom = (targetMesh ? 14 : (frameID == 4 ? 5 : 14)); //centaur always divided by 14 to be consistent w/ other datasets; centaur4 is an exception w/ fewer bases
				if (targetMesh && (frameID == 7 || frameID == 11))
					pruningDistDenom = 3; //david7/11 has too many samples w/ 14
			}
			else if (theInput == CENTAUR_GIRL)
			{
				pruningDistDenom = (targetMesh ? 14 : (frameID == 4 ? 5 : 14)); //centaur always divided by 14 to be consistent w/ other datasets; centaur4 is an exception w/ fewer bases
				if (targetMesh && frameID == 11)
					pruningDistDenom = 6; //vic11 has too many samples w/ 14
			}
			else if (theInput == SHREC11)
			{
				//max geo dists are not from toe to thumb in these part models; so, max geo dists are not compatible and therefore denom must be selected individually for each of 5 part models
				if (frameID == 101)
					pruningDistDenom = 14;
				else if (frameID == 103)
					pruningDistDenom = 10;
				else if (frameID == 104)
					pruningDistDenom = 12;
				else if (frameID == 105 || frameID == 102)
					pruningDistDenom = 11;
				else if (frameID > 51 && frameID < 56) //scaling (51-55)
					pruningDistDenom = (frameID == 53 ? 15 : 17);
				else if (frameID == 0) //null shape
					pruningDistDenom = (targetMesh ? 13 : 10);
				else //isometry class (11-15)
					pruningDistDenom = (frameID == 13 ? 5 : 13);
			}
			else if (theInput == SEAHORSE_HORSE)
				pruningDistDenom = (targetMesh ? 8 : 10); //horse always divided by 8 to be consistent w/ else above
			else if (theInput == HORSE || theInput == SCAPE)
				pruningDistDenom = 8; //horse always divided by 8 to be consistent w/ else above
			else if (theInput == GORILLA_GIRL)
				//pruningDistDenom = (targetMesh ? 13 : 21);//(frameID == 1 || frameID == 20 ? 17 : (frameID == 0 || frameID == 12 ? 22 : 19))); //use 21 for gorilla12
				pruningDistDenom =  (targetMesh ? 13 : (frameID == 1 || (frameID > 12 && frameID < 21) ? 17 : (frameID > 1 && frameID < 12 ? 19 : (frameID == 12 ? 21 : 22))));
									//(targetMesh ? 13 : (frameID == 1 ? 17 : frameID == 14 ? 23 : 20)); //gorilla 1-14-18-19 delLater
			else
				pruningDistDenom = 6;*/
			pruneBases2(maxGeoDistG / pruningDistDenom, false);
if (maxGeoDistG == INF) cout << "wrong maxGeoDistG=INF used for pruning!!!!!!!!!!! due to disconnected components; rerun w/ manual pruneBases2 parameter!!!!!!\n\n\n\n\n";

/*			if (! targetMesh && theInput == SEAHORSE_HORSE && frameID == 0) //add the 3rd tail sample on seahorse which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(1196);
			else if (! targetMesh && theInput == SEAHORSE_HORSE && frameID == 2) //add the 3rd tail sample on seahorse which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(2148);
			else if (! targetMesh && theInput == SEAHORSE_HORSE && frameID == 1) //add the 3rd tail sample on seahorse which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(569);
			else if (theInput == SHREC11 && frameID == 11) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(2310);
			else if (theInput == SHREC11 && frameID == 14) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(48864);
			else if (theInput == SHREC11 && frameID == 12) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(30311);
			else if (theInput == SHREC11 && frameID == 13) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(25155);
			else if (theInput == SHREC11 && frameID == 15) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(5390);
			else if (theInput == SHREC11 && frameID == 0) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(4863);
			else if (theInput == SHREC11 && frameID == 101) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(3170);
			else if (theInput == SHREC11 && frameID == 51) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(22264);
			else if (theInput == SHREC11 && frameID == 52) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(22349);		
			else if (theInput == SHREC11 && frameID == 53) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(36414);
			else if (theInput == SHREC11 && frameID == 103) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(11597);
			else if (theInput == SHREC11 && frameID == 54) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(14128);
			else if (theInput == SHREC11 && frameID == 55) //add the head sample on man which doesn't appear on some models due to noise and/or unlucky precision err
				baseVerts.push_back(17144);*/
		}
		else
		{
			//1) prune maxima/minima bases so that maximaMu verts whose mu is lower than avgMu are excluded and minimaMu verts whose mu > avgMu are excluded; this makes maxima's really at tips, and minimas really at centers
			for (int i = 0; i < (int) baseVerts.size(); i++)
				if ((verts[ baseVerts[i] ]->lMaxMinMu == 1 && verts[ baseVerts[i] ]->Mu < avgMu) || //maximaMu vert and has mu lower than avgMu
					(verts[ baseVerts[i] ]->lMaxMinMu == 2 && verts[ baseVerts[i] ]->Mu > avgMu))	//minimaMu vert and has mu higher than avgMu (almost never happens 'cos minimas are very few in numbers as compared to # of maximas)
					verts[ baseVerts[i] ]->base = false; //loses its base membership
			for (int i = 0; i < (int) baseVerts.size(); i++)
				if (! verts[ baseVerts[i] ]->base) //marked to be removed/erased
				{
					verts[ baseVerts[i] ]->baseCandidate = true; //redundant
					//if (verts[ baseVerts[i] ]->baseNghbhood) delete verts[ baseVerts[i] ]->baseNghbhood; baseNghbhood=NULL; //redundant
					verts[ baseVerts[i] ]->idxInBaseVerts = -1; //redundant
					baseVerts.erase(baseVerts.begin() + i); //erase(begin + 4) erases [4]
					i--; //handle vector shifting
				}
			for (int i = 0; i < (int) baseVerts.size(); i++) //go through the new baseVerts to update idxInBaseVerts values
				verts[ baseVerts[i] ]->idxInBaseVerts = i; //redundant

			//2) prune maxima/minima bases so that i've a fixed number (k) of fps (farthest point sampling) samples on each mesh
			int k = (int) minOf(10.0f, (float) baseVerts.size());
			//prepare k fps extremities in tmpK
			vector< int > tmpK;
			int m = (int) baseVerts.size();			
			//first pick the most-extreme one w.r.t. maxima of mu
			float maxMu = -INF;
			int maxMuIdx = -1;
			for (int i = 0; i < (int) baseVerts.size(); i++)
				if (verts[ baseVerts[i] ]->Mu > maxMu)
				{
					maxMu = verts[ baseVerts[i] ]->Mu;
					maxMuIdx = baseVerts[i];
				}
			for (int v = 0; v < (int) verts.size(); v++)
				verts[v]->processed = false; //initialize this to prevent duplications in tmpK; true means added to tmpK
			tmpK.push_back(maxMuIdx);
			verts[maxMuIdx]->processed = true; //to prevent duplications in tmpK; true means added to tmpK
			//second, fill the remaining k-1 items via fps for uniform distribution that lets nice coverage on models
			//recall fpsOnMaximaMinima = true makes fps algo select from the pool of baseVerts which have local maxima/minima mu verts only; false makes fps algo select from the pool of all verts, hence even better coverage but likely to miss salient maxima/minima points at tips/centers
			while ((int) tmpK.size() < k)
			{
				//in fps, the next sample candidate x_i \in all vertices finds its closest existing sample x_clo and remembers d_i = d(x_i, x_clo); if d_i is the max among all other candidates closest remembered dists, then x_i is selected as the next sample;
				//here my candidates are restricted to baseVerts only; so, find the closest existing base e2 \in tmpK to bv2 \in baseVerts and add bv2 to tmpK as nextSample if that closest dist is very big, i.e. the biggest among all closest dists for different bv2's
				float maxDist = -INF;
				int nextSample = -1;
				if (fpsOnMaximaMinima)
					for (int s = 0; s < (int) baseVerts.size(); s++)
					{
						int bv2 = baseVerts[s], e2 = -1;
						if (verts[bv2]->processed)
							continue; //no chance to add bv2 to iBaseVerts 'cos it's already added
						float minDist = verts[ tmpK[0] ]->dSpanningUnn[bv2]; //=INF is tricky 'cos objects may be scaled to those high coordinates			
						for (int e = 0; e < (int) tmpK.size(); e++) //find the closest existing base e \in tmpK to bv2 \in baseVerts
							if (verts[ tmpK[e] ]->dSpanningUnn[bv2] <= minDist)
							{
								minDist = verts[ tmpK[e] ]->dSpanningUnn[bv2];
								e2 = tmpK[e];
							}
						if (verts[e2]->dSpanningUnn[bv2] > maxDist)
						{
							maxDist = verts[e2]->dSpanningUnn[bv2];
							nextSample = bv2;
						}
					} //end of for s
				else //fps selects from amongst all vertices (same as i-block just-above except s is used instead of bv2 and verts[] instead of baseVerts[])
					for (int s = 0; s < (int) verts.size(); s++)
					{
						int e2 = -1;
						if (verts[s]->processed)
							continue; //no chance to add s to iBaseVerts 'cos it's already added
						float minDist = verts[ tmpK[0] ]->dSpanningUnn[s]; //=INF is tricky 'cos objects may be scaled to those high coordinates			
						for (int e = 0; e < (int) tmpK.size(); e++) //find the closest existing base e \in tmpK to bv2 \in baseVerts
							if (verts[ tmpK[e] ]->dSpanningUnn[s] <= minDist)
							{
								minDist = verts[ tmpK[e] ]->dSpanningUnn[s];
								e2 = tmpK[e];
							}
						if (verts[e2]->dSpanningUnn[s] > maxDist)
						{
							maxDist = verts[e2]->dSpanningUnn[s];
							nextSample = s;
						}
					} //end of for s
if (nextSample == -1) {cout << "can't happen in fps\n"; exit(0);}
				tmpK.push_back(nextSample);
				verts[nextSample]->processed = true; //added, so not add again later
				if (! fpsOnMaximaMinima)
					dijkstraShortestPaths(nextSample); //upcoming iteration needs tmpK.dSpanningUnn which may not be ready when selecting from the pool of all verts
			} //end of while k
			//get rid of existing bases that did not make into tmpK, i.e. processed=false; redundant but anyway
			for (int i = 0; i < (int) verts.size(); i++)
				if (! verts[i]->processed)
				{
					verts[i]->base = false;
					verts[i]->baseCandidate = true;
					verts[i]->idxInBaseVerts = -1;
				}
			//k fps samples are the new bases to be used in isometricPartMatching()
			baseVerts = tmpK;
		}

		//dSpanningUnn filled above (to be used in pruneBases2()) are deleted so that i can refill them later w/ original/unsmoothed coords later
		for (int i = 0; i < (int) baseVerts.size(); i++)
		{
cout << "base = " << baseVerts[i] << "\t\t" << verts[ baseVerts[i] ]->Mu << "\t" << avgMu << " maxima? = " << (verts[ baseVerts[i] ]->lMaxMinMu == 1) << endl;
			delete verts[ baseVerts[i] ]->dSpanningUnn;
			verts[ baseVerts[i] ]->dSpanningUnn = NULL; //so that i recompute dSpanning of these bases (that taught me mu values and are exposed to pruning) on original, i.e. not smoothed (coords2), mesh if needed later
		}

		///////////////// roll back to original mesh (smoothed version used just for better local max/minima above) and prune extra bases /////////////////
		if (laplSmoothing)
		{
			//note that current edgeLenghts are for smooth coords (used to decide local max/min baseVerts); roll back to original coords and edgeLengths from now on
			for (int i = 0; i < (int) verts.size(); i++)
				for (int c = 0; c < 3; c++)
					verts[i]->coords[c] = verts[i]->coords2[c];
			updateEdgeLengths(); //original edgeLengths to be used by dijkstraShortestPaths() below
			updateSedgeLengths(); //original shortcut edgeLengths to be used by dijkstraShortestPaths() below
			updateNormals();
		}
		cout << "original Dijkstra from each baseVert being computed....\n"; //new bases to be matched (via original edgeLengths) (for SCALE_INV_DISTORTION_DEMO, the fps subset of samples'll be matched but anyway; find dSpanningUnn[][] for all candidates)
		for (int i = 0; i < (int) baseVerts.size(); i++)
			dijkstraShortestPaths(baseVerts[i]);
		cout << "Done!\n\n";
		///////////////// roll back to original mesh (smoothed version used just for better local max/minima above) and prune extra bases ends /////////////////
	}
	else
	{
		/////////////////////////// if called from initial2Matches(): intermediate ~10 mu-based evenly-spaced bases to be used in initial2Matches(), i.e. C(~10, 2)*C(~10, 2)*2! trials ///////////////////////////
		/////////////////////////// or if called from initial50Matches(): final ~50 COES evenly-spaced bases to be matched ///////////////////////////
		if (! COESforDenseSampling)
		{
			//recompute ~10 evenly-spaced baseVerts at high-mu (extremity) points	
			r = 0.6f * sqrt(wholeSurfaceArea / PI); //0.6 for ~5 bases on a disk due to our ad-hoc formula; on a closed mesh, it should give 2*5=10

#ifdef DBG_DETAIL
		cout << "~10 bases to be computed for upcoming initial 2 matches selection..\n"; //called from initial2Matches()
#endif
		}
		else
		{
			r = 0.2f * sqrt(wholeSurfaceArea / PI); //recompute ~50 evenly-spaced baseVerts at high-curvature points (COES)
#ifdef DBG_DETAIL
		cout << "~50 bases to be computed for upcoming and desired ~50 match creation..\n"; //called from initial50Matches()
#endif
		}

		//first get rid of the existing bases that have been used for mu computation	
		vector< int > tmp = baseVerts;
		for (int i = 0; i < (int) baseVerts.size(); i++)
		{
			verts[ baseVerts[i] ]->base = false;
			if (verts[ baseVerts[i] ]->baseNghbhood)
			{
				delete verts[ baseVerts[i] ]->baseNghbhood; //mandatory 'cos if not done, then i try to delete it in computeBaseVerts() below if the same vert is selected again as base
				verts[ baseVerts[i] ]->baseNghbhood = NULL;
			}
			delete verts[ baseVerts[i] ]->dSpanningUnn;
			verts[ baseVerts[i] ]->dSpanningUnn = NULL; //so that i recompute dSpanning of these bases (that taught me mu values) on original, i.e. not smoothed (coords2), mesh if needed later			
		}
		
		baseVerts.clear();
		for (int i = 0; i < (int) verts.size(); i++)
		{
			verts[i]->base = false;
			verts[i]->baseCandidate = true;
			verts[i]->idxInBaseVerts = -1;
		}
		//sample bases that are at least r apart from each other
		if (! COESforDenseSampling)
			sortMus(); //this makes muOrder not NULL and hence getBaseVertIdx() uses mu-based evenly spaced sampling
		else
		{
			computeGaussianCurvatures(); //gc values ready
			//before sorting w.r.t. gc values, deliberately overwrite gc's of the existing mu-based extremity bases to let them get selected as new COES bases first; then, fill the remaining slots w.r.t. curvatures
			for (int i = 0; i < (int) tmp.size(); i++)
				verts[ tmp[i] ]->gc = INF;
			sortCurvatures(); //this makes gcOrder not NULL and hence getBaseVertIdx() uses COES-based evenly spaced sampling
		}
		computeBaseVerts(r); //baseVerts refilled w/ ~10 bases (initial2Matches()) or ~50 (initial50Matches())
#ifdef DBG_DETAIL
		cout << "done\n"; //called from initial2/50Matches()
#endif
		cout << "original Dijkstra from each to-be-matched baseVert being computed....\n"; //new bases to be matched
		for (int i = 0; i < (int) baseVerts.size(); i++)
			dijkstraShortestPaths(baseVerts[i]);
		cout << "Done!\n\n";
		radius = r; //to be used later in computeBaseNeighbs()
		/////////////////////////// if called from initial2Matches(): intermediate ~10 mu-based evenly-spaced bases to be used in initial2Matches(), i.e. C(~10, 2)*C(~10, 2)*2! trials ends ///////////////////////////
		/////////////////////////// or if called from initial50Matches(): final ~50 COES evenly-spaced bases to be matched ends ///////////////////////////
	}

	return (int) baseVerts.size();
}
int Mesh::computeAdditionalBases()
{
	//heavily adapted from computeBaseVerts(); evenly-spaced sampling of additional COES baseVerts

	//# of bases will be decided by r
	float wholeSurfaceArea = 0.0f, r;
	for (int t = 0; t < (int) tris.size(); t++)
		wholeSurfaceArea += tris[t]->area;

#ifdef DBG_DETAIL
		cout << "\n~40 additional bases to be computed for upcoming matchBasesVia*()..\n";
#endif

	//r = 0.6f * sqrt(wholeSurfaceArea / PI) * 2.0f; //our original formula to give ~5 bases during c2f algo (SGP) is 0.6f * sqrt(wholeSurfaceArea / PI) and not good here 'cos wholeSurfaceArea here is a closed mesh unlike disk/patch used in that formula; so, *2 needed for ~5
	r = 0.1f * sqrt(wholeSurfaceArea / PI) * 1.7f; //A = 80 pi r^2 ==> r = 0.1 sqrt(A/pi) gives 80 on a disk/patch; *~2 to make 80 on a closed surface (as 2 same patches connected to make the closed surface)

	//first, mark the nghborhoods/patches of the existing bases as unavailable, i.e. baseCandidate=false, to ensure evenly-spaced sampling
	for (int v = 0; v < (int) verts.size(); v++)
	{
		verts[v]->baseCandidate = true;
		verts[v]->base = false;
	}	
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		verts[ baseVerts[i] ]->base = true;
		for (int v = 0; v < (int) verts.size(); v++)
			if (verts[ baseVerts[i] ]->dSpanningUnn[v] <= r) //v is a close vertex i want to insert to sourceVert.patch, i.e. make it unavailable to be selected as base later
				verts[v]->baseCandidate = false; //aim of this fnctn; this includes sourceVert itself (dSpanningUnn = 0 for that case)
				//verts[ baseVerts[i] ]->baseNghbhood->push_back(v); no need to maintain patch info (did it just to compute mu values but no need at this point)
	}
	int nOriginalBases = (int) baseVerts.size(); //# of original bases before new additional ones added below

	//second, create the new/additional COES samples using the baseCandidate=true high-curvature spots of the mesh
	computeGaussianCurvatures(); //gc values ready
	sortCurvatures(); //this makes gcOrder not NULL and hence getBaseVertIdx() uses COES-based evenly spaced sampling		
	//bases that are at least r apart from each other
	computeBaseVerts(r);

#ifdef DBG_DETAIL
	cout << "done\n"; //called from initial2Matches()
#endif

	cout << "original Dijkstra from each baseVert (after additionals) being computed....\n";
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		dijkstraShortestPaths(baseVerts[i]);
		verts[ baseVerts[i] ]->additionalBase = (i >= nOriginalBases); //additionals start right after the originals
	}
	cout << "Done!\n\n";

	return (int) baseVerts.size();
}

void Mesh::pruneBases(int nRing)
{
	//prunes current baseVerts by checking nRing-ring nghbhood of each base and clustering all checked bases to the best one (i.e. biggest Mu) (used for results in progress report4 and not in use at this time)

	cout << "pruning baseVerts....\n";

	//run BFS from i'th baseVert until a far away vert, i.e. at (nRing+1)-ring nghbhood, is reached
	////////////////////////////// for localMaxMinMu //////////////////////////////
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int source = baseVerts[i];
		if (! verts[source]->base)
			continue; //already clustered into a better base during a prev iter, so not casre this ex-base (will be thrown away from baseVerts after this loop)

		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		
		bool farVertReached = false;
		queue< int > q;
		q.push(source);
		verts[source]->d = 0; //length of unweighted shortest path from source to source is 0
		int v;
		while (! q.empty() && ! farVertReached)
		{
			v = q.front();
			q.pop();

			farVertReached = 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)
				{
					//one of base source and base neighbVertIdx will be canceled, i.e. clustered in to the better one
					if (verts[source]->lMaxMinMu == 1) //source is marked as a local maxima mu
					{
						if (verts[source]->Mu > verts[(*neighbVertIdx)]->Mu)
							verts[(*neighbVertIdx)]->base = false; //neighbVertIdx loses its base membership since source is better, i.e. more extreme
						else
						{
							verts[source]->base = false; //source loses its base membership since neighbVertIdx is better, i.e. more extreme
							farVertReached = true; //naming sucks but this shortcuts the loop 'cos i'm no longer interested in source which just lost its base membership
						}
					}
					else if (verts[source]->lMaxMinMu == 2) //source is marked as a local minima mu; so, use < test as opposed to > above
					{
						if (verts[source]->Mu < verts[(*neighbVertIdx)]->Mu)
							verts[(*neighbVertIdx)]->base = false; //neighbVertIdx loses its base membership since source is better, i.e. more extreme
						else
						{
							verts[source]->base = false; //source loses its base membership since neighbVertIdx is better, i.e. more extreme
							farVertReached = true; //naming sucks but this shortcuts the loop 'cos i'm no longer interested in source which just lost its base membership
						}
					}
				}
				q.push((*neighbVertIdx));
			} //end of neighbVertIdx

			if (verts[v]->d > nRing) //reached nRing+1, so i'm far enough
				farVertReached = true;
		} //end of while
	} //end of i
	////////////////////////////// for localMaxMinMu ends //////////////////////////////

	////////////////////////////// for localMaxMinCurv //////////////////////////////
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int source = baseVerts[i];
		if (! verts[source]->base)
			continue; //already clustered into a better base during a prev iter, so not casre this ex-base (will be thrown away from baseVerts after this loop)

		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		
		bool farVertReached = false;
		queue< int > q;
		q.push(source);
		verts[source]->d = 0; //length of unweighted shortest path from source to source is 0
		int v;
		while (! q.empty() && ! farVertReached)
		{
			v = q.front();
			q.pop();

			farVertReached = 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)
				{
					//one of base source and base neighbVertIdx will be canceled, i.e. clustered in to the better one
					if (verts[source]->lMaxMinCurv == 1) //source is marked as a local maxima curvature
					{
						if (verts[source]->gc > verts[(*neighbVertIdx)]->gc)
							verts[(*neighbVertIdx)]->base = false; //neighbVertIdx loses its base membership since source is better, i.e. more extreme
						else
						{
							verts[source]->base = false; //source loses its base membership since neighbVertIdx is better, i.e. more extreme
							farVertReached = true; //naming sucks but this shortcuts the loop 'cos i'm no longer interested in source which just lost its base membership
						}
					}
					else if (verts[source]->lMaxMinCurv == 2) //source is marked as a local minima curvature; so, use < test as opposed to > above
					{
						if (verts[source]->gc < verts[(*neighbVertIdx)]->gc)
							verts[(*neighbVertIdx)]->base = false; //neighbVertIdx loses its base membership since source is better, i.e. more extreme
						else
						{
							verts[source]->base = false; //source loses its base membership since neighbVertIdx is better, i.e. more extreme
							farVertReached = true; //naming sucks but this shortcuts the loop 'cos i'm no longer interested in source which just lost its base membership
						}
					}
				}
				q.push((*neighbVertIdx));
			} //end of neighbVertIdx

			if (verts[v]->d > nRing) //reached nRing+1, so i'm far enough
				farVertReached = true;
		} //end of while
	} //end of i
	////////////////////////////// for localMaxMinCurv ends //////////////////////////////

	//remove all ex-bases (that are marked as base=false above) from baseVerts
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		if (! verts[ baseVerts[i] ]->base) //marked to be removed/erased
		{
			verts[ baseVerts[i] ]->baseCandidate = true; //redundant
			//if (verts[ baseVerts[i] ]->baseNghbhood) delete verts[ baseVerts[i] ]->baseNghbhood; baseNghbhood=NULL; //redundant
			verts[ baseVerts[i] ]->idxInBaseVerts = -1; //redundant
			baseVerts.erase(baseVerts.begin() + i); //erase(begin + 4) erases [4]
			i--; //handle vector shifting
		}
	}
	for (int i = 0; i < (int) baseVerts.size(); i++) //go through the new baseVerts to update idxInBaseVerts values
		verts[ baseVerts[i] ]->idxInBaseVerts = i; //redundant

	cout << "Done! # of bases (after pruning) to be matched: " << (int) baseVerts.size() << ", for " << (int) verts.size() << " verts\n\n";
}
void Mesh::pruneBases2(float dist, bool populateTill10)
{
	//prunes current baseVerts to make all remaining maxima (minima) bases at least dist apart from each other maxima (minima); so, cluster all checked bases to the best one (e.g. biggest Mu for maxima)

	cout << "pruning baseVerts....\n";

	//for each base source, check all other bases within dist distance and cancel base membership of source if the checked one has a bigger mu, i.e. more extreme, if source is a maxima, and smaller mu if it is minima
	////////////////////////////// for localMaxMinMu //////////////////////////////
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int source = baseVerts[i];
//		if (! verts[source]->base)
//			continue; //base=false means already clustered into a better base during a prev iter, so not care this ex-base (will be thrown away from baseVerts after this loop)
					  //no!!!! care it; this bv will be thrown away for sure but let bv try to cancel out bad close bases also 'cos bv may be the bridge that brings me to a bad base close to bv which would otherwise never be reached (for bases w/ bigger mu values, there's no canceling problem anyway; this cancels bad ones whose mu are even less than bv.mu (maxima pruning case))

		for (int j = 0; j < (int) baseVerts.size(); j++)
		{
			int bv = baseVerts[j];
			if (source == bv || verts[source]->dSpanningUnn[bv] > dist) //source and bv are irrelevant, i.e. too distant, so not consider this source-bv pair for clustering
				continue;

			//either source or bv will be canceled, i.e. clustered in to the better one
			if (verts[source]->lMaxMinMu == 1) //source is marked as a local maxima mu
			{
				if (verts[source]->Mu > verts[bv]->Mu && (! verts[bv]->maxMaximaMu && ! verts[bv]->minMinimaMu)) //2nd condition to protect maximaMu (minimaMu) vert w/ the max (min) mu value from being excluded from bases (base=false)
					verts[bv]->base = false; //bv loses its base membership since source is better, i.e. more extreme
				else if (! verts[source]->maxMaximaMu && ! verts[source]->minMinimaMu) //if condition to protect maximaMu (minimaMu) vert w/ the max (min) mu value from being excluded from bases (base=false)
				{
					verts[source]->base = false; //source loses its base membership since bv is better, i.e. more extreme
					break; //shortcuts the loop 'cos i'm no longer interested in source which just lost its base membership
				}
			}
			else if (verts[source]->lMaxMinMu == 2) //source is marked as a local minima mu; so, use < test as opposed to > above
			{
				if (verts[source]->Mu < verts[bv]->Mu && (! verts[bv]->maxMaximaMu && ! verts[bv]->minMinimaMu)) //2nd condition to protect maximaMu (minimaMu) vert w/ the max (min) mu value from being excluded from bases (base=false)
					verts[bv]->base = false; //bv loses its base membership since source is better, i.e. more extreme
				else if (! verts[source]->maxMaximaMu && ! verts[source]->minMinimaMu) //if condition to protect maximaMu (minimaMu) vert w/ the max (min) mu value from being excluded from bases (base=false)
				{
					verts[source]->base = false; //source loses its base membership since neighbVertIdx is better, i.e. more extreme
					break; //shortcuts the loop 'cos i'm no longer interested in source which just lost its base membership
				}
			}
		} //end of j
	} //end of i

	//finally, exclude maximaMu verts whose mu is lower than avgMu, and minimaMu verts whose mu > avgMu
	if (! scaleInvDemo) //unnecessary for SCALE_INV_DISTORTION_DEMO 'cos it handles up to 30 initial extremes, so no further trimming
	{
		float f = 1.0f;
		for (int i = 0; i < (int) baseVerts.size(); i++)
			if ((verts[ baseVerts[i] ]->lMaxMinMu == 1 && verts[ baseVerts[i] ]->Mu < f*avgMu) || //maximaMu vert and has mu lower than avgMu
				(verts[ baseVerts[i] ]->lMaxMinMu == 2 && verts[ baseVerts[i] ]->Mu > f*avgMu))	//minimaMu vert and has mu higher than avgMu (almost never happens 'cos minimas are very few in numbers as compared to # of maximas)
				verts[ baseVerts[i] ]->base = false; //loses its base membership*/
	}
	////////////////////////////// for localMaxMinMu ends //////////////////////////////

	////////////////////////////// for localMaxMinCurv //////////////////////////////
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int source = baseVerts[i];
//		if (! verts[source]->base)
//			continue; //base=false means already clustered into a better base during a prev iter, so not care this ex-base (will be thrown away from baseVerts after this loop)

		for (int j = 0; j < (int) baseVerts.size(); j++)
		{
			int bv = baseVerts[j];
			if (source == bv || verts[source]->dSpanningUnn[bv] > dist) //source and bv are irrelevant, i.e. too distant, so not consider this source-bv pair for clustering
				continue;

			//either source or bv will be canceled, i.e. clustered in to the better one
			if (verts[source]->lMaxMinCurv == 1) //source is marked as a local maxima curvature
			{
				if (verts[source]->gc > verts[bv]->gc)
					verts[bv]->base = false; //bv loses its base membership since source is better, i.e. more extreme
				else
				{
					verts[source]->base = false; //source loses its base membership since bv is better, i.e. more extreme
					break; //shortcuts the loop 'cos i'm no longer interested in source which just lost its base membership
				}
			}
			else if (verts[source]->lMaxMinCurv == 2) //source is marked as a local minima curvature; so, use < test as opposed to > above
			{
				if (verts[source]->gc < verts[bv]->gc)
					verts[bv]->base = false; //bv loses its base membership since source is better, i.e. more extreme
				else
				{
					verts[source]->base = false; //source loses its base membership since neighbVertIdx is better, i.e. more extreme
					break; //shortcuts the loop 'cos i'm no longer interested in source which just lost its base membership
				}
			}
		} //end of j
	} //end of i
	////////////////////////////// for localMaxMinCurv ends //////////////////////////////

	//remove all ex-bases (that are marked as base=false above) from baseVerts
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		if (! verts[ baseVerts[i] ]->base) //marked to be removed/erased
		{
			verts[ baseVerts[i] ]->baseCandidate = true; //redundant
			//if (verts[ baseVerts[i] ]->baseNghbhood) delete verts[ baseVerts[i] ]->baseNghbhood; baseNghbhood=NULL; //redundant
			verts[ baseVerts[i] ]->idxInBaseVerts = -1; //redundant
			baseVerts.erase(baseVerts.begin() + i); //erase(begin + 4) erases [4]
			i--; //handle vector shifting
		}
	}
	for (int i = 0; i < (int) baseVerts.size(); i++) //go through the new baseVerts to update idxInBaseVerts values
		verts[ baseVerts[i] ]->idxInBaseVerts = i; //redundant

	if (populateTill10)
		//why not simply decrease dist here? 'cos a cool extreme (big mu) may be close to a base in baseVerts, e.g. sign finger when middle in baseVerts; if i decrease dist, then it'll add too many redundant extremes, e.g. elbow, shoulder, as well, which is not what i want; i want to populate w/ real extremes
		//so, add maximaMu vert w/ the next max mu value to baseVerts until we have at least 10 bases to be matched (below 10 makes excessive sym flips, ranking not enter disto5 and 6 computations)
		//no!!!!!!! sucks 'cos maximaMu not distributed in cool 1 per finger configuration; it may happen just near an existing base, 1 or 2-ring, which has no useful info at all; so, use dist reduction tactic; rather use farthest point sampling
		while ((int) baseVerts.size() < 10)
		{
			int nextFarthestIdx = -1;
			float maxDistSoFar = -INF;
			for (int v = 0; v < (int) verts.size(); v++)
				if (! verts[v]->base && verts[v]->lMaxMinMu == 1) //v not a base in baseVerts and is marked as a local maxima mu (so a candidate to be added to baseVerts)
				//if (! verts[v]->base) //use all verts as candidates, not the only maximaMu verts (inconsistent joint sampling; not in use)
				{
					//find the base in baseVerts that is closest to v and set that dist as minDist
					float minDist = (INF > maxGeoDistG ? INF : maxGeoDistG*100); //if INF not big enough use maxGeoDistG*100
					for (int bv = 0; bv < (int) baseVerts.size(); bv++)
						if (baseVerts[bv] != v && verts[ baseVerts[bv] ]->dSpanningUnn[v] < minDist) //prevent 0 distance b/w two same verts which ruins the minDist below
							minDist = verts[ baseVerts[bv] ]->dSpanningUnn[v];
					if (minDist > maxDistSoFar)
					{
						maxDistSoFar = minDist;
						nextFarthestIdx = v;
					}
				}
//cout << nextFarthestIdx << " added\n\n";
			if (nextFarthestIdx != -1) //-1 happens when there're very few maximaMu vert exist, which is almost never the case
			{				
				baseVerts.push_back(nextFarthestIdx);
				verts[nextFarthestIdx]->base = true;
				//below redundant but anyway			
				verts[nextFarthestIdx]->baseCandidate = false;
				verts[nextFarthestIdx]->idxInBaseVerts = (int) baseVerts.size()-1;
				//dijkstraShortestPaths(nextFarthestIdx);//required if using all verts as candidates, not the only maximaMu verts
			}
		}//*/

	cout << "Done! # of bases (after pruning) to be matched: " << (int) baseVerts.size() << ", for " << (int) verts.size() << " verts\n\n";
}

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

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

	for (int v = 0; v < (int) verts.size(); v++)
		if (verts[v]->disconnected) //disconnected (seahorse) verts not made candidate
			verts[v]->baseCandidate = false;

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

	//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
		verts[baseVertIdx]->baseCandidate = false;
		verts[baseVertIdx]->idxInBaseVerts = (int) baseVerts.size()-1;
		verts[baseVertIdx]->topCurv10 = (verts[baseVertIdx]->idxInBaseVerts < 10);
		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
		baseVertIdx = getBaseVertIdx(muOrder != NULL, gcOrder != NULL);
	} //end of baseVertIdx

	delete vlist;

	if (additionalBases) //add initial samples used in combinatorial matching; may ruin the uniformity a bit but not that important
		for (int i = 0; i < (int) baseVerts2.size(); i++)
		{
			bool exists = false; //prevent duplicates
			for (int j = 0; j < (int) baseVerts.size(); j++)
				if (baseVerts2[i] == baseVerts[j])
				{
					exists = true;
					break;
				}
			if (! exists)
			{
				baseVerts.push_back(baseVerts2[i]);
				verts[ baseVerts2[i] ]->base = true;
				verts[ baseVerts2[i] ]->baseCandidate = false;
				verts[ baseVerts2[i] ]->idxInBaseVerts = (int) baseVerts.size()-1;
			}
		}

	if (print)
		cout << "Done! # of base vertices: " << (int) baseVerts.size() << ", for " << (int) verts.size() << " verts\n\n";
}
vector< int > Mesh::computeRegionBaseVerts(float r, vector< int > regionVerts, int denseDistoComputation)
{
	//light version of computeBaseVerts() that works on regionVerts instead of verts

	for (int rv = 0; rv < (int) regionVerts.size(); rv++)
		verts[ regionVerts[rv] ]->baseCandidate = true; //this assumes all verts have baseCandidate = false initially

	int baseCounter = 0, baseVertIdx = regionVerts[baseCounter++]; //first 3 of regionVerts'll be selected for sure to make extremes bases to be matched (regardless of r, so nonuniformity happens if extremes in [0..2] are too close, which is a very rare case and not hurt the algorithm)
	vector< int >* vlist = new vector<int>, //first a baseVert enters this list, and then verts in the area/patch of that baseVert
				   regBases; //resulting regional bases

	//output dependent: O(k * V (or RV=regionVerts.size for efficient version)), where k = # of baseVerts generated, and V is # of region 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;//no need to init d for all verts
		for (int rv = 0; rv < (int) regionVerts.size(); rv++) verts[ regionVerts[rv] ]->d = INF; //efficient version
//99% sure about efficient version accuracy but double check (see comment in step 4)????????????????????????????????????????????????????????????????????????????????????
		



		//step 2: select a 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
		regBases.push_back(baseVertIdx);	//used later; whole aim is to fill this vector indeed
		verts[baseVertIdx]->baseCandidate = false;

//disable for not bothering w/ this timing trick 'cos it does not cost too much time as most of the regional bases overlap and therefore dijkstraShortestPaths() returns immediately; real timing is on computeRegionBaseVerts() i guess
if (denseDistoComputation != 8) dijkstraShortestPaths(baseVertIdx); //fill dSpanningUnn[] if not filled yet (fnctn returns immediately if already filled)

		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) //a non-regionVerts vert va that is in v.edgeList not have d=INF 'cos i made step 1 efficient; still no prob 'cos even if va.d=0 and this if fails, i don't add va to vlist, which is no problem 'cos a non-regionVerts vert va will never be selected as baseVertIdx below anyway
				{
					verts[va]->d = verts[v]->d + edges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->baseCandidate = false;
					}
				}
			}
			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;
					}
				}
			}
		} //end of vlist.empty()
		/////// shortcut Dijkstra w/ slow arrays to create 1 baseVert ends ///////

		//baseVertIdx = getBaseVertIdx(false, false); effect
		baseVertIdx = -1;
		if (baseCounter < 3)
			baseVertIdx = regionVerts[baseCounter++]; //first 3 of regionVerts'll be selected for sure to make extremes bases to be matched (regardless of r, so nonuniformity happens if extremes in [0..2] are too close, which is a very rare case and not hurt the algorithm)
		else
			for (int rv = 0; rv < (int) regionVerts.size(); rv++)
				if (verts[ regionVerts[rv] ]->baseCandidate)
				{
					baseVertIdx = regionVerts[rv];
					break;
				}
		//if baseVertIdx remains -1 then noone can be base, i.e. termination of computeRegionBaseVerts()
	} //end of baseVertIdx

	delete vlist;

//cout << "# region bases: " << (int) regBases.size() << " / " << (int) regionVerts.size() << endl;

	return regBases;
}
vector< int > Mesh::computeRegionBaseVerts2(float r, vector< int > regionVerts)
{
	//similar to computeRegionBaseVerts() but inherits the existing region base vertices for the overlapping regions and then creates new region base verts on the unoverlapped parts

	for (int rv = 0; rv < (int) regionVerts.size(); rv++)
		verts[ regionVerts[rv] ]->baseCandidate = true; //this assumes all verts have baseCandidate = false initially

	int baseCounter = 0, baseVertIdx = regionVerts[baseCounter++]; //first 3 of regionVerts'll be selected for sure to make extremes bases to be matched (regardless of r, so nonuniformity happens if extremes in [0..2] are too close, which is a very rare case and not hurt the algorithm)
	vector< int >* vlist = new vector<int>, //first a baseVert enters this list, and then verts in the area/patch of that baseVert
				   regBases; //resulting regional bases

	//output dependent: O(k * V (or RV=regionVerts.size for efficient version)), where k = # of baseVerts generated, and V is # of region 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 rv = 0; rv < (int) regionVerts.size(); rv++) verts[ regionVerts[rv] ]->d = INF; //efficient version
		//step 2: select a 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
		regBases.push_back(baseVertIdx);	//used later; whole aim is to fill this vector indeed
		verts[baseVertIdx]->baseCandidate = false;
		verts[baseVertIdx]->rbase = true; //to be processed as a regional base vert; so, mark it so that when overlapped in future, use/inherit this trivially as a regional base vert
		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) //a non-regionVerts vert va that is in v.edgeList not have d=INF 'cos i made step 1 efficient; still no prob 'cos even if va.d=0 and this if fails, i don't add va to vlist, which is no problem 'cos a non-regionVerts vert va will never be selected as baseVertIdx below anyway
				{
					verts[va]->d = verts[v]->d + edges[e]->length;
					if (verts[va]->d < r)
					{
						vlist->push_back(va);
						verts[va]->baseCandidate = false;
					}
				}
			}
			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;
					}
				}
			}
		} //end of vlist.empty()
		/////// shortcut Dijkstra w/ slow arrays to create 1 baseVert ends ///////

		//baseVertIdx = getBaseVertIdx(false, false); effect
		baseVertIdx = -1;
		if (baseCounter < 3)
			baseVertIdx = regionVerts[baseCounter++]; //first 3 of regionVerts'll be selected for sure to make extremes bases to be matched (regardless of r, so nonuniformity happens if extremes in [0..2] are too close, which is a very rare case and not hurt the algorithm)
		else
		{
			//first, try to inherit existing region base verts which happen on the overlapped part of this region (regionVerts) and a previous region which already produced region bases (rbase = true)
			bool inherited = false;
			for (int rv = 0; rv < (int) regionVerts.size(); rv++)
				if (verts[ regionVerts[rv] ]->rbase && verts[ regionVerts[rv] ]->baseCandidate)
				{
					baseVertIdx = regionVerts[rv];
					inherited = true;
					break;
				}
			//if no more to inherit, then try to create new region base verts which happen on the unoverlapped part of this region (regionVerts)
			if (! inherited)
				for (int rv = 0; rv < (int) regionVerts.size(); rv++)
					if (verts[ regionVerts[rv] ]->baseCandidate)
					{
						baseVertIdx = regionVerts[rv];
						break;
					}
		}
		//if baseVertIdx remains -1 then noone can be base, i.e. termination of computeRegionBaseVerts()
	} //end of baseVertIdx

	delete vlist;

//cout << "# region bases: " << (int) regBases.size() << " / " << (int) regionVerts.size() << endl;

	return regBases;
}
int Mesh::getBaseVertIdx(bool mu, bool COES)
{
	//returns the idx of the first vertex that's eligible to be base according to our COES or mu-based 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) //no matter what mu is, do the COES sampling (better for denseSampling 'cos mu values get irrelevant as # samples increase)
		{
			if (verts[ gcOrder[v] ]->baseCandidate) return gcOrder[v]; //test the next which comes from gcOrder
		}
		else if (mu)
		{			
			if (verts[ muOrder[v] ]->baseCandidate) return muOrder[v]; //test the next which comes from muOrder
		}
		else //not COES or mu-based, so pick the next arbitrarily
		{
			if (verts[v]->baseCandidate)
				return v;
		}

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

void Mesh::updateEdgeLengths()
{
	//called after coords scaling which changes edge lengths change

	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;
	}
	avgEdgeLen = edgeLenTotal/((int) edges.size());
//	cout << "after updateEdgeLengths(): min <= avg <= maxEdgeLen: " << minEdgeLen << " <= " << avgEdgeLen << " <= " << maxEdgeLen << "\n";
}
void Mesh::updateSedgeLengths()
{
	//similar to updateEdgeLengths() but this updates shortcut edges

	sedgeLenTotal = 0.0f;
	for (unsigned int e = 0; e < sedges.size(); e++)
	{
		sedges[e]->length = distanceBetween(verts[sedges[e]->v1i]->coords, verts[sedges[e]->v2i]->coords);
		sedgeLenTotal += sedges[e]->length;
	}
//	cout << "after updateSedgeLengths(): avg shortcut edge len = " << sedgeLenTotal/((int) sedges.size()) << "\n";
}

void Mesh::dijkstraShortestPaths(int sourceVert, float far)
{
	//computes shortest paths from sourceVert to all other verts that are at distance far or less

	if (verts[sourceVert]->dSpanningUnn) //don't recompute if already computed
		return;
	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 ///////

//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::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() ];
	vector< int >* tmp = new vector<int>; //use this instead of vector< int > tmp; for memo deallocation when done
	//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;
	}
	//info: x64 run must not use quickSort() to prevent weird run-time crash; use insertionSort there instead: insertionSort(gcVals, (int) verts.size(), vIdxs);
	quickSort(gcVals, vIdxs, 0, (int) verts.size()); //call be ref to gcVals (redundant) and vIdxs (output to be used below)
	for (int i = 0; i < (int) verts.size(); i++)
	{
		if (theInput == JUMPING || //nonmanifold neck side of jumping has bad curvature info and hence skipped
			(theInput == JUMPING_DANCING && ! targetMesh)) //jumping of dancing-jumping hybrid run
		{
			int j = vIdxs[i];
			if (j >= 15811 || (j >= 364 && j <= 374) || j == 627 || j == 389 || j == 146 || j == 244 || j == 245) //ugly verts around jumping.neck
			{
				tmp->push_back(j);
				continue;
			}
		}

		gcOrder[count] = vIdxs[i];
//fprintf(fPtr, "%d\t%f\n", gcOrder[count], verts[ gcOrder[count] ]->gc);
		count++;
		//verts[ vIdxs[i] ]->topCurv10 = (count <= 10); not do here 'cos vert that will not be selected as base will occupy top10 in vain, if this is done here
	}
	//fill the rest of gcOrder[] w/ these ugly vertices of jumping man, so that they are discouraged to be selected
	for (int j = 0; j < (int) tmp->size(); j++)
	{
		gcOrder[count] = (*tmp)[j]; //fill the rest w/ these ugly vertices of jumping man, so that they are discouraged to be selected
		count++;
	}
	delete [] gcVals;
	delete [] vIdxs;
	delete tmp;

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

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

	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;
	}
	quickSort(muVals, vIdxs, 0, (int) verts.size()); //call be ref to muVals (redundant) and vIdxs (output to be used below)
	for (int i = 0; i < (int) verts.size(); i++)
		muOrder[i] = vIdxs[i];
	delete [] muVals;
	delete [] vIdxs;
	cout << "done!\n\n";
}

void Mesh::scaleBy(float s)
{
	//scales this mesh by s

	for (int i = 0; i < (int) verts.size(); i++)
		for (int c = 0; c < 3; c++)
			verts[i]->coords[c] *= s;
	//edge lengths also need to be updated since all endpnts have been scaled above
	updateEdgeLengths();
	updateSedgeLengths();
	updateNormals(); //redundant
}

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()
{
	//computes base neighbors for each base vert, close ones that are at most 3r far away, i.e. 1st and 2nd ring base nghbs

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

	float far = 2*radius;
//far = 100*radius; //works fine w/ big radius 'cos this makes me use everyone as traversalList
	int minBaseNghbsCardinality = 5; //baseNghbs will be of size minBaseNghbsCardinality+ for sure
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int sourceVert = baseVerts[i];
		for (int v = 0; v < (int) verts.size(); v++)
			if (verts[v]->base && v != sourceVert && verts[sourceVert]->dSpanningUnn[v] <= far) //base, not itself, and sufficiently close
				verts[sourceVert]->baseNghbs.push_back(v);
		
		//i want at least 5 baseNghbs to be used in adaptive traversalList per entry (note that traversalList size for the same entry may decrease when we go 13x13 from 26x26 blossom but that's not a problem; initial traversalList must be of size 5+)
		float atLeast = far, atMost = 2*far;
		while ((int) verts[sourceVert]->baseNghbs.size() < minBaseNghbsCardinality)
		{
			for (int v = 0; v < (int) verts.size(); v++)
				if (verts[v]->base && v != sourceVert && verts[sourceVert]->dSpanningUnn[v] > atLeast && verts[sourceVert]->dSpanningUnn[v] <= atMost) //> atLeast to prevent duplicates of < atLEast added above; < atMost to prevent too too far aways
				{
					verts[sourceVert]->baseNghbs.push_back(v);
					if ((int) verts[sourceVert]->baseNghbs.size() == minBaseNghbsCardinality) //desired min baseNghbs cardinality achieved
						break;
				}
			atLeast = atMost;
			atMost *= 2.0f; //skip to next window in case size still < 5
		}
/*#ifdef DBG_DETAIL
		cout << "# of baseNeighbs for base v" << sourceVert << ": [" << (int) verts[sourceVert]->baseNghbs.size() << "]\t";
		for (int j = 0; j < (int) verts[sourceVert]->baseNghbs.size(); j++)
			cout << verts[sourceVert]->baseNghbs[j] << "\t";
		cout << endl;
#endif*/
	} //end of i

	cout << "done!\n\n";
}
void Mesh::computeBaseNeighbsK(int k)
{
	//computes k closest base neighbors for each base vert in a sorted way s.t. bv.baseNghbsK[0] is closest to bv, [1] next closest, ..

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

/* BFS tactic is easy to implement but wrong 'cos it count edges instead of edge lengths; so, geodesically close ones may not be captured here
	//run BFS from i'th baseVert until n closest base is found for it (BFS visits close ones first)
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int source = baseVerts[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		
		queue< int > q;
		q.push(source);
		verts[source]->d = 0; //length of unweighted shortest path from source to source is 0
		int v;
		while (! q.empty())
		{
			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)]->base && (int) verts[source]->baseNghbsK.size() < k)
					verts[source]->baseNghbsK.push_back((*neighbVertIdx));
				q.push((*neighbVertIdx));
			} //end of neighbVertIdx

			if ((int) verts[source]->baseNghbsK.size() >= k) //n closest bases found for it
				break;
		} //end of while
	} //end of i*/

	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		int bv = baseVerts[i]; //find k closest bases to this base

		//duplicate prevention by processed variable; clear them for this fresh bv
		for (int ii = 0; ii < (int) baseVerts.size(); ii++)
			verts[ baseVerts[ii] ]->processed = false; //true means added to bv.baseNghbsK

		//check all other bases to select the k closest ones among them	
		while ((int) verts[bv]->baseNghbsK.size() < k)
		{			
			float minDist = INF;
			int bestCandid = -1;
			for (int j = 0; j < (int) baseVerts.size(); j++)
			{
				int candidBV = baseVerts[j];
				if (i != j && ! verts[candidBV]->processed && verts[bv]->dSpanningUnn[candidBV] < minDist)
				{
					minDist = verts[bv]->dSpanningUnn[candidBV];
					bestCandid = candidBV;
				}
			} //end of j
assert(bestCandid != -1);
			verts[bv]->baseNghbsK.push_back(bestCandid);
			verts[bestCandid]->processed = true; //added to bv.baseNghbsK
		} //end of while
	} //end of i

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

void Mesh::sortSafeCorresp1to1(Mesh* mesh2, char* fName, int n, float avgErr)
{
	//clears and then refills 1-to-1 safeCorresp w/ current baseVerts entries in the ascending order of isometryCosts; topN w.r.t. matchCotI decided as well; processed tactic below sucks if safeCorresp is many-to-1, e.g. 7-34 makes 7.processed=true, and then 7-55 can't be added to safeCorresp below

	//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() < (int) mesh2->baseVerts.size() ? (int) baseVerts.size() : (int) mesh2->baseVerts.size()), radius, mesh2->radius, 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\n", nextMinCost, verts[nextMinCost]->matchIdx, verts[nextMinCost]->matchCostI);
		verts[nextMinCost]->topN = ((int) safeCorresp.size() <= n*2); //true if in topN
	}
	fprintf(fPtr, "%f\n", avgErr);
	fclose(fPtr);
}

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

	for (int p = 1; p < s; p++)
	{
		int tmp = A[p], j;
		for (j = p; j > 0 && tmp < A[j-1]; j--)
			A[j] = A[j-1];
		A[j] = tmp;
	}
}

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

void Mesh::partialMatching(Mesh* mesh2, float arbitraryScale)
{
	//partial matching b/w a complete mesh (this) and its cropped and rescaled isometric part (mesh2)
	

	//distortionByNormalizedGeodesics = true (indirect approach) vs. false (direct approach); DA is slightly better 'cos when 5 in complete model not match w/ the 5 in cropped, the max geodesics to be normalized w/
	//will be inconsistent which may make a low-distortion for the bad match b/w unmatching 5x5 samples; this should never happen w/ direct approach that does not use normalized geodesics at all
	//when is this not a problem? cropped model is cut so nicely that max geo on it is not affected, e.g. one arm is cropped so that max geo is still toe to uncropped hand


	cout << "\n\npartial matching in action......\n";

	double start_time = get_time();

	//more accurate shortest paths
	//prepareForGeodesicComputation();
	//mesh2->prepareForGeodesicComputation();

	//a noisy surface often contains many small bumps that correspond to local maxima of (small) mu values; clear that noise by smoothing
	laplSmoothing = mesh2->laplSmoothing = true;
	if (laplSmoothing)
		laplacianSmoothing();
	if (mesh2->laplSmoothing)
		mesh2->laplacianSmoothing();
	//few bases by local maxima and minima (of mu and/or curvature values) on both meshes
	int m = computeBaseVerts(false, true), n = mesh2->computeBaseVerts(false, true);
	cout << "# bases = " << m << " & " << n << endl;
//return;//see initial bases to be matched
	cout << ">>>>>>>\t" << get_time()-start_time << " secs for feature selection\n\n";

	float s = arbitraryScale;
//	if (theInput != SHREC11) //shrec11 is a standard benchmark w/ its own scaling class
	{
		//to see the benefit of divided geodesics (scale-invariant distortion) over normalized geodesics (distortion)		
		mesh2->scaleBy(s);
		for (int i = 0; i < (int) mesh2->baseVerts.size(); i++)
		{
			mesh2->verts[ mesh2->baseVerts[i] ]->dSpanningUnn = NULL; //recompute dSpanningUnn
			mesh2->dijkstraShortestPaths(mesh2->baseVerts[i]);
		}
		//computeBaseVerts called before to decide the for-loop just-below; but then mesh2->maxGeoDistG remained unscaled 'cos it was calculated there; so, scale it here
		mesh2->maxGeoDistG *= s;
	}

	//matching stuff
	start_time = get_time(); //start timer again for combinatorial matching
	float finalIsoDistortion = isometricPartMatching(mesh2);
	cout << ">>>>>>>\t" << get_time()-start_time << " secs for this combinatorial matching whose final isometric distortion: " << finalIsoDistortion << "\n\n";

	//results to file
	char fName[300], * inputStr = "jumping", * approach = (distortionByNormalizedGeodesics ? "" : " DA"); //direct approach (DA) where our new scale-inv distortion measure in use to create the combinatorial match that brings the 2 meshes to the same scale
/*	if (theInput == SHREC11)
		inputStr = "shrec11";
	else if (theInput == VICTORIA_SHREC11)
		inputStr = "victoria-shrec11";
	else if (theInput == HORSE)
		inputStr = "horse";
	else if (theInput == DANCING)
		inputStr = "dancing";
	else if (theInput == JUMPING_DANCING)
		inputStr = "jumping-dancing";
	else if (theInput == WOLF_CAT)
		inputStr = "wolf-cat";
	else if (theInput == DOG_TOSCA)
		inputStr = "dog";
	else if (theInput == WATERTIGHT)
		inputStr = "watertight";
	else if (theInput == SCAPE)
		inputStr = "scape";
	else if (theInput == GORILLA_GIRL)
		inputStr = "gorilla-girl";
	sprintf(fName, "..\\..\\..\\code\\output\\PartialShapeCorresponder_x64\\ScaleInvDistortion\\%s\\dense\\C_%d-%d%s.dat", inputStr, frameID, mesh2->frameID, approach); //m & n changed from above*/
	sprintf(fName, "output\\[1]\\C_%d-%d.dat", frameID, mesh2->frameID); //publicly shared mode does not know about theInput stuff
	FILE* fPtr = fopen(fName, "w");
	fprintf(fPtr, "%d\t%f\t%f\t%f\t%f\n", (int) safeCorresp.size() / 2, minGeoDistG, maxGeoDistG, mesh2->minGeoDistG, mesh2->maxGeoDistG/s); //# of correspondences to be written below and min/maxGeoDists (div by s for unscaled/original version of mesh2->maxGeoDistG)
	for (int s = 0; s < (int) safeCorresp.size() / 2; s++)
		fprintf(fPtr, "%d\t%d\t%f\n", safeCorresp[2*s], safeCorresp[2*s + 1], verts[ safeCorresp[2*s] ]->matchCostI);
	fprintf(fPtr, "%f\n%d\n", finalIsoDistortion, -1);
	fprintf(fPtr, "pruned mesh1 bases:\n# = %d\n", (int) baseVerts.size()); //extra info to show pruned bases whose subsets provided safeCorresp correspondence above; this is to draw all bases later
	for (int i = 0; i < (int) baseVerts.size(); i++)
	{
		fprintf(fPtr, "%d\n", baseVerts[i]);
		baseVerts2.push_back(baseVerts[i]); //store the originals
	}
	fprintf(fPtr, "pruned mesh2 bases:\n# = %d\n", (int) mesh2->baseVerts.size());
	for (int i = 0; i < (int) mesh2->baseVerts.size(); i++)
	{
		fprintf(fPtr, "%d\n", mesh2->baseVerts[i]);
		mesh2->baseVerts2.push_back(mesh2->baseVerts[i]);
	}
	baseVerts.clear(); //baseVerts to be filled again for 120x120 blossom samples plus baseVerts2 below
	mesh2->baseVerts.clear();

	/////////////////////// extending 5x5 combinatorial match to a denser one //////////////////////////////////
	cout << "bringing 2 meshes to the same scale wherein dense map judges the quality of this generating combinatorial match..\n";
	//save the original mesh2 coords before they are exposed to scalings below; when done w/ this denser matching, i roll-back to original coords to display the originals
	for (int i = 0; i < (int) mesh2->verts.size(); i++)
	{
		if (! mesh2->verts[i]->coords2) //laplacian smoothing already filled coords2
			mesh2->verts[i]->coords2 = new float[3]; //initialize coords2, if not inited yet, which holds the original coords no matter what happens to coords[]
		for (int c = 0; c < 3; c++)
			mesh2->verts[i]->coords2[c] = mesh2->verts[i]->coords[c];
	}
	//below heavily adapted from matchBasesViaVoting2()
	//bring meshes to the same scale by multiplying mesh2 via scaler which uses all C(5,2) distances assuming safeCorresp.size=10
	float scaler = 0.0f, nAdds = 0.0f;
	for (int a = 0; a < (int) safeCorresp.size(); a += 2)
		for (int b = a+2; b < (int) safeCorresp.size(); b += 2)
		{
			scaler += (verts[ safeCorresp[a] ]->dSpanningUnn[ safeCorresp[b] ] / mesh2->verts[ safeCorresp[a+1] ]->dSpanningUnn[ safeCorresp[b+1] ]);
			nAdds++;
		}
	scaler /= nAdds;
	for (int i = 0; i < (int) mesh2->verts.size(); i++)
	{
		mesh2->verts[i]->coords[0] *= scaler;
		mesh2->verts[i]->coords[1] *= scaler;
		mesh2->verts[i]->coords[2] *= scaler;
		mesh2->verts[i]->baseCandidate = true; //= false in matchBasesViaVoting2() 'cos computeRegionBaseVerts2() expects so
	}
	for (int i = 0; i < (int) verts.size(); i++)
		verts[i]->baseCandidate = true;
	for (int e = 0; e < (int) mesh2->edges.size(); e++)
		mesh2->edges[e]->length = distanceBetween(mesh2->verts[ mesh2->edges[e]->v1i ]->coords, mesh2->verts[ mesh2->edges[e]->v2i ]->coords);
	for (int e = 0; e < (int) mesh2->sedges.size(); e++)
		mesh2->sedges[e]->length = distanceBetween(mesh2->verts[ mesh2->sedges[e]->v1i ]->coords, mesh2->verts[ mesh2->sedges[e]->v2i ]->coords);
	float wholeSurfaceArea = 0.0f;
	for (int t = 0; t < (int) tris.size(); t++)
		wholeSurfaceArea += tris[t]->area;
	float r1 = 0.171f * sqrt(wholeSurfaceArea / PI); //120 region bases on complete model; w/ this same radius i expect to create less bases on cropped model mesh2, which in turn leads to a 120x120 blossom in the worst case
//	sortCurvatures(); //enable this to use COES sampling just-below
	computeBaseVerts(r1, true);
	mesh2->computeBaseVerts(r1, true);

	//////////////////////// dense base verts matching ////////////////////////
	//create dense blossom map b/w dense baseVerts & mesh2.baseVerts and evaluate the resulting map to infer the quality of the initial generating combinatorial match
	//relevant geodesic distances of mesh2 are brought to the same of mesh1.geodesics using scaler and stored in dSpanningUnnSS
	for (int y = 0; y < (int) mesh2->baseVerts2.size(); y++)
	{
		if (! mesh2->verts[ mesh2->baseVerts2[y] ]->dSpanningUnnSS)
			mesh2->verts[ mesh2->baseVerts2[y] ]->dSpanningUnnSS = new float[ (int) mesh2->verts.size() ];
		for (int v = 0; v < (int) mesh2->verts.size(); v++)
			mesh2->verts[ mesh2->baseVerts2[y] ]->dSpanningUnnSS[v] = mesh2->verts[ mesh2->baseVerts2[y] ]->dSpanningUnn[v] * scaler;
	}
	//maxExtremeDist2 *= scaler; i could easily compute maxExtremeDist1/2 above while doing scaler += additions but won't use this additional normalization 'cos ~5 samples from combinatorial matching are not necessary the extremes which makes normalization slightly wrong
	int n1 = (int) baseVerts.size(), n2 = (int) mesh2->baseVerts.size();
	int nRows = (n1 > n2 ? n1 : n2), nCols = nRows;
	int* vIdx1 = new int[n1], * vIdx2 = new int[n2];
	float** costs = new float*[nRows];
	float discourage = INF; //must be INF 'cos entries are not in the [0,1] interval as i don't do the maxExtremeDist1/2 normalizations
	int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e, * edges = new int[2*nEdgeNum], * matches = new int[nVertNum];
	double* weightsD = new double[nEdgeNum];
	int row = 0;
	for (int i = 0; i < n1; i++)
	{
		costs[row] = new float[nCols];
		vIdx1[row] = baseVerts[i];
		int col = 0;
		for (int j = 0; j < n2; j++)
			vIdx2[col++] = mesh2->baseVerts[j];
		while (col < nCols)
			costs[row][col++] = discourage;
		row++;
	}
	while (row < nRows)
	{
		costs[row] = new float[nCols];
		for (int col = 0; col < nCols; col++)
			costs[row][col] = discourage;
		row++;
	}
	row = 0;
	for (int i = 0; i < n1; i++)
	{
		int base1 = baseVerts[i];
		verts[base1]->matchIdxs.clear(); //start a fresh matchIdxs list (already empty/fresh but anyway be safe) to store the blossom costs to be used in denseDistortion computation below
		int col = 0;
		for (int j = 0; j < n2; j++)
		{
			int base2 = mesh2->baseVerts[j];
			//bool combinatorialMatch = false; for (int y = 0; y < (int) safeCorresp.size(); y += 2) if (safeCorresp[y] == base1 && safeCorresp[y+1] == base2) combinatorialMatch = true;
			float vertexBasedIso = 0.0f;
			int nAdds = 0;
			for (int ss = 0; ss < (int) safeCorresp.size(); ss += 2)
			{
				int s1 = safeCorresp[ss], s2 = safeCorresp[ss + 1];
				//if (s1 == base1) continue; not done here 'cos unlike getCorrespListDistortion() i don't just compute distortion of a given mapping here; rather, i create all possible pairwise distortions using a safe list in safeCorresp which requires no continue here
				float t1s1 = verts[s1]->dSpanningUnn[base1], t2s2 = mesh2->verts[s2]->dSpanningUnnSS[base2]; //t1s1 = verts[s1]->dSpanningUnn[base1] / maxExtremeDist1, t2s2 = mesh2->verts[s2]->dSpanningUnnSS[base2] / maxExtremeDist2;
				vertexBasedIso += fabs(t1s1 - t2s2);
				nAdds++;				
			}
if (nAdds <= 0) {cout << "can't traverse 0-size\n";exit(0);}
			vertexBasedIso /= nAdds;
			//combinatorial matches will have vertexBasedIso=0 for sure'cos both t1s1 and t2s2'll be 0 above; so, they're encouraged already
			costs[row][col++] = vertexBasedIso; //(! combinatorialMatch ? vertexBasedIso : -INF); //encourage combinatorial matches (initial ~5 samples in baseVerts2) to be selected by blossom
			verts[base1]->matchIdxs.push_back((float) base2);
			verts[base1]->matchIdxs.push_back(vertexBasedIso);
		}
		row++;
	}
	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* blossom = new Blossom();
	blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches);
	delete blossom;
	float denseDistortion = 0.0f;
	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
				denseMap.push_back(vIdx1[m1]); //base1 from this mesh1; be sure even idxs ~ mesh1, odds ~ mesh2
				denseMap.push_back(mi); //base2 from mesh2
				verts[ vIdx1[m1] ]->matchIdx = mi; //vIdx1[m1] ~ mesh1, mi ~ mesh2 for sure
										
				//match and its cost
				//learn the individual distortion used in blossom mtrx to use it in matchIdxs2
				float vertexBasedIso = -1.0f;
				for (int j = 0; j < (int) verts[ vIdx1[m1] ]->matchIdxs.size(); j += 2)
					if (verts[ vIdx1[m1] ]->matchIdxs[j] == mi)
					{
						vertexBasedIso = verts[ vIdx1[m1] ]->matchIdxs[j+1];
						break;
					}
if (vertexBasedIso == -1.0f){cout<<"\n\n\n\n\nsth wrong\n";exit(0);}
				denseDistortion += vertexBasedIso;
			}
		}
	} //end of m
	denseDistortion /= ((int) denseMap.size() / 2);
	denseDistortion /= mesh2->maxGeoDistG; //distortion computed on distances that are not necessarily in [0,1] after they're brought to the same scale; to make distortion in [0,1] div by summation by mesh2.maxGeo 'cos dense map is supposed to span the cropped model mesh2 and hence normalized distortion is interpreted w/ max geo on mesh2 = 1
	cout << "distortion of the dense map of size " << ((int) denseMap.size() / 2) << " = " << denseDistortion << "\n\n";
	fprintf(fPtr, "dense map %dx%d:\n", (int) denseMap.size() / 2, (int) denseMap.size() / 2);
	for (int i = 0; i < (int) denseMap.size(); i += 2)
		fprintf(fPtr, "%d\t%d\n", denseMap[i], denseMap[i+1]);
	fprintf(fPtr, "%f\n", denseDistortion);
	//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;
	//////////////// desiredMap provides the regions on which dense maps are done ends ////////////////

	//roll-back to original coords 'cos mesh2 coords scaled above and i want to display the originals (recall that originals also multiplied at the top of this function (scaleBy()) for scale demo, so i'm returning to these multiplied original coords indeed)
	for (int v = 0; v < (int) mesh2->verts.size(); v++)
		for (int c = 0; c < 3; c++)
			mesh2->verts[v]->coords[c] = mesh2->verts[v]->coords2[c];
	/////////////////////// extending 5x5 combinatorial match to a denser one ends //////////////////////////////////
	//done w/ fprints whether dense extension above done or not done
	fclose(fPtr);
	cout << "done!\n\n";
}

inline 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;
}

float Mesh::isometricPartMatching(Mesh* mesh2)
{
	//tries n farthest point sampling (fps) samples of cropped model mesh2 w/ C(baseVerts.size, n)*n! n-tuples of this complete mesh and picks the best match bestCorresp^^; then brings 2 meshes to the same scale using bestCorresp and performs a dense matching at the same scale	
	//^^ computation of bestCorresp'll be done in 2 comparative ways: 1) distoByNormalizedGeodesics = true which normalizes geodesics by local max geodesic distance and use the second-order distortion (cvpr'10, pami, sgp distortion), 2) = false: applies scale-inveariant distortion directly on unnormalized geodesics

	//prepare n fps samples of mesh2 in iBaseVerts; n fps samples used instead of top-n most extreme samples 'cos fps makes the best coverage over whole part, e.g, few-finger upper body model would pick all 5 fingers w/ pure top-n tactic but w/ this fps tactic i'll have sample/coverage on other hand and head and body parts as well
	int m = (int) baseVerts.size(), n = ((int) mesh2->baseVerts.size() >= 5 ? 5/*4*/ : 4);
if (n > (int) mesh2->baseVerts.size() || n > 7 || n <= 2 || n > m) {cout << "bad m n\n"; exit(0);} //n fps will be selected from mesh2.baseVerts (1st condition), and have 7!.txt at most (2nd), and C(m,n) will be used (3rd)
	vector< int > iBaseVerts;
	//first pick the most-extreme one w.r.t. maxima of mu
	float maxMu = -INF;
	int maxMuIdx = -1;
	for (int i = 0; i < (int) mesh2->baseVerts.size(); i++)
	{
		if (mesh2->verts[ mesh2->baseVerts[i] ]->Mu > maxMu)
		{
			maxMu = mesh2->verts[ mesh2->baseVerts[i] ]->Mu;
			maxMuIdx = mesh2->baseVerts[i];
		}
		mesh2->verts[ mesh2->baseVerts[i] ]->processed = false; //to prevent duplications in iBaseVerts; true means added to iBaseVerts
	}
	iBaseVerts.push_back(maxMuIdx);
	mesh2->verts[maxMuIdx]->processed = true;

	//second, fill the remaining n-1 items via fps for uniform distribution for well-coverage
	while ((int) iBaseVerts.size() < n)
	{
		//in fps, the next sample candidate x_i \in all vertices finds its closest existing sample x_clo and remembers d_i = d(x_i, x_clo); if d_i is the max among all other candidates closest remembered dists, then x_i is selected as the next sample;
		//here my candidates are restricted to mesh2->baseVerts; so, find the closest existing base e2 \in iBaseVerts to bv2 \in mesh2->baseVerts and add bv2 to iBaseVerts as nextSample if that closest dist is very big, i.e. the biggest among all closest dists for different bv2's
		float maxDist = -INF;
		int nextSample = -1;
		for (int s = 0; s < (int) mesh2->baseVerts.size(); s++)
		{
			int bv2 = mesh2->baseVerts[s], e2 = -1;
			if (mesh2->verts[bv2]->processed)
				continue; //no chance to add bv2 to iBaseVerts 'cos it's already added
			float minDist = mesh2->verts[ iBaseVerts[0] ]->dSpanningUnn[bv2]; //=INF is tricky 'cos objects may be scaled to those high coordinates			
			for (int e = 0; e < (int) iBaseVerts.size(); e++) //find the closest existing base e \in iBaseVerts to bv2 \in mesh2->baseVerts
				if (mesh2->verts[ iBaseVerts[e] ]->dSpanningUnn[bv2] <= minDist)
				{
					minDist = mesh2->verts[ iBaseVerts[e] ]->dSpanningUnn[bv2];
					e2 = iBaseVerts[e];
				}
			if (mesh2->verts[e2]->dSpanningUnn[bv2] > maxDist)
			{
				maxDist = mesh2->verts[e2]->dSpanningUnn[bv2];
				nextSample = bv2;
			}
		} //end of for s
if (nextSample == -1) {cout << "can't happen in fps\n"; exit(0);}
		iBaseVerts.push_back(nextSample);
		mesh2->verts[nextSample]->processed = true; //added, so not add again later
	} //end of while n
	
	///////////// combinations + permutations ///////////////////////
	bool candidsToFile = false;
	FILE* fPtr;
	if (candidsToFile)
		fPtr = fopen("combinatorial matchings.dat", "w");
	//try n fps samples of mesh2 in iBaseVerts w/ C(baseVerts.size, n)*n! n-tuples of this mesh1 and pick the best match bm as the initial matching
	fillPermutationMtrxs(); //to be used for permutations below
	cout << "combinations + permutations for isometric part matching (" << m << " vs. " << n << ")..\n";
	//bMesh is the bigger one (if sizes different); so, some bMesh.bases'll remain unmatched; to decide that use combinations;
	//say bMesh.size = m = 5, sMesh.size = n = 3; then, try C(5,3) bMesh combinations and for each combination try 3! permutations to match up w/ 3 sMesh.bases in iBaseVerts
	float minIsometryCost = INF, tooBigDistortion = (distortionByNormalizedGeodesics ? 0.1f : 0.6f); //w/ scale-inv distortion measure, isometryCostTry below tends to be bigger than distortionByNormalizedGeodesics indirect tactic
	vector< int > bestCorresp;
	vector< float > allDistortions;
	vector< vector< int > > candidateMapsUnsorted;
	if (n == 4) //use C(m,4) combinations to decide the 4 related verts in to-side as good as possible
	{
		int twoR = 2*n, factR = factorial(n);
		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(baseVerts[a]);
						tmp.push_back(baseVerts[b]);
						tmp.push_back(baseVerts[c]);
						tmp.push_back(baseVerts[d]);

						float maxDist1 = -INF, maxDist2 = -INF;
						if (distortionByNormalizedGeodesics)
						{
							//find the max geo dist b/w baseVerts (this mesh1 side) to be used as a normalizer in getCorrespListDistortion()								
							for (int i = 0; i < (int) tmp.size(); i++)
								for (int j = 1; j < (int) tmp.size(); j++) //j=i is a more efficient start but anyway
									if (verts[ tmp[i] ]->dSpanningUnn[ tmp[j] ] > maxDist1)
										maxDist1 = verts[ tmp[i] ]->dSpanningUnn[ tmp[j] ];
							//find the max geo dist b/w iBaseVerts (from mesh2 side) to be used as a normalizer in getCorrespListDistortion()
							for (int i = 0; i < (int) iBaseVerts.size(); i++)
								for (int j = 1; j < (int) iBaseVerts.size(); j++)
									if (mesh2->verts[ iBaseVerts[i] ]->dSpanningUnn[ iBaseVerts[j] ] > maxDist2)
										maxDist2 = mesh2->verts[ iBaseVerts[i] ]->dSpanningUnn[ iBaseVerts[j] ];
						}

						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;
							for (int y = 0; y < twoR; y++)
								if (y % 2 == 1)
									tmpSafeCorrespTry.push_back(iBaseVerts[idx1++]); //odd idxs from stable from mesh2 side
								else
									tmpSafeCorrespTry.push_back(tmp[ perms4[r][idx2++] ]); //even idxs for permutations from this mesh1 side
						
							//compute isometry cost of this 4-4 mapping and ..
							float isometryCostTry = (distortionByNormalizedGeodesics ? getCorrespListDistortion(mesh2, tmpSafeCorrespTry, maxDist1, maxDist2) :
																				       getCorrespListScaleInvDistortion(mesh2, tmpSafeCorrespTry));
							//..save it if it's the best so far
							if (isometryCostTry < minIsometryCost)
							{
								minIsometryCost = isometryCostTry;
								bestCorresp = tmpSafeCorrespTry;
							}

							if (trackCombinatorialMaps && isometryCostTry < tooBigDistortion) //very big distortions very unlikely to be ground-truth maps; so, don't even consider them in sorted list of candidate maps to be tracked
							{ 
								candidateMapsUnsorted.push_back(tmpSafeCorrespTry); //a candidate map (to be tracked if its distortion is in top5)
								allDistortions.push_back(isometryCostTry); //distortion of this candidate map
							}
						} //end of r
					}
		}
	}
	else if (n == 5) //use C(m,5) combinations to decide the 5 related verts in to-side as good as possible
	{
		int twoR = 2*n, factR = factorial(n);
		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(baseVerts[a]);
							tmp.push_back(baseVerts[b]);
							tmp.push_back(baseVerts[c]);
							tmp.push_back(baseVerts[d]);
							tmp.push_back(baseVerts[e]);

							float maxDist1 = -INF, maxDist2 = -INF;
							if (distortionByNormalizedGeodesics)
							{
								//find the max geo dist b/w baseVerts (this mesh1 side) to be used as a normalizer in getCorrespListDistortion()								
								for (int i = 0; i < (int) tmp.size(); i++)
									for (int j = 1; j < (int) tmp.size(); j++) //j=i is a more efficient start but anyway
										if (verts[ tmp[i] ]->dSpanningUnn[ tmp[j] ] > maxDist1)
											maxDist1 = verts[ tmp[i] ]->dSpanningUnn[ tmp[j] ];
								//find the max geo dist b/w iBaseVerts (from mesh2 side) to be used as a normalizer in getCorrespListDistortion()
								for (int i = 0; i < (int) iBaseVerts.size(); i++)
									for (int j = 1; j < (int) iBaseVerts.size(); j++)
										if (mesh2->verts[ iBaseVerts[i] ]->dSpanningUnn[ iBaseVerts[j] ] > maxDist2)
											maxDist2 = mesh2->verts[ iBaseVerts[i] ]->dSpanningUnn[ iBaseVerts[j] ];
							}

							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;
								for (int y = 0; y < twoR; y++)
									if (y % 2 == 1)
										tmpSafeCorrespTry.push_back(iBaseVerts[idx1++]); //odd idxs from stable from mesh2 side
									else
										tmpSafeCorrespTry.push_back(tmp[ perms5[r][idx2++] ]); //even idxs for permutations from this mesh1 side
							
								//compute isometry cost of this 5-5 mapping and ..
								float isometryCostTry = (distortionByNormalizedGeodesics ? getCorrespListDistortion(mesh2, tmpSafeCorrespTry, maxDist1, maxDist2) :
																					  	   getCorrespListScaleInvDistortion(mesh2, tmpSafeCorrespTry));
								//..save it if it's the best so far
								if (isometryCostTry < minIsometryCost)
								{
									minIsometryCost = isometryCostTry;
									bestCorresp = tmpSafeCorrespTry;
								}

								if (trackCombinatorialMaps && isometryCostTry < tooBigDistortion) //very big distortions very unlikely to be ground-truth maps; so, don't even consider them in sorted list of candidate maps to be tracked
								{
									if (candidsToFile)
									{
										for (int i = 0; i < (int) tmpSafeCorrespTry.size(); i += 2)
											fprintf(fPtr, "%d\t%d\n", tmpSafeCorrespTry[i], tmpSafeCorrespTry[i+1]);
										fprintf(fPtr, "%f\n\n", isometryCostTry);
									}
									candidateMapsUnsorted.push_back(tmpSafeCorrespTry); //a candidate map (to be tracked if its distortion is in top5)
									allDistortions.push_back(isometryCostTry); //distortion of this candidate map
								}
							} //end of r
						}
	}
	else if (n == 6) //use C(m,6) combinations to decide the 6 related verts in to-side as good as possible
	{
		int twoR = 2*n, factR = factorial(n);
		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(baseVerts[a]);
								tmp.push_back(baseVerts[b]);
								tmp.push_back(baseVerts[c]);
								tmp.push_back(baseVerts[d]);
								tmp.push_back(baseVerts[e]);
								tmp.push_back(baseVerts[f]);

								float maxDist1 = -INF, maxDist2 = -INF;
								if (distortionByNormalizedGeodesics)
								{
									//find the max geo dist b/w baseVerts (this mesh1 side) to be used as a normalizer in getCorrespListDistortion()								
									for (int i = 0; i < (int) tmp.size(); i++)
										for (int j = 1; j < (int) tmp.size(); j++) //j=i is a more efficient start but anyway
											if (verts[ tmp[i] ]->dSpanningUnn[ tmp[j] ] > maxDist1)
												maxDist1 = verts[ tmp[i] ]->dSpanningUnn[ tmp[j] ];
									//find the max geo dist b/w iBaseVerts (from mesh2 side) to be used as a normalizer in getCorrespListDistortion()
									for (int i = 0; i < (int) iBaseVerts.size(); i++)
										for (int j = 1; j < (int) iBaseVerts.size(); j++)
											if (mesh2->verts[ iBaseVerts[i] ]->dSpanningUnn[ iBaseVerts[j] ] > maxDist2)
												maxDist2 = mesh2->verts[ iBaseVerts[i] ]->dSpanningUnn[ iBaseVerts[j] ];
								}

								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;
									for (int y = 0; y < twoR; y++)
										if (y % 2 == 1)
											tmpSafeCorrespTry.push_back(iBaseVerts[idx1++]); //odd idxs from stable from mesh2 side
										else
											tmpSafeCorrespTry.push_back(tmp[ perms6[r][idx2++] ]); //even idxs for permutations from this mesh1 side

									//compute isometry cost of this 6-6 mapping and ..
									float isometryCostTry = (distortionByNormalizedGeodesics ? getCorrespListDistortion(mesh2, tmpSafeCorrespTry, maxDist1, maxDist2) :
																							   getCorrespListScaleInvDistortion(mesh2, tmpSafeCorrespTry));
									//..save it if it's the best so far
									if (isometryCostTry < minIsometryCost)
									{
										minIsometryCost = isometryCostTry;
										bestCorresp = tmpSafeCorrespTry;
									}

									if (trackCombinatorialMaps && isometryCostTry < tooBigDistortion) //very big distortions very unlikely to be ground-truth maps; so, don't even consider them in sorted list of candidate maps to be tracked
									{ 
										candidateMapsUnsorted.push_back(tmpSafeCorrespTry); //a candidate map (to be tracked if its distortion is in top5)
										allDistortions.push_back(isometryCostTry); //distortion of this candidate map
									}
								} //end of r
							}
	}
	else
	{
		cout << "undefined n for n fps samples to be matched\n";
		exit(0);
	}
	///////////// combinations + permutations ends ///////////////////////

	if (trackCombinatorialMaps)
	{
		//sort allDistortions and the corresponding candidate maps; if there are k distortions, then there are k candidateMaps each of size n; in the end, both allDistortions & candidateMapsUnsorted will be sorted
		for (int i = 0; i < (int) allDistortions.size(); i++)
		{
			//get the min from the right side of [i] 'cos left of [i] already sorted and fixed
			float minDisto = INF;
			int minDistoIdx = -1;
			for (int j = i; j < (int) allDistortions.size(); j++)
				if (allDistortions[j] < minDisto)
				{
					minDisto = allDistortions[j];
					minDistoIdx = j;
				}
			//replace [i] & [minDistoIdx]
			float tmp = allDistortions[i];
			allDistortions[i] = minDisto;
			allDistortions[minDistoIdx] = tmp;
			//replace corresponding maps
			vector< int > tmp2 = candidateMapsUnsorted[i]; //remember i'th map
			candidateMapsUnsorted[i] = candidateMapsUnsorted[minDistoIdx]; //update i'th map to be this next min-distortion map
			candidateMapsUnsorted[minDistoIdx] = tmp2; //remembered i'th map goes to the slot of the next min-distortion used just-above
		}
		if (candidsToFile)
		{
			fprintf(fPtr, "sorted distortions:\n");
			for (int i = 0; i < (int) allDistortions.size(); i++) fprintf(fPtr, "%f\n", allDistortions[i]);
			fclose(fPtr);
		}

		int topN = 5; //put the first topN entry of sorted candidateMapsUnsorted list into global candidateMaps to be used later in dense matches; keep this low 'cos after all i compare distortionByNormalizedGeodesics = true vs. false; so, if i track all maps, then dense distortion tactic will dominate the comparisons; if true/false is a good cost, then grd-truth to be tracked must be in top5 anyway, not in top20 'cos cost assigned good values
		for (int i = 0; i < topN; i++)
		{
			candidateMaps.push_back(candidateMapsUnsorted[i]);
//			for (int j = 0; j < (int) candidateMaps[i].size(); j += 2)
//				cout << candidateMaps[i][j] << "\t" << candidateMaps[i][j+1] << endl;
//			cout << allDistortions[i] << "\n\n";
		}
		//candidateMaps[] has topN min-distortion 5x5 combinatorial maps, each of which will create dense maps and get evaluated to decide which is the best
		bestCorresp = candidateMaps[ trackCandidateMaps(mesh2) ];
	}
	
	//now that bestCorresp is ready, fill the matchIdx values and safeCorresp
	cout << "\nbestCorresp for complete model baseVerts vs. cropped model iBaseVerts:\n";
	safeCorresp.clear();
	for (int s = 0; s < (int) bestCorresp.size() / 2; s++)
	{
		int bv1 = bestCorresp[2*s], bv2 = bestCorresp[2*s + 1]; //even idx for mesh1, odds for mesh2
		verts[bv1]->matchIdx = bv2;
		mesh2->verts[bv2]->matchIdx = bv1;
		safeCorresp.push_back(bv1);
		safeCorresp.push_back(bv2); //even idxs of safeCorresp and interestingMatches must be this mesh and odds be mesh2
		cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
	}	

	//make v.matchCostI values ready (to be fprinted later)
	if (distortionByNormalizedGeodesics)
	{
		//maxDist1/2 need to be found first to be able to call getCorrespListDistortion()
		float maxDist1 = -INF, maxDist2 = -INF;
		//find the max geo dist b/w baseVerts (this mesh1 side) to be used as a normalizer in getCorrespListDistortion()								
		for (int i = 0; i < (int) safeCorresp.size(); i += 2)
			for (int j = 2; j < (int) safeCorresp.size(); j += 2) //j=i more efficient but anyway
				if (verts[ safeCorresp[i] ]->dSpanningUnn[ safeCorresp[j] ] > maxDist1)
					maxDist1 = verts[ safeCorresp[i] ]->dSpanningUnn[ safeCorresp[j] ];
		//find the max geo dist b/w iBaseVerts (from mesh2 side) to be used as a normalizer in getCorrespListDistortion()
		for (int i = 1; i < (int) safeCorresp.size(); i += 2)
			for (int j = 3; j < (int) safeCorresp.size(); j += 2)
				if (mesh2->verts[ safeCorresp[i] ]->dSpanningUnn[ safeCorresp[j] ] > maxDist2)
					maxDist2 = mesh2->verts[ safeCorresp[i] ]->dSpanningUnn[ safeCorresp[j] ];
		return getCorrespListDistortion(mesh2, safeCorresp, maxDist1, maxDist2);
	}
	else
		return getCorrespListScaleInvDistortion(mesh2, safeCorresp);
}

int Mesh::trackCandidateMaps(Mesh* mesh2)
{
	//each 5x5 candidate map brings 2 meshes to the same scale wherein dense map is found; distortions of these dense maps let me decide the bestCandidate to be used later; heavily adapted from partialMatching()

	//store the originals
	baseVerts2 = baseVerts;
	mesh2->baseVerts2 = mesh2->baseVerts;	

	//one of candidateMaps'll be selected based on dense map distortions
	float minDenseDisto = INF;
	int minDenseDistoIdx = -1;
	for (int cm = 0; cm < (int) candidateMaps.size(); cm++)
	{
		vector< int > mapInTest = candidateMaps[cm];
		baseVerts.clear(); //baseVerts to be filled again for 120x120 blossom samples plus baseVerts2 below
		mesh2->baseVerts.clear();
		/////////////////////// extending 5x5 combinatorial match to a denser one //////////////////////////////////
		//save the original mesh2 coords before they are exposed to scalings below; when done w/ this denser matching, i roll-back to original coords to display the originals
		for (int i = 0; i < (int) mesh2->verts.size(); i++)
		{
			if (! mesh2->verts[i]->coords2) //laplacian smoothing already filled coords2
				mesh2->verts[i]->coords2 = new float[3]; //initialize coords2, if not inited yet, which holds the original coords no matter what happens to coords[]
			for (int c = 0; c < 3; c++)
				mesh2->verts[i]->coords2[c] = mesh2->verts[i]->coords[c];
		}
		//below heavily adapted from matchBasesViaVoting2()
		//bring meshes to the same scale by multiplying mesh2 via scaler which uses all C(5,2) distances assuming mapInTest.size=10
		float scaler = 0.0f, nAdds = 0.0f;
		for (int a = 0; a < (int) mapInTest.size(); a += 2)
			for (int b = a+2; b < (int) mapInTest.size(); b += 2)
			{
				scaler += (verts[ mapInTest[a] ]->dSpanningUnn[ mapInTest[b] ] / mesh2->verts[ mapInTest[a+1] ]->dSpanningUnn[ mapInTest[b+1] ]);
				nAdds++;
			}
		scaler /= nAdds;	
		for (int i = 0; i < (int) mesh2->verts.size(); i++)
		{
			mesh2->verts[i]->coords[0] *= scaler;
			mesh2->verts[i]->coords[1] *= scaler;
			mesh2->verts[i]->coords[2] *= scaler;
			mesh2->verts[i]->baseCandidate = true; //= false in matchBasesViaVoting2() 'cos computeRegionBaseVerts2() expects so
		}
		for (int i = 0; i < (int) verts.size(); i++)
			verts[i]->baseCandidate = true;
		for (int e = 0; e < (int) mesh2->edges.size(); e++)
			mesh2->edges[e]->length = distanceBetween(mesh2->verts[ mesh2->edges[e]->v1i ]->coords, mesh2->verts[ mesh2->edges[e]->v2i ]->coords);
		for (int e = 0; e < (int) mesh2->sedges.size(); e++)
			mesh2->sedges[e]->length = distanceBetween(mesh2->verts[ mesh2->sedges[e]->v1i ]->coords, mesh2->verts[ mesh2->sedges[e]->v2i ]->coords);
		float wholeSurfaceArea = 0.0f;
		for (int t = 0; t < (int) tris.size(); t++)
			wholeSurfaceArea += tris[t]->area;
		float r1 = 0.171f * sqrt(wholeSurfaceArea / PI); //120 region bases on complete model; w/ this same radius i expect to create less bases on cropped model mesh2, which in turn leads to a 120x120 blossom in the worst case
//		sortCurvatures(); //enable this to use COES sampling just-below
		computeBaseVerts(r1, true, false);
		mesh2->computeBaseVerts(r1, true, false);

		//////////////////////// dense base verts matching ////////////////////////
		//create dense blossom map b/w dense baseVerts & mesh2.baseVerts and evaluate the resulting map to infer the quality of the initial generating combinatorial match
		//relevant geodesic distances of mesh2 are brought to the same of mesh1.geodesics using scaler and stored in dSpanningUnnSS
		for (int y = 0; y < (int) mesh2->baseVerts2.size(); y++)
		{
			if (! mesh2->verts[ mesh2->baseVerts2[y] ]->dSpanningUnnSS)
				mesh2->verts[ mesh2->baseVerts2[y] ]->dSpanningUnnSS = new float[ (int) mesh2->verts.size() ];
			for (int v = 0; v < (int) mesh2->verts.size(); v++)
				mesh2->verts[ mesh2->baseVerts2[y] ]->dSpanningUnnSS[v] = mesh2->verts[ mesh2->baseVerts2[y] ]->dSpanningUnn[v] * scaler;
		}
		//maxExtremeDist2 *= scaler; i could easily compute maxExtremeDist1/2 above while doing scaler += additions but won't use this additional normalization 'cos ~5 samples from combinatorial matching are not necessary the extremes which makes normalization slightly wrong
		int n1 = (int) baseVerts.size(), n2 = (int) mesh2->baseVerts.size();
		int nRows = (n1 > n2 ? n1 : n2), nCols = nRows;
		int* vIdx1 = new int[n1], * vIdx2 = new int[n2];
		float** costs = new float*[nRows];
		float discourage = INF; //must be INF 'cos entries are not in the [0,1] interval as i don't do the maxExtremeDist1/2 normalizations
		int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e, * edges = new int[2*nEdgeNum], * matches = new int[nVertNum];
		double* weightsD = new double[nEdgeNum];
		int row = 0;
		for (int i = 0; i < n1; i++)
		{
			costs[row] = new float[nCols];
			vIdx1[row] = baseVerts[i];
			int col = 0;
			for (int j = 0; j < n2; j++)
				vIdx2[col++] = mesh2->baseVerts[j];
			while (col < nCols)
				costs[row][col++] = discourage;
			row++;
		}
		while (row < nRows)
		{
			costs[row] = new float[nCols];
			for (int col = 0; col < nCols; col++)
				costs[row][col] = discourage;
			row++;
		}
		row = 0;
		for (int i = 0; i < n1; i++)
		{
			int base1 = baseVerts[i];
			verts[base1]->matchIdxs.clear(); //start a fresh matchIdxs list (already empty/fresh but anyway be safe) to store the blossom costs to be used in denseDistortion computation below
			int col = 0;
			for (int j = 0; j < n2; j++)
			{
				int base2 = mesh2->baseVerts[j];
				//bool combinatorialMatch = false; for (int y = 0; y < (int) mapInTest.size(); y += 2) if (mapInTest[y] == base1 && mapInTest[y+1] == base2) combinatorialMatch = true;
				float vertexBasedIso = 0.0f;
				int nAdds = 0;
				for (int ss = 0; ss < (int) mapInTest.size(); ss += 2)
				{
					int s1 = mapInTest[ss], s2 = mapInTest[ss + 1];
					//if (s1 == base1) continue; not done here 'cos unlike getCorrespListDistortion() i don't just compute distortion of a given mapping here; rather, i create all possible pairwise distortions using a safe list in mapInTest which requires no continue here
					float t1s1 = verts[s1]->dSpanningUnn[base1], t2s2 = mesh2->verts[s2]->dSpanningUnnSS[base2]; //t1s1 = verts[s1]->dSpanningUnn[base1] / maxExtremeDist1, t2s2 = mesh2->verts[s2]->dSpanningUnnSS[base2] / maxExtremeDist2;
					vertexBasedIso += fabs(t1s1 - t2s2);
					nAdds++;				
				}
if (nAdds <= 0) {cout << "can't traverse 0-size\n";exit(0);}
				vertexBasedIso /= nAdds;
				//combinatorial matches will have vertexBasedIso=0 for sure'cos both t1s1 and t2s2'll be 0 above; so, they're encouraged already
				costs[row][col++] = vertexBasedIso; //(! combinatorialMatch ? vertexBasedIso : -INF); //encourage combinatorial matches (initial ~5 samples in baseVerts2) to be selected by blossom
				verts[base1]->matchIdxs.push_back((float) base2);
				verts[base1]->matchIdxs.push_back(vertexBasedIso);
			}
			row++;
		}
		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* blossom = new Blossom();
		blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches);
		delete blossom;
		float denseDistortion = 0.0f, nMatches = 0.0f;
		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)
				{
					//no need to keep denseMap matches explicitly 'cos only interested in dense map distortion					
					//match and its cost
					//learn the individual distortion used in blossom mtrx to use it in matchIdxs2
					float vertexBasedIso = -1.0f;
					for (int j = 0; j < (int) verts[ vIdx1[m1] ]->matchIdxs.size(); j += 2)
						if (verts[ vIdx1[m1] ]->matchIdxs[j] == mi)
						{
							vertexBasedIso = verts[ vIdx1[m1] ]->matchIdxs[j+1];
							break;
						}
if (vertexBasedIso == -1.0f){cout<<"\n\n\n\n\nsth wrong\n";exit(0);}
					denseDistortion += vertexBasedIso;
					nMatches++;
				}
			}
		} //end of m
		denseDistortion /= nMatches;
		denseDistortion /= mesh2->maxGeoDistG; //distortion computed on distances that are not necessarily in [0,1] after they're brought to the same scale; to make distortion in [0,1] div by summation by mesh2.maxGeo 'cos dense map is supposed to span the cropped model mesh2 and hence normalized distortion is interpreted w/ max geo on mesh2 = 1
		cout << "distortion of the dense map of size " << nMatches << " due to track # " << cm << " = " << denseDistortion << "\n";
		if (denseDistortion < minDenseDisto)
		{
			minDenseDisto = denseDistortion;
			minDenseDistoIdx = cm;
		}
		//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;
		//////////////// desiredMap provides the regions on which dense maps are done ends ////////////////

		//roll-back to original coords 'cos mesh2 coords scaled above and i want to display the originals (recall that originals also multiplied at the top of this function (scaleBy()) for scale demo, so i'm returning to these multiplied original coords indeed)
		for (int v = 0; v < (int) mesh2->verts.size(); v++)
			for (int c = 0; c < 3; c++)
				mesh2->verts[v]->coords[c] = mesh2->verts[v]->coords2[c];
		/////////////////////// extending 5x5 combinatorial match to a denser one ends //////////////////////////////////
	} //end of cm

	//roll-back to the original baseVerts which are needed for the upcoming calls
	baseVerts = baseVerts2;
	mesh2->baseVerts = mesh2->baseVerts2;

	return minDenseDistoIdx;
}

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, "A_%d %d.dat", frameID, n);
	FILE* fPtr = fopen(fName, "w");
	sprintf(fName, "bases_%d.dat", frameID); //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";
}

inline float Mesh::getCorrespListDistortion(Mesh* bMesh, vector< int > tmpSafeCorrespTry, float maxDist1, float maxDist2)
{
	//returns D_iso = distortion, i.e. subtraction of normalized (by maxDist1 & maxDist2) geodesics, isometryCost of the correspondence lying in the tmpSafeCorrespTry list using tmpSafeCorrespTry itself as the traversalList
	//a		c'	cannot be healed here or in getCorrespListScaleInvDistortion(); if i use parent patches corresps as the traversalList then this can be fixed (getCorrespListDistortionCP()) 'cos i need more samples (here sym flip happens if samples separated by same dists up to scaling)
	//b		b'
	//c		a'

	float isometryCostTry = 0.0f;
	int n = (int) tmpSafeCorrespTry.size() / 2;
	for (int s = 0; s < n; s++)
	{
		int bvs = tmpSafeCorrespTry[2*s], bvb = tmpSafeCorrespTry[2*s + 1]; //tmpSafeCorrespTry[even idxs] ~ sMesh and [odd] ~ bMesh
		float vertexBasedIso = 0.0f;
		for (int ss = 0; ss < n; ss++) //traversal
		{
			int s1 = tmpSafeCorrespTry[2*ss], s2 = tmpSafeCorrespTry[2*ss + 1];
			if (s1 == bvs) //when bvs-bvb is a bad match, s1-s2 will wrongly award it by += 0 effect when s1=bvs (and hence s2=bvb); to prevent this, skip s1=bvs case; if bvs-bvb is a good match then this += 0 has no harm
				continue;

			//if bvs vs. bvb is a good correspondence, then geoDist(bvs, s1) = geoDist(bvb, s2) for all s1 & s2 in tmpSafeCorrespTry
			float t1s1 = verts[bvs]->dSpanningUnn[s1] / maxDist1, t2s2 = bMesh->verts[bvb]->dSpanningUnn[s2] / maxDist2; //getCorrespListDistortion() called over sMesh; so, verts[bvs]->dSpanningUnn is indeed sMesh.verts[bvs]->dSpanningUnn
			vertexBasedIso += fabs(t1s1 - t2s2); //normalized difference here is small if tbv1 vs. tbv2 is a good correspondence
		}
		if (n != 1) //no div by zero
			vertexBasedIso /= (n-1); //in computation of vertexBasedIso of bvs-bvb, 1 match, bvs-bvb itself, is skipped during traversal, and therefore n-1 += performed
		else
			vertexBasedIso = 0.0f; //due to symmetry problem, all costs will be the same and hence best selected arbitrarily
		isometryCostTry += vertexBasedIso;

		verts[bvs]->matchCostI = bMesh->verts[bvb]->matchCostI = vertexBasedIso; //for the last call in isometricPartMatching() that sets matchCostI for fprinting purposes
	} //end of s
	//overall isometry cost of this tmpSafeCorrespTry combination
	isometryCostTry /= n;

	return isometryCostTry;
}
inline float Mesh::getCorrespListDistortionSS(Mesh* bMesh, vector< int > tmpSafeCorrespTry, float maxDistS, float maxDistB, bool avg, bool sameConfToAll, float w25)
{
	//similar to getCorrespListDistortion() finds distortion of tmpSafeCorrespTry except this uses bMesh.dSpanningUnn[] vs. sMesh.dSpanningUnnSS[]

	float isometryCostTry = 0.0f, maxDisto = -INF;
	int n = (int) tmpSafeCorrespTry.size() / 2;
	for (int s = 0; s < n; s++)
	{
		int bvs = tmpSafeCorrespTry[2*s + 1], bvb = tmpSafeCorrespTry[2*s]; //tmpSafeCorrespTry[even idxs] ~ bMesh and [odd] ~ sMesh

//disable for not bothering w/ this timing trick 'cos it does not cost too much time as most of the regional bases overlap and therefore dijkstraShortestPaths() returns immediately; real timing is on computeRegionBaseVerts() i guess
if (! bMesh->verts[bvb]->dSpanningUnn || ! verts[bvs]->dSpanningUnnSS) continue; //go below only for i-p, j-r, or k-s pairs (for which dSpanningUnn/SS defined for sure) 'cos i'm interested in their individual distortions only (which is computed by traversing 40x40 list)	

		float vertexBasedIso = 0.0f;
		for (int ss = 0; ss < n; ss++) //traversal
		{
			int s1 = tmpSafeCorrespTry[2*ss + 1], s2 = tmpSafeCorrespTry[2*ss]; //s1 ~ sMesh, s2 ~ bMesh
			if (s1 == bvs) //when bvs-bvb is a bad match, s1-s2 will wrongly award it by += 0 effect when s1=bvs (and hence s2=bvb); to prevent this, skip s1=bvs case; if bvs-bvb is a good match then this += 0 has no harm
				continue;

			//if bvs vs. bvb is a good correspondence, then geoDist(bvs, s1) = geoDist(bvb, s2) for all s1 & s2 in tmpSafeCorrespTry
			float t1s1 = verts[bvs]->dSpanningUnnSS[s1] / maxDistS, t2s2 = bMesh->verts[bvb]->dSpanningUnn[s2] / maxDistB; //getCorrespListDistortion() called over sMesh; so, verts[bvs]->dSpanningUnnSS is indeed sMesh.verts[bvs]->dSpanningUnnSS
			vertexBasedIso += fabs(t1s1 - t2s2); //normalized difference here is small if tbv1 vs. tbv2 is a good correspondence
		}
		if (n != 1) //no div by zero
			vertexBasedIso /= (n-1); //in computation of vertexBasedIso of bvs-bvb, 1 match, bvs-bvb itself, is skipped during traversal, and therefore n-1 += performed
		else
			vertexBasedIso = 0.0f; //due to symmetry problem, all costs will be the same and hence best selected arbitrarily
		isometryCostTry += vertexBasedIso;

		if (vertexBasedIso > maxDisto)
			maxDisto = vertexBasedIso;

		if (! sameConfToAll)
		{
			if (w25 == -1.0f) //no w7*d7 of w7*d7 + w25*d2 available; so, no += will be done; just set i.confidence to this current vertexBasedIso based confidence
				bMesh->verts[bvb]->confidence = pow(EXP, -vertexBasedIso); //individual distortion of this bMesh region base is stored as its individual confidence: the more it is the less the confidence (redundant v.confidences set as well)
			else //if (bvb == e1 || bvb == e2 || bvb == e3) //prevent redundant assignments below 'cos only 3 elements in tmpSafeCorrespTry[even idxs] are 3 extremes i, j, and k; other 40-3=37 redundant; these redundants not affect distortion2 accuracy, but may violate normalizers 'cos normalizers'll be applied only to nonredundant extremes when this returns
			{
//float wd2 = w25 * pow(EXP, -vertexBasedIso); //weighted d2 (40x40 traversal based distortion)
//bMesh->verts[bvb]->confidence += wd2; //now v.confidence becomes w7*d7 + w25*d2 (note that d7 and d2 are specific to v) (redundant v.confidences set as well 'cos only 3 elements in tmpSafeCorrespTry[even idxs] are 3 extremes i, j, and k; other 40-3=37 redundant)
				//not update v.confidence at this time!!!!!!!! use normalized vertexBasedIso in [0,1] to make weighted v.confidence computation healthier; so, remember this distorton2/5 (i-p traverses a safeList of 40x40 blossom dense map)
				bMesh->verts[bvb]->distortion2 = vertexBasedIso; //bvb is one of tmpSafeCorrespTry[3 of all even idx], i.e., bMesh extreme				
				/*//distortion2 normalizers [normalization not in use as explained in header file definitions]
				if (vertexBasedIso > bMesh->maxDistortion2)
					bMesh->maxDistortion2 = vertexBasedIso;
				if (vertexBasedIso < bMesh->minDistortion2)
					bMesh->minDistortion2 = vertexBasedIso;//*/
			}
		}
	} //end of s
	//overall isometry cost of this tmpSafeCorrespTry combination
	isometryCostTry /= n;

	if (avg)
		return isometryCostTry; //average distortion
	return maxDisto; //worst distortion which is more suitable for voting purposes in matchBasesViaVoting2()
}
inline float Mesh::getIndividualDistortion(Mesh* bMesh, int si, int bj, vector< int > tmpSafeCorrespTry, float maxDist1, float maxDist2)
{
	//same as getCorrespListDistortion() except this returns the individual distortion of si-bj match using traversalListTry as traversal list; therefore unnecessary stuff, e.g. isometryCostTry, are removed here

	int n = (int) tmpSafeCorrespTry.size() / 2;
	for (int s = 0; s < n; s++)
	{
		int bvs = tmpSafeCorrespTry[2*s], bvb = tmpSafeCorrespTry[2*s + 1]; //tmpSafeCorrespTry[even idxs] ~ sMesh and [odd] ~ bMesh
		//si-bj will come from traversalListTry as bvs-bvb; so, don't bother computation of other unasked individual distortions
		if (! (bvs == si && bvb == bj))
			continue;
		float vertexBasedIso = 0.0f; //of bvs-bvb match
		int nAdds = 0;
		for (int ss = 0; ss < n; ss++) //traversal
		{
			int s1 = tmpSafeCorrespTry[2*ss], s2 = tmpSafeCorrespTry[2*ss + 1];
			if (s1 == bvs) //when bvs-bvb is a bad match, s1-s2 will wrongly award it by += 0 effect when s1=bvs (and hence s2=bvb); to prevent this, skip s1=bvs case; if bvs-bvb is a good match then this += 0 has no harm
				continue;

			//if bvs vs. bvb is a good correspondence, then geoDist(bvs, s1) = geoDist(bvb, s2) for all s1 & s2 in tmpSafeCorrespTry
			float t1s1 = verts[bvs]->dSpanningUnn[s1] / maxDist1, t2s2 = bMesh->verts[bvb]->dSpanningUnn[s2] / maxDist2; //getCorrespListDistortion() called over sMesh; so, verts[bvs]->dSpanningUnn is indeed sMesh.verts[bvs]->dSpanningUnn
			vertexBasedIso += fabs(t1s1 - t2s2); //normalized difference here is small if tbv1 vs. tbv2 is a good correspondence
		}
		if (n != 1) //no div by zero
			vertexBasedIso /= (n-1); //in computation of vertexBasedIso of bvs-bvb, 1 match, bvs-bvb itself, is skipped during traversal, and therefore n-1 += performed
		else
			vertexBasedIso = 0.0f; //due to symmetry problem, all costs will be the same and hence best selected arbitrarily
		return vertexBasedIso;
	} //end of s

	cout << "si-bj not in traversalListTry; should never come here\n";
	exit(0);
}
inline float Mesh::getCorrespListScaleInvDistortion(Mesh* bMesh, vector< int > tmpSafeCorrespTry)
{
	//returns D_iso = scale-invariant distortion, i.e. subtraction of divided geodesics (= preservation of geodesic ratios), isometryCost of the correspondence lying in the tmpSafeCorrespTry list using tmpSafeCorrespTry itself as the traversalList

	float isometryCostTry = 0.0f, maxIsoCost = -INF;
	int m = (int) tmpSafeCorrespTry.size() / 2;
//cout << m << "\n\n";
	for (int s = 0; s < m; s++)
	{
		int bvs = tmpSafeCorrespTry[2*s], bvb = tmpSafeCorrespTry[2*s + 1]; //tmpSafeCorrespTry[even idxs] ~ sMesh and [odd] ~ bMesh
		float vertexBasedIso = 0.0f; //of bvs-bvb match
		int nAdds = 0;
		//for all 2-correspondences from tmpSafeCorrespTry other than bvs-bvb, decide a vertexBasedIso due to those 2 matches; there'll be C(m-1,2) combinations to be used as pairs matches (-1 to ignore bvs-bvb)
		for (int a = 0; a < m; a++)
			for (int b = a+1; b < m; b++)		
			{
				//current 2-correspondences are copied from appropriate slots of tmpSafeCorrespTry; let these 2 matches be s1-s2 & u1-u2
				int s1 = tmpSafeCorrespTry[2*a], s2 = tmpSafeCorrespTry[2*a + 1], //a'th match is s1-s2
					u1 = tmpSafeCorrespTry[2*b], u2 = tmpSafeCorrespTry[2*b + 1]; //b'th match is u1-u2
				if (s1 == bvs || u1 == bvs)
					continue; //2-corresps must be different from bvs-bvb whose vertexBasedIso is computed here; this is the -1 effect in C(m-1,2) 

				float numer1 = maxOf(verts[bvs]->dSpanningUnn[s1], bMesh->verts[bvb]->dSpanningUnn[s2]), //getCorrespListScaleInvDistortion() called over sMesh; so, verts[bvs]->dSpanningUnn is indeed sMesh.verts[bvs]->dSpanningUnn;
					  denom1 = minOf(verts[bvs]->dSpanningUnn[s1], bMesh->verts[bvb]->dSpanningUnn[s2]), //denominator has the bigger geodesic for both s1-s2 and u1-u2 for consistency
					  numer2 = maxOf(verts[bvs]->dSpanningUnn[u1], bMesh->verts[bvb]->dSpanningUnn[u2]),
					  denom2 = minOf(verts[bvs]->dSpanningUnn[u1], bMesh->verts[bvb]->dSpanningUnn[u2]);
				vertexBasedIso += fabs(numer1/denom1 - numer2/denom2); //= due to s1-s2 - due to u1-u2
				nAdds++;
if (numer1 == INF || numer2 == INF) {cout << "assure failed\n"; exit(0);} //assure mesh not have any disconnected components; if there is a disconnected part, then geodesic from a vert on that part to a vert on other part is left INF
			}
if (nAdds <= 0) {cout << "assure2 failed\n"; exit(0);}
		vertexBasedIso /= nAdds; //not required now 'cos all mappings are 5-5 due to my matcher, but will be required when i test mappings of varius sizes here, i.e. vertexBasedIso will be huge if i traverse a huge tmpSafeCorrespTry
		isometryCostTry += vertexBasedIso;

		verts[bvs]->matchCostI = bMesh->verts[bvb]->matchCostI = vertexBasedIso; //to decide the bad matches to be excluded from initial ~50x50 blossom matches for the upcoming reduced-size blossom
		if (vertexBasedIso > maxIsoCost)
		{
			worstMatchI = bvs;
			maxIsoCost = vertexBasedIso;
		}
	} //end of s

	//overall isometry cost of this tmpSafeCorrespTry
	isometryCostTry /= m; //save time by skipping division here since i only use return value for comparison, but anyway; # of += to isometryCostTry is m; so, div by m

	return isometryCostTry;
}
inline float Mesh::getIndividualScaleInvDistortion(Mesh* bMesh, int si, int bj, vector< int > traversalListTry)
{
	//same as getCorrespListScaleInvDistortion() except this returns the individual distortion of si-bj match using traversalListTry as traversal list; therefore unnecessary stuff, e.g. isometryCostTry, are removed here

	int m = (int) traversalListTry.size() / 2;
	for (int s = 0; s < m; s++)
	{		
		int bvs = traversalListTry[2*s], bvb = traversalListTry[2*s + 1]; //traversalListTry[even idxs] ~ sMesh and [odd] ~ bMesh; similarly, si ~ sMesh, bj ~ bMesh (small and big meshes)
		//si-bj will come from traversalListTry as bvs-bvb; so, don't bother computation of other unasked individual distortions
		if (! (bvs == si && bvb == bj))
			continue;
		float vertexBasedIso = 0.0f; //of bvs-bvb match
		int nAdds = 0;
		//for all 2-correspondences from traversalListTry other than bvs-bvb, decide a vertexBasedIso due to those 2 matches; there'll be C(m-1,2) combinations to be used as pairs matches (-1 to ignore bvs-bvb)
		for (int a = 0; a < m; a++)
			for (int b = a+1; b < m; b++)		
			{
				//current 2-correspondences are copied from appropriate slots of traversalListTry; let these 2 matches be s1-s2 & u1-u2
				int s1 = traversalListTry[2*a], s2 = traversalListTry[2*a + 1], //a'th match is s1-s2
					u1 = traversalListTry[2*b], u2 = traversalListTry[2*b + 1]; //b'th match is u1-u2
				if (s1 == bvs || u1 == bvs)
					continue; //2-corresps must be different from bvs-bvb whose vertexBasedIso is computed here; this is the -1 effect in C(m-1,2) 

				float numer1 = maxOf(verts[bvs]->dSpanningUnn[s1], bMesh->verts[bvb]->dSpanningUnn[s2]), //getCorrespListScaleInvDistortion() called over sMesh; so, verts[bvs]->dSpanningUnn is indeed sMesh.verts[bvs]->dSpanningUnn;
					  denom1 = minOf(verts[bvs]->dSpanningUnn[s1], bMesh->verts[bvb]->dSpanningUnn[s2]), //denominator has the bigger geodesic for both s1-s2 and u1-u2 for consistency
					  numer2 = maxOf(verts[bvs]->dSpanningUnn[u1], bMesh->verts[bvb]->dSpanningUnn[u2]),
					  denom2 = minOf(verts[bvs]->dSpanningUnn[u1], bMesh->verts[bvb]->dSpanningUnn[u2]);
				vertexBasedIso += fabs(numer1/denom1 - numer2/denom2); //= due to s1-s2 - due to u1-u2
				nAdds++;
assert(numer1 != INF && numer2 != INF); //assure mesh not have any disconnected components; if there is a disconnected part, then geodesic from a vert on that part to a vert on other part is left INF
			}
assert(nAdds > 0);
		vertexBasedIso /= nAdds; //not required now 'cos all mappings are 5-5 due to my matcher, but will be required when i test mappings of varius sizes here, i.e. vertexBasedIso will be huge if i traverse a huge traversalListTry
		return vertexBasedIso;
	} //end of s

	cout << "si-bj not in traversalListTry; should never come here\n";
	exit(0);
}

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::partialMatching2(Mesh* mesh2)
{
	//partial matching b/w partially overlapped 2 meshes
	
	cout << "\n\npartial matching in action......\n";

	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();

	//i will reduce initial 50x50 blossom mtrx (of 50 evenly-spaced bases) to 5x5 by moving the worst entry after each iteration (so picking the subset of size 5 from initial 50 bases); entries will be filled w/ d_iso, i.e. vertex-based scale-invariant isometric distortion of our ICCV paper (getCorrespListScaleInvDistortion());
	
	//to initialize 50x50 mtrx, i need a traversalList (of size 2+) to be used for d_iso computations (2+ 'cos d_iso needs at least 2 size; i'll use size 3 'cos it is fast to compute)
	//initial3Matches(mesh2); //sym problems very possible //initial4Matches(mesh2); //sym kinda handled but too costly
	initial2Matches(mesh2); //2 matches but w/ a different algo from just-above [4 secs]
	//initial3MatchesV2(mesh2); //3 matches w/ same algo in initial2Matches() [6 mins]

	//traversalList of size 2 is ready and stored at safeCorresp; use it for the initial blossom matching of n=50x50 evenly-spaced bases
	initial50Matches(mesh2); //via 2 trusted matches (in safeCorresp)	
//transformForVisual(mesh2);return; //see initial 50 matches based on 2-size traversalList of initial2Matches()

	//remember the ~50 bases matched above as i'll exclude the worst from base sets after each stage and end up w/ a very good 8x8 one-to-one mapping; then use that 8x8 to match the whole 50x50 again, but better; so save the initial bases
	vector< int > iBases1 = baseVerts, iBases2 = mesh2->baseVerts;
	int m = (int) baseVerts.size(), n = (int) mesh2->baseVerts.size();
	//fprint the results in sorted order w.r.t. matchCostI values via sortSafeCorresp1to1() below
	char* inputStr = "jumping";
	if (theInput == CENTAUR_HORSE)
		inputStr = "centaur-horse";
	else if (theInput == CENTAUR_DAVID)
		inputStr = "centaur-david";
	else if (theInput == CENTAUR_GIRL)
		inputStr = "centaur-girl";
	else if (theInput == GORILLA_GIRL)
			inputStr = "gorilla-girl";
	char fName[300]; sprintf(fName, "..\\..\\..\\code\\output\\PartialShapeCorresponder_x64\\%s\\C_%d-%d %dx%d.dat", inputStr, frameID, mesh2->frameID, (n < m ? n : m), (n < m ? n : m));

	//adaptiveTraversalList=false: traversalList of size 50-{worstMatch}=49 will be used for the current 49x49 blossom matching, i.e. same traversalList for each of 49x49 entries; generally, traversalList of the current matching is the matchList of the previous blossom matching
	//adaptiveTraversalList=true: adaptive traversalList per entry will be used for blossom mtrx; adaptive traversalList will involve nearby base nghbors only w/ a valid matchIdx value set in the previous blossom matching
	if (adaptiveTraversalList)
	{
		//s = v.baseNghbs (of size ~7, i.e. at most 2-ring base neighbors) is set and when it comes to compute d_iso(v,*) blossom mtrx entry, i use a subset of s (matchIDx=-1's excluded)
		computeBaseNeighbs();
		mesh2->computeBaseNeighbs();
	}

	int nStages = 0, maxStages = (int) baseVerts.size() -8, //reduce computed 1-to-1 matching size by 1 after each stage, doing it maxStages times; i.e. -8 makes me stop w/ 8 one-to-one matches in the very end
		minOfNM, topNduringStages = -1; //-1: go until maxStages and make topN marking below (original algo); x: shortcut at an stage intermediate stage which gives x matches (that are all assumed to be good, and no further exclusion)
//vector< int > traversalList8;
	while (nStages++ < maxStages)
	{
		//exclude endpnts of the worst match from the base vertex sets; exclude it from safeCorresp (traversalList to be used below) as well
		int m2 = excludeFromBases(worstMatchI),
			n2 = mesh2->excludeFromBases(verts[worstMatchI]->worstIin2);
		for (int i = 0; i < (int) safeCorresp.size() / 2; i++)
			if (safeCorresp[2*i] == worstMatchI) //this and the right-adjacent slot is the worst match to be removed
			{
				verts[ safeCorresp[2*i] ]->matchIdx = mesh2->verts[ safeCorresp[2*i + 1] ]->matchIdx = -1; //note that matchIdx2 remains intact so i can use it for adaptiveTraversalList case
				safeCorresp.erase(safeCorresp.begin() + 2*i); //erase(begin + 4) erases [4]
				safeCorresp.erase(safeCorresp.begin() + 2*i); //right-adjacent at 2i+1 now shifted to 2i
				i--; //handle vector shifting; redundant 'cos i'm breaking anyway
				break;
			}
		
		//~49x49 blossom matching of worst-excluded bases; safeCorresp argument just-below is the 49 trusted corresps of previous 50x50 blossom matching (worst excluded and hence 49 trusted corresps)
		float blossomCorrespErr = blossomOnBases(mesh2, safeCorresp); //adaptiveTraversalList=false: safeCorresp used as fixed traversalList and is now overwritten to keep ~49x49 matching
																	  //adaptiveTraversalList=true: safeCorresp not used as traversalList and is now overwritten to keep ~49x49 matching
		minOfNM = (n2 < m2 ? n2 : m2);
		cout << blossomCorrespErr << " for " << minOfNM << " x " << minOfNM << " blossom matching listed here:\n";

		//now that safeCorresp is ready, fill the matchIdx values appropriately
		//for (int i = 0; i < (int) verts.size(); i++) verts[i]->matchIdx = -1;for (int i = 0; i < (int) mesh2->verts.size(); i++) mesh2->verts[i]->matchIdx = -1; no need to reset 'cos i made matchIdx=-1 for the excluded above, and others'll be overwritten below
		for (int s = 0; s < (int) safeCorresp.size() / 2; s++)
		{
			int bvs = safeCorresp[2*s], bvt = safeCorresp[2*s + 1]; //even idx for source mesh1, odds for target mesh2
			verts[bvs]->matchIdx = verts[bvs]->matchIdx2 = bvt; //note that matchIdx2 keeps this most recent non -1 matchIdx value so i can use it for adaptiveTraversalList case
			mesh2->verts[bvt]->matchIdx = mesh2->verts[bvt]->matchIdx2 = bvs;
/*#ifdef DBG_DETAIL
			cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif*/
		} //end of s
		verts[worstMatchI]->worstIin2 = verts[worstMatchI]->matchIdx;
		cout << verts[worstMatchI]->matchCostI << " for " << worstMatchI << "-" << verts[worstMatchI]->worstIin2 << " worst match\n\n";

		if (topNduringStages == minOfNM)
			break;
//if(nStages==31)break;
	} //end of while
//traversalList8 = safeCorresp; //store it in case i prefer to use it as traversalList below
//transformForVisual(mesh2);return; //see 8x8 matches reduced from 50x50 of initial50Matches() via stages above

	if (topNduringStages == -1) //no shortcut done at stages above; so, we've a 8x8 good match after all stages; use it to get to good ~50x50 match, which may then be reduced/excluded
	{
		//initial ~50 bases that was reduced to 8x8 after stages above can now be matched again using very good 8x8 one-to-one mapping computed above
		baseVerts = iBases1;
		mesh2->baseVerts = iBases2;
		for (int i = 0; i < (int) baseVerts.size(); i++) verts[ baseVerts[i] ]->base = true;
		for (int i = 0; i < (int) mesh2->baseVerts.size(); i++) mesh2->verts[ mesh2->baseVerts[i] ]->base = true;
		float blossomCorrespErr = blossomOnBases(mesh2, safeCorresp); //initially 8x8 safeCorresp is now overwritten to keep ~50x50 matching
		cout << blossomCorrespErr << " for " << (n < m ? n : m) << " x " << (n < m ? n : m) << " blossom matching listed here:\n";
		for (int i = 0; i < (int) verts.size(); i++) verts[i]->matchIdx = -1; for (int i = 0; i < (int) mesh2->verts.size(); i++) mesh2->verts[i]->matchIdx = -1; //first reset to -1s to clear the effects of previous assignments
		for (int s = 0; s < (int) safeCorresp.size() / 2; s++)
		{
			int bvs = safeCorresp[2*s], bvt = safeCorresp[2*s + 1]; //even idx for source mesh1, odds for target mesh2
			verts[bvs]->matchIdx = verts[bvs]->matchIdx2 = bvt; //note that matchIdx2 keeps this most recent non -1 matchIdx value so i can use it for adaptiveTraversalList case
			mesh2->verts[bvt]->matchIdx = mesh2->verts[bvt]->matchIdx2 = bvs;
/*#ifdef DBG_DETAIL
			cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif*/
		} //end of s
		verts[worstMatchI]->worstIin2 = verts[worstMatchI]->matchIdx;
		cout << verts[worstMatchI]->matchCostI << " for " << worstMatchI << "-" << verts[worstMatchI]->worstIin2 << " worst match\n\n";
		int topN = 25; //keep the first topN good matches by marking them below (and therefore preventing from getting excluded below)
		sortSafeCorresp1to1(mesh2, fName, topN, blossomCorrespErr); //sorted results fprinted and topN=50 w.r.t. matchCostI marked, i.e., keep the bases belonging to first N matches
//transformForVisual(mesh2);return; //see initial 50 matches based on 8-size traversalList resulting from stages above (no exclusions from the initial base set)

		////////////// final exclusions ///////////////
		//exclude topN=falses from the base vertex sets; exclude them from safeCorresp as well
		for (int i = 0; i < (int) safeCorresp.size() / 2; i++)
			if (! verts[ safeCorresp[2*i] ]->topN) //this and the right-adjacent slot is not a topN base; so remove them
			{
				excludeFromBases(safeCorresp[2*i]),
				mesh2->excludeFromBases(safeCorresp[2*i + 1]);
				verts[ safeCorresp[2*i] ]->matchIdx = mesh2->verts[ safeCorresp[2*i + 1] ]->matchIdx = -1; //note that matchIdx2 remains intact so i can use it for adaptiveTraversalList case
				safeCorresp.erase(safeCorresp.begin() + 2*i); //erase(begin + 4) erases [4]
				safeCorresp.erase(safeCorresp.begin() + 2*i); //right-adjacent at 2i+1 now shifted to 2i
				i--; //handle vector shifting				
			}
		////////////// final exclusions end ///////////////

		//final blossom on excluded bases using (safeCorresp as traversalList) or simply calculation of distortion on of mapping (safeCorresp) b/w excluded bases
		bool finalBlossomOnExcludedBases = false;
		m = (int) baseVerts.size();
		n = (int) mesh2->baseVerts.size();
		if (finalBlossomOnExcludedBases)
			blossomCorrespErr = blossomOnBases(mesh2, safeCorresp); //safeCorresp is now overwritten to keep final matching
//blossomCorrespErr = blossomOnBases(mesh2, traversalList8); //use 8x8 traversalList8 instead of 45x45 traversalList (of final-excluded bases)
		else
			blossomCorrespErr = getCorrespListScaleInvDistortion(mesh2, safeCorresp); //naming sucks; blossomCorrespErr should have been correspErr
		cout << blossomCorrespErr << " for " << (n < m ? n : m) << " x " << (n < m ? n : m) << " final matching\n";
	
		//now that safeCorresp is ready, fill the matchIdx values appropriately
		for (int i = 0; i < (int) verts.size(); i++) verts[i]->matchIdx = -1;
		for (int i = 0; i < (int) mesh2->verts.size(); i++) mesh2->verts[i]->matchIdx = -1; //first reset to -1s to clear the effects of previous assignments
		for (int s = 0; s < (int) safeCorresp.size() / 2; s++)
		{
			int bvs = safeCorresp[2*s], bvt = safeCorresp[2*s + 1]; //even idx for source mesh1, odds for target mesh2
			verts[bvs]->matchIdx = verts[bvs]->matchIdx2 = bvt; //note that matchIdx2 keeps this most recent non -1 matchIdx value so i can use it for adaptiveTraversalList case [redundant here 'cos no more blossomOnBases() is coming]
			mesh2->verts[bvt]->matchIdx = mesh2->verts[bvt]->matchIdx2 = bvs;
//#ifdef DBG_DETAIL
//			cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
//#endif
		} //end of s

		verts[worstMatchI]->worstIin2 = verts[worstMatchI]->matchIdx;
		cout << verts[worstMatchI]->matchCostI << " for " << worstMatchI << "-" << verts[worstMatchI]->worstIin2 << " worst match\n\n";
		
		sprintf(fName, "..\\..\\..\\code\\output\\PartialShapeCorresponder_x64\\%s\\C_%d-%d %dx%d.dat", inputStr, frameID, mesh2->frameID, (n < m ? n : m), (n < m ? n : m)); //m & n changed from above
		sortSafeCorresp1to1(mesh2, fName, topN, blossomCorrespErr); //sorted results fprinted and top-topN w.r.t. matchCostI marked (marking redundant)
	}
	else
	{
		float correspErr = getCorrespListScaleInvDistortion(mesh2, safeCorresp);
		cout << correspErr << " for " << minOfNM << " x " << minOfNM << " final matching\n";
		
		sprintf(fName, "..\\..\\..\\code\\output\\PartialShapeCorresponder_x64\\%s\\C_%d-%d %dx%d.dat", inputStr, frameID, mesh2->frameID, minOfNM, minOfNM);
		sortSafeCorresp1to1(mesh2, fName, topNduringStages, correspErr); //sorted results fprinted and top-topNduringStages w.r.t. matchCostI marked (marking redundant)
	}
}

void Mesh::initial3Matches(Mesh* mesh2)
{
	//out of ~10 evenly-spaced extremity bases on both meshes, find the best triple of matches as traversalList (stored in safeCorresp) by evaluating all possible triplets via D_iso = getCorrespListScaleInvDistortion()
	
	cout << "initial traversalList of size 3 being computed..\n";

	//traversalList (of size 3) to be returned is kept in bestList
	vector< int > bestList;

	//~10 evenly-spaced extremity bases on both meshes
	int m = computeBaseVerts(),
		n = mesh2->computeBaseVerts();	

	//all traversalList combinations of size 3 will be tried; list that gives the min D_iso (getCorrespListScaleInvDistortion()) is our bestList; 3 is the min size to be checked by D_iso 'cos i use 3-2=1 of them as self-traversalList	
	cout << "C(" << m << " ,3) * C(" << n << ", 3)3! traversalListTry will be tried..\n";
	fillPermutationMtrxs();
	float minIsometryCost = INF;
//FILE* fPtr = fopen("dbg.dat", "w");
	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 baseVerts to tmpS (for tmp source)
				vector< int > tmpS; //holds the current triple of all C(m,3) triples
				tmpS.push_back(baseVerts[a]);
				tmpS.push_back(baseVerts[b]);
				tmpS.push_back(baseVerts[c]);

				//this fixed combination in tmpS will be tried w/ C(10,3)3! target tuple permutations, tmpT
				for (int d = 0; d < n; d++)
					for (int e = d+1; e < n; e++)
						for (int f = e+1; f < n; f++)
						{
							//current 3-combination is copied from appropriate slots of mesh2.baseVerts to tmpT (for tmp target)
							vector< int > tmpT; //holds the current tuple of all C(m,2) tuples
							tmpT.push_back(mesh2->baseVerts[d]);
							tmpT.push_back(mesh2->baseVerts[e]);
							tmpT.push_back(mesh2->baseVerts[f]);
							//int factR = 6; //3 factorial
							//try 3! reordering/permutations of tmpT w/ stable tmpS of size 3
							for (int r = 0; r < 6; r++)
							{
								//refill traversalListTry according to the current reordering (one of r! permutations)
								//get the overall isometryCost of this current triple combination matched w/ stable triple of fromMesh
								vector< int > traversalListTry;
								int idx1 = 0, idx2 = 0;
								//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
								for (int y = 0; y < 6; y++) //traversalListTry of size 3, hence 3*2=6
									if (y % 2 == 0)
										traversalListTry.push_back(tmpS[idx1++]); //stable from source side
									else
										traversalListTry.push_back(tmpT[ perms3[r][idx2++] ]);	//perms3[0] = {0,1,2}, perms3[1] = {1,0,2}, ..

								//evaluate this traversal list in trial
								//compute isometry cost of this 3-3 mapping and ..
								float isometryCostTry = getCorrespListScaleInvDistortion(mesh2, traversalListTry);
								//..save it if it's the best so far
								if (isometryCostTry < minIsometryCost)
								{
									minIsometryCost = isometryCostTry;
									bestList = traversalListTry;
								}
//if (tmpS[0]==5&&tmpS[1]==2370&&tmpS[2]==1578)fprintf(fPtr,"%d-%d %d-%d %d-%d\t\t%f\n", traversalListTry[0], traversalListTry[1], traversalListTry[2], traversalListTry[3], traversalListTry[4], traversalListTry[5], isometryCostTry);
							} //end of r
						} //end of f
			} //end of c
//fclose(fPtr);
	cout << "initial traversalList (to be used for 50x50 matching) w/ D_iso = " << minIsometryCost << ":\n";
	//now that bestCorresp is ready, fill the matchIdx valuess appropriately
	safeCorresp.clear();
	for (int s = 0; s < (int) bestList.size() / 2; s++)
	{
		int bvs = bestList[2*s], bvt = bestList[2*s + 1]; //even idx for source mesh1, odds for target mesh2
		verts[bvs]->matchIdx = bvt;
		mesh2->verts[bvt]->matchIdx = bvs;
		safeCorresp.push_back(bvs);
		safeCorresp.push_back(bvt); //even idxs of safeCorresp and interestingMatches must be source/this mesh
#ifdef DBG_DETAIL
		cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif
	} //end of s

//int i=0;safeCorresp[i++]=5;safeCorresp[i++]=3064;		safeCorresp[i++]=2370;safeCorresp[i++]=2460;		safeCorresp[i++]=1578;safeCorresp[i++]=1707;

	cout << "done!\n\n";
}
void Mesh::initial4Matches(Mesh* mesh2)
{
	//out of ~10 evenly-spaced extremity bases on both meshes, find the best quadruple of matches as traversalList (stored in safeCorresp) by evaluating all possible quadruple via D_iso = getCorrespListScaleInvDistortion()
	
	cout << "initial traversalList of size 4 being computed..\n";

	//traversalList (of size 4) to be returned is kept in bestList
	vector< int > bestList;

	//~10 evenly-spaced extremity bases on both meshes
	int m = computeBaseVerts(),
		n = mesh2->computeBaseVerts();	

	//all traversalList combinations of size 4 will be tried; list that gives the min D_iso (getCorrespListScaleInvDistortion()) is our bestList; 4 is the min size to be checked by D_iso 'cos i use 3-2=1 of them as self-traversalList	
	cout << "C(" << m << " ,4) * C(" << n << ", 4)4! traversalListTry will be tried..\n";
	fillPermutationMtrxs();
	float minIsometryCost = INF;
//FILE* fPtr = fopen("dbg.dat", "w");
	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 x = c+1; x < m; x++)
				{
					//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
					vector< int > tmpS; //holds the current quadruple of all C(m,4) quadruples
					tmpS.push_back(baseVerts[a]);
					tmpS.push_back(baseVerts[b]);
					tmpS.push_back(baseVerts[c]);
					tmpS.push_back(baseVerts[x]);

					//this fixed combination in tmpS will be tried w/ C(10,4)4! target tuple permutations, tmpT
					for (int d = 0; d < n; d++)
						for (int e = d+1; e < n; e++)
							for (int f = e+1; f < n; f++)
								for (int y = f+1; y < n; y++)
								{
									//current 3-combination is copied from appropriate slots of mesh2.baseVerts to tmpT (for tmp target)
									vector< int > tmpT; //holds the current quadruple of all C(m,2) quadruples
									tmpT.push_back(mesh2->baseVerts[d]);
									tmpT.push_back(mesh2->baseVerts[e]);
									tmpT.push_back(mesh2->baseVerts[f]);
									tmpT.push_back(mesh2->baseVerts[y]);

									//int factR = 24; //4 factorial
									//try 4! reordering/permutations of tmpT w/ stable tmpS of size 4
									for (int r = 0; r < 24; r++)
									{
										//refill traversalListTry according to the current reordering (one of r! permutations)
										//get the overall isometryCost of this current quadruple combination matched w/ stable quadruple of fromMesh
										vector< int > traversalListTry;
										int idx1 = 0, idx2 = 0;
										//stable quadruple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
										for (int y = 0; y < 8; y++) //traversalListTry of size 4, hence 4*2=8
											if (y % 2 == 0)
												traversalListTry.push_back(tmpS[idx1++]); //stable from source side
											else
												traversalListTry.push_back(tmpT[ perms4[r][idx2++] ]);	//perms4[0] = {0,1,2,3}, perms4[1] = {1,0,3,2}, ..

										//evaluate this traversal list in trial
										//compute isometry cost of this 4-4 mapping and ..
										float isometryCostTry = getCorrespListScaleInvDistortion(mesh2, traversalListTry);
										//..save it if it's the best so far
										if (isometryCostTry < minIsometryCost)
										{
											minIsometryCost = isometryCostTry;
											bestList = traversalListTry;
										}
//if (tmpS[0]==5&&tmpS[1]==2370&&tmpS[2]==1578)fprintf(fPtr,"%d-%d %d-%d %d-%d\t\t%f\n", traversalListTry[0], traversalListTry[1], traversalListTry[2], traversalListTry[3], traversalListTry[4], traversalListTry[5], isometryCostTry);
									} //end of r
								} //end of y
				} //end of x
//fclose(fPtr);
	cout << "initial traversalList (to be used for 50x50 matching) w/ D_iso = " << minIsometryCost << ":\n";
	//now that bestCorresp is ready, fill the matchIdx valuess appropriately
	safeCorresp.clear();
	for (int s = 0; s < (int) bestList.size() / 2; s++)
	{
		int bvs = bestList[2*s], bvt = bestList[2*s + 1]; //even idx for source mesh1, odds for target mesh2
		verts[bvs]->matchIdx = bvt;
		mesh2->verts[bvt]->matchIdx = bvs;
		safeCorresp.push_back(bvs);
		safeCorresp.push_back(bvt); //even idxs of safeCorresp and interestingMatches must be source/this mesh
#ifdef DBG_DETAIL
		cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif
	} //end of s

//int i=0;safeCorresp[i++]=5;safeCorresp[i++]=3064;		safeCorresp[i++]=2370;safeCorresp[i++]=2460;		safeCorresp[i++]=1578;safeCorresp[i++]=1707;

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

void Mesh::computePotentialTriplets(Mesh* sMesh, bool bestTriplet)
{
	//similar to initial3Matches() except faster 'cos this does not consider all mesh1 triplets w/ all sMesh triplets; it first finds potentail triplet combinations and then does the permutations only on potentials
	//aim is to fill potentialTriplets, all potential triplet of matches from the same part (if bestTriplet=true, best of them will be stored (not recommended as it may confuse at this initial stage))

	cout << "potential triplets being computed..\n";

	//~10 evenly-spaced extremity bases on both meshes
	int m = (int) baseVerts.size(), n = (int) sMesh->baseVerts.size(), nCombs = 0;

	//////////////////////// potentialTriplets forming //////////////////////////////////////
	//triplet a,b,c from bMesh and 1,2,3 from sMesh goes to the next 6 slots of potentialTriplets as a,1,b,2,c,3 if they are from the same part which is decided by avg geodesic dist (agd) function: if sum_i{normalized agd from i to other 2 pnts} ~same for both triplets, they're from the same part and make into potentialTriplets; normalize by max geo dist of 3 in sum_i{} which is possible if they're from the same part
	cout << "C(" << m << " ,3) * C(" << n << ", 3) triplets will be tried..\n";
	float minDiff = INF, maxDiff = -INF;
	for (int a = 0; a < m; a++)
		for (int b = a+1; b < m; b++)
			for (int c = b+1; c < m; c++)
			{
				float dist1 = verts[ baseVerts[a] ]->dSpanningUnn[ baseVerts[b] ],
					  dist2 = verts[ baseVerts[a] ]->dSpanningUnn[ baseVerts[c] ],
					  dist3 = verts[ baseVerts[b] ]->dSpanningUnn[ baseVerts[c] ], //so baseVerts[a-b-c] are 3 pnts in test here
					  maxDist = (dist1 > dist2 ? dist1 : dist2); //max of dist1 and 2
				maxDist = (maxDist > dist3 ? maxDist : dist3); //max of dist1 and 2 and 3
				//dist1 /= maxDist; dist2 /= maxDist; dist3 /= maxDist;
				float sum1 = //(dist1 + dist2)/2 + //= normalized dist + dist2 added for dists generated from a to other 2 verts and take avg by dividing by 2
							 //(dist1 + dist3)/2 + (dist2 + dist3)/2;
							 (dist1 + dist2 + dist3) / maxDist; //same w/ less operations

				//this fixed combination in tmpS will be tried w/ C(10,3)3! target tuple permutations, tmpT
				for (int d = 0; d < n; d++)
					for (int e = d+1; e < n; e++)
						for (int f = e+1; f < n; f++)
						{
							//same stuff for sMesh to get sum2
							dist1 = sMesh->verts[ sMesh->baseVerts[d] ]->dSpanningUnn[ sMesh->baseVerts[e] ];
							dist2 = sMesh->verts[ sMesh->baseVerts[d] ]->dSpanningUnn[ sMesh->baseVerts[f] ];
							dist3 = sMesh->verts[ sMesh->baseVerts[e] ]->dSpanningUnn[ sMesh->baseVerts[f] ];
							maxDist = (dist1 > dist2 ? dist1 : dist2);
							maxDist = (maxDist > dist3 ? maxDist : dist3);
							float sum2 = (dist1 + dist2 + dist3) / maxDist, //= 2 times dist1/maxDist + dist2/maxDist + dist3/maxDist (so, normalized dists added)
								  diff = fabs(sum1 - sum2);

//if(diff<0.0001)cout<<diff<<"\t\t"<<baseVerts[a]<<"\t"<<baseVerts[b]<<"\t"<<baseVerts[c]<<"\n"		<<sMesh->baseVerts[d]<<"\t"<<sMesh->baseVerts[e]<<"\t"<<sMesh->baseVerts[f]<<"\n\n";
//if (baseVerts[a] == 762 && baseVerts[b] == 2426 && baseVerts[c] == 3157)
//cout << baseVerts[a] << "-" << baseVerts[b] << "-" << baseVerts[c] << "\t\t" << sMesh->baseVerts[d] << "-" << sMesh->baseVerts[e] << "-" << sMesh->baseVerts[f] << "\t\t" << diff << endl;
							
							if (diff < POTENTIAL_THRESHOLD) //closeness threshold
							{
								potentialTriplets.push_back(baseVerts[a]);		potentialTriplets.push_back(sMesh->baseVerts[d]);
								potentialTriplets.push_back(baseVerts[b]);		potentialTriplets.push_back(sMesh->baseVerts[e]);
								potentialTriplets.push_back(baseVerts[c]);		potentialTriplets.push_back(sMesh->baseVerts[f]);

//if (sMesh->baseVerts[d] == 3181 || sMesh->baseVerts[e] == 3181 || sMesh->baseVerts[f] == 3181)
//cout << diff << ": " << baseVerts[a] << "\t" << baseVerts[b] << "\t" << baseVerts[c] << endl << sMesh->baseVerts[d] << "\t" << sMesh->baseVerts[e] << "\t" << sMesh->baseVerts[f] << "\n\n";
							}
							if (diff < minDiff)
								minDiff = diff;
							if (diff > maxDiff)
								maxDiff = diff;
							nCombs++; //just for info to see how much pruning done
						} //end of f
			} //end of c
	cout << (int) potentialTriplets.size() / 6 << " / " << nCombs << " potential triplets of matches\tmin, max= " << minDiff << ", " << maxDiff << "\ndone!\n";
	//////////////////////// potentialTriplets forming ends //////////////////////////////////////

	if (! bestTriplet)
	{
		cout << "done!\n\n";
		return; //no need to find the min-distortion mapping in potentialTriplets[]		
	}

	//match (of size 3) to be returned
	vector< int > bestList;	

	//all potential combinations of size 3 will be tried; list that gives the min D_iso (getCorrespListScaleInvDistortion()) is our bestList; 3 is the min size to be checked by D_iso 'cos i use 3-2=1 of them as self-traversalList
	fillPermutationMtrxs();
	float minIsometryCost = INF;
	int pts = (int) potentialTriplets.size();
	for (int a = 0; a < pts; a += 6) //6 consecutive entries make 1 triplet of matches, i.e. 3 matches; so, no 3 a-b-c loops for combinations here; just go linearly w/ += 6
	{
		//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
		vector< int > tmpS;
		tmpS.push_back(potentialTriplets[a]); //even idxs ~ bMesh
		tmpS.push_back(potentialTriplets[a+2]);
		tmpS.push_back(potentialTriplets[a+4]);
		//find the max geo dist b/w bMesh pnts in tmpS to be used as a normalizer in getCorrespListDistortion()
		float maxDist1 = -INF;
		for (int i = 0; i < (int) tmpS.size(); i++)
			for (int j = 1; j < (int) tmpS.size(); j++)
				if (verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ] > maxDist1)
					maxDist1 = verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ];

		//this fixed combination in tmpS will be tried w/ 3! target triplet permutations tmpT
		vector< int > tmpT;
		tmpT.push_back(potentialTriplets[a+1]); //odd idxs ~ sMesh
		tmpT.push_back(potentialTriplets[a+3]);
		tmpT.push_back(potentialTriplets[a+5]);
		//find the max geo dist b/w sMesh pnts in tmpT to be used as a normalizer in getCorrespListDistortion()
		float maxDist2 = -INF;
		for (int i = 0; i < (int) tmpT.size(); i++)
			for (int j = 1; j < (int) tmpT.size(); j++)
				if (sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ] > maxDist2)
					maxDist2 = sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ];

		//int factR = 6; //3 factorial
		//try 3! reordering/permutations of tmpT w/ stable tmpS of size 3
		for (int r = 0; r < 6; r++)
		{
			//refill traversalListTry according to the current reordering (one of r! permutations)
			//get the overall isometryCost of this current triple combination matched w/ stable triple of fromMesh
			vector< int > traversalListTry;
			int idx1 = 0, idx2 = 0;
			//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
			for (int y = 0; y < 6; y++) //traversalListTry of size 3, hence 3*2=6
				if (y % 2 == 0)
					traversalListTry.push_back(tmpS[idx1++]); //stable from source side
				else
					traversalListTry.push_back(tmpT[ perms3[r][idx2++] ]);	//perms3[0] = {0,1,2}, perms3[1] = {1,0,2}, ..

			//evaluate this traversal list in trial
			//compute isometry cost of this 3-3 mapping and ..
			float isometryCostTry = //getCorrespListScaleInvDistortion(sMesh, traversalListTry);
									getCorrespListDistortion(sMesh, traversalListTry, maxDist1, maxDist2); //naming in getCorrespListDistortion() parameter as bMesh sucks but not important
			//..save it if it's the best so far
			if (isometryCostTry < minIsometryCost)
			{
				minIsometryCost = isometryCostTry;
				bestList = traversalListTry;
			}
		} //end of r
	} //end of a

	cout << "initial 3 matches w/ D_iso = " << minIsometryCost << ":\n";
	//now that bestCorresp is ready, fill the matchIdx valuess appropriately
	safeCorresp.clear();
	for (int s = 0; s < (int) bestList.size() / 2; s++)
	{
		int bvs = bestList[2*s], bvt = bestList[2*s + 1]; //even idx for source bMesh, odds for target sMesh
		//verts[bvs]->matchIdx = bvt; sMesh->verts[bvt]->matchIdx = bvs; //cancel 'cos extendToDense() uses matchIdx values that stay untouched here and that are updated later
		safeCorresp.push_back((sMesh->targetMesh ? bvs : bvt));
		safeCorresp.push_back((sMesh->targetMesh ? bvt : bvs)); //even idxs of safeCorresp and interestingMatches must be source/this mesh
#ifdef DBG_DETAIL
		cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif
	} //end of s

//int i=0;safeCorresp[i++]=5;safeCorresp[i++]=3064;		safeCorresp[i++]=2370;safeCorresp[i++]=2460;		safeCorresp[i++]=1578;safeCorresp[i++]=1707;

	cout << "done!\n\n";
}
void Mesh::computePotentialQuadruples(Mesh* sMesh, bool bestQuadruple)
{
	//same as computePotentialTriplets() except this finds 4 matches
	
	cout << "potential quadruples being computed..\n";

	//~10 evenly-spaced extremity bases on both meshes
	int m = (int) baseVerts.size(), n = (int) sMesh->baseVerts.size(), nCombs = 0;

	//////////////////////// potentialQuadruples forming //////////////////////////////////////
	//triplet a,b,c,d from bMesh and 1,2,3,4 from sMesh goes to the next 8 slots of potentialQuadruples as a,1,b,2,c,3,d,4 if they are from the same part which is decided by avg geodesic dist (agd) function: if sum_i{normalized agd from i to other 2 pnts} ~same for both quadruples, they're from the same part and make into potentialQuadruples; normalize by max geo dist of 4 in sum_i{} which is possible if they're from the same part
	cout << "C(" << m << " ,4) * C(" << n << ", 4) quadruples will be tried..\n";
	float minDiff = INF, maxDiff = -INF, * dists = new float[6];
	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 t = c+1; t < m; t++)
				{
					int i = 0;
					dists[i++] = verts[ baseVerts[a] ]->dSpanningUnn[ baseVerts[b] ];
					dists[i++] = verts[ baseVerts[a] ]->dSpanningUnn[ baseVerts[c] ];
					dists[i++] = verts[ baseVerts[a] ]->dSpanningUnn[ baseVerts[t] ];
					dists[i++] = verts[ baseVerts[b] ]->dSpanningUnn[ baseVerts[c] ];
					dists[i++] = verts[ baseVerts[b] ]->dSpanningUnn[ baseVerts[t] ];
					dists[i++] = verts[ baseVerts[c] ]->dSpanningUnn[ baseVerts[t] ];
					//C(4,2)=6 different distances to be normalized by maxDist among them
					float maxDist = -INF, sum1 = 0.0f;
					for (i = 0; i < 6; i++)
					{
						sum1 += dists[i];
						if (dists[i] > maxDist)
						  maxDist = dists[i];
					}
					sum1 = 0.66f * sum1/maxDist; //2/3=.66 'cos each dists[] is added twice (one due to a-b and one due to b-a) and agd of each vert is divided by 3 for averaging 'cos geodesics from a to other 3 in use

					//this fixed combination in tmpS will be tried w/ C(10,3)3! target tuple permutations, tmpT
					for (int d = 0; d < n; d++)
						for (int e = d+1; e < n; e++)
							for (int f = e+1; f < n; f++)
								for (int u = f+1; u < n; u++)
								{
									//same stuff for sMesh to get sum2
									i = 0;
									dists[i++] = sMesh->verts[ sMesh->baseVerts[d] ]->dSpanningUnn[ sMesh->baseVerts[e] ];
									dists[i++] = sMesh->verts[ sMesh->baseVerts[d] ]->dSpanningUnn[ sMesh->baseVerts[f] ];
									dists[i++] = sMesh->verts[ sMesh->baseVerts[d] ]->dSpanningUnn[ sMesh->baseVerts[u] ];
									dists[i++] = sMesh->verts[ sMesh->baseVerts[e] ]->dSpanningUnn[ sMesh->baseVerts[f] ];
									dists[i++] = sMesh->verts[ sMesh->baseVerts[e] ]->dSpanningUnn[ sMesh->baseVerts[u] ];
									dists[i++] = sMesh->verts[ sMesh->baseVerts[f] ]->dSpanningUnn[ sMesh->baseVerts[u] ];
									maxDist = -INF;
									float sum2 = 0.0f;
									for (i = 0; i < 6; i++)
									{
										sum2 += dists[i];
										if (dists[i] > maxDist)
										  maxDist = dists[i];
									}
									sum2 = 0.66f * sum2/maxDist;
									float diff = fabs(sum1 - sum2);

//if(diff<0.061&&diff>0.06)cout<<diff<<"\t\t"<<baseVerts[a]<<"\t"<<baseVerts[b]<<"\t"<<baseVerts[c]<<"\t"<<baseVerts[t]<<"\n"		<<sMesh->baseVerts[d]<<"\t"<<sMesh->baseVerts[e]<<"\t"<<sMesh->baseVerts[f]<<"\t"<<sMesh->baseVerts[u]<<"\n\n";

									if (diff < POTENTIAL_THRESHOLD) //closeness threshold
									{
										potentialQuadruples.push_back(baseVerts[a]);		potentialQuadruples.push_back(sMesh->baseVerts[d]);
										potentialQuadruples.push_back(baseVerts[b]);		potentialQuadruples.push_back(sMesh->baseVerts[e]);
										potentialQuadruples.push_back(baseVerts[c]);		potentialQuadruples.push_back(sMesh->baseVerts[f]);
										potentialQuadruples.push_back(baseVerts[t]);		potentialQuadruples.push_back(sMesh->baseVerts[u]);
//cout << diff << ": " << baseVerts[a] << "\t" << baseVerts[b] << "\t" << baseVerts[c] << "\t" << baseVerts[t] << endl << sMesh->baseVerts[d] << "\t" << sMesh->baseVerts[e] << "\t" << sMesh->baseVerts[f] << "\t" << sMesh->baseVerts[u] << "\n\n";
									}
									if (diff < minDiff)
										minDiff = diff;
									if (diff > maxDiff)
										maxDiff = diff;
									nCombs++; //just for info to see how much pruning done
								} //end of u
				} //end of t
	cout << (int) potentialQuadruples.size() / 8 << " / " << nCombs << " potential quadruples of matches\tmin, max= " << minDiff << ", " << maxDiff << "\ndone!\n";
	delete [] dists;
	//////////////////////// potentialQuadruples forming ends //////////////////////////////////////

	if (! bestQuadruple)
	{
		cout << "done!\n\n";
		return; //no need to find the min-distortion mapping in potentialQuadruples[]
	}
	
	//match (of size 4) to be returned
	vector< int > bestList; //all potential quadruple of matches from the same part (best of them will be the bestList)

	//all potential combinations of size 4 will be tried; list that gives the min D_iso (getCorrespListScaleInvDistortion()) is our bestList; 3 is the min size to be checked by D_iso 'cos i use 3-2=1 of them as self-traversalList
	fillPermutationMtrxs();
	float minIsometryCost = INF;
	int pts = (int) potentialQuadruples.size();
	for (int a = 0; a < pts; a += 8) //8 consecutive entries make 1 quadruple of matches, i.e. 4 matches; so, no 4 a-b-c-d loops for combinations here; just go linearly w/ += 8
	{
		//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
		vector< int > tmpS;
		tmpS.push_back(potentialQuadruples[a]); //even idxs ~ bMesh
		tmpS.push_back(potentialQuadruples[a+2]);
		tmpS.push_back(potentialQuadruples[a+4]);
		tmpS.push_back(potentialQuadruples[a+6]);
		//find the max geo dist b/w bMesh pnts in tmpS to be used as a normalizer in getCorrespListDistortion()
		float maxDist1 = -INF;
		for (int i = 0; i < (int) tmpS.size(); i++)
			for (int j = 1; j < (int) tmpS.size(); j++)
				if (verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ] > maxDist1)
					maxDist1 = verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ];

		//this fixed combination in tmpS will be tried w/ 4! target quadruple permutations tmpT
		vector< int > tmpT;
		tmpT.push_back(potentialQuadruples[a+1]); //odd idxs ~ sMesh
		tmpT.push_back(potentialQuadruples[a+3]);
		tmpT.push_back(potentialQuadruples[a+5]);
		tmpT.push_back(potentialQuadruples[a+7]);
		//find the max geo dist b/w sMesh pnts in tmpT to be used as a normalizer in getCorrespListDistortion()
		float maxDist2 = -INF;
		for (int i = 0; i < (int) tmpT.size(); i++)
			for (int j = 1; j < (int) tmpT.size(); j++)
				if (sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ] > maxDist2)
					maxDist2 = sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ];

		//int factR = 24; //4 factorial
		//try 4! reordering/permutations of tmpT w/ stable tmpS of size 4
		for (int r = 0; r < 24; r++)
		{
			//refill traversalListTry according to the current reordering (one of r! permutations)
			//get the overall isometryCost of this current triple combination matched w/ stable triple of fromMesh
			vector< int > traversalListTry;
			int idx1 = 0, idx2 = 0;
			//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
			for (int y = 0; y < 8; y++) //traversalListTry of size 4, hence 4*2=8
				if (y % 2 == 0)
					traversalListTry.push_back(tmpS[idx1++]); //stable from source side
				else
					traversalListTry.push_back(tmpT[ perms4[r][idx2++] ]);	//perms4[0] = {0,1,2,3}, perms4[1] = {1,0,3,2}, ..

			//evaluate this traversal list in trial
			//compute isometry cost of this 4-4 mapping and ..
			float isometryCostTry = //getCorrespListScaleInvDistortion(sMesh, traversalListTry);
									getCorrespListDistortion(sMesh, traversalListTry, maxDist1, maxDist2); //naming in getCorrespListDistortion() parameter as bMesh sucks but not important
			//..save it if it's the best so far
			if (isometryCostTry < minIsometryCost)
			{
				minIsometryCost = isometryCostTry;
				bestList = traversalListTry;
			}
		} //end of r
	} //end of a

	cout << "initial 4 matches w/ D_iso = " << minIsometryCost << ":\n";
	//now that bestCorresp is ready, fill the matchIdx valuess appropriately
	safeCorresp.clear();
	for (int s = 0; s < (int) bestList.size() / 2; s++)
	{
		int bvs = bestList[2*s], bvt = bestList[2*s + 1]; //even idx for source bMesh, odds for target sMesh
		//verts[bvs]->matchIdx = bvt; sMesh->verts[bvt]->matchIdx = bvs; //cancel 'cos extendToDense() uses matchIdx values that stay untouched here and that are updated later
		safeCorresp.push_back((sMesh->targetMesh ? bvs : bvt));
		safeCorresp.push_back((sMesh->targetMesh ? bvt : bvs)); //even idxs of safeCorresp and interestingMatches must be source/this mesh
#ifdef DBG_DETAIL
		cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif
	} //end of s

	cout << "done!\n\n";
}
void Mesh::computePotentialQuintuples(Mesh* sMesh, bool bestQuintuple)
{
	//same as computePotentialQuadruples() except this finds 5 matches
	
	cout << "potential quintuples being computed..\n";

	//~10 evenly-spaced extremity bases on both meshes
	int m = (int) baseVerts.size(), n = (int) sMesh->baseVerts.size(), nCombs = 0;

	//////////////////////// potentialQuintuples forming //////////////////////////////////////
	//triplet a,b,c,d,e from bMesh and 1,2,3,4,5 from sMesh goes to the next 10 slots of potentialQuintuples as a,1,b,2,c,3,d,4,e,5 if they are from the same part which is decided by avg geodesic dist (agd) function: if sum_i{normalized agd from i to other 2 pnts} ~same for both quintuples, they're from the same part and make into potentialQuintuples; normalize by max geo dist of 5 in sum_i{} which is possible if they're from the same part
	cout << "C(" << m << ", 5) * C(" << n << ", 5) quintuples will be tried..\n";
	float minDiff = INF, maxDiff = -INF, * dists = new float[10];
	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 t = c+1; t < m; t++)
					for (int x = t+1; x < m; x++)
					{
						int i = 0;
						dists[i++] = verts[ baseVerts[a] ]->dSpanningUnn[ baseVerts[b] ];
						dists[i++] = verts[ baseVerts[a] ]->dSpanningUnn[ baseVerts[c] ];
						dists[i++] = verts[ baseVerts[a] ]->dSpanningUnn[ baseVerts[t] ];
						dists[i++] = verts[ baseVerts[a] ]->dSpanningUnn[ baseVerts[x] ];
						dists[i++] = verts[ baseVerts[b] ]->dSpanningUnn[ baseVerts[c] ];
						dists[i++] = verts[ baseVerts[b] ]->dSpanningUnn[ baseVerts[t] ];
						dists[i++] = verts[ baseVerts[b] ]->dSpanningUnn[ baseVerts[x] ];
						dists[i++] = verts[ baseVerts[c] ]->dSpanningUnn[ baseVerts[t] ];
						dists[i++] = verts[ baseVerts[c] ]->dSpanningUnn[ baseVerts[x] ];
						dists[i++] = verts[ baseVerts[t] ]->dSpanningUnn[ baseVerts[x] ];
						//C(5,2)=10 different distances to be normalized by maxDist among them
						float maxDist = -INF, sum1 = 0.0f;
						for (i = 0; i < 10; i++)
						{
							sum1 += dists[i];
							if (dists[i] > maxDist)
							  maxDist = dists[i];
						}
						sum1 = 0.5f * sum1/maxDist; //2/4=.5 'cos each dists[] is added twice (one due to a-b and one due to b-a) and agd of each vert is divided by 4 for averaging 'cos geodesics from a to other 4 in use

						//this fixed combination in tmpS will be tried w/ C(10,3)3! target tuple permutations, tmpT
						for (int d = 0; d < n; d++)
							for (int e = d+1; e < n; e++)
								for (int f = e+1; f < n; f++)
									for (int u = f+1; u < n; u++)
										for (int y = u+1; y < n; y++)
										{
											//same stuff for sMesh to get sum2
											i = 0;
											dists[i++] = sMesh->verts[ sMesh->baseVerts[d] ]->dSpanningUnn[ sMesh->baseVerts[e] ];
											dists[i++] = sMesh->verts[ sMesh->baseVerts[d] ]->dSpanningUnn[ sMesh->baseVerts[f] ];
											dists[i++] = sMesh->verts[ sMesh->baseVerts[d] ]->dSpanningUnn[ sMesh->baseVerts[u] ];
											dists[i++] = sMesh->verts[ sMesh->baseVerts[d] ]->dSpanningUnn[ sMesh->baseVerts[y] ];
											dists[i++] = sMesh->verts[ sMesh->baseVerts[e] ]->dSpanningUnn[ sMesh->baseVerts[f] ];
											dists[i++] = sMesh->verts[ sMesh->baseVerts[e] ]->dSpanningUnn[ sMesh->baseVerts[u] ];
											dists[i++] = sMesh->verts[ sMesh->baseVerts[e] ]->dSpanningUnn[ sMesh->baseVerts[y] ];
											dists[i++] = sMesh->verts[ sMesh->baseVerts[f] ]->dSpanningUnn[ sMesh->baseVerts[u] ];
											dists[i++] = sMesh->verts[ sMesh->baseVerts[f] ]->dSpanningUnn[ sMesh->baseVerts[y] ];
											dists[i++] = sMesh->verts[ sMesh->baseVerts[u] ]->dSpanningUnn[ sMesh->baseVerts[y] ];
											maxDist = -INF;
											float sum2 = 0.0f;
											for (i = 0; i < 10; i++)
											{
												sum2 += dists[i];
												if (dists[i] > maxDist)
												  maxDist = dists[i];
											}
											sum2 = 0.5f * sum2/maxDist;
											float diff = fabs(sum1 - sum2);
											if (diff < POTENTIAL_THRESHOLD) //closeness threshold
											{
												potentialQuintuples.push_back(baseVerts[a]);		potentialQuintuples.push_back(sMesh->baseVerts[d]);
												potentialQuintuples.push_back(baseVerts[b]);		potentialQuintuples.push_back(sMesh->baseVerts[e]);
												potentialQuintuples.push_back(baseVerts[c]);		potentialQuintuples.push_back(sMesh->baseVerts[f]);
												potentialQuintuples.push_back(baseVerts[t]);		potentialQuintuples.push_back(sMesh->baseVerts[u]);
												potentialQuintuples.push_back(baseVerts[x]);		potentialQuintuples.push_back(sMesh->baseVerts[y]);
//cout << diff << ": " << baseVerts[a] << "\t" << baseVerts[b] << "\t" << baseVerts[c] << "\t" << baseVerts[t] << "\t" << baseVerts[x] << endl << sMesh->baseVerts[d] << "\t" << sMesh->baseVerts[e] << "\t" << sMesh->baseVerts[f] << "\t" << sMesh->baseVerts[u] << "\t" << sMesh->baseVerts[y] << "\n\n";
											}
											if (diff < minDiff)
												minDiff = diff;
											if (diff > maxDiff)
												maxDiff = diff;
											nCombs++; //just for info to see how much pruning done
										} //end of y
					} //end of x
	cout << (int) potentialQuintuples.size() / 10 << " / " << nCombs << " potential quintuples of matches\tmin, max= " << minDiff << ", " << maxDiff << "\ndone!\n";
	delete [] dists;
	//////////////////////// potentialQuintuples forming ends //////////////////////////////////////

	if (! bestQuintuple)
	{
		cout << "done!\n\n";
		return; //no need to find the min-distortion mapping in potentialQuadruples[]		
	}
	
	//match (of size 5) to be returned
	vector< int > bestList; //all potential quintuple of matches from the same part (best of them will be the bestList)

	//all potential combinations of size 5 will be tried; list that gives the min D_iso (getCorrespListScaleInvDistortion()) is our bestList; 3 is the min size to be checked by D_iso 'cos i use 3-2=1 of them as self-traversalList
	fillPermutationMtrxs();
	float minIsometryCost = INF;
	int pts = (int) potentialQuintuples.size();
	for (int a = 0; a < pts; a += 10) //10 consecutive entries make 1 quintuple of matches, i.e. 5 matches; so, no 5 a-b-c-d-e loops for combinations here; just go linearly w/ += 10
	{
		//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
		vector< int > tmpS;
		tmpS.push_back(potentialQuintuples[a]); //even idxs ~ bMesh
		tmpS.push_back(potentialQuintuples[a+2]);
		tmpS.push_back(potentialQuintuples[a+4]);
		tmpS.push_back(potentialQuintuples[a+6]);
		tmpS.push_back(potentialQuintuples[a+8]);
		//find the max geo dist b/w bMesh pnts in tmpS to be used as a normalizer in getCorrespListDistortion()
		float maxDist1 = -INF;
		for (int i = 0; i < (int) tmpS.size(); i++)
			for (int j = 1; j < (int) tmpS.size(); j++)
				if (verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ] > maxDist1)
					maxDist1 = verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ];

		//this fixed combination in tmpS will be tried w/ 4! target quadruple permutations tmpT
		vector< int > tmpT;
		tmpT.push_back(potentialQuintuples[a+1]); //odd idxs ~ sMesh
		tmpT.push_back(potentialQuintuples[a+3]);
		tmpT.push_back(potentialQuintuples[a+5]);
		tmpT.push_back(potentialQuintuples[a+7]);
		tmpT.push_back(potentialQuintuples[a+9]);
		//find the max geo dist b/w sMesh pnts in tmpT to be used as a normalizer in getCorrespListDistortion()
		float maxDist2 = -INF;
		for (int i = 0; i < (int) tmpT.size(); i++)
			for (int j = 1; j < (int) tmpT.size(); j++)
				if (sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ] > maxDist2)
					maxDist2 = sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ];

		//int factR = 120; //5 factorial
		//try 5! reordering/permutations of tmpT w/ stable tmpS of size 5
		for (int r = 0; r < 120; r++)
		{
			//refill traversalListTry according to the current reordering (one of r! permutations)
			//get the overall isometryCost of this current triple combination matched w/ stable triple of fromMesh
			vector< int > traversalListTry;
			int idx1 = 0, idx2 = 0;
			//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
			for (int y = 0; y < 10; y++) //traversalListTry of size 5, hence 5*2=10
				if (y % 2 == 0)
					traversalListTry.push_back(tmpS[idx1++]); //stable from source side
				else
					traversalListTry.push_back(tmpT[ perms5[r][idx2++] ]);	//perms5[0] = {0,1,2,3,4}, perms4[1] = {1,0,3,4,2}, ..

			//evaluate this traversal list in trial
			//compute isometry cost of this 5-5 mapping and ..
			float isometryCostTry = //getCorrespListScaleInvDistortion(sMesh, traversalListTry);
									getCorrespListDistortion(sMesh, traversalListTry, maxDist1, maxDist2); //naming in getCorrespListDistortion() parameter as bMesh sucks but not important
			//..save it if it's the best so far
			if (isometryCostTry < minIsometryCost)
			{
				minIsometryCost = isometryCostTry;
				bestList = traversalListTry;
			}
		} //end of r
	} //end of a

	cout << "initial 5 matches w/ D_iso = " << minIsometryCost << ":\n";
	//now that bestCorresp is ready, fill the matchIdx valuess appropriately
	safeCorresp.clear();
	for (int s = 0; s < (int) bestList.size() / 2; s++)
	{
		int bvs = bestList[2*s], bvt = bestList[2*s + 1]; //even idx for source bMesh, odds for target sMesh
		//verts[bvs]->matchIdx = bvt; sMesh->verts[bvt]->matchIdx = bvs; //cancel 'cos extendToDense() uses matchIdx values that stay untouched here and that are updated later
		safeCorresp.push_back((sMesh->targetMesh ? bvs : bvt));
		safeCorresp.push_back((sMesh->targetMesh ? bvt : bvs)); //even idxs of safeCorresp and interestingMatches must be source/this mesh
#ifdef DBG_DETAIL
		cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif
	} //end of s

	cout << "done!\n\n";
}
void Mesh::computeAllPotentialsIncremental(Mesh* sMesh, int bestTuple)
{
	//fill potentialTriplets as before and then build potentialQuadruples on potentialTriplets, and then potentialQuintuples on potentialQuadruples

	cout << "potential tuples (3-4-5) being computed..\n";
		
	//~10 evenly-spaced extremity bases on both meshes
	int m = (int) baseVerts.size(), n = (int) sMesh->baseVerts.size(), nCombs = 0; //not all combinations, e.g. for potentialQuadruples forming, nCombs < m*n*potentialTriplets.size

	if (n < 3)
	{
		cout << "too few samples to be matched; use at least 3\n";
		exit(0);
	}

	//fill potentialTriplets (computeAllPotentialsIncremental() called over bMesh; so, this = bMesh)
	computePotentialTriplets(sMesh);


/*	for (int a = 0; a < (int) potentialTriplets.size(); a += 6)
		if (potentialTriplets[a] == 947 && potentialTriplets[a+2] == 1763 && potentialTriplets[a+4] == 2277)
			cout << potentialTriplets[a] << "-" << potentialTriplets[a+2] << "-" << potentialTriplets[a+4] << "\t\t" << potentialTriplets[a+1] << "-" << potentialTriplets[a+3] << "-" << potentialTriplets[a+5] << endl;
	exit(0);//*/

	if (n <= 4)
		return; //no quadruples can be generated w/ <= 4 samples

	//////////////////////// potentialQuadruples forming //////////////////////////////////////
	float* dists6 = new float[6], minDiff = INF, maxDiff = -INF;
	for (int i = 0; i < m; i++)
		for (int j = 0; j < n; j++)
			for (int a = 0; a < (int) potentialTriplets.size(); a += 6)
			{
				//current 3-combination + 1 fixed i of i-j in test is used in dSpanningUnn[]
				if (potentialTriplets[a] == baseVerts[i] || potentialTriplets[a+2] == baseVerts[i] || potentialTriplets[a+4] == baseVerts[i] ||
					potentialTriplets[a+1] == sMesh->baseVerts[j] || potentialTriplets[a+3] == sMesh->baseVerts[j] || potentialTriplets[a+5] == sMesh->baseVerts[j])
					continue; //no verts[ baseVerts[i] ]->dSpanningUnn[ baseVerts[i] ] (= 0) below

				int ii = 0;
				dists6[ii++] = verts[ potentialTriplets[a] ]->dSpanningUnn[ potentialTriplets[a+2] ];
				dists6[ii++] = verts[ potentialTriplets[a] ]->dSpanningUnn[ potentialTriplets[a+4] ];
				dists6[ii++] = verts[ potentialTriplets[a] ]->dSpanningUnn[ baseVerts[i] ];
				dists6[ii++] = verts[ potentialTriplets[a+2] ]->dSpanningUnn[ potentialTriplets[a+4] ];
				dists6[ii++] = verts[ potentialTriplets[a+2] ]->dSpanningUnn[ baseVerts[i] ];
				dists6[ii++] = verts[ potentialTriplets[a+4] ]->dSpanningUnn[ baseVerts[i] ];
				//C(4,2)=6 different distances to be normalized by maxDist among them
				float sum1 = 0.0f, maxDistB = -INF;
				for (ii = 0; ii < 6; ii++)
				{
					sum1 += dists6[ii];
					if (dists6[ii] > maxDistB)
						maxDistB = dists6[ii];
				}
				sum1 = 0.66f * sum1/maxDistB; //2/3=.66 'cos each dists[] is added twice (one due to a-b and one due to b-a) and agd of each vert is divided by 3 for averaging 'cos geodesics from a to other 3 in use

				//same stuff for sMesh to get sum2
				ii = 0;
				dists6[ii++] = sMesh->verts[ potentialTriplets[a+1] ]->dSpanningUnn[ potentialTriplets[a+3] ];
				dists6[ii++] = sMesh->verts[ potentialTriplets[a+1] ]->dSpanningUnn[ potentialTriplets[a+5] ];
				dists6[ii++] = sMesh->verts[ potentialTriplets[a+1] ]->dSpanningUnn[ sMesh->baseVerts[j] ];
				dists6[ii++] = sMesh->verts[ potentialTriplets[a+3] ]->dSpanningUnn[ potentialTriplets[a+5] ];
				dists6[ii++] = sMesh->verts[ potentialTriplets[a+3] ]->dSpanningUnn[ sMesh->baseVerts[j] ];
				dists6[ii++] = sMesh->verts[ potentialTriplets[a+5] ]->dSpanningUnn[ sMesh->baseVerts[j] ];

				float sum2 = 0.0f, maxDistS = -INF;
				for (ii = 0; ii < 6; ii++)
				{
					sum2 += dists6[ii];
					if (dists6[ii] > maxDistS)
						maxDistS = dists6[ii];
				}
				sum2 = 0.66f * sum2/maxDistS;
				float diff = fabs(sum1 - sum2);
				if (diff < POTENTIAL_THRESHOLD) //closeness threshold
				{
					bool quadExists = false; //if this pair of triplets overlaps w/ a prev triplet pair than this quad pair (bMesh.triplet + i + sMesh.triplet + j) may be the same as an existing quad pair; so, prevent insertion of this duplicate
					for (int k = 0; k < (int) potentialQuadruples.size(); k += 8)
					{
						//check potentialTriplets[a] (from bMesh part (evens)) w/ all 4 of this existing quadruple (from bMesh part (evens))
						if ((potentialQuadruples[k] == potentialTriplets[a] || potentialQuadruples[k+2] == potentialTriplets[a] || potentialQuadruples[k+4] == potentialTriplets[a] || potentialQuadruples[k+6] == potentialTriplets[a])
							&&
							//check potentialTriplets[a+2] (from bMesh part (evens)) w/ all 4 of this existing quadruple (from bMesh part (evens))
							(potentialQuadruples[k] == potentialTriplets[a+2] || potentialQuadruples[k+2] == potentialTriplets[a+2] || potentialQuadruples[k+4] == potentialTriplets[a+2] || potentialQuadruples[k+6] == potentialTriplets[a+2])
							&&
							//check potentialTriplets[a+4] (from bMesh part (evens)) w/ all 4 of this existing quadruple (from bMesh part (evens))
							(potentialQuadruples[k] == potentialTriplets[a+4] || potentialQuadruples[k+2] == potentialTriplets[a+4] || potentialQuadruples[k+4] == potentialTriplets[a+4] || potentialQuadruples[k+6] == potentialTriplets[a+4])
							&&
							//check baseVerts[i] (from bMesh part (evens)) w/ all 4 of this existing quadruple (from bMesh part (evens))
							(potentialQuadruples[k] == baseVerts[i] || potentialQuadruples[k+2] == baseVerts[i] || potentialQuadruples[k+4] == baseVerts[i] || potentialQuadruples[k+6] == baseVerts[i])
							&&

							//check potentialTriplets[a+1] (from sMesh part (odds)) w/ all 4 of this existing quadruple (sMesh side (odds))
							(potentialQuadruples[k+1] == potentialTriplets[a+1] || potentialQuadruples[k+3] == potentialTriplets[a+1] || potentialQuadruples[k+5] == potentialTriplets[a+1] || potentialQuadruples[k+7] == potentialTriplets[a+1])
							&&
							//check potentialTriplets[a+3] (from sMesh part (odds)) w/ all 4 of this existing quadruple (sMesh side (odds))
							(potentialQuadruples[k+1] == potentialTriplets[a+3] || potentialQuadruples[k+3] == potentialTriplets[a+3] || potentialQuadruples[k+5] == potentialTriplets[a+3] || potentialQuadruples[k+7] == potentialTriplets[a+3])
							&&
							//check potentialTriplets[a+5] (from sMesh part (odds)) w/ all 4 of this existing quadruple (sMesh side (odds))
							(potentialQuadruples[k+1] == potentialTriplets[a+5] || potentialQuadruples[k+3] == potentialTriplets[a+5] || potentialQuadruples[k+5] == potentialTriplets[a+5] || potentialQuadruples[k+7] == potentialTriplets[a+5])
							&&
							//check sMesh->baseVerts[j] (from sMesh part (odds)) w/ all 4 of this existing quadruple (sMesh side (odds))
							(potentialQuadruples[k+1] == sMesh->baseVerts[j] || potentialQuadruples[k+3] == sMesh->baseVerts[j] || potentialQuadruples[k+5] == sMesh->baseVerts[j] || potentialQuadruples[k+7] == sMesh->baseVerts[j]))
						{
							quadExists = true;
							break;
						}
					}
					if (! quadExists)
					{
						potentialQuadruples.push_back(potentialTriplets[a]);		potentialQuadruples.push_back(potentialTriplets[a+1]);
						potentialQuadruples.push_back(potentialTriplets[a+2]);		potentialQuadruples.push_back(potentialTriplets[a+3]);
						potentialQuadruples.push_back(potentialTriplets[a+4]);		potentialQuadruples.push_back(potentialTriplets[a+5]);
						potentialQuadruples.push_back(baseVerts[i]);				potentialQuadruples.push_back(sMesh->baseVerts[j]);
					}
				}
				if (diff < minDiff)
					minDiff = diff;
				if (diff > maxDiff)
					maxDiff = diff;
				nCombs++; //just for info to see how much pruning done
			} //end of a
	cout << (int) potentialQuadruples.size() / 8 << " / " << nCombs << " potential quadruples of matches\tmin, max= " << minDiff << ", " << maxDiff << "\ndone!\n";
	delete [] dists6;
	//////////////////////// potentialQuadruples forming ends //////////////////////////////////////

	if (n < 5)
		return; //no quintuples can be generated w/ less than 5 samples

	//////////////////////// potentialQuintuples forming //////////////////////////////////////
	float* dists10 = new float[10];
	minDiff = INF;
	maxDiff = -INF;
	nCombs = 0;
	for (int i = 0; i < m; i++)
		for (int j = 0; j < n; j++)
			for (int a = 0; a < (int) potentialQuadruples.size(); a += 8)
			{
				//current 4-combination + 1 fixed i of i-j in test is used in dSpanningUnn[]
				if (potentialQuadruples[a] == baseVerts[i] || potentialQuadruples[a+2] == baseVerts[i] || potentialQuadruples[a+4] == baseVerts[i] || potentialQuadruples[a+6] == baseVerts[i] ||
					potentialQuadruples[a+1] == sMesh->baseVerts[j] || potentialQuadruples[a+3] == sMesh->baseVerts[j] || potentialQuadruples[a+5] == sMesh->baseVerts[j] || potentialQuadruples[a+7] == sMesh->baseVerts[j])
					continue; //no verts[ baseVerts[i] ]->dSpanningUnn[ baseVerts[i] ] (= 0) below

				int ii = 0;
				dists10[ii++] = verts[ potentialQuadruples[a] ]->dSpanningUnn[ potentialQuadruples[a+2] ];
				dists10[ii++] = verts[ potentialQuadruples[a] ]->dSpanningUnn[ potentialQuadruples[a+4] ];
				dists10[ii++] = verts[ potentialQuadruples[a] ]->dSpanningUnn[ potentialQuadruples[a+6] ];
				dists10[ii++] = verts[ potentialQuadruples[a] ]->dSpanningUnn[ baseVerts[i] ];
				dists10[ii++] = verts[ potentialQuadruples[a+2] ]->dSpanningUnn[ potentialQuadruples[a+4] ];
				dists10[ii++] = verts[ potentialQuadruples[a+2] ]->dSpanningUnn[ potentialQuadruples[a+6] ];
				dists10[ii++] = verts[ potentialQuadruples[a+2] ]->dSpanningUnn[ baseVerts[i] ];
				dists10[ii++] = verts[ potentialQuadruples[a+4] ]->dSpanningUnn[ potentialQuadruples[a+6] ];
				dists10[ii++] = verts[ potentialQuadruples[a+4] ]->dSpanningUnn[ baseVerts[i] ];
				dists10[ii++] = verts[ potentialQuadruples[a+6] ]->dSpanningUnn[ baseVerts[i] ];
				float sum1 = 0.0f, maxDistB = -INF;
				for (ii = 0; ii < 10; ii++)
				{
					sum1 += dists10[ii];
					if (dists10[ii] > maxDistB)
						maxDistB = dists10[ii];
				}
				sum1 = 0.5f * sum1/maxDistB; //2/4=.5 'cos each dists[] is added twice (one due to a-b and one due to b-a) and agd of each vert is divided by 4 for averaging 'cos geodesics from a to other 4 in use

				//same stuff for sMesh to get sum2
				ii = 0;
				dists10[ii++] = sMesh->verts[ potentialQuadruples[a+1] ]->dSpanningUnn[ potentialQuadruples[a+3] ];
				dists10[ii++] = sMesh->verts[ potentialQuadruples[a+1] ]->dSpanningUnn[ potentialQuadruples[a+5] ];
				dists10[ii++] = sMesh->verts[ potentialQuadruples[a+1] ]->dSpanningUnn[ potentialQuadruples[a+7] ];
				dists10[ii++] = sMesh->verts[ potentialQuadruples[a+1] ]->dSpanningUnn[ sMesh->baseVerts[j] ];
				dists10[ii++] = sMesh->verts[ potentialQuadruples[a+3] ]->dSpanningUnn[ potentialQuadruples[a+5] ];
				dists10[ii++] = sMesh->verts[ potentialQuadruples[a+3] ]->dSpanningUnn[ potentialQuadruples[a+7] ];
				dists10[ii++] = sMesh->verts[ potentialQuadruples[a+3] ]->dSpanningUnn[ sMesh->baseVerts[j] ];
				dists10[ii++] = sMesh->verts[ potentialQuadruples[a+5] ]->dSpanningUnn[ potentialQuadruples[a+7] ];
				dists10[ii++] = sMesh->verts[ potentialQuadruples[a+5] ]->dSpanningUnn[ sMesh->baseVerts[j] ];
				dists10[ii++] = sMesh->verts[ potentialQuadruples[a+7] ]->dSpanningUnn[ sMesh->baseVerts[j] ];
				float sum2 = 0.0f, maxDistS = -INF;
				for (ii = 0; ii < 10; ii++)
				{
					sum2 += dists10[ii];
					if (dists10[ii] > maxDistS)
						maxDistS = dists10[ii];
				}
				sum2 = 0.5f * sum2/maxDistS;
				float diff = fabs(sum1 - sum2);
				if (diff < POTENTIAL_THRESHOLD) //closeness threshold
				{
					bool quintExists = false; //if this pair of quadruples overlaps w/ a prev quadruple pair than this quintuple pair (bMesh.quadruple + i + sMesh.quadruple + j) may be the same as an existing quintuple pair; so, prevent insertion of this duplicate
					for (int k = 0; k < (int) potentialQuintuples.size(); k += 10)
					{
						//check potentialQuadruples[a] (from bMesh part (evens)) w/ all 5 of this existing quintuple (from bMesh part (evens))
						if ((potentialQuintuples[k] == potentialQuadruples[a] || potentialQuintuples[k+2] == potentialQuadruples[a] || potentialQuintuples[k+4] == potentialQuadruples[a] || potentialQuintuples[k+6] == potentialQuadruples[a] || potentialQuintuples[k+8] == potentialQuadruples[a])
							&&
							//check potentialQuadruples[a+2] (from bMesh part (evens)) w/ all 5 of this existing quintuple (from bMesh part (evens))
							(potentialQuintuples[k] == potentialQuadruples[a+2] || potentialQuintuples[k+2] == potentialQuadruples[a+2] || potentialQuintuples[k+4] == potentialQuadruples[a+2] || potentialQuintuples[k+6] == potentialQuadruples[a+2] || potentialQuintuples[k+8] == potentialQuadruples[a+2])
							&&
							//check potentialQuadruples[a+4] (from bMesh part (evens)) w/ all 5 of this existing quintuple (from bMesh part (evens))
							(potentialQuintuples[k] == potentialQuadruples[a+4] || potentialQuintuples[k+2] == potentialQuadruples[a+4] || potentialQuintuples[k+4] == potentialQuadruples[a+4] || potentialQuintuples[k+6] == potentialQuadruples[a+4] || potentialQuintuples[k+8] == potentialQuadruples[a+4])
							&&
							//check potentialQuadruples[a+6] (from bMesh part (evens)) w/ all 5 of this existing quintuple (from bMesh part (evens))
							(potentialQuintuples[k] == potentialQuadruples[a+6] || potentialQuintuples[k+2] == potentialQuadruples[a+6] || potentialQuintuples[k+4] == potentialQuadruples[a+6] || potentialQuintuples[k+6] == potentialQuadruples[a+6] || potentialQuintuples[k+8] == potentialQuadruples[a+6])
							&&
							//check baseVerts[i] (from bMesh part (evens)) w/ all 5 of this existing quintuple (from bMesh part (evens))
							(potentialQuintuples[k] == baseVerts[i] || potentialQuintuples[k+2] == baseVerts[i] || potentialQuintuples[k+4] == baseVerts[i] || potentialQuintuples[k+6] == baseVerts[i] || potentialQuintuples[k+8] == baseVerts[i])
							&&

							//check potentialQuadruples[a+1] (from sMesh part (odds)) w/ all 5 of this existing quintuple (from bMesh part (evens))
							(potentialQuintuples[k+1] == potentialQuadruples[a+1] || potentialQuintuples[k+3] == potentialQuadruples[a+1] || potentialQuintuples[k+5] == potentialQuadruples[a+1] || potentialQuintuples[k+7] == potentialQuadruples[a+1] || potentialQuintuples[k+9] == potentialQuadruples[a+1])
							&&
							//check potentialQuadruples[a+3] (from sMesh part (odds)) w/ all 5 of this existing quintuple (from bMesh part (evens))
							(potentialQuintuples[k+1] == potentialQuadruples[a+3] || potentialQuintuples[k+3] == potentialQuadruples[a+3] || potentialQuintuples[k+5] == potentialQuadruples[a+3] || potentialQuintuples[k+7] == potentialQuadruples[a+3] || potentialQuintuples[k+9] == potentialQuadruples[a+3])
							&&
							//check potentialQuadruples[a+5] (from sMesh part (odds)) w/ all 5 of this existing quintuple (from bMesh part (evens))
							(potentialQuintuples[k+1] == potentialQuadruples[a+5] || potentialQuintuples[k+3] == potentialQuadruples[a+5] || potentialQuintuples[k+5] == potentialQuadruples[a+5] || potentialQuintuples[k+7] == potentialQuadruples[a+5] || potentialQuintuples[k+9] == potentialQuadruples[a+5])
							&&
							//check potentialQuadruples[a+7] (from sMesh part (odds)) w/ all 5 of this existing quintuple (from bMesh part (evens))
							(potentialQuintuples[k+1] == potentialQuadruples[a+7] || potentialQuintuples[k+3] == potentialQuadruples[a+7] || potentialQuintuples[k+5] == potentialQuadruples[a+7] || potentialQuintuples[k+7] == potentialQuadruples[a+7] || potentialQuintuples[k+9] == potentialQuadruples[a+7])
							&&
							//check sMesh->baseVerts[j] (from sMesh part (odds)) w/ all 5 of this existing quintuple (from bMesh part (evens))
							(potentialQuintuples[k+1] == sMesh->baseVerts[j] || potentialQuintuples[k+3] == sMesh->baseVerts[j] || potentialQuintuples[k+5] == sMesh->baseVerts[j] || potentialQuintuples[k+7] == sMesh->baseVerts[j] || potentialQuintuples[k+9] == sMesh->baseVerts[j]))
						{
							quintExists = true;
							break;
						}
					}
					if (! quintExists)
					{
						potentialQuintuples.push_back(potentialQuadruples[a]);			potentialQuintuples.push_back(potentialQuadruples[a+1]);
						potentialQuintuples.push_back(potentialQuadruples[a+2]);		potentialQuintuples.push_back(potentialQuadruples[a+3]);
						potentialQuintuples.push_back(potentialQuadruples[a+4]);		potentialQuintuples.push_back(potentialQuadruples[a+5]);
						potentialQuintuples.push_back(potentialQuadruples[a+6]);		potentialQuintuples.push_back(potentialQuadruples[a+7]);
						potentialQuintuples.push_back(baseVerts[i]);					potentialQuintuples.push_back(sMesh->baseVerts[j]);
					}
				}
				if (diff < minDiff)
					minDiff = diff;
				if (diff > maxDiff)
					maxDiff = diff;
				nCombs++; //just for info to see how much pruning done
			} //end of a
	cout << (int) potentialQuintuples.size() / 10 << " / " << nCombs << " potential quintuples of matches\tmin, max= " << minDiff << ", " << maxDiff << "\ndone!\n";
	delete [] dists10;
	//////////////////////// potentialQuintuples forming //////////////////////////////////////

	if (! (bestTuple == 3 || bestTuple == 4 || bestTuple == 5))
	{
		cout << "done!\n\n";
		return; //no need to find the min-distortion mapping in potentialTriplets[]
	}

	//best, i.e. min-distortion, match to be returned
	vector< int > bestList;	
	fillPermutationMtrxs();
	float minIsometryCost = INF;
	if (bestTuple == 3) //the best, min-distortion, 3x3 mapping
	{
		//all potential combinations of size 3 will be tried; list that gives the min D_iso (getCorrespListScaleInvDistortion()) is our bestList; 3 is the min size to be checked by D_iso 'cos i use 3-2=1 of them as self-traversalList			
		int pts = (int) potentialTriplets.size();
		for (int a = 0; a < pts; a += 6) //6 consecutive entries make 1 triplet of matches, i.e. 3 matches; so, no 3 a-b-c loops for combinations here; just go linearly w/ += 6
		{
			//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
			vector< int > tmpS;
			tmpS.push_back(potentialTriplets[a]); //even idxs ~ bMesh
			tmpS.push_back(potentialTriplets[a+2]);
			tmpS.push_back(potentialTriplets[a+4]);
			//find the max geo dist b/w bMesh pnts in tmpS to be used as a normalizer in getCorrespListDistortion()
			float maxDist1 = -INF;
			for (int i = 0; i < (int) tmpS.size(); i++)
				for (int j = 1; j < (int) tmpS.size(); j++)
					if (verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ] > maxDist1)
						maxDist1 = verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ];

			//this fixed combination in tmpS will be tried w/ 3! target triplet permutations tmpT
			vector< int > tmpT;
			tmpT.push_back(potentialTriplets[a+1]); //odd idxs ~ sMesh
			tmpT.push_back(potentialTriplets[a+3]);
			tmpT.push_back(potentialTriplets[a+5]);
			//find the max geo dist b/w sMesh pnts in tmpT to be used as a normalizer in getCorrespListDistortion()
			float maxDist2 = -INF;
			for (int i = 0; i < (int) tmpT.size(); i++)
				for (int j = 1; j < (int) tmpT.size(); j++)
					if (sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ] > maxDist2)
						maxDist2 = sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ];

			//int factR = 6; //3 factorial
			//try 3! reordering/permutations of tmpT w/ stable tmpS of size 3
			for (int r = 0; r < 6; r++)
			{
				//refill traversalListTry according to the current reordering (one of r! permutations)
				//get the overall isometryCost of this current triple combination matched w/ stable triple of fromMesh
				vector< int > traversalListTry;
				int idx1 = 0, idx2 = 0;
				//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
				for (int y = 0; y < 6; y++) //traversalListTry of size 3, hence 3*2=6
					if (y % 2 == 0)
						traversalListTry.push_back(tmpS[idx1++]); //stable from source side
					else
						traversalListTry.push_back(tmpT[ perms3[r][idx2++] ]);	//perms3[0] = {0,1,2}, perms3[1] = {1,0,2}, ..

				//evaluate this traversal list in trial
				//compute isometry cost of this 3-3 mapping and ..
				float isometryCostTry = //getCorrespListScaleInvDistortion(sMesh, traversalListTry);
										getCorrespListDistortion(sMesh, traversalListTry, maxDist1, maxDist2); //naming in getCorrespListDistortion() parameter as bMesh sucks but not important
				//..save it if it's the best so far
				if (isometryCostTry < minIsometryCost)
				{
					minIsometryCost = isometryCostTry;
					bestList = traversalListTry;
				}
			} //end of r
		} //end of a
	}
	else if (bestTuple == 4) //the best, min-distortion, 4x4 mapping
	{
		//all potential combinations of size 4 will be tried; list that gives the min D_iso (getCorrespListScaleInvDistortion()) is our bestList; 3 is the min size to be checked by D_iso 'cos i use 3-2=1 of them as self-traversalList
		int pts = (int) potentialQuadruples.size();
		for (int a = 0; a < pts; a += 8) //8 consecutive entries make 1 quadruple of matches, i.e. 4 matches; so, no 4 a-b-c-d loops for combinations here; just go linearly w/ += 8
		{
			//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
			vector< int > tmpS;
			tmpS.push_back(potentialQuadruples[a]); //even idxs ~ bMesh
			tmpS.push_back(potentialQuadruples[a+2]);
			tmpS.push_back(potentialQuadruples[a+4]);
			tmpS.push_back(potentialQuadruples[a+6]);
			//find the max geo dist b/w bMesh pnts in tmpS to be used as a normalizer in getCorrespListDistortion()
			float maxDist1 = -INF;
			for (int i = 0; i < (int) tmpS.size(); i++)
				for (int j = 1; j < (int) tmpS.size(); j++)
					if (verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ] > maxDist1)
						maxDist1 = verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ];

			//this fixed combination in tmpS will be tried w/ 4! target quadruple permutations tmpT
			vector< int > tmpT;
			tmpT.push_back(potentialQuadruples[a+1]); //odd idxs ~ sMesh
			tmpT.push_back(potentialQuadruples[a+3]);
			tmpT.push_back(potentialQuadruples[a+5]);
			tmpT.push_back(potentialQuadruples[a+7]);
			//find the max geo dist b/w sMesh pnts in tmpT to be used as a normalizer in getCorrespListDistortion()
			float maxDist2 = -INF;
			for (int i = 0; i < (int) tmpT.size(); i++)
				for (int j = 1; j < (int) tmpT.size(); j++)
					if (sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ] > maxDist2)
						maxDist2 = sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ];

			//int factR = 24; //4 factorial
			//try 4! reordering/permutations of tmpT w/ stable tmpS of size 4
			for (int r = 0; r < 24; r++)
			{
				//refill traversalListTry according to the current reordering (one of r! permutations)
				//get the overall isometryCost of this current triple combination matched w/ stable triple of fromMesh
				vector< int > traversalListTry;
				int idx1 = 0, idx2 = 0;
				//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
				for (int y = 0; y < 8; y++) //traversalListTry of size 4, hence 4*2=8
					if (y % 2 == 0)
						traversalListTry.push_back(tmpS[idx1++]); //stable from source side
					else
						traversalListTry.push_back(tmpT[ perms4[r][idx2++] ]);	//perms4[0] = {0,1,2,3}, perms4[1] = {1,0,3,2}, ..

				//evaluate this traversal list in trial
				//compute isometry cost of this 4-4 mapping and ..
				float isometryCostTry = //getCorrespListScaleInvDistortion(sMesh, traversalListTry);
										getCorrespListDistortion(sMesh, traversalListTry, maxDist1, maxDist2); //naming in getCorrespListDistortion() parameter as bMesh sucks but not important
				//..save it if it's the best so far
				if (isometryCostTry < minIsometryCost)
				{
					minIsometryCost = isometryCostTry;
					bestList = traversalListTry;
				}
			} //end of r
		} //end of a
	}
	else if (bestTuple == 5) //the best, min-distortion, 5x5 mapping
	{
		//all potential combinations of size 3 will be tried; list that gives the min D_iso (getCorrespListScaleInvDistortion()) is our bestList; 3 is the min size to be checked by D_iso 'cos i use 3-2=1 of them as self-traversalList
		int pts = (int) potentialQuintuples.size();
		for (int a = 0; a < pts; a += 10) //10 consecutive entries make 1 quintuple of matches, i.e. 5 matches; so, no 5 a-b-c-d-e loops for combinations here; just go linearly w/ += 10
		{
			//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
			vector< int > tmpS;
			tmpS.push_back(potentialQuintuples[a]); //even idxs ~ bMesh
			tmpS.push_back(potentialQuintuples[a+2]);
			tmpS.push_back(potentialQuintuples[a+4]);
			tmpS.push_back(potentialQuintuples[a+6]);
			tmpS.push_back(potentialQuintuples[a+8]);
			//find the max geo dist b/w bMesh pnts in tmpS to be used as a normalizer in getCorrespListDistortion()
			float maxDist1 = -INF;
			for (int i = 0; i < (int) tmpS.size(); i++)
				for (int j = 1; j < (int) tmpS.size(); j++)
					if (verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ] > maxDist1)
						maxDist1 = verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ];

			//this fixed combination in tmpS will be tried w/ 4! target quadruple permutations tmpT
			vector< int > tmpT;
			tmpT.push_back(potentialQuintuples[a+1]); //odd idxs ~ sMesh
			tmpT.push_back(potentialQuintuples[a+3]);
			tmpT.push_back(potentialQuintuples[a+5]);
			tmpT.push_back(potentialQuintuples[a+7]);
			tmpT.push_back(potentialQuintuples[a+9]);
			//find the max geo dist b/w sMesh pnts in tmpT to be used as a normalizer in getCorrespListDistortion()
			float maxDist2 = -INF;
			for (int i = 0; i < (int) tmpT.size(); i++)
				for (int j = 1; j < (int) tmpT.size(); j++)
					if (sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ] > maxDist2)
						maxDist2 = sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ];

			//int factR = 120; //5 factorial
			//try 5! reordering/permutations of tmpT w/ stable tmpS of size 5
			for (int r = 0; r < 120; r++)
			{
				//refill traversalListTry according to the current reordering (one of r! permutations)
				//get the overall isometryCost of this current triple combination matched w/ stable triple of fromMesh
				vector< int > traversalListTry;
				int idx1 = 0, idx2 = 0;
				//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
				for (int y = 0; y < 10; y++) //traversalListTry of size 5, hence 5*2=10
					if (y % 2 == 0)
						traversalListTry.push_back(tmpS[idx1++]); //stable from source side
					else
						traversalListTry.push_back(tmpT[ perms5[r][idx2++] ]);	//perms5[0] = {0,1,2,3,4}, perms4[1] = {1,0,3,4,2}, ..

				//evaluate this traversal list in trial
				//compute isometry cost of this 5-5 mapping and ..
				float isometryCostTry = //getCorrespListScaleInvDistortion(sMesh, traversalListTry);
										getCorrespListDistortion(sMesh, traversalListTry, maxDist1, maxDist2); //naming in getCorrespListDistortion() parameter as bMesh sucks but not important
				//..save it if it's the best so far
				if (isometryCostTry < minIsometryCost)
				{
					minIsometryCost = isometryCostTry;
					bestList = traversalListTry;
				}
			} //end of r
		} //end of a
	}

	cout << "initial matches w/ D_iso = " << minIsometryCost << ":\n";
	//now that bestCorresp is ready, fill the matchIdx valuess appropriately
	safeCorresp.clear();
	for (int s = 0; s < (int) bestList.size() / 2; s++)
	{
		int bvs = bestList[2*s], bvt = bestList[2*s + 1]; //even idx for source bMesh, odds for target sMesh
		//verts[bvs]->matchIdx = bvt; sMesh->verts[bvt]->matchIdx = bvs; //cancel 'cos extendToDense() uses matchIdx values that stay untouched here and that are updated later
		safeCorresp.push_back((sMesh->targetMesh ? bvs : bvt));
		safeCorresp.push_back((sMesh->targetMesh ? bvt : bvs)); //even idxs of safeCorresp and interestingMatches must be source/this mesh
#ifdef DBG_DETAIL
		cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif
	} //end of s

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

void Mesh::initial2Matches(Mesh* mesh2)
{
	//out of ~10 evenly-spaced extremity bases on both meshes, find the best pair of matches as traversalList (stored in safeCorresp) by evaluating blossom 10x10 match of bases 
	
	cout << "initial traversalList of size 2 being computed..\n";

	//traversalList (of size 2) to be returned (as safeCorresp) is kept in bestList
	vector< int > bestList;

	//~10 evenly-spaced extremity bases on both meshes
	int m = computeBaseVerts(),
		n = mesh2->computeBaseVerts();	

	//all traversalList combinations of size 2 will be tried; list that gives 10x10 blossom mtrx w/ min D_iso is our bestList (D_iso of blossom match is just the avg of vertext-based iso costs of selected 1-to-1 matching)
	cout << "C(" << m << ", 2) * C(" << n << ", 2) * 2! traversalListTry will be tried..\n";
	fillPermutationMtrxs();
	float minIsometryCost = INF;
//FILE* fPtr = fopen("dbg.dat", "w");
	for (int a = 0; a < m; a++)
		for (int b = a+1; b < m; b++)
		{
			//current 2-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
			vector< int > tmpS; //holds the current pair of all C(m,2) pairs
			tmpS.push_back(baseVerts[a]);
			tmpS.push_back(baseVerts[b]);

			//this fixed combination in tmpS will be tried w/ C(~10,2)2! target tuple permutations, tmpT
			for (int d = 0; d < n; d++)
				for (int e = d+1; e < n; e++)
				{
					//current 2-combination is copied from appropriate slots of mesh2.baseVerts to tmpT (for tmp target)
					vector< int > tmpT; //holds the current tuple of all C(m,2) tuples
					tmpT.push_back(mesh2->baseVerts[d]);
					tmpT.push_back(mesh2->baseVerts[e]);

					//int factR = 2; //2 factorial
					//try 2! reordering/permutations of tmpT w/ stable tmpS of size 2
					for (int r = 0; r < 2; r++)
					{
						//refill traversalListTry according to the current reordering (one of r! permutations)
						//get the overall isometryCost of this current quadruple combination matched w/ stable quadruple of fromMesh
						vector< int > traversalListTry;
						int idx1 = 0, idx2 = 0;
						//stable pair of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
						for (int y = 0; y < 4; y++) //traversalListTry of size 2, hence 2*2=4
							if (y % 2 == 0)
								traversalListTry.push_back(tmpS[idx1++]); //stable from source side
							else
								traversalListTry.push_back(tmpT[ perms2[r][idx2++] ]);	//perms2[0] = {0,1}, perms2[1] = {1,0}

						//evaluate this traversal list in trial by evaluating the 1-1 blossom match of the mtrx that it implies
						//compute isometry cost of this 1-1 mapping of 10x10 bases and ..
						float isometryCostTry = blossomOnBases(mesh2, traversalListTry);
						//..save the implying traversal list if it's the best so far
						if (isometryCostTry < minIsometryCost)
						{
							minIsometryCost = isometryCostTry;
							bestList = traversalListTry;
						}
//if (isometryCostTry < 0.25f) {for (int y = 0; y < (int) traversalListTry.size(); y += 2)cout << traversalListTry[y] << "\t" << traversalListTry[y+1] << endl; cout << isometryCostTry << "\n\n";}
					} //end of r
				} //end of e
		} //end of b
//fclose(fPtr);
	cout << minIsometryCost << " for ~10x10 blossom matching\ninitial traversalList (to be used for ~50x50 blossom matching):\n";
	//now that bestCorresp is ready, fill the matchIdx values appropriately
	safeCorresp.clear();
	for (int s = 0; s < (int) bestList.size() / 2; s++)
	{
		int bvs = bestList[2*s], bvt = bestList[2*s + 1]; //even idx for source mesh1, odds for target mesh2
		verts[bvs]->matchIdx = bvt;
		mesh2->verts[bvt]->matchIdx = bvs;
		safeCorresp.push_back(bvs);
		safeCorresp.push_back(bvt); //even idxs of safeCorresp and interestingMatches must be source/this mesh
#ifdef DBG_DETAIL
		cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif
	} //end of s

	cout << "with isometryCost: " << minIsometryCost << "\ndone!\n\n";
}
void Mesh::initial3MatchesV2(Mesh* mesh2)
{
	//same as initial2Matches() except that this tries traversalLists of sizes 3, not 2
	
	cout << "initial traversalList of size 3 being computed..\n";

	//traversalList (of size 3) to be returned is kept in bestList
	vector< int > bestList;

	//~10 evenly-spaced extremity bases on both meshes
	int m = computeBaseVerts(),
		n = mesh2->computeBaseVerts();	

	//all traversalList combinations of size 3 will be tried; list that gives 10x10 blossom mtrx w/ min D_iso is our bestList (D_iso of blossom match is just the avg of vertext-based iso costs of selected 1-to-1 matching)
	cout << "C(" << m << " ,3) * C(" << n << ", 3)3! traversalListTry will be tried..\n";
	fillPermutationMtrxs();
	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++)
			{
				//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
				vector< int > tmpS; //holds the current triple of all C(m,3) triples
				tmpS.push_back(baseVerts[a]);
				tmpS.push_back(baseVerts[b]);
				tmpS.push_back(baseVerts[c]);

				//this fixed combination in tmpS will be tried w/ C(10,3)3! target triple permutations, tmpT
				for (int d = 0; d < n; d++)
					for (int e = d+1; e < n; e++)
						for (int f = e+1; f < n; f++)
						{
							//current 3-combination is copied from appropriate slots of mesh2.baseVerts to tmpT (for tmp target)
							vector< int > tmpT; //holds the current triple of all C(m,3) triple
							tmpT.push_back(mesh2->baseVerts[d]);
							tmpT.push_back(mesh2->baseVerts[e]);
							tmpT.push_back(mesh2->baseVerts[f]);

							//int factR = 6; //3 factorial
							//try 3! reordering/permutations of tmpT w/ stable tmpS of size 3
							for (int r = 0; r < 6; r++)
							{
								//refill traversalListTry according to the current reordering (one of r! permutations)
								//get the overall isometryCost of this current quadruple combination matched w/ stable quadruple of fromMesh
								vector< int > traversalListTry;
								int idx1 = 0, idx2 = 0;
								//stable triplet of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
								for (int y = 0; y < 6; y++) //traversalListTry of size 3, hence 3*2=6
									if (y % 2 == 0)
										traversalListTry.push_back(tmpS[idx1++]); //stable from source side
									else
										traversalListTry.push_back(tmpT[ perms3[r][idx2++] ]);	//perms3[0] = {0,1,2}, perms3[1] = {1,0,2}, ..

								//evaluate this traversal list in trial by evaluating the 1-1 blossom match of the mtrx that it implies
								//compute isometry cost of this 1-1 mapping of 10x10 bases and ..
								float isometryCostTry = blossomOnBases(mesh2, traversalListTry);
								//..save the implying traversal list if it's the best so far
								if (isometryCostTry < minIsometryCost)
								{
									minIsometryCost = isometryCostTry;
									bestList = traversalListTry;
								}
							} //end of r
						} //end of f
			} //end of c
	cout << minIsometryCost << " for ~10x10 blossom matching\ninitial traversalList (to be used for ~50x50 blossom matching):\n";
	//now that bestCorresp is ready, fill the matchIdx valuess appropriately
	safeCorresp.clear();
	for (int s = 0; s < (int) bestList.size() / 2; s++)
	{
		int bvs = bestList[2*s], bvt = bestList[2*s + 1]; //even idx for source mesh1, odds for target mesh2
		verts[bvs]->matchIdx = bvt;
		mesh2->verts[bvt]->matchIdx = bvs;
		safeCorresp.push_back(bvs);
		safeCorresp.push_back(bvt); //even idxs of safeCorresp and interestingMatches must be source/this mesh
#ifdef DBG_DETAIL
		cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif
	} //end of s

//int i=0;safeCorresp[i++]=5;safeCorresp[i++]=3064;		safeCorresp[i++]=2370;safeCorresp[i++]=2460;		safeCorresp[i++]=1578;safeCorresp[i++]=1707;

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

void Mesh::initial50Matches(Mesh* mesh2)
{
	//out of n (~50) evenly-spaced COES bases on both meshes, find the best nxn blossom match of bases using safeCorresp as the traversalList
	
	cout << "~50x50 blossom match using traversalList of size 2..\n";

	//traversalList (of size 2) is ready at safeCorresp

	//~50 COES bases on both meshes
	int m = computeBaseVerts(true),
		n = mesh2->computeBaseVerts(true);	

	//~50x50 blossom matching; safeCorresp argument just-below is the 2 trusted corresps of initial2Matches()
	float blossomCorrespErr = blossomOnBases(mesh2, safeCorresp); //safeCorresp is now overwritten to keep ~50x50 matching
	cout << blossomCorrespErr << " for " << (n < m ? n : m) << " x " << (n < m ? n : m) << " blossom matching listed here:\n";

	//now that safeCorresp is ready, fill the matchIdx values appropriately which are first reset to -1s to clear the effects of previous assignments (not so imprtant here but anyway)
	for (int i = 0; i < (int) verts.size(); i++) verts[i]->matchIdx = -1;
	for (int i = 0; i < (int) mesh2->verts.size(); i++) mesh2->verts[i]->matchIdx = -1;
	for (int s = 0; s < (int) safeCorresp.size() / 2; s++)
	{
		int bvs = safeCorresp[2*s], bvt = safeCorresp[2*s + 1]; //even idx for source mesh1, odds for target mesh2
		verts[bvs]->matchIdx = verts[bvs]->matchIdx2 = bvt; //note that matchIdx2 keeps this most recent non -1 matchIdx value so i can use it for adaptiveTraversalList case
		mesh2->verts[bvt]->matchIdx = mesh2->verts[bvt]->matchIdx2 = bvs;
/*#ifdef DBG_DETAIL
		cout << safeCorresp[2*s] << " --> " << safeCorresp[2*s + 1] << endl;
#endif*/
	} //end of s
	verts[worstMatchI]->worstIin2 = verts[worstMatchI]->matchIdx;
	cout << verts[worstMatchI]->matchCostI << " for " << worstMatchI << "-" << verts[worstMatchI]->worstIin2 << " worst match\tbig initialization done!\n\n";
}

float Mesh::blossomOnBases(Mesh* mesh2, vector< int > traversalListTry)
{
	//nRows x nRows mtrx (nRows = max(# baseVerts, # mesh2.baseVerts)) will be created w/ vertex-based scale-invariant distortion values decided by traversalListTry; 1-to-1 blossom match of that mtrx is then evaluated
	//adaptiveTraversalList=true means no fixed traversalListTry'll be used here for costs[][] mtrx entries; rather, i use adaptive traversalList of nearby bases per entry; again, 1-to-1 blossom match of that mtrx is then evaluated

	int n1 = (int) baseVerts.size(), n2 = (int) mesh2->baseVerts.size();
	int nRows = (n1 > n2 ? n1 : n2), nCols = nRows; //square mtrx
//#ifdef DBG_DETAIL
//	cout << "\nbases being matched (" << nRows << " x " << nCols << ")..\n";
//#endif
	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 no; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; w/ unnormalized geodesics, costs[][] not necessarily in [0,1]
	//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]; //= base1
		int col = 0;
		for (int j = 0; j < n2; j++)
			vIdx2[col++] = mesh2->baseVerts[j]; //base2
		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++;
	}
	
	//////////////////// blossom matching //////////////////////////
	//create the blossom matching matchList to be evaluated (for return) using the traversalListTry
	vector< int > matchList; //blossom matching to be evaluated
	row = 0;
	for (int i = 0; i < n1; i++)
	{
		int base1 = baseVerts[i];
		//find the unmatched mesh2.base[j] as a 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])
			if (adaptiveTraversalList)
			{
				////////////// adaptiveTraversalList creation ////////////////
				vector< int > adaptTraversalList;
				for (int bn = 0; bn < (int) verts[base1]->baseNghbs.size(); bn++) //important to keep baseNghbs.size=0 until the end of initial50Matches() 'cos in such early calls i want 2-size safeCorresp be my traversalList below
					//if (verts[ verts[base1]->baseNghbs[bn] ]->matchIdx != -1) //has a valid match, i.e. not made -1 by being detected as worst of a prev stage, recall worst are excluded from being base
					//use matchIdx2, the most recent non -1 matchIdx value, at all times 'cos w/ matchIdx that becomes -1, i create tiny adaptTraversalList's which is not so good
					{
						adaptTraversalList.push_back(verts[base1]->baseNghbs[bn]);
						adaptTraversalList.push_back(verts[ verts[base1]->baseNghbs[bn] ]->matchIdx2); //not matchIdx
					}
				if ((int) adaptTraversalList.size() > 3) //no overwriting to traversalListTry (i.e. use default safeCorresp of all matches of prev stage) if adaptList is of size 0 (0*2) or 1 (1*2 for actual element size) 'cos in that case i cannot compute d_iso)
					traversalListTry = adaptTraversalList; //use adaptive one as traversalListTry below
				////////////// adaptiveTraversalList creation ends ////////////////
			}
			int m = (int) traversalListTry.size() / 2; //traversalList in trial
			//for all m-correspondences from traversalListTry other than base1-base2, decide a vertexBasedIso due to those m matches in traversalList
			int nAdds = 0;
			if (m == 2) //must replace continue; w/ perturbation when traversalList is of size 2 as explained in my -------- =========== drawing below
				for (int a = 0; a < m; a++)
					for (int b = a+1; b < m; b++)
					{
						//current 2-correspondences are copied from appropriate slots of tmpSafeCorrespTry; let these 2 matches be s1-s2 & u1-u2
						int s1 = traversalListTry[2*a], s2 = traversalListTry[2*a + 1], //a'th match is s1-s2
							u1 = traversalListTry[2*b], u2 = traversalListTry[2*b + 1]; //b'th match is u1-u2
//							if (s1 == base1 || u1 == base1 || s2 == base2 || u2 == base2) //extra 2 ||s different from getCorrespListScaleInvDistortion() 'cos here i fill costs[i][j] and j might be base2 even if i is not base1 (which can't occur in getCorrespListScaleInvDistortion()); so take precautions not to end up w/ 0 as numer or denom
//								continue; //2-corresps must be different from base1-base2 whose vertexBasedIso is computed here; this is same as if (denom1==0||denom2==0)continue; effect
//not do this continue tactic just-above when traversalListTry is of size 2 'cos in that case base1-base2 can never pass below and remains vertexBasedIso=0 even if it is a bad match; consider this:
//base1---------------- x
//     ---------------- 
//s1   ================ s2=base2 [======= means traversalList match]
//	   ---------------- 
//u1   ================ u2
//so, above base1-x cost can be computed 'cos none is colliding w/ s1-s2,u1-u2; base1-base2 not computable though 'cos s2 collides w/ base2; so, that entry in mtrx remains as 0, which is wrong; so, perturb a little if dSpanning=0, with a little being min edge length which is nonzero as desired

						float b1s1 = (verts[base1]->dSpanningUnn[s1] != 0.0f ? verts[base1]->dSpanningUnn[s1] : minEdgeLen), //perturbed version to prevent div by 0
								b2s2 = (mesh2->verts[base2]->dSpanningUnn[s2] != 0.0f ? mesh2->verts[base2]->dSpanningUnn[s2] : mesh2->minEdgeLen),
								b1u1 = (verts[base1]->dSpanningUnn[u1] != 0.0f ? verts[base1]->dSpanningUnn[u1] : minEdgeLen),
								b2u2 = (mesh2->verts[base2]->dSpanningUnn[u2] != 0.0f ? mesh2->verts[base2]->dSpanningUnn[u2] : mesh2->minEdgeLen);
						float numer1 = maxOf(b1s1, b2s2),
								denom1 = minOf(b1s1, b2s2), //denominator has the bigger geodesic for both s1-s2 and u1-u2 for consistency
								numer2 = maxOf(b1u1, b2u2),
								denom2 = minOf(b1u1, b2u2);
						vertexBasedIso += fabs(numer1/denom1 - numer2/denom2); //= due to s1-s2 - due to u1-u2
						nAdds++; //# of += applied to vertexBasedIso for normalization below
					}
			else if (m == 3)
				for (int a = 0; a < m; a++)
					for (int b = a+1; b < m; b++)
					{
						//current 2-correspondences are copied from appropriate slots of tmpSafeCorrespTry; let these 2 matches be s1-s2 & u1-u2
						int s1 = traversalListTry[2*a], s2 = traversalListTry[2*a + 1], //a'th match is s1-s2
							u1 = traversalListTry[2*b], u2 = traversalListTry[2*b + 1]; //b'th match is u1-u2
//						if (s1 == base1 || u1 == base1 || s2 == base2 || u2 == base2) //extra 2 ||s different from getCorrespListScaleInvDistortion() 'cos here i fill costs[i][j] and j might be base2 even if i is not base1 (which can't occur in getCorrespListScaleInvDistortion()); so take precautions not to end up w/ 0 as numer or denom
//							continue; //2-corresps must be different from base1-base2 whose vertexBasedIso is computed here; this is same as if (denom1==0||denom2==0)continue; effect
//do this continue tactic just-above when traversalListTry is of size 3+ 'cos in that case base1-base2 will pass below for sure^^ and can't remain vertexBasedIso=0 if it is a bad match

//						float b1s1 = verts[base1]->dSpanningUnn[s1], b2s2 = mesh2->verts[base2]->dSpanningUnn[s2],
//							  b1u1 = verts[base1]->dSpanningUnn[u1], b2u2 = mesh2->verts[base2]->dSpanningUnn[u2]; //0 entries prevented for sure by continue above; ^^no!!!!! for m=3 case, s1=base1 and u2=base2 case, i still can't find 2 free traversal matches; so, nAdds would be 0
						float b1s1 = (verts[base1]->dSpanningUnn[s1] != 0.0f ? verts[base1]->dSpanningUnn[s1] : minEdgeLen), //perturbed version to prevent div by 0
								b2s2 = (mesh2->verts[base2]->dSpanningUnn[s2] != 0.0f ? mesh2->verts[base2]->dSpanningUnn[s2] : mesh2->minEdgeLen),
								b1u1 = (verts[base1]->dSpanningUnn[u1] != 0.0f ? verts[base1]->dSpanningUnn[u1] : minEdgeLen),
								b2u2 = (mesh2->verts[base2]->dSpanningUnn[u2] != 0.0f ? mesh2->verts[base2]->dSpanningUnn[u2] : mesh2->minEdgeLen);
						float numer1 = maxOf(b1s1, b2s2),
								denom1 = minOf(b1s1, b2s2), //denominator has the bigger geodesic for both s1-s2 and u1-u2 for consistency
								numer2 = maxOf(b1u1, b2u2),
								denom2 = minOf(b1u1, b2u2);
						vertexBasedIso += fabs(numer1/denom1 - numer2/denom2); //= due to s1-s2 - due to u1-u2
						nAdds++; //# of += applied to vertexBasedIso for normalization below
					}
			else
				for (int a = 0; a < m; a++)
					for (int b = a+1; b < m; b++)
					{
						//current 2-correspondences are copied from appropriate slots of tmpSafeCorrespTry; let these 2 matches be s1-s2 & u1-u2
						int s1 = traversalListTry[2*a], s2 = traversalListTry[2*a + 1], //a'th match is s1-s2
							u1 = traversalListTry[2*b], u2 = traversalListTry[2*b + 1]; //b'th match is u1-u2
						if (s1 == base1 || u1 == base1 || s2 == base2 || u2 == base2) //extra 2 ||s different from getCorrespListScaleInvDistortion() 'cos here i fill costs[i][j] and j might be base2 even if i is not base1 (which can't occur in getCorrespListScaleInvDistortion()); so take precautions not to end up w/ 0 as numer or denom
							continue; //2-corresps must be different from base1-base2 whose vertexBasedIso is computed here; this is same as if (denom1==0||denom2==0)continue; effect
//do this continue tactic just-above when traversalListTry is of size 3+ 'cos in that case base1-base2 will pass below for sure and can't remain vertexBasedIso=0 if it is a bad match

						float b1s1 = verts[base1]->dSpanningUnn[s1], b2s2 = mesh2->verts[base2]->dSpanningUnn[s2],
							  b1u1 = verts[base1]->dSpanningUnn[u1], b2u2 = mesh2->verts[base2]->dSpanningUnn[u2]; //0 entries prevented for sure by continue above
						float numer1 = maxOf(b1s1, b2s2),
								denom1 = minOf(b1s1, b2s2), //denominator has the bigger geodesic for both s1-s2 and u1-u2 for consistency
								numer2 = maxOf(b1u1, b2u2),
								denom2 = minOf(b1u1, b2u2);
						vertexBasedIso += fabs(numer1/denom1 - numer2/denom2); //= due to s1-s2 - due to u1-u2
						nAdds++; //# of += applied to vertexBasedIso for normalization below
					}
assert(nAdds > 0);
			vertexBasedIso /= nAdds;
			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 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
				matchList.push_back(vIdx1[m1]);
				matchList.push_back(mi);
			}
		}
	} //end of m
	//..and compute its error, in order to help keep it later if it's the current best so far
	float blossomCorrespErr = getCorrespListScaleInvDistortion(mesh2, matchList);

assert(((int) matchList.size() / 2) == (n1 < n2 ? n1 : n2)); //small of n1 & n2 must be matched all
	//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;
	//////////////////// blossom matching ends //////////////////////////

//	if (traversalListTry == safeCorresp) //false only if called from initial2Matches(), which does not want to set safeCorresp to matchList; actually, i do safeCorresp.clear() there wen this returns; so, not confuse w/ if clause
		safeCorresp = matchList; //update safeCorresp, used as traversalList above, to matchList to represent the ~50x50 blossom matching to be used later (as a safeList for ~49x49 later or in decision of new matchIdx values)

	return blossomCorrespErr;
}
float Mesh::blossomOnBases2(Mesh* bMesh, Mesh* sMesh)
{
	//similar to blossomOnBases(); nRows x nRows mtrx (nRows = # bMesh.baseVerts) will be created w/ vertex-based scale-invariant distortion values stored in bMesh.matchIdxs' odd idxs; 1-to-1 blossom match of that mtrx is then evaluated and the worst match is announced

	int n1 = (int) bMesh->baseVerts.size(), n2 = (int) sMesh->baseVerts.size(); //n1 >= n2 'cos bMesh is the big mesh
	int nRows = n1, 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 no; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; w/ unnormalized geodesics, costs[][] not necessarily in [0,1]
	//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] = bMesh->baseVerts[i]; //base1 from bMesh
		int col = 0;
		for (int j = 0; j < n2; j++)
			vIdx2[col++] = sMesh->baseVerts[j]; //base2 from sMesh
		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++;
	}
	
	//////////////////// blossom matching //////////////////////////
	//create the blossom matching matchList to be evaluated (for return) using the traversalListTry
	vector< int > matchList; //blossom matching to be evaluated
	row = 0;
	for (int i = 0; i < n1; i++)
	{
		int base1 = bMesh->baseVerts[i]; //base1 from bMesh
		//find the unmatched sMesh.base[j] as a match for base1; to do so globally, use blossom matching
		int col = 0;
//assert((int) bMesh->verts[base1]->matchIdxs.size() == 2*n2); //base1 is matched to all possible base2's in matchBasesViaAll() (matchIdxs.evenIdxs) with matching distortions (odd idxs); so, 2 entries per match, hence 2*n2
		for (int j = 0; j < n2; j++)
		{
			int base2 = sMesh->baseVerts[j]; //base2 from sMesh
			//base1 vs. base2
			costs[row][col++] = bMesh->verts[base1]->matchIdxs[2*j + 1]; //overwrite the costs[][] w/ distortion of base1 vs. base2
		} //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..
	float blossomCorrespErr = 0.0f, maxIsoCost = -INF;
	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
				matchList.push_back(vIdx1[m1]); //base1 from bMesh
				matchList.push_back(mi); //base2 from sMesh

				int i = -1;
				for (int j = 0; j < n2; j++)
					if (sMesh->baseVerts[j] == mi)
					{
						i = j;
						break;
					}
if (i == -1){cout<<"\n\n\n\n\nsth wrong\n";exit(0);}
				float vertexBasedIso = bMesh->verts[ vIdx1[m1] ]->matchIdxs[2*i + 1];
				blossomCorrespErr += vertexBasedIso; //cost used in blossom mtrx (probably the min of all columns in vIdx1[m1]'s row but since blossom makes global match, it doesn't always select the min)
				bMesh->verts[ vIdx1[m1] ]->matchCostI = sMesh->verts[mi]->matchCostI = vertexBasedIso; //matchCostI fprinted in return
				if (vertexBasedIso > maxIsoCost)
				{
					bMesh->worstMatchI = vIdx1[m1];
					maxIsoCost = vertexBasedIso;
				}
			}
		}
	} //end of m
	//..and compute its error, in order to decide when to stop partialMatching3() iterations
	blossomCorrespErr /= n2; //n2 += done above
	//float blossomCorrespErr = bMesh->getCorrespListScaleInvDistortion(sMesh, matchList); //matchList.evenIdxs ~ bMesh, odd ~ sMesh (naming in getCorrespListScaleInvDistortion()'s parameter list sucks)
	//no!!!!!!! don't use all matches at once via getCorrespListScaleInvDistortion() 'cos pnts from uncommon parts fail the scales of the ones from same parts; by using permutations in match..viaAllEff(), i use pnts w/ pnts from the same part; soln: blossomCorrespErr already computed above w/ values used in blossom mtrx (and created during permutations in match..viaAllEff())

assert(((int) matchList.size() / 2) == n2); //small of n1 & n2 (which is n2 for sure) must be matched all
	//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;
	//////////////////// blossom matching ends //////////////////////////

	//store blossom match in global safeCorresp; distortion of each constituing match is stored in matchCostI set during getCorrespListScaleInvDistortion()
	if (bMesh == this) //i want safeCorresp.evenIdxs ~ mesh1 (this), odd ~ mesh2; since, matchList.evenIdxs ~ bMesh, odd ~ sMesh, no flip needed to set global safeCorresp 
		safeCorresp = matchList;
	else
	{
		safeCorresp.clear();
		for (int i = 0; i < (int) matchList.size(); i += 2)
		{
			safeCorresp.push_back(matchList[i + 1]);
			safeCorresp.push_back(matchList[i]); //flipping 2 consecutive elements in matchList
		}
	}

	return blossomCorrespErr;
}

int Mesh::excludeFromBases(int v)
{
	//v is not a base anymore after this

	for (int i = 0; i < (int) baseVerts.size(); i++)
		if (baseVerts[i] == v) //to be removed/erased
		{
			verts[ baseVerts[i] ]->base = false; //redundant
			verts[ baseVerts[i] ]->baseCandidate = true; //redundant				
			//if (verts[ baseVerts[i] ]->baseNghbhood) delete verts[ baseVerts[i] ]->baseNghbhood; //redundant
			verts[ baseVerts[i] ]->idxInBaseVerts = -1; //redundant
			baseVerts.erase(baseVerts.begin() + i); //erase(begin + 4) erases [4]
			i--; //handle vector shifting; redundant 'cos i'm breaking anyway
			break;
		}

	return (int) baseVerts.size();
}

int nIters; //# of times the worst of the current map is removed from costs[][] and blossom is done on this refined costs[][] at the conquering, i.e. 1-to-1 conversion of votes, step of matchBasesViaVoting2()
void Mesh::partialMatching3(Mesh* mesh2, bool extendCoarseMapToDense)
{
	//partial matching b/w partially overlapped 2 meshes
	
	cout << "\n\npartial matching in action......\n";

	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();

	//a noisy surface often contains many small bumps that correspond to local maxima of (small) mu values; clear that noise by smoothing
	laplSmoothing = mesh2->laplSmoothing = true;
	if (laplSmoothing)
		laplacianSmoothing();
	if (mesh2->laplSmoothing)
		mesh2->laplacianSmoothing();

	//few bases by local maxima and minima (of mu and/or curvature values) on both meshes
	int m = computeBaseVerts(false, true), n = mesh2->computeBaseVerts(false, true);

	//compute individual distortion of each i-j match (to select the i-j's w/ cool distortion values later)
	int indDistoComputation = 2; //0: viaNghbs, 1: viaAll, 2: viaAllEff (best)
	if (indDistoComputation == 0)
	{
		m = computeAdditionalBases(); //~10 bases by local maxima is populated by evenly-spaced ~40 more bases ('cos it turns out that matchBasesViaNghbs() not good w/ ~10 bases)
		n = mesh2->computeAdditionalBases();

		//k closest base nghbs of each base
		int k = 5;
		computeBaseNeighbsK(k);
		mesh2->computeBaseNeighbsK(k);
	}
	cout << "\n# bases = initial + additional\t" << m << " & " << n << endl;
//return;//see ~10 bases to be matched

	/////////////////////// match ~10 extreme bases ///////////////////////
	Mesh* sMesh, * bMesh; //biggerMesh has more baseVerts than smallerMesh sMesh; so, quite probably bMesh is the centaur w/ extra 2 arms compared to a sMesh of horse
	if (m < n)
	{
		sMesh = this;
		bMesh = mesh2;
	}
	else
	{
		sMesh = mesh2;
		bMesh = this;
	}
	m = (int) bMesh->baseVerts.size();
	n = (int) sMesh->baseVerts.size(); //so m >= n for sure

	//instead of running matchBasesViaAll() that goes over all combinations (even if they're from uncommon parts), use potential* matches only (matchBasesViaAllEff()) which are the ~good combinations that are very small compared to all combinations: 92 vs. 14700
	bool incrementalBuild = true; //true: build potential* list by using the prev one, e.g. use potentialQuadruples using potentialTriplets; false: build potential* lists individually (true should be produce slightly smaller and more accurate lists)
	if (incrementalBuild)
		bMesh->computeAllPotentialsIncremental(sMesh);
	else
	{
		bMesh->computePotentialTriplets(sMesh);
		bMesh->computePotentialQuadruples(sMesh);
		bMesh->computePotentialQuintuples(sMesh);
	}//*/
	
	//fprint the matches w/ their distortions
	char fName[300];
/*	char* inputStr = "jumping";
	if (theInput == CENTAUR_HORSE)
		inputStr = "centaur-horse";
	else if (theInput == CENTAUR_DAVID)
		inputStr = "centaur-david";
	else if (theInput == CENTAUR_GIRL)
		inputStr = "centaur-girl";
	else if (theInput == GORILLA_GIRL)
		inputStr = "gorilla-girl";
	else if (theInput == HORSE)
		inputStr = "horse-horsepartial";
	else if (theInput == SEAHORSE_HORSE)
		inputStr = "seahorse-horse";
	else if (theInput == SCAPE)
		inputStr = "scape";
	else if (theInput == SHREC11)
		inputStr = "shrec11";
	char inpStr[250];
	sprintf(inpStr, "%s%s", inputStr, (extendCoarseMapToDense ? "\\dense" : ""));	
	if (batchRun)
		sprintf(fName, "..\\..\\..\\code\\output\\PartialShapeCorresponder_x64\\%s\\C_%d-%d w7=%.2f, #dense=%d.dat", inpStr, frameID, mesh2->frameID, weight7, (dense40 ? 40 : 120));
	else
		sprintf(fName, "..\\..\\..\\code\\output\\PartialShapeCorresponder_x64\\%s\\C_%d-%d.dat", inpStr, frameID, mesh2->frameID);*/
	sprintf(fName, "output\\[2]\\C_%d-%d.dat", frameID, mesh2->frameID);
	bool finalMatchReady = false;

	if (indDistoComputation == 0)
		matchBasesViaNghbs(bMesh, sMesh); //matchIdxs[] ready to be used below
	else if (indDistoComputation == 1)
		matchBasesViaAll(bMesh, sMesh); //matchIdxs[] ready to be used below
	else
	{
		matchBasesViaAllEff(bMesh, sMesh); //matchIdxs2[] ready to be used in matchBasesViaDenseMatches()
		finalMatchReady = matchBasesViaVoting2(bMesh, sMesh, fName, false);//matchBasesViaVoting(bMesh, sMesh, fName); //just disable this to see and fprint the resulting match of matchBasesViaAllEff()
	}//*/

	FILE* fPtr = fopen(fName, (finalMatchReady ? "a" :"w")); //matchBasesViaVoting() already computed and fprinted final match; append just the pruned bases info here
	int indDistortionProcessing = 1; //individual distortions (stored in matchIdxs' odd idxs) will be used in blossom matching (2) or trivial many-to-1 min-distortion selection (3) or min-row-col algo (1)
	if (! finalMatchReady && indDistortionProcessing == 3) //trivial min selection
	{
		//assigning the min-distortion match to each bMesh verts
		fprintf(fPtr, "%d\t%f\t%f\t%f\t%f\n", m, minGeoDistG, maxGeoDistG, mesh2->minGeoDistG, mesh2->maxGeoDistG); //# of correspondences to be written below is m for sure when additional bases not present
		int nCalls = 0, nMaxCalls = 0; //# of matchBasesViaAll() calls below
		while (true)
		{
			safeCorresp.clear(); //all line drawingss are based on safeCorresp; sphere drawing use interestingMatches
			float finalIsoDistortion = 0.0f, maxIsoCost = -INF;
			for (int i = 0; i < m; i++)
			{
				if (bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.empty())
					continue; //this is a helper additionalBase and therefore skipped by matchBasesViaNghbs() for matchIdxs

				float minDisto = INF;
				int bestMatchIdx = -1;
				for (int j = 1; j < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.size(); j += 2) //check odd idxs for distortion values of the followed matchIdx
					if (bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[j] < minDisto)
					{
						minDisto = bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[j];
						bestMatchIdx = (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[j-1]; //-1: followed integer matchIdx value
					}
				if (bestMatchIdx == -1) //all matchIdxs[j] above are INF, so no match for [i]
				{
					fprintf(fPtr, "%d\t%d\t\t%f\n", (bMesh == this ? bMesh->baseVerts[i] : bestMatchIdx), (bMesh == this ? bestMatchIdx : bMesh->baseVerts[i]), minDisto); //fprint the -1 match to keep the file in the same format, that is m lines per mapping
					continue;
				}
				bMesh->verts[ bMesh->baseVerts[i] ]->matchIdx = bestMatchIdx;
				bMesh->verts[ bMesh->baseVerts[i] ]->matchCostI = minDisto;
				finalIsoDistortion += minDisto;

				//mesh1 on even, mesh2 on odd idxs in safeCorresp[]; also, mesh1 on the left, mesh2 on the right in all prints; matchIdx is defined only for mesh1 verts
				if (bMesh == this)
				{
					safeCorresp.push_back(bMesh->baseVerts[i]);
					safeCorresp.push_back(bestMatchIdx); //mesh1.matchIdx/matchCostI handled above as bMesh = this here
					cout << bMesh->baseVerts[i] << " -- " << bestMatchIdx << "\t\t" << minDisto << endl;
					fprintf(fPtr, "%d\t%d\t\t%f\n", bMesh->baseVerts[i], bestMatchIdx, minDisto);
					if (minDisto > maxIsoCost)
					{
						bMesh->worstMatchI = bMesh->baseVerts[i];
						bMesh->verts[ bMesh->worstMatchI ]->worstIin2 = bestMatchIdx;
						maxIsoCost = minDisto;
					}
				}
				else
				{
					safeCorresp.push_back(bestMatchIdx);
					safeCorresp.push_back(bMesh->baseVerts[i]); //matchIdx & matchCostI handled above as bMesh = this here
					verts[bestMatchIdx]->matchIdx = bMesh->baseVerts[i];
					verts[bestMatchIdx]->matchCostI = minDisto; //mesh1.matchIdx/matchCostI handled as sMesh = this here
					cout << bestMatchIdx << " ---- " << bMesh->baseVerts[i]  << "\t\t" << minDisto << endl;
					fprintf(fPtr, "%d\t%d\t\t%f\n", bestMatchIdx, bMesh->baseVerts[i], minDisto);
					if (minDisto > maxIsoCost)
					{
						sMesh->worstMatchI = bestMatchIdx; //sMesh is this
						sMesh->verts[ sMesh->worstMatchI ]->worstIin2 = bMesh->baseVerts[i];
						maxIsoCost = minDisto;
					}
				}			
			} //end of i
			cout << "final isometric distortion: " << (finalIsoDistortion / ((int) safeCorresp.size() / 2)) << "\ndone\n\n";
			fprintf(fPtr, "%f\n", (finalIsoDistortion / ((int) safeCorresp.size() / 2)));

			if (bMesh == this)
				cout << maxIsoCost << " for " << bMesh->worstMatchI << "-" << bMesh->verts[bMesh->worstMatchI]->worstIin2 << " worst match\n\n"; //left ~ mesh1 (this) for prints (matchCostI may be overwritten in this many-to-1 map; so, use maxIsoCost)
			else
				cout << maxIsoCost << " for " << bMesh->verts[bMesh->worstMatchI]->worstIin2 << "-" << bMesh->worstMatchI << " worst match\n\n"; //left ~ mesh1 (this) for prints

			//evenIdxs of both worsts & tmpSafeCorrespTry (in matchBasesViaAll()) ~ sMesh, odd ~ bMesh
			worsts.push_back(bMesh->verts[bMesh->worstMatchI]->worstIin2); //even ~ sMesh (recall, in2 means in sMesh here)
			worsts.push_back(bMesh->worstMatchI); //odd ~ bMesh

			if (nCalls++ == nMaxCalls)
				break;

			if (indDistoComputation == 1)
			{
				cout << "\nmatchBasesViaAll() extra call # " << nCalls << " in action..\n";
				matchBasesViaAll(bMesh, sMesh); //matchIdxs[] ready to be used above
			}
			else
			{
				cout << "\nmatchBasesViaAllEff() extra call # " << nCalls << " in action..\n";
				matchBasesViaAllEff(bMesh, sMesh); //matchIdxs[] ready to be used above
			}
		}
	}
	else if (! finalMatchReady && indDistortionProcessing == 2) //blossom
	{
		fprintf(fPtr, "%d\t%f\t%f\t%f\t%f\n", n, minGeoDistG, maxGeoDistG, mesh2->minGeoDistG, mesh2->maxGeoDistG); //# of correspondences to be written below is n for sure 'cos blossom discards m-n outliers and gives 1-to-1 map of size n (sMesh)
		int nCalls = 0, nMaxCalls = 0; //# of matchBasesViaAll() calls below
		while (true)
		{
			//1-to-1 blossom matching of bases using individual distortions set by matchBasesViaAll() as cost mtrx
			float blossomErr = blossomOnBases2(bMesh, sMesh); //safeCorresp ready as well match distortions in matchCostI and worst match
			float finalIsoDistortion = 0.0f;
			for (int i = 0; i < (int) safeCorresp.size(); i += 2)
			{
				cout << safeCorresp[i] << " -- " << safeCorresp[i+1] << "\t\t" << verts[ safeCorresp[i] ]->matchCostI << endl; //left ~ mesh1 (this) for prints
				fprintf(fPtr, "%d\t%d\t\t%f\n", safeCorresp[i], safeCorresp[i+1], verts[ safeCorresp[i] ]->matchCostI);
				finalIsoDistortion += verts[ safeCorresp[i] ]->matchCostI;
				verts[ safeCorresp[i] ]->matchIdx = safeCorresp[i+1];
				mesh2->verts[ safeCorresp[i+1] ]->matchIdx = safeCorresp[i];
			}
			finalIsoDistortion /= ((int) safeCorresp.size() / 2);
			cout << "final isometric distortion: " << finalIsoDistortion << "\ndone\n";
			fprintf(fPtr, "%f\n", finalIsoDistortion);

			//getCorrespListScaleInvDistortion() that set worstMatchI called over bMesh, so worstMatchI defined on bMesh.verts
			bMesh->verts[ bMesh->worstMatchI ]->worstIin2 = bMesh->verts[ bMesh->worstMatchI ]->matchIdx; //in2 means inS (in sMesh) here
			if (bMesh == this)
				cout << bMesh->verts[bMesh->worstMatchI]->matchCostI << " for " << bMesh->worstMatchI << "-" << bMesh->verts[bMesh->worstMatchI]->worstIin2 << " worst match\n\n"; //left ~ mesh1 (this) for prints
			else
				cout << bMesh->verts[bMesh->worstMatchI]->matchCostI << " for " << bMesh->verts[bMesh->worstMatchI]->worstIin2 << "-" << bMesh->worstMatchI << " worst match\n\n"; //left ~ mesh1 (this) for prints

			//evenIdxs of both worsts & tmpSafeCorrespTry (in matchBasesViaAll()) ~ sMesh, odd ~ bMesh
			worsts.push_back(bMesh->verts[bMesh->worstMatchI]->worstIin2); //even ~ sMesh (recall, in2 means in sMesh here)
			worsts.push_back(bMesh->worstMatchI); //odd ~ bMesh

			if (nCalls++ == nMaxCalls)
				break;

			if (indDistoComputation == 1)
			{
				cout << "\nmatchBasesViaAll() extra call # " << nCalls << " in action..\n";
				matchBasesViaAll(bMesh, sMesh); //matchIdxs[] ready to be used above
			}
			else
			{
				cout << "\nmatchBasesViaAllEff() extra call # " << nCalls << " in action..\n";
				matchBasesViaAllEff(bMesh, sMesh); //matchIdxs[] ready to be used above
			}
		}
	}
	else if (! finalMatchReady && indDistortionProcessing == 1) //min-row-col algo
	{
		//similar to trivial min selection but when the min of mtrx selected in i-j'th entry, then i'th row and j'th column becomes INF to prevent many-to-1's; then the min of new mtrx is selected and it repeats
		fprintf(fPtr, "%d\t%f\t%f\t%f\t%f\n", m, minGeoDistG, maxGeoDistG, mesh2->minGeoDistG, mesh2->maxGeoDistG); //# of correspondences to be written below is m for sure when additional bases not present
		int nCalls = 0, nMaxCalls = 0; //# of matchBasesViaAll() calls below
		while (true)
		{
			safeCorresp.clear(); //all line drawingss are based on safeCorresp; sphere drawing use interestingMatches
			for (int i = 0; i < n; i++)
				sMesh->verts[ sMesh->baseVerts[i] ]->processed = false; //true means matched (and therefore cannot be matched again to maintain 1-to-1)
			for (int i = 0; i < m; i++)
				bMesh->verts[ bMesh->baseVerts[i] ]->processed = false; //true means matched (and therefore cannot be matched again to maintain 1-to-1)
		
			float finalIsoDistortion = 0.0f, maxIsoCost = -INF;
			while (true)
			{
				//first, find the next unmatched bMesh base w/ min entry in distortion mtrx
				int idxInB = -1;
				float minDisto = INF;
				for (int i = 0; i < m; i++)
				{
					if (bMesh->verts[ bMesh->baseVerts[i] ]->processed)// || bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.empty())
						continue; //already matched or this is a helper additionalBase and therefore skipped by matchBasesViaNghbs() for matchIdxs (or is skipped 'cos size'll be 0 and skipped below anyway)
					for (int j = 1; j < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.size(); j += 2) //check odd idxs for distortion values of the followed matchIdx
					{
						int idxInS = (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[j-1]; //-1: followed integer matchIdx value
						if (! sMesh->verts[idxInS]->processed && bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[j] < minDisto)
						{
							minDisto = bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[j];
							idxInB = bMesh->baseVerts[i];
						}
					}
				}
				if (idxInB == -1) //next unmatched bMesh base w/ min entry not found 'cos all bMesh verts have been marked as processed=true; cannot go further
				{
					//to keep file consistent, i need m matches per mapping; so, fprint the unmatched ones w/ -1 matchIdx to have m lines
					for (int i = 0; i < m; i++)
						if (! bMesh->verts[ bMesh->baseVerts[i] ]->processed)
							fprintf(fPtr, "%d\t%d\t\t%f\n", (bMesh == this ? bMesh->baseVerts[i] : -1), (bMesh == this ? -1 : bMesh->baseVerts[i]), INF); //fprint the -1 match to keep the file in the same format, that is m lines per mapping
					break;
				}
				else
				{
					//second, find the next unmatched min-dostortion match of idxInB in idxInB's row					
					float minDisto2 = INF;
					int bestMatchIdx = -1;
					for (int j = 1; j < (int) bMesh->verts[idxInB]->matchIdxs.size(); j += 2) //check odd idxs for distortion values of the followed matchIdx
					{
						int idxInS = (int) bMesh->verts[idxInB]->matchIdxs[j-1]; //-1: followed integer matchIdx value
						if (! sMesh->verts[idxInS]->processed && bMesh->verts[idxInB]->matchIdxs[j] < minDisto2)
						{
							minDisto2 = bMesh->verts[idxInB]->matchIdxs[j];
							bestMatchIdx = idxInS;						
						}
					}
					if (bestMatchIdx == -1) //all matchIdxs[j] above are INF, so no match for [i]
					{
						fprintf(fPtr, "%d\t%d\t\t%f\n", (bMesh == this ? idxInB : bestMatchIdx), (bMesh == this ? bestMatchIdx : idxInB), minDisto); //fprint the -1 match to keep the file in the same format, that is m lines per mapping
						continue;
					}
if (minDisto != minDisto2){cout<<"weird\n";exit(0);}
					bMesh->verts[idxInB]->matchIdx = bestMatchIdx;
					bMesh->verts[idxInB]->matchCostI = minDisto;
					finalIsoDistortion += minDisto;
					sMesh->verts[bestMatchIdx]->processed = bMesh->verts[idxInB]->processed = true; //matched; so, prevent from being matched again

					//mesh1 on even, mesh2 on odd idxs in safeCorresp[]; also, mesh1 on the left, mesh2 on the right in all prints; matchIdx is defined only for mesh1 verts
					if (bMesh == this)
					{
						safeCorresp.push_back(idxInB);
						safeCorresp.push_back(bestMatchIdx); //mesh1.matchIdx/matchCostI handled above as bMesh = this here
						verts[idxInB]->matchIdx = bestMatchIdx;
						verts[idxInB]->matchCostI = minDisto;
						cout << idxInB << " -- " << bestMatchIdx << "\t\t" << minDisto << endl;
						fprintf(fPtr, "%d\t%d\t\t%f\n", idxInB, bestMatchIdx, minDisto);
						if (minDisto > maxIsoCost)
						{
							bMesh->worstMatchI = idxInB;
							bMesh->verts[ bMesh->worstMatchI ]->worstIin2 = bestMatchIdx;
							maxIsoCost = minDisto;
						}
					}
					else
					{
						safeCorresp.push_back(bestMatchIdx);
						safeCorresp.push_back(idxInB); //matchIdx & matchCostI handled above as sMesh = this here
						verts[bestMatchIdx]->matchIdx = idxInB;
						verts[bestMatchIdx]->matchCostI = minDisto;
						cout << bestMatchIdx << " ---- " << idxInB  << "\t\t" << minDisto << endl;
						fprintf(fPtr, "%d\t%d\t\t%f\n", bestMatchIdx, idxInB, minDisto);
						if (minDisto > maxIsoCost)
						{
							sMesh->worstMatchI = bestMatchIdx; //sMesh is this
							sMesh->verts[ sMesh->worstMatchI ]->worstIin2 = idxInB;
							maxIsoCost = minDisto;
						}
					}
				}
			}
			cout << "final isometric distortion: " << (finalIsoDistortion / ((int) safeCorresp.size() / 2)) << "\ndone\n\n";
			fprintf(fPtr, "%f\n", (finalIsoDistortion / ((int) safeCorresp.size() / 2)));

			if (bMesh == this)
				cout << maxIsoCost << " for " << bMesh->worstMatchI << "-" << bMesh->verts[bMesh->worstMatchI]->worstIin2 << " worst match\n\n"; //left ~ mesh1 (this) for prints (matchCostI may be overwritten in this many-to-1 map; so, use maxIsoCost)
			else
				cout << maxIsoCost << " for " << bMesh->verts[bMesh->worstMatchI]->worstIin2 << "-" << bMesh->worstMatchI << " worst match\n\n"; //left ~ mesh1 (this) for prints

			//evenIdxs of both worsts & tmpSafeCorrespTry (in matchBasesViaAll()) ~ sMesh, odd ~ bMesh
			worsts.push_back(bMesh->verts[bMesh->worstMatchI]->worstIin2); //even ~ sMesh (recall, in2 means in sMesh here)
			worsts.push_back(bMesh->worstMatchI); //odd ~ bMesh

			if (nCalls++ == nMaxCalls)
				break;

			if (indDistoComputation == 1)
			{
				cout << "\nmatchBasesViaAll() extra call # " << nCalls << " in action..\n";
				matchBasesViaAll(bMesh, sMesh); //matchIdxs[] ready to be used above
			}
			else
			{
				cout << "\nmatchBasesViaAllEff() extra call # " << nCalls << " in action..\n";
				matchBasesViaAllEff(bMesh, sMesh); //matchIdxs[] ready to be used above
			}
		}
	}//*/
	fprintf(fPtr, "%d\npruned mesh1 bases:\n# = %d\n", -1, (int) baseVerts.size()); //extra info to show pruned bases whose subsets provided safeCorresp correspondence above; this is to draw all bases later
	for (int i = 0; i < (int) baseVerts.size(); i++)
		fprintf(fPtr, "%d\n", baseVerts[i]);
	fprintf(fPtr, "pruned mesh2 bases:\n# = %d\n", (int) mesh2->baseVerts.size());
	for (int i = 0; i < (int) mesh2->baseVerts.size(); i++)
		fprintf(fPtr, "%d\n", mesh2->baseVerts[i]);
	if (extendCoarseMapToDense) //extend coarse generic partial corresp b/w extremes to dense match via regional bases tactic used in confidence computation
	{
		bMesh->extendToDense2(sMesh, bMesh == this); //bMesh->extendToDense(sMesh, bMesh == this);
		fprintf(fPtr, "dense map %dx%d:\n", (int) denseMap.size() / 2, (int) denseMap.size() / 2);
		for (int i = 0; i < (int) denseMap.size(); i += 2)
			fprintf(fPtr, "%d\t%d\n", denseMap[i], denseMap[i+1]);
	}
//	fprintf(fPtr, "\n%d\n", nIters-1); //to see iteration id that gives the selected cool coarse map, to simplfy my freading later
	fclose(fPtr);
	/////////////////////// match ~10 extreme bases ends ///////////////////////
	cout << "done\n\n";	
}

void Mesh::extendToDense(Mesh* sMesh, bool bMeshIsThis)
{
	//extends coarse generic partial corresp b/w extremes to dense match by defining regional bases via potential triplets consisting of matched extremes

	vector< int > usedPotTriplets, usedMatchedExtremes; //prevent using the same potential triplet pair and matched extreme vert more than once
	int nIters = 0;
	while (nIters < 2)
	{
		//for the next best potential triplet, generate the desired map consisting of 3 extreme matches computed during partialMatching3(); this map provides the regions on which dense maps are done

		//////////////// find the next best potential triplet as bestPotTripletS and bestPotTripletT ////////////////
		vector< int > bestPotTripletS, bestPotTripletT;
		float minGeoConsistency = INF;
		int nextBestTripletID;		
		for (int a = 0; a < (int) potentialTriplets.size(); a += 6)
		{
			//heavily adapted from matchBasesViaAllEff()

			bool potTripletAlreadyUsed = false; //next best triplet must be different from the ones already used (my usual processed=true effect)
			for (int j = 0; j < (int) usedPotTriplets.size(); j++)
				if (a == usedPotTriplets[j])
				{
					potTripletAlreadyUsed = true;
					break;
				}
			if (potTripletAlreadyUsed)
				continue;

			//current 3-combination is copied from appropriate slots of potentialTriplets to tmpS (for tmp source)
			vector< int > tmpS;
			tmpS.push_back(potentialTriplets[a]); //even idxs ~ bMesh
			tmpS.push_back(potentialTriplets[a+2]);
			tmpS.push_back(potentialTriplets[a+4]);
			bool extremeAlreadyUsed = false; //dense map generator extreme vert must not have generated a dense map before; if i consider it again, it'll create similar regional bases as before redundantly (my usual processed=true effect)
			for (int j = 0; j < (int) usedMatchedExtremes.size(); j++)
				for (int k = 0; k < (int) tmpS.size(); k++)
					if (usedMatchedExtremes[j] == tmpS[k])
					{
						extremeAlreadyUsed = true;
						j = (int) usedMatchedExtremes.size(); //break outer as well
						break;
					}
			
			//this fixed combination in tmpS will be tried w/ 3! target triplet permutations tmpT
			vector< int > tmpT;
			tmpT.push_back(potentialTriplets[a+1]); //odd idxs ~ sMesh
			tmpT.push_back(potentialTriplets[a+3]);
			tmpT.push_back(potentialTriplets[a+5]);

			//compute geodesic consistency of this potential triplet
			float dist1 = verts[ tmpS[0] ]->dSpanningUnn[ tmpS[1] ],
				  dist2 = verts[ tmpS[0] ]->dSpanningUnn[ tmpS[2] ],
				  dist3 = verts[ tmpS[1] ]->dSpanningUnn[ tmpS[2] ], //so baseVerts[a-b-c] are 3 pnts in test here
				  maxDist = (dist1 > dist2 ? dist1 : dist2); //max of dist1 and 2
			maxDist = (maxDist > dist3 ? maxDist : dist3); //max of dist1 and 2 and 3
			float sum1 = (dist1 + dist2 + dist3) / maxDist; //same w/ less operations
			//same stuff for sMesh to get sum2
			dist1 = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[1] ];
			dist2 = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[2] ];
			dist3 = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[2] ];
			maxDist = (dist1 > dist2 ? dist1 : dist2);
			maxDist = (maxDist > dist3 ? maxDist : dist3);
			float sum2 = (dist1 + dist2 + dist3) / maxDist, diff = fabs(sum1 - sum2);
			if (diff < minGeoConsistency && ! extremeAlreadyUsed)
			{				
				//remember this next best potential triplet pair
				minGeoConsistency = diff;
				bestPotTripletS = tmpS;
				bestPotTripletT = tmpT;
				nextBestTripletID = a;
			}
		} //end of a
		usedPotTriplets.push_back(nextBestTripletID);
		//////////////// find the next best potential triplet as bestPotTripletS and bestPotTripletT ends ////////////////

		//////////////// generate the desired map consisting of 3 extreme matches computed during partialMatching3() ////////////////
		vector< int > desiredMap; //will be filled such that even idxs ~ bMesh, odd ~ sMesh
		//try 3!=6 reordering/permutations of bestPotTripletT w/ stable bestPotTripletS of size 3
		for (int r = 0; r < 6; r++)
		{
			vector< int > tmpSafeCorrespTry; //will be filled such that even idxs ~ bMesh, odd ~ sMesh
			int idx1 = 0, idx2 = 0;
			bool consistsOf3ExtremeMatches = true; //a valid/desired tmpSafeCorrespTry must consist of 3 extreme matches computed during partialMatching3()
			for (int y = 0; y < 6; y++) //traversalListTry of size 3, hence 2*3=6
				if (y % 2 == 0)
					tmpSafeCorrespTry.push_back(bestPotTripletS[idx1++]); //stable from bMesh side				
				else
				{
					if (verts[ tmpSafeCorrespTry[y-1] ]->matchIdx != bestPotTripletT[ perms3[r][idx2] ]) //last bMesh element ([y-1]) is not matched during partialMatching3() w/ the current sMesh element to be pushed below ([idx2]); so, [y-1]-[idx2] not a good match which makes this permutation undesired
					{
						consistsOf3ExtremeMatches = false;
						break;
					}
					tmpSafeCorrespTry.push_back(bestPotTripletT[ perms3[r][idx2++] ]); //permutations from sMesh side
				}
			if (consistsOf3ExtremeMatches) //detected no bad [y-1]-[idx2] which makes this permutation desired
			{
				desiredMap = tmpSafeCorrespTry; //no evaluation of this potentially bad/confusing tmpSafeCorrespTry that involves a bad match
				break;
			}
		} //end of r
		if (desiredMap.empty())
		{
//			cout << "desired map consisting of matched-extremes not generated w/ this (weird) next-best potential triplet pair\n";
			continue; //restart a-iterations but this time this weird potential triplet pair is excluded as it is now in usedPotTriplets (although not used anywhere, so naming sucks)
		}
		//////////////// generate the desired map consisting of 3 extreme matches computed during partialMatching3() ends ////////////////

		//////////////// desiredMap provides the regions on which dense maps are done ////////////////
cout << "\ndesiredMap in use:\n"; for (int i = 0; i < 6; i += 2) cout << (bMeshIsThis ? desiredMap[i] : desiredMap[i+1]) << "\t" << (bMeshIsThis ? desiredMap[i+1] : desiredMap[i]) << endl;
		for (int i = 0; i < 3; i++)
			usedMatchedExtremes.push_back(desiredMap[2*i]); //remember generating bMesh extremes not to use them again
		//heavily adapted from matchBasesViaVoting2(); comments removed to save space

		//////////////////////// regional verts ////////////////////////
		int m = (int) baseVerts.size(), n = (int) sMesh->baseVerts.size();
		vector< int > regionVertsB, regionVertsS;
		float maxExtremeDistB = (verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[2] ] > verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[4] ] ? verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[2] ] : verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[4] ]);
		maxExtremeDistB = (verts[ desiredMap[2] ]->dSpanningUnn[ desiredMap[4] ] > maxExtremeDistB ? verts[ desiredMap[2] ]->dSpanningUnn[ desiredMap[4] ] : maxExtremeDistB);
		for (int v = 0; v < (int) verts.size(); v++)
			verts[v]->processed = false;
		for (int v = 0; v < (int) sMesh->verts.size(); v++)
			sMesh->verts[v]->processed = false;
		for (int y = 0; y < 6; y += 2)
		{
			regionVertsB.push_back(desiredMap[y]);
			regionVertsS.push_back(desiredMap[y+1]);
			verts[ desiredMap[y] ]->processed = sMesh->verts[ desiredMap[y+1] ]->processed = true;
		}
		for (int v = 0; v < (int) verts.size(); v++)
			if (! verts[v]->processed && verts[ desiredMap[0] ]->dSpanningUnn[v] <= maxExtremeDistB && verts[ desiredMap[2] ]->dSpanningUnn[v] <= maxExtremeDistB && verts[ desiredMap[4] ]->dSpanningUnn[v] <= maxExtremeDistB)
			{
				regionVertsB.push_back(v);
				verts[v]->processed = true;
			}
		float maxExtremeDistS = (sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[3] ] > sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[5] ] ? sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[3] ] : sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[5] ]);
		maxExtremeDistS = (sMesh->verts[ desiredMap[3] ]->dSpanningUnn[ desiredMap[5] ] > maxExtremeDistS ? sMesh->verts[ desiredMap[3] ]->dSpanningUnn[ desiredMap[5] ] : maxExtremeDistS);
		for (int v = 0; v < (int) sMesh->verts.size(); v++)
			if (! sMesh->verts[v]->processed && sMesh->verts[ desiredMap[1] ]->dSpanningUnn[v] <= maxExtremeDistS && sMesh->verts[ desiredMap[3] ]->dSpanningUnn[v] <= maxExtremeDistS && sMesh->verts[ desiredMap[5] ]->dSpanningUnn[v] <= maxExtremeDistS)
			{
				regionVertsS.push_back(v);
				sMesh->verts[v]->processed = true;
			}
		for (int e = 0; e < m; e++)
			if (verts[ baseVerts[e] ]->lMaxMinMu == 1 && baseVerts[e] != desiredMap[0] && baseVerts[e] != desiredMap[2] && baseVerts[e] != desiredMap[4])
			{
				float h = (verts[ desiredMap[0] ]->dSpanningUnn[ baseVerts[e] ] < verts[ desiredMap[2] ]->dSpanningUnn[ baseVerts[e] ] ? verts[ desiredMap[0] ]->dSpanningUnn[ baseVerts[e] ] : verts[ desiredMap[2] ]->dSpanningUnn[ baseVerts[e] ]);
				h = (verts[ desiredMap[4] ]->dSpanningUnn[ baseVerts[e] ] < h ? verts[ desiredMap[4] ]->dSpanningUnn[ baseVerts[e] ] : h);
				h /= 2;
				for (int rv = 0; rv < (int) regionVertsB.size(); rv++)
					if (verts[ baseVerts[e] ]->dSpanningUnn[ regionVertsB[rv] ] <= h)
						verts[ regionVertsB[rv] ]->processed = false;
			}
		for (int e = 0; e < n; e++)
			if (sMesh->verts[ sMesh->baseVerts[e] ]->lMaxMinMu == 1 && sMesh->baseVerts[e] != desiredMap[1] && sMesh->baseVerts[e] != desiredMap[3] && sMesh->baseVerts[e] != desiredMap[5]) //all but prs ([1][3][5]), also use only local maximas (lMaxMinMu=1), i.e. ignore minimas which are lMaxMinMu=2
			{
				float h = (sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ sMesh->baseVerts[e] ] < sMesh->verts[ desiredMap[3] ]->dSpanningUnn[ sMesh->baseVerts[e] ] ? sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ sMesh->baseVerts[e] ] : sMesh->verts[ desiredMap[3] ]->dSpanningUnn[ sMesh->baseVerts[e] ]);
				h = (sMesh->verts[ desiredMap[5] ]->dSpanningUnn[ sMesh->baseVerts[e] ] < h ? sMesh->verts[ desiredMap[5] ]->dSpanningUnn[ sMesh->baseVerts[e] ] : h);
				h /= 2;				
				for (int rv = 0; rv < (int) regionVertsS.size(); rv++)
					if (sMesh->verts[ sMesh->baseVerts[e] ]->dSpanningUnn[ regionVertsS[rv] ] <= h)
						sMesh->verts[ regionVertsS[rv] ]->processed = false;
			}
		vector< int > tmp;
		for (int rv = 0; rv < (int) regionVertsB.size(); rv++)
			if (verts[ regionVertsB[rv] ]->processed)
				tmp.push_back(regionVertsB[rv]);
		regionVertsB = tmp;
		tmp.clear();
		for (int rv = 0; rv < (int) regionVertsS.size(); rv++)
			if (sMesh->verts[ regionVertsS[rv] ]->processed)
				tmp.push_back(regionVertsS[rv]);
		regionVertsS = tmp;
		//////////////////////// regional verts ends ////////////////////////

		//////////////////////// regional base verts ////////////////////////
		float scaler = ((verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[2] ] / sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[3] ]) +
						(verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[4] ] / sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[5] ]) +
						(verts[ desiredMap[2] ]->dSpanningUnn[ desiredMap[4] ] / sMesh->verts[ desiredMap[3] ]->dSpanningUnn[ desiredMap[5] ])) / 3.0f,
			  rb = 0.284f * sqrt(computeRegionArea(regionVertsB) / PI), //40 region bases: A = 40 pi r^2 ==> r = 0.1 sqrt(A/pi) gives 40 on a disk/patch; *~2 (=1.8) to make 40 on a closed surface (as 2 same patches connected to make the closed surface) (.284=.158*1.8)
			  nMaxS = 200;//120		blossom's will be done if # of regional bases not differ by too much
		if (! dense40)
		{
			rb = 0.171f * sqrt(computeRegionArea(regionVertsB) / PI); //120 region bases [don't change 200 below 'cos then it gets too slow as 200+ x 200+ blossom takes ~1 sec] first term = 0.18f ~ 100 region bases, .201f ~ 80 region bases, .44f ~ 20 region bases
			nMaxS = 200;
		}

		for (int i = 0; i < (int) sMesh->verts.size(); i++)
		{
			for (int c = 0; c < 3; c++)
				sMesh->verts[i]->coords[c] = sMesh->verts[i]->coords2[c];
			sMesh->verts[i]->coords[0] *= scaler;
			sMesh->verts[i]->coords[1] *= scaler;
			sMesh->verts[i]->coords[2] *= scaler;
			sMesh->verts[i]->baseCandidate = false;
		}
		for (int i = 0; i < (int) verts.size(); i++)
			verts[i]->baseCandidate = false;
		for (int e = 0; e < (int) sMesh->edges.size(); e++)
			sMesh->edges[e]->length = distanceBetween(sMesh->verts[sMesh->edges[e]->v1i]->coords, sMesh->verts[sMesh->edges[e]->v2i]->coords);
		for (int e = 0; e < (int) sMesh->sedges.size(); e++)
			sMesh->sedges[e]->length = distanceBetween(sMesh->verts[sMesh->sedges[e]->v1i]->coords, sMesh->verts[sMesh->sedges[e]->v2i]->coords);
		vector< int > regionBaseVertsB = computeRegionBaseVerts(rb, regionVertsB, 8), regionBaseVertsS = sMesh->computeRegionBaseVerts(rb, regionVertsS, 8);
		//////////////////////// regional base verts ends ////////////////////////

		//////////////////////// dense base verts matching ////////////////////////
		if ((int) regionBaseVertsS.size() >= nMaxS) //rb makes ~40 bases on regionVertsB for sure; same rb is expected to make ~40 on regionVertsS if two regions are compatible, i.e. generating triplets are good matches; regionBaseVertsS.size() >= 200 (nMaxS) means there's a huge difference b/w regions so don't waste time to produce confidence values for generating (bad) matches; blossom on 300x300 takes ~1 sec so lots of 1 secs make me loose big time; simply use 0 confidences for a huge difference case (in else part)
		{
			cout << "this much difference shouldn't happen b/w regions which are supposed to be very compatible\n";
			exit(0);
		}
		//create dense map b/w regionBaseVertsB & regionBaseVertsS and set the matchIdx of unmatched regional bases (matched means that base is overlapped by a previous best potential triplet pair; keep that one 'cos that pair was a better best than this current best, i.e. less minGeoConsistency for prev)

		for (int y = 1; y < 6; y += 2)
		{
			if (! sMesh->verts[ desiredMap[y] ]->dSpanningUnnSS)
				sMesh->verts[ desiredMap[y] ]->dSpanningUnnSS = new float[ (int) sMesh->verts.size() ];
			for (int v = 0; v < (int) sMesh->verts.size(); v++)
				sMesh->verts[ desiredMap[y] ]->dSpanningUnnSS[v] = sMesh->verts[ desiredMap[y] ]->dSpanningUnn[v] * scaler;
		}
		maxExtremeDistS *= scaler;
		int n1 = (int) regionBaseVertsB.size(), n2 = (int) regionBaseVertsS.size();
		int nRows = (n1 > n2 ? n1 : n2), nCols = nRows;
		int* vIdx1 = new int[n1], * vIdx2 = new int[n2];
		float** costs = new float*[nRows];
		float discourage = INF;
		int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e, * edges = new int[2*nEdgeNum], * matches = new int[nVertNum];
		double* weightsD = new double[nEdgeNum];
		int row = 0;
		for (int i = 0; i < n1; i++)
		{
			costs[row] = new float[nCols];
			vIdx1[row] = regionBaseVertsB[i];
			int col = 0;
			for (int j = 0; j < n2; j++)
				vIdx2[col++] = regionBaseVertsS[j];
			while (col < nCols)
				costs[row][col++] = discourage;
			row++;
		}
		while (row < nRows)
		{
			costs[row] = new float[nCols];
			for (int col = 0; col < nCols; col++)
				costs[row][col] = discourage;
			row++;
		}
		row = 0;
		for (int i = 0; i < n1; i++)
		{
			int base1 = regionBaseVertsB[i];
			verts[base1]->matchIdxs.clear();
			int col = 0;
			for (int j = 0; j < n2; j++)
			{
				int base2 = regionBaseVertsS[j];
				bool extremeMatch = false;
				for (int y = 0; y < 6; y += 2)
					if (desiredMap[y] == base1 && desiredMap[y+1] == base2)
						extremeMatch = true;
				float vertexBasedIso = 0.0f;
				int nAdds = 0;
				for (int ss = 0; ss < 3; ss++)
				{
					int s1 = desiredMap[2*ss], s2 = desiredMap[2*ss + 1];
					if (s1 == base1)
						continue;
					float t1s1 = verts[s1]->dSpanningUnn[base1] / maxExtremeDistB, t2s2 = sMesh->verts[s2]->dSpanningUnnSS[base2] / maxExtremeDistS;
					vertexBasedIso += fabs(t1s1 - t2s2);
					nAdds++;
				}
if (nAdds <= 0) {cout << "can't traverse 0-size\n";exit(0);}
				vertexBasedIso /= nAdds;
				costs[row][col++] = (! extremeMatch ? vertexBasedIso : -INF);
				verts[base1]->matchIdxs.push_back((float) base2);
				verts[base1]->matchIdxs.push_back(vertexBasedIso);
			}
			row++;
		}
		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* blossom = new Blossom();
		blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches);
		delete blossom;
		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 && verts[ vIdx1[m1] ]->matchIdx == -1) //2nd condition to guarantee that bMesh vert not matched yet; so match it below by updateing matchIdx; if already matched then do nothing to keep the existing match, which is from a prev, and hence better, triplet pair w/ less minGeoConsistency
				{
					//the correspondences
					denseMap.push_back((bMeshIsThis ? vIdx1[m1] : mi)); //base1 from bMesh; be sure even idxs ~ mesh1, odds ~ mesh2
					denseMap.push_back((bMeshIsThis ? mi : vIdx1[m1])); //base2 from sMesh
					verts[ vIdx1[m1] ]->matchIdx = mi; //vIdx1[m1] ~ bMesh, mi ~ sMesh for sure (bool bMeshIsThis is a naming good for caller partialMatching3() fnctn, not for here)
				}
			}
		} //end of m
		//////////////// desiredMap provides the regions on which dense maps are done ends ////////////////
		nIters++;
	}
}
void Mesh::extendToDense2(Mesh* sMesh, bool bMeshIsThis)
{
	//extends coarse generic partial corresp b/w extremes to dense match by defining regions for each triplet of matched extremes and distributing dense samples on them; matches of these dense samples are blended in the end	

	int nCoarseMap = (bMeshIsThis ? (int) safeCorresp.size() / 2 : (int) sMesh->safeCorresp.size() / 2);
	cout << "extending " << nCoarseMap << " x " << nCoarseMap << " to dense match via blending on regions..\n";
	fillPermutationMtrxs(); //perms[][] filled over mesh1; when bMeshIsThis=false, this.perms[][] remain null below; so, be safe and fill perms over bMesh as well

	//for the next triplet of extreme matches, find the regions on which dense maps are done
	int m = (int) baseVerts.size(), n = (int) sMesh->baseVerts.size(), nDesiredMaps = 0;
	for (int i = 0; i < m; i++)
		verts[ baseVerts[i] ]->matchIdxs2.clear(); //matches of bMesh.regionBaseVerts'll be stored here along w/ their costs and then blended according to the match costs
	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 bMesh.baseVerts to tmpS (for tmp source)
				if (verts[ baseVerts[a] ]->matchIdx == -1 || verts[ baseVerts[b] ]->matchIdx == -1 || verts[ baseVerts[c] ]->matchIdx == -1)
					continue; //use triplets of matched extremes; so, all 3 a-b-c must have a match to proceed
				vector< int > tmpS;
				tmpS.push_back(baseVerts[a]);
				tmpS.push_back(baseVerts[b]);
				tmpS.push_back(baseVerts[c]);

				//this fixed combination in tmpS will be tried w/ all target tuple permutations, tmpT
				for (int d = 0; d < n; d++)
					for (int e = d+1; e < n; e++)
						for (int f = e+1; f < n; f++)
						{
							if ((verts[ baseVerts[a] ]->matchIdx != sMesh->baseVerts[d] && verts[ baseVerts[a] ]->matchIdx != sMesh->baseVerts[e] && verts[ baseVerts[a] ]->matchIdx != sMesh->baseVerts[f]) || //baseVerts[a] can't find a match w/ this set of sMesh bases no matter how much i permute
								(verts[ baseVerts[b] ]->matchIdx != sMesh->baseVerts[d] && verts[ baseVerts[b] ]->matchIdx != sMesh->baseVerts[e] && verts[ baseVerts[b] ]->matchIdx != sMesh->baseVerts[f]) || //baseVerts[b] can't find a match w/ this set of sMesh bases no matter how much i permute
								(verts[ baseVerts[c] ]->matchIdx != sMesh->baseVerts[d] && verts[ baseVerts[c] ]->matchIdx != sMesh->baseVerts[e] && verts[ baseVerts[c] ]->matchIdx != sMesh->baseVerts[f])) //baseVerts[c] can't find a match w/ this set of sMesh bases no matter how much i permute
								continue; //use triplets of extremes that are matchable w/ bMesh triplet; so, all 3 d-e-f must have a related match to proceed
							//current 3-combination is copied from appropriate slots of sMesh.baseVerts to tmpT (for tmp target)
							vector< int > tmpT;
							tmpT.push_back(sMesh->baseVerts[d]);
							tmpT.push_back(sMesh->baseVerts[e]);
							tmpT.push_back(sMesh->baseVerts[f]);

							//////////////// generate the desired map consisting of 3 extreme matches computed during partialMatching3() ////////////////
							vector< int > desiredMap; //will be filled such that even idxs ~ bMesh, odd ~ sMesh
							//try 3!=6 reordering/permutations of bestPotTripletT w/ stable bestPotTripletS of size 3
							for (int r = 0; r < 6; r++)
							{
								vector< int > tmpSafeCorrespTry; //will be filled such that even idxs ~ bMesh, odd ~ sMesh
								int idx1 = 0, idx2 = 0;
								bool consistsOf3ExtremeMatches = true; //a valid/desired tmpSafeCorrespTry must consist of 3 extreme matches computed during partialMatching3()
								for (int y = 0; y < 6; y++) //traversalListTry of size 3, hence 2*3=6
									if (y % 2 == 0)
										tmpSafeCorrespTry.push_back(tmpS[idx1++]); //stable from bMesh side
									else
									{
										if (verts[ tmpSafeCorrespTry[y-1] ]->matchIdx != tmpT[ perms3[r][idx2] ]) //last bMesh element ([y-1]) is not matched during partialMatching3() w/ the current sMesh element to be pushed below ([idx2]); so, [y-1]-[idx2] not a good match which makes this permutation undesired
										{
											consistsOf3ExtremeMatches = false;
											break;
										}
										tmpSafeCorrespTry.push_back(tmpT[ perms3[r][idx2++] ]); //permutations from sMesh side
									}
								if (consistsOf3ExtremeMatches) //detected no bad [y-1]-[idx2] which makes this permutation desired
								{
									desiredMap = tmpSafeCorrespTry; //no evaluation of this potentially bad/confusing tmpSafeCorrespTry that involves a bad match
									break;
								}
							} //end of r
							if (desiredMap.empty())
							{
								cout << "desired map consisting of matched-extremes not generated w/ this triplet of matched extremes: " << (bMeshIsThis ? baseVerts[a] : sMesh->baseVerts[d]) << "-" << (bMeshIsThis ? baseVerts[b] : sMesh->baseVerts[e]) << "-" << (bMeshIsThis ? baseVerts[c] : sMesh->baseVerts[f]) << "\t" << (bMeshIsThis ? baseVerts[d] : sMesh->baseVerts[a]) << "-" << (bMeshIsThis ? baseVerts[e] : sMesh->baseVerts[b]) << "-" << (bMeshIsThis ? baseVerts[f] : sMesh->baseVerts[c]) << "\n";
								exit(0);
							}
							//////////////// generate the desired map consisting of 3 extreme matches computed during partialMatching3() ends ////////////////

							//////////////// desiredMap provides the regions on which dense maps are done ////////////////
//cout << "\ndesiredMap in use:\n"; for (int i = 0; i < 6; i += 2) cout << (bMeshIsThis ? desiredMap[i] : desiredMap[i+1]) << "\t" << (bMeshIsThis ? desiredMap[i+1] : desiredMap[i]) << endl;
							nDesiredMaps++;
							//heavily adapted from matchBasesViaVoting2(); comments removed to save space

							//////////////////////// regional verts ////////////////////////
							vector< int > regionVertsB, regionVertsS;
							float maxExtremeDistB = (verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[2] ] > verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[4] ] ? verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[2] ] : verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[4] ]);
							maxExtremeDistB = (verts[ desiredMap[2] ]->dSpanningUnn[ desiredMap[4] ] > maxExtremeDistB ? verts[ desiredMap[2] ]->dSpanningUnn[ desiredMap[4] ] : maxExtremeDistB);
							for (int v = 0; v < (int) verts.size(); v++)
								verts[v]->processed = false;
							for (int v = 0; v < (int) sMesh->verts.size(); v++)
								sMesh->verts[v]->processed = false;
							for (int y = 0; y < 6; y += 2)
							{
								regionVertsB.push_back(desiredMap[y]);
								regionVertsS.push_back(desiredMap[y+1]);
								verts[ desiredMap[y] ]->processed = sMesh->verts[ desiredMap[y+1] ]->processed = true;
							}
							for (int v = 0; v < (int) verts.size(); v++)
								if (! verts[v]->processed && verts[ desiredMap[0] ]->dSpanningUnn[v] <= maxExtremeDistB && verts[ desiredMap[2] ]->dSpanningUnn[v] <= maxExtremeDistB && verts[ desiredMap[4] ]->dSpanningUnn[v] <= maxExtremeDistB)
								{
									regionVertsB.push_back(v);
									verts[v]->processed = true;
								}
							float maxExtremeDistS = (sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[3] ] > sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[5] ] ? sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[3] ] : sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[5] ]);
							maxExtremeDistS = (sMesh->verts[ desiredMap[3] ]->dSpanningUnn[ desiredMap[5] ] > maxExtremeDistS ? sMesh->verts[ desiredMap[3] ]->dSpanningUnn[ desiredMap[5] ] : maxExtremeDistS);
							for (int v = 0; v < (int) sMesh->verts.size(); v++)
								if (! sMesh->verts[v]->processed && sMesh->verts[ desiredMap[1] ]->dSpanningUnn[v] <= maxExtremeDistS && sMesh->verts[ desiredMap[3] ]->dSpanningUnn[v] <= maxExtremeDistS && sMesh->verts[ desiredMap[5] ]->dSpanningUnn[v] <= maxExtremeDistS)
								{
									regionVertsS.push_back(v);
									sMesh->verts[v]->processed = true;
								}
							for (int e = 0; e < m; e++)
								if (verts[ baseVerts[e] ]->lMaxMinMu == 1 && baseVerts[e] != desiredMap[0] && baseVerts[e] != desiredMap[2] && baseVerts[e] != desiredMap[4])
								{
									float h = (verts[ desiredMap[0] ]->dSpanningUnn[ baseVerts[e] ] < verts[ desiredMap[2] ]->dSpanningUnn[ baseVerts[e] ] ? verts[ desiredMap[0] ]->dSpanningUnn[ baseVerts[e] ] : verts[ desiredMap[2] ]->dSpanningUnn[ baseVerts[e] ]);
									h = (verts[ desiredMap[4] ]->dSpanningUnn[ baseVerts[e] ] < h ? verts[ desiredMap[4] ]->dSpanningUnn[ baseVerts[e] ] : h);
									h /= 2;
									for (int rv = 0; rv < (int) regionVertsB.size(); rv++)
										if (verts[ baseVerts[e] ]->dSpanningUnn[ regionVertsB[rv] ] <= h)
											verts[ regionVertsB[rv] ]->processed = false;
								}
							for (int e = 0; e < n; e++)
								if (sMesh->verts[ sMesh->baseVerts[e] ]->lMaxMinMu == 1 && sMesh->baseVerts[e] != desiredMap[1] && sMesh->baseVerts[e] != desiredMap[3] && sMesh->baseVerts[e] != desiredMap[5]) //all but prs ([1][3][5]), also use only local maximas (lMaxMinMu=1), i.e. ignore minimas which are lMaxMinMu=2
								{
									float h = (sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ sMesh->baseVerts[e] ] < sMesh->verts[ desiredMap[3] ]->dSpanningUnn[ sMesh->baseVerts[e] ] ? sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ sMesh->baseVerts[e] ] : sMesh->verts[ desiredMap[3] ]->dSpanningUnn[ sMesh->baseVerts[e] ]);
									h = (sMesh->verts[ desiredMap[5] ]->dSpanningUnn[ sMesh->baseVerts[e] ] < h ? sMesh->verts[ desiredMap[5] ]->dSpanningUnn[ sMesh->baseVerts[e] ] : h);
									h /= 2;				
									for (int rv = 0; rv < (int) regionVertsS.size(); rv++)
										if (sMesh->verts[ sMesh->baseVerts[e] ]->dSpanningUnn[ regionVertsS[rv] ] <= h)
											sMesh->verts[ regionVertsS[rv] ]->processed = false;
								}
							vector< int > tmp;
							for (int rv = 0; rv < (int) regionVertsB.size(); rv++)
								if (verts[ regionVertsB[rv] ]->processed)
									tmp.push_back(regionVertsB[rv]);
							regionVertsB = tmp;
							tmp.clear();
							for (int rv = 0; rv < (int) regionVertsS.size(); rv++)
								if (sMesh->verts[ regionVertsS[rv] ]->processed)
									tmp.push_back(regionVertsS[rv]);
							regionVertsS = tmp;
							//////////////////////// regional verts ends ////////////////////////

							//////////////////////// regional base verts ////////////////////////
							float scaler = ((verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[2] ] / sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[3] ]) +
											(verts[ desiredMap[0] ]->dSpanningUnn[ desiredMap[4] ] / sMesh->verts[ desiredMap[1] ]->dSpanningUnn[ desiredMap[5] ]) +
											(verts[ desiredMap[2] ]->dSpanningUnn[ desiredMap[4] ] / sMesh->verts[ desiredMap[3] ]->dSpanningUnn[ desiredMap[5] ])) / 3.0f,
									rb = 0.284f * sqrt(computeRegionArea(regionVertsB) / PI), //40 region bases: A = 40 pi r^2 ==> r = 0.1 sqrt(A/pi) gives 40 on a disk/patch; *~2 (=1.8) to make 40 on a closed surface (as 2 same patches connected to make the closed surface) (.284=.158*1.8)
									nMaxS = 200;//120		blossom's will be done if # of regional bases not differ by too much
//							if (! dense40)
							{
								rb = 0.171f * sqrt(computeRegionArea(regionVertsB) / PI); //120 region bases [don't change 200 below 'cos then it gets too slow as 200+ x 200+ blossom takes ~1 sec] first term = 0.18f ~ 100 region bases, .201f ~ 80 region bases, .44f ~ 20 region bases
								nMaxS = 200;
							}

							for (int i = 0; i < (int) sMesh->verts.size(); i++)
							{
								for (int c = 0; c < 3; c++)
									sMesh->verts[i]->coords[c] = sMesh->verts[i]->coords2[c];
								sMesh->verts[i]->coords[0] *= scaler;
								sMesh->verts[i]->coords[1] *= scaler;
								sMesh->verts[i]->coords[2] *= scaler;
								sMesh->verts[i]->baseCandidate = false;
							}
							for (int i = 0; i < (int) verts.size(); i++)
								verts[i]->baseCandidate = false;
							for (int e = 0; e < (int) sMesh->edges.size(); e++)
								sMesh->edges[e]->length = distanceBetween(sMesh->verts[sMesh->edges[e]->v1i]->coords, sMesh->verts[sMesh->edges[e]->v2i]->coords);
							for (int e = 0; e < (int) sMesh->sedges.size(); e++)
								sMesh->sedges[e]->length = distanceBetween(sMesh->verts[sMesh->sedges[e]->v1i]->coords, sMesh->verts[sMesh->sedges[e]->v2i]->coords);
							vector< int > regionBaseVertsB = computeRegionBaseVerts2(rb, regionVertsB), regionBaseVertsS = sMesh->computeRegionBaseVerts2(rb, regionVertsS);
							//////////////////////// regional base verts ends ////////////////////////

							//////////////////////// dense base verts matching ////////////////////////
							if ((int) regionBaseVertsS.size() >= nMaxS) //rb makes ~40 bases on regionVertsB for sure; same rb is expected to make ~40 on regionVertsS if two regions are compatible, i.e. generating triplets are good matches; regionBaseVertsS.size() >= 200 (nMaxS) means there's a huge difference b/w regions so don't waste time to produce confidence values for generating (bad) matches; blossom on 300x300 takes ~1 sec so lots of 1 secs make me loose big time; simply use 0 confidences for a huge difference case (in else part)
							{
								cout << "this much difference shouldn't happen b/w regions which are supposed to be very compatible\n";
								//exit(0);
							}
							//create dense map b/w regionBaseVertsB & regionBaseVertsS and set the matchIdx of unmatched regional bases (matched means that base is overlapped by a previous best potential triplet pair; keep that one 'cos that pair was a better best than this current best, i.e. less minGeoConsistency for prev)

							for (int y = 1; y < 6; y += 2)
							{
								if (! sMesh->verts[ desiredMap[y] ]->dSpanningUnnSS)
									sMesh->verts[ desiredMap[y] ]->dSpanningUnnSS = new float[ (int) sMesh->verts.size() ];
								for (int v = 0; v < (int) sMesh->verts.size(); v++)
									sMesh->verts[ desiredMap[y] ]->dSpanningUnnSS[v] = sMesh->verts[ desiredMap[y] ]->dSpanningUnn[v] * scaler;
							}
							maxExtremeDistS *= scaler;
							int n1 = (int) regionBaseVertsB.size(), n2 = (int) regionBaseVertsS.size();
							int nRows = (n1 > n2 ? n1 : n2), nCols = nRows;
							int* vIdx1 = new int[n1], * vIdx2 = new int[n2];
							float** costs = new float*[nRows];
							float discourage = INF;
							int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e, * edges = new int[2*nEdgeNum], * matches = new int[nVertNum];
							double* weightsD = new double[nEdgeNum];
							int row = 0;
							for (int i = 0; i < n1; i++)
							{
								costs[row] = new float[nCols];
								vIdx1[row] = regionBaseVertsB[i];
								int col = 0;
								for (int j = 0; j < n2; j++)
									vIdx2[col++] = regionBaseVertsS[j];
								while (col < nCols)
									costs[row][col++] = discourage;
								row++;
							}
							while (row < nRows)
							{
								costs[row] = new float[nCols];
								for (int col = 0; col < nCols; col++)
									costs[row][col] = discourage;
								row++;
							}
							row = 0;
							for (int i = 0; i < n1; i++)
							{
								int base1 = regionBaseVertsB[i];
								verts[base1]->matchIdxs.clear();
								int col = 0;
								for (int j = 0; j < n2; j++)
								{
									int base2 = regionBaseVertsS[j];
									bool extremeMatch = false;
									for (int y = 0; y < 6; y += 2)
										if (desiredMap[y] == base1 && desiredMap[y+1] == base2)
											extremeMatch = true;
									float vertexBasedIso = 0.0f;
									int nAdds = 0;
									for (int ss = 0; ss < 3; ss++)
									{
										int s1 = desiredMap[2*ss], s2 = desiredMap[2*ss + 1];
										if (s1 == base1)
											continue;
										float t1s1 = verts[s1]->dSpanningUnn[base1] / maxExtremeDistB, t2s2 = sMesh->verts[s2]->dSpanningUnnSS[base2] / maxExtremeDistS;
										vertexBasedIso += fabs(t1s1 - t2s2);
										nAdds++;
									}
if (nAdds <= 0) {cout << "can't traverse 0-size\n";exit(0);}
									vertexBasedIso /= nAdds;
									costs[row][col++] = (! extremeMatch ? vertexBasedIso : -INF);
									verts[base1]->matchIdxs.push_back((float) base2);
									verts[base1]->matchIdxs.push_back(vertexBasedIso);
								}
								row++;
							}
							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* blossom = new Blossom();
							blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches);
							delete blossom;
							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 [not now 'cos i'll blend all these matches later to decide the final matchIdx values]
										//denseMap.push_back((bMeshIsThis ? vIdx1[m1] : mi)); //base1 from bMesh; be sure even idxs ~ mesh1, odds ~ mesh2
										//denseMap.push_back((bMeshIsThis ? mi : vIdx1[m1])); //base2 from sMesh
										//verts[ vIdx1[m1] ]->matchIdx = mi; //vIdx1[m1] ~ bMesh, mi ~ sMesh for sure (bool bMeshIsThis is a naming good for caller partialMatching3() fnctn, not for here)
										
										//match and its cost to be blended later
										//learn the individual distortion used in blossom mtrx to use it in matchIdxs2
										float vertexBasedIso = -1.0f;
										for (int j = 0; j < (int) verts[ vIdx1[m1] ]->matchIdxs.size(); j += 2)
											if (verts[ vIdx1[m1] ]->matchIdxs[j] == mi)
											{
												vertexBasedIso = verts[ vIdx1[m1] ]->matchIdxs[j+1];
												verts[ vIdx1[m1] ]->matchIdxs2.push_back((float) mi); //match of this bMesh region base is stored
												verts[ vIdx1[m1] ]->matchIdxs2.push_back(vertexBasedIso); //individual distortion of this bMesh region base is stored
												break;
											}
if (vertexBasedIso == -1.0f){cout<<"\n\n\n\n\nsth wrong\n";exit(0);}
									}
								}
							} //end of m
							//////////////// desiredMap provides the regions on which dense maps are done ends ////////////////
						} //end of f
			} //end of c
	cout << "C(" << nCoarseMap << ", 3) = " << nDesiredMaps << " pairs of triplets defined regions for the blended dense map\n";

/*	for (int v = 0; v < (int) verts.size(); v++) //just for info
	{
		if (verts[v]->matchIdxs2.empty())// || (v != 10672 && v != 4283 && v != 2217))
			continue;
		verts[v]->avgBlossomCost = 0.0f;
		verts[v]->minBlossomCost = INF;
		verts[v]->maxBlossomCost = -INF;
		for (int i = 0; i < (int) verts[v]->matchIdxs2.size(); i += 2)
		{
//cout << verts[v]->matchIdxs2[i] << " - " << v << ":::::::: " << verts[v]->matchIdxs2[i+1] << endl;
			verts[v]->avgBlossomCost += verts[v]->matchIdxs2[i+1];
			if (verts[v]->matchIdxs2[i+1] < verts[v]->minBlossomCost)
				verts[v]->minBlossomCost = verts[v]->matchIdxs2[i+1];
			if (verts[v]->matchIdxs2[i+1] > verts[v]->maxBlossomCost)
				verts[v]->maxBlossomCost = verts[v]->matchIdxs2[i+1];
		}
		verts[v]->avgBlossomCost /= ((int) verts[v]->matchIdxs2.size() / 2);
		cout << v << "/" << ((int) verts[v]->matchIdxs2.size() / 2) << "/" << verts[v]->avgBlossomCost << "/" << verts[v]->minBlossomCost << "/" << verts[v]->maxBlossomCost << "\n\n";
	}//*/

	//all bMesh.regionBaseVerts.matchIdxs2 are ready; so, for each bMesh regional base vert, i know its 1+ matches along w/ the match costs; blend these results by setting ultimate (brbv, c(brbv)) matches where c() is the correspondence of bMesh regional base vert brbv, and
	//c(brbv) is the closest sMesh vertex to blended sMesh coord bsc = sum_i (1-distortion(brbv's i'th match)) * coords(brbv's i'th match)
	for (int v = 0; v < (int) sMesh->verts.size(); v++) //roll-back to original coords 'cos sMesh coords scaled above
		for (int c = 0; c < 3; c++)
			sMesh->verts[v]->coords[c] = sMesh->verts[v]->coords2[c];
	float* bsc = new float[3]; //avgCostDense = 0.0f;minCostDense = INF;maxCostDense = -INF; can't find avg, min, max costs used in blossom costs[][] as the isometric distortion of each individual match 'cos blended sMesh vert not necessarily have a blossom cost; only the euclidean dists known
	for (int v = 0; v < (int) verts.size(); v++)
		if (verts[v]->rbase && ! verts[v]->matchIdxs2.empty() && verts[v]->matchIdx == -1) //do the blended matching only for the regional base verts that has 1+ candid matches to be blended and that are not extreme bases that already has matches (matchIdx != -1) from coarse mapping (bMesh is this so no bMesh-> stuff)
		{
			//set the blended sMesh coord bsc
			bsc[0] = bsc[1] = bsc[2] = 0.0f;
			for (int i = 0; i < (int) verts[v]->matchIdxs2.size(); i += 2)
				for (int c = 0; c < 3; c++)
					bsc[c] += //((1.0f - verts[v]->matchIdxs2[i+1]) * sMesh->verts[ (int) verts[v]->matchIdxs2[i] ]->coords[c]);
							  sMesh->verts[ (int) verts[v]->matchIdxs2[i] ]->coords[c]; //no weighting w/ blossom distortion 'cos it may ruin good blends that would be achieved w/ this simple non-weighted blending
			//normalize bsc by the number of += done just-above
			for (int c = 0; c < 3; c++)
				bsc[c] /= ((int) verts[v]->matchIdxs2.size() / 2);

			//find the closest sMesh vertex csv to bsc as the ultimate match of v
			float minDist, dist;
			int csv = -1;
			for (int w = 0; w < (int) sMesh->verts.size(); w++)
//				if (sMesh->verts[w]->rbase) //force csv to be a sMesh regional base vertex for better results [just cancel this line to use all sMesh vertices as closest candidates]
				{
					dist = distanceBetween2(bsc, sMesh->verts[w]->coords);
					if (w == 0 || dist < minDist)
					{
						minDist = dist;
						csv = w;
					}
				}

			//ultimate match (quite probably 1-to-1 but may be many-to-1 if blended coords coincide at the same sMesh vertex)
			if (bMeshIsThis) //bMesh was this/mesh1 in the caller partialMatching3(); so, setting denseMap sets mesh1.denseMap below 'cos this fnctn called over bMesh (= this)
			{
				denseMap.push_back(v); //even idxs ~ mesh1, odds ~ mesh2
				denseMap.push_back(csv);
				verts[v]->matchIdx = csv; //v ~ bMesh, csv ~ sMesh for sure (bool bMeshIsThis is a naming good for caller partialMatching3() fnctn, not for here)
			}
			else //sMesh was this/mesh1 in the caller partialMatching3(); so, setting sMesh.denseMap sets mesh1.denseMap below 'cos this fnctn called over bMesh and mesh1 passed as argument sMesh
			{
				sMesh->denseMap.push_back(csv); //csv is from sMesh and sMesh is mesh1; so, even idxs ~ mesh1, odds ~ mesh2
				sMesh->denseMap.push_back(v);
				sMesh->verts[csv]->matchIdx = v;
			}
		}
//	cout << "avg, min, max blossom costs[][] as isometric distortions: " << avgCostDense << ", " << minCostDense << " (" << miv1 << ", " << miv2 << "), " << maxCostDense << " (" << mav1 << ", " << mav2 << ")\n"; can't do that 'cos blended sMesh vertex not necessarily has a blossom cost; these are just euclidean distances to the closest sMesh vert
	delete [] bsc;
	cout << "done!\n\n";
}

void Mesh::matchBasesViaAll(Mesh* bMesh, Mesh* sMesh)
{
	//d_iso(i, j)'s made ready by putting i-j to all mappings of sizes 2 and/or 3 and/or 4 and/or 5, and evaluating that resulting mapping; distortion of min-distortion resulting mapping becomes d_iso(i,j), the individual distortion, for match i-j (i-j will then be put in final/output match if it's distortion is good enough)

	cout << "distortions for each pair of matches being computed....\n";
	
	int m = (int) bMesh->baseVerts.size(),
		n = (int) sMesh->baseVerts.size(), //so m >= n for sure
		distortionComputation = 1; //how to decide the individual i-j distortion: 1: sum all nDisto* (best), 2: min(nDisto*), 3: sum all nDisto* but the worst one assuming worst cannot be relied on

	//assign a distortion d_iso to all i-j pairs b/w base vertices:
	//d_iso(i, j) = 2! * C(m-1, 2) * C(n-1, 2) //all triplets involving i-j match, i.e. i-j is fixed and i combine it w/ all other pairs
	//			  + 3! * C(m-1, 3) * C(n-1, 3) //all 4-tuples involving i-j match, i.e. i-j is fixed and i combine it w/ all other triplets
	//			  + 4! * C(m-1, 4) * C(n-1, 4)
	//			  + 5! * C(m-1, 5) * C(n-1, 5) //each term added is actually the minimum distortion value among all x! * C(m-1, x) * C(n-1, x) mappings evaluated below tested
	//normally m and n are ~10 but stop at 5! (disto6) just-above due to efficiency and also accuracy (a bad i-j may give less distortion if it is tested w/ 6+ good mappings)
	fillPermutationMtrxs(); //required for the first call, and redundant for upcoming calls (returns immediately so no worries)
	for (int i = 0; i < m; i++)
		bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.clear(); //redundant for the first call, and required for upcoming calls
//FILE* fPtr = fopen("dbg.dat", "w");
	for (int i = 0; i < m; i++)
		for (int j = 0; j < n; j++)
		{
			//an i-j match
			bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.push_back((float) sMesh->baseVerts[j]);
			//and its scale-invariant distortion
			float distortion = 0.0f, //= disto3 + disto4 + disto5 (compatible versions)
				  disto3 = INF, disto4 = INF, disto5 = INF, disto6 = INF, //store distortion of different-size mappings separately 'cos they need to be normalized to the compatible scale (via maxDisto*) before being added for ultimate distortion value
				  maxDisto3 = -INF, maxDisto4 = -INF, maxDisto5 = -INF, maxDisto6 = -INF, nDisto3, nDisto4, nDisto5, nDisto6 = -1.0f;
//fprintf(fPtr, "%d-%d:\t\t", bMesh->baseVerts[i], sMesh->baseVerts[j]);
			////////////////////////////////////////// disto3: distortion of map size 3 //////////////////////////////////////////
//cout << "disto3\n";		
			for (int a = 0; a < m; a++)
				for (int b = a+1; b < m; b++)
				{
					//current 2-combination + 1 fixed i of i-j match is copied from appropriate slots of bMesh.baseVerts to tmpS (for tmp source)
					if (a == i || b == i)
						continue; //no duplicates in tmpS below
					vector< int > tmpS; //s for source, not small
					tmpS.push_back(bMesh->baseVerts[a]);
					tmpS.push_back(bMesh->baseVerts[b]);
					//tmpS.push_back(bMesh->baseVerts[i]); not add here 'cos 2 above will make permutations and this'll be appended to tmpSafeCorrespTry

					//this fixed combination in tmpS will be tried w/ all target tuple permutations, tmpT
					for (int d = 0; d < n; d++)
						for (int e = d+1; e < n; e++)
						{
							//current 2-combination + 1 j of i-j match is copied from appropriate slots of sMesh.baseVerts to tmpT (for tmp target)
							if (d == j || e == j)
								continue; //no duplicates in tmpT below

							vector< int > tmpT;
							tmpT.push_back(sMesh->baseVerts[d]);
							tmpT.push_back(sMesh->baseVerts[e]);
							//tmpT.push_back(sMesh->baseVerts[j]); not add here 'cos 2 above will make permutations and this'll be appended to tmpSafeCorrespTry

							//try 2!=2 reordering/permutations of tmpT w/ stable tmpS of size 2
							for (int r = 0; r < 2; r++)
							{
								//refill traversalListTry according to the current reordering (one of r! permutations)
								//get the overall isometryCost of this current quadruple combination matched w/ stable quadruple of fromMesh
								vector< int > tmpSafeCorrespTry;
								int idx1 = 0, idx2 = 0;
								//stable pair of tmpS goes to odd indices, and permuted tmpT to even slots 'cos getCorrespListScaleInvDistortion() expects tmpSafeCorrespTry's odd idxs be from bMesh
								for (int y = 0; y < 4; y++) //traversalListTry of size 2, hence 2*2=4
									if (y % 2 == 1)
										tmpSafeCorrespTry.push_back(tmpS[idx1++]); //stable from source side (tmpS ~ bMesh)
									else			//even idxs ~ sMesh
										tmpSafeCorrespTry.push_back(tmpT[ perms2[r][idx2++] ]);	//perms2[0] = {1,0}, perms2[1] = {0,1} (tmpT ~ sMesh)

								//previous worst matches cannot live in tmpSafeCorrespTry 'cos they will cause an inaccurate distortion for tmpSafeCorrespTry and may therefore confuse disto3 and/or maxDisto3 which is used for desired distortion to be pushed in matchIdxs below
								bool prevWorstFound = false;
								for (int w = 0; w < (int) worsts.size(); w += 2)
									for (int t = 0; t < (int) tmpSafeCorrespTry.size(); t += 2)
										if (worsts[w] == tmpSafeCorrespTry[t] && worsts[w+1] == tmpSafeCorrespTry[t+1]) //evenIdxs of both worsts & tmpSafeCorrespTry ~ sMesh, odd ~ bMesh
										{
											prevWorstFound = true;
											w = (int) worsts.size(); //break outer loop as well
											break;
										}
								if (prevWorstFound)
									continue; //no evaluation of this potentially bad/confusing tmpSafeCorrespTry that involves a bad match

								tmpSafeCorrespTry.push_back(sMesh->baseVerts[j]); //sMesh appended (to even idx)
								tmpSafeCorrespTry.push_back(bMesh->baseVerts[i]); //bMesh appended (to odd idx)

								//compute isometry cost of this 3-3 mapping and ..
								float isometryCostTry = sMesh->getCorrespListScaleInvDistortion(bMesh, tmpSafeCorrespTry); //no!!!!!! not whole mapping; just i-j distortion using this mapping as traversalList
														//sMesh->getIndividualScaleInvDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry);
								//..save it if it's the best so far
								if (isometryCostTry < disto3)
									disto3 = isometryCostTry;
								if (isometryCostTry > maxDisto3)
									maxDisto3 = isometryCostTry; //to be used in normalization of disto3
							} //end of r
						} //end of e
				} //end of b
			nDisto3 = (disto3 / maxDisto3); //normalized disto3
//fprintf(fPtr, "%f/%f: %f\t", disto3, maxDisto3, nDisto3);
			distortion += nDisto3; //divide so that max value to the right of += is 1
			////////////////////////////////////////// disto3: distortion of map size 3 ends //////////////////////////////////////////*/

			////////////////////////////////////////// disto4: distortion of map size 4 ends //////////////////////////////////////////
//cout << "disto4\n";
			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 + 1 fixed i of i-j match is copied from appropriate slots of bMesh.baseVerts to tmpS (for tmp source)
						if (a == i || b == i || c == i)
							continue; //no duplicates in tmpS below

						vector< int > tmpS;
						tmpS.push_back(bMesh->baseVerts[a]);
						tmpS.push_back(bMesh->baseVerts[b]);
						tmpS.push_back(bMesh->baseVerts[c]);
						//tmpS.push_back(bMesh->baseVerts[i]); not add here 'cos 3 above will make permutations and this'll be appended to tmpSafeCorrespTry

						//this fixed combination in tmpS will be tried w/ all target tuple permutations, tmpT
						for (int d = 0; d < n; d++)
							for (int e = d+1; e < n; e++)
								for (int f = e+1; f < n; f++)
								{
									//current 3-combination + 1 j of i-j match is copied from appropriate slots of sMesh.baseVerts to tmpT (for tmp target)
									if (d == j || e == j || f == j)
										continue; //no duplicates in tmpT below
									vector< int > tmpT;
									tmpT.push_back(sMesh->baseVerts[d]);
									tmpT.push_back(sMesh->baseVerts[e]);
									tmpT.push_back(sMesh->baseVerts[f]);
									//tmpT.push_back(sMesh->baseVerts[j]); not add here 'cos 3 above will make permutations and this'll be appended to tmpSafeCorrespTry

									//try 3!=6 reordering/permutations of tmpT w/ stable tmpS of size 3
									for (int r = 0; r < 6; r++)
									{
										vector< int > tmpSafeCorrespTry;
										int idx1 = 0, idx2 = 0;
										//stable pair of tmpS goes to odd indices, and permuted tmpT to even slots 'cos getCorrespListScaleInvDistortion() expects tmpSafeCorrespTry's odd idxs be from bMesh
										for (int y = 0; y < 6; y++) //traversalListTry of size 3, hence 2*3=6
											if (y % 2 == 1)
												tmpSafeCorrespTry.push_back(tmpS[idx1++]); //stable from source side
											else
												tmpSafeCorrespTry.push_back(tmpT[ perms3[r][idx2++] ]);

										//previous worst matches cannot live in tmpSafeCorrespTry 'cos they will cause an inaccurate distortion for tmpSafeCorrespTry and may therefore confuse disto3 and/or maxDisto3 which is used for desired distortion to be pushed in matchIdxs below
										bool prevWorstFound = false;
										for (int w = 0; w < (int) worsts.size(); w += 2)
											for (int t = 0; t < (int) tmpSafeCorrespTry.size(); t += 2)
												if (worsts[w] == tmpSafeCorrespTry[t] && worsts[w+1] == tmpSafeCorrespTry[t+1]) //evenIdxs of both worsts & tmpSafeCorrespTry ~ sMesh, odd ~ bMesh
												{
													prevWorstFound = true;
													w = (int) worsts.size(); //break outer loop as well
													break;
												}
										if (prevWorstFound)
											continue; //no evaluation of this potentially bad/confusing tmpSafeCorrespTry that involves a bad match

										tmpSafeCorrespTry.push_back(sMesh->baseVerts[j]); //sMesh appended (to even idx)
										tmpSafeCorrespTry.push_back(bMesh->baseVerts[i]); //bMesh appended (to odd idx)

										//compute isometry cost of this 4-4 mapping and ..
										float isometryCostTry = sMesh->getCorrespListScaleInvDistortion(bMesh, tmpSafeCorrespTry); //no!!!!!! not whole mapping; just i-j distortion using this mapping as traversalList
																//sMesh->getIndividualScaleInvDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry);
										//..save it if it's the best so far
										if (isometryCostTry < disto4)
											disto4 = isometryCostTry;
										if (isometryCostTry > maxDisto4)
											maxDisto4 = isometryCostTry; //to be used in normalization of disto4
									} //end of r
								} //end of f
					} //end of c
			nDisto4 = (disto4 / maxDisto4);
//fprintf(fPtr, "%f/%f: %f\t", disto4, maxDisto4, nDisto4);
			distortion += nDisto4; //divide so that max value to the right of += is 1
			////////////////////////////////////////// disto4: distortion of map size 4 ends //////////////////////////////////////////

			////////////////////////////////////////// disto5: distortion of map size 5 ends //////////////////////////////////////////
//cout << "disto5\n";
			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 z = c+1; z < m; z++)
						{
							//current 4-combination + 1 fixed i of i-j match is copied from appropriate slots of bMesh.baseVerts to tmpS (for tmp source)
							if (a == i || b == i || c == i || z == i)
								continue; //no duplicates in tmpS below

							vector< int > tmpS;
							tmpS.push_back(bMesh->baseVerts[a]);
							tmpS.push_back(bMesh->baseVerts[b]);
							tmpS.push_back(bMesh->baseVerts[c]);
							tmpS.push_back(bMesh->baseVerts[z]);
							//tmpS.push_back(bMesh->baseVerts[i]); not add here 'cos 4 above will make permutations and this'll be appended to tmpSafeCorrespTry

							//this fixed combination in tmpS will be tried w/ all target tuple permutations, tmpT
							for (int d = 0; d < n; d++)
								for (int e = d+1; e < n; e++)
									for (int f = e+1; f < n; f++)
										for (int g = f+1; g < n; g++)
										{
											//current 4-combination + 1 j of i-j match is copied from appropriate slots of sMesh.baseVerts to tmpT (for tmp target)
											if (d == j || e == j || f == j || g == j)
												continue; //no duplicates in tmpT below
											vector< int > tmpT;
											tmpT.push_back(sMesh->baseVerts[d]);
											tmpT.push_back(sMesh->baseVerts[e]);
											tmpT.push_back(sMesh->baseVerts[f]);
											tmpT.push_back(sMesh->baseVerts[g]);
											//tmpT.push_back(sMesh->baseVerts[j]); not add here 'cos 4 above will make permutations and this'll be appended to tmpSafeCorrespTry

											//try 4!=24 reordering/permutations of tmpT w/ stable tmpS of size 4
											for (int r = 0; r < 24; r++)
											{
												vector< int > tmpSafeCorrespTry;
												int idx1 = 0, idx2 = 0;
												//stable pair of tmpS goes to odd indices, and permuted tmpT to even slots 'cos getCorrespListScaleInvDistortion() expects tmpSafeCorrespTry's odd idxs be from bMesh
												for (int y = 0; y < 8; y++) //traversalListTry of size 5, hence 2*4=8
													if (y % 2 == 1)
														tmpSafeCorrespTry.push_back(tmpS[idx1++]); //stable from source side
													else
														tmpSafeCorrespTry.push_back(tmpT[ perms4[r][idx2++] ]);

												//previous worst matches cannot live in tmpSafeCorrespTry 'cos they will cause an inaccurate distortion for tmpSafeCorrespTry and may therefore confuse disto3 and/or maxDisto3 which is used for desired distortion to be pushed in matchIdxs below
												bool prevWorstFound = false;
												for (int w = 0; w < (int) worsts.size(); w += 2)
													for (int t = 0; t < (int) tmpSafeCorrespTry.size(); t += 2)
														if (worsts[w] == tmpSafeCorrespTry[t] && worsts[w+1] == tmpSafeCorrespTry[t+1]) //evenIdxs of both worsts & tmpSafeCorrespTry ~ sMesh, odd ~ bMesh
														{
															prevWorstFound = true;
															w = (int) worsts.size(); //break outer loop as well
															break;
														}
												if (prevWorstFound)
													continue; //no evaluation of this potentially bad/confusing tmpSafeCorrespTry that involves a bad match

												tmpSafeCorrespTry.push_back(sMesh->baseVerts[j]); //sMesh appended (to even idx)
												tmpSafeCorrespTry.push_back(bMesh->baseVerts[i]); //bMesh appended (to odd idx)												

												//compute isometry cost of this 5-5 mapping and ..
												float isometryCostTry = sMesh->getCorrespListScaleInvDistortion(bMesh, tmpSafeCorrespTry); //no!!!!!! not whole mapping; just i-j distortion using this mapping as traversalList
																		//sMesh->getIndividualScaleInvDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry);
												//..save it if it's the best so far
												if (isometryCostTry < disto5)
													disto5 = isometryCostTry;
												if (isometryCostTry > maxDisto5)
													maxDisto5 = isometryCostTry; //to be used in normalization of disto5
											} //end of r
										} //end of g
						} //end of z
			nDisto5 = (disto5 / maxDisto5);
//fprintf(fPtr, "%f/%f: %f\t", disto5, maxDisto5, nDisto5);
			distortion += nDisto5; //divide so that max value to the right of += is 1
			////////////////////////////////////////// disto5: distortion of map size 5 ends //////////////////////////////////////////*/

/*			////////////////////////////////////////// disto6: distortion of map size 6 ends //////////////////////////////////////////
//cout << "disto6\n";
			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 z = c+1; z < m; z++)
							for (int y = z+1; y < m; y++)
							{
								//current 5-combination + 1 fixed i of i-j match is copied from appropriate slots of bMesh.baseVerts to tmpS (for tmp source)
								if (a == i || b == i || c == i || z == i || y == i)
									continue; //no duplicates in tmpS below

								vector< int > tmpS;
								tmpS.push_back(bMesh->baseVerts[a]);
								tmpS.push_back(bMesh->baseVerts[b]);
								tmpS.push_back(bMesh->baseVerts[c]);
								tmpS.push_back(bMesh->baseVerts[z]);
								tmpS.push_back(bMesh->baseVerts[y]);
								//tmpS.push_back(bMesh->baseVerts[i]); not add here 'cos 5 above will make permutations and this'll be appended to tmpSafeCorrespTry

								//this fixed combination in tmpS will be tried w/ all target tuple permutations, tmpT
								for (int d = 0; d < n; d++)
									for (int e = d+1; e < n; e++)
										for (int f = e+1; f < n; f++)
											for (int g = f+1; g < n; g++)
												for (int h = g+1; h < n; h++)
												{
													//current 5-combination + 1 j of i-j match is copied from appropriate slots of sMesh.baseVerts to tmpT (for tmp target)
													if (d == j || e == j || f == j || g == j || h == j)
														continue; //no duplicates in tmpT below
													vector< int > tmpT;
													tmpT.push_back(sMesh->baseVerts[d]);
													tmpT.push_back(sMesh->baseVerts[e]);
													tmpT.push_back(sMesh->baseVerts[f]);
													tmpT.push_back(sMesh->baseVerts[g]);
													tmpT.push_back(sMesh->baseVerts[h]);
													//tmpT.push_back(sMesh->baseVerts[j]); not add here 'cos 5 above will make permutations and this'll be appended to tmpSafeCorrespTry

													//try 5!=120 reordering/permutations of tmpT w/ stable tmpS of size 5
													for (int r = 0; r < 120; r++)
													{
														vector< int > tmpSafeCorrespTry;
														int idx1 = 0, idx2 = 0;
														//stable pair of tmpS goes to odd indices, and permuted tmpT to even slots 'cos getCorrespListScaleInvDistortion() expects tmpSafeCorrespTry's odd idxs be from bMesh
														for (int y = 0; y < 10; y++) //traversalListTry of size 5, hence 2*5=10
															if (y % 2 == 1)
																tmpSafeCorrespTry.push_back(tmpS[idx1++]); //stable from source side
															else
																tmpSafeCorrespTry.push_back(tmpT[ perms5[r][idx2++] ]);

														//previous worst matches cannot live in tmpSafeCorrespTry 'cos they will cause an inaccurate distortion for tmpSafeCorrespTry and may therefore confuse disto3 and/or maxDisto3 which is used for desired distortion to be pushed in matchIdxs below
														bool prevWorstFound = false;
														for (int w = 0; w < (int) worsts.size(); w += 2)
															for (int t = 0; t < (int) tmpSafeCorrespTry.size(); t += 2)
																if (worsts[w] == tmpSafeCorrespTry[t] && worsts[w+1] == tmpSafeCorrespTry[t+1]) //evenIdxs of both worsts & tmpSafeCorrespTry ~ sMesh, odd ~ bMesh
																{
																	prevWorstFound = true;
																	w = (int) worsts.size(); //break outer loop as well
																	break;
																}
														if (prevWorstFound)
															continue; //no evaluation of this potentially bad/confusing tmpSafeCorrespTry that involves a bad match

														tmpSafeCorrespTry.push_back(sMesh->baseVerts[j]); //sMesh appended (to even idx)
														tmpSafeCorrespTry.push_back(bMesh->baseVerts[i]); //bMesh appended (to odd idx)														

														//compute isometry cost of this 6-6 mapping and ..
														float isometryCostTry = sMesh->getCorrespListScaleInvDistortion(bMesh, tmpSafeCorrespTry); //no!!!!!! not whole mapping; just i-j distortion using this mapping as traversalList
																				//sMesh->getIndividualScaleInvDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry);
														//..save it if it's the best so far
														if (isometryCostTry < disto6)
															disto6 = isometryCostTry;
														if (isometryCostTry > maxDisto6)
															maxDisto6 = isometryCostTry; //to be used in normalization of disto6
													} //end of r
												} //end of h
							} //end of y
			nDisto6 = (disto6 / maxDisto6);
//fprintf(fPtr, "%f/%f: %f\t", disto6, maxDisto6, nDisto6);
			distortion += nDisto6; //divide so that max value to the right of += is 1
			////////////////////////////////////////// disto6: distortion of map size 6 ends //////////////////////////////////////////*/

			//distortionComputation == 1 already taken care of above
			if (distortionComputation == 2) //use min(nDisto*)
			{
				//instead of summing nDisto*, take the minimum of them as the ultimate individual i-j distortion (just disable two-lines below to use summed nDisto*, which is better)
				distortion = (nDisto3 < nDisto4 ? nDisto3 : nDisto4); //min of nDisto3 and 4
				distortion = (distortion < nDisto5 ? distortion : nDisto5); //min of nDisto3 and 4 and 5
				distortion = (nDisto6 == -1.0f || distortion < nDisto6 ? distortion : nDisto6); //min of nDisto3 and 4 and 5 and 6 (if nDisto6 available, i.e. not -1.0f)
			}
			else if (distortionComputation == 3) //use sum of all nDisto* but the worst
			{
				//worst nDisto
				distortion = (nDisto3 > nDisto4 ? nDisto3 : nDisto4); //max of nDisto3 and 4
				distortion = (distortion > nDisto5 ? distortion : nDisto5); //max of nDisto3 and 4 and 5
				distortion = (nDisto6 == -1.0f || distortion > nDisto6 ? distortion : nDisto6); //max of nDisto3 and 4 and 5 and 6 (if nDisto6 available, i.e. not -1.0f)
				distortion = nDisto3 + nDisto4 + nDisto5 + (nDisto6 == -1.0f ? 0.0f : nDisto6) - distortion; //consider nDisto6 if it's available
			}

			//previously worst matches are discouraged to be selected in blossom; so, make their distortion a big value regardless of what computed above
			for (int w = 0; w < (int) worsts.size(); w += 2)
				if (worsts[w] == sMesh->baseVerts[j] && worsts[w+1] == bMesh->baseVerts[i]) //evenIdxs of both worsts sMesh, odd ~ bMesh
				{					
					distortion = INF; //discouarge
					cout << "distortion of prev worst: " << bMesh->baseVerts[i] << "--" << sMesh->baseVerts[j] << ": " << distortion << " (order arbitrary, no worries)\n";
					break;
				}
//fprintf(fPtr, "%f%s", distortion, (j == n-1 ? "\n\n" : "\n"));
			//this candidate i-j match comes w/ the following distortion (to be used below to select the best)
			bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.push_back(distortion);
//printf("%4.2f %4.2f %4.2f\t\t", maxDisto3, maxDisto4, maxDisto5); //.2f for 2 digits after dot
cout << i+1 << " / " << m << "\t" << (sMesh == this ? sMesh->baseVerts[j] : bMesh->baseVerts[i]) << " - " << (sMesh == this ? bMesh->baseVerts[i] : sMesh->baseVerts[j]) << "\t\t distortion = " << distortion << (j == n-1 ? "\n\n" : "\n");
		} //end of j
//fclose(fPtr);
	cout << "done!\n";
}
void Mesh::matchBasesViaAllEff(Mesh* bMesh, Mesh* sMesh)
{
	//similar to matchBasesViaAll() except this one inserts i-j into pre-computed potential* mappings to save time and accuracy

	cout << "distortions for each pair of matches being computed....\n";
	
	int m = (int) bMesh->baseVerts.size(),
		n = (int) sMesh->baseVerts.size(), //so m >= n for sure
		distortionComputation = 1; //how to decide the individual i-j distortion: 1: sum all nDisto* (best), 2: min(nDisto*), 3: sum all nDisto* but the worst one assuming worst cannot be relied on

	//assign a distortion d_iso to all i-j pairs b/w base vertices:
	//d_iso(i, j) = 2! * C(m-1, 2) * C(n-1, 2) //all triplets involving i-j match, i.e. i-j is fixed and i combine it w/ all other pairs
	//			  + 3! * potentialTriplets //all 4-tuples involving i-j match, i.e. i-j is fixed and i combine it w/ all other triplets
	//			  + 4! * potentialQuadruples
	//			  + 5! * potentialQuintuples //each term added is actually the minimum distortion value among all x! * C(m-1, x) * C(n-1, x) mappings evaluated below tested
	fillPermutationMtrxs(); //required for the first call, and redundant for upcoming calls (returns immediately so no worries)
	for (int i = 0; i < m; i++)
		bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.clear(); //redundant for the first call, and required for upcoming calls
	bool distoByNormalizedGeodesics = true, //true: normalize by local max geodesic distance and use the second-order distortion (cvpr'10, pami, sgp distortion), false: apply scale-inveariant distortion directly on unnormalized geodesics
		 permuteCompatiblesOnly = true; //false: don't do the permutations b/w current pnt sets in vain if they're not compatible (no geodesic consistency), i.e. not from the same part
	float* dists6 = new float[6], * dists66 = new float[6], * dists10 = new float[10], * dists1010 = new float[10], * dists15 = new float[15], * dists1515 = new float[15];  //to be used if permuteCompatiblesOnly = true
	//getIndividual*() below is a better idea than using the distortion of whole mapping as d_iso(i,j) 'cos a good i-j fed into a ~bad mapping that involves t-k has a high distortion although its individual distortion is better relative to other matches; if whole map used, then d_iso(t,k)'ll have the exactly same value as d_iso(i,j) which is wrong
//FILE* fPtr = fopen("dbg.dat", "w");
//FILE* fPtr2 = fopen("dbg2.dat", "w");
	for (int i = 0; i < m; i++)
	{
//float mminD = INF, mmaxD = -INF, ttotalD = 0;
		for (int j = 0; j < n; j++)
		{
			//an i-j match
			bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.push_back((float) sMesh->baseVerts[j]);
			//and its scale-invariant distortion
			float distortion = 0.0f, //= disto3 + disto4 + disto5 (compatible versions)
				  disto3 = INF, disto4 = INF, disto5 = INF, disto6 = INF, //store distortion of different-size mappings separately 'cos they need to be normalized to the compatible scale (via maxDisto*) before being added for ultimate distortion value
				  maxDisto3 = -INF, maxDisto4 = -INF, maxDisto5 = -INF, maxDisto6 = -INF, nDisto3 = -1.0f, nDisto4 = -1.0f, nDisto5 = -1.0f, nDisto6 = -1.0f;
//fprintf(fPtr, "%d-%d:\t\t", bMesh->baseVerts[i], sMesh->baseVerts[j]);
			////////////////////////////////////////// disto3: distortion of map size 3 //////////////////////////////////////////
//cout << "disto3\n";
			//potentialPairs cannot be computed initially; so, check all pairs here explicitly
			for (int a = 0; a < m; a++)
				for (int b = a+1; b < m; b++)
				{
					//current 2-combination + 1 fixed i of i-j match is copied from appropriate slots of bMesh.baseVerts to tmpS (for tmp source)
					if (a == i || b == i)
						continue; //no duplicates in tmpS below

					vector< int > tmpS; //s for source, not small
					tmpS.push_back(bMesh->baseVerts[a]);
					tmpS.push_back(bMesh->baseVerts[b]);
					//tmpS.push_back(bMesh->baseVerts[i]); not add here 'cos 2 above will make permutations and this'll be appended to tmpSafeCorrespTry

					//this fixed combination in tmpS will be tried w/ all target tuple permutations, tmpT
					for (int d = 0; d < n; d++)
						for (int e = d+1; e < n; e++)
						{
							//current 2-combination + 1 j of i-j match is copied from appropriate slots of sMesh.baseVerts to tmpT (for tmp target)
							if (d == j || e == j)
								continue; //no duplicates in tmpT below

							vector< int > tmpT;
							tmpT.push_back(sMesh->baseVerts[d]);
							tmpT.push_back(sMesh->baseVerts[e]);
							//tmpT.push_back(sMesh->baseVerts[j]); not add here 'cos 2 above will make permutations and this'll be appended to tmpSafeCorrespTry

							//compute isometry cost of this 4-4 mapping and ..
							float maxDistB = -INF, maxDistS = -INF;				
							if (permuteCompatiblesOnly) //don't do the permutations below in vain if these 4 pnts not compatible, i.e. not from the same part
							{
								//heavily adapted from computePotentialTriplets()
								if ((int) tmpS.size() == 2) //add [i] only once during d-e iterations
									tmpS.push_back(bMesh->baseVerts[i]); //make 3 pnts ready for computations in this block (i is the outer loop counter)
								float dist1 = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[1] ],
									  dist2 = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[2] ],
									  dist3 = bMesh->verts[ tmpS[1] ]->dSpanningUnn[ tmpS[2] ];
								maxDistB = (dist1 > dist2 ? dist1 : dist2); //max of dist1 and 2
								maxDistB = (maxDistB > dist3 ? maxDistB : dist3); //max of dist1 and 2 and 3
								float sum1 = (dist1 + dist2 + dist3) / maxDistB;
								//same stuff for sMesh to get sum2
								tmpT.push_back(sMesh->baseVerts[j]); //just for maxDistS computation below (tmpT created within d-e loop, so no size == 2 worries here)
								dist1 = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[1] ];
								dist2 = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[2] ];
								dist3 = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[2] ];
								maxDistS = (dist1 > dist2 ? dist1 : dist2);
								maxDistS = (maxDistS > dist3 ? maxDistS : dist3);
								float sum2 = (dist1 + dist2 + dist3) / maxDistS, //= 2 times dist1/maxDist + dist2/maxDist + dist3/maxDist (so, normalized dists added)
									  diff = fabs(sum1 - sum2);
								//a bad match i-j can never go below POTENTIAL_THRESHOLD no matter how good the potentialTriplets are; in that case permutations below never reached and disto4 remains INF
								if (diff >= POTENTIAL_THRESHOLD)
									continue; //permutations below allowed only if geodesic consistency b/w these 2 pnt sets is below the threshold so they can make a potentialQuadruple
							}
							else if (distoByNormalizedGeodesics) //maxDistB/S made ready in if above; so don't do below in vain
							{
								//find the max geo dist b/w bMesh pnts in tmpS to be used as a normalizer in getCorrespListDistortion()
								if ((int) tmpS.size() == 2) //add [i] only once during d-e iterations
									tmpS.push_back(bMesh->baseVerts[i]); //just for maxDistB computation below
								for (int i = 0; i < (int) tmpS.size(); i++)
									for (int j = 1; j < (int) tmpS.size(); j++)
										if (bMesh->verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ] > maxDistB)
											maxDistB = bMesh->verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ];
								//find the max geo dist b/w mesh2 pnts in tmpT to be used as a normalizer in getCorrespListDistortion()
								tmpT.push_back(sMesh->baseVerts[j]); //just for maxDistS computation below
								for (int i = 0; i < (int) tmpT.size(); i++)
									for (int j = 1; j < (int) tmpT.size(); j++)
										if (sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ] > maxDistS)
											maxDistS = sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ];					
							}
							//try 2!=2 reordering/permutations of tmpT w/ stable tmpS of size 2
							for (int r = 0; r < 2; r++)
							{
								//refill traversalListTry according to the current reordering (one of r! permutations)
								//get the overall isometryCost of this current quadruple combination matched w/ stable quadruple of fromMesh
								vector< int > tmpSafeCorrespTry;
								int idx1 = 0, idx2 = 0;
								//stable pair of tmpS goes to odd indices, and permuted tmpT to even slots 'cos getCorrespListScaleInvDistortion() expects tmpSafeCorrespTry's odd idxs be from bMesh
								for (int y = 0; y < 4; y++) //traversalListTry of size 2, hence 2*2=4
									if (y % 2 == 1)
										tmpSafeCorrespTry.push_back(tmpS[idx1++]); //stable from source side (tmpS ~ bMesh)
									else			//even idxs ~ sMesh
										tmpSafeCorrespTry.push_back(tmpT[ perms2[r][idx2++] ]);	//perms2[0] = {1,0}, perms2[1] = {0,1} (tmpT ~ sMesh)

								//previous worst matches cannot live in tmpSafeCorrespTry 'cos they will cause an inaccurate distortion for tmpSafeCorrespTry and may therefore confuse disto3 and/or maxDisto3 which is used for desired distortion to be pushed in matchIdxs below
								bool prevWorstFound = false;
								for (int w = 0; w < (int) worsts.size(); w += 2)
									for (int t = 0; t < (int) tmpSafeCorrespTry.size(); t += 2)
										if (worsts[w] == tmpSafeCorrespTry[t] && worsts[w+1] == tmpSafeCorrespTry[t+1]) //evenIdxs of both worsts & tmpSafeCorrespTry ~ sMesh, odd ~ bMesh
										{
											prevWorstFound = true;
											w = (int) worsts.size(); //break outer loop as well
											break;
										}
								if (prevWorstFound)
									continue; //no evaluation of this potentially bad/confusing tmpSafeCorrespTry that involves a bad match

								tmpSafeCorrespTry.push_back(sMesh->baseVerts[j]); //sMesh appended (to even idx)
								tmpSafeCorrespTry.push_back(bMesh->baseVerts[i]); //bMesh appended (to odd idx)

								//compute isometry cost of this 3-3 mapping and ..
								float isometryCostTry = (distoByNormalizedGeodesics ? //sMesh->getCorrespListDistortion(bMesh, tmpSafeCorrespTry, maxDistS, maxDistB) : //whole mapping; just i-j distortion using this mapping as traversalList
																					  sMesh->getIndividualDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry, maxDistS, maxDistB) : //not whole mapping; just i-j distortion using this mapping as traversalList
																					  //sMesh->getCorrespListScaleInvDistortion(bMesh, tmpSafeCorrespTry)); //whole mapping; just i-j distortion using this mapping as traversalList
																					  sMesh->getIndividualScaleInvDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry)); //not whole mapping; just i-j distortion using this mapping as traversalList
								//..save it if it's the best so far
								if (isometryCostTry < disto3)
									disto3 = isometryCostTry;
								if (isometryCostTry > maxDisto3)
									maxDisto3 = isometryCostTry; //to be used in normalization of disto3
							} //end of r
						} //end of e
				} //end of b
			//nDisto3 = (distoByNormalizedGeodesics ? disto3 : (disto3 / maxDisto3)); //normalized disto3
			//nDisto3 = (disto3 / maxDisto3); //normalized disto3
			nDisto3 = disto3;
//fprintf(fPtr, "%f/%f: %f\t", disto3, maxDisto3, nDisto3); //234e-009 fprinted as 0 if %f used instead of %e
			distortion += nDisto3; //divide so that max value to the right of += is 1
			////////////////////////////////////////// disto3: distortion of map size 3 ends //////////////////////////////////////////*/

			////////////////////////////////////////// disto4: distortion of map size 4 ends //////////////////////////////////////////
//cout << "disto4\n";
			for (int a = 0; a < (int) bMesh->potentialTriplets.size(); a += 6)
			{
				//current 3-combination + 1 fixed i of i-j match is copied from appropriate slots of bMesh.baseVerts to tmpS (for tmp source)
				if (bMesh->potentialTriplets[a] == bMesh->baseVerts[i] || bMesh->potentialTriplets[a+2] == bMesh->baseVerts[i] || bMesh->potentialTriplets[a+4] == bMesh->baseVerts[i] ||
				    bMesh->potentialTriplets[a+1] == sMesh->baseVerts[j] || bMesh->potentialTriplets[a+3] == sMesh->baseVerts[j] || bMesh->potentialTriplets[a+5] == sMesh->baseVerts[j])
					continue; //no duplicates in tmpS or tmpT below

				//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
				vector< int > tmpS;
				tmpS.push_back(bMesh->potentialTriplets[a]); //even idxs ~ bMesh
				tmpS.push_back(bMesh->potentialTriplets[a+2]);
				tmpS.push_back(bMesh->potentialTriplets[a+4]);
				//tmpS.push_back(bMesh->baseVerts[i]); not add here 'cos 3 above will make permutations and this'll be appended to tmpSafeCorrespTry

				//this fixed combination in tmpS will be tried w/ 3! target triplet permutations tmpT
				vector< int > tmpT;
				tmpT.push_back(bMesh->potentialTriplets[a+1]); //odd idxs ~ sMesh
				tmpT.push_back(bMesh->potentialTriplets[a+3]);
				tmpT.push_back(bMesh->potentialTriplets[a+5]);
				//tmpT.push_back(sMesh->baseVerts[j]); not add here 'cos 3 above will make permutations and this'll be appended to tmpSafeCorrespTry

				//compute isometry cost of this 4-4 mapping and ..
				float maxDistB = -INF, maxDistS = -INF;				
				if (permuteCompatiblesOnly) //don't do the permutations below in vain if these 4 pnts not compatible, i.e. not from the same part
				{
					//heavily adapted from computePotentialQuadruples()					
					tmpS.push_back(bMesh->baseVerts[i]); //make 4 pnts ready for computations in this block (i is the outer loop counter)
					int i = 0;
					dists6[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[1] ];
					dists6[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[2] ];
					dists6[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[3] ];
					dists6[i++] = bMesh->verts[ tmpS[1] ]->dSpanningUnn[ tmpS[2] ];
					dists6[i++] = bMesh->verts[ tmpS[1] ]->dSpanningUnn[ tmpS[3] ];
					dists6[i++] = bMesh->verts[ tmpS[2] ]->dSpanningUnn[ tmpS[3] ];
					//C(4,2)=6 different distances to be normalized by maxDist among them
					float sum1 = 0.0f;
					for (i = 0; i < 6; i++)
					{
						sum1 += dists6[i];
						if (dists6[i] > maxDistB)
							maxDistB = dists6[i];
					}
					sum1 = 0.66f * sum1/maxDistB; //2/3=.66 'cos each dists[] is added twice (one due to a-b and one due to b-a) and agd of each vert is divided by 3 for averaging 'cos geodesics from a to other 3 in use

					//same stuff for sMesh to get sum2
					i = 0;
					tmpT.push_back(sMesh->baseVerts[j]); //make 4 pnts ready for computations in this block
					dists66[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[1] ];
					dists66[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[2] ];
					dists66[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[3] ];
					dists66[i++] = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[2] ];
					dists66[i++] = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[3] ];
					dists66[i++] = sMesh->verts[ tmpT[2] ]->dSpanningUnn[ tmpT[3] ];
					float sum2 = 0.0f;
					for (i = 0; i < 6; i++)
					{
						sum2 += dists66[i];
						if (dists66[i] > maxDistS)
							maxDistS = dists66[i];
					}
					sum2 = 0.66f * sum2/maxDistS;
					float diff = fabs(sum1 - sum2);

//if (remember == 22650 && sMesh->baseVerts[j] == 3181) cout << diff << " >=? " << POTENTIAL_THRESHOLD << endl;cout << diff << " >=? " << POTENTIAL_THRESHOLD << "\t" << remember << "-" << sMesh->baseVerts[j] << endl;

					//a bad match i-j can never go below POTENTIAL_THRESHOLD no matter how good the bMesh->potentialTriplets are; in that case permutations below never reached and disto4 remains INF
					if (diff >= POTENTIAL_THRESHOLD)
						continue; //permutations below allowed only if geodesic consistency b/w these 2 pnt sets is below the threshold so they can make a potentialQuadruple
				}
				else if (distoByNormalizedGeodesics) //maxDistB/S made ready in if above; so don't do below in vain
				{
					//find the max geo dist b/w bMesh pnts in tmpS to be used as a normalizer in getCorrespListDistortion()
					tmpS.push_back(bMesh->baseVerts[i]); //just for maxDistB computation below
					for (int i = 0; i < (int) tmpS.size(); i++)
						for (int j = 1; j < (int) tmpS.size(); j++)
							if (bMesh->verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ] > maxDistB)
								maxDistB = bMesh->verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ];
					//find the max geo dist b/w mesh2 pnts in tmpT to be used as a normalizer in getCorrespListDistortion()
					tmpT.push_back(sMesh->baseVerts[j]); //just for maxDistS computation below
					for (int i = 0; i < (int) tmpT.size(); i++)
						for (int j = 1; j < (int) tmpT.size(); j++)
							if (sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ] > maxDistS)
								maxDistS = sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ];					
				}
				//try 3!=6 reordering/permutations of tmpT w/ stable tmpS of size 3 (not 4! 'cos i-j is fixed and it's used w/ 3! permutations of other 3 pnts to make 4-4 mappings)
				for (int r = 0; r < 6; r++)
				{
					vector< int > tmpSafeCorrespTry;
					int idx1 = 0, idx2 = 0;
					//stable pair of tmpS goes to odd indices, and permuted tmpT to even slots 'cos getCorrespListScaleInvDistortion() expects tmpSafeCorrespTry's odd idxs be from bMesh
					for (int y = 0; y < 6; y++) //traversalListTry of size 3, hence 2*3=6
						if (y % 2 == 1)
							tmpSafeCorrespTry.push_back(tmpS[idx1++]); //stable from source side
						else
							tmpSafeCorrespTry.push_back(tmpT[ perms3[r][idx2++] ]);

					//previous worst matches cannot live in tmpSafeCorrespTry 'cos they will cause an inaccurate distortion for tmpSafeCorrespTry and may therefore confuse disto3 and/or maxDisto3 which is used for desired distortion to be pushed in matchIdxs below
					bool prevWorstFound = false;
					for (int w = 0; w < (int) worsts.size(); w += 2)
						for (int t = 0; t < (int) tmpSafeCorrespTry.size(); t += 2)
							if (worsts[w] == tmpSafeCorrespTry[t] && worsts[w+1] == tmpSafeCorrespTry[t+1]) //evenIdxs of both worsts & tmpSafeCorrespTry ~ sMesh, odd ~ bMesh
							{
								prevWorstFound = true;
								w = (int) worsts.size(); //break outer loop as well
								break;
							}
					if (prevWorstFound)
						continue; //no evaluation of this potentially bad/confusing tmpSafeCorrespTry that involves a bad match

					tmpSafeCorrespTry.push_back(sMesh->baseVerts[j]); //sMesh appended (to even idx)
					tmpSafeCorrespTry.push_back(bMesh->baseVerts[i]); //bMesh appended (to odd idx)

					//compute isometry cost of this 4-4 mapping and ..
					float isometryCostTry = (distoByNormalizedGeodesics ? //sMesh->getCorrespListDistortion(bMesh, tmpSafeCorrespTry, maxDistS, maxDistB) : //whole mapping; just i-j distortion using this mapping as traversalList
																		  sMesh->getIndividualDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry, maxDistS, maxDistB) : //not whole mapping; just i-j distortion using this mapping as traversalList
																		  //sMesh->getCorrespListScaleInvDistortion(bMesh, tmpSafeCorrespTry)); //whole mapping; just i-j distortion using this mapping as traversalList
																		  sMesh->getIndividualScaleInvDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry)); //not whole mapping; just i-j distortion using this mapping as traversalList
					//..save it if it's the best so far
					if (isometryCostTry < disto4)
						disto4 = isometryCostTry;
					if (isometryCostTry > maxDisto4)
						maxDisto4 = isometryCostTry; //to be used in normalization of disto4
				} //end of r
			} //end of a
			//nDisto4 = (distoByNormalizedGeodesics ? disto4 : (disto4 / maxDisto4)); //normalized disto4
			//nDisto4 = (disto4 / maxDisto4); //normalized disto4
			nDisto4 = disto4;
//fprintf(fPtr, "%f/%f: %f\t", disto4, maxDisto4, nDisto4);
			distortion += nDisto4; //divide so that max value to the right of += is 1
			if (disto4 == INF) //a bad match i-j never escaped from POTENTIAL_THRESHOLD above no matter how good the bMesh->potentialTriplets are; in that case disto4 remains INF
			{
				distortion = INF;
				bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.push_back(distortion);
				cout << i+1 << " / " << m << "\t" << (sMesh == this ? sMesh->baseVerts[j] : bMesh->baseVerts[i]) << " - " << (sMesh == this ? bMesh->baseVerts[i] : sMesh->baseVerts[j]) << "\t\t distortion = " << distortion << (j == n-1 ? " (bad i-j (4))\n\n" : " (bad i-j (4))\n");
				continue; //no need to go further w/ this bad i-j
			}
			////////////////////////////////////////// disto4: distortion of map size 4 ends //////////////////////////////////////////*/
			
			////////////////////////////////////////// disto5: distortion of map size 5 ends //////////////////////////////////////////
			if (n > 7) //if sMesh has 7 or less elements, than it'll be too hursh to insert i-j into a map of size 4 and expect that it'll pass the POTENTIAL_THRESHOLD test 'cos 4+1=5 out of 7 elements are probably never gonna be from the same part; so do below only for n > 7
			{
//cout << "disto5\n";
				for (int a = 0; a < (int) bMesh->potentialQuadruples.size(); a += 8)
				{
					//current 4-combination + 1 fixed i of i-j match is copied from appropriate slots of bMesh.baseVerts to tmpS (for tmp source)
					if (bMesh->potentialQuadruples[a] == bMesh->baseVerts[i] || bMesh->potentialQuadruples[a+2] == bMesh->baseVerts[i] || bMesh->potentialQuadruples[a+4] == bMesh->baseVerts[i] || bMesh->potentialQuadruples[a+6] == bMesh->baseVerts[i] ||
						bMesh->potentialQuadruples[a+1] == sMesh->baseVerts[j] || bMesh->potentialQuadruples[a+3] == sMesh->baseVerts[j] || bMesh->potentialQuadruples[a+5] == sMesh->baseVerts[j] || bMesh->potentialQuadruples[a+7] == sMesh->baseVerts[j])
						continue; //no duplicates in tmpS or tmpT below

					//current 4-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
					vector< int > tmpS;
					tmpS.push_back(bMesh->potentialQuadruples[a]); //even idxs ~ bMesh
					tmpS.push_back(bMesh->potentialQuadruples[a+2]);
					tmpS.push_back(bMesh->potentialQuadruples[a+4]);
					tmpS.push_back(bMesh->potentialQuadruples[a+6]);
					//tmpS.push_back(bMesh->baseVerts[i]); not add here 'cos 4 above will make permutations and this'll be appended to tmpSafeCorrespTry

					//this fixed combination in tmpS will be tried w/ 4! target permutations tmpT
					vector< int > tmpT;
					tmpT.push_back(bMesh->potentialQuadruples[a+1]); //odd idxs ~ sMesh
					tmpT.push_back(bMesh->potentialQuadruples[a+3]);
					tmpT.push_back(bMesh->potentialQuadruples[a+5]);
					tmpT.push_back(bMesh->potentialQuadruples[a+7]);
					//tmpT.push_back(sMesh->baseVerts[j]); not add here 'cos 4 above will make permutations and this'll be appended to tmpSafeCorrespTry

					float maxDistB = -INF, maxDistS = -INF;
					if (permuteCompatiblesOnly) //don't do the permutations below in vain if these 4 pnts not compatible, i.e. not from the same part
					{
						//heavily adapted from computePotentialQuintuples()
						tmpS.push_back(bMesh->baseVerts[i]); //make 5 pnts ready for computations in this block (i is the outer loop counter)
						int i = 0;
						dists10[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[1] ];
						dists10[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[2] ];
						dists10[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[3] ];
						dists10[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[4] ];
						dists10[i++] = bMesh->verts[ tmpS[1] ]->dSpanningUnn[ tmpS[2] ];
						dists10[i++] = bMesh->verts[ tmpS[1] ]->dSpanningUnn[ tmpS[3] ];
						dists10[i++] = bMesh->verts[ tmpS[1] ]->dSpanningUnn[ tmpS[4] ];
						dists10[i++] = bMesh->verts[ tmpS[2] ]->dSpanningUnn[ tmpS[3] ];
						dists10[i++] = bMesh->verts[ tmpS[2] ]->dSpanningUnn[ tmpS[4] ];
						dists10[i++] = bMesh->verts[ tmpS[3] ]->dSpanningUnn[ tmpS[4] ];
						//C(5,2)=10 different distances to be normalized by maxDist among them
						float sum1 = 0.0f;
						for (i = 0; i < 10; i++)
						{
							sum1 += dists10[i];
							if (dists10[i] > maxDistB)
								maxDistB = dists10[i];
						}
						sum1 = 0.5f * sum1/maxDistB; //2/4=.5 'cos each dists[] is added twice (one due to a-b and one due to b-a) and agd of each vert is divided by 4 for averaging 'cos geodesics from a to other 4 in use

						//same stuff for sMesh to get sum2
						i = 0;
						tmpT.push_back(sMesh->baseVerts[j]); //make 4 pnts ready for computations in this block
						dists1010[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[1] ];
						dists1010[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[2] ];
						dists1010[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[3] ];
						dists1010[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[4] ];
						dists1010[i++] = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[2] ];
						dists1010[i++] = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[3] ];
						dists1010[i++] = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[4] ];
						dists1010[i++] = sMesh->verts[ tmpT[2] ]->dSpanningUnn[ tmpT[3] ];
						dists1010[i++] = sMesh->verts[ tmpT[2] ]->dSpanningUnn[ tmpT[4] ];
						dists1010[i++] = sMesh->verts[ tmpT[3] ]->dSpanningUnn[ tmpT[4] ];
						float sum2 = 0.0f;
						for (i = 0; i < 10; i++)
						{
							sum2 += dists1010[i];
							if (dists1010[i] > maxDistS)
								maxDistS = dists1010[i];
						}
						sum2 = 0.5f * sum2/maxDistS;
						float diff = fabs(sum1 - sum2);
						if (diff >= POTENTIAL_THRESHOLD)
							continue; //permutations below allowed only if geodesic consistency b/w these 2 pnt sets is below the threshold so they can make a potentialQuadruple
					}
					else if (distoByNormalizedGeodesics) //maxDistB/S made ready in if above; so don't do below in vain
					{
						//find the max geo dist b/w bMesh pnts in tmpS to be used as a normalizer in getCorrespListDistortion()
						tmpS.push_back(bMesh->baseVerts[i]); //just for maxDistB computation below
						for (int i = 0; i < (int) tmpS.size(); i++)
							for (int j = 1; j < (int) tmpS.size(); j++)
								if (bMesh->verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ] > maxDistB)
									maxDistB = bMesh->verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ];
						//find the max geo dist b/w mesh2 pnts in tmpT to be used as a normalizer in getCorrespListDistortion()
						tmpT.push_back(sMesh->baseVerts[j]); //just for maxDistS computation below
						for (int i = 0; i < (int) tmpT.size(); i++)
							for (int j = 1; j < (int) tmpT.size(); j++)
								if (sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ] > maxDistS)
									maxDistS = sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ];					
					}
					//try 4!=24 reordering/permutations of tmpT w/ stable tmpS of size 4
					for (int r = 0; r < 24; r++)
					{
						vector< int > tmpSafeCorrespTry;
						int idx1 = 0, idx2 = 0;
						//stable pair of tmpS goes to odd indices, and permuted tmpT to even slots 'cos getCorrespListScaleInvDistortion() expects tmpSafeCorrespTry's odd idxs be from bMesh
						for (int y = 0; y < 8; y++) //traversalListTry of size 4, hence 2*4=8
							if (y % 2 == 1)
								tmpSafeCorrespTry.push_back(tmpS[idx1++]); //stable from source side
							else
								tmpSafeCorrespTry.push_back(tmpT[ perms4[r][idx2++] ]);

						//previous worst matches cannot live in tmpSafeCorrespTry 'cos they will cause an inaccurate distortion for tmpSafeCorrespTry and may therefore confuse disto3 and/or maxDisto3 which is used for desired distortion to be pushed in matchIdxs below
						bool prevWorstFound = false;
						for (int w = 0; w < (int) worsts.size(); w += 2)
							for (int t = 0; t < (int) tmpSafeCorrespTry.size(); t += 2)
								if (worsts[w] == tmpSafeCorrespTry[t] && worsts[w+1] == tmpSafeCorrespTry[t+1]) //evenIdxs of both worsts & tmpSafeCorrespTry ~ sMesh, odd ~ bMesh
								{
									prevWorstFound = true;
									w = (int) worsts.size(); //break outer loop as well
									break;
								}
						if (prevWorstFound)
							continue; //no evaluation of this potentially bad/confusing tmpSafeCorrespTry that involves a bad match

						tmpSafeCorrespTry.push_back(sMesh->baseVerts[j]); //sMesh appended (to even idx)
						tmpSafeCorrespTry.push_back(bMesh->baseVerts[i]); //bMesh appended (to odd idx)
					
						//compute isometry cost of this 5-5 mapping and ..
						float isometryCostTry = (distoByNormalizedGeodesics ? //sMesh->getCorrespListDistortion(bMesh, tmpSafeCorrespTry, maxDistS, maxDistB) : //whole mapping; just i-j distortion using this mapping as traversalList
																			  sMesh->getIndividualDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry, maxDistS, maxDistB) : //not whole mapping; just i-j distortion using this mapping as traversalList
																			  //sMesh->getCorrespListScaleInvDistortion(bMesh, tmpSafeCorrespTry)); //whole mapping; just i-j distortion using this mapping as traversalList
																			  sMesh->getIndividualScaleInvDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry)); //not whole mapping; just i-j distortion using this mapping as traversalList

						//..save it if it's the best so far
						if (isometryCostTry < disto5)
							disto5 = isometryCostTry;
						if (isometryCostTry > maxDisto5)
							maxDisto5 = isometryCostTry; //to be used in normalization of disto5
					} //end of r
				} //end of a
				//nDisto5 = (distoByNormalizedGeodesics ? disto5 : (disto5 / maxDisto5)); //normalized disto5
				//nDisto5 = (disto5 / maxDisto5); //normalized disto5
				nDisto5 = disto5;
//fprintf(fPtr, "%f/%f: %f\t", disto5, maxDisto5, nDisto5);
				distortion += nDisto5; //divide so that max value to the right of += is 1
				if (disto5 == INF) //a bad match i-j never escaped from POTENTIAL_THRESHOLD above no matter how good the bMesh->potentialQuadruples are; in that case disto5 remains INF
				{
					distortion = INF;
					bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.push_back(distortion);
					cout << i+1 << " / " << m << "\t" << (sMesh == this ? sMesh->baseVerts[j] : bMesh->baseVerts[i]) << " - " << (sMesh == this ? bMesh->baseVerts[i] : sMesh->baseVerts[j]) << "\t\t distortion = " << distortion << (j == n-1 ? " (bad i-j (5))\n\n" : " (bad i-j (5))\n");
					continue; //no need to go further w/ this bad i-j
				}
			}
			////////////////////////////////////////// disto5: distortion of map size 5 ends //////////////////////////////////////////*/

			////////////////////////////////////////// disto6: distortion of map size 6 ends //////////////////////////////////////////
			if (n > 7) //if sMesh has 7 or less elements, than it'll be too hursh to insert i-j into a map of size 5 and expect that it'll pass the POTENTIAL_THRESHOLD test 'cos 5+1=6 out of 7 elements are probably never gonna be from the same part; so do below only for n > 7
			{
//cout << "disto6\n";
				for (int a = 0; a < (int) bMesh->potentialQuintuples.size(); a += 10)
				{
					//current 4-combination + 1 fixed i of i-j match is copied from appropriate slots of bMesh.baseVerts to tmpS (for tmp source)
					if (bMesh->potentialQuintuples[a] == bMesh->baseVerts[i] || bMesh->potentialQuintuples[a+2] == bMesh->baseVerts[i] || bMesh->potentialQuintuples[a+4] == bMesh->baseVerts[i] || bMesh->potentialQuintuples[a+6] == bMesh->baseVerts[i] || bMesh->potentialQuintuples[a+8] == bMesh->baseVerts[i] ||
						bMesh->potentialQuintuples[a+1] == sMesh->baseVerts[j] || bMesh->potentialQuintuples[a+3] == sMesh->baseVerts[j] || bMesh->potentialQuintuples[a+5] == sMesh->baseVerts[j] || bMesh->potentialQuintuples[a+7] == sMesh->baseVerts[j] || bMesh->potentialQuintuples[a+9] == sMesh->baseVerts[j])
						continue; //no duplicates in tmpS or tmpT below

					//current 5-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
					vector< int > tmpS;
					tmpS.push_back(bMesh->potentialQuintuples[a]); //even idxs ~ bMesh
					tmpS.push_back(bMesh->potentialQuintuples[a+2]);
					tmpS.push_back(bMesh->potentialQuintuples[a+4]);
					tmpS.push_back(bMesh->potentialQuintuples[a+6]);
					tmpS.push_back(bMesh->potentialQuintuples[a+8]);
					//tmpS.push_back(bMesh->baseVerts[i]); not add here 'cos 5 above will make permutations and this'll be appended to tmpSafeCorrespTry

					//this fixed combination in tmpS will be tried w/ 4! target permutations tmpT
					vector< int > tmpT;
					tmpT.push_back(bMesh->potentialQuintuples[a+1]); //odd idxs ~ sMesh
					tmpT.push_back(bMesh->potentialQuintuples[a+3]);
					tmpT.push_back(bMesh->potentialQuintuples[a+5]);
					tmpT.push_back(bMesh->potentialQuintuples[a+7]);
					tmpT.push_back(bMesh->potentialQuintuples[a+9]);
					//tmpT.push_back(sMesh->baseVerts[j]); not add here 'cos 5 above will make permutations and this'll be appended to tmpSafeCorrespTry

					float maxDistB = -INF, maxDistS = -INF;
					if (permuteCompatiblesOnly) //don't do the permutations below in vain if these 4 pnts not compatible, i.e. not from the same part
					{
						//heavily adapted from computePotentialQuintuples()
						tmpS.push_back(bMesh->baseVerts[i]); //make 6 pnts ready for computations in this block (i is the outer loop counter)
						int i = 0;
						dists15[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[1] ];
						dists15[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[2] ];
						dists15[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[3] ];
						dists15[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[4] ];
						dists15[i++] = bMesh->verts[ tmpS[0] ]->dSpanningUnn[ tmpS[5] ];
						dists15[i++] = bMesh->verts[ tmpS[1] ]->dSpanningUnn[ tmpS[2] ];
						dists15[i++] = bMesh->verts[ tmpS[1] ]->dSpanningUnn[ tmpS[3] ];
						dists15[i++] = bMesh->verts[ tmpS[1] ]->dSpanningUnn[ tmpS[4] ];
						dists15[i++] = bMesh->verts[ tmpS[1] ]->dSpanningUnn[ tmpS[5] ];
						dists15[i++] = bMesh->verts[ tmpS[2] ]->dSpanningUnn[ tmpS[3] ];
						dists15[i++] = bMesh->verts[ tmpS[2] ]->dSpanningUnn[ tmpS[4] ];
						dists15[i++] = bMesh->verts[ tmpS[2] ]->dSpanningUnn[ tmpS[5] ];
						dists15[i++] = bMesh->verts[ tmpS[3] ]->dSpanningUnn[ tmpS[4] ];
						dists15[i++] = bMesh->verts[ tmpS[3] ]->dSpanningUnn[ tmpS[5] ];
						dists15[i++] = bMesh->verts[ tmpS[4] ]->dSpanningUnn[ tmpS[5] ];
						//C(6,2)=15 different distances to be normalized by maxDist among them
						float sum1 = 0.0f;
						for (i = 0; i < 15; i++)
						{
							sum1 += dists15[i];
							if (dists15[i] > maxDistB)
								maxDistB = dists15[i];
						}
						sum1 = 0.4f * sum1/maxDistB; //2/5=.4 'cos each dists[] is added twice (one due to a-b and one due to b-a) and agd of each vert is divided by 5 for averaging 'cos geodesics from a to other 5 in use

						//same stuff for sMesh to get sum2
						i = 0;
						tmpT.push_back(sMesh->baseVerts[j]); //make 4 pnts ready for computations in this block
						dists1515[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[1] ];
						dists1515[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[2] ];
						dists1515[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[3] ];
						dists1515[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[4] ];
						dists1515[i++] = sMesh->verts[ tmpT[0] ]->dSpanningUnn[ tmpT[5] ];
						dists1515[i++] = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[2] ];
						dists1515[i++] = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[3] ];
						dists1515[i++] = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[4] ];
						dists1515[i++] = sMesh->verts[ tmpT[1] ]->dSpanningUnn[ tmpT[5] ];
						dists1515[i++] = sMesh->verts[ tmpT[2] ]->dSpanningUnn[ tmpT[3] ];
						dists1515[i++] = sMesh->verts[ tmpT[2] ]->dSpanningUnn[ tmpT[4] ];
						dists1515[i++] = sMesh->verts[ tmpT[2] ]->dSpanningUnn[ tmpT[5] ];
						dists1515[i++] = sMesh->verts[ tmpT[3] ]->dSpanningUnn[ tmpT[4] ];
						dists1515[i++] = sMesh->verts[ tmpT[3] ]->dSpanningUnn[ tmpT[5] ];
						dists1515[i++] = sMesh->verts[ tmpT[4] ]->dSpanningUnn[ tmpT[5] ];
						float sum2 = 0.0f;
						for (i = 0; i < 15; i++)
						{
							sum2 += dists1515[i];
							if (dists1515[i] > maxDistS)
								maxDistS = dists1515[i];
						}
						sum2 = 0.4f * sum2/maxDistS;
						float diff = fabs(sum1 - sum2);
						if (diff >= POTENTIAL_THRESHOLD)
							continue; //permutations below allowed only if geodesic consistency b/w these 2 pnt sets is below the threshold so they can make a potentialQuadruple
					}
					else if (distoByNormalizedGeodesics) //maxDistB/S made ready in if above; so don't do below in vain
					{
						//find the max geo dist b/w bMesh pnts in tmpS to be used as a normalizer in getCorrespListDistortion()
						tmpS.push_back(bMesh->baseVerts[i]); //just for maxDistB computation below
						for (int i = 0; i < (int) tmpS.size(); i++)
							for (int j = 1; j < (int) tmpS.size(); j++)
								if (bMesh->verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ] > maxDistB)
									maxDistB = bMesh->verts[ tmpS[i] ]->dSpanningUnn[ tmpS[j] ];
						//find the max geo dist b/w mesh2 pnts in tmpT to be used as a normalizer in getCorrespListDistortion()
						tmpT.push_back(sMesh->baseVerts[j]); //just for maxDistS computation below
						for (int i = 0; i < (int) tmpT.size(); i++)
							for (int j = 1; j < (int) tmpT.size(); j++)
								if (sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ] > maxDistS)
									maxDistS = sMesh->verts[ tmpT[i] ]->dSpanningUnn[ tmpT[j] ];					
					}
					//try 5!=120 reordering/permutations of tmpT w/ stable tmpS of size 5
					for (int r = 0; r < 120; r++)
					{
						vector< int > tmpSafeCorrespTry;
						int idx1 = 0, idx2 = 0;
						//stable pair of tmpS goes to odd indices, and permuted tmpT to even slots 'cos getCorrespListScaleInvDistortion() expects tmpSafeCorrespTry's odd idxs be from bMesh
						for (int y = 0; y < 10; y++) //traversalListTry of size 5, hence 2*5=10
							if (y % 2 == 1)
								tmpSafeCorrespTry.push_back(tmpS[idx1++]); //stable from source side
							else
								tmpSafeCorrespTry.push_back(tmpT[ perms5[r][idx2++] ]);

						//previous worst matches cannot live in tmpSafeCorrespTry 'cos they will cause an inaccurate distortion for tmpSafeCorrespTry and may therefore confuse disto3 and/or maxDisto3 which is used for desired distortion to be pushed in matchIdxs below
						bool prevWorstFound = false;
						for (int w = 0; w < (int) worsts.size(); w += 2)
							for (int t = 0; t < (int) tmpSafeCorrespTry.size(); t += 2)
								if (worsts[w] == tmpSafeCorrespTry[t] && worsts[w+1] == tmpSafeCorrespTry[t+1]) //evenIdxs of both worsts & tmpSafeCorrespTry ~ sMesh, odd ~ bMesh
								{
									prevWorstFound = true;
									w = (int) worsts.size(); //break outer loop as well
									break;
								}
						if (prevWorstFound)
							continue; //no evaluation of this potentially bad/confusing tmpSafeCorrespTry that involves a bad match

						tmpSafeCorrespTry.push_back(sMesh->baseVerts[j]); //sMesh appended (to even idx)
						tmpSafeCorrespTry.push_back(bMesh->baseVerts[i]); //bMesh appended (to odd idx)

						//compute isometry cost of this 6-6 mapping and ..
						float isometryCostTry = (distoByNormalizedGeodesics ? //sMesh->getCorrespListDistortion(bMesh, tmpSafeCorrespTry, maxDistS, maxDistB) : //whole mapping; just i-j distortion using this mapping as traversalList
																			  sMesh->getIndividualDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry, maxDistS, maxDistB) : //not whole mapping; just i-j distortion using this mapping as traversalList
																			  //sMesh->getCorrespListScaleInvDistortion(bMesh, tmpSafeCorrespTry)); //whole mapping; just i-j distortion using this mapping as traversalList
																			  sMesh->getIndividualScaleInvDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry)); //not whole mapping; just i-j distortion using this mapping as traversalList
						//..save it if it's the best so far
						if (isometryCostTry < disto6)
							disto6 = isometryCostTry;
						if (isometryCostTry > maxDisto6)
							maxDisto6 = isometryCostTry; //to be used in normalization of disto4
					} //end of r
				} //end of a
				//nDisto6 = (distoByNormalizedGeodesics ? disto6 : (disto6 / maxDisto6)); //normalized disto6
				//nDisto6 = (disto6 / maxDisto6); //normalized disto6
				nDisto6 = disto6;
//fprintf(fPtr, "%f/%f: %f\t", disto6, maxDisto6, nDisto6);
				distortion += nDisto6; //divide so that max value to the right of += is 1
				if (disto6 == INF) //a bad match i-j never escaped from POTENTIAL_THRESHOLD above no matter how good the bMesh->potentialQuintuples are; in that case disto6 remains INF
				{
					distortion = INF;
					bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.push_back(distortion);
					cout << i+1 << " / " << m << "\t" << (sMesh == this ? sMesh->baseVerts[j] : bMesh->baseVerts[i]) << " - " << (sMesh == this ? bMesh->baseVerts[i] : sMesh->baseVerts[j]) << "\t\t distortion = " << distortion << (j == n-1 ? " (bad i-j (6))\n\n" : " (bad i-j (6))\n");
					continue; //no need to go further w/ this bad i-j
				}
			}
			////////////////////////////////////////// disto6: distortion of map size 6 ends //////////////////////////////////////////*/

			if (distortionComputation == 1) //use sum of all nDisto* (already summed up above; so, just avg it here)
			{
				int nInUse = 4; //4 terms if all used: nDisto3-4-5-6
				if (nDisto3 == -1.0f) //nDisto3 not in use
					nInUse--;
				if (nDisto4 == -1.0f) //nDisto4 not in use
					nInUse--;
				if (nDisto5 == -1.0f) //nDisto5 not in use
					nInUse--;
				if (nDisto6 == -1.0f) //nDisto6 not in use (nDisto4-5 in use for sure)
					nInUse--;
if (nInUse <= 0){cout<<"wrong1\n";exit(0);}
				distortion /= nInUse;
			}
			else if (distortionComputation == 2) //use min(nDisto*)
			{
				//instead of summing nDisto*, take the minimum of them as the ultimate individual i-j distortion (just disable two-lines below to use summed nDisto*, which is better)
				distortion = (nDisto3 == -1.0f || nDisto4 < nDisto3 ? nDisto4 : nDisto3); //min of nDisto3 and 4
				distortion = (distortion < nDisto5 ? distortion : nDisto5); //min of nDisto3 and 4 and 5
				distortion = (nDisto6 == -1.0f || distortion < nDisto6 ? distortion : nDisto6); //min of nDisto3 and 4 and 5 and 6 (if nDisto6 available, i.e. not -1.0f)
if (nDisto4 == -1.0f || nDisto5 == -1.0f){cout<<"wrong2\n";exit(0);} //nDisto4-5 in use for sure (otherwise comparisons in this block would be too long)
			}
			else if (distortionComputation == 3) //use sum of all nDisto* but the worst
			{
				//worst nDisto
				distortion = (nDisto3 == -1.0f || nDisto4 > nDisto3 ? nDisto4 : nDisto3); //max of nDisto3 and 4
				distortion = (distortion > nDisto5 ? distortion : nDisto5); //max of nDisto3 and 4 and 5
				distortion = (nDisto6 == -1.0f || distortion > nDisto6 ? distortion : nDisto6); //max of nDisto3 and 4 and 5 and 6 (if nDisto6 available, i.e. not -1.0f)
				distortion = (nDisto3 == -1.0f ? 0.0f : nDisto3) + nDisto4 + nDisto5 + (nDisto6 == -1.0f ? 0.0f : nDisto6) - distortion; //consider nDisto6 if it's available
if (nDisto4 == -1.0f || nDisto5 == -1.0f){cout<<"wrong3\n";exit(0);} //nDisto4-5 in use for sure (otherwise comparisons in this block would be too long)
			}

			//previously worst matches are discouraged to be selected in blossom; so, make their distortion a big value regardless of what computed above
			for (int w = 0; w < (int) worsts.size(); w += 2)
				if (worsts[w] == sMesh->baseVerts[j] && worsts[w+1] == bMesh->baseVerts[i]) //evenIdxs of both worsts sMesh, odd ~ bMesh
				{					
					distortion = INF; //discouarge
					cout << "distortion of prev worst: " << (sMesh == this ? sMesh->baseVerts[j] : bMesh->baseVerts[i]) << "--" << (sMesh == this ? bMesh->baseVerts[i] : sMesh->baseVerts[j]) << ": " << distortion << "\n";
					break;
				}
//fprintf(fPtr, "%f%s", distortion, (j == n-1 ? "\n\n" : "\n"));
			//this candidate i-j match comes w/ the following distortion (to be used below to select the best)
			bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.push_back(distortion);
//			avgDisto += distortion;
//printf("%4.2f %4.2f %4.2f\t\t", maxDisto3, maxDisto4, maxDisto5); //.2f for 2 digits after dot
cout << i+1 << " / " << m << "\t" << (sMesh == this ? sMesh->baseVerts[j] : bMesh->baseVerts[i]) << " - " << (sMesh == this ? bMesh->baseVerts[i] : sMesh->baseVerts[j]) << "\t\t distortion = " << distortion << endl;//(j == n-1 ? "\n\n" : "\n");
//if (distortion < mminD) mminD = distortion;		if (distortion > mmaxD) mmaxD = distortion;		ttotalD += distortion;
		} //end of j
//cout << "min & max & avg distortion = " << mminD << " & " << mmaxD << " & " << ttotalD / n << endl;

		//mark cool matches whose distortions are below avgDisto*2 //avgDisto /= n; cout << "avgDisto = " << avgDisto << "\tpotential coolness threshold: " << avgDisto << "\n"; not using avgDisto as cool match decider
		//mark cool matches which are the ones before the first jump
		float jump = 0.0f;
		vector< float > matchIdxsSorted; //sorted version of matchIdxs from which jump will be learnt
		for (int h = 0; h < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.size(); h += 2)
			sMesh->verts[ (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[h] ]->processed = false; //true if added to matchIdxsSorted as nextMin
		for (int h = 0; h < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.size(); h += 2) //make p=matchIdxs.size passes each of which gives the next min; so, o(p^2) algo
		{
			float minDisto = INF;
			int nextMinK = -1;
			for (int k = 0; k < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.size(); k += 2)
				if (! sMesh->verts[ (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[k] ]->processed && bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[k+1] <= minDisto) //not added yet
				{
					minDisto = bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[k+1];
					nextMinK = k;
				}
if (nextMinK == -1){cout<<"can't\n";exit(0);}
			//add the next min to matchIdxsSorted
			matchIdxsSorted.push_back( bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[nextMinK] );
			matchIdxsSorted.push_back( bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[nextMinK+1] );
			sMesh->verts[ (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs[nextMinK] ]->processed = true; //not add again
//		if (jumpBasedOnFirstTwoDifferences)
				jump += (h == 2 || h == 4 ? (matchIdxsSorted[h+1] - matchIdxsSorted[h-1]) : 0.0f); //use only the first 2 steps to decide the jump
//			else
//				jump += (h == 2 ? (matchIdxsSorted[h+1] - matchIdxsSorted[h-1]) : 0.0f); //use only the first step to decide the jump [cancel jump updates below 'cos /= 2 no longer valid]*/
		}
//		if (jumpBasedOnFirstTwoDifferences)
		{
			jump /= 2; //jump = avgStep at this time 'cos first 2 steps added above, so avg them here
			jump *= 2.0f; //2 x avg step	*= 1.5f for 1.5 x avg step
		}
		//not using all differences unlike confidence-based jump in matchBasesViaVoting2() 'cos there're irrelevant distortions above for sure as i try all matches j for a given i*/

		//transfer cool matches in sorted list, which are the ones w/ distortion difference w/ neighbor < jump
		for (int h = 0; h + 2 < (int) matchIdxsSorted.size(); h += 2) //could have used h < size as well but w/ h + 2 < size i guarantee that i exclude the worst match (max distortion) for sure even if it obeys the jump rule
			if (h <= 2 || matchIdxsSorted[h+1] - matchIdxsSorted[h-1] < jump) //add the first 2 min matches to matchIdxs2 directly and stop adding when a jump detected; when more reliable (due to dense sampling) confidences in use, any extra approved here will be canceled anyway; nevertheless, don't approva too bads
			{
				if (matchIdxsSorted[h+1] == INF)
					break; //rare but can happen if matchIdxsSorted[2] is a match w/ INF distortion (due to (bad i-j (x))), which makes jump > INF which accepts everything; prevent such redundant accepts by breaking here

//				if ((int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs2.size() == 10) //at most 5 matches approved by ranking here
//					break;

				bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs2.push_back( matchIdxsSorted[h] ); //same format as matchIdxs: match idx and match distortion as 2 consecutive items
				bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs2.push_back( matchIdxsSorted[h+1] );				
cout << "\t" << i+1 << " // " << m << "\t" << (sMesh == this ? matchIdxsSorted[h] : bMesh->baseVerts[i]) << " -- " << (sMesh == this ? bMesh->baseVerts[i] : matchIdxsSorted[h]) << "\t\tdistortion = " << matchIdxsSorted[h+1] << "\n";
			}
			else
				break;
cout << "\tsubset of " << (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs2.size() / 2 << " / " << n << " potentially cool matches\n\n";
	}
//fclose(fPtr);

	delete [] dists6;
	delete [] dists66;
	delete [] dists10;
	delete [] dists1010;
	delete [] dists15;
	delete [] dists1515;
	cout << "done!\n";
}

void Mesh::matchBasesViaNghbs(Mesh* bMesh, Mesh* sMesh)
{
	//d_iso(i, j)'s made ready by putting i-j to size 3 mapping whose left side is i's 3 closest nghbs (on bMesh) and right is permutations of j's 3 closest nghbs (on sMesh), and evaluating that resulting mapping; distortion of min-distortion resulting mapping becomes d_iso(i,j), the individual distortion, for match i-j (i-j will then be put in final/output match if it's distortion is good enough)
//notInUse 'cos this idea sucks: i is at toe and has 3 close nghbs p, r, s; j is at tail and has 3 close nghbs at tail u, v, w; dists b/w i-p-r-s may be consistent be j-u-v-w which makes i-j match which is wrong :( besides, joint sampling on arm of centaur and arm of human not same although arms are same (same evenly-spaced # of bases sparse at centaur.arm 'cos it has a bigger body to cover than human)
	
	cout << "\ndistortions for each pair of matches being computed....\n";
	
	int m = (int) bMesh->baseVerts.size(),
		n = (int) sMesh->baseVerts.size(), //so m >= n for sure
		nNghbs, //# of closest nghbs to use in decision of d_iso values below
		minNghbs = 2, maxNghbs = 5; //closest nghbs of amount [minNghbs, maxNghbs], e.g. for minNghbs = 2, maxNgbs = 5; i try 2,3,4,5 closest nghbs, hence use disto3,4,5,6 (+1 i-j itself is added to mappings to be evaluated)
assert(minNghbs >= 2 && maxNghbs <= 5); //at least 2, at most 5 nghbors used (2 is the least allowed for computation of scale inv distortion, and below is coded up to 5 which is sufficient)

	//assign a distortion d_iso to all i-j pairs b/w base vertices:
	//d_iso(i, j) = 3! * i.baseNghbsK[0..2] to j.baseNghbsK[0..2] //all close 4-tuples involving i-j match, i.e. i-j is fixed and i combine it w/ all other close neighbor pairs (this example uses nNghbs = 3)
	
	//normally m and n are ~10 but stop at 5! (disto6) just-above due to efficiency and also accuracy (a bad i-j may give less distortion if it is tested w/ 6+ good mappings); no!!!!!! stopping at disto3 for efficiency
	fillPermutationMtrxs();
	for (int i = 0; i < m; i++)
		if (! bMesh->verts[ bMesh->baseVerts[i] ]->additionalBase) //don't set matchIdxs for an additionalBase 'cos it is there to help the matchIdxs computation of initial original ~10 local maxima bases (helpers accessed by baseNghbsK[])
			for (int j = 0; j < n; j++)
			{
				if (sMesh->verts[ sMesh->baseVerts[j] ]->additionalBase)
					continue; //don't set matchIdxs for an additionalBase 'cos it is there to help the matchIdxs computation of initial original ~10 local maxima bases (helpers accessed by baseNghbsK[])

				//i-j match whose d_iso will be computed below
				bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.push_back((float) sMesh->baseVerts[j]);
				//and scale-invariant distortion
				float distortion = 0.0f;

				////////////////////////////////////////// distoX: distortion of map size X ends //////////////////////////////////////////
				for (nNghbs = minNghbs; nNghbs <= maxNghbs; nNghbs++) //for disto3, 4, and 5
				{
					//scale-invariant distortion
					float distoX = INF, maxDistoX = -INF; //store distortion of different-size mappings separately 'cos they need to be normalized to the compatible scale (via maxDisto*) before being added for ultimate distortion value				  

//cout << "disto" << nNghbs+1 << "\n";
assert((int) bMesh->verts[ bMesh->baseVerts[i] ]->baseNghbsK.size() >= nNghbs); //to be able to provide nNghbs nghbors for distoX computation
	
					vector< int > tmpS;
					tmpS.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->baseNghbsK[0]);
					tmpS.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->baseNghbsK[1]); //nNghbs=2
					if (nNghbs == 3 || nNghbs == 4 || nNghbs == 5)
						tmpS.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->baseNghbsK[2]); //nNghbs=3
					if (nNghbs == 4 || nNghbs == 5)
						tmpS.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->baseNghbsK[3]); //nNghbs=4
					if (nNghbs == 5)
						tmpS.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->baseNghbsK[4]); //nNghbs=5
					//tmpS.push_back(bMesh->baseVerts[i]); not add here 'cos 3 above will make permutations and this'll be appended to tmpSafeCorrespTry

					vector< int > tmpT;
					tmpT.push_back(sMesh->verts[ sMesh->baseVerts[j] ]->baseNghbsK[0]);
					tmpT.push_back(sMesh->verts[ sMesh->baseVerts[j] ]->baseNghbsK[1]); //nNghbs=2
					if (nNghbs == 3 || nNghbs == 4 || nNghbs == 5)
						tmpT.push_back(sMesh->verts[ sMesh->baseVerts[j] ]->baseNghbsK[2]); //nNghbs=3
					if (nNghbs == 4 || nNghbs == 5)
						tmpT.push_back(sMesh->verts[ sMesh->baseVerts[j] ]->baseNghbsK[3]); //nNghbs=4
					if (nNghbs == 5)
						tmpT.push_back(sMesh->verts[ sMesh->baseVerts[j] ]->baseNghbsK[4]); //nNghbs=3
					//tmpT.push_back(sMesh->baseVerts[j]); not add here 'cos 3 above will make permutations and this'll be appended to tmpSafeCorrespTry

					//try nNghbs! reordering/permutations of tmpT w/ stable tmpS of size nNghbs
					int nPerms = factorial(nNghbs), mapSize = 2*nNghbs;
					for (int r = 0; r < nPerms; r++)
					{
						vector< int > tmpSafeCorrespTry;
						int idx1 = 0, idx2 = 0;
						//stable pair of tmpS goes to odd indices, and permuted tmpT to even slots 'cos getCorrespListScaleInvDistortion() expects tmpSafeCorrespTry's odd idxs be from bMesh
						for (int y = 0; y < mapSize; y++) //traversalListTry of size 3, hence 2*3=6 (for nNghbs=3)
							if (y % 2 == 1)
								tmpSafeCorrespTry.push_back(tmpS[idx1++]); //stable from source side
							else
							{
								if (nNghbs == 2) //use perms2[]
									tmpSafeCorrespTry.push_back(tmpT[ perms2[r][idx2++] ]);
								else if (nNghbs == 3) //use perms3[]
									tmpSafeCorrespTry.push_back(tmpT[ perms3[r][idx2++] ]);
								else if (nNghbs == 4) //use perms4[]
									tmpSafeCorrespTry.push_back(tmpT[ perms4[r][idx2++] ]);
								else				  //use perms5[]
									tmpSafeCorrespTry.push_back(tmpT[ perms5[r][idx2++] ]);
							}
						tmpSafeCorrespTry.push_back(sMesh->baseVerts[j]); //sMesh appended (to even idx)
						tmpSafeCorrespTry.push_back(bMesh->baseVerts[i]); //bMesh appended (to odd idx)

						//compute isometry cost of this (nNghbs+1)-(nNghbs+1) mapping and ..
						float isometryCostTry = //sMesh->getCorrespListScaleInvDistortion(bMesh, tmpSafeCorrespTry); //no!!!!!! not whole mapping; just i-j distortion using this mapping as traversalList
												sMesh->getIndividualScaleInvDistortion(bMesh, sMesh->baseVerts[j], bMesh->baseVerts[i], tmpSafeCorrespTry);
						//..save it if it's the best so far
						if (isometryCostTry < distoX)
							distoX = isometryCostTry;
						if (isometryCostTry > maxDistoX)
							maxDistoX = isometryCostTry; //to be used in normalization of disto3
					} //end of r

					distortion += (distoX /	maxDistoX); //divide so that max value to the right of += is 1
				} //end of nNghbs
				////////////////////////////////////////// distoX: distortion of map size X ends //////////////////////////////////////////

				//this candidate i-j match comes w/ the following distortion (to be used below to select the best)
				bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs.push_back(distortion);
cout << i+1 << " / " << m << "\t" << (sMesh == this ? sMesh->baseVerts[j] : bMesh->baseVerts[i]) << " - " << (sMesh == this ? bMesh->baseVerts[i] : sMesh->baseVerts[j]) << "\t\t distortion = " << distortion << endl;
			} //end of j
	cout << "done!\n";
}

bool Mesh::matchBasesViaVoting(Mesh* bMesh, Mesh* sMesh, char* fName)
{
	//given potentialTriplets/Quads/Quints, use the very reliable ones (which pass ranking test based on matchIdxs2 values) and add votes to the constituent matches, hence integer (not fuzzy) vote mtrx created which then gives the 1-1 map via max-row-col algo

	cout << "\nconfidence vote mtrx being computed....\n";
	
	int m = (int) bMesh->baseVerts.size(),
		n = (int) sMesh->baseVerts.size();

	//////////////////// votes from potentialTriplets ////////////////////
	for (int a = 0; a < (int) bMesh->potentialTriplets.size(); a += 6)
	{
		//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
		vector< int > tmpS;
		tmpS.push_back(bMesh->potentialTriplets[a]); //even idxs ~ bMesh
		tmpS.push_back(bMesh->potentialTriplets[a+2]);
		tmpS.push_back(bMesh->potentialTriplets[a+4]);

		//this fixed combination in tmpS will be tried w/ 3! target triplet permutations tmpT
		vector< int > tmpT;
		tmpT.push_back(bMesh->potentialTriplets[a+1]); //odd idxs ~ sMesh
		tmpT.push_back(bMesh->potentialTriplets[a+3]);
		tmpT.push_back(bMesh->potentialTriplets[a+5]);

//cout << "\ntriplet: " << tmpS[0] << "-" << tmpS[1] << "-" << tmpS[2] << "\t\t" << tmpT[0] << "-" << tmpT[1] << "-" << tmpT[2] << "\n";

		//try 3!=6 reordering/permutations of tmpT w/ stable tmpS of size 3
		for (int r = 0; r < 6; r++)
		{
			///////////////// ranking test /////////////////
			//bMesh.ijk vs. sMesh.prs gives the i-p, j-r, k-s triplet of correspondences at this permutation; if any of these 3 matches not ranked good by matchBasesViaAllEff(), 
			//i.e. not in matchIdxs2, then ranking test fails and i pass to the next permutation w/ no further action; if they're all good, then dense sampling begins
			vector< int > tmpSafeCorrespTry;
			int idx1 = 0, idx2 = 0, nGoods = 0;
			//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
			for (int y = 0; y < 6; y++) //tmpSafeCorrespTry of size 3, hence 3*2=6
				if (y % 2 == 0)
					tmpSafeCorrespTry.push_back(tmpS[idx1]); //stable from source side (++ not here 'cos idx1 used in else part)
				else
				{
					tmpSafeCorrespTry.push_back(tmpT[ perms3[r][idx2++] ]);	//perms3[0] = {0,1,2}, perms3[1] = {1,0,2}, ..

					//y=1 means i-perm, y=3: j-perm, and y=5: k-perm (y is odd here)
					for (int i = 0; i < (int) bMesh->verts[ tmpS[idx1] ]->matchIdxs2.size(); i += 2) //+= 2 to skip redundant match distortions
						if (bMesh->verts[ tmpS[idx1] ]->matchIdxs2[i] == tmpSafeCorrespTry[y]) //[i] from sMesh (match of bMesh->verts[ tmpS[idx1] ]) compared w/ [y] from sMesh (odd idxs from tmpT)
						{
							nGoods++; //i-perm oberved in i.matchIdxs2 which means that i-perm is good; so, # of good matches increases
							break;
						}
					idx1++;
				}
			if (nGoods != 3) //ranking test fails, so this permutation not fits good w/ ijk; try the next one
				continue;
			///////////////// ranking test ends /////////////////

//cout << "a good correspondence whose constituent matches will be voted up:\n";
			for (int y = 0; y < 6; y += 2) //tmpSafeCorrespTry has bMesh entries in even idxs, and sMesh in odds
			{
//cout << (sMesh == this ? tmpSafeCorrespTry[y+1] : tmpSafeCorrespTry[y]) << "-" << (sMesh == this ? tmpSafeCorrespTry[y] : tmpSafeCorrespTry[y+1]) << "\t";
				bool exists = false;
				for (int i = 0; i < (int) bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.size(); i += 2) //2 consecutive items concern 1 match: idx and distortion
					if ((int) bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3[i] == tmpSafeCorrespTry[y+1]) //match already exists; so vote it up by 1
					{
						bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3[i+1]++;
						exists = true;
						break;
					}
				if (! exists) //this match for bMesh.tmpSafeCorrespTry[y] is new; so add it w/ vote 1
				{
					bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.push_back((float) tmpSafeCorrespTry[y+1]);
					bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.push_back(1.0f);
				}
			}
//cout << endl;
		} //end of r		
	} //end of a
	//////////////////// votes from potentialTriplets ends ////////////////////*/

	//////////////////// votes from potentialQuadruples ////////////////////
	for (int a = 0; a < (int) bMesh->potentialQuadruples.size(); a += 8)
	{
		//current 4-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
		vector< int > tmpS;
		tmpS.push_back(bMesh->potentialQuadruples[a]); //even idxs ~ bMesh
		tmpS.push_back(bMesh->potentialQuadruples[a+2]);
		tmpS.push_back(bMesh->potentialQuadruples[a+4]);
		tmpS.push_back(bMesh->potentialQuadruples[a+6]);

		//this fixed combination in tmpS will be tried w/ 4! target triplet permutations tmpT
		vector< int > tmpT;
		tmpT.push_back(bMesh->potentialQuadruples[a+1]); //odd idxs ~ sMesh
		tmpT.push_back(bMesh->potentialQuadruples[a+3]);
		tmpT.push_back(bMesh->potentialQuadruples[a+5]);
		tmpT.push_back(bMesh->potentialQuadruples[a+7]);

		//try 4!=24 reordering/permutations of tmpT w/ stable tmpS of size 4
		for (int r = 0; r < 24; r++)
		{
			///////////////// ranking test /////////////////
			//bMesh.ijkl vs. sMesh.prst gives the i-p, j-r, k-s, l-t quadruple of correspondences at this permutation; if any of these 4 matches not ranked good by matchBasesViaAllEff(), 
			//i.e. not in matchIdxs2, then ranking test fails and i pass to the next permutation w/ no further action; if they're all good, then dense sampling begins
			vector< int > tmpSafeCorrespTry;
			int idx1 = 0, idx2 = 0, nGoods = 0;
			//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
			for (int y = 0; y < 8; y++) //tmpSafeCorrespTry of size 4, hence 4*2=8
				if (y % 2 == 0)
					tmpSafeCorrespTry.push_back(tmpS[idx1]); //stable from source side (++ not here 'cos idx1 used in else part)
				else
				{
					tmpSafeCorrespTry.push_back(tmpT[ perms4[r][idx2++] ]);

					//y=1 means i-perm, y=3: j-perm, y=5: k-perm, and y=7: l-perm (y is odd here)
					for (int i = 0; i < (int) bMesh->verts[ tmpS[idx1] ]->matchIdxs2.size(); i += 2) //+= 2 to skip redundant match distortions
						if (bMesh->verts[ tmpS[idx1] ]->matchIdxs2[i] == tmpSafeCorrespTry[y]) //[i] from sMesh (match of bMesh->verts[ tmpS[idx1] ]) compared w/ [y] from sMesh (odd idxs from tmpT)
						{
							nGoods++; //i-perm oberved in i.matchIdxs2 which means that i-perm is good; so, # of good matches increases
							break;
						}
					idx1++;
				}
			if (nGoods != 4) //ranking test fails, so this permutation not fits good w/ ijkl; try the next one
				continue;
			///////////////// ranking test ends /////////////////

//cout << "a good correspondence whose constituent matches will be voted up:\n";
			for (int y = 0; y < 8; y += 2) //tmpSafeCorrespTry has bMesh entries in even idxs, and sMesh in odds
			{
//cout << (sMesh == this ? tmpSafeCorrespTry[y+1] : tmpSafeCorrespTry[y]) << "-" << (sMesh == this ? tmpSafeCorrespTry[y] : tmpSafeCorrespTry[y+1]) << "\t";
				bool exists = false;
				for (int i = 0; i < (int) bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.size(); i += 2)
					if ((int) bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3[i] == tmpSafeCorrespTry[y+1]) //match already exists; so vote it up by 1
					{
						bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3[i+1]++;
						exists = true;
						break;
					}
				if (! exists) //this match for bMesh.tmpSafeCorrespTry[y] is new; so add it w/ vote 1
				{
					bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.push_back((float) tmpSafeCorrespTry[y+1]);
					bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.push_back(1.0f);
				}
			}
//cout << endl;
		} //end of r		
	} //end of a
	//////////////////// votes from potentialQuadruples ends ////////////////////*/

	//////////////////// votes from potentialQuintuples ////////////////////
	for (int a = 0; a < (int) bMesh->potentialQuintuples.size(); a += 10)
	{
		//current 5-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
		vector< int > tmpS;
		tmpS.push_back(bMesh->potentialQuintuples[a]); //even idxs ~ bMesh
		tmpS.push_back(bMesh->potentialQuintuples[a+2]);
		tmpS.push_back(bMesh->potentialQuintuples[a+4]);
		tmpS.push_back(bMesh->potentialQuintuples[a+6]);
		tmpS.push_back(bMesh->potentialQuintuples[a+8]);

		//this fixed combination in tmpS will be tried w/ 5! target triplet permutations tmpT
		vector< int > tmpT;
		tmpT.push_back(bMesh->potentialQuintuples[a+1]); //odd idxs ~ sMesh
		tmpT.push_back(bMesh->potentialQuintuples[a+3]);
		tmpT.push_back(bMesh->potentialQuintuples[a+5]);
		tmpT.push_back(bMesh->potentialQuintuples[a+7]);
		tmpT.push_back(bMesh->potentialQuintuples[a+9]);

		//try 5!=120 reordering/permutations of tmpT w/ stable tmpS of size 5
		for (int r = 0; r < 120; r++)
		{
			///////////////// ranking test /////////////////
			//bMesh.ijklm vs. sMesh.prstu gives the i-p, j-r, k-s, l-t, m-u quintuple of correspondences at this permutation; if any of these 5 matches not ranked good by matchBasesViaAllEff(), 
			//i.e. not in matchIdxs2, then ranking test fails and i pass to the next permutation w/ no further action; if they're all good, then dense sampling begins
			vector< int > tmpSafeCorrespTry;
			int idx1 = 0, idx2 = 0, nGoods = 0;
			//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
			for (int y = 0; y < 10; y++) //tmpSafeCorrespTry of size 5, hence 5*2=10
				if (y % 2 == 0)
					tmpSafeCorrespTry.push_back(tmpS[idx1]); //stable from source side (++ not here 'cos idx1 used in else part)
				else
				{
					tmpSafeCorrespTry.push_back(tmpT[ perms5[r][idx2++] ]);

					//y=1 means i-perm, y=3: j-perm, y=5: k-perm, y=7: l-perm, and y=9: m-perm (y is odd here)
					for (int i = 0; i < (int) bMesh->verts[ tmpS[idx1] ]->matchIdxs2.size(); i += 2) //+= 2 to skip redundant match distortions
						if (bMesh->verts[ tmpS[idx1] ]->matchIdxs2[i] == tmpSafeCorrespTry[y]) //[i] from sMesh (match of bMesh->verts[ tmpS[idx1] ]) compared w/ [y] from sMesh (odd idxs from tmpT)
						{
							nGoods++; //i-perm oberved in i.matchIdxs2 which means that i-perm is good; so, # of good matches increases
							break;
						}
					idx1++;
				}
			if (nGoods != 5) //ranking test fails, so this permutation not fits good w/ ijkl; try the next one
				continue;
			///////////////// ranking test ends /////////////////

//cout << "a good correspondence whose constituent matches will be voted up:\n";
			for (int y = 0; y < 10; y += 2) //tmpSafeCorrespTry has bMesh entries in even idxs, and sMesh in odds
			{
//cout << (sMesh == this ? tmpSafeCorrespTry[y+1] : tmpSafeCorrespTry[y]) << "-" << (sMesh == this ? tmpSafeCorrespTry[y] : tmpSafeCorrespTry[y+1]) << "\t";
				bool exists = false;
				for (int i = 0; i < (int) bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.size(); i += 2)
					if ((int) bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3[i] == tmpSafeCorrespTry[y+1]) //match already exists; so vote it up by 1
					{
						bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3[i+1]++;
						exists = true;
						break;
					}
				if (! exists) //this match for bMesh.tmpSafeCorrespTry[y] is new; so add it w/ vote 1
				{
					bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.push_back((float) tmpSafeCorrespTry[y+1]);
					bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.push_back(1.0f);
				}
			}
//cout << endl;
		} //end of r		
	} //end of a
	//////////////////// votes from potentialQuintuples ends ////////////////////*/
	cout << "done!\n";

	cout << "converting votes in matchIdxs3 to final 1-to-1 correspondences..\n";
	//similar to indDistortionProcessing == 1) case but this one uses max-row-col algo instead of min-row-col 'cos the higher the votes the better the match
	//when the max of mtrx selected in i-j'th entry, then i'th row and j'th column becomes 0 to prevent many-to-1's; then the max of new mtrx is selected and it repeats
	FILE* fPtr = fopen(fName, "w");
	//# of correspondences to be written below is m for sure when additional bases not present
	fprintf(fPtr, "%d\t%f\t%f\t%f\t%f\n", m, (sMesh == this ? sMesh->minGeoDistG : bMesh->minGeoDistG), (sMesh == this ? sMesh->maxGeoDistG : bMesh->maxGeoDistG), //mesh1 sutff
											 (sMesh == this ? bMesh->minGeoDistG : sMesh->minGeoDistG), (sMesh == this ? bMesh->maxGeoDistG : sMesh->maxGeoDistG)); //mesh2 sutff		

	safeCorresp.clear(); //all line drawingss are based on safeCorresp; sphere drawing use interestingMatches
	for (int i = 0; i < n; i++)
		sMesh->verts[ sMesh->baseVerts[i] ]->processed = false; //true means matched (and therefore cannot be matched again to maintain 1-to-1)
	for (int i = 0; i < m; i++)
		bMesh->verts[ bMesh->baseVerts[i] ]->processed = false; //true means matched (and therefore cannot be matched again to maintain 1-to-1)
		
//	float finalIsoDistortion = 0.0f, maxIsoCost = -INF; //no distortion data below; if i try harder, i can find it but redundant anyway
	float leastVotedMatch = INF; //for least-voted match detection
	while (true)
	{
		//first, find the next unmatched bMesh base w/ min entry in distortion mtrx
		int idxInB = -1;
		float maxVote = -INF;
		for (int i = 0; i < m; i++)
		{
			if (bMesh->verts[ bMesh->baseVerts[i] ]->processed)// || bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3.empty())
				continue; //already matched or no vote collected for this above in matchIdxs3 (or is skipped 'cos size'll be 0 below anyway)
			for (int j = 1; j < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3.size(); j += 3) //check 1-4-7-.. idxs for vote values of the followed matchIdx
			{
				int idxInS = (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[j-1]; //-1: followed integer matchIdx value
				if (! sMesh->verts[idxInS]->processed && bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[j] > maxVote)
				{
					maxVote = bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[j];
					idxInB = bMesh->baseVerts[i];
				}
			}
		}
		if (idxInB == -1) //next unmatched bMesh base w/ max entry not found 'cos all bMesh verts have been marked as processed=true; cannot go further
		{
			//to keep file consistent, i need m matches per mapping; so, fprint the unmatched ones w/ -1 matchIdx to have m lines
			for (int i = 0; i < m; i++)
				if (! bMesh->verts[ bMesh->baseVerts[i] ]->processed)
					fprintf(fPtr, "%d\t%d\t\t%f\n", (bMesh == this ? bMesh->baseVerts[i] : -1), (bMesh == this ? -1 : bMesh->baseVerts[i]), INF); //fprint the -1 match to keep the file in the same format, that is m lines per mapping
			break;
		}
		else
		{
			//second, find the next unmatched max-vote match of idxInB in idxInB's row					
			int bestMatchIdx = -1;
			float maxVote2 = -INF;
			for (int j = 1; j < (int) bMesh->verts[idxInB]->matchIdxs3.size(); j += 3) //check 1-4-7-.. idxs for distortion values of the followed matchIdx
			{
				int idxInS = (int) bMesh->verts[idxInB]->matchIdxs3[j-1]; //-1: followed integer matchIdx value
				if (! sMesh->verts[idxInS]->processed && bMesh->verts[idxInB]->matchIdxs3[j] > maxVote2)
				{
					maxVote2 = bMesh->verts[idxInB]->matchIdxs3[j];
					bestMatchIdx = idxInS;						
				}
			}
			if (bestMatchIdx == -1) //all matchIdxs[j] above are INF, so no match for [i]
			{
				fprintf(fPtr, "%d\t%d\t\t%f\n", (bMesh == this ? idxInB : bestMatchIdx), (bMesh == this ? bestMatchIdx : idxInB), maxVote); //fprint the -1 match to keep the file in the same format, that is m lines per mapping
				continue;
			}
if (maxVote != maxVote2){cout<<"weird in voting\n";exit(0);}
			bMesh->verts[idxInB]->matchIdx = bestMatchIdx;
			//bMesh->verts[idxInB]->matchCostI = minDisto; no data; if i try harder i can find it but redundant anyway
			//finalIsoDistortion += minDisto;
			sMesh->verts[bestMatchIdx]->processed = bMesh->verts[idxInB]->processed = true; //matched; so, prevent from being matched again

			//mesh1 on even, mesh2 on odd idxs in safeCorresp[]; also, mesh1 on the left, mesh2 on the right in all prints; matchIdx is defined only for mesh1 verts
			if (bMesh == this)
			{
				safeCorresp.push_back(idxInB);
				safeCorresp.push_back(bestMatchIdx); //mesh1.matchIdx/matchCostI handled above as bMesh = this here
				verts[idxInB]->matchIdx = bestMatchIdx;
				//verts[idxInB]->matchCostI = minDisto;
				cout << idxInB << " -- " << bestMatchIdx << "\t\t" << maxVote << endl;
				fprintf(fPtr, "%d\t%d\t\t%f\n", idxInB, bestMatchIdx, maxVote);
				if (maxVote < leastVotedMatch)
				{
					bMesh->worstMatchI = idxInB; //not w.r.t isometry distortion, but w.r.t. # of votes recevied
					bMesh->verts[ bMesh->worstMatchI ]->worstIin2 = bestMatchIdx;
					leastVotedMatch = maxVote;
				}
			}
			else
			{
				safeCorresp.push_back(bestMatchIdx);
				safeCorresp.push_back(idxInB); //matchIdx & matchCostI handled above as sMesh = this here
				verts[bestMatchIdx]->matchIdx = idxInB;
				//verts[bestMatchIdx]->matchCostI = minDisto;
				cout << bestMatchIdx << " ---- " << idxInB  << "\t\t" << maxVote << endl;
				fprintf(fPtr, "%d\t%d\t\t%f\n", bestMatchIdx, idxInB, maxVote);
				if (maxVote < leastVotedMatch)
				{
					sMesh->worstMatchI = bestMatchIdx; //sMesh is this
					sMesh->verts[ sMesh->worstMatchI ]->worstIin2 = idxInB;
					leastVotedMatch = maxVote;
				}
			}			
		}
	} //end of while
	//cout << "final isometric distortion: " << (finalIsoDistortion / ((int) safeCorresp.size() / 2)) << "\ndone\n\n";
	cout << "\ndone\n\n";
	//fprintf(fPtr, "%f\n", (finalIsoDistortion / ((int) safeCorresp.size() / 2)));

	if (bMesh == this)
		cout << leastVotedMatch << " votes for " << bMesh->worstMatchI << "-" << bMesh->verts[bMesh->worstMatchI]->worstIin2 << " worst match (least-voted)\n\n"; //left ~ mesh1 (this) for prints (matchCostI may be overwritten in this many-to-1 map; so, use maxIsoCost)
	else
		cout << leastVotedMatch << " votes for " << bMesh->verts[bMesh->worstMatchI]->worstIin2 << "-" << bMesh->worstMatchI << " worst match (least-voted)\n\n"; //left ~ mesh1 (this) for prints
		
	return true; //final match ready and fprinted
}
bool Mesh::matchBasesViaVoting2(Mesh* bMesh, Mesh* sMesh, char* fName, bool oneToOneGreedy)
{
	//similar to matchBasesViaVoting() but more advanced & accurate due to the dense map stuff here; given bMesh->potentialTriplets, use the very reliable ones (which pass ranking test based on matchIdxs2 values), populate their neighborhoods w/ new evenly-spaced bases and use the distortion of that matching in initial base voting

	cout << "\nconfidence vote mtrx being computed....\n";

	int m = (int) bMesh->baseVerts.size(),
		n = (int) sMesh->baseVerts.size();
	
	//save the original sMesh coords before they are exposed to scalings below; for each fresh pair of potentialTriplets, i roll-back to original coords, and then expose the originals to float scaler
	for (int i = 0; i < (int) sMesh->verts.size(); i++)
	{
		if (! sMesh->verts[i]->coords2)
			sMesh->verts[i]->coords2 = new float[3]; //initialize coords2, if not inited yet, which holds the original coords no matter what happens to coords[]
		for (int c = 0; c < 3; c++)
			sMesh->verts[i]->coords2[c] = sMesh->verts[i]->coords[c];
	}

FILE* fPtr2 = NULL, * fPtr4 = NULL, * fPtr6 = NULL, * fPtr6_1 = NULL;
	//////////////////// votes from potentialTriplets ////////////////////
//float maxDiff = -INF, minDenseDistortion = INF, maxDenseDistortion = -INF; //max difference b/w # of regional bases, min and max distortions b/w regional bases
	int denseDistoComputation = 8; //1: averages the blossom costs[][] mtrx entries of the selected matches (fast, not so distinguishing 'cos costs[][] relies on 3 trusted corresps from potentialTriplets only),
								   //2: extreme match i-p traverses^^ 40x40 blossom dense match to learn i.confidence; or avg distortion of 40x40 blossom match (that checks normalized geodesic distances b/w all pairs) is used as i/j/k.confidence (sameConfToAll=true case)
								   //3, 4: worst distortions instead of the averages in 1, 2, respectively
								   //5: to compute i.confidence, 40-size geodesic feauture vector is extracted for each region bases and differences b/w mutually closest ones are averaged
								   //6: worst distortion instead of the average in 5
								   //7: blossom 40x40 match found based on 3 forced extreme matches i-p, j-r, k-s; then, i.confidence is inversely related to normalized geo(p, blossomMatch(i))
								   //8: weighted combination of 7 and (denseDistoComputation = 2 & sameConfidenceToAll = true)
								   //9: weighted combination of 7 and (denseDistoComputation = 5 & sameConfidenceToAll = true)
								   //^^ when sameConfidenceToAll = false, 40 dijkstra not needed 'cos i can traverse only 3 extremes over 39 regional bases; currently not implemeted that efficient way 'cos whole mapping distortion can't be computed that way
//cool setting in use: denseDistoComputation = 8; before finalizing my decision, i found 7 or (denseDistoComputation = 2 or 5 & sameConfidenceToAll = false) cool as well
//more accurate linear kernel (conf = 1 - distortion) is used for denseDistoComputation = 8 instead of exponential kernel (conf = e^-distortion) which expands intervals unevenly; no!!!!!!! uneven expansion actually works 'cos distortion7 tends to be in [0,1] and distortion2 in [0,0.36]; [0,0.36] distortion --> [1,0.64] confidence w/
//both linear and exponential kernel; [0,1] distortion --> [1,0] confidence w/ linear kernel and --> [1,0.4] w/ exponential kernel which is more compatible w/ [1,0.64]; so, use exponential kernel on both distortion7 and 2; linear kernel can be used when mapping confidences to distortions for blossom matching of conquering step at bottom
//another idea is to map [0,1] distortion --> [1,0] confidence w/ linear kernel and [0,0.36] distortion ==> [0,1] distortion --> [1,0] confidence w/ linear kernel but [0,0.36] distortion ==> [0,1] distortion is a bad normalization as it forces 1 of 3 matches to have 1 distortion, which sucks when all 3 generating matches are good
	float w7 = /*0.25f*/weight7, w25 = 1.0f - w7; //denseDistoComputation == 8 or 9 mode uses v.confidence = w7 * disto7-based-confidence + w25 * disto2 (or 5)-based-confidence (disto2 if denseDistoComputation=8, 5 if =9)

	//weight7 is always 0 for paper results which makes me use w25 with full weight of 1; w7 is just one of other confidence computation tactics that i've tried during the development of the final algorithm; you may understand it (and all others) from the comments below

	bool sameConfidenceToAll = false; //true: all matches i-p, j-r, k-s from triplet ijk vs. prs receive the same confidence, false: each receives its own confidence based on its individual distortion on the denseDistortion
	if (denseDistoComputation == 7)
		sameConfidenceToAll = false; //cannot be true in this mode as i'll have match-specific distortion measures for 3 extreme matches only
	else if (denseDistoComputation == 8 || denseDistoComputation == 9)
		sameConfidenceToAll = false; //don't want this true in these modes 'cos false performance is better on denseDistoComputation = 2 or 5 modes
	//denseDistoComputation == 5 || denseDistoComputation == 6) //geodesic vector comparison based distortion has cubic complexity but not change run time dramatically thanks to n ~ 40 forced by area-based r selection
	for (int a = 0; a < (int) bMesh->potentialTriplets.size(); a += 6)
	{
if (a % 800 == 0) cout << a/6 << " / " << (int) bMesh->potentialTriplets.size()/6 << "\n";
		//current 3-combination is copied from appropriate slots of baseVerts to tmpS (for tmp source)
		vector< int > tmpS;
		tmpS.push_back(bMesh->potentialTriplets[a]); //even idxs ~ bMesh
		tmpS.push_back(bMesh->potentialTriplets[a+2]);
		tmpS.push_back(bMesh->potentialTriplets[a+4]);

		//this fixed combination in tmpS will be tried w/ 3! target triplet permutations tmpT
		vector< int > tmpT;
		tmpT.push_back(bMesh->potentialTriplets[a+1]); //odd idxs ~ sMesh
		tmpT.push_back(bMesh->potentialTriplets[a+3]);
		tmpT.push_back(bMesh->potentialTriplets[a+5]);

//cout << "\ntriplet: " << tmpS[0] << "-" << tmpS[1] << "-" << tmpS[2] << "\t\t" << tmpT[0] << "-" << tmpT[1] << "-" << tmpT[2] << "\n";

		//try 3!=6 reordering/permutations of tmpT w/ stable tmpS of size 3
		for (int r = 0; r < 6; r++)
		{
			///////////////// ranking test /////////////////
			//bMesh.ijk vs. sMesh.prs gives the i-p, j-r, k-s triplet of correspondences at this permutation; if any of these 3 matches not ranked good by matchBasesViaAllEff(), 
			//i.e. not in matchIdxs2, then ranking test fails and i pass to the next permutation w/ no further action; if they're all good, then dense sampling begins
			vector< int > tmpSafeCorrespTry;
			int idx1 = 0, idx2 = 0, nGoods = 0;
			//stable triple of tmpS goes to even indices: 0 & 2, and permuted tmpT goes to odd slots: 1 & 3
			for (int y = 0; y < 6; y++) //tmpSafeCorrespTry of size 3, hence 3*2=6
				if (y % 2 == 0)
					tmpSafeCorrespTry.push_back(tmpS[idx1]); //stable from source side (++ not here 'cos idx1 used in else part)
				else
				{
					tmpSafeCorrespTry.push_back(tmpT[ perms3[r][idx2++] ]);	//perms3[0] = {0,1,2}, perms3[1] = {1,0,2}, ..

					//y=1 means i-perm, y=3: j-perm, and y=5: k-perm (y is odd here)
					for (int i = 0; i < (int) bMesh->verts[ tmpS[idx1] ]->matchIdxs2.size(); i += 2) //+= 2 to skip match distortions
						if (bMesh->verts[ tmpS[idx1] ]->matchIdxs2[i] == tmpSafeCorrespTry[y]) //[i] from sMesh (match of bMesh->verts[ tmpS[idx1] ]) compared w/ [y] from sMesh (odd idxs from tmpT)
						{
							nGoods++; //i-perm oberved in i.matchIdxs2 which means that i-perm is good; so, # of good matches increases
							break;
						}
					idx1++;
				}
			if (nGoods != 3) //ranking test fails, so this permutation not fits good w/ ijk; try the next one
				continue;
			///////////////// ranking test ends /////////////////


//if(tmpSafeCorrespTry[0]==11 && tmpSafeCorrespTry[2]==1578 && tmpSafeCorrespTry[4]==3398)
//cout << (sMesh == this ? tmpSafeCorrespTry[1] : tmpSafeCorrespTry[0]) << "-" << (sMesh == this ? tmpSafeCorrespTry[0] : tmpSafeCorrespTry[1]) << "\t" <<
//(sMesh == this ? tmpSafeCorrespTry[3] : tmpSafeCorrespTry[2]) << "-" << (sMesh == this ? tmpSafeCorrespTry[2] : tmpSafeCorrespTry[3]) << "\t" << (sMesh == this ? tmpSafeCorrespTry[5] : tmpSafeCorrespTry[4]) << "-" << (sMesh == this ? tmpSafeCorrespTry[4] : tmpSafeCorrespTry[5]) << "\n";
//bool fprint = false; if(tmpSafeCorrespTry[0]==607 && tmpSafeCorrespTry[1]==506 && tmpSafeCorrespTry[2]==1627 && tmpSafeCorrespTry[3]==1689 && tmpSafeCorrespTry[4]==2505 && tmpSafeCorrespTry[5]==2858)fprint=true;
//fprint = false;//cancel all fprints below no matter what said just-above
//bool fprint=false;
/*if(fprint) { cout << "a good correspondence whose constituent matches will be voted up:\n" << (sMesh == this ? tmpSafeCorrespTry[1] : tmpSafeCorrespTry[0]) << "-" << (sMesh == this ? tmpSafeCorrespTry[0] : tmpSafeCorrespTry[1]) << "\t" <<
				     (sMesh == this ? tmpSafeCorrespTry[3] : tmpSafeCorrespTry[2]) << "-" << (sMesh == this ? tmpSafeCorrespTry[2] : tmpSafeCorrespTry[3]) << "\t" << (sMesh == this ? tmpSafeCorrespTry[5] : tmpSafeCorrespTry[4]) << "-" << (sMesh == this ? tmpSafeCorrespTry[4] : tmpSafeCorrespTry[5]) << "\n";
fPtr2 = fopen("regVerts before and after pruning.dat", "w");fPtr4 = fopen("regBaseVerts.dat", "w");fPtr6 = fopen("regBaseVerts matches, extremes forced to remain unchanged.dat", "w"); fPtr6_1 = fopen("regBaseVerts matches, extreme matches may change.dat", "w");}//*/

			//vote up 3 constituent matches of this cool corresp w/ 1 - distortion (of the dense mapping to be solved below)
			//tmpSafeCorrespTry has i-p ([0]-[1]), j-r ([2]-[3]), k-s ([4]-[5]) constituent matches of the triplet pair ijk vs. prs

			//////////////////////// regional verts ////////////////////////
			//decide the vertices (regionVertsB/S) that fall inside ijk extremes; to get that region, look each vert and select the ones whose dist to both i, j, and k is less than maxExtremeDist
			vector< int > regionVertsB, regionVertsS;
			float maxExtremeDistB = (bMesh->verts[ tmpSafeCorrespTry[0] ]->dSpanningUnn[ tmpSafeCorrespTry[2] ] > bMesh->verts[ tmpSafeCorrespTry[0] ]->dSpanningUnn[ tmpSafeCorrespTry[4] ] ? bMesh->verts[ tmpSafeCorrespTry[0] ]->dSpanningUnn[ tmpSafeCorrespTry[2] ] : bMesh->verts[ tmpSafeCorrespTry[0] ]->dSpanningUnn[ tmpSafeCorrespTry[4] ]);
			maxExtremeDistB = (bMesh->verts[ tmpSafeCorrespTry[2] ]->dSpanningUnn[ tmpSafeCorrespTry[4] ] > maxExtremeDistB ? bMesh->verts[ tmpSafeCorrespTry[2] ]->dSpanningUnn[ tmpSafeCorrespTry[4] ] : maxExtremeDistB);
			for (int v = 0; v < (int) bMesh->verts.size(); v++)
				bMesh->verts[v]->processed = false; //true means this is a region vertex; a true may later be false due to the extremes other than i, j, k
			for (int v = 0; v < (int) sMesh->verts.size(); v++)
				sMesh->verts[v]->processed = false; //true means this is a region vertex; a true may later be false due to the extremes other than p, r, s
			//add ijk and prs to first 3 slots of regionVertsB and regionVertsS which guarantees their base membership 'cos computeRegionBaseVerts() traverses regionVertsB/S from left to right
			for (int y = 0; y < 6; y += 2)
			{
				regionVertsB.push_back(tmpSafeCorrespTry[y]);
				regionVertsS.push_back(tmpSafeCorrespTry[y+1]);
				bMesh->verts[ tmpSafeCorrespTry[y] ]->processed = sMesh->verts[ tmpSafeCorrespTry[y+1] ]->processed = true;
//if(fprint) fprintf(fPtr2, "%d\n", tmpSafeCorrespTry[y]);
			}
			for (int v = 0; v < (int) bMesh->verts.size(); v++)
				if (! bMesh->verts[v]->processed && bMesh->verts[ tmpSafeCorrespTry[0] ]->dSpanningUnn[v] <= maxExtremeDistB && bMesh->verts[ tmpSafeCorrespTry[2] ]->dSpanningUnn[v] <= maxExtremeDistB && bMesh->verts[ tmpSafeCorrespTry[4] ]->dSpanningUnn[v] <= maxExtremeDistB) //!processed to prevent duplicate insertion of tmpSafeCorrespTry[0-2-4]
				{
					regionVertsB.push_back(v);
					bMesh->verts[v]->processed = true;
//if(fprint) fprintf(fPtr2, "%d\n", v);
				}
			//same stuff for sMesh
//if(fprint)fprintf(fPtr2, "%d\n%d\n%d\n%d\n", -999999, regionVertsS[0], regionVertsS[1], regionVertsS[2]); //these 3 not push_back'ed below 'cos it's done above; just fprint them
			float maxExtremeDistS = (sMesh->verts[ tmpSafeCorrespTry[1] ]->dSpanningUnn[ tmpSafeCorrespTry[3] ] > sMesh->verts[ tmpSafeCorrespTry[1] ]->dSpanningUnn[ tmpSafeCorrespTry[5] ] ? sMesh->verts[ tmpSafeCorrespTry[1] ]->dSpanningUnn[ tmpSafeCorrespTry[3] ] : sMesh->verts[ tmpSafeCorrespTry[1] ]->dSpanningUnn[ tmpSafeCorrespTry[5] ]);
			maxExtremeDistS = (sMesh->verts[ tmpSafeCorrespTry[3] ]->dSpanningUnn[ tmpSafeCorrespTry[5] ] > maxExtremeDistS ? sMesh->verts[ tmpSafeCorrespTry[3] ]->dSpanningUnn[ tmpSafeCorrespTry[5] ] : maxExtremeDistS);
			for (int v = 0; v < (int) sMesh->verts.size(); v++)
				if (! sMesh->verts[v]->processed && sMesh->verts[ tmpSafeCorrespTry[1] ]->dSpanningUnn[v] <= maxExtremeDistS && sMesh->verts[ tmpSafeCorrespTry[3] ]->dSpanningUnn[v] <= maxExtremeDistS && sMesh->verts[ tmpSafeCorrespTry[5] ]->dSpanningUnn[v] <= maxExtremeDistS)
				{
					regionVertsS.push_back(v);
					sMesh->verts[v]->processed = true;
//if(fprint)fprintf(fPtr2, "%d\n", v);
				}
//if(fprint){fprintf(fPtr2, "%d\n", -888888);cout<<regionVertsB.size()<<" np " << regionVertsS.size() << "\n";}

			//regionVerts above is too large; prune it by going through all extreme bases except the 3 that decides the regions, i.e. ijk and prs; for each extreme e, find the closest decider extreme d and say dist b/w them is geo(d,e) = h; then all region verts that are at distance h/2 or less to e are excluded, i.e. processed=false
			for (int e = 0; e < m; e++)
				if (bMesh->verts[ bMesh->baseVerts[e] ]->lMaxMinMu == 1 && bMesh->baseVerts[e] != tmpSafeCorrespTry[0] && bMesh->baseVerts[e] != tmpSafeCorrespTry[2] && bMesh->baseVerts[e] != tmpSafeCorrespTry[4]) //all but ijk ([0][2][4]), also use only local maximas (lMaxMinMu=1), i.e. ignore minimas which are lMaxMinMu=2
				{
					//find the closest decider extreme d to bMesh->baseVerts[e]; what i really need is geo(d,e) / 2 actually; so, don't keep d explicitly; just use lengths
					float h = (bMesh->verts[ tmpSafeCorrespTry[0] ]->dSpanningUnn[ bMesh->baseVerts[e] ] < bMesh->verts[ tmpSafeCorrespTry[2] ]->dSpanningUnn[ bMesh->baseVerts[e] ] ? bMesh->verts[ tmpSafeCorrespTry[0] ]->dSpanningUnn[ bMesh->baseVerts[e] ] : bMesh->verts[ tmpSafeCorrespTry[2] ]->dSpanningUnn[ bMesh->baseVerts[e] ]);
					h = (bMesh->verts[ tmpSafeCorrespTry[4] ]->dSpanningUnn[ bMesh->baseVerts[e] ] < h ? bMesh->verts[ tmpSafeCorrespTry[4] ]->dSpanningUnn[ bMesh->baseVerts[e] ] : h);
					h /= 2;
					//all region verts that are at distance h (h=h/2 made above) or less to e are excluded, i.e. processed=false
					for (int rv = 0; rv < (int) regionVertsB.size(); rv++)
						if (bMesh->verts[ bMesh->baseVerts[e] ]->dSpanningUnn[ regionVertsB[rv] ] <= h)
							bMesh->verts[ regionVertsB[rv] ]->processed = false; //marked to be excluded
				}
			//same exclusion stuff for sMesh
			for (int e = 0; e < n; e++)
				if (sMesh->verts[ sMesh->baseVerts[e] ]->lMaxMinMu == 1 && sMesh->baseVerts[e] != tmpSafeCorrespTry[1] && sMesh->baseVerts[e] != tmpSafeCorrespTry[3] && sMesh->baseVerts[e] != tmpSafeCorrespTry[5]) //all but prs ([1][3][5]), also use only local maximas (lMaxMinMu=1), i.e. ignore minimas which are lMaxMinMu=2
				{
					float h = (sMesh->verts[ tmpSafeCorrespTry[1] ]->dSpanningUnn[ sMesh->baseVerts[e] ] < sMesh->verts[ tmpSafeCorrespTry[3] ]->dSpanningUnn[ sMesh->baseVerts[e] ] ? sMesh->verts[ tmpSafeCorrespTry[1] ]->dSpanningUnn[ sMesh->baseVerts[e] ] : sMesh->verts[ tmpSafeCorrespTry[3] ]->dSpanningUnn[ sMesh->baseVerts[e] ]);
					h = (sMesh->verts[ tmpSafeCorrespTry[5] ]->dSpanningUnn[ sMesh->baseVerts[e] ] < h ? sMesh->verts[ tmpSafeCorrespTry[5] ]->dSpanningUnn[ sMesh->baseVerts[e] ] : h);
					h /= 2;				
					for (int rv = 0; rv < (int) regionVertsS.size(); rv++)
						if (sMesh->verts[ sMesh->baseVerts[e] ]->dSpanningUnn[ regionVertsS[rv] ] <= h)
							sMesh->verts[ regionVertsS[rv] ]->processed = false; //marked to be excluded
				}
			//since excluding each processed=false marked region verts takes many erase() calls of o(n) time, accumulate all processed=true remained region verts in tmp and then set regionVertsB/S=tmp
			vector< int > tmp;
			for (int rv = 0; rv < (int) regionVertsB.size(); rv++)
				if (bMesh->verts[ regionVertsB[rv] ]->processed)
				{
					tmp.push_back(regionVertsB[rv]);
//if(fprint) fprintf(fPtr2, "%d\n", regionVertsB[rv]);
				}
			regionVertsB = tmp;
			//same stuff for sMesh
//if(fprint) fprintf(fPtr2, "%d\n", -888888);
			tmp.clear();
			for (int rv = 0; rv < (int) regionVertsS.size(); rv++)
				if (sMesh->verts[ regionVertsS[rv] ]->processed)
				{
					tmp.push_back(regionVertsS[rv]);
//if(fprint) fprintf(fPtr2, "%d\n", regionVertsS[rv]);
				}
			regionVertsS = tmp;
			//////////////////////// regional verts ends ////////////////////////

			//////////////////////// regional base verts ////////////////////////
			//decide the evenly-spaced base vertices that fall inside regionVertsB/S above, i.e. select a subset from regionVertsB/S as regionBaseVertsB/S
			//scale sMesh using scaler to bring both meshes to the same scale; evenly-space sample bMesh w/ rb = 4*avgEdgeLen, and then use the same rb to evenly-space sMesh; this makes similar compatible distances b/w bases on bMesh and sMesh
			float scaler = ((bMesh->verts[ tmpSafeCorrespTry[0] ]->dSpanningUnn[ tmpSafeCorrespTry[2] ] / sMesh->verts[ tmpSafeCorrespTry[1] ]->dSpanningUnn[ tmpSafeCorrespTry[3] ]) + //= (geo(i,j) / geo(p,r) +
							(bMesh->verts[ tmpSafeCorrespTry[0] ]->dSpanningUnn[ tmpSafeCorrespTry[4] ] / sMesh->verts[ tmpSafeCorrespTry[1] ]->dSpanningUnn[ tmpSafeCorrespTry[5] ]) + //geo(i,k) / geo(p,s) +
							(bMesh->verts[ tmpSafeCorrespTry[2] ]->dSpanningUnn[ tmpSafeCorrespTry[4] ] / sMesh->verts[ tmpSafeCorrespTry[3] ]->dSpanningUnn[ tmpSafeCorrespTry[5] ])) / 3.0f, //= geo(j,k) / geo(r,s)) / 3
				  //sampling distance, i.e. min dist b/w 2 evenly-spaced regional base vertices
				  rb = //4 * bMesh->avgEdgeLen; //is too simple and gives the same rb for all triplets; this may make ~50x50 matching for one triplet, and ~20x20 for another; distortions of these mappings should be compatible anyway 'cos avg is taken but nevertheless 20x20 may be more advantageous 'cos it's less likely to hit very bad matches
					   0.284f * sqrt(bMesh->computeRegionArea(regionVertsB) / PI), //40 region bases: A = 40 pi r^2 ==> r = 0.1 sqrt(A/pi) gives 40 on a disk/patch; *~2 (=1.8) to make 40 on a closed surface (as 2 same patches connected to make the closed surface) (.284=.158*1.8)
				  nMaxS = 200;//120		blossom's will be done if # of regional bases not differ by too much
			if (! dense40) //observed no execution time difference b/w 40 and 120 bases on GORILLA_GIRL runs which has low-reso and high-reso meshes
			{
				rb = 0.171f * sqrt(bMesh->computeRegionArea(regionVertsB) / PI); //120 region bases [don't change 200 below 'cos then it gets too slow as 200+ x 200+ blossom takes ~1 sec] first term = 0.18f ~ 100 region bases, .201f ~ 80 region bases, .44f ~ 20 region bases
				nMaxS = 200;
			}//*/
			
			//with this area-dependent rb, regionVertsB has ~40 bases on it; since sMesh is brought to the same scale as bMesh below, the same rb makes ~40 on regionVertsS too; note that, bMesh coords never scaled, so no need to re-compute tri.areas for each new computeRegionArea()
			//roll-back to the original sMesh coords for this fresh pair of potentialTriplets ijk-prs before they are exposed to scalings below
			for (int i = 0; i < (int) sMesh->verts.size(); i++)
			{
				for (int c = 0; c < 3; c++)
					sMesh->verts[i]->coords[c] = sMesh->verts[i]->coords2[c]; //took 10 hrs!!!! to see that coords2 must be inited w/ new and this explicit copying must be done instead of coords = coords2 assignment
				//scaling of the existing dSpanningUnn[] is not enough 'cos i'll sample bases on regionVertsS for which new scaled coords needed to be compatible w/ rb
				sMesh->verts[i]->coords[0] *= scaler;
				sMesh->verts[i]->coords[1] *= scaler;
				sMesh->verts[i]->coords[2] *= scaler;
				sMesh->verts[i]->baseCandidate = false; //computeRegionBaseVerts() assumes all verts have baseCandidate = false initially; do the sMesh part here
			}
			for (int i = 0; i < (int) bMesh->verts.size(); i++)
				bMesh->verts[i]->baseCandidate = false; //computeRegionBaseVerts() assumes all verts have baseCandidate = false initially; do the bMesh part here
			//edge lengths also need to be updated since all endpnts have been scaled above; these lengths to be used for shortcut dijkstra's below that select regionBaseVerts
			for (int e = 0; e < (int) sMesh->edges.size(); e++)
				sMesh->edges[e]->length = distanceBetween(sMesh->verts[sMesh->edges[e]->v1i]->coords, sMesh->verts[sMesh->edges[e]->v2i]->coords);
			for (int e = 0; e < (int) sMesh->sedges.size(); e++)
				sMesh->sedges[e]->length = distanceBetween(sMesh->verts[sMesh->sedges[e]->v1i]->coords, sMesh->verts[sMesh->sedges[e]->v2i]->coords);
			//sMesh->updateNormals(); not necessary at all

//cout << "a\t";
			vector< int > regionBaseVertsB = bMesh->computeRegionBaseVerts(rb, regionVertsB, denseDistoComputation),
						  regionBaseVertsS = sMesh->computeRegionBaseVerts(rb, regionVertsS, denseDistoComputation); //use the same radius rb; has weird unexpected behavior; so, copy fnctn contents here for both bMesh and then sMesh
//if(tmpSafeCorrespTry[4]==3297 && tmpSafeCorrespTry[5]==2802) cout << tmpSafeCorrespTry[0] << " " << tmpSafeCorrespTry[1] << "\t" << tmpSafeCorrespTry[2] << " " << tmpSafeCorrespTry[3] << "\t" << tmpSafeCorrespTry[4] << " " << tmpSafeCorrespTry[5] <<endl;
/*if(tmpSafeCorrespTry[0]==37 && tmpSafeCorrespTry[1]==142 && tmpSafeCorrespTry[2]==2541 && tmpSafeCorrespTry[3]==3252 && tmpSafeCorrespTry[4]==3297 && tmpSafeCorrespTry[5]==2802)
{fPtr4=fopen("dbg4_1.dat","w");
cout << regionBaseVertsB.size() << "\t" << regionBaseVertsS.size() << endl;
for (int rbvb = 0; rbvb < (int) regionBaseVertsB.size(); rbvb++) fprintf(fPtr4, "%d\n", regionBaseVertsB[rbvb]); fprintf(fPtr4, "%d\n", -7777);for (int rbvs = 0; rbvs < (int) regionBaseVertsS.size(); rbvs++) fprintf(fPtr4, "%d\n", regionBaseVertsS[rbvs]);}//*/
//if (fprint) { for (int rbvb = 0; rbvb < (int) regionBaseVertsB.size(); rbvb++) fprintf(fPtr4, "%d\n", regionBaseVertsB[rbvb]); fprintf(fPtr4, "%d\n", -7777);for (int rbvs = 0; rbvs < (int) regionBaseVertsS.size(); rbvs++) fprintf(fPtr4, "%d\n", regionBaseVertsS[rbvs]);}
//cout << regionBaseVertsB.size() << "\t" << regionBaseVertsS.size() << endl;
			//////////////////////// regional base verts ends ////////////////////////

			//////////////////////// dense base verts matching ////////////////////////
			float denseDistortion; //in case sameConfidenceToAll = false
			if ((int) regionBaseVertsS.size() < nMaxS) //rb makes ~40 bases on regionVertsB for sure; same rb is expected to make ~40 on regionVertsS if two regions are compatible, i.e. generating triplets are good matches; regionBaseVertsS.size() >= 200 (nMaxS) means there's a huge difference b/w regions so don't waste time to produce confidence values for generating (bad) matches; blossom on 300x300 takes ~1 sec so lots of 1 secs make me loose big time; simply use 0 confidences for a huge difference case (in else part)
			{
				//evaluate dense map b/w regionBaseVertsB & regionBaseVertsS to use it as the confidence of 3 constituent matches i-p, j-r, k-s; below heavily adapted from blossomOnBases()				

				//bMesh.dSpanningUnn[] not affected by scaler which is applied to sMesh only; so, scale the original sMesh.dSpanningUnn's to sMesh.dSpanningUnnSS's to be used below			
				for (int y = 1; y < 6; y += 2)
				{
					if (! sMesh->verts[ tmpSafeCorrespTry[y] ]->dSpanningUnnSS)
						sMesh->verts[ tmpSafeCorrespTry[y] ]->dSpanningUnnSS = new float[ (int) sMesh->verts.size() ]; //init only once
					for (int v = 0; v < (int) sMesh->verts.size(); v++) //dists from [y] to all other sMesh.verts are scaled to the same scale as bMesh; these dists from [y] to regionBaseVertsS will be compared w/ dists from [y-1] to regionBaseVertsB below
						sMesh->verts[ tmpSafeCorrespTry[y] ]->dSpanningUnnSS[v] = sMesh->verts[ tmpSafeCorrespTry[y] ]->dSpanningUnn[v] * scaler;
				}
				maxExtremeDistS *= scaler; //this is compared w/ original/unscaled sMesh.dSpanningUnn's above; but below, it'll be used w/ new/scaled sMesh.dSpanningUnnSS's, so, apply scaler to this as well

				//nRows x nRows mtrx (nRows = max(# regionBaseVertsB, # regionBaseVertsS)) will be created w/ vertex-based distortion values decided by tmpSafeCorrespTry; 1-to-1 blossom match of that mtrx is then evaluated
				int n1 = (int) regionBaseVertsB.size(), n2 = (int) regionBaseVertsS.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], ** costs7; //last one inited only for denseDistoComputation == 8 or 9 modes; it's the same as costs[][] except -INF encouragement is not done for extreme matches (therefore name costs7, it holds denseDistoComputation=7 part of the combined distortions to be used by denseDistoComputation=8 or 9 mode)
				if (denseDistoComputation == 8 || denseDistoComputation == 9)
					costs7 = new float*[nRows];
				float discourage = INF; //1.0f no; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; w/ unnormalized geodesics, costs[][] not necessarily in [0,1]
				//stuff for blossom
				int nVertNum = nRows*2, nEdgeNum = nRows*nRows, e, * edges = new int[2*nEdgeNum], * matches = new int[nVertNum], * matches7; //see matches[] as lines of matches where 2 consecutive items make a matching line, e.g. 369 694
				double* weightsD = new double[nEdgeNum], * weightsD7; //last one is same as weightsD except it's filled w/ costs7[][]
				if (denseDistoComputation == 8 || denseDistoComputation == 9)
				{
					matches7 = new int[nVertNum]; //same as matches except this keeps blossom match based on costs7
					weightsD7 = 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
					if (denseDistoComputation == 8 || denseDistoComputation == 9)
						costs7[row] = new float[nCols];
					vIdx1[row] = regionBaseVertsB[i]; //= bMesh base
					int col = 0;
					for (int j = 0; j < n2; j++)
						vIdx2[col++] = regionBaseVertsS[j]; //sMesh base
					while (col < nCols) //fill the remaining virtual columns w/ infinitive costs
					{
						costs[row][col++] = discourage;
						if (denseDistoComputation == 8 || denseDistoComputation == 9)
							costs7[row][col-1] = discourage; //-1 to clear col++ effect
					}
					row++;
				}
				while (row < nRows) //fill the remaining virtual rows w/ infinitive costs
				{
					costs[row] = new float[nCols]; //nRows = nCols
					if (denseDistoComputation == 8 || denseDistoComputation == 9)
						costs7[row] = new float[nCols];
					for (int col = 0; col < nCols; col++)
					{
						costs[row][col] = discourage;
						if (denseDistoComputation == 8 || denseDistoComputation == 9)
							costs7[row][col] = discourage;
					}
					row++;
				}

				//create the blossom matching matchList to be evaluated (for return) using the traversalListTry
				vector< int > matchList; //blossom matching to be evaluated
				row = 0;
				for (int i = 0; i < n1; i++)
				{
					int base1 = regionBaseVertsB[i];
					bMesh->verts[base1]->matchIdxs.clear(); //start a fresh matchIdxs list to store the blossom costs to be used in denseDistortion computation below
					//find the unmatched mesh2.base[j] as a match for base1; to do so globally, use blossom matching
					int col = 0;
					for (int j = 0; j < n2; j++)
					{
						int base2 = regionBaseVertsS[j];
						bool extremeMatch = false; //true if base1-base2 is a generating extreme match i-p or j-r or k-s
						if (denseDistoComputation != 7) //extremeMatch remains false for sure in this mode 7 which is mandatory 'cos i want extremes to naturally match to their best candidate by gloabal blossom so that i can use geo(p, blossomMatch(i)) for i-p extremem match
							for (int y = 0; y < 6; y += 2)
								if (tmpSafeCorrespTry[y] == base1 && tmpSafeCorrespTry[y+1] == base2)
									extremeMatch = true;

						//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])
						int nAdds = 0;
						for (int ss = 0; ss < 3; ss++) //traversal list is the potential triplet, so of size 3 for sure
						{
							int s1 = tmpSafeCorrespTry[2*ss], s2 = tmpSafeCorrespTry[2*ss + 1];
							if (s1 == base1)
								continue;

							//if bvs vs. bvb is a good correspondence, then geoDist(bvs, s1) = geoDist(bvb, s2) for all s1 & s2 in tmpSafeCorrespTry
							float t1s1 = bMesh->verts[s1]->dSpanningUnn[base1] / maxExtremeDistB, t2s2 = sMesh->verts[s2]->dSpanningUnnSS[base2] / maxExtremeDistS;
							vertexBasedIso += fabs(t1s1 - t2s2); //normalized difference here is small if tbv1 vs. tbv2 is a good correspondence
							nAdds++;
						}
if (nAdds <= 0) {cout << "can't traverse 0-size\n";exit(0);}
						vertexBasedIso /= nAdds;
						costs[row][col++] = (! extremeMatch ? vertexBasedIso : -INF); //-INF cost to encourage the selection of this extreme match base1-base2 (using 0 cost not always guarantee selection, so use -INF)
						if (denseDistoComputation == 8 || denseDistoComputation == 9)
							costs7[row][col-1] = vertexBasedIso; //no -INF encouragement on extremeMatches; -1 to clear col++ effect
						bMesh->verts[base1]->matchIdxs.push_back((float) base2);
						bMesh->verts[base1]->matchIdxs.push_back(vertexBasedIso); //regardless of extremeMatch, use the actual distortion in case i use it in confidence later (if (denseDistoComputation == 1 || denseDistoComputation == 3) block below)
					} //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];
						if (denseDistoComputation == 8 || denseDistoComputation == 9)
							weightsD7[e-1] = (double) costs7[i][j]; //-1 to clear e++ effect
					}
				//blossom output
				Blossom* blossom = new Blossom();
				blossom->match(nVertNum, nEdgeNum, edges, weightsD, matches); //matches updated by call-by-ref
				delete blossom;
				if (denseDistoComputation == 8 || denseDistoComputation == 9)
				{
					Blossom* blossom2 = new Blossom(); //same as blossom except it works on weightsD7 which is based on costs7
					blossom2->match(nVertNum, nEdgeNum, edges, weightsD7, matches7); //matches7 updated by call-by-ref
					delete blossom2;
				}

//if(fprint) fprintf(fPtr6, "%d vs. %d\n", n1, n2);
//if(tmpSafeCorrespTry[0]==37 && tmpSafeCorrespTry[1]==142 && tmpSafeCorrespTry[2]==2541 && tmpSafeCorrespTry[3]==3252 && tmpSafeCorrespTry[4]==3297 && tmpSafeCorrespTry[5]==2802)
//{ fPtr6 = fopen("dbg4_3.dat", "w"); fprintf(fPtr6, "%d vs. %d\n", n1, n2); }

				//transform data in matches[] to matchList.push_back's consistently
				float denseDistortion1 = 0.0f, denseDistortion2 = 0.0f, maxDenseDisto = -INF;
				int nExtremeMatches = 0; //confidence vote is applied if all 3 extremes are matched after this blossom match of compatible regions; nExtremeMatches < 3 means, regions not that compatible and therefore some extremes not even matched
				if (denseDistoComputation == 7)
					//init i/j/k.confidence to 0 so that in case they've not been matched by blossom, their confidence remains as discouraging 0, not an arbitrary number from prev iterations
					bMesh->verts[ tmpSafeCorrespTry[0] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[2] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[4] ]->confidence = 0.0f; //will be updated in loop just-below if blossom has found a match for these extremes
				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)
						{
//if(fprint) fprintf(fPtr6, "%d\t%d\n", vIdx1[m1], mi); //matches[] that forces extreme matches remain unchanged by -inf encouragements

							//the correspondences
							if (denseDistoComputation == 1 || denseDistoComputation == 3)
							{
								//learn the individual distortion used in blossom mtrx to use it in denseDistortion computation
								float vertexBasedIso = -1.0f;
								for (int j = 0; j < (int) bMesh->verts[ vIdx1[m1] ]->matchIdxs.size(); j += 2)
									if (bMesh->verts[ vIdx1[m1] ]->matchIdxs[j] == mi)
									{
										vertexBasedIso = bMesh->verts[ vIdx1[m1] ]->matchIdxs[j+1];
										if (! sameConfidenceToAll)
											bMesh->verts[ vIdx1[m1] ]->confidence = pow(EXP, -vertexBasedIso); //individual distortion of this bMesh region base is stored as its individual confidence: the more it is the less the confidence (redundant v.confidences set as well)
										break;
									}
if (vertexBasedIso == -1.0f){cout<<"\n\n\n\n\nsth wrong\n";exit(0);}						
								denseDistortion1 += vertexBasedIso; //cost used in blossom mtrx (probably the min of all columns in vIdx1[m1]'s row but since blossom makes global match, it doesn't always select the min)
								if (vertexBasedIso > maxDenseDisto)
									maxDenseDisto = vertexBasedIso;
							}
							else if (denseDistoComputation == 7)
							{
								//current blossom match is vIdx1[m1]-mi							
								for (int y = 0; y < 6; y += 2)
									if (tmpSafeCorrespTry[y] == vIdx1[m1]) //true: vIdx1[m1] is i of i-p (or j of j-r or k of k-s); so, set i.confidence = normalized geo(p, blossomMatch(i)) = normalized geo(p, mi)
									{
										float vertexBasedIso = sMesh->verts[ tmpSafeCorrespTry[y+1] ]->dSpanningUnnSS[mi] / maxExtremeDistS; //[y+1] is p of i-p (or r of j-r or s of k-s)
										bMesh->verts[ vIdx1[m1] ]->confidence = pow(EXP, -vertexBasedIso); //individual distortion of extreme i is stored as its individual confidence: the more it is the less the confidence
if (vertexBasedIso < 0 || vertexBasedIso > 1.1f) {cout << vertexBasedIso << ": unexpected normalized distance\n";exit(0);}
									}
							} //info: {} crucial here; otherwise else just-below is attached to if (tmpSafeCorrespTry[y] == vIdx1[m1]) which is not what i want
							else //denseDistoComputation == 2/4/5/6/8/9 (don't waste time for denseDistoComputation=1 or 3 modes which don't use matchList and dSpanningUnn stuff)
							{
								matchList.push_back(vIdx1[m1]); //base1 from bMesh
								matchList.push_back(mi); //base2 from sMesh
if (denseDistoComputation != 8) //don't waste time in vain as dists b/w regional bases (extremes + nonextremes) needed only when avg distortion via 40x40 traversalList (denseDistoComputation==2) in use or geodesic vector comparisons in use (== 5 or 6 or 9)
{
									//still some redundant settings enter this block, e.g. denseDistoComputation = 2 & sameConfidenceToAll = false, but no harm except time wasting; also, you can efficiently get this example setting by using w7=0, w25=1, denseDistoComputation=8 (so, no entering here)
									//shortest paths b/w regional bases needed as each match from matchList'll traverse all matches in matchList; to be sure, use dijkstra that covers whole surface
									bMesh->dijkstraShortestPaths(vIdx1[m1]); //if already computed, it returns immediately
									sMesh->dijkstraShortestPaths(mi); //if already computed, it returns immediately
									//sMesh.dSpanningUnnSS needed in getCorrespListDistortionSS() below
									if (! sMesh->verts[mi]->dSpanningUnnSS)
										sMesh->verts[mi]->dSpanningUnnSS = new float[ (int) sMesh->verts.size() ]; //init only once
									for (int v = 0; v < (int) sMesh->verts.size(); v++) //dists from mi to all other sMesh.verts are scaled to the same scale as bMesh
										sMesh->verts[mi]->dSpanningUnnSS[v] = sMesh->verts[mi]->dSpanningUnn[v] * scaler;
} //disable for not bothering w/ this timing trick 'cos it does not cost too much time (361 secs becomes 356 secs for 20K input) as most of the regional bases overlap and therefore dijkstraShortestPaths() returns immediately; real timing is on computeRegionBaseVerts() i guess (go update getCorrespListDistortionSS() if this trick is re-enabled later)
							}
							for (int y = 0; y < 6; y += 2)
								if (tmpSafeCorrespTry[y] == vIdx1[m1] && tmpSafeCorrespTry[y+1] == mi)
									nExtremeMatches++;
						}
					}
				} //end of m
				if (denseDistoComputation == 8 || denseDistoComputation == 9) //incorporate the distortion7 part (based on costs7) to v.confidences
				{
					//below heavily adapted from for (int m = 0; m < nRows; m++) loop above; this works on matches7 though, not matches

//if(fprint) fprintf(fPtr6_1, "%d vs. %d\n", n1, n2);

					//init i/j/k.confidence to 0 so that in case they've not been matched by blossom2, their confidence remains as discouraging 0, not an arbitrary number from prev iterations
					bMesh->verts[ tmpSafeCorrespTry[0] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[2] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[4] ]->confidence = 0.0f; //will be updated in loop just-below if blossom2 has found a match for these extremes [redundant 'cos i'll use normalized distortion7 later to assign this confidence]
					bMesh->verts[ tmpSafeCorrespTry[0] ]->distortion7 = bMesh->verts[ tmpSafeCorrespTry[2] ]->distortion7 = bMesh->verts[ tmpSafeCorrespTry[4] ]->distortion7 = 1.0f; //will be updated in loop just-below if blossom2 has found a match for these extremes, i.e. extreme matches i-p, j-r, k-s can be suggested by 40x40 unforced blossom, also i-x, j-y, k-z matches can also be suggested; either case causes an update on this initially pessimistic distortions (remains pessimistic if no matches found for extremes, which is logical 'cos they're so bad that can't be matched)
					for (int m = 0; m < nRows; m++)
					{
						int m1 = matches7[2*m]; //left side of the (m+1)'th matching line
						if (m1 < n1)
						{
							int m2 = matches7[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)
							{
//if(fprint) fprintf(fPtr6_1, "%d\t%d\n", vIdx1[m1], mi); //matches7[] that may assign different extreme matches than the initial generating extreme matches
								//else if (denseDistoComputation == 7) block from above
								//current blossom match is vIdx1[m1]-mi
								for (int y = 0; y < 6; y += 2)
									if (tmpSafeCorrespTry[y] == vIdx1[m1]) //true: vIdx1[m1] is i of i-p (or j of j-r or k of k-s); so, set i.confidence = normalized geo(p, blossomMatch(i)) = normalized geo(p, mi)
									{
										float vertexBasedIso = sMesh->verts[ tmpSafeCorrespTry[y+1] ]->dSpanningUnnSS[mi] / maxExtremeDistS; //[y+1] is p of i-p (or r of j-r or s of k-s)
//bMesh->verts[ vIdx1[m1] ]->confidence = w7 * pow(EXP, -vertexBasedIso); //v.confidence = w7 * disto7-based-confidence; later v.confidence += w25 * disto2 (or 5)-based-confidence will be done to complete v.confidence = w7 * d7 + w25 * d2 (or 5)
										//not initialize v.confidence at this time!!!!!!!! use normalized vertexBasedIso in [0,1] to make weighted v.confidence computation healthier; so, remember this distorton7 (geodesic b/w p of i-p and where this blossom maps i)
										bMesh->verts[ vIdx1[m1] ]->distortion7 = vertexBasedIso; //vIdx1[m1] is one of tmpSafeCorrespTry[even idx], i.e., bMesh extreme
										/*//distortion7 normalizers [not in use 'cos normalization is a bad idea as explained in header file definitions]
										if (vertexBasedIso > bMesh->maxDistortion7)
											bMesh->maxDistortion7 = vertexBasedIso;
										if (vertexBasedIso < bMesh->minDistortion7)
											bMesh->minDistortion7 = vertexBasedIso;//*/
if (vertexBasedIso < 0 || vertexBasedIso > 1.1f) {cout << vertexBasedIso << ": unexpected normalized distance that makes geodesic-based vertexBasedIso not in [0,1]\n";exit(0);}
									}
							}
						}
					} //end of m
				}

				if (nExtremeMatches != 3 && denseDistoComputation != 7) //denseDistoComputation == 7 mode not force extreme matched via costs[][]=-INF tactic; so can't expect it to make nExtremeMatches == 3
				{
					denseDistortion1 = denseDistortion2 = -1.0f; //special flag that tells me not to use blossom match distortion as confidence 'cos regions are not that compatible; denseDistortion'll be -1.0f below for sure
cout << "blossom shouldn't leave a generating extreme match unmatched after extremeMatch=true tactic except denseDistoComputation == 7 mode\t" << nExtremeMatches << " / 3 matched\n";//exit(0);
				}
				else if (denseDistoComputation == 1)
					denseDistortion1 /= n2; //n2 += done above
				else if (denseDistoComputation == 3)
				{
					denseDistortion1 = maxDenseDisto;
					if (! sameConfidenceToAll)
						bMesh->verts[ tmpSafeCorrespTry[0] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[2] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[4] ]->confidence = pow(EXP, -denseDistortion1); //worst distortion of all individual distortions is stored as its individual confidence: the more it is the less the confidence
				}			
				else if (denseDistoComputation == 2)
					denseDistortion2 = sMesh->getCorrespListDistortionSS(bMesh, matchList, maxExtremeDistS, maxExtremeDistB, true, sameConfidenceToAll); //evaluate distortion of whole mapping by traversing itself, i.e. 40 trusted (blossom) correspondences used here, hence more accurate than denseDistortion1 which uses only 3 trusted corresps (in potentialTriplets)
				else if (denseDistoComputation == 8)
				{
					denseDistortion2 = sMesh->getCorrespListDistortionSS(bMesh, matchList, maxExtremeDistS, maxExtremeDistB, true, sameConfidenceToAll, w25); //evaluate distortion of whole mapping by traversing itself, i.e. 40 trusted (blossom) correspondences used here, hence more accurate than denseDistortion1 which uses only 3 trusted corresps (in potentialTriplets)
					//at this time, i/j/k.distortion7/2 ready as raw/unnormalized distortions, distortion7 tends to be in [0,1], distortion2 in [0,0.36], neither can exceed [0,1] interval; map distortions --> confidences via linear kernel conf = 1 - distortion (can't exceed [0,1] interval); then make a compatible conf = conf = w7*(1-distortion7) + (1-w7)*(1-distortion2) formulation
					for (int e = 0; e < 6; e += 2)
						/*//raw to normalized distortions [not in use as explained in header file definitions]
						bMesh->verts[ tmpSafeCorrespTry[2*e] ]->distortion7 = (bMesh->maxDistortion7 == bMesh->minDistortion7 ? 0.0f : (bMesh->verts[ tmpSafeCorrespTry[2*e] ]->distortion7 - bMesh->minDistortion7) / (bMesh->maxDistortion7 - bMesh->minDistortion7)); //maxDistortion7 = minDistortion7 is valid 'cos i-p, j-r, k-s can be suggested by 40x40 unforced blossom as well which would make maxDistortion7=minDistortion7=0; in this case, i want the highest confidence (= lowest distortion) on these extremes
						bMesh->verts[ tmpSafeCorrespTry[2*e] ]->distortion2 = (bMesh->verts[ tmpSafeCorrespTry[2*e] ]->distortion2 - bMesh->minDistortion2) / (bMesh->maxDistortion2 - bMesh->minDistortion2);//*/
						//weighted confidence
					    //bMesh->verts[ tmpSafeCorrespTry[e] ]->confidence = w7 * (1.0f - bMesh->verts[ tmpSafeCorrespTry[e] ]->distortion7) + w25 * (1.0f - bMesh->verts[ tmpSafeCorrespTry[e] ]->distortion2); //conf = w7*(1-distortion7) + (1-w7)*(1-distortion2) (distortion to confidence via linear kernel)
						bMesh->verts[ tmpSafeCorrespTry[e] ]->confidence = w7 * (bMesh->verts[ tmpSafeCorrespTry[e] ]->distortion7 == 1.0f ? 0.0f : pow(EXP, -bMesh->verts[ tmpSafeCorrespTry[e] ]->distortion7)) //if distortion7 remained 1, then i want 0 confidence which is achieved automatically w/ linear kernel but not w/ this exponential kernel; make it 0 w/ this trick (note that distortion2 never 1 so no trick for the 2nd term)
																		   + w25 * pow(EXP, -bMesh->verts[ tmpSafeCorrespTry[e] ]->distortion2); //distortion to confidence via exponential kernel (distortion to confidence via exponential kernel)
				}
				else if (denseDistoComputation == 4)
				{
					denseDistortion2 = sMesh->getCorrespListDistortionSS(bMesh, matchList, maxExtremeDistS, maxExtremeDistB, false, sameConfidenceToAll);
					if (! sameConfidenceToAll)
						bMesh->verts[ tmpSafeCorrespTry[0] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[2] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[4] ]->confidence = pow(EXP, -denseDistortion2); //worst distortion of all individual distortions is stored as its individual confidence: the more it is the less the confidence
				}
				else if (denseDistoComputation == 5 || denseDistoComputation == 6 || denseDistoComputation == 9) //when # of regional bases is n, this geodesic vector tactic demands o(n^3) 'cos each of n^2 pairs makes a n-size vector comparisons; thanks to ~40 regional bases forced by area-based r selection, this not hurts: 211 secs for the fastest denseDistoComputation=1 becomes 215 secs here for 20K tosca horse
				{
					//geodesic distances to known correspondences in matchList is used to define feature vectors which are used to produce mutually-closest pairs
					int ml = (int) matchList.size() / 2;
					//for each region base bMesh.bv, geodesic distances from bv to all other ml bMesh regional bases is stored in bv.geo2coarses (ml = # of coarse correspoondence verts) where v.geo2coarses[4] gives the geodesic dist from v to 5th coarse corresp matched vert
					for (int bv = 0; bv < n1; bv++)
						for (int s = 0; s < ml; s++)
							bMesh->verts[ regionBaseVertsB[bv] ]->geo2coarses.push_back(bMesh->verts[ matchList[2*s] ]->dSpanningUnn[ regionBaseVertsB[bv] ] / maxExtremeDistB); //2s is even and therefore on bMesh; use 2s+1 for correspondence in sMesh
					//fill geo2coarses[] for sMesh as well to compare them below for distortion computation
					for (int bv = 0; bv < n2; bv++)
						for (int s = 0; s < ml; s++)
							sMesh->verts[ regionBaseVertsS[bv] ]->geo2coarses.push_back(sMesh->verts[ matchList[2*s + 1] ]->dSpanningUnnSS[ regionBaseVertsS[bv] ] / maxExtremeDistS); //sMesh.dSpanningUnnSS[] is compatible w/ bMesh.dSpanningUnn[]

					//find the closest (in geo2coarses sense) match for each regional bases from more populated side; this way i appreciate n1-n2 difference automatically, that is, if n1=50 and n2=30, then 20 regionBaseVertsB'll be assigned to bad matches (many-to-many) and therefore more distortion'll come for regions having big n1-n2 difference
					float maxDist = -INF;
					if (n1 > n2) //regionBaseVertsB is more populated than regionBaseVertsS (n1 > n2)
					{
						//each regionBaseVertsB.bv1 is matched to regionBaseVertsS.bv2 where bv2.geo2coarses is closest to bv1.geo2coarses; so, many-to-1 is possible
						for (int bv1 = 0; bv1 < n1; bv1++)
						{
							float minDist = INF;
							for (int bv2 = 0; bv2 < n2; bv2++)
							{
								float dist = 0.0f; //dist b/w bv1.geo2coarses & bv2.geo2coarses geodesic feature vectors
								for (int i = 0; i < ml; i++)
									dist += fabs(bMesh->verts[ regionBaseVertsB[bv1] ]->geo2coarses[i] - sMesh->verts[ regionBaseVertsS[bv2] ]->geo2coarses[i]); //i'th entries are compatible as they're geodesic dists to i'th coarse match
								if (dist < minDist)
									minDist = dist;
							}
							minDist /= ml; //ml many += [0,1] done above; so to make denseDistortion2 += [0,1], div by ml
							denseDistortion2 += minDist; //ml many += [0,1] done above; so to make denseDistortion2 += [0,1], div by ml
							if (minDist > maxDist)
								maxDist = minDist; //remember the max += contribution in case denseDistoComputation = 6
							if (! sameConfidenceToAll)
							{
								if (denseDistoComputation == 5) //no w7*d7 of w7*d7 + w25*d5 available; so, no += will be done; just set i.confidence to this current minDist based confidence
									bMesh->verts[ regionBaseVertsB[bv1] ]->confidence = pow(EXP, -minDist); //individual distortion of this bMesh region base is stored as its individual confidence: the more it is the less the confidence (redundant v.confidences set as well)
								else // == 9 (or 6, but results of 6'll be overwritten below)
								{
									float wd5 = w25 * pow(EXP, -minDist); //weighted d5 (geodesic vector based distortion)
									bMesh->verts[ regionBaseVertsB[bv1] ]->confidence += wd5; //now v.confidence becomes w7*d7 + w25*d5 (note that d7 and d5 are specific to v) (redundant v.confidences set as well)
								}
							}
						}
					}
					else //regionBaseVertsS is more populated than regionBaseVertsB (n2 >= n1)
					{
						//each regionBaseVertsS.bv1 is matched to regionBaseVertsB.bv2 where bv2.geo2coarses is closest to bv1.geo2coarses; so, many-to-1 is possible
						for (int bv1 = 0; bv1 < n2; bv1++)
						{
							float minDist = INF;
							for (int bv2 = 0; bv2 < n1; bv2++)
							{
								float dist = 0.0f; //dist b/w bv1.geo2coarses & bv2.geo2coarses geodesic feature vectors
								for (int i = 0; i < ml; i++)
									dist += fabs(sMesh->verts[ regionBaseVertsS[bv1] ]->geo2coarses[i] - bMesh->verts[ regionBaseVertsB[bv2] ]->geo2coarses[i]); //i'th entries are compatible as they're geodesic dists to i'th coarse match
								if (dist < minDist)
									minDist = dist;
							}
							minDist /= ml; //ml many += [0,1] done above; so to make denseDistortion2 += [0,1], div by ml
							denseDistortion2 += minDist; //ml many += [0,1] done above; so to make denseDistortion2 += [0,1], div by ml
							if (minDist > maxDist)
								maxDist = minDist; //remember the max += contribution in case denseDistoComputation = 6

							if (! sameConfidenceToAll)
							{
								for (int c = 1; c < 6; c += 2) //above i was setting confidence if all regionBaseVertsB thanks to for loop traversed over bv1 < n1; here, i don't easily access to all; so, access the ones that i'll actually use in matchIdxs3 below
									if (regionBaseVertsS[bv1] == tmpSafeCorrespTry[c]) //current sMesh region base is p or r or s, which makes me set bMesh i or j or k cofindence w/ the current distortion
									{
										if (denseDistoComputation == 5) //no w7*d7 of w7*d7 + w25*d5 available; so, no += will be done; just set i.confidence to this current minDist based confidence
											bMesh->verts[ tmpSafeCorrespTry[c-1] ]->confidence = pow(EXP, -minDist); //individual distortion of this bMesh region base is stored as its individual confidence: the more it is the less the confidence
										else // == 9 (or 6, but results of 6'll be overwritten below)
										{
											float wd5 = w25 * pow(EXP, -minDist); //weighted d5 (geodesic vector based distortion)
											bMesh->verts[ tmpSafeCorrespTry[c-1] ]->confidence += wd5; //now v.confidence becomes w7*d7 + w25*d5 (note that d7 and d5 are specific to v) (redundant v.confidences set as well)
										}
									}
							}
						}
					}
					denseDistortion2 = (denseDistoComputation == 5 ? denseDistortion2 / nRows : maxDist); //ml = min(n1,n2); i need nRows=max(n1,n2) as divisor 'cos that many += [0,1] done above; using ml emphasizes n1-n2 difference more on distortion; but 
																										  //then denseDistortion2 may be >1 which makes confidence=1-denseDistortion < 0 which is bad; so, use exponential kernel in confidence!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
					if (! sameConfidenceToAll && denseDistoComputation == 6) //v.confidence holds v-specific minDist currently; make them hold the same maxDist
						bMesh->verts[ tmpSafeCorrespTry[0] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[2] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[4] ]->confidence = pow(EXP, -denseDistortion2); //worst distortion of all individual distortions is stored as its individual confidence: the more it is the less the confidence
				}

				denseDistortion = (denseDistoComputation == 1 || denseDistoComputation == 3 ? denseDistortion1 : denseDistortion2); //won't be used for denseDistoComputation == 7 mode
//if (denseDistortion < 0 || denseDistortion > 1){cout<<"unexpected denseDistortion: " << denseDistortion << endl; exit(0);} //denseDistortion1/3: 'cos t1s1 and t2s2 above are at most 1, least 0; so, nAdds additions of [0,1] divided by nAdds can't exceed 1; for denseDistortion2, since i make biggest dist b/w 3 extremes 1, dist b/w 2 nonextreme bases may exceed 1; so, disable to prevent exit which may rarely happen validly
//if(fprint) fprintf(fPtr6, "%f\n", denseDistortion);
//if(tmpSafeCorrespTry[0]==11 && tmpSafeCorrespTry[2]==1578 && tmpSafeCorrespTry[4]==3398) cout << denseDistortion << "\n\n";

				//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;
				if (denseDistoComputation == 8 || denseDistoComputation == 9)
				{
					delete [] matches7;
					delete [] weightsD7;
					for (int i = 0; i < nRows; i++) delete [] costs7[i];		delete [] costs7;
				}			
			}
			else //huge difference b/w regions (rare 'cos potentialTriplets expected to be good, or at least not that bad); so generating matches must be very bad; simply give them 0 confidences (when divided by # of votes, this will have a discouraging effect)
			{
				bMesh->verts[ tmpSafeCorrespTry[0] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[2] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[4] ]->confidence = 0.0f;
				denseDistortion = -1.0f;
			}
			//////////////////////// dense base verts matching ends ////////////////////////

//denseDistortion range from 0 to say 0.0666; i want to map [0,0.0666] -> [0,1] where values (distortions) close to 0 map close to 1 (confidences) and close to 0.0666 map to 0: 0 -> 1, 0.0666 -> ~0
//this effect is obtained via gaussian kernel; mean 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 = 0.0666 --> sigma = 0.0666/3=0.0222, making variance = 0.0222^2 = 0.00049284; 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
////float sigma = 1.0f/*denseDistortion*/ / 2.0f, aa = 2.0f / (sqrt(2*PI) * sigma); //true 0-mean gaussian kernel parameters explained above

			//////////////////////// voting //////////////////////
			//each match making here has initially 0 confidences and they gain confidence in the amount of h = 1/n1n2 * (1 - denseDistortion) as they're visited here; 1/n1n2 factor is to encourage matches producing regional bases of ~same amounts
			//int n1n2 = (n1 > n2 ? n1 - n2 : n2 - n1); //difference b/w # of regional base vertices; expected to be small if regions are very compatible (only potentialTriplets considered in this fnctn but some weird triplets may still exist in this set which give high differences)
//if (n1n2 > maxDiff) {maxDiff = n1n2;
//cout << n1 << " vvvv" << n2 << "\ta good correspondence whose constituent matches will be voted up:\n" << (sMesh == this ? tmpSafeCorrespTry[1] : tmpSafeCorrespTry[0]) << "-" << (sMesh == this ? tmpSafeCorrespTry[0] : tmpSafeCorrespTry[1]) << "\t" <<
//(sMesh == this ? tmpSafeCorrespTry[3] : tmpSafeCorrespTry[2]) << "-" << (sMesh == this ? tmpSafeCorrespTry[2] : tmpSafeCorrespTry[3]) << "\t" << (sMesh == this ? tmpSafeCorrespTry[5] : tmpSafeCorrespTry[4]) << "-" << (sMesh == this ? tmpSafeCorrespTry[4] : tmpSafeCorrespTry[5]) << "\n";}

			bool sameConfToAll = (denseDistoComputation == 8 || denseDistoComputation == 9 ? false : sameConfidenceToAll); //sameConfidenceToAll forced to be true at 8 or 9 mode to make d2 or d5 accurate; but now i want to apply v.confidence = w7*d7 + w25*d2 (or 5) to each extreme vert separately, so make a sameConfidenceToAll = false effect via sameConfToAll; for other modes, initial sameConfidenceToAll stays as is
			float confidence = 0.0f;
			if (denseDistortion == -1.0f) //no confidence at all 'cos not all 3 extremes are matched by blossom, realized by special flag -1.0f
			{
				//blossom shouldn't leave a generating extreme match unmatched after extremeMatch=true tactic; so, denseDistortion = -1.0f never happens for all modes except denseDistoComputation == 7
				confidence = 0.0f;
				//at least 1 match is wrong so that nExtremeMatches not 3 here; i can make either all 3 individual confidences = 0, or better do the worst even worse than 0 (-1) and don't touch the others
				//if (! sameConfToAll) bMesh->verts[ bMesh->leastConfidentExtreme(tmpSafeCorrespTry[0], tmpSafeCorrespTry[2], tmpSafeCorrespTry[4]) ]->confidence = 0.0f; //for the leastConfidentExtreme, i keep a bad confidence of 0; note that, best confidence can be 1 at most (e^0) which make 0 a good choice 'cos votes are normalized and 0-contribution is bad for a match
				bMesh->verts[ tmpSafeCorrespTry[0] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[2] ]->confidence = bMesh->verts[ tmpSafeCorrespTry[4] ]->confidence = 0.0f; //confidence values are not set accordingly 'cos i've not called getCorrespListDistortionSS() as denseDistortion=-1 here; so, simply discourage all 3 extreme matches in this rare event
			}
			else if (denseDistoComputation != 8 && denseDistoComputation != 9)
				//confidence = 1.0f - denseDistortion; //not sufficiently distinguish good and bad dense mappings
				//confidence = 0.8f*(1.0f - denseDistortion) + 0.2f*(n1n2 / 95.0f));
				confidence = pow(EXP, -denseDistortion); //exponential kernel [not in use in denseDistoComputation == 8/9 mode]
				//confidence = aa * pow(EXP, -(denseDistortion*denseDistortion) / (2 * sigma * sigma))); //Gaussian kernel

			for (int y = 0; y < 6; y += 2) //tmpSafeCorrespTry has bMesh entries in even idxs, and sMesh in odds
			{
				//check whether tmpSafeCorrespTry[y]-[y+1] match exists in the [y].matchIdxs3 list; if so, accumulate confidence of that slot w/ confidence; otherwise, create a new slow w/ the initial confidence of float confidence value above
				bool exists = false;
				for (int i = 0; i < (int) bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.size(); i += 3) //3 consecutive items concern 1 match: idx and distortion and # of votes on this idx
					if ((int) bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3[i] == tmpSafeCorrespTry[y+1]) //match already exists; so vote it up by confidence
					{
						bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3[i+1] += (sameConfToAll ? confidence : bMesh->verts[ tmpSafeCorrespTry[y] ]->confidence);
						bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3[i+2]++; //# of votes this [y]-[y+1] receives for averaging purposes later
						exists = true;
						break;
					}
				if (! exists) //this match for bMesh.tmpSafeCorrespTry[y] is new; so add it w/ confidence vote of float confidence above
				{
					bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.push_back((float) tmpSafeCorrespTry[y+1]);
					bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.push_back((sameConfToAll ? confidence : bMesh->verts[ tmpSafeCorrespTry[y] ]->confidence));
					bMesh->verts[ tmpSafeCorrespTry[y] ]->matchIdxs3.push_back(1); //1st vote for match [y]-[y+1]
				}
			}
			//////////////////////// voting //////////////////////*/
		} //end of r
	} //end of a
//cout << "min & max & avg distortion = " << mminD << " & " << mmaxD << " & " << ttotalD / nttotalD << endl;exit(0);
//cout << "max difference b/w # of regional bases: " << maxDiff << "\tmin/max denseDistortion: " << minDenseDistortion << " / " << maxDenseDistortion << endl;
	cout << "vote mtrx ready\ndone!\n\n";
	//////////////////// votes from potentialTriplets ends ////////////////////
	
	cout << "converting votes in matchIdxs3 to final correspondences: ";
	//similar to indDistortionProcessing == 1) case but this one uses max-row-col algo instead of min-row-col 'cos the higher the votes the better the match
	//when the max of mtrx selected in i-j'th entry, then i'th row and j'th column becomes 0 to prevent many-to-1's; then the max of new mtrx is selected and it repeats
	FILE* fPtr = fopen(fName, "w");	

	//unwanted matches are voted up above by some unlucky/wrong potentialTriplets; to reduce their confidences, it's best to take avg distortion of confidences by dividing them ([j+1]) w/ # of votes of the related match ([j+2])
	for (int i = 0; i < m; i++)
		for (int j = 0; j < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3.size(); j += 3)
			bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[j+1] /= bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[j+2]; //[j]: match in sMesh, [j+1]: confidence, [j+2]: # of confidence additions
	//all confidences are normalized by the # of votes recevied by the related match; just comment-out above 3 lines to cancel this normalization*/

	safeCorresp.clear(); //all line drawings are based on safeCorresp; sphere drawing use interestingMatches

	float leastConfidentMatch = INF, mostConfidentMatch;  //for least-voted match detection and most-votes match to be used as loop condition
	if (oneToOneGreedy)
	{
		cout << "as a 1-to-1 mapping via greedy..\n";

		//# of correspondences to be written below is m for sure in 1-to-1 mode when additional bases not present (i could have discarded virtual matches w/ -1 endpnts and used n here instead of m but anyway)
		fprintf(fPtr, "%d\t%f\t%f\t%f\t%f\n", m, (sMesh == this ? sMesh->minGeoDistG : bMesh->minGeoDistG), (sMesh == this ? sMesh->maxGeoDistG : bMesh->maxGeoDistG), //mesh1 sutff
												 (sMesh == this ? bMesh->minGeoDistG : sMesh->minGeoDistG), (sMesh == this ? bMesh->maxGeoDistG : sMesh->maxGeoDistG)); //mesh2 sutff

		for (int i = 0; i < n; i++)
			sMesh->verts[ sMesh->baseVerts[i] ]->processed = false; //true means matched (and therefore cannot be matched again to maintain 1-to-1)
		for (int i = 0; i < m; i++)
			bMesh->verts[ bMesh->baseVerts[i] ]->processed = false; //true means matched (and therefore cannot be matched again to maintain 1-to-1)
		
		//max-row-col algo
		float avgVote = 0.0f;
		while (true)
		{
			//first, find the next unmatched bMesh base w/ min entry in distortion mtrx
			int idxInB = -1;
			float maxConfidence = -INF;
			for (int i = 0; i < m; i++)
			{
				if (bMesh->verts[ bMesh->baseVerts[i] ]->processed)// || bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3.empty())
					continue; //already matched or no vote collected for this above in matchIdxs3 (or is skipped 'cos size'll be 0 below anyway)
				for (int j = 1; j < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3.size(); j += 3) //check 1-4-7-.. idxs for vote values of the followed matchIdx
				{
					int idxInS = (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[j-1]; //-1: followed integer matchIdx value
					if (! sMesh->verts[idxInS]->processed && bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[j] > maxConfidence)
					{
						maxConfidence = bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[j];
						idxInB = bMesh->baseVerts[i];
					}
				}
			}
			if (idxInB == -1) //next unmatched bMesh base w/ max entry not found 'cos all bMesh verts have been marked as processed=true; cannot go further
			{
				//to keep file consistent, i need m matches per mapping; so, fprint the unmatched ones w/ -1 matchIdx to have m lines
				for (int i = 0; i < m; i++)
					if (! bMesh->verts[ bMesh->baseVerts[i] ]->processed)
						fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", (bMesh == this ? bMesh->baseVerts[i] : -1), (bMesh == this ? -1 : bMesh->baseVerts[i]), -INF, 0); //fprint the -1 match to keep the file in the same format, that is m lines per mapping
				break;
			}
			else
			{
				//second, find the next unmatched max-vote match of idxInB in idxInB's row					
				int bestMatchIdx = -1, nVotes = -1;
				float maxConfidence2 = -INF;
				for (int j = 1; j < (int) bMesh->verts[idxInB]->matchIdxs3.size(); j += 3) //check 1-4-7-.. idxs for distortion values of the followed matchIdx
				{
					int idxInS = (int) bMesh->verts[idxInB]->matchIdxs3[j-1]; //-1: followed integer matchIdx value
					if (! sMesh->verts[idxInS]->processed && bMesh->verts[idxInB]->matchIdxs3[j] > maxConfidence2)
					{
						maxConfidence2 = bMesh->verts[idxInB]->matchIdxs3[j];
						bestMatchIdx = idxInS;
						nVotes = (int) bMesh->verts[idxInB]->matchIdxs3[j+1];
					}
				}
				if (bestMatchIdx == -1) //all matchIdxs[j] above are INF, so no match for [i]
				{
					fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", (bMesh == this ? idxInB : bestMatchIdx), (bMesh == this ? bestMatchIdx : idxInB), -INF, 0); //fprint the -1 match to keep the file in the same format, that is m lines per mapping
					continue;
				}
if (maxConfidence != maxConfidence2){cout<<"weirdness in voting\n";exit(0);}
				bMesh->verts[idxInB]->matchIdx = bestMatchIdx;
				//bMesh->verts[idxInB]->matchCostI = minDisto; no data; if i try harder i can find it but redundant anyway
				//finalIsoDistortion += minDisto;
				sMesh->verts[bestMatchIdx]->processed = bMesh->verts[idxInB]->processed = true; //matched; so, prevent from being matched again

				//mesh1 on even, mesh2 on odd idxs in safeCorresp[]; also, mesh1 on the left, mesh2 on the right in all prints; matchIdx is defined only for mesh1 verts
				if (bMesh == this)
				{
					safeCorresp.push_back(idxInB);
					safeCorresp.push_back(bestMatchIdx); //mesh1.matchIdx/matchCostI handled above as bMesh = this here
					verts[idxInB]->matchIdx = bestMatchIdx;
					//verts[idxInB]->matchCostI = minDisto;
					cout << idxInB << " -- " << bestMatchIdx << "\t\t" << maxConfidence << "\t" << nVotes << endl;
					fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", idxInB, bestMatchIdx, maxConfidence, nVotes);
					avgVote += maxConfidence;
					if (maxConfidence < leastConfidentMatch)
					{
						bMesh->worstMatchI = idxInB; //not w.r.t isometry distortion, but w.r.t. # of votes recevied
						bMesh->verts[ bMesh->worstMatchI ]->worstIin2 = bestMatchIdx;
						leastConfidentMatch = maxConfidence;
					}
				}
				else
				{
					safeCorresp.push_back(bestMatchIdx);
					safeCorresp.push_back(idxInB); //matchIdx & matchCostI handled above as sMesh = this here
					verts[bestMatchIdx]->matchIdx = idxInB;
					//verts[bestMatchIdx]->matchCostI = minDisto;
					cout << bestMatchIdx << " ---- " << idxInB  << "\t\t" << maxConfidence << "\t" << nVotes << endl;
					fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", bestMatchIdx, idxInB, maxConfidence, nVotes);
					avgVote += maxConfidence;
					if (maxConfidence < leastConfidentMatch)
					{
						sMesh->worstMatchI = bestMatchIdx; //sMesh is this
						sMesh->verts[ sMesh->worstMatchI ]->worstIin2 = idxInB;
						leastConfidentMatch = maxConfidence;
					}
				}			
			}
		} //end of while
		cout << "\ndone\n\n";
		fprintf(fPtr, "%f\n", avgVote / n); //div by n for avg vote 'cos n += done above
		fclose(fPtr);
	}
	else //one-to-one using a refined many-to-many mapping (safeCorresp/fContent) as cost matrix; not greedy 'cos many-to-many cost matrix is solved via global blossom
	{
		cout << "as a 1-to-1 mapping via blossom..\n";

		//matches that will be non-infinity in the blossom costs[][] mtrx for final 1-to-1 conversion
		vector< float > fContent; //4 consecutive elements mesh1.v	mesh2.v	vote(mesh1.v-mesh2.v) nVotesFor(mesh1.v-mesh2.v)

/*		////////////////////////////// many-to-many mapping //////////////////////////////		
		//first create a refined many-to-many mapping in safeCorresp/fContent (select max-vote match of each bMesh.v, then max-vote of each still-unmatched sMesh.v), and then convert it to 1-to-1 via blossom and keep the result in overwriten safeCorresp
		for (int i = 0; i < (int) sMesh->baseVerts.size(); i++)
			sMesh->verts[ sMesh->baseVerts[i] ]->matchIdx = -1; //the ones whose matchIdx remain -1 are matched in the second for loop on i2
		for (int i = 0; i < (int) bMesh->baseVerts.size(); i++)
		{
			int idxInB = bMesh->baseVerts[i];
			//find the unmatched max-vote match of idxInB in idxInB's row
			int bestMatchIdx = -1, nVotes = -1;
			float maxVote2 = -INF;
			for (int j = 1; j < (int) bMesh->verts[idxInB]->matchIdxs3.size(); j += 3) //check 1-4-7-.. idxs for distortion values of the followed matchIdx
			{
				int idxInS = (int) bMesh->verts[idxInB]->matchIdxs3[j-1]; //-1: followed integer matchIdx value
				if (bMesh->verts[idxInB]->matchIdxs3[j] > maxVote2)
				{
					maxVote2 = bMesh->verts[idxInB]->matchIdxs3[j];
					bestMatchIdx = idxInS;
					nVotes = (int) bMesh->verts[idxInB]->matchIdxs3[j+1];
				}
			}
			if (bestMatchIdx == -1)
			{
				cout << "shouldn't happen in many-to-many\n"; //'cos i insert the first 2 matches into matchIdxs2 for sure regardless of jump value during ranking (matchBasesViaAllEff())
				exit(0);
			}
			bMesh->verts[idxInB]->matchIdx = bestMatchIdx;
			sMesh->verts[bestMatchIdx]->matchIdx = idxInB; //non -1 flag tells me that this sMesh vert is already matched and therefore not look for a match for it after this for loop (in loop on i2)

			//mesh1 on even, mesh2 on odd idxs in safeCorresp[]; also, mesh1 on the left, mesh2 on the right in all prints; matchIdx is defined only for mesh1 verts
			if (bMesh == this)
			{
				safeCorresp.push_back(idxInB);
				safeCorresp.push_back(bestMatchIdx); //bMesh = this here
				cout << idxInB << " -- " << bestMatchIdx << "\t\t" << maxVote2 << "\t" << nVotes << endl;
				//fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", idxInB, bestMatchIdx, maxVote2, nVotes); not to file 'cos # matches unknown at this time; put into fContent
				fContent.push_back((float) idxInB);	fContent.push_back((float) bestMatchIdx);	fContent.push_back(maxVote2);	fContent.push_back((float) nVotes); //to be fprinted later
				if (maxVote2 < leastConfidentMatch)
				{
					bMesh->worstMatchI = idxInB; //not w.r.t isometry distortion, but w.r.t. # of votes received
					bMesh->verts[ bMesh->worstMatchI ]->worstIin2 = bestMatchIdx;
					leastConfidentMatch = maxVote2;
				}
			}
			else
			{
				safeCorresp.push_back(bestMatchIdx);
				safeCorresp.push_back(idxInB); //sMesh = this here
				cout << bestMatchIdx << " ---- " << idxInB  << "\t\t" << maxVote2 << "\t" << nVotes << endl;
				//fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", bestMatchIdx, idxInB, maxVote2, nVotes);
				fContent.push_back((float) bestMatchIdx);	fContent.push_back((float) idxInB);	fContent.push_back(maxVote2);	fContent.push_back((float) nVotes);
				if (maxVote2 < leastConfidentMatch)
				{
					sMesh->worstMatchI = bestMatchIdx; //sMesh is this
					sMesh->verts[ sMesh->worstMatchI ]->worstIin2 = idxInB;
					leastConfidentMatch = maxVote2;
				}
			}
		} //end of i
		//second for loop over sMesh to assign still-unmatched sMesh bases the best matches possible
		for (int i2 = 0; i2 < (int) sMesh->baseVerts.size(); i2++)
		{
			int idxInS = sMesh->baseVerts[i2];
			if (sMesh->verts[idxInS]->matchIdx == -1)
			{
				//find the unmatched max-vote match of idxInS in idxInS's col; note that, sMesh.matchIdxs3 not defined; so, traverse bMesh.matchIdxs3 for each bMesh base and use the related entry that concerns idxInS
				int maxVoteB = -1, nVotesB = -1; //related entry in bMesh that gives the max vote for idxInS match, i.e. maxVoteB-idxInS has the max vote for all votes connected to idxInS
				float maxVote = -INF;
				for (int ib = 0; ib < (int) bMesh->baseVerts.size(); ib++)
				{
					//int bConcerningS = -1; //related entry in bMesh; no need to keep this; just keep the vote of bConcerningS-idxInS match
					float voteConcerningS = -INF-100; //-100 'cos in case bConcerningS remains -1 (i.e. no bConcerningS-idxInS match), then i want if (voteConcerningS > maxVote2) be false
					int nVotesConcerningS = -1;
					for (int j = 0; j < (int) bMesh->verts[ bMesh->baseVerts[ib] ]->matchIdxs3.size(); j += 3) //check 0-3-6-.. idxs for matchIdx values
						if (bMesh->verts[ bMesh->baseVerts[ib] ]->matchIdxs3[j] == idxInS)
						{
							voteConcerningS = bMesh->verts[ bMesh->baseVerts[ib] ]->matchIdxs3[j+1]; //[j+1] gives the vote for [j]; bConcerningS = bMesh->baseVerts[ib] not done to save time
							nVotesConcerningS = (int) bMesh->verts[ bMesh->baseVerts[ib] ]->matchIdxs3[j+2]; //[j+2] gives the # of votes for [j]
							break;
						}

					if (voteConcerningS > maxVote)
					{
						maxVote = voteConcerningS;
						maxVoteB = bMesh->baseVerts[ib];
						nVotesB = nVotesConcerningS;
					}
				}

				if (maxVoteB == -1)
				{
					cout << "sMesh base is expected to be connected to at least one bMesh base but still valid if isolated\n"; //idxInS is a very isolated vert so that no bMesh marked it in ranking as a candidate for themselves, which makes maxVoteB = -1 as the match of this isolated idxInS
					continue;
				}
				bMesh->verts[maxVoteB]->matchIdx = idxInS;
				sMesh->verts[idxInS]->matchIdx = maxVoteB;

				//mesh1 on even, mesh2 on odd idxs in safeCorresp[]; also, mesh1 on the left, mesh2 on the right in all prints; matchIdx is defined only for mesh1 verts
				if (bMesh == this)
				{
					safeCorresp.push_back(maxVoteB);
					safeCorresp.push_back(idxInS); //bMesh = this here
					cout << maxVoteB << " -- " << idxInS << "\t\t" << maxVote << "\t" << nVotesB << endl;
					//fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", maxVoteB, idxInS, maxVote, nVotesB);
					fContent.push_back((float) maxVoteB);	fContent.push_back((float) idxInS);	fContent.push_back(maxVote);	fContent.push_back((float) nVotesB);
					if (maxVote < leastConfidentMatch)
					{
						bMesh->worstMatchI = maxVoteB; //not w.r.t isometry distortion, but w.r.t. # of votes received
						bMesh->verts[ bMesh->worstMatchI ]->worstIin2 = idxInS;
						leastConfidentMatch = maxVote;
					}
				}
				else
				{
					safeCorresp.push_back(idxInS);
					safeCorresp.push_back(maxVoteB); //sMesh = this here
					cout << idxInS << " ---- " << maxVoteB  << "\t\t" << maxVote << "\t" << nVotesB << endl;
					//fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", idxInS, maxVoteB, maxVote, nVotesB);
					fContent.push_back((float) idxInS);	fContent.push_back((float) maxVoteB);	fContent.push_back(maxVote);	fContent.push_back((float) nVotesB);
					if (maxVote < leastConfidentMatch)
					{
						sMesh->worstMatchI = idxInS; //sMesh is this
						sMesh->verts[ sMesh->worstMatchI ]->worstIin2 = maxVoteB;
						leastConfidentMatch = maxVote;
					}
				}
			}
		} //end of i2
		cout << "refined many-to-many ready for 1-to-1 conversion\n\n";
		bool fprintManyToMany = true; //fprint refined many-to-many mapping
		if (fprintManyToMany)
		{
			//i know # of matches to be fprinted; so, fprint to target file
			fprintf(fPtr, "%d\t%f\t%f\t%f\t%f\n", (int) fContent.size() / 4, (sMesh == this ? sMesh->minGeoDistG : bMesh->minGeoDistG), (sMesh == this ? sMesh->maxGeoDistG : bMesh->maxGeoDistG), //mesh1 sutff
																			  (sMesh == this ? bMesh->minGeoDistG : sMesh->minGeoDistG), (sMesh == this ? bMesh->maxGeoDistG : sMesh->maxGeoDistG)); //mesh2 sutff
			for (int i = 0; i < (int) fContent.size(); i += 4)
				fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", (int) fContent[i], (int) fContent[i + 1], fContent[i + 2], (int) fContent[i + 3]);
			cout << "\ndone\n\n";
			fprintf(fPtr, "%f\n", 0.0f); //can't compute overall distortion at this time; just some up the matchIdxs3 costs of the selected matches later
			fclose(fPtr);
			return true; //fprinting an additional 1-to-1 will ruin the file format; so, finish here
		}
		////////////////////////////// many-to-many mapping ends //////////////////////////////*/

		int nMaxIters = 80, prevCms = -1; //at most how many times the worst of the current map is removed from costs[][] and blossom is done on this refined costs[][]; use nMaxIters = 0 to cancel this worst-removal effect, i.e. the old algo
		nIters = 0;
		bool jumpDetected = false, detectionByConsecutiveDiffs = false; //true checks each consecutive step w/ jump value, false checks difference b/w max and min confs only (more robust)
		vector< float > mostConfMap; //if jump detected when current map is of size oms, then mostConfMap keeps the most confident map among all maps of size oms, and i use that as the final result, i.e. not the one that caused the jump (altough that causing-one is usually the mostConfMap itself)
		float maxConf = -INF;
		do
		{
			cout << "\n****************** iter # " << nIters << " ****************** \n"; //cleaning safeCorresp[] from matches detected as worsts in prev iters so that global blossom matching hopefully gets better as iters proceed

			////////////////////////////// many-to-many mapping v2 //////////////////////////////
			//clear effects of prev iter(s)
			safeCorresp.clear();
			fContent.clear();
			//first create a refined many-to-many mapping in safeCorresp/fContent (select multiple matches of each bMesh.v until the first jump except matches detected as worst in prev iteration(s)), and then convert it to 1-to-1 via blossom and keep the result in overwriten safeCorresp		
			//mark cool matches which are the ones before the first jump and which are the ones not detected as worst in the prev iter(s)
			for (int i = 0; i < (int) bMesh->baseVerts.size(); i++)
			{
				if (nIters == 0) //match candidates before the first significant jump decided in the first iter, and then i simply use the worst-excluded version of that list for the upcoming iters
				{
					float jump = 0.0f;
					vector< float > matchIdxs3Sorted; //sorted version of matchIdxs3 from which jump will be learnt; the ones in this before the first jump them are stored permanently in bv.matchIdxs3FirstJump for the upcoming iters
					for (int h = 0; h < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3.size(); h += 3)
						sMesh->verts[ (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[h] ]->processed = false; //true if added to matchIdxs3Sorted as nextMax
					for (int h = 0; h < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3.size(); h += 3)
					{
						float maxVote = -INF;
						int nextMaxK = -1;
						for (int k = 0; k < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3.size(); k += 3)
							if (! sMesh->verts[ (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[k] ]->processed && bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[k+1] >= maxVote) //not added yet
							{
								maxVote = bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[k+1];
								nextMaxK = k;
							}
if (nextMaxK == -1){cout<<"can't\n";exit(0);}
						//add the next min to matchIdxsSorted
						matchIdxs3Sorted.push_back( bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[nextMaxK] ); //match idx
						matchIdxs3Sorted.push_back( bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[nextMaxK+1] ); //confidence
						matchIdxs3Sorted.push_back( bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[nextMaxK+2] ); //# of votes
						sMesh->verts[ (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3[nextMaxK] ]->processed = true; //not add again
/*						if (jumpBasedOnFirstTwoDifferences)
							jump += (h == 3 || h == 6 ? (matchIdxs3Sorted[h-2] - matchIdxs3Sorted[h+1]) : 0.0f); //use only the first 2 steps to decide the jump (-2 to skip # of votes as well when going backwards; note that, matchIdxs3Sorted is in descending order of confidences)
						else
							jump += (h == 3 ? (matchIdxs3Sorted[h-2] - matchIdxs3Sorted[h+1]) : 0.0f); //use only the first step to decide the jump [cancel jump updates below and go update if statement below 'cos /= 2 no longer valid]*/
						jump += (h >= 3 ? (matchIdxs3Sorted[h-2] - matchIdxs3Sorted[h+1]) : 0.0f); //use all steps 'cos confidences here are reliable as they belong to selected matches of a given map and that map improves as we continue worst-removals
					}
/*					if (jumpBasedOnFirstTwoDifferences)
					{
						jump /= 2; //jump = avgStep at this time 'cos first 2 steps added above, so avg them here
						jump *= 2.0f; //2 x avg step	*= 1.5f for 1.5 x avg step
					}*/
					jump /= (((int) matchIdxs3Sorted.size() / 3) - 1); //this many += additions done above, so avg here
/*					if (theInput == CENTAUR_GIRL)
						jump *= 1.5f; //1.5 x avg step, so give some space to move (relaxation) w/o triggering jump (0-9 cool w/ 1.5, sucks w/ 1.75; so, 1.5 in use for all centaur-girl stuff)
					else if (theInput == CENTAUR_HORSE || theInput == HORSE || theInput == SCAPE)
						jump *=	1.5f;//		1.75f; //1.5 sucks for 0-16
					else if (theInput == CENTAUR_DAVID)
						jump *=	1.5f;
					else if (theInput == SEAHORSE_HORSE)
						jump *=	1.5f;
					else if (theInput == GORILLA_GIRL)
						jump *=	1.5f;
					else if (theInput == SHREC11)
						jump *=	2.0f; //2.0f for 11-14 'cos many fingers cause small jumps b/w same hand's fingers which causes small avg jump when divided by all samples/intervals; so, use 2 instead of 1.5 not to cancel out correct match candidates
									  //1.5f not bad for 12-14*/
					jump *=	1.5f; //for publicly shared code, use always 1.5, which is the typical value anyway

					//transfer cool matches in sorted list, which are the ones w/ distortion difference w/ neighbor < jump
					int nSubset = 0;
////jump = INF;//enable this to use all candidates, i.e. ignore jump stuff at all
					for (int h = 0; h < (int) matchIdxs3Sorted.size(); h += 3)
/*						if ((jumpBasedOnFirstTwoDifferences && (h <= 3 || matchIdxs3Sorted[h-2] - matchIdxs3Sorted[h+1] < jump)) || //add the first 2 max confident matches to fContent directly and stop adding when a jump detected (-2 to skip # of votes as well when going backwards)
															   (h <= 0 || matchIdxs3Sorted[h-2] - matchIdxs3Sorted[h+1] <= jump)) //add the first max confident match to fContent directly and stop adding when a jump detected (-2 to skip # of votes as well when going backwards)*/
						if (h <= 0 || matchIdxs3Sorted[h-2] - matchIdxs3Sorted[h+1] <= jump)
						{
if (matchIdxs3Sorted[h+1] == INF) { cout << "can't 2\n"; exit(0); }
							//mesh1 on even, mesh2 on odd idxs in safeCorresp[]; also, mesh1 on the left, mesh2 on the right in all prints
							if (bMesh == this)
							{
								safeCorresp.push_back(bMesh->baseVerts[i]);
								safeCorresp.push_back((int) matchIdxs3Sorted[h]); //bMesh = this here
								cout << bMesh->baseVerts[i] << " ++ " << matchIdxs3Sorted[h] << "\t\t" << matchIdxs3Sorted[h+1] << "\t" << matchIdxs3Sorted[h+2] << endl;
								//fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", bMesh->baseVerts[i], matchIdxs3Sorted[h], matchIdxs3Sorted[h+1], matchIdxs3Sorted[h+2]); not to file 'cos # matches unknown at this time; put into fContent
								fContent.push_back((float) bMesh->baseVerts[i]);	fContent.push_back(matchIdxs3Sorted[h]);	fContent.push_back(matchIdxs3Sorted[h+1]);	fContent.push_back(matchIdxs3Sorted[h+2]); //to be fprinted later
							}
							else
							{
								safeCorresp.push_back((int) matchIdxs3Sorted[h]);
								safeCorresp.push_back(bMesh->baseVerts[i]); //sMesh = this here
								cout << matchIdxs3Sorted[h] << " ++++ " << bMesh->baseVerts[i]  << "\t\t" << matchIdxs3Sorted[h+1] << "\t" << matchIdxs3Sorted[h+2] << endl;
								//fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", matchIdxs3Sorted[h],  bMesh->baseVerts[i], matchIdxs3Sorted[h+1], matchIdxs3Sorted[h+2]);
								fContent.push_back(matchIdxs3Sorted[h]);	fContent.push_back((float)  bMesh->baseVerts[i]);	fContent.push_back(matchIdxs3Sorted[h+1]);	fContent.push_back(matchIdxs3Sorted[h+2]);
							}
							//store these cool matches before jump in bv.matchIdxs3FirstJump[] to be used by upcoming iters
							bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump.push_back(matchIdxs3Sorted[h]); //match idx poiting to the sMesh
							bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump.push_back(matchIdxs3Sorted[h+1]); //confidence
							bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump.push_back(matchIdxs3Sorted[h+2]); //# of votes
							nSubset++;
						}
						else
							break;
cout << "subset of " << nSubset << " / " << ((int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3.size() / 3) << " potentially cool w/ non-infinity entries in blossom\n\n";
				}
				else //iters following the first one
				{
					for (int h = 0; h < (int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump.size(); h += 3)
					{
						//mesh1 on even, mesh2 on odd idxs in safeCorresp[]; also, mesh1 on the left, mesh2 on the right in all prints
						if (bMesh == this)
						{
							safeCorresp.push_back(bMesh->baseVerts[i]);
							safeCorresp.push_back((int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h]); //bMesh = this here
							cout << bMesh->baseVerts[i] << " ++ " << bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h] << "\t\t" << bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h+1] << "\t" << bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h+2] << endl;
							fContent.push_back((float) bMesh->baseVerts[i]);	fContent.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h]);	fContent.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h+1]);	fContent.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h+2]);
						}
						else
						{
							safeCorresp.push_back((int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h]);
							safeCorresp.push_back(bMesh->baseVerts[i]); //sMesh = this here
							cout << bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h] << " ++++ " << bMesh->baseVerts[i]  << "\t\t" << bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h+1] << "\t" << bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h+2] << endl;
							fContent.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h]);	fContent.push_back((float)  bMesh->baseVerts[i]);	fContent.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h+1]);	fContent.push_back(bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump[h+2]);
						}
					}
					cout << ((int) bMesh->verts[ bMesh->baseVerts[i] ]->matchIdxs3FirstJump.size() / 3) << " potentially cool w/ non-infinity entries in blossom\n\n";
				}
			} //end of i
			////////////////////////////// many-to-many mapping v2 ends //////////////////////////////

			////////////////////////////// 1-to-1 mapping //////////////////////////////
			//blossom matching on costs[][] whose entries are discouraged INF for matches that're not in safeCorresp/fContent and other entries are e^-vote values for the remaining ones; below heavily adapted from blossom module on regionBaseVertsB/S above
			//nRows x nRows mtrx (nRows = max(m, n) = m) will be created w/ vertex-based distortion values decided by tmpSafeCorrespTry; 1-to-1 blossom match of that mtrx is then evaluated
			int n1 = m, n2 = n;
			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 no; //discourage is compatible w/ costs[][] below; recall, discourage must be the largest cost value in costs[][]; w/ unnormalized geodesics, costs[][] not necessarily in [0,1]
			//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] = bMesh->baseVerts[i]; //= bMesh base
				int col = 0;
				for (int j = 0; j < n2; j++)
					vIdx2[col++] = sMesh->baseVerts[j]; //sMesh base
				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++;
			}

//float mminC=INF, mmaxC=-INF, ttotalC=0, nttotalC=0;
			//create the blossom matching matchList to be evaluated (for return) using the traversalListTry
			vector< int > matchList; //1-to-1 blossom matching
			row = 0;
			for (int i = 0; i < n1; i++)
			{
				int base1 = bMesh->baseVerts[i];
				int col = 0;
				for (int j = 0; j < n2; j++)
				{
					int base2 = sMesh->baseVerts[j];

					//base1 (vIdx1[row]) vs. base2 (vIdx2[col]) active assumption
					float vote = -1.0f; //for vIdx1[r] (under the assumption that it is paired w/ vIdx2[c])
					//vote remains -1 if base1 vs. base2 not exist in fContent; so, i discourage costs[][] for such nonexistences; otherwise, costs[][] becomes e^-vote
					for (int i = 0; i < (int) fContent.size(); i += 4) //fContent format: mesh1.v - mesh2.v; so, [i] is from mesh1 (this); recall, base1 ~ bMesh, base2 ~ sMesh
						if ((bMesh == this && (int) fContent[i] == base1 && (int) fContent[i + 1] == base2) ||
							(sMesh == this && (int) fContent[i] == base2 && (int) fContent[i + 1] == base1))
						{
							//vote = pow(EXP, -fContent[i + 2]); //the more the vote, the less the costs[][] hence more likely to be selected in final 1-to-1
							vote = 1.0f - fContent[i + 2]; //the more the vote, the less the costs[][] hence more likely to be selected in final 1-to-1 (linear kernel is better than exponential kernel just-above as it treats all intervals evenly as opposed to the exponential kernel)
							break;
						}				
					costs[row][col++] = (vote != -1.0f ? vote : discourage);
				} //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
//cout << "min & max & avg confidences = " << mminC << " & " << mmaxC << " & " << ttotalC / nttotalC << endl;exit(0);
			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 updated by call-by-ref
			delete blossom;

			//transform data in matches[] to matchList.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
						matchList.push_back(vIdx1[m1]); //base1 from bMesh
						matchList.push_back(mi); //base2 from sMesh
					}
				}
			} //end of m
if (((int) matchList.size() / 2) != n2) { cout << "small of n1 & n2 (which is n2 for sure) must've been matched all\n"; exit(0); }
			//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;

			//store blossom match in global safeCorresp
			if (bMesh == this) //i want safeCorresp.evenIdxs ~ mesh1 (this), odd ~ mesh2; since, matchList.evenIdxs ~ bMesh, odd ~ sMesh, no flip needed to set global safeCorresp 
				safeCorresp = matchList;
			else
			{
				safeCorresp.clear();
				for (int i = 0; i < (int) matchList.size(); i += 2)
				{
					safeCorresp.push_back(matchList[i + 1]);
					safeCorresp.push_back(matchList[i]); //flipping 2 consecutive elements in matchList
				}
			}

			//clear blossom matches that's not in the many-to-many mapping; 273-645 and 3393-645 suggested by 273 and 3393 whose all other entries are INF, i.e. 3393-1362 not suggested by
			//many-to-many; when 273-645 /selected, 3393-645 cannot be selected by blossom which makes 3393-1362 which is not in fContent, i.e. no vote known; so, remove such entries from safeCorresp
			for (int i = 0; i < (int) safeCorresp.size(); i += 2)
			{
				bool exists = false;
				//find this safeCorresp match in fContent; if not exist, then remove it from safeCorresp
				for (int j = 0; j < (int) fContent.size(); j += 4)
					if (safeCorresp[i] == (int) fContent[j] && safeCorresp[i+1] == (int) fContent[j+1])
					{
						exists = true;
						break;
					}
				if (! exists) //clear this blossom match in safeCorresp as it is not suggested by refined many-to-many in fContent
				{
cout << safeCorresp[i] << "-" << safeCorresp[i+1] << " removed from safeCorresp[] as that entry in costs[][] is INF\n\n";
					safeCorresp.erase(safeCorresp.begin() + i); //erase(begin + 4) erases [4]
					safeCorresp.erase(safeCorresp.begin() + i); //right-adjacent at i+1 now shifted to i
					i -= 2; //handle vector shifting
				}
			}//*/

			//fprint final 1-to-1 matches along w/ their votes
			fprintf(fPtr, "%d\t%f\t%f\t%f\t%f\n", (int) safeCorresp.size() / 2, (sMesh == this ? sMesh->minGeoDistG : bMesh->minGeoDistG), (sMesh == this ? sMesh->maxGeoDistG : bMesh->maxGeoDistG), //mesh1 sutff
																				(sMesh == this ? bMesh->minGeoDistG : sMesh->minGeoDistG), (sMesh == this ? bMesh->maxGeoDistG : sMesh->maxGeoDistG)); //mesh2 sutff
			float avgVote = 0.0f;
			leastConfidentMatch = INF;
			mostConfidentMatch = -INF; //for loop condition [not in use anymore]
			for (int i = 0; i < (int) safeCorresp.size(); i += 2)
			{
				float vote = 0.0f, nVotes = 0.0f; //these may remain 0 if blossom makes a match that's not in the many-to-many mapping; 273-645 and 3393-645 suggested by 273 and 3393 whose all other entries are INF, i.e. 3393-1362 not suggested by many-to-many; when 273-645 selected, 3393-645 cannot be selected by blossom which makes 3393-1362 which is not in fContent, i.e. no vote known; so, use 0 vote for it
				//learn the vote and # of votes of this match by finding it in fContent list
				for (int j = 0; j < (int) fContent.size(); j += 4)
					if (safeCorresp[i] == (int) fContent[j] && safeCorresp[i+1] == (int) fContent[j+1])
					{
						vote = fContent[j+2];
						nVotes = fContent[j+3];
						avgVote += vote;
						break;
					}				
#ifndef JUMP_BASED_CONVERGENCE //will be printed in sorted order below when this defined
					fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", safeCorresp[i], safeCorresp[i + 1], vote, (int) nVotes); //-1 cannot fprinted here
					cout << safeCorresp[i] << " ---- " << safeCorresp[i + 1] << "\t\t" << vote << "\t" << nVotes << endl;
#endif
				if (vote < leastConfidentMatch)
				{
					worstMatchI = safeCorresp[i]; //this.worstMatchI is defined
					verts[worstMatchI]->worstIin2 = safeCorresp[i + 1];
					leastConfidentMatch = vote;
				}
				if (vote > mostConfidentMatch)
					mostConfidentMatch = vote;
			}
			cout << "\ndone\n\n";
#ifndef JUMP_BASED_CONVERGENCE //will be printed in sorted order below when this defined
			fprintf(fPtr, "%f\n", avgVote / ((int) safeCorresp.size() / 2));
#endif
			////////////////////////////// 1-to-1 mapping ends //////////////////////////////

			//worst (least confident) match detection so that this detected match is removed from bMesh.matchIdxs3FirstJump[] which eventually makes its entry inf in costs[][] and hence
			//not selected again (which changes the global blossom result as well, hopefully in a good way as a sample occupied by this worst match can now pair up w/ its correct match)
			int erased = -1, worstInB = (bMesh == this ? worstMatchI : verts[worstMatchI]->worstIin2), worstInS = (bMesh == this ? verts[worstMatchI]->worstIin2 : worstMatchI); //worstInB-worstInS is the least confident match on safeCorresp[]
			for (int h = 0; h < (int) bMesh->verts[worstInB]->matchIdxs3FirstJump.size(); h += 3)
				if (bMesh->verts[worstInB]->matchIdxs3FirstJump[h] == worstInS)
				{
					//remove worstInB-worstInS from bMesh.matchIdxs3FirstJump which makes worstInB-worstInS entry in costs[][] infinity for the upcoming iteration(s)
					bMesh->verts[worstInB]->matchIdxs3FirstJump.erase(bMesh->verts[worstInB]->matchIdxs3FirstJump.begin() + h); //erase(begin + 4) erases [4]
					bMesh->verts[worstInB]->matchIdxs3FirstJump.erase(bMesh->verts[worstInB]->matchIdxs3FirstJump.begin() + h); //right-adjacent at h+1 now shifted to h
					bMesh->verts[worstInB]->matchIdxs3FirstJump.erase(bMesh->verts[worstInB]->matchIdxs3FirstJump.begin() + h); //right-adjacent at h+1 now shifted to h
					erased = 1;
					//h -= 3; //handle vector shifting which is redundant as i break just-below
cout << "current worst " << (bMesh == this ? worstInB : worstInS) << "-" << (bMesh == this ? worstInS : worstInB) << " to be removed next\n";
					break;
				}
if (erased == -1) { cout << "break must have been hit but not :(\n"; exit(0); } //worstInB-worstInS must have existed in worstInB.matchIdxs3 so that i hit break above*/

#ifdef JUMP_BASED_CONVERGENCE
			//see if there is at least 1 jump b/w consecutive sorted confidences of the mapping in fContent (= extende version of safeCorresp w/ confidence/vote info); if so, then loop continues by removing current worst announced above; if not, then we converged so break the loop
			float jump = 0.0f; //to be decided automatically w/ the same method used in approval in ranking (matchBasesViaAllEff()) and noninfinite entry detection in costs[][] above; similaryl, jump is the average of the first 2 steps in sorted list, hence giving the incrementing pattern based on 2 differences
			vector< float > matchIdxsSorted, tmpMap; //sorted version of matchIdxs from which jump will be learnt
			for (int j = 0; j < (int) fContent.size(); j += 4) //fContent format: 4 consecutive elements mesh1.v	mesh2.v	vote(mesh1.v-mesh2.v) nVotesFor(mesh1.v-mesh2.v)
				verts[ (int) fContent[j] ]->processed = false; //true if added to matchIdxsSorted as nextMax
			int nExisting = 0;
			for (int j = 0; j < (int) fContent.size(); j += 4)
			{
				//fContent has all noninfinite entry matches and the ones selected by blossom in safeCorresp; safeCorresp is important to detect jump stuff but it doesn't have confidence info; so, traverse over fContent and go below only if this match exists in safeCorresp
				bool exists = false;
				for (int e = 0; e < (int) safeCorresp.size(); e += 2)
					if (safeCorresp[e] == fContent[j] && safeCorresp[e+1] == fContent[j+1])
					{
						exists = true;
						break;
					}
				if (! exists)
					continue;
				nExisting++; //2nd and 3rd existing matches (in safeCorresp) decides the jump value

				float maxConf = -INF;
				int nextMaxK = -1;
				for (int k = 0; k < (int) fContent.size(); k += 4)
				{
					//similarly, consoder only the related matches of fContent
					bool exists = false;
					for (int e = 0; e < (int) safeCorresp.size(); e += 2)
						if (safeCorresp[e] == fContent[k] && safeCorresp[e+1] == fContent[k+1])
						{
							exists = true;
							break;
						}
					if (exists && ! verts[ (int) fContent[k] ]->processed && fContent[k+2] >= maxConf) //not added yet
					{
						maxConf = fContent[k+2];
						nextMaxK = k;
					}
				}
if (nextMaxK == -1){cout<<"can't\n";exit(0);}
				//add the next max to matchIdxsSorted
				matchIdxsSorted.push_back( fContent[nextMaxK] ); //mesh1.v
				matchIdxsSorted.push_back( fContent[nextMaxK+1] ); //mesh2.v
				matchIdxsSorted.push_back( fContent[nextMaxK+2] ); //confidence
				verts[ (int) fContent[nextMaxK] ]->processed = true; //not add again
				fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", (int) fContent[nextMaxK], (int) fContent[nextMaxK+1], fContent[nextMaxK+2], (int) fContent[nextMaxK+3]); //-1 cannot fprinted here				
				for (int col = 0; col < 4; col++)
					tmpMap.push_back(fContent[nextMaxK+col]); //store it in case this one is the most conf (of this size cms) so far
cout << fContent[nextMaxK] << " ---- " << fContent[nextMaxK+1] << "\t\t" << fContent[nextMaxK+2] << "\t" << fContent[nextMaxK+3] << endl;
/*				if (nExisting == 2)
					jump += (matchIdxsSorted[2] - matchIdxsSorted[5]);
				else if (jumpBasedOnFirstTwoDifferences && nExisting == 3) //cancel this else block to use only the first step to decide the jump [cancel jump updates below 'cos /= 2 no longer valid]
					jump += (matchIdxsSorted[5] - matchIdxsSorted[8]); //use only the first 2 steps to decide the jump*/
				jump += (nExisting >= 2 ? (matchIdxsSorted[ 3*(nExisting-1) - 1 ] - matchIdxsSorted[ 3*(nExisting-1) + 2 ]) : 0.0f); //use all steps 'cos confidences here are reliable as they belong to selected matches of a given map and that map improves as we continue worst-removals
			}
			int cms = (int) safeCorresp.size() / 2;
			avgVote /= cms;
			fprintf(fPtr, "%f\n", avgVote);
/*			if (jumpBasedOnFirstTwoDifferences)
			{
				jump /= 2; //jump = avgStep at this time 'cos first 2 steps added above, so avg them here
				jump *= (theInput == SEAHORSE_HORSE ? 1.25f : 2.0f); //2 x avg step	*= 1.5f for 1.5 x avg step
			}*/
			jump /= (cms-1); //this many += additions done above, so avg here
			//jump *= (cms-1); //this many avg step from max conf should make me reach min confidence; if not, then there's a jump, i.e. huge difference, b/w max and min and so worst-removal iters must continue			
/*			if (theInput == CENTAUR_GIRL || theInput == CENTAUR_DAVID || theInput == SHREC11 || theInput == GORILLA_GIRL)
				jump *= 3.0f; //3.0 x avg step, so give some space to move (relaxation) w/o triggering jump [2.25f converges too late for shrec11.15-12, loosing important matches due to late stop]
			else if (theInput == HORSE || theInput == SCAPE)
				jump *= 3.5f; //4.0f for horse0-10, 16-12
			else if (theInput == CENTAUR_HORSE)
				jump *=	2.25f;
			else if (theInput == SEAHORSE_HORSE)
				jump *=	1.75f;*/
			jump *=	xZeta; //for publicly shared code, use always 3, which is the typical value anyway

			//see if there's a jump, i.e. huge difference b/w consecutive confidences (detectionByConsecutiveDiffs=true) or max and min confidences (detectionByConsecutiveDiffs=false), in matchIdxsSorted
			jumpDetected = false;
//			if (detectionByConsecutiveDiffs)
//			{			
				cout << "stop removing worst if all sorted consecutive differences <= jump " << jump << endl;
				if ((int) matchIdxsSorted.size() >= 9) //at least 3 matches in matchIdxsSorted, so safely do the jump check; if 2 matches is there, then jump above is not healthy and so skip test just-below
					for (int h = 0; h < (int) matchIdxsSorted.size(); h += 3)
						if (h > 0 && matchIdxsSorted[h-1] - matchIdxsSorted[h+2] > jump)
						{
							jumpDetected = true;
							break;
						}
/*			}
			else
			{
				cout << "stop removing worst if max and min confidence difference <= jump " << jump << endl;
				jumpDetected = mostConfidentMatch - leastConfidentMatch > jump; //if differnce is huge, then there's a jump b/w average and min conf, which implies another worst-removal iteration
			}//no!!!!! 'cos mostConfidentMatch - leastConfidentMatch = jump above for sure, which breaks the loop in the first iter*/

			//store the most confident map of this current size cms so far
			if (cms != prevCms) //tmpMap is a different sized map 'cos its size cms not match w/ the previous iter's cms; so, reset maxConf
				maxConf = -INF;
			if (avgVote > maxConf)
			{
				mostConfMap = tmpMap;
				maxConf = avgVote;				
			}
			prevCms = cms; //prepare for the upcoming iteration

			if (! jumpDetected) //jump not detected in the optimum map size cms; so, use the most confident map among maps of size cms, which is already stored in mostConfMap (and which may exhibit jump; that jump is falsely detected due to numerical precisions and stuff; so, roll-back to that mostConfMap, which is usually the current tmpMap)
			{
				cout << "\n*******************************\nrolling-back to the most confident map of optimum size suggested by jump:\n";
				safeCorresp.clear(); //redundant to update safeCorresp but do it anyway
				leastConfidentMatch = INF;
				mostConfidentMatch = -INF; //for loop condition [not in use anymore]
				//fprint final 1-to-1 matches along w/ their votes
				fprintf(fPtr, "%d\t%f\t%f\t%f\t%f\n", (int) mostConfMap.size() / 4, (sMesh == this ? sMesh->minGeoDistG : bMesh->minGeoDistG), (sMesh == this ? sMesh->maxGeoDistG : bMesh->maxGeoDistG), //mesh1 sutff
																					(sMesh == this ? bMesh->minGeoDistG : sMesh->minGeoDistG), (sMesh == this ? bMesh->maxGeoDistG : sMesh->maxGeoDistG)); //mesh2 sutff
				for (int mcm = 0; mcm < (int) mostConfMap.size(); mcm += 4)
				{
					fprintf(fPtr, "%d\t%d\t\t%f\t%d\n", (int) mostConfMap[mcm], (int) mostConfMap[mcm+1], mostConfMap[mcm+2], (int) mostConfMap[mcm+3]); //-1 cannot fprinted here
					cout << mostConfMap[mcm] << " ---- " << mostConfMap[mcm+1] << "\t\t" << mostConfMap[mcm+2] << "\t" << mostConfMap[mcm+3] << endl;
					safeCorresp.push_back((int) mostConfMap[mcm]);
					safeCorresp.push_back((int) mostConfMap[mcm+1]);
					//set bMesh.matchIdx values to be used in extendToDense()
					if (bMesh == this)
					{
						verts[ (int) mostConfMap[mcm] ]->matchIdx = (int) mostConfMap[mcm+1]; //[mcm] ~ this mesh1, [mcm+1] ~ mesh2
						sMesh->verts[ (int) mostConfMap[mcm+1] ]->matchIdx = (int) mostConfMap[mcm]; //dual; sMesh ~ mesh2 here
					}
					else
					{
						verts[ (int) mostConfMap[mcm] ]->matchIdx = (int) mostConfMap[mcm+1]; //[mcm] ~ this mesh1, [mcm+1] ~ mesh2
						bMesh->verts[ (int) mostConfMap[mcm+1] ]->matchIdx = (int) mostConfMap[mcm]; //dual; bMesh ~ mesh2 here
					}
					denseMap.push_back((int) mostConfMap[mcm]);
					denseMap.push_back((int) mostConfMap[mcm+1]); //denseMap, if used, will involve these extreme matches for sure
					if (mostConfMap[mcm+2] < leastConfidentMatch)
					{
						worstMatchI = (int) mostConfMap[mcm]; //this.worstMatchI is defined
						verts[worstMatchI]->worstIin2 = (int) mostConfMap[mcm+1];
						leastConfidentMatch = mostConfMap[mcm+2];
					}
					if (mostConfMap[mcm+2] > mostConfidentMatch)
						mostConfidentMatch = mostConfMap[mcm+2];
				}
				fprintf(fPtr, "%f\n", maxConf);
				//mostConf map fprinted twice, once when it's detected as mostConf before, and once now; so, make extra increment for correct fprint in return
				nIters++;
			}
		} while (nIters++ < nMaxIters/*1*/ && jumpDetected); //at most nMaxIters is done; cool 2nd condition early-breaks the loop when difference b/w all consecutive confidences is small enough, i.e. <= jump
#else
		} while (nIters++ < nMaxIters/*1*/ && mostConfidentMatch - leastConfidentMatch > 0.07f); //at most nMaxIters is done; cool 2nd condition early-breaks the loop when the difference b/w the best and worst confidences is small enough, i.e. <= 0.05f
#endif
		fclose(fPtr);
	}

	if (worstMatchI != -1)
		cout << "\n\n" << leastConfidentMatch << " votes for " << worstMatchI << "-" << verts[worstMatchI]->worstIin2 << " worst match (least-voted)\nload iter id: " << nIters-1 << "\n\n";

/*//see the confidence ranking of some particular matches, or all matches
for (int i = 0; i < (int) bMesh->baseVerts.size(); i++)
{
int iiB = bMesh->baseVerts[i];
for (int j = 1; j < (int) bMesh->verts[iiB]->matchIdxs3.size(); j += 3)
	cout << iiB << "--" << (int) bMesh->verts[iiB]->matchIdxs3[j-1] << "\t" << bMesh->verts[iiB]->matchIdxs3[j] << " " << bMesh->verts[iiB]->matchIdxs3[j+1] << endl;
cout << endl;
}//*/

	return true; //final match ready and fprinted
}

inline int Mesh::leastConfidentExtreme(int e1, int e2, int e3)
{
	//return the id of the extreme whose confidence value is the least

	int leastE = (verts[e1]->confidence < verts[e2]->confidence ? e1 : e2); //min of e1, e2
	leastE = (verts[leastE]->confidence < verts[e3]->confidence ? leastE : e3); //min of e1, e2, e3
	return leastE;
}

void Mesh::laplacianSmoothing()
{
	//laplacian smooothing, i.e. move a given point to the center of gravity of its 1-ring neighborhood
	
	//save the original coords before they are exposed to laplacian smooothing below
	for (int i = 0; i < (int) verts.size(); i++)
	{
		if (! verts[i]->coords2)
			verts[i]->coords2 = new float[3]; //initialize coords2, if not inited yet, which holds the original coords no matter what happens to coords[]
		for (int c = 0; c < 3; c++)
			verts[i]->coords2[c] = verts[i]->coords[c];
	}

	for (int v = 0; v < (int) verts.size(); v++)	//not v = 0; v < numOfVertsInUse; v++!!!
	{
		verts[v]->regulator = new float[3];
		verts[v]->regulator[0] = verts[v]->regulator[1] = verts[v]->regulator[2] = 0.0f;
		float howManyNeighb = 0.0f;
		//create a constant iterator
		set< int, std::less< int > >::const_iterator neighbVertIdx;
		for (neighbVertIdx = verts[v]->vertNeighborsSet.begin(); neighbVertIdx != verts[v]->vertNeighborsSet.end(); neighbVertIdx++)
		{
			float* neighb = verts[(*neighbVertIdx)]->coords;
			for (int c = 0; c < 3; c++)
				verts[v]->regulator[c] += (neighb[c] - verts[v]->coords[c]);
			howManyNeighb++;
		}
		//each non-removed vert must have 1+ neighb
assert(howManyNeighb != 0.0f);
		for (int c = 0; c < 3; c++)
			verts[v]->regulator[c] /= howManyNeighb;
	} //end of v

	//apply all displacements (regulator) to each vert
	for (int v = 0; v < (int) verts.size(); v++)
		for (int c = 0; c < 3; c++)
			verts[v]->coords[c] += verts[v]->regulator[c];
	
	//edge lengths also need to be updated since all endpnts have been scaled above
	updateEdgeLengths();
	updateSedgeLengths();
	updateNormals();
}
