//
// Created by minseok on 11/09/18.
//
#include <Eigen/Geometry>
#include "BVHparser.h"
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <fstream>
#include <vector>
#include <string>
#include <math.h>
#include <algorithm>
#define M_PI 3.14159265358979323846

using namespace std;

MotionNode::MotionNode()
{
	name = "";
	axisOrder = "";
	isRoot = false;
	isEnd = false;
	parent = nullptr;
	next = nullptr;
	childs = vector<MotionNode*>();
	channelNum = 0;
	// orientation = Eigen::AngleAxisd::Identity();
}
MotionNode* MotionNode::getParent()
{
	return parent;
}
vector<MotionNode*> MotionNode::getChilds()
{
	return childs;
}
void MotionNode::setParent(MotionNode* pnode)
{
	parent = pnode;
	pnode->addChild(this);
}
void MotionNode::addChild(MotionNode* cnode)
{
	childs.push_back(cnode);
}
void MotionNode::setRoot()
{
	isRoot = true;
}
void MotionNode::setEnd()
{
	isEnd = true;
}
void MotionNode::setName(string mname)
{
	name = mname;
}
void MotionNode::setAxisOrder(string maxisOrder)
{
	axisOrder = maxisOrder;
}
void MotionNode::setOffset(float x, float y, float z)
{
	offset[0] = x;
	offset[1] = y;
	offset[2] = z;
}
void MotionNode::setNext(MotionNode *nextNode)
{
	next = nextNode;
}
void MotionNode::setChannelNum(int mchannelNum)
{
	channelNum = mchannelNum;
}
bool MotionNode::checkEnd()
{
	return isEnd;
}
string MotionNode::getName_std()
{
	if(match_name_list.size()>=1)
		return match_name_list[0];
	else
	{
		cout<<"no such name in list"<<name<<endl;
		return "";
	}
}
string MotionNode::getName()
{
	return name;
}
string MotionNode::getAxisOrder()
{
	return axisOrder;
}
void MotionNode::getOffset(float* outOffset)
{
	for(int i=0;i<3;i++)
	{
		outOffset[i] = offset[i];
	}
}
float MotionNode::getOffset(int index)
{
	return offset[index];
}
int MotionNode::getChannelNum()
{
	return channelNum;
}
MotionNode* MotionNode::getNextNode()
{
	return next;
}
void MotionNode::initData(int frames)
{
	data = new float*[frames];
	for(int i = 0; i < frames; i++)
	{
		data[i] = new float[channelNum];
	}
}
bool MotionNode::ContainedInModel()
{
	if(match_name_list.size()==0)
		return false;
	else
		return true;
}
/// convert quaternion -> log(quaternion)
Eigen::Vector3d QuaternionToAngleAxis(Eigen::Quaterniond qt)
{
	double angle = 2.0*atan2(qt.vec().norm(), qt.w());
	if(std::abs(angle) < 1e-4)
		return Eigen::Vector3d::Zero();
	return angle * qt.vec().normalized();
}

/// convert log(quaternion) -> quaternion
Eigen::Quaterniond AngleAxisToQuaternion(Eigen::Vector3d angleAxis)
{
	Eigen::Quaterniond qt;
	if(angleAxis.norm() < 1e-4)
		return Eigen::Quaterniond(1,0,0,0);
	qt.vec() = angleAxis.normalized() * sin(angleAxis.norm());
	qt.w() = cos(angleAxis.norm());
	return qt;
}

void BVHparser::initMatchNameListForMotionNode(const char* path)
{
	string line;
	stringstream s;
	ifstream in(path);
    // std::cout<<"# reading file "<<path<<std::endl;
	string muscleName_std;
	string muscleName;
	MotionNode* curNode = getRootNode();
	float value;
	while(!in.eof())
	{
		getline(in, line);
		if(line.empty())
			break;
		s = stringstream(line);
		std::vector<string> match_name_list;
		while(!s.eof())
		{
			s>>muscleName;
			match_name_list.push_back(muscleName);
		}
		curNode = getRootNode();
		while(curNode!=NULL)
		{
			for(auto& name : match_name_list)
			{
				if(curNode->getName() == name)
				{
					curNode->match_name_list = match_name_list;
					break;
				}
			}
			curNode = curNode->getNextNode();
		}
	}
	in.close();
}

// BVHparser::BVHparser(const char* path)
// {

// }

BVHparser::BVHparser(const char* path, BVHType bvhType)
{
	mBvhType = bvhType;
	this->scale = 1.0;
	int lineNum = 0;
	int channelNum = 0;
	string channels[6];
	float offx, offy, offz;
	mPath = path;
	lowerBodyOnly = false;
	upperBodyOnly = false;
	ifstream in;
	in.open(path, ios::in);
	if(!in)
	{
		cerr << "Cannot open "<<path<<endl; exit(1);
	}
	string line;
	getline(in, line);										//HIERARCHY
	lineNum++;

	if(line.compare(0,9,"HIERARCHY")!=0)
	{
		cout << "Check the file format. The line number "<<lineNum<<" is not fit to the format"<<endl;
		cout<<line.compare("HIERARCHY")<<endl;
		cout<<line.length()<<endl;
		cout << line<<endl;
		exit(-1);
	}
	getline(in, line);										//ROOT Hips
	lineNum++;
	rootNode = new MotionNode();
	rootNode->setRoot();
	istringstream s(line);
	string bvh_keyword;
	string bvh_nodeName;
	s >> bvh_keyword; s >> bvh_nodeName;
	if(bvh_keyword.compare("ROOT")!=0)
	{
		cout << "Check the file format. The line number "<<lineNum<<" is not fit to the format"<<endl;
		cout << line<<endl;
		exit(-1);
	}
	rootNode->setName(bvh_nodeName);
	getline(in, line);										//{

	getline(in, line);										//	OFFSET 0.00 0.00 0.00
	s.str("");
	s = istringstream(line);
	s >> bvh_keyword; s >> offx; s >> offy; s >> offz;
	if(mBvhType == BVHType::BASKET)
		rootNode->setOffset(offx, offy-85.5177, offz);
	else
		rootNode->setOffset(offx, offy, offz);

	getline(in, line);										//	CHANNELS 6 Xposition Yposition Zposition Xrotation Yrotation Zrotation
	s.str("");
	s = istringstream(line);
	s >> bvh_keyword; s >> channelNum;
	string newAxisOrder = "";
	for(int i = 0; i < channelNum; i++)
	{
		s >> channels[i];
		if(channels[i].substr(1) == "rotation")
		{
			transform(channels[i].begin(), channels[i].end(), channels[i].begin(), ::tolower);
			newAxisOrder += channels[i][0];
		}
	}
	rootNode->setAxisOrder(newAxisOrder);
	rootNode->setChannelNum(channelNum);
	MotionNode* prevNode = rootNode;
	MotionNode* prevNode4NextNode = rootNode;
	getline(in, line);
	while(line.compare(0, 6, "MOTION") != 0)
	{
		s.str("");
		s = istringstream(line);
		s >> bvh_keyword; s >> bvh_nodeName;
		if(bvh_keyword=="JOINT")							//	JOINT LeftUpLeg
		{
			MotionNode *newNode = new MotionNode();
			newNode->setName(bvh_nodeName);

			getline(in, line);								//	{
			getline(in, line);								//		OFFSET 3.64953 0.00000 0.00000
			s.str("");
			s = istringstream(line);
			s >> bvh_keyword; s >> offx; s >> offy; s >> offz;
			newNode->setOffset(offx, offy, offz);

			getline(in, line);								//		CHANNELS 3 Xrotation Yrotation Zrotation
			s.str("");
			s = istringstream(line);
			s >> bvh_keyword; s >> channelNum;

			newAxisOrder = "";
			for(int i = 0;i < channelNum;i++)
			{
				s >> channels[i];
				if(channels[i].substr(1) == "rotation")
				{
					transform(channels[i].begin(), channels[i].end(), channels[i].begin(), ::tolower);
					newAxisOrder += channels[i][0];
				}
			}
			newNode->setParent(prevNode);
			newNode->setChannelNum(channelNum);
			newNode->setAxisOrder(newAxisOrder);
			prevNode4NextNode->setNext(newNode);
			prevNode = newNode;
			prevNode4NextNode = newNode;
		}
		else if(bvh_keyword =="End")						//	End Site
		{
			MotionNode *newNode = new MotionNode();
			newNode->setName(prevNode->getName()+bvh_nodeName);
			newNode->setEnd();
			getline(in, line);								//	{
			getline(in, line);								//		OFFSET 3.64953 0.00000 0.00000
			s.str("");
			s = istringstream(line);
			s >> bvh_keyword; s >> offx; s >> offy; s >> offz;
			newNode->setOffset(offx, offy, offz);

			newNode->setParent(prevNode);
			getline(in, line);								//	}
		}
		else if(bvh_keyword =="}")
		{
			prevNode = prevNode->getParent();
		}
		else
		{
			cout << "Check the file format. "<< bvh_keyword <<" is right?" <<endl;
			exit(-1);
		}
		getline(in, line);
	}

// 	Start MotionNode										//MOTION
	string str1, str2;	//to get the string for format
	float fvalue;
	getline(in, line);										//Frames: 4692
	s.str("");
	s = istringstream(line);
	s >> str1; s >> fvalue;
	frames = fvalue;

	getline(in, line);										//Frame Time: 0.008333
	s.str("");
	s = istringstream(line);
	s >> str1; s >> str2; s >> fvalue;
	frameTime = fvalue;

	// cout << "frames : " << frames <<", frame time : " << frameTime << endl;

	float f[6];
	MotionNode *curNode;
	curNode = rootNode;
	while(curNode != nullptr)
	{
		curNode->initData(frames);
		curNode = curNode->getNextNode();
	}

	for(int i = 0; i < frames; i++)
	{
		curNode = rootNode;
		getline(in, line);
		s.str("");
		s = istringstream(line);
		while(curNode != nullptr)
		{
			for(int j = 0; j < curNode->getChannelNum(); j++)
			{
				s >> f[j];
				curNode->data[i][j] = f[j];
			}
			curNode = curNode->getNextNode();
		}

	}
	in.close();
	Eigen::Quaterniond q;
	Eigen::Vector3d euler;
	curNode = rootNode;
	for(int i = 0; i < frames; i++)
	{
		q = Eigen::Quaterniond::Identity();
		for(int j = 0; j < 3; j++)
		{
			if(curNode->getAxisOrder().substr(j,1).compare("x") == 0)
			{
				q = q * Eigen::AngleAxisd(curNode->data[i][j+3]/360.0 * 2.0 * M_PI, Eigen::Vector3d::UnitX());
			}
			else if(curNode->getAxisOrder().substr(j,1).compare("y") == 0)
			{
				q = q * Eigen::AngleAxisd(curNode->data[i][j+3]/360.0 * 2.0 * M_PI, Eigen::Vector3d::UnitY());
			}
			else
			{
				q = q * Eigen::AngleAxisd(curNode->data[i][j+3]/360.0 * 2.0 * M_PI, Eigen::Vector3d::UnitZ());
			}
		}
		float theta;
		theta = atan2(sqrt(q.x()*q.x() + q.y()*q.y() + q.z()*q.z()), q.w());
		Eigen::Vector3d root_axis = Eigen::Vector3d(q.x(), q.y(), q.z());
		root_axis.normalize();
		curNode->data[i][3] = 2.0 * theta * root_axis.x();
		curNode->data[i][4] = 2.0 * theta * root_axis.y();
		curNode->data[i][5] = 2.0 * theta * root_axis.z();
	}
	curNode = curNode->getNextNode();
	while(curNode != nullptr)
	{
		for(int i = 0; i < frames; i++)
		{
			q = Eigen::Quaterniond::Identity();
			for(int j = 0; j < 3; j++)
			{
				if(curNode->getAxisOrder().substr(j,1).compare("x") == 0)
				{
					q = q * Eigen::AngleAxisd(curNode->data[i][j]/360.0 * 2.0 * M_PI, Eigen::Vector3d::UnitX());
				}
				else if(curNode->getAxisOrder().substr(j,1).compare("y") == 0)
				{
					q = q * Eigen::AngleAxisd(curNode->data[i][j]/360.0 * 2.0 * M_PI, Eigen::Vector3d::UnitY());
				}
				else
				{
					q = q * Eigen::AngleAxisd(curNode->data[i][j]/360.0 * 2.0 * M_PI, Eigen::Vector3d::UnitZ());
				}
			}

			float theta;
			theta = atan2(sqrt(q.x()*q.x() + q.y()*q.y() + q.z()*q.z()), q.w());
			Eigen::Vector3d root_axis = Eigen::Vector3d(q.x(), q.y(), q.z());
			root_axis.normalize();
			curNode->data[i][0] = 2.0 * theta * root_axis.x();
			curNode->data[i][1] = 2.0 * theta * root_axis.y();
			curNode->data[i][2] = 2.0 * theta * root_axis.z();
		}
		curNode = curNode->getNextNode();
	}
	// this->initMatchNameListForMotionNode("../motion/bodyNameMatch.txt");
}

MotionNode* BVHparser::getRootNode()
{
	return rootNode;
}
string bvhpath_2_skelpath_(const char* path)
{
	string pathString; pathString = path;
	if(pathString.find(".bvh") != std::string::npos)
	{
		pathString.erase(pathString.find(".bvh"), 5);
	}
	return pathString;
}

