//- ****************************************************************************
//- 
//- Copyright 2009 Sandia Corporation. Under the terms of Contract
//- DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
//- retains certain rights in this software.
//- 
//- BSD Open Source License.
//- All rights reserved.
//- 
//- Redistribution and use in source and binary forms, with or without
//- modification, are permitted provided that the following conditions are met:
//- 
//-    * Redistributions of source code must retain the above copyright notice,
//-      this list of conditions and the following disclaimer.
//-    * Redistributions in binary form must reproduce the above copyright
//-      notice, this list of conditions and the following disclaimer in the
//-      documentation and/or other materials provided with the distribution.
//-    * Neither the name of Sandia National Laboratories nor the names of its
//-      contributors may be used to endorse or promote products derived from
//-      this software without specific prior written permission.
//- 
//- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
//- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
//- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
//- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
//- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
//- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//- POSSIBILITY OF SUCH DAMAGE.
//-
//- ****************************************************************************

#include <cmath>

// **** _LOCAL INCLUDES_ *******************************************************

#include "GeoTessGrid.h"
#include "CPPUtils.h"
#include "IFStreamAscii.h"
#include "IFStreamBinary.h"
#include "CpuTimer.h"

// **** _BEGIN GEOTESS NAMESPACE_ **********************************************

namespace geotess {

// **** _EXPLICIT TEMPLATE INSTANTIATIONS_ *************************************

// **** _STATIC INITIALIZATIONS_************************************************

// **** _FUNCTION IMPLEMENTATIONS_ *********************************************

/**
 * Destructor.
 */
GeoTessGrid::~GeoTessGrid()
{
	if (refCount > 0)
	{
		ostringstream os;
		os << endl << "ERROR in GeoTessGrid::~GeoTessGrid" << endl
			 << "Reference count (" << refCount << ") is not zero." << endl;
		throw GeoTessException(os, __FILE__, __LINE__, 2002);
	}

	if (vertices != NULL) CPPUtils::delete2DArray<double>(vertices);
	if (triangles != NULL) CPPUtils::delete2DArray<int>(triangles);
	if (tessellations != NULL) CPPUtils::delete2DArray<int>(tessellations);
	if (levels != NULL) CPPUtils::delete2DArray<int>(levels);
	if (neighbors != NULL) CPPUtils::delete2DArray<int>(neighbors);
	if (circumCenters != NULL) CPPUtils::delete2DArray<double>(circumCenters);
	if (edges != NULL) CPPUtils::delete3DArray<double>(edges);
	clearVertexTriangles();
}

/**
 * Copy constructor.
 */
GeoTessGrid::GeoTessGrid(GeoTessGrid &other) :
	vertices(NULL), triangles(NULL), levels(NULL), tessellations(NULL),
	neighbors(NULL), edges(NULL), circumCenters(NULL), gridID(other.gridID),
	gridInputFile(other.gridInputFile), gridOutputFile(other.gridOutputFile),
	gridSoftwareVersion(other.gridSoftwareVersion),
	gridGenerationDate(other.gridGenerationDate),
	refCount(0), optimization(other.optimization)
{
	nVertices = other.nVertices;
	vertices = CPPUtils::new2DArray<double>(nVertices, 3);
	for (int i=0; i<nVertices; ++i)
	{
		vertices[i][0] = other.vertices[i][0];
		vertices[i][1] = other.vertices[i][1];
		vertices[i][2] = other.vertices[i][2];
	}

	nTriangles = other.nTriangles;
	triangles = CPPUtils::new2DArray<int>(nTriangles, 3);
	neighbors = CPPUtils::new2DArray<int>(nTriangles, 4);
	for (int i=0; i<nTriangles; ++i)
	{
		triangles[i][0] = other.triangles[i][0];
		triangles[i][1] = other.triangles[i][1];
		triangles[i][2] = other.triangles[i][2];
		neighbors[i][0] = other.neighbors[i][0];
		neighbors[i][1] = other.neighbors[i][1];
		neighbors[i][2] = other.neighbors[i][2];
		neighbors[i][3] = other.neighbors[i][3];
	}

	nLevels = other.nLevels;
	levels = CPPUtils::new2DArray<int>(nLevels, 2);
	for (int i=0; i<nLevels; ++i)
	{
		levels[i][0] = other.levels[i][0];
		levels[i][1] = other.levels[i][1];
	}

	nTessellations = other.nTessellations;
	tessellations = CPPUtils::new2DArray<int>(nTessellations, 2);
	for (int i=0; i<nTessellations; ++i)
	{
		tessellations[i][0] = other.tessellations[i][0];
		tessellations[i][1] = other.tessellations[i][1];
	}

	if (other.edges)
	{
		edges = CPPUtils::new3DArray<double>(nTriangles, 3, 3);
		for (int i=0; i<nTriangles; ++i)
		{
			edges[i][0][0] = other.edges[i][0][0];
			edges[i][0][1] = other.edges[i][0][1];
			edges[i][0][2] = other.edges[i][0][2];
			edges[i][1][0] = other.edges[i][1][0];
			edges[i][1][1] = other.edges[i][1][1];
			edges[i][1][2] = other.edges[i][1][2];
			edges[i][2][0] = other.edges[i][2][0];
			edges[i][2][1] = other.edges[i][2][1];
			edges[i][2][2] = other.edges[i][2][2];
		}
	}

}

GeoTessGrid& GeoTessGrid::operator=(const GeoTessGrid& other)
{
	if (vertices != NULL) CPPUtils::delete2DArray<double>(vertices);
	if (triangles != NULL) CPPUtils::delete2DArray<int>(triangles);
	if (tessellations != NULL) CPPUtils::delete2DArray<int>(tessellations);
	if (levels != NULL) CPPUtils::delete2DArray<int>(levels);
	if (neighbors != NULL) CPPUtils::delete2DArray<int>(neighbors);
	if (circumCenters != NULL) CPPUtils::delete2DArray<double>(circumCenters);
	if (edges != NULL) CPPUtils::delete3DArray<double>(edges);
	clearVertexTriangles();

	nVertices = other.nVertices;
	vertices = CPPUtils::new2DArray<double>(nVertices, 3);
	for (int i=0; i<nVertices; ++i)
	{
		vertices[i][0] = other.vertices[i][0];
		vertices[i][1] = other.vertices[i][1];
		vertices[i][2] = other.vertices[i][2];
	}

	nTriangles = other.nTriangles;
	triangles = CPPUtils::new2DArray<int>(nTriangles, 3);
	neighbors = CPPUtils::new2DArray<int>(nTriangles, 4);
	for (int i=0; i<nTriangles; ++i)
	{
		triangles[i][0] = other.triangles[i][0];
		triangles[i][1] = other.triangles[i][1];
		triangles[i][2] = other.triangles[i][2];
		neighbors[i][0] = other.neighbors[i][0];
		neighbors[i][1] = other.neighbors[i][1];
		neighbors[i][2] = other.neighbors[i][2];
		neighbors[i][3] = other.neighbors[i][3];
	}

	nLevels = other.nLevels;
	levels = CPPUtils::new2DArray<int>(nLevels, 2);
	for (int i=0; i<nLevels; ++i)
	{
		levels[i][0] = other.levels[i][0];
		levels[i][1] = other.levels[i][1];
	}

	nTessellations = other.nTessellations;
	tessellations = CPPUtils::new2DArray<int>(nTessellations, 2);
	for (int i=0; i<nTessellations; ++i)
	{
		tessellations[i][0] = other.tessellations[i][0];
		tessellations[i][1] = other.tessellations[i][1];
	}

	if (other.edges)
	{
		edges = CPPUtils::new3DArray<double>(nTriangles, 3, 3);
		for (int i=0; i<nTriangles; ++i)
		{
			edges[i][0][0] = other.edges[i][0][0];
			edges[i][0][1] = other.edges[i][0][1];
			edges[i][0][2] = other.edges[i][0][2];
			edges[i][1][0] = other.edges[i][1][0];
			edges[i][1][1] = other.edges[i][1][1];
			edges[i][1][2] = other.edges[i][1][2];
			edges[i][2][0] = other.edges[i][2][0];
			edges[i][2][1] = other.edges[i][2][1];
			edges[i][2][2] = other.edges[i][2][2];
		}
	}
	else
		edges = NULL;

	gridID = other.gridID;
	gridInputFile = other.gridInputFile;
	gridOutputFile = other.gridOutputFile;
	gridSoftwareVersion = other.gridSoftwareVersion;
	gridGenerationDate = other.gridGenerationDate;

	optimization = other.optimization;

	return *this;
}

/**
 * Test a file to see if it is a GeoTessGrid file.
 *
 * @param inputFile
 * @return true if inputFile is a GeoTessGrid file.
 */
bool GeoTessGrid::isGeoTessGrid(const string& fileName)
{
	string line = "";
	if (fileName.find(".ascii", fileName.length() - 6) != string::npos)
	{
		IFStreamAscii input;
		input.openForRead(fileName);
		line = input.readString();
		input.close();
	}
	else
	{
		IFStreamBinary ifs(fileName, 512);
		ifs.boundaryAlignmentOff();
	  ifs.resetPos();
		ifs.readCharArray(line, 11);
	}

	return (line == "GEOTESSGRID");
}

/**
 * Open the specified file using the appropriate format, and read only enough of the file to
 * retrieve the gridID.
 *
 * @param fileName
 * @return String gridID
 * @throws IOException
 */
string GeoTessGrid::getGridID(const string& fileName)
{
	string gridID = "";
	if (fileName.find(".ascii", fileName.length() - 6) != string::npos)
	{
		int gridFileFormat;
		string gridSWVersion, fileCreationDate;

		IFStreamAscii input;
		input.openForRead(fileName);

		loadGridAsciiFront(input, gridFileFormat, gridSWVersion, fileCreationDate, gridID, "null");
		input.close();
	}
	else
	{
		int gridFileFormat;
		string grdSWVersion, fileCreationDate;

		IFStreamBinary ifs(fileName, 512);
		ifs.boundaryAlignmentOff();
		ifs.resetPos();

		loadGridBinaryFront(ifs, gridFileFormat, grdSWVersion, fileCreationDate, gridID, "null");
	}

	return gridID;
}

/**
 * Load GeoTessGrid object from a File.
 *
 * @param inputFile The name of the file from which the grid is to be read.  if the extension
 *                  is 'bin' the model is written to a binary file, otherwise it is written to
 *                  an ascii file.
 * @throws GeoTessException
 */
GeoTessGrid* GeoTessGrid::loadGrid(const string& inputFile)
{
	if (inputFile.find(".ascii", inputFile.length() - 6) != string::npos)
		loadGridAscii(inputFile);
	else
		loadGridBinary(inputFile);

	return this;
}

/**
 * Load the 2D grid from an Ascii File.
 *
 * @param file
 * @throws IOException
 */
GeoTessGrid* GeoTessGrid::loadGridAscii(const string& inputFile)
{
	IFStreamAscii input;
	input.openForRead(inputFile);
	loadGridAscii(input);
	input.close();

	return this;
}

void GeoTessGrid::loadGridAsciiFront(IFStreamAscii& input, int& gridFileFormat,
																		 string& gridSWVersion, string& fileCreationDate,
																		 string& gridid, const string& grdInptFile)
{
	string line = input.readString();
	if (line != "GEOTESSGRID")
	{
		ostringstream os;
		os << endl << "ERROR in GeoTessModel::loadModelAscii" << endl
			 << "  expected file : " << grdInptFile << endl
			 << " to start with \"GEOTESSGRID\" as first line but found \"" << endl
			 << line << "\" instead" << endl;
		throw GeoTessException(os, __FILE__, __LINE__, 2003);
	}

	// read file format version number

	gridFileFormat = input.readInteger();

	if (gridFileFormat != 2)
	{
		ostringstream os;
		os << endl << "ERROR in GeoTessGrid::loadGridAscii" << endl
			 << "Grid file format version " << gridFileFormat
			 << " is not supported by this version of GeoTessGridAscii" << endl;
		throw GeoTessException(os, __FILE__, __LINE__, 2004);
	}

	// read grid population software version and file creation date

	gridSWVersion = "";
	input.readLine(gridSWVersion);
	fileCreationDate = CPPUtils::trim(gridSWVersion);

	fileCreationDate = "";
	input.readLine(fileCreationDate);
	fileCreationDate = CPPUtils::trim(fileCreationDate);

	// read grid data

	input.readString(gridid);
}

/**
 * Read grid from a Scanner in ascii format. Scanner is not closed by this method.
 *
 * @param input Scanner from which to read grid definition.
 */
GeoTessGrid* GeoTessGrid::loadGridAscii(IFStreamAscii& input)
{
	int gridFileFormat;
	string gridSWVersion, fileCreationDate;

	loadGridAsciiFront(input, gridFileFormat, gridSWVersion, fileCreationDate,
			               gridID, gridInputFile);

	setGridSoftwareVersion(gridSWVersion);
	setGridGenerationDate(fileCreationDate);

	// read nTessellations, nLevels, nTriangles and nVertices.
	nTessellations = input.readInteger();
	nLevels = input.readInteger();
	nTriangles = input.readInteger();
	nVertices = input.readInteger();

	tessellations = CPPUtils::new2DArray<int>(nTessellations, 2);
	levels = CPPUtils::new2DArray<int>(nLevels, 2);
	triangles = CPPUtils::new2DArray<int>(nTriangles, 3);
	vertices = CPPUtils::new2DArray<double>(nVertices, 3);

	//comment = input.nextLine();
	//comment = input.nextLine(); // skip comment
	// System.out.println(comment);
	for (int i = 0; i < nTessellations; ++i)
		for (int j = 0; j < 2; ++j)
			tessellations[i][j] = input.readInteger();

	//comment = input.nextLine();
	//comment = input.nextLine(); // skip comment
	for (int i = 0; i < nLevels; ++i)
		for (int j = 0; j < 2; ++j)
			levels[i][j] = input.readInteger();

	//comment = input.nextLine();
	//comment = input.nextLine(); // skip comment
	// System.out.println(comment);
	// for each vertex, read the geocentric unit vector
	for (int i = 0; i < nVertices; ++i)
		for (int j = 0; j < 3; ++j)
			vertices[i][j] = input.readDouble();

	//comment = input.nextLine();
	//comment = input.nextLine(); // skip comment
	// System.out.println(comment);
	// for each triangle read the indexes of the 3 vertices that form the
	// corners of the triangle,
	for (int i = 0; i < nTriangles; ++i)
		for (int j = 0; j < 3; ++j)
			triangles[i][j] = input.readInteger();

	neighbors = findNeighbors();

	return this;
}

/**
 * Load the 2D grid from a File.
 *
 * @param file
 * @throws IOException
 */
GeoTessGrid* GeoTessGrid::loadGridBinary(const string& inputFile)
{
	IFStreamBinary ifs(inputFile);
	ifs.boundaryAlignmentOff();
	ifs.resetPos();

	loadGridBinary(ifs);

	return this;
}

const set<int>&	GeoTessGrid::getVertexIndices(const int& level)
{
	if (connectedVertices.size() == 0) connectedVertices.resize(nLevels);

	if (connectedVertices[level].size() == 0)
	{
		for (int t = levels[level][0]; t < levels[level][1]; ++t)
		{
			int* triangle = triangles[t];
			connectedVertices[level].insert(triangle[0]);
			connectedVertices[level].insert(triangle[1]);
			connectedVertices[level].insert(triangle[2]);
		}
	}

	return connectedVertices[level];
}

//void	GeoTessGrid::getVertexIndices(const int& tessellation, const int& level,set<int>& indices)
//{
//	indices.clear();
//	int t0 = levels[tessellations[tessellation][0] + level][0];
//	for (int i = t0; i < t0 + getNTriangles(tessellation, level); ++i)
//	{
//		int* triangle = triangles[i];
//		indices.insert(triangle[0]);
//		indices.insert(triangle[1]);
//		indices.insert(triangle[2]);
//	}
//}

void	GeoTessGrid::getVertices(const int& tessellation, const int& level,set<const double*>& vectors)
{
	vectors.clear();
	int lvl = tessellations[tessellation][0] + level;
	getVertexIndices(lvl);
	set<int>::iterator it;
	for (it = connectedVertices[lvl].begin(); it != connectedVertices[lvl].end(); ++it)
		vectors.insert(vertices[*it]);
}

/**
 * Retrieve list of edges that emanate from the specified vertex.
 * <ol start="0">
 * Each edge is defined by 4 indexes:
 * <li>the specified vertex
 * <li>index of a neighboring vertex
 * <li>index of triangle that is to the 'left' of edge.
 * <li>index of triangle that is to the 'right' of edge.
 * </ol>
 * The first edge will extend from vertex to firstNeighborVertex and
 * subsequent edges will be ordered such that the left triangle of a given
 * edge will be equal to the right triangle of the previous edge (clockwise
 * ordering).
 *
 * @param tessId
 *            Tessellation index
 * @param level
 *            Index of a level relative to the first level of the specified
 *            tessellation
 * @param vertex
 *						The vertex about which all connecting edges will be returned.
 * @param firstNeighborVertex
 *            if one of the neighbor vertexes is firstNeighborVertex then
 *            that edge will be the first edge in the list. If no
 *            neighborVertex has index firstNeighborVertex, then first edge
 *            will be random.
 * @param store
 *            The allocated store of int* arrays allocated by this function
 *            and its caller (InterpolatorNaturalNeighbor.update2D()) for
 *            reuse. If empty new ones are created and later recovered by
 *            the caller.
 * @param edges
 *						The returned list of edge arrays containing the ordered list
 *            of edges emanating from the input vertex
 */
void GeoTessGrid::getEdgeList(const int& tessId, const int& level, const int& vertex,
		const int& firstNeighborVertex,
															ArrayReuse<int>& store,
															list<int*>& vedges) const
{
	int* edge = NULL;

	// get triangle list about vertex

	const vector<int>& triangleList = getVertexTriangles(tessId, level, vertex);

	// clear edge list and loop over all triangles filling edge list

	vedges.clear();
	for (int i = 0; i < (int) triangleList.size(); ++i)
	{
		int triangle = triangleList[i];
		int* nodes = triangles[triangle];

		// use storage, if available, or create new int[] container

		edge = store.getArray();

		// assign edge entries

		edge[0] = vertex; edge[3] = triangle;
		if (nodes[0] == vertex)
		{
			edge[1] = nodes[1]; edge[2] = neighbors[triangle][2];
			//edge = new int[] { vertex, nodes[1], neighbors[triangle][2], triangle };
		}
		else if (nodes[1] == vertex)
		{
			edge[1] = nodes[2]; edge[2] = neighbors[triangle][0];
			//edge = new int[] { vertex, nodes[2], neighbors[triangle][0], triangle };
		}
		else if (nodes[2] == vertex)
		{
			edge[1] = nodes[0]; edge[2] = neighbors[triangle][1];
			//edge = new int[] { vertex, nodes[0], neighbors[triangle][1], triangle };
		}

		// add edge to list

		if (edge[1] == firstNeighborVertex)
			vedges.push_front(edge);
		else
			vedges.push_back(edge);
	}

	// done ... order edges

	list<int*>::iterator itin = vedges.begin();
	int* current = *itin;
	++itin;
	list<int*>::iterator itparse = vedges.begin();
	++itparse;
	while (itparse != vedges.end())
	{
	  int* next = *itparse;
	  if (next[2] == current[3])
	  {
	    if (*itin == *itparse)
	    {
	      ++itin;
	    }
	    else
	    {
	      vedges.insert(itin, next);
	      vedges.erase(itparse);
	    }
	    current = next;
	    itparse = itin;
	  }
	  else
	    ++itparse;
	}

//	e.clear();/
//	int* current = edges.front();
//	edges.pop_front();
//	e.push_front(current);
//
//	while (edges.size() > 0)
//	{
//		list<int*>::iterator itt = edges.begin();
//		while (edges.size() > 0)
//		{
//			int* next = *itt;
//			if (next[2] == current[3])
//			{
//				edges.erase(itt);
//				e.push_back(next);
//				current = next;
//				break;
//			}
//			++itt;
//		}
//	}
}

/**
 * Retrieve a list of the triangles a particular vertex is a member of, considering only
 * triangles in the specified tessellation/level.
 * <p>
 * Lazy evaluation is used here. The list of indexes is initially empty and is computed and
 * stored on demand. Once computed the indexes remain in memory for the next time they might be
 * called.
 *
 * For parallel application where thread safety of the GeoTessGrid object
 * is important, this method will need to be synchronized.
 *
 * @param tessId tessellation index
 * @param level index of a level relative to the first level of the specified tessellation
 * @param vertex
 * @return list of triangle indeces
 */
const vector<int>& GeoTessGrid::getVertexTriangles(const int& tessId, const int& level, const int& vertex) const
{
	int lvl = getLevel(tessId, level);

	if (vtxTriangles.size() == 0) vtxTriangles.resize(nLevels);
	vector< vector<int> >& vtxT = vtxTriangles[lvl];

	if (vtxT.size() == 0)
	{
		vtxT.resize(nVertices);

		for (int t = levels[lvl][0]; t < levels[lvl][1]; ++t)
		{
			for (int c = 0; c < 3; ++c)
			{
				int v = triangles[t][c];
				vector<int>& vtxTv = vtxT[v];
				if (vtxTv.size() == 0) vtxTv.reserve(6);

				// Add the index of triangle t to the list of triangles that
				// vertex belongs to.
				// if (!vtxT[v].contains(t))
				vtxTv.push_back(t);
			}
		}
	}
	return vtxTriangles[lvl][vertex];
}

/**
 * For every level of every multi-level tessellation, there may be stored an nVerteces x n array
 * containing the indeces of the triangles for which each vertex is a member. This method will
 * clear some of that information by setting the entry for the specified tessId/level to null.
 *
 * For parallel application where thread safety of the GeoTessGrid object
 * is important, this method will need to be synchronized.
 *
 * @param tessId
 * @param level
 */
void GeoTessGrid::clearVertexTriangles(const int& level)
{
	if (vtxTriangles.size() > 0) vtxTriangles[level].resize(0);
}

/**
 * Clear (set to null) vtxTriangles which stores a record of which triangles each vertex is a
 * member of.
 *
 * For parallel application where thread safety of the GeoTessGrid object
 * is important, this method will need to be synchronized.
 *
 */
void GeoTessGrid::clearVertexTriangles()
{
	vtxTriangles.resize(0);
}

/**
 * Retrieve a list of the indexes of all the vertexes that are connected to the specified vertex
 * by a single edge, considering only triangles in the specified tessellation and level. The
 * vertices will be arranged in clockwise order when viewed from outside the unit sphere.
 *
 * @param tessId tessellation index
 * @param level index of a level relative to the first level of the specified tessellation
 * @param vertex
 * @return list of vertex indices in clockwise order.
 * @throws GeoTessException
 */
void GeoTessGrid::getVertexNeighborsOrdered(const int& tessId, const int& level, const int& vertex,
																						vector<int>& v)
{
	list<int*> edgelist;
	ArrayReuse<int> store;
	store.initialize(4, 12);
	getEdgeList(tessId, level, vertex, -1, store, edgelist);
	v.clear();
	v.reserve(edgelist.size());
	list<int*>::iterator it;
	for (it = edgelist.begin(); it != edgelist.end(); ++it) v.push_back((*it)[1]);
}

void GeoTessGrid::getVertexNeighbors(const int& tessId, const int& level, const int& vertex, const int& order,
																		 set<int>& nbrs)
{
	set<int> nghbrOrder;
	set<int>::iterator it;

	nbrs.clear();
	nbrs.insert(vertex);
	for (int o = 0; o < order; ++o)
	{
		nghbrOrder.clear();
		for (it = nbrs.begin(); it != nbrs.end(); ++it)
		{
			const vector<int>& tneighbors = getVertexTriangles(tessId, level, *it);
			for (int i = 0; i < (int) tneighbors.size(); ++i)
			{
				nghbrOrder.insert(getTriangleVertexIndex(tneighbors[i], 0));
				nghbrOrder.insert(getTriangleVertexIndex(tneighbors[i], 1));
				nghbrOrder.insert(getTriangleVertexIndex(tneighbors[i], 2));
			}
		}
		for (it = nghbrOrder.begin(); it != nghbrOrder.end(); ++it)
			nbrs.insert(*it);
	}
	nbrs.erase(vertex);
}

/**
 * @return summary information about this GeoTessGrid object.
 */
string GeoTessGrid::toString()
{
	ostringstream os;
	os << "GeoTessGrid" << endl
		 << "gridID = " << gridID << endl
     << "Input Grid File : "
     << ((gridInputFile == "") ? "null" : gridInputFile) << endl
     << "Generated by software version : " << gridSoftwareVersion
     << "  " << gridGenerationDate << endl;
	if (gridOutputFile != "null")
		os << "  Output Grid File : " << gridOutputFile << endl;

	os << endl
		 << "nTessellations = " << nTessellations << endl
		 << "nLevels = " << nLevels << endl
		 << "nVertices = " << nVertices << endl
		 << "nTriangles = " << nTriangles << endl << endl
     << "    Tess    Level  LevelID     NTri    First   Last+1" << endl;

	char buf[300];
	for (int tess = 0; tess < nTessellations; ++tess)
	{
		for (int level = 0; level < getNLevels(tess); ++level)
		{
			sprintf(buf, " %6d %8d %8d %8d %8d %8d", tess, level,
							tessellations[tess][0] + level,
							getNTriangles(tess, level),
							getTriangle(tess, level, 0),
							getTriangle(tess, level, getNTriangles(tess, level)));
			os << buf << endl;
		}
		os << endl;
	}
	return os.str();
}

/**
 * For every edge of every triangle, compute the inward pointing normal to the edge, i.e., in a
 * triangle formed by nodes ni, nj and nk, set edge[i] = nk cross nj.
 * <p>
 * This variable can be large and hence it is optional. If it is computed then the triangle
 * walking algorithm will be much faster because it will only have to compute dot products
 * instead of scalar triple products. If it remains null (is never computed) then the walking
 * triangle algorithm will be slower.
 * <p>
 * Whether or not edges are computed is determined by the setting of variable
 * <it>optimization</it>. If set to SPEED, the edges are computed, otherwise they are not.
 */
void GeoTessGrid::findEdges()
{
	if (edges != NULL) CPPUtils::delete3DArray<double>(edges);
	edges = CPPUtils::new3DArray<double>(nTriangles, 3, 3);
	for (int i = 0; i < nTriangles; ++i)
	{
		int* triangle = triangles[i];
		double** n = edges[i];

		GeoTessUtils::cross(vertices[triangle[2]],
				vertices[triangle[1]], n[0]);
		GeoTessUtils::cross(vertices[triangle[0]],
				vertices[triangle[2]], n[1]);
		GeoTessUtils::cross(vertices[triangle[1]],
				vertices[triangle[0]], n[2]);
	}
}

/**
 * Perform walking triangle search to find the index of the triangle that contains position
 * defined by vector and which has no descendant.
 *
 * @param triangleIndex the index of a triangle from which to start the search.
 * @param vector the unit vector representing the position for which to search.
 * @return the index of the triangle containing the specified position.
 */
int GeoTessGrid::getTriangle(int triangleIndex, double* vector)
{
	if (edges == NULL)
	{
		while (true)
		{
			if (GeoTessUtils::scalarTripleProduct(
					vertices[triangles[triangleIndex][2]],
					vertices[triangles[triangleIndex][1]], vector) > -1e-15)
			{
				if (GeoTessUtils::scalarTripleProduct(
						vertices[triangles[triangleIndex][0]],
						vertices[triangles[triangleIndex][2]], vector) > -1e-15)
				{
					if (GeoTessUtils::scalarTripleProduct(
							vertices[triangles[triangleIndex][1]],
							vertices[triangles[triangleIndex][0]], vector) > -1e-15)
					{
						if (neighbors[triangleIndex][3] < 0)
							return triangleIndex;
						else
							triangleIndex = neighbors[triangleIndex][3];
					}
					else
						triangleIndex = neighbors[triangleIndex][2];
				}
				else
					triangleIndex = neighbors[triangleIndex][1];
			}
			else
				triangleIndex = neighbors[triangleIndex][0];
		}
	}
	else
	{
		while (true)
		{
			if (GeoTessUtils::dot(edges[triangleIndex][0], vector) > -1e-15)
			{
				if (GeoTessUtils::dot(edges[triangleIndex][1], vector) > -1e-15)
				{
					if (GeoTessUtils::dot(edges[triangleIndex][2], vector) > -1e-15)
					{
						if (neighbors[triangleIndex][3] < 0)
							return triangleIndex;
						else
							triangleIndex = neighbors[triangleIndex][3];
					}
					else
						triangleIndex = neighbors[triangleIndex][2];
				}
				else
					triangleIndex = neighbors[triangleIndex][1];
			}
			else
				triangleIndex = neighbors[triangleIndex][0];
		}
	}
	return -1; // this is impossible!
}

/**
 * Identify the neighbors and descendants of each triangle. This method is called during
 * construction of a GeoTessGrid object (i.e., when it is loaded from a file). Applications
 * should not call this method.
 *
 * <p>
 * If optimization is set to SPEED, then for each edge of a triangle the unit vector normal to
 * the plane of the great circle containing the edge will be computed during input of the grid
 * from file and stored in memory. With this information, the walking triangle algorithm can use
 * dot products instead of scalar triple products when determining if a point resides inside a
 * triangle. While much more computationally efficient, it requires alot of memory to store all
 * those unit vectors.
 *
 * @param optimization
 * @return
 */
int** GeoTessGrid::findNeighbors()
{
	//long timer = System.nanoTime();

	neighbors = CPPUtils::new2DArray<int>(nTriangles, 4);

	// First: find the 3 neighbors of each triangle.
	// This is an implementation of the "Triangle neighbor identification"
	// algorithm in Ballard, Hipp and Young, 2009,
	// Efficient and Accurate Calculation of Ray Theory Seismic Travel
	// Time through Variable Resolution 3D Earth Models, SRL, 80, 989-999.

	int t, n, c, vj, vk;
	int* corners;

	bool* marked = new bool[nTriangles]; // initial values are
	CPPUtils::resetArray<bool>(nTriangles, marked, false);

	// all false

	// loop over all the levels of all tessellations
	for (int tess = 0; tess < nTessellations; ++tess)
		for (int level = 0; level < getNLevels(tess); ++level)
		{
			for (int i = 0; i < getNTriangles(tess, level); ++i)
			{
				t = getTriangle(tess, level, i);

				corners = triangles[t];
				// t is the index of a triangle and corners are the indexes
				// of the 3 vertices that reside at the corners of triangle
				// t.
				// Loop over corners of triangle t
				for (c = 0; c < 3; ++c)
				{
					vj = corners[(c + 1) % 3];
					vk = corners[(c + 2) % 3];
					// c is the index of a corner of triangle t (c is one of
					// 0,1,2).
					// vj is the index of the vertex (not corner) that is
					// found by
					// moving clockwise around t from corner c.
					// vk is the index of the vertex (not corner) that is
					// found by
					// moving clockwise around t from vj

					// indexes of the triangles of which vj is a member
					const vector<int>& tj = getVertexTriangles(tess, level, vj);

					// mark all the triangles of which vertex vj is a member
					for (n = 0; n < (int) tj.size(); ++n) marked[tj[n]] = true;

					// indexes of the triangles of which vk is a member
					const vector<int>& tk = getVertexTriangles(tess, level, vk);

					// loop over all the triangles of which vk is a member.
					// Two of them will be marked. One of the ones that is
					// marked is triangle t. The other one is the triangle
					// that resides on the other side of the edge that
					// connects
					// vertices vj and vk. That second triangle is the
					// neighbor
					// of triangle t.
					for (n = 0; n < (int) tk.size(); ++n)
						if (marked[tk[n]] && (tk[n] != t))
						{
							neighbors[t][c] = tk[n];
							break;
						}

					// unmark all the triangles that were recently marked.
					for (n = 0; n < (int) tj.size(); ++n) marked[tj[n]] = false;
				}
			}
			clearVertexTriangles(getLevel(tess, level));
		}

	clearVertexTriangles();

	// done with marked
	delete [] marked;

	// for every edge of every triangle, find the inward pointing normal to
	// the edge, i.e., in a triangle with nodes ni, nj, and nk, set
	// edge[i] = nk cross nj.
	if (*optimization == OptimizationType::SPEED)
		findEdges();

	// Second: find the descendant of each triangle at the next higher
	// tessellation level.

	// initialize all the descendants to -1.
	for (int i = 0; i < nTriangles; ++i)
		neighbors[i][3] = -1;

	double len;
	double* v;
	double x[3];

	// loop over all but the last level of each tessellations. The
	// descendants of elements
	// on the last level of each tessellation will remain -1.
	for (int tess = 0; tess < nTessellations; ++tess)
		for (int i = 0; i < getNLevels(tess) - 1; ++i)
		{
			int startTriangle = getTriangle(tess, i + 1, 0);

			for (int j = 0; j < getNTriangles(tess, i); ++j)
			{
				t = getTriangle(tess, i, j);
				// t is the index of a triangle.
				corners = triangles[t];

				// set x.vector to a unit vector at center of triangle t
				v = vertices[corners[0]];
				x[0] = v[0];
				x[1] = v[1];
				x[2] = v[2];
				v = vertices[corners[1]];
				x[0] += v[0];
				x[1] += v[1];
				x[2] += v[2];
				v = vertices[corners[2]];
				x[0] += v[0];
				x[1] += v[1];
				x[2] += v[2];
				len = sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
				x[0] /= len;
				x[1] /= len;
				x[2] /= len;

				// start from startTriangle, which is on the next higher
				// level from the
				// current triangle, and walk to the triangle that contains
				// x.vector.
				// That is the descendant of the current triangle.
				// Set startTriangle equal to the descendant so we won't
				// have to walk too
				// far when we search for the descendant of the next
				// triangle, which is likely
				// close by (this makes a huge difference).
				neighbors[t][3] = startTriangle = getTriangle(
						startTriangle, x);
			}
		}

	//timer = System.nanoTime() - timer;
	// System.out.printf("GeoTessfindNeighbors() execution time = %1.6f%n", timer * 1e-9);
	return neighbors;
}

/**
 * Tests the integrity of the grid. Visits every triangle T, and (1) checks
 * to ensure that every neighbor of T includes T in its list of neighbors,
 * and (2) checks that every neighbor of T shares exactly two nodes with T.
 *
 * @throws GeoTessException
 *             if anything is amiss.
 */
void GeoTessGrid::testGrid()
{
	for (int tessId = 0; tessId < getNTessellations(); ++tessId)
		for (int level = 0; level < getNLevels(tessId); ++level)
		{
			for (int t = 0; t < getNTriangles(tessId, level); ++t)
			{
				// triangle is an index in the triangle array
				int triangle = getTriangle(tessId, level, t);

				for (int triangleSide = 0; triangleSide < 3; triangleSide++)
				{
					// neighbor and triangle share and edge
					int neighbor = getNeighbor(triangle, triangleSide);

					// the index of triangle in neighbors array of neighbors.
					int neighborSide = getNeighborIndex(neighbor, triangle);

					if (neighborSide < 0)
					{
						ostringstream os;
						os << endl << "ERROR in GeoTessGrid::testGrid" << endl
							 << "tessId="<<tessId<<" level="<<level<<" triangle="<<triangle
							 <<" side="<<triangleSide<<" neighbor="<<neighbor << endl;
						throw GeoTessException(os, __FILE__, __LINE__, 2005);
					}

					// compare the vertices.
					if (triangles[neighbor][(neighborSide+1)%3] != triangles[triangle][(triangleSide+2)%3])
					{
						ostringstream os;
						os << endl << "ERROR in GeoTessGrid::testGrid" << endl
							 << "tessId="<<tessId<<" level="<<level<<" triangle="<<triangle
							 <<" side="<<triangleSide<<" neighbor="<<neighbor << endl
							 << "triangles[neighbor][(neighborSide+1)%3] != triangles[triangle][(triangleSide+2)%3]"
							 << endl;
						throw GeoTessException(os, __FILE__, __LINE__, 2006);
					}

					if (triangles[neighbor][(neighborSide+2)%3] != triangles[triangle][(triangleSide+1)%3])
					{
						ostringstream os;
						os << endl << "ERROR in GeoTessGrid::testGrid" << endl
							 << "tessId="<<tessId<<" level="<<level<<" triangle="<<triangle
							 <<" side="<<triangleSide<<" neighbor="<<neighbor << endl
							 << "triangles[neighbor][(neighborSide+2)%3] != triangles[triangle][(triangleSide+1)%3]"
							 << endl;
						throw GeoTessException(os, __FILE__, __LINE__, 2007);
					}

				}

				// if not the top level then check descendants.
				if (level < getNLevels(tessId)-1)
				{
					if (neighbors[triangle][3] <= getLastTriangle(tessId, level))
					{
						ostringstream os;
						os << endl << "ERROR in GeoTessGrid::testGrid" << endl
							 << "tessId="<<tessId<<" level="<<level<<" triangle="<<triangle
							 << "neighbors[triangle][3] <= getLastTriangle(tessId, level)"
							 << endl;
						throw GeoTessException(os, __FILE__, __LINE__, 2008);
					}

					if (neighbors[triangle][3] > getLastTriangle(tessId, level+1))
					{
						ostringstream os;
						os << endl << "ERROR in GeoTessGrid::testGrid" << endl
							 << "tessId="<<tessId<<" level="<<level<<" triangle="<<triangle
							 << "neighbors[triangle][3] > getLastTriangle(tessId, level+1)"
							 << endl;
						throw GeoTessException(os, __FILE__, __LINE__, 2009);
					}
				}
			}
		}
}

/**
 * Load the 2D grid from an InputStream, which is neither opened nor closed by this method.
 *
 * @param input
 * @throws IOException
 */
GeoTessGrid* GeoTessGrid::loadGridBinary(IFStreamBinary& ifs)
{
	int gridFileFormat;
	string grdSWVersion, fileCreationDate;

	loadGridBinaryFront(ifs, gridFileFormat, grdSWVersion, fileCreationDate,
			                gridID, gridInputFile);

	setGridSoftwareVersion(grdSWVersion);
	setGridGenerationDate(fileCreationDate);

	nTessellations = ifs.readInt();
	tessellations = CPPUtils::new2DArray<int>(nTessellations, 2);
	nLevels = ifs.readInt();
	levels = CPPUtils::new2DArray<int>(nLevels, 2);
	nTriangles = ifs.readInt();
	triangles = CPPUtils::new2DArray<int>(nTriangles, 3);
	nVertices = ifs.readInt();
	vertices = CPPUtils::new2DArray<double>(nVertices, 3);

	for (int i = 0; i < nTessellations; ++i)
	{
		int* a = tessellations[i];
		a[0] = ifs.readInt();
		a[1] = ifs.readInt();
	}

	for (int i = 0; i < nLevels; ++i)
	{
		int* a = levels[i];
		a[0] = ifs.readInt();
		a[1] = ifs.readInt();
	}

	for (int i = 0; i < nVertices; ++i)
	{
		double* a = vertices[i];
		a[0] = ifs.readDouble();
		a[1] = ifs.readDouble();
		a[2] = ifs.readDouble();
	}

	for (int i = 0; i < nTriangles; ++i)
	{
		int* a = triangles[i];
		a[0] = ifs.readInt();
		a[1] = ifs.readInt();
		a[2] = ifs.readInt();
	}

	findNeighbors();

	return this;
}

void GeoTessGrid::loadGridBinaryFront(IFStreamBinary& ifs, int& gridFileFormat,
																			string& gridSWVersion, string& fileCreationDate,
																			string& gridid, const string& grdInptFile)
{
	// get the GeoTess name (GEOTESSGRID) and validate

	string GTName;
	ifs.readCharArray(GTName, 11);
	if (GTName != "GEOTESSGRID")
	{
		ostringstream os;
		os << endl << "ERROR in GeoTessGrid::loadGridBinary" << endl
		<< "  expected file : " << grdInptFile << endl
		<< "  to start with char array \"GEOTESSGRID\" as first entry "
		<< "but found \"";
		for (int i = 0; i < 11; ++i)
		if ((GTName[i] != 127) && (GTName[i] > 31))
		os << GTName[i];
		else
		os << "[" << (int) GTName[i] << "]";
		os << "\" instead ..." << endl;

		throw GeoTessException(os, __FILE__, __LINE__, 2010);
	}

	// read in format ... if it exceeds 65536 then turn on byte reversal,
	// reset read pointer back by sizeof(int) and try again.

	gridFileFormat = ifs.readInt();
	if ((gridFileFormat < 0) || (gridFileFormat > 65536))
	{
		ifs.setByteOrderReverse(!ifs.isByteOrderReversalOn());
		ifs.decrementPos(CPPUtils::SINT);
		gridFileFormat = ifs.readInt();
	}

	if (gridFileFormat != 2)
	{
		ostringstream os;
		os << endl << "ERROR in GeoTessGrid::loadGridBinary" << endl
		<< gridFileFormat << " is not a recognized file format version" << endl;
		throw GeoTessException(os, __FILE__, __LINE__, 2011);
	}

	// read grid population software version and file creation date

	gridSWVersion = "";
	ifs.readString(gridSWVersion);
	fileCreationDate = "";
	ifs.readString(fileCreationDate);

	// read grid data

	ifs.readString(gridid);
}


void GeoTessGrid::writeGrid(const string& fileName)
{
	if (fileName.find(".ascii", fileName.length() - 6) != string::npos)
		writeGridAscii(fileName);
	else
		writeGridBinary(fileName);

	gridOutputFile = fileName;
}

/**
 * Write the 2D grid to a file.
 *
 * @param output
 * @throws IOException
 */
void GeoTessGrid::writeGridBinary(const string& fileName)
{
	IFStreamBinary ofs;
	if (!CPPUtils::isBigEndian()) ofs.byteOrderReverseOn();
	ofs.boundaryAlignmentOff();

	writeGridBinary(ofs);
	ofs.writeToFile(fileName);
}

/**
 * Write the 2D grid to a file.
 *
 * @param output
 * @throws IOException
 */
void GeoTessGrid::writeGridBinary(IFStreamBinary& output)
{
	// write out file type identifier ("GEOTESSGRID"), format version,
	// code version, and data stamp

	output.writeCharArray("GEOTESSGRID", 11);
	output.writeInt(2);
	output.writeString(getGridSoftwareVersion());
	output.writeString(getGridGenerationDate());

	// write grid data

	output.writeString(gridID);

	output.writeInt(nTessellations);
	output.writeInt(nLevels);
	output.writeInt(nTriangles);
	output.writeInt(nVertices);

	for (int i = 0; i < nTessellations; ++i)
	{
		output.writeInt(tessellations[i][0]);
		output.writeInt(tessellations[i][1]);
	}

	for (int i = 0; i < nLevels; ++i)
	{
		output.writeInt(levels[i][0]);
		output.writeInt(levels[i][1]);
	}

	for (int i = 0; i < nVertices; ++i)
	{
		output.writeDouble(vertices[i][0]);
		output.writeDouble(vertices[i][1]);
		output.writeDouble(vertices[i][2]);
	}

	for (int i = 0; i < nTriangles; ++i)
	{
		output.writeInt(triangles[i][0]);
		output.writeInt(triangles[i][1]);
		output.writeInt(triangles[i][2]);
	}
}

/**
 * Write the grid out to an ascii file. File format 2.
 */
void GeoTessGrid::writeGridAscii(const string& fileName)
{
	IFStreamAscii ifs;
	ifs.openForWrite(fileName);
	writeGridAscii(ifs);
	ifs.close();
}

/**
 * Write the grid out to an ascii file. File format 2.
 */
void GeoTessGrid::writeGridAscii(IFStreamAscii& output)
{
	// write out file type identifier ("GEOTESSGRID"), format version,
	// code version, and data stamp

	output.writeStringNL("GEOTESSGRID");
	output.writeStringNL("2");
	output.writeStringNL(getGridSoftwareVersion());
	output.writeStringNL(getGridGenerationDate());

	// write grid data

	output.writeStringNL("#unique Grid ID:");
	output.writeStringNL(gridID);

	output.writeStringNL("#geotess grid java: nTessellations, nLevels, nTriangles, nVertices:");
	output.writeInt(nTessellations); output.writeString(" ");
	output.writeInt(nLevels); output.writeString(" ");
	output.writeInt(nTriangles); output.writeString(" ");
	output.writeIntNL(nVertices);

	output.writeStringNL("#geotess grid tessellations:");
	for (int i = 0; i < nTessellations; ++i)
	{
		output.writeInt(tessellations[i][0]); output.writeString(" ");
		output.writeIntNL(tessellations[i][1]);
	}

	output.writeStringNL("#geotess grid levels:");
	for (int i = 0; i < nLevels; ++i)
	{
		output.writeInt(levels[i][0]); output.writeString(" ");
	  output.writeIntNL(levels[i][1]);
	}

	output.writeStringNL("#geotess grid vertices(unit_vectors):");
	for (int i = 0; i < nVertices; ++i)
	{
		output.writeDouble(vertices[i][0]); output.writeString(" ");
		output.writeDouble(vertices[i][1]); output.writeString(" ");
		output.writeDoubleNL(vertices[i][2]);
	}

	output.writeStringNL("#geotess grid triangles:");
	for (int i = 0; i < nTriangles; ++i)
	{
		output.writeInt(triangles[i][0]); output.writeString(" ");
		output.writeInt(triangles[i][1]); output.writeString(" ");
		output.writeIntNL(triangles[i][2]);
	}

	output.flush();
}

} // end namespace geotess
