/***********************************************************************
 *                      Kuramoto oscillators                           *
 *             Copyright (c) 2014-2015 Alex Khrabrov                   *
 ***********************************************************************
 * This program is free software. It comes without any warranty, to    *
 * the extent permitted by applicable law. You can redistribute it     *
 * and/or modify it under the terms of the Do What The Fuck You Want   *
 * To Public License, Version 2, as published by Sam Hocevar. See      *
 * license text below.                                                 *
 ***********************************************************************
 *                                                                     *
 *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE              *
 *                    Version 2, December 2004                         *
 *                                                                     *
 * Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>                    *
 *                                                                     *
 * Everyone is permitted to copy and distribute verbatim or modified   *
 * copies of this license document, and changing it is allowed as long *
 * as the name is changed.                                             *
 *                                                                     *
 *            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE              *
 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION   *
 *                                                                     *
 *  0. You just DO WHAT THE FUCK YOU WANT TO.                          *
 *                                                                     *
 ***********************************************************************/

typedef double fp_type;

#define _USE_MATH_DEFINES
#include <cmath>
static const fp_type TWO_PI = 2.0 * M_PI;
#undef _USE_MATH_DEFINES

#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <map>
#include <random>
#include <algorithm>
#include <numeric>

#include <cassert>
#include <cstdint>
#include <cstdlib>

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

#if defined(WIN32) || defined(_WIN32)
#define PATH_SEPARATOR "\\"
#else
#define PATH_SEPARATOR "/"
#endif

#ifdef _MSC_VER
#define FORCE_INLINE __forceinline
#else
#define FORCE_INLINE inline
#endif

//--------------------------------------------------------------------------------------------------

template<typename T>
FORCE_INLINE T mean(const std::vector<T> & v)
{
    const auto size = v.size();
    assert(size != 0);
    T sum(0);
    for(typename std::vector<T>::size_type i = 0; i < size; i++)
        sum += v[i];
    return sum / T(size);
}

static std::vector<fp_type> tmp_s;
static std::vector<fp_type> tmp_c;

template<typename T>
T calc_order_parameter(const std::vector<T> & phases)
{
    using namespace std;
    const auto size = phases.size();
    tmp_s.resize(size);
    tmp_c.resize(size);
    for(typename std::vector<T>::size_type i = 0; i < size; i++)
    {
        const T p = phases[i];
        tmp_s[i] = sin(p);
        tmp_c[i] = cos(p);
    }
    const T mean_sin = mean(tmp_s);
    const T mean_cos = mean(tmp_c);
    return sqrt(mean_cos * mean_cos + mean_sin * mean_sin);
}

template<typename T>
FORCE_INLINE T rand_range(T min, T max)
{
    assert(max > min);
    return ((T(rand()) / T(RAND_MAX)) * (max - min)) + min;
}

//--------------------------------------------------------------------------------------------------

template<typename T>
FORCE_INLINE T wrap_phase_1(T p)
{
    if(p > T(TWO_PI))
        p -= T(TWO_PI);
    else if(p < 0.0)
        p += T(TWO_PI);
    return p;
}

template<typename T>
FORCE_INLINE T wrap_phase_2(T p)
{
    while(p < 0.0 || p > T(TWO_PI))
    {
        if(p > T(TWO_PI))
            p -= T(TWO_PI);
        else if(p < 0.0)
            p += T(TWO_PI);
    }
}

//--------------------------------------------------------------------------------------------------

template<typename T>
FORCE_INLINE T kuramoto_classic(const T phi)
{
    return std::sin(phi);
}

template<int n, typename T>
FORCE_INLINE T kuramoto_n(const T phi)
{
    return std::sin(T(n) * phi);
}

template<typename T>
FORCE_INLINE T daido_original(const T phi)
{
    return std::sin(phi) +
        T(0.2) * std::cos(phi) -
        T(0.3) * std::sin(T(2.0) * phi) +
        T(0.6) * std::cos(T(2.0) * phi) +
        T(0.7) * std::sin(T(3.0) * phi) -
        T(0.4) * std::cos(T(3.0) * phi);
}

template<typename T>
FORCE_INLINE T coupling_1(const T phi)
{
    return std::sin(phi) + std::sin(T(3.0) * phi);
}

template<typename T>
FORCE_INLINE T coupling_2(const T phi)
{
    return std::sin(phi) +
           std::sin(T(3.0) * phi) +
           std::sin(T(6.0) * phi);
}

template<typename T>
FORCE_INLINE T coupling_3(const T phi)
{
    return std::sin(phi) + std::cos(T(3.0) * phi);
}

template<typename T>
FORCE_INLINE T coupling_4(const T phi)
{
    return std::cos(phi) + std::cos(T(3.0) * phi);
}

//--------------------------------------------------------------------------------------------------

template<typename T>
void write_vector(std::ostream & os, const std::vector<T> & vec)
{
    for(const auto x : vec)
        os << x << ' ';
}

template<typename T>
void dump_state
(
    const std::string & out_file_name,
    const std::vector<T> & phase,
    const std::vector<T> & vel,
    const unsigned int step,
    const unsigned int N,
    const T r,
    const T mean_phase
)
{
    using namespace std;
    ofstream out(out_file_name, ofstream::trunc);
    out << step << ' ' << N << ' ' << r << ' ' << mean_phase << '\n';
    write_vector(out, phase);
    out << '\n';
    write_vector(out, vel);
}

template<typename T, T(*H)(T)>
void run_simulation
(
    const bool quiet,                    // if true produce no output to console

    const unsigned int N,                // number of oscillators
    const unsigned int N_steps,          // simulations steps

    const T dt,                          // time step

    std::vector<T> phase,                // initial phases
    const std::vector<T> freq,           // oscillator's frequencies
    const std::vector<T> k,              // coupling matrix

    const std::string & name,            // current working set name

    const T noise,                       // noise amplitude

    const T forcing_strength,            // when != 0 all oscillators are forced by external field
    const T forcing_freq,                // external forcing frequency

    const unsigned int dump_interval,    // how often to dump frame snapshots

    const bool r_history_enabled,        // write r values to file
    const bool mean_history_enabled,     // write mean values to file
    const bool mean_vel_history_enabled, // write mean velocity values to file

    const bool freq_modulation_enabled,  // is oscillator frequency modulation enabled
    const std::vector<T> freq_ampl,      // frequency modulation amplitude
    const std::vector<T> freq_freq,      // frequency modulation frequency
    const std::vector<T> freq_offset,    // frequency modulation offset

    const bool k_modulation_enabled,     // is coupling coefficient modulation enabled
    const std::vector<T> k_ampl,         // coupling coefficient modulation amplitude
    const std::vector<T> k_freq,         // coupling coefficient modulation frequency
    const std::vector<T> k_offset,       // coupling coefficient modulation offset

    const std::string & out_dir_name,      // MUST end with directory separator (/ or \)
    const std::string & r_file_path,       // if specified r history will be written to this file
    const std::string & mean_file_path,    // if specified mean phase will be written to this file
    const std::string & mean_vel_file_path // if specified mean velocity will be written to this file
)
{
    using namespace std;

    assert(N > 0 && dt > 0 && phase.size() == N && freq.size() == N && k.size() == N*N);
    assert(freq_modulation_enabled ? (freq_ampl.size() == N && freq_freq.size() == N && freq_offset.size() == N) : true);
    assert(k_modulation_enabled ? (k_ampl.size() == N && k_freq.size() == N*N && k_offset.size() == N*N) : true);

    const T two_dt = T(2.0) * dt;

    const bool add_noise = (noise != 0.0);
    const bool forcing_enabled = (forcing_strength != 0.0);
    const bool dump_enabled = (dump_interval != 0);

    // collect global statistics during simulation
    vector<T> r_hist(N_steps);
    vector<T> mean_hist(N_steps);
    vector<T> mean_vel_hist(N_steps);

    vector<T> phase_old = phase;
    vector<T> phase_old_old = phase;

    vector<T> vel(N);

    unsigned int dump_counter = 0;

    for(unsigned int step = 0; step < N_steps; step++)
    {
        const T t = T(step) * dt;
        for(unsigned int i = 0; i < N; i++)
        {
            T f;
            if(!freq_modulation_enabled)
                f = freq[i];
            else
                f = freq[i] + freq_ampl[i] * sin(T(TWO_PI) * freq_freq[i] * t + freq_offset[i]);

            // NOTE: k[i*N + j] can be interpreted as k[i][j]
            if(!k_modulation_enabled)
            {
                const unsigned int offset = i * N;
                const T phase_old_i = phase_old[i];
                for(unsigned int j = 0; j < N; j++)
                    f += k[offset + j] * H(phase_old[j] - phase_old_i);
            }
            else
            {
                const unsigned int offset = i * N;
                const T phase_old_i = phase_old[i];
                for(unsigned int j = 0; j < N; j++)
                {
                    const unsigned int idx = offset + j;
                    f += (k[idx] + k_ampl[i] * sin(T(TWO_PI) * k_freq[idx] * t + k_offset[idx]))
                        * H(phase_old[j] - phase_old_i);
                }
            }

            if(forcing_enabled)
                f += forcing_strength * sin(forcing_freq * t - phase_old[i]);

            if(add_noise)
                f += rand_range<T>(-noise, noise);

            // calculate new phase
            const T p = phase_old[i] + f * dt;
            phase[i] = wrap_phase_1(p);
        }

        for(unsigned int i = 0; i < N; i++)
            vel[i] = (phase[i] - phase_old_old[i]) / two_dt;

        const T r = calc_order_parameter(phase);
        r_hist[step] = r;

        const T mean_phase = mean(phase);
        mean_hist[step] = mean_phase;

        mean_vel_hist[step] = mean(vel);

        if(dump_enabled)
        {
            if(dump_counter == 0)
            {
                dump_counter = dump_interval;
                const string out_file_name(out_dir_name + to_string(step) + ".txt");
                if(!quiet)
                    cout << "Saving at step: " << step << endl;
                dump_state(out_file_name, phase, vel, step, N, r, mean_phase);
            }
            dump_counter--;
        }
        else if(!(step % 100))
            if(!quiet)
                cout << "Step: " << step << endl;

        phase_old_old = phase_old;
        phase_old = phase;
    }

    if(r_history_enabled)
    {
        ofstream r_out(r_file_path, ofstream::trunc);
        write_vector(r_out, r_hist);
    }

    if(mean_history_enabled)
    {
        ofstream mean_out(mean_file_path, ofstream::trunc);
        write_vector(mean_out, mean_hist);
    }

    if(mean_vel_history_enabled)
    {
        ofstream mean_vel_out(mean_vel_file_path, ofstream::trunc);
        write_vector(mean_vel_out, mean_vel_hist);
    }
}

void read_preset(std::ifstream & input,
                 unsigned int & N,
                 std::vector<double> & freq,
                 std::vector<double> & phase,
                 std::vector<double> & k)
{
    using namespace std;

    uint32_t N_;
    input.read(reinterpret_cast<char*>(&N_), sizeof(N_));
    N = N_;

    freq.resize(N);
    phase.resize(N);
    k.resize(N*N);

    input.read(reinterpret_cast<char*>(freq.data()), N*sizeof(double));
    input.read(reinterpret_cast<char*>(phase.data()), N*sizeof(double));
    input.read(reinterpret_cast<char*>(k.data()), N*N*sizeof(double));
}

void read_freq_modulation_data(std::ifstream & input,
                               unsigned int N,
                               std::vector<double> & freq_ampl,
                               std::vector<double> & freq_freq,
                               std::vector<double> & freq_offset)
{
    using namespace std;

    freq_ampl.resize(N);
    freq_freq.resize(N);
    freq_offset.resize(N);

    input.read(reinterpret_cast<char*>(freq_ampl.data()), N*sizeof(double));
    input.read(reinterpret_cast<char*>(freq_freq.data()), N*sizeof(double));
    input.read(reinterpret_cast<char*>(freq_offset.data()), N*sizeof(double));
}

void read_k_modulation_data(std::ifstream & input,
                            unsigned int N,
                            std::vector<double> & k_ampl,
                            std::vector<double> & k_freq,
                            std::vector<double> & k_offset)
{
    using namespace std;

    k_ampl.resize(N);
    k_freq.resize(N*N);
    k_offset.resize(N*N);

    input.read(reinterpret_cast<char*>(k_ampl.data()), N*sizeof(double));
    input.read(reinterpret_cast<char*>(k_freq.data()), N*N*sizeof(double));
    input.read(reinterpret_cast<char*>(k_offset.data()), N*N*sizeof(double));
}