string getFileName_(const char* path)
{
	string pathString; pathString = path;
	int cur = 0;
	while(pathString.find("/") != std::string::npos)
	{
		pathString.erase(0, pathString.find("/")+1);
	}

	return pathString.substr(0, pathString.find("."));
}

Eigen::Vector3d getShaft(float* childOffset)
{
	Eigen::Vector3d shaft((fabs(childOffset[0])>fabs(childOffset[1]) && fabs(childOffset[0])>fabs(childOffset[2]))? childOffset[0]/fabs(childOffset[0]): 0.0,
						(fabs(childOffset[1])>fabs(childOffset[0]) && fabs(childOffset[1])>fabs(childOffset[2]))? childOffset[1]/fabs(childOffset[1]) : 0.0,
						(fabs(childOffset[2])>fabs(childOffset[0]) && fabs(childOffset[2])>fabs(childOffset[1]))? childOffset[2]/fabs(childOffset[2]) : 0.0);
	return shaft;

}
//node's size is fixed to 0.1, 0.1, 0.1
#include <boost/filesystem.hpp>
// #define PATH_MAX 100
void BVHparser::writeSkelFile()
{
	ofstream outfile;

	boost::filesystem::path dir("../data/skels/");

	if(!(boost::filesystem::exists(dir))){
	    // std::cout<<"Doesn't Exists"<<std::endl;
	    if (boost::filesystem::create_directory(dir))
	        std::cout << "../data/skels directory was successfully created !" << std::endl;
	}
	// if(n == 0)
	outfile.open("../data/skels/"+getFileName_(mPath)+".skel", ios::trunc);

	char resolved_path[PATH_MAX];
	realpath("../", resolved_path);
	// printf("\n%s\n",resolved_path);

	std::string absolutePathProject = resolved_path;

	skelFilePath = absolutePathProject+"/data/skels/"+getFileName_(mPath)+".skel";
	// else
	// 	outfile.open(bvhpath_2_skelpath_(mPath)+"Mocap.skel", ios::trunc);
	outfile << fixed << showpoint;
	outfile << setprecision(4);
	outfile << "<?xml version=\"1.0\" ?>" <<endl;
	outfile << "<skel version=\"1.0\">" <<endl;
	// outfile << "    <world name=\"world 1\">" <<endl;
	outfile << "		<physics>"<<endl;
    outfile << "			<time_step>0.001</time_step>"<<endl;
    outfile << "			<gravity>0 -9.81 0</gravity>"<<endl;
    outfile << "			<collision_detector>bullet</collision_detector>"<<endl;
    outfile << "		</physics>"<<endl;
    outfile << "		<skeleton name=\""<< getFileName_(mPath) << "\">"<<endl;

    //outfile << "		<skeleton name=\"biped\">"<<endl;
    outfile << "			<transformation>0 0 0 0 0 0</transformation>"<<endl;
    MotionNode *curNode = rootNode;
    MotionNode *parentNode = nullptr;
    int tabCount = 3;
    float curOffset[3];
    float parentOffset[3];
    float childOffset[3];
    float resize_factor = 0.01;
    while(curNode != nullptr)
    {

    	parentNode = curNode->getParent();
    	curNode->getOffset(curOffset);
    	outfile << "			";
    	outfile << "<body name=\"" <<curNode->getName() << "\">" <<endl;

    	outfile << "			";

    	// Eigen::AngleAxisd curOrientation = Eigen::AngleAxisd::Identity();
    	while(parentNode!= nullptr)
    	{
    		parentNode->getOffset(parentOffset);
    		for(int i = 0; i<3; i++)
    		{
    			curOffset[i] += parentOffset[i];
    		}
    		// curOrientation = parentNode->orientation * curOrientation;
    		parentNode = parentNode->getParent();
    	}
    	outfile << "	<transformation>";

	   	outfile << curOffset[0]*resize_factor <<" "<< curOffset[1]*resize_factor<<" "<< curOffset[2]*resize_factor<<" ";

    	if(curNode->getChilds().size()==1)
    	{
    		curNode->getChilds()[0]->getOffset(childOffset);
    		Eigen::Vector3d shaft = getShaft(childOffset);

    		if(shaft.norm() == 0.0)
    		{
    			outfile <<"0 0 0</transformation>" <<endl;
	   		}
    		else
    		{
    			Eigen::Vector3d targetShaft(childOffset[0], childOffset[1], childOffset[2]);

    			targetShaft.normalize();
    			shaft = getShaft(childOffset);

    			Eigen::VectorXd axis = shaft.cross(targetShaft);
    			double cosAngle = shaft.dot(targetShaft);
    			double angle = atan2(axis.norm(), cosAngle);
    			axis.normalize();
    			axis*= angle;
    			// Eigen::AngleAxisd mNodeOrientation = Eigen::AngleAxisd(angle, axis);

    			// curNode->orientation = mNodeOrientation;
    			// axis = mNodeOrientation.axis();

    			outfile<<axis[0]<<" "<<axis[1]<<" "<<axis[2]<<"</transformation>"<<endl;
    		}
    	}
    	else
    	{
    		outfile <<"0 0 0</transformation>" <<endl;
    	}

    	outfile << "			";
    	outfile << "	<inertia>" <<endl;

    	outfile << "			";
    	if(curNode->getName().compare(0, 4, "Head")==0)
    	{
    		outfile <<	"		<mass>"<<8.0<<"</mass>"<<endl;
    	}
    	else if(curNode->getName().compare(0, 5, "Spine")==0)
    	{
    		curNode->getChilds()[0]->getOffset(childOffset);
       		outfile << "		<mass>";
       		outfile <<5.0
       		<<"</mass>"<<endl;
    	}
       	else if(curNode->getChilds().size()==1)
       	{
       		curNode->getChilds()[0]->getOffset(childOffset);
	    	outfile << "		<mass>"
	    	<<((fabs(childOffset[0])>fabs(childOffset[1]) && fabs(childOffset[0])>fabs(childOffset[2]))? fabs(childOffset[0]) : 5.0 )
	    	*((fabs(childOffset[1])>fabs(childOffset[0]) && fabs(childOffset[1])>fabs(childOffset[2]))? fabs(childOffset[1]) : 5.0 )
	    	*((fabs(childOffset[2])>fabs(childOffset[0]) && fabs(childOffset[2])>fabs(childOffset[1]))? fabs(childOffset[2]) : 5.0 ) *resize_factor
	    	<<"</mass>" <<endl;
    	}
    	else
    	{
    		outfile <<	"		<mass>"<<4.0<<"</mass>"<<endl;
    	}
    	outfile << "			";
    	outfile << "		<offset>0.0 0 0.0</offset>" <<endl;

    	outfile << "			";
    	outfile << "	</inertia>" <<endl;

    	outfile << "			";
    	outfile << "	<visualization_shape>" <<endl;

    	outfile << "			";

   		curNode->getChilds()[0]->getOffset(childOffset);

    	Eigen::VectorXd shaftScaled = getShaft(childOffset);
    	double length = sqrt(pow(childOffset[0],2)+pow(childOffset[1],2)+pow(childOffset[2],2));
    	shaftScaled*= length;
   		outfile << "		<transformation>"
  		<< (shaftScaled[0])/2.0 * resize_factor<<" "
   		<< (shaftScaled[1])/2.0 * resize_factor<<" "
   		<< (shaftScaled[2])/2.0 * resize_factor<<" "
   		<<" 0 0 0</transformation>" <<endl;

    	outfile << "			";
    	outfile << "		<geometry>" <<endl;

    	outfile << "			";
    	outfile << "			<box>" <<endl;

    	outfile << "			";
    	if(curNode->getName().compare(0, 4, "Head")==0)
    	{
    		outfile <<	"				<size>0.3 0.3 0.3</size>"<<endl;
    	}
    	if(mBvhType == BVHType::BASKET && curNode->getName().compare(0, 5, "Spine")==0)
    	{
    		curNode->getOffset(curOffset);
    		curNode->getChilds()[0]->getOffset(childOffset);
       		outfile << "				<size>";

	       		outfile <<sqrt(pow(childOffset[0],2)+pow(childOffset[1],2)+pow(childOffset[2],2)) *resize_factor<<" "
	       		<<"0.3 0.3</size>"<<endl;

    	}
       	else if(curNode->getChilds().size()==1)
       	{
       		curNode->getChilds()[0]->getOffset(childOffset);
       		outfile << "				<size>";

    		Eigen::Vector3d shaft = getShaft(childOffset);
    		shaft *= sqrt(pow(childOffset[0],2)+pow(childOffset[1],2)+pow(childOffset[2],2));

       		outfile <<((shaft[0] != 0)? fabs(shaft[0]) : 5.0 )*resize_factor<<" "
       		<<((shaft[1] != 0)? fabs(shaft[1]) : 5.0 )*resize_factor<<" "
       		<<((shaft[2] != 0)? fabs(shaft[2]) : 5.0 )*resize_factor;
       		outfile <<"</size>" <<endl;
       	}
       	else
       	{

       		outfile << "				<size>0.1 0.1 0.1</size>" <<endl;
       	}

    	outfile << "			";
    	outfile << "			</box>" <<endl;

    	outfile << "			";
    	outfile << "		</geometry>" <<endl;

    	outfile << "		<color> 1.0 1.0 1.0 </color>"<<endl;

    	outfile << "			";
    	outfile << "	</visualization_shape>" <<endl;

    	outfile << "			";
    	outfile << "	<collision_shape>" <<endl;

    	outfile << "			";

   		outfile << "		<transformation>"
  		<< (shaftScaled[0])/2.0 * resize_factor<<" "
   		<< (shaftScaled[1])/2.0 * resize_factor<<" "
   		<< (shaftScaled[2])/2.0 * resize_factor<<" "
   		<<" 0 0 0</transformation>" <<endl;

    	outfile << "			";
    	outfile << "		<geometry>" <<endl;

    	outfile << "			";
    	outfile << "			<box>" <<endl;

    	outfile << "			";
    	if(curNode->getName().compare(0, 4, "Head")==0)
    	{
    		outfile <<	"				<size>0.3 0.3 0.3</size>"<<endl;
    	}
    	if(mBvhType == BVHType::BASKET && curNode->getName().compare(0, 5, "Spine")==0)
    	{
    		curNode->getOffset(curOffset);
    		curNode->getChilds()[0]->getOffset(childOffset);
       		outfile << "				<size>";

	       		outfile <<sqrt(pow(childOffset[0],2)+pow(childOffset[1],2)+pow(childOffset[2],2)) *resize_factor<<" "
	       		<<"0.3 0.3</size>"<<endl;

    	}
       	else if(curNode->getChilds().size()==1)
       	{
       		curNode->getChilds()[0]->getOffset(childOffset);
       		outfile << "				<size>";

    		Eigen::Vector3d shaft = getShaft(childOffset);
    		shaft *= sqrt(pow(childOffset[0],2)+pow(childOffset[1],2)+pow(childOffset[2],2));

       		outfile <<((shaft[0] != 0)? fabs(shaft[0]) : 5.0 )*resize_factor<<" "
       		<<((shaft[1] != 0)? fabs(shaft[1]) : 5.0 )*resize_factor<<" "
       		<<((shaft[2] != 0)? fabs(shaft[2]) : 5.0 )*resize_factor;
       		outfile <<"</size>" <<endl;
       	}
       	else
		{
       		outfile << "				<size>0.1 0.1 0.1</size>" <<endl;
       	}

    	outfile << "			";
    	outfile << "			</box>" <<endl;

    	outfile << "			";
    	outfile << "		</geometry>" <<endl;

    	outfile << "			";
    	outfile << "	</collision_shape>" <<endl;

    	outfile << "			";
    	outfile << "</body>" <<endl;
    	curNode = curNode->getNextNode();
    }

    curNode = rootNode;
    while(curNode != nullptr)
    {
    	outfile << "			";
    	if(curNode->getName() == rootNode->getName())
    		outfile << "<joint type=\"free\" name=\"j_"<< curNode->getName() << "\">" <<endl;
    	else
    		outfile << "<joint type=\"ball\" name=\"j_"<< curNode->getName() << "\">" <<endl;

    	if(curNode->getName() != rootNode->getName())
    	{
    		outfile << "			";
    		outfile << "	<transformation>0.0 0.0 0.0 0.0 0.0 0.0</transformation>" << endl;
    	}

    	outfile << "			";
    	if(curNode->getName() == rootNode->getName())
    		outfile << "	<parent>world</parent>" <<endl;
    	else
    		outfile << "	<parent>" << curNode->getParent()->getName() << "</parent>" <<endl;

    	outfile << "			";
    	outfile << "	<child>" << curNode->getName() << "</child>" <<endl;

		// if(curNode->getName() != rootNode->getName())
  //   	{
  //   		outfile << "			";
  //   		outfile << "	<axis_order>" << "xyz" << "</axis_order>" << endl;
  //   	}

     	outfile << "			";
    	if(curNode->getName() == rootNode->getName())
    		outfile << "	<init_pos>0 0 0 0 0 0</init_pos>" <<endl;
    	else
    		outfile << "	<init_pos>0 0 0</init_pos>" <<endl;

     	outfile << "			";
    	if(curNode->getName() == rootNode->getName())
			outfile << "	<init_vel>0 0 0 0 0 0</init_vel>" <<endl;
		else
			outfile << "	<init_vel>0 0 0</init_vel>" <<endl;

     	outfile << "			";
		outfile << "</joint>" <<endl;
		curNode = curNode->getNextNode();
    }

    outfile << "		</skeleton>" <<endl;
    // outfile << "	</world>" <<endl;
    outfile << "</skel>" <<endl;
    outfile.close();

    // if(n == 0)
    cout<< "Generating skel file is completed ! : "<< "../data/skels/"+getFileName_(mPath)+".skel" <<endl;
    // else
    // 	cout<< "Generating skel file is completed ! : "<< bvhpath_2_skelpath_(mPath)+"Mocap.skel" <<endl;

}


