/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

#include <assert.h>

#include <sstream>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <memory>
#include <tuple>
#include <vector>
#include <math.h>
#include <chrono>
#include <stdlib.h>
#include "matcher.h"
#include <Eigen/Dense>
//delete this probably
#include <ctime>
#include "boost/filesystem.hpp"
namespace fs = boost::filesystem;

using namespace std;
using namespace Eigen;

namespace PQ
{

Matcher::Matcher(string code_file)
{
    N = 200;
    description.push_back("minutiae similarity");

    description.push_back("obtaining corr");

    description.push_back("second order fast");

    description.push_back("second order original");

    dist_N = 50;
    table_dist.resize(dist_N*dist_N);
    max_nrof_templates = 0;
    int n = 0;
    for(int i=0;i<dist_N; ++i)
    {
        for(int j=i;j<dist_N;++j)
        {
            table_dist[i*dist_N+j] = sqrt((i*16.0)*(i*16.0) +  (j*16.0)*(j*16.0));
            table_dist[j*dist_N+i] = table_dist[i*dist_N+j];
        }
    }
    //    load code book
    ifstream is;
    is.open(code_file, ifstream::binary);
    // get length of file:
    is.seekg(0, ios::end);
    int length = is.tellg();

    if( length<=0 )
    {
        cout<<"codebook is empty!"<<endl;
    }
    is.seekg(0, ios::beg);

    nrof_subs = 0;
    nrof_clusters=0;
    sub_dim = 0;

    is.read(reinterpret_cast<char*>(&nrof_subs),sizeof(short));
    is.read(reinterpret_cast<char*>(&nrof_clusters),sizeof(short));
    is.read(reinterpret_cast<char*>(&sub_dim),sizeof(short));

    int len = nrof_subs*nrof_clusters*sub_dim;
    if(len<=0)
    {
        cout<<"codebook is empty!"<<endl;
    }

    codewords = std::vector<float>(len);
    float *pword = codewords.data();
    for(int i=0;i<nrof_subs; ++i)
    {
        for(int j=0; j<nrof_clusters; ++j)
        {
            is.read(reinterpret_cast<char*>(pword),sizeof(float)*sub_dim);
            pword += sub_dim;
        }
    }
}

int Matcher::List2List_matching(string latent_path, string rolled_path, string score_path)
{
    string template_file, score_file;

    int nrof_latents = 0;
    vector<fs::path> latent_template_files;
    fs::directory_iterator end_itr;
    for(fs::directory_iterator dir_itr(latent_path); dir_itr != end_itr; ++dir_itr)
    {
        if(dir_itr->path().extension() == ".dat")
        {
            latent_template_files.push_back(dir_itr->path());
            cout<<"latent template file"<<dir_itr->path()<<endl;
            ++nrof_latents;
        }
    }
    if(nrof_latents <= 0)
    {
        cout<<"No latent templates found in directory: "<<latent_path<<endl;
        return -1;
    }

    int i,j,k;

    int nrof_rolled = 0;
    vector<fs::path> rolled_template_files;
    for(fs::directory_iterator dir_itr(rolled_path); dir_itr != end_itr; ++dir_itr)
    {
    if(dir_itr->path().extension() == ".dat")
        {
            rolled_template_files.push_back(dir_itr->path());
            cout<<"rolled template file"<<dir_itr->path()<<endl;
            ++nrof_rolled;
        }
    }
    if(nrof_rolled <= 0)
    {
        cout<<"No rolled templates found in directory: "<<rolled_path<<endl;
        return -1;
    }
    cout<<"Gallery size: "<<nrof_rolled<<endl;
    {
        using namespace std::chrono;
        vector<high_resolution_clock::time_point> t(10);
        duration<double, std::milli> time_span;
        t[0] = high_resolution_clock::now();

        for(i=0;i<nrof_latents; ++i)
        {
            vector<float> scores(nrof_rolled, -1);
            cout<<latent_template_files[i]<<endl;

            //load latent original template and create a latent FP object
            LatentFPTemplate latent_FP{};
            try {
	            latent_FP = load_latent_template(latent_template_files[i].string());
	    } catch (const std::exception&) {
	    	cout<<"No minutiae or texture templates found"<<endl;
                ofstream output;
                output.open(score_path + latent_template_files[i].stem().string() + ".csv");

                output<<0<<endl;
                output.close();

                continue;
	    }
            cout<<"Latent minutiae templates: "<<latent_FP.m_minu_templates.size()<<endl;
            cout<<"Latent texture templates: "<<latent_FP.m_texture_templates.size()<<endl;
            if(latent_FP.m_minu_templates.size()<=0 && latent_FP.m_texture_templates.size()<=0)
            {
				cout<<"No minutiae or texture templates found"<<endl;
                ofstream output;
                output.open(score_path + latent_template_files[i].stem().string() + ".csv");

                output<<0<<endl;
                output.close();

                continue;
            }

            using namespace std::chrono;
            high_resolution_clock::time_point t_start = high_resolution_clock::now();
            int result = 0;
	        #pragma omp parallel for num_threads(8) schedule(static,16)
            for(j=0;j<nrof_rolled; ++j)
            {

                RolledFPTemplate rolled_FP;
                try {
                	rolled_FP = load_rolled_template(rolled_template_files[j].string());
                } catch (const std::exception&) {
                    rolled_FP.m_minu_templates.clear();
                    rolled_FP.m_texture_templates.clear();
                }

				vector<float> score;
				result = One2One_matching_selected_templates(latent_FP,rolled_FP,score);
                if(result == 1){
                    continue;
                }
                else if(result == 2){
                    cout<<"Comparison failed: rolled template is empty. Skipping."<<endl;
                    continue;
                }
				float final_score = score[0] + score[1] + score[2] + score[28]*0.3;
                scores[j] = final_score;
            }
            if(result == 1){
                cout<<"Matching failed: latent template is empty. Skipping."<<endl;
                continue;
            }
            auto t_end = high_resolution_clock::now();
            duration<double, std::milli> duration = (t_end - t_start);

			ofstream output;
			output.open(score_path + latent_template_files[i].stem().string() + ".csv");

			for(j=0;j<nrof_rolled; ++j)
			{
                output<<rolled_template_files[j]<<","<<std::setprecision(3)<<std::fixed<<scores[j]<<endl;
			}
			output.close();
        }
        t[1] = high_resolution_clock::now();
        time_span = t[1] - t[0];
        cout<<"Total matching duration (ms): "<<time_span.count()<<endl;

		auto timenow = chrono::system_clock::to_time_t(chrono::system_clock::now());
    }
    return 0;
}

int Matcher::One2List_matching(string latent_template_file_string, string rolled_list_file, string score_path)
{

    int i,j,k;

    fs::path latent_template_file(latent_template_file_string);
    string score_file = score_path + latent_template_file.stem().string() + ".csv";

    int nrof_rolled = 0;
    vector<fs::path> rolled_template_files;
    fs::directory_iterator end_itr;
    for(fs::directory_iterator dir_itr(rolled_list_file); dir_itr != end_itr; ++dir_itr)
    {
    if(dir_itr->path().extension() == ".dat")
        {
            rolled_template_files.push_back(dir_itr->path());
            ++nrof_rolled;
        }
    }
    if(nrof_rolled <= 0)
    {
        cout<<"No rolled templates found in directory: "<<rolled_list_file<<endl;
        return -1;
    }

    // Create a vector of indices
    // Allows us to output a sorted score list for use with the GUI
    vector<int> ind(rolled_template_files.size(), 0);
    for(int n = 0; n != rolled_template_files.size(); n++){
        ind[n] = n;
    }

    {
        using namespace std::chrono;
        vector<high_resolution_clock::time_point> t(10);
        duration<double, std::milli> time_span;
        t[0] = high_resolution_clock::now();

        vector<float> scores(nrof_rolled, -1);
        cout<<"Latent Query: "<<latent_template_file<<endl;
        cout<<"Gallery size: "<<nrof_rolled<<endl;

        LatentFPTemplate latent_FP;
        try {
        	latent_FP = load_latent_template(latent_template_file.string());
        } catch (const std::exception&) {
            ofstream output;
            output.open(score_file);

            output<<0<<endl;
            output.close();
        }
        if(latent_FP.m_minu_templates.size()<=0 && latent_FP.m_texture_templates.size()<=0)
        {
            ofstream output;
            output.open(score_file);

            output<<0<<endl;
            output.close();

        }

        using namespace std::chrono;
        high_resolution_clock::time_point t_start = high_resolution_clock::now();
        int result = 0;
        #pragma omp parallel for num_threads(8) schedule(static,16)
        for(j=0;j<nrof_rolled; ++j)
        {

            RolledFPTemplate rolled_FP;
            try {
            	rolled_FP = load_rolled_template(rolled_template_files[j].string());

            } catch (const std::exception&) {
                rolled_FP.m_minu_templates.clear();
                rolled_FP.m_texture_templates.clear();
            }

            vector<float> score;
            result = One2One_matching_selected_templates(latent_FP,rolled_FP,score);
            if(result == 1){
                continue;
            }
            else if(result == 2){
                cout<<"Comparison failed: rolled template is empty. Skipping."<<endl;
                continue;
            }
            float final_score = score[0] + score[1] + score[2] + score[28]*0.3;
            scores[j] = final_score;
        }
        if(result == 1){
            cout<<"Matching failed: latent template is empty. Exiting."<<endl;
            return 1;
        }
        auto t_end = high_resolution_clock::now();
        duration<double, std::milli> duration = (t_end - t_start);
        ofstream output;
        output.open(score_file);

        // Sort scores to create rank list
        sort(ind.begin(), ind.end(), [&](const int& a, const int& b){
                return (scores[a] > scores[b]);
            }
        );
        output<<"filename,score"<<endl;
        //generate correspondence files for top 24 only
        cout<<"Match Results"<<endl;
        cout<<"----------------"<<endl;
        cout<<"Rank     Filename      Score"<<endl;
        for(j=0; j<24; ++j)
        {
            if(j >= nrof_rolled){
                break;
            }
            output<<to_string(j+1)<<rolled_template_files[ind[j]]<<","<<scores[ind[j]]<<endl;
            RolledFPTemplate rolled_FP;
            try {
	            rolled_FP = load_rolled_template(rolled_template_files[ind[j]].string());
	    } catch (const std::exception&) {
		    rolled_FP.m_minu_templates.clear();
                    rolled_FP.m_texture_templates.clear();
	    }
            string latent_fname = latent_template_file.stem().string();
            string rolled_fname = rolled_template_files[ind[j]].stem().string();
            string corr_file = "/LatentAFIS/scores/corr" + latent_fname + "_" + rolled_fname;
            vector<float> score;
            One2One_matching_selected_templates(latent_FP,rolled_FP,score, true, corr_file);
            cout<<to_string(j+1)<<"        "<<rolled_template_files[ind[j]].filename()<<"       "<<scores[ind[j]]<<endl;
        }
        output.close();
        t[1] = high_resolution_clock::now();
        time_span = t[1] - t[0];
        cout<<"Total matching duration (ms): "<<time_span.count()<<endl;

    }
    return 0;
}

int Matcher::One2One_matching_all_templates(const LatentFPTemplate &latent_template, const RolledFPTemplate &rolled_template, vector<float> & score)
{

    score.resize(latent_template.m_minu_templates.size() + latent_template.m_texture_templates.size());
    std::fill(score.begin(), score.end(), 0);

   if(latent_template.m_minu_templates.size()<=0 && latent_template.m_texture_templates.size()<=0)
   {
        return 1;
    }

    if(rolled_template.m_minu_templates.size()<=0 && rolled_template.m_texture_templates.size()<=0)
    {
        return 2;
    }
    int i,j;

    using namespace std::chrono;
    vector<high_resolution_clock::time_point> t(10);

    t[0] = high_resolution_clock::now();

    for(i=0;i<latent_template.m_minu_templates.size() && rolled_template.m_minu_templates.size(); ++i)
    {
        float s = One2One_minutiae_matching(latent_template.m_minu_templates[i], rolled_template.m_minu_templates[0]);
        score[i] = s;
    }
    t[1] = high_resolution_clock::now();

    for(i=0;i<latent_template.m_texture_templates.size() && rolled_template.m_texture_templates.size()>0 ; ++i)
    {
        float s = One2One_texture_matching(latent_template.m_texture_templates[i], rolled_template.m_texture_templates[0]);
        score[i+latent_template.m_minu_templates.size()] = s;
    }

   return 0;
}

int Matcher::One2One_matching_selected_templates(const LatentFPTemplate &latent_template, const RolledFPTemplate &rolled_template, vector<float> & score, bool save_corr, string corr_file)
const
{
    score.resize(latent_template.m_minu_templates.size() + latent_template.m_texture_templates.size());
    std::fill(score.begin(), score.end(), 0);
    vector<int> selected_ind{27-1, 3-1, 12-1};

    if(latent_template.m_minu_templates.size()<=selected_ind[0] && latent_template.m_texture_templates.size()<=0)
    {
        return 1;
    }

    if(rolled_template.m_minu_templates.size()<=0 && rolled_template.m_texture_templates.size()<=0)
    {
        return 2;
    }
    int i,j;

    using namespace std::chrono;
    vector<high_resolution_clock::time_point> t(10);

    t[0] = high_resolution_clock::now();

    for(i=0;i<selected_ind.size() && rolled_template.m_minu_templates.size()>0; ++i)
    {
        int ind = selected_ind[i];
        if(latent_template.m_minu_templates.size()<=ind)
            continue;
        string one_corr_file = corr_file + "_" + to_string(i) + ".csv";
        float s = One2One_minutiae_matching(latent_template.m_minu_templates[ind], rolled_template.m_minu_templates[0], save_corr, one_corr_file);
        score[i] = s;
    }
    t[1] = high_resolution_clock::now();

    for(i=0;i<min(1ul,latent_template.m_texture_templates.size()) && rolled_template.m_texture_templates.size()>0 ; ++i)
    {
        float s = One2One_texture_matching(latent_template.m_texture_templates[i], rolled_template.m_texture_templates[0]);
        score[i+latent_template.m_minu_templates.size()] = s;
    }

    return 0;
}

float Matcher::One2One_minutiae_matching(const MinutiaeTemplate &latent_minu_template_in, const MinutiaeTemplate &rolled_minu_template_in, bool save_corr, string corr_file)
const
{
	// XXX: Can we avoid the manipulation from eigen below and not dup?
	MinutiaeTemplate latent_minu_template(latent_minu_template_in);
	MinutiaeTemplate rolled_minu_template(rolled_minu_template_in);

    // step 1: compute pairwise similarity between descriptors

    int n_time = 0;
    int i,j,k;

    int des_len = rolled_minu_template.des_length();
    if(des_len!=latent_minu_template.des_length()){
        cout<<latent_minu_template.des_length()<<endl;
	cout<<rolled_minu_template.des_length()<<endl;
	}
    assert(des_len == latent_minu_template.des_length());

    float simi = 0.0;

    using namespace std::chrono;
    vector<high_resolution_clock::time_point> t(10);

    Matrix<float, Eigen::Dynamic, Eigen::Dynamic> aa =  Map<Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(latent_minu_template.m_des.data(),latent_minu_template.m_minutiae.size(),des_len);
    Matrix<float, Eigen::Dynamic, Eigen::Dynamic> bb =  Map<Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(rolled_minu_template.m_des.data(),rolled_minu_template.m_minutiae.size(),des_len);

    MatrixXf  simi_matrix=aa*bb.transpose();

    for(i=0; i<latent_minu_template.m_minutiae.size(); ++i)
    {
        for(j = 0; j<rolled_minu_template.m_minutiae.size(); ++j)
        {
            if(simi_matrix(i,j)<0)
                simi_matrix(i,j) = 0;
        }
    }

    //  step 2:  similarity normalization
    VectorXf  rolled_simi_sum = simi_matrix.colwise().sum();
    VectorXf  latent_simi_sum = simi_matrix.rowwise().sum();

    int ind_1, ind_2 ;
    vector<float> norm_simi_matrix(latent_minu_template.m_minutiae.size()*rolled_minu_template.m_minutiae.size());
    float norm_simi=0.0;
    for(i=0; i<latent_minu_template.m_minutiae.size(); ++i)
    {
        ind_1 = i*rolled_minu_template.m_minutiae.size();
        for(j = 0; j<rolled_minu_template.m_minutiae.size(); ++j)
        {
            ind_2  = ind_1 + j;
            norm_simi = simi_matrix(i,j)/(latent_simi_sum(i) + rolled_simi_sum(j) - simi_matrix(i,j)+0.000001); // //simi_matrix[ind_2]*
            norm_simi_matrix[ind_2] = norm_simi;
        }
    }
    // step 3: find top N correspondences using norm_simi_matrix;
    // the sorting part can be replaced by a min-heap
    std::vector<int> y(norm_simi_matrix.size());
    std::iota(y.begin(), y.end(), 0);
    auto comparator = [&norm_simi_matrix](int a, int b){ return norm_simi_matrix[a] > norm_simi_matrix[b]; };
    std::sort(y.begin(), y.end(), comparator);

    std::vector<tuple<float, int, int>>corr;
    int topN = 120;
    if(rolled_minu_template.m_minutiae.size()*latent_minu_template.m_minutiae.size()<topN)
        topN = rolled_minu_template.m_minutiae.size()*latent_minu_template.m_minutiae.size();
    for(i=0; i<topN ; ++i)
    {
        ind_1 = y[i]/rolled_minu_template.m_minutiae.size(); // latent minutiae  index
        ind_2 = y[i] - ind_1*rolled_minu_template.m_minutiae.size(); // rolled minutiae index
        simi = simi_matrix(ind_1,ind_2);
        corr.push_back(make_tuple(simi,ind_1,ind_2));
    }

     // step 4: remove false correspondences using two graph matching
    int d_thr = 30;
    vector<tuple<float, int, int>> corr2 = LSS_R_Fast2_Dist_eigen(corr, latent_minu_template, rolled_minu_template, d_thr);

    vector<tuple<float, int, int>> corr3  = LSS_R_Fast2(corr2, latent_minu_template, rolled_minu_template, d_thr);

    if (save_corr){
        ofstream output;
        output.open(corr_file);
        for(int i = 0; i < corr3.size(); i++){
            output<<latent_minu_template.m_minutiae[get<1>(corr3[i])].x<<","<<latent_minu_template.m_minutiae[get<1>(corr3[i])].y
                <<","<<rolled_minu_template.m_minutiae[get<2>(corr3[i])].x<<","<<rolled_minu_template.m_minutiae[get<2>(corr3[i])].y<<endl;
        }
        output.close();
    }

    float score = 0.0;

    for(i=0; i<corr3.size(); ++i)
    {
        score += get<0>(corr3[i]);
    }
    return score;

}

int Matcher::One2One_matching(string latent_file, string rolled_file)
{
    LatentFPTemplate latent_template{};
    try {
    	load_latent_template(latent_file);
    } catch (const std::exception&) {
    	return (1);
    }

    RolledFPTemplate rolled_template{};
    try {
	    rolled_template = load_rolled_template(rolled_file);
    } catch (const std::exception&) {
    	return (2);
    }

    vector<float> score;
    int ret = One2One_matching_selected_templates(latent_template, rolled_template, score);

    return ret;
}

float Matcher::One2One_texture_matching(const LatentTextureTemplate &latent_texture_template, const RolledTextureTemplatePQ &rolled_texture_template)
const
{
    // step 1: compute pairwise similarity between descriptors
    int n_time = 0;
    int i,j,k;

    int des_len = rolled_texture_template.des_length();

   std::unique_ptr<float[]> simi_matrix{new float[MaxNRolledMinu*MaxNLatentMinu]};
   memset(simi_matrix.get(),0,MaxNRolledMinu*MaxNLatentMinu*sizeof(float));

    const int numLatentMinutiae = std::min(latent_texture_template.m_minutiae.size(), MaxNLatentMinu);
    const int numExemplarMinutiae = std::min(rolled_texture_template.m_minutiae.size(), MaxNRolledMinu);

    float simi = 0.0;
    float *p_latent_des, *p_latent_des0, *p_rolled_des;

    using namespace std::chrono;
    vector<high_resolution_clock::time_point> t(10);

    t[n_time++] = high_resolution_clock::now();
    float dist0=0.0, dist1= 0.0, dist2= 0, dist3=0.0, dist4 = 0.0; //, dist5, dist6,dist7, dist8;
    int code1 = 0, code2 = 0, code3 = 0, code4=0;
    float *p_dist_codewords0 = NULL, *p_dist_codewords1 = NULL, *p_dist_codewords2 =NULL;

    /* Make a copy of the codewords from the latent texture template */
    std::vector<float> latent_texture_template_m_dist_codewords_copy{latent_texture_template.m_dist_codewords};

    int n=0;
    int nrof_clusters3 = nrof_clusters*3, nrof_clusters2 = nrof_clusters*2;
    const int method = 1;
    if(method == 1)
    {
        for(i=0; i<numLatentMinutiae; ++i)
        {
            p_dist_codewords0 = latent_texture_template_m_dist_codewords_copy.data() + i*nrof_subs*nrof_clusters;
            for(j=0; j<numExemplarMinutiae; ++j)
            {
                dist1 = 6.;
                dist2 = 0.;
                dist3 = 0.;
                dist4 = 0.;
                p_dist_codewords1 = p_dist_codewords0;
                const unsigned char *p_des0 = rolled_texture_template.m_desPQ.data() + j* rolled_texture_template.des_length();
                for(k=0; k<nrof_subs; k+=4, p_dist_codewords1+=4*nrof_clusters)
                {
                    code1 = *(p_des0+k);
                    dist1 -= *(p_dist_codewords1 + code1);

                    code2 = *(p_des0+k+1);
                    dist2 -= *(p_dist_codewords1 + code2 + nrof_clusters);

                    code3 = *(p_des0+k+2);
                    dist3 -= *(p_dist_codewords1 + code3 + nrof_clusters2);

                    code4 = *(p_des0+k+3);
                    dist4 -= *(p_dist_codewords1 + code4 + nrof_clusters3);

                }
                simi_matrix[n++] = (dist1+dist2)+ (dist3+dist4);
            }
        }
    }
    else if (method==2)
    {
        int B1=64, B2 = 64;
        for(i=0; i<numLatentMinutiae-B1; i+=B1)
        {

            for(j=0; j<numExemplarMinutiae-B2; j += B2)
            {

                for(int ii=i; ii<i+B1; ++ii)
                {
                    p_dist_codewords0 = latent_texture_template_m_dist_codewords_copy.data() + ii*nrof_subs*nrof_clusters;
                    for(int jj=j; jj<j+B2; ++jj)
                    {
                       dist1 = 6.;
                       dist2 = 0.;
                       dist3 = 0.;
                       dist4 = 0.;
                       p_dist_codewords1 = p_dist_codewords0;
                       const unsigned char *p_des0 = rolled_texture_template.m_desPQ.data() + jj* rolled_texture_template.des_length();

                        for(k=0; k<nrof_subs; k+=4, p_dist_codewords1+=4*nrof_clusters)
                        {
                            code1 = *(p_des0+k);
                            dist1 -= *(p_dist_codewords1 + code1);

                            code2 = *(p_des0+k+1);
                            dist2 -= *(p_dist_codewords1 + code2 + nrof_clusters);

                            code3 = *(p_des0+k+2);
                            dist3 -= *(p_dist_codewords1 + code3 + nrof_clusters2);

                            code4 = *(p_des0+k+3);
                            dist4 -= *(p_dist_codewords1 + code4 + nrof_clusters3);

                        }
                        simi_matrix[ii*numExemplarMinutiae+jj] = (dist1+dist2)+ (dist3+dist4);
                    }
                }
            }
        }
    }
    else if(method == 3)
    {
         int B1=64, B2 = 64;
        for(i=0; i<numLatentMinutiae-B1; i+=B1)
        {
            p_dist_codewords0 = latent_texture_template_m_dist_codewords_copy.data() + i*nrof_subs*nrof_clusters;
            for(j=0; j<numExemplarMinutiae-B2; j += B2)
            {
                p_dist_codewords1 = p_dist_codewords0;
                const unsigned char *p_des0 = rolled_texture_template.m_desPQ.data() + j* rolled_texture_template.des_length();
                for(int ii=i; ii<i+B1; ++ii)
                {
                    const unsigned char *p_des1 = p_des0;
                    for(int jj=j; jj<j+B2; ++jj)
                    {
                       p_dist_codewords2 = p_dist_codewords1;
                       dist1 = 6.;
                       dist2 = 0.;
                       dist3 = 0.;
                       dist4 = 0.;

                        for(k=0; k<nrof_subs; k+=4, p_dist_codewords2+=4*nrof_clusters)
                        {
                            code1 = *(p_des1+k);
                            dist1 -= *(p_dist_codewords2 + code1);

                            code2 = *(p_des1+k+1);
                            dist2 -= *(p_dist_codewords2 + code2 + nrof_clusters);

                            code3 = *(p_des1+k+2);
                            dist3 -= *(p_dist_codewords2 + code3 + nrof_clusters2);

                            code4 = *(p_des1+k+3);
                            dist4 -= *(p_dist_codewords2 + code4 + nrof_clusters3);

                        }
                        simi_matrix[ii*numExemplarMinutiae+jj] = (dist1+dist2)+ (dist3+dist4);
                        p_des1 +=  rolled_texture_template.des_length();
                    }
                    p_dist_codewords1 += nrof_subs*nrof_clusters;
                }
            }
        }
    }
    else if(method==4)
    {
        for(i=0; i<numLatentMinutiae; ++i)
        {
            p_dist_codewords0 = latent_texture_template_m_dist_codewords_copy.data() + i*nrof_subs*nrof_clusters;
            for(k=0; k<nrof_subs; ++k)
            {
                for(j=0; j<numExemplarMinutiae-4; j+=4)
                {
                    n = i*numExemplarMinutiae + j;
                    const unsigned char *p_des0 = rolled_texture_template.m_desPQ.data() + j* rolled_texture_template.des_length() + k;
                    const unsigned char *p_des1 = p_des0 + rolled_texture_template.des_length();
                    const unsigned char *p_des2 = p_des1 + rolled_texture_template.des_length();
                    const unsigned char *p_des3 = p_des2 + rolled_texture_template.des_length();

                    dist0 -= *(p_dist_codewords0 + *p_des0);

                    dist1 -= *(p_dist_codewords0 + *p_des1);

                    dist2 -= *(p_dist_codewords0 + *p_des2);

                    dist3 -= *(p_dist_codewords0 + *p_des3);

                    simi_matrix[n] += dist0;
                    simi_matrix[n+1] += dist1;
                    simi_matrix[n+2] += dist2;
                    simi_matrix[n+3] += dist3;
                }
            }
        }
    }
    t[n_time] = high_resolution_clock::now();
    duration<double, std::milli> time_span = t[n_time] - t[n_time-1];

    vector<float> time(10);
    time[n_time-1]+=time_span.count() ;  // minutiae similarity
    n_time++;

//
    std::vector<tuple<float, int, int>>tmp_corr(numLatentMinutiae), corr(N);
    float max_val;
    float *psimi = simi_matrix.get();
    int max_index;
    for(i=0;i<numLatentMinutiae; ++i)
    {

        max_index = std::distance(psimi, std::max_element(psimi, psimi+numExemplarMinutiae));
        max_val = *(psimi + max_index);
        tmp_corr[i] = make_tuple(max_val,i,max_index);

        psimi += numExemplarMinutiae;
    }
    if(tmp_corr.size()>N)
    {
        std::vector<int> y(tmp_corr.size());
        std::iota(y.begin(), y.end(), 0);
        auto comparator = [&tmp_corr](int a, int b){ return get<0>(tmp_corr[a]) > get<0>(tmp_corr[b]); };
        std::sort(y.begin(), y.end(), comparator);

        for(i=0;i<N; ++i)
        {
            corr[i] = tmp_corr[y[i]];
        }
    }
    else
        corr = tmp_corr;
    t[n_time] = high_resolution_clock::now();

    time_span = t[n_time] - t[n_time-1];

    time[n_time-1]+=time_span.count() ;  // obtaining initial correspondences
    n_time++;

     // step 4: remove false correspondences using two graph matching
    int d_thr = 30;
    vector<tuple<float, int, int>> corr2 = LSS_R_Fast2_Dist_lookup(corr, latent_texture_template, rolled_texture_template, d_thr);

    t[n_time] = high_resolution_clock::now();
    time_span = t[n_time] - t[n_time-1];
    time[n_time-1]+=time_span.count() ;   // second order graph matching: distance
    n_time++;

    vector<tuple<float, int, int>> corr3  = LSS_R_Fast2(corr2, latent_texture_template, rolled_texture_template, d_thr);

    t[n_time] = high_resolution_clock::now();
    time_span = t[n_time] - t[n_time-1];
    time[n_time-1]+=time_span.count() ;   // second order graph matching: original
    n_time++;

    float score = 0.0;

    for(i=0; i<corr3.size(); ++i)
    {
        score += get<0>(corr3[i]);
    }
    return score;

}

LatentFPTemplate Matcher::load_latent_template(const std::string &tname) const
{
	std::ifstream is{tname, ifstream::binary};
	if (!is)
		throw std::runtime_error{"Could not open " + tname};

	is.seekg(0, std::ios_base::end);
	std::vector<uint8_t> buf(is.tellg());
	is.seekg(0, std::ios_base::beg);

	is.read(reinterpret_cast<char *>(buf.data()), buf.size());
	return (load_latent_template(buf));
}

LatentFPTemplate Matcher::load_latent_template(const std::vector<uint8_t> &buf) const
{
    LatentFPTemplate fp_template{};
    /*
     * FIXME: When using dynamic-sized containers, these maximums aren't
     *        necessary. Preserving them for historical reasons (we probably
     *        don't want to search when there's >2000 minutiae anyway).
     */
    static const short Max_Nrof_Minutiae = 2*1000; // including virtual minutiae. We only consider top 1000 minutiae including both real and virtual minutiae for each template.
    static const short Max_Des_Length = 192;
    static const short Max_BlkSize = 100;

    std::istringstream is{
        {reinterpret_cast<const char *>(buf.data()), buf.size()},
        std::istringstream::binary};

    // get length of file:
    is.seekg(0, ios::end);
    int length = is.tellg();

    if( length<=0 )
    {
            throw std::runtime_error{"Length of latent template is 0"};
    }
    is.seekg(0, ios::beg);
    short header[12];
    short h,w,blkH,blkW;
    unsigned char nrof_minu_template,nrof_texture_template;
    short nrof_minutiae;

    short nrof_minutiae_feature;
    short des_len;
    int i,j;

    std::vector<short> x{};
    std::vector<short> y{};
    std::vector<float> ori{};
    std::vector<float> des{};

    for(int i=0; i<12; i++){
        is.read(reinterpret_cast<char*>(&header[i]),sizeof(short));
    }
    /*
     * FIXME: Seen at least once where a template appears corrupt (possibly
     *        because an image was too large? it was a palm), so fail fast
     *        before we trigger a segfault allocating one of the internal
     *        structures.
     */
    static const short expectedHeader[12] = {
        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    if (memcmp(header, expectedHeader, 12 * sizeof(short)) != 0) {
    	throw std::runtime_error{"Template appears corrupt (invalid header)"};
    }

    is.read(reinterpret_cast<char*>(&h),sizeof(short));
    is.read(reinterpret_cast<char*>(&w),sizeof(short));
    is.read(reinterpret_cast<char*>(&blkH),sizeof(short));
    is.read(reinterpret_cast<char*>(&blkW),sizeof(short));
    is.read(reinterpret_cast<char*>(&nrof_minu_template),sizeof(unsigned char));
    if(blkH>50)
        blkH = 50;
    if(blkW>50)
        blkW = 50;
    for(i=0;i<nrof_minu_template; ++i)
    {
        is.read(reinterpret_cast<char*>(&nrof_minutiae),sizeof(short));
        if(nrof_minutiae<=0)
            continue;
        if(nrof_minutiae>Max_Nrof_Minutiae)
        {
            throw std::runtime_error{"Number of minutiae is larger than "
                "Max Number of Minutiae (latent): " +
                std::to_string(nrof_minutiae) + ">" +
                std::to_string(Max_Nrof_Minutiae)};
        }
        if(blkH>Max_BlkSize || blkW>Max_BlkSize)
        {
            throw std::runtime_error{"The size of the ridge flow is larger "
                "than maximum size: " + std::to_string(Max_BlkSize)};
        }
        x.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(x.data()),sizeof(short)*nrof_minutiae);
        y.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(y.data()),sizeof(short)*nrof_minutiae);
        ori.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(ori.data()),sizeof(float)*nrof_minutiae);
        is.read(reinterpret_cast<char*>(&des_len),sizeof(short));

