#include <iostream>
#include <string>
#include <fstream>
#include <boost/algorithm/string.hpp>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <string>
#include <iostream>
#include <experimental/filesystem>
#include <stdio.h>
#include <dirent.h>
using namespace std;

/* ------------------------------ xyz2pcd ---------------------------------- */

// int main(int argc, char *argv[])
// {
//     vector<string> result;
//     pcl::PointCloud<pcl::PointXYZRGB> cloud;
//     pcl::PointXYZRGB p;
//     string filename = "";

//     // cout << "[*] This tool will convert your .xyz with rgb information into .pcd v7 file." << endl;
//     // cout << "[*] Write the .xyz filename, e.g. test.xyz : " << endl;
//     // cin >> filename;

//     // ifstream xyz_file(filename);
//     // //Check that the input file has being successfully opened
//     // if (!(xyz_file.is_open()))
//     // {
//     //     cout << "[x] Cannot open file!" << endl;
//     // }

//     struct dirent *entry = nullptr;
//     DIR *dp = nullptr;

//     dp = opendir(argc > 1 ? argv[1] : "/home/rambo/pvcnn/data/own/02958343/");
//     if (dp != nullptr)
//     {
//         while ((entry = readdir(dp)))
//         {
//             std::string path = "/home/rambo/pvcnn/data/own/02958343/";
//             std::string filename = path + entry->d_name;

//             if (filename.find(".txt") != std::string::npos)
//             {
//                 string content = "";
//                 int i = 0;

//                 ifstream xyz_file(filename);

//                 while (getline(xyz_file, content))
//                 {
//                     boost::split(result, content, [](char c) { return c == ' '; });
//                     //        cout << "x: " << result[0] << " y: " << result[1] << " z: " << result[2] << endl;
//                     p.x = stof(result[0]);
//                     p.y = stof(result[1]);
//                     p.z = stof(result[2]);
//                     // p.r = stof(result[3]);
//                     // p.g = stof(result[4]);
//                     // p.b = stof(result[5]);
//                     cloud.points.push_back(p);
//                     i++;
//                 }

//                 cloud.width = cloud.points.size();
//                 cout << "Number of points:" << cloud.width << endl;
//                 cloud.height = 1;
//                 cloud.points.resize(cloud.width * cloud.height);
//                 cloud.is_dense = false;
//                 xyz_file.close();
//                 filename = filename.substr(0, filename.size() - 3);
//                 printf ("Name: %s \n", filename.c_str());
//                 pcl::io::savePCDFileASCII(filename + "pcd", cloud);
//                 cout << "[*] Conversion finished!" << endl;
//                 cloud.clear();
//             }
//         }
//     }
// }

/* ------------------------------ pcd2xyz ---------------------------------- */
int main(int argc, const char**argv)
{
    struct dirent *entry = nullptr;
    DIR *dp = nullptr;

    dp = opendir(argc > 1 ? argv[1] : "/home/rambo/pvcnn/data/own/02691156/depth/pcd/");
    if (dp != nullptr)
    {
        while ((entry = readdir(dp)))
        {
            std::string path = "/home/rambo/pvcnn/data/own/02691156/depth/pcd/";
            std::string filename = path + entry->d_name;

            if (filename.find(".pcd") != std::string::npos)
            {
                printf ("Name: %s \n", filename.c_str());

                pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

                // cout << "[*] Write the .pcd filename " << endl;
                // std::cin >> filename;
                pcl::PCDReader reader;
                reader.read(filename.c_str(), *cloud);
                filename = filename.substr(0, filename.size() - 3);
                ofstream myfile;
                myfile.open(filename + "xyz");
                for (int i = 0; i < cloud->size(); i++)
                {
                    myfile << cloud->points[i].x << " " << cloud->points[i].y << " " << cloud->points[i].z << "\n";
                }
                //myfile << "Writing this to a file.\n";
                myfile.close();
            }
        }
    }
    closedir(dp);

    cout << "[*] Conversion finished!" << endl;
    return 0;
}

// int main(int argc, const char**argv) {
//     struct dirent *entry = nullptr;
//     DIR *dp = nullptr;

//     dp = opendir(argc > 1 ? argv[1] : "/home/rambo/pvcnn/data/own/02691156/");
//     if (dp != nullptr)
//     {
//         while ((entry = readdir(dp)))
//         {
//             std::string path = "/home/rambo/pvcnn/data/own/02691156/";
//             std::string test = path + entry->d_name;
//             printf ("%s \n", test.c_str());
//             // strcat("/home/rambo/pvcnn/data/own/cylinder/pcd/" + entry->d_name)
//             // printf ("%s\n", "/home/rambo/pvcnn/data/own/cylinder/pcd/" + entry->d_name);
//         }
//     }

//     closedir(dp);
//     return 0;
// }


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

#include <boost/thread.hpp>
#include <boost/bind.hpp>

#include <time.h>
#include <math.h>
#include <wx/filename.h>
#include <wx/stopwatch.h>
#include "../DataViewer/TableInterface.h"
#include "../ShapeOperations/RateSmoothing.h"
#include "../ShapeOperations/Randik.h"
#include "../ShapeOperations/WeightsManState.h"
#include "../ShapeOperations/WeightUtils.h"
#include "../VarCalc/WeightsManInterface.h"
#include "../logger.h"
#include "../Project.h"
#include "LisaCoordinatorObserver.h"
#include "LisaCoordinator.h"

LisaWorkerThread::LisaWorkerThread(const GalElement* W_,
                                   const std::vector<bool>& undefs_,
                                   int obs_start_s, int obs_end_s,
								   uint64_t	seed_start_s,
								   LisaCoordinator* lisa_coord_s,
								   wxMutex* worker_list_mutex_s,
								   wxCondition* worker_list_empty_cond_s,
								   std::list<wxThread*> *worker_list_s,
								   int thread_id_s)
: wxThread(),
W(W_),
undefs(undefs_),
obs_start(obs_start_s), obs_end(obs_end_s), seed_start(seed_start_s),
lisa_coord(lisa_coord_s),
worker_list_mutex(worker_list_mutex_s),
worker_list_empty_cond(worker_list_empty_cond_s),
worker_list(worker_list_s),
thread_id(thread_id_s)
{
}

LisaWorkerThread::~LisaWorkerThread()
{
}

wxThread::ExitCode LisaWorkerThread::Entry()
{
	LOG_MSG(wxString::Format("LisaWorkerThread %d started", thread_id));

	// call work for assigned range of observations
	lisa_coord->CalcPseudoP_range(W, undefs, obs_start, obs_end, seed_start);

	wxMutexLocker lock(*worker_list_mutex);

	// remove ourself from the list
	worker_list->remove(this);

    // if empty, signal on empty condition since only main thread
	// should be waiting on this condition
	if (worker_list->empty()) {
		worker_list_empty_cond->Signal();
	}

	return NULL;
}

/**
 Since the user has the ability to synchronise either variable over time,
 we must be able to reapply weights and recalculate lisa values as needed.

 1. We will have original data as complete space-time data for both variables

 2. From there we will work from info in var_info for both variables.  Must
    determine number of time_steps for canvas.

 3. Adjust data1(2)_vecs sizes and initialize from data.

 3.5. Resize localMoran, sigLocalMoran, sigCat, and cluster arrays

 4. If rates, then calculate rates for working_data1

 5. Standardize working_data1 (and 2 if bivariate)

 6. Compute LISA for all time-stesp and save in localMoran sp/time array

 7. Calc Pseudo P for all time periods.  Results saved in sigLocalMoran,
    sigCat and cluster arrays

 8. Notify clients that values have been updated.

 */

LisaCoordinator::
LisaCoordinator(boost::uuids::uuid weights_id,
                Project* project,
                const std::vector<GdaVarTools::VarInfo>& var_info_s,
                const std::vector<int>& col_ids,
                LisaType lisa_type_s,
                bool calc_significances_s,
                bool row_standardize_s)
: w_man_state(project->GetWManState()),
w_man_int(project->GetWManInt()),
w_id(weights_id),
num_obs(project->GetNumRecords()),
permutations(999),
lisa_type(lisa_type_s),
calc_significances(calc_significances_s),
isBivariate(lisa_type_s == bivariate),
var_info(var_info_s),
data(var_info_s.size()),
undef_data(var_info_s.size()),
last_seed_used(0), reuse_last_seed(false),
row_standardize(row_standardize_s)
{
    reuse_last_seed = GdaConst::use_gda_user_seed;
    if ( GdaConst::use_gda_user_seed) {
        last_seed_used = GdaConst::gda_user_seed;
    }

	TableInterface* table_int = project->GetTableInt();
	for (int i=0; i<var_info.size(); i++) {
		table_int->GetColData(col_ids[i], data[i]);
        table_int->GetColUndefined(col_ids[i], undef_data[i]);
        var_info[i].is_moran = true;
	}

    undef_tms.resize(var_info_s[0].time_max - var_info_s[0].time_min + 1);

	weight_name = w_man_int->GetLongDispName(w_id);

    weights = w_man_int->GetGal(w_id);

	SetSignificanceFilter(1);

	InitFromVarInfo();
	w_man_state->registerObserver(this);
}

LisaCoordinator::
LisaCoordinator(wxString weights_path,
                int n,
                std::vector<double> vals_1,
                std::vector<double> vals_2,
                int lisa_type_s,
                int permutations_s,
                bool calc_significances_s,
                bool row_standardize_s)
{
    num_obs = n;
    num_time_vals = 1;
    permutations = permutations_s;
    calc_significances = calc_significances_s;
    row_standardize = row_standardize_s;
    last_seed_used = 0;
    reuse_last_seed = false;
    isBivariate = false;

    // std::vector<GdaVarTools::VarInfo> var_info;
    int num_vars = 1;
    isBivariate = false;

    if (lisa_type_s == 0) {
        lisa_type = univariate;

    } else if (lisa_type_s == 1) {
        lisa_type = bivariate;
        isBivariate = true;
        num_vars = 2;

    } else if (lisa_type_s == 2) {
        lisa_type = eb_rate_standardized;
        num_vars = 2;

    } else if (lisa_type_s == 3) {
        lisa_type = differential;
        num_vars = 2;
    }

    undef_tms.resize(num_time_vals);
    data.resize(num_vars);
    undef_data.resize(num_vars);
    var_info.resize(num_vars);

    // don't handle time variable for now
    for (int i=0; i<var_info.size(); i++) {
        data[i].resize(boost::extents[num_time_vals][num_obs]);
        undef_data[i].resize(boost::extents[num_time_vals][num_obs]);
        var_info[i].is_moran = true;
        var_info[i].is_time_variant = false;
        var_info[i].fixed_scale = true;
        var_info[i].sync_with_global_time  = false;
        var_info[i].time_max = 0;
        var_info[i].time_min = 0;
    }

    for (int i=0; i<num_obs; i++) {
        data[0][0][i] = vals_1[i];
        undef_data[0][0][i] = false;
    }
    if (num_vars == 2) {
        for (int i=0; i<num_obs; i++) {
            data[1][0][i] = vals_1[i];
            undef_data[1][0][i] = false;
        }
    }

    // create weights
    w_man_state = NULL;
    w_man_int = NULL;

    wxString ext = GenUtils::GetFileExt(weights_path).Lower();
    GalElement* tempGal = 0;
    if (ext == "gal") {
        tempGal = WeightUtils::ReadGal(weights_path, NULL);
    } else {
        tempGal = WeightUtils::ReadGwtAsGal(weights_path, NULL);
    }

    weights = new GalWeight();
    weights->num_obs = num_obs;
    weights->wflnm = weights_path;
    weights->id_field = "ogc_fid";
    weights->gal = tempGal;

    SetSignificanceFilter(1);
    InitFromVarInfo();
}