/*****************************************************************************
*
* Copyright (c) 2003-2020 by The University of Queensland
* http://www.uq.edu.au
*
* Primary Business: Queensland, Australia
* Licensed under the Apache License, version 2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Development until 2012 by Earth Systems Science Computational Center (ESSCC)
* Development 2012-2013 by School of Earth Sciences
* Development from 2014-2017 by Centre for Geoscience Computing (GeoComp)
* Development from 2019 by School of Earth and Environmental Sciences
**
*****************************************************************************/

#include "DataVector.h"

#include "DataException.h"
#include "DataTypes.h"
#include "Taipan.h"
#include "WrappedArray.h"

#include <boost/python/extract.hpp>

#ifdef ESYS_HAVE_BOOST_NUMPY
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
#endif

using namespace std;
using namespace escript;
using namespace boost::python;
using namespace DataTypes;

namespace {

inline
void cplxout(std::ostream& os, const DataTypes::cplx_t& c)
{
    os << c.real();
    if (c.imag()>=0)
    {
        os << '+';
    }
    os << c.imag() << 'j';
}

}

namespace escript {

   void
   DataTypes::pointToStream(std::ostream& os, const CplxVectorType::ElementType* data,const ShapeType& shape, int offset, bool needsep, const std::string& sep)
   {
      using namespace std;
      ESYS_ASSERT(data!=0, "Error - data is null");
//      ESYS_ASSERT(data.size()>0,"Error - Data object is empty.");
      switch (getRank(shape)) {
      case 0:
         if (needsep){
            os << sep;
         } else {
            needsep = true;
         }
         cplxout(os,data[offset]);
         break;
      case 1:
         for (int i=0;i<shape[0];i++) {
            if (needsep) {
               os << sep;
            } else {
               needsep=true;
            }
            cplxout(os,data[i+offset]);
         }
         break;
      case 2:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               if (needsep){
                  os << sep;
               } else {
                  needsep=true;
               }
               cplxout(os,data[offset+getRelIndex(shape,i,j)]);
            }
         }
         break;
      case 3:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  if (needsep) {
                     os << sep;
                  } else {
                     needsep=true;
                  }
                  cplxout(os,data[offset+getRelIndex(shape,i,j,k)]);
               }
            }
         }
         break;
      case 4:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  for (int l=0;l<shape[3];l++) {
                     if (needsep) {
                        os << sep;
                     } else {
                        needsep=true;
                     }
                     cplxout(os,data[offset+getRelIndex(shape,i,j,k,l)]);
                  }
               }
            }
         }
         break;
      default:
         stringstream mess;
         mess << "Error - (pointToStream) Invalid rank: " << getRank(shape);
         throw DataException(mess.str());
      }
   }

   void
   DataTypes::pointToStream(std::ostream& os, const RealVectorType::ElementType* data,const ShapeType& shape, int offset, bool needsep, const std::string& sep)
   {
      using namespace std;
      ESYS_ASSERT(data!=0, "Error - data is null");
//      ESYS_ASSERT(data.size()>0,"Error - Data object is empty.");
      switch (getRank(shape)) {
      case 0:
         if (needsep) {
            os << sep;
         } else {
            needsep=true;
         }
         os << data[offset];
         break;
      case 1:
         for (int i=0;i<shape[0];i++) {
            if (needsep) {
               os << sep;
            } else {
               needsep=true;
            }
            os << data[i+offset];
         }
         break;
      case 2:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               if (needsep) {
                  os << sep;
               } else {
                  needsep=true;
               }
               os << data[offset+getRelIndex(shape,i,j)];
            }
         }
         break;
      case 3:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  if (needsep) {
                     os << sep;
                  } else {
                     needsep=true;
                  }
                  os << data[offset+getRelIndex(shape,i,j,k)];
               }
            }
         }
         break;
      case 4:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  for (int l=0;l<shape[3];l++) {
                     if (needsep) {
                        os << sep;
                     } else {
                        needsep=true;
                     }
                     os << data[offset+getRelIndex(shape,i,j,k,l)];
                  }
               }
            }
         }
         break;
      default:
         stringstream mess;
         mess << "Error - (pointToStream) Invalid rank: " << getRank(shape);
         throw DataException(mess.str());
      }
   }

#ifdef ESYS_HAVE_BOOST_NUMPY
   void
   DataTypes::pointToNumpyArrayOld(boost::python::numpy::ndarray& dataArray, const RealVectorType::ElementType* data,
      const ShapeType& shape, long offset, long numsamples, long dpps, long totalNumSamples)
   {
      using namespace std;

      ESYS_ASSERT(data != 0, "Error - data is null");
      ESYS_ASSERT(data.size() > 0,"Error - Data object is empty.");

      switch (getRank(shape)) {
      case 0:
         dataArray[0] = data[offset];
         break;
      case 1:
         for (int i = 0; i < shape[0]; i++) {
            dataArray[getRelIndex(shape,i)][numsamples+dpps*totalNumSamples] = data[offset+getRelIndex(shape,i)];
         }
         break;
      case 2:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               dataArray[getRelIndex(shape,i,j)][numsamples+dpps*totalNumSamples] = data[offset+getRelIndex(shape,i,j)];
            }
         }
         break;
      case 3:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  dataArray[getRelIndex(shape,i,j,k)][numsamples+dpps*totalNumSamples] = data[offset+getRelIndex(shape,i,j,k)];
               }
            }
         }
         break;
      case 4:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  for (int l=0;l<shape[3];l++) {
                     dataArray[getRelIndex(shape,i,j,k,l)][numsamples+dpps*totalNumSamples] = data[offset+getRelIndex(shape,i,j,k,l)];
                  }
               }
            }
         }
         break;
      default:
         stringstream mess;
         mess << "Error - (pointToStream) Invalid rank: " << getRank(shape);
         throw DataException(mess.str());
      }
   }

   void
   DataTypes::pointToNumpyArrayOld(boost::python::numpy::ndarray& dataArray, const CplxVectorType::ElementType* data,
      const ShapeType& shape, long offset, long numsamples, long dpps, long totalNumSamples)
   {

      using namespace std;

      ESYS_ASSERT(data != 0, "Error - data is null");
      ESYS_ASSERT(data.size() > 0,"Error - Data object is empty.");

      switch (getRank(shape)) {
      case 0:
         dataArray[0] = data[offset];
         break;
      case 1:
         for (int i = 0; i < shape[0]; i++) {
            dataArray[getRelIndex(shape,i)][numsamples+dpps*totalNumSamples] = data[offset+getRelIndex(shape,i)];
         }
         break;
      case 2:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               dataArray[getRelIndex(shape,i,j)][numsamples+dpps*totalNumSamples] = data[offset+getRelIndex(shape,i,j)];
            }
         }
         break;
      case 3:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  dataArray[getRelIndex(shape,i,j,k)][numsamples+dpps*totalNumSamples] = data[offset+getRelIndex(shape,i,j,k)];
               }
            }
         }
         break;
      case 4:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  for (int l=0;l<shape[3];l++) {
                     dataArray[getRelIndex(shape,i,j,k,l)][numsamples+dpps*totalNumSamples] = data[offset+getRelIndex(shape,i,j,k,l)];
                  }
               }
            }
         }
         break;
      default:
         stringstream mess;
         mess << "Error - (pointToStream) Invalid rank: " << getRank(shape);
         throw DataException(mess.str());
      }
   }

   void
   DataTypes::pointToNumpyArray(boost::python::numpy::ndarray& dataArray, const RealVectorType::ElementType* data,
      const ShapeType& shape, int offset, int d, int index)
   {
      using namespace std;

      ESYS_ASSERT(data != 0, "Error - data is null");
      ESYS_ASSERT(data.size() > 0,"Error - Data object is empty.");

      switch (getRank(shape)) {
      case 0:
         dataArray[d] = data[offset];
         break;
      case 1:
         for (int i = 0; i < shape[0]; i++) {
            dataArray[d + getRelIndex(shape,i)][index] = data[offset+getRelIndex(shape,i)];
         }
         break;
      case 2:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               dataArray[d + getRelIndex(shape,i,j)][index] = data[offset+getRelIndex(shape,i,j)];
            }
         }
         break;
      case 3:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  dataArray[d + getRelIndex(shape,i,j,k)][index] = data[offset+getRelIndex(shape,i,j,k)];
               }
            }
         }
         break;
      case 4:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  for (int l=0;l<shape[3];l++) {
                     dataArray[d + getRelIndex(shape,i,j,k,l)][index] = data[offset+getRelIndex(shape,i,j,k,l)];
                  }
               }
            }
         }
         break;
      default:
         stringstream mess;
         mess << "Error - (pointToStream) Invalid rank: " << getRank(shape);
         throw DataException(mess.str());
      }
   }

   void
   DataTypes::pointToNumpyArray(boost::python::numpy::ndarray& dataArray, const CplxVectorType::ElementType* data,
      const ShapeType& shape, int offset, int d, int index)
   {

      using namespace std;

      ESYS_ASSERT(data != 0, "Error - data is null");
      ESYS_ASSERT(data.size() > 0,"Error - Data object is empty.");

      switch (getRank(shape)) {
      case 0:
         dataArray[d] = data[offset];
         break;
      case 1:
         for (int i = 0; i < shape[0]; i++) {
            dataArray[d + getRelIndex(shape,i)][index] = data[offset+getRelIndex(shape,i)];
         }
         break;
      case 2:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               dataArray[d + getRelIndex(shape,i,j)][index] = data[offset+getRelIndex(shape,i,j)];
            }
         }
         break;
      case 3:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  dataArray[d + getRelIndex(shape,i,j,k)][index] = data[offset+getRelIndex(shape,i,j,k)];
               }
            }
         }
         break;
      case 4:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  for (int l=0;l<shape[3];l++) {
                     dataArray[d + getRelIndex(shape,i,j,k,l)][index] = data[offset+getRelIndex(shape,i,j,k,l)];
                  }
               }
            }
         }
         break;
      default:
         stringstream mess;
         mess << "Error - (pointToStream) Invalid rank: " << getRank(shape);
         throw DataException(mess.str());
      }
   }