        des.resize(nrof_minutiae * des_len);
        is.read(reinterpret_cast<char*>(des.data()),sizeof(float)*nrof_minutiae*des_len);

        MinutiaeTemplate minu_template(nrof_minutiae,x,y,ori,des,blkH, blkW);
        fp_template.add_template(minu_template);
    }

    is.read(reinterpret_cast<char*>(&nrof_texture_template),sizeof(unsigned char));

   for(i=0;i<nrof_texture_template; ++i)
    {
        is.read(reinterpret_cast<char*>(&nrof_minutiae),sizeof(short));
        if(nrof_minutiae<=0)
            continue;
        if(nrof_minutiae>Max_Nrof_Minutiae)
        {
            throw std::runtime_error{"Number of minutiae is larger than Max "
                "Number of Minutiae: " + std::to_string(nrof_minutiae) + ">" +
                std::to_string(Max_Nrof_Minutiae)};
        }
        x.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(x.data()),sizeof(short)*nrof_minutiae);
        y.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(y.data()),sizeof(short)*nrof_minutiae);
        ori.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(ori.data()),sizeof(float)*nrof_minutiae);
        is.read(reinterpret_cast<char*>(&des_len),sizeof(short));
        des.resize(nrof_minutiae * des_len);
        is.read(reinterpret_cast<char*>(des.data()),sizeof(float)*nrof_minutiae*des_len);

        LatentTextureTemplate texture_template(nrof_minutiae,x,y,ori,des);
        texture_template.compute_dist_to_codewords(this->codewords, nrof_subs,  sub_dim,  nrof_clusters);
        fp_template.add_texture_template(texture_template);
    }

    return (fp_template);
}