LisaCoordinator::~LisaCoordinator()
{
    if (w_man_state) {
        w_man_state->removeObserver(this);
    }
	DeallocateVectors();
}

void LisaCoordinator::DeallocateVectors()
{
	for (int i=0; i<lags_vecs.size(); i++) {
		if (lags_vecs[i]) delete [] lags_vecs[i];
	}
	lags_vecs.clear();

	for (int i=0; i<local_moran_vecs.size(); i++) {
		if (local_moran_vecs[i]) delete [] local_moran_vecs[i];
	}
	local_moran_vecs.clear();
	for (int i=0; i<sig_local_moran_vecs.size(); i++) {
		if (sig_local_moran_vecs[i]) delete [] sig_local_moran_vecs[i];
	}
	sig_local_moran_vecs.clear();
	for (int i=0; i<sig_cat_vecs.size(); i++) {
		if (sig_cat_vecs[i]) delete [] sig_cat_vecs[i];
	}
	sig_cat_vecs.clear();
	for (int i=0; i<cluster_vecs.size(); i++) {
		if (cluster_vecs[i]) delete [] cluster_vecs[i];
	}
	cluster_vecs.clear();
	for (int i=0; i<data1_vecs.size(); i++) {
		if (data1_vecs[i]) delete [] data1_vecs[i];
	}
	data1_vecs.clear();
	for (int i=0; i<data2_vecs.size(); i++) {
		if (data2_vecs[i]) delete [] data2_vecs[i];
	}
	data2_vecs.clear();

    // clear W_vecs
    for (size_t i=0; i<has_undefined.size(); i++) {
        if (has_undefined[i]) {
            // clean the copied weights
            delete Gal_vecs[i];
        }
    }
    Gal_vecs.clear();
}

/** allocate based on var_info and num_time_vals **/
void LisaCoordinator::AllocateVectors()
{
	int tms = num_time_vals;

	lags_vecs.resize(tms);
	local_moran_vecs.resize(tms);
	sig_local_moran_vecs.resize(tms);
	sig_cat_vecs.resize(tms);
	cluster_vecs.resize(tms);
	data1_vecs.resize(tms);
	map_valid.resize(tms);
	map_error_message.resize(tms);
	has_isolates.resize(tms);
	has_undefined.resize(tms);

	for (int i=0; i<tms; i++) {
		lags_vecs[i] = new double[num_obs];
		local_moran_vecs[i] = new double[num_obs];
		if (calc_significances) {
			sig_local_moran_vecs[i] = new double[num_obs];
			sig_cat_vecs[i] = new int[num_obs];
		}
		cluster_vecs[i] = new int[num_obs];
		data1_vecs[i] = new double[num_obs];
		map_valid[i] = true;
		map_error_message[i] = wxEmptyString;
	}

	if (lisa_type == bivariate) {
		data2_vecs.resize((var_info[1].time_max - var_info[1].time_min) + 1);
		for (int i=0; i<data2_vecs.size(); i++) {
			data2_vecs[i] = new double[num_obs];
		}
	}
}

/** We assume only that var_info is initialized correctly.
 ref_var_index, is_any_time_variant, is_any_sync_with_global_time and
 num_time_vals are first updated based on var_info */
void LisaCoordinator::InitFromVarInfo()
{
	DeallocateVectors();

	num_time_vals = 1;
    is_any_time_variant = false;
    is_any_sync_with_global_time = false;
    ref_var_index = -1;

    if (lisa_type != differential) {
        for (int i=0; i<var_info.size(); i++) {
            if (var_info[i].is_time_variant && var_info[i].sync_with_global_time) {
                num_time_vals = (var_info[i].time_max - var_info[i].time_min) + 1;
                is_any_sync_with_global_time = true;
                ref_var_index = i;
                break;
            }
        }
        for (int i=0; i<var_info.size(); i++) {
            if (var_info[i].is_time_variant) {
                is_any_time_variant = true;
                break;
            }
        }
    }

	AllocateVectors();

    if (lisa_type == differential) {
        int t=0;
        for (int i=0; i<num_obs; i++) {
            int t0 = var_info[0].time;
            int t1 = var_info[1].time;
            data1_vecs[0][i] = data[0][t0][i] - data[0][t1][i];
        }

    } else if (lisa_type == univariate || lisa_type == bivariate) {
		for (int t=var_info[0].time_min; t<=var_info[0].time_max; t++) {
			int d1_t = t - var_info[0].time_min;
            for (int i=0; i<num_obs; i++) {
                data1_vecs[d1_t][i] = data[0][t][i];
            }
		}
		if (lisa_type == bivariate) {
			for (int t=var_info[1].time_min; t<=var_info[1].time_max; t++) {
				int d2_t = t - var_info[1].time_min;
				for (int i=0; i<num_obs; i++) {
					data2_vecs[d2_t][i] = data[1][t][i];
				}
			}
		}
	} else { // lisa_type == eb_rate_standardized
		std::vector<bool> undef_res(num_obs, false);
		double* smoothed_results = new double[num_obs];
		double* E = new double[num_obs]; // E corresponds to var_info[0]
		double* P = new double[num_obs]; // P corresponds to var_info[1]
		// we will only fill data1 for eb_rate_standardized and
		// further lisa calcs will treat as univariate
		for (int t=0; t<num_time_vals; t++) {
			int v0_t = var_info[0].time_min;
			if (var_info[0].is_time_variant &&
				var_info[0].sync_with_global_time) {
				v0_t += t;
			}
			for (int i=0; i<num_obs; i++) E[i] = data[0][v0_t][i];
			int v1_t = var_info[1].time_min;
			if (var_info[1].is_time_variant &&
				var_info[1].sync_with_global_time) {
				v1_t += t;
			}
			for (int i=0; i<num_obs; i++) P[i] = data[1][v1_t][i];
			bool success = GdaAlgs::RateStandardizeEB(num_obs, P, E,
														smoothed_results,
														undef_res);
			if (!success) {
				map_valid[t] = false;
				map_error_message[t] << "Emprical Bayes Rate ";
				map_error_message[t] << "Standardization failed.";
			} else {
				for (int i=0; i<num_obs; i++) {
					data1_vecs[t][i] = smoothed_results[i];
				}
			}
		}
		if (smoothed_results) delete [] smoothed_results;
		if (E) delete [] E;
		if (P) delete [] P;
	}

	StandardizeData();

    CalcLisa();

    if (calc_significances) {
        CalcPseudoP();
    }

}

void LisaCoordinator::GetRawData(int time, double* data1, double* data2)
{
    if (lisa_type == differential) {
        int t=0;
        for (int i=0; i<num_obs; i++) {
            int t0 = var_info[0].time;
            int t1 = var_info[1].time;
            data1[i] = data[0][t0][i] - data[0][t1][i];
        }

    } else if (lisa_type == univariate || lisa_type == bivariate) {
        for (int i=0; i<num_obs; i++) {
            data1[i] = data[0][time][i];
        }
        if (lisa_type == bivariate) {
            for (int i=0; i<num_obs; i++) {
                data2[i] = data[1][time][i];
            }
        }
    } else { // lisa_type == eb_rate_standardized
        std::vector<bool> undef_res(num_obs, false);
        double* smoothed_results = new double[num_obs];
        double* E = new double[num_obs]; // E corresponds to var_info[0]
        double* P = new double[num_obs]; // P corresponds to var_info[1]
        // we will only fill data1 for eb_rate_standardized and
        // further lisa calcs will treat as univariate
        for (int i=0; i<num_obs; i++) {
            E[i] = data[0][time][i];
        }
        for (int i=0; i<num_obs; i++) {
            P[i] = data[1][time][i];
        }
        bool success = GdaAlgs::RateStandardizeEB(num_obs, P, E,
                                                  smoothed_results,
                                                  undef_res);
        if (success) {
            for (int i=0; i<num_obs; i++) {
                data1[i] = smoothed_results[i];
            }
        }
        if (smoothed_results) delete [] smoothed_results;
        if (E) delete [] E;
        if (P) delete [] P;
    }
}

/** Update Secondary Attributes based on Primary Attributes.
 Update num_time_vals and ref_var_index based on Secondary Attributes. */
void LisaCoordinator::VarInfoAttributeChange()
{
	GdaVarTools::UpdateVarInfoSecondaryAttribs(var_info);

	is_any_time_variant = false;
	is_any_sync_with_global_time = false;
	for (int i=0; i<var_info.size(); i++) {
		if (var_info[i].is_time_variant) is_any_time_variant = true;
		if (var_info[i].sync_with_global_time) {
			is_any_sync_with_global_time = true;
		}
	}
	ref_var_index = -1;
	num_time_vals = 1;
	for (int i=0; i<var_info.size() && ref_var_index == -1; i++) {
		if (var_info[i].is_ref_variable) ref_var_index = i;
	}
	if (ref_var_index != -1) {
		num_time_vals = (var_info[ref_var_index].time_max -
						 var_info[ref_var_index].time_min) + 1;
	}
	//GdaVarTools::PrintVarInfoVector(var_info);
}