#endif

   std::string
   DataTypes::pointToString(const CplxVectorType& data,const ShapeType& shape, int offset, const std::string& prefix)
   {
      using namespace std;
      ESYS_ASSERT(data.size()>0,"Error - Data object is empty.");
      stringstream temp;
      string finalPrefix=prefix;
      if (prefix.length() > 0) {
         finalPrefix+=" ";
      }
      switch (getRank(shape)) {
      case 0:
         temp << finalPrefix;
         cplxout(temp,data[offset]);
         break;
      case 1:
         for (int i=0;i<shape[0];i++) {
            temp << finalPrefix << "(" << i <<  ") ";
            cplxout(temp,data[i+offset]);
            if (i!=(shape[0]-1)) {
               temp << endl;
            }
         }
         break;
      case 2:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               temp << finalPrefix << "(" << i << "," << j << ") ";
               cplxout(temp,data[offset+getRelIndex(shape,i,j)]);
               if (!(i==(shape[0]-1) && j==(shape[1]-1))) {
                  temp << endl;
               }
            }
         }
         break;
      case 3:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  temp << finalPrefix << "(" << i << "," << j << "," << k << ") ";
                  cplxout(temp,data[offset+getRelIndex(shape,i,j,k)]);
                  if (!(i==(shape[0]-1) && j==(shape[1]-1) && k==(shape[2]-1))) {
                     temp << endl;
                  }
               }
            }
         }
         break;
      case 4:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  for (int l=0;l<shape[3];l++) {
                     temp << finalPrefix << "(" << i << "," << j << "," << k << "," << l << ") ";
                     cplxout(temp,data[offset+getRelIndex(shape,i,j,k,l)]);
                     if (!(i==(shape[0]-1) && j==(shape[1]-1) && k==(shape[2]-1) && l==(shape[3]-1))) {
                        temp << endl;
                     }
                  }
               }
            }
         }
         break;
      default:
         stringstream mess;
         mess << "Error - (toString) Invalid rank: " << getRank(shape);
         throw DataException(mess.str());
      }
      return temp.str();
   }

   std::string
   DataTypes::pointToString(const RealVectorType& data,const ShapeType& shape, int offset, const std::string& prefix)
   {
      using namespace std;
      ESYS_ASSERT(data.size()>0,"Error - Data object is empty.");
      stringstream temp;
      string finalPrefix=prefix;
      if (prefix.length() > 0) {
         finalPrefix+=" ";
      }
      switch (getRank(shape)) {
      case 0:
         temp << finalPrefix << data[offset];
         break;
      case 1:
         for (int i=0;i<shape[0];i++) {
            temp << finalPrefix << "(" << i <<  ") " << data[i+offset];
            if (i!=(shape[0]-1)) {
               temp << endl;
            }
         }
         break;
      case 2:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               temp << finalPrefix << "(" << i << "," << j << ") " << data[offset+getRelIndex(shape,i,j)];
               if (!(i==(shape[0]-1) && j==(shape[1]-1))) {
                  temp << endl;
               }
            }
         }
         break;
      case 3:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  temp << finalPrefix << "(" << i << "," << j << "," << k << ") " << data[offset+getRelIndex(shape,i,j,k)];
                  if (!(i==(shape[0]-1) && j==(shape[1]-1) && k==(shape[2]-1))) {
                     temp << endl;
                  }
               }
            }
         }
         break;
      case 4:
         for (int i=0;i<shape[0];i++) {
            for (int j=0;j<shape[1];j++) {
               for (int k=0;k<shape[2];k++) {
                  for (int l=0;l<shape[3];l++) {
                     temp << finalPrefix << "(" << i << "," << j << "," << k << "," << l << ") " << data[offset+getRelIndex(shape,i,j,k,l)];
                     if (!(i==(shape[0]-1) && j==(shape[1]-1) && k==(shape[2]-1) && l==(shape[3]-1))) {
                        temp << endl;
                     }
                  }
               }
            }
         }
         break;
      default:
         stringstream mess;
         mess << "Error - (toString) Invalid rank: " << getRank(shape);
         throw DataException(mess.str());
      }
      return temp.str();
   }

   void DataTypes::copyPoint(RealVectorType& dest, RealVectorType::size_type doffset, RealVectorType::size_type nvals, const RealVectorType& src, RealVectorType::size_type soffset)
   {
      ESYS_ASSERT((dest.size()>0&&src.size()>0&&checkOffset(doffset,dest.size(),nvals)),
                 "Error - Couldn't copy due to insufficient storage.");
      if (checkOffset(doffset,dest.size(),nvals) && checkOffset(soffset,src.size(),nvals)) {
         memcpy(&dest[doffset],&src[soffset],sizeof(real_t)*nvals);
      } else {
         throw DataException("Error - invalid offset specified.");
      }
   }

   void DataTypes::copyPoint(CplxVectorType& dest, CplxVectorType::size_type doffset, CplxVectorType::size_type nvals, const CplxVectorType& src, CplxVectorType::size_type soffset)
   {
      ESYS_ASSERT((dest.size()>0&&src.size()>0&&checkOffset(doffset,dest.size(),nvals)),
                 "Error - Couldn't copy due to insufficient storage.");
      if (checkOffset(doffset,dest.size(),nvals) && checkOffset(soffset,src.size(),nvals)) {
         memcpy(&dest[doffset],&src[soffset],sizeof(cplx_t)*nvals);
      } else {
         throw DataException("Error - invalid offset specified.");
      }
   }

   /**
    * \brief copy data from a real vector to a complex vector
    * The complex vector will be resized as needed and any previous
    * values will be replaced.
   */
   void DataTypes::fillComplexFromReal(const RealVectorType& r, CplxVectorType& c)
   {
       if (c.size()!=r.size())
       {
	   c.resize(r.size(), 0, 1);
       }
       size_t limit=r.size();
#ifdef _WIN32 // error C3016: 'i': index variable in OpenMP 'for' statement must have signed integral type
#define OMP_LOOP_IDX_T int
#else
#define OMP_LOOP_IDX_T size_t
#endif
       #pragma omp parallel for schedule(static)
       for (OMP_LOOP_IDX_T i=0;i<limit;++i)
       {
	   c[i]=r[i];
       }
   }

} // end of namespace


/**
 * GeoDa TM, Copyright (C) 2011-2015 by Luc Anselin - all rights reserved
 *
 * This file is part of GeoDa.
 *
 * GeoDa is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * GeoDa is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <cfloat>
#include <iomanip>
#include <limits>
#include <math.h>
#include <sstream>
#include <boost/math/distributions/students_t.hpp>
#include <wx/dc.h>
#include <wx/msgdlg.h>
#include <wx/stdpaths.h>
#include "GdaConst.h"
#include "GenUtils.h"

using namespace std;

wxString GdaColorUtils::ToHexColorStr(const wxColour& c)
{
	return c.GetAsString(wxC2S_HTML_SYNTAX);
}

wxColour GdaColorUtils::ChangeBrightness(const wxColour& input_col,
										 int brightness)
{
	unsigned char r = input_col.Red();
	unsigned char g = input_col.Green();
	unsigned char b = input_col.Blue();
	unsigned char alpha = input_col.Alpha();
	wxColour::ChangeLightness(&r, &g, &b, brightness);
	return wxColour(r,g,b,alpha);
}

uint64_t Gda::ThomasWangHashUInt64(uint64_t key) {
	key = (~key) + (key << 21); // key = (key << 21) - key - 1;
	key = key ^ (key >> 24);
	key = (key + (key << 3)) + (key << 8); // key * 265
	key = key ^ (key >> 14);
	key = (key + (key << 2)) + (key << 4); // key * 21
	key = key ^ (key >> 28);
	key = key + (key << 31);
	return key;
}

double Gda::ThomasWangHashDouble(uint64_t key) {
	key = (~key) + (key << 21); // key = (key << 21) - key - 1;
	key = key ^ (key >> 24);
	key = (key + (key << 3)) + (key << 8); // key * 265
	key = key ^ (key >> 14);
	key = (key + (key << 2)) + (key << 4); // key * 21
	key = key ^ (key >> 28);
	key = key + (key << 31);
	return 5.42101086242752217E-20 * key;
}

/** Use with std::sort for sorting in ascending order */
bool Gda::dbl_int_pair_cmp_less(const dbl_int_pair_type& ind1,
								  const dbl_int_pair_type& ind2)
{
	return ind1.first < ind2.first;
}

/** Use with std::sort for sorting in descending order */
bool Gda::dbl_int_pair_cmp_greater(const dbl_int_pair_type& ind1,
									 const dbl_int_pair_type& ind2)
{
	return ind1.first > ind2.first;
}

/** Use with std::sort for sorting in ascending order */
bool Gda::dbl_int_pair_cmp_second_less(const dbl_int_pair_type& ind1,
										 const dbl_int_pair_type& ind2)
{
	return ind1.second < ind2.second;
}

/** Use with std::sort for sorting in descending order */
bool Gda::dbl_int_pair_cmp_second_greater(const dbl_int_pair_type& ind1,
											const dbl_int_pair_type& ind2)
{
	return ind1.second > ind2.second;
}

void
HingeStats::
CalculateHingeStats(const std::vector<Gda::dbl_int_pair_type>& data)
{
	num_obs = data.size();
	double N = num_obs;
	is_even_num_obs = (num_obs % 2) == 0;
	min_val = data[0].first;
	max_val = data[num_obs-1].first;
	Q2_ind = (N+1)/2.0 - 1;
	if (is_even_num_obs) {
		Q1_ind = (N+2)/4.0 - 1;
		Q3_ind = (3*N+2)/4.0 - 1;
	} else {
		Q1_ind = (N+3)/4.0 - 1;
		Q3_ind = (3*N+1)/4.0 - 1;
	}
	Q1 = (data[(int) floor(Q1_ind)].first +
		  data[(int) ceil(Q1_ind)].first)/2.0;
	Q2 = (data[(int) floor(Q2_ind)].first +
		  data[(int) ceil(Q2_ind)].first)/2.0;
	Q3 = (data[(int) floor(Q3_ind)].first +
		  data[(int) ceil(Q3_ind)].first)/2.0;
	IQR = Q3 - Q1;
	extreme_lower_val_15 = Q1 - 1.5*IQR;
	extreme_lower_val_30 = Q1 - 3.0*IQR;
	extreme_upper_val_15 = Q3 + 1.5*IQR;
	extreme_upper_val_30 = Q3 + 3.0*IQR;
	min_IQR_ind = -1;
	for (int i=0; i<num_obs; i++) {
		if (data[i].first < Q1) min_IQR_ind = i;
		else break;
	}
	if (min_IQR_ind < num_obs-1) min_IQR_ind++;
	max_IQR_ind = num_obs;
	for (int i=num_obs-1; i>=0; i--) {
		if (data[i].first > Q3) max_IQR_ind = i;
		else break;
	}
	if (max_IQR_ind > 0) max_IQR_ind--;
}

void
HingeStats::
CalculateHingeStats(const std::vector<Gda::dbl_int_pair_type>& data,
                    const std::vector<bool>& data_undef)
{
    num_obs = data.size();
    double N = 0.0;
    std::vector<double> data_valid;

    bool has_init = false;
    for (size_t i =0; i<num_obs; i++) {
        int obs_idx = data[i].second;
        if (!data_undef[obs_idx]) {
            double val = data[i].first;
            data_valid.push_back(val); // sorted
            if (!has_init) {
                min_val = val;
                max_val = val;
                has_init = true;
            }
            if (val < min_val)
                min_val = val;
            if (val > max_val)
                max_val = val;
        }
    }

    N = data_valid.size();
    is_even_num_obs = (data_valid.size() % 2) == 0;

    Q2_ind = (N+1)/2.0 - 1;
    if (is_even_num_obs) {
        Q1_ind = (N+2)/4.0 - 1;
        Q3_ind = (3*N+2)/4.0 - 1;
    } else {
        Q1_ind = (N+3)/4.0 - 1;
        Q3_ind = (3*N+1)/4.0 - 1;
    }
    Q1 = (data_valid[(int) floor(Q1_ind)] + data_valid[(int) ceil(Q1_ind)])/2.0;
    Q2 = (data_valid[(int) floor(Q2_ind)] + data_valid[(int) ceil(Q2_ind)])/2.0;
    Q3 = (data_valid[(int) floor(Q3_ind)] + data_valid[(int) ceil(Q3_ind)])/2.0;

    IQR = Q3 - Q1;

    extreme_lower_val_15 = Q1 - 1.5*IQR;
    extreme_lower_val_30 = Q1 - 3.0*IQR;
    extreme_upper_val_15 = Q3 + 1.5*IQR;
    extreme_upper_val_30 = Q3 + 3.0*IQR;

    min_IQR_ind = -1;
    for (int i=0; i<num_obs; i++) {
        if (data[i].first < Q1) {
            min_IQR_ind = i;
        }
        else
            break;
    }
    if (min_IQR_ind < num_obs-1) {
        min_IQR_ind++;
    }
    max_IQR_ind = num_obs;

    for (int i=num_obs-1; i>=0; i--) {
        if (data[i].first > Q3) {
            max_IQR_ind = i;
        }
        else
            break;
    }
    if (max_IQR_ind > 0)
        max_IQR_ind--;
}

// Assume input v is sorted.  If not, can sort
// with std::sort(v.begin(), v.end())
// Testing: for v = {15, 20, 35, 40, 50},
// percentile(1, v) = 15, percentile(10, v) = 15, percentile(11) = 15.25
// percentile(50, v) = 35, percentile(89, v) = 49.5,
// percentile(90, v) = 50, percentile(99, v) = 50
double Gda::percentile(double x, const std::vector<double>& v)
{
	int N = v.size();
	double Nd = (double) N;
	double p_0 = (100.0/Nd) * (1.0-0.5);
	double p_Nm1 = (100.0/Nd) * (Nd-0.5);
	if (x <= p_0) return v[0];
	if (x >= p_Nm1) return v[N-1];

	for (int i=1; i<N; i++) {
		double p_i = (100.0/Nd) * ((((double) i)+1.0)-0.5);
		if (x == p_i) return v[i];
		if (x < p_i) {
			double p_im1 = (100.0/Nd) * ((((double) i))-0.5);
			return v[i-1] + Nd*((x-p_im1)/100.0)*(v[i]-v[i-1]);
		}
	}
	return v[N-1]; // execution should never get here
}

// Same assumptions as above
double Gda::percentile(double x, const Gda::dbl_int_pair_vec_type& v,
                       const std::vector<bool>& undefs)
{
    std::vector<double> valid_data;
    for (size_t i = 0; i<v.size(); i++ ) {
        double val = v[i].first;
        int ind = v[i].second;

        if (undefs[ind])
            continue;

        valid_data.push_back(val);
    }
    return percentile(x, valid_data);
}

// Same assumptions as above
double Gda::percentile(double x, const Gda::dbl_int_pair_vec_type& v)
{
	int N = v.size();
	double Nd = (double) N;
	double p_0 = (100.0/Nd) * (1.0-0.5);
	double p_Nm1 = (100.0/Nd) * (Nd-0.5);

	if (x <= p_0)
        return v[0].first;

	if (x >= p_Nm1)
        return v[N-1].first;

	for (int i=1; i<N; i++) {
		double p_i = (100.0/Nd) * ((((double) i)+1.0)-0.5);
		if (x == p_i)
            return v[i].first;
		if (x < p_i) {
			double p_im1 = (100.0/Nd) * ((((double) i))-0.5);
			return v[i-1].first + Nd*((x-p_im1)/100.0)*(v[i].first
														-v[i-1].first);
		}
	}
	return v[N-1].first; // execution should never get here
}

SampleStatistics::SampleStatistics(const std::vector<double>& data)
	: sample_size(0), min(0), max(0), mean(0),
	var_with_bessel(0), var_without_bessel(0),
	sd_with_bessel(0), sd_without_bessel(0)
{
	CalculateFromSample(data);
}

SampleStatistics::SampleStatistics(const std::vector<double>& data,
                                   const std::vector<bool>& undefs)
	: sample_size(0), min(0), max(0), mean(0),
	var_with_bessel(0), var_without_bessel(0),
	sd_with_bessel(0), sd_without_bessel(0)
{
    std::vector<double> valid_data;
    for (int i=0; i<data.size(); i++) {
        if (undefs[i] == false)
            valid_data.push_back(data[i]);
    }
	CalculateFromSample(valid_data);
}