RolledFPTemplate Matcher::load_rolled_template(const string &tname) const
{
	std::ifstream is{tname, ifstream::binary};
	if (!is)
		throw std::runtime_error{"Could not open " + tname};

	is.seekg(0, std::ios_base::end);
	std::vector<uint8_t> buf(is.tellg());
	is.seekg(0, std::ios_base::beg);

	is.read(reinterpret_cast<char *>(buf.data()), buf.size());
	return (load_rolled_template(buf));
}

RolledFPTemplate Matcher::load_rolled_template(const std::vector<uint8_t> &buf) const
{
    RolledFPTemplate fp_template{};
    /*
     * FIXME: When using dynamic-sized containers, these maximums aren't
     *        necessary. Preserving them for historical reasons (we probably
     *        don't want to search when there's >2000 minutiae anyway).
     */
    static const short Max_Nrof_Minutiae = 2*1000; // including virtual minutiae. We only consider top 1000 minutiae including both real and virtual minutiae for each template.
    static const short Max_Des_Length = 192;
    static const short Max_BlkSize = 100;

    std::istringstream is{
        {reinterpret_cast<const char *>(buf.data()), buf.size()},
        std::istringstream::binary};

    // get length of file:
    is.seekg(0, ios::end);
    int length = is.tellg();

    if( length<=10 )
    {
        throw std::runtime_error{"Size of rolled template is " +
            std::to_string(length) + " (<= 10)"};
    }
    is.seekg(0, ios::beg);
    short header[12];
    short h,w,blkH,blkW;
    unsigned char nrof_minu_template,nrof_texture_template;
    short nrof_minutiae;

    short nrof_minutiae_feature;
    short des_len=96;
    int i,j;

    std::vector<short> x{};
    std::vector<short> y{};
    std::vector<float> ori{};

    std::vector<float> des{};

    for(int i=0; i<12; i++){
        is.read(reinterpret_cast<char*>(&header[i]),sizeof(short));
    }
    /*
     * FIXME: Seen at least once where a template appears corrupt (possibly
     *        because an image was too large? it was a palm), so fail fast
     *        before we trigger a segfault allocating one of the internal
     *        structures.
     */
    static const short expectedHeader[12] = {
        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    if (memcmp(header, expectedHeader, 12 * sizeof(short)) != 0) {
    	throw std::runtime_error{"Template appears corrupt (invalid header)"};
    }

    is.read(reinterpret_cast<char*>(&h),sizeof(short));
    is.read(reinterpret_cast<char*>(&w),sizeof(short));
    is.read(reinterpret_cast<char*>(&blkH),sizeof(short));
    is.read(reinterpret_cast<char*>(&blkW),sizeof(short));
    is.read(reinterpret_cast<char*>(&nrof_minu_template),sizeof(unsigned char));
    if(blkH>50)
        blkH = 50;
    if(blkW>50)
        blkW = 50;
    for(i=0;i<nrof_minu_template; ++i)
    {
        is.read(reinterpret_cast<char*>(&nrof_minutiae),sizeof(short));
        if(nrof_minutiae<=0)
            continue;
        if(nrof_minutiae>Max_Nrof_Minutiae)
        {
            throw std::runtime_error{"Number of minutiae is larger than Max "
                "Number of Minutiae: " + std::to_string(nrof_minutiae) + ">" +
                std::to_string(Max_Nrof_Minutiae)};
        }
        if(blkH>Max_BlkSize || blkW>Max_BlkSize)
        {
            throw std::runtime_error{"The size of the ridge flow is larger "
                "than maximum size: " + std::to_string(Max_BlkSize)};
        }
        x.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(x.data()),sizeof(short)*nrof_minutiae);
        y.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(y.data()),sizeof(short)*nrof_minutiae);
        ori.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(ori.data()),sizeof(float)*nrof_minutiae);
        is.read(reinterpret_cast<char*>(&des_len),sizeof(short));

        des.resize(nrof_minutiae * des_len);
        is.read(reinterpret_cast<char*>(des.data()),sizeof(float)*nrof_minutiae*des_len);

        MinutiaeTemplate minu_template(nrof_minutiae,x,y,ori,des,blkH, blkW);
        fp_template.add_template(minu_template);
    }

    is.read(reinterpret_cast<char*>(&nrof_texture_template),sizeof(unsigned char));

   for(i=0;i<nrof_texture_template; ++i)
    {
        is.read(reinterpret_cast<char*>(&nrof_minutiae),sizeof(short));
        if(nrof_minutiae<=0)
            continue;
        if(nrof_minutiae>Max_Nrof_Minutiae)
        {
            throw std::runtime_error{"Number of minutiae is larger than Max "
                "Number of Minutiae: " + std::to_string(nrof_minutiae) + ">" +
                std::to_string(Max_Nrof_Minutiae)};
        }
        x.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(x.data()),sizeof(short)*nrof_minutiae);
        y.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(y.data()),sizeof(short)*nrof_minutiae);
        ori.resize(nrof_minutiae);
        is.read(reinterpret_cast<char*>(ori.data()),sizeof(float)*nrof_minutiae);
        is.read(reinterpret_cast<char*>(&des_len),sizeof(short));
        des.resize(nrof_minutiae * des_len);
        is.read(reinterpret_cast<char*>(des.data()),sizeof(float)*nrof_minutiae*des_len);

        RolledTextureTemplatePQ texture_template(nrof_minutiae,x,y,ori,des);
        fp_template.add_texture_template(texture_template);
    }

    return (fp_template);
}