void LisaCoordinator::StandardizeData()
{
	for (int t=0; t<data1_vecs.size(); t++) {
        undef_tms[t].resize(num_obs);

        for (int i=0; i<num_obs; i++) {
            undef_tms[t][i] = undef_tms[t][i] || undef_data[0][t][i];
        }
        if (isBivariate) {
            for (int i=0; i<num_obs; i++) {
                if ( undef_data[1].size() > t ) {
                    undef_tms[t][i] = undef_tms[t][i] || undef_data[1][t][i];
                }
            }
        }
    }

	for (int t=0; t<data1_vecs.size(); t++) {
		GenUtils::StandardizeData(num_obs, data1_vecs[t], undef_tms[t]);
        if (isBivariate) {
            if (data2_vecs.size() > t)
                GenUtils::StandardizeData(num_obs, data2_vecs[t], undef_tms[t]);
        }
	}
}

/** assumes StandardizeData already called on data1 and data2 */
void LisaCoordinator::CalcLisa()
{
	for (int t=0; t<num_time_vals; t++) {
		data1 = data1_vecs[t];
		if (isBivariate) {
			data2 = data2_vecs[0];
			if (var_info[1].is_time_variant && var_info[1].sync_with_global_time)
                data2 = data2_vecs[t];
		}
		lags = lags_vecs[t];
		localMoran = local_moran_vecs[t];
		cluster = cluster_vecs[t];

		has_isolates[t] = false;

        // get undefs of objects/values at this time step
        std::vector<bool> undefs;
        bool has_undef = false;
        for (int i=0; i<undef_data[0][t].size(); i++){
            bool is_undef = undef_data[0][t][i];
            if (isBivariate) {
                if (undef_data[1].size() > t)
                    is_undef = is_undef || undef_data[1][t][i];
            }
            if (is_undef && !has_undef) {
                has_undef = true;
            }
            undefs.push_back(is_undef);
        }
        has_undefined[t] = has_undef;

        // local weights copy
        GalWeight* gw = NULL;
        if ( has_undef ) {
            gw = new GalWeight(*weights);
            gw->Update(undefs);
        } else {
            gw = weights;
        }
        GalElement* W = gw->gal;
        Gal_vecs.push_back(gw);
        Gal_vecs_orig.push_back(weights);

		for (int i=0; i<num_obs; i++) {

            if (undefs[i] == true) {
                lags[i] = 0;
                localMoran[i] = 0;
                cluster[i] = 6; // undefined value
                continue;
            }

			double Wdata = 0;
			if (isBivariate) {
				Wdata = W[i].SpatialLag(data2);
			} else {
				Wdata = W[i].SpatialLag(data1);
			}
			lags[i] = Wdata;
			localMoran[i] = data1[i] * Wdata;

			// assign the cluster
			if (W[i].Size() > 0) {
				if (data1[i] > 0 && Wdata < 0) cluster[i] = 4;
				else if (data1[i] < 0 && Wdata > 0) cluster[i] = 3;
				else if (data1[i] < 0 && Wdata < 0) cluster[i] = 2;
				else cluster[i] = 1; //data1[i] > 0 && Wdata > 0
			} else {
				has_isolates[t] = true;
				cluster[i] = 5; // neighborless
			}
		}
	}
}

void LisaCoordinator::CalcPseudoP()
{
	if (!calc_significances) return;
	wxStopWatch sw;
	int nCPUs = wxThread::GetCPUCount();

	// To ensure thread safety, only work on one time slice of data
	// at a time.  For each time period t:
	// 1. copy data for time period t into data1 and data2 arrays
	// 2. Perform multi-threaded computation
	// 3. copy results into results array

	for (int t=0; t<num_time_vals; t++) {

        std::vector<bool>& undefs = undef_tms[t];
		data1 = data1_vecs[t];
		if (isBivariate) {
			data2 = data2_vecs[0];
			if (var_info[1].is_time_variant &&
				var_info[1].sync_with_global_time)
                data2 = data2_vecs[t];
		}
		lags = lags_vecs[t];
		localMoran = local_moran_vecs[t];
		sigLocalMoran = sig_local_moran_vecs[t];
		sigCat = sig_cat_vecs[t];
		cluster = cluster_vecs[t];

		if (nCPUs <= 1 || num_obs <= nCPUs * 10) {
            if (!reuse_last_seed) {
                last_seed_used = time(0);
            }
			CalcPseudoP_range(Gal_vecs[t]->gal, undefs,
                              0, num_obs-1, last_seed_used);
		} else {
			CalcPseudoP_threaded(Gal_vecs[t]->gal, undefs);
			//CalcPseudoP_range(Gal_vecs[t]->gal, undefs,
                        //      0, num_obs-1, last_seed_used);
		}
	}

	{
		wxString m;
		m << "LISA on " << num_obs << " obs with " << permutations;
		m << " perms over " << num_time_vals << " time periods took ";
		m << sw.Time() << " ms. Last seed used: " << last_seed_used;
	}
	LOG_MSG("Exiting LisaCoordinator::CalcPseudoP");
}

void LisaCoordinator::CalcPseudoP_threaded(const GalElement* W,
                                           const std::vector<bool>& undefs)
{
	int nCPUs = wxThread::GetCPUCount();

	// mutext protects access to the worker_list
    wxMutex worker_list_mutex;
	// signals that worker_list is empty
	wxCondition worker_list_empty_cond(worker_list_mutex);
    // mutex should be initially locked
	worker_list_mutex.Lock();

    // List of all the threads currently alive.  As soon as the thread
	// terminates, it removes itself from the list.
	std::list<wxThread*> worker_list;

	// divide up work according to number of observations
	// and number of CPUs
	int work_chunk = num_obs / nCPUs;

    if (work_chunk == 0) {
        work_chunk = 1;
    }

	int obs_start = 0;
	int obs_end = obs_start + work_chunk;

	bool is_thread_error = false;
	int quotient = num_obs / nCPUs;
	int remainder = num_obs % nCPUs;
	int tot_threads = (quotient > 0) ? nCPUs : remainder;

    boost::thread_group threadPool;

	if (!reuse_last_seed)
        last_seed_used = time(0);
	for (int i=0; i<tot_threads && !is_thread_error; i++) {
		int a=0;
		int b=0;
		if (i < remainder) {
			a = i*(quotient+1);
			b = a+quotient;
		} else {
			a = remainder*(quotient+1) + (i-remainder)*quotient;
			b = a+quotient-1;
		}
		uint64_t seed_start = last_seed_used+a;
		uint64_t seed_end = seed_start + ((uint64_t) (b-a));
		int thread_id = i+1;
		wxString msg;
		msg << "thread " << thread_id << ": " << a << "->" << b;
		msg << ", seed: " << seed_start << "->" << seed_end;

        /*
		LisaWorkerThread* thread =
			new LisaWorkerThread(W, undefs, a, b, seed_start, this,
								 &worker_list_mutex,
								 &worker_list_empty_cond,
								 &worker_list, thread_id);
		if ( thread->Create() != wxTHREAD_NO_ERROR ) {
			delete thread;
			is_thread_error = true;
		} else {
			worker_list.push_front(thread);
		}
         */
        boost::thread* worker = new boost::thread(boost::bind(&LisaCoordinator::CalcPseudoP_range,this, W, undefs, a, b, seed_start));
        threadPool.add_thread(worker);
	}
    threadPool.join_all();
    /*
	if (is_thread_error) {
		// fall back to single thread calculation mode
		CalcPseudoP_range(W, undefs, 0, num_obs-1, last_seed_used);
	} else {
		std::list<wxThread*>::iterator it;
		for (it = worker_list.begin(); it != worker_list.end(); it++) {
			(*it)->Run();
		}

		while (!worker_list.empty()) {
			// wait until thread_list might be empty
			worker_list_empty_cond.Wait();
			// We have been woken up. If this was not a false
			// alarm (sprious signal), the loop will exit.
		}
	}
     */
}

void LisaCoordinator::CalcPseudoP_range(const GalElement* W,
                                        const std::vector<bool>& undefs,
                                        int obs_start, int obs_end,
										uint64_t seed_start)
{
	GeoDaSet workPermutation(num_obs);
	//Randik rng;
	int max_rand = num_obs-1;
	for (int cnt=obs_start; cnt<=obs_end; cnt++) {

        if (undefs[cnt])
            continue;

		const int numNeighbors = W[cnt].Size();

		uint64_t countLarger = 0;
		for (int perm=0; perm<permutations; perm++) {
			int rand=0;
			while (rand < numNeighbors) {
				// computing 'perfect' permutation of given size
                double rng_val = Gda::ThomasWangHashDouble(seed_start++) * max_rand;
                // round is needed to fix issue
                //https://github.com/GeoDaCenter/geoda/issues/488
				int newRandom = (int) (rng_val < 0.0 ? ceil(rng_val - 0.5) : floor(rng_val + 0.5));
				if (newRandom != cnt &&
                    !workPermutation.Belongs(newRandom) &&
                    undefs[newRandom] == false)
				{
					workPermutation.Push(newRandom);
					rand++;
				}
			}
			double permutedLag=0;
			// use permutation to compute the lag
			// compute the lag for binary weights
			if (isBivariate) {
				for (int cp=0; cp<numNeighbors; cp++) {
					permutedLag += data2[workPermutation.Pop()];
				}
			} else {
				for (int cp=0; cp<numNeighbors; cp++) {
					permutedLag += data1[workPermutation.Pop()];
				}
			}

			//NOTE: we shouldn't have to row-standardize or
			// multiply by data1[cnt]
            if (numNeighbors && row_standardize) {
                permutedLag /= numNeighbors;
            }
			const double localMoranPermuted = permutedLag * data1[cnt];
            if (localMoranPermuted >= localMoran[cnt]) {
                countLarger++;
            }
		}
		// pick the smallest
		if (permutations-countLarger <= countLarger) {
			countLarger = permutations-countLarger;
		}

		sigLocalMoran[cnt] = (countLarger+1.0)/(permutations+1);
		// 'significance' of local Moran
		if (sigLocalMoran[cnt] <= 0.0001) sigCat[cnt] = 4;
		else if (sigLocalMoran[cnt] <= 0.001) sigCat[cnt] = 3;
		else if (sigLocalMoran[cnt] <= 0.01) sigCat[cnt] = 2;
		else if (sigLocalMoran[cnt] <= 0.05) sigCat[cnt]= 1;
		else sigCat[cnt]= 0;

		// observations with no neighbors get marked as isolates
        // NOTE: undefined should be marked as well, however, since undefined_cat has covered undefined category, we don't need to handle here
		if (numNeighbors == 0) {
			sigCat[cnt] = 5;
		}
	}
}

void LisaCoordinator::SetSignificanceFilter(int filter_id)
{
	// 0: >0.05 1: 0.05, 2: 0.01, 3: 0.001, 4: 0.0001
	if (filter_id < 1 || filter_id > 4) return;
	significance_filter = filter_id;
	if (filter_id == 1) significance_cutoff = 0.05;
	if (filter_id == 2) significance_cutoff = 0.01;
	if (filter_id == 3) significance_cutoff = 0.001;
	if (filter_id == 4) significance_cutoff = 0.0001;
}