SampleStatistics::SampleStatistics(const std::vector<double>& data,
                                   const std::vector<bool>& undefs1,
                                   const std::vector<bool>& undefs2)
	: sample_size(0), min(0), max(0), mean(0),
	var_with_bessel(0), var_without_bessel(0),
	sd_with_bessel(0), sd_without_bessel(0)
{
    std::vector<double> valid_data;
    for (int i=0; i<data.size(); i++) {
        if (undefs1[i] || undefs2[i])
            continue;
        valid_data.push_back(data[i]);
    }
	CalculateFromSample(valid_data);
}

void SampleStatistics::CalculateFromSample(const std::vector<double>& data,
                                           const std::vector<bool>& undefs)
{
    std::vector<double> valid_data;
    for (int i=0; i<data.size(); i++) {
        if (undefs[i] == false)
            valid_data.push_back(data[i]);
    }
    CalculateFromSample(valid_data);
}
void SampleStatistics::CalculateFromSample(const std::vector<double>& data)
{
	sample_size = data.size();
	if (sample_size == 0) return;

	CalcMinMax(data, min, max);
	mean = CalcMean(data);

	double n = sample_size;
	double sum_squares = 0;
	for (int i=0, iend = data.size(); i<iend; i++) {
		sum_squares += data[i] * data[i];
	}

	var_without_bessel = (sum_squares/n) - (mean*mean);
	sd_without_bessel = sqrt(var_without_bessel);

	if (sample_size == 1) {
		var_with_bessel = var_without_bessel;
		sd_with_bessel = sd_without_bessel;
	} else {
		var_with_bessel = (n/(n-1)) * var_without_bessel;
		sd_with_bessel = sqrt(var_with_bessel);
	}
}

/** We assume that the data has been sorted in ascending order */
void
SampleStatistics::
CalculateFromSample(const std::vector<Gda::dbl_int_pair_type>& data_,
                    const std::vector<bool>& undefs)
{
    std::vector<double> data;
    for (int i=0, iend = data_.size(); i<iend; i++) {
        int id = data_[i].second;
        if (!undefs[id]) {
            data.push_back(data_[i].first);
        }
    }

	sample_size = data.size();
	if (sample_size == 0) return;

	min = data[0];
	max = data[sample_size-1];
	mean = CalcMean(data);

	double n = sample_size;
	double sum_squares = 0;
	for (int i=0, iend = data.size(); i<iend; i++) {
		sum_squares += data[i] * data[i];
	}

	var_without_bessel = (sum_squares/n) - (mean*mean);
	sd_without_bessel = sqrt(var_without_bessel);

	if (sample_size == 1) {
		var_with_bessel = var_without_bessel;
		sd_with_bessel = sd_without_bessel;
	} else {
		var_with_bessel = (n/(n-1)) * var_without_bessel;
		sd_with_bessel = sqrt(var_with_bessel);
	}
}

string SampleStatistics::ToString()
{
	ostringstream ss;
	ss << "sample_size = " << sample_size << endl;
	ss << "min = " << min << endl;
	ss << "max = " << max << endl;
	ss << "mean = " << mean << endl;
	ss << "var_with_bessel = " << var_with_bessel << endl;
	ss << "var_without_bessel = " << var_without_bessel << endl;
	ss << "sd_with_bessel = " << sd_with_bessel << endl;
	ss << "sd_without_bessel = " << sd_without_bessel << endl;
	return ss.str();
}

double SampleStatistics::CalcMin(const std::vector<double>& data)
{
	double min = std::numeric_limits<double>::max();
	for (int i=0, iend=data.size(); i<iend; i++) {
		if ( data[i] < min ) min = data[i];
	}
	return min;
}

double SampleStatistics::CalcMax(const std::vector<double>& data)
{
	double max = -std::numeric_limits<double>::max();
	for (int i=0, iend=data.size(); i<iend; i++) {
		if ( data[i] > max ) max = data[i];
	}
	return max;
}

void SampleStatistics::CalcMinMax(const std::vector<double>& data,
								  double& min, double& max)
{
	if (data.size() == 0) return;
	min = data[0];
	max = data[0];
	for (int i=1, iend=data.size(); i<iend; i++) {
		if ( data[i] < min ) {
			min = data[i];
		} else if ( data[i] > max ) {
			max = data[i];
		}
	}
}

double SampleStatistics::CalcMean(const std::vector<double>& data)
{
	if (data.size() == 0) return 0;
	double total = 0;
	for (int i=0, iend=data.size(); i<iend; i++) {
		total += data[i];
	}
	return total / (double) data.size();
}

double SampleStatistics::CalcMean(
							const std::vector<Gda::dbl_int_pair_type>& data)
{
	if (data.size() == 0) return 0;
	double total = 0;
	for (int i=0, iend=data.size(); i<iend; i++) {
		total += data[i].first;
	}
	return total / (double) data.size();
}

SimpleLinearRegression::SimpleLinearRegression(const std::vector<double>& X,
											   const std::vector<double>& Y,
											   double meanX, double meanY,
											   double varX, double varY)
	: n(0), covariance(0), correlation(0), alpha(0), beta(0), r_squared(0),
	std_err_of_estimate(0), std_err_of_beta(0), std_err_of_alpha(0),
	t_score_alpha(0), t_score_beta(0), p_value_alpha(0), p_value_beta(0),
	valid(false), valid_correlation(false), valid_std_err(false),
	error_sum_squares(0)
{
	CalculateRegression(X, Y, meanX, meanY, varX, varY);
}

SimpleLinearRegression::SimpleLinearRegression(const std::vector<double>& X,
											   const std::vector<double>& Y,
                                               const std::vector<bool>& X_undef,
                                               const std::vector<bool>& Y_undef,
											   double meanX, double meanY,
											   double varX, double varY)
	: n(0), covariance(0), correlation(0), alpha(0), beta(0), r_squared(0),
	std_err_of_estimate(0), std_err_of_beta(0), std_err_of_alpha(0),
	t_score_alpha(0), t_score_beta(0), p_value_alpha(0), p_value_beta(0),
	valid(false), valid_correlation(false), valid_std_err(false),
	error_sum_squares(0)
{

    std::vector<double> X_valid;
    std::vector<double> Y_valid;

    for (int i=0; i<X.size(); i++) {
        if (X_undef[i] || Y_undef[i])
            continue;

        X_valid.push_back(X[i]);
        Y_valid.push_back(Y[i]);
    }
	CalculateRegression(X_valid, Y_valid, meanX, meanY, varX, varY);
}

void SimpleLinearRegression::CalculateRegression(const std::vector<double>& X,
												 const std::vector<double>& Y,
												 double meanX, double meanY,
												 double varX, double varY)
{
    n = X.size();
	if (X.size() != Y.size() || X.size() < 2 )
        return;
	double expectXY = 0;
	for (int i=0, iend=X.size(); i<iend; i++) {
		expectXY += X[i]*Y[i];
	}
	expectXY /= (double) X.size();
	covariance = expectXY - meanX * meanY;
	if (varX > 4*DBL_MIN) {
		beta = covariance / varX;
		alpha = meanY - beta * meanX;
		valid = true;
	}
	double SS_tot = varY*Y.size();
	error_sum_squares = 0; // error_sum_squares = SS_err
	double err=0;
	for (int i=0, iend=Y.size(); i<iend; i++) {
		err = Y[i] - (alpha + beta * X[i]);
		error_sum_squares += err * err;
	}
	if (error_sum_squares < 16*DBL_MIN) {
		r_squared = 1;
	} else {
		r_squared = 1 - error_sum_squares / SS_tot;
	}

	if (Y.size()>2 && varX > 4*DBL_MIN) {
		// error_sum_squares/(n-k-1), k=1
		std_err_of_estimate = error_sum_squares/(Y.size()-2);
		std_err_of_estimate = sqrt(std_err_of_estimate);
		std_err_of_beta = std_err_of_estimate/sqrt(X.size()*varX);
		double sum_x_squared = 0;
		for (int i=0, iend=X.size(); i<iend; i++) {
			sum_x_squared += X[i] * X[i];
		}
		std_err_of_alpha = std_err_of_beta * sqrt(sum_x_squared / X.size());

		if (std_err_of_alpha >= 16*DBL_MIN) {
			t_score_alpha = alpha / std_err_of_alpha;
		} else {
			t_score_alpha = 100;
		}
		if (std_err_of_beta >= 16*DBL_MIN) {
			t_score_beta = beta	/ std_err_of_beta;
		} else {
			t_score_beta = 100;
		}
		p_value_alpha = TScoreTo2SidedPValue(t_score_alpha, X.size()-2);
		p_value_beta = TScoreTo2SidedPValue(t_score_beta, X.size()-2);

		valid_std_err = true;
	}

	double d = sqrt(varX)*sqrt(varY);
	if (d > 4*DBL_MIN) {
		correlation = covariance / d;
		valid_correlation = true;
	}
}

double SimpleLinearRegression::TScoreTo2SidedPValue(double tscore, int df)
{
	using namespace boost::math;
	students_t dist(df);
	// Cumulative Distribution Function evaluated at tscore
	if ( tscore >= 0) {
		return 2*(1.0-cdf(dist, tscore));
	} else {
		return 2*cdf(dist,tscore);
	}

}

string SimpleLinearRegression::ToString()
{
	ostringstream ss;
	ss << "covariance = " << covariance << endl;
	ss << "correlation = " << correlation << endl;
	ss << "alpha = " << alpha << endl;
	ss << "beta = " << beta << endl;
	ss << "r_squared = " << r_squared << endl;
	ss << "valid = " << (valid ? "true" : "false") << endl;
	ss << "valid_correlation = " << (valid_correlation ? "true" : "false")
		<< endl;
	ss << "error_sum_squares = " << error_sum_squares << endl;
	return ss.str();
}

AxisScale::AxisScale(double data_min_s, double data_max_s, int ticks_s, int lbl_precision_s)
: data_min(0), data_max(0), scale_min(0), scale_max(0),
scale_range(0), tic_inc(0), p(0), ticks(ticks_s), lbl_precision(lbl_precision_s)
{
	CalculateScale(data_min_s, data_max_s, ticks_s);
}

AxisScale::AxisScale(const AxisScale& s)
: data_min(s.data_min), data_max(s.data_max),
	scale_min(s.scale_min), scale_max(s.scale_max),
	scale_range(s.scale_range), tic_inc(s.tic_inc), p(s.p),
	tics(s.tics), tics_str(s.tics_str), tics_str_show(s.tics_str_show),
	ticks(s.ticks)
{
}

AxisScale& AxisScale::operator=(const AxisScale& s)
{
	data_min = s.data_min;
	data_max = s.data_max;
	scale_min = s.scale_min;
	scale_max = s.scale_max;
	scale_range = s.scale_range;
	tic_inc = s.tic_inc;
	p = s.p;
	tics = s.tics;
	tics_str = s.tics_str;
	tics_str_show = s.tics_str_show;
	ticks = s.ticks;
	return *this;
}

void AxisScale::CalculateScale(double data_min_s, double data_max_s,
							   const int ticks)
{
	if (data_min_s <= data_max_s) {
		data_min = data_min_s;
		data_max = data_max_s;
	} else {
		data_min = data_max_s;
		data_max = data_min_s;
	}

	double data_range = data_max - data_min;
	if ( data_range <= 2*DBL_MIN ) {
		scale_max = ceil((data_max + 0.05)*10)/10;
		scale_min = floor((data_min - 0.05)*10)/10;
		scale_range = scale_max - scale_min;
		p = 1;
		tic_inc = scale_range/2;
		tics.resize(3);
		tics_str.resize(3);
		tics[0] = scale_min;
		tics[1] = scale_min + tic_inc;
		tics[2] = scale_max;
	} else {
		p = floor(log10(data_range))-1;
		scale_max = ceil(data_max / pow((double)10,p)) * pow((double)10,p);
		scale_min = floor(data_min / pow((double)10,p)) * pow((double)10,p);
		scale_range = scale_max - scale_min;
		tic_inc = floor((scale_range / pow((double)10,p))/ticks)
			* pow((double)10,p);
		if (scale_min + tic_inc*(ticks+1) <= scale_max + 2*DBL_MIN) {
			tics.resize(ticks+2);
			tics_str.resize(ticks+2);
		} else {
			tics.resize(ticks+1);
			tics_str.resize(ticks+1);
		}
		for (int i=0, iend=tics.size(); i<iend; i++) {
			tics[i] = scale_min + i*tic_inc;
		}
	}
	tics_str_show.resize(tics_str.size());
	for (int i=0, iend=tics.size(); i<iend; i++) {
        tics_str[i] = GenUtils::DblToStr(tics[i], lbl_precision);
		tics_str_show[i] = true;
	}
}

/** only display every other tic value */
void AxisScale::SkipEvenTics()
{
	for (int i=0; i<tics_str_show.size(); i++) tics_str_show[i] = (i%2 == 0);
}

void AxisScale::ShowAllTics()
{
	for (int i=0; i<tics_str_show.size(); i++) tics_str_show[i] = true;
}

string AxisScale::ToString()
{
	ostringstream ss;
	ss << "data_min = " << data_min << endl;
	ss << "data_max = " << data_max << endl;
	ss << "scale_min = " << scale_min << endl;
	ss << "scale_max = " << scale_max << endl;
	ss << "scale_range = " << scale_range << endl;
	ss << "p = " << p << endl;
	ss << "tic_inc = " << tic_inc << endl;
	for (int i=0, iend=tics.size(); i<iend; i++) {
		ss << "tics[" << i << "] = " << tics[i];
		ss << ",  tics_str[" << i << "] = " << tics_str[i] << endl;
	}
	ss << "Exiting AxisScale::CalculateScale" << endl;
	return ss.str();
}