vector<tuple<float, int, int>>  Matcher::LSS_R_Fast2_Dist(vector<tuple<float, int, int>> &corr, const SingleTemplate & latent_template, const SingleTemplate & rolled_template, float d_thr)
const
{
    int num = corr.size();
    vector<float> H(num*num);

    vector<short> flag_latent(latent_template.m_minutiae.size()),flag_rolled(rolled_template.m_minutiae.size());

    int i,j,k;

    float dist_1, dist_2, dist;
    float dx_1, dy_1, dx_2, dy_2;

    for(i=0; i<num-1; ++i)
    {
        const MinuPoint p_latent_minutia_1 = latent_template.m_minutiae[get<1>(corr[i])];
        const MinuPoint p_rolled_minutia_1 = rolled_template.m_minutiae[get<2>(corr[i])];
        for(j=i+1; j<num;++j)
        {
            const MinuPoint p_latent_minutia_2 = latent_template.m_minutiae[get<1>(corr[j])];
            const MinuPoint p_rolled_minutia_2 = rolled_template.m_minutiae[get<2>(corr[j])];

            dx_1 = p_latent_minutia_1.x-p_latent_minutia_2.x;
            dx_2 = p_rolled_minutia_1.x-p_rolled_minutia_2.x;

            dy_1 = p_latent_minutia_1.y-p_latent_minutia_2.y;
            dy_2 = p_rolled_minutia_1.y-p_rolled_minutia_2.y;

            dist_1 = (dx_1*dx_1)+(dy_1*dy_1);
            dist_1 = sqrt(dist_1);

            dist_2 = (dx_2*dx_2)+(dy_2*dy_2);
            dist_2 = sqrt(dist_2);

            dist = fabs(dist_1-dist_2);

            H[i*num+j] = (30-dist)/(25.0);
            if(H[i*num+j]>1)
                H[i*num+j] = 1.0;
            else if(H[i*num+j]<0)
                H[i*num+j] = 0.0;

            H[j*num+i] = H[i*num+j];
        }
    }

    vector<float> S(num),S1(num);

    float s0 = 1.0/num;
    for(i=0; i<num; ++i)
        S[i] = get<0>(corr[i]);

    float sum = 0.0;
    for(i=0;i<5 ; ++i)
    {
        sum = 0.0;
        for(j=0;j<num; ++j)
        {
            S1[j] = 0;
            for(k=0; k<num;++k)
            {
                //if(H[j*num+k])
                S1[j] += H[j*num+k]*S[k];
            }
            sum += S1[j];
        }
        sum = 1.0/(sum+0.0001);
        for(j=0;j<num; ++j)
        {
            S[j] = S1[j]*sum;
        }
    }

    // sort the S
    // the sorting part can be replaced by a min-heap
    std::vector<int> y(S.size());
    std::iota(y.begin(), y.end(), 0);
    auto comparator = [&S](int a, int b){ return S[a] > S[b]; };
    std::sort(y.begin(), y.end(), comparator);

    vector<tuple<float, int, int>>  new_corr;
    vector<int>  selected_ind;
    short ind;
    for(i=0; i<num; ++i)
    {
        ind = y[i];
        if(S[ind]<0.0001)
            break;
        if((flag_latent[get<1>(corr[ind])] == 1) || (flag_rolled[get<2>(corr[ind])] == 1))
            continue;

        if(i==0)
        {
            selected_ind.push_back(ind);
            new_corr.push_back(make_tuple(get<0>(corr[ind]),get<1>(corr[ind]),get<2>(corr[ind])));

            flag_latent[get<1>(corr[ind])] = 1;
            flag_rolled[get<2>(corr[ind])] = 1;
        }
        else
        {
            int found =0;
            for(j=0;j<selected_ind.size(); ++j)
            {
                if(H[ind*num+selected_ind[j]]<0.00001)
                {
                    found = 1;
                    break;
                }
            }
            if(found==0)
            {
                selected_ind.push_back(ind);
                new_corr.push_back(make_tuple(get<0>(corr[ind]),get<1>(corr[ind]),get<2>(corr[ind])));

                flag_latent[get<1>(corr[ind])] = 1;
                flag_rolled[get<2>(corr[ind])] = 1;
            }
        }
    }

    return new_corr;
}

vector<tuple<float, int, int>>  Matcher::LSS_R_Fast2_Dist_lookup(vector<tuple<float, int, int>> &corr, const SingleTemplate & latent_template, const SingleTemplate & rolled_template, float d_thr)
const
{
    int num = corr.size();
    std::unique_ptr<float[]> H{new float [num*num]()};
    vector<short> flag_latent(latent_template.m_minutiae.size()),flag_rolled(rolled_template.m_minutiae.size());

    int i,j,k;

    float dist_1, dist_2, dist;
    int  dx_1, dy_1, dx_2, dy_2;

    for(i=0; i<num-1; ++i)
    {
        const MinuPoint p_latent_minutia_1 = latent_template.m_minutiae[get<1>(corr[i])];
        const MinuPoint p_rolled_minutia_1 = rolled_template.m_minutiae[get<2>(corr[i])];
        for(j=i+1; j<num;++j)
        {
            const MinuPoint p_latent_minutia_2 = latent_template.m_minutiae[get<1>(corr[j])];
            const MinuPoint p_rolled_minutia_2 = rolled_template.m_minutiae[get<2>(corr[j])];

            dx_1 = p_latent_minutia_1.x-p_latent_minutia_2.x;
            dx_2 = p_rolled_minutia_1.x-p_rolled_minutia_2.x;

            dx_1 = abs(dx_1);
            dx_2 = abs(dx_2);
            dy_1 = p_latent_minutia_1.y-p_latent_minutia_2.y;
            dy_2 = p_rolled_minutia_1.y-p_rolled_minutia_2.y;

            dy_1 = abs(dy_1);
            dy_2 = abs(dy_2);

            if((dx_1>=dist_N) || (dx_2>=dist_N) || (dy_1>=dist_N) || (dy_2>=dist_N))
                continue;

            dist_1 = table_dist[dx_1*dist_N+dy_1];

            dist_2 = table_dist[dx_2*dist_N+dy_2];

            dist = fabs(dist_1-dist_2);
            if(dist>d_thr)
                continue;

            H[i*num+j] = (30-dist)/(25.0);
            if(H[i*num+j]>1)
                H[i*num+j] = 1.0;
            else if(H[i*num+j]<0)
                H[i*num+j] = 0.0;
            H[j*num+i] = H[i*num+j];
        }
    }

    Matrix<float, Eigen::Dynamic, Eigen::Dynamic> aa =  Map<Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(H.get(),num,num);

    float sum = 0.0;
    VectorXf b(num);
    VectorXf c;
    for(i=0; i<num; ++i)
        b(i) = get<0>(corr[i]);
    for(i=0;i<3 ; ++i)
    {
        c = aa*b;
        sum = c.sum();
        b = c*(1./(sum+0.00001));
    }

    vector<float> S(num);
    for(i=0;i<num; ++i)
    {
        S[i] = b(i);
    }

    // sort S
    std::vector<int> y(S.size());
    std::iota(y.begin(), y.end(), 0);
    auto comparator = [&S](int a, int b){ return S[a] > S[b]; };
    std::sort(y.begin(), y.end(), comparator);

    vector<tuple<float, int, int>>  new_corr;
    vector<int>  selected_ind;
    short ind;
    for(i=0; i<num; ++i)
    {

        ind = y[i];
        if(S[ind]<0.0001)
            break;
        if((flag_latent[get<1>(corr[ind])] == 1) || (flag_rolled[get<2>(corr[ind])] == 1))
            continue;

        if(i==0)
        {
            selected_ind.push_back(ind);
            new_corr.push_back(make_tuple(get<0>(corr[ind]),get<1>(corr[ind]),get<2>(corr[ind])));

            flag_latent[get<1>(corr[ind])] = 1;
            flag_rolled[get<2>(corr[ind])] = 1;
        }
        else
        {
            int found = 0;
            for(j=0;j<selected_ind.size(); ++j)
            {
                if(H[ind*num+selected_ind[j]]<0.00001)
                {
                    found = 1;
                    break;
                }
            }
            if(found==0)
            {
                selected_ind.push_back(ind);
                new_corr.push_back(make_tuple(get<0>(corr[ind]),get<1>(corr[ind]),get<2>(corr[ind])));

                flag_latent[get<1>(corr[ind])] = 1;
                flag_rolled[get<2>(corr[ind])] = 1;
            }
        }
    }

    return new_corr;
}