void LisaCoordinator::update(WeightsManState* o)
{
    if (w_man_int) {
        weight_name = w_man_int->GetLongDispName(w_id);
    }
}

int LisaCoordinator::numMustCloseToRemove(boost::uuids::uuid id) const
{
	return id == w_id ? observers.size() : 0;
}

void LisaCoordinator::closeObserver(boost::uuids::uuid id)
{
	if (numMustCloseToRemove(id) == 0) return;
	std::list<LisaCoordinatorObserver*> obs_cpy = observers;
	for (std::list<LisaCoordinatorObserver*>::iterator i=obs_cpy.begin();
		 i != obs_cpy.end(); ++i) {
		(*i)->closeObserver(this);
	}
}

void LisaCoordinator::registerObserver(LisaCoordinatorObserver* o)
{
	observers.push_front(o);
}

void LisaCoordinator::removeObserver(LisaCoordinatorObserver* o)
{
	LOG_MSG("Entering LisaCoordinator::removeObserver");
	observers.remove(o);
	LOG(observers.size());
	if (observers.size() == 0) {
		delete this;
	}
	LOG_MSG("Exiting LisaCoordinator::removeObserver");
}

void LisaCoordinator::notifyObservers()
{
	for (std::list<LisaCoordinatorObserver*>::iterator  it=observers.begin();
		 it != observers.end(); ++it) {
		(*it)->update(this);
	}
}


#include <boost/geometry/strategies/spherical/compare.hpp>


/*
  RTQL8, Copyright (c) 2011 Georgia Tech Graphics Lab
  All rights reserved.

  Author	Sehoon Ha
  Date		06/19/2011
*/

#ifndef DART_OPTIMIZER_SNOPT_SNOPT_INTERFACE_H
#define DART_OPTIMIZER_SNOPT_SNOPT_INTERFACE_H

#include <vector>
#include <Eigen/Dense>
#include "dart/optimizer/OptimizerArrayTypes.h"

#ifndef	ZERO
#define	ZERO 1.0e-30
#endif  // ifndef ZERO

namespace dart {
namespace optimizer {
namespace snopt {

class SnoptInterface {
public:
    enum Return { Solution, UserStop, Error, Stop, Infeasible};
    enum UpdateType { Obj = 1, Constr = 2 };
    enum SlackType { NoSlack = 0, Vslack = 1, Wslack = 2 };
    enum AbnormalType { NONE = 0, INFEASIBLE = 1, HESSIAN_UPDATE = 2, HESSIAN_RESET = 3 };

    struct Slack {
        int constr_idx;
        SlackType type;
        double val;
    };

    typedef int (*updateFunc) (long mask, int compute_gradients, double *coef_values, void *update_data);

    SnoptInterface(int constr_total, int coef_total,
                   int nonlin_constr_total, int nonlin_obj_coef, int nonlin_jac_coef,
                   int *constr_eqns, int has_objective, VVD J, VVB JMap, std::vector<double> *constraints,
                   double *objective, std::vector<double> *gradient,	updateFunc update_f, void *update_d);

    SnoptInterface(int has_objective, VVD J, VVB JMap, std::vector<double> *constraints, double *objective,
                   std::vector<double> *gradient, updateFunc update_f, void *update_d);
    ~SnoptInterface();

    Return solve(double *x, double *lo_bounds, double *hi_bounds, int unit = 4);

    void clear(long mask, int compute_derivs);

    void resizeJacobian(int coef_total, int nonlin_coef_total, int constr_total, int nonlin_constr_total);
    void resizeCoef();

    int mNumConstr;
    int mNumCoef;
    int mNumNonlinConstr;
    int mNumNonlinObjCoef;
    int mNumNonlinJacCoef;

    double *mSolverX;
    double *mProblemX;
    double *mBoundsLo;
    double *mBoundsHi;
    int *mConstrEqns;

    int mHasObjective;

    static SnoptInterface *mRef;

    double *mObj;
    std::vector<double> *mdObjdCoef;

    std::vector<double> *mConstr;
    VVD mdConstrdCoef;

    Eigen::VectorXd mConstrScale;
    Eigen::VectorXd mCoefScale;

    VVB mCoefMap;
    double mReturnedObj;

    int mOutput;
    int mSum;
    bool mCheckTerm;
    bool mTermination;
    AbnormalType mAbnormal;
    int mBreak;

    void updateSolverX();
    void update(long mask, int compute_derivs, double *x);
    static void checkTermination(int *iAbort, double *xs);

protected:
    SnoptInterface::updateFunc mUpdateFunc;
    void *mUpdateData;

    void scaleValues(long update_type, int compute_derivs);

private:
    static void snoptObj(int *mode, int *nn_obj, double *x,
                         double *f_obj, double *g_obj, int *nstate,
                         char *cu, int *lencu, int *iu, int *leniu,
                         double *ru, int *lenru);
    static void snoptJac(int *mode, int *nn_con, int *nn_jac, int *ne_jac,
                         double *x, double *f_con, double *g_con, int *nstate,
                         char *cu, int *lencu, int *iu, int *leniu,
                         double *ru, int *lenru);
    void fillUpSnoptFormat(VVD jacobian, double **a, int **ha, int **ka);
    int sparseCount(int col);

};

} // namespace snopt
} // namespace optimizer
} // namespace dart

#endif // #ifndef DART_OPTIMIZER_SNOPT_SNOPT_INTERFACE_H


/*
 *  @file   rnxToGtsam_singleFreq.cpp
 *  @author Ryan
 *  @brief  Script to convert Rinex File format to format utilized by GTSAM.

 * To do ::
 * Currenlty only works with SP3
 * Need to get nom. pos. from rinex header
 * Get DOY from header for trop estimation
 * need to write to file. Currently only prints to screen.
 */

// GPSTK
#include <gpstk/MJD.hpp>
// #include <gpstk/ModelObs.hpp>
#include <gpstk/PowerSum.hpp>
#include <gpstk/Decimate.hpp>
#include <gpstk/IonoModel.hpp>
#include <gpstk/TropModel.hpp>
#include <gpstk/BasicModel.hpp>
#include <gpstk/CommonTime.hpp>
#include <gpstk/PCSmoother.hpp>
#include <gpstk/IonexModel.hpp>
#include <gpstk/CodeSmoother.hpp>
#include <gpstk/SimpleFilter.hpp>
#include <gpstk/MWCSDetector.hpp>
#include <gpstk/SatArcMarker.hpp>
#include <gpstk/DCBDataReader.hpp>
#include <gpstk/ComputeWindUp.hpp>
#include <gpstk/Rinex3NavData.hpp>
#include <gpstk/GNSSconstants.hpp>
#include <gpstk/ComputeLinear.hpp>
#include <gpstk/GPSWeekSecond.hpp>
#include <gpstk/LICSDetector2.hpp>
#include <gpstk/IonoModelStore.hpp>
#include <gpstk/RinexNavStream.hpp>
#include <gpstk/DataStructures.hpp>
#include <gpstk/Rinex3ObsStream.hpp>
#include <gpstk/Rinex3NavStream.hpp>
#include <gpstk/ComputeIonoModel.hpp>
#include <gpstk/ComputeTropModel.hpp>
#include <gpstk/OneFreqCSDetector.hpp>
#include <gpstk/SP3EphemerisStore.hpp>
#include <gpstk/ComputeSatPCenter.hpp>
#include <gpstk/EclipsedSatFilter.hpp>
#include <gpstk/GPSEphemerisStore.hpp>
#include <gpstk/CorrectCodeBiases.hpp>
#include <gpstk/ComputeSatPCenter.hpp>
#include <gpstk/RequireObservables.hpp>
#include <gpstk/CorrectObservables.hpp>
#include <gpstk/LinearCombinations.hpp>
#include <gpstk/GravitationalDelay.hpp>
#include <gpstk/PhaseCodeAlignment.hpp>

// BOOST
#include <boost/program_options.hpp>

// STD
#include <iomanip>
#include <iostream>

using namespace std;
using namespace gpstk;
using namespace boost;

namespace po = boost::program_options;