wxString GenUtils::BoolToStr(bool b)
{
	return b ? "true" : "false";
}

bool GenUtils::StrToBool(const wxString& s)
{
	if (s.CmpNoCase("1") == 0) return true;
	if (s.CmpNoCase("true") == 0) return true;
	return false;
}

/** If input string has length < width, then prepends (or appends
 if pad_left=false) string with spaces so that total length is now width.
 If string length >= width, then returns original input string. */
wxString GenUtils::Pad(const wxString& s, int width, bool pad_left)
{
	if (s.length() >= width) return s;
	int pad_len = width - s.length();
	wxString output;
	if (!pad_left) output << s;
	for (int i=0; i<pad_len; i++) output << " ";
	if (pad_left) output << s;
	return output;
}

wxString GenUtils::PadTrim(const wxString& s, int width, bool pad_left)
{
    if (s.length() > width) {
        int trim_w = width - 2; //"xxx..xxx"
        int second_w = trim_w / 2;
        int first_w = trim_w - second_w;
        wxString tmp = s.SubString(0, first_w-2);
        tmp << ".." << s.SubString(s.length() - second_w -1, s.length()-1);
        return tmp;
    }
    int pad_len = width - s.length();
    wxString output;
    if (!pad_left) output << s;
    for (int i=0; i<pad_len; i++) output << " ";
    if (pad_left) output << s;
    return output;
}

wxString GenUtils::DblToStr(double x, int precision)
{
	std::stringstream ss;
    if (x < 10000000) {
        ss << std::fixed;
    }
	ss << std::setprecision(precision);
	ss << x;
	return wxString(ss.str().c_str(), wxConvUTF8);
}

wxString GenUtils::PtToStr(const wxPoint& p)
{
	std::stringstream ss;
	ss << "(" << p.x << "," << p.y << ")";
	return wxString(ss.str().c_str(), wxConvUTF8);
}

wxString GenUtils::PtToStr(const wxRealPoint& p)
{
	std::stringstream ss;
	ss << std::setprecision(5);
	ss << "(" << p.x << "," << p.y << ")";
	return wxString(ss.str().c_str(), wxConvUTF8);
}

// NOTE: should take into account undefined values.
void GenUtils::DeviationFromMean(int nObs, double* data)
{
	if (nObs == 0) return;
	double sum = 0.0;
	for (int i=0, iend=nObs; i<iend; i++) sum += data[i];
	const double mean = sum / (double) nObs;
	for (int i=0, iend=nObs; i<iend; i++) data[i] -= mean;
}

void GenUtils::DeviationFromMean(int nObs, double* data, std::vector<bool>& undef)
{
	if (nObs == 0) return;

    int nValid = 0;
	double sum = 0.0;
    for (int i=0, iend=nObs; i<iend; i++) {
        if (undef[i])
            continue;
        sum += data[i];
        nValid += 1;
    }
	const double mean = sum / (double) nValid;
    for (int i=0, iend=nObs; i<iend; i++) {
        data[i] -= mean;
    }
}

void GenUtils::DeviationFromMean(std::vector<double>& data)
{
	if (data.size() == 0) return;
	double sum = 0.0;
	for (int i=0, iend=data.size(); i<iend; i++) sum += data[i];
	const double mean = sum / (double) data.size();
	for (int i=0, iend=data.size(); i<iend; i++) data[i] -= mean;
}

bool GenUtils::StandardizeData(int nObs, double* data)
{
	if (nObs <= 1) return false;
	GenUtils::DeviationFromMean(nObs, data);
	double ssum = 0.0;
	for (int i=0, iend=nObs; i<iend; i++) ssum += data[i] * data[i];
	const double sd = sqrt(ssum / (double) (nObs-1.0));
	if (sd == 0) return false;
	for (int i=0, iend=nObs; i<iend; i++) data[i] /= sd;
	return true;
}

bool GenUtils::StandardizeData(int nObs, double* data, std::vector<bool>& undef)
{
	if (nObs <= 1) return false;

    int nValid = 0;
    for (int i=0; i<undef.size(); i++) {
        if (!undef[i])
            nValid += 1;
    }

	GenUtils::DeviationFromMean(nObs, data, undef);
	double ssum = 0.0;
    for (int i=0, iend=nObs; i<iend; i++) {
        if (undef[i])
            continue;
        ssum += data[i] * data[i];
    }
	const double sd = sqrt(ssum / (double) (nValid-1.0));
	if (sd == 0)
        return false;
    for (int i=0, iend=nObs; i<iend; i++) {
        data[i] /= sd;
    }
	return true;
}

bool GenUtils::StandardizeData(std::vector<double>& data)
{
	if (data.size() <= 1) return false;
	GenUtils::DeviationFromMean(data);
	double ssum = 0.0;
	for (int i=0, iend=data.size(); i<iend; i++) ssum += data[i] * data[i];
	const double sd = sqrt(ssum / (double) (data.size()-1.0));
	if (sd == 0) return false;
	for (int i=0, iend=data.size(); i<iend; i++) data[i] /= sd;
	return true;
}

wxString GenUtils::swapExtension(const wxString& fname, const wxString& ext)
{
	if (ext.IsEmpty()) return fname;
	wxString prefix = fname.BeforeLast('.');
	if (prefix.IsEmpty()) return fname + "." + ext;
	return prefix + "." + ext;
}

wxString GenUtils::GetFileDirectory(const wxString& path)
{
	int pos = path.Find('/',true);
	if (pos >= 0)
		return path.Left(pos) + '/';
	pos = path.Find('\\',true);
	if (pos >= 0)
		return path.Left(pos) + '\\';
	return wxEmptyString;
}

wxString GenUtils::GetFileName(const wxString& path)
{
	int pos = path.Find('/',true);
	if (pos >= 0)
		return path.Right(path.length() - pos - 1);
	pos = path.Find('\\',true);
	if (pos >= 0)
		return path.Right(path.length() - pos - 1);
	return wxEmptyString;
}

wxString GenUtils::GetFileNameNoExt(const wxString& path)
{
    wxString fname = GetFileName(path);
    int pos = fname.Find('.');
    if (pos >=0)
        return fname.SubString(0, pos-1);
    return fname;
}

wxString GenUtils::GetFileExt(const wxString& path)
{
	int pos = path.Find('.',true);
	if (pos >= 0)
		return path.Right(path.length() - pos - 1);
	return wxEmptyString;
}

wxString GenUtils::RestorePath(const wxString& proj_path, const wxString& path)
{
	wxFileName path_fn(path);
	if (path_fn.IsAbsolute()) return path;
	if (!path_fn.IsOk()) return path;
	wxFileName wd;
	wxFileName prj_path_fn(proj_path);
	if (prj_path_fn.GetExt().IsEmpty()) {
		wd.AssignDir(proj_path);
	} else {
		wd.AssignDir(prj_path_fn.GetPath());
	}
	if (!wd.IsOk() || !wd.IsDir() || !wd.IsAbsolute()) return path;
	if (path_fn.MakeAbsolute(wd.GetPath())) {
		if (path_fn.GetExt().IsEmpty()) {
			return path_fn.GetPath();
		}
		return path_fn.GetFullPath();
	}
	return path;
}

wxString GenUtils::SimplifyPath(const wxString& proj_path, const wxString& path)
{
	wxFileName wd;
        wxFileName proj_path_fn(proj_path);
	if (proj_path_fn.GetExt().IsEmpty()) {
		wd.AssignDir(proj_path);
	} else {
		wd.AssignDir(proj_path_fn.GetPath());
	}
	return GenUtils::SimplifyPath(wd, path);
}

wxString GenUtils::SimplifyPath(const wxFileName& wd, const wxString& path)
{
    wxFileName path_fn(path);
	if (!wd.IsOk() || !wd.IsDir() || !wd.IsAbsolute() ||
		path_fn.IsRelative()) return path;
	wxFileName p;
	if (wxDirExists(path)) {
		p.AssignDir(path);
	} else {
		p.Assign(path);
	}
	if (p.GetVolume() != wd.GetVolume()) return path;
	wxArrayString p_dirs = p.GetDirs();
	wxArrayString wd_dirs = wd.GetDirs();
	if (p_dirs.size() < wd_dirs.size()) return path;
	for (int i=0, sz=wd_dirs.size(); i<sz; ++i) {
		if (p_dirs[i] != wd_dirs[i]) return path;
	}
	if (p.MakeRelativeTo(wd.GetPath())) {
		if (p.IsDir()) {
			return p.GetPath();
		}
		return p.GetFullPath();
	}
	return path;
}

void GenUtils::SplitLongPath(const wxString& path,
							 std::vector<wxString>& parts,
							 wxString& html_formatted,
							 int max_chars_per_part)
{
	if (max_chars_per_part < 15) max_chars_per_part = 15;
	parts.clear();
	html_formatted = "";
	if (path.size() <= max_chars_per_part) {
		parts.push_back(path);
		html_formatted = path;
		return;
	}
	wxFileName fn(path);
	wxArrayString dirs(fn.GetDirs());
	if (dirs.size() <= 1) {
		parts.push_back(path);
		html_formatted = path;
		return;
	}
	wxString sep = wxFileName::GetPathSeparator();
	// Note: always add sep char after dir added
	if (path.SubString(0,0) == sep) {
		parts.push_back(sep);
	} else if (fn.HasVolume()) {
		// We'll assume this is a Windows system since only other
		// supported are OSX and Linux and HasVolume should always
		// be false for these.
		parts.push_back(fn.GetVolume());
		parts[0] << wxFileName::GetVolumeSeparator(wxPATH_WIN);
		parts[0] << wxFileName::GetPathSeparator(wxPATH_WIN);
	} else {
		parts.push_back("");
	}
	size_t cp = 0; // current part
	for (size_t i=0; i<dirs.size(); ++i) {
		if (parts[cp].size() > max_chars_per_part) {
			++cp;
			parts.push_back("");
		}
		if ((parts[cp].size() + dirs[i].size() > max_chars_per_part) &&
			(parts[cp].size() > 0)) {
			++cp;
			parts.push_back("");
		}
		parts[cp] << dirs[i] << sep;
	}
	if (fn.HasName()) {
		wxString name = fn.GetName();
		name << "." << fn.GetExt();
		if ((parts[cp].size() + name.size() > max_chars_per_part) &&
			(parts[cp].size() > 0)) {
			++cp;
			parts.push_back("");
		}
		parts[cp] << name;
	}
	for (size_t i=0, last_part=parts.size()-1; i<=last_part; ++i) {
		html_formatted << parts[i];
		if (i < last_part) html_formatted << "<br />    ";
	}
}

/*
 Reverse
 Changes the order of bytes in the presentation of a 4 byte number.
  */
wxInt32 GenUtils::Reverse(const wxInt32 &val)
{
	union {
		wxInt32 v;
		char d[4];
	} chameleon;
	chameleon.v= val;
	char tmp = chameleon.d[0];
	chameleon.d[0] = chameleon.d[3];
	chameleon.d[3] = tmp;
	tmp = chameleon.d[1];
	chameleon.d[1] = chameleon.d[2];
	chameleon.d[2] = tmp;
	return chameleon.v;
}

long GenUtils::ReverseInt(const int &val)
{
	union {
		int v;
		char d[4];
	} chameleon;
	chameleon.v= val;
	char tmp = chameleon.d[0];
	chameleon.d[0] = chameleon.d[3];
	chameleon.d[3] = tmp;
	tmp = chameleon.d[1];
	chameleon.d[1] = chameleon.d[2];
	chameleon.d[2] = tmp;
	return chameleon.v;
}

void GenUtils::SkipTillNumber(std::istream &s)
{
	char ch;
	while (s >> ch) {
		if ((ch >= '0' && ch <= '9') || ch == '-' || ch == '+' || ch == '.')
			break;
	}
	if (s.good()) s.putback(ch);
}

// This is an implementation of ltoa
void GenUtils::longToString(const long d, char* Id, const int base)
{
	int i = 0;
	long j = d;
	char rId[ GdaConst::ShpObjIdLen ];
	if (d == 0) {
		Id[0] = '0';
		Id[1] = '\0';
		return;
	}
	if (d < 0) j = -d;
	while (j != 0) {
		rId[i] = (j % base) + '0';
		j = j / base;
		i++;
	}
	j = i;
	if (d < 0) {
		Id[0] = '-';
		Id[i + 1] = '\0';
		while (i > 0) {
			Id[i] = rId[j - i];
			i--;
		}
		return;
	}

	Id[i] = '\0';
	while (i > 0) {
		Id[i - 1] = rId[j - i];
		i--;
	}
	return;
}

// Calculates Euclidean distance
double GenUtils::distance(const wxRealPoint& p1, const wxRealPoint& p2)
{
	double dx = p1.x - p2.x;
	double dy = p1.y - p2.y;
	return sqrt(dx*dx + dy*dy);
}

double GenUtils::distance(const wxRealPoint& p1, const wxPoint& p2)
{
	double dx = p1.x - p2.x;
	double dy = p1.y - p2.y;
	return sqrt(dx*dx + dy*dy);
}

double GenUtils::distance(const wxPoint& p1, const wxRealPoint& p2)
{
	double dx = p1.x - p2.x;
	double dy = p1.y - p2.y;
	return sqrt(dx*dx + dy*dy);
}

