
//Uncomment SHOW_DBG if you d like to see the time of calculation for each procedure
//#define  SHOW_DBG ;
#include "utilities_all_classes.hpp"
#include "stdio.h"
#include "stdint.h"
#include "stdlib.h"
#include "permutation.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <csetjmp> 
#include <ctime>
#include <list>
#include <iterator>
#include <vector>
#include <deque>
#include <set>
#include <map>
#include <utility>
#include <iostream>
#include <fstream>
#include <istream>
#include <sstream>
#include <string>
#include <new>
#include <cassert>
#include <iomanip>
#if defined(SHOW_DBG)||defined(SHOW_DBG_DAT)

#include "Windows.h"
#endif
//#include "shlwapi.h"
//#include "shlobj.h"
//#include <random>
#include <iterator>
#include "mtrand/mtrand.h"
#include "alglib/ap.h"
#include "alglib/linalg.h"
#include "alglib/solvers.h"
#if defined(_OPENMP)
#include <omp.h>
#endif
#undef min
//using namespace  boost::spirit::qi;
using namespace alglib;
using namespace std;

//random genereators for path and value
MTRand* drand_path;
MTRand* drand_val;
//Flag for testing
static bool test_flag=false;
//Flag whether shortcuts are taken during sumulation(Fast calculation option)
static bool true_calc = true;
//Flag for local/global stationarity
static bool localStationarity = 0;
//Flag for log transform of initial values(used for grades)
static bool log_transform = 0;
//Flag for testing
bool test_good=false;
#if defined(SHOW_DBG)
double prepare=0;
double post=0;
double post2=0;
int post_cnt=0;
double before=0;
double after=0;
double alloc1=0;
double alloc2=0;
double computecdf=0;
double convertval=0;
double drawval=0;
double find_nneib=0;
double find_octa=0;
double minmaxval=0;
double buildtemp=0;
double find_nneib_all=0;
double simulatepoint=0;
double intermid=0;
#endif
//Compair pairs with double and int enteties. Needed to sort indexes by distances
bool pairCompare(const std::pair<double, int>& firstElem, const std::pair<double, int>& secondElem) {
	return firstElem.first < secondElem.first;

}
//compare triplets of int, double , int. Needed for sorting
bool IntDoubleIntCompare(const IntDoubleInt& firstElem, const IntDoubleInt& secondElem) {
	if( firstElem.num == secondElem.num)
	{
		return firstElem.dist > secondElem.dist;
	}else
		return firstElem.num > secondElem.num;

}
//Return random integer from 0 to i
int myrandom(int i) {
	//double r = ((double) rand()/((double)(RAND_MAX)+(double)(1)));
	double r = (*drand_path)();

	/* x is a random floating point value in the range [0,n) {including 0, not including n}. */
	double x = (r * i);
	// integer random number in [0,n-1]
	return ((int)x);
}

//////////////////
//Assign parameters from parameter file to members of the class InfoIn
void InfoIn::ReadExtInfo(int& dimension, int& method,
	double& x0d, double& y0d, double& z0d, int& nxd, int& nyd, int& nzd, double& dxd, double& dyd, double& dzd, 
	double& x0t, double& y0t, double& z0t, int& nxt, int& nyt, int& nzt, double& dxt, double& dyt, double& dzt,
	int& nxtl, int& nytl, int& nztl, double& dxtl, double& dytl, double& dztl,
	int& verbosity, int& seed, int& nbr, int& nbg, bool& dynamic_template, int& n_max_levels_x, int& n_max_levels_y, int& n_max_levels_z, int& nb_locations_stats, int& mx_s, int& mn_s, int& order, String1Vec& files)
{
	//dimension of the problem 2D or 3D
	m_dimension = dimension;
	//index of the method: Legendre or splines
	m_method    = method;
	// Grid info
	//Origin
	m_grid_info.m_x0        = x0d;
	m_grid_info.m_y0        = y0d;
	m_grid_info.m_z0        = z0d;
	//number or nodes
	m_grid_info.m_nx        = nxd;
	m_grid_info.m_ny        = nyd;
	m_grid_info.m_nz        = nzd;
	//grid steps
	m_grid_info.m_dx        = dxd;
	m_grid_info.m_dy        = dyd;
	m_grid_info.m_dz        = dzd;
	// Training image info
	//Origin
	m_ti_info.m_x0          = x0t;
	m_ti_info.m_y0          = y0t;
	m_ti_info.m_z0          = z0t;
	//number of grid nodes
	m_ti_info.m_nx          = nxt;
	m_ti_info.m_ny          = nyt;
	m_ti_info.m_nz          = nzt;
	//grid steps
	m_ti_info.m_dx          = dxt;
	m_ti_info.m_dy          = dyt;
	m_ti_info.m_dz          = dzt;
	// Template info
	//Searching radius as a number of grid nodes
	m_template_info.m_nx    = nxtl;
	m_template_info.m_ny    = nytl;
	m_template_info.m_nz    = nztl;
	//searching steps
	m_template_info.m_dx    = dxtl;
	m_template_info.m_dy    = dytl;
	m_template_info.m_dz    = dztl;
	// Dynamic template .Not used
	m_dynamic_template      = dynamic_template;


	// Minimum and maximum number of neighbors
	m_mx_s                  = mx_s;
	m_mn_s                  = mn_s;
	//nneighb_pdf.resize(m_mx_s+1);//ILNUR.To know how bad the points are.
	// Order of the series
	m_order                 = order;

	// Verbosity level for debugging information output.
	m_verbosity    = verbosity;

	// Seed
	m_seed         = seed;

	// Number of realizations.
	m_n_realization=nbr;

	// Number of grid level
	m_ngrid        = nbg;

	// Input & ouput files.
	m_files        = files;

	if(m_dimension==1)
	{
		m_grid_info.m_nv     = m_grid_info.m_nx;    
		m_ti_info.m_nv       = m_ti_info.m_nx;    
		m_template_info.m_nv = m_template_info.m_nx;  
	}
	else if(m_dimension==2)
	{
		m_grid_info.m_nv     = m_grid_info.m_nx * m_grid_info.m_ny;
		m_ti_info.m_nv       = m_ti_info.m_nx * m_ti_info.m_ny;
		m_template_info.m_nv = m_template_info.m_nx * m_template_info.m_ny;
	}
	else
	{
		m_grid_info.m_nv     = m_grid_info.m_nx * m_grid_info.m_ny * m_grid_info.m_nz;
		m_ti_info.m_nv       = m_ti_info.m_nx * m_ti_info.m_ny * m_ti_info.m_nz;
		m_template_info.m_nv = m_template_info.m_nx * m_template_info.m_ny * m_template_info.m_nz;
	}
	// should be an input...
	m_nb_locations_stats = nb_locations_stats;
	//
	
	//ILNUR. Calculating revisits
	m_n_revisits=1;//(int)(log(m_grid_info.m_nv*1.0/m_nb_locations_stats)/log(2.0)+3);
	std::string path = m_files[3];
	m_fout_debug.open(path.c_str());
	m_fout_debug.precision(5); 
	m_fout_debug << "********************************************************************"<<endl;
	m_fout_debug << "*******************DEBUGGING INFORMATION****************************"<<endl;
	m_fout_debug << "********************************************************************"<<endl;

	int ngrx = (int)floor(log(1.0*m_grid_info.m_nx)/ log(2.0));
	int ngry = (int)floor(log(1.0*m_grid_info.m_ny) / log(2.0));
	int ngrz = (int)floor(log(1.0*m_grid_info.m_nz) / log(2.0));
	int ngr = max(ngry, ngrx);
	 ngr = max(ngr, ngrz);
	m_ngrid = min(ngr, m_ngrid);

}

void 
	InfoIn::WriteDebuggingInfo(Grid& grid, Tools& tools, InfoIn& infoin, int ind)
{
	if(m_hard_point)
		return;

	int real_no = tools.m_real_curr;

	if(ind==0)
	{
		m_fout_debug << "-----------------------------"<<endl;
		m_fout_debug << "GENERATION OF REALIZATION #: "<<real_no+1<<" "<<endl;
		m_fout_debug << "-----------------------------"<<endl;
	}

	double min=1e+10,max=-1e+10;

	if(ind > 0)
	{
		for(size_t i=0;i<tools.m_neighbors_info.m_samples_neighbor.size();i++)
		{
			int pn = tools.m_neighbors_info.m_samples_neighbor[i];
			if(grid.m_data[pn][infoin.m_dimension + tools.m_real_curr] < min)
				min = grid.m_data[pn][infoin.m_dimension + tools.m_real_curr];
			if(grid.m_data[pn][infoin.m_dimension + tools.m_real_curr] > max)
				max = grid.m_data[pn][infoin.m_dimension + tools.m_real_curr];
		}
	}

	if(ind > 0)
	{
		m_fout_debug << "ooooooooooooooooooooooooooooo"<<endl;
		if(infoin.m_dimension==2)
		{
			m_fout_debug << "Simulation of point: id \t i \t j \t value_calculated"<<endl;
			m_fout_debug << "                     "<<tools.m_cp<<"\t"<<grid.m_data[tools.m_cp][0]<<"\t"<<grid.m_data[tools.m_cp][1]<<"\t"<<grid.m_data[tools.m_cp][infoin.m_dimension + tools.m_real_curr]<<endl;
		}
		else
		{
			m_fout_debug << "Simulation of point: id \t i \t j \t k \t value_calculated"<<endl;
			m_fout_debug << "                     "<<tools.m_cp<<"\t"<<grid.m_data[tools.m_cp][0]<<"\t"<<grid.m_data[tools.m_cp][1]<<"\t"<<grid.m_data[tools.m_cp][2]<<"\t"<<grid.m_data[tools.m_cp][infoin.m_dimension + tools.m_real_curr]<<endl;
		}
		m_fout_debug << "ooooooooooooooooooooooooooooo"<<endl;
		m_fout_debug << endl;
		if(ind > 1)
		{

			m_fout_debug << "1- Samples used:"<<endl;
			m_fout_debug << endl;
			if(infoin.m_dimension==2)
				m_fout_debug << "id \t i \t j \t value"<<endl;
			else
				m_fout_debug << "id \t i \t j \t k \t value"<<endl;
			m_fout_debug << endl;

			for(size_t i=0;i<tools.m_neighbors_info.m_samples_neighbor.size();i++)
			{
				int pn = tools.m_neighbors_info.m_samples_neighbor[i];
				if(infoin.m_dimension==2)
					m_fout_debug << pn <<"\t"<<grid.m_data[pn][0]<<"\t"<<grid.m_data[pn][1]<<"\t"<<grid.m_data[pn][infoin.m_dimension + tools.m_real_curr]<<endl;
				else
					m_fout_debug << pn <<"\t"<<grid.m_data[pn][0]<<"\t"<<grid.m_data[pn][1]<<"\t"<<grid.m_data[pn][2]<<"\t"<<grid.m_data[pn][infoin.m_dimension + tools.m_real_curr]<<endl;
			}
			m_fout_debug << "-----------------------------"<<endl;
		}
		if(ind > 2)
		{
			m_fout_debug << "2- CPDF & CCDF calculated:   "<<endl;
			m_fout_debug << endl;

			m_fout_debug <<"Xi" <<"\t \t \t "<<"PDF(Xi)"<<"\t \t \t"<<"CDF(Xi)"<<endl;
			for(size_t i=0;i<tools.m_x_Leg_cdf.size();i++)
			{
				double xi = tools.m_x_Leg_cdf[i];
				xi = (xi + 1) * ( tools.m_neighbors_info.m_local_max - tools.m_neighbors_info.m_local_min) / 2.0 + tools.m_neighbors_info.m_local_min;
				double yi = tools.m_pdf[i];
				double zi = tools.m_cdf[i];

				if(i==0 || i==tools.m_x_Leg_cdf.size()-1)
				{
					m_fout_debug <<xi<<"\t \t \t"<< yi <<"\t \t \t"<< zi <<endl;
				}
				else
				{
					if(xi>0)
						m_fout_debug <<xi<<"\t \t"<< yi <<"\t \t \t"<< zi <<endl;
					else
						m_fout_debug <<xi<<"\t"<< yi <<"\t \t \t"<< zi <<endl;             
				}
			}
			m_fout_debug << "-----------------------------"<<endl;
		}

		if(ind > 3)
		{

			m_fout_debug << "3- Moments used:                "<<endl;
			m_fout_debug <<endl;

			int order = m_order;

			if(tools.m_stats_map_point.size()>0)
			{
				for(int j=0;j<=order;j++)
				{
					m_fout_debug << " o-Order"<<" "<<j<<": "<<endl;
					m_fout_debug <<endl;

					int nm = (int) tools.m_stats_map_point[j].size();

					m_fout_debug << "   *-Template shape" <<"\t \t \t \t \t "<<"Value"<<endl;
					m_fout_debug <<endl;

					for(int k=0;k<nm;k++)
					{

						int npair = (int) tools.m_stats_map_point[j][k].size();

						m_fout_debug << "     ";

						for(int l=0;l<npair;l++)
						{
							if(l<npair-1)
							{
								m_fout_debug <<tools.m_stats_map_point[j][k][l]<<" ";
							}
							else
							{
								if(npair==1)
									m_fout_debug <<tools.m_stats_map_point[j][k][l]<<"\t \t \t \t \t \t \t \t "<<tools.m_stats_map_values[j][k]<<" "<<endl;
								else
									m_fout_debug <<tools.m_stats_map_point[j][k][l]<<"\t \t \t \t \t \t "<<tools.m_stats_map_values[j][k]<<" "<<endl;
							}
						}
					}
					m_fout_debug << "-----------------------------"<<" "<<endl;
				}
			}

		}
		// Close the debugging file
		if(ind==-1)
			m_fout_debug.close();
	}
}


void
	Tools::ComputeXCdf(double& min, double& max)
{
	int n = m_cdf_samples;

	if(m_x_Leg_cdf.size()==0)
	{
		m_x_Leg_cdf.resize(n,0.0);
		m_x_real_cdf.resize(n,0.0);
		m_cdf.resize(n,0.0);
		m_pdf.resize(n,0.0);
	}

	double step = 2.0 / (n-1);

	for(int i=0;i<n;i++)
		m_x_Leg_cdf[i] = -1 + i*step;

	if(max<min)
	{
		std::cout << "Min is greater than Max in a local neighborhood, min: "<<min<<", max: "<<max<<std::endl;
		std::system("pause");
	}

	step = std::abs(max-min)/(n-1);

	for(int i=0;i<n;i++)
		m_x_real_cdf[i] = min + i*step;
}


double Tools::Dist_P1_P2(double& x1, double& x2)
{
	double dist = std::pow(std::abs(x2-x1),2.0);
	return (std::pow(dist,0.5));

}

double Tools::Dist_P1_P2(double& x1, double& y1, double& x2, double& y2)
{
	double dist = std::pow(std::abs(x2-x1),2.0) + std::pow(std::abs(y2-y1),2.0);
	return (std::pow(dist,0.5));

}

double Tools::Dist_P1_P2(double& x1, double& y1, double& z1, double& x2, double& y2, double& z2)
{
	double dist = std::pow(std::abs(x2-x1),2.0) + std::pow(std::abs(y2-y1),2.0) + std::pow(std::abs(z2-z1),2.0);
	return (std::pow(dist,0.5));

}

bool
	Tools::Poind2DInsideDomain(InfoIn& infoin, double& x, double& y)
{
	bool inside =true;
	if(x<infoin.m_grid_info.m_x0 ||
		y<infoin.m_grid_info.m_y0 ||
		x>infoin.m_grid_info.m_x0 + (infoin.m_grid_info.m_nx-1)*infoin.m_grid_info.m_dx ||
		y>infoin.m_grid_info.m_y0 + (infoin.m_grid_info.m_ny-1)*infoin.m_grid_info.m_dy )
	{
		inside = false;
	}
	return inside;
}

bool
	Tools::Poind3DInsideDomain(InfoIn& infoin, double& x, double& y, double& z)
{
	bool inside =true;
	if(x<infoin.m_grid_info.m_x0 ||
		y<infoin.m_grid_info.m_y0 ||
		z<infoin.m_grid_info.m_z0 ||
		x>infoin.m_grid_info.m_x0 + (infoin.m_grid_info.m_nx-1)*infoin.m_grid_info.m_dx ||
		y>infoin.m_grid_info.m_y0 + (infoin.m_grid_info.m_ny-1)*infoin.m_grid_info.m_dy ||
		z>infoin.m_grid_info.m_z0 + (infoin.m_grid_info.m_nz-1)*infoin.m_grid_info.m_dz)
	{
		inside = false;
	}
	return inside;
}


Int2Vec 
	Tools::UpdateUnsamples(InfoIn& infoin, Double2Vec& m_data, Int1Vec& m_multigrid_index, Int1Vec& unsamples)
{
	int n1,n2,n3,ix,iy,iz,grid_no;
	double rx,ry,rz;
	Int2Vec vec; 
	bool found_it;
	vec.resize(infoin.m_ngrid);

	for(int i=0;i<infoin.m_grid_info.m_nv;i++)
	{
		found_it = false;

		n1 = (int)alglib::round ((m_data[i][0]-infoin.m_grid_info.m_x0)/infoin.m_grid_info.m_dx);

		if(infoin.m_dimension>1)
			n2 =(int)alglib::round((m_data[i][1] -infoin.m_grid_info.m_y0)/infoin.m_grid_info.m_dy);
		if(infoin.m_dimension>2)
			n3 =(int)alglib::round((m_data[i][2] -infoin.m_grid_info.m_z0)/infoin.m_grid_info.m_dz);

		for(int gn=infoin.m_ngrid-1;gn>=0;gn--)
		{

			int grid_step = (int) std::pow(2.0,gn);

			rx = n1*1.0/(grid_step);
			ix = (int)rx;

			if(ix == rx)
			{
				if(infoin.m_dimension>1)
				{
					ry = n2*1.0 /(grid_step);
					iy = (int)ry;

					if(iy==ry)
					{
						if(infoin.m_dimension>2)
						{
							rz = n3*1.0 /(grid_step);
							iz = (int)rz;

							if(iz==rz)
							{
								grid_no = gn;
								found_it = true;
								break;
							}
						}
						else
						{
							grid_no = gn;
							found_it = true;
							break;
						}
					}
				}
				else
				{
					grid_no = gn;
					found_it = true;
					break;
				} 
			}
		}

		if(infoin.m_ngrid<=1)
			grid_no = 0;

		m_multigrid_index[i] = grid_no;

		if(unsamples[i]>=0)
		{
			if(m_data[i][infoin.m_dimension]!=-1111)
				vec[grid_no].push_back(i);
			else
				m_data[i][infoin.m_dimension]=gNullvalue;
		}



	}
	return (vec);
}

int 
	Tools::rand_int(int n)
{
	//double r = ((double) rand()/((double)(RAND_MAX)+(double)(1)));
	double r=(*drand_path)();

	/* x is a random floating point value in the range [0,n) {including 0, not including n}. */
	double x = (r * n);
	// integer random number in [0,n-1]
	return ((int) x);
}
Double1Vec
	Tools::ComputeLegendrePolyFull(double& p, int order)
{
	Double1Vec LP;
	LP.resize(order+1);
	for(int i=0;i<=order;i++)
	{
		if(i==0) {
			LP[i] = 1;
		} else {
			if(i==1){
				LP[i] = p;
			} else {
				LP[i] = (2*i-1)*(p)*LP[i-1] - (i-1)*LP[i-2];
				LP[i] = LP[i]/((double) i);
			}
		}
	}
	return LP;
}
double
	Tools::ComputeLegendrePoly(double& p, int& order)
{
	//ILNUR. lets make it faster
	double res=1;
	switch (order)
	{
	case 0:
		return 1;
	case 1:
		return p;
	default:
		double val=p;
		double val_0=1;
		for(int i=2;i<=order;i++)
		{
			res = ((2*i-1)*(p)*val - (i-1)*val_0)/i;
			val_0=val;
			val=res;
		}
		return res;	
	}
	/*Double1Vec LP;
	LP.resize(order+1);
	for(int i=0;i<=order;i++)
	{
	if(i==0) {
	LP[i] = 1;
	} else {
	if(i==1){
	LP[i] = p;
	} else {
	LP[i] = (2*i-1)*(p)*LP[i-1] - (i-1)*LP[i-2];
	LP[i] = LP[i]/((double) i);
	}
	}
	}
	return(LP[order]);*/
};


double
	Tools::ComputeLocalLegendreMoments(double& p, int& order)
{
	vector<double> vec(order+1,0.0);

	for(int orn=0;orn<=order;orn++)
	{
		if(orn==0) {
			vec[orn] = 1;
		} else {
			if(orn==1){
				vec[orn] = p;
			} else {
				vec[orn] = ( (2*orn-1)* p *vec[orn-1] - (orn-1) * vec[orn-2]) / ((double) orn);          
			}
		}
	}
	return (vec[order]);
}


void
	Tools::ComputeLegendreMomentsPdf(InfoIn& infoin)
{
	if(m_min_one_one.size()==0)
	{
		m_min_one_one.resize(m_cdf_samples);
		step_x_pdf = 2.0/(m_cdf_samples-1);
		for(int i=0;i<m_cdf_samples;i++)
			m_min_one_one[i] = -1 + i*step_x_pdf;
	}
	if(m_table_cond_legendre.size()==0)
	{
		m_table_cond_legendre.resize(m_min_one_one.size());
		for(size_t i=0;i<m_min_one_one.size();i++)
			m_table_cond_legendre[i].resize(infoin.m_order+1);
	}

	// n * p_n(x) = (2*n - 1) * x * p_(n-1)(x) - (n-1) * p_(n-2)(x)
	for(size_t i=0;i<m_min_one_one.size();i++)
	{
		double p = m_min_one_one[i];
		for(int order=0;order<=infoin.m_order;order++)
		{
			if(order==0) {
				m_table_cond_legendre[i][order] = 1;
			} else {
				if(order==1){
					m_table_cond_legendre[i][order] = p;
				} else {
					m_table_cond_legendre[i][order] = ( (2*order-1)* p *m_table_cond_legendre[i][order-1] - 
						(  order-1)    *m_table_cond_legendre[i][order-2]) / ((double) order);          
				}
			}
		}
	}
}

void
	Tools::ComputeLegendreMomentsSamples(InfoIn& infoin, Tools& tools, Samples& samples)
{
	int order = infoin.m_order;

	m_LegendreMomentsSamples.resize(order+1,0);

	int nsamples = samples.m_samples_info.m_n_samples;

	double val, mom = 0.0, min = samples.m_samples_info.m_min, max = samples.m_samples_info.m_max;

	for(int i=0;i<nsamples;i++)
	{
		for(int orn=0;orn<=order;orn++)
		{
			val = (samples.m_data[i][infoin.m_dimension] - min ) * 2 / (max - min) -1;
			m_LegendreMomentsSamples[orn] = m_LegendreMomentsSamples[orn] +  tools.ComputeLocalLegendreMoments(val,orn) / nsamples; 
		}
	}


}

int
	Tools:: binarySearch(Double1Vec &arr, double key)
{
	int position;

	int lowerbound=0;
	int upperbound=(int)arr.size()-1;
	// To start, find the subscript of the middle position.
	position = ( lowerbound + upperbound) / 2;

	while(lowerbound+1 < upperbound)
	{

		if (arr[position] > key)               // If the number is > key, ..
		{                                                       // decrease position by one.
			upperbound = position ;    
		}                                                      
		else                                                
		{                                                        // Else, increase position by one.
			lowerbound = position ;     
		}
		position = (lowerbound + upperbound) / 2;
	}
	return position;  // you may also consider returning the subscript
} 


Int2Vec Tools::GetAllPermutation(vector<int> map,int n_neib)
{
	Int2Vec permuts;
	int nsize= (int) (map.size()/2);
	vector<int> a;
	a.resize(n_neib);
	int n_zero_val=n_neib-nsize;
	for (int k=0;k<n_zero_val;k++)
	{
		a[k]=0;
	}
	for (int k=n_zero_val;k<n_neib;k++)
	{
		a[k]=map[2*(k-n_zero_val)+1];
	}
	Permute perm_manager(a);

	perm_manager.GenMult(n_neib);
	return perm_manager.perms;;
}
double 
	Tools::Weight_Average(InfoIn& infoin, Grid& grid, TrainingImage& ti, Samples& samples, Tools& tools, Stats& stats)
{
	double res=0;
	double w_sum=0;
	double w;
	for (int i=0;i<tools.m_neighbors_info.m_n_local_samples;i++)
	{
		w=(tools.m_neighbors_info.m_samples_distance[i]);
		w=w*w;
		w=1/w;
		w_sum+=w;
		int gp0 = tools.m_neighbors_info.m_samples_neighbor[i];
		res+=w* grid.m_data[gp0][infoin.m_dimension];
	}
	return res/w_sum;
}
double Tools::ConvertVal(double val,int ind_cur)
{
	
	/*int k=ind_cur/10000;
	if (k==1)
	{
	int ind=-1;
	for(int i=0;i<cat_vals.size();i++)
	{
	if(val==cat_vals[i])
	{
	ind=i;
	break;
	}
	}
	val=(ind+0.5)*2/(Lspline_cat.m_division-1)-1;

	}else*/
	if(Lspline.splines.size()>0&&Lspline.order==0)
	{
		int ind=-1;
		for(int i=0;i<cat_vals.size();i++)
		{
			if(val==cat_vals[i])
			{
				ind=i;
				break;
			}
		}
		val=(ind+0.5)*2/(Lspline.m_division-1)-1;

	}else
	{
		double maxv = (val>m_neighbors_info.m_local_max) ? val : m_neighbors_info.m_local_max;
		double minv = (val<m_neighbors_info.m_local_min) ? val : m_neighbors_info.m_local_min;
		if (log_transform)
		{
			val = max(1e-6, val);
			maxv = max(1e-6, maxv);
			minv = max(1e-6, minv);
			val = log(val);
			maxv = log(maxv);
			minv = log(minv);
			val = ((val - minv) * 2 / (maxv - minv)) - 1;
		}
		else
		{
		
			val = ((val - minv) * 2 / (maxv - minv)) - 1;
		}
		
	}
	return val;
}
double Tools::BackConvertVal(double val,int ind_cur)
{
	//int k=ind_cur/10000;
	//if(k==1)
	//{
	//	
	//	int ind=floor((val+1)*0.5*(Lspline_cat.m_division-1));
	//	val=cat_vals[ind];

	//}else
	if(Lspline.splines.size()>0&&Lspline.order==0)
	{
		int ind=(int)(floor((val+1)*0.5*(Lspline.m_division-1)));
		val=cat_vals[ind];

	}else
	{
		double maxv = m_neighbors_info.m_local_max;
		double minv = m_neighbors_info.m_local_min;
		if (log_transform)
		{
			maxv = max(1e-6, maxv);
			minv = max(1e-6, minv);
		
			maxv = log(maxv);
			minv = log(minv); 
			val = (val + 1)*0.5*(maxv - minv) + minv;
			val = exp(val);
		}else
			val = (val + 1)*0.5*(maxv - minv) + minv;
		
		
	}
	return val;
}
void Tools::Get_variogram_par(InfoIn& infoin,TrainingImage& ti)
{
	nug=0.01;
	sill=1;
	double mean=0;
	int cnt=0;
	double minti=1e+10;
	double maxti=1e-10;
	for(int i=0;i<infoin.m_ti_info.m_nx-1;i++)
		for(int j=0;j<infoin.m_ti_info.m_ny-1;j++)
		{
			int gp0=i+j*infoin.m_ti_info.m_nx;
			if(ti.m_data[gp0][infoin.m_dimension]!=gNullvalue)
			{
				double val0=ti.m_data[gp0][infoin.m_dimension];
				mean+=val0;
				maxti=max(val0,maxti);
				minti=min(val0,minti);
				cnt++;
			}
		}
		if(cnt>0)
			mean=mean/cnt;
		else
			return;
		double cnt_sill=0;
		double cnt_nug=0;
		double  nug_buf=0;
		sill=0;
		for(int i=0;i<infoin.m_ti_info.m_nx-1;i++)
			for(int j=0;j<infoin.m_ti_info.m_ny-1;j++)
			{
				int gp0=i+j*infoin.m_ti_info.m_nx;
				if(ti.m_data[gp0][infoin.m_dimension]!=gNullvalue)
				{
					double val0=ti.m_data[gp0][infoin.m_dimension]-mean;
					sill+=val0*val0;
					cnt_sill++;
					int gp=i+1+(j)*infoin.m_ti_info.m_nx;
					if(ti.m_data[gp][infoin.m_dimension]!=gNullvalue)
					{
						double val1=ti.m_data[gp][infoin.m_dimension]-mean;
						double varr=(val1-val0);
						nug_buf+=varr*varr*0.5;
						cnt_nug++;
					}

				}
			}

			sill=sill/cnt_sill;
			if(cnt_nug>0)
				nug_buf=nug_buf/cnt_nug;
			else
				return;
			double h=Get_dist_elp(infoin,infoin.m_template_info.m_dx,0,0);
			nug=(nug_buf-(1-exp(-3*h*h))*sill)/(exp(-3*h*h));


			double aa=2.0/(maxti-minti);
			nug*=aa*aa;
			sill*=aa*aa;
			nug=max(0.001,nug);
			nug=min(0.99, nug);
			return;

}
double Tools::variogram_test(double h)
{


	double val=(sill-nug)*(1-exp(-3*h*h))+nug;
	if(h==0)
		val=0;
	return val;
}
double Tools::covariance_test(double h)
{

	return sill-variogram_test(h);
}
int Tools:: var2mdiv(double varr)
{
	int mdivv=(int)(2.0/sqrt(varr*0.02)+1);
	mdivv=min(60,mdivv);
	mdivv=max(60,mdivv);
	return mdivv;
}
double Tools::kriging_var(Double2Vec Cov,Double1Vec var_b)
{
	real_1d_array b_alg,x;

	real_2d_array Cov_alg;
	int n= (int)var_b.size()+1;
	b_alg.setlength(n);
	Cov_alg.setlength(n,n);
	for (int i=0;i<n-1;i++)
		for (int j=0;j<n-1;j++)
			Cov_alg[i][j]=Cov[i][j];

	for (int i=0;i<n-1;i++)
	{
		b_alg[i]=var_b[i];
		Cov_alg[i][n-1]=1;
		Cov_alg[n-1][i]=1;
	}
	b_alg[n-1]=1;
	Cov_alg[n-1][n-1]=0;
	densesolverreport rep;
	ae_int_t inf=0;
	rmatrixsolve(Cov_alg,n,b_alg,inf,rep,x);

	double val=covariance_test(0)-x[n-1];
	for (int i=0;i<n-1;i++)
	{
		val-=x[i]*(var_b[i]);
	}
	if(val<nug)
	{
	//	cout<<"less than nugget "<<val<<endl;
		val=nug;
	}
	return val;
}
void Tools::Prepare_Repl(Double1Vec& coeff_1D_leg,int nneib,Grid& grid,InfoIn& infoin, Replicates& replicate_vec,vector<bool>& nneib_vec,vector<double>& neihbors_replicate,SplineContainer*& L_spline_rat,vector<SplineContainer*>& Lspline_all)
{
#if defined(SHOW_DBG)
	LARGE_INTEGER t1, t2;           // ticks
	double elapsedTime;
	LARGE_INTEGER frequency;        // ticks per second
	// get ticks per second
	QueryPerformanceFrequency(&frequency);

	// start timer
	QueryPerformanceCounter(&t1);
#endif
	/*Double1Vec cc;
	cc.resize(n_replicates);*/
	double thresh=0.2;
	int n_cnt=0;
	fill(coeff_1D_leg.begin(),coeff_1D_leg.end(),0);


	int m_div=31;

	neihbors_replicate.resize(nneib+1);
	neihbors_replicate[0]=-999;
	for (int i=0;i<nneib;i++)
	{

		int gp0 = m_neighbors_info.m_samples_neighbor[i];
		double val0 = m_neighbors_info.m_samples_neighbor_vals[i];
		neihbors_replicate[i+1]=ConvertVal(val0,gp0);
	}
	Double1Vec dummy;


	if(Lspline.splines.size()>0)
	{
		m_div=(int)Lspline.splines[Lspline.splines.size()-1].breaks.size();
		int order=Lspline.order;


		/*	double dxx=thresh;
		double dtt=2.0/(m_div-1);*/

		Double2Vec Cov;
		Cov.resize(nneib);
		for (int i=0;i<nneib;i++)
			Cov[i].resize(nneib);
		Double1Vec var_b(nneib);
		for (int i=0;i<nneib;i++)
		{

			// t_elp=clock();
			int gp0 = m_neighbors_info.m_samples_neighbor[i];
			int kk=gp0/(infoin.m_grid_info.m_nx*infoin.m_grid_info.m_ny);
			if(kk==infoin.m_index_cat)
			{

				Lspline_all[i]=&(Lspline_cat);
			}else
			{
				double val0 = m_neighbors_info.m_samples_neighbor_vals[i];
				neihbors_replicate[i+1]=ConvertVal(val0,gp0);

				Cov[i].resize(nneib);
				var_b[i]=covariance_test( m_neighbors_info.m_samples_distance[i]);
				for (int j=i;j<nneib;j++)
				{
					int gp_j= m_neighbors_info.m_samples_neighbor[j];
					double xx=grid.m_data[gp0][0]-grid.m_data[gp_j][0];
					double yy=grid.m_data[gp0][1]-grid.m_data[gp_j][1];
					double zz=0;
					if(infoin.m_dimension>2)
						zz=grid.m_data[gp0][2]-grid.m_data[gp_j][2];
					double hh=Get_dist_elp(infoin,xx,yy,zz);
					Cov[i][j]=Cov[j][i]=covariance_test(hh);

				}
				/*Double1Vec knots;
				knots.reserve(m_div);
				double x1=neihbors_replicate.m_value_replicate[i+1]-dxx;
				x1=max(x1,-1.0);
				double x2=neihbors_replicate.m_value_replicate[i+1]+dxx;
				x2=min(x2,1.0);

				int n1=(int)((x1+1)/dtt+1);
				if(x1>-1)
				knots.push_back(-1);
				for (int j=1;j<n1;j++)
				{
				knots.push_back(x1-dtt*(n1-j));

				}
				knots.push_back(x1);
				knots.push_back(x2);
				int n2=(int)((1-x2)/dtt+1);
				for (int j=1;j<n2;j++)
				{
				knots.push_back(x2+dtt*(j));

				}
				if(x2<1)
				knots.push_back(1);*/
				//Lspline_all[i].Create(Lspline.order,m_div,knots);
				double varr=variogram_test(m_neighbors_info.m_samples_distance[i]);
				int m_div_cur=var2mdiv(varr);
				m_div_cur = std::min(m_div_cur, infoin.m_mdiv_max);
				if(Lspline.order==0)
					m_div_cur=Lspline.m_division;
				//		cout<<"kriging \t"<<clock()-t_elp<<endl;
				//		t_elp=clock();
		//		m_div_cur=31;
			//	cout<<"node neigh "<<gp0<<endl;
				if( infoin.Lspline_Dump[m_div_cur].splines.size()<1)
					infoin.Lspline_Dump[m_div_cur].Create(Lspline.order,m_div_cur,dummy,true);
				Lspline_all[i]=&(infoin.Lspline_Dump[m_div_cur]);
				//Lspline_all[i]=&Lspline;//ILNUR.Get rid of it
				//			cout<<"Lspline_all[i] \t"<<clock()-t_elp<<endl;
			}
		}
#if defined(SHOW_DBG)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

		//cout<<"--------------------New_Splines \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
		QueryPerformanceCounter(&t1);
#endif
		coeff_1D_leg.clear();
		int k_central=m_cp/(infoin.m_grid_info.m_nx*infoin.m_grid_info.m_ny);
		if(k_central!=infoin.m_index_cat)

		{double min_dist=m_neighbors_info.m_samples_distance[0];

		Int1Vec neib_ind_def;
		Int1Vec neib_ind_undef;
		neib_ind_def.reserve(nneib);
		neib_ind_undef.reserve(nneib);

		neib_ind_def.clear();

		for (int i=0;i<nneib;i++)
		{
			neib_ind_def.push_back(i+1);

		}

		Double1Vec filtred_vals;


		int max_neib=0;
		//		cout<<"Lspline_all \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
		//	t_elp=clock();
		filtred_vals=Get_Filtred_Vals(neihbors_replicate,replicate_vec, neib_ind_def, 0, thresh,max_neib);
		//		cout<<"filtred_vals1 \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
		// t_elp=clock();
#if defined(SHOW_DBG)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

		//cout<<"--------------------filtred_vals \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
		QueryPerformanceCounter(&t1);
#endif
		for(int ppp=max_neib;ppp<nneib;ppp++)
		{
			neib_ind_def.pop_back();
			m_neighbors_info.m_samples_neighbor.pop_back();
			m_neighbors_info.m_samples_neighbor_vals.pop_back();
			m_neighbors_info.m_samples_distance.pop_back();
			m_neighbors_info.m_n_local_samples=(int) m_neighbors_info.m_samples_neighbor.size();
		}
		//if(max_neib>=infoin.m_mn_s)
		//{		
		//	filtred_vals=Get_Filtred_Vals(neihbors_replicate,Repl_TI, neib_ind_def, 0, thresh,max_neib);
		//	if(isspline>0)
		//		{
		//			L_spline_rat=Get_Knots(neihbors_replicate,Repl_TI, neib_ind_def, 0, thresh,m_div,order);

		//		}
		//}




		nneib_vec.clear();
		nneib_vec.resize(neib_ind_def.size(),true);



		//	L_spline_rat=Lspline;///bad!!

		var_b.resize(m_neighbors_info.m_n_local_samples);
		double varr=kriging_var(Cov,var_b);
		//	cout<<"kriging_var \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
		// t_elp=clock();
		int mm_div=var2mdiv(varr);;
		mm_div = min(mm_div, infoin.m_mdiv_max);
		if(Lspline.order==0)
			mm_div=Lspline.m_division;

	//	mm_div=31;
//		cout<<"mm_div "<<mm_div<<endl;
		if(infoin.Lspline_Dump[mm_div].splines.size()<1)
			infoin.Lspline_Dump[mm_div].Create(Lspline.order,mm_div,dummy,true);

		L_spline_rat=&(infoin.Lspline_Dump[mm_div]);
		/*
		SplineContainer Lget_knots=Get_Knots(filtred_vals, thresh*0.5,51,order);//ILNUR.Get rid of it
		if(Lget_knots.splines.size()>0)
		{
			Lspline=Lget_knots;
			L_spline_rat=&Lspline;
		}//ILNUR.Get rid of it
		*/

		}else
		{

			L_spline_rat=&(Lspline_cat);
		}
		Lspline_buf=L_spline_rat;
		coeff_1D_leg.resize(L_spline_rat->splines.size());
#if defined(SHOW_DBG)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

		//cout<<"--------------------kriging_var \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
		QueryPerformanceCounter(&t1);
#endif

	}else
	{
		L_spline_rat=&(Lspline);
		Lspline_buf=L_spline_rat;
		for (int i=0;i<nneib;i++)
			Lspline_all[i]=&(Lspline);

		Int1Vec neib_ind_def;
		Int1Vec neib_ind_undef;
		neib_ind_def.reserve(nneib);
		neib_ind_undef.reserve(nneib);

		neib_ind_def.clear();

		for (int i=0;i<nneib;i++)
		{
			neib_ind_def.push_back(i+1);

		}

		Double1Vec filtred_vals;


		int max_neib=0;
		//		cout<<"Lspline_all \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
		//	t_elp=clock();
		filtred_vals=Get_Filtred_Vals(neihbors_replicate,replicate_vec, neib_ind_def, 0, thresh,max_neib);

		//		cout<<"filtred_vals1 \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
		// t_elp=clock();
#if defined(SHOW_DBG)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

		//cout<<"--------------------filtred_vals \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
		QueryPerformanceCounter(&t1);
#endif
		for(int ppp=max_neib;ppp<nneib;ppp++)
		{
			neib_ind_def.pop_back();
			m_neighbors_info.m_samples_neighbor.pop_back();
			m_neighbors_info.m_samples_neighbor_vals.pop_back();
			m_neighbors_info.m_samples_distance.pop_back();
			m_neighbors_info.m_n_local_samples= (int)m_neighbors_info.m_samples_neighbor.size();
		}


		AdjustZeroProbability(filtred_vals);
		nneib_vec.clear();
		nneib_vec.resize(neib_ind_def.size(),true);


	}
	fill(coeff_1D_leg.begin(),coeff_1D_leg.end(),0);
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

	//cout<<"--------------------rest prepare \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
	QueryPerformanceCounter(&t1);
#endif

}
void Tools::Prepare_ReplOLD(Double1Vec& coeff_1D_leg,int nneib,Grid& grid,InfoIn& infoin, vector<Replicate>& replicate_vec,vector<bool>& nneib_vec,Replicate& neihbors_replicate,SplineContainer*& L_spline_rat,vector<SplineContainer*>& Lspline_all)
{
	/*
	#if defined(SHOW_DBG)
	LARGE_INTEGER t1, t2;           // ticks
	double elapsedTime;
	LARGE_INTEGER frequency;        // ticks per second
	// get ticks per second
	QueryPerformanceFrequency(&frequency);

	// start timer
	QueryPerformanceCounter(&t1);
	#endif

	double thresh=0.1;
	int n_cnt=0;
	fill(coeff_1D_leg.begin(),coeff_1D_leg.end(),0);


	int m_div=31;

	neihbors_replicate.m_value_replicate.resize(nneib+1);
	neihbors_replicate.m_value_replicate[0]=-999;
	for (int i=0;i<nneib;i++)
	{

	int gp0 = m_neighbors_info.m_samples_neighbor[i];
	double val0 = grid.m_data[gp0][infoin.m_dimension];
	neihbors_replicate.m_value_replicate[i+1]=ConvertVal(val0);
	}
	Double1Vec dummy;
	if(Lspline.splines.size()>0)
	{
	m_div=Lspline.splines[Lspline.splines.size()-1].breaks.size();
	int order=Lspline.order;



	Double2Vec Cov;
	Cov.resize(nneib);
	for (int i=0;i<nneib;i++)
	Cov[i].resize(nneib);
	Double1Vec var_b(nneib);
	for (int i=0;i<nneib;i++)
	{
	// t_elp=clock();
	int gp0 = m_neighbors_info.m_samples_neighbor[i];
	double val0 = grid.m_data[gp0][infoin.m_dimension];
	neihbors_replicate.m_value_replicate[i+1]=ConvertVal(val0);

	Cov[i].resize(nneib);
	var_b[i]=covariance_test( m_neighbors_info.m_samples_distance[i]);
	for (int j=i;j<nneib;j++)
	{
	int gp_j= m_neighbors_info.m_samples_neighbor[j];
	double xx=grid.m_data[gp0][0]-grid.m_data[gp_j][0];
	double yy=grid.m_data[gp0][1]-grid.m_data[gp_j][1];
	double zz=0;
	if(infoin.m_dimension>2)
	zz=grid.m_data[gp0][2]-grid.m_data[gp_j][2];
	double hh=Get_dist_elp(infoin,xx,yy,zz);
	Cov[i][j]=Cov[j][i]=covariance_test(hh);

	}

	//Lspline_all[i].Create(Lspline.order,m_div,knots);
	double varr=variogram_test(m_neighbors_info.m_samples_distance[i]);
	int m_div_cur=var2mdiv(varr);
	if(Lspline.order==0)
	m_div_cur=Lspline.m_division;
	//		cout<<"kriging \t"<<clock()-t_elp<<endl;
	//		t_elp=clock();
	//m_div_cur=200;
	if( infoin.Lspline_Dump[m_div_cur].splines.size()<1)
	infoin.Lspline_Dump[m_div_cur].Create(Lspline.order,m_div_cur,dummy,true);
	Lspline_all[i]=&(infoin.Lspline_Dump[m_div_cur]);
	//Lspline_all[i]=&Lspline;//ILNUR.Get rid of it
	//			cout<<"Lspline_all[i] \t"<<clock()-t_elp<<endl;
	}
	#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

	cout<<"--------------------New_Splines \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
	QueryPerformanceCounter(&t1);
	#endif
	double min_dist=m_neighbors_info.m_samples_distance[0];

	Int1Vec neib_ind_def;
	Int1Vec neib_ind_undef;
	neib_ind_def.reserve(nneib);
	neib_ind_undef.reserve(nneib);

	neib_ind_def.clear();

	for (int i=0;i<nneib;i++)
	{
	neib_ind_def.push_back(i+1);

	}

	Double1Vec filtred_vals;


	int max_neib=0;
	//		cout<<"Lspline_all \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
	//	t_elp=clock();
	filtred_vals=Get_Filtred_Vals(neihbors_replicate,replicate_vec, neib_ind_def, 0, thresh,max_neib);
	//		cout<<"filtred_vals1 \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
	// t_elp=clock();
	#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

	cout<<"--------------------filtred_vals \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
	QueryPerformanceCounter(&t1);
	#endif
	for(int ppp=max_neib;ppp<nneib;ppp++)
	{
	neib_ind_def.pop_back();
	m_neighbors_info.m_samples_neighbor.pop_back();
	m_neighbors_info.m_samples_distance.pop_back();
	m_neighbors_info.m_n_local_samples= m_neighbors_info.m_samples_neighbor.size();
	}
	//if(max_neib>=infoin.m_mn_s)
	//{		
	//	filtred_vals=Get_Filtred_Vals(neihbors_replicate,Repl_TI, neib_ind_def, 0, thresh,max_neib);
	//	if(isspline>0)
	//		{
	//			L_spline_rat=Get_Knots(neihbors_replicate,Repl_TI, neib_ind_def, 0, thresh,m_div,order);

	//		}
	//}




	nneib_vec.clear();
	nneib_vec.resize(neib_ind_def.size(),true);

	coeff_1D_leg.clear();

	//	L_spline_rat=Lspline;///bad!!

	var_b.resize(m_neighbors_info.m_n_local_samples);
	double varr=kriging_var(Cov,var_b);
	//	cout<<"kriging_var \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
	// t_elp=clock();
	int mm_div=var2mdiv(varr);;

	if(Lspline.order==0)
	mm_div=Lspline.m_division;


	if(infoin.Lspline_Dump[mm_div].splines.size()<1)
	infoin.Lspline_Dump[mm_div].Create(Lspline.order,mm_div,dummy,true);

	L_spline_rat=&(infoin.Lspline_Dump[mm_div]);

	//SplineContainer Lget_knots=Get_Knots(filtred_vals, thresh*0.5,51,order);//ILNUR.Get rid of it
	//if(Lget_knots.splines.size()>0)
	//{
	//	Lspline=Lget_knots;
	//	L_spline_rat=&Lspline;
	//}//ILNUR.Get rid of it

	Lspline_buf=L_spline_rat;
	coeff_1D_leg.resize(L_spline_rat->splines.size());
	#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

	cout<<"--------------------kriging_var \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
	QueryPerformanceCounter(&t1);
	#endif

	}else
	{
	L_spline_rat=&(Lspline);
	Lspline_buf=L_spline_rat;
	for (int i=0;i<nneib;i++)
	Lspline_all[i]=&(Lspline);

	Int1Vec neib_ind_def;
	Int1Vec neib_ind_undef;
	neib_ind_def.reserve(nneib);
	neib_ind_undef.reserve(nneib);

	neib_ind_def.clear();

	for (int i=0;i<nneib;i++)
	{
	neib_ind_def.push_back(i+1);

	}

	Double1Vec filtred_vals;


	int max_neib=0;
	//		cout<<"Lspline_all \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
	//	t_elp=clock();
	filtred_vals=Get_Filtred_Vals(neihbors_replicate,replicate_vec, neib_ind_def, 0, thresh,max_neib);

	//		cout<<"filtred_vals1 \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
	// t_elp=clock();
	#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

	cout<<"--------------------filtred_vals \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
	QueryPerformanceCounter(&t1);
	#endif
	for(int ppp=max_neib;ppp<nneib;ppp++)
	{
	neib_ind_def.pop_back();
	m_neighbors_info.m_samples_neighbor.pop_back();
	m_neighbors_info.m_samples_distance.pop_back();
	m_neighbors_info.m_n_local_samples= m_neighbors_info.m_samples_neighbor.size();
	}


	AdjustZeroProbability(filtred_vals);
	nneib_vec.clear();
	nneib_vec.resize(neib_ind_def.size(),true);


	}
	fill(coeff_1D_leg.begin(),coeff_1D_leg.end(),0);
	#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

	cout<<"--------------------rest prepare \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
	QueryPerformanceCounter(&t1);
	#endif
	*/

}
void Tools:: AdjustZeroProbability(Double1Vec& filtred_vals)
{
	double thresh=0.15;
	int Ndisc= (int) m_pdf.size();
	fill(m_pdf.begin(),m_pdf.end(),-999);
	int di=(int)(thresh*Ndisc*0.5)+2;
	for (int j=0;j<filtred_vals.size();j++)
	{
		int ii=(int)( (filtred_vals[j]+1)*Ndisc*0.5);
		int i0=max(ii-di,0);
		int i1=min(ii+di+1,Ndisc);
		for (int i=i0; i<i1;i++)
		{

			if (fabs(m_x_Leg_cdf[i]-filtred_vals[j])<thresh)
				m_pdf[i]=0;


		}

	}


}


bool
	Tools::ComputeCdf(InfoIn& infoin, Grid& grid, TrainingImage& ti, Samples& samples, Tools& tools, Stats& stats,int calc_type,		Tools& dat_tools)
{
#if defined(SHOW_DBG) ||defined(SHOW_DBG_DAT)
	LARGE_INTEGER t1, t2;           // ticks
	double elapsedTime;
	LARGE_INTEGER frequency;        // ticks per second
	// get ticks per second
	QueryPerformanceFrequency(&frequency);

	// start timer
	QueryPerformanceCounter(&t1);
#endif
	clock_t t_elp;
	t_elp=clock();
	Lspline_buf=&(Lspline);
	int size=1,rank=0;

	double thresh=0.05;//2.0/(m_div-1);
	//MPI_Init(&argcc,&argvv)	;
	//cout<<"INIT"<<endl;
	if(calc_type>1)
	{
#ifdef USE_MPI
		MPI_Comm_rank(MPI_COMM_WORLD,&rank);
		//    cout<<"rank"<<endl;

		MPI_Comm_size(MPI_COMM_WORLD,&size);

		MPI_Request request;
		MPI_Status status;
#endif
	}
	int nm, npair,  orn, dim, gp, n_replicates;
	double  mom, val,val_legandre,  mom_t;

	for(int i=0;i<tools.m_cdf_samples;i++)
	{
		tools.m_pdf[i] = 0.0;
		tools.m_cdf[i] = 0.0;
	}

	int order = infoin.m_order;

	bool m_use_only_samples = false;

	int order_fs_up = 0;

	//if(m_use_only_samples)
	//{
	//	for(int i=0;i<tools.m_cdf_samples;i++)
	//	{
	//		tools.m_pdf[i] = 0.0;

	//		if(tools.m_stats_map_point.size()>0)
	//		{
	//			for(int j=0;j<=order;j++)
	//			{
	//				nm = (int) tools.m_stats_map_point[j].size();

	//				for(int k=0;k<nm;k++)
	//				{
	//					npair = (int) tools.m_stats_map_point[j][k].size();

	//					poly= 1.0;
	//					mom = 1.0;

	//					for(int l=0;l<npair;l++)
	//					{
	//						pg  = m_stats_map_point[j][k][l];
	//						orn = 1;

	//						if(pg==0)
	//						{
	//							if(npair==1)
	//								orn = j;

	//							if(orn>0)
	//							{
	//								poly = tools.m_table_cond_legendre[i][orn];
	//								mom  = tools.m_table_cond_legendre[i][orn];
	//							}
	//							mom = mom * poly * (double) (2*orn+1)/2.0;
	//						}
	//						else
	//						{
	//							// remember orn is 1 for all samples around for now.
	//							dim= infoin.m_dimension;
	//							//gp = tools.m_neighbors_info.m_samples_neighbor[pg-1];
	//							gp = tools.m_neighbors_info.m_samples_neighbor[pg];
	//							val = grid.m_data[gp][dim];
	//							max = (grid.m_data[gp][dim]>tools.m_neighbors_info.m_local_max) ? grid.m_data[gp][dim]:tools.m_neighbors_info.m_local_max ;
	//							min = (grid.m_data[gp][dim]<tools.m_neighbors_info.m_local_max) ? grid.m_data[gp][dim]:tools.m_neighbors_info.m_local_max ;
	//							val = ((val-min)*2/(max-min)) - 1;
	//							poly = val;
	//							mom = mom * val * poly * (double) (2*orn+1)/2.0;
	//						}
	//					}
	//					tools.m_pdf[i] += mom;
	//				}      
	//			}
	//		}      
	//	}
	//}
	//else // use training image
	//{
	double n_var=tools.m_neighbors_info.m_n_local_samples+1; // ILNUR.for now!!! should be size of neighbors+1
	// For j=2: we need to re-visit this to model more accurately from samples.
	// Evaluate statistics first
	int nneib=(int)tools.m_neighbors_info.m_samples_neighbor.size();
	Double1Vec leg_mom;

	int isspline=infoin.m_method>2;
	int max_perm=0;
	Double1Vec coeff_1D_leg;
	coeff_1D_leg.resize( coeff_1D.size());
	vector<bool> nneib_vec(nneib,true);
	n_replicates = tools.m_all_value_replicates.m_size;  
	SplineContainer* L_spline_rat=&(Lspline);
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	QueryPerformanceCounter(&t1);
	if(nneib==infoin.m_mx_s)
		alloc1+=elapsedTime;
	//cout<<"----------alloc1 \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;

#endif
	Replicates & Repl_TI=tools.m_all_value_replicates;


	if(n_replicates<1)
		return false;
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	QueryPerformanceCounter(&t1);
	//cout<<"----------alloc2 \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
	if(nneib==infoin.m_mx_s)
		alloc2+=elapsedTime;
#endif
	if (calc_type<1)
	{
		if(tools.m_stats_map_point.size() > 0)
		{
			// ILNUR.Let's try do more general

			leg_mom.reserve(10000);
			for(int j=0;j<=order;j++)
			{
				nm = (int) tools.m_stats_map_point[j].size();

				for(int k=0;k<nm;k++)
				{
					npair = (int) tools.m_stats_map_point[j][k].size()/2;
					mom_t = 0.0;


					int n_cnt=0;	

					Int2Vec permuts=GetAllPermutation( tools.m_stats_map_point[j][k],nneib+1);

					double poly_sum=0;

					for (int p=0;p<permuts.size();p++)
					{
						double mom_t=0;
						int n_cnt=0;
						for(int nt=0;nt<n_replicates;nt++)
						{
							double mom=1;
							for(int l=0;l<nneib+1;l++)
							{

								orn=permuts[p][l];

								dim= infoin.m_dimension;
								val = tools.m_all_value_replicates.m_values[nt* tools.m_all_value_replicates.m_shift+l];


								val_legandre=ComputeLegendrePoly(val,orn)*sqrt( (2.0*orn+1));
								mom = mom * val_legandre;

							}
							n_cnt++;
							mom_t = mom_t + mom ;
						}

						//mom_t = mom_t + mom / n_replicates;//ILNUR. n_replicates could change
						if (n_cnt==0)
						{
							cout<<"Division by zero.S omething wrong"<<endl;
							cin>>mom_t;
						}
						mom_t/=n_cnt*pow(2.0,n_var*0.5);// warning 0 n_cnt!!
						leg_mom.push_back( mom_t);// * (double) (2*j+1)/2.0;//lookout coeff.
					}
				}

			}
			//	for(int j=0;j<=order;j++)
			//	{
			//		nm = (int) tools.m_stats_map_point[j].size();

			//		for(int k=0;k<nm;k++)
			//		{
			//			npair = (int) tools.m_stats_map_point[j][k].size()/2;
			//			mom_t = 0.0;

			//			n_replicates = (int) stats.m_all_value_replicates.size();        
			//			int n_cnt=0;	

			//			for(int nt=0;nt<n_replicates;nt++)
			//			{
			//				mom  = 1.0;
			//				bool is_bad_val=false;
			//				//Checking if there is no NULL_value
			//				for(int l=0;l<npair;l++)
			//				{
			//					pg  = m_stats_map_point[j][k][2*l];
			//					val = stats.m_all_value_replicates[nt].m_value_replicate[pg];
			//					if(val==-999)
			//					{						
			//						is_bad_val=true;
			//						break;
			//					}
			//				}
			//				if (!is_bad_val)
			//				{
			//					for(int l=0;l<npair;l++)
			//					{
			//						pg  = m_stats_map_point[j][k][2*l];
			//						orn = m_stats_map_point[j][k][2*l+1];
			//						dim= infoin.m_dimension;
			//						val = stats.m_all_value_replicates[nt].m_value_replicate[pg];
			//						max = (val>tools.m_neighbors_info.m_local_max) ? val:tools.m_neighbors_info.m_local_max ;
			//						min = (val<tools.m_neighbors_info.m_local_min) ? val:tools.m_neighbors_info.m_local_min ;
			//						val = ((val-min)*2/(max-min)) - 1;

			//						val_legandre=ComputeLegendrePoly(val,orn)*sqrt( (2.0*orn+1));
			//						mom = mom * val_legandre;
			//					}
			//					n_cnt++;
			//					mom_t = mom_t + mom ;
			//				}

			//			}

			//				//mom_t = mom_t + mom / n_replicates;//ILNUR. n_replicates could change
			//			if (n_cnt==0)
			//			{
			//				cout<<"Division by zero.S omething wrong"<<endl;
			//				cin>>mom_t;
			//			}
			//			mom_t/=n_cnt;// warning 0 n_cnt!!
			//			m_stats_map_values[j][k] = mom_t/pow(2.0,n_var*0.5);// * (double) (2*j+1)/2.0;//lookout coeff.
			//		}
			//}
			/* ILNUR.Let's try do mo general
			for(int j=0;j<=order;j++)
			{
			if(j >= order_fs_up ) 
			{
			nm = (int) tools.m_stats_map_point[j].size();

			for(int k=0;k<nm;k++)
			{
			npair = (int) tools.m_stats_map_point[j][k].size();

			mom_t = 0.0;

			n_replicates = (int) stats.m_list_point_stats.size();

			if(npair<0)
			//if (k==0)//ILNUR. Get all legendre from TI
			{
			pg  = 0; 
			orn = j;
			mom_t = tools.m_LegendreMomentsSamples[orn];
			}
			else
			{
			int n_cnt=0;	

			for(int nt=0;nt<n_replicates;nt++)
			{
			mom  = 1.0;
			bool is_bad_val=false;
			for(int l=0;l<npair;l++)
			{
			pg  = m_stats_map_point[j][k][l];

			orn = 1;


			if(pg==0)
			{
			if(npair==1)
			orn = j;

			val = ti.m_data[stats.m_list_point_stats[nt]][infoin.m_dimension];
			max = (val>tools.m_neighbors_info.m_local_max) ? val:tools.m_neighbors_info.m_local_max ;
			min = (val<tools.m_neighbors_info.m_local_min) ? val:tools.m_neighbors_info.m_local_min ;
			val = ((val-min)*2/(max-min)) - 1;
			val = tools.ComputeLocalLegendreMoments(val,orn);
			mom = mom * tools.ComputeLocalLegendreMoments(val,orn); //tools.m_table_cond_legendre[loc][orn];;
			}
			else
			{
			// remember orn is 1 for all samples around for now.
			dim= infoin.m_dimension;
			val = stats.m_all_value_replicates[nt].m_value_replicate[pg];

			if(val!=-999)
			{
			max = (val>tools.m_neighbors_info.m_local_max) ? val:tools.m_neighbors_info.m_local_max ;
			min = (val<tools.m_neighbors_info.m_local_min) ? val:tools.m_neighbors_info.m_local_min ;
			val = ((val-min)*2/(max-min)) - 1;
			mom = mom * val; // * (double) (2*orn+1)/2.0;
			}
			else
			{
			//something wrong
			is_bad_val=true;
			}
			}
			}
			if (!is_bad_val)
			{
			n_cnt++;
			mom_t = mom_t + mom ;
			}
			//mom_t = mom_t + mom / n_replicates;//ILNUR. n_replicates could change

			}
			mom_t/=n_cnt;// warning 0 n_cnt!!
			}
			m_stats_map_values[j][k] = mom_t * (double) (2*j+1)/2.0;
			}
			}
			else
			{
			nm = (int) tools.m_stats_map_point[j].size();

			for(int k=0;k<nm;k++)
			{
			npair = (int) tools.m_stats_map_point[j][k].size();

			mom = 1.0;

			for(int l=0;l<npair;l++)
			{
			pg  = m_stats_map_point[j][k][l];
			orn = 1;

			if(pg==0)
			{
			}
			else
			{
			// remember orn is 1 for all samples around for now.
			dim= infoin.m_dimension;
			//gp = tools.m_neighbors_info.m_samples_neighbor[pg-1];
			gp = tools.m_neighbors_info.m_samples_neighbor[pg];
			val = grid.m_data[gp][dim];
			max = (val>tools.m_neighbors_info.m_local_max) ? val:tools.m_neighbors_info.m_local_max ;
			min = (val<tools.m_neighbors_info.m_local_min) ? val:tools.m_neighbors_info.m_local_min ;
			val = ((val-min)*2/(max-min)) - 1;
			mom = mom * val; // * 1.5;
			}
			}
			m_stats_map_values[j][k] = mom * (double) (2*j+1)/2.0;
			}
			}
			}*/
		}
	}
	else if (calc_type<2)
	{

		//cout<<"Prepair "<<clock()-t_elp<<endl;
		//Linqquin algorithm
		// t_elp=clock();
		Double1Vec neihbors_replicate;
		vector<SplineContainer*> Lspline_all(nneib);


		Prepare_Repl(coeff_1D_leg, nneib, grid, infoin, Repl_TI,nneib_vec, neihbors_replicate,L_spline_rat,Lspline_all);
		//		cout<<"filtred_vals \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
#if defined(SHOW_DBG)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
		QueryPerformanceCounter(&t1);
		if(nneib==infoin.m_mx_s)
			prepare+=elapsedTime;
		//cout<<"----------prepare \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;

#endif
		int nrepl_max = Repl_TI.m_neibcount_indexes[Repl_TI.m_neibcount_indexes.size() - 1]- Repl_TI.m_neibcount_indexes[nneib];
		if (isspline>0)	
			//Fill_Cond_Spline( 1,neihbors_replicate_parr, Repl_TI_parr,coeff_1D_leg_parr, nneib_vec_parr,mt,L_spline_rat_parr,Lspline_all_parr);
			Fill_Cond_Spline(nrepl_max,neihbors_replicate, Repl_TI,coeff_1D_leg, nneib_vec, Repl_TI.m_neibcount_indexes[nneib],L_spline_rat,Lspline_all);
		else
			//Fill_Cond_Legendre(1,neihbors_replicate_parr,  Repl_TI_parr,coeff_1D_leg_parr, nneib_vec_parr,mt,order_parr);
			Fill_Cond_Legendre(nrepl_max,neihbors_replicate,  Repl_TI,coeff_1D_leg, nneib_vec, Repl_TI.m_neibcount_indexes[nneib],order);


#if defined(SHOW_DBG)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
		QueryPerformanceCounter(&t1);
		if(nneib==infoin.m_mx_s)
		{
			post+=elapsedTime;
			post_cnt++;
		}
		//cout<<"----------post \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;

#endif

		// t_elp=clock();
		for (int w=0;w<coeff_1D_leg.size();w++)
			coeff_1D_leg[w]/=n_replicates;

	}else if(calc_type<3)
	{
		time_t start_real,end_real;

		time (&start_real);
		//tools.m_cp=52;
#if DEBUG
		cout<<"nneib:\t"<<nneib<<endl;
#endif
		fill(coeff_1D_leg.begin(),coeff_1D_leg.end(),0);


		Int1Vec neib_ind_def;
		Int1Vec neib_ind_undef;
		neib_ind_def.reserve(nneib);
		neib_ind_undef.reserve(nneib);
		Replicates& data_repl=tools.m_data_value_replicates;

		int n_Data_repl=min((int)data_repl.m_size,n_replicates);
		int n_Data_repl_cnt=0;
		time (&start_real);

		int dim2=(int)(n_Data_repl/size);
		int beg_ind=dim2*(rank);
		if(size==rank+1)
		{
			dim2=n_Data_repl-dim2*(size-1);
		}
		int size_buf=dim2*(nneib+1);
		int* rcounts=new int[size];
		int* displs=new int[size];
		int dim_std=(nneib+1)*(int)(n_Data_repl/size);

		for (int ii=0;ii<size;ii++)
		{
			rcounts[ii]=dim_std;
			displs[ii]=dim_std*ii;
			//	cout<<"process "<<rank<< "rcount "<<rcounts[ii]<<endl;
			//	cout<<"process "<<rank<< "displs "<<displs[ii]<<endl;
		}
		rcounts[size-1]=n_Data_repl*(nneib+1)-(size-1)*dim_std;
		//	cout<<"process "<<rank<< "rcount "<<rcounts[size-1]<<endl;
		//cout<<"process "<<rank<<"begin at "<<beg_ind<<" dim \t"<<dim2<<" size_buf"<< size_buf<<endl;

		//		MPI_Finalize();
		//				int size_buf=dim2*(nneib+1);
		/*ofstream fout;
		std::stringstream ss;
		ss<<"debug_proc_"<<rank<<".txt"<<endl;
		std::string fname;
		ss>>fname;
		fout.open(fname.c_str(),std::ios_base::app);*/
		double * arr_parts=new double[size_buf];
		double * arr_whole=new double[(nneib+1)*n_Data_repl];
		if (rank==0)
		{

			for(int mt=0;mt<n_Data_repl;mt++)
			{
				int repl_base=data_repl.m_shift*mt;
				for(int j=0;j<nneib+1;j++)
				{
					arr_whole[mt*(nneib+1)+j]=data_repl.m_values[j+repl_base];
					//	fout<<"arr_whole proc "<<rank<<"data"<<mt<<"_"<<"\t"<<arr_whole[mt*(nneib+1)+j]<<endl;
				}
			}
		}
#ifdef USE_MPI
		MPI_Barrier(MPI_COMM_WORLD);
		MPI_Scatterv(arr_whole,rcounts,displs,MPI_DOUBLE,	arr_parts,size_buf,MPI_DOUBLE,0,MPI_COMM_WORLD);
#else
		for(int mt=beg_ind;mt<dim2+beg_ind;mt++)
		{
			for(int j=0;j<nneib+1;j++)
			{
				arr_parts[(mt-beg_ind)*(nneib+1)+j]=arr_whole[mt*(nneib+1)+j];
				//fout<<"arr_parts proc "<<rank<<"data"<<mt<<"_"<<"\t"<<data_repl[mt].m_value_replicate[j]<<endl;	
			}

		}
#endif
		for(int mt=beg_ind;mt<dim2+beg_ind;mt++)
		{
			int repl_base=data_repl.m_shift*mt;
			for(int j=0;j<nneib+1;j++)
			{
				data_repl.m_values[j+repl_base]=arr_parts[(mt-beg_ind)*(nneib+1)+j];
				//fout<<"arr_parts proc "<<rank<<"data"<<mt<<"_"<<"\t"<<data_repl[mt].m_value_replicate[j]<<endl;	
			}

		}
		clock_t t_elp=clock();
		//			#pragma omp for nowait
		//Temprorary output of Repl_Dump

		//

#if defined(SHOW_DBG_DAT)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

		cout<<"----------prepare datadrv \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
		QueryPerformanceCounter(&t1);
#endif
		for(int mt=beg_ind;mt<dim2+beg_ind;mt++)
		{
			int repl_base=data_repl.m_shift*mt;
			//indexes[tid].push_back(mt);
			//	t_elp=clock();
			//Full staff
			neib_ind_def.clear();
			neib_ind_undef.clear();
			int j_cnt=0;
			for(int j=0;j<nneib+1;j++)
			{
				if(data_repl.m_values[j+repl_base]!=gNullvalue)
				{
					neib_ind_def.push_back(j);


				}else
					neib_ind_undef.push_back(j);

			}
			//	cout<<"time 1 "<<mt<<" is"<<clock()-t_elp<<endl;

			Get_Data_Replicate_Spline(data_repl,repl_base,neib_ind_def, neib_ind_undef,  dat_tools,  infoin,grid, ti,  samples, stats);
			//cout<<mt<<endl;
			//	t_elp=clock();
			for(int j=0;j<nneib+1;j++)
			{
				arr_parts[(mt-beg_ind)*(nneib+1)+j]=data_repl.m_values[repl_base+j];
				//	fout<<"arr_parts after proc "<<rank<<"data"<<mt<<"_"<<"\t"<<data_repl[mt].m_value_replicate[j]<<endl;	
			}
			//	cout<<"time2"<<mt<<" is"<<clock()-t_elp<<endl;
		}
#if defined(SHOW_DBG_DAT)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

		cout<<"----------Get_Data_Replicate_Spline datadrv \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
		QueryPerformanceCounter(&t1);
#endif
		//	cout<<"time for repl "<<clock()-t_elp<<endl;

		//				cout<<"size double"<<sizeof(double)<<"size MPI"<<sizeof(MPI_DOUBLE)<<endl;
#ifdef USE_MPI
		//		cout<<"befor MPI_Gatherv on process"<<rank<<endl;	
		MPI_Barrier(MPI_COMM_WORLD);
		MPI_Gatherv(arr_parts,size_buf,MPI_DOUBLE,arr_whole,rcounts,displs,MPI_DOUBLE,0,MPI_COMM_WORLD);

		//	cout<<"good before barrier 0 "<<rank<<endl;

		MPI_Barrier(MPI_COMM_WORLD);
		//cout<<"good before bcast 0 "<<rank<<endl;

		MPI_Bcast(arr_whole, (nneib+1)*n_Data_repl, MPI_DOUBLE,0,MPI_COMM_WORLD);
		//	cout<<"good aftrt Bcast 0 "<<rank<<endl;

#else
		for(int mt=beg_ind;mt<dim2+beg_ind;mt++)
		{
			for(int j=0;j<nneib+1;j++)
			{
				arr_whole[mt*(nneib+1)+j]=arr_parts[(mt-beg_ind)*(nneib+1)+j];
				//fout<<"arr_parts proc "<<rank<<"data"<<mt<<"_"<<"\t"<<data_repl[mt].m_value_replicate[j]<<endl;	
			}

		}
#endif

		int have_val=0;			
		//  MPI_Finalize();

		for (int mt=0;mt<n_Data_repl;mt++)
		{
			int repl_base=data_repl.m_shift*mt;
			for(int j=0;j<nneib+1;j++)
			{
				data_repl.m_values[repl_base+j]=  arr_whole[(mt)*(nneib+1)+j];
				//	 fout<<"arr_whole proc_after "<<rank<<"data"<<mt<<"_"<<j<<"\t"<<data_repl[mt].m_value_replicate[j]<<endl;
			}
		}
		for(int mt=0;mt<n_Data_repl;mt++)
		{	
			int repl_base=data_repl.m_shift*mt;
			int ind0=data_repl.m_indexes[repl_base+0];
			int data_ind=dat_tools.m_sample_indicator_orig[ind0]-1;
			for(int l=1;l<nneib+1;l++)
			{
				double datval=data_repl.m_values[repl_base+l];
				if(datval!=gNullvalue)
				{

					int Dump_ind=Repl_Dump.Get_Dump_ind( m_ijk_ref_replicate[l-1]);
					if(Dump_ind>=0)
					{

						if(data_ind>=0)
						{
							if(Repl_Dump.values[data_ind][Dump_ind]!=-999)
							{
								//cout<<"not_null"<<endl;
							}else
								Repl_Dump.values[data_ind][Dump_ind]=BackConvertVal(datval,data_ind);
							//	cout<<"proc "<<rank<<" is " <<datval<<endl;
						}
						else
							cout<<"Bad data_ind"<<endl;

					}
				}
			}
		}
		//	fout.close();
		//		  }  /* end of parallel section */

		time (&end_real);
		//	cout<<"Get_Data_Replicate_Spline_MP:\t"<< difftime (end_real,start_real)<<endl;
		//				time (&start_real);
		//				for(int mt=0;mt<n_Data_repl;mt++)
		//				{
		//					
		//					
		//					int start_mt=mt;//stats.m_multi_template[mt];
		//					int end_mt=mt+1;//stats.m_multi_template[mt+1];
		//						if(end_mt>=n_Data_repl)
		//					{
		//						end_mt=n_Data_repl;
		//						
		//					}
		//					//Full staff
		//					neib_ind_def.clear();
		//					neib_ind_undef.clear();
		//					for(int j=0;j<nneib+1;j++)
		//					{
		//						if(data_repl[start_mt].m_value_replicate[j]!=-999)
		//						{
		//							neib_ind_def.push_back(j);
		//						}else
		//							neib_ind_undef.push_back(j);
		//
		//					}
		//						
		////					time (&start_real);
		//					Get_Data_Replicate_Spline( data_repl[start_mt],ReplBasis, Repl_TI,neib_ind_def, neib_ind_undef,  Lspline,m_x_Leg_cdf);
		//	//				time (&end_real);
		//		//			cout<<"Get_Data_Replicate_Spline:\t"<< difftime (end_real,start_real)<<endl;
		//											if(end_mt>=n_Data_repl)
		//					{
		//						break;
		//						}
		//				}
		//	
		//	time (&end_real);
		//	/*THE TOTAL RUN TIME*/
		//	
		//	cout<<"Get_Data_Replicate_Spline:\t"<< difftime (end_real,start_real)<<endl;

		time (&start_real);
		//tools.m_cp=52;
		int n_Data_repl_max=Repl_TI.m_size;
		n_Data_repl_max=min(n_Data_repl_max,n_Data_repl);
		Replicates all_repl(n_Data_repl_max,nneib);
		int cnt_add=0;
		all_repl.m_size=0;
		for (int nt=0;nt<data_repl.m_size;nt++)
		{
			bool need_add=true;
			int repl_base=nt*data_repl.m_shift;
			for(int j=0;j<nneib+1;j++)
				if(data_repl.m_values[j+repl_base]==gNullvalue)
				{

					need_add=false;
					break;
				}
				if(need_add)
				{
					for(int j=0;j<nneib+1;j++)
					{
						all_repl.m_values[cnt_add]=data_repl.m_values[j+repl_base];
						all_repl.m_indexes[cnt_add]=data_repl.m_indexes[j+repl_base];
						cnt_add++;
					}
					all_repl.m_size++;
				}
		}
		/*	if(n_Data_repl>data_repl.size())	
		cout<<"bad repl N is \t"<<n_Data_repl-data_repl.size()<<endl;*/
		int n_Data_repl_new=all_repl.m_size;

		for (int nt=n_Data_repl_new;nt<n_Data_repl_max;nt++)
		{
			int repl_base=(nt-n_Data_repl_new)*Repl_TI.m_shift;
			for (int j=0;j<nneib+1;j++)
			{
				all_repl.m_values[cnt_add]=Repl_TI.m_values[repl_base+j];
				all_repl.m_indexes[cnt_add]=-1;
				cnt_add++;
			}
		}
		/*if (n_Data_repl==0)
		return false;*/
		vector<double> neihbors_replicate;
		vector<SplineContainer*>	Lspline_all(nneib);
#if defined(SHOW_DBG_DAT)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

		cout<<"----------Prepare2 datadrv \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
		QueryPerformanceCounter(&t1);
#endif
		Prepare_Repl(coeff_1D_leg, nneib, grid, infoin, all_repl,nneib_vec, neihbors_replicate,L_spline_rat,Lspline_all);
#if defined(SHOW_DBG_DAT)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

		cout<<"----------Prepare3 datadrv \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
		QueryPerformanceCounter(&t1);
#endif


		if (isspline>0)	
			Fill_Cond_Spline( all_repl.m_size,neihbors_replicate, all_repl,coeff_1D_leg, nneib_vec,0,L_spline_rat,Lspline_all);
		else
			Fill_Cond_Legendre(all_repl.m_size,neihbors_replicate,  all_repl,coeff_1D_leg, nneib_vec,0,order);

#if defined(SHOW_DBG_DAT)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

		cout<<"----------Fill_Cond_Spline datadrv \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
		QueryPerformanceCounter(&t1);
#endif			

		/*THE TOTAL RUN TIME*/
		//		cout<<"Fill_Cond_Spline:\t"<< difftime (end_real,start_real)<<endl;	

		for (int w=0;w<coeff_1D_leg.size();w++)
			coeff_1D_leg[w]/=data_repl.m_size;



		delete [] rcounts;
		delete []  displs;
		delete []  arr_parts;
		delete [] arr_whole;


		//fout.close();
		//	 cout<<" proc "<<rank<<"have_val"<<have_val<<endl;



	}
	else
	{




	}

	//}else
	//	{
	//			//Linqquin algorithm + data_set replicates
	//		Double1Vec cc;
	//		cc.resize(n_replicates);
	//		int n_cnt=0;

	//		//TI
	//	Double1Vec coeff_1D_leg_ti,coeff_1D_leg_repl;
	//	coeff_1D_leg_repl.resize(coeff_1D.size());
	//	coeff_1D_leg_ti.resize(coeff_1D.size());
	//		for(int mt=0;mt<stats.m_multi_template.size()-1;mt++)
	//		{
	//			fill(coeff_1D_leg_repl.begin(),coeff_1D_leg_repl.end(),0);

	//			int n_Data_repl=stats.m_multi_template[mt+1]-stats.m_multi_template[mt];
	//			if (isspline>0)
	//					Fill_Cond_Spline(n_Data_repl, infoin,tools,stats, grid,  stats.m_data_value_replicates,coeff_1D_leg_repl, mt,stats.m_multi_template[mt]);
	//				else
	//					Fill_Cond_Legendre(n_Data_repl, infoin,tools,stats, grid,  stats.m_data_value_replicates,coeff_1D_leg_repl, mt,stats.m_multi_template[mt],false);
	//			int n_TI_repl=0;

	//	
	//			//n_TI_repl=0;
	//			if(calc_type<3)
	//			{
	//				fill(coeff_1D_leg_ti.begin(),coeff_1D_leg_ti.end(),0);
	//				 n_TI_repl=max(n_replicates-n_Data_repl,0);

	//				 	if (isspline>0)
	//						Fill_Cond_Spline(n_TI_repl, infoin,tools,stats, grid,  stats.m_all_value_replicates,coeff_1D_leg_ti, mt,0,false);
	//					else
	//						Fill_Cond_Legendre(n_TI_repl, infoin,tools,stats, grid,  stats.m_all_value_replicates,coeff_1D_leg_ti, mt,0,false);

	//			}
	//			int n_total_repl=n_TI_repl+n_Data_repl;
	//			if(n_total_repl>0)
	//				for (int w=0;w<coeff_1D_leg.size();w++)
	//					coeff_1D_leg[w]+=(coeff_1D_leg_ti[w]+coeff_1D_leg_repl[w])/n_total_repl;
	//			 
	//		}
	//		

	//	}

	//ILNUR. We calculate coeff for a_n*X^n+a_n-1*X^(n-1)... once and then use it
	//think about sorting indexes for avoiding permutations
	//ofstream outf("coeff.txt");
	if (calc_type<1)
	{
		int leg_cnt=0;
		for(int j=0;j<=order;j++)
		{
			nm = (int) tools.m_stats_map_point[j].size();

			for(int k=0;k<nm;k++)
			{
				mom = m_stats_map_values[j][k];

				//npair = (int) tools.m_stats_map_point[j][k].size();
				npair = (int) tools.m_stats_map_point[j][k].size()/2;//ILNUR.changed structure of stats

				int nneib=(int)tools.m_neighbors_info.m_samples_neighbor.size();
				Int2Vec permuts=GetAllPermutation( tools.m_stats_map_point[j][k],nneib+1);
				max_perm=max(max_perm,(int)permuts.size());
				double poly_sum=0;

				for (int p=0;p<permuts.size();p++)
				{

					//poly = 1.0;
					double coeff = leg_mom[leg_cnt];
					leg_cnt++;
					int orn_0=0;
					for(int l=0;l<nneib+1;l++)
					{

						orn=permuts[p][l];
						//	outf<<	orn<<"\t";
						if(orn>0)
						{

							if (l==0)
							{
								coeff*=sqrt((double) (2*orn+1));
								orn_0=orn;
							}	
							else
							{
								gp = tools.m_neighbors_info.m_samples_neighbor[l-1];
								val = ConvertVal(grid.m_data[gp][infoin.m_dimension],gp);

								val_legandre=ComputeLegendrePoly(val,orn)*  sqrt((2.0*orn+1));
								coeff = coeff * val_legandre;
							}
						}
					}
					coeff=coeff/pow(2.0,n_var*0.5);
					//				outf<<coeff<<endl;
					coeff_1D_leg[orn_0]+=coeff;

				}

			}
		}
	}
	//}
	//outf.close();
	//Converting legendre coeff to canonical form. It is neccessary for integration of pdf
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	QueryPerformanceCounter(&t1);
	//cout<<"----------before cdf \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
	if(nneib==infoin.m_mx_s)
		before+=elapsedTime;
#endif
	if(isspline<1)
		Legendre2Poly(coeff_1D_leg,coeff_1D);
	else
		coeff_1D=coeff_1D_leg;
	int flag_res=1;
	if (rank==0)
	{

		Double1Vec pdff(m_pdf.size());
		double tot_area=0;
		double neg_par=0;
		double abs_par=0;
		for (int ix=0;ix<m_x_Leg_cdf.size();ix++)
		{

			if(m_pdf[ix]!=-999)
			{

				double pval=get_pdf_val(m_x_Leg_cdf[ix],coeff_1D,L_spline_rat);

				if(pval>=0)
					m_pdf[ix]=pval;

				else
					neg_par+=fabs(pval);

				m_pdf[ix]=pval;
				abs_par+=fabs(pval);
				pdff[ix]=pval;
				tot_area+=m_pdf[ix];
			}else
				m_pdf[ix]=0;
		}
		if(tot_area!=0&&neg_par/abs_par<0.4)
		{

			m_cdf[0] = m_pdf[0];

			for(int i=1;i<m_cdf_samples;i++)
				m_cdf[i] =  m_cdf[i-1]+m_pdf[i];
			for(int i=0;i<m_cdf_samples;i++)
				m_cdf[i]/=m_cdf[m_cdf_samples-1];

		}else
			flag_res=0;

	}
	if(calc_type>1)
	{
#ifdef USE_MPI
		//	cout<<"good before barrier end"<<rank<<endl;

		MPI_Barrier(MPI_COMM_WORLD);
		MPI_Bcast(&flag_res, 1, MPI_INT,0,MPI_COMM_WORLD);

		//cout<<"good after end"<<rank<<endl;	
#endif
	}
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	QueryPerformanceCounter(&t1);
	//cout<<"----------after cdf \t"<<nneib<<"\t is\t"<<elapsedTime<<endl;
	if(nneib==infoin.m_mx_s)
		after+=elapsedTime;
#endif	

	//	cout<<"flag_res \t"<<tools.m_cp<<"\t is\t"<<clock()-t_elp<<endl;
//	cout<<"flag_res is "<<flag_res<<endl;
	return flag_res>0;


	//ILNUR. old version, too long
	//	for(int i=0;i<tools.m_cdf_samples;i++)
	//	{
	//		tools.m_pdf[i]=0;
	//		if(tools.m_stats_map_point.size() > 0)
	//		{
	//			for(int j=0;j<=order;j++)
	//			{
	//				nm = (int) tools.m_stats_map_point[j].size();
	//
	//				for(int k=0;k<nm;k++)
	//				{
	//					mom = m_stats_map_values[j][k];
	//
	//					//npair = (int) tools.m_stats_map_point[j][k].size();
	//					npair = (int) tools.m_stats_map_point[j][k].size()/2;//ILNUR.changed structure of stats
	//					
	//					int nneib=tools.m_neighbors_info.m_samples_neighbor.size();
	//					Int2Vec permuts=GetAllPermutation( tools.m_stats_map_point[j][k],nneib+1);
	//					max_perm=max(max_perm,permuts.size());
	//					double poly_sum=0;
	//
	//					for (int p=0;p<permuts.size();p++)
	//					{
	//						poly = 1.0;
	//						for(int l=0;l<nneib+1;l++)
	//						{
	//							orn=permuts[p][l];
	//							if(orn>0)
	//							{
	//								pg  =l;
	//								if (pg==0)
	//									poly=tools.m_table_cond_legendre[i][orn]* sqrt((double) (2*orn+1));
	//								else
	//								{
	//									gp = tools.m_neighbors_info.m_samples_neighbor[pg-1];
	//									val = grid.m_data[gp][infoin.m_dimension];
	//									max = (val>tools.m_neighbors_info.m_local_max) ? val:tools.m_neighbors_info.m_local_max ;
	//									min = (val<tools.m_neighbors_info.m_local_min) ? val:tools.m_neighbors_info.m_local_min ;
	//									val = ((val-min)*2/(max-min)) - 1;
	//									val_legandre=ComputeLegendrePoly(val,orn)*  sqrt((double)(2*orn+1));
	//									poly = poly * val_legandre;
	//								}
	//							}
	//						
	//						}
	//
	//						poly_sum+=poly;
	//					}
	//					tools.m_pdf[i] += mom * poly_sum/pow(2.0,n_var*0.5);
	//						/* lets try to do more genereal
	//						pg  = m_stats_map_point[j][k][l];
	//						orn = 1;
	//
	//						if(pg==0)
	//						{
	//							poly=0;
	//							if(npair==1)
	//							{  
	//								orn = j;
	//								//if(orn<0)//ILNUR.ONLY for stationary processes
	//								//	for (int mm=1;mm<tools.m_neighbors_info.m_samples_neighbor.size();mm++)
	//								//	{
	//								//		gp = tools.m_neighbors_info.m_samples_neighbor[mm];
	//								//		val = grid.m_data[gp][infoin.m_dimension];
	//								//		max = (val>tools.m_neighbors_info.m_local_max) ? val:tools.m_neighbors_info.m_local_max ;
	//								//		min = (val<tools.m_neighbors_info.m_local_min) ? val:tools.m_neighbors_info.m_local_min ;
	//								//		val = ((val-min)*2/(max-min)) - 1;
	//								//		//non-constant step
	//								//		//int nb_ind=binarySearch(tools.m_x_Leg_cdf,val); 
	//								//		double dt=((val-m_min_one_one[0])/step_x_pdf);
	//								//		int nb_ind=(int)dt;
	//								//		nb_ind=min(nb_ind,m_min_one_one.size()-2);
	//								//		dt-=nb_ind;
	//								//		poly = poly + tools.m_table_cond_legendre[nb_ind][orn]*(1-dt)+tools.m_table_cond_legendre[nb_ind+1][orn]*dt;
	//
	//								//	}
	//							}
	//							// poly = tools.m_table_cond_legendre[i][orn];//ILNUR poly+
	//							poly+= tools.m_table_cond_legendre[i][orn];
	//						}
	//						else
	//						{
	//							// remember orn is 1 for all samples around for now.
	//							//gp = tools.m_neighbors_info.m_samples_neighbor[pg-1];
	//							gp = tools.m_neighbors_info.m_samples_neighbor[pg];
	//							val = grid.m_data[gp][infoin.m_dimension];
	//							max = (val>tools.m_neighbors_info.m_local_max) ? val:tools.m_neighbors_info.m_local_max ;
	//							min = (val<tools.m_neighbors_info.m_local_min) ? val:tools.m_neighbors_info.m_local_min ;
	//							val = ((val-min)*2/(max-min)) - 1;
	//							poly = poly * val;
	//						}
	//					}
	//					tools.m_pdf[i] += mom * poly;*/
	//				}  
	//			}
	//		}
	//	}
	//}

	//tools.m_pdf[m_pdf.size()-1] = tools.m_pdf[m_pdf.size()-2]*1.1;
	//
	//double tot_area=0.0;
	//double min_pdf =1.0e+10;
	//double step = 2.0 / (m_cdf_samples-1);
	////ILNUR comment shifting
	//// for(int i=0;i<tools.m_cdf_samples;i++)
	////   if(tools.m_pdf[i] < min_pdf) min_pdf = tools.m_pdf[i];
	//// 
	//// // Shift ?
	//// if(min_pdf < 0)
	////   for(int i=0;i<tools.m_cdf_samples;i++)
	////     tools.m_pdf[i] = (tools.m_pdf[i] - 2*min_pdf); 
	//// 
	////ILNUR instead shifting lets cut zero
	// for(int i=0;i<tools.m_cdf_samples;i++)
	//	 if ( tools.m_pdf[i]<0)
	//		  tools.m_pdf[i]=0;
	//
	////
	// for(int i=0;i<tools.m_cdf_samples;i++)
	//   tot_area += tools.m_pdf[i]*step;
	//
	//
	// 
	// if(tot_area!=0)
	// {
	//   for(int i=0;i<tools.m_cdf_samples;i++)
	//     tools.m_pdf[i] = tools.m_pdf[i]*step / tot_area;
	//   
	//   tools.m_cdf[0] = tools.m_pdf[0];
	//
	//   for(int i=1;i<tools.m_cdf_samples;i++)
	//     tools.m_cdf[i] = tools.m_pdf[i] + tools.m_cdf[i-1];
	//}   

}
void Tools::get_Statistics(vector<Replicate> &data_repl,int nneib)
{
	Double1Vec mean,mean_cnt;
	Double2Vec Cov,Cov_cnt;
	mean_cnt.resize(nneib+1);
	mean.resize(nneib+1);
	Cov.resize(nneib+1);
	Cov_cnt.resize(nneib+1);
	for(int j=0;j<nneib+1;j++)
	{
		mean_cnt[j]=0;
		Cov[j].resize(nneib+1);
		Cov_cnt[j].resize(nneib+1);
	}
	for (int nt=0;nt<data_repl.size();nt++)
	{
		for(int j=0;j<nneib+1;j++)
		{
			if(data_repl[nt].m_value_replicate[j]!=-999)
			{

				mean[j]+=data_repl[nt].m_value_replicate[j];
				mean_cnt[j]++;
				for(int k=0;k<nneib+1;k++)
				{
					if(data_repl[nt].m_value_replicate[k]!=-999)
					{
						Cov[j][k]+=data_repl[nt].m_value_replicate[k]*data_repl[nt].m_value_replicate[j];
						Cov_cnt[j][k]++;
					}
				}

			}
		}
	}
	for(int j=0;j<nneib+1;j++)
	{	
		if(mean_cnt[j]>0)
			mean[j]/=mean_cnt[j];
		for(int k=0;k<nneib+1;k++)
		{
			if(Cov_cnt[j][k]>0)
				Cov[j][k]/=Cov_cnt[j][k];
		}
	}


}
void Tools::Fill_Cond_Legendre(int end_ind,vector<double>& neib_replicate,  Replicates& replicates,Double1Vec& coeff_1D_leg, vector<bool>& n_neib_max,int ind_repl0,int order)
{

	int nneib=(int)n_neib_max.size();

	for(int nt=0;nt<end_ind;nt++)
	{
		//	cc_arr[nt].resize(n_splines);
		double cc=1;	
		int repl_base=(ind_repl0+nt)*replicates.m_shift;
		for(int l=0;l<nneib;l++)
		{
			if(!n_neib_max[l])
			{
				cc*=(2*0+1)*0.5;
				continue;
			}
			double mult=0;
			int pg  =l+1;
			double val0 = neib_replicate [pg];




			double	val =replicates.m_values[repl_base+pg];//already converted;

			int w0=0;
			/*	if(!flag&&l==n_neib_max-1)
			w0=1;*/
			Double1Vec val_vec=ComputeLegendrePolyFull(val,order);
			Double1Vec val_vec0=ComputeLegendrePolyFull(val0,order);
			for (int w=w0;w<order+1;w++)
			{

				double val_spline=val_vec[w];
				double val_spline0=val_vec0[w];
				mult+=val_spline0*val_spline*(2.0*w+1)*0.5;

			}

			cc*=mult;
		}

		/*if(!flag)
		for(int l=n_neib_max;l<nneib;l++)
		{
		cc*=Lspline.splines[0].GetVal(0);
		}*/
		int pg  =0;
		double val = replicates.m_values[repl_base+pg];;//already converted;

		Double1Vec val_vec=ComputeLegendrePolyFull(val,order);
		for (int w=0;w<order+1;w++)
		{
			double val_spline=val_vec[w];


			coeff_1D_leg[w]+=val_spline*cc*sqrt( (2.0*w+1)*0.5);
			//	cc_arr[nt][w]=coeff_1D_leg[w];
		}

	}


	return ;

}
void Tools::Fill_Cond_LegendreOLD(int end_ind,Replicate& neib_replicate,  vector<Replicate>& replicate,Double1Vec& coeff_1D_leg, vector<bool>& n_neib_max,int ind_repl0,int order)
{
	/*
	int nneib=n_neib_max.size();

	for(int nt=0;nt<end_ind;nt++)
	{
	//	cc_arr[nt].resize(n_splines);
	double cc=1;	

	for(int l=0;l<nneib;l++)
	{
	if(!n_neib_max[l])
	{
	cc*=(2*0+1)*0.5;
	continue;
	}
	double mult=0;
	int pg  =l+1;
	double val0 = neib_replicate.m_value_replicate [pg];




	double	val =replicate[ind_repl0+nt].m_value_replicate[pg];//already converted;

	int w0=0;

	Double1Vec val_vec=ComputeLegendrePolyFull(val,order);
	Double1Vec val_vec0=ComputeLegendrePolyFull(val0,order);
	for (int w=w0;w<order+1;w++)
	{

	double val_spline=val_vec[w];
	double val_spline0=val_vec0[w];
	mult+=val_spline0*val_spline*(2.0*w+1)*0.5;

	}

	cc*=mult;
	}


	int pg  =0;
	double val = replicate[ind_repl0+nt].m_value_replicate[pg];//already converted;

	Double1Vec val_vec=ComputeLegendrePolyFull(val,order);
	for (int w=0;w<order+1;w++)
	{
	double val_spline=val_vec[w];


	coeff_1D_leg[w]+=val_spline*cc*sqrt( (2.0*w+1)*0.5);
	//	cc_arr[nt][w]=coeff_1D_leg[w];
	}

	}

	*/
	return ;

}
void Tools::GetSplines(int order,int m_div,SplineContainer &Lspline_cur)
{
	Double1Vec dummy;
	Lspline_cur.Create(order,m_div,dummy,true);
	/* ofstream fout("splines.txt");
	for (int i=0;i<Lspline.n_spln;i++)
	{
	for (int j=0;j<Lspline.splines[i].breaks.size();j++)
	{
	fout<<Lspline.splines[i].breaks[j]<<endl;

	}
	for (int j=0;j<Lspline.splines[i].coefs.size();j++)
	{
	for (int k=0;k<Lspline.splines[i].coefs[j].size();k++)
	fout<<Lspline.splines[i].coefs[j][k]<<endl;

	}
	for (int j=0;j<Lspline.splines[i].coefs.size();j++)
	{
	for (int k=0;k<Lspline.splines[i].coefs_integral[j].size();k++)
	fout<<Lspline.splines[i].coefs_integral[j][k]<<endl;

	}
	}
	fout.close();*/
}
void Tools::Calc_Cond_Coeff(Replicates& Repl_TI,vector<double>& replicate, vector<int> neib_ind_def, Double1Vec& cond_coef, vector<SplineContainer>& Lspline_all)
{

	for(int nt=0;nt<Repl_TI.m_size;nt++)
	{
		int repl_base=nt*Repl_TI.m_shift;
		cond_coef[nt]=1;
		for(int l_def=0;l_def<neib_ind_def.size();l_def++)
		{
			int l=neib_ind_def[l_def];
			double mult=0;
			//all converted;
			double val0 =replicate[l];
			double val=Repl_TI.m_values[repl_base+l];
			for (int w=0;w<Lspline_all[l].splines.size();w++)
			{

				double val_spline=Lspline_all[l].splines[w].GetVal(val);
				double val_spline0=Lspline_all[l].splines[w].GetVal(val0);

				mult+=val_spline0*val_spline;

			}

			cond_coef[nt]*=mult;
		}
	}
}
void Tools::Calc_Cond_CoeffOLD(vector<Replicate>& Repl_TI,Replicate& replicate, vector<int> neib_ind_def, Double1Vec& cond_coef, vector<SplineContainer>& Lspline_all)
{
	/*
	for(int nt=0;nt<Repl_TI.size();nt++)
	{
	cond_coef[nt]=1;
	for(int l_def=0;l_def<neib_ind_def.size();l_def++)
	{
	int l=neib_ind_def[l_def];
	double mult=0;
	//all converted;
	double val0 =replicate.m_value_replicate[l];
	double val=Repl_TI[nt].m_value_replicate[l];
	for (int w=0;w<Lspline_all[l].splines.size();w++)
	{

	double val_spline=Lspline_all[l].splines[w].GetVal(val);
	double val_spline0=Lspline_all[l].splines[w].GetVal(val0);

	mult+=val_spline0*val_spline;

	}

	cond_coef[nt]*=mult;
	}
	}*/
}
bool Tools::Calc_Cond_Pdf(Replicates& replicates,int repl_base, vector<int>& neib_ind_def,vector<int>& neib_ind_undef, Tools& dat_tools, InfoIn& infoin,Grid& grid,TrainingImage& ti, Samples& samples,Stats& stats)
{

	clock_t t;
	/*int ord=Lspline_cur.order;
	Double1Vec dummy;
	Double1Vec pdf(x_Leg.size());
	Double1Vec cdf(x_Leg.size());*/
	//int m_div=Lspline_cur.splines[Lspline_cur.splines.size()-1].breaks.size();
	//	double thresh=2.0/(m_div-1);
	for(int l_def=0;l_def<neib_ind_def.size();l_def++)
	{
		int l=neib_ind_def[l_def];

		int def_ind=replicates.m_indexes[repl_base+l];


		if(dat_tools.m_sample_indicator_curr[def_ind]==0)
		{
			dat_tools.m_sample_indicator_curr[def_ind]=(int)dat_tools.m_sample_indicator_curr.size();
			dat_tools.tool_grid.m_data[def_ind][infoin.m_dimension]=BackConvertVal( replicates.m_values[repl_base+l],def_ind);
		}

	}
	int ind0=replicates.m_indexes[repl_base+0];
	int data_ind=dat_tools.m_sample_indicator_orig[ind0]-1;

	int cnt_test=0;
	Int1Vec coords(3);
	for (int k=0;k<Repl_Dump.values[data_ind].size();k++)
	{

		if(Repl_Dump.values[data_ind][k]!=-999)
		{

			Repl_Dump.Get_XYZ_from_Dump_ind(k, coords);
			int xr=(int)(dat_tools.tool_grid.m_data[ind0][0]-coords[0]);
			int yr= (int)(dat_tools.tool_grid.m_data[ind0][1]-coords[1]);
			int zr= (int)(dat_tools.tool_grid.m_data[ind0][2]-coords[2]);

			if(xr>=0&&xr<infoin.m_grid_info.m_nx&&yr>=0&&yr<infoin.m_grid_info.m_ny)
			{
				int gp=xr+yr*infoin.m_grid_info.m_nx;
				if(infoin.m_dimension==3)
				{
					if(zr<0||zr>infoin.m_grid_info.m_nz)
						continue;
					gp+=zr*infoin.m_grid_info.m_nx*infoin.m_grid_info.m_ny;

				}
				if(dat_tools.m_sample_indicator_curr[gp]==0)
				{
					dat_tools.m_sample_indicator_curr[gp]= (int)dat_tools.m_sample_indicator_curr.size();
					dat_tools.tool_grid.m_data[gp][infoin.m_dimension]=Repl_Dump.values[data_ind][k];
				}else
				{
					cnt_test++;
				}
			}


		}
	}

	for(int l_undef=0;l_undef<neib_ind_undef.size();l_undef++)
	{
		int l=neib_ind_undef[l_undef];

		/*THE TOTAL RUN TIME*/

		t=clock();

		/*	if(!flag&&l==n_neib_max-1)
		w0=1;*/
		//SplineContainer L_spline_rat;//=Get_Knots(replicate, repl_TI, neib_ind_def, l, thresh,m_div,Lspline_cur.order);
		//double var_cur=kriging_var(Cov,var_b);
		//int m_div_cur=2/sqrt(var_cur/2)+1;
		//L_spline_rat.Create(ord,m_div_cur,dummy);
		////	cout<<"SplineContainer:\t"<<clock()-t<<endl;
		//t=clock();
		//if(L_spline_rat.splines.size()==0)
		//{
		//	L_spline_rat=Lspline_cur;
		//}
		//Double1Vec coeff_1D_leg(L_spline_rat.splines.size());
		//fill(coeff_1D_leg.begin(),coeff_1D_leg.end(),0);
		//for(int nt=0;nt<repl_TI.size();nt++)
		//{
		//	for (int w=0;w<L_spline_rat.splines.size();w++)
		//	{

		//		coeff_1D_leg[w]+=L_spline_rat.splines[w].GetVal(repl_TI[nt].m_value_replicate[l])*cond_coeff[nt];// all converted

		//		//	cc_arr[nt][w]=coeff_1D_leg[w];
		//	}
		//}
		//for (int w=0;w<coeff_1D_leg.size();w++)
		//	coeff_1D_leg[w]/=repl_TI.size();
		//double abs_par=0;
		//double neg_par=0;
		//double tot_area=0;
		//for (int ix=0;ix<x_Leg.size();ix++)
		//{
		//	pdf[ix]=0;
		//	double pval=get_pdf_val(x_Leg[ix],coeff_1D_leg,L_spline_rat);

		//	if(pval>=0)
		//		pdf[ix]=pval;
		//	else
		//		neg_par+=fabs(pval);
		//	tot_area+=pdf[ix];
		//	abs_par+=fabs(pval);
		//}
		//if(tot_area!=0)
		//{

		//	cdf[0]=pdf[0];

		//	for(int i=0;i<x_Leg.size()-1;i++)
		//		cdf[i+1] =  cdf[i]+pdf[i];
		//	tot_area=cdf[x_Leg.size()-1];
		//	for(int i=0;i<x_Leg.size();i++)
		//		cdf[i] /= tot_area;

		//}else 
		//	return false;
		// if(neg_par/abs_par>0.5)
		//	 return false;
		//double val=draw_value_coeff(coeff_1D_leg,L_spline_rat);
		////double val=draw_value_cdf(cdf,x_Leg);
		//replicate.m_value_replicate[l]=val;

		//if(l_undef<neib_ind_undef.size()-1)
		//{
		//	SplineContainer Lspline_next;
		//	double var_cur=variogram_test();
		//	int m_div_cur=2/sqrt(var_cur/2)+1;
		//	L_spline_rat.Create(ord,m_div_cur,dummy);
		//	for(int nt=0;nt<repl_TI.size();nt++)
		//	{
		//		double mult=0;

		//		for (int w=0;w<n_splines;w++)
		//		{

		//			double val_spline=Lspline_cur.splines[w].GetVal(repl_TI[nt].m_value_replicate[l]);
		//			double val_spline0=Lspline_cur.splines[w].GetVal(val);

		//			mult+=val_spline0*val_spline;

		//		}

		//		cond_coeff[nt]*=mult;
		//	}

		//}
		if(replicates.m_indexes[repl_base+l]==-999)
		{
			//	cout<<"Calc_cond_pdf:\t"<<clock()-t<<endl;
			break;

		}
		dat_tools.m_cp=replicates.m_indexes[repl_base+l];

		double val=HosDataManager::SimulatePoint(infoin, dat_tools.tool_grid, ti, samples,dat_tools,stats, 1,dat_tools);
		//		cout<<"Calc_cond_pdf:\t"<<clock()-t<<endl;
		if(val==gNullvalue||val==-199||val==-1099)
			break; 
		replicates.m_values[repl_base+l]=ConvertVal(val,dat_tools.m_cp);

		int Dump_ind=Repl_Dump.Get_Dump_ind( m_ijk_ref_replicate[l-1]);

		dat_tools.tool_grid.m_data[dat_tools.m_cp][infoin.m_dimension]=val;
		dat_tools.m_sample_indicator_curr[dat_tools.m_cp]= (int)dat_tools.m_sample_indicator_curr.size()+1;

		//neib_ind_def.push_back(l);
		//	 cout<<"rest:\t"<<clock()-t<<endl;

	}
	for(int l_def=0;l_def<neib_ind_def.size();l_def++)
	{
		int l=neib_ind_def[l_def];

		int def_ind=replicates.m_indexes[repl_base+l];
		if(dat_tools.m_sample_indicator_orig[def_ind]==0)
		{
			dat_tools.tool_grid.m_data[def_ind][infoin.m_dimension]=0;
			dat_tools.m_sample_indicator_curr[def_ind]=0;
		}
	}
	for (int k=0;k<Repl_Dump.values[data_ind].size();k++)
	{

		if(Repl_Dump.values[data_ind][k]!=-999)
		{
			int xr= (int)(dat_tools.tool_grid.m_data[ind0][0]-coords[0]);
			int yr=(int)( dat_tools.tool_grid.m_data[ind0][1]-coords[1]);
			int zr= (int)(dat_tools.tool_grid.m_data[ind0][2]-coords[2]);

			if(xr>=0&&xr<infoin.m_grid_info.m_nx&&yr>=0&&yr<infoin.m_grid_info.m_ny)
			{
				int gp=xr+yr*infoin.m_grid_info.m_nx;
				if(infoin.m_dimension==3)
				{
					if(zr<0||zr>infoin.m_grid_info.m_nz)
						continue;
					gp+=zr*infoin.m_grid_info.m_nx*infoin.m_grid_info.m_ny;

				}

				if(dat_tools.m_sample_indicator_orig[gp]==0)
				{
					dat_tools.m_sample_indicator_curr[gp]=0;
					dat_tools.tool_grid.m_data[gp][infoin.m_dimension]=0;
				}
			}


		}
	}
	for(int l_undef=0;l_undef<neib_ind_undef.size();l_undef++)
	{
		int l=neib_ind_undef[l_undef];
		if(replicates.m_indexes[repl_base+l]==-999)
			return false;
		dat_tools.m_cp=replicates.m_indexes[repl_base+ l];
		if (replicates.m_values[repl_base+l]==gNullvalue)
			return false;
		dat_tools.tool_grid.m_data[dat_tools.m_cp][infoin.m_dimension]=0;
		dat_tools.m_sample_indicator_curr[dat_tools.m_cp]=0;
	}
	return true;
} 
bool Tools::Calc_Cond_PdfOLD(Replicate& replicate, vector<int>& neib_ind_def,vector<int>& neib_ind_undef, Tools& dat_tools, InfoIn& infoin,Grid& grid,TrainingImage& ti, Samples& samples,Stats& stats)
{
	/*
	clock_t t;

	//int m_div=Lspline_cur.splines[Lspline_cur.splines.size()-1].breaks.size();
	//	double thresh=2.0/(m_div-1);
	for(int l_def=0;l_def<neib_ind_def.size();l_def++)
	{
	int l=neib_ind_def[l_def];

	int def_ind=replicate.m_index[l];


	if(dat_tools.m_sample_indicator_curr[def_ind]==0)
	{
	dat_tools.m_sample_indicator_curr[def_ind]=dat_tools.m_sample_indicator_curr.size();
	dat_tools.tool_grid.m_data[def_ind][infoin.m_dimension]=BackConvertVal( replicate.m_value_replicate[l]);
	}

	}
	int ind0=replicate.m_index[0];
	int data_ind=dat_tools.m_sample_indicator_orig[ind0]-1;
	int n_siz=(sqrt(Repl_Dump[data_ind].size()*1.0)-1)/2;
	int cnt_test=0;
	for (int k=0;k<Repl_Dump[data_ind].size();k++)
	{

	if(Repl_Dump[data_ind][k]!=-999)
	{
	int xr=dat_tools.tool_grid.m_data[ind0][0]-(k%(2*n_siz+1)-n_siz);
	int yr=dat_tools.tool_grid.m_data[ind0][1]-(k/(2*n_siz+1)-n_siz);
	if(xr>=0&&xr<infoin.m_grid_info.m_nx&&yr>=0&&yr<infoin.m_grid_info.m_ny)
	{
	int gp=xr+yr*infoin.m_grid_info.m_nx;

	if(dat_tools.m_sample_indicator_curr[gp]==0)
	{
	dat_tools.m_sample_indicator_curr[gp]=dat_tools.m_sample_indicator_curr.size();
	dat_tools.tool_grid.m_data[gp][infoin.m_dimension]=Repl_Dump[data_ind][k];
	}else
	{
	cnt_test++;
	}
	}


	}
	}

	for(int l_undef=0;l_undef<neib_ind_undef.size();l_undef++)
	{
	int l=neib_ind_undef[l_undef];


	t=clock();


	//SplineContainer L_spline_rat;//=Get_Knots(replicate, repl_TI, neib_ind_def, l, thresh,m_div,Lspline_cur.order);
	//double var_cur=kriging_var(Cov,var_b);
	//int m_div_cur=2/sqrt(var_cur/2)+1;
	//L_spline_rat.Create(ord,m_div_cur,dummy);
	////	cout<<"SplineContainer:\t"<<clock()-t<<endl;
	//t=clock();
	//if(L_spline_rat.splines.size()==0)
	//{
	//	L_spline_rat=Lspline_cur;
	//}
	//Double1Vec coeff_1D_leg(L_spline_rat.splines.size());
	//fill(coeff_1D_leg.begin(),coeff_1D_leg.end(),0);
	//for(int nt=0;nt<repl_TI.size();nt++)
	//{
	//	for (int w=0;w<L_spline_rat.splines.size();w++)
	//	{

	//		coeff_1D_leg[w]+=L_spline_rat.splines[w].GetVal(repl_TI[nt].m_value_replicate[l])*cond_coeff[nt];// all converted

	//		//	cc_arr[nt][w]=coeff_1D_leg[w];
	//	}
	//}
	//for (int w=0;w<coeff_1D_leg.size();w++)
	//	coeff_1D_leg[w]/=repl_TI.size();
	//double abs_par=0;
	//double neg_par=0;
	//double tot_area=0;
	//for (int ix=0;ix<x_Leg.size();ix++)
	//{
	//	pdf[ix]=0;
	//	double pval=get_pdf_val(x_Leg[ix],coeff_1D_leg,L_spline_rat);

	//	if(pval>=0)
	//		pdf[ix]=pval;
	//	else
	//		neg_par+=fabs(pval);
	//	tot_area+=pdf[ix];
	//	abs_par+=fabs(pval);
	//}
	//if(tot_area!=0)
	//{

	//	cdf[0]=pdf[0];

	//	for(int i=0;i<x_Leg.size()-1;i++)
	//		cdf[i+1] =  cdf[i]+pdf[i];
	//	tot_area=cdf[x_Leg.size()-1];
	//	for(int i=0;i<x_Leg.size();i++)
	//		cdf[i] /= tot_area;

	//}else 
	//	return false;
	// if(neg_par/abs_par>0.5)
	//	 return false;
	//double val=draw_value_coeff(coeff_1D_leg,L_spline_rat);
	////double val=draw_value_cdf(cdf,x_Leg);
	//replicate.m_value_replicate[l]=val;

	//if(l_undef<neib_ind_undef.size()-1)
	//{
	//	SplineContainer Lspline_next;
	//	double var_cur=variogram_test();
	//	int m_div_cur=2/sqrt(var_cur/2)+1;
	//	L_spline_rat.Create(ord,m_div_cur,dummy);
	//	for(int nt=0;nt<repl_TI.size();nt++)
	//	{
	//		double mult=0;

	//		for (int w=0;w<n_splines;w++)
	//		{

	//			double val_spline=Lspline_cur.splines[w].GetVal(repl_TI[nt].m_value_replicate[l]);
	//			double val_spline0=Lspline_cur.splines[w].GetVal(val);

	//			mult+=val_spline0*val_spline;

	//		}

	//		cond_coeff[nt]*=mult;
	//	}

	//}
	if(replicate.m_index[l]==-999)
	{
	//	cout<<"Calc_cond_pdf:\t"<<clock()-t<<endl;
	break;

	}
	dat_tools.m_cp=replicate.m_index[l];

	double val=HosDataManager::SimulatePoint(infoin, dat_tools.tool_grid, ti, samples,dat_tools,stats, 1,dat_tools);
	//		cout<<"Calc_cond_pdf:\t"<<clock()-t<<endl;
	if(val==gNullvalue||val==-1||val==-10)
	break; 
	replicate.m_value_replicate[l]=ConvertVal(val);

	int Dump_ind=Get_Dump_ind( m_ijk_ref_replicate[l-1][0], m_ijk_ref_replicate[l-1][1],infoin.m_dimension);

	dat_tools.tool_grid.m_data[dat_tools.m_cp][infoin.m_dimension]=val;
	dat_tools.m_sample_indicator_curr[dat_tools.m_cp]=dat_tools.m_sample_indicator_curr.size()+1;

	//neib_ind_def.push_back(l);
	//	 cout<<"rest:\t"<<clock()-t<<endl;

	}
	for(int l_def=0;l_def<neib_ind_def.size();l_def++)
	{
	int l=neib_ind_def[l_def];

	int def_ind=replicate.m_index[l];
	if(dat_tools.m_sample_indicator_orig[def_ind]==0)
	{
	dat_tools.tool_grid.m_data[def_ind][infoin.m_dimension]=0;
	dat_tools.m_sample_indicator_curr[def_ind]=0;
	}
	}
	for (int k=0;k<Repl_Dump[data_ind].size();k++)
	{

	if(Repl_Dump[data_ind][k]!=-999)
	{
	int xr=dat_tools.tool_grid.m_data[ind0][0]-(k%(2*n_siz+1)-n_siz);
	int yr=dat_tools.tool_grid.m_data[ind0][1]-(k/(2*n_siz+1)-n_siz);
	if(xr>=0&&xr<infoin.m_grid_info.m_nx&&yr>=0&&yr<infoin.m_grid_info.m_ny)
	{
	int gp=xr+yr*infoin.m_grid_info.m_nx;

	if(dat_tools.m_sample_indicator_orig[gp]==0)
	{
	dat_tools.m_sample_indicator_curr[gp]=0;
	dat_tools.tool_grid.m_data[gp][infoin.m_dimension]=0;
	}
	}


	}
	}
	for(int l_undef=0;l_undef<neib_ind_undef.size();l_undef++)
	{
	int l=neib_ind_undef[l_undef];
	if(replicate.m_index[l]==-999)
	return false;
	dat_tools.m_cp=replicate.m_index[l];
	if (replicate.m_value_replicate[l]==gNullvalue)
	return false;
	dat_tools.tool_grid.m_data[dat_tools.m_cp][infoin.m_dimension]=0;
	dat_tools.m_sample_indicator_curr[dat_tools.m_cp]=0;
	}

	*/
	return true;
} 
Double1Vec Tools::Get_Filtred_Vals(vector<double>& replicate,Replicates& repl_TI,vector<int>& neib_ind_def,int ind_undef,double thresh,int & max_cnt)
{

	Double1Vec filtred_vals;
	filtred_vals.reserve(1000);
	max_cnt=0;
	Int1Vec counter(neib_ind_def.size()+1,0);

	for (int nt=0;nt<repl_TI.m_size;nt++)
	{
		bool flag=true;
		double drr=0;
		int good_Cnt=0;
		int repl_base=repl_TI.m_shift*nt;
		for (int l=0;l<neib_ind_def.size();l++)
		{
			int ln=neib_ind_def[l];
			double	val_TI =repl_TI.m_values[repl_base+ln];
			double val_neib = 0;
			if (val_TI != -999)
			{
				val_neib = replicate[ln];
				drr += (val_TI - val_neib)*(val_TI - val_neib);
			}

			if(abs(val_TI-val_neib)>thresh)
			{
				flag=false;	
				break;
			}else
			{
				good_Cnt++;
			}
		}
		counter[good_Cnt]++;
		max_cnt=max(good_Cnt,max_cnt);
		
		
	}

	for(int i=(int)counter.size()-1;i>=1;i--)
	{
		if(counter[i]>=n_min_good_repl)
		{
			max_cnt=i;
			break;
		}
		counter[i-1]+=counter[i];
	}
	for (int nt=0;nt<repl_TI.m_size;nt++)
	{
		bool flag=true;
		double drr=0;
		int good_Cnt=0;
		int repl_base=repl_TI.m_shift*nt;
		for (int l=0;l<max_cnt;l++)
		{
			int ln=neib_ind_def[l];
			double	val_TI =repl_TI.m_values[repl_base+ln];
			double val_neib = 0;
			if (val_TI != -999)
			{
				val_neib = replicate[ln];
				drr += (val_TI - val_neib)*(val_TI - val_neib);
			}
			if(abs(val_TI-val_neib)>thresh)
			{
				flag=false;	
				break;
			}else
			{
				good_Cnt++;
			}
		}
		
		
		if(flag)
		{
			filtred_vals.push_back(repl_TI.m_values[repl_base+ind_undef]);

		}
	}
	//cout<<filtred_vals.size();
	bool need_filt=0;
	if(need_filt)
	{
		int nt0=0;
		Replicates newRepl=repl_TI;
		for (int nt=0;nt<repl_TI.m_size;nt++)
		{
			bool flag=true;
			double drr=0;
			int good_Cnt=0;
			int repl_base=repl_TI.m_shift*nt;
			for (int l=0;l<neib_ind_def.size();l++)
			{
				int ln=neib_ind_def[l];
				double	val_TI =repl_TI.m_values[repl_base+ln];
				double val_neib=replicate[ln];
				drr+=(val_TI-val_neib)*(val_TI-val_neib);
				if(abs(val_TI-val_neib)>thresh)
				{
					flag=false;	
					break;
				}else
				{
					good_Cnt++;
				}
			}
		
			if(good_Cnt>=max_cnt)
			{
			
				int repl_base0=newRepl.m_shift*nt0;
				for (int j=0;j<newRepl.m_neib_num;j++)
				{
					newRepl.m_indexes[repl_base0+j]=repl_TI.m_indexes[repl_base+j];
					newRepl.m_values[repl_base0+j]=repl_TI.m_values[repl_base+j];
				}
				nt0++;
			}
		}
		newRepl.m_size=nt0;
		repl_TI=newRepl;
	}
	//cout<<"nt0="<<nt0<<endl;
//	cout<<"max_neib="<<max_cnt<<endl;
	if(test_flag)
	{
		std::stringstream ss;
		m_hardpoints.resize(1);
		int i=m_hardpoints[0];
		m_hardpoints[0]++;
		ss<<"filte_val_"<<i<<".txt";
		std::string sname;
		ss>>sname;
		if(filtred_vals.size()>0)
		{
			sort(filtred_vals.begin(),filtred_vals.end());
			double prev=filtred_vals[0];
			for (int i=0;i<filtred_vals.size();i++)
			{
				if(fabs(prev-filtred_vals[i])>0.5)
					test_good=true;
				prev=filtred_vals[i];
			}
			if (test_good)
			{	
				ofstream fout(sname.c_str());
				for (int i=0;i<filtred_vals.size();i++)
				{

					fout<<filtred_vals[i]<<endl;
				}
				fout.close();
			}
		}
	}
	return filtred_vals;
}
Double1Vec Tools::Get_Filtred_ValsOLD(Replicate& replicate,vector<Replicate>& repl_TI,vector<int>& neib_ind_def,int ind_undef,double thresh,int & max_cnt)
{

	Double1Vec filtred_vals;
	/*filtred_vals.reserve(1000);
	max_cnt=0;
	for (int nt=0;nt<repl_TI.size();nt++)
	{
	bool flag=true;
	double drr=0;
	int good_Cnt=0;
	for (int l=0;l<neib_ind_def.size();l++)
	{
	int ln=neib_ind_def[l];
	double	val_TI =repl_TI[nt].m_value_replicate[ln];
	double val_neib=replicate.m_value_replicate[ln];
	drr+=(val_TI-val_neib)*(val_TI-val_neib);
	if(abs(val_TI-val_neib)>thresh)
	{
	flag=false;	

	}else
	{
	good_Cnt++;
	}
	}
	max_cnt=max(good_Cnt,max_cnt);
	drr=sqrt(drr);
	flag=(drr<thresh);
	if(flag)
	{
	filtred_vals.push_back(repl_TI[nt].m_value_replicate[ind_undef]);
	}
	}

	if(test_flag)
	{
	std::stringstream ss;
	m_hardpoints.resize(1);
	int i=m_hardpoints[0];
	m_hardpoints[0]++;
	ss<<"filte_val_"<<i<<".txt";
	std::string sname;
	ss>>sname;
	if(filtred_vals.size()>0)
	{
	sort(filtred_vals.begin(),filtred_vals.end());
	double prev=filtred_vals[0];
	for (int i=0;i<filtred_vals.size();i++)
	{
	if(fabs(prev-filtred_vals[i])>0.5)
	test_good=true;
	prev=filtred_vals[i];
	}
	if (test_good)
	{	
	ofstream fout(sname);
	for (int i=0;i<filtred_vals.size();i++)
	{

	fout<<filtred_vals[i]<<endl;
	}
	fout.close();
	}
	}
	}*/
	return filtred_vals;

}
SplineContainer Tools::Get_Knots(Double1Vec filtred_vals,double thresh, int m_div,int order_spl)
{


	Double1Vec knots;

	knots.reserve(m_div);

	if(filtred_vals.size()>0)
	{

		double dx_min=thresh/4;
		sort (filtred_vals.begin(), filtred_vals.end()); 


		for (int ix=0;ix<filtred_vals.size();ix++)
		{   
			double xx=filtred_vals[ix]-dx_min;
			if(xx>-1&&xx<1)
				knots.push_back(xx);
			xx=filtred_vals[ix]+dx_min;
			if(xx>-1&&xx<1)
				knots.push_back(xx);
			xx=filtred_vals[ix]-2*dx_min;
			if(xx>-1&&xx<1)
				knots.push_back(xx);
			xx=filtred_vals[ix]+2*dx_min;
			if(xx>-1&&xx<1)
				knots.push_back(xx);

		}
		sort (knots.begin(), knots.end()); 
		double dt_max=thresh;
		int it=0;
		Int1Vec ind_knot;
		vector<bool> good_knots(knots.size(),true);
		ind_knot.reserve(knots.size());
		bool good_zone=false;
		while (it<knots.size())
		{ 
			ind_knot.clear();
			int jt;
			for( jt=it;jt>=0;jt--)
			{
				if(abs(knots[jt]-knots[it])<dt_max)
				{
					ind_knot.push_back(jt);
				}
			}
			reverse(ind_knot.begin(),ind_knot.end());

			for(int jt=it+1;jt<knots.size();jt++)
			{
				if(abs(knots[jt]-knots[it])<dt_max)
				{
					ind_knot.push_back(jt);

				}
			}

			if(ind_knot.size()>4)
			{

				double min_dist=1e+10;
				int min_ind=-1;
				for (jt=1;jt<ind_knot.size()-1;jt++)
				{
					double dist=fabs(knots[ind_knot[jt+1]]-knots[ind_knot[jt]]);
					if(dist<min_dist)
					{
						min_ind=jt;
						min_dist=dist;
					}
				}
				if(min_ind>-1)
				{
					if(min_ind==0)
						min_ind++;
					knots.erase(knots.begin()+ind_knot[min_ind]);

					good_knots.erase(good_knots.begin()+ind_knot[min_ind]);

				}

			}else
			{



				it++;

			}

		}
		good_knots.insert(good_knots.begin(),false);
		good_knots.push_back(false);
		knots.insert(knots.begin(),-1.0);
		knots.push_back(1);
		for(int it=1;it<knots.size()-1;it++)
		{
			if((fabs(knots[it]-knots[it-1])>=dt_max)||(fabs(knots[it+1]-knots[it])>=dt_max))
			{
				good_knots[it]=false;
			}else
				good_knots[it]=true;



		}
		//sort (knots.begin(), knots.end()); 
		/*int n_add=m_div-knots.size();
		for (int it=0;it<n_add;it++)
		{
		double max_dist=-1e+10;
		int max_ind=-1;

		for (int jt=0;jt<knots.size()-1;jt++)
		{
		if(good_knots[jt])
		continue;
		double dist=fabs(knots[jt+1]-knots[jt]);
		if(dist>max_dist)
		{
		max_ind=jt;
		max_dist=dist;

		}
		}
		double val=(knots[max_ind+1]+knots[max_ind])*0.5;
		knots.insert(knots.begin()+max_ind+1,val);
		good_knots.insert(good_knots.begin()+max_ind+1,false);
		}*/

		int nn=(int)knots.size();
		double dx=0.00001;

		for(int jj=0;jj<nn;jj++)
		{
			if(!good_knots[jj])
			{
				if((jj>0&&good_knots[jj-1]))
				{
					for(int pp=0;pp<order_spl-1;pp++)
						knots.push_back(knots[jj]-(pp+1)*dx);

				}
				if((jj+1<nn&&good_knots[jj+1]))
				{
					for(int pp=0;pp<order_spl-1;pp++)
						knots.push_back(knots[jj]+(pp+1)*dx);

				}
			}
		}
		sort(knots.begin(),knots.end());

	}
	SplineContainer spl_cont;
	if(knots.size()>0)
	{
		knots.erase( unique( knots.begin(), knots.end() ), knots.end() );
		spl_cont.Create(order_spl,0,knots,false);

	}
	return spl_cont;

}
void  Tools::Get_Data_Replicate_Spline(Replicates& replicate, int repl_base,vector<int>& neib_ind_def, vector<int>& neib_ind_undef, Tools& dat_tools, InfoIn& infoin,Grid& grid,TrainingImage& ti, Samples& samples,Stats& stats)
{
	/*int n_relp_ti=repl_basis.size();
	Double1Vec cond_coef(n_relp_ti);


	*/
	double cc=1;	

	//	Calc_Cond_Coeff(repl_basis,replicate,neib_ind_def,  cond_coef,Lspline_curr,Lspline_all);

	bool flag=Calc_Cond_Pdf(replicate,repl_base, neib_ind_def,neib_ind_undef, dat_tools,infoin,grid,ti,samples,stats);




	return ;

}
void  Tools::Get_Data_Replicate_SplineOLD(Replicate& replicate, vector<int>& neib_ind_def, vector<int>& neib_ind_undef, Tools& dat_tools, InfoIn& infoin,Grid& grid,TrainingImage& ti, Samples& samples,Stats& stats)
{
	/*
	double cc=1;	

	//	Calc_Cond_Coeff(repl_basis,replicate,neib_ind_def,  cond_coef,Lspline_curr,Lspline_all);

	bool flag=Calc_Cond_PdfOLD(replicate,repl_base, neib_ind_def,neib_ind_undef, dat_tools,infoin,grid,ti,samples,stats);


	*/

	return ;

}
void  Tools::Fill_Cond_Spline(int end_ind,vector<double>& neib_replicate,  Replicates& replicates,Double1Vec& coeff_1D_leg, vector<bool>& n_neib_max,int ind_repl0, SplineContainer*& L_spline_rat,vector<SplineContainer*>& L_spline_all)
{
	int nneib=(int)n_neib_max.size();

	// do something

	

	if(true_calc)	
	{
		//clock_t t_elp=clock();
		for(int nt=0;nt<end_ind;nt++)
		{
			//	cc_arr[nt].resize(n_splines);
			double cc=1;	
			int repl_base=(ind_repl0+nt)*replicates.m_shift;

			double val_centr = replicates.m_values[repl_base+0];//already converted;

			for(int l=0;l<nneib;l++)
			{
				if(!n_neib_max[l])
				{
					cc*=L_spline_all[l]->splines[0].GetVal(0);
					continue;
				}
				double mult=0;
				int pg  =l+1;
				double val0 = neib_replicate[pg];

				double	val =replicates.m_values[repl_base+pg];//already converted;
				assert(val != -999);
				int w0=0;
				/*	if(!flag&&l==n_neib_max-1)
				w0=1;*/
				int ndist= (int)(L_spline_all[l]->Dumpd_vals.size());
				double ddx=2.0/(ndist-1);
				int idist0=(int)floor((val+1)/ddx);
				int jdist0= (int)floor((val0+1)/ddx);
				if(true&&!test_flag)
				{

					if(jdist0<ndist-1)
					{
						double vv0=(jdist0*ddx-1);
						double vv1=((jdist0+1)*ddx-1);
						double t=(val0-vv0)/(vv1-vv0);
						mult=L_spline_all[l]->Dumpd_vals[idist0][jdist0]*(1-t)+t*L_spline_all[l]->Dumpd_vals[idist0][jdist0+1];

					}else
						mult=L_spline_all[l]->Dumpd_vals[idist0][jdist0];
					/*int idist1=min(idist0+1,ndist-1);

					int jdist=0;
					if(val<val0)
					jdist=1;
					else
					jdist=0;
					double dist=max(L_spline_all[l].maxdist[2*idist0+jdist],L_spline_all[l].maxdist[2*idist1+jdist]);

					if(dist<fabs(val-val0))
					{

					mult=(L_spline_all[l].baselevel[idist0]+L_spline_all[l].baselevel[idist1])*0.5;*/

				}else
				{
					for (int w=w0;w<L_spline_all[l]->splines.size();w++)
					{

						double val_spline=L_spline_all[l]->splines[w].GetVal(val);
						double val_spline0=L_spline_all[l]->splines[w].GetVal(val0);

						mult+=val_spline0*val_spline;

					}
				}
				cc*=mult;
			}



			/*if(!flag)
			for(int l=n_neib_max;l<nneib;l++)
			{
			cc*=Lspline.splines[0].GetVal(0);
			}*/


			for (int w=0;w<L_spline_rat->splines.size();w++)
			{
				double val_spline=L_spline_rat->splines[w].GetVal(val_centr);


				coeff_1D_leg[w]+=val_spline*cc;
				//	cc_arr[nt][w]=coeff_1D_leg[w];
			}


		}
	}
	else
	{
		double* cc_arr=new double[end_ind];
		double cc_max=-1e+10;
		coeff_1D_leg[0]=0;
		#if defined(_OPENMP)
		
#pragma omp parallel shared(replicates,neib_replicate,cc_arr,cc_max,end_ind,ind_repl0,L_spline_all,n_neib_max)
		{

		

#pragma omp for schedule(dynamic,32)      
			#endif
			

		for(int nt=0;nt<end_ind;nt++)
		{
			//	cc_arr[nt].resize(n_splines);
			double cc=1;	
			int repl_base=(ind_repl0+nt)*replicates.m_shift;



			for(int l=0;l<nneib;l++)
			{
				if(!n_neib_max[l])
				{
					cc*=L_spline_all[l]->splines[0].GetVal(0);
					continue;
				}
				double mult=0;
				int pg  =l+1;
				double val0 = neib_replicate[pg];

				double	val =replicates.m_values[repl_base+pg];//already converted;

				int w0=0;
				/*	if(!flag&&l==n_neib_max-1)
				w0=1;*/
				int ndist= (int)(L_spline_all[l]->Dumpd_vals.size());
				double ddx=2.0/(ndist-1);
				int idist0= (int)floor((val+1)/ddx);
				int jdist0= (int)floor((val0+1)/ddx);
				if(true&&!test_flag)
				{

					if(jdist0<ndist-1)
					{
						double vv0=(jdist0*ddx-1);
						double vv1=((jdist0+1)*ddx-1);
						double t=(val0-vv0)/(vv1-vv0);
						mult=L_spline_all[l]->Dumpd_vals[idist0][jdist0]*(1-t)+t*L_spline_all[l]->Dumpd_vals[idist0][jdist0+1];

					}else
						mult=L_spline_all[l]->Dumpd_vals[idist0][jdist0];
					/*int idist1=min(idist0+1,ndist-1);

					int jdist=0;
					if(val<val0)
					jdist=1;
					else
					jdist=0;
					double dist=max(L_spline_all[l].maxdist[2*idist0+jdist],L_spline_all[l].maxdist[2*idist1+jdist]);

					if(dist<fabs(val-val0))
					{

					mult=(L_spline_all[l].baselevel[idist0]+L_spline_all[l].baselevel[idist1])*0.5;*/

				}else
				{
					for (int w=w0;w<L_spline_all[l]->splines.size();w++)
					{

						double val_spline=L_spline_all[l]->splines[w].GetVal(val);
						double val_spline0=L_spline_all[l]->splines[w].GetVal(val0);

						mult+=val_spline0*val_spline;

					}
				}
				cc*=mult;
			}
			cc_max=max(cc_max,abs(cc));
			cc_arr[nt]=cc;

			/*if(!flag)
			for(int l=n_neib_max;l<nneib;l++)
			{
			cc*=Lspline.splines[0].GetVal(0);
			}*/



		}
			#if defined(_OPENMP)
				}
		#endif
		double coefff=0.01/end_ind;
		for(int nt=0;nt<end_ind;nt++)
		{
			//	cc_arr[nt].resize(n_splines);

			int repl_base=(ind_repl0+nt)*replicates.m_shift;
			double val_centr = replicates.m_values[repl_base+0];//already converted;
			if(cc_max*coefff<abs(cc_arr[nt]))
				for (int w=0;w<L_spline_rat->splines.size();w++)
				{
					double val_spline=L_spline_rat->splines[w].GetVal(val_centr);


					coeff_1D_leg[w]+=val_spline*cc_arr[nt];
					//	cc_arr[nt][w]=coeff_1D_leg[w];
				}
		}
		delete [] cc_arr;

	}


	return ;

}
void  Tools::Fill_Cond_SplineOLD(int end_ind,Replicate& neib_replicate,  vector<Replicate>& replicate,Double1Vec& coeff_1D_leg, vector<bool>& n_neib_max,int ind_repl0, SplineContainer*& L_spline_rat,vector<SplineContainer*>& L_spline_all)
{
	int nneib= (int)(n_neib_max.size());
	/*
	// do something




	//clock_t t_elp=clock();
	for(int nt=0;nt<end_ind;nt++)
	{
	//	cc_arr[nt].resize(n_splines);
	double cc=1;	

	for(int l=0;l<nneib;l++)
	{
	if(!n_neib_max[l])
	{
	cc*=L_spline_all[l]->splines[0].GetVal(0);
	continue;
	}
	double mult=0;
	int pg  =l+1;
	double val0 = neib_replicate.m_value_replicate[pg];

	double	val =replicate[ind_repl0+nt].m_value_replicate[pg];//already converted;


	int ndist=L_spline_all[l]->Dumpd_vals.size();
	double ddx=2.0/(ndist-1);
	int idist0=floor((val+1)/ddx);
	int jdist0=floor((val0+1)/ddx);
	if(true&&!test_flag)
	{

	if(jdist0<ndist-1)
	{
	double vv0=(jdist0*ddx-1);
	double vv1=((jdist0+1)*ddx-1);
	double t=(val0-vv0)/(vv1-vv0);
	mult=L_spline_all[l]->Dumpd_vals[idist0][jdist0]*(1-t)+t*L_spline_all[l]->Dumpd_vals[idist0][jdist0+1];

	}else
	mult=L_spline_all[l]->Dumpd_vals[idist0][jdist0];


	}else
	{
	for (int w=w0;w<L_spline_all[l]->splines.size();w++)
	{

	double val_spline=L_spline_all[l]->splines[w].GetVal(val);
	double val_spline0=L_spline_all[l]->splines[w].GetVal(val0);

	mult+=val_spline0*val_spline;

	}
	}
	cc*=mult;
	}



	int pg  =0;
	double val = replicate[ind_repl0+nt].m_value_replicate[pg];//already converted;


	for (int w=0;w<L_spline_rat->splines.size();w++)
	{
	double val_spline=L_spline_rat->splines[w].GetVal(val);


	coeff_1D_leg[w]+=val_spline*cc;
	//	cc_arr[nt][w]=coeff_1D_leg[w];
	}

	}
	*/

	return ;

}
void Tools::Legendre2Poly(Double1Vec& coeff_from,Double1Vec& coeff_to)
{

	for(int i=0;i<coeff_from.size();i++)
	{
		coeff_to[i]=0;
		Double1Vec LP=Legendre_coeff(i);
		for(int j=0;j<=i;j++)
		{
			coeff_to[j]+=LP[j]*coeff_from[i]*sqrt(0.5+i);
		}
	}
}
Double1Vec Tools::Legendre_coeff(int n)
{
	Double1Vec LP;
	LP.resize(n+1);
	int i;
	for( i=0; i<=n; i++)
	{
		LP[i] = 0;
	}
	LP[n] = 1;
	for(i=1; i<=n; i++)
	{
		LP[n] = LP[n]*(n+i)*0.5/i;
	}
	for(i=0; i<=n/2-1; i++)
	{
		LP[n-2*(i+1)] = -LP[n-2*i]*(n-2*i)*(n-2*i-1)*0.5/((i+1)*(2*(n-i)-1));
	}
	return LP;
}
double
	Tools::draw_value_cdf(double& rr)
{
	m_rand_nb_pass++;

	int loc_int = m_rand_nb_pass%m_n_unif_dist;

	double low = (double) (std::abs(loc_int - 1) ) / m_n_unif_dist, up = (double) (loc_int) / m_n_unif_dist;
	//int quatr=m_x_Leg_cdf.size()/4;
	low =0;//m_cdf[quatr]; 
	up =1;// m_cdf[3*quatr];

	//double rand_01 = (   (double) rand() / ((double)(RAND_MAX)+(double)(1)) );
	double rand_01=(*drand_val)();
	rand_01 = rand_01 * (up-low) + low;
	rr=rand_01;
	// rand_01=0.5;
	if(rand_01==low || rand_01 < m_cdf[0]) return (m_x_Leg_cdf[0              ]);
	if(rand_01==up)                       return (m_x_Leg_cdf[m_cdf_samples-1]);  

	for(int i=0;i<m_cdf_samples;i++)
	{
		if(rand_01 <= m_cdf[i] )
			return( m_x_Leg_cdf[i] );      
	}
	return (m_x_Leg_cdf[m_cdf_samples-1]);  
}
double Tools::
	draw_value_cdf(Double1Vec& cdf_curr,Double1Vec& x_Leg)
{
	int cdf_samples= (int)(x_Leg.size());
	//double rand_01 = (   (double) rand() / ((double)(RAND_MAX)+(double)(1)) );
	double rand_01=(*drand_val)();

	if(rand_01==0 || rand_01 < cdf_curr[0]) 
		return (x_Leg[0              ]);
	if(rand_01==1)                       return (x_Leg[cdf_samples-1]);  
	double val=0;
	for(int i=0;i<cdf_samples;i++)
	{
		if(rand_01 <=cdf_curr[i] )
		{
			val=x_Leg[i] ;      
			return val;
		}
	}

	return (x_Leg[cdf_samples-1]);  
}
std::string HosDataManager::GetBaseFilename(std::string fName)
{
	size_t pos = fName.rfind(".");
	//if(pos == std::string::npos)  //No extension.
	//    return fName;

	//if(pos == 0)    //. is at the front. Not an extension.
	//    return fName;

	return fName.substr(0, pos);
}
void Tools::WriteCdf(Double1Vec& coeff_1D_cur)
{
	ofstream fout("cdf.txt");
	fout.precision(5); 
	double cdf_1=get_cdf_val(-1,coeff_1D_cur,Lspline_buf);
	double norm=1.0/(get_cdf_val(1,coeff_1D_cur,Lspline_buf)-cdf_1);
	for (int i=0;i<100;i++)	
	{
		double x=-1+2*i/(99.0);
		double val=(get_cdf_val(x,coeff_1D_cur,Lspline_buf)-cdf_1);
		fout<<x<<"\t"<<val<<"\t"<<val*norm<<"\t"<<bisection(i*1.0/(99.0),-1,1,0,1,20,coeff_1D_cur,Lspline_buf)<<endl;
	}
	fout.close();
}
void Tools::WritePdf(std::string name,std::string descrip,Double1Vec& coeff_1D_cur,SplineContainer *&Lspline_cur)
{

	ofstream fout;
	std::string out_name=HosDataManager::GetBaseFilename(name);
	std::string out_coeff(out_name);
	std::string out_check_neighbors(out_name);
	out_name.append("_pdf.txt");
	fout.open(out_name.c_str());
	fout.precision(5); 
	double minp=1e+10;
	double maxp=-1e+10;
	SplineContainer* Lwrite=(Lspline_cur);
	for (int i=0;i<100;i++)
	{
		double x=-1+2*i/(99.0);
		double valp=get_pdf_val(x,coeff_1D_cur,Lwrite);
		minp=min(valp,minp);
		maxp=max(valp,maxp);
	}
	double norm=maxp-minp;
	int Ndiscr=500;
	for (int i=0;i<Ndiscr;i++)
	{
		double x=-1+i*2.0/(Ndiscr-1);
		//fout<<infoin.m_nb_locations_stats<<"\t";
		//fout<<infoin.m_mx_s<<"\t";
		//fout<<infoin.m_order<<"\t";
		fout<<descrip<<"\t";
		if(Lspline_cur->splines.size()>0)
			fout<<x<<"\t"<<get_pdf_val(x,coeff_1D_cur,Lwrite)/norm<<endl;
		else
			fout<<x<<"\t"<<m_pdf[i]<<endl;
	}
	fout.close();

	//delete Lwrite;
}
double
	Tools::draw_value_coeff(Double1Vec& coeff_1D_cur,SplineContainer*& Lspline_cur)
{
	double res=0;
	//	double rand_01 = (   (double) rand() / ((double)(RAND_MAX)+(double)(1)) );
	double rand_01=(*drand_val)();

	//rand_01=0.5;
	//ofstream fout;
	//fout.open("random.txt",std::ios_base::app);
	//fout<<rand_01<<endl;
	//fout.close();
	//Let's try BISECTION method
	res=bisection(rand_01,-1,1,0,1,20,coeff_1D_cur,Lspline_cur);



	return res;  
}
double
	Tools::draw_value_rous()
{
	double res=0;
	//	double rand_01 = (   (double) rand() / ((double)(RAND_MAX)+(double)(1)) );
	double rand_01=(*drand_val)();
	Double1Vec cdf1(m_cdf.size());
	Double1Vec cdf2(m_cdf.size());
	int NN= (int)(m_cdf.size());
	cdf1[0]=m_cdf[0];
	cdf2[NN-1]=m_cdf[NN-1];
	for (int i=1;i<NN;i++)
	{  
		if(m_cdf[i]<cdf1[i-1])
			cdf1[i]=cdf1[i-1];
		else
			cdf1[i]=m_cdf[i];
		int ii=NN-i-1;
		if(m_cdf[ii]>cdf2[ii+1])
			cdf2[ii]=cdf2[ii+1];
		else
			cdf2[ii]=m_cdf[ii];
	}
	for (int i=0;i<NN;i++)
		m_cdf[i]=(cdf1[i]+cdf2[i])*0.5;

	double minv=m_cdf[0];
	double maxv=m_cdf[NN-1];
	double norm=1/(maxv-minv);
	for (int i=0;i<NN;i++)
		m_cdf[i]=(m_cdf[i]-minv)*norm;
	//rand_01=0.5;
	//ofstream fout;
	//fout.open("random.txt",std::ios_base::app);
	//fout<<rand_01<<endl;
	//fout.close();
	//Let's try BISECTION method
	res=draw_value_cdf(rand_01);



	return res;  
}
double Tools::bisection(double fval,double a,double b,double fa, double fb,int NMAX,Double1Vec& coeff_1D_cur,SplineContainer*& Lspline_cur)

{
	//	return 0;
	int N=1;
	double c,fc;
	fa-=fval;
	fb-=fval;
	double cdf_1=get_cdf_val(a,coeff_1D_cur,Lspline_cur);
	double norm=1.0/(get_cdf_val(b,coeff_1D_cur,Lspline_cur)-cdf_1);
	double TOL=(b-a)/1000;
	while (N <= NMAX)// limit iterations to prevent infinite loop
	{
		c = (a + b)*0.5;
		if ((b - a)*0.5 < TOL )
			return (c);
		N++; 
		fc=(get_cdf_val(c,coeff_1D_cur,Lspline_cur)-cdf_1)*norm-fval;
		if (fc*fa>0)
		{
			fa=fc;
			a = c ;
		}
		else 
			b =c ;

	}
	cout<<"imposibru"<<endl;
	return c;
}
double	Tools::get_cdf_val(double x,Double1Vec& coeff_1D_cur,SplineContainer*& Lspline_cur)
{
	double res=0;

	if (Lspline_cur->splines.size()<1)
	{
		double x_pow=x;
		for (int i=0;i<coeff_1D_cur.size();i++)
		{
			res+=coeff_1D_cur[i]*x_pow/(i+1);
			x_pow*=x;
		}
	}else
	{
		for (int i=0;i<coeff_1D_cur.size();i++)
			res+=coeff_1D_cur[i]*Lspline_cur->splines[i].GetVal(x,1);
	}
	return res;
}
double	Tools::get_pdf_val(double x,Double1Vec& coeff_1D_cur,SplineContainer*& Lspline_cur)
{
	double res=0;
	if (Lspline_cur->splines.size()<1)
	{

		double x_pow=1;
		for (int i=0;i<coeff_1D_cur.size();i++)
		{
			res+=coeff_1D_cur[i]*x_pow;
			x_pow*=x;
		}
	}else
		for (int i=0;i<coeff_1D_cur.size();i++)
			res+=coeff_1D_cur[i]*Lspline_cur->splines[i].GetVal(x);
	return res;
}
int	Tools::CalcFullNumberCoeff_NL(int order,int& k,int nsn,std::vector< std::vector<int> >&   vec_map)
{	
	if(nsn>0)
	{
		std::vector<int> sub_vec;
		if (vec_map.size()>k)
			sub_vec.assign(vec_map[k].begin(),vec_map[k].end());

		for (int i=0;i<=order;i++)
		{
			if (i>0)
			{
				vec_map[k]=sub_vec;
				vec_map[k].push_back(nsn-1);
				vec_map[k].push_back(i);
			}
			CalcFullNumberCoeff_NL(i,k,nsn-1,vec_map);

		}
	}
	k++;
	return 0;
}
int
	Tools::CalcFullNumberCoeff(int max_order,int order, int nsn,int& k, std::vector< std::vector<int> >&   vec_map)
{

	int res=0;
	if (order==0)
	{
		k++;
		return res;
	}
	if (nsn==0)
	{
		return 0;
	}
	int new_nsn=nsn-1;
	std::vector<int> sub_vec;
	if (vec_map.size()>k)
		sub_vec.assign(vec_map[k].begin(),vec_map[k].end());
	for (int i=order;i>0;i--)
	{
		int new_max_order=max_order-i;
		int new_order=min(new_max_order,i);
		if (vec_map.size()>k) 
		{
			if(vec_map[k].size()<1)
				vec_map[k].reserve(2*nsn);
			if (i>0)
			{
				vec_map[k]=sub_vec;
				vec_map[k].push_back(new_nsn);
				vec_map[k].push_back(i);
			}
		}
		res++;

		int N_elem=CalcFullNumberCoeff(new_max_order,new_order,new_nsn,k,vec_map);

	}

	return res;
}
void
	Tools::FindStatsMap(InfoIn& infoin)
{
	int order = infoin.m_order;
	int nsn  = m_neighbors_info.m_n_local_samples;
	//ILNUR. get all cumulants

	m_stats_map_point.clear();
	m_stats_map_point.resize(order+1);
	m_stats_map_point[0].resize(1);
	m_stats_map_point[0][0].push_back(0);

	//
	//for (int i=1;i<=order;i++)
	//{
	//	m_stats_map_point[i].resize(int(pow(i+1.0,nsn)));
	//	int k=0;
	//	if (i>0)
	//			{
	//				m_stats_map_point[i][k].push_back(nsn);
	//				m_stats_map_point[i][k].push_back(i);
	//			}
	//	CalcFullNumberCoeff_NL(i,k,nsn,m_stats_map_point[i]);

	//}
	//m_stats_map_values.clear();

	//m_stats_map_values.resize(m_stats_map_point.size());

	//for(size_t i=0;i<m_stats_map_values.size();i++)
	//	m_stats_map_values[i].resize(m_stats_map_point[i].size(),0.0);
	//
	return;//Linquin;
	for (int i=1;i<=order;i++)
	{
		int N_cum_size=0;
		CalcFullNumberCoeff(i,i,nsn+1,N_cum_size,m_stats_map_point[i]);
		m_stats_map_point[i].resize(N_cum_size);
		N_cum_size=0;
		CalcFullNumberCoeff(i,i,nsn+1,N_cum_size,m_stats_map_point[i]);
		for (int j=0;j<m_stats_map_point[i].size();j++)
			for (int k=0;k<m_stats_map_point[i][j].size();k+=2)
			{
				m_stats_map_point[i][j][k]=nsn-m_stats_map_point[i][j][k];
			}
	}
	m_stats_map_values.clear();

	m_stats_map_values.resize(m_stats_map_point.size());

	for(size_t i=0;i<m_stats_map_values.size();i++)
		m_stats_map_values[i].resize(m_stats_map_point[i].size(),0.0);
	return;
	//

	m_stats_map_point.clear();
	m_stats_map_point.resize(order+1);
	m_stats_map_point[0].resize(1);
	m_stats_map_point[1].resize(1);
	m_stats_map_point[0][0].push_back(0);
	m_stats_map_point[1][0].push_back(0);
	m_stats_map_point[1][0].push_back(1);//ILNUR. add order to stats
	for(int i=2;i<=order;i++)
	{
		if(nsn==0 && nsn < infoin.m_mn_s)
		{
			m_stats_map_point[i].resize(1);
			m_stats_map_point[i][0].push_back(0);
			m_stats_map_point[i][0].push_back(i);//ILNUR. add order to stats
		}
		if(nsn<i)
		{
			m_stats_map_point[i].resize(1);
			m_stats_map_point[i][0].push_back(0);
			m_stats_map_point[i][0].push_back(i);//ILNUR. add order to stats
		}
		else
		{
			int cr_size = nsn-i+1; //2;
			m_stats_map_point[i].resize(cr_size+1);
			m_stats_map_point[i][0].push_back(0);
			m_stats_map_point[i][0].push_back(i);//ILNUR. add order to stats
			int start = 1, end = i-1;
			int cr = 1;
			while (end<nsn) //+1)
			{
				m_stats_map_point[i][cr].push_back(0);
				m_stats_map_point[i][cr].push_back(1);//ILNUR. add order to stats
				for(int k=start;k<=end;k++)
				{
					m_stats_map_point[i][cr].push_back(k);
					m_stats_map_point[i][cr].push_back(1);//ILNUR. add order to stats
				}
				cr++;
				start++;
				end++;
			}
		}
		/*
		if(i==2) // add all other combinations of data-data
		{
		int cr = (int) m_stats_map_point[i].size();
		m_stats_map_point[i].resize(m_stats_map_point[i].size() + (nsn*(nsn-1)/2));
		for(int p=0;p<nsn-1;p++)
		{
		for(int q=p+1;q<nsn;q++)
		{
		m_stats_map_point[i][cr].push_back(p+1);	
		m_stats_map_point[i][cr].push_back(q+1);
		cr++;
		}
		}
		}
		*/
	}

	m_stats_map_values.clear();

	m_stats_map_values.resize(m_stats_map_point.size());

	for(size_t i=0;i<m_stats_map_values.size();i++)
		m_stats_map_values[i].resize(m_stats_map_point[i].size(),0.0);
}

void
	Tools::ArrangeSol(InfoIn& infoin, Grid& grid, Samples& samples, Tools& tools, int& real_no)
{
	double min, max, mean;
	min=samples.m_samples_info.m_min;
	max=samples.m_samples_info.m_max;
	mean=samples.m_samples_info.m_mean;
	int n_thres = samples.m_n_thres;
	m_stat_grid.resize(n_thres);
	double step = (max-min)/n_thres;

	for(int i=0;i<n_thres;i++)
	{
		{
			m_stat_grid[i].resize(2,0.0);
			if(i==0)
				m_stat_grid[i][0] = min + step;
			else
				m_stat_grid[i][0] = m_stat_grid[i-1][0] + step;
		}

		m_stat_grid[i][1] = 0.0;
	} 

	m_stat_grid[n_thres-1][0] = max;

	for(int i=0;i<infoin.m_grid_info.m_nv;i++)
	{
		double val = grid.m_data[i][infoin.m_dimension+real_no];
		if(tools.m_sample_indicator_orig[i]<1 && (grid.m_max[real_no]-grid.m_min[real_no]) > 1e-8)
			val  = (val -grid.m_min[real_no]) * (max - min)/(grid.m_max[real_no]-grid.m_min[real_no]) + min;      

		grid.m_data[i][infoin.m_dimension+real_no] = val;

		bool find_it = false;
		for(int j=0;j<n_thres;j++)
		{
			if(val<=m_stat_grid[j][0])
			{
				m_stat_grid[j][1] += 1.0/infoin.m_grid_info.m_nv;
				find_it = true;
				break;
			}
		}
	}

	for(int i=1;i<n_thres;i++)
		m_stat_grid[i][1] = m_stat_grid[i][1] + m_stat_grid[i-1][1];

	m_stat_grid[n_thres-1][1] = 1.0;

	for(int i=0;i<infoin.m_grid_info.m_nv;i++)
	{
		if(tools.m_sample_indicator_orig[i] ==0)
		{
			double val = grid.m_data[i][infoin.m_dimension+real_no];
			for(int j=0;j<n_thres;j++)
			{
				if(val<samples.m_stat_samples[j][0])
				{
					double pv = m_stat_grid[j][1];
					for(int k=0;k<n_thres;k++)
					{
						if(samples.m_stat_samples[k][1]>pv)
						{
							grid.m_data[i][infoin.m_dimension+real_no] = samples.m_stat_samples[k][0];
							break;
						}
					}
					break;
				}
			}
		}      
	}
}

void Grid::FillData(InfoIn& infoin)
{


	// fill in the grid coordinates
	int cp = 0;

	if(infoin.m_dimension==1)
	{
		for(int i=0;i<infoin.m_grid_info.m_nx;i++)
		{
			m_data[cp][0] = infoin.m_grid_info.m_x0 + i*infoin.m_grid_info.m_dx;
			cp++;
		}
	}
	else if(infoin.m_dimension==2)
	{
		for(int j=0;j<infoin.m_grid_info.m_ny;j++)
		{
			for(int i=0;i<infoin.m_grid_info.m_nx;i++)
			{
				m_data[cp][0] = infoin.m_grid_info.m_x0 + i*infoin.m_grid_info.m_dx;
				m_data[cp][1] = infoin.m_grid_info.m_y0 + j*infoin.m_grid_info.m_dy;
				cp++;
			}
		}
	}
	else if(infoin.m_dimension==3)
	{
		for(int k=0;k<infoin.m_grid_info.m_nz;k++)
		{
			for(int j=0;j<infoin.m_grid_info.m_ny;j++)
			{
				for(int i=0;i<infoin.m_grid_info.m_nx;i++)
				{
					m_data[cp][0] = infoin.m_grid_info.m_x0 + i*infoin.m_grid_info.m_dx;
					m_data[cp][1] = infoin.m_grid_info.m_y0 + j*infoin.m_grid_info.m_dy;
					m_data[cp][2] = infoin.m_grid_info.m_z0 + k*infoin.m_grid_info.m_dz;
					cp++;  
				}
			}
		}
	}
}
void Grid::Fill_Mask(InfoIn& infoin)
{
	//check mask grid
	if(infoin.m_files[4].length()>0)
	{

		cout<<"Mask grid reading is started...."<<endl;
		Double1Vec nodes_to_simulate(infoin.m_grid_info.m_nv,0);
		int  line_cnt;
		Double2Vec mask_data(infoin.m_grid_info.m_nv);
		for(int i=0;i<mask_data.size();i++)
			mask_data[i].resize(1,0.0);
		if(HosDataManager::Read_SGEMS(infoin.m_files[4],mask_data,infoin))
		{
			for(int i=0;i<mask_data.size();i++)
				nodes_to_simulate[i]=mask_data[i][0];
			line_cnt= (int)nodes_to_simulate.size();
		}else
		{
			const char* para_file = infoin.m_files[4].c_str();
			ifstream f_parafile(para_file);

			if(!f_parafile)
			{
				std::cout << "ERROR: The mask grid file does not exist. Please check the input parameters file."<<endl;
				std::cout << "All grid nodes are going to be simulated"<<endl;
				system("pause");
			}else
			{
				nodes_to_simulate.resize(infoin.m_grid_info.m_nv,0);
				char line[1024];
				int n_vars=0;

				f_parafile.getline(line,sizeof(line));
				f_parafile.getline(line,sizeof(line));
				stringstream ss(line);
				ss >>n_vars;
				for (int i=0;i<n_vars;i++)
					f_parafile.getline(line,sizeof(line));

				vector<string> str_values;
				line_cnt=0;
				bool isgood=true;

				double temp;

				while (!f_parafile.eof( ))  
				{
					//f_parafile.getline(line,sizeof(line));
					//ss<<line;
					for (int i=0;i<n_vars-1;i++)
					{
						f_parafile >> temp;
					}
					f_parafile>>(temp); 
					nodes_to_simulate[line_cnt]=temp;
					line_cnt++;
					if(line_cnt>=infoin.m_grid_info.m_nv)
					{
						break;
					}
				}
				f_parafile.close();
			}
		}
		if(line_cnt!=infoin.m_grid_info.m_nv)
		{
			std::cout << "ERROR: The mask grid file does not match the size of grid"<<endl;
			std::cout << "All grid nodes are going to be simulated"<<endl;
			system("pause");
		}else
		{
			for (int i=0;i<infoin.m_grid_info.m_nv;i++)

				if(nodes_to_simulate[i]<=0)
				{
					m_data[i][infoin.m_dimension]=-1111;

				}

		}

		cout<<"Mask grid reading is finished...."<<endl;

	}

}

void Grid::Grid_Create(InfoIn& infoin)
{
	// allocate memory
	cout<<"Grid allocation is started...."<<endl;
	//m_data.reserve(infoin.m_grid_info.m_nv);

	m_data.resize(infoin.m_grid_info.m_nv);
	//cout<<sizeof(m_data)<<endl;

	m_multigrid_index.resize(infoin.m_grid_info.m_nv);
	//	cout<<sizeof(m_multigrid_index[0])<<endl;
	for(int i=0;i<infoin.m_grid_info.m_nv;i++)
		m_data[i].resize(infoin.m_dimension+infoin.m_n_realization+1,gNullvalue);

	//cout<<sizeof(m_data[0][0])<<endl;

	// fullin info
	FillData(infoin);
	Fill_Mask(infoin);
	cout<<"Grid allocation is finished"<<endl;
}





void Grid::AssignNeigbors(Tools& tools, std::vector<int> indexv)
{
	int cr = (int) indexv.size();
	////Ilnur. get rid of octantsearch for now
	tools.m_neighbors_info.m_n_local_samples=cr;
	tools.m_neighbors_info.m_samples_neighbor.resize(cr,0);

	for(int i=0;i<cr;i++)
	{
		tools.m_neighbors_info.m_samples_neighbor[i] = indexv[i];

	}

	return;
	//Ilnur. get rid of octantsearch for now

}
void InfoIn::Get_Search_Template()
{


	if (search_template.size()<1)
	{

		double Rx=max((m_template_info.m_nx-1)/2,1);
		double Ry=max((m_template_info.m_ny-1)/2,1);
		double Rz=max((m_template_info.m_nz-1)/2,1);
		if(m_dimension<3)
			Rz=0;
		DoubleIntVec forsort;
		int sort_nx= (int)(Rx*2+1);
		int sort_ny= (int)(Ry*2+1);
		int sort_nz= (int)(Rz*2+1);
		int forsort_size=max(sort_nx*sort_ny*sort_nz,m_mx_s);
		search_template.resize(forsort_size);
		forsort.resize(forsort_size);
		int grid_step = 1; 
		int cnt=0;
		double dx_grid=m_grid_info.m_dx;
		double dy_grid=m_grid_info.m_dy;
		double dz_grid=m_grid_info.m_dz;
		double Rx2_meter=pow(Rx*m_template_info.m_dx,2.0);
		double Ry2_meter=pow(Ry*m_template_info.m_dy,2.0);
		double Rz2_meter=pow(Rz*m_template_info.m_dz,2.0);
		int k0 = (int)floor(-Rz*grid_step);
		int k1 = (int)ceil(Rz*grid_step);
		int j0 = (int)floor(-Ry*grid_step);
		int j1 = (int)ceil(Ry*grid_step);
		int i0 = (int)floor(-Rx*grid_step);
		int i1 = (int)ceil(Rx*grid_step);
		for (int k=k0;k<=k1;k+=grid_step)
			for (int j=j0;j<=j1;j+=grid_step)
				for (int i=i0;i<=i1;i+=grid_step)
				{

					forsort[cnt].first=j*j*dy_grid*dy_grid/(Ry2_meter)+i*i*dx_grid*dx_grid/(Rx2_meter);
					if(m_dimension>2)
						forsort[cnt].first+=k*k*dz_grid*dz_grid/(Rz2_meter);
					forsort[cnt].second=cnt;
					cnt++;
				}
				std::sort(forsort.begin(), forsort.end(), pairCompare);
				n_search_template=0;
				for (int i=0;i<search_template.size();i++)
				{
					int ind=forsort[i].second;
					int kk=ind/(sort_nx*sort_ny);
					ind=ind-kk*sort_nx*sort_ny;
					int jj=(ind)/(sort_nx);
					ind=ind-jj*sort_nx;
					int ii=ind;
					search_template[i].resize(3);
					search_template[i][2]=(int)floor((kk-Rz)*m_template_info.m_dz);
					search_template[i][1]= (int)floor((jj-Ry)*m_template_info.m_dy);
					search_template[i][0]= (int)floor((ii-Rx)*m_template_info.m_dx);
					if(forsort[i].first>1)
						break;
					n_search_template++;
				}
				
				/*for (int i=0;i<search_template.size();i++)
				{
					n_search_template++;
					if(search_template[i][0]>Rx*m_template_info.m_dx||search_template[i][1]>Ry*m_template_info.m_dy||search_template[i][2]>Rz*m_template_info.m_dz)
						break;
				}*/
				search_template.resize(n_search_template);
				rand_path_in_search.resize(n_search_template);
				for (int i = 0; i < search_template.size(); i++)
				{
					rand_path_in_search[i] = i;
				}
	}
}
double Tools::Get_dist_elp(InfoIn& infoin,double x,double y, double z=0)
{
	double Rx=max((infoin.m_template_info.m_nx-1)/2,1)*infoin.m_template_info.m_dx;
	double Ry=max((infoin.m_template_info.m_ny-1)/2,1)*infoin.m_template_info.m_dy;;
	double Rz=max((infoin.m_template_info.m_nz-1)/2,1)*infoin.m_template_info.m_dz;;
	double dist=y*y*1.0/(Ry*Ry)+x*x*1.0/(Rx*Rx)+z*z*1.0/(Rz*Rz);
	return sqrt(dist);
}
std::vector<int> 
	Grid::Find_Samples_Within_Template(InfoIn& infoin, TrainingImage& ti, Samples& samples, Tools& tools)
{
	/*TEMP VARIABLES*/
	double x0,y0,z0, x0t, y0t, z0t, eps = (samples.m_samples_info.m_max - samples.m_samples_info.m_min)/20.0;
	int n1, n2, n3, gp, index, dim, n_samples_curr, ns_c, cr, 
		propag_x, propag_y, propag_z, 
		propag_x_t, propag_y_t, propag_z_t, propag_x_t_old, propag_y_t_old, propag_z_t_old, 
		curr_propagation_x = 0, curr_propagation_y = 0, curr_propagation_z = 0;

	bool good_number_samples, test;

	Int1Vec indexv;
	Double1Vec point;
	index = tools.m_cp;
	dim = infoin.m_dimension;

	point.resize(dim);
	double x_orig=m_data[0][0];
	double y_orig=0;
	double z_orig=0;
	x0= m_data[index][0];   point[0]=x0;


	if(dim>1)  {    y0= m_data[index][1];    point[1]=y0;  y_orig= m_data[0][1];}
	if(dim>2)  {    z0= m_data[index][2];    point[2]=z0; z_orig= m_data[0][2] ;}


	n_samples_curr = (int) tools.m_samples_curr.size();

	ns_c = 0;

	propag_x = infoin.m_template_info.m_nx/2;
	propag_y = infoin.m_template_info.m_ny/2;
	propag_z = infoin.m_template_info.m_nz/2;


	good_number_samples=true;

	cr = 0;

	propag_x_t=1;
	propag_y_t=1;
	propag_z_t=1;
	propag_x_t_old=1;
	propag_y_t_old=1;
	propag_z_t_old=1;

	test = true;

	if(infoin.m_dimension==2)
	{
		propag_z       = 0;
		propag_z_t     = 0;
		propag_z_t_old = 0;
	}
	tools.m_neighbors_info.m_samples_neighbor_vals.clear();
	tools.m_neighbors_info.m_samples_distance.clear();
	//19.09.2014. Ilnur. Simplier version of searching
	infoin.Get_Search_Template();

	if(dim<3)
	{
		z0=0;
	}

	int grid_step = pow(2.0,tools.m_grid_curr);
	int nx_mg = (int)ceil(infoin.m_grid_info.m_nx / grid_step)+2;
	int ny_mg = (int)ceil(infoin.m_grid_info.m_ny / grid_step)+2;
	int nz_mg = (int)ceil(infoin.m_grid_info.m_nz / grid_step)+2;
	for (int i=0;i<infoin.n_search_template;i++)
	{
		double dx=infoin.search_template[i][0];
		double dy=infoin.search_template[i][1];
		double dz=infoin.search_template[i][2];
		dx *= grid_step;
		dy *= grid_step;
		dz *= grid_step;
		int ii=(int)alglib::round((x0+ dx -x_orig)/infoin.m_grid_info.m_dx);
		int jj=(int)alglib::round((y0+ dy -y_orig)/infoin.m_grid_info.m_dy);
		int kk=(int)alglib::round((z0+ dz -z_orig)/infoin.m_grid_info.m_dz);
		if (ii>=0&&ii<infoin.m_grid_info.m_nx&& jj>=0&&jj<infoin.m_grid_info.m_ny&&kk>=0&&kk<infoin.m_grid_info.m_nz)
		{
			gp = ii + jj*infoin.m_grid_info.m_nx + kk * infoin.m_grid_info.m_nx* infoin.m_grid_info.m_ny;
			double x0t=m_data[gp][0];
			double y0t=m_data[gp][1];
			double z0t=m_data[gp][2];

			if(tools.m_sample_indicator_curr[gp]>0)
			{

				indexv.push_back(gp);

				if(dim==2)
					tools.m_neighbors_info.m_samples_distance.push_back(tools.Get_dist_elp(infoin,dx,dy));
				else
					tools.m_neighbors_info.m_samples_distance.push_back(tools.Get_dist_elp(infoin,dx,dy,dz));
				tools.m_neighbors_info.m_samples_neighbor_vals.push_back(m_data[gp][infoin.m_dimension]);
				if (indexv.size()>=infoin.m_mx_s_cur)
					break;

			}
			else
			{

				if (tools.m_grid_curr > 0)
				{
					 ii = (int)alglib::round((x0 + dx - x_orig) / (infoin.m_grid_info.m_dx*grid_step));
					 jj = (int)alglib::round((y0 + dy - y_orig) / (infoin.m_grid_info.m_dy*grid_step));
					 kk = (int)alglib::round((z0 + dz - z_orig) / (infoin.m_grid_info.m_dz*grid_step));
					 int gp_mgr = ii + jj*nx_mg + kk * nx_mg* ny_mg;

					 if (tools.m_sample_multigrid[tools.m_grid_curr][gp_mgr] !=gNullvalue)
					 {
						 gp = tools.m_sample_index_multigrid[tools.m_grid_curr][gp_mgr];
						 indexv.push_back(gp);
						 dx = m_data[gp][0] - x0;
						 dy = m_data[gp][1] - y0;
						 dz = 0;
						 if (dim> 2)
							 dz = m_data[gp][2] - z0;

						 if (dim == 2)
							 tools.m_neighbors_info.m_samples_distance.push_back(tools.Get_dist_elp(infoin, dx, dy));
						 else
							 tools.m_neighbors_info.m_samples_distance.push_back(tools.Get_dist_elp(infoin, dx, dy, dz));

						 tools.m_neighbors_info.m_samples_neighbor_vals.push_back(tools.m_sample_multigrid[tools.m_grid_curr][gp_mgr]);
						
						 if (indexv.size() >= infoin.m_mx_s_cur)
							 break;
					 }


				}


			}
		}
	}
	Int1Vec indexSort = tools.getSortIndex(tools.m_neighbors_info.m_samples_distance);
	Int1Vec newIndexv = indexv;
	Double1Vec newVals =  tools.m_neighbors_info.m_samples_neighbor_vals;
	for (int i = 0; i < indexSort.size(); i++)
	{
		newVals[i] = tools.m_neighbors_info.m_samples_neighbor_vals[indexSort[i]];
		newIndexv[i] = indexv[indexSort[i]];

	}
	tools.m_neighbors_info.m_samples_neighbor_vals = newVals;
	indexv = newIndexv;
	return indexv;
}
Int1Vec Tools::getSortIndex(Double1Vec & arr)
{
	vector<pair<double, int>> forSort(arr.size());
	for (int i = 0; i < arr.size(); i++)
	{
		forSort[i].first = arr[i];
		forSort[i].second = i;

	}
	sort(forSort.begin(), forSort.end(), pairCompare);
	Int1Vec indexes(arr.size());
	for (int i = 0; i < arr.size(); i++)
	{
		indexes[i] = forSort[i].second;
		arr[i] = forSort[i].first;
	}
	return indexes;
}
void Grid::FindSamplesNeighbor(InfoIn& infoin, TrainingImage& ti, Samples& samples, Tools& tools, Stats& stats,int calc_type)
{
#if defined(SHOW_DBG)
	LARGE_INTEGER t1, t2;           // ticks
	double elapsedTime;
	LARGE_INTEGER frequency;        // ticks per second
	// get ticks per second
	QueryPerformanceFrequency(&frequency);

	// start timer
	QueryPerformanceCounter(&t1);
#endif
	std::vector<int> indexv = Find_Samples_Within_Template(infoin, ti, samples, tools);
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	QueryPerformanceCounter(&t1);
	//cout<<"Find_Samples_Within_Template\t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;
	find_nneib+=elapsedTime;
#endif
	AssignNeigbors( tools, indexv);
	//infoin, samples,
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	QueryPerformanceCounter(&t1);
	//cout<<"AssignNeigbors\t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;
	find_octa+=elapsedTime;
#endif
	stats.Build_Local_Templates(infoin,ti,tools,m_data,indexv);
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	QueryPerformanceCounter(&t1);
	//cout<<"Build_Local_Templates\t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;
	buildtemp+=elapsedTime;
#endif
	//Ilnur
	double minv=1e+10;
	double maxv=-1e+10;
	minv=min(ti.m_ti_min,minv);
	maxv=max(ti.m_ti_max,maxv);
	minv=min(samples.m_samples_info.m_min,minv);
	maxv=max(samples.m_samples_info.m_max,maxv);
	tools.m_neighbors_info.m_local_min=minv-(maxv-minv)*0.1;
	tools.m_neighbors_info.m_local_max=maxv+(maxv-minv)*0.1;
	//
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	minmaxval+=elapsedTime;
	//	cout<<"min max\t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;
	QueryPerformanceCounter(&t1);
#endif
	if(calc_type>1)
		stats.Get_Data_Replicates(infoin,   samples,tools,m_data,infoin.search_template, indexv);
	indexv.clear();
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	QueryPerformanceCounter(&t1);
	//cout<<"Get_Data_Replicates\t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;

#endif
}

int Dump::Get_Dump_ind(Int1Vec& coord)
{
	int Dump_ind=0;
	int mult=1;
	for(int i=0;i<m_dimension;i++)
	{
		int centr=Dump_size[i]/2;
		int shifted_coord=coord[i]+centr;
		if(shifted_coord>=0&&shifted_coord<Dump_size[i])
			Dump_ind+=shifted_coord*mult;		
		else 
			return -1;
		mult*=Dump_size[i];
	}
	return Dump_ind;


}
void Dump::Get_XYZ_from_Dump_ind(int Dump_ind,Int1Vec& coord)
{
	if(Dump_ind<0&&Dump_ind>=n_values)
	{
		std::cout << "ERROR: The input parameters file does not exist. Please check the name again"<<endl;
		system("pause");
		exit (0);
		return;
	}

	for(int i=0;i<m_dimension;i++)
	{
		int centr=Dump_size[i]/2;
		coord[i]=Dump_ind%Dump_size[i]-centr;
		Dump_ind/=Dump_size[i];
	}
	return ;

}

void Stats::Get_Data_Replicates(InfoIn& infoin,  Samples& samples,Tools& tools, Double2Vec& m_data,Int2Vec& search_tmp, const Int1Vec& indexv)
{

	if(tools.m_neighbors_info.m_n_local_samples > 0)
	{
		tools.m_ijk_ref_replicate.resize(tools.m_neighbors_info.m_n_local_samples);

		//	m_all_value_replicates[0].m_value_replicate.resize(tools.m_neighbors_info.m_n_local_samples+1); //+1 is the center of the template.

		//	m_all_value_replicates[0].m_value_replicate[0] = ti.m_data[tools.m_cp][infoin.m_dimension];

		for(int i=0;i<tools.m_neighbors_info.m_n_local_samples;i++)
		{
			//	m_all_value_replicates[0].m_value_replicate[i+1] = m_data[indexv[i]][infoin.m_dimension];

			tools.m_ijk_ref_replicate[i].resize(infoin.m_dimension);

			tools.m_ijk_ref_replicate[i][0] = (int)alglib::round( (m_data[tools.m_cp][0] - m_data[indexv[i]][0])/infoin.m_grid_info.m_dx);

			tools.m_ijk_ref_replicate[i][1] = (int)alglib::round( (m_data[tools.m_cp][1] -m_data[indexv[i]][1])/infoin.m_grid_info.m_dy);;

			if(infoin.m_dimension==3)
				tools.m_ijk_ref_replicate[i][2] = (int)alglib::round ((m_data[tools.m_cp][2] - m_data[indexv[i]][2])/infoin.m_grid_info.m_dz);;
		}

		int ix0,iy0,iz0;
		int ix,iy,iz;
		int n1,n2,n3;

		int count=0;
		//int n_size=samples.m_data.size();
		int n_size=infoin.m_nb_locations_stats;


		int cnt=0;
		Replicates& data_value_replicates=tools.m_data_value_replicates;
		data_value_replicates.m_size=0;
		data_value_replicates.m_neib_num=tools.m_neighbors_info.m_n_local_samples+1;
		//	IntDoubleIntVec forsort;

		//forsort.reserve(n_size);
		time_t start_real,end_real=0;

		time (&start_real);
		//Int1Vec perm_ind(tools.m_samples_curr.size());
		Int1Vec perm_ind(samples.m_data.size());
		for (int ip=0;ip<perm_ind.size();ip++)
			perm_ind[ip]=ip;
		random_shuffle(perm_ind.begin(),perm_ind.end());

		n_size=min(n_size,(int)perm_ind.size());
		int avg_nneib=0;
		Int1Vec coord0(3,0);
		for (int ip=0;ip<n_size;ip++)
		{
			/*for (int ip=0;ip<search_tmp.size();ip++)
			{*/

			if(data_value_replicates.m_size>=n_size)
				break;

			int data_repl_base=data_value_replicates.m_size*data_value_replicates.m_shift;

			//n1= (int)( search_tmp[ip][0]+m_data[tools.m_cp][0]);

			//n2= (int) (search_tmp[ip][1]+m_data[tools.m_cp][1]);
			//if(n1 < 0 || n2 < 0 || n1 >= infoin.m_ti_info.m_nx || n2 >= infoin.m_ti_info.m_ny)
			//{
			//	continue;
			//}
			//int gp0 = n1 + n2 * infoin.m_ti_info.m_nx;

			//if(infoin.m_dimension==3)
			//{
			//	n3=(int) (search_tmp[ip][2]+m_data[tools.m_cp][2]);
			//	if(n3<0 || n3 >= infoin.m_ti_info.m_nz)
			//	{
			//		continue;
			//	}
			//	gp0+=n3 * infoin.m_ti_info.m_nx * infoin.m_ti_info.m_ny;
			//}
			//if(tools.m_sample_indicator_curr[gp0]==0)
			//	continue;


			n1= (int)alglib::round(( samples.m_data[perm_ind[ip]][0]-infoin.m_grid_info.m_x0)/infoin.m_grid_info.m_dx);;

			n2= (int)alglib::round ((samples.m_data[perm_ind[ip]][1]-infoin.m_grid_info.m_y0)/infoin.m_grid_info.m_dy);;
			int gp0=n1 + n2 * infoin.m_grid_info.m_nx;
			if(infoin.m_dimension==3)
			{
				n3=(int)alglib::round((samples.m_data[perm_ind[ip]][2]-infoin.m_grid_info.m_z0)/infoin.m_grid_info.m_dz);;
				gp0+=n3 * infoin.m_grid_info.m_nx * infoin.m_grid_info.m_ny;
			}



			//int gp0=tools.m_samples_curr[perm_ind[ip]];
			//n1= (int) m_data[gp0][0];

			//n2= (int)  m_data[gp0][1];
			//
			//if(infoin.m_dimension==3)
			//		{
			//			n3=(int)m_data[gp0][2];

			//}
			//Replicate repl_buf;
			//repl_buf.m_value_replicate.resize(tools.m_neighbors_info.m_n_local_samples+1);
			//repl_buf.m_index.resize(repl_buf.m_value_replicate.size());

			//data_value_replicates[i].m_value_replicate[0] = samples.m_data[i][infoin.m_dimension];

			/*	ix0= (int) samples.m_data[i][0];

			iy0= (int)samples.m_data[i][1];

			if(infoin.m_dimension==3)
			iz0=(int)samples.m_data[i][2];*/

			data_value_replicates.m_values[data_repl_base+0] =m_data[gp0][infoin.m_dimension];

			int Dump_ind=tools.Repl_Dump.Get_Dump_ind(coord0);
			int i_dat=tools.m_sample_indicator_orig[gp0]-1;

			if(i_dat<0||(tools.Repl_Dump.values[i_dat][Dump_ind]!=-999&&tools.Repl_Dump.values[i_dat][Dump_ind]!=data_value_replicates.m_values[data_repl_base+0]))
			{
				cout<<"something wrong in get_data_repl"<<endl;
			}
			tools.Repl_Dump.values[i_dat][Dump_ind]=data_value_replicates.m_values[data_repl_base+0];
			data_value_replicates.m_indexes[data_repl_base+0]=gp0;
			ix0=n1;

			iy0= n2;

			if(infoin.m_dimension==3)
				iz0=n3;

			cnt=0;
			int ix_tol0=0,iy_tol0=0,iz_tol0=0;
			double sum_dist=0;
			for(int j=0;j<tools.m_neighbors_info.m_n_local_samples;j++)
			{
				//ILNUR. Was +, I changed for -,because you if you hav 0,0 with neihbor (1,2), you will have (-1,-2) for m_ijk_ref_replicate and you should not add it to central node.
				int ix_tol0 = ix0 - tools.m_ijk_ref_replicate[j][0];
				int iy_tol0 = iy0 - tools.m_ijk_ref_replicate[j][1];
				double xx=tools.m_ijk_ref_replicate[j][0]*infoin.m_grid_info.m_dx;
				double yy=tools.m_ijk_ref_replicate[j][1]*infoin.m_grid_info.m_dy;
				double zz=0;
				if(infoin.m_dimension==3)
				{	
					iz_tol0 = iz0 - tools.m_ijk_ref_replicate[j][2];
					zz=tools.m_ijk_ref_replicate[j][2]*infoin.m_grid_info.m_dz;
				}
				double dist0=sqrt(xx*xx+yy*yy+zz*zz);

				//ILNUR. Add tolerance for large distances. Need check on big fields.
				double ang=0;
				double ang_tol=0.17;
				bool is_found=false;
				data_value_replicates.m_values[data_repl_base+j+1]=-999;
				data_value_replicates.m_indexes[data_repl_base+j+1]=-999;
				for (int ind_neigh_tol=0;ind_neigh_tol<1;ind_neigh_tol++)
					//for (int ind_neigh_tol=0;ind_neigh_tol<search_tmp.size();ind_neigh_tol++)
				{
					double xx=search_tmp[ind_neigh_tol][0];
					double yy=search_tmp[ind_neigh_tol][1];
					double zz=search_tmp[ind_neigh_tol][2];
					ang=sqrt(xx*xx+yy*yy+zz*zz)/dist0;
					if (ang>ang_tol)
						break;
					ix=ix_tol0+search_tmp[ind_neigh_tol][0];
					iy=iy_tol0+search_tmp[ind_neigh_tol][1];
					iz=iz_tol0+search_tmp[ind_neigh_tol][2];

					n1 = (int) (ix );
					n2 = (int) (iy);

					if(n1 < 0 || n2 < 0 || n1 >= infoin.m_grid_info.m_nx || n2 >= infoin.m_grid_info.m_ny)
					{
						continue;
					}

					int gp = n1 + n2 * infoin.m_grid_info.m_nx;

					if(infoin.m_dimension==3)
					{
						n3 = (int) (iz );
						if(n3<0 || n3 >= infoin.m_grid_info.m_nz)
						{
							continue;
						}
						gp = gp + n3 * infoin.m_grid_info.m_nx * infoin.m_grid_info.m_ny;
					}
					data_value_replicates.m_indexes[data_repl_base+j+1]=gp;
					double val=gNullvalue;
					Dump_ind=tools.Repl_Dump.Get_Dump_ind(tools.m_ijk_ref_replicate[j]);
					if(tools.m_sample_indicator_orig[gp]==0)
					{


						if(Dump_ind>=0)
						{

							if(tools.Repl_Dump.values[i_dat][Dump_ind]!=gNullvalue)
							{
								val=tools.Repl_Dump.values[i_dat][Dump_ind];
							}
							else
								continue;
						}else
							continue;
					}else
					{
						val=m_data[gp][infoin.m_dimension];
						if(Dump_ind>=0)
						{
							if(tools.Repl_Dump.values[i_dat][Dump_ind]!=-999&&tools.Repl_Dump.values[i_dat][Dump_ind]!=val)
							{
								cout<<"something wrong in get_data_repl2"<<endl;
							}
							tools.Repl_Dump.values[i_dat][Dump_ind]=val;
							//cout<<tools.m_ijk_ref_replicate[j][0]<<"\t"<<tools.m_ijk_ref_replicate[j][1]<<endl;
						}
					}

					is_found=true;
					data_value_replicates.m_values[data_repl_base+j+1]= val;
					avg_nneib++;
					cnt++;
					break;
				}
				if(is_found)
					sum_dist+=dist0;
				//	break;
			}

			//	IntDoubleInt forsort_elm;
			//	forsort_elm.num=cnt+1;
			//	forsort_elm.dist=sum_dist/(cnt+1);
			//	forsort_elm.ind=gp0;//data_value_replicates.size();
			//	forsort.push_back(forsort_elm);

			data_value_replicates.m_size++;

		}
		//cout<<"Avg_nneib=\t"<<avg_nneib*1.0/data_value_replicates.size()<<endl;


		return;


	}

}

void Stats::Build_Local_Templates(InfoIn& infoin, TrainingImage& ti, Tools& tools, const Double2Vec& m_data, const Int1Vec& indexv)
{
	
	int	k_central=0;

	if (infoin.m_dimension > 2)
	{
		k_central = (int)alglib::round((m_data[tools.m_cp][2] - infoin.m_grid_info.m_z0) / infoin.m_grid_info.m_dz);
		//k_central += (infoin.m_template_info.m_nz - 1) / 2;
	}
	// Fill in the reference replicate ijk.
	if(tools.m_neighbors_info.m_n_local_samples > 0)
	{
		///
		//	  tools.m_cp=indexv[0]+1;
		//
		tools.m_ijk_ref_replicate.resize(tools.m_neighbors_info.m_n_local_samples);

		//	m_all_value_replicates[0].m_value_replicate.resize(tools.m_neighbors_info.m_n_local_samples+1); //+1 is the center of the template.

		//	m_all_value_replicates[0].m_value_replicate[0] = ti.m_data[tools.m_cp][infoin.m_dimension];

		for(int i=0;i<tools.m_neighbors_info.m_n_local_samples;i++)
		{
			//	m_all_value_replicates[0].m_value_replicate[i+1] = m_data[indexv[i]][infoin.m_dimension];

			tools.m_ijk_ref_replicate[i].resize(infoin.m_dimension);

			tools.m_ijk_ref_replicate[i][0] =(int)alglib::round((m_data[tools.m_cp][0] - m_data[indexv[i]][0])/infoin.m_grid_info.m_dx);

			tools.m_ijk_ref_replicate[i][1] =(int)alglib::round((m_data[tools.m_cp][1] - m_data[indexv[i]][1])/infoin.m_grid_info.m_dy);

			if(infoin.m_dimension==3)
				tools.m_ijk_ref_replicate[i][2] = (int)alglib::round ((m_data[tools.m_cp][2] - m_data[indexv[i]][2])/infoin.m_grid_info.m_dz);
		}

		int ix0,iy0,iz0=0;
		int ix,iy,iz=0;
		int n1,n2,n3,gp;

		int count=0;
		int size_repl=infoin.m_nb_locations_stats;
		Replicates& all_repl=tools.m_all_value_replicates;
		fill(all_repl.m_values.begin(), all_repl.m_values.end(), gNullvalue);
		all_repl.m_neib_num=tools.m_neighbors_info.m_n_local_samples+1;
		all_repl.m_size=0;
		
		
		int beg_ind=0;
		int end_ind= (int)m_list_point_stats.size();
	
		
		DoubleIntVec forsort;
		forsort.reserve(min(end_ind- beg_ind, size_repl));
		std::pair<double, int> sort_elem;
		infoin.Get_Search_Template();
		
		if (localStationarity)
		{
			
			random_shuffle(infoin.rand_path_in_search.begin(), infoin.rand_path_in_search.end(), myrandom);
			beg_ind = 0;
			end_ind = infoin.rand_path_in_search.size();
		}else
			random_shuffle(m_list_point_stats.begin() + beg_ind, m_list_point_stats.begin() + end_ind, myrandom);

		// Need to create all replicates in the TI................
		for(size_t pp=beg_ind;pp<end_ind;pp++)
			//		for(size_t i=0;i<size_repl;i++)
		{
			
			size_t icentr = 0;
			if (localStationarity)
			{

				n3 = tools.m_cp / (infoin.m_grid_info.m_nx*infoin.m_grid_info.m_ny);
				int ind = tools.m_cp - n3*(infoin.m_grid_info.m_nx*infoin.m_grid_info.m_ny);
				n1 = (ind) % infoin.m_grid_info.m_nx;
				n2 = (ind) / infoin.m_grid_info.m_nx;
				Int1Vec rand_path = infoin.search_template[infoin.rand_path_in_search[pp]];
				n1 += rand_path[0];
				n2 += rand_path[1];
				n3 += rand_path[2];
				if (n1 < 0 || n2 < 0 || n1 >= infoin.m_ti_info.m_nx || n2 >= infoin.m_ti_info.m_ny)
					continue;
				if (n3<0 || n3 >= infoin.m_ti_info.m_nz)
					continue;
				icentr = n1 + n2*infoin.m_ti_info.m_nx + (infoin.m_grid_info.m_nx*infoin.m_grid_info.m_ny)*n3;
			}else
				icentr=m_list_point_stats[pp];
			if(count>=size_repl)
			{
				//cout<<"number of cycles"<<pp-beg_ind<<endl;
				break;
			}
			int repl_base=all_repl.m_size*all_repl.m_shift;
			if( ti.m_data[icentr][infoin.m_dimension]==gNullvalue)
				continue;
			all_repl.m_values[0+repl_base] = ti.m_data[icentr][infoin.m_dimension];
			all_repl.m_indexes[0+repl_base] = (int) icentr;
			ix0= (int)alglib::round( (ti.m_data[icentr][0]-infoin.m_ti_info.m_x0)/infoin.m_ti_info.m_dx);;

			iy0=  (int)alglib::round((ti.m_data[icentr][1]-infoin.m_ti_info.m_y0)/infoin.m_ti_info.m_dy);;

			if(infoin.m_dimension==3)
				iz0=(int)alglib::round( (ti.m_data[icentr][2]-infoin.m_ti_info.m_z0)/infoin.m_ti_info.m_dz);;
		//	if(!full_3d)
		//		assert(iz0==k_central);
			int cnt=0;
			for(int j=0;j<tools.m_neighbors_info.m_n_local_samples;j++)
			{
	/*			double drr=0;
				for (int pp=0;pp<infoin.m_dimension;pp++)
					drr+=tools.m_ijk_ref_replicate[j][pp]*tools.m_ijk_ref_replicate[j][pp];
				
				drr=sqrt(drr);
				double perturbation=Gauss_rand(0,0.3*drr/2);//hard coded 30% of distance is the 2sigma of perturbation
				Double1Vec testg(1000,0);
				for (int pp=0;pp<1000;pp++)
					testg[pp]=Gauss_rand(0,0.3/2);
*/
				//ILNUR. Was +, I changed for -,because you if you hav 0,0 with neihbor (1,2), you will have (-1,-2) for m_ijk_ref_replicate and you should not add it to central node.
				ix = ix0 - tools.m_ijk_ref_replicate[j][0];
				iy = iy0 - tools.m_ijk_ref_replicate[j][1];
				if(infoin.m_dimension==3)
					iz = iz0 - tools.m_ijk_ref_replicate[j][2];
				/*double perturbation_fi=2*acos(-1.0)*(*drand_val)();
				double perturbation_teta=acos(-1.0)*(*drand_val)();
				double ix_pert=perturbation*sin(perturbation_fi);
				double iy_pert=perturbation*cos(perturbation_fi);
				double iz_pert=0;
				
				if(infoin.m_dimension>2)
				{
					ix_pert*=cos(perturbation_teta);
					iy_pert*=cos(perturbation_teta);
					iz_pert=perturbation*sin(perturbation_teta);
				}*/
		//		ix += round(ix_pert);
			//	iy += round(iy_pert);
			//	iz += round(iz_pert);

				n1 = (ix);
				n2 = (iy);

				if(n1 < 0 || n2 < 0 || n1 >= infoin.m_ti_info.m_nx || n2 >= infoin.m_ti_info.m_ny)
				{
					all_repl.m_values[repl_base+j+1] = gNullvalue; // outside the domain
					break;
				}

				gp = n1 + n2 * infoin.m_ti_info.m_nx;

				if(infoin.m_dimension==3)
				{
					n3 = (iz );
					if(n3<0 || n3 >= infoin.m_ti_info.m_nz)
					{
						all_repl.m_values[repl_base+j+1]  =gNullvalue;
						break;
					}
					gp = gp + n3 * infoin.m_ti_info.m_nx * infoin.m_ti_info.m_ny;
				}
				if(ti.m_data[gp][infoin.m_dimension]==gNullvalue)
					break;
				all_repl.m_values[repl_base+j+1]  = ti.m_data[gp][infoin.m_dimension];
				all_repl.m_indexes[repl_base+j+1] =gp;
				cnt++;
			}
			if (cnt>= infoin.m_mn_s|| cnt >= tools.m_neighbors_info.m_n_local_samples)
			{
				sort_elem.first = cnt;
				sort_elem.second = count;
				forsort.push_back(sort_elem);
				all_repl.m_size++;
				count++;
			}
		}
		sort(forsort.begin(), forsort.end(), pairCompare);
		Replicates repl_sort = all_repl;
		
		fill(all_repl.m_neibcount_indexes.begin(), all_repl.m_neibcount_indexes.end(), 0);
		all_repl.m_neibcount_indexes.resize(all_repl.m_neib_num +1,0);
		for (int i = 0; i<all_repl.m_size; i++)
		{
			all_repl.m_neibcount_indexes[(int)forsort[i].first+1]++;
			int repl_base = i*all_repl.m_shift;
			int repl_base_new = forsort[i].second*all_repl.m_shift;
			for (int j = 0; j < all_repl.m_neib_num; j++)
			{
				all_repl.m_values[repl_base + j] = repl_sort.m_values[repl_base_new + j];
				all_repl.m_indexes[repl_base + j] = repl_sort.m_indexes[repl_base_new + j];
			}
					

		}
		for (int i = 1; i<all_repl.m_neibcount_indexes.size(); i++)
		{
			all_repl.m_neibcount_indexes[i]+= all_repl.m_neibcount_indexes[i-1];
		}
		if (count<1)
			cout<<"Low number of replicates"<<endl;

	}

};

void TrainingImage::FillData(InfoIn& infoin)
{
	double min=1e+10,max=-1e+10;

	// open TI data file
	const char* para_file = infoin.m_files[1].c_str();
	ifstream f_parafile(para_file);
	if(!f_parafile)
	{
		std::cout << "ERROR: The training data file does not exist. Please check the input parameters file."<<endl;
		system("pause");
		exit (0);
	}
	assert(f_parafile);  

	std::string data_name,tmp_name;
	int nvr;
	f_parafile >> tmp_name;  
	f_parafile >> nvr;
	infoin.m_ti_info.m_var_names.resize(nvr);
	for(int i=0;i<nvr;i++)
		f_parafile >> infoin.m_ti_info.m_var_names[i];

	for(int i=0;i<infoin.m_ti_info.m_nv;i++)
	{
		if(infoin.m_dimension==1)
			f_parafile >> m_data[i][0] >> m_data[i][1];
		else if(infoin.m_dimension==2)
			f_parafile >> m_data[i][0] >> m_data[i][1] >> m_data[i][2];
		else
			f_parafile >> m_data[i][0] >> m_data[i][1] >> m_data[i][2] >> m_data[i][3];
		if(m_data[i][infoin.m_dimension]!=gNullvalue)
		{
			if(m_data[i][infoin.m_dimension] > max) max = m_data[i][infoin.m_dimension];
			if(m_data[i][infoin.m_dimension] < min) min = m_data[i][infoin.m_dimension];
		}
	}


	m_ti_max = max;
	m_ti_min = min;


}
bool TrainingImage::FillDataFast(InfoIn& infoin)
{

	bool flag=HosDataManager::Read_SGEMS(infoin.m_files[1],m_data,infoin);
	return flag;
	// % eol
	//  if (!ok)   
	//         std::cerr << "Parse failed at #" << std::distance(mmap.const_data(), f) << " around '" << std::string(f,f+50) << "'\n";


}

void TrainingImage::TrainingImage_Create(InfoIn& infoin, Samples& samples)
{

	// allocate memory
	cout<<"TI allocation is started...."<<endl;
	m_data.resize(infoin.m_ti_info.m_nv);
	for(int i=0;i<infoin.m_ti_info.m_nv;i++)
		m_data[i].resize(infoin.m_dimension+1,0.0);
	cout<<"TI allocation is finished"<<endl;
	cout<<"TI reading is started...."<<endl;

	//time_t start_real,end_real;
	//clock_t t_elp;
	//t_elp=clock();
	//	time (&start_real);
	if(!FillDataFast(infoin))
		FillData(infoin);
	//	time (&end_real);
	//cout<<"alloc1 \t"<<end_real-start_real<<endl;
	//time (&start_real);
	//FillDataFast(infoin);
	//time (&end_real);
	//cout<<"alloc2 \t"<<end_real-start_real<<endl;
	m_ti_min=1e+10;
	m_ti_max=-1e+10;
	for(int i=0;i<infoin.m_ti_info.m_nv;i++)
	{
		if(m_data[i][infoin.m_dimension]!=gNullvalue)
		{
			if(m_data[i][infoin.m_dimension] > m_ti_max) m_ti_max = m_data[i][infoin.m_dimension];
			if(m_data[i][infoin.m_dimension] < m_ti_min) m_ti_min = m_data[i][infoin.m_dimension];
		}
	}
	cout<<"TI reading is finished...."<<endl;
	if(m_ti_max<=-1e+10)
	{
		m_ti_min = samples.m_samples_info.m_min;
		m_ti_max = samples.m_samples_info.m_max;
	}
	if(samples.m_samples_info.m_max<=-1e+10)
	{
		samples.m_samples_info.m_min=m_ti_min;
		samples.m_samples_info.m_max=m_ti_max;
		samples.Post_min_max(infoin);
	}
}
void Samples::Post_min_max(InfoIn& infoin)
{
	double max=	m_samples_info.m_max;
	double min=m_samples_info.m_min;
	m_n_thres=infoin.m_grid_info.m_nv/100;

	m_stat_samples.resize(m_n_thres);

	double step = (max-min)/m_n_thres;
	for(int i=0;i<m_n_thres;i++)
	{
		m_stat_samples[i].resize(2,0.0);
		if(i==0)
			m_stat_samples[i][0] = min + step;
		else
			m_stat_samples[i][0] = m_stat_samples[i-1][0] + step;
	} 
	for(int i=0;i<m_samples_info.m_n_samples;i++)
	{
		bool find_it = false;
		for(int j=0;j<m_n_thres;j++)
		{
			if(m_data[i][infoin.m_dimension]<=m_stat_samples[j][0])
			{
				m_stat_samples[j][1] += 1.0/m_samples_info.m_n_samples;
				find_it = true;
				break;
			}
		}
		if(!find_it)
		{
			m_stat_samples[m_n_thres-1][1] += 1.0/m_samples_info.m_n_samples;
		}
	}

	for(int i=1;i<m_n_thres;i++)
		m_stat_samples[i][1] = m_stat_samples[i][1] + m_stat_samples[i-1][1];

	m_stat_samples[m_n_thres-1][1] = 1.0;

}

void Samples::FillData(InfoIn& infoin, Grid& grid, Tools& tools)
{
	Double2Vec distanceToMultigrid(infoin.m_ngrid);
	tools.m_sample_multigrid.resize(infoin.m_ngrid);
	tools.m_sample_index_multigrid.resize(infoin.m_ngrid);
	for (int ngr = 1; ngr < infoin.m_ngrid; ngr++)
	{
		int grid_step = (int)std::pow(2.0, ngr);
		int nx_mg = (int)ceil(infoin.m_grid_info.m_nx / grid_step) + 2;
		int ny_mg = (int)ceil(infoin.m_grid_info.m_ny / grid_step) + 2;
		int nz_mg = (int)ceil(infoin.m_grid_info.m_nz / grid_step) + 2;
		int nv_mg = nx_mg*ny_mg;
		if (infoin.m_dimension == 3)
			nv_mg *= nz_mg;
		distanceToMultigrid[ngr].resize(nv_mg, 1e+20);
		tools.m_sample_multigrid[ngr].resize(nv_mg, gNullvalue);
		tools.m_sample_index_multigrid[ngr].resize(nv_mg, -1);
	}
	// Initialize the vector of points to be simulated.
	Int1Vec unsamples;
	unsamples.resize(infoin.m_grid_info.m_nv,0);
	tools.m_sample_indicator_orig.resize(infoin.m_grid_info.m_nv,0);
	tools.m_path_pass.resize(infoin.m_grid_info.m_nv,0);
	tools.m_null_vec.resize(infoin.m_grid_info.m_nv,0);
	double min=1e+10,max=-1e+10;
	//If it is SGEMS file
	Double2Vec values(infoin.m_grid_info.m_nv, Double1Vec(infoin.m_dimension+1,gNullvalue)) ;
	bool isgesm=HosDataManager::Read_SGEMS( infoin.m_files[0],values,infoin);
	if(isgesm)
	{
		int n_data=0;
		m_samples_info.m_mean =0;
		for (int k=0;k<values.size();k++)
		{
			if(values[k][infoin.m_dimension]!=gNullvalue)
			{
				if(values[k][infoin.m_dimension]<min) min =values[k][infoin.m_dimension];
				if(values[k][infoin.m_dimension]>max) max =values[k][infoin.m_dimension];
				n_data++;
				m_samples_info.m_mean +=values[k][infoin.m_dimension];
				
				tools.m_samples_orig.push_back(k);
				tools.m_sample_indicator_orig[k]= (int) tools.m_samples_orig.size();
				unsamples[k]=-1;    
				for(int r=0;r<infoin.m_n_realization;r++)
					grid.m_data[k][infoin.m_dimension+r]=values[k][infoin.m_dimension];
				
			}
			
		}
		m_samples_info.m_mean/=n_data;
		m_samples_info.m_n_samples=n_data;
		m_data.resize(n_data);
		int cnt=0;
		for (int k=0;k<values.size();k++)
		{
			if(values[k][infoin.m_dimension]!=gNullvalue)
			{
				m_data[cnt]=values[k];
				tools.FillMultigrid(infoin, m_data[cnt], distanceToMultigrid, k);
				cnt++;
			}
		}
	}
	else
	// open data file
	{	const char* para_file = infoin.m_files[0].c_str();
		ifstream f_parafile(para_file);
		if(!f_parafile)
		{
			std::cout << "ERROR: The samples data file does not exist. Please check the input parameters file."<<endl;
			system("pause");
			exit (0);
		}
		assert(f_parafile);  

		std::string data_name,tmp_name, tv;

		int nvar;
		f_parafile >> m_samples_info.m_n_samples;

		f_parafile >> nvar;
		for(int i=0;i<nvar;i++)
			f_parafile >> tv;

		int ns = m_samples_info.m_n_samples;

		m_data.resize(ns);
		for(int i=0;i<ns;i++)
			m_data[i].resize(infoin.m_dimension+1,0.0);

		m_data.resize(ns);
	
		m_samples_info.m_mean=0.0;

	
		int n_data=ns;
		for(int i=0;i<n_data;i++)
		{
			int gp=0;
			m_data[i].resize(infoin.m_dimension+1);
			if(infoin.m_dimension==1)
			{
				f_parafile >> m_data[i][0] >> m_data[i][1];
				//------------ Ilnur. 18.09.2014. Skiping the data if it is outside of grid
				if (gp<0||gp>infoin.m_grid_info.m_nv-1||m_data[i][infoin.m_dimension]==gNullvalue)
				{

					i--;n_data--;
					continue;
				} 
				//------------
				// update the grid points.
				gp = (int)alglib::round ((m_data[i][0]-infoin.m_grid_info.m_x0)/infoin.m_grid_info.m_dx);
				if(m_data[i][infoin.m_dimension]<min) min = m_data[i][infoin.m_dimension];
				if(m_data[i][infoin.m_dimension]>max) max = m_data[i][infoin.m_dimension];
				m_samples_info.m_mean =  m_samples_info.m_mean + m_data[i][infoin.m_dimension];
			}
			else if(infoin.m_dimension==2)
			{
				f_parafile >> m_data[i][0] >> m_data[i][1] >> m_data[i][2];
				// update the grid points.
				int n1 = (int)alglib::round ((m_data[i][0] -infoin.m_grid_info.m_x0)/infoin.m_grid_info.m_dx);
				int n2 =(int)alglib::round((m_data[i][1] -infoin.m_grid_info.m_y0)/infoin.m_grid_info.m_dy);
				//------------ Ilnur. 18.09.2014. Skiping the data if it is outside of grid
				if (n1<0||n1>infoin.m_grid_info.m_nx-1||n2<0||n2>infoin.m_grid_info.m_ny-1||m_data[i][infoin.m_dimension]==gNullvalue)
				{
					i--;n_data--;
					continue;
				} 
				//------------ Ilnur. 18.09.2014. Skiping the data if it is outside of grid
				gp = n1 + n2 * infoin.m_grid_info.m_nx;
				if(m_data[i][infoin.m_dimension]<min) min = m_data[i][infoin.m_dimension];
				if(m_data[i][infoin.m_dimension]>max) max = m_data[i][infoin.m_dimension];
				m_samples_info.m_mean =  m_samples_info.m_mean + m_data[i][infoin.m_dimension];
			}
			else
			{
				f_parafile >> m_data[i][0] >> m_data[i][1] >> m_data[i][2] >> m_data[i][3];;
				int n1 = (int)alglib::round ((m_data[i][0] -infoin.m_grid_info.m_x0)/infoin.m_grid_info.m_dx);
				int n2 = (int)alglib::round ((m_data[i][1] -infoin.m_grid_info.m_y0)/infoin.m_grid_info.m_dy);
				int n3 = (int)alglib::round ((m_data[i][2] -infoin.m_grid_info.m_z0)/infoin.m_grid_info.m_dz);
				//------------ Ilnur. 18.09.2014. Skiping the data if it is outside of grid
				if (n1<0||n1>infoin.m_grid_info.m_nx-1||n2<0||n2>infoin.m_grid_info.m_ny-1||n3<0||n3>infoin.m_grid_info.m_nz-1||m_data[i][infoin.m_dimension]==gNullvalue)
				{
					i--;n_data--;
					continue;
				} 
				//------------ Ilnur. 18.09.2014. Skiping the data if it is outside of grid
				gp = n1 + n2 * infoin.m_grid_info.m_nx + n3 * infoin.m_grid_info.m_nx * infoin.m_grid_info.m_ny; 
				if(m_data[i][infoin.m_dimension]<min) min = m_data[i][infoin.m_dimension];
				if(m_data[i][infoin.m_dimension]>max) max = m_data[i][infoin.m_dimension];
				m_samples_info.m_mean =  m_samples_info.m_mean + m_data[i][infoin.m_dimension];
			}

			m_samples_info.m_mean =  m_samples_info.m_mean/ns;

			for(int r=0;r<infoin.m_n_realization;r++)
				grid.m_data[gp][infoin.m_dimension+r]=m_data[i][infoin.m_dimension];

			tools.FillMultigrid(infoin, m_data[i], distanceToMultigrid, gp);
			tools.m_samples_orig.push_back(gp);
			tools.m_sample_indicator_orig[gp]= (int)tools.m_samples_orig.size();
		

			unsamples[gp]=-1;    
		}
		//------------ Ilnur. 18.09.2014. Add warning of skiping the data and resize the data_set
		if(n_data< ns)
			cout << "WARNING: Some data are outside of grid"<<endl;
		ns=n_data;
		m_samples_info.m_n_samples=n_data;
		m_data.resize(ns);
	}
	//------------
	infoin.m_unsamples_orig = tools.UpdateUnsamples(infoin, grid.m_data, grid.m_multigrid_index, unsamples);

	tools.m_loc_val = infoin.m_dimension;

	m_samples_info.m_min=min;
	m_samples_info.m_max=max;
	

	unsamples.clear();
	tools.coeff_1D.resize(infoin.m_order+1);

}
void Tools::FillMultigrid(InfoIn & infoin, Double1Vec & sampl_data, Double2Vec & distanceToMultigrid, int gp)
{

	for (int ngr = 1; ngr <infoin.m_ngrid; ngr++)
	{
		int grid_step = (int)std::pow(2.0, ngr);
		int ixx = (int)alglib::round((sampl_data[0] - infoin.m_grid_info.m_x0) / (grid_step*infoin.m_grid_info.m_dx));
		int jxx = (int)alglib::round((sampl_data[1] - infoin.m_grid_info.m_y0) / (grid_step*infoin.m_grid_info.m_dy));
		int kxx = (int)alglib::round((sampl_data[2] - infoin.m_grid_info.m_z0) / (grid_step*infoin.m_grid_info.m_dz));
		int nx_mg = (int)ceil(infoin.m_grid_info.m_nx / grid_step) + 2;
		int ny_mg = (int)ceil(infoin.m_grid_info.m_ny / grid_step) + 2;
		int nz_mg = (int)ceil(infoin.m_grid_info.m_nz / grid_step) + 2;
		assert(ixx < nx_mg);
		assert(jxx < ny_mg);
		assert(kxx < nz_mg);
		int gp_mgr = ixx + jxx * nx_mg;
		double dxx = (sampl_data[0] - infoin.m_grid_info.m_x0 - ixx*grid_step) / (grid_step*infoin.m_grid_info.m_dx);
		double dyy = (sampl_data[1] - infoin.m_grid_info.m_y0 - jxx*grid_step) / (grid_step*infoin.m_grid_info.m_dy);
		double dzz = (sampl_data[2] - infoin.m_grid_info.m_y0 - kxx*grid_step) / (grid_step*infoin.m_grid_info.m_dz);
		double distt = (dxx*dxx + dyy*dyy);
		if (infoin.m_dimension == 3)
		{
			gp_mgr = kxx * nx_mg * ny_mg;
			distt += dzz*dzz;
		}
		distt = sqrt(distt);
		if (distanceToMultigrid[ngr][gp_mgr] > distt)
		{
			distanceToMultigrid[ngr][gp_mgr] = distt;
			m_sample_multigrid[ngr][gp_mgr] = sampl_data[infoin.m_dimension];
			m_sample_index_multigrid[ngr][gp_mgr] = gp;
		}

	}


}
void Samples::Samples_Create(InfoIn& infoin, Grid& grid, Tools& tools)
{
	cout<<"Data samples reading is started...."<<endl;
	// Fill samples data structure
	FillData(infoin, grid, tools);
	cout<<"Data samples reading is finished...."<<endl;
	
}


void HosDataManager::Intro()
{
	cout << "*********************************************************"<<endl;
	cout << "---------------------------------------------------------"<<endl;
	cout << "HOSIM 3+: A 2D/3D PROGRAM TO SIMULATE COMPLEX GEOLOGICAL "<<endl;
	cout << "          PATTERNS USING HIGH-ORDER SPATIAL CUMULANTS    "<<endl;
	cout << "          WITH ( & SOON WITHOUT) TRAINING IMAGES         "<<endl;
	cout << "---------------------------------------------------------"<<endl;
	cout << "          ILNUR MINNIAKHMETOV HUSSEIN MUSTAPHA           "<<endl;
	cout << "    MODELLING & SIMULATION: SOFTWARE ENGINEERING         "<<endl;
	cout << "---------------------------------------------------------"<<endl;
	cout << "---------------     VERSION 2.0   -----------------------"<<endl;
	cout << "---------------     SEP. 30 2015  -----------------------"<<endl;
	cout << "*********************************************************"<<endl;
}

InfoExt HosDataManager::ReadArgs(int argc,char *argv[])
{
	InfoExt infoext;

	int i=1;
	bool isgood=true;

	for (i=1;i<argc;i+=2)
	{	

		if(strcmp(argv[i],"-h")==0)
		{
			cout<<"Working format:\thosim.exe [-option] [value]"<<endl;
			cout<<"Example:\thosim.exe -p parameters.par"<<endl;
			cout<<"This runs HOSIM with parameter file \"parameters.par\""<<endl;
			cout<<"Options:"<<endl;
			cout<<"\t\t\t-d:\tConditioning data file name"<<endl;
			cout<<"\t\t\t-h:\thelp"<<endl;
			cout<<"\t\t\t-mxn:\tMaximum number of neighbors"<<endl;
			cout<<"\t\t\t-mnn:\tMinimum number of neighbors"<<endl;
			cout<<"\t\t\t-o:\tMaximum order of moments"<<endl;
			cout<<"\t\t\t-of:\tOutput file name"<<endl;			
			cout<<"\t\t\t-p:\tParameter file name"<<endl;
			cout<<"\t\t\t-r:\tNumber of replicates used for extimation of moments"<<endl;
			cout<<"\t\t\t-tsX:\tSize of template X"<<endl;
			cout<<"\t\t\t-tsY:\tSize of template Y"<<endl;
			cout<<"\t\t\t-tsZ:\tSize of template Z"<<endl;
			cout<<"\t\t\t-sZ:\tSeed number"<<endl;
			i--;
		}else
		{
			if (i+1>=argc)
			{
				isgood=false;
				break;
			}
			std::stringstream ss;
			int val_int=-1;
			ss<<argv[i+1];
			stringstream(argv[i+1])>>val_int; 
			if(strcmp(argv[i],"-p")==0)
			{

				ss>>infoext.par_file_name;
				cout<<"Parameter file name:\t"<<infoext.par_file_name<<endl;
			}else if (strcmp(argv[i],"-d")==0)
			{
				ss>>infoext.files[0];
				cout<<"Conditioning data file name:\t"<<infoext.files[0]<<endl;
			}
			else if (strcmp(argv[i],"-of")==0)
			{
				ss>>infoext.files[2];
				cout<<"Output file name:\t"<<infoext.files[2]<<endl;
			}else if (strcmp(argv[i],"-r")==0)
			{
				if (val_int<1)
					isgood=false;
				infoext.m_nb_locations_stats=val_int;
				cout<<"Number of replicates:\t"<<val_int<<endl;
			}else if (strcmp(argv[i],"-mxn")==0)
			{
				if (val_int<1)
					isgood=false;
				infoext.m_mx_s=val_int;
				cout<<"Maximum number of neighbors:\t"<<val_int<<endl;	

			}else if (strcmp(argv[i],"-mnn")==0)
			{
				if (val_int<1)
					isgood=false;
				infoext.m_mn_s=val_int;
				cout<<"Minimum number of neighbors:\t"<<val_int<<endl;	

			}else if(strcmp(argv[i],"-s")==0)
			{
				if (val_int<1)
					isgood=false;
				infoext.m_seed=val_int;
				cout<<"Seed is :\t"<<val_int<<endl;	
			}else if(strcmp(argv[i],"-o")==0)
			{
				if (val_int<1)
					isgood=false;
				infoext.m_order=val_int;
				cout<<"Maximum order of moments:\t"<<val_int<<endl;	
			}else if(strcmp(argv[i],"-tsX")==0)
			{
				if (val_int<1)
					isgood=false;
				infoext.m_template_info.m_nx=val_int;
				cout<<"Size of template X:\t"<<val_int<<endl;	
			}else if(strcmp(argv[i],"-tsY")==0)
			{
				if (val_int<1)
					isgood=false;
				infoext.m_template_info.m_ny=val_int;
				cout<<"Size of template Y:\t"<<val_int<<endl;	
			}else if(strcmp(argv[i],"-tsZ")==0)
			{
				if (val_int<1)
					isgood=false;
				infoext.m_template_info.m_nz=val_int;
				cout<<"Size of template Z:\t"<<val_int<<endl;	
			}
			else if (strcmp(argv[i], "-m") == 0)
			{
				if (val_int < 1)
					isgood = false;
				infoext.m_mdiv_max = val_int;
				cout << "Maximum number of intervals:\t" << val_int << endl;
			}
			else 
			{
				isgood=false;
			}
		}
		if(!isgood)
			break;

	}
	if (i<argc)
	{
		cout<<"Reading arguments is failed on argument "<<argv[i-1]<<endl;
		cout<<"Standard running..."<<endl;
		InfoExt infoext_null;
		return infoext_null;
	}
	return  infoext;
}

void HosDataManager::ReadAll(InfoIn& infoin,InfoExt& infoext) 
{
	int dimension, method, nbr, nbg, seed, nxd, nyd, nzd,nxt, nyt, nzt,nxtl, nytl, nztl, mx_s, mn_s, order, verbosity,n_max_levels_x,n_max_levels_y,n_max_levels_z,nb_locations_stats;
	double x0d, y0d, z0d, dxd, dyd, dzd, x0t, y0t, z0t, dxt, dyt, dzt, dxtl, dytl, dztl;

	bool dynamic_template;

	infoin.m_mdiv_max = infoext.m_mdiv_max;
	if (infoin.m_mdiv_max < 10 || infoin.m_mdiv_max>100)
		infoin.m_mdiv_max = 31;
	std::string pf;
	std::string s;
	std::stringstream ss;
	std::cout << "============================================"<<endl;
	std::cout << "GIVE THE NAME OF THE INPUT PARAMETERS FILE: "<<endl;
	if (infoext.par_file_name.length()<1)
		std::cin  >> pf;
	else
	{
		std::stringstream ss2;
		ss2<<infoext.par_file_name;
		ss2>>pf ;

	}
	const char* para_file = pf.c_str();

	ifstream f_parafile(para_file);

	if(!f_parafile)
	{
		std::cout << "ERROR: The input parameters file does not exist. Please check the name again"<<endl;
		system("pause");
		exit (0);
	}
	assert(f_parafile);  

	std::cout << "!-----------------------------------------------!"<<endl;
	std::cout << "!           READ THE PARAMETERS FILE            !"<<endl;
	std::cout << "!-----------------------------------------------!"<<endl;
	std::cout << "*************************************************"<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>>dimension;
	ss.str("");
	cout << "o- Dimension of the problem (2,3)  : "<<dimension<<endl;
	if(dimension <1 || dimension > 3){
		cout << "o- Check the dimension             : "<<dimension<<endl;
		system("pause");	
	}
	getline(f_parafile,s);
	ss<<s;
	ss>>nbr;
	ss.str("");
	cout << "o- Number of realizations          : "<<nbr<<endl;
	if(nbr<1) {
		cout << "o- Check the number of realizations: "<<nbr<<endl;
		system("pause");	
	}
	getline(f_parafile,s);
	ss<<s;
	ss>>nbg;
	ss.str("");
	cout << "o- Number of multi-grid            : "<<nbg<<endl;
	if(nbg<1) {
		cout << "o- Check the number of multi-grid  : "<<nbg<<endl;
		system("pause");	
	}

	getline(f_parafile,s);
	ss<<s;
	ss>>method;
	ss.str("");

	infoin.m_method=method;
	if(method==1)
	{
		cout << "o- TI-driven Simulation with Legendre polynomials             : "<<method<<endl;
		//ILNUR
		//cout << "o- Please use a different METHOD   : "<<method<<endl;
		//system("pause");
		//exit (0);
	}
	else if(method==2)
	{
		cout << "o- DATA-driven Simulation with Legendre         : "<<method<<endl;
	}
	else if(method==3)
	{
		cout << "o- TI-driven Simulation with Splines         : "<<method<<endl;
	}
	else if(method==4)
	{
		cout << "o- DATA-driven Simulation with Splines         : "<<method<<endl;
	}
	else
	{
		std::cout << "ERROR: Check the simulation method. Should be from 1 to 4"<<endl;
		system("pause");
		exit (0);

		//ILNUR//system("pause");	
	}

	getline(f_parafile,s);
	ss<<s;
	ss>>seed;
	ss.str("");
	//std::stringstream(s.c_str()) >> seed;
	// Validation
	if (infoext.m_seed!=-999)
		seed=infoext.m_seed;
	if(seed<0)
	{
		std::cout << "ERROR: The seed number is negative. Please specify a positive number."<<endl;
		system("pause");
		exit (0);
	}
	cout << "o- The seed                        : "<<seed<<endl;
	getline(f_parafile,s);

	String1Vec files;	  
	files.resize(5);
	for(size_t i=0;i<4;i++)
	{
		getline(f_parafile,s);
		stringstream ss1;
		int rank = 0;
#ifdef USE_MPI
		MPI_Comm_rank(MPI_COMM_WORLD, &rank);

		if (i == 0 || i == 2)
		{

			ss1 << "seed_" << rank;

		}
#endif

		ss1<<s;
		ss1>>files[i];
		//ss.str(std::string());
		//std::stringstream(s) >> files[i];
		if (infoext.files[i].length()>0)
		{
			std::stringstream ss2;
			ss2<<infoext.files[i];
			ss2>>files[i];
			//ss2.str(std::string());
			//std::stringstream(infoext.files[i]) >> files[i];

		}
		if(i==0)
			cout << "o- Data file                       : "<<files[0]<<endl;
		else if(i==1)
			cout << "o- Training Image file             : "<<files[1]<<endl;
		else if(i==2)
			cout << "o- Output file                     : "<<files[2]<<endl;
		else if(i==3)
			cout << "o- Debugging file                  : "<<files[3]<<endl;
	}
	getline(f_parafile,s);
	std::cout << "-------------------------------------------------"<<endl;
	cout << "o- Grid info                         "<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>>x0d >> nxd >> dxd;
	ss.str("");
	//std::stringstream(s) >> x0d >> nxd >> dxd;
	// Validation
	if(nxd<=0 || dxd <=0)
	{
		std::cout << "ERROR: The grid data in the X axis needs to be checked."<<endl;
		system("pause");
		exit (0);
	}
	cout << "   #- xmin,nx,dx                   : "<<x0d<<" "<<nxd<<" "<<dxd<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>> y0d >> nyd >> dyd;
	ss.str("");
	if(dimension<2)
	{
		y0d=0;nyd=1;dyd=1;
	}

	//std::stringstream(s) >> y0d >> nyd >> dyd;
	// Validation
	if(nyd<=0 || dyd <=0)
	{
		std::cout << "ERROR: The grid data in the Y axis needs to be checked."<<endl;
		system("pause");
		exit (0);
	}
	cout << "   #- ymin,ny,dy                   : "<<y0d<<" "<<nyd<<" "<<dyd<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>> z0d >> nzd >> dzd;
	ss.str("");
	if(dimension<3)
	{
		z0d=0;nzd=1;dzd=1;
	}
	//std::stringstream(s) >> z0d >> nzd >> dzd;
	// Validation
	if( nzd<=0 || dzd <=0)
	{
		std::cout << "ERROR: The grid data in the Z axis needs to be checked."<<endl;
		system("pause");
		exit (0);
	}
	cout << "   #- zmin,nz,dz                   : "<<z0d<<" "<<nzd<<" "<<dzd<<endl;
	std::cout << "-------------------------------------------------"<<endl;

	getline(f_parafile,s);
	string mask_grid;
	ss<<s;
	ss>>files[4];
	ss.str("");
	if(files[4].compare("0")==0)
	{
		files[4]="";
		cout << "o- Mask grid is not given. All nodes is going to be simulated"<<endl;

	}else
		cout << "o- Mask grid:                       : "<<files[4]<<endl;


	getline(f_parafile,s);
	cout << "o- Training image info               "<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>> x0t >> nxt >> dxt;
	ss.str("");
	//std::stringstream(s) >> x0t >> nxt >> dxt;
	// Validation
	if( nxt<=0 || dxt <=0)
	{
		std::cout << "ERROR: The training image data in the X axis needs to be checked."<<endl;
		system("pause");
		exit (0);
	}
	cout << "   #- xmin,nx,dx                   : "<<x0t<<" "<<nxt<<" "<<dxt<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>>y0t >> nyt >> dyt;
	ss.str("");
	if(dimension<2)
	{
		y0t=0;nyt=1;dyt=1;
	}
	//std::stringstream(s) >> y0t >> nyt >> dyt;
	// Validation
	if(nyt<=0 || dyt <=0)
	{
		std::cout << "ERROR: The training image data in the Y axis needs to be checked."<<endl;
		system("pause");
		exit (0);
	}
	cout << "   #- ymin,ny,dy                   : "<<y0t<<" "<<nyt<<" "<<dyt<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>>z0t >> nzt >> dzt;
	ss.str("");
	//std::stringstream(s) >> z0t >> nzt >> dzt;
	if(dimension<3)
	{
		z0t=0;nzt=1;dzt=1;
	}
	// Validation
	if(nzt<=0 || dzt <=0)
	{
		std::cout << "ERROR: The training image data in the Z axis needs to be checked."<<endl;
		system("pause");
		exit (0);
	}

	/*if(x0d != x0t || y0d != y0t || z0d != z0t ||
	nxd != nxt || nyd != nyt || nzd != nzt ||
	dxd != dxt || dyd != dyt || dzd != dzt )
	{
	std::cout << "ERROR: The grid and training image dimensions should be the same, please double check."<<endl;
	system("pause");
	exit (0);
	}*/

	cout << "   #- zmin,nz,dz                   : "<<z0t<<" "<<nzt<<" "<<dzt<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>>nb_locations_stats;
	ss.str("");
	//	std::stringstream(s) >> nb_locations_stats;
	if (infoext.m_nb_locations_stats!=-999)
		nb_locations_stats=infoext.m_nb_locations_stats;
	// Validation
	if(nb_locations_stats<=0)
	{
		std::cout << "ERROR: The number of random locations used to estimate statistics from a training image should be a positive number."<<endl;
		system("pause");
		exit (0);
	}
	cout << "   #- Nb loc. from TI for stats    : "<<nb_locations_stats<<endl;
	std::cout << "-------------------------------------------------"<<endl;
	getline(f_parafile,s);
	cout << "o- Local searching neighbors info   "<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>>nxtl >> nytl >> nztl;
	ss.str("");
	if(dimension<2)
	{
		nytl=1;
	}
	if(dimension<3)
	{
		nztl=1;
	}
	//std::stringstream(s) >> nxtl >> nytl >> nztl;
	// Validation
	if (infoext.m_template_info.m_nx!=-999)
		nxtl=infoext.m_template_info.m_nx;
	if (infoext.m_template_info.m_ny!=-999)
		nytl=infoext.m_template_info.m_ny;
	if (infoext.m_template_info.m_nz!=-999)
		nztl=infoext.m_template_info.m_nz;
	if(nxtl<=0 || nytl<=0 || nztl <=0)
	{
		std::cout << "ERROR: The dimensions of the template search need to be checked. "<<endl;
		system("pause");
		exit (0);
	}
	cout << "   #- nx, ny, nz                   : "<<nxtl<<" "<<nytl<<" "<<nztl<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>> dxtl >> dytl >> dztl;
	ss.str("");

	if(dimension<2)
	{
		dytl=1;
	}
	if(dimension<3)
	{
		dztl=1;
	}
	//std::stringstream(s) >> dxtl >> dytl >> dztl;
	// Validation
	if(dxtl<=0 || dytl<=0 || dztl <=0)
	{
		std::cout << "ERROR: The steps of the template search along X, Y and Z need to be checked. "<<endl;
		system("pause");
		exit (0);
	}
	cout << "   #- dx, dy, dz                   : "<<dxtl<<" "<<dytl<<" "<<dztl<<endl;
	cout << "o- Dynamic search: 0 NO, 1 YES      "<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>> dynamic_template;
	ss.str("");

	//std::stringstream(s) >> dynamic_template;
	// Validation
	if((int) dynamic_template<0)
	{
		std::cout << "ERROR: The dynamic search template option should be 0 or 1. Please check."<<endl;
		system("pause");
		exit (0);
	}
	cout << "   #- dynamic_template             : "<<dynamic_template<<endl;
	getline(f_parafile,s);
	ss<<s;
	ss>> n_max_levels_x >> n_max_levels_y >> n_max_levels_z;
	ss.str("");
	//std::stringstream(s) >> n_max_levels_x >> n_max_levels_y >> n_max_levels_z;
	// Validation
	if(n_max_levels_x<0 || n_max_levels_y<0 || n_max_levels_z<0)
	{
		std::cout << "ERROR: The expanding of the dynamic search template along X, Y and Z needs to be checked. No negative expansion is allowed."<<endl;
		system("pause");
		exit (0);
	}
	cout << "   #- max number of extra levels   : "<<n_max_levels_x<<" "<<n_max_levels_y<<" "<<n_max_levels_z<<endl;
	if(n_max_levels_x == 0 && n_max_levels_y == 0 && n_max_levels_z ==0)
		dynamic_template = false;
	std::cout << "-------------------------------------------------"<<endl;
	getline(f_parafile,s);
	getline(f_parafile,s);
	ss<<s;
	ss>> mn_s >> mx_s;
	ss.str("");
	//	std::stringstream(s) >> mn_s >> mx_s;
	if (infoext.m_mx_s!=-999)
		mx_s=infoext.m_mx_s;

	if (infoext.m_mn_s!=-999)
		mn_s=infoext.m_mn_s;
	// Validation
	if(mn_s>mx_s || mn_s<0 || mx_s <=0)
	{
		std::cout << "ERROR: The min and max number of samples need to be checked. max > min >=0."<<endl;
		system("pause");
		exit (0);
	}
	cout << "o- Min and max number of samples   : "<<mn_s<<" "<<mx_s<<endl;
	int indedx_cat=-1;
	getline(f_parafile,s);
	ss<<s;
	ss>> order;//For future index for categorical variable>>indedx_cat;
	ss.str("");
	infoin.m_index_cat=indedx_cat;
	//std::stringstream(s) >> order;
	if (infoext.m_order!=-999)
		order=infoext.m_order;
	cout << "o- Order of approximation          : "<<order<<endl;
	std::cout << "-------------------------------------------------"<<endl;
	std::cout << "-------------------------------------------------"<<endl;
	std::cout << "*************************************************"<<endl;
	getline(f_parafile,s);
	getline(f_parafile,s);
	ss<<s;
	ss>> verbosity;
	ss.str("");
	//std::stringstream(s) >> verbosity;
	// Validation
	/*if(verbosity<0)
	{
		std::cout << "ERROR: The verbosity level needs to be checked. Valid values >=0."<<endl;
		system("pause");
		exit (0);
	}*/
	cout << "o- Verbosity level for debugging   : "<<verbosity<<endl;

	infoin.ReadExtInfo(dimension, method,
		x0d, y0d, z0d, nxd, nyd, nzd, dxd, dyd, dzd, 
		x0t, y0t, z0t, nxt, nyt, nzt, dxt, dyt, dzt,
		nxtl, nytl, nztl, dxtl, dytl, dztl,
		verbosity, seed, nbr, nbg, dynamic_template,n_max_levels_x,n_max_levels_y,n_max_levels_z, nb_locations_stats, mx_s, mn_s, order, files);
}

void Stats::Stats_Create(InfoIn& infoin, TrainingImage& ti, Tools& tools)
{
	// create the list of points
	//m_list_point_stats.resize(infoin.m_nb_locations_stats);
	//ILNUR.lets have all replicates, but use only n_repl.
	m_list_point_stats.reserve(infoin.m_ti_info.m_nv);
	//ILNUR.Dump for data
	if((infoin.m_method-1)%2>0)
	{
		tools.Repl_Dump.Init((int)tools.m_samples_orig.size(),infoin.m_dimension,gNullvalue);

	}
	// create the list of replicates
	//m_all_value_replicates.resize(infoin.m_nb_locations_stats+1); // +1 is the data template.
	// Allocation of each replicate should be done during simulation.
	// Fill in m_list_point_stats : m_nb_locations_stats locations generated randomly in the training image.
	int nt = (int)ti.m_data.size();

	int ncp = 0;




	int nxx=max(1,infoin.m_ti_info.m_nx);
	int nyy=max(1,infoin.m_ti_info.m_ny);
	int nzz=max(1,infoin.m_ti_info.m_nz);
	if (infoin.m_dimension<3)
		nzz=1;
	int cnt=0;
	m_rand_path_num.reserve(nzz+1);
	m_rand_path_num.push_back(0);
	int i0 = (infoin.m_template_info.m_nx-1)/2;
	int j0 = (infoin.m_template_info.m_ny - 1) / 2;
	int k0 = (infoin.m_template_info.m_nz - 1) / 2;
	i0 = 0;
	j0 = 0;
	k0 = 0;
	for(int k=k0;k<nzz-k0;k++)
	{
		for(int j=j0;j<nyy-j0;j++)
			for(int i=i0;i<nxx-i0;i++)
			{
				int gp = i + j*infoin.m_ti_info.m_nx+k*infoin.m_ti_info.m_nx*infoin.m_ti_info.m_ny;
				if(ti.m_data[gp][infoin.m_dimension]!=gNullvalue)
					m_list_point_stats.push_back(gp);
				
			}
			m_rand_path_num.push_back((int)m_list_point_stats.size())	;
	}
	
	/*
	while(ncp < infoinm_list_point_stats.m_nb_locations_stats)
	{
	int np = tools.rand_int(nt);
	m_list_point_stats[ncp] = tmp[np];
	tmp.erase(tmp.begin() + np);
	ncp++;
	nt--;
	}
	tmp.clear();*/
	//ILNUR.Lets do shuffle
	
	//cout<<"repl total "<<m_list_point_stats.size()<<endl;
	for(int k=0;k<m_rand_path_num.size()-1;k++)
		random_shuffle(m_list_point_stats.begin()+m_rand_path_num[k],m_list_point_stats.begin()+m_rand_path_num[k+1], myrandom);

/*	for(int k=0;k<10;k++)
	{
		cout<<m_list_point_stats[k]<<endl;
		cout<<m_list_point_stats[m_list_point_stats.size()-1-k]<<endl;
	}*/
	//ILNUR. continious replicates
	/*infoin.Get_Search_Template();
	int x0=infoin.m_ti_info.m_nx/2;
	int y0=infoin.m_ti_info.m_ny/2;
	int z0=infoin.m_ti_info.m_nz/2;
	int cnt=0;
	for (int i=0;i<infoin.search_template.size();i++)
	{
	int x=x0+infoin.search_template[i][0];
	int y=y0+infoin.search_template[i][1];

	if(x>=0&&x<infoin.m_ti_info.m_nx&&y>=0&&y<infoin.m_ti_info.m_ny)
	{
	int gp=x + y * infoin.m_ti_info.m_nx;

	if(infoin.m_dimension==3)
	{
	int z=z0+infoin.search_template[i][2];
	if(z>=0&&z<infoin.m_ti_info.m_nz)
	gp+=z * infoin.m_ti_info.m_nx * infoin.m_ti_info.m_ny;
	else
	continue;
	}
	tmp[cnt]=gp;
	cnt++;
	}
	}*/

	//ILNUR.for testing
	/*for (int i=0;i<tools.m_samples_orig.size();i++)
	m_list_point_stats[i]=tools.m_samples_orig[i];*/
	int m_size= (int)m_list_point_stats.size();
	if (localStationarity)
		m_size = min(m_size, infoin.m_nb_locations_stats);
	int neib_max=infoin.m_mx_s;
	tools.m_all_value_replicates.m_size=m_size;
	tools.m_all_value_replicates.m_shift=neib_max+1;
	tools.m_all_value_replicates.m_values.resize(m_size*tools.m_all_value_replicates.m_shift);
	tools.m_all_value_replicates.m_indexes.resize(m_size*tools.m_all_value_replicates.m_shift);

	if((infoin.m_method-1)%2>0)
	{
		tools.m_data_value_replicates.m_size=m_size;
		tools.m_data_value_replicates.m_shift=neib_max+1;
		tools.m_data_value_replicates.m_values.resize(m_size* tools.m_data_value_replicates.m_shift);
		tools.m_data_value_replicates.m_indexes.resize(m_size* tools.m_data_value_replicates.m_shift);

	}

}
void HosDataManager::CreateAll(InfoIn& infoin, Grid& grid,TrainingImage& ti, Samples& samples, Tools& tools, Stats& stats)
{
	int rank = 0;

#ifdef USE_MPI
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	cout << "rank=" << rank << endl;
#endif
	int seedno = infoin.m_seed + rank;
	srand(seedno);
	drand_path = new MTRand(seedno);;
	drand_val = new MTRand(seedno);;
	grid.Grid_Create(infoin);
	samples.Samples_Create(infoin, grid, tools);
	ti.TrainingImage_Create(infoin, samples);
	stats.Stats_Create(infoin, ti, tools);


}

void HosDataManager::WriteTiming(InfoIn& infoin,Tools &tools,double diff)
{
	std::string out_name=HosDataManager::GetBaseFilename(infoin.m_files[0]);
	out_name.append("_timing.txt");
	ofstream fout(out_name.c_str(), std::ios_base::app);
	fout.precision(5); 
	fout<<infoin.m_nb_locations_stats<<"\t";
	fout<<infoin.m_mx_s<<"\t";
	fout<<infoin.m_order<<"\t";
	fout<<diff<<endl;
	fout.close();
}
double HosDataManager::FillUnsampled(InfoIn& infoin, Grid& grid, TrainingImage& ti, Samples& samples, Tools& tools, Stats& stats)
{

	std::vector<int> indexv = grid.Find_Samples_Within_Template(infoin, ti, samples, tools);
	grid.AssignNeigbors( tools, indexv);//infoin, samples,
	return tools.Weight_Average(infoin,grid,ti,samples,tools,stats);
}
void HosDataManager::FillUnsampledAll(InfoIn& infoin, Grid& grid, TrainingImage& ti, Samples& samples, Tools& tools, Stats& stats)
{
	for (int i=0;i<infoin.m_unsamples_orig.size();i++)
		for (int j=0;j<infoin.m_unsamples_orig[i].size();j++)
		{
			int ind=infoin.m_unsamples_orig[i][j];
			for (int nr=0;nr<infoin.m_n_realization;nr++)
			{
				if(grid.m_data[ind][infoin.m_dimension+nr]==gNullvalue)
				{
					tools.m_cp=ind;
					double val=FillUnsampled( infoin,  grid, ti,  samples,  tools,  stats);
					grid.m_data[ind][infoin.m_dimension+nr]=val;
				}
			}
		}

}
double
	HosDataManager::SimulatePoint(InfoIn& infoin, Grid& grid, TrainingImage& ti, Samples& samples, Tools& tools, Stats& stats, int calc_type,Tools& dat_tools)
{
	//if (tools.m_cp==19788)
	//{
	//	cout<<"ff"<<endl;
	////tools.m_cp=1585;
	//}
	//cout<<"current node is"<<tools.m_cp<<endl;
#if defined(SHOW_DBG)
	LARGE_INTEGER t1, t2;           // ticks
	double elapsedTime;
	LARGE_INTEGER frequency;        // ticks per second
	// get ticks per second
	QueryPerformanceFrequency(&frequency);

	// start timer
	QueryPerformanceCounter(&t1);
#endif
	time_t start_real,end_real;
	clock_t t_elp;
	t_elp=clock();
	time (&start_real);
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	//	QueryPerformanceCounter(&t1);
	//cout<<"Find neigb before \t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;

#endif
	//tools.m_cp=52;
	//Find Neighbors and Replicates from TI
	grid.FindSamplesNeighbor(infoin, ti, samples,tools,stats,calc_type);
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	QueryPerformanceCounter(&t1);
	//cout<<"Find neigb \t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;
	if(tools.m_neighbors_info.m_n_local_samples==infoin.m_mx_s)
		find_nneib_all +=elapsedTime;
#endif
	/* OLD
	//converitng vals
	for (int i=0;i<tools.m_all_value_replicates.size();i++)
	for (int j=0;j<tools.m_all_value_replicates[i].m_value_replicate.size();j++)
	if(tools.m_all_value_replicates[i].m_value_replicate[j]!=-999)
	tools.m_all_value_replicates[i].m_value_replicate[j]=tools.ConvertVal(tools.m_all_value_replicates[i].m_value_replicate[j]);
	//

	if(calc_type>1)
	{
	for (int i=0;i<tools.m_data_value_replicates.size();i++)
	for (int j=0;j<tools.m_data_value_replicates[i].m_value_replicate.size();j++)
	if(tools.m_data_value_replicates[i].m_value_replicate[j]!=-999)
	tools.m_data_value_replicates[i].m_value_replicate[j]=tools.ConvertVal(tools.m_data_value_replicates[i].m_value_replicate[j]);

	}
	*/
	//converitng vals
	for (int i=0;i<tools.m_all_value_replicates.m_size;i++)
	{
		int repl_base=i*tools.m_all_value_replicates.m_shift;
		for (int j=0;j<tools.m_all_value_replicates.m_neib_num;j++)
			if(tools.m_all_value_replicates.m_values[repl_base+j]!=gNullvalue)
				tools.m_all_value_replicates.m_values[repl_base+j]=tools.ConvertVal(tools.m_all_value_replicates.m_values[repl_base+j],tools.m_all_value_replicates.m_indexes[repl_base+j]);

	}
	//

	if(calc_type>1)
	{

		for (int i=0;i<tools.m_data_value_replicates.m_size;i++)
		{
			int repl_base=i*tools.m_data_value_replicates.m_shift;
			for (int j=0;j<tools.m_data_value_replicates.m_neib_num;j++)
				if(tools.m_data_value_replicates.m_values[repl_base+j]!=gNullvalue)
					tools.m_data_value_replicates.m_values[repl_base+j]=tools.ConvertVal(tools.m_data_value_replicates.m_values[repl_base+j],tools.m_data_value_replicates.m_indexes[repl_base+j]);

		}


	}
	//
#if defined(SHOW_DBG)
	QueryPerformanceCounter(&t2);
	elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
	QueryPerformanceCounter(&t1);
	//cout<<"ConvertVal \t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;
	if(tools.m_neighbors_info.m_n_local_samples==infoin.m_mx_s)
		convertval +=elapsedTime;
#endif
	//	cout<<"time_neighb "<<clock()-t_elp<<endl;
	t_elp=clock();
	// STOP THE TIMER
	time (&end_real);
	/*THE TOTAL RUN TIME*/
	tools.m_dif_1 += difftime (end_real,start_real);	

	//cout<<"FindSamplesNeighbor\t"<<clock()-t_elp<<endl;
	infoin.m_hard_point = false;

	// Unconditional simulation: need to be revised ?
	//ILNUR. lets do unconditional if we could not find any points
	//if(samples.m_samples_info.m_n_samples==0 && tools.m_neighbors_info.m_n_local_samples == 0)
	if(tools.m_neighbors_info.m_n_local_samples == 0)
	{
		int dim = (int)ti.m_data.size();
		int ri = tools.rand_int(dim);
		return(ti.m_data[ri][infoin.m_dimension]);
	}

	// Then conditional if enough samples are in the area ?
	if(tools.m_neighbors_info.m_n_local_samples > 0 && tools.m_neighbors_info.m_n_local_samples < infoin.m_mn_s &&  !infoin.m_dynamic_template)
	{
		/*double val = 0.0, dtot=0.0;
		for (int k=0;k<tools.m_neighbors_info.m_n_local_samples;k++)
		{
		val = val + grid.m_data[tools.m_neighbors_info.m_samples_neighbor[k]][infoin.m_dimension] / tools.m_neighbors_info.m_samples_distance[k];
		dtot = dtot + 1.0 / tools.m_neighbors_info.m_samples_distance[k];
		}
		m_curr_hard_psim++;
		infoin.m_hard_point = true;
		return(val/dtot);*/
		if(tools.m_neighbors_info.m_n_local_samples<infoin.m_mx_s_cur)
		{
			//	m_curr_hard_psim++;
			return (gNullvalue);           
		}
		else
		{
			// push it to the next grid level
			if(tools.m_grid_curr > 0)
			{
				return (-1099); 
			}
			else
			{
				//cout<<tools.m_path_pass[tools.m_cp]<<endl;
				infoin.m_hard_point = true;
				infoin.m_n_hard_point++;
				return gNullvalue;
				return tools.Weight_Average(infoin,grid,ti,samples,tools,stats);
			}
		}
	}     
	else
		if(tools.m_neighbors_info.m_n_local_samples == 0)
		{
			if(tools.m_path_pass[tools.m_cp] < 5)
			{
				//				m_curr_hard_psim++;
				return (-199);           
			}
			else
			{
				// push it to the next grid level
				if(tools.m_grid_curr > 0)
				{
					return (-1099); 
				}
				else
				{
					infoin.m_hard_point = true;
					return (ti.m_data[tools.m_cp][infoin.m_dimension]);
				}
			}
		}

		
		time (&start_real);

		tools.ComputeXCdf(tools.m_neighbors_info.m_local_min, tools.m_neighbors_info.m_local_max);

		// STOP THE TIMER
		time (&end_real);
		/*THE TOTAL RUN TIME*/
		tools.m_dif_3 += difftime (end_real,start_real);	

		if(tools.m_table_cond_legendre.size()==0)
		{
			time (&start_real);
			tools.ComputeLegendreMomentsPdf(infoin);
			// STOP THE TIMER
			time (&end_real);
			/*THE TOTAL RUN TIME*/
			tools.m_dif_4 += difftime (end_real,start_real);	
		}

		time (&start_real);
		//ILNUR testing
		//if ((tools.m_neighbors_info.m_samples_distance.size()<-1)&&(tools.m_neighbors_info.m_samples_distance[0]<4))

		//
		bool good_pdf=false;
		//	cout<<"before go "<<clock()-t_elp<<endl;
		while(tools.m_neighbors_info.m_n_local_samples>=infoin.m_mn_s)
		{
			//	t_elp=clock();
			//int repl_num[]={50, 100 ,500 ,1000, 2000 ,5000, 10000};
			if (test_flag)
			{
				int n_case=1;
				int repl_num[]={10000};//, 10000 ,10000 ,10000, 10000 , 10000};
				Double1Vec val_nneib(tools.m_neighbors_info.m_samples_neighbor.size());
				for (int i = 0; i < val_nneib.size(); i++)
				{
					val_nneib[i] = grid.m_data[-i][2];
					
				}
				Replicates& repls=tools.m_all_value_replicates;
				for(int i=n_case-1;i>=0;i--)
				{/*
				 for (int j=0;j<repls.size();j++)
				 {

				 for(int k=0;k<repls[j].m_value_replicate.size();k++)
				 {

				 double x1=(*drand_val)();
				 double x2=(*drand_val)();

				 double dval=sqrt( - 2* log(x1) )* cos( 2*3.14*x2 );
				 dval=dval/30;
				 if(repls[j].m_value_replicate[k]==-999)
				 break;
				 double valrepl=repls[j].m_value_replicate[k]*(1+dval);
				 valrepl=min(1.0,valrepl);
				 valrepl=max(-1.0,valrepl);
				 tools.m_all_value_replicates[j].m_value_replicate[k]=valrepl;
				 }
				 }*/
					/*		if(i>0)
					for (int j=0;j<val_nneib.size();j++)
					{

					double x1=(*drand_val)();
					double x2=(*drand_val)();

					double dval=sqrt( - 2* log(x1) )* cos( 2*3.14*x2 );
					dval=dval/30;
					cout<<dval<<endl;
					grid.m_data[tools.m_neighbors_info.m_samples_neighbor[j]][2]=val_nneib[j]*(1+dval);

					}*/
					//tools.m_all_value_replicates=repls;
					//random_shuffle(tools.m_all_value_replicates.begin(),tools.m_all_value_replicates.end());
					//			tools.m_all_value_replicates.resize(7000);
					int sizerepl=tools.m_all_value_replicates.m_size;
					//	tools.m_all_value_replicates.resize(min(repl_num[i],sizerepl));
					good_pdf=tools.ComputeCdf(infoin,grid,ti,samples,tools,stats,calc_type,dat_tools);

					std::stringstream ss;
					if(infoin.m_method>2)
						ss<<"pdf_test_TI_spl_"<<i<<".txt";
					else
						ss<<"pdf_test_TI_leg_"<<infoin.m_order<<"_"<<i<<".txt";
					std::string str;
					ss>>str;
					tools.WritePdf(str,"0",tools.coeff_1D,tools.Lspline_buf);

				}
				if(!test_good)
				{

					return gNullvalue;
				}else
				{
					cout<<tools.m_cp;
					system("pause");  
				}
			}
			//	cout<<"ComputeCdf \t"<<tools.m_cp<<"\"t is\t"<<clock()-t_elp<<endl;
			//break;
			//Calculate PDF
			good_pdf=tools.ComputeCdf(infoin,grid,ti,samples,tools,stats,calc_type,dat_tools);
			if(tools.m_neighbors_info.m_n_local_samples<infoin.m_mn_s)
			{
				good_pdf=false;
				break;
			}
			if(good_pdf)
			{
				break;
			}
			
			//if(tools.m_cp==5835)//tools.m_neighbors_info.m_n_local_samples==infoin.m_mn_s+1)
			//{
			//	std::stringstream ss;
			//	ss<<tools.m_cp<<"\t"<<tools.m_neighbors_info.m_n_local_samples;

			//				tools.WritePdf(infoin.m_files[3],ss.str());

			//}
			//If bad pdf, decrease number of neighbours
			tools.m_neighbors_info.m_samples_neighbor.pop_back();
			tools.m_neighbors_info.m_samples_neighbor_vals.pop_back();
			tools.m_neighbors_info.m_samples_distance.pop_back();
			tools.m_neighbors_info.m_n_local_samples= (int)tools.m_neighbors_info.m_samples_neighbor.size();
		}
		t_elp=clock();

#if defined(SHOW_DBG)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
		QueryPerformanceCounter(&t1);
		//cout<<"ComputeCDF \t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;
		if(tools.m_neighbors_info.m_samples_neighbor.size()==infoin.m_mx_s)
			computecdf +=elapsedTime;
#endif
		//ILNUR. Revisiting
		if(!good_pdf)
		{
			infoin.m_hard_point = true;
			infoin.m_n_hard_point++;
			return gNullvalue;
			return tools.Weight_Average(infoin,grid,ti,samples,tools,stats);
			return (-199); 
			//	if(tools.m_neighbors_info.m_n_local_samples>=infoin.m_mn_s)
			//	{
			//		m_curr_hard_psim++;
			//		return (-1);           
			//	}
			//	else
			//	{
			//		// push it to the next grid level
			//		if(tools.m_grid_curr > 0)
			//		{
			//			return (-10); 
			//		}
			//		else
			//		{
			//			//cout<<tools.m_path_pass[tools.m_cp]<<endl;
			//			infoin.m_hard_point = true;
			//			infoin.m_n_hard_point++;
			//			return gNullvalue;
			//			return tools.Weight_Average(infoin,grid,ti,samples,tools,stats);
			//		}
			//	}
		}else
		{
		//	infoin.nneighb_pdf[tools.m_neighbors_info.m_samples_neighbor.size()]++;
		}
		// STOP THE TIMER
		time (&end_real);

		/*THE TOTAL RUN TIME*/
		tools.m_dif_5 += difftime (end_real,start_real);	
		//	double val = tools.draw_value_cdf(); //ILNUR. We wont restrict our cdf by discretizing. Let's use direct intergration of pdf.
		//	double rr=0;
		//double val= tools.draw_value_rous(); 
		//Draw value from CDF
		double val=gNullvalue;
		if(infoin.m_method>2)
			val= tools.draw_value_coeff(tools.coeff_1D,tools.Lspline_buf); 
		else
			val= tools.draw_value_rous(); 


		//cout<<"val\t"<<val<<endl;
		//if (val>0.8)
		//{
		//	std::string val_str;
		//	std::stringstream ss;
		//	ss<<val;
		//	
		//	ss>>val_str;
		//	//cout<<val_str<<endl;
		//	tools.WritePdf("debug_pdf",val_str,tools.coeff_1D);
		//}

#if defined(SHOW_DBG)
		QueryPerformanceCounter(&t2);
		elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;

		//	cout<<"draw val \t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;
		if(tools.m_neighbors_info.m_samples_neighbor.size()==infoin.m_mx_s)
			drawval +=elapsedTime;
#endif
		double val_proc= 0;
		if(calc_type>1)
		{
#ifdef USE_MPI

			MPI_Barrier(MPI_COMM_WORLD);
			MPI_Bcast(&val, 1, MPI_DOUBLE,0,MPI_COMM_WORLD);
			//int rank=0;
			//MPI_Comm_rank(MPI_COMM_WORLD,&rank);
			//		cout<<"val of proc"<<rank<<" is "<<val<<endl;
#endif	

		}
		//	double val= tools.draw_value_coeff(infoin,tools.coeff_1D); 
		//

		val        = tools.BackConvertVal(val,tools.m_cp);

		
		//	cout<<"draw val "<<clock()-t_elp<<endl;
		//val=0;
		return (val);
}

void HosDataManager::reverseINT(uint32_t* &intarr,uint32_t length)
{


	for (uint32_t i=0;i<length;i++)
	{
		uint32_t x =intarr[i];
		uint32_t y=_byteswap_ulong(x);
		//uint32_t y = __builtin_bswap32(x);
		intarr[i]=y;
	}
	return;
}
float HosDataManager::reverseFloat( const float inFloat )
{
	float retVal;
	char *floatToConvert = ( char* ) & inFloat;
	char *returnFloat = ( char* ) & retVal;

	// swap the bytes into a temporary buffer
	returnFloat[0] = floatToConvert[3];
	returnFloat[1] = floatToConvert[2];
	returnFloat[2] = floatToConvert[1];
	returnFloat[3] = floatToConvert[0];

	return retVal;
}

void HosDataManager::reverseFLT(float*  &arr,unsigned int length)
{ 

for (unsigned int i=0;i<length;i++)
{




	arr[i]=reverseFloat(arr[i]);
}

return ;
}

//SGEMS binary format reader
bool HosDataManager::Read_SGEMS(std::string name,Double2Vec& fill_data,InfoIn& infoin_)
{
	char sgemsformat[4];
	sgemsformat[0]=178;
	sgemsformat[1]=17;
	sgemsformat[2]=23;
	sgemsformat[3]=93;
	std::vector<double> read_values;
	bool bad_case=true;
	std::ifstream is (name.c_str(), std::ifstream::binary);
	if (is) {
		// get length of file:
		is.seekg (0, is.end);
		int endp = (int)is.tellg();

		is.seekg (0, is.beg);
		const int buf_size=1024;

		char buffer [buf_size] ;

		float* flt_buf =new float[buf_size];
		unsigned int*  int_buf=new unsigned int[buf_size];

		int pos=0;
		int nfloat=-1;
		is.read ((char*)buffer,4);//header
		bool is_header=true;
		for(int p=0;p<4;p++)
		{
			if(buffer[p]!=sgemsformat[p])
			{
				std::cout << "Not sgems_format header. Trying to run values reader"<<std::endl;
				is_header=false;
				break;
			}
		}
		int var_ind=0;

		if(is_header)
		{
			is.read ((char*)int_buf,4);//get n char of Cgrid
			reverseINT(int_buf,1);
			int next_n_bytes=int_buf[0];
			is.read ((char*)buffer,next_n_bytes);//get Cgrid string
			std::string ctype(buffer);
			is.read ((char*)int_buf,4);//get n char of grid name
			reverseINT(int_buf,1);
			next_n_bytes=int_buf[0];
			is.read ((char*)buffer,next_n_bytes);//get grid name
			std::string grid_name(buffer);
			is.read ((char*)buffer,4);//skip 4bytes unknown why
			is.read ((char*)int_buf,4*3);//get nx ny nz
			reverseINT(int_buf,3);
			int nx=int_buf[0];
			int ny=int_buf[1];
			int nz=int_buf[2];
			is.read ((char*)flt_buf,4*6);//get dx dy dz ox oy oz
			reverseFLT(flt_buf,6);
			float dx=flt_buf[0];
			float dy=flt_buf[1];
			float dz=flt_buf[2];
			float ox=flt_buf[3];
			float oy=flt_buf[4];
			float oz=flt_buf[5];
			is.read ((char*)int_buf,4);//get nvar
			reverseINT(int_buf,1);
			int nvar=int_buf[0];
			bad_case=(nx*ny*nz!=fill_data.size());
			if(bad_case)
			{
				cout<<"ERROR: Wrong number of values in binary file. Check nx*ny*nz"<<endl;

				system("pause");
				exit (0);
			}
			else
			{
				std::vector<std::string> var_names;
				var_names.reserve(nvar);
				for (int iv=0;iv<nvar;iv++)
				{
					is.read ((char*)int_buf,4);//get  n char of var iv
					reverseINT(int_buf,1);
					int nchars=int_buf[0];
					is.read ((char*)buffer,nchars);//get  name of var
					var_names.push_back(buffer);
				}
				int pos0= (int)is.tellg();
				nfloat=nx*ny*nz;
				var_ind=0;
				if (fill_data[0].size()==1)
					var_ind=0;
				else 
				{
					if(nvar<1)
					{
						bad_case=true;
						cout<<"No columns for variables in file"<<endl;
					}
					else 
					{

						/*vector<std::string> XYZxyz(6);
						XYZxyz[0]="X";
						XYZxyz[1]="Y";
						XYZxyz[2]="Z";
						XYZxyz[3]="x";
						XYZxyz[4]="y";
						XYZxyz[5]="z";
						vector<bool> have_var(3);
						have_var[0]=false;
						have_var[1]=false;
						have_var[2]=false;
						vector<int> var_indexes;
						for (int vv=0;vv<nvar;vv++)
						var_indexes.push_back(vv);
						int count_names=0;*/
						cout<<"XYZ is taken as a regular grid from binary file"<<endl;
						/*for (int p=0;p<3;p++)
						{
						for (int vn=0;vn<var_names.size();vn++)
						{
						bool found0 =  var_names[vn].compare(XYZxyz[p]) == 0;
						bool found1 =  var_names[vn].compare(XYZxyz[p+3]) == 0;
						if (found0||found1)
						{

						var_names.erase(var_names.begin()+vn);
						var_indexes.erase(var_indexes.begin()+vn);
						count_names++;
						break;
						}
						}
						}*/



						int cnt=0;
						for(int k=0;k<nz;k++)
							for(int j=0;j<ny;j++)
								for(int i=0;i<nx;i++)
								{
									if(infoin_.m_dimension>0)
										fill_data[cnt][0]=ox+dx*i;
									if(infoin_.m_dimension>1)
										fill_data[cnt][1]=oy+dy*j;
									if(infoin_.m_dimension>2)
										fill_data[cnt][2]=oz+dz*k;

									cnt++;
								}

								/*
								if(var_indexes.size()<1)
								{*/
								var_ind=0;
								cout<<"Column \""<<var_names[0]<<"\" is taken for values "<<endl;
								/*	}else 
								{
								if(var_indexes.size()>1)
								cout<<"More than 1 column left for TI values. The one with name \""<<var_names[0]<<"\" is taken"<<endl;
								var_ind=var_indexes[0];
								}*/
								pos=nx*ny*nz*var_ind+pos0;
								is.seekg(pos);

					}

				}

			}


		}else 
		{
			pos=0;
			is.seekg(pos);
			nfloat=(endp-pos)/sizeof(float);
			bad_case=(nfloat!=fill_data.size());
			if(!bad_case&&(fill_data[0].size()>1))
				// if(bad_case)
				//	cout<<"Wrong number of values in binary file. Check nx*ny*nz"<<endl;
			{ cout<<"XYZ is taken as a regular grid from x0,dx,nx,y0,dy,ny,z0,dz,nz, in parameter file"<<endl;
			int cnt=0;
			int nz=max(infoin_.m_ti_info.m_nz,1);
			int ny=max(infoin_.m_ti_info.m_ny,1);
			int nx=max(infoin_.m_ti_info.m_nx,1);
			for(int k=0;k<nz;k++)
				for(int j=0;j<ny;j++)
					for(int i=0;i<nx;i++)
					{
						if(infoin_.m_dimension>0)
							fill_data[cnt][0]=infoin_.m_ti_info.m_x0+infoin_.m_ti_info.m_dx*i;
						if(infoin_.m_dimension>1)
							fill_data[cnt][1]=infoin_.m_ti_info.m_y0+infoin_.m_ti_info.m_dy*i;
						if(infoin_.m_dimension>2)
							fill_data[cnt][2]=infoin_.m_ti_info.m_z0+infoin_.m_ti_info.m_dz*i;

						cnt++;
					}
			}

		}
		int ind_of_variable=infoin_.m_dimension;
		if(fill_data[0].size()==1)
			ind_of_variable=0;
		if(!bad_case)
			ReadPropBin(is, nfloat,fill_data,ind_of_variable,is_header,gNullvalue);


		delete[]int_buf;
		delete[]flt_buf;

		if (is&&!bad_case)
			std::cout << "The file is binary."<<endl;;

		is.close();

		// ...buffer contains the entire file...

	}
	return !bad_case&&is;
}
void HosDataManager::ReadPropBin(ifstream& is,int nfloat,Double2Vec& ddata,int ind,bool is_reverse,double nullvalue)
{
	float* flt_buf=new float[nfloat];
	is.read ((char*)flt_buf,nfloat*sizeof(flt_buf[0]));//get  var values
	if(is_reverse)
		reverseFLT(flt_buf,nfloat);
	for(int j=0;j<nfloat;j++)
	{
		if(flt_buf[j]!=flt_buf[j])
			ddata[j][ind]=nullvalue;
		else
			ddata[j][ind]=flt_buf[j];
	}


	delete[] flt_buf;

}
void
	HosDataManager::WriteSolution_SGEMS(InfoIn& infoin, Grid& grid,double minv,double maxv)
{
	std::string path = HosDataManager::GetBaseFilename(infoin.m_files[2]);
	std::string prop_name=path;
	path.append(".sgems");

	char sgemsformat[4];
	sgemsformat[0]=178;
	sgemsformat[1]=17;
	sgemsformat[2]=23;
	sgemsformat[3]=93;
	std::vector<double> read_values;
	std::ofstream fout (path.c_str(), std::ofstream::binary);
	if (fout) 
	{
		// get length of file:
		fout.write(sgemsformat,4);

		const int buf_size=1024;

		
		int32_t size_int=4;
		int32_t size_flt=4;
		if(size_flt!=sizeof(float))
		{
			cout<<"System float is not 4 bytes. Cannot write sgems file."<<endl;
			fout.close();
			return;
		}
		float *flt_buf = new  float[buf_size];
		uint32_t* int_buf=new uint32_t[buf_size];
		if(sizeof(int_buf[0])>4)
		{
			cout<<"WHY SIZE IS OF INT NOT 4"<<endl;
			system("pause"); 
		}
		int pos=0;

		std::string Cname="Cgrid";
		int_buf[0]= (uint32_t)Cname.length()+1;
		uint32_t next_n_bytes=int_buf[0];
		reverseINT(int_buf,1);
		fout.write((char*)int_buf,size_int);//write n char of Cgrid
		fout.write ((char*)Cname.c_str(),next_n_bytes);//get Cgrid string

		int_buf[0]= (uint32_t)prop_name.length()+1;
		next_n_bytes=int_buf[0];
		reverseINT(int_buf,1);
		fout.write ((char*)int_buf,size_int);//write n char of grid name
		fout.write((char*)prop_name.c_str(),next_n_bytes);//write grid name
		int_buf[0]=100;
		reverseINT(int_buf,1);
		fout.write((char*)int_buf,size_int);//skip 4bytes unknown why
		int_buf[0]=infoin.m_grid_info.m_nx;
		int_buf[1]=infoin.m_grid_info.m_ny;
		int_buf[2]=infoin.m_grid_info.m_nz;
		reverseINT(int_buf,3);
		fout.write((char*)int_buf,size_int*3);//write nx ny nz
		flt_buf[0]=(float)infoin.m_grid_info.m_dx;
		flt_buf[1]= (float)infoin.m_grid_info.m_dy;
		flt_buf[2]= (float)infoin.m_grid_info.m_dz;
		flt_buf[3]= (float)infoin.m_grid_info.m_x0;
		flt_buf[4]= (float)infoin.m_grid_info.m_y0;
		flt_buf[5]= (float)infoin.m_grid_info.m_z0;
		reverseFLT(flt_buf,6);
		fout.write((char*)flt_buf,size_int*6);//write dx dy dz ox oy oz
		int_buf[0]=infoin.m_dimension+infoin.m_n_realization;
		reverseINT(int_buf,1);
		fout.write ((char*)int_buf,size_int);//write nvar

		

		std::string real = "Real_";

		for (int iv=0;iv<infoin.m_n_realization;iv++)
		{
			std::string cur_real;
			stringstream ss;
			ss<<real<<iv;
			ss>>cur_real;
			int_buf[0]= (uint32_t) cur_real.length()+1;
			next_n_bytes=int_buf[0];
			reverseINT(int_buf,1);
			fout.write ((char*)int_buf,size_int);//write  n char of var iv

			fout.write ((char*)cur_real.c_str(),next_n_bytes);//write  name of var

		}
		std::string var_name("X");
		for (int iv=0;iv<infoin.m_dimension;iv++)
		{
			std::string cur_var_name;
			stringstream ss;
			ss<<var_name<<iv;
			ss>>cur_var_name;
			int_buf[0]= (uint32_t) cur_var_name.length()+1;
			next_n_bytes=int_buf[0];
			reverseINT(int_buf,1);
			fout.write ((char*)int_buf,size_int);//write  n char of var iv

			fout.write ((char*)cur_var_name.c_str(),next_n_bytes);//write  name of var

		}

		int nfloat=infoin.m_grid_info.m_nv;
		delete[] flt_buf;
		flt_buf=new float[nfloat];
		for (int iv=0;iv<infoin.m_n_realization;iv++)
		{
			for(int j=0;j<nfloat;j++)
			{
				double 	val=grid.m_data[j][iv+infoin.m_dimension];
				if(val!=gNullvalue)
				{
					val=max(val,minv);
					val=min(val,maxv);
				}else
					val = std::numeric_limits<double>::quiet_NaN();;
				flt_buf[j]= (float)val;
	
			}
			reverseFLT(flt_buf,nfloat);
			fout.write ((char*)flt_buf,nfloat*size_flt);//get  var values
		}
		for (int iv=0;iv<infoin.m_dimension;iv++)
		{
			for(int j=0;j<nfloat;j++)
				flt_buf[j]= (float)grid.m_data[j][iv];
			reverseFLT(flt_buf,nfloat);
			fout.write ((char*)flt_buf,nfloat*size_flt);//get  var values
		}


		delete[] flt_buf;
		delete[] int_buf;


		if (!fout)
			//  std::cout << "simulation has been saved"<<endl;
			//  else
			std::cout << "error in saving"<<endl;
		fout.close();
		cout << "Preliminary result is saved" << endl;
		// ...buffer contains the entire file...

	}



}
void
	HosDataManager::WriteSolutions(InfoIn& infoin, Grid& grid,double minv,double maxv)
{
	std::string path = infoin.m_files[2];
	ofstream fout(path.c_str());
	if(fout)
	{
		fout.precision(5); 

		int dim = infoin.m_dimension;
		int nb_var = infoin.m_dimension + infoin.m_n_realization + 1 + 1; // +1 for E-Type, +1 for multigrid_index.

		fout << "Realizations"<< endl;
		fout << nb_var<< endl;
		fout << "X"<< endl;
		if(dim>1)
			fout << "Y"<< endl;
		if(dim>2)
			fout << "Z"<< endl;

		fout << "Multi-Grid Index" << endl;

		std::string real = "Real_";
		for(int i=0; i<infoin.m_n_realization;i++)
		{
			std::string s;
			std::stringstream out;
			out << i;
			s = out.str(); 
			std::string cur_real = real + s;
			fout << cur_real << endl;
		}

		fout << "E-Type" << endl;
		long long int vector_size=infoin.m_grid_info.m_nv;
		long long int chunk_size=100000000/(sizeof(double)*nb_var);
		long long int buf_size=min(chunk_size,vector_size);
		int buf_cnt=0;
		std::stringstream str(std::stringstream::out);
		for(int i=0;i<infoin.m_grid_info.m_nv;i++)
		{
			// point coordinates
			for(int d=0;d<infoin.m_dimension;d++)
				str<< grid.m_data[i][d] << " ";

			// point grid index. Default 1 grid.
			str<< grid.m_multigrid_index[i] << " ";

			// realizations.
			double e_type = 0.0;
			for(int real_no=0;real_no<infoin.m_n_realization;real_no++)
			{
				e_type += grid.m_data[i][infoin.m_dimension+real_no]/infoin.m_n_realization;
				double 	val=grid.m_data[i][infoin.m_dimension+real_no];
				if(val!=gNullvalue)
				{
					val=max(val,minv);
					val=min(val,maxv);
				}
				str<<val<< " ";
			}
			// E-type.
			str<<e_type << " ";
			str<< endl;
			if(buf_cnt==buf_size)
			{

				buf_cnt=0;
				fout.write(str.str().c_str(),str.str().length());
				str.str("");
				buf_cnt=0;
				std::cout<<i*100.0/infoin.m_grid_info.m_nv<<"% of blocks have been written.."<<std::endl;
			}
			buf_cnt++;
		}
		if(buf_cnt>0)
			fout.write(str.str().c_str(),str.str().length());



		fout.close();
	}
	else
		cout<<"Output file is busy. Cannot write solution."<<endl;
}



void
	HosDataManager::PrepareAct(InfoIn& infoin, Grid& grid, Tools& tools, TrainingImage& ti, Samples& samples, int& nt_cr, int& i, double val, int ind)
{

	if (ind ==1)
	{
		double min, max;
		if(samples.m_samples_info.m_max>0)
		{
			min = samples.m_samples_info.m_min;
			max = samples.m_samples_info.m_max;
		}
		else
		{
			min = ti.m_ti_min;
			max = ti.m_ti_max;
		}
		grid.m_min.resize(infoin.m_n_realization,min);
		grid.m_max.resize(infoin.m_n_realization,max);
		grid.m_mean.resize(infoin.m_n_realization,0.0);
		
		
		tools.ComputeLegendreMomentsSamples(infoin, tools, samples);
	}
	else if (ind ==2)
	{
		tools.m_dif_1 = 0;tools.m_dif_2 = 0;tools.m_dif_3 = 0;tools.m_dif_4 = 0;tools.m_dif_5 = 0;
//		tools.m_neighbors_info.m_n_local_samples_old=-1;
		infoin.m_unsamples_curr = infoin.m_unsamples_orig;
		tools.m_sample_indicator_curr = tools.m_sample_indicator_orig;
		tools.m_samples_curr = tools.m_samples_orig;
		tools.m_path_pass = tools.m_null_vec;
		m_max_hard_psim = infoin.m_grid_info.m_nv; 
		m_curr_hard_psim= 0;
		//tools.m_hardpoints.resize(m_curr_hard_psim);
		tools.m_rand_nb_pass = 0;
	}
	else if (ind == 3)
	{
		/*double r0,r1,r2;
		int nnn=1000;
		for (int pp=0;pp<10;pp++)
		{
			r0=(*drand_path)();
			r1=(*drand_val)();
			r2=tools.rand_int(nnn);
		//	cout<<r0<<'\t'<<r1<<'\t'<<r2<<'\t'<<endl;
		}*/
		tools.m_rp =tools.rand_int(nt_cr);
		//cout<<"rp="<<tools.m_rp<<" i="<<i<<endl;
		/*	for (int ii=0;ii<10;ii++)
		cout<<"rand_"<<ii<<"="<<drand()<<endl;*/
		tools.m_cp = infoin.m_unsamples_curr[tools.m_grid_curr][tools.m_rp];
		//tools.m_n_repl=(infoin.m_nb_locations_stats)*pow(2.0,tools.m_path_pass[tools.m_cp]);
		//	tools.m_n_repl=infoin.m_nb_locations_stats;
		//tools.m_n_repl=min(tools.m_n_repl,infoin.m_grid_info.m_nv);
		infoin.m_mx_s_cur=infoin.m_mx_s-tools.m_path_pass[tools.m_cp];
		//infoin.m_mx_s_cur=infoin.m_mx_s;
		infoin.m_mx_s_cur=max(infoin.m_mx_s_cur,0);
		tools.m_path_pass[tools.m_cp]++;

	}
	else if (ind ==4)
	{
		if((val!=-199&&val!=-1099)||val==gNullvalue)
		{
			if(val!=gNullvalue)
			{
				tools.m_sample_indicator_curr[tools.m_cp]= (int)tools.m_samples_curr.size();
				tools.m_samples_curr.push_back(tools.m_cp);
				if(val<grid.m_min[tools.m_real_curr]) grid.m_min[tools.m_real_curr] = val;
				if(val>grid.m_max[tools.m_real_curr]) grid.m_max[tools.m_real_curr] = val;
				infoin.WriteDebuggingInfo(grid, tools, infoin, infoin.m_verbosity);
			}
			grid.m_data[tools.m_cp][infoin.m_dimension + tools.m_real_curr] = val;
			grid.m_mean[tools.m_real_curr]+= val/infoin.m_grid_info.m_nv;
			infoin.m_unsamples_curr[tools.m_grid_curr].erase(infoin.m_unsamples_curr[tools.m_grid_curr].begin() + tools.m_rp);
		//	tools.m_neighbors_info.m_n_local_samples_old = tools.m_neighbors_info.m_n_local_samples;
			nt_cr--;
			i++;
		}
		if(val==-1099)
		{
			tools.m_path_pass[tools.m_cp]=0;
			infoin.m_unsamples_curr[tools.m_grid_curr  ].erase(infoin.m_unsamples_curr[tools.m_grid_curr].begin() + tools.m_rp);
			infoin.m_unsamples_curr[tools.m_grid_curr-1].push_back(tools.m_cp);
			nt_cr--;
			i++;
		}
		if(val==-199||val==-1099)
			val=gNullvalue;
	}
}
void
	HosDataManager::SimulateAll(InfoIn& infoin, Grid& grid, TrainingImage& ti, Samples& samples, Tools& tools, Stats& stats)
{

		//Ilnur
	double minv=1e+10;
	double maxv=-1e+10;
	minv=min(ti.m_ti_min,minv);
	maxv=max(ti.m_ti_max,maxv);
	minv=min(samples.m_samples_info.m_min,minv);
	maxv=max(samples.m_samples_info.m_max,maxv);
	tools.m_neighbors_info.m_local_min=minv-(maxv-minv)*0.1;
	tools.m_neighbors_info.m_local_max=maxv+(maxv-minv)*0.1;
	int rank=0;
#ifdef USE_MPI
	MPI_Comm_rank(MPI_COMM_WORLD,&rank);
#endif
#if defined(_OPENMP)
	int nthreads, tid;

	/* Fork a team of threads giving them their own copies of variables */
#pragma omp parallel private(nthreads, tid)
	{

		/* Obtain thread number */
		tid = omp_get_thread_num();
		printf("Hello World from thread = %d\n", tid);

		/* Only master thread does this */
		if (tid == 0) 
		{
			nthreads = omp_get_num_threads();
			printf("Number of threads = %d\n", nthreads);
		}

	}  /* All threads join master thread and disband */


#endif
	clock_t t_elp=0;
	double dif;
	time_t start_real,end_real;
	int dummy1 = 0, dummy2=0;
	double dummy3 = 0.0;
	int calc_type=(infoin.m_method-1)%2+1;


	PrepareAct(infoin, grid, tools, ti, samples, dummy1, dummy2, dummy3, 1);
	tools.Get_variogram_par(infoin,ti);
	if(infoin.m_method>2)
	{
		//Making Dump
		//for(int ii=10;ii<150;ii++)
		//{
		//	Double1Vec dummyvec;
		//	SplineContainer spl_Dump;
		//	spl_Dump.Create(infoin.m_order,ii,dummyvec);
		//}
		//cout<<"Dumpd!"<<endl;
		//exit(0);
		/*	Double1Vec dummyvec;
		SplineContainer spl_Dump;
		spl_Dump.Create(infoin.m_order,142,dummyvec,true);*/
		//
		if (infoin.m_mdiv_max < 10|| infoin.m_mdiv_max>100)
			infoin.m_mdiv_max = 31;

		int m_div=infoin.m_mdiv_max;
		if(infoin.m_order==0)
		{
			Double1Vec data_vals;
			data_vals.reserve(samples.m_data.size());
			for (int i=0;i<samples.m_data.size();i++)
			{
				if(samples.m_data[i][infoin.m_dimension]!=gNullvalue)
					data_vals.push_back(samples.m_data[i][infoin.m_dimension]);

			}
			sort(data_vals.begin(),data_vals.end());

			tools.cat_vals.clear();
			tools.cat_vals.push_back(data_vals[0]);
			for(int i=1;i<data_vals.size();i++)
			{
				if(data_vals[i-1]!=data_vals[i])
					tools.cat_vals.push_back(data_vals[i]);
			}
			m_div= (int)tools.cat_vals.size()+1;
		}

		tools.GetSplines(infoin.m_order,m_div,tools.Lspline);//TODO: hardocded 10 should be in parameter file
		tools.coeff_1D.resize(tools.Lspline.n_spln);
		int n_Dump=tools.var2mdiv(tools.nug)+1;
		infoin.Lspline_Dump.resize(n_Dump+1);
		//ILNUR temprorary for categorical
		if(infoin.m_index_cat>=0&&infoin.m_index_cat<infoin.m_grid_info.m_nz&&infoin.m_dimension==3)
		{
			Double1Vec data_vals;
			data_vals.reserve(samples.m_data.size());
			for (int i=0;i<samples.m_data.size();i++)
			{
				if(samples.m_data[i][infoin.m_dimension]!=gNullvalue&&samples.m_data[i][2]==infoin.m_index_cat)
					data_vals.push_back(samples.m_data[i][infoin.m_dimension]);

			}
			sort(data_vals.begin(),data_vals.end());

			tools.cat_vals.clear();
			tools.cat_vals.push_back(data_vals[0]);
			for(int i=1;i<data_vals.size();i++)
			{
				if(data_vals[i-1]!=data_vals[i])
					tools.cat_vals.push_back(data_vals[i]);
			}
			int m_div2= (int)tools.cat_vals.size()+1;


			tools.GetSplines(0,m_div2,tools.Lspline_cat);//TODO: hardocded 10 should be in parameter file

		}
		//
		//ILNUR.Get rid of it
		/*for(int ii=10;ii<infoin.Lspline_Dump.size();ii++)
		{
		infoin.Lspline_Dump[ii]=tools.Lspline;
		}*/
		/*	for(int ii=10;ii<infoin.Lspline_Dump.size();ii++)
		{
		Double1Vec dummyvec;
		bool isgood=tools.Load_Splines(infoin.Lspline_Dump[ii],ii);
		if(!isgood)
		{
		infoin.Lspline_Dump[ii].Create(infoin.m_order,ii,dummyvec);
		tools.Save_Splines(infoin.Lspline_Dump[ii]);
		}
		}*/
		if(infoin.m_method>3)
			tools.Repl_Dump.Read_Repl_Dump((int)tools.m_samples_orig.size());
	}
	for(int real_no=0;real_no<infoin.m_n_realization;real_no++)
	{
		int i_old=-1;
		tools.m_real_curr = real_no;
		//cout << "-----------------------------------------------"<<endl;
		cout << "GENERATION OF REALIZATION #                    "<<real_no+1<<"                    "<<endl;
		time (&start_real);

		infoin.WriteDebuggingInfo(grid, tools, infoin, 0);

		PrepareAct(infoin, grid, tools, ti, samples, dummy1, dummy2, dummy3, 2);
		Tools dat_tools;
		if(calc_type>1)
		{
			dat_tools=tools;
			dat_tools.tool_grid=grid;
			dat_tools.m_all_value_replicates.m_values.resize(tools.m_all_value_replicates.m_shift*tools.m_all_value_replicates.m_size);
			dat_tools.m_all_value_replicates.m_indexes.resize(tools.m_all_value_replicates.m_shift*tools.m_all_value_replicates.m_size);
			for (int i=0;i<tools.m_all_value_replicates.m_size;i++)
			{
				int repl_base=i*tools.m_all_value_replicates.m_shift;
				for (int j=0;j<tools.m_all_value_replicates.m_shift;j++)
				{
					dat_tools.m_all_value_replicates.m_values[repl_base+j]=tools.m_all_value_replicates.m_values[repl_base+j];
					dat_tools.m_all_value_replicates.m_indexes[repl_base+j]=tools.m_all_value_replicates.m_indexes[repl_base+j];
				}
			}

			dat_tools.m_data_value_replicates.m_indexes.clear();
			dat_tools.m_data_value_replicates.m_values.clear();
		}
		int i, i_acc = 0;
	//	FillUnsampledAll(infoin, grid, ti, samples, tools, stats);
		WriteSolution_SGEMS(infoin,grid,minv,maxv);
	//	return;
		// Loop over unsampled grid points
		for(int grid_no=infoin.m_ngrid-1;grid_no>=0;grid_no--)
		{
			i = 0;

			tools.m_grid_curr = grid_no;

			int nt_cr = (int) infoin.m_unsamples_curr[grid_no].size();

			while(infoin.m_unsamples_curr[grid_no].size()>0)
			{

#if defined(SHOW_DBG)
				LARGE_INTEGER t1, t2;           // ticks
				double elapsedTime;
				LARGE_INTEGER frequency;        // ticks per second
				// get ticks per second
				QueryPerformanceFrequency(&frequency);

				// start timer
				QueryPerformanceCounter(&t1);
#endif
				//return 0;
				/*if (!tools.first_node)
				return;*/

				PrepareAct(infoin, grid, tools, ti, samples, nt_cr, dummy2, dummy3, 3);
				//		cout<<"PrepareAct2 \t"<<clock()-t_elp<<endl;
				double val=gNullvalue;
				//	if(ti.m_data[tools.m_cp][infoin.m_dimension]!=gNullvalue)
			
				val = SimulatePoint(infoin, grid, ti, samples, tools,stats,calc_type,dat_tools);

				PrepareAct(infoin, grid, tools, ti, samples, nt_cr, i, val, 4);

				if(val!=gNullvalue)
					i_acc ++;


#if defined(SHOW_DBG)
				QueryPerformanceCounter(&t2);
				elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
				QueryPerformanceCounter(&t1);
				//cout<<"ConvertVal \t"<<tools.m_cp<<"\t is\t"<<elapsedTime<<endl;
				if(tools.m_neighbors_info.m_n_local_samples==infoin.m_mx_s)
					simulatepoint +=elapsedTime;
#endif
				t_elp=clock();
				int discr= 5000;
				if (i%discr ==0)
				{
					
					if(calc_type>1)
					{
						cout<<i<<" nodes"<<endl;
						ofstream fout;
						std::string out_name;
						std::stringstream ss;
						ss<<"repl_Dump_"<<rank<<".txt";
						ss>>out_name;

						fout.open(out_name.c_str());
						int repl_c_size= (int)tools.Repl_Dump.values.size();
						for (int i=0;i<min(100,repl_c_size);i++)
						{
							fout<<tools.m_cp<<"\t"<<i;
							for (int j=0;j<tools.Repl_Dump.values[i].size();j++)
							{
								double foval=tools.Repl_Dump.values[i][j];
								fout<<"\t"<<foval;
							}
							fout<<endl;
						}
						fout.close();


					}

					WriteSolution_SGEMS(infoin,grid,minv,maxv);
					

				}
				if( (i%200 ==0 || i_acc==(infoin.m_grid_info.m_nv-samples.m_samples_info.m_n_samples)) &&i>0 )
				{

#if defined(SHOW_DBG)
					cout<<"------------find_nneib\t is \t"<<find_nneib<<endl;
					cout<<"------------find_octa\t is \t"<<find_octa<<endl;
					cout<<"------------minmax\t is \t"<<minmaxval<<endl;
					cout<<"------------buildtemp\t is \t"<<buildtemp<<endl;

					cout<<"------find_nneib_all\t is \t"<<find_nneib_all<<endl;
					cout<<"------------prepare\t is \t"<<prepare<<endl;
					cout<<"------------post\t"<<post_cnt<<" is \t"<<post<<endl;
					cout<<"------------post2\t"<<post_cnt<<" is \t"<<post2<<endl;
					cout<<"------------before\t is \t"<<before<<endl;
					cout<<"------------after\t is \t"<<after<<endl;
					cout<<"------------alloc1\t is \t"<<alloc1<<endl;
					cout<<"------------alloc2\t is \t"<<alloc2<<endl;
					cout<<"------computecdf\t is \t"<<computecdf<<endl;

					cout<<"------convertval\t is \t"<<convertval<<endl;
					cout<<"------drawval\t is \t"<<drawval<<endl;

					cout<<"simulatepoint\t is \t"<<simulatepoint<<endl;		
					prepare=0;
					post_cnt=0;
					post=0;
					post2=0;
					before=0;
					after=0;
					alloc1=0;
					alloc2=0;
					computecdf=0;
					convertval=0;
					drawval=0;
					find_nneib=0;
					find_octa=0;
					minmaxval=0;
					buildtemp=0;
					find_nneib_all=0;
					simulatepoint=0;
					intermid=0;
#endif
					// STOP THE TIMER
					time (&end_real);
					/*THE TOTAL RUN TIME*/
					dif = difftime (end_real,start_real);	
					// simulation of the point no
					cout<<"REAL # "<<real_no+1<<"- GRID # "<<grid_no+1<<"- NB POINTS PROCESSED: "<<i<<"- TOTAL TIME (s): "<<dif<<endl;          
					i_old = i_acc;

				}      
			}
		}
		//ILNUR. Do not want to make it fits artificially
		//tools.ArrangeSol(infoin,grid,samples,tools,real_no);
		// STOP THE TIMER
		time (&end_real);
		/*THE TOTAL RUN TIME*/
		dif = difftime (end_real,start_real);	
		/* PRINT OUT THE CPU */
		cout << "Bad_points:	"<<infoin.m_n_hard_point<<endl;
		cout << "-----------------------------------------------"<<endl;
		cout << "THE GENERATION OF THIS REALIZATION IS COMPLETED"<<endl;
		cout << "THE CPU TIME (SECONDS) FOR THIS REALIZATION:   "<<" "<<dif<<endl;


	}
	//WriteStats(infoin, grid, samples,tools);
	if(rank==0)          
	{
		bool needFill=false;
		if(needFill)
			FillUnsampledAll(infoin,  grid,  ti,  samples,  tools,  stats);

		WriteSolution_SGEMS(infoin,grid,minv,maxv);
		WriteSolutions(infoin,grid,minv,maxv);
	}
	//MPI_Finalize();
	//delete[] argvv;
	// Close
	delete drand_val;
	delete drand_path;
}
void Dump::Read_Repl_Dump(int n_samples)
{
	double val;
	int sizeofline=n_values*8;
	char* line=new char[sizeofline];

	ifstream fin("repl_Dump_in.txt");
	int i=0;
	bool flag=true;
	double nullvalue=values[0][0];
	while (!fin.eof( ))      //if not at end of file, continue reading numbers
	{
		fin.getline(line,sizeofline);

		std::stringstream ss;
		Double1Vec vect;
		vect.reserve(n_values);
		ss<<line;
		ss>>val;
		ss>>val;
		while (ss >> val)
		{
			vect.push_back(val);

			//	if (ss.peek() == '\t'||ss.peek() == ' '||ss.peek() == ','||ss.peek() == ';')
			//		ss.ignore();
		}
		if(i>values.size()||values[i].size()!=vect.size())
		{
			flag=false;
			break;
		}
		values[i]=vect;
		i++;
	}
	fin.close();
	if(!flag)
	{
		Init(n_samples,m_dimension,nullvalue);

	}
	delete[] line;
	return;
}
void SplineContainer::Get_split(Double1Vec& knots)
{

	double	dt=(b-a)/(nk+1);
	gl_t.resize(2*order+2+nk);
	for (int i=0;i<order+1;i++)
	{
		gl_t[i]=a;
	}


	for (int i=nk+order+1;i<gl_t.size();i++)
	{
		gl_t[i]=b;
	}
	if(knots.size()>0)
	{
		for (int i=order+1;i<nk+order+1;i++)
		{
			gl_t[i]=knots[i-(order+1)+1];
		}
	}
	else
	{
		double a_new=a;
		int ii0=order;
		int ii1=nk+order+1;
		if(false)
		{
			int shft=order;
			int nk_freq=(nk-2)/4;
			double dt_freq=dt*0.5;
			double t0=-0.5-nk_freq*dt_freq;
			double t1=0.5+nk_freq*dt_freq;
			gl_t[1+shft]=t0;
			shft++;
			int ii=0;
			for ( ii=1;ii<nk_freq;ii++)
			{
				gl_t[ii+shft]=t0+dt_freq*ii;

			}
			ii0=ii+shft-1;
			a_new=gl_t[ii0];
			shft=nk+order+1;
			gl_t[shft-1]=t1;
			shft--;
			for ( ii=1;ii<nk_freq;ii++)
			{
				gl_t[shft-ii]=t1-dt_freq*ii;
			}
			ii1=shft-ii+1;
			double b_new=gl_t[ii1];
			int nk_new=ii1-ii0;
			dt=(b_new-a_new)/(nk_new);
		}
		//
		for (int i=ii0+1;i<ii1;i++)
		{
			gl_t[i]=a_new+dt*(i-ii0);
		}

	}
	tk.resize(nk);
	for (int k=0;k<nk;k++)
	{

		int n_tk0=order+1+k+1;
		int n_tk=order+1+n_tk0;
		tk[k].resize(n_tk);
		for (int i=0;i<n_tk0;i++)
		{
			tk[k][i]=gl_t[i];
		}

		for (int i=n_tk0;i<n_tk;i++)
		{
			tk[k][i]=b;
		}

	}
	fast_coeffs.resize(nk);
	for (int k=0;k<fast_coeffs.size();k++)
		fast_coeffs[k].resize(nk-1);
}
void  SplineContainer::Get_Ortho()
{
	Lcoeff.resize(nk);
	vector<double> f;

	Get_Fast_coeffs();
	for (int k=0;k<nk;k++)
	{

		f=Get_Function(k);
		Lcoeff[k]=Get_Coeff(k,f);

	}


}
void  SplineContainer::Create(int ord,int m_div,Double1Vec& knots,bool Dumpd)
{


	if(knots.size()>0)
		m_div= (int)knots.size();
	m_division=m_div;
	a=-1;
	b=1;
	order=ord;
	nk=m_div-2;
	n_spln=nk+ord+1;
	splines.resize(n_spln);
	Get_split(knots);
	//clock_t t=clock();
	Get_Ortho();
	//	cout<<"Get_Ortho\t"<<clock()-t<<endl;
	//	t=clock();
	Get_Out_Format();	
	//	cout<<"Get_Out_Format\t"<<clock()-t<<endl;
	Get_Integral();
	if(Dumpd)
		Get_Dump_Responce();

}
//bool Tools::Load_Splines(SplineContainer& spl,int m_div)
//{
//	std::stringstream ss;
//	ss<<"Dump/spline_"<<m_div<<"_all.bin";
//	ss>>f_save_name;
//	ifstream fin (f_save_name, ios::in  | ios::binary);
//    if(fin.fail())
//		return false;
//	
//    	fin.seekp (0);
//	//Save easy staff
//		int n_all_spln=0;
//	fin.read ((char*)spl.m_division,  sizeof(spl.m_division));
//	fin.read ((char*)spl.n_spln,  sizeof(spl.n_spln));
//	fin.read ((char*)n_all_spln,  sizeof(n_all_spln));
//	fin.read ((char*)spl.order,  sizeof(spl.order));
//	//Save Dumpd vals
//	bool isgood =spl.Read_Dump_Vals_Bin(fin);
//	if(isgood)
//	{
//	
//	//Save splines
//		spl.splines.resize(n_all_spln);
//		for (int nn=0;nn<n_all_spln;nn++)
//		{
//			isgood=spl.splines[nn].Load(fin);
//			if(!isgood)
//				break;
//		}
//		
//	}
//	fin.close();
//
//	return isgood;
//
//}
//
//void Tools::Save_Splines(SplineContainer& spl)
//{
//
//	ofstream fout(f_save_name, ios::out  | ios::binary);
//	if(fout.fail())
//	{
//			cout<<"Please create folde named \"Dump\" for spline Dump"<<endl;
//			return;
//	}
//	
//	fout.seekp (0);
//	//Save easy staff
//	int n_all_spln=spl.splines.size();
//	fout.write ((char*)spl.m_division,  sizeof(spl.m_division));
//	fout.write ((char*)spl.n_spln,  sizeof(spl.n_spln));
//	fout.write((char*)n_all_spln,  sizeof(n_all_spln));
//	fout.write ((char*)spl.order,  sizeof(spl.order));
//	//Save Dumpd vals
//	spl.Write_Dump_Vals_Bin(fout);
//	//Save splines
//	for (int nn=0;nn<spl.splines.size();nn++)
//		spl.splines[nn].Save(fout);
//
//	fout.close();
//
//}
//void Spline::Save_Spline(ofstream& fout)
//{
//	
//
//	//Save easy staff
//
//	fout.write ((char*)order,  sizeof(order));
//	fout.write ((char*)nk,  sizeof(nk));
//	fout.write((char*)a,  sizeof(a));
//	fout.write ((char*)b,  sizeof(b));
//	//Save Vecs
//	Save_Vec(fout,breaks);
//	Save_Vec(fout,coefs_bias);
//	Save_Vec(fout,coefs);
//	Save_Vec(fout,coefs_integral);
//	
//
//
//
//}
//bool Spline::Read_Spline(ifstream& fin)
//{
//	
//
//	//read easy staff
//
//	fin.read ((char*)order,  sizeof(order));
//	fin.read ((char*)nk,  sizeof(nk));
//	fin.read((char*)a,  sizeof(a));
//	fin.read ((char*)b,  sizeof(b));
//
//	if(fin.fail)
//		return false;
//	bool isgood=true;
//	//read Vecs
//	isgood=isgood&&Read_Vec(fin,breaks);
//	isgood=isgood&&Read_Vec(fin,coefs_bias);
//	isgood=isgood&&Read_Vec(fin,coefs);
//	isgood=isgood&&Read_Vec(fin,coefs_integral);
//	return isgood;
//}
//void Spline::Save_Vec(ofstream& fout, Double1Vec& vec)
//{
//
//	int nvec=vec.size();
//	double y*=new double[nvec];
//	fout.write ((char*)nvec,  sizeof(nvec));
//	for(int i=0;i<nvec;i++)
//	{
//		y[i]=vec[i];
//
//	}
//	fout.write ((char*)&y,  sizeof(double)*nvec);
//	delete[] y;
//}
//void Spline::Save_Vec2(ofstream& fout, Double2Vec& vec)
//{
//
//	int nvec=vec.size();
//	fout.write ((char*)nvec,  sizeof(nvec));
//	for(int i=0;i<nvec;i++)
//	{
//		Save_Vec(fout,  vec[i])
//
//	}
//
//}
//bool Spline::Read_Vec(ifstream& fin, Double1Vec& vec)
//{
//
//	int nvec=0;
//	fin.read ((char*)nvec,  sizeof(nvec));
//	double y*=new double[nvec];
//
//	fin.read  ((char*)&y,  sizeof(double)*nvec);
//	if(fin.fail)
//		return false;
//	vec.resize(nvec);
//	for(int i=0;i<nvec;i++)
//	{
//		vec[i]=y[i];
//	}
//	delete[] y;
//	return true;
//}
//void Spline::Read_Vec2((ifstream& fin, Double1Vec& vec)
//{
//
//	int nvec=0;
//	fin.read ((char*)nvec,  sizeof(nvec));
//	if(fin.fail)
//		return false;
//	for(int i=0;i<nvec;i++)
//	{
//		
//		if(!Read_Vec(fin,  vec[i]))
//			return false;
//	}
//	return true;
//}
bool  SplineContainer::Read_Dump_Vals_Bin(ifstream& fin)
{

	if(fin.fail())
		return false;
	int nn= (int)Dumpd_vals.size();
	double *y = new double[nn*nn*sizeof(double)];
	fin.read ((char*)y, nn*nn*sizeof(double));
	if(!fin)
	{

		return false;
	}

	bool isgood=true;
	for (int k=0;k<nn*nn;k++)
	{
		int i=k/nn;
		int j=k%nn;
		if(fabs(y[k])>1e+20)
		{
			isgood=false;
			break;
		}
		Dumpd_vals[i][j]=y[k];
	}

	delete[] y;

	return isgood;

}
bool  SplineContainer::Read_Dump_Vals_Bin()
{
	std::stringstream ss;
	ss<<"Dump/spline_"<<order<<"_"<<m_division<<".bin";
	ss>>f_Dump_name;
	ifstream fin (f_Dump_name.c_str(), ios::in  | ios::binary);
	bool isgood=Read_Dump_Vals_Bin(fin);
	fin.close();
	return isgood;

}
void SplineContainer:: Write_Dump_Vals_Bin(ofstream& fout)
{
	int nn= (int)Dumpd_vals.size();
	double *y = new double[nn*nn*sizeof(double)];

	for (int k=0;k<nn*nn;k++)
	{
		int i=k/nn;
		int j=k%nn;
		y[k]=Dumpd_vals[i][j];
	}
	fout.seekp (0);
	fout.write ((char*)y,  (nn*nn*sizeof(double)));

	delete[] y;
}
void SplineContainer:: Write_Dump_Vals_Bin()
{

	ofstream fout(f_Dump_name.c_str(), ios::out  | ios::binary);
	if(fout.fail())
	{
		cout<<"Please create folde named \"Dump\" for spline Dump"<<endl;
		return;
	}
	Write_Dump_Vals_Bin(fout);
	fout.close();
}

bool SplineContainer:: Read_Dump_Vals()
{
	std::stringstream ss;
	ss<<"Dump/spline_"<<m_division<<".txt";
	ss>>f_Dump_name;
	ifstream fin(f_Dump_name.c_str());
	if(fin.fail())
		return false;

	int nn= (int)Dumpd_vals.size();
	char line[500*16];
	double dx=2.0/(nn-1);
	bool isgood=true;
	for (int i=0;i<nn;i++)
	{
		fin.getline(line,sizeof(line));
		double val;
		std::stringstream ss_val;
		Double1Vec vect;
		vect.reserve(nn);
		ss_val<<line;
		while(ss_val>>val)
		{
			vect.push_back(val);
		}
		if (vect.size()!=nn)
		{
			isgood=false;
			break;

		}
		Dumpd_vals[i]=vect;
	}
	fin.close();

	return isgood;
}
void SplineContainer::Get_Dump_Responce()
{
	bool is_readable= Read_Dump_Vals_Bin();
	if(!is_readable)
	{
		int nn= (int)Dumpd_vals.size();
		double dx=2.0/(nn-1);
		for (int i=0;i<nn;i++)
		{
			double x=-1+dx*i;
			double mindist0=fabs(x+1);
			double mindist1=2;
			bool prev0=true;
			bool prev1=true;
			double multmax=0;
			for (int w=0;w<splines.size();w++)
			{

				double val_spline=splines[w].GetVal(x);
				double val_spline0=splines[w].GetVal(x);

				multmax+=val_spline0*val_spline;

			}

			for (int j=0;j<nn;j++)
			{
				double y=-1+dx*j;
				double mult=0;
				for (int w=0;w<splines.size();w++)
				{

					double val_spline=splines[w].GetVal(x);
					double val_spline0=splines[w].GetVal(y);

					mult+=val_spline0*val_spline;

				}

				Dumpd_vals[i][j]=mult;

			}
		}
		int rank=0;
#ifdef USE_MPI
		MPI_Comm_rank(MPI_COMM_WORLD,&rank);
		//    cout<<"rank"<<endl;
#endif
		if(rank==0)
			Write_Dump_Vals_Bin();
	}
	cout<<"spline completed "<<splines.size()<<endl;
	return;
}
void SplineContainer:: Write_Dump_Vals()
{

	ofstream fout(f_Dump_name.c_str());
	if(fout.fail())
	{
		cout<<"Please create folde named \"Dump\" for spline Dump"<<endl;
		return;
	}
	for (int i=0;i<Dumpd_vals.size();i++)
	{
		for (int j=0;j<Dumpd_vals.size();j++)
		{
			fout<<Dumpd_vals[i][j]<<"\t";
		}
		fout<<endl;
	}
	fout.close();
}
void SplineContainer::Get_Integral()
{
	for(int k=0;k<n_spln;k++)
		splines[k].Get_Integral();

}
void Spline::Get_Integral()
{
	coefs_integral.resize(coefs.size());
	coefs_bias.resize(coefs.size());

	for(int k=0;k<coefs_integral.size();k++)
	{
		coefs_integral[k].resize(coefs[k].size()+1,0);
		coefs_bias[k]=0;
		for(int j=0;j<coefs[k].size();j++)
			coefs_integral[k][j]=coefs[k][j]/(coefs[k].size()-j);

		if(k>0)
			coefs_bias[k]=GetVal(breaks[k],k-1,1);
	}
}
void  SplineContainer::Get_Out_Format()
{   


	Spline pp;
	int nn=n_spln-nk;
	for (int k=0;k<nn;k++)
	{  
		int nn=k;
		vector<double> tl(2);
		tl[0]=-1;
		tl[1]=1;
		splines[k].Create(tl,0,0,nn);
		splines[k].SetLegendre(nn);
		splines[k].Normalize();
	}
	vector<Spline> pp_vec(n_spln);
	for (int kk=nn;kk<n_spln;kk++)
	{
		int k=kk-nn;
		vector<double>& t=tk[k];

		splines[kk].Create(t,order,k+1,order);

		for (int i=0;i<Lcoeff[k].size();i++)
		{
			Spline pp;
			//Spline pp2;
			if(i<k)
			{
				pp=pp_vec[i];
			}else
			{
				pp.Create(t,i,order,order);
				pp.GetSpline(0,false);
				if (i==k)
				{
					pp_vec[i]=pp;
				}
			}
			//pp2.Create(t,i,order,order);
			//pp2.GetSpline(0,false);
			//for (int j=0;j<pp.coefs.size();j++)
			//{
			//	 for (int mm=0;mm<pp.coefs[j].size();mm++)
			//		 if(fabs(pp.coefs[j][mm]-pp2.coefs[j][mm]))
			//			 cout<<"bad"<<endl;
			//}
			for (int j=0;j<pp.coefs.size();j++)
			{    
				int ind=i+j-order;
				for (int mm=0;mm<pp.coefs[j].size();mm++)
					splines[kk].coefs[ind][mm]+=pp.coefs[j][mm]*Lcoeff[k][i];
			}

		}



		splines[kk].Normalize();


	}

}
void Spline::Normalize()
{
	double val=0;
	vector<double> fornorm(2*(order+1-1)+1+1);
	for (int i=0;i<coefs.size();i++)
	{
		fill(fornorm.begin(), fornorm.end(), 0);
		for (int l1=0;l1<coefs[i].size();l1++)
			for (int l2=0;l2<coefs[i].size();l2++)
			{
				fornorm[l1+l2]+=coefs[i][l1]*coefs[i][l2];
			}
			for (int l1=0;l1<fornorm.size()-1;l1++)
				fornorm[l1]/=(fornorm.size()-l1-1);               
			val=val+(PolyVal(fornorm,breaks[i+1]-breaks[i])-PolyVal(fornorm,0));
	}
	val=1/sqrt(val);
	for (int i=0;i<coefs.size();i++)
		for (int l1=0;l1<coefs[i].size();l1++)
			coefs[i][l1]*=(val);

}
double  Spline::PolyVal(vector<double> &coeff, double x)
{
	double val=0;
	double xpow=1;
	for (int j= (int)coeff.size()-1;j>=0;j--)
	{
		val+=coeff[j]*xpow;
		xpow*=x;
	}
	return val;
}
double Spline::GetBasisVal(double x,int i,int p,double mult)
{

	int level=order-p;
	///i;
	double out_val=0;
	if(breaks[i+p]!=breaks[i])
	{
		double val=mult*(x-breaks[i])/(breaks[i+p]-breaks[i]);
		if (p==1)
		{
			if(x>=breaks[i]&&x<breaks[i+1])
				out_val+=val;
		}
		else
			out_val+=GetBasisVal(x,i,p-1,val);
	}



	if(breaks[i+1+p]!=breaks[i+1])

	{
		double val=mult*(breaks[i+1+p]-x)/(breaks[i+1+p]-breaks[i+1]);
		if (p==1)
		{
			if(x>=breaks[i+1]&&x<breaks[i+2])
				out_val+=val;

		}
		else
			out_val+=GetBasisVal(x,i+1,p-1,val);
	}
	return out_val;
}
void  SplineContainer::Get_Fast_coeffs()
{
	Spline pp;
	int k=nk-1;
	for (int j=0;j<nk;j++)
	{	 

		pp.Create(tk[k],j,2*order+1,2*order+1); 



		int cnt=1;   

		for (int i=0;i<k;i++)
		{
			int n_=order+1+i;   

			fast_coeffs[j][i]=pp.GetBasisVal(tk[k][n_],0, 2*order+1,1);


		}		


	}
}
vector<double>  SplineContainer::Get_Function(int k)
{
	//	clock_t t;


	vector<double> res(k+1);
	vector<vector<double> > A(k+1);
	//vector<vector<double> > A2(k+1);
	Spline pp;


	for (int j=0;j<k+1;j++)
	{	 


		A[j].resize(k);
		//	A2[j].resize(k);
		pp.Create(tk[k],j,2*order+1,2*order+1); 




		//	t = clock();

		//	pp.GetSpline(0,false);


		int cnt=1;   

		/*					
		for (int i=0;i<k;i++)
		{
		int n_=order+1+i;   
		if(tk[k][n_]<pp.breaks[0]||tk[k][n_]>pp.breaks[pp.breaks.size()-1])
		continue;
		else
		A[j][i]=pp.GetVal(tk[k][n_]);

		}*/
		/*	if(k==nk-1)
		cout<<"GetSpline:\t"<< clock() - t<<endl;
		*/
		for (int i=0;i<k;i++)
		{
			int n_=order+1+i;   
			//double val_check=pp.GetBasisVal(tk[k][n_],0, 2*order+1,1);
			//	A[j][i]=pp.GetBasisVal(tk[k][n_],0, 2*order+1,1);
			if(j+2*order+1+1<k+1+order+1)
			{
				A[j][i]=fast_coeffs[j][i];
				//if (fabs(val_check-A[j][i])>1e-10)
				//	cout<<"bad_spline"<<endl;

			}else
			{
				A[j][i]=pp.GetBasisVal(tk[k][n_],0, 2*order+1,1);
			}
			//	
		}		



	}

	//t = clock();
	vector<vector<double> > forDeterm(k);
	Double1Vec determ_mult(k);
	real_2d_array forInverse;
	forInverse.setlength(k,k);
	//real_2d_array forInverse1;
	//forInverse1.setlength(k,k);
	Double1Vec trace(k);
	//Double1Vec determ_mult1(k);
	matinvreport rep;
	ae_int_t info=0;
	double determ0,determ_new;
	//double determ_old; 
	//t=clock();
	//for (int j=0;j<k+1;j++)
	//{
	//
	// for (int j0=0;j0<j;j0++)
	//  forDeterm[j0]=A[j0];
	// for (int j0=j+1;j0<k+1;j0++)
	//   forDeterm[j0-1]=A[j0];
	// 	
	// determ_old=Determinant(forDeterm,k);
	// res[j]=(1 -2*(j%2))*determ_old;

	//}
	//		 
	//	t=clock();
	//	Double1Vec res2(k+1);
	for (int j=0;j<k+1;j++)
	{


		res[j]=(1 -2*(j%2));
		determ_new=1;
		if(k==1)
		{
			if(j==0)
				determ_new=A[1][0];
			else
				determ_new=A[0][0];
		}else
			if(k>0)
				if(j==0)
				{

					for (int j0=0;j0<j;j0++)
						forDeterm[j0]=A[j0];
					for (int j0=j+1;j0<k+1;j0++)
						forDeterm[j0-1]=A[j0];

					for(int i1=0;i1<k;i1++)
						for(int i2=0;i2<k;i2++)
							forInverse[i1][i2]=forDeterm[i1][i2];
					double trace_val=1;
					for(int i1=0;i1<k;i1++)
					{
						trace[i1]=forDeterm[i1][i1];
						trace_val*=trace[i1];
					}

					for(int i1=0;i1<k;i1++)
						for(int i2=0;i2<k;i2++)
						{
							forDeterm[i1][i2]=forDeterm[i1][i2]/trace[i1];
							forInverse[i1][i2]=forDeterm[i1][i2];
							//forInverse1[i1][i2]=forDeterm[i1][i2];
						}
						determ0=trace_val*Determinant(forDeterm,k);
						if(order==1)
						{
							//solve triangle system
							for(int i1=0;i1<k;i1++)
							{
								determ_mult[i1]=A[0][i1];
								for(int i2=0;i2<i1;i2++)
									determ_mult[i1]-=forDeterm[i2][i1]*determ_mult[i2];

							}
							for(int i1=0;i1<k;i1++)
							{	
								determ_mult[i1]*=determ0/trace[i1];
								if(i1%2>0)
									determ_mult[i1]=-determ_mult[i1];
							}
							determ_new=determ0;

						}else
						{
							rmatrixinverse(forInverse,info,rep);

							if(info!=1)
							{

								cout<<"bad determ"<<endl;
							}


							for(int i1=0;i1<k;i1++)
								for(int i2=0;i2<k;i2++)
									forInverse[i1][i2]/=trace[i2];

							fill(determ_mult.begin(),determ_mult.end(),0);

							for(int i1=0;i1<k;i1++)
							{
								for(int i2=0;i2<k;i2++)
									determ_mult[i1]+=A[0][i2]*forInverse[i2][i1];
								determ_mult[i1]*=determ0;
								if(i1%2>0)
									determ_mult[i1]=-determ_mult[i1];
							}
							determ_new=determ0;
						}

				}else
				{

					determ_new=determ_mult[j-1];				
				}



				res[j]*=determ_new;


	}

	//	 if(k==nk-1)
	//	cout<<"determ_new\t"<<k<<":\t"<< clock() - t<<endl;
	return res;
}
void SplineContainer::Crout(vector<vector<double> > & A, vector<vector<double> >  &L, vector<vector<double> > &U, int n) {
	int i, j, k;
	double sum = 0;

	for (i = 0; i < n; i++) 
	{
		U[i][i] = 1;
	}

	for (j = 0; j < n; j++) 
	{
		for (i = j; i < n; i++) 
		{
			sum = 0;
			for (k = 0; k < j; k++) 
			{
				sum = sum + L[i][k] * U[k][j];	
			}
			L[i][j] = A[i][j] - sum;
		}

		for (i = j; i < n; i++) 
		{
			sum = 0;
			for(k = 0; k < j; k++) 
			{
				sum = sum + L[j][k] * U[k][i];
			}
			if (L[j][j] == 0)
			{
				printf("det(L) close to 0!\n Can't divide by 0...\n");
				exit(EXIT_FAILURE);
			}
			U[j][i] = (A[j][i] - sum) / L[j][j];
		}
	}
}
void SplineContainer::Crout_Cut(vector<vector<double> > & A, vector<vector<double> >  &L, vector<vector<double> > &U, int n,int p_ord) {
	int i, j, k;
	double sum = 0;

	for (i = 0; i < n; i++) 
	{
		U[i][i] = 1;
	}
	int p_ord2=2*p_ord+2-p_ord;
	for (j = 0; j < n; j++) 
	{
		int max_n= min(j+p_ord+1,n);
		int min_k= max(j-(p_ord),0);
		for (i = j; i <max_n; i++) 
		{
			sum = 0;
			for (k = min_k; k < j; k++) 
			{
				sum = sum + L[i][k] * U[k][j];	
			}
			L[i][j] = A[i][j] - sum;
		}
		max_n= min(j+p_ord2+1,n);
		//min_k= max(j-(p_ord),0);
		for (i = j; i < max_n; i++) 
		{
			sum = 0;
			for(k = min_k; k < j; k++) 
			{
				sum = sum + L[j][k] * U[k][i];
			}
			if (L[j][j] == 0)
			{
				printf("det(L) close to 0!\n Can't divide by 0...\n");
				exit(EXIT_FAILURE);
			}
			U[j][i] = (A[j][i] - sum) / L[j][j];
		}
	}
}


double  SplineContainer::Determinant(vector<vector<double> > & b,int m)
{
	if (m==0)
		return 1;
	if (m==1)
		return b[0][0];
	//	int i,j;
	double sum = 0;
	vector<vector<double> > c(m-1);
	if(m==2)
	{                                        //BASE CONDITION
		sum = b[0][0]*b[1][1] - b[0][1]*b[1][0];
		return sum;
	}
	vector<vector<double> > L(m);
	vector<vector<double> > U(m);

	//	vector<vector<double> > L_cut(m);
	//vector<vector<double> > U_cut(m);
	double trace=1;
	for(int i=0;i<m;i++)
	{
		trace*=b[i][i];
		L[i].resize(m,0);
		U[i].resize(m,0);
		//L_cut[i].resize(m,0);
		//U_cut[i].resize(m,0);
	}
	if(trace==0)
		return 0;




	//Crout(b,L,U,m);
	/*	for(int i=0;i<m;i++)
	for(int j=0;j<m;j++)
	{
	if()
	}*/
	//	  if(m==nk-1)
	///	cout<<"Crout:\t"<< clock() - t<<endl;
	//t = clock();*/
	//Crout_Cut(b,L_cut,U_cut,m,order); 
	Crout_Cut(b,L,U,m,order); 
	//if(m==nk-1)
	//		cout<<"Crout_cut:\t"<< clock() - t<<endl;
	/*	for(int i=0;i<m;i++)
	for(int j=0;j<m;j++)
	{
	if(fabs (L[i][j]-L_cut[i][j])>1e-6||fabs (U[i][j]-U_cut[i][j])>1e-6)
	cout<<"Crout_bad"<<endl;
	}*/
	double detU=1;
	double detL=1;
	for(int i=0;i<m;i++)
	{
		detL*=L[i][i];
		detU*=U[i][i];
	}
	sum=detU*detL;
	return sum;
}


vector<double>  SplineContainer::Get_Coeff(int k,vector<double> f)
{
	vector<double> res(order+k+2,0);

	vector<double> buf(order+k+2,0);
	for(int i=0;i<f.size();i++)
	{
		diffN(i,2*order+1,k,f[i], buf);
		for (int j=0;j<res.size();j++)
		{
			res[j]+=buf[j];
			buf[j]=0;
		}
	}
	return res;
}
void  SplineContainer::diffN(int i,int p,int k,double mult,vector<double> &ncoeff)
{
	///i;
	double val=mult*p/(tk[k][i+p]-tk[k][i]);
	if (p==order+1)
		ncoeff[i]+=val;
	else
		diffN(i,p-1,k,val,ncoeff);
	///i+1;
	val=-mult*p/(tk[k][i+p+1]-tk[k][i+1]);
	if (p==order+1)
		ncoeff[i+1]+=val;
	else
		diffN(i+1,p-1,k,val,ncoeff);
}

void Spline::GetPPCoeff(int i,int p,double mult,vector<double> &fortensor,bool wr1)
{

	vector<double> fortensor2(fortensor);
	int level=order-p;
	///i;
	if(fortensor2.size()==0)
	{
		coefs[0][0]=1;
		return ;
	}
	fortensor2[level]=breaks[i];
	if(breaks[i+p]!=breaks[i])
	{
		double val=mult*1/(breaks[i+p]-breaks[i]);
		if (p==1)
		{
			clock_t t;
			t = clock();
			SetOneBreak(i,val,fortensor2);
			if(wr1)
				cout<<"SetOneBreak1:\t"<< clock() - t<<endl;
		}
		else
			GetPPCoeff(i,p-1,val,fortensor2,wr1);
	}



	///i+1;

	fortensor2[level]=breaks[i+1+p];
	if(breaks[i+1+p]!=breaks[i+1])

	{
		double val=-mult*1/(breaks[i+1+p]-breaks[i+1]);
		if (p==1)
		{
			clock_t t;
			t = clock();
			SetOneBreak(i+1, val,fortensor2);
			if(wr1)
				cout<<"SetOneBreak2:\t"<< clock() - t<<endl;

		}
		else
			GetPPCoeff(i+1,p-1,val,fortensor2,wr1);
	}
}

void Spline::SetOneBreak(int i,double mult,vector<double> &fortensor)
{
	int n_vec= (int)fortensor.size();
	int Ntensor=(int)pow(2.0,n_vec);
	vector<double> tensor(n_vec,0);

	vector<int> indexes(n_vec);
	//tensor
	for (int p=0;p<Ntensor;p++)
	{
		ind2sub(p,indexes,2);
		double val=1;
		int ind=0;
		for (int j=0;j<n_vec;j++)
		{
			if(indexes[j]>0)
				val*=(breaks[i]-fortensor[j]);
			ind+=indexes[j];
		}
		coefs[i][ind]+=val*mult;

	}

}
void Spline::ind2sub(int ind,vector<int> &sub,int Nx)
{
	int buf=1;
	for (int i=0;i<sub.size();i++)
	{
		sub[i]=ind%Nx;
		ind/=Nx;
	}
}
int Spline:: binary_search(Double1Vec& A, double x, int imin, int imax)
{
	int imid= (imin+imax)/2;
	// continue searching while [imin,imax] is not empty
	while (imax > imin+1)
	{
		// calculate the midpoint for roughly equal partition
		imid= (imin+imax)/2;
		if(A[imid] < x)
			// change min index to search upper subarray
			imin = imid ;
		else         
			// change max index to search lower subarray
			imax = imid;
	}

	// assert(A[imin]<=x&&x<=A[imin+1]);
	// key was not found
	return imin;
}
double Spline::GetVal(double x,int ingrl)
{
	if (x<breaks[0])
		return 0;
	if (x>breaks[breaks.size()-1])
	{
		if(ingrl<1)
			return 0;
		else
			return GetVal(breaks[breaks.size()-1], (int)breaks.size()-1-1,ingrl);
	}
	int i=binary_search(breaks, x,0, (int)breaks.size()-1);
	return GetVal(x,i,ingrl);
	for (int i=1;i<breaks.size();i++)
	{
		if(x<=breaks[i])
			return GetVal(x,i-1,ingrl);
	}
	return 0;
}
double Spline::GetVal(double x,int i,int ingrl)
{
	double val=0;
	double xx=x-breaks[i];
	double xpow=1;
	if(ingrl<1)
	{

		for (int j= (int)coefs[i].size()-1;j>=0;j--)
		{
			val+=coefs[i][j]*xpow;
			xpow*=xx;
		}
		return val;
	}else
	{
		val=coefs_bias[i];
		for (int j= (int)coefs_integral[i].size()-1;j>=0;j--)
		{
			val+=coefs_integral[i][j]*xpow;
			xpow*=xx;
		}
	}

	return val;
}
void  Spline::Create(int ord)
{
	a=-1;
	b=1;
	order=ord;
	breaks.resize(order+1);
	fill(breaks.begin(),breaks.end(),0);
	coefs.resize((breaks.size()-1));
	for (int i=0;i<coefs.size();i++)
	{
		coefs[i].resize(order+1);
		fill(coefs[i].begin(),coefs[i].end(),0);
	}
}
void Spline::Create(vector<double> t,int i0,int p,int ord)
{

	order=ord;
	int i1=i0+p+1;
	breaks.resize(p+2);
	for(int i=i0;i<=i1;i++)
	{
		breaks[i-i0]=t[i];
	}
	a=breaks[0];
	b=breaks[breaks.size()-1];
	coefs.resize((breaks.size()-1));
	for (int i=0;i<coefs.size();i++)
	{
		if(breaks[i+1]!=breaks[i])
		{
			coefs[i].resize(order+1);
			fill(coefs[i].begin(),coefs[i].end(),0);
		}
	}

}
void Spline::GetSpline(int i0,bool wr1)
{

	vector<double> fortensor(order);

	GetPPCoeff(i0,order,1,fortensor, wr1);
}
void Spline::SetLegendre(int nn)
{
	vector<double> leg_coeffs=Tools::Legendre_coeff(nn);

	double shft_mult=1;
	double dmult=2/(b-a);
	double val=0;
	double oldbreak=(a+b)/2;

	for (int i=0;i<leg_coeffs.size();i++)
	{
		for (int j=i-1;j>=0;j--)
		{
			coefs[0][j+1]=coefs[0][j];
		}
		coefs[0][0]=0;
		val=leg_coeffs[i]*shft_mult*sqrt(nn+0.5);
		shft_mult*=-dmult;
		vector<double> fortensor(i,oldbreak);
		SetOneBreak(0,val,fortensor);
	}


}