int main(int argc, char *argv[])
{

        double break_thresh;
        bool usingP1 = false;
        int dec_int, itsBelowThree = 0, count = 0, break_window;
        string rnx_file, nav_file, sp3_file, out_file, iono_file, brdc_nav_file;

        cout << fixed << setprecision(12); // Set a proper output format

        po::options_description desc("Available options");
        desc.add_options()
                ("help,h", "Print help message")
                ("obs", po::value<string>(&rnx_file)->default_value(""),
                "Observation file to read")
                ("sp3", po::value<string>(&sp3_file)->default_value(""),
                "SP3 file to read.")
                ("brdc_nav", po::value<string>(&brdc_nav_file)->default_value(""),
                "Broadcast nav file to read.")
                ("iono", po::value<string>(&iono_file)->default_value(""),
                "IonoMap file to read.")
                ("break_window",  po::value<int>(&break_window)->default_value(100), "Size of window (in samples) to check for cycle-slips")
                ("break_thresh",  po::value<double>(&break_thresh)->default_value(4.5), "deviation magnitude to classify as phase break")
                ("usingP1", "Are you using C1 instead of P1?")
                ("dec", po::value<int>(&dec_int)->default_value(0),
                "decimate input obs file");
        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);
        po::notify(vm);

        usingP1 = (vm.count("usingP1")>0);

        if ( rnx_file.empty() )
        {
                cout << " Must pass in obs file !!! Try --obs " << desc << endl;
                exit(1);
        }
        if ( nav_file.empty() && sp3_file.empty() )
        {
                cout << " Must pass in ephemeris file !!! Try --sp3 or --nav" << desc << endl;
                exit(1);
        }

        // Create the input observation file stream
        Rinex3ObsStream rin(rnx_file);

        // Declare a "SP3EphemerisStore" object to handle precise ephemeris
        SP3EphemerisStore SP3EphList;

        // Set flags to reject satellites with bad or absent positional
        // values or clocks
        SP3EphList.rejectBadPositions(true);
        SP3EphList.rejectBadClocks(true);

        // Load all the SP3 ephemerides files
        SP3EphList.loadFile(sp3_file);

        // BELL station nominal position
        // Nom. pos. for the greenhouse dataset.
        // Position nominalPos(856514.1467,-4843013.0689, 4047939.8237);
		// Nom. pos. for JRC, Ispra, Italy
        Position nominalPos(4403261.3874,667819.3824,4550797.4440);
        // Nom. pos. for the dec12 dataset.
        // Position nomXYZ(856295.3346, -4843033.4111, 4048017.6649);

        CorrectCodeBiases corrCode;
        // corrCode.setDCBFile("/localhome/ryan_watson/data/p1p2_12.DCB", "/localhome/ryan_watson/data/p1c1_12.DCB");
        corrCode.setDCBFile("/home/altboc/c1/data/shared_data/p1p2_9.DCB", "/home/altboc/c1/data/shared_data/p1c1_9.DCB");

        if (!usingP1) {
                corrCode.setUsingC1(true);
        }

        // This is the GNSS data structure that will hold all the
        // GNSS-related information
        gnssRinex gRin;

        RequireObservables requireObs;
        requireObs.addRequiredType(TypeID::L1);
        // requireObs.addRequiredType(TypeID::L2);

        SimpleFilter pObsFilter;
        pObsFilter.setFilteredType(TypeID::C1);

        if ( usingP1 )
        {
                requireObs.addRequiredType(TypeID::P1);
                pObsFilter.addFilteredType(TypeID::P1);
        }
        else
        {
                requireObs.addRequiredType(TypeID::C1);
                pObsFilter.addFilteredType(TypeID::C1);
        }

        // Object to correct for SP3 Sat Phase-center offset
        AntexReader antexread;
        // antexread.open( "/localhome/ryan_watson/data/antenna_corr.atx" );
        antexread.open( "/home/altboc/c1/data/shared_data/igs14.atx" );
        ComputeSatPCenter svPcenter(SP3EphList, nominalPos);
        svPcenter.setAntexReader( antexread );

        // Declare an object to correct observables
        CorrectObservables corr(SP3EphList);

        // object def several linear combinations
        LinearCombinations comb;

        // Setup single-freq cycle-slip detection
        OneFreqCSDetector markCSC1(TypeID::C1);
        markCSC1.setMaxNumSigmas(break_thresh);
        markCSC1.setMaxWindowSize(break_window);
        markCSC1.setDeltaTMax(0.11);

        // Object to keep track of satellite arcs
        SatArcMarker markArc;
        markArc.setDeleteUnstableSats(true);

        // Objects to compute gravitational delay effects
        GravitationalDelay grDelay(nominalPos);

        // Object to align phase with code measurements
        PhaseCodeAlignment phaseAlign;

        // Object to remove eclipsed satellites
        EclipsedSatFilter eclipsedSV;

        //Object to compute wind-up effect
        ComputeWindUp windup( SP3EphList,
                              nominalPos );

        // Object to compute prefit-residuals
        ComputeLinear linear3(comb.pcPrefit);
        linear3.addLinear(comb.lcPrefit);

        CodeSmoother smoothC1;

        TypeIDSet tset;
        tset.insert(TypeID::prefitC);
        tset.insert(TypeID::prefitL);

        // Declare a NeillTropModel object, setting the defaults
        NeillTropModel neillTM(355, 39.09, 355);
        ComputeTropModel computeTropo(neillTM);

        // Objects to compute the iono data
        // IonexStore IonexMapList;
        // IonexMapList.loadFile(iono_file);
        // IonexModel ionex(nominalPos, IonexMapList);

        ComputeIonoModel computeIono(nominalPos);
        RinexNavHeader rNavHeader;
        RinexNavStream rnavin(brdc_nav_file.c_str());
        rnavin >> rNavHeader;
        computeIono.setKlobucharModel(rNavHeader.ionAlpha,rNavHeader.ionBeta);

        SimpleFilter pcFilter;

        // Declare a couple of basic modelers
        BasicModel basic(nominalPos, SP3EphList);

        // Loop over all data epochs
        while(rin >> gRin)
        {
                CommonTime gpsT(gRin.header.epoch);
                gpsT.setTimeSystem(TimeSystem::GPS);
                GPSWeekSecond gpstime( gpsT );

                try
                {
                        gRin
                        >> requireObs // Check if required observations are present
                        >> pObsFilter // Filter out spurious data
                        >> markCSC1 // Mark cycle slips
                        >> markArc // Keep track of satellite arcs
                        >> basic // Compute the basic components of model
                        >> eclipsedSV // Remove satellites in eclipse
                        >> svPcenter // Computer delta for sat. phase center
                        >> corr // SP3 Corrections
                        >> corrCode // Correct for differential code biases
                        >> windup // phase windup correction
                        >> grDelay // Compute gravitational delay
                        >> computeTropo // Compute slant trop. for L1 --- neill trop function
                        >> computeIono;  // Compute slant iono. for L1
                        // >> phaseAlign
                        // >> pcFilter;  // screen c1

                }
                catch(Exception& e)
                {
                        //cerr << "Exception at epoch: " << time << "; " << e << endl;
                        continue;
                }
                catch(...)
                {
                        cerr << "Unknown exception at epoch: " << time << endl;
                        continue;
                }
                TypeIDSet types;
                types.insert(TypeID::satX);
                types.insert(TypeID::satY);
                types.insert(TypeID::satZ);
                types.insert(TypeID::C1);
                types.insert(TypeID::L1);
                types.insert(TypeID::rho);
                types.insert(TypeID::tropo);
                types.insert(TypeID::tropoSlant);
                types.insert(TypeID::ionoL1);
                types.insert(TypeID::dtSat);
                types.insert(TypeID::rel);
                types.insert(TypeID::gravDelay);
                types.insert(TypeID::instC1);
                types.insert(TypeID::satArc);
                types.insert(TypeID::satPCenter);
                types.insert(TypeID::windUp);
                gRin.keepOnlyTypeID(types);

                // Iterate through the GNSS Data Structure
                satTypeValueMap::const_iterator it;
                typeValueMap::const_iterator itObs;
                if (gRin.numSats() > 3)
                {
                        if ( itsBelowThree > 0 )
                        {
                                itsBelowThree = 0;
                                continue;
                        }
                        for (it = gRin.body.begin(); it!= gRin.body.end(); it++)
                        {
                                cout << gpstime.week << " ";
                                cout << gpstime.sow << " ";
                                cout << count << " ";
                                cout << (*it).first << " ";

                                typeValueMap::const_iterator itObs;
                                for( itObs  = (*it).second.begin(); itObs != (*it).second.end(); itObs++ )
                                {
                                        // cout << (*itObs).first << " ";
                                        cout << (*itObs).second << " ";
                                }
                                cout << endl;
                        }
                        count++;
                }
                else { itsBelowThree++; }
        }
        return 0;
}


//
// interest-control.hpp
//
//  Created by Peter Gusev on 09 June 2016.
//  Copyright 2013-2016 Regents of the University of California
//

#ifndef __interest_control_h__
#define __interest_control_h__

#include <deque>
#include <boost/atomic.hpp>

#include "ndnrtc-common.hpp"
#include "ndnrtc-object.hpp"
#include "drd-estimator.hpp"
#include "buffer-control.hpp"
#include "playout-impl.hpp"

namespace ndn
{
class Name;
}

namespace ndnrtc
{
namespace statistics
{
class StatisticsStorage;
}

class WireSegment;
class InterestControlStrategy;

/**
 * Interface for Interest pipeline adjustment strategy
 */
class IInterestControlStrategy
{
  public:
    virtual void getLimits(double rate, boost::shared_ptr<DrdEstimator> drdEstimator,
                           unsigned int &lowerLimit, unsigned int &upperLimit) = 0;
    virtual int calculateDemand(double rate, double drdAvgValue, double drdDeviation) const = 0;
    virtual int burst(unsigned int currentLimit,
                      unsigned int lowerLimit, unsigned int upperLimit) = 0;
    virtual int withhold(unsigned int currentLimit,
                         unsigned int lowerLimit, unsigned int upperLimit) = 0;
};

class IInterestControl
{
  public:
    virtual void reset() = 0;
    virtual void initialize(double rate, int initPipelineLimit) = 0;
    virtual bool decrement() = 0;
    virtual bool increment() = 0;
    virtual size_t pipelineLimit() const = 0;
    virtual size_t pipelineSize() const = 0;
    virtual int room() const = 0;
    virtual bool burst() = 0;
    virtual bool withhold() = 0;
    virtual void markLowerLimit(unsigned int lowerLimit) = 0;
    virtual std::string snapshot() const = 0;
    virtual const boost::shared_ptr<const IInterestControlStrategy> getCurrentStrategy() const = 0;
};

/**
	 * InterestControl implements algorithm for lambda control or Interests
	 * expression control. It keeps track of the max limit size of the pipeline
	 * of outstanding Interests, current size of the pipeline and size of
	 * the room for expressing more Interests.
	 */
class InterestControl : public NdnRtcComponent,
                        public IInterestControl,
                        public IDrdEstimatorObserver,
                        public IBufferControlObserver
{
  public:
    static const unsigned int MinPipelineSize;

    /**
     * Default Interest pipeline adjustment strategy:
     *  - set pipeline limit based on current DRD and samplre rate values
     *  - bursts by half of the current limit value
     *  - withholding - decrease to the limit found by binary search
     *		between lower limit and current limit
     */
    class StrategyDefault : public IInterestControlStrategy
    {
      public:
        void getLimits(double rate, boost::shared_ptr<DrdEstimator> drdEstimator,
                       unsigned int &lowerLimit, unsigned int &upperLimit) override;
        int calculateDemand(double rate, double drdAvgValue, double drdDeviation) const override;
        int burst(unsigned int currentLimit,
                  unsigned int lowerLimit, unsigned int upperLimit) override;
        int withhold(unsigned int currentLimit,
                     unsigned int lowerLimit, unsigned int upperLimit) override;
    };

    InterestControl(const boost::shared_ptr<DrdEstimator> &,
                    const boost::shared_ptr<statistics::StatisticsStorage> &storage,
                    boost::shared_ptr<IInterestControlStrategy> strategy = boost::make_shared<StrategyDefault>());
    ~InterestControl();

    /**
     * Initializes interest control class with given sample rate and initial
     * pipeline size limit (i.e. maximum number of outstanding interests to
     * be sent out by pipeliner initially).
     */
    void initialize(double rate, int pipelineLimit) override;
    void reset() override;

    /**
     * Should be called every time when counter needs to be decremented,
     * for instance, when previously requested data arrives.
     * @return true if counter was decremented, false if pipeline can't be
     * 	decremented
     */
    bool decrement() override;

    /**
     * Should be called every time when counter needs to be incremented,
     * for instance, when Interests towards a new sample were issued.
     * @return true if counter was incremented, false if pipeline can't be
     * 	incremented (room is less than or equal zero)
     * @see room
     */
    bool increment() override;

    /**
     * TODO: rename pipeline limit to pipeline capacity
     * Returns current pipeline maximum size. This size can be changed
     * by client.
     * @see decrease(), increase()
     */
    size_t pipelineLimit() const override { return limit_; }

    /**
     * Returns current pipeline size, i.e. number of outstanding samples
     */
    size_t pipelineSize() const override { return pipeline_; }

    /**
     * Returns size of the room for expressing more Interests. If room is
     * zero or negative, no Interests for new samples should be expressed.
     */
    int room() const override { return limit_ - pipeline_; }

    /**
     * Increases current pipeline limit, if possible.
     * @return true if limit was increased, false otherwise
     */
    bool burst() override;

    /**
     * Decreases current pipeline limit, if possible.
     * @return true if decrease was possible, false - otherwise
     */
    bool withhold() override;

    /**
     * Sets lower limit for pipeline size
     */
    void markLowerLimit(unsigned int lowerLimit) override;

    /**
     * Returns symbolic representation of pipeline state
     * For example:
     *      1-8[⬆⬆⬆⬆◻◻◻◆]4-8 (4)
     * - first two numbers represent lower limit and upper limit for the pipeline
     * - up arrow (⬆) represents outstanding interest
     * - empty slot (◻) represents room to express an interest
     * - diamond (◆) represents current pipeline limit
     * - last three numbers:
     *      - current number of outstanding interests (pipeline size)
     *      - current pipeline limit
     *      - current pipeline room size (limit-pipeline)
     */
    std::string snapshot() const override;

    const boost::shared_ptr<const IInterestControlStrategy> getCurrentStrategy() const override { return strategy_; }

    // IDrdEstimatorObserver
    void onDrdUpdate() override;
    void onCachedDrdUpdate(double, double) override;
    void onOriginalDrdUpdate(double, double) override;

    // IBufferControlObserver
    void targetRateUpdate(double rate) override;
    void sampleArrived(const PacketNumber &) override { }

  private:
    boost::shared_ptr<IInterestControlStrategy> strategy_;
    boost::atomic<bool> initialized_, limitSet_;
    unsigned int lowerLimit_, limit_, upperLimit_;
    boost::atomic<int> pipeline_;
    boost::shared_ptr<DrdEstimator> drdEstimator_;
    double targetRate_;
    boost::shared_ptr<statistics::StatisticsStorage> sstorage_;

    void setLimits();
    void changeLimitTo(unsigned int newLimit);
};
}