double GenUtils::distance(const wxPoint& p1, const wxPoint& p2)
{
	double dx = p1.x - p2.x;
	double dy = p1.y - p2.y;
	return sqrt(dx*dx + dy*dy);
}

// Calculates Euclidean distance
double GenUtils::distance_sqrd(const wxRealPoint& p1, const wxRealPoint& p2)
{
	double dx = p1.x - p2.x;
	double dy = p1.y - p2.y;
	return dx*dx + dy*dy;
}

double GenUtils::distance_sqrd(const wxRealPoint& p1, const wxPoint& p2)
{
	double dx = p1.x - p2.x;
	double dy = p1.y - p2.y;
	return dx*dx + dy*dy;
}

double GenUtils::distance_sqrd(const wxPoint& p1, const wxRealPoint& p2)
{
	double dx = p1.x - p2.x;
	double dy = p1.y - p2.y;
	return dx*dx + dy*dy;
}

double GenUtils::distance_sqrd(const wxPoint& p1, const wxPoint& p2)
{
	double dx = p1.x - p2.x;
	double dy = p1.y - p2.y;
	return dx*dx + dy*dy;
}

// calculates distance from point p0 to an infinite line passing through
// points p1 and p2
double GenUtils::pointToLineDist(const wxPoint& p0, const wxPoint& p1,
								 const wxPoint& p2)
{
	double d_p1p2 = distance(p1, p2);
	if (d_p1p2 <= 16*DBL_MIN) return distance(p0, p1);
	return abs((p2.x-p1.x)*(p1.y-p0.y)-(p1.x-p0.x)*(p2.y-p1.y))/d_p1p2;
}

void GenUtils::strToInt64(const wxString& str, wxInt64 *val)
{
	char buf[1024];
	strcpy( buf, (const char*)str.mb_str(wxConvUTF8) );
	strToInt64(buf, val);
}

// Convert an ASCII string into a wxInt64 (or long long)
void GenUtils::strToInt64(const char *str, wxInt64 *val)
{
	wxInt64 total = 0;
	bool minus = 0;

	while (isspace(*str)) str++;
	if (*str == '+') {
		str++;
	} else if (*str == '-') {
		minus = true;
		str++;
	}
	while (isdigit(*str)) {
		total *= 10;
		total += (*str++ - '0');
	}
	*val = minus ? -total : total;
}

bool GenUtils::validInt(const wxString& str)
{
	char buf[1024];
	strcpy( buf, (const char*)str.mb_str(wxConvUTF8) );
	return validInt(buf);
}

// Checks that an ASCII string can be parsed to a valid integer.  At least
// one digit must been found.
bool GenUtils::validInt(const char* str)
{
	//LOG_MSG(wxString::Format("GenUtils::validInt(\"%s\"):", str));
	while (isspace(*str)) str++;
	if (*str == '+' || *str == '-') str++;
	const char* t = str;
	while (isdigit(*str)) str++;
	if (t == str) {
		// no digit found so return false
		//LOG_MSG("   no digit found");
		return false;
	}
	while (isspace(*str)) str++;
	// only return true if we are finally pointing at
	// the null terminating character.
	//LOG_MSG(wxString::Format("   final char is null: %d", *str == '\0'));
	return *str == '\0';
}

bool GenUtils::isEmptyOrSpaces(const wxString& str)
{
	char buf[1024];
	strcpy( buf, (const char*)str.mb_str(wxConvUTF8) );
	return isEmptyOrSpaces(buf);
}

// returns true if the string is either empty
// or has only space characters
bool GenUtils::isEmptyOrSpaces(const char *str)
{
	while (isspace(*str)) str++;
	// if the first not-space char is not the end of the string,
	// return false.
	return *str == '\0';
}

bool GenUtils::ExistsShpShxDbf(const wxFileName& fname, bool* shp_found,
							   bool* shx_found, bool* dbf_found)
{
	wxFileName shp(fname);
	shp.SetExt("shp");
	wxFileName shx(fname);
	shx.SetExt("shx");
	wxFileName dbf(fname);
	dbf.SetExt("dbf");
	if (shp_found) *shp_found = shp.FileExists();
	if (shx_found) *shx_found = shx.FileExists();
	if (dbf_found) *dbf_found = dbf.FileExists();
	return shp.FileExists() && shx.FileExists() && dbf.FileExists();
}

wxString GenUtils::FindLongestSubString(const std::vector<wxString> strings,
										bool cs)
{
	using namespace std;
	int n=strings.size();
	if (n == 0) return "";
	vector<wxString> strs(strings);
	if (!cs) for (int i=0; i<n; i++) strs[i].MakeLower();
	wxString ref_str = strs[0];
	for (int i=0; i<n; ++i) {
		if (strs[i].length() < ref_str.length()) ref_str = strs[i];
	}
	int len = ref_str.length();
	if (len == 0) return "";
	// iterate over all possible substrings in ref_str starting from first
	// position in ref_str, and starting with full ref_str.  Reduce length
	// of substring to search each iteration.
	for (int cur_len=len; cur_len > 0; --cur_len) {
		for (int cur_pos=0; cur_pos <= len-cur_len; ++cur_pos) {
			wxString ss = ref_str.substr(cur_pos, cur_len);
			bool all_match = true; // substring found everywhere currently
			for (int i=0; i<n && all_match; i++) {
				if (strs[i].find(ss) == wxString::npos) all_match = false;
			}
			if (all_match) {
				// common substring found.  Return unmodified (case-preserved)
				// substring from first string
				return strings[0].substr(strs[0].find(ss), cur_len);
			}
		}
	}
	return ""; // no substring match, return empty string.
}

bool GenUtils::less_vectors(const std::vector<int>& a,const std::vector<int>& b) {
    return a.size() > b.size();
}

wxString GenUtils::WrapText(wxWindow *win, const wxString& text, int widthMax)
{
	class HardBreakWrapper : public wxTextWrapper
	{
		public:
		HardBreakWrapper(wxWindow *win, const wxString& text, int widthMax) {
			Wrap(win, text, widthMax);
		}
		wxString const& GetWrapped() const { return m_wrapped; }
		protected:
		virtual void OnOutputLine(const wxString& line) {
			m_wrapped += line;
		}
		virtual void OnNewLine() {
			m_wrapped += '\n';
		}
		private:
		wxString m_wrapped;
	};
	HardBreakWrapper wrapper(win, text, widthMax);
	return wrapper.GetWrapped();
}

wxString GenUtils::GetBasemapCacheDir()
{
	wxString exePath = wxStandardPaths::Get().GetExecutablePath();
	wxFileName exeFile(exePath);
	wxString exeDir = exeFile.GetPathWithSep();
	return exeDir;
}

wxString GenUtils::GetWebPluginsDir()
{
	wxString exePath = wxStandardPaths::Get().GetExecutablePath();
	wxFileName exeFile(exePath);
	wxString exeDir = exeFile.GetPathWithSep();
    exeDir << "web_plugins" << wxFileName::GetPathSeparator();

	return exeDir;
}

wxString GenUtils::GetResourceDir()
{
	wxString exePath = wxStandardPaths::Get().GetExecutablePath();
	wxFileName exeFile(exePath);
	wxString exeDir = exeFile.GetPathWithSep();
    exeDir << "../Resources" << wxFileName::GetPathSeparator();

	return exeDir;
}

wxString GenUtils::GetSamplesDir()
{
#ifdef __WXOSX__
    return GetResourceDir();
#else
    return GetWebPluginsDir();
#endif
}


//  Copyright (c) 2019 AUTHORS
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include "octotiger/defs.hpp"
#include "octotiger/grid.hpp"
#include "octotiger/options.hpp"
#include "octotiger/physcon.hpp"
#include "octotiger/real.hpp"
#include "octotiger/common_kernel/interaction_constants.hpp"

#include <boost/program_options.hpp>

#include <cmath>
#include <iosfwd>
#include <sstream>
#include <string>
#include <vector>

#define IN_OPTIONS_CPP

constexpr real mass_solar = 1.2969;
constexpr real number_solar = 1.0994;
constexpr real X_solar = 0.7068;
constexpr real Z_solar = 0.0181;

options& opts() {
	static options opts_;
	return opts_;
}

inline std::string to_string(const std::string &str) {
	return str;
}

inline std::string to_string(const real &num) {
	std::ostringstream strm;
	strm << std::scientific << num;
	return strm.str();
}

inline std::string to_string(const integer &num) {
	return std::to_string(num);
}

inline std::string to_string(const size_t &num) {
	return std::to_string(num);
}

inline std::string to_string(const bool &b) {
	return b ? "T" : "F";
}