int main(int argc, char * argv[])
{
    using namespace std;

    ios_base::sync_with_stdio(false);

    try
    {
        namespace po = boost::program_options;
        po::options_description desc("Options");
        desc.add_options()
            ("help,h", "print help message")
            ("quiet,q", "produce no output to console")

            ("preset,p", po::value<std::string>()->required(), "preset name")
            ("steps,s", po::value<unsigned int>()->default_value(1000), "simulation steps")
            ("dump-interval,di", po::value<unsigned int>()->default_value(100), "data dump interval")
            ("dt", po::value<double>()->default_value(0.01), "simulation time step")

            ("noise", po::value<double>()->default_value(0.0), "noise level")

            ("coupling,c", po::value<std::string>()->default_value("kuramoto"), "coupling type")

            ("forcing-strength,fs", po::value<double>()->default_value(0.0), "forcing strength")
            ("forcing-freq,ff", po::value<double>()->default_value(0.0), "forcing frequency")

            ("enable-k-modulation", "enable coupling coefficient modulation")
            ("enable-freq-modulation", "enable frequency modulation")

            ("r-file", po::value<std::string>()->implicit_value(""), "r file path")
            ("mean-phase-file", po::value<std::string>()->implicit_value(""), "mean phase file path")
            ("mean-vel-file", po::value<std::string>()->implicit_value(""), "mean velocity file path");

        po::variables_map vm;

        try
        {
            po::store(po::parse_command_line(argc, argv, desc), vm);

            if(vm.count("help"))
            {
                std::cout << std::endl
                          << "kuramoto_simulation - versatile Kuramoto simulation program" << std::endl
                          << std::endl
                          << desc << std::endl;
                return EXIT_SUCCESS;
            }
        }
        catch(const po::error & e)
        {
            std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
            std::cerr << desc << std::endl;
            return EXIT_FAILURE;
        }

        if(!vm.count("preset"))
        {
            std::cerr << "Preset name is required!" << std::endl
                      << "Hint: use -h option to display help." << std::endl;
            return EXIT_FAILURE;
        }

        const bool quiet = vm.count("quiet");

        const string preset_name = vm["preset"].as<std::string>();

        const unsigned int N_steps = vm["steps"].as<unsigned int>();
        unsigned int dump_interval = vm["dump-interval"].as<unsigned int>();
        const fp_type dt = vm["dt"].as<double>();
        const string coupling_type = vm["coupling"].as<std::string>();

        const fp_type noise = vm["noise"].as<double>();

        const fp_type forcing_strength = vm["forcing-strength"].as<double>();
        const fp_type forcing_freq = vm["forcing-freq"].as<double>();

        const bool freq_modulation_enabled = vm.count("enable-freq-modulation");
        const bool k_modulation_enabled = vm.count("enable-k-modulation");

        unsigned int N;
        vector<fp_type> freq;
        vector<fp_type> phase;
        vector<fp_type> k;

        // read preset data
        {
            ifstream input(preset_name + ".preset", ifstream::in | ifstream::binary);

            if(!input)
            {
                cerr << "Unable to open input file: " << preset_name << ".preset" << endl;
                return EXIT_FAILURE;
            }

            input.exceptions(ifstream::failbit | ifstream::badbit);

            read_preset(input, N, freq, phase, k);
        }

        vector<fp_type> freq_ampl;
        vector<fp_type> freq_freq;
        vector<fp_type> freq_offset;

        vector<fp_type> k_ampl;
        vector<fp_type> k_freq;
        vector<fp_type> k_offset;

        if(freq_modulation_enabled)
        {
            if(!quiet)
                cout << "freq_modulation_enabled" << endl;
            ifstream input_s(preset_name + ".fm.preset", ifstream::in | ifstream::binary);
            if (!input_s)
            {
                cerr << "Unable to open frequency modulation data file: " << preset_name << ".fm.preset" << endl;
                return EXIT_FAILURE;
            }
            input_s.exceptions(ifstream::failbit | ifstream::badbit);
            read_freq_modulation_data(input_s, N, freq_ampl, freq_freq, freq_offset);
        }

        if(k_modulation_enabled)
        {
            if(!quiet)
                cout << "k_modulation_enabled" << endl;
            ifstream input_s(preset_name + ".km.preset", ifstream::in | ifstream::binary);
            if (!input_s)
            {
                cerr << "Unable to open k modulation data file: " << preset_name << ".km.preset" << endl;
                return EXIT_FAILURE;
            }
            input_s.exceptions(ifstream::failbit | ifstream::badbit);
            read_k_modulation_data(input_s, N, k_ampl, k_freq, k_offset);
        }

        if(!quiet)
            std::cout << "Data loaded." << std::endl;

        const bool r_history_enabled = vm.count("r-file");
        const bool mean_history_enabled = vm.count("mean-phase-file");
        const bool mean_vel_history_enabled = vm.count("mean-vel-file");

        string r_file_path;
        string mean_file_path;
        string mean_vel_file_path;

        string out_dir_name;

        // initialize dump directory
        if(dump_interval > 0)
        {
            out_dir_name = "dump_" + preset_name + PATH_SEPARATOR + "steps" + PATH_SEPARATOR;
            // std::cout << "Creating steps dir: " << out_dir_name << std::endl;
            boost::filesystem::create_directories(out_dir_name);
        }

        if(r_history_enabled)
        {
            r_file_path = vm["r-file"].as<std::string>();
            if(r_file_path.empty())
                r_file_path = out_dir_name + "r.txt";
            const string path_str = boost::filesystem::path(r_file_path).parent_path().string();
            if(!path_str.empty())
                boost::filesystem::create_directories(path_str);
        }

        if(mean_history_enabled)
        {
            mean_file_path = vm["mean-phase-file"].as<std::string>();
            if(mean_file_path.empty())
                mean_file_path = out_dir_name + "mean.txt";
            const string path_str = boost::filesystem::path(mean_file_path).parent_path().string();
            if(!path_str.empty())
                boost::filesystem::create_directories(path_str);
        }

        if(mean_vel_history_enabled)
        {
            mean_vel_file_path = vm["mean-vel-file"].as<std::string>();
            if(mean_vel_file_path.empty())
                mean_vel_file_path = out_dir_name + "mean_vel.txt";
            const string path_str = boost::filesystem::path(mean_vel_file_path).parent_path().string();
            if(!path_str.empty())
                boost::filesystem::create_directories(path_str);
        }

        //std::cout << "r_file_path: " << r_file_path << std::endl;
        //std::cout << "mean_file_path: " << mean_file_path << std::endl;
        //std::cout << "mean_vel_file_path: " << mean_vel_file_path << std::endl;

#define RUN_SIMUL(coupling)                  \
        run_simulation<fp_type, coupling>(   \
            quiet,                           \
            N, N_steps, dt,                  \
            phase, freq, k, preset_name,     \
            noise,                           \
            forcing_strength, forcing_freq,  \
            dump_interval,                   \
            r_history_enabled,               \
            mean_history_enabled,            \
            mean_vel_history_enabled,        \
            freq_modulation_enabled,         \
            freq_ampl,                       \
            freq_freq,                       \
            freq_offset,                     \
            k_modulation_enabled,            \
            k_ampl,                          \
            k_freq,                          \
            k_offset,                        \
            out_dir_name,                    \
            r_file_path,                     \
            mean_file_path,                  \
            mean_vel_file_path               \
        )

        if(coupling_type == "kuramoto")
            RUN_SIMUL(kuramoto_classic);
        else if(coupling_type == "kuramoto_2")
            RUN_SIMUL(kuramoto_n<2>);
    	else if(coupling_type == "kuramoto_3")
    		RUN_SIMUL(kuramoto_n<3>);
    	else if(coupling_type == "kuramoto_4")
    		RUN_SIMUL(kuramoto_n<4>);
    	else if(coupling_type == "kuramoto_5")
    		RUN_SIMUL(kuramoto_n<5>);
    	else if(coupling_type == "kuramoto_6")
    		RUN_SIMUL(kuramoto_n<6>);
    	else if(coupling_type == "kuramoto_7")
    		RUN_SIMUL(kuramoto_n<7>);
    	else if(coupling_type == "kuramoto_8")
    		RUN_SIMUL(kuramoto_n<8>);
    	else if(coupling_type == "kuramoto_9")
    		RUN_SIMUL(kuramoto_n<9>);
    	else if(coupling_type == "kuramoto_10")
    		RUN_SIMUL(kuramoto_n<10>);
    	else if(coupling_type == "daido")
            RUN_SIMUL(daido_original);
        else if(coupling_type == "coupling_1")
            RUN_SIMUL(coupling_1);
        else if(coupling_type == "coupling_2")
            RUN_SIMUL(coupling_2);
        else if(coupling_type == "coupling_3")
            RUN_SIMUL(coupling_3);
        else if(coupling_type == "coupling_4")
            RUN_SIMUL(coupling_4);
        else
        {
            std::cerr << "Unknown coupling type: " << coupling_type << std::endl;
            return EXIT_FAILURE;
        }
    }
    catch(std::exception& e)
    {
        std::cerr << "Unhandled Exception reached the top of main: "
                  << e.what() << ", application will now exit." << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}


//  Copyright John Maddock 2008.
//  Use, modification and distribution are 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)
//
// Basic sanity check that header <boost/math/tr1.hpp>
// #includes all the files that it needs to.
//
#include <boost/math/tr1.hpp>
//
// Note this header includes no other headers, this is
// important if this test is to be meaningful:
//
#include "test_compile_result.hpp"

void compile_and_link_test()
{
   unsigned ui = 0;

   check_result<float>(boost::math::tr1::assoc_laguerre(ui, ui, f));
   check_result<float>(boost::math::tr1::assoc_laguerref(ui, ui, f));
   check_result<double>(boost::math::tr1::assoc_laguerre(ui, ui, d));
   check_result<long double>(boost::math::tr1::assoc_laguerre(ui, ui, l));
   check_result<long double>(boost::math::tr1::assoc_laguerrel(ui, ui, l));
   check_result<double>(boost::math::tr1::assoc_laguerre(ui, ui, i));
   check_result<double>(boost::math::tr1::assoc_laguerre(ui, ui, ui));

   check_result<float>(boost::math::tr1::assoc_legendre(ui, ui, f));
   check_result<float>(boost::math::tr1::assoc_legendref(ui, ui, f));
   check_result<double>(boost::math::tr1::assoc_legendre(ui, ui, d));
   check_result<long double>(boost::math::tr1::assoc_legendre(ui, ui, l));
   check_result<long double>(boost::math::tr1::assoc_legendrel(ui, ui, l));
   check_result<double>(boost::math::tr1::assoc_legendre(ui, ui, i));
   check_result<double>(boost::math::tr1::assoc_legendre(ui, ui, ui));

   check_result<float>(boost::math::tr1::beta(f, f));
   check_result<float>(boost::math::tr1::betaf(f, f));
   check_result<double>(boost::math::tr1::beta(d, d));
   check_result<long double>(boost::math::tr1::beta(l, l));
   check_result<long double>(boost::math::tr1::betal(l, l));
   check_result<double>(boost::math::tr1::beta(ui, ui));
   check_result<double>(boost::math::tr1::beta(i, ui));
   check_result<double>(boost::math::tr1::beta(f, d));
   check_result<long double>(boost::math::tr1::beta(l, d));

   check_result<float>(boost::math::tr1::comp_ellint_1(f));
   check_result<float>(boost::math::tr1::comp_ellint_1f(f));
   check_result<double>(boost::math::tr1::comp_ellint_1(d));
   check_result<long double>(boost::math::tr1::comp_ellint_1(l));
   check_result<long double>(boost::math::tr1::comp_ellint_1l(l));
   check_result<double>(boost::math::tr1::comp_ellint_1(ui));
   check_result<double>(boost::math::tr1::comp_ellint_1(i));

   check_result<float>(boost::math::tr1::comp_ellint_2(f));
   check_result<float>(boost::math::tr1::comp_ellint_2f(f));
   check_result<double>(boost::math::tr1::comp_ellint_2(d));
   check_result<long double>(boost::math::tr1::comp_ellint_2(l));
   check_result<long double>(boost::math::tr1::comp_ellint_2l(l));
   check_result<double>(boost::math::tr1::comp_ellint_2(ui));
   check_result<double>(boost::math::tr1::comp_ellint_2(i));

   check_result<float>(boost::math::tr1::comp_ellint_3(f, f));
   check_result<float>(boost::math::tr1::comp_ellint_3f(f, f));
   check_result<double>(boost::math::tr1::comp_ellint_3(d, d));
   check_result<long double>(boost::math::tr1::comp_ellint_3(l, l));
   check_result<long double>(boost::math::tr1::comp_ellint_3l(l, l));
   check_result<double>(boost::math::tr1::comp_ellint_3(ui, ui));
   check_result<double>(boost::math::tr1::comp_ellint_3(i, ui));
   check_result<double>(boost::math::tr1::comp_ellint_3(f, d));
   check_result<long double>(boost::math::tr1::comp_ellint_3(l, d));

   check_result<float>(boost::math::tr1::cyl_bessel_i(f, f));
   check_result<float>(boost::math::tr1::cyl_bessel_if(f, f));
   check_result<double>(boost::math::tr1::cyl_bessel_i(d, d));
   check_result<long double>(boost::math::tr1::cyl_bessel_i(l, l));
   check_result<long double>(boost::math::tr1::cyl_bessel_il(l, l));
   check_result<double>(boost::math::tr1::cyl_bessel_i(ui, ui));
   check_result<double>(boost::math::tr1::cyl_bessel_i(i, ui));
   check_result<double>(boost::math::tr1::cyl_bessel_i(f, d));
   check_result<long double>(boost::math::tr1::cyl_bessel_i(l, d));

   check_result<float>(boost::math::tr1::cyl_bessel_j(f, f));
   check_result<float>(boost::math::tr1::cyl_bessel_jf(f, f));
   check_result<double>(boost::math::tr1::cyl_bessel_j(d, d));
   check_result<long double>(boost::math::tr1::cyl_bessel_j(l, l));
   check_result<long double>(boost::math::tr1::cyl_bessel_jl(l, l));
   check_result<double>(boost::math::tr1::cyl_bessel_j(ui, ui));
   check_result<double>(boost::math::tr1::cyl_bessel_j(i, ui));
   check_result<double>(boost::math::tr1::cyl_bessel_j(f, d));
   check_result<long double>(boost::math::tr1::cyl_bessel_j(l, d));

   check_result<float>(boost::math::tr1::cyl_bessel_k(f, f));
   check_result<float>(boost::math::tr1::cyl_bessel_kf(f, f));
   check_result<double>(boost::math::tr1::cyl_bessel_k(d, d));
   check_result<long double>(boost::math::tr1::cyl_bessel_k(l, l));
   check_result<long double>(boost::math::tr1::cyl_bessel_kl(l, l));
   check_result<double>(boost::math::tr1::cyl_bessel_k(ui, ui));
   check_result<double>(boost::math::tr1::cyl_bessel_k(i, ui));
   check_result<double>(boost::math::tr1::cyl_bessel_k(f, d));
   check_result<long double>(boost::math::tr1::cyl_bessel_k(l, d));

   check_result<float>(boost::math::tr1::cyl_neumann(f, f));
   check_result<float>(boost::math::tr1::cyl_neumannf(f, f));
   check_result<double>(boost::math::tr1::cyl_neumann(d, d));
   check_result<long double>(boost::math::tr1::cyl_neumann(l, l));
   check_result<long double>(boost::math::tr1::cyl_neumannl(l, l));
   check_result<double>(boost::math::tr1::cyl_neumann(ui, ui));
   check_result<double>(boost::math::tr1::cyl_neumann(i, ui));
   check_result<double>(boost::math::tr1::cyl_neumann(f, d));
   check_result<long double>(boost::math::tr1::cyl_neumann(l, d));

   check_result<float>(boost::math::tr1::ellint_1(f, f));
   check_result<float>(boost::math::tr1::ellint_1f(f, f));
   check_result<double>(boost::math::tr1::ellint_1(d, d));
   check_result<long double>(boost::math::tr1::ellint_1(l, l));
   check_result<long double>(boost::math::tr1::ellint_1l(l, l));
   check_result<double>(boost::math::tr1::ellint_1(ui, ui));
   check_result<double>(boost::math::tr1::ellint_1(i, ui));
   check_result<double>(boost::math::tr1::ellint_1(f, d));
   check_result<long double>(boost::math::tr1::ellint_1(l, d));

   check_result<float>(boost::math::tr1::ellint_2(f, f));
   check_result<float>(boost::math::tr1::ellint_2f(f, f));
   check_result<double>(boost::math::tr1::ellint_2(d, d));
   check_result<long double>(boost::math::tr1::ellint_2(l, l));
   check_result<long double>(boost::math::tr1::ellint_2l(l, l));
   check_result<double>(boost::math::tr1::ellint_2(ui, ui));
   check_result<double>(boost::math::tr1::ellint_2(i, ui));
   check_result<double>(boost::math::tr1::ellint_2(f, d));
   check_result<long double>(boost::math::tr1::ellint_2(l, d));

   check_result<float>(boost::math::tr1::ellint_3(f, f, f));
   check_result<float>(boost::math::tr1::ellint_3f(f, f, f));
   check_result<double>(boost::math::tr1::ellint_3(d, d, d));
   check_result<long double>(boost::math::tr1::ellint_3(l, l, l));
   check_result<long double>(boost::math::tr1::ellint_3l(l, l, l));
   check_result<double>(boost::math::tr1::ellint_3(ui, ui, i));
   check_result<double>(boost::math::tr1::ellint_3(i, ui, f));
   check_result<double>(boost::math::tr1::ellint_3(f, d, i));
   check_result<long double>(boost::math::tr1::ellint_3(l, d, f));

   check_result<float>(boost::math::tr1::expint(f));
   check_result<float>(boost::math::tr1::expintf(f));
   check_result<double>(boost::math::tr1::expint(d));
   check_result<long double>(boost::math::tr1::expint(l));
   check_result<long double>(boost::math::tr1::expintl(l));
   check_result<double>(boost::math::tr1::expint(ui));
   check_result<double>(boost::math::tr1::expint(i));

   check_result<float>(boost::math::tr1::hermite(ui, f));
   check_result<float>(boost::math::tr1::hermitef(ui, f));
   check_result<double>(boost::math::tr1::hermite(ui, d));
   check_result<long double>(boost::math::tr1::hermite(ui, l));
   check_result<long double>(boost::math::tr1::hermitel(ui, l));
   check_result<double>(boost::math::tr1::hermite(ui, i));
   check_result<double>(boost::math::tr1::hermite(ui, ui));

   check_result<float>(boost::math::tr1::laguerre(ui, f));
   check_result<float>(boost::math::tr1::laguerref(ui, f));
   check_result<double>(boost::math::tr1::laguerre(ui, d));
   check_result<long double>(boost::math::tr1::laguerre(ui, l));
   check_result<long double>(boost::math::tr1::laguerrel(ui, l));
   check_result<double>(boost::math::tr1::laguerre(ui, i));
   check_result<double>(boost::math::tr1::laguerre(ui, ui));

   check_result<float>(boost::math::tr1::legendre(ui, f));
   check_result<float>(boost::math::tr1::legendref(ui, f));
   check_result<double>(boost::math::tr1::legendre(ui, d));
   check_result<long double>(boost::math::tr1::legendre(ui, l));
   check_result<long double>(boost::math::tr1::legendrel(ui, l));
   check_result<double>(boost::math::tr1::legendre(ui, i));
   check_result<double>(boost::math::tr1::legendre(ui, ui));

   check_result<float>(boost::math::tr1::riemann_zeta(f));
   check_result<float>(boost::math::tr1::riemann_zetaf(f));
   check_result<double>(boost::math::tr1::riemann_zeta(d));
   check_result<long double>(boost::math::tr1::riemann_zeta(l));
   check_result<long double>(boost::math::tr1::riemann_zetal(l));
   check_result<double>(boost::math::tr1::riemann_zeta(ui));
   check_result<double>(boost::math::tr1::riemann_zeta(i));

   check_result<float>(boost::math::tr1::sph_bessel(ui, f));
   check_result<float>(boost::math::tr1::sph_besself(ui, f));
   check_result<double>(boost::math::tr1::sph_bessel(ui, d));
   check_result<long double>(boost::math::tr1::sph_bessel(ui, l));
   check_result<long double>(boost::math::tr1::sph_bessell(ui, l));
   check_result<double>(boost::math::tr1::sph_bessel(ui, i));
   check_result<double>(boost::math::tr1::sph_bessel(ui, ui));

   check_result<float>(boost::math::tr1::sph_legendre(ui, ui, f));
   check_result<float>(boost::math::tr1::sph_legendref(ui, ui, f));
   check_result<double>(boost::math::tr1::sph_legendre(ui, ui, d));
   check_result<long double>(boost::math::tr1::sph_legendre(ui, ui, l));
   check_result<long double>(boost::math::tr1::sph_legendrel(ui, ui, l));
   check_result<double>(boost::math::tr1::sph_legendre(ui, ui, i));
   check_result<double>(boost::math::tr1::sph_legendre(ui, ui, ui));

   check_result<float>(boost::math::tr1::sph_neumann(ui, f));
   check_result<float>(boost::math::tr1::sph_neumannf(ui, f));
   check_result<double>(boost::math::tr1::sph_neumann(ui, d));
   check_result<long double>(boost::math::tr1::sph_neumann(ui, l));
   check_result<long double>(boost::math::tr1::sph_neumannl(ui, l));
   check_result<double>(boost::math::tr1::sph_neumann(ui, i));
   check_result<double>(boost::math::tr1::sph_neumann(ui, ui));

   check_result<float>(boost::math::tr1::acosh(f));
   check_result<float>(boost::math::tr1::acoshf(f));
   check_result<double>(boost::math::tr1::acosh(d));
   check_result<long double>(boost::math::tr1::acosh(l));
   check_result<long double>(boost::math::tr1::acoshl(l));
   check_result<double>(boost::math::tr1::acosh(ui));
   check_result<double>(boost::math::tr1::acosh(i));

   check_result<float>(boost::math::tr1::asinh(f));
   check_result<float>(boost::math::tr1::asinhf(f));
   check_result<double>(boost::math::tr1::asinh(d));
   check_result<long double>(boost::math::tr1::asinh(l));
   check_result<long double>(boost::math::tr1::asinhl(l));
   check_result<double>(boost::math::tr1::asinh(ui));
   check_result<double>(boost::math::tr1::asinh(i));

   check_result<float>(boost::math::tr1::atanh(f));
   check_result<float>(boost::math::tr1::atanhf(f));
   check_result<double>(boost::math::tr1::atanh(d));
   check_result<long double>(boost::math::tr1::atanh(l));
   check_result<long double>(boost::math::tr1::atanhl(l));
   check_result<double>(boost::math::tr1::atanh(ui));
   check_result<double>(boost::math::tr1::atanh(i));

   check_result<float>(boost::math::tr1::cbrt(f));
   check_result<float>(boost::math::tr1::cbrtf(f));
   check_result<double>(boost::math::tr1::cbrt(d));
   check_result<long double>(boost::math::tr1::cbrt(l));
   check_result<long double>(boost::math::tr1::cbrtl(l));
   check_result<double>(boost::math::tr1::cbrt(ui));
   check_result<double>(boost::math::tr1::cbrt(i));

   check_result<float>(boost::math::tr1::copysign(f, f));
   check_result<float>(boost::math::tr1::copysignf(f, f));
   check_result<double>(boost::math::tr1::copysign(d, d));
   check_result<long double>(boost::math::tr1::copysign(l, l));
   check_result<long double>(boost::math::tr1::copysignl(l, l));
   check_result<double>(boost::math::tr1::copysign(d, i));
   check_result<double>(boost::math::tr1::copysign(ui, f));

   check_result<float>(boost::math::tr1::erf(f));
   check_result<float>(boost::math::tr1::erff(f));
   check_result<double>(boost::math::tr1::erf(d));
   check_result<long double>(boost::math::tr1::erf(l));
   check_result<long double>(boost::math::tr1::erfl(l));
   check_result<double>(boost::math::tr1::erf(ui));
   check_result<double>(boost::math::tr1::erf(i));

   check_result<float>(boost::math::tr1::erfc(f));
   check_result<float>(boost::math::tr1::erfcf(f));
   check_result<double>(boost::math::tr1::erfc(d));
   check_result<long double>(boost::math::tr1::erfc(l));
   check_result<long double>(boost::math::tr1::erfcl(l));
   check_result<double>(boost::math::tr1::erfc(ui));
   check_result<double>(boost::math::tr1::erfc(i));

   check_result<float>(boost::math::tr1::expm1(f));
   check_result<float>(boost::math::tr1::expm1f(f));
   check_result<double>(boost::math::tr1::expm1(d));
   check_result<long double>(boost::math::tr1::expm1(l));
   check_result<long double>(boost::math::tr1::expm1l(l));
   check_result<double>(boost::math::tr1::expm1(ui));
   check_result<double>(boost::math::tr1::expm1(i));

   check_result<float>(boost::math::tr1::fmin(f, f));
   check_result<float>(boost::math::tr1::fminf(f, f));
   check_result<double>(boost::math::tr1::fmin(d, d));
   check_result<long double>(boost::math::tr1::fmin(l, l));
   check_result<long double>(boost::math::tr1::fminl(l, l));
   check_result<double>(boost::math::tr1::fmin(d, i));
   check_result<double>(boost::math::tr1::fmin(ui, f));

   check_result<float>(boost::math::tr1::fmax(f, f));
   check_result<float>(boost::math::tr1::fmaxf(f, f));
   check_result<double>(boost::math::tr1::fmax(d, d));
   check_result<long double>(boost::math::tr1::fmax(l, l));
   check_result<long double>(boost::math::tr1::fmaxl(l, l));
   check_result<double>(boost::math::tr1::fmax(d, i));
   check_result<double>(boost::math::tr1::fmax(ui, f));

   check_result<float>(boost::math::tr1::hypot(f, f));
   check_result<float>(boost::math::tr1::hypotf(f, f));
   check_result<double>(boost::math::tr1::hypot(d, d));
   check_result<long double>(boost::math::tr1::hypot(l, l));
   check_result<long double>(boost::math::tr1::hypotl(l, l));
   check_result<double>(boost::math::tr1::hypot(d, i));
   check_result<double>(boost::math::tr1::hypot(ui, f));

   check_result<float>(boost::math::tr1::lgamma(f));
   check_result<float>(boost::math::tr1::lgammaf(f));
   check_result<double>(boost::math::tr1::lgamma(d));
   check_result<long double>(boost::math::tr1::lgamma(l));
   check_result<long double>(boost::math::tr1::lgammal(l));
   check_result<double>(boost::math::tr1::lgamma(ui));
   check_result<double>(boost::math::tr1::lgamma(i));

   check_result<long long>(boost::math::tr1::llround(f));
   check_result<long long>(boost::math::tr1::llroundf(f));
   check_result<long long>(boost::math::tr1::llround(d));
   check_result<long long>(boost::math::tr1::llround(l));
   check_result<long long>(boost::math::tr1::llroundl(l));
   check_result<long long>(boost::math::tr1::llround(ui));
   check_result<long long>(boost::math::tr1::llround(i));

   check_result<float>(boost::math::tr1::log1p(f));
   check_result<float>(boost::math::tr1::log1pf(f));
   check_result<double>(boost::math::tr1::log1p(d));
   check_result<long double>(boost::math::tr1::log1p(l));
   check_result<long double>(boost::math::tr1::log1pl(l));
   check_result<double>(boost::math::tr1::log1p(ui));
   check_result<double>(boost::math::tr1::log1p(i));

   check_result<long>(boost::math::tr1::lround(f));
   check_result<long>(boost::math::tr1::lroundf(f));
   check_result<long>(boost::math::tr1::lround(d));
   check_result<long>(boost::math::tr1::lround(l));
   check_result<long>(boost::math::tr1::lroundl(l));
   check_result<long>(boost::math::tr1::lround(ui));
   check_result<long>(boost::math::tr1::lround(i));

   check_result<float>(boost::math::tr1::round(f));
   check_result<float>(boost::math::tr1::roundf(f));
   check_result<double>(boost::math::tr1::round(d));
   check_result<long double>(boost::math::tr1::round(l));
   check_result<long double>(boost::math::tr1::roundl(l));
   check_result<double>(boost::math::tr1::round(ui));
   check_result<double>(boost::math::tr1::round(i));

   check_result<float>(boost::math::tr1::nextafter(f, f));
   check_result<float>(boost::math::tr1::nextafterf(f, f));
   check_result<double>(boost::math::tr1::nextafter(d, d));
   check_result<long double>(boost::math::tr1::nextafter(l, l));
   check_result<long double>(boost::math::tr1::nextafterl(l, l));
   check_result<double>(boost::math::tr1::nextafter(d, i));
   check_result<double>(boost::math::tr1::nextafter(ui, f));

   check_result<float>(boost::math::tr1::nexttoward(f, f));
   check_result<float>(boost::math::tr1::nexttowardf(f, f));
   check_result<double>(boost::math::tr1::nexttoward(d, d));
   check_result<long double>(boost::math::tr1::nexttoward(l, l));
   check_result<long double>(boost::math::tr1::nexttowardl(l, l));
   check_result<double>(boost::math::tr1::nexttoward(d, i));
   check_result<double>(boost::math::tr1::nexttoward(ui, f));

   check_result<float>(boost::math::tr1::tgamma(f));
   check_result<float>(boost::math::tr1::tgammaf(f));
   check_result<double>(boost::math::tr1::tgamma(d));
   check_result<long double>(boost::math::tr1::tgamma(l));
   check_result<long double>(boost::math::tr1::tgammal(l));
   check_result<double>(boost::math::tr1::tgamma(ui));
   check_result<double>(boost::math::tr1::tgamma(i));

   check_result<float>(boost::math::tr1::trunc(f));
   check_result<float>(boost::math::tr1::truncf(f));
   check_result<double>(boost::math::tr1::trunc(d));
   check_result<long double>(boost::math::tr1::trunc(l));
   check_result<long double>(boost::math::tr1::truncl(l));
   check_result<double>(boost::math::tr1::trunc(ui));
   check_result<double>(boost::math::tr1::trunc(i));

}


#ifndef BIT_VECTOR_HPP
#define BIT_VECTOR_HPP

#include <assert.h>
#include <string>
#include <stdexcept>
#include <algorithm>
#include <vector>
#include <iostream>

#include "config/config.hpp"

#include <boost/dynamic_bitset/dynamic_bitset.hpp>

namespace boost {
	/**
	 * @brief The multiplication of two dynamic_bitsets.
	 * Assume A=[a_1, a_2, ..., a_n] and B=[b_1, b_2, ..., b_n], the multiplication
	 * of A*B = (a_1*b_1)+(a_2*b_2)+...+(a_n*b_n). This operation is equivalent
	 * in function to determine whether A intersects with B.
	 * @param two dynamic_bitsets a and b
	 * @return the bool value of A*B.
	 */
	template <typename Block, typename Allocator>
	inline bool operator*(const dynamic_bitset<Block, Allocator>& a,
		const dynamic_bitset<Block, Allocator>& b)
	{
		assert(a.size() == b.size());

		/************************************************************************/
		/* Methods one, needing to add friend function in source file           */
		/************************************************************************/
		//typedef typename dynamic_bitset<Block, Allocator>::size_type size_type;

		//for (size_type ii = a.num_blocks(); ii > 0; --ii) {
		//  size_type i = ii-1;
		//  if (a.m_bits[i] & b.m_bits[i])
		//    return true;
		//}
		//return false;
		//

		/************************************************************************/
		/* Method two, a quite easy way!!!                                      */
		/************************************************************************/

		return a.intersects(b);
	}

} // boost namespace end

namespace argumatrix {  // argumatrix

using namespace boost;
class bitvector: public dynamic_bitset<block_type>
{
public:
	// Constructor
	bitvector(): dynamic_bitset<block_type>() { }
	bitvector(size_type _sz, unsigned long value = 0): dynamic_bitset<block_type>(_sz, value) { }
	bitvector(const std::string& _s): dynamic_bitset<block_type>(_s) { }
	bitvector(const dynamic_bitset<block_type>& _db): dynamic_bitset<block_type>(_db) { }

public:
	/**
	 * @brief The multiplication of two bitvector.
	 * Assume A=[a_1, a_2, ..., a_n] and B=[b_1, b_2, ..., b_n], the multiplication
	 * of A*B = (a_1*b_1)+(a_2*b_2)+...+(a_n*b_n). This operation is equivalent
	 * in function to determine whether A intersects with B.
	 * @param two bitvector a and b
	 * @return the bool value of A*B.
	 */
	bool operator*(const bitvector& _bv);

	/**
	 * @brief To decide whether the bitvector of an argument set is empty-set?
	 * If all entries of this bitvector are 0's, then it is an empty set, and
	 * this function return true, else return false. we must discriminate this
	 * function from the function empty(), which is used to determine the size of
	 * the bitvector is zero. This function is the same as the function none().
	 * @return true if all entries of the bitvector are 0's, otherwise return false.
	 */
	bool is_emptyset();

	/**
	 * @brief To decide whether the bitvector of an argument set is universal?
	 * If all entries of this bitvector are 1's, then it is an universal set, and
	 * this function return true, else return false.
	 */
	bool is_universal();

	/**
	 * @brief Create an empty set (all 0's) or a universal set (all 1's) under a given size.
	 * @param _sz the size of the empty set or the universal set
	 * @return the bitvector of the empty set or the universal set.
	 */
	static bitvector EmptySet(size_type _sz);
	static bitvector UniversalSet(size_type _sz);

	/**
	 * increaser the bit vector with 1.
	 * @param _sz the size of the empty set or the universal set
	 * @return the bitvector of the empty set or the universal set.
	 */
	bool Increase();
};

__inline
bool bitvector::operator*(const bitvector& _bv)
{
	return intersects(_bv);
}

inline
bool bitvector::is_emptyset()
{
	return !any();
}

__inline
argumatrix::bitvector bitvector::EmptySet(size_type _sz)
{
	return bitvector(_sz, 0);
}

argumatrix::bitvector bitvector::UniversalSet(size_type _sz)
{
	//bitvector universal_bv(_sz, 0);
	//universal_bv.set();  // set all 0's to all 1's
	//return universal_bv;

	return bitvector(_sz, 0).set();
}

bool bitvector::Increase()
{
	return true;
}

__inline
bool bitvector::is_universal()
{
	// dynamic_bitset<>::all() may not be supported in the version
	// less than 1.61. For compatibility, an inefficient way is used.

	//return all();
	return bitvector(this->size(), 0).set() == *this;
}

} // namespace argumatrix
#endif


// Copyright Louis Dionne 2015
// Distributed under the Boost Software License, Version 1.0.

#include <boost/mpl/less.hpp>
#include <boost/mpl/min_element.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/sizeof.hpp>
#include <boost/mpl/vector.hpp>

#include <boost/hana/tuple.hpp>
#include <boost/hana/type.hpp>

#include <type_traits>

template <int n>
struct storage { char weight[n]; };

namespace then {
using namespace boost::mpl;

// sample(smallest_type-then)
template <typename ...T>
struct smallest
  : deref<
    typename min_element<
      vector<T...>, less<sizeof_<_1>, sizeof_<_2>>
    >::type
  >
{ };

template <typename ...T>
using smallest_t = typename smallest<T...>::type;

static_assert(std::is_same<
  smallest_t<char, long, long double>, char
>::value, "");
// end-sample

static_assert(std::is_same<
  smallest_t<storage<3>, storage<1>, storage<2>>,
  storage<1>
>::value, "");
}

namespace now {
using namespace boost::hana;

// sample(smallest_type-now)
template <typename ...T>
auto smallest = minimum(tuple_t<T...>, [](auto t, auto u) {
  return sizeof_(t) < sizeof_(u);
});

template <typename ...T>
using smallest_t = typename decltype(smallest<T...>)::type;

static_assert(std::is_same<
  smallest_t<char, long, long double>, char
>::value, "");
// end-sample

static_assert(std::is_same<
  smallest_t<storage<3>, storage<1>, storage<2>>,
  storage<1>
>::value, "");
}

int main() { }


#ifndef YASMIC_ISTREAM_AS_MATRIX
#define YASMIC_ISTREAM_AS_MATRIX

#include <iostream>
#include <boost/tuple/tuple.hpp>
#include <yasmic/smatrix_traits.hpp>
#include <iterator>

namespace yasmic
{
	template <>
    struct smatrix_traits<std::istream>
    {
    	typedef unsigned int size_type;
    	typedef int index_type;
		typedef double value_type;

		typedef std::istream_iterator<boost::tuple<index_type, index_type, value_type> > nonzero_iterator;

		typedef void row_iterator;
		typedef void row_nonzero_iterator;
		typedef void column_iterator;
		typedef void column_iterator;
    };

    inline std::pair<typename smatrix_traits<std::istream>::size_type,
                      typename smatrix_traits<std::istream>::size_type>
    dimensions(std::istream& f)
    {
    	smatrix_traits<std::istream>::size_type nrows,ncols;

    	f.seekg(0, std::ios_base::beg);
        f >> nrows >> ncols;

        return (std::make_pair(nrows, ncols));
    }

	inline smatrix_traits<std::istream>::size_type ncols(std::istream& f)
	{
		smatrix_traits<std::istream>::size_type nrows,ncols;

		boost::tie(nrows, ncols) = dimensions(f);

		return (ncols);
	}

	inline smatrix_traits<std::istream>::size_type nrows(std::istream& f)
	{
		smatrix_traits<std::istream>::size_type nrows,ncols;

		boost::tie(nrows, ncols) = dimensions(f);

		return (nrows);
	}

	inline smatrix_traits<std::istream>::size_type nnz(std::istream& f)
	{
		smatrix_traits<std::istream>::size_type d1,d2,nnz;

        f.seekg(0, std::ios_base::beg);
        f >> d1 >>  d2 >> nnz;

        return (nnz);
	}

	inline std::pair<smatrix_traits<std::istream>::nonzero_iterator,
                      smatrix_traits<std::istream>::nonzero_iterator>
    nonzeros(std::ifstream& f)
    {
    	smatrix_traits<std::istream>::size_type d1,d2,d3;
    	f.seekg(0, std::ios_base::beg);
        f >> d1  >> d2 >> d3;

        typedef smatrix_traits<std::istream>::nonzero_iterator nz_iter;

        return (std::make_pair(nz_iter(f), nz_iter()));
    }
}

#endif //YASMIC_ISTREAM_AS_MATRIX


/**
 * @author J Howard Johnson
 * @file sigprune_fet.cc
 * @brief Calculate Fisher's exact test significance levels
 *
 *
 * Technologies langagieres interactives / Interactive Language Technologies
 * Inst. de technologie de l'information / Institute for Information Technology
 * Conseil national de recherches Canada / National Research Council Canada
 * Copyright 2006-2011, Sa Majeste la Reine du Chef du Canada /
 * Copyright 2006-2011, Her Majesty in Right of Canada
 */

#include <iostream>
#include <fstream>
#include <cmath>
#include <boost/math/special_functions/gamma.hpp>

#include "file_utils.h"
#include "arg_reader.h"
#include "printCopyright.h"

using namespace Portage;
using namespace std;

static char help_message[] = "\n\
sigprune_fet [options] [INFILE [OUTFILE]]\n\
\n\
  Copy INFILE to OUTFILE (default stdin to stdout)\n\
  adding 3 fields to the beginning:\n\
     (1) sort key for log p level\n\
     (2) log p level\n\
     (3) flag\n\
         0 : anti-linked (p_level > 0.50)\n\
         1 : linked with log p level > alpha\n\
         2 : linked with log p level = alpha (1-1-1's)\n\
         3 : linked with log p level < alpha\n\
\n\
Options:\n\
\n\
  -v    Write progress reports to cerr.\n\
  -c    Don't cap anti-linked p_levels at 0.5. Using this\n\
        flag ensures monotonic scores as joint frequency decreases while\n\
        marginals are held constant.\n\
  -l    Minimum flag value to output (for pruning)\n\
  -n NL Copy only first NL lines [0 = all]\n\
";

// globals

static bool verbose = false;
static bool no_cap = false;
static Uint num_lines = 0;
static Uint flag_threshold = 0;
static string infile( "-" );
static string outfile( "-" );

// arg processing

void getArgs( int argc, char* argv[] )
{
   const char* switches[] = { "v", "c", "n:", "l:" };
   ArgReader arg_reader( ARRAY_SIZE( switches ), switches, 0, 2, help_message );
   arg_reader.read( argc - 1, argv + 1 );

   arg_reader.testAndSet( "v", verbose );
   arg_reader.testAndSet( "c", no_cap );
   arg_reader.testAndSet( "n", num_lines );
   arg_reader.testAndSet( "l", flag_threshold );

   arg_reader.testAndSet( 0, "infile", infile );
   arg_reader.testAndSet( 1, "outfile", outfile );
}

typedef long double quad;

quad lnfact( double x )
{
   return lgamma( (quad) x + 1.0 );
}

double ln_hyper( double C_xy, double C_x, double C_y, double nn )
{
   quad result =
      - lnfact( C_xy )
      + lnfact( C_x )
      - lnfact( C_x - C_xy )
      + lnfact( C_y )
      - lnfact( C_y - C_xy )
      + lnfact( nn - C_x )
      - lnfact( nn - C_x - C_y + C_xy )
      + lnfact( nn - C_y )
      - lnfact( nn );
   return result;
}

double lnsum( double ln_x, double ln_y )
{
   if ( ln_x < ln_y ) swap( ln_x, ln_y );
   double del = ln_y - ln_x;
   double eps = exp( del );
   if ( del < 10.0 ) return ln_x + log( 1.0 + eps );
   double last_result = 0.0;
   double result = ln_x;
   double power = eps;
   double i = 1.0;
   while ( result != last_result ) {
      last_result = result;
      result = last_result + power / i;
      power *= -eps;
      ++i;
   }
   return result;
//   return ln_x + log( exp( ln_y - ln_x ) + 1.0 );
}

double ln_p_value( double C_xy, double C_x, double C_y, double nn )
{
   double ln_p = ln_hyper( C_xy, C_x, C_y, nn );
   double result = ln_p;
   double C_xY = C_x - C_xy;
   double C_Xy = C_y - C_xy;
   double C_XY = nn - C_x - C_y + C_xy;
   double C_xy_lim = ( ( C_x < C_y ) ? C_x : C_y );
   double last_result = 0.0;
   for ( ++C_xy;
         C_xy <= C_xy_lim && result != last_result;
         ++C_xy ) {
      last_result = result;
      ln_p +=   ( log( C_Xy-- ) + log( C_xY-- ) )
              - ( log( C_xy   ) + log( ++C_XY ) );
      result = lnsum( result, ln_p );
   }
   return result;
}

string write_b64( double i )
{
   string result;
   int nd = 0;
   int digit;
   int d[ 20 ];
   if ( i > 0 ) {
      while ( nd < 20 && i > 0 ) {
         digit = fmod( i, 64.0 );
         d[ nd++ ] = digit;
         i = ( i - digit ) / 64.0;
      }
      result.push_back( 'P' + nd );
      while ( nd > 0 ) {
         result.push_back( '0' + d[ --nd ] );
      }
   } else {
      i = -i;
      while ( nd < 20 && i > 0 ) {
         digit = fmod( i, 64.0 );
         d[ nd++ ] = digit;
         i = ( i - digit ) / 64.0;
      }
      result.push_back( 'O' - nd );
      while ( nd > 0 ) {
         result.push_back( 'o' - d[ --nd ] );
      }
   }
   return result;
}

int main( int argc, char* argv[] )
{
   printCopyright(2006, "sigprune_fet");
   getArgs( argc, argv );

   iSafeMagicStream istr( infile );
   oSafeMagicStream ostr( outfile );

   Uint lineno = 0;
   string line;
   string line_copy;

   ssize_t read;

   int i;
   vector< char * > field;

   while (    getline( istr, line )
           && (    lineno++ < num_lines
                || num_lines == 0 ) ) {
      line_copy = line;
      read = line.length();
      field.resize( 0 );
      field.push_back( &line[ 0 ] );
      for ( i = 0; i < read; ++i ) {
         if (    line[ i ] == '\t'
              || line[ i ] == '\n' ) {
            line[ i ] = '\0';
            field.push_back( &line[ i + 1 ] );
         }
      }
      double C_fr_en = strtod( field[ 1 ], 0 );
      double C_fr    = strtod( field[ 2 ], 0 );
      double C_en    = strtod( field[ 3 ], 0 );
      double nn      = strtod( field[ 4 ], 0 );
      if (    0.0 > C_fr_en
           || C_fr_en > C_fr
           || C_fr_en > C_en
           || C_fr > nn
           || C_en > nn ) {
         error(ETFatal, "Illegal contingency table\n%d : %d : %d : %d : %d", lineno, C_fr_en, C_fr, C_en, nn);
      }
      double minus_ln_nn = -log( nn );
      Uint flag;
      double p_value;

// Anti-linked
      if ( C_fr_en * nn < C_fr * C_en ) {
         flag = 0;
         p_value = no_cap ? ln_p_value( C_fr_en, C_fr, C_en, nn ) : -log( 2.0 );
      }

// 1-1-1 's
      else if ( C_fr_en == 1.0 && C_fr == 1.0 && C_en == 1.0 ) {
         flag = 2;
         p_value = minus_ln_nn;
      }

// Associated less strongly than 1-1-1's
      else {
         p_value = ln_p_value( C_fr_en, C_fr, C_en, nn );
         if ( p_value > minus_ln_nn ) {
            flag = 1;
         }

// Associated more strongly than 1-1-1's
         else {
            flag = 3;
         }
      }

      if ( flag >= flag_threshold ) {
         double s_p_value = floor( p_value * 1.0e8 + 0.5 );
         ostr << write_b64( s_p_value ) << '\t';
         ostr << setprecision( 14 );
         ostr << s_p_value * 1.0e-8 << '\t';
         ostr << flag << '\t';
         ostr << line_copy << '\n';
      }
   }
}


//=====================================================================//
/*! @file
	@brief  Perlin Noise メイン関係
	@author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include <iostream>
#include <tuple>
#include "pn_main.hpp"
#include "core/glcore.hpp"
#include "widgets/widget_utils.hpp"
#include <boost/lexical_cast.hpp>

namespace app {

	void pn_main::create_texture_()
	{
//		std::cout << "Ovtv: " << octave_value_ << std::endl;
//		std::cout << "Freq: " << frequency_value_ << std::endl;
//		std::cout << "Gain: " << gain_value_ << std::endl;

		int w = src_image_->get_size().x;
		int h = src_image_->get_size().y;

		prn_image_.create(vtx::ipos(w, h), true);

		int task = 0;
		if(pn_menu_) {
			task = pn_menu_->get_select_pos();
		}
		if(task == 0) {
			for(int y = 0; y < h; ++y) {
				for(int x = 0; x < w; ++x) {
					prn_image_.put_pixel(vtx::ipos(x, y), img::rgba8(0, 0, 0, 0));
				}
			}
		} else if(task == 1 || task == 2) {
			typedef img::perlin_noise<float> perlin_noise;
			perlin_noise pn(12345);
			float fx = static_cast<float>(w) / frequency_value_;
			float fy = static_cast<float>(h) / frequency_value_;

			for(int y = 0; y < h; ++y) {
				for(int x = 0; x < w; ++x) {
					float n = pn.octave_noise(x / fx, y / fy, octave_value_) * gain_value_;
					if(task == 1) {
						n = perlin_noise::clamp(n * 0.5f + 0.5f, 0.0, 1.0);
					} else {
						n = perlin_noise::compressor(n, -1.0f, -0.8f, 0.8f, 1.0f);
						n += 1.0f;
						n *= 0.5f;
					}
					uint8_t gray = static_cast<uint8_t>(n * 255);
					prn_image_.put_pixel(vtx::ipos(x, y), img::rgba8(gray, gray, gray, gray ^ 255));
				}
			}
		}
	}

	void pn_main::blend_()
	{
		mobj_.destroy();
		mobj_.initialize();
		bld_image_ = img::shared_img(img::copy_image(src_image_.get()));
		img::img_rgba8* img = static_cast<img::img_rgba8*>(bld_image_.get());
		img->blend(vtx::ipos(0), prn_image_, vtx::srect(vtx::ipos(0), prn_image_.get_size()));
		img_handle_ = mobj_.install(img);
		image_->at_local_param().mobj_ = mobj_;
		image_->at_local_param().mobj_handle_ = img_handle_;
	}

	typedef std::tuple<const std::string, const img::shared_img> save_t;

	bool save_task_(save_t t)
	{
		img::img_files imfs;
		imfs.set_image(std::get<1>(t));
		return imfs.save(std::get<0>(t));
	}

	void pn_main::image_info_(const std::string& file, const img::i_img* img)
	{
		std::string s;
		if(!file.empty()) {
			size_t fsz = utils::get_file_size(file);
			if(fsz > 0) s = ": " + boost::lexical_cast<std::string>(fsz) + '\n';
			term_->output(s);
		}
		s = "W: " + boost::lexical_cast<std::string>(img->get_size().x) + '\n';
		term_->output(s);
		s = "H: " + boost::lexical_cast<std::string>(img->get_size().y) + '\n';
		term_->output(s);
		img::IMG::type t = img->get_type();
		if(t == img::IMG::INDEXED8) {
			term_->output("INDEXED8\n");
		} else if(t == img::IMG::FULL8) {
			term_->output("FULL8\n");
		}
		if(img->test_alpha()) {
			term_->output("Alpha\n");
		}
		s = "C: " + boost::lexical_cast<std::string>(img->count_color()) + '\n';
		term_->output(s);
		term_->output('\n');
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  初期化
	*/
	//-----------------------------------------------------------------//
	void pn_main::initialize()
	{
		gl::core& core = gl::core::get_instance();

		using namespace gui;
		widget_director& wd = director_.at().widget_director_;

		{ // 画像ファイル表示用フレーム
			widget::param wp(vtx::irect(30, 30, 256, 256));
			widget_frame::param wp_;
			wp_.plate_param_.set_caption(30);
			frame_ = wd.add_widget<widget_frame>(wp, wp_);
		}

		{ // 画像ファイル表示イメージ
			widget::param wp(vtx::irect(0, 0, 256, 256), frame_);
			widget_image::param wp_;
			image_ = wd.add_widget<widget_image>(wp, wp_);
			image_->set_state(widget::state::CLIP_PARENTS);
			image_->set_state(widget::state::RESIZE_ROOT);
			image_->set_state(widget::state::MOVE_ROOT, false);
			image_->set_state(widget::state::POSITION_LOCK, false);
		}

		{ // 機能ツールパレット
			widget::param wp(vtx::irect(10, 10, 150, 430));
			widget_frame::param wp_;
			tools_ = wd.add_widget<widget_frame>(wp, wp_);
			tools_->set_state(widget::state::SIZE_LOCK);
		}
		{ // octave スライダー
			widget::param wp(vtx::irect(10, 10+30*0, 130, 20), tools_);
			widget_slider::param wp_;
			wp_.slider_param_.grid_ = 1.0f / 7.0f;
			wp_.select_func_ = [this](float pos) {
				octave_value_ = static_cast<int>(pos * 7.0f);
				update_ = true;
//				std::cout << "Ovtv: " << octave_value_ << ", " << pos << std::endl;
			};
			octave_ = wd.add_widget<widget_slider>(wp, wp_);
		}
		{ // frequency スライダー
			widget::param wp(vtx::irect(10, 10+30*1, 130, 20), tools_);
			widget_slider::param wp_;
			wp_.slider_param_.grid_ = 1.0f / 15.0f;
			wp_.select_func_ = [this](float pos){
				frequency_value_ = pos * 15.0f + 1.0f;
				update_ = true;
			};
			frequency_ = wd.add_widget<widget_slider>(wp, wp_);
		}
		{ // gain スライダー
			widget::param wp(vtx::irect(10, 10+30*2, 130, 20), tools_);
			widget_slider::param wp_;
			wp_.slider_param_.grid_ = 1.0f / 20.0f;
			wp_.select_func_ = [this](float pos){
				gain_value_ = pos * 20.0f;
				update_ = true;
			};
			gain_ = wd.add_widget<widget_slider>(wp, wp_);
		}
		{ // リスト
			widget::param wp(vtx::irect(10, 10+30*3, 130, 40), tools_);
			widget_list::param wp_;
			wp_.init_list_.push_back("None");
			wp_.init_list_.push_back("Smoke");
			wp_.init_list_.push_back("Flow");
			pn_menu_ = wd.add_widget<widget_list>(wp, wp_);
			pn_menu_->at_local_param().select_func_ = [this](const std::string& text, int pos) {
				update_ = true;
			};
		}

		short ofs = 150;
		{ // ロードボタン
			widget::param wp(vtx::irect(10, ofs+50*0, 100, 40), tools_);
			widget_button::param wp_("load");
			load_ = wd.add_widget<widget_button>(wp, wp_);
			load_->at_local_param().select_func_ = [this](int id) {
				if(load_ctx_) {
					bool f = load_ctx_->get_state(gui::widget::state::ENABLE);
					load_ctx_->enable(!f);
				}
			};
		}

		{ // セーブボタン
			widget::param wp(vtx::irect(10, ofs+50*1, 100, 40), tools_);
			widget_button::param wp_("save");
			save_ = wd.add_widget<widget_button>(wp, wp_);
			save_->at_local_param().select_func_ = [this](int id) {
				if(save_ctx_) {
					bool f = save_ctx_->get_state(gui::widget::state::ENABLE);
					save_ctx_->enable(!f);
				}
			};
		}
		ofs += 100;

		{ // スケール FIT
			widget::param wp(vtx::irect(10, ofs+30*0, 90, 30), tools_);
			widget_radio::param wp_("fit");
			wp_.check_ = true;
			scale_fit_ = wd.add_widget<widget_radio>(wp, wp_);
		}
		{ // スケール 1X
			widget::param wp(vtx::irect(10, ofs+30*1, 90, 30), tools_);
			widget_radio::param wp_("1x");
			scale_1x_ = wd.add_widget<widget_radio>(wp, wp_);
		}
		{ // スケール 2X
			widget::param wp(vtx::irect(10, ofs+30*2, 90, 30), tools_);
			widget_radio::param wp_("2x");
			scale_2x_ = wd.add_widget<widget_radio>(wp, wp_);
		}
		{ // スケール 3X
			widget::param wp(vtx::irect(10, ofs+30*3, 90, 30), tools_);
			widget_radio::param wp_("3x");
			scale_3x_ = wd.add_widget<widget_radio>(wp, wp_);
		}
		{ // スケーラーボタン
			widget::param wp(vtx::irect(10, ofs+30*4+10, 100, 40), tools_);
			widget_button::param wp_("scale");
			scale_ = wd.add_widget<widget_button>(wp, wp_);
		}

		{ // ターミナル
			{
				widget::param wp(vtx::irect(10, 320, 9*14-8, 18*16+28));
				widget_frame::param wp_;
				wp_.plate_param_.set_caption(20);
				info_ = wd.add_widget<widget_frame>(wp, wp_);
			}
			{
				widget::param wp(vtx::irect(0), info_);
				widget_terminal::param wp_;
				wp_.echo_ = false;
				term_ = wd.add_widget<widget_terminal>(wp, wp_);
			}
		}

		{ // load ファイラー本体
			widget::param wp(vtx::irect(10, 30, 300, 200));
			widget_filer::param wp_(core.get_current_path());
			load_ctx_ = wd.add_widget<widget_filer>(wp, wp_);
			load_ctx_->enable(false);
		}

		{ // save ファイラー本体
			widget::param wp(vtx::irect(10, 30, 300, 200));
			widget_filer::param wp_(core.get_current_path(), "", true);
			save_ctx_ = wd.add_widget<widget_filer>(wp, wp_);
			save_ctx_->enable(false);
		}

		{ // ダイアログ
			widget::param wp(vtx::irect(10, 30, 450, 200));
			widget_dialog::param wp_;
			dialog_ = wd.add_widget<widget_dialog>(wp, wp_);
			dialog_->enable(false);
		}
		{ // ダイアログ(cancel/ok)
			widget::param wp(vtx::irect(10, 30, 450, 200));
			widget_dialog::param wp_(widget_dialog::style::CANCEL_OK);
			dialog_yes_no_ = wd.add_widget<widget_dialog>(wp, wp_);
			dialog_yes_no_->enable(false);
		}
		{ // ダイアログ(scale)
			widget::param wp(vtx::irect(10, 30, 450, 200));
			widget_dialog::param wp_(widget_dialog::style::CANCEL_OK);
			dialog_scale_ = wd.add_widget<widget_dialog>(wp, wp_);
			dialog_scale_->enable(false);
		}

		mobj_.initialize();

		// プリファレンスの取得
		sys::preference& pre = director_.at().preference_;
		if(load_ctx_) load_ctx_->load(pre);
		if(save_ctx_) save_ctx_->load(pre);
		if(frame_) frame_->load(pre);
		if(octave_) octave_->load(pre);
		if(frequency_) frequency_->load(pre);
		if(gain_) gain_->load(pre);
		if(pn_menu_) pn_menu_->load(pre);
		if(tools_) tools_->load(pre, false, false);
		if(scale_fit_) scale_fit_->load(pre);
		if(scale_1x_) scale_1x_->load(pre);
		if(scale_2x_) scale_2x_->load(pre);
		if(scale_3x_) scale_3x_->load(pre);
		if(info_) info_->load(pre);
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  アップデート
	*/
	//-----------------------------------------------------------------//
	void pn_main::update()
	{
		gl::core& core = gl::core::get_instance();

		gui::widget_director& wd = director_.at().widget_director_;

		if(update_) {
			if(src_image_) {
				create_texture_();
				blend_();
				update_ = false;
			}
		}

		if(scale_) {
			if(scale_->get_selected()) {
				bool f = dialog_scale_->get_state(gui::widget::state::ENABLE);
				dialog_scale_->enable(!f);
			}
		}

		std::string imfn;
		int id = core.get_recv_files_id();
		if(dd_id_ != id) {
			dd_id_ = id;
			const utils::strings& ss = core.get_recv_files_path();
			if(!ss.empty()) {
				imfn = ss.back();
			}
		}

		bool load_stall = false;
		bool save_stall = false;

		if(load_ctx_) {
			if(load_ctx_->get_state(gui::widget::state::ENABLE)) {
				save_stall = true;
			}
			if(load_id_ != load_ctx_->get_select_file_id()) {
				load_id_ = load_ctx_->get_select_file_id();
				imfn = load_ctx_->get_file();
			}
		}

		if(!imfn.empty()) {
			img::img_files& imf = wd.at_img_files();
			if(!imf.load(imfn)) {
				dialog_->set_text("Can't decode image file:\n '"
								  + load_ctx_->get_file() + "'");
				dialog_->enable();
			} else {
				src_image_ = imf.get_image();
				term_->output("Ld");
				image_info_(load_ctx_->get_file(), src_image_.get());
				image_offset_.set(0.0f);
				frame_->at_local_param().text_param_.set_text(imfn);
				update_ = true;
			}
		}

		// frame 内 image のサイズを設定
		if(frame_ && image_) {
			if(!image_->get_local_param().mobj_handle_) {
				save_stall = true;
			}

			float s = 1.0f;
			if(scale_fit_->get_check()) {
				vtx::fpos is = mobj_.get_size(img_handle_);
				vtx::fpos ss = image_->at_rect().size;
				vtx::fpos sc = ss / is;
				if(sc.x < sc.y) s = sc.x; else s = sc.y;
				image_->at_local_param().offset_ = 0.0f;
			} else {
				if(scale_1x_->get_check()) s = 1.0f;
 				else if(scale_2x_->get_check()) s = 2.0f;
 				else if(scale_3x_->get_check()) s = 3.0f;

				if(image_->get_select_in()) {
					image_offset_ = image_->get_local_param().offset_;
				}
				if(image_->get_select()) {
					vtx::ipos d = image_->get_param().move_pos_ - image_->get_param().move_org_;
					image_->at_local_param().offset_ = image_offset_ + d / s;
				}
			}
			image_->at_local_param().scale_ = s;

			//エリアの作成
///			area_->at_param().rect_.org.set(0);
///			area_->at_param().rect_.size = frame_->get_param().rect_.size;
		}

		if(save_ctx_) {
			if(save_ctx_->get_state(gui::widget::state::ENABLE)) {
				load_stall = true;
			}
			if(save_id_ != save_ctx_->get_select_file_id()) {
				save_id_ = save_ctx_->get_select_file_id();
				const std::string& fn = save_ctx_->get_file();
				if(utils::probe_file(fn)) {
					dialog_yes_no_->set_text("Over write ?:\n'"
											 + fn + "'");
					dialog_yes_no_->enable();
					save_dialog_ = true;
				} else {
					save_dialog_ = false;
				}
				save_file_name_ = fn;
			}
		}

		load_->set_state(gui::widget::state::STALL, load_stall);
		save_->set_state(gui::widget::state::STALL, save_stall);

		wd.update();

		if(!save_file_name_.empty()) {
			save_t t = std::make_tuple(save_file_name_, src_image_);
			if(!save_task_(t)) {
				dialog_->set_text("Can't encode image file:\n'"
								  + save_file_name_ + "'");
				dialog_->enable();
			} else {
				term_->output("Sv");
				image_info_(save_file_name_, src_image_.get());
			}
			save_file_name_.clear();
#if 0
			if(image_saver_.valid()) {
				if(!image_saver_.get()) {
					dialog_->set_text("Can't encode image file:\n'"
									  + save_file_name_ + "'");
					dialog_->enable();
				} else {
					term_->output("Sv");
					image_info_(save_file_name_, src_image_.get());
				}
				save_file_name_.clear();
			} else {
				bool launch = false;
				if(save_dialog_) {
					if(!dialog_yes_no_->get_state(gui::widget::state::ENABLE)) {
						if(dialog_yes_no_->get_local_param().return_ok_) {
							launch = true;
						}
					}
				} else {
					launch = true;
				}
				if(launch) {
					save_t t = std::make_tuple(save_file_name_, src_image_);
					image_saver_ = std::async(std::launch::async, save_task_, t);
				}
			}
#endif
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  レンダリング
	*/
	//-----------------------------------------------------------------//
	void pn_main::render()
	{
		director_.at().widget_director_.service();
		director_.at().widget_director_.render();
	}

	//-----------------------------------------------------------------//
	/*!
		@brief  廃棄
	*/
	//-----------------------------------------------------------------//
	void pn_main::destroy()
	{
		sys::preference& pre = director_.at().preference_;
		if(load_ctx_) load_ctx_->save(pre);
		if(save_ctx_) save_ctx_->save(pre);
		if(frame_) frame_->save(pre);
		if(tools_) tools_->save(pre);
		if(octave_) octave_->save(pre);
		if(frequency_) frequency_->save(pre);
		if(gain_) gain_->save(pre);
		if(pn_menu_) pn_menu_->save(pre);
		if(scale_fit_) scale_fit_->save(pre);
		if(scale_1x_) scale_1x_->save(pre);
		if(scale_2x_) scale_2x_->save(pre);
		if(scale_3x_) scale_3x_->save(pre);
		if(info_) info_->save(pre);
	}
}


#include <boost/math/special_functions/bessel.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
    using namespace boost::math;
    std::cout << cyl_bessel_i(1000, 10) << " " << std::endl;
}


/*=============================================================================
Copyright 2020 Syed Ali Hasan <alihasan9922@gmail.com>

Distributed under the Boost Software License, Version 1.0. (See accompanying
file License.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/

#ifndef BOOST_ASTRONOMY_HORIZON_COORD_HPP
#define BOOST_ASTRONOMY_HORIZON_COORD_HPP

#include <iostream>
#include <boost/static_assert.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/units/get_dimension.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/units/systems/si/plane_angle.hpp>
#include <boost/units/systems/si/dimensionless.hpp>
#include <boost/units/physical_dimensions/plane_angle.hpp>
#include <boost/astronomy/coordinate/coord_sys/coord_sys.hpp>

/**
 * The Horizon Coordinates, Altitude and Azimuth, of an object
 * in the sky are referred to the plane of the observer’s horizon
 *
 * Azimuth
 * THe Azimuth, is in the range of 0◦ to 360◦ and indicates how far an object
 * in the sky is from the north as measured along an observer’s horizon.
 *
 * Altitude
 * The Altitude, represented by the symbol h, and ranges from −90◦ to +90◦.
 * Positive altitudes indicate objects above the horizon while negative
 * altitudes indicate objects below the horizon.
 *
**/

namespace boost { namespace astronomy { namespace coordinate {

namespace bu = boost::units;
namespace bg = boost::geometry;

template
<
    typename CoordinateType = double,
    typename AltitudeQuantity = bu::quantity<bu::si::plane_angle, CoordinateType>,
    typename AzimuthQuantity = bu::quantity<bu::si::plane_angle, CoordinateType>
>
struct horizon_coord : public coord_sys
    <2, bg::cs::spherical<bg::radian>, CoordinateType>
{
  ///@cond INTERNAL
  BOOST_STATIC_ASSERT_MSG(
      ((std::is_same<typename bu::get_dimension<AltitudeQuantity>::type,
          bu::plane_angle_dimension>::value) &&
       (std::is_same<typename bu::get_dimension<AzimuthQuantity>::type,
           bu::plane_angle_dimension>::value)),
      "Altitude and Azimuth must be of plane angle type");
  BOOST_STATIC_ASSERT_MSG((std::is_floating_point<CoordinateType>::value),
                          "CoordinateType must be a floating-point type");
  ///@endcond
public:
    typedef AltitudeQuantity quantity1;
    typedef AzimuthQuantity quantity2;

    //Default constructor
    horizon_coord() {}

    horizon_coord
    (
        AltitudeQuantity const &Altitude,
        AzimuthQuantity const &Azimuth
    )
    {
        this->set_altitude_azimuth(Altitude, Azimuth);
    }

    //Create tuple of Altitude and Azimuth
    std::tuple<AltitudeQuantity, AzimuthQuantity> get_altitude_azimuth() const
    {
        return std::make_tuple(this->get_altitude(), this->get_azimuth());
    }

    //Get Altitude
    AltitudeQuantity get_altitude() const
    {
        return static_cast<AltitudeQuantity>
            (
                bu::quantity<bu::si::plane_angle, CoordinateType>::from_value
                        (bg::get<0>(this->point))
            );
    }

    //Get Azimuth
    AzimuthQuantity get_azimuth() const
    {
        return static_cast<AzimuthQuantity>
            (
                bu::quantity<bu::si::plane_angle, CoordinateType>::from_value
                        (bg::get<1>(this->point))
            );
    }

    //Set value of Altitude and Azimuth
    void set_altitude_azimuth
    (
        AltitudeQuantity const &Altitude,
        AzimuthQuantity const &Azimuth
    )
    {
        this->set_altitude(Altitude);
        this->set_azimuth(Azimuth);
    }

    //Set Altitude
    void set_altitude(AltitudeQuantity const &Altitude)
    {
        bg::set<0>
            (
                this->point,
                static_cast<bu::quantity<bu::si::plane_angle, CoordinateType>>(Altitude).value()
            );
    }

    //Set Azimuth
    void set_azimuth(AzimuthQuantity const &Azimuth)
    {
        bg::set<1>
            (
                this->point,
                static_cast<bu::quantity<bu::si::plane_angle, CoordinateType>>(Azimuth).value()
            );
    }

}; //horizon_coord

//Make Horizon Coordinate
template
<
    typename CoordinateType,
    template<typename Unit1, typename CoordinateType_> class AltitudeQuantity,
    template<typename Unit2, typename CoordinateType_> class AzimuthQuantity,
    typename Unit1,
    typename Unit2
>
horizon_coord
<
    CoordinateType,
    AltitudeQuantity<Unit1, CoordinateType>,
    AzimuthQuantity<Unit2, CoordinateType>
> make_horizon_coord
(
    AltitudeQuantity<Unit1, CoordinateType> const &Altitude,
    AzimuthQuantity<Unit2, CoordinateType> const &Azimuth
)
{
    return horizon_coord
        <
            CoordinateType,
            AltitudeQuantity<Unit1, CoordinateType>,
            AzimuthQuantity<Unit2, CoordinateType>
        > (Altitude, Azimuth);
}

//Print Horizon Coordinates
template
<
    typename CoordinateType,
    class AltitudeQuantity,
    class AzimuthQuantity
>
std::ostream &operator << (std::ostream &out, horizon_coord
        <CoordinateType, AltitudeQuantity, AzimuthQuantity> const &point) {
    out << "Horizon Coordinate (Altitude: "
        << point.get_altitude() << ", Azimuth: "
        << point.get_azimuth() << ")";

    return out;
}

}}}

#endif //BOOST_ASTRONOMY_HORIZON_COORD_HPP


#include <stan/math/prim/scal.hpp>
#include <gtest/gtest.h>
#include <boost/random/mersenne_twister.hpp>
#include <boost/math/distributions.hpp>

TEST(ProbDistributionsNegBinomial, error_check) {
  boost::random::mt19937 rng;
  EXPECT_NO_THROW(stan::math::neg_binomial_2_rng(6, 2, rng));
  EXPECT_NO_THROW(stan::math::neg_binomial_2_rng(0.5, 1, rng));
  EXPECT_NO_THROW(stan::math::neg_binomial_2_rng(1e8, 1, rng));

  EXPECT_THROW(stan::math::neg_binomial_2_rng(0, -2, rng),
                 std::domain_error);
  EXPECT_THROW(stan::math::neg_binomial_2_rng(6, -2, rng),
                   std::domain_error);
  EXPECT_THROW(stan::math::neg_binomial_2_rng(-6, -0.1, rng),
                   std::domain_error);
  EXPECT_THROW(stan::math::neg_binomial_2_rng(
                 stan::math::positive_infinity(), 2, rng),
                 std::domain_error);
  EXPECT_THROW(stan::math::neg_binomial_2_rng(
                 stan::math::positive_infinity(), 6, rng),
                 std::domain_error);
  EXPECT_THROW(stan::math::neg_binomial_2_rng(2,
                 stan::math::positive_infinity(), rng),
                 std::domain_error);

  std::string error_msg;

  error_msg = "neg_binomial_2_rng: Location parameter "
              "divided by the precision parameter is "
              "inf, but must be finite!";
  try {
    stan::math::neg_binomial_2_rng(1e300, 1e-300, rng);
    FAIL() << "neg_binomial_2_rng should have thrown" << std::endl;
  } catch (const std::exception& e) {
    if (std::string(e.what()).find(error_msg) == std::string::npos)
      FAIL() << "Error message is different than expected" << std::endl
             << "EXPECTED: " << error_msg << std::endl
             << "FOUND: " << e.what() << std::endl;
    SUCCEED();
  }

  error_msg = "neg_binomial_2_rng: Random number that "
              "came from gamma distribution is";
  try {
    stan::math::neg_binomial_2_rng(1e10, 1e20, rng);
    FAIL() << "neg_binomial_2_rng should have thrown" << std::endl;
  } catch (const std::exception& e) {
    if (std::string(e.what()).find(error_msg) == std::string::npos)
      FAIL() << "Error message is different than expected" << std::endl
             << "EXPECTED: " << error_msg << std::endl
             << "FOUND: " << e.what() << std::endl;
    SUCCEED();
  }
}

TEST(ProbDistributionsNegBinomial, chiSquareGoodnessFitTest) {
  boost::random::mt19937 rng;
  int N = 1000;
  int K = boost::math::round(2 * std::pow(N, 0.4));
  boost::math::negative_binomial_distribution<>dist (1.1, 1.1/(1.1+2.4));
  boost::math::chi_squared mydist(K-1);

  int loc[K - 1];
  for(int i = 1; i < K; i++)
    loc[i - 1] = i - 1;

  int count = 0;
  double bin [K];
  double expect [K];
  for(int i = 0 ; i < K; i++)  {
    bin[i] = 0;
    expect[i] = N * pdf(dist, i);
  }
  expect[K-1] = N * (1 - cdf(dist, K - 1));

  while (count < N) {
    int a = stan::math::neg_binomial_2_rng(2.4, 1.1, rng);
    int i = 0;
    while (i < K-1 && a > loc[i])
      ++i;
    ++bin[i];
    count++;
   }

  double chi = 0;

  for(int j = 0; j < K; j++)
    chi += ((bin[j] - expect[j]) * (bin[j] - expect[j]) / expect[j]);

  EXPECT_TRUE(chi < quantile(complement(mydist, 1e-6)));
}

TEST(ProbDistributionsNegBinomial, chiSquareGoodnessFitTest2) {
  boost::random::mt19937 rng;
  int N = 1000;
  int K = boost::math::round(2 * std::pow(N, 0.4));
  boost::math::negative_binomial_distribution<>dist (0.6, 0.6/(0.6+2.4));
  boost::math::chi_squared mydist(K-1);

  int loc[K - 1];
  for(int i = 1; i < K; i++)
    loc[i - 1] = i - 1;

  int count = 0;
  double bin [K];
  double expect [K];
  for(int i = 0 ; i < K; i++)  {
    bin[i] = 0;
    expect[i] = N * pdf(dist, i);
  }
  expect[K-1] = N * (1 - cdf(dist, K - 1));

  while (count < N) {
    int a = stan::math::neg_binomial_2_rng(2.4, 0.6, rng);
    int i = 0;
    while (i < K-1 && a > loc[i])
      ++i;
    ++bin[i];
    count++;
   }

  double chi = 0;

  for(int j = 0; j < K; j++)
    chi += ((bin[j] - expect[j]) * (bin[j] - expect[j]) / expect[j]);

  EXPECT_TRUE(chi < quantile(complement(mydist, 1e-6)));
}

TEST(ProbDistributionsNegBinomial, chiSquareGoodnessFitTest3) {
  boost::random::mt19937 rng;
  int N = 1000;
  int K = boost::math::round(2 * std::pow(N, 0.4));
  boost::math::negative_binomial_distribution<>dist (30, 30/(30+60.4));
  boost::math::chi_squared mydist(K-1);

  int loc[K - 1];
  for(int i = 1; i < K; i++)
    loc[i - 1] = i - 1;

  int count = 0;
  double bin [K];
  double expect [K];
  for(int i = 0 ; i < K; i++)  {
    bin[i] = 0;
    expect[i] = N * pdf(dist, i);
  }
  expect[K-1] = N * (1 - cdf(dist, K - 1));

  while (count < N) {
    int a = stan::math::neg_binomial_2_rng(60.4, 30, rng);
    int i = 0;
    while (i < K-1 && a > loc[i])
      ++i;
    ++bin[i];
    count++;
   }

  double chi = 0;

  for(int j = 0; j < K; j++)
    chi += ((bin[j] - expect[j]) * (bin[j] - expect[j]) / expect[j]);

  EXPECT_TRUE(chi < quantile(complement(mydist, 1e-6)));
}

TEST(ProbDistributionsNegBinomial, chiSquareGoodnessFitTest4) {
  boost::random::mt19937 rng;
  int N = 1000;
  int K = boost::math::round(2 * std::pow(N, 0.4));
  boost::math::negative_binomial_distribution<>dist (80, 80/(80+30.4));
  boost::math::chi_squared mydist(K-1);

  int loc[K - 1];
  for(int i = 1; i < K; i++)
    loc[i - 1] = i - 1;

  int count = 0;
  double bin [K];
  double expect [K];
  for(int i = 0 ; i < K; i++)  {
    bin[i] = 0;
    expect[i] = N * pdf(dist, i);
  }
  expect[K-1] = N * (1 - cdf(dist, K - 1));

  while (count < N) {
    int a = stan::math::neg_binomial_2_rng(30.4, 80, rng);
    int i = 0;
    while (i < K-1 && a > loc[i])
      ++i;
    ++bin[i];
    count++;
   }

  double chi = 0;

  for(int j = 0; j < K; j++)
    chi += ((bin[j] - expect[j]) * (bin[j] - expect[j]) / expect[j]);

  EXPECT_TRUE(chi < quantile(complement(mydist, 1e-6)));
}

TEST(ProbDistributionsNegBinomial, extreme_values) {
  int N = 100;
  double mu = 8;
  double phi = 1e12;
  for (int n = 0; n < 10; ++n) {
    phi *= 10;
    double logp = stan::math::neg_binomial_2_log<false>(N, mu, phi);
    EXPECT_TRUE(logp < 0);
  }
}


// __BEGIN_LICENSE__
// Copyright (C) 2006-2011 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

#include <gtest/gtest.h>
#include <vw/Math/MatrixSparseSkyline.h>

using namespace vw;
using namespace vw::math;

static const double DELTA = 1e-4;

// This is a fix for old versions of boost, the distribution uniform_01
// was implemented with expectations of working with the variate generator.
// instead we switch over to uniform_real as a backup solution in old boost.
#if BOOST_VERSION <= 103800

#include <boost/random/uniform_real.hpp>
#define UNIFORM01 boost::uniform_real

#else

#include <boost/random/uniform_01.hpp>
#define UNIFORM01 boost::uniform_01

#endif

#include <boost/random.hpp>

template <class GenT>
Vector<size_t> create_test_skyline(size_t size, size_t max_offset,
                                   GenT& generator ) {
  Vector<size_t> result(size);
  for ( size_t i = 0; i < size; ++i ) {
    ssize_t offset(i - size_t(generator()*(max_offset-1)) );
    if ( offset < 0 ) offset = 0;
    result[i] = static_cast<size_t>(offset);
  }
  return result;
}

template <class VectorT, class GenT>
void fill_vector(VectorT& b, GenT& generator) {
  for ( size_t i = 0; i < b.size(); ++i )
    b[i] = lround(generator()*100)+1;
}

template <class MatrixT, class GenT>
void fill_symmetric_matrix(MatrixT& A, Vector<size_t> const& skyline,
                           GenT& generator ) {
  for (size_t i = 0; i < A.rows(); ++i)
    for (size_t j = skyline[i]; j < std::min(i+1,A.cols()); ++j) {
      A(i,j) = lround(generator()*100)+1;
      A(j,i) = A(i,j);
    }
}

// Unoptimized LDLT decomposition
template <class MatrixT>
void ldl_decomposition(MatrixT& A) {
  VW_ASSERT(A.cols() == A.rows(), ArgumentErr() << "ldl_decomposition: argument must be square and symmetric.\n");
  for (size_t j = 0; j < A.cols(); ++j) {

    // Compute v(1:j)
    std::vector<double> v(j+1);
    v[j] = A(j,j);
    for (size_t i = 0; i < j; ++i) {
      v[i] = A(j,i)*A(i,i);
      v[j] -= A(j,i)*v[i];
    }

    // Store d(j) and compute L(j+1:n,j)
    A(j,j) = v[j];
    for (size_t i = j+1; i < A.cols(); ++i) {
      double row_sum = 0;
      for (size_t jj = 0; jj < j; ++jj)
        row_sum += A(i,jj)*v[jj];
      A(i,j) = ( A(i,j)-row_sum ) / v[j];
    }
  }
}

// Creating a Buckey Ball Sparse Skyline Matrix
template <class ElemT>
void fill_with_buckeyball(MatrixSparseSkyline<ElemT>& A) {
  A(2,1) = 1; A(5,1) = 1; A(6,1) = 1; A(3,2) = 1; A(11,2) = 1;
  A(2,3) = 1; A(4,3) = 1; A(16,3) = 1; A(5,4) = 1; A(21,4) = 1;
  A(26,5) = 1; A(7,6) = 1; A(10,6) = 1; A(8,7) = 1; A(30,7) = 1;
  A(9,8) = 1; A(42,8) = 1; A(10,9) = 1; A(38,9) = 1; A(12,10) = 1;
  A(12,11) = 1; A(15,11) = 1; A(13,12) = 1; A(14,13) = 1; A(37,13) = 1;
  A(15,14) = 1; A(33,14) = 1; A(17,15) = 1; A(17,16) = 1; A(20,16) = 1;
  A(18,17) = 1; A(19,18) = 1; A(32,18) = 1; A(20,19) = 1; A(53,19) = 1;
  A(22,20) = 1; A(22,21) = 1; A(25,21) = 1; A(23,22) = 1; A(24,23) = 1;
  A(52,23) = 1; A(25,24) = 1; A(48,24) = 1; A(27,25) = 1; A(27,26) = 1;
  A(30,26) = 1; A(28,27) = 1; A(29,28) = 1; A(47,28) = 1; A(30,29) = 1;
  A(43,29) = 1; A(32,31) = 1; A(35,31) = 1; A(54,31) = 1; A(33,32) = 1;
  A(34,33) = 1; A(35,34) = 1; A(36,34) = 1; A(56,35) = 1; A(37,36) = 1;
  A(40,36) = 1; A(38,37) = 1; A(39,38) = 1; A(40,39) = 1; A(41,39) = 1;
  A(57,40) = 1; A(42,41) = 1; A(45,41) = 1; A(43,42) = 1; A(44,43) = 1;
  A(45,44) = 1; A(46,44) = 1; A(58,45) = 1; A(47,46) = 1; A(50,46) = 1;
  A(48,47) = 1; A(49,48) = 1; A(50,49) = 1; A(51,49) = 1; A(59,50) = 1;
  A(52,51) = 1; A(55,51) = 1; A(53,52) = 1; A(54,53) = 1; A(55,54) = 1;
  A(60,55) = 1; A(57,56) = 1; A(60,56) = 1; A(58,57) = 1; A(59,58) = 1;
  A(60,59) = 1;
}

TEST(SparseSkyline, Creation ) {
  MatrixSparseSkyline<double> sparse(4);
  sparse(0,0) = 1;
  sparse(0,1) = 2;
  sparse(0,2) = 3;
  sparse(0,3) = 4;
  sparse(1,1) = 5;
  EXPECT_EQ( sparse(1,0), sparse(0,1) );
  EXPECT_EQ( sparse(2,0), sparse(0,2) );
  EXPECT_EQ( sparse(3,0), 4 );

  Vector<double> cv = select_col(sparse,0);
  ASSERT_EQ( 4u, cv.size() );
  EXPECT_EQ( 1, cv(0) );
  EXPECT_EQ( 2, cv(1) );
  EXPECT_EQ( 3, cv(2) );
  EXPECT_EQ( 4, cv(3) );

  cv = select_col(sparse,1);
  ASSERT_EQ( 4u, cv.size() );
  EXPECT_EQ( 2, cv(0) );
  EXPECT_EQ( 5, cv(1) );
  EXPECT_EQ( 0, cv(2) );
  EXPECT_EQ( 0, cv(3) );

  Vector<double> rv = select_row(sparse,2);
  ASSERT_EQ( 4u, rv.size() );
  EXPECT_EQ( 3, rv(0) );
  EXPECT_EQ( 0, rv(1) );
  EXPECT_EQ( 0, rv(2) );
  EXPECT_EQ( 0, rv(3) );
}

TEST(SparseSkyline, LDL_decomp_correctness) {
  size_t N = 50;
  size_t S = 10;
  boost::mt19937 random_gen(86);
  typedef boost::variate_generator<boost::mt19937&,UNIFORM01<> > vargen_type;
  vargen_type generator( random_gen, UNIFORM01<>() );

  MatrixSparseSkyline<double> sparse_mat(N);

  Vector<size_t> test_skyline = create_test_skyline(N, S, generator);

  fill_symmetric_matrix(sparse_mat, test_skyline, generator);
  Matrix<double> nonsparse_mat = sparse_mat;
  MatrixSparseSkyline<double> original_sparse_mat = sparse_mat;

  sparse_ldl_decomposition(sparse_mat);
  for ( size_t i = 0; i < N; i++ )
    for ( size_t j = 0; j < N; j++ )
      if ( (sparse_mat(i,j) == 0)^(original_sparse_mat(i,j) == 0) )
        FAIL() << "Sparse structure was not preserved by sparse LDLT decomposition.\nIndex(" << i << "," << j << ") is " << sparse_mat(i,j) << " and " << original_sparse_mat(i,j) << "\n";

  ldl_decomposition(nonsparse_mat);

  for ( size_t i = 0; i < N; i++ )
    for ( size_t j = 0; j < i; j++ )
      EXPECT_NEAR(sparse_mat(i,j),nonsparse_mat(i,j),DELTA);
}

TEST(SparseSkyline, LDL_decomp_scalability) {
  size_t N = 5000;
  size_t S = 150;
  boost::mt19937 random_gen(86);
  typedef boost::variate_generator<boost::mt19937&,UNIFORM01<> > vargen_type;
  vargen_type generator( random_gen, UNIFORM01<>() );

  MatrixSparseSkyline<double> sparse_mat(N,N);
  MatrixSparseSkyline<double> original_sparse_mat(N,N);
  Vector<size_t> test_skyline = create_test_skyline(N,S,generator);

  fill_symmetric_matrix(sparse_mat, test_skyline, generator);
  original_sparse_mat = sparse_mat;

  sparse_ldl_decomposition(sparse_mat);
  for ( size_t i = 0; i < N; i++ )
    for ( size_t j = 0; j < i; j++ )
      if ( (sparse_mat(i,j) == 0)^(original_sparse_mat(i,j) == 0) )
        FAIL() << "Sparse structure was not preserved by sparse LDLT decomposition.\nIndex(" << i << "," << j << ") is " << sparse_mat(i,j) << " and " << original_sparse_mat(i,j) << "\n";
}

TEST(SparseSkyline, LDL_solve) {
  size_t N = 50;
  size_t S = 10;
  boost::mt19937 random_gen(42);
  typedef boost::variate_generator<boost::mt19937&,UNIFORM01<> > vargen_type;
  vargen_type generator( random_gen, UNIFORM01<>() );

  Matrix<double> A_nonsparse(N,N);
  MatrixSparseSkyline<double> A_sparse(N,N);
  Vector<size_t> test_skyline = create_test_skyline(N,S,generator);

  fill_symmetric_matrix(A_sparse, test_skyline, generator);
  MatrixSparseSkyline<double> A_sparse_original = A_sparse;
  EXPECT_EQ( A_sparse.cols(), A_sparse_original.cols() );
  EXPECT_EQ( A_sparse.rows(), A_sparse_original.rows() );
  A_nonsparse = A_sparse;

  // Create a vector to solve against
  Vector<double> b(N);
  fill_vector(b,generator);

  // Solve using normal
  Vector<double> x_nonsparse = inverse(A_nonsparse)*b;

  // Sparse Version
  Vector<double> x_sparse = sparse_solve(A_sparse, b);

  for ( size_t i = 0; i < N; i++ )
    EXPECT_NEAR( x_nonsparse[i], x_sparse[i], DELTA );

  // Back checking (also showing off that multiplication is possible)
  Vector<double> b_prime = A_sparse_original*x_sparse;
  for ( size_t i = 0; i < N; i++ )
    EXPECT_NEAR( b_prime[i], b[i], DELTA );
}

TEST(SparseSkyline, LDL_solve_scalability) {
  int N = 1000;
  int S = 100;
  boost::mt19937 random_gen(86);
  typedef boost::variate_generator<boost::mt19937&,UNIFORM01<> > vargen_type;
  vargen_type generator( random_gen, UNIFORM01<>() );

  MatrixSparseSkyline<double> A_sparse(N,N);
  Vector<size_t> test_skyline = create_test_skyline(N,S,generator);
  fill_symmetric_matrix(A_sparse, test_skyline, generator);

  // Create a vector to solve against
  Vector<double> b(A_sparse.cols());
  fill_vector(b,generator);

  // Solving for X
  Vector<double> x_result = sparse_solve(A_sparse, b);
}

// Rearrangement data types
TEST(SparseSkyline, VectorReorganize) {
  std::vector<size_t> lookup;
  lookup.push_back(2);
  lookup.push_back(0);
  lookup.push_back(1);
  Vector3f vec(1,2,3);

  VectorReorganize<Vector3f> rvec(vec, lookup);
  EXPECT_EQ(rvec(0),3);
  EXPECT_EQ(rvec(1),1);
  EXPECT_EQ(rvec(2),2);

  // Testing a convenience function
  Vector3f nrvec = reorganize(vec, lookup);
  EXPECT_EQ(nrvec(0),3);
  EXPECT_EQ(nrvec(1),1);
  EXPECT_EQ(nrvec(2),2);

  // Reording back into self
  nrvec = reorganize(nrvec, rvec.inverse());
  for ( size_t i = 0; i < nrvec.size(); i++ )
    EXPECT_EQ(nrvec[i],vec[i]);
}

TEST(SparseSkyline, VectorLargeReorganize) {
  // Different from above in that this will actually invoke the
  // VectorAssignImpl that calls std::copy and uses the iterators.
  std::vector<size_t> lookup;
  Vector<float> lvec(10);
  for ( size_t i = 0, j = 9; i < 10; i++, j-- ) {
    lookup.push_back(j);
    lvec[i] = (i+2)*0.5f;
  }

  VectorReorganize<Vector<float> > rlvec2(lvec, lookup);
  for ( size_t i = 0; i < 10; i++ )
    EXPECT_EQ( rlvec2[i], lvec[lookup[i]] );

  Vector<float> rlvec3 = rlvec2;
  for ( size_t i = 0; i < 10; i++ )
    EXPECT_EQ( rlvec3[i], lvec[lookup[i]] );
}

TEST(SparseSkyline, MatrixReorganize) {
  std::vector<size_t> lookup;
  lookup.push_back(2);
  lookup.push_back(0);
  lookup.push_back(1);
  Matrix3x3f mat;
  mat(0,0) = 1;
  mat(1,1) = 2;
  mat(2,2) = 3;
  mat(0,1) = 4;
  mat(0,2) = 6;

  MatrixReorganize<Matrix3x3f> rmat(mat, lookup);
  EXPECT_EQ(rmat(0,0), 3);
  EXPECT_EQ(rmat(1,2), 4);
  EXPECT_EQ(rmat(1,0), 6);

  // Testing convenience function
  Matrix3x3f nrmat = reorganize(mat, lookup);
  EXPECT_EQ(nrmat(0,0), 3);
  EXPECT_EQ(nrmat(1,2), 4);
  EXPECT_EQ(nrmat(1,0), 6);

  // Reordering back into self
  nrmat = reorganize(nrmat, rmat.inverse());
  for ( size_t i = 0; i < nrmat.cols(); i++ )
    EXPECT_EQ(nrmat(i,i),mat(i,i));
}

TEST(SparseSkyline, MatrixLargeReorganize) {
  std::vector<size_t> lookup;
  Matrix<float> mat(10,10);
  for ( size_t i = 0, j = 9; i < 10; i++, j-- ) {
    lookup.push_back(j);
    for ( size_t k = 0; k <= i; k++ ) {
      mat(i,k) = i*k*0.5+2;
      mat(k,i) = mat(i,k);
    }
  }

  MatrixReorganize<Matrix<float> > rmat(mat, lookup);
  for ( size_t i = 0; i < 10; i++ )
    EXPECT_EQ( rmat(i,i), mat(lookup[i],lookup[i]) );

  Matrix<float> rmat_copy = rmat;
  for ( size_t i = 0; i < 10; i++ )
    EXPECT_EQ( rmat_copy(i,i), mat(lookup[i],lookup[i]) );
}

TEST(SparseSkyline, CuthillMcKee) {
  MatrixSparseSkyline<float> sparse(61);
  fill_with_buckeyball(sparse);

  // Solving for ordering
  std::vector<size_t> new_ordering = cuthill_mckee_ordering(sparse,1);

  // This ordering comes from the MATLAB cuthill mckee example
  size_t ideal_order[61] = {1,6,2,5,7,26,30,10,11,12,3,4,8,27,29,9,15,13,16,17,21,25,42,28,43,38,37,14,20,18,22,24,41,47,44,39,36,33,19,32,23,48,45,46,40,34,53,31,52,49,58,50,57,35,54,51,59,56,55,60,0};

  for ( size_t i = 0; i < new_ordering.size(); i++ )
    EXPECT_EQ(ideal_order[i], new_ordering[i]);
}

TEST(SparseSkyline, ReorderOptimization) {
  MatrixSparseSkyline<float> sparse(61);
  fill_with_buckeyball(sparse);
  sparse(1,0) = 2.0;
  sparse(12,0) = 1.0;
  sparse(33,0) = 5.0;
  // Insuring positive definite
  for ( size_t i = 0; i < 61; i++ ) {
    sparse(i,i) += 5;
    if ( i > 0 )
      sparse(i-1,i) += 1;
  }
  Matrix<float> common = sparse;
  Vector<float> x_ideal(61);
  for ( size_t i = 0; i < 61; i++ )
    x_ideal[i] = float((i+1)*0.5);

  Vector<float> b = common*x_ideal;

  // Standard unsparse unoptimized method
  Vector<double> x_unsparse = inverse(common)*b;
  EXPECT_EQ( 61u, x_unsparse.size() );

  // Sparse Unoptimized method
  MatrixSparseSkyline<float> sparse_copy = sparse;
  Vector<double> x_sparse = sparse_solve(sparse_copy,b);
  EXPECT_EQ( 61u, x_sparse.size() );

  // Optimized method;
  std::vector<size_t> new_ordering = cuthill_mckee_ordering(sparse,1);
  math::MatrixReorganize<MatrixSparseSkyline<float> > rsparse( sparse, new_ordering);

  Vector<size_t> new_skyline = solve_for_skyline(rsparse);
  Vector<double> x_reorder = sparse_solve( rsparse,
                                           reorganize(b,new_ordering),
                                            new_skyline );
  x_reorder = reorganize(x_reorder, rsparse.inverse() );
  EXPECT_EQ( 61u, x_reorder.size() );

  //std::cout << "Unsparse Result: " << x_unsparse << "\n";
  //std::cout << "StandardSparseR: " << x_sparse << "\n";
  //std::cout << "Reorder SparseR: " << x_reorder << "\n";

  // Make sure old methods still work
  for ( size_t i = 0; i < 61; i++ )
    EXPECT_NEAR( x_unsparse[i], x_sparse[i], DELTA );

  // Does reorganized method work?
  for ( size_t i = 0; i < 61; i++ )
    EXPECT_NEAR( x_unsparse[i], x_reorder[i], DELTA );
}

TEST(SparseSkyline, ReorderConstCorrectness) {
  // Checking that SparseSkyline works
  math::MatrixSparseSkyline<double> sparse(1,1);
  ASSERT_EQ( 1u, sparse.cols() );
  ASSERT_EQ( 1u, sparse.rows() );
  sparse(0,0) = 1;
  ASSERT_EQ( 1, sparse(0,0) );
  Vector<size_t> skyline = sparse.skyline();
  ASSERT_EQ( 1u, skyline.size() );
  EXPECT_EQ( 0u, skyline[0] );

  { // Applying second layers that could goof const
    math::MatrixTranspose<math::MatrixSparseSkyline<double> > tsparse( sparse );
    ASSERT_EQ( 1u, tsparse.cols() );
    ASSERT_EQ( 1u, tsparse.rows() );
    EXPECT_EQ( 1, tsparse(0,0) );
    const math::MatrixTranspose<math::MatrixSparseSkyline<double> > ctsparse( sparse );
    ASSERT_EQ( 1u, ctsparse.cols() );
    ASSERT_EQ( 1u, ctsparse.rows() );
    EXPECT_EQ( 1, ctsparse(0,0) );

    // Another example of the error
    std::vector<size_t> reorder(1);
    reorder[0] = 0;
    math::MatrixReorganize<math::MatrixSparseSkyline<double> > rsparse( sparse, reorder );
    ASSERT_EQ( 1u, rsparse.cols() );
    ASSERT_EQ( 1u, rsparse.rows() );
    EXPECT_EQ( 1, rsparse(0,0) );
    const math::MatrixReorganize<math::MatrixSparseSkyline<double> > crsparse( sparse, reorder );
    ASSERT_EQ( 1u, crsparse.cols() );
    ASSERT_EQ( 1u, crsparse.rows() );
    EXPECT_EQ( 1, crsparse(0,0) );

  }
}