#endif


#pragma once

#include <boost/utility.hpp>
#include <math/Rectangle.hpp>
#include <math/Vec.hpp>

class TouchHandler;

class Touchable : boost::noncopyable {
	public:
		enum TouchableTypeID {
			TypeIDNone = 0,
			TypeIDLinkMovingTouchable,
		};

		virtual ~Touchable() { }

		Rectangle<float> const & get_bounding_rect() { return bounding_rect_; }

		// NOTE: TouchHandlers should return true to indicate that the
		// Touchable should remain in the container after being handled.

		virtual bool handle_move_start(TouchHandler * handler, Vec2 const & start) { return true; }
		virtual bool handle_move_move(TouchHandler * handler, Vec2 const & loc) { return true; }
		virtual bool handle_move_end(TouchHandler * handler, Vec2 const & end) { return true; }
		virtual bool handle_single_tap(TouchHandler * handler, Vec2 const & loc) { return true; }

		void set_bounding_rectangle(Rectangle<float> const & rect)
			{ bounding_rect_ = rect; }

		virtual void handle_rendezvous(Touchable * touchable) { }

		virtual TouchableTypeID get_touchable_type_id() const { return TypeIDNone; }

	protected:
		Touchable() { }

	private:
		Rectangle<float> bounding_rect_;
};


#ifndef PYTHONIC_TYPES_LONG_HPP
#define PYTHONIC_TYPES_LONG_HPP

#ifndef USE_GMP
typedef long long pythran_long_t;
#define pythran_long(a) pythran_long_t(a)
#else
#include <gmpxx.h>
typedef mpz_class pythran_long_t;
#define pythran_long(a) pythran_long_t(#a)

namespace pythonic {

    /* some math overloads { */

    namespace operator_ {
        template<class T, class U, class T1>
            auto mod(__gmp_expr<T,U> const& t0, T1 const& t1) -> decltype( t0 % t1 ) {
                return t0 % t1;
            }
        template<class T, class U>
            auto floordiv(__gmp_expr<T,U> const& a, long b) -> decltype(a/b) {
                return a/b;
            }
        template<class T, class U, class Tp, class Up>
            auto floordiv(__gmp_expr<T,U> const& a, __gmp_expr<Tp,Up> const& b) -> decltype(a/b) {
                return a/b;
            }
    }
    /* } */

    template<class T,class U>
        struct gmp_compo;

    template<class T>
        struct gmp_type;
    template<class T, class U>
        struct assignable<__gmp_expr<T,U> >
        {
            typedef typename gmp_compo<T,U>::type type;
            static_assert(!std::is_same<type,double>::value,"Cannot combine long and float.");
        };
    template<class T>
        struct assignable<__gmp_expr<T,T> >
        {
            typedef typename gmp_type<T>::type type;
        };
    template<class T, class U>
        struct lazy<__gmp_expr<T,U> > : assignable<__gmp_expr<T,U>>
        {
        };

    //GMP_COMPO

    template<class T, class U>
        struct gmp_compo
        {
            typedef typename gmp_compo<typename gmp_type<T>::type, typename gmp_type<U>::type>::type type;
        };

    template<>
        struct gmp_compo<long,double>
        {
            typedef double type;
        };

    template<>
        struct gmp_compo<double,long>
        {
            typedef double type;
        };

    template<>
        struct gmp_compo<mpz_class,long>
        {
            typedef mpz_class type;
        };

    template<>
        struct gmp_compo<long,mpz_class>
        {
            typedef mpz_class type;
        };

    template<>
        struct gmp_compo<double,mpz_class>
        {
            typedef double type;
        };

    template<>
        struct gmp_compo<mpz_class,double>
        {
            typedef double type;
        };

    template<class T>
        struct gmp_compo<double,T>
        {
            typedef double type;
        };

    template<class T>
        struct gmp_compo<T,double>
        {
            typedef double type;
        };

    template<class T>
        struct gmp_compo<long,T>
        {
            typedef typename gmp_type<T>::type type;
        };

    template<class T>
        struct gmp_compo<T,long>
        {
            typedef typename gmp_type<T>::type type;
        };

    template<class T>
        struct gmp_compo<mpz_class,T>
        {
            typedef typename gmp_type<T>::type type;
        };

    template<class T>
        struct gmp_compo<T,mpz_class>
        {
            typedef typename gmp_type<T>::type type;
        };

    template<class T>
        struct gmp_compo<T,T>
        {
            typedef typename gmp_type<T>::type type;
        };

    template<>
        struct gmp_compo<mpz_class, mpz_class>
        {
            typedef mpz_class type;
        };

    template<>
        struct gmp_compo<long,long>
        {
            typedef long type;
        };

    template<>
        struct gmp_compo<double,double>
        {
            typedef double type;
        };

    //GMP_TYPE

    template<class T>
        struct gmp_type
        {
            typedef T type;
        };

    template<>
        struct gmp_type<__mpz_struct [1]>
        {
            typedef mpz_class type;
        };

    template<class T, class U, class V>
        struct gmp_type<__gmp_binary_expr<T,U,V> >
        {
            typedef typename gmp_compo<T,U>::type type;
        };

    template<class T, class V>
        struct gmp_type<__gmp_unary_expr<T,V> >
        {
            typedef typename gmp_type<T>::type type;
        };

    template<class T, class U, class V>
        struct gmp_type<__gmp_unary_expr<__gmp_expr<T,U>,V> >
        {
            typedef typename gmp_compo<T,U>::type type;
        };

    template<class T, class U>
        struct gmp_type<__gmp_expr<T,U> >
        {
            typedef typename gmp_compo<T,U>::type type;
        };

    template<class T>
        struct gmp_type<__gmp_expr<T,T> >
        {
            typedef typename gmp_type<T>::type type;
        };

}
/* compute hash of a gmp { */
namespace std {
    template <class T, class U>
        struct hash<__gmp_expr<T,U>>
        {
            size_t operator()(__gmp_expr<T,U> const & x) const
            {
                return hash<std::string>()(x.get_str());
            }
        };
}

#include <boost/functional/hash_fwd.hpp>
    template <class T, class U>
std::size_t hash_value(__gmp_expr<T,U> const & x)
{
    return std::hash<__gmp_expr<T,U> >()(x);
}

/* } */
#ifdef ENABLE_PYTHON_MODULE
#include "pythonic/python/register_once.hpp"

namespace pythonic {

    template<>
        struct python_to_pythran<mpz_class>{
            python_to_pythran(){
                static bool registered =false;
                if(not registered) {
                    registered=true;
                    boost::python::converter::registry::push_back(&convertible,&construct,boost::python::type_id<mpz_class>());
                }
            }
            static void* convertible(PyObject* obj_ptr){
                if(!PyLong_Check(obj_ptr)) return 0;
                return obj_ptr;
            }
            static void construct(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data){
                void* storage=((boost::python::converter::rvalue_from_python_storage<mpz_class>*)(data))->storage.bytes;
                auto s = PyObject_Str(obj_ptr);
                new (storage) mpz_class(PyString_AsString(s));
                Py_DECREF(s);
                data->convertible=storage;
            }
        };
    struct custom_mpz_to_long {
        static PyObject* convert(const mpz_class& v){
            auto s = PyString_FromString(v.get_str().c_str());
            auto l = PyNumber_Long(s);
            Py_DECREF(s);
            return l;
        }
    };
    template<>
        struct pythran_to_python< mpz_class > {
            pythran_to_python() {
                register_once< mpz_class, custom_mpz_to_long >();
            }
        };
}

#endif

#endif

#endif


/**
 *  @file
 *  @copyright defined in BetterChain/LICENSE.txt
 */

#include <boost/test/unit_test.hpp>

#include <betterchain/chain/block_schedule.hpp>
#include "../common/expect.hpp"

using namespace betterchain;
using namespace chain;

/*
 * templated meta schedulers for fuzzing
 */
template<typename NEXT>
struct shuffled_functor {
   shuffled_functor(NEXT &&_next) : next(_next){};

   block_schedule operator()(const vector<pending_transaction>& transactions, const global_property_object& properties) {
      std::random_device rd;
      std::mt19937 rng(rd());
      auto copy = std::vector<pending_transaction>(transactions);
      std::shuffle(copy.begin(), copy.end(), rng);
      return next(copy, properties);
   }