vector<tuple<float, int, int>>  Matcher::LSS_R_Fast2_Dist_eigen(vector<tuple<float, int, int>> &corr, const SingleTemplate & latent_template, const SingleTemplate & rolled_template, float d_thr)
const
{
    int num = corr.size();
    std::unique_ptr<float[]> H{new float [num*num]()};

    vector<short> flag_latent(latent_template.m_minutiae.size()),flag_rolled(rolled_template.m_minutiae.size());

    int i,j,k;

    MinuPoint *p_latent_minutia_1, *p_latent_minutia_2, *p_rolled_minutia_1, *p_rolled_minutia_2;
    float dist_1, dist_2, dist;
    float dx_1, dy_1, dx_2, dy_2;

    for(i=0; i<num-1; ++i)
    {
        const MinuPoint p_latent_minutia_1 = latent_template.m_minutiae[get<1>(corr[i])];
        const MinuPoint p_rolled_minutia_1 = rolled_template.m_minutiae[get<2>(corr[i])];
        for(j=i+1; j<num;++j)
        {
            const MinuPoint p_latent_minutia_2 = latent_template.m_minutiae[get<1>(corr[j])];
            const MinuPoint p_rolled_minutia_2 = rolled_template.m_minutiae[get<2>(corr[j])];

            dx_1 = p_latent_minutia_1.x-p_latent_minutia_2.x;
            dx_2 = p_rolled_minutia_1.x-p_rolled_minutia_2.x;

            dy_1 = p_latent_minutia_1.y-p_latent_minutia_2.y;
            dy_2 = p_rolled_minutia_1.y-p_rolled_minutia_2.y;

            dist_1 = (dx_1*dx_1)+(dy_1*dy_1);
            dist_1 = sqrt(dist_1);

            dist_2 = (dx_2*dx_2)+(dy_2*dy_2);
            dist_2 = sqrt(dist_2);
            dist = fabs(dist_1-dist_2);
           if(dist>d_thr)
                continue;

            H[i*num+j] = (30-dist)/(25.0);
            if(H[i*num+j]>1)
                H[i*num+j] = 1.0;
            else if(H[i*num+j]<0)
                H[i*num+j] = 0.0;

            H[j*num+i] = H[i*num+j];
        }
    }

    Matrix<float, Eigen::Dynamic, Eigen::Dynamic> aa =  Map<Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(H.get(),num,num);

    float sum = 0.0;
    VectorXf b(num);
    VectorXf c;
    for(i=0; i<num; ++i)
        b(i) = get<0>(corr[i]);
    for(i=0;i<5 ; ++i)
    {
        c = aa*b;
        sum = c.sum();
        b = c*(1./(sum+0.00001));
    }

    vector<float> S(num);
    for(i=0;i<num; ++i)
    {
        S[i] = b(i);
    }
    // sort the S
    // the sorting part can be replaced by a min-heap
    std::vector<int> y(S.size());
    std::iota(y.begin(), y.end(), 0);
    auto comparator = [&S](int a, int b){ return S[a] > S[b]; };
    std::sort(y.begin(), y.end(), comparator);

    vector<tuple<float, int, int>>  new_corr;
    vector<int>  selected_ind;
    short ind;
    for(i=0; i<num; ++i)
    {

        ind = y[i];
        if(S[ind]<0.0001)
            break;
        if((flag_latent[get<1>(corr[ind])] == 1) || (flag_rolled[get<2>(corr[ind])] == 1))
            continue;

        if(i==0)
        {
            selected_ind.push_back(ind);
            new_corr.push_back(make_tuple(get<0>(corr[ind]),get<1>(corr[ind]),get<2>(corr[ind])));

            flag_latent[get<1>(corr[ind])] = 1;
            flag_rolled[get<2>(corr[ind])] = 1;
        }
        else
        {
            int found =0;
            for(j=0;j<selected_ind.size(); ++j)
            {
                if(H[ind*num+selected_ind[j]]<0.00001)
                {
                    found = 1;
                    break;
                }
            }
            if(found==0)
            {
                selected_ind.push_back(ind);
                new_corr.push_back(make_tuple(get<0>(corr[ind]),get<1>(corr[ind]),get<2>(corr[ind])));

                flag_latent[get<1>(corr[ind])] = 1;
                flag_rolled[get<2>(corr[ind])] = 1;
            }
        }
    }

    return new_corr;
}

vector<tuple<float, int, int>>  Matcher::LSS_R_Fast2(vector<tuple<float, int, int>> &corr, const SingleTemplate & latent_template, const SingleTemplate & rolled_template, int d_thr)
const
{
    int num = corr.size();
    vector<bool> H(num*num);
    vector<short> flag_latent(latent_template.m_minutiae.size()),flag_rolled(rolled_template.m_minutiae.size());

    int i,j,k;

    float dist_1, dist_2;
    float angle_1, angle_2, angle_diff;
    float line_angle_1, line_angle_2;
    float dx_1,dx_2,dy_1,dy_2;

    for(i=0; i<num-1; ++i)
    {
        const MinuPoint p_latent_minutia_1 = latent_template.m_minutiae[get<1>(corr[i])];
        const MinuPoint p_rolled_minutia_1 = rolled_template.m_minutiae[get<2>(corr[i])];
        for(j=i+1; j<num;++j)
        {
            const MinuPoint p_latent_minutia_2 = latent_template.m_minutiae[get<1>(corr[j])];
            const MinuPoint p_rolled_minutia_2 = rolled_template.m_minutiae[get<2>(corr[j])];

            angle_1 = p_latent_minutia_1.ori-p_latent_minutia_2.ori;
            angle_1 = adjust_angle(angle_1);

            angle_2 = p_rolled_minutia_1.ori-p_rolled_minutia_2.ori;
            angle_2 = adjust_angle(angle_2);

            angle_diff = fabs(angle_1 - angle_2);

            if(angle_diff>PI)
               angle_diff = 2*PI - angle_diff;

            if(angle_diff>PI/4.)
                continue;

            dx_1 = p_latent_minutia_1.x-p_latent_minutia_2.x;
            dy_1 = p_latent_minutia_1.y-p_latent_minutia_2.y;

            line_angle_1 = -atan2(dy_1,dx_1);
            angle_1 = p_latent_minutia_1.ori - line_angle_1;
            angle_1 = adjust_angle(angle_1);

            dx_2 = p_rolled_minutia_1.x-p_rolled_minutia_2.x;
            dy_2 = p_rolled_minutia_1.y-p_rolled_minutia_2.y;

            line_angle_2 = -atan2(dy_2,dx_2);
            angle_2 = p_rolled_minutia_1.ori - line_angle_2;
            angle_2 = adjust_angle(angle_2);

            angle_diff = fabs(angle_1 - angle_2);

            if(angle_diff>PI)
               angle_diff = 2*PI - angle_diff;
            if(angle_diff>PI/6.)
                continue;

            angle_1 = p_latent_minutia_2.ori - line_angle_1;
            angle_1 = adjust_angle(angle_1);

            angle_2 = p_rolled_minutia_2.ori - line_angle_2;
            angle_2 = adjust_angle(angle_2);

            angle_diff = fabs(angle_1 - angle_2);

            if(angle_diff>PI)
               angle_diff = 2*PI - angle_diff;
            if(angle_diff>PI/6.)
                continue;

            H[i*num+j] = true;
            H[j*num+i] = true;
        }
    }

    vector<float> S(num),S1(num);

    float s0 = 1.0/num;
    for(i=0; i<num; ++i)
        S[i] = s0;

    float sum = 0.0;
    for(i=0;i<5 ; ++i)
    {
        sum = 0.0;
        for(j=0;j<num; ++j)
        {
            S1[j] = 0;
            for(k=0; k<num;++k)
            {
                if(H[j*num+k])
                   S1[j] += S[k];
            }
            sum += S1[j];
        }
        sum = 1.0/(sum+0.00001);
        for(j=0;j<num; ++j)
        {
            S[j] = S1[j]*sum;
        }
    }

    s0 = 0.0;

    // sort the S
    // the sorting part can be replaced by a min-heap
    std::vector<int> y(S.size());
    std::iota(y.begin(), y.end(), 0);
    auto comparator = [&S](int a, int b){ return S[a] > S[b]; };
    std::sort(y.begin(), y.end(), comparator);

    vector<tuple<float, int, int>>  new_corr;
    vector<int> selected_ind;
    short ind;
    for(i=0; i<num; ++i)
    {
        ind = y[i];
        if(S[ind]<0.001)
            break;
        if((flag_latent[get<1>(corr[ind])] == 1) || (flag_rolled[get<2>(corr[ind])] == 1))
            continue;

          if(i==0)
        {
            selected_ind.push_back(ind);
            new_corr.push_back(make_tuple(get<0>(corr[ind]),get<1>(corr[ind]),get<2>(corr[ind])));

            flag_latent[get<1>(corr[ind])] = 1;
            flag_rolled[get<2>(corr[ind])] = 1;
        }
        else
        {
            int found =0;
            for(j=0;j<selected_ind.size(); ++j)
            {
                if(!H[ind*num+selected_ind[j]])
                {
                    found = 1;
                    break;
                }
            }
            if(found==0)
            {
                selected_ind.push_back(ind);
                new_corr.push_back(make_tuple(get<0>(corr[ind]),get<1>(corr[ind]),get<2>(corr[ind])));

                flag_latent[get<1>(corr[ind])] = 1;
                flag_rolled[get<2>(corr[ind])] = 1;
            }

        }
    }

    return new_corr;
}

float Matcher::adjust_angle(float angle)
const
{
    if(angle>PI)
        angle -= 2*PI;
    else if (angle<-PI)
    {
        angle += 2*PI;
    }
    return angle;
}
}


// Copyright (C) 2015 National ICT Australia (NICTA)
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// -------------------------------------------------------------------
//
// Written by Conrad Sanderson - http://conradsanderson.id.au

#include <armadillo>
#include "catch.hpp"

using namespace arma;

TEST_CASE("fn_conv_to_1")
  {
  typedef std::vector<double> stdvec;

  stdvec x(3);
  x[0] = 10.0; x[1] = 20.0;  x[2] = 30.0;

  colvec y = conv_to< colvec >::from(x);
  stdvec z = conv_to< stdvec >::from(y);

  REQUIRE( z[0] == Approx(10.0) );
  REQUIRE( z[1] == Approx(20.0) );
  REQUIRE( z[2] == Approx(30.0) );
  }

TEST_CASE("fn_conv_to2")
  {
  mat A(5,6); A.fill(0.1);

  umat uA = conv_to<umat>::from(A);
  imat iA = conv_to<imat>::from(A);

  REQUIRE( (uA.n_rows - A.n_rows) == 0 );
  REQUIRE( (iA.n_rows - A.n_rows) == 0 );

  REQUIRE( (uA.n_cols - A.n_cols) == 0 );
  REQUIRE( (iA.n_cols - A.n_cols) == 0 );

  REQUIRE( any(vectorise(uA)) == false);
  REQUIRE( any(vectorise(iA)) == false);
  }