bool options::process_options(int argc, char *argv[]) {
	namespace po = boost::program_options;
	code_to_s = code_to_g = code_to_cm = 1.0;

	po::options_description command_opts("options");

	command_opts.add_options() //
	("help", "produce help message")("xscale", po::value<real>(&(opts().xscale))->default_value(1.0), "grid scale")           //
	("dt_max", po::value<real>(&(opts().dt_max))->default_value(0.333333), "max allowed pct change for positive fields in a timestep")           //
	("cfl", po::value<real>(&(opts().cfl))->default_value(0.4), "cfl factor")           //
	("omega", po::value<real>(&(opts().omega))->default_value(0.0), "(initial) angular frequency")                          //
	("v1309", po::value<bool>(&(opts().v1309))->default_value(false), "V1309 subproblem of DWD")                   //
	("idle_rates", po::value<bool>(&(opts().idle_rates))->default_value(true), "show idle rates and locality info in SILO")                 //
	("eblast0", po::value<real>(&(opts().eblast0))->default_value(1.0), "energy for blast wave")     //
	("rho_floor", po::value<real>(&(opts().rho_floor))->default_value(0.0), "density floor")     //
	("tau_floor", po::value<real>(&(opts().tau_floor))->default_value(0.0), "entropy tracer floor")     //
	("sod_rhol", po::value<real>(&(opts().sod_rhol))->default_value(1.0), "density in the left part of the grid")     //
	("sod_rhor", po::value<real>(&(opts().sod_rhor))->default_value(0.125), "density in the right part of the grid")     //
	("sod_pl", po::value<real>(&(opts().sod_pl))->default_value(1.0), "pressure in the left part of the grid")     //
	("sod_pr", po::value<real>(&(opts().sod_pr))->default_value(0.1), "pressure in the right part of the grid")     //
	("sod_theta", po::value<real>(&(opts().sod_theta))->default_value(0.0), "angle made by diaphragm normal w/x-axis (deg)")     //
	("sod_phi", po::value<real>(&(opts().sod_phi))->default_value(90.0), "angle made by diaphragm normal w/z-axis (deg)")     //
	("sod_gamma", po::value<real>(&(opts().sod_gamma))->default_value(1.4), "ratio of specific heats for gas")     //
        ("solid_sphere_xcenter", po::value<real>(&(opts().solid_sphere_xcenter))->default_value(0.25), "x-position of the sphere center")     //
        ("solid_sphere_ycenter", po::value<real>(&(opts().solid_sphere_ycenter))->default_value(0.0), "y-position of the sphere center")     //
        ("solid_sphere_zcenter", po::value<real>(&(opts().solid_sphere_zcenter))->default_value(0.0), "z-position of the sphere center")     //
        ("solid_sphere_radius", po::value<real>(&(opts().solid_sphere_radius))->default_value(1.0 / 3.0), "radius of the sphere")     //
        ("solid_sphere_mass", po::value<real>(&(opts().solid_sphere_mass))->default_value(1.0), "total mass enclosed inside the sphere")     //
        ("solid_sphere_rho_min", po::value<real>(&(opts().solid_sphere_rho_min))->default_value(1.0e-12), "minimal density outside (and within) the sphere")     //
        ("star_xcenter", po::value<real>(&(opts().star_xcenter))->default_value(0.0), "x-position of the star center")     //
        ("star_ycenter", po::value<real>(&(opts().star_ycenter))->default_value(0.0), "y-position of the star center")     //
        ("star_zcenter", po::value<real>(&(opts().star_zcenter))->default_value(0.0), "z-position of the star center")     //
        ("star_n", po::value<real>(&(opts().star_n))->default_value(1.5), "polytropic index of the star")     //
        ("star_rmax", po::value<real>(&(opts().star_rmax))->default_value(1.0 / 3.0), "maximal star radius")     //
        ("star_dr", po::value<real>(&(opts().star_dr))->default_value(1.0 / (3.0 * 128.0)), "differential radius for solving the Lane-Emden equation")     //
        ("star_alpha", po::value<real>(&(opts().star_alpha))->default_value(1.0 / (3.0 * 3.65375)), "scaling factor for the Lane-Emden equation") // for default n=3/2, ksi_1=3.65375 and alpha=rmax/ksi_1
        ("star_rho_center", po::value<real>(&(opts().star_rho_center))->default_value(1.0), "density at the center of the star")     //
        ("star_rho_out", po::value<real>(&(opts().star_rho_out))->default_value(1.0e-10), "density outside the star")     //
	("star_egas_out", po::value<real>(&(opts().star_egas_out))->default_value(1.0e-10), "gas energy outside the star")     //
        ("moving_star_xvelocity", po::value<real>(&(opts().moving_star_xvelocity))->default_value(1.0), "velocity of the star in the x-direction")     //
        ("moving_star_yvelocity", po::value<real>(&(opts().moving_star_yvelocity))->default_value(1.0), "velocity of the star in the y-direction")     //
        ("moving_star_zvelocity", po::value<real>(&(opts().moving_star_zvelocity))->default_value(1.0), "velocity of the star in the z-direction")     //
	("clight_retard", po::value<real>(&(opts().clight_retard))->default_value(1.0), "retardation factor for speed of light")                 //
	("driving_rate", po::value<real>(&(opts().driving_rate))->default_value(0.0), "angular momentum loss driving rate")     //
	("driving_time", po::value<real>(&(opts().driving_time))->default_value(0.0), "A.M. driving rate time")                 //
	("entropy_driving_time", po::value<real>(&(opts().entropy_driving_time))->default_value(0.0), "entropy driving rate time")                 //
	("entropy_driving_rate", po::value<real>(&(opts().entropy_driving_rate))->default_value(0.0), "entropy loss driving rate")      //
	("future_wait_time", po::value<integer>(&(opts().future_wait_time))->default_value(-1), "")      //
	("silo_offset_x", po::value<integer>(&(opts().silo_offset_x))->default_value(0), "")      //
	("silo_offset_y", po::value<integer>(&(opts().silo_offset_y))->default_value(0), "")      //
	("silo_offset_z", po::value<integer>(&(opts().silo_offset_z))->default_value(0), "")      //
	("amrbnd_order", po::value<integer>(&(opts().amrbnd_order))->default_value(1), "amr boundary interpolation order")        //
	("scf_output_frequency", po::value<integer>(&(opts().scf_output_frequency))->default_value(25), "Frequency of SCF output")        //
	("silo_num_groups", po::value<integer>(&(opts().silo_num_groups))->default_value(-1), "Number of SILO I/O groups")        //
	("core_refine", po::value<bool>(&(opts().core_refine))->default_value(false), "refine cores by one more level")           //
	("accretor_refine", po::value<integer>(&(opts().accretor_refine))->default_value(0), "number of extra levels for accretor") //
	("extra_regrid", po::value<integer>(&(opts().extra_regrid))->default_value(0), "number of extra regrids on startup") //
	("donor_refine", po::value<integer>(&(opts().donor_refine))->default_value(0), "number of extra levels for donor")      //
	("ngrids", po::value<integer>(&(opts().ngrids))->default_value(-1), "fix numbger of grids")                             //
	("refinement_floor", po::value<real>(&(opts().refinement_floor))->default_value(1.0e-3), "density refinement floor")      //
	("theta", po::value<real>(&(opts().theta))->default_value(0.5), "controls nearness determination for FMM, must be between 1/3 and 1/2")               //
	("eos", po::value<eos_type>(&(opts().eos))->default_value(IDEAL), "gas equation of state")                              //
	("hydro", po::value<bool>(&(opts().hydro))->default_value(true), "hydro on/off")    //
	("radiation", po::value<bool>(&(opts().radiation))->default_value(false), "radiation on/off")    //
	("correct_am_hydro", po::value<bool>(&(opts().correct_am_hydro))->default_value(true), "Angular momentum correction switch for hydro")    //
	("correct_am_grav", po::value<bool>(&(opts().correct_am_grav))->default_value(true), "Angular momentum correction switch for gravity")    //
	("rewrite_silo", po::value<bool>(&(opts().rewrite_silo))->default_value(false), "rewrite silo and exit")    //
	("rad_implicit", po::value<bool>(&(opts().rad_implicit))->default_value(true), "implicit radiation on/off")    //
	("gravity", po::value<bool>(&(opts().gravity))->default_value(true), "gravity on/off")    //
	("bench", po::value<bool>(&(opts().bench))->default_value(false), "run benchmark") //
	("datadir", po::value<std::string>(&(opts().data_dir))->default_value("./"), "directory for output") //
	("output", po::value<std::string>(&(opts().output_filename))->default_value(""), "filename for output") //
	("odt", po::value<real>(&(opts().output_dt))->default_value(1.0 / 100.0), "output frequency") //
	("dual_energy_sw1", po::value<real>(&(opts().dual_energy_sw1))->default_value(0.001), "dual energy switch 1") //
	("dual_energy_sw2", po::value<real>(&(opts().dual_energy_sw2))->default_value(0.1), "dual energy switch 2") //
	("hard_dt", po::value<real>(&(opts().hard_dt))->default_value(-1), "timestep size") //
	("experiment", po::value<int>(&(opts().experiment))->default_value(0), "experiment") //
	("unigrid", po::value<bool>(&(opts().unigrid))->default_value(false), "unigrid") //
	("inflow_bc", po::value<bool>(&(opts().inflow_bc))->default_value(false), "Inflow Boundary Conditions") //
	("reflect_bc", po::value<bool>(&(opts().reflect_bc))->default_value(false), "Reflecting Boundary Conditions") //
	("cdisc_detect", po::value<bool>(&(opts().cdisc_detect))->default_value(true), "PPM contact discontinuity detection") //
	("disable_output", po::value<bool>(&(opts().disable_output))->default_value(false), "disable silo output") //
	("disable_diagnostics", po::value<bool>(&(opts().disable_diagnostics))->default_value(false), "disable diagnostics") //
	("problem", po::value<problem_type>(&(opts().problem))->default_value(NONE), "problem type")                            //
	("restart_filename", po::value<std::string>(&(opts().restart_filename))->default_value(""), "restart filename")         //
	("stop_time", po::value<real>(&(opts().stop_time))->default_value(std::numeric_limits<real>::max()), "time to end simulation") //
	("stop_step", po::value<integer>(&(opts().stop_step))->default_value(std::numeric_limits<integer>::max() - 1), "number of timesteps to run")          //
	("min_level", po::value<integer>(&(opts().min_level))->default_value(1), "minimum number of refinement levels")         //
	("max_level", po::value<integer>(&(opts().max_level))->default_value(1), "maximum number of refinement levels")         //
	("multipole_kernel_type", po::value<interaction_kernel_type>(&(opts().m2m_kernel_type))->default_value(SOA_CPU), "boundary multipole-multipole kernel type") //
	("p2p_kernel_type", po::value<interaction_kernel_type>(&(opts().p2p_kernel_type))->default_value(SOA_CPU), "boundary particle-particle kernel type")   //
	("p2m_kernel_type", po::value<interaction_kernel_type>(&(opts().p2m_kernel_type))->default_value(SOA_CPU), "boundary particle-multipole kernel type") //
	("cuda_streams_per_locality", po::value<size_t>(&(opts().cuda_streams_per_locality))->default_value(size_t(0)), "cuda streams per HPX locality") //
	("cuda_streams_per_gpu", po::value<size_t>(&(opts().cuda_streams_per_gpu))->default_value(size_t(0)), "cuda streams per GPU (per locality)") //
	("cuda_scheduling_threads", po::value<size_t>(&(opts().cuda_scheduling_threads))->default_value(size_t(0)),
			"Number of worker threads per locality that mamage cuda streams") //
	("input_file", po::value<std::string>(&(opts().input_file))->default_value(""), "input file for test problems") //
	("config_file", po::value<std::string>(&(opts().config_file))->default_value(""), "configuration file") //
	("n_species", po::value<integer>(&(opts().n_species))->default_value(5), "number of mass species") //
	("atomic_mass", po::value<std::vector<real>>(&(opts().atomic_mass))->multitoken(), "atomic masses") //
	("atomic_number", po::value<std::vector<real>>(&(opts().atomic_number))->multitoken(), "atomic numbers") //
	("X", po::value<std::vector<real>>(&(opts().X))->multitoken(), "X - hydrogen mass fraction") //
	("Z", po::value<std::vector<real>>(&(opts().Z))->multitoken(), "Z - metallicity") //
	("code_to_g", po::value<real>(&(opts().code_to_g))->default_value(1), "code units to grams") //
	("code_to_cm", po::value<real>(&(opts().code_to_cm))->default_value(1), "code units to centimeters") //
	("code_to_s", po::value<real>(&(opts().code_to_s))->default_value(1), "code units to seconds") //
	("rotating_star_amr", po::value<bool>(&(opts().rotating_star_amr))->default_value(false), "rotating star with AMR boundary in star") //
	("rotating_star_x", po::value<real>(&(opts().rotating_star_x))->default_value(0.0), "x center of rotating_star") //
			;

	boost::program_options::variables_map vm;
	po::store(po::parse_command_line(argc, argv, command_opts), vm);
	po::notify(vm);
	if (vm.count("help")) {
		std::cout << command_opts << "\n";
		return false;
	}
	if (!config_file.empty()) {
		std::ifstream cfg_fs { vm["config_file"].as<std::string>() };
		if (cfg_fs) {
			po::store(po::parse_config_file(cfg_fs, command_opts), vm);
		} else {
			printf("Configuration file %s not found!\n", config_file.c_str());
			return false;
		}
	}
	po::notify(vm);
	if (opts().silo_num_groups == -1) {
		opts().silo_num_groups = hpx::find_all_localities().size();

	}
	if (opts().problem == DWD) {
		opts().n_species = std::max(int(5), int(opts().n_species));
	}
        if (opts().problem == MOVING_STAR || opts().problem == ROTATING_STAR) {
                opts().n_species = std::max(int(2), int(opts().n_species));
	}
	n_fields = n_species + 10;
	if (!opts().restart_filename.empty()) {
		FILE *fp = fopen(opts().restart_filename.c_str(), "rb");
		if (fp == NULL) {
			printf("restart.silo does not exist or invalid permissions\n");
			sleep(10);
			abort();
		} else {
			fclose(fp);
		}
		load_options_from_silo(opts().restart_filename);
	}
	if (opts().theta < octotiger::fmm::THETA_FLOOR) {
		std::cerr << "theta " << theta << " is too small since Octo-Tiger was compiled for a minimum of " << octotiger::fmm::THETA_FLOOR << std::endl;
		std::cerr << "Either increase theta or recompile with a new theta minimum using the cmake parameter OCTOTIGER_THETA_MINIMUM";
		abort();
	}
	{
#define SHOW( opt ) std::cout << std::string( #opt ) << " = " << to_string(opt) << '\n';
		std::cout << "atomic_number=";
		for (auto r : atomic_number) {
			std::cout << std::to_string(r) << ',';
		}
		std::cout << '\n';
		std::cout << "atomic_mass=";
		for (auto r : atomic_mass) {
			std::cout << std::to_string(r) << ',';
		}
		std::cout << '\n';
		std::cout << "X=";
		for (auto r : X) {
			std::cout << std::to_string(r) << ',';
		}
		std::cout << '\n';
		std::cout << "Z=";
		for (auto r : Z) {
			std::cout << std::to_string(r) << ',';
		}
		std::cout << '\n';
		const auto num_loc = hpx::find_all_localities().size();
		if (silo_num_groups > num_loc) {
			printf("Number of SILO file groups cannot be greater than number of localities. Setting silo_num_groupds to %li\n", num_loc);
			silo_num_groups = num_loc;
		}
		SHOW(accretor_refine);
		SHOW(amrbnd_order);
		SHOW(bench);
		SHOW(cdisc_detect);
		SHOW(cfl);
		SHOW(clight_retard);
		SHOW(config_file);
		SHOW(core_refine);
		SHOW(correct_am_grav);
		SHOW(correct_am_hydro);
		SHOW(code_to_cm);
		SHOW(code_to_g);
		SHOW(code_to_s);
		SHOW(cuda_streams_per_locality);
		SHOW(cuda_streams_per_gpu);
		SHOW(data_dir);
		SHOW(disable_output);
		SHOW(driving_rate);
		SHOW(driving_time);
		SHOW(dt_max);
		SHOW(donor_refine);
		SHOW(dual_energy_sw1);
		SHOW(dual_energy_sw2);
		SHOW(eblast0);
		SHOW(eos);
		SHOW(entropy_driving_rate);
		SHOW(entropy_driving_time);
		SHOW(future_wait_time);
		SHOW(hard_dt);
		SHOW(hydro);
		SHOW(inflow_bc);
		SHOW(input_file);
		SHOW(m2m_kernel_type);
		SHOW(min_level);
		SHOW(max_level);
		SHOW(n_species);
		SHOW(ngrids);
		SHOW(omega);
		SHOW(output_dt);
		SHOW(output_filename);
		SHOW(p2m_kernel_type);
		SHOW(p2p_kernel_type);
		SHOW(problem);
		SHOW(rad_implicit);
		SHOW(radiation);
		SHOW(refinement_floor);
		SHOW(reflect_bc);
		SHOW(restart_filename);
		SHOW(rotating_star_amr);
		SHOW(rotating_star_x);
		SHOW(scf_output_frequency);
		SHOW(silo_num_groups);
		SHOW(stop_step);
		SHOW(stop_time);
		SHOW(theta);
		SHOW(unigrid);
		SHOW(v1309);
		SHOW(idle_rates);
		SHOW(xscale);

	}
	while (atomic_number.size() < opts().n_species) {
		atomic_number.push_back(number_solar);
	}
	while (atomic_mass.size() < opts().n_species) {
		atomic_mass.push_back(mass_solar);
	}
	while (X.size() < opts().n_species) {
		X.push_back(X_solar);
	}
	while (Z.size() < opts().n_species) {
		Z.push_back(Z_solar);
	}
	normalize_constants();
	if (opts().problem == DWD) {
		if (opts().restart_filename == "" && opts().disable_diagnostics) {
			printf("Diagnostics must be enabled for DWD\n");
			sleep(10);
			abort();
		}
	}

	return true;
}

std::vector<hpx::id_type> options::all_localities = { };