   NEXT &&next;
};

template<typename NEXT>
static shuffled_functor<NEXT> shuffled(NEXT &&next) {
   return shuffled_functor<NEXT>(next);
}

template<int NUM, int DEN, typename NEXT>
struct lossy_functor {
   lossy_functor(NEXT &&_next) : next(_next){};

   block_schedule operator()(const vector<pending_transaction>& transactions, const global_property_object& properties) {
      std::random_device rd;
      std::mt19937 rng(rd());
      std::uniform_real_distribution<> dist(0, 1);
      double const cutoff = (double)NUM / (double)DEN;

      auto copy = std::vector<pending_transaction>();
      copy.reserve(transactions.size());
      std::copy_if (transactions.begin(), transactions.end(), copy.begin(), [&](const pending_transaction& trx){
         return dist(rng) >= cutoff;
      });

      return next(copy, properties);
   }

   NEXT &&next;
};

template<int NUM, int DEN, typename NEXT>
static lossy_functor<NUM, DEN, NEXT> lossy(NEXT &&next) {
   return lossy_functor<NUM, DEN, NEXT>(next);
}

/*
 * Policy based Fixtures for chain properties
 */
class common_fixture {
public:
   struct test_transaction {
      test_transaction(const std::initializer_list<account_name>& _scopes)
         : scopes(_scopes)
      {
      }

      const std::initializer_list<account_name>& scopes;
   };

protected:
   auto create_transactions( const std::initializer_list<test_transaction>& transactions ) {
      std::vector<signed_transaction> result;
      for (const auto& t: transactions) {
         signed_transaction st;
         st.scope.reserve(t.scopes.size());
         st.scope.insert(st.scope.end(), t.scopes.begin(), t.scopes.end());
         result.emplace_back(st);
      }
      return result;
   }

   auto create_pending( const std::vector<signed_transaction>& transactions ) {
      std::vector<pending_transaction> result;
      for (const auto& t: transactions) {
         result.emplace_back(std::reference_wrapper<signed_transaction const> {t});
      }
      return result;
   }
};

template<typename PROPERTIES_POLICY>
class compose_fixture: public common_fixture {
public:
   template<typename SCHED_FN, typename ...VALIDATORS>
   void schedule_and_validate(SCHED_FN sched_fn, const std::initializer_list<test_transaction>& transactions, VALIDATORS ...validators) {
      try {
         auto signed_transactions = create_transactions(transactions);
         auto pending = create_pending(signed_transactions);
         auto schedule = sched_fn(pending, properties_policy.properties);
         validate(schedule, validators...);
      } FC_LOG_AND_RETHROW()
   }

private:
   template<typename VALIDATOR>
   void validate(const block_schedule& schedule, VALIDATOR validator) {
      validator(schedule);
   }

   template<typename VALIDATOR, typename ...VALIDATORS>
   void validate(const block_schedule& schedule, VALIDATOR validator, VALIDATORS ... others) {
      validate(schedule, validator);
      validate(schedule, others...);
   }

   PROPERTIES_POLICY properties_policy;
};

static void null_global_property_object_constructor(const global_property_object& )
{}

static chainbase::allocator<global_property_object> null_global_property_object_allocator(nullptr);

struct base_properties {
   base_properties()
      : properties(null_global_property_object_constructor, null_global_property_object_allocator)
   {
   }

   global_property_object properties;
};

struct default_properties : public base_properties {
   default_properties() {
      properties.configuration.max_blk_size = 256 * 1024;
   }
};

struct small_block_properties : public base_properties {
   small_block_properties() {
      properties.configuration.max_blk_size = 512;
   }
};

typedef compose_fixture<default_properties> default_fixture;

/*
 * Evaluators for expect
 */
static uint transaction_count(const block_schedule& schedule) {
   uint result = 0;
   for (const auto& c : schedule.cycles) {
      for (const auto& t: c) {
         result += t.transactions.size();
      }
   }

   return result;
}

static uint cycle_count(const block_schedule& schedule) {
   return schedule.cycles.size();
}

static bool schedule_is_valid(const block_schedule& schedule) {
   for (const auto& c : schedule.cycles) {
      std::vector<bool> scope_in_use;
      for (const auto& t: c) {
         std::set<size_t> thread_bits;
         size_t max_bit = 0;
         for(const auto& pt: t.transactions) {
            auto scopes = pt.visit(scope_extracting_visitor());
            for (const auto& s : scopes) {
               size_t bit = boost::numeric_cast<size_t>((uint64_t)s);
               thread_bits.emplace(bit);
               max_bit = std::max(max_bit, bit);
            }
         }

         if (scope_in_use.size() <= max_bit) {
            scope_in_use.resize(max_bit + 1);
         }

         for ( auto b: thread_bits ) {
            if(scope_in_use.at(b)) {
               return false;
            };
            scope_in_use.at(b) = true;
         }
      }
   }

   return true;
}

/*
 * Test Cases
 */

BOOST_AUTO_TEST_SUITE(block_schedule_tests)

BOOST_FIXTURE_TEST_CASE(no_conflicts, default_fixture) {
   // ensure the scheduler can handle basic non-conflicted transactions
   // in a single cycle
   schedule_and_validate(
      block_schedule::by_threading_conflicts,
      {
         {0x1ULL, 0x2ULL},
         {0x3ULL, 0x4ULL},
         {0x5ULL, 0x6ULL},
         {0x7ULL, 0x8ULL},
         {0x9ULL, 0xAULL}
      },
      EXPECT(schedule_is_valid),
      EXPECT(transaction_count, 5),
      EXPECT(cycle_count, 1)
   );
}

BOOST_FIXTURE_TEST_CASE(some_conflicts, default_fixture) {
   // ensure the scheduler can handle conflicted transactions
   // using multiple cycles
   schedule_and_validate(
      block_schedule::by_threading_conflicts,
      {
         {0x1ULL, 0x2ULL},
         {0x3ULL, 0x2ULL},
         {0x5ULL, 0x1ULL},
         {0x7ULL, 0x1ULL},
         {0x1ULL, 0x7ULL}
      },
      EXPECT(schedule_is_valid),
      EXPECT(transaction_count, 5),
      EXPECT(std::greater<uint>, cycle_count, 1)
   );
}

BOOST_FIXTURE_TEST_CASE(basic_cycle, default_fixture) {
   // ensure the scheduler can handle a basic scope cycle
   schedule_and_validate(
      block_schedule::by_threading_conflicts,
      {
         {0x1ULL, 0x2ULL},
         {0x2ULL, 0x3ULL},
         {0x3ULL, 0x1ULL},
      },
      EXPECT(schedule_is_valid),
      EXPECT(transaction_count, 3)
   );
}

BOOST_FIXTURE_TEST_CASE(small_block, compose_fixture<small_block_properties>) {
   // ensure the scheduler can handle basic block size restrictions
   schedule_and_validate(
      block_schedule::by_threading_conflicts,
      {
         {0x1ULL, 0x2ULL},
         {0x3ULL, 0x4ULL},
         {0x5ULL, 0x6ULL},
         {0x7ULL, 0x8ULL},
         {0x9ULL, 0xAULL},
         {0xBULL, 0xCULL},
         {0xDULL, 0xEULL},
         {0x11ULL, 0x12ULL},
         {0x13ULL, 0x14ULL},
         {0x15ULL, 0x16ULL},
         {0x17ULL, 0x18ULL},
         {0x19ULL, 0x1AULL},
         {0x1BULL, 0x1CULL},
         {0x1DULL, 0x1EULL},
         {0x21ULL, 0x22ULL},
         {0x23ULL, 0x24ULL},
         {0x25ULL, 0x26ULL},
         {0x27ULL, 0x28ULL},
         {0x29ULL, 0x2AULL},
         {0x2BULL, 0x2CULL},
         {0x2DULL, 0x2EULL},
      },
      EXPECT(schedule_is_valid),
      EXPECT(std::less<uint>, transaction_count, 21)
   );
}

BOOST_FIXTURE_TEST_CASE(no_conflicts_shuffled, default_fixture) {
   // stochastically verify that the order of non-conflicting transactions
   // does not affect the ability to schedule them in a single cycle
   for (int i = 0; i < 3000; i++) {
      schedule_and_validate(
         shuffled(block_schedule::by_threading_conflicts),
         {
            {0x1ULL, 0x2ULL},
            {0x3ULL, 0x4ULL},
            {0x5ULL, 0x6ULL},
            {0x7ULL, 0x8ULL},
            {0x9ULL, 0xAULL},
            {0xBULL, 0xCULL},
            {0xDULL, 0xEULL},
            {0x11ULL, 0x12ULL},
            {0x13ULL, 0x14ULL},
            {0x15ULL, 0x16ULL},
            {0x17ULL, 0x18ULL},
            {0x19ULL, 0x1AULL},
            {0x1BULL, 0x1CULL},
            {0x1DULL, 0x1EULL},
            {0x21ULL, 0x22ULL},
            {0x23ULL, 0x24ULL},
            {0x25ULL, 0x26ULL},
            {0x27ULL, 0x28ULL},
            {0x29ULL, 0x2AULL},
            {0x2BULL, 0x2CULL},
            {0x2DULL, 0x2EULL},
         },
         EXPECT(schedule_is_valid),
         EXPECT(transaction_count, 21),
         EXPECT(cycle_count, 1)
      );
   }
}

BOOST_FIXTURE_TEST_CASE(some_conflicts_shuffled, default_fixture) {
   // stochastically verify that the order of conflicted transactions
   // does not affect the ability to schedule them in multiple cycles
   for (int i = 0; i < 3000; i++) {
      schedule_and_validate(
         shuffled(block_schedule::by_threading_conflicts),
         {
            {0x1ULL, 0x2ULL},
            {0x3ULL, 0x2ULL},
            {0x5ULL, 0x1ULL},
            {0x7ULL, 0x1ULL},
            {0x1ULL, 0x7ULL},
            {0x11ULL, 0x12ULL},
            {0x13ULL, 0x12ULL},
            {0x15ULL, 0x11ULL},
            {0x17ULL, 0x11ULL},
            {0x11ULL, 0x17ULL},
            {0x21ULL, 0x22ULL},
            {0x23ULL, 0x22ULL},
            {0x25ULL, 0x21ULL},
            {0x27ULL, 0x21ULL},
            {0x21ULL, 0x27ULL}
         },
         EXPECT(schedule_is_valid),
         EXPECT(transaction_count, 15),
         EXPECT(std::greater<uint>, cycle_count, 1)
      );
   }
}

BOOST_AUTO_TEST_SUITE_END()