TEST_CASE("fn_conv_to3")
  {
  mat A(5,6); A.fill(1.0);

  umat uA = conv_to<umat>::from(A);
  imat iA = conv_to<imat>::from(A);

  REQUIRE( all(vectorise(uA)) == true);
  REQUIRE( all(vectorise(iA)) == true);
  }

TEST_CASE("fn_conv_to4")
  {
  mat A =   linspace<rowvec>(1,5,6);
  mat B = 2*linspace<colvec>(1,5,6);
  mat C = randu<mat>(5,6);

  REQUIRE( as_scalar( conv_to<rowvec>::from(A) * conv_to<colvec>::from(B) ) == Approx(130.40) );

  REQUIRE( conv_to<double>::from(A * B) == Approx(130.40) );

  REQUIRE_THROWS( conv_to<colvec>::from(C) );
  }


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#define NT2_UNIT_MODULE "nt2 complex.trigonometric toolbox - acos/scalar Mode"

//////////////////////////////////////////////////////////////////////////////
// unit test behavior of trigonometric components in scalar mode
//////////////////////////////////////////////////////////////////////////////
/// created by jt the 08/12/2010
///
#include <nt2/include/functions/acos.hpp>
#include <boost/type_traits/is_same.hpp>
#include <nt2/sdk/functor/meta/call.hpp>
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/meta/as_floating.hpp>
#include <nt2/sdk/meta/as_signed.hpp>
#include <nt2/sdk/meta/upgrade.hpp>
#include <nt2/sdk/meta/downgrade.hpp>
#include <nt2/sdk/meta/scalar_of.hpp>
#include <boost/dispatch/meta/as_floating.hpp>
#include <boost/type_traits/common_type.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/memory/buffer.hpp>
#include <nt2/toolbox/constant/constant.hpp>

NT2_TEST_CASE_TPL ( cos_real__1_0,  NT2_REAL_TYPES)
{

  using nt2::cos;
  using nt2::tag::cos_;
  typedef std::complex<T> cT;
  typedef typename nt2::meta::as_integer<T>::type iT;
  typedef typename nt2::meta::call<acos_(cT)>::type r_t;
  typedef typename nt2::meta::scalar_of<r_t>::type ssr_t;
  typedef typename nt2::meta::upgrade<T>::type u_t;
  typedef typename nt2:: meta::as_complex<T>::type wished_r_t;

  // return type conformity test
  NT2_TEST( (boost::is_same < r_t, wished_r_t >::value) );
  std::cout << std::endl;
  double ulpd;
  ulpd=0.0;

  // specific values tests
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Zero<T>(), nt2::Zero<T>())), cT(nt2::Pio_2<T>(), nt2::Zero<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Nan <T>(), nt2::Zero<T>())), cT(nt2::Nan <T>(), nt2::Zero<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Inf <T>(), nt2::Zero<T>())), cT(nt2::Nan <T>(), nt2::Zero<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Minf<T>(), nt2::Zero<T>())), cT(nt2::Nan <T>(), nt2::Zero<T>()), 0.75);

  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Zero<T>(), nt2::Nan <T>())), cT(nt2::Pio_2<T>(), nt2::Zero<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::One <T>(), nt2::Inf <T>())), cT(nt2::Pio_2<T>(), nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Mone<T>(), nt2::Inf <T>())), cT(nt2::Pio_2<T>(), nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::One <T>(), nt2::Nan <T>())), cT(nt2::Nan<T>(),   nt2::NanT>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Mone<T>(), nt2::Inf <T>())), cT(nt2::Pio_2<T>(), nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Minf<T>(), nt2::One <T>())), cT(nt2::Pi <T>(),   nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Inf <T>(), nt2::One <T>())), cT(nt2::Zero<T>(),  nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Minf<T>(), nt2::Inf <T>())), cT(3*nt2::Pi <T>()/4,nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Inf <T>(), nt2::Inf <T>())), cT(nt2::Pi<T>()/4,  nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Inf <T>(), nt2::Nan<T>())),  cT(nt2::Nan <T>(), nt2::Inf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Nan  <T>(), nt2::One<T>())), cT(nt2::Nan <T>(), nt2::Nan<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Nan  <T>(), nt2::Mone<T>())),cT(nt2::Nan <T>(), nt2::Nan<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Nan  <T>(), nt2::Inf<T>())),  cT(nt2::Inf <T>(), nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Nan  <T>(), nt2::Nan<T>())),  cT(nt2::Nan <T>(), nt2::Nan<T>()), 0.75);

  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Zero<T>(), -nt2::Nan <T>())), cT(nt2::Pio_2<T>(), -nt2::Zero<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::One <T>(), -nt2::Inf <T>())), cT(nt2::Pio_2<T>(), -nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Mone<T>(), -nt2::Inf <T>())), cT(nt2::Pio_2<T>(), -nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::One <T>(), -nt2::Nan <T>())), cT(nt2::Nan<T>(),   -nt2::NanT>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Mone<T>(), -nt2::Inf <T>())), cT(nt2::Pio_2<T>(), -nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Minf<T>(), -nt2::One <T>())), cT(nt2::Pi <T>(),   -nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Inf <T>(), -nt2::One <T>())), cT(nt2::Zero<T>(),  -nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Minf<T>(), -nt2::Inf <T>())), cT(3*nt2::Pi <T>()/4,-nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Inf <T>(), -nt2::Inf <T>())), cT(nt2::Pi<T>()/4,  -nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Inf <T>(), -nt2::Nan<T>())),  cT(nt2::Nan <T>(), -nt2::Inf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Nan  <T>(), -nt2::One<T>())), cT(nt2::Nan <T>(), -nt2::Nan<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Nan  <T>(), -nt2::Mone<T>())),cT(nt2::Nan <T>(), -nt2::Nan<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Nan  <T>(), -nt2::Inf<T>())),  cT(nt2::Inf <T>(), -nt2::Minf<T>()), 0.75);
  NT2_TEST_ULP_EQUAL(nt2::acos(cT(nt2::Nan  <T>(), -nt2::Nan<T>())),  cT(nt2::Nan <T>(), -nt2::Nan<T>()), 0.75);

 } // end of test for floating_

// cacos(conj(z)) = conj(cacos(z)).
//  cacos((+/-)0 + i0) returns pi /2 $B!](B i0.
//  cacos((+/-)0 + iNaN) returns pi /2 + iNaN.
//  cacos(x + i inf) returns pi /2 $B!](B i inf, for finite x.
//  cacos(x + iNaN) returns NaN + iNaN and optionally raises the invalid floating-point exception, for nonzero finite x.
//  cacos($B!](Binf + iy) returns pi $B!](B i inf, for positive-signed finite y.
//  cacos(+inf + iy) returns +0 $B!](B i inf, for positive-signed finite y.
//  cacos($B!](Binf + i inf) returns 3 pi /4 $B!](B i inf.
//  cacos(+inf + i inf) returns pi /4 $B!](B i inf.
//  cacos((+/-)inf + iNaN) returns NaN (+/-) i inf (where the sign of the imaginary part of the
// result is unspecified).
//  cacos(NaN + iy) returns NaN + iNaN and optionally raises the invalid floating-point exception, for finite y.
//  cacos(NaN + i inf) returns NaN $B!](B i inf.
//  cacos(NaN + iNaN) returns NaN + iNaN.


/*=============================================================================
    Phoenix V1.2.1
    Copyright (c) 2001-2003 Joel de Guzman

    Use, modification and distribution is subject to the Boost Software
    License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
    http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#include <iostream>
#include <boost/detail/lightweight_test.hpp>

#define PHOENIX_LIMIT 15
#include <boost/spirit/include/phoenix1_primitives.hpp>
#include <boost/spirit/include/phoenix1_composite.hpp>
#include <boost/spirit/include/phoenix1_functions.hpp>
#include <boost/spirit/include/phoenix1_operators.hpp>

using namespace phoenix;
using namespace std;

    ///////////////////////////////////////////////////////////////////////////////
    struct sqr_ {

        template <typename ArgT>
        struct result { typedef ArgT type; };

        template <typename ArgT>
        ArgT operator()(ArgT n) const { return n * n; }
    };

    function<sqr_> sqr;

    ///////////////////////////////////////////////////////////////////////////////
    struct adder_ {

        template <typename Arg1T, typename Arg2T, typename ArgT3>
        struct result { typedef Arg1T type; };

        template <typename Arg1T, typename Arg2T, typename ArgT3>
        Arg1T operator()(Arg1T a, Arg2T b, ArgT3 c) const { return a + b + c; }
    };

    function<adder_> adder;

///////////////////////////////////////////////////////////////////////////////
int
main()
{
    int     i2 = 2, i = 4, i50 = 50, i10 = 10, i20 = 20, i100 = 100;
    double  d5 = 5, d10 = 10;
    string hello = "hello";

///////////////////////////////////////////////////////////////////////////////
//
//  More complex expressions
//
///////////////////////////////////////////////////////////////////////////////
    BOOST_TEST((10 - arg1)(i100) == (10 - i100));
    BOOST_TEST((20 - arg1)(i100) == (20 - i100));
    BOOST_TEST((arg1 - 10)(i100) == (i100 - 10));
    BOOST_TEST((arg1 - 20)(i100) == (i100 - 20));
    BOOST_TEST((arg1 - arg2)(i100, i50) == (i100 - i50));
    BOOST_TEST((arg1 - var(i))(i10) == (i10 - i));
    BOOST_TEST((arg1 + arg2 - arg3)(i100, i50, i20) == (i100 + i50 - i20));
    BOOST_TEST((sqr(arg1) + arg2 - arg3)(i100, i50, i20) == ((i100*i100) + i50 - i20));

    int ii = i;
    BOOST_TEST((var(i) += arg1)(i2) == (ii += i2));
    BOOST_TEST((sqr(sqr(arg1)))(i100) == (i100*i100*i100*i100));

#if 0   /*** SHOULD NOT COMPILE ***/
    (val(3) += arg1)(i100);
    (val(3) = 3)();
#endif

    BOOST_TEST(((adder(arg1, arg2, arg3) + arg2 - arg3)(i100, i50, i20)) == (i100 + i50 + i20) + i50 - i20);
    BOOST_TEST((adder(arg1, arg2, arg3)(i100, i50, i20)) == (i100 + i50 + i20));
    BOOST_TEST((sqr(sqr(sqr(sqr(arg1)))))(d10) == 1e16);
    BOOST_TEST((sqr(sqr(arg1)) / arg1 / arg1)(d5) == 25);

    for (int j = 0; j < 20; ++j)
    {
        cout << (10 < arg1)(j);
        BOOST_TEST((10 < arg1)(j) == (10 < j));
    }
    cout << endl;

    for (int k = 0; k < 20; ++k)
    {
        bool r = ((arg1 % 2 == 0) && (arg1 < 15))(k);
        cout << r;
        BOOST_TEST(r == ((k % 2 == 0) && (k < 15)));
    }
    cout << endl;

///////////////////////////////////////////////////////////////////////////////
//
//  End asserts
//
///////////////////////////////////////////////////////////////////////////////

    return boost::report_errors();
}


/*******************************************************************************
 * Copyright 2013-2014 Sebastian Niemann <niemann@sra.uni-hannover.de>.
 *
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/MIT
 *
 * Developers:
 *   Sebastian Niemann - Lead developer
 *   Daniel Kiechle - Unit testing
 ******************************************************************************/
// http://arma.sourceforge.net/docs.html#config_hpp
#define ARMA_USE_CXX11 // Use C++11 features, such as initialiser lists

#include <Input.hpp>
using armadilloJava::Input;

#include <algorithm>
using std::max;
using std::sort;
using std::set_union;

#include <string>
using std::to_string;

#include <stdexcept>
using std::runtime_error;

#include <vector>
using std::vector;

#include <initializer_list>
using std::initializer_list;

#include <armadillo>
using arma::Mat;
using arma::Col;
using arma::Row;
using arma::uword;
using arma::zeros;
using arma::ones;
using arma::eye;
using arma::datum;
using arma::span;
using arma::SizeMat;
using arma::distr_param;

#include <iostream>
using std::cout;
using std::endl;

namespace armadilloJava {
  vector<vector<pair<string, void*>>> Input::getTestParameters(const vector<InputClass>& inputClasses) {
    vector<vector<pair<string, void*>>> inputs;

    for (InputClass inputClass : inputClasses) {
      switch (inputClass) {
        case InputClass::ElemInd:
          inputs.push_back(getElemInd());
          break;
        case InputClass::ExtElemInd:
          inputs.push_back(getExtElemInd());
          break;
        case InputClass::ColInd:
          inputs.push_back(getColInd());
          break;
        case InputClass::ExtColInd:
          inputs.push_back(getExtColInd());
          break;
        case InputClass::RowInd:
          inputs.push_back(getRowInd());
          break;
        case InputClass::ExtRowInd:
          inputs.push_back(getExtRowInd());
          break;
        case InputClass::NumElems:
          inputs.push_back(getNumElems());
          break;
        case InputClass::NumCols:
          inputs.push_back(getNumCols());
          break;
        case InputClass::NumRows:
          inputs.push_back(getNumRows());
          break;
        case InputClass::Normal:
          inputs.push_back(getNormal());
          break;
        case InputClass::Dim:
          inputs.push_back(getDim());
          break;
        case InputClass::Exp:
          inputs.push_back(getExp());
          break;
        case InputClass::MatNormInt:
          inputs.push_back(getMatNormInt());
          break;
        case InputClass::VecNormInt:
          inputs.push_back(getVecNormInt());
          break;
        case InputClass::GenDouble:
          inputs.push_back(getGenDouble());
          break;
        case InputClass::SinValTol:
          inputs.push_back(getSinValTol());
          break;
        case InputClass::ElemIndRange:
          inputs.push_back(getElemIndRange());
          break;
        case InputClass::ColIndRange:
          inputs.push_back(getColIndRange());
          break;
        case InputClass::RowIndRange:
          inputs.push_back(getRowIndRange());
          break;
        case InputClass::MatSize:
          inputs.push_back(getMatSize());
          break;
        case InputClass::GenMat:
          inputs.push_back(getGenMat());
          break;
        case InputClass::SquMat:
          inputs.push_back(getSquMat());
          break;
        case InputClass::InvMat:
          inputs.push_back(getInvMat());
          break;
        case InputClass::SymMat:
          inputs.push_back(getSymMat());
          break;
        case InputClass::SymPDMat:
          inputs.push_back(getSymPDMat());
          break;
        case InputClass::LogicMat:
          inputs.push_back(getLogicMat());
          break;
        case InputClass::OOMat:
          inputs.push_back(getOOMat());
          break;
        case InputClass::GenColVec:
          inputs.push_back(getGenColVec());
          break;
        case InputClass::MonColVec:
          inputs.push_back(getMonColVec());
          break;
        case InputClass::LogicColVec:
          inputs.push_back(getLogicColVec());
          break;
        case InputClass::OOColVec:
          inputs.push_back(getOOColVec());
          break;
        case InputClass::GenRowVec:
          inputs.push_back(getGenRowVec());
          break;
        case InputClass::MonRowVec:
          inputs.push_back(getMonRowVec());
          break;
        case InputClass::LogicRowVec:
          inputs.push_back(getLogicRowVec());
          break;
        case InputClass::OORowVec:
          inputs.push_back(getOORowVec());
          break;
        case InputClass::ElemInds:
          inputs.push_back(getElemInds());
          break;
        case InputClass::ColInds:
          inputs.push_back(getColInds());
          break;
        case InputClass::RowInds:
          inputs.push_back(getRowInds());
          break;
        case InputClass::Text:
          inputs.push_back(getText());
          break;
        case InputClass::MatNormString:
          inputs.push_back(getMatNormString());
          break;
        case InputClass::VecNormString:
          inputs.push_back(getVecNormString());
          break;
        case InputClass::Sort:
          inputs.push_back(getSort());
          break;
        case InputClass::Search:
          inputs.push_back(getSearch());
          break;
        case InputClass::SinValSel:
          inputs.push_back(getSinValSel());
          break;
        case InputClass::DistrParam:
          inputs.push_back(getDistrParam());
          break;
        case InputClass::Fill:
          inputs.push_back(getFill());
          break;
        case InputClass::Random:
          inputs.push_back(getRandom());
          break;
        default:
          throw new runtime_error("Unsupported input class requested.");
      }
    }

    return cartesianProduct(inputs);
  }

  vector<vector<pair<string, void*>>> Input::cartesianProduct(const vector<vector<pair<string, void*>>>& inputs) {
    vector<vector<pair<string, void*>>> cartesianProduct = {{}};
    for (vector<pair<string, void*>> input : inputs) {
      vector<vector<pair<string, void*>>> tempProduct;
      for (vector<pair<string, void*>> tempInput : cartesianProduct) {
        for (pair<string, void*> keyValuePair : input) {
          tempProduct.push_back(tempInput);
          tempProduct.back().push_back(keyValuePair);
        }
      }
      cartesianProduct.swap(tempProduct);
    }
    return cartesianProduct;
  }

  vector<pair<string, void*>> Input::vectorUnion(const vector<vector<pair<string, void*>>>& inputs) {
    vector<pair<string, void*>> vectorUnion = inputs.at(0);

    sort(vectorUnion.begin(), vectorUnion.end(), [](const pair<string, void*>& lhs, const pair<string, void*>& rhs) {return lhs.first < rhs.first;});

    for (int n = 1; n < inputs.size(); n++) {
      vector<pair<string, void*>> input = inputs.at(n);
      sort(input.begin(), input.end(), [](const pair<string, void*>& lhs, const pair<string, void*>& rhs) {return lhs.first < rhs.first;});

      vector<pair<string, void*>> temp(vectorUnion.size() + input.size());
      vector<pair<string, void*>>::iterator end = set_union(vectorUnion.begin(), vectorUnion.end(), input.begin(), input.end(), temp.begin(), [](const pair<string, void*>& lhs, const pair<string, void*>& rhs) {return lhs.first < rhs.first;});
      temp.resize(end - temp.begin());

      vectorUnion = temp;
    }

    return vectorUnion;
  }

  Mat<double> Input::getHilbertMatrix(int n_rows, int n_cols) {
    Mat<double> hilbertMatrix(n_rows, n_cols);

    for (int j = 0; j < n_cols; j++) {
      for (int i = 0; i < n_rows; i++) {
        hilbertMatrix.at(i, j) = 1.0 / (i + j + 1);
      }
    }

    return hilbertMatrix;
  }

  Mat<double> Input::getKMSMatrix(int n_rows, int n_cols) {
    Mat<double> kmsMatrix(n_rows, n_cols);

    for (int j = 0; j < n_cols; j++) {
      for (int i = 0; i < n_rows; i++) {
        kmsMatrix.at(i, j) = pow(2, abs(i - j));
      }
    }

    return kmsMatrix;
  }

  vector<pair<string, void*>> Input::getElemInd() {
    return {
      pair<string, void*>("0", new int(0)),
      pair<string, void*>("9", new int(9))
    };
  }

  vector<pair<string, void*>> Input::getExtElemInd() {
    return {
      pair<string, void*>("0", new int(0)),
      pair<string, void*>("9", new int(9)),
      pair<string, void*>("10", new int(10))
    };
  }

  vector<pair<string, void*>> Input::getColInd() {
    return getRowInd();
  }

  vector<pair<string, void*>> Input::getExtColInd() {
    return getExtRowInd();
  }

  vector<pair<string, void*>> Input::getRowInd() {
    return {
      pair<string, void*>("0", new int(0)),
      pair<string, void*>("1", new int(1))
    };
  }

  vector<pair<string, void*>> Input::getExtRowInd() {
    return {
      pair<string, void*>("0", new int(0)),
      pair<string, void*>("1", new int(1)),
      pair<string, void*>("2", new int(2))
    };
  }

  vector<pair<string, void*>> Input::getNumElems() {
    return {
      pair<string, void*>("1", new int(1)),
      pair<string, void*>("10", new int(10))
    };
  }

  vector<pair<string, void*>> Input::getNumCols() {
    return getNumRows();
  }

  vector<pair<string, void*>> Input::getNumRows() {
    return {
      pair<string, void*>("1", new int(1)),
      pair<string, void*>("5", new int(5))
    };
  }

  vector<pair<string, void*>> Input::getNormal() {
    return {
      pair<string, void*>("0", new int(0)),
      pair<string, void*>("1", new int(1))
    };
  }

  vector<pair<string, void*>> Input::getDim() {
    return {
      pair<string, void*>("0", new int(0)),
      pair<string, void*>("1", new int(1))
    };
  }

  vector<pair<string, void*>> Input::getExp() {
    return {
      pair<string, void*>("0.5", new double(0.5)),
      pair<string, void*>("1.0", new double(1)),
      pair<string, void*>("2.0", new double(2)),
      pair<string, void*>("3.0", new double(3))
    };
  }

  vector<pair<string, void*>> Input::getMatNormInt() {
    return {
      pair<string, void*>("1", new int(1)),
      pair<string, void*>("2", new int(2))
    };
  }

  vector<pair<string, void*>> Input::getVecNormInt() {
    return {
      pair<string, void*>("1", new int(1)),
      pair<string, void*>("2", new int(2)),
      pair<string, void*>("3", new int(3)),
      pair<string, void*>("4", new int(4))
    };
  }

  vector<pair<string, void*>> Input::getGenDouble() {
    return {
      pair<string, void*>("-inf", new double(-datum::inf)),
      pair<string, void*>("-2.0", new double(-2)),
      pair<string, void*>("0.0", new double(0)),
      pair<string, void*>("machine_epsilon", new double(datum::eps)),
      pair<string, void*>("0.5", new double(0.5)),
      pair<string, void*>("1.0", new double(1)),
      pair<string, void*>("euler_number", new double(datum::e)),
      pair<string, void*>("pi", new double(datum::pi)),
      pair<string, void*>("inf", new double(datum::inf))
    };
  }

  vector<pair<string, void*>> Input::getSinValTol() {
    return {
      pair<string, void*>("0.0", new double(0)),
      pair<string, void*>("1.0", new double(1))
    };
  }

  vector<pair<string, void*>> Input::getElemIndRange() {
    return {
      pair<string, void*>("span.all", new span(span::all)),
      pair<string, void*>("span(0)", new span(0)),
      pair<string, void*>("span(0,9)", new span(0, 9)),
      pair<string, void*>("span(1,4)", new span(1, 4))
    };
  }

  vector<pair<string, void*>> Input::getColIndRange() {
    return getRowIndRange();
  }

  vector<pair<string, void*>> Input::getRowIndRange() {
    return {
      pair<string, void*>("span.all", new span(span::all)),
      pair<string, void*>("span(0)", new span(0)),
      pair<string, void*>("span(0,4)", new span(0, 4)),
      pair<string, void*>("span(1,2)", new span(1, 2))
    };
  }

  vector<pair<string, void*>> Input::getMatSize() {
    return {
      pair<string, void*>("size(1,1)", new pair<int, int>(1, 1)),
      pair<string, void*>("size(1,5)", new pair<int, int>(1, 5)),
      pair<string, void*>("size(2,5)", new pair<int, int>(2, 5)),
      pair<string, void*>("size(5,1)", new pair<int, int>(5, 1)),
      pair<string, void*>("size(5,2)", new pair<int, int>(5, 2)),
      pair<string, void*>("size(5,5)", new pair<int, int>(5, 5))
    };
  }