/*
 * LikelihoodInputAnalyser.cpp
 *
 *  Created on: May 5, 2015
 *      Author: ejclemen
 */
#include "../../interface/Analysers/LikelihoodInputAnalyser.h"
#include "../../interface/GlobalVariables.h"
#include <boost/scoped_ptr.hpp>

using namespace BAT;

void LikelihoodInputAnalyser::analyse(const EventPtr event) {

	weight_ = event->weight();
	treeMan_->setCurrentFolder(histogramFolder_);
	unsigned int parton = 0;
	double CSV = -1;

	const JetCollection AllJets(event->Jets());
	const JetCollection jets(event->CleanedJets());
	const JetCollection bjets(event->CleanedBJets());
	// const JetCollection genJets( event->GenJets() );

	METAlgorithm::value metType = (METAlgorithm::value) 0;
	const METPointer met(event->MET(metType));

	if (event->isSemiLeptonicElectron() || event->isSemiLeptonicMuon()){
		for ( unsigned int AllJetIndex=0; AllJetIndex < AllJets.size(); ++AllJetIndex ) {

			JetPointer AllJet = AllJets[AllJetIndex];

			if (AllJet->matched_parton() == 0) continue;//if null pointer

			parton = abs(AllJet->matched_parton()->pdgId());
			CSV = AllJet->getBTagDiscriminator(BAT::BtagAlgorithm::value::CombinedSecondaryVertexV2);

			if ( abs(CSV) >= 11 ){
				// cout << "CSV : " << CSV << " Run Number : " << event->runnumber() << " Event Number : " << event->eventnumber() << " Lumi Number : " << event->lumiblock() << endl;
				// event->inspect(); //For more info on events failing CSV
				continue;
				}

			if ( (parton >= 1 && parton <= 4) || (parton == 21) ){
				treeMan_->Fill("LightJets", CSV);
				// cout << "LightJet CSV : " << CSV << endl;
			}

			else if ( parton == 5 ){
				treeMan_->Fill("BJets", CSV);
				// cout << "BJet CSV : " << CSV << endl;
			}

			else{
				// cout << "Everything is Rubbish" << endl;
			}
		}
	}

	// Require one of the event selections to have been satisfied, and we have a genuine ttbar event
	int selectionCriteria = -1;
	if ( event->PassesElectronTriggerAndSelection() && event->isSemiLeptonicElectron() ) selectionCriteria = SelectionCriteria::ElectronPlusJetsReference;
	else if ( event->PassesMuonTriggerAndSelection() && event->isSemiLeptonicMuon() ) selectionCriteria = SelectionCriteria::MuonPlusJetsReference;
	if ( selectionCriteria < 0 ) return;

	// Get objects
	const LeptonPointer signalLepton = event->getSignalLepton( selectionCriteria );

	JetCollection jetsWithoutBs;
	FourVector WLep, TopLep, WHad, TopHad;

	// Get cleaned jets that aren't b tagged
	for ( unsigned int jetIndex=0; jetIndex < jets.size(); ++jetIndex ) {
		bool isBJet = false;
		JetPointer thisJet = jets[jetIndex];
		for ( unsigned int bJetIndex=0; bJetIndex < bjets.size(); ++bJetIndex ) {
			JetPointer thisBJet = bjets[bJetIndex];
			if ( thisJet == thisBJet ) {
				isBJet = true;
				break;
			}
		}
		if ( !isBJet ) jetsWithoutBs.push_back( thisJet );
	}

	if ( jetsWithoutBs.size() < 2 ) return;

	// cout << endl << "*****************************************" << endl;
	// cout << "Number of Jets : " << jets.size() << endl;
	// cout << "Number of bJets : " << bjets.size() << endl;
	// cout << "Number of Light Jets : " << jets.size() - bjets.size() << endl;
	// cout << "-----------------------------------------" << endl;

	for ( unsigned int bjetIndex=0; bjetIndex < bjets.size(); ++bjetIndex ) {

		JetPointer bjet = bjets[bjetIndex];
		bool leptonicB = false;
		bool hadronicB = false;
		bool mistaggedB = false;
		double CorrectEvent = 0;

		if (bjet->ttbar_decay_parton() == 5){
			leptonicB = true;
			// cout << "B is Actually Leptonic: " << endl;
		}
		if (bjet->ttbar_decay_parton() == 6){
			hadronicB = true;
			mistaggedB = true;
			// cout << "B is Actually Hadronic: " << endl;
		}
		if (bjet->ttbar_decay_parton() < 5){
			mistaggedB = true;
			// cout << "B is Mis-Tagged: " << endl;
		}

		//For particular b jet and signal lepton - return the best solution for the neutrino given the t/W mass constraints
		NeutrinoSolver neutrinoSolver( &(signalLepton->getFourVector()), &(bjet->getFourVector()), 80, 173 );
		double test = -1;
		FourVector neutrino = neutrinoSolver.GetBest(met->getFourVector().Px(), met->getFourVector().Py(), 25., 25., 0., test );
		// cout << "Neutrino ChiSq : " << test << endl;

		// Calculate the leptonic W and hence leptonic top four vectors.
		WLep = neutrino + signalLepton->getFourVector();
		TopLep = WLep + bjet->getFourVector();

		// cout << "-----------------------------------------" << endl;
		// cout << "Mass of Leptonic W : " << WLep.M() << "GeV" << endl;
		// cout << "Mass of Leptonic Top : " << TopLep.M() << "GeV" << endl;

		//Store the outcome of the Chisq test for leptonic/hadronic and incorrect b jets (inc hadronic)
		if (leptonicB == true){
			treeMan_->Fill("Leptonic_B", test);
		}

		if (hadronicB == true){
			treeMan_->Fill("Hadronic_B", test);
		}

		if (mistaggedB == true){
			treeMan_->Fill("False_B", test);
		}

		// Reconstruct the hadronic W and hence top.
		for ( unsigned int jet1Index=0; jet1Index < (jetsWithoutBs.size()-1); ++jet1Index ) {
			for ( unsigned int jet2Index=jet1Index+1; jet2Index < jetsWithoutBs.size(); ++jet2Index ) {
				JetPointer jet1 = jetsWithoutBs[jet1Index];
				JetPointer jet2 = jetsWithoutBs[jet2Index];

				WHad = jet1->getFourVector() + jet2->getFourVector();
				// cout << "-----------------------------------------" << endl;
				// cout << "Mass of Hadronic W : " << WHad.M() << "GeV" << endl;

				for ( unsigned int AltbjetIndex=0; AltbjetIndex < bjets.size(); ++AltbjetIndex ) {
					if (AltbjetIndex == bjetIndex) continue;
					JetPointer AltBjet = bjets[AltbjetIndex];

					TopHad = WHad + AltBjet->getFourVector();
					// cout << "Mass of Hadronic Top : " << TopHad.M() << "GeV" << endl;

					if (AltBjet->ttbar_decay_parton() == 6 && leptonicB == true){
						CorrectEvent = 1;
					}

					treeMan_->Fill("LeptonicTop_Pt",TopLep.Pt() );
					treeMan_->Fill("LeptonicTop_Energy",TopLep.Energy() );
					treeMan_->Fill("LeptonicTop_Mass",TopLep.M() );

					treeMan_->Fill("LeptonicW_Pt",WLep.Pt());
					treeMan_->Fill("LeptonicW_Energy",WLep.Energy());
					treeMan_->Fill("LeptonicW_Mass",WLep.M() );

					treeMan_->Fill("HadronicTop_Pt",TopHad.Pt());
					treeMan_->Fill("HadronicTop_Energy",TopHad.Energy());
					treeMan_->Fill("HadronicTop_Mass",TopHad.M() );

					treeMan_->Fill("HadronicW_Pt",WHad.Pt());
					treeMan_->Fill("HadronicW_Energy",WHad.Energy());
					treeMan_->Fill("HadronicW_Mass",WHad.M() );

					treeMan_->Fill("CorrectReconstruction", CorrectEvent);
				}
			}
		}
	}
}

LikelihoodInputAnalyser::LikelihoodInputAnalyser(HistogramManagerPtr histMan, boost::shared_ptr<TreeManager> treeMan, std::string histogramFolder ) :
		BasicAnalyser(histMan, treeMan, histogramFolder){

}

LikelihoodInputAnalyser::~LikelihoodInputAnalyser() {
}

void LikelihoodInputAnalyser::createHistograms() {
	histMan_->setCurrentHistogramFolder(histogramFolder_);
}

void LikelihoodInputAnalyser::createTrees() {
	treeMan_->setCurrentFolder(histogramFolder_);

	treeMan_->addBranch("BRANCH", "F", "Likelihood" + Globals::treePrefix_);
	treeMan_->addBranch("Leptonic_B", "F", "Neutrino" + Globals::treePrefix_);
	treeMan_->addBranch("Hadronic_B", "F", "Neutrino" + Globals::treePrefix_);
	treeMan_->addBranch("False_B", "F", "Neutrino" + Globals::treePrefix_);

	treeMan_->addBranch("BJets", "F", "CSV" + Globals::treePrefix_);
	treeMan_->addBranch("LightJets", "F", "CSV" + Globals::treePrefix_);

	treeMan_->addBranch("LeptonicTop_Pt", "F", "TopReco" + Globals::treePrefix_);
	treeMan_->addBranch("LeptonicTop_Energy", "F", "TopReco" + Globals::treePrefix_);
	treeMan_->addBranch("LeptonicTop_Mass", "F", "TopReco" + Globals::treePrefix_);

	treeMan_->addBranch("HadronicTop_Pt", "F", "TopReco" + Globals::treePrefix_);
	treeMan_->addBranch("HadronicTop_Energy", "F", "TopReco" + Globals::treePrefix_);
	treeMan_->addBranch("HadronicTop_Mass", "F", "TopReco" + Globals::treePrefix_);

	treeMan_->addBranch("LeptonicW_Pt", "F", "TopReco" + Globals::treePrefix_);
	treeMan_->addBranch("LeptonicW_Energy", "F", "TopReco" + Globals::treePrefix_);
	treeMan_->addBranch("LeptonicW_Mass", "F", "TopReco" + Globals::treePrefix_);

	treeMan_->addBranch("HadronicW_Pt", "F", "TopReco" + Globals::treePrefix_);
	treeMan_->addBranch("HadronicW_Energy", "F", "TopReco" + Globals::treePrefix_);
	treeMan_->addBranch("HadronicW_Mass", "F", "TopReco" + Globals::treePrefix_);

	treeMan_->addBranch("CorrectReconstruction", "F", "TopReco"+ Globals::treePrefix_);
}