  vector<pair<string, void*>> Input::getGenMat() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getMatSize()) {
      SizeMat size = *static_cast<SizeMat*>(keyValuePair.second);

      input.push_back(pair<string, void*>("Mat(zeros(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(zeros<Mat<double>>(size.n_rows, size.n_cols))));
      input.push_back(pair<string, void*>("Mat(ones(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(ones<Mat<double>>(size.n_rows, size.n_cols))));
      input.push_back(pair<string, void*>("Mat(hilbert(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(getHilbertMatrix(size.n_rows, size.n_cols))));
    }

    Mat<double> infinite = {datum::inf, -datum::inf, -datum::inf, datum::inf};
    infinite.reshape(2, 2);
    input.push_back(pair<string, void*>("Mat({{inf,-inf},{-inf,inf}})", new Mat<double>(infinite)));

    return input;
  }

  vector<pair<string, void*>> Input::getSquMat() {
    return vectorUnion({
      getInvMat(),
      getSymMat()
    });
  }

  vector<pair<string, void*>> Input::getInvMat() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getMatSize()) {
      SizeMat size = *static_cast<SizeMat*>(keyValuePair.second);

      if(size.n_rows == size.n_cols) {
        input.push_back(pair<string, void*>("Mat(eye(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(eye<Mat<double>>(size.n_rows, size.n_cols))));
        input.push_back(pair<string, void*>("Mat(kms(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(getKMSMatrix(size.n_rows, size.n_cols))));
      }
    }

    return input;
  }

  vector<pair<string, void*>> Input::getSymMat() {
    vector<vector<pair<string, void*>>> inputs = {};
    inputs.push_back(getSymPDMat());

    for (pair<string, void*> keyValuePair : getMatSize()) {
      SizeMat size = *static_cast<SizeMat*>(keyValuePair.second);

      if(size.n_rows == size.n_cols) {
        inputs.push_back({
          pair<string, void*>("Mat(zeros(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(zeros<Mat<double>>(size.n_rows, size.n_cols))),
          pair<string, void*>("Mat(ones(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(ones<Mat<double>>(size.n_rows, size.n_cols)))
        });
      }
    }

    return vectorUnion(inputs);
  }

  vector<pair<string, void*>> Input::getSymPDMat() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getMatSize()) {
      SizeMat size = *static_cast<SizeMat*>(keyValuePair.second);

      if(size.n_rows == size.n_cols) {
        input.push_back(pair<string, void*>("Mat(eye(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(eye<Mat<double>>(size.n_rows, size.n_cols))));
        input.push_back(pair<string, void*>("Mat(hilbert(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(getHilbertMatrix(size.n_rows, size.n_cols))));
      }
    }

    return input;
  }

  vector<pair<string, void*>> Input::getLogicMat() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getMatSize()) {
      SizeMat size = *static_cast<SizeMat*>(keyValuePair.second);

      input.push_back(pair<string, void*>("Mat(zeros(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(zeros<Mat<double>>(size.n_rows, size.n_cols))));
      input.push_back(pair<string, void*>("Mat(ones(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(ones<Mat<double>>(size.n_rows, size.n_cols))));
      input.push_back(pair<string, void*>("Mat(eye(" + to_string(size.n_rows) + "," + to_string(size.n_cols) + "))", new Mat<double>(eye<Mat<double>>(size.n_rows, size.n_cols))));
    }

    return input;
  }

  vector<pair<string, void*>> Input::getOOMat() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getGenDouble()) {
      double genDouble = *static_cast<double*>(keyValuePair.second);

      input.push_back(pair<string, void*>("Mat({" + keyValuePair.first + "})", new Mat<double>({genDouble})));
    }

    return input;
  }

  vector<pair<string, void*>> Input::getGenColVec() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getMatSize()) {
      SizeMat size = *static_cast<SizeMat*>(keyValuePair.second);

      if(size.n_cols == 1) {
        input.push_back(pair<string, void*>("Col(zeros(" + to_string(size.n_rows) + ",1))", new Col<double>(zeros<Col<double>>(size.n_rows))));
        input.push_back(pair<string, void*>("Col(ones(" + to_string(size.n_rows) + ",1))", new Col<double>(ones<Col<double>>(size.n_rows))));
        input.push_back(pair<string, void*>("Col(hilbert(" + to_string(size.n_rows) + ",1))", new Col<double>(getHilbertMatrix(size.n_rows, 1))));
       }
    }
    input.push_back(pair<string, void*>("Col({inf,-inf})", new Col<double>({datum::inf, -datum::inf})));

    return input;
  }

  vector<pair<string, void*>> Input::getMonColVec() {
    return {
      pair<string, void*>("Col({-10,-5,0.5,10})", new Col<double>({-10, -5, 0.5, 10})),
      pair<string, void*>("Col({-inf,0,inf})", new Col<double>({datum::inf, 0, -datum::inf})),
      pair<string, void*>("Col({0})", new Col<double>({0.0})),
      pair<string, void*>("Col({-inf})", new Col<double>({-datum::inf})),
      pair<string, void*>("Col({inf})", new Col<double>({datum::inf}))
    };
  }

  vector<pair<string, void*>> Input::getLogicColVec() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getMatSize()) {
      SizeMat size = *static_cast<SizeMat*>(keyValuePair.second);

      if(size.n_cols == 1) {
        input.push_back(pair<string, void*>("Col(zeros(" + to_string(size.n_rows) + ",1))", new Col<double>(zeros<Col<double>>(size.n_rows))));
        input.push_back(pair<string, void*>("Col(ones(" + to_string(size.n_rows) + ",1))", new Col<double>(ones<Col<double>>(size.n_rows))));
        input.push_back(pair<string, void*>("Col(eye(" + to_string(size.n_rows) + ",1))", new Col<double>(eye<Col<double>>(size.n_rows, 1))));
      }
    }

    return input;
  }

  vector<pair<string, void*>> Input::getOOColVec() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getGenDouble()) {
      double genDouble = *static_cast<double*>(keyValuePair.second);

      input.push_back(pair<string, void*>("Col({" + keyValuePair.first + "})", new Col<double>({genDouble})));
    }

    return input;
  }

  vector<pair<string, void*>> Input::getGenRowVec() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getMatSize()) {
      SizeMat size = *static_cast<SizeMat*>(keyValuePair.second);

      if(size.n_rows == 1) {
        input.push_back(pair<string, void*>("Row(zeros(1," + to_string(size.n_cols) + "))", new Row<double>(zeros<Row<double>>(size.n_cols))));
        input.push_back(pair<string, void*>("Row(ones(1," + to_string(size.n_cols) + "))", new Row<double>(ones<Row<double>>(size.n_cols))));
        input.push_back(pair<string, void*>("Row(hilbert(1," + to_string(size.n_cols) + "))", new Row<double>(getHilbertMatrix(1, size.n_cols))));
       }
    }
    input.push_back(pair<string, void*>("Row({inf,-inf})", new Row<double>({datum::inf, -datum::inf})));

    return input;
  }

  vector<pair<string, void*>> Input::getMonRowVec() {
    return {
      pair<string, void*>("Row({-10,-5,0.5,10})", new Row<double>({-10, -5, 0.5, 10})),
      pair<string, void*>("Row({-inf,0,inf})", new Row<double>({datum::inf, 0, -datum::inf})),
      pair<string, void*>("Row({0})", new Row<double>({0.0})),
      pair<string, void*>("Row({-inf})", new Row<double>({-datum::inf})),
      pair<string, void*>("Row({inf})", new Row<double>({datum::inf}))
    };
  }

  vector<pair<string, void*>> Input::getLogicRowVec() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getMatSize()) {
      SizeMat size = *static_cast<SizeMat*>(keyValuePair.second);

      if(size.n_rows == 1) {
        input.push_back(pair<string, void*>("Row(zeros(1," + to_string(size.n_cols) + "))", new Row<double>(zeros<Row<double>>(size.n_cols))));
        input.push_back(pair<string, void*>("Row(ones(1," + to_string(size.n_cols) + "))", new Row<double>(ones<Row<double>>(size.n_cols))));
        input.push_back(pair<string, void*>("Row(eye(1," + to_string(size.n_cols) + "))", new Row<double>(eye<Row<double>>(1, size.n_cols))));
      }
    }

    return input;
  }

  vector<pair<string, void*>> Input::getOORowVec() {
    vector<pair<string, void*>> input = {};

    for (pair<string, void*> keyValuePair : getGenDouble()) {
      double genDouble = *static_cast<double*>(keyValuePair.second);

      input.push_back(pair<string, void*>("Row({" + keyValuePair.first + "})", new Row<double>({genDouble})));
    }

    return input;
  }

  vector<pair<string, void*>> Input::getElemInds() {
    return {
      pair<string, void*>("Col({0})", new Col<uword>(initializer_list<unsigned int>({0}))),
      pair<string, void*>("Col({9})", new Col<uword>(initializer_list<unsigned int>({9}))),
      pair<string, void*>("Col({9,0,1})", new Col<uword>({9, 0, 1})),
      pair<string, void*>("Col({1,1,1})", new Col<uword>({1, 1, 1}))
    };
  }

  vector<pair<string, void*>> Input::getColInds() {
    return getRowInds();
  }

  vector<pair<string, void*>> Input::getRowInds() {
    return {
      pair<string, void*>("Col({0})", new Col<uword>(initializer_list<unsigned int>({0}))),
      pair<string, void*>("Col({4})", new Col<uword>(initializer_list<unsigned int>({4}))),
      pair<string, void*>("Col({4,0,1})", new Col<uword>({4, 0, 1})),
      pair<string, void*>("Col({1,1,1})", new Col<uword>({1, 1, 1}))
    };
  }

  vector<pair<string, void*>> Input::getText() {
    return {
      pair<string, void*>("'Test_string'", new string("Test string"))
    };
  }

  vector<pair<string, void*>> Input::getMatNormString() {
    return {
      pair<string, void*>("'inf'", new string("inf")),
      pair<string, void*>("'fro'", new string("fro"))
    };
  }

  vector<pair<string, void*>> Input::getVecNormString() {
    return {
      pair<string, void*>("'inf'", new string("inf")),
      pair<string, void*>("'-inf'", new string("-inf")),
      pair<string, void*>("'fro'", new string("fro"))
    };
  }

  vector<pair<string, void*>> Input::getSort() {
    return {
      pair<string, void*>("'ascend'", new string("ascend")),
      pair<string, void*>("'descend'", new string("descend"))
    };
  }

  vector<pair<string, void*>> Input::getSearch() {
    return {
      pair<string, void*>("'first'", new string("first")),
      pair<string, void*>("'last'", new string("last"))
    };
  }

  vector<pair<string, void*>> Input::getSinValSel() {
    return {
      pair<string, void*>("'left'", new string("left")),
      pair<string, void*>("'right'", new string("right")),
      pair<string, void*>("'both'", new string("both"))
    };
  }

  vector<pair<string, void*>> Input::getDistrParam() {
    return {
      pair<string, void*>("distr_param(0,10)", new distr_param(0, 10)),
      pair<string, void*>("distr_param(1,1)", new distr_param(1, 1)),
      pair<string, void*>("distr_param(-5,6)", new distr_param(-5, 6))
    };
  }

  vector<pair<string, void*>> Input::getFill() {
    return {
            pair<string, void*>("Fill EYE", new int(0)),
            pair<string, void*>("Fill NONE", new int(1)),
            pair<string, void*>("Fill ONES", new int(2)),
            pair<string, void*>("Fill RANDN", new int(3)),
            pair<string, void*>("Fill RANDU", new int(4)),
            pair<string, void*>("Fill ZEROS", new int (5))
    };
  }

  vector<pair<string, void*>> Input::getRandom() {
    return {
      pair<string, void*>("1000", new int(1000))
    };
  }
}