#ifndef TISSOT_CONVERTER_HPP
#define TISSOT_CONVERTER_HPP

// Tissot, converts projecton source code (Proj4) to Boost.Geometry
// (or potentially other source code)
//
// Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
//
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include "tissot_structs.hpp"
#include "tissot_util.hpp"

#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>

#include <sstream>

namespace boost { namespace geometry { namespace proj4converter
{

class proj4_converter_cpp_bg
{
public :
    proj4_converter_cpp_bg(projection_properties& prop)
    : m_prop(prop)
    {
    }

    void convert()
    {
        for_each_line(&proj4_converter_cpp_bg::replace_macros);
        for_each_line(&proj4_converter_cpp_bg::remove_fwd_inv);
        replace_setup();
        replace_parameters();
        replace_parameter_usage();
        replace_return();
        replace_xy_ll();

        fix_lastlines();
        replace_apple_macros();
        replace_ctx();
        replace_struct_parameters();
        determine_const_types();

        for_each_line(&proj4_converter_cpp_bg::replace_exceptions);
        trim(); // we do this twice
    }

    // Called after converting specific projections
    void post_convert()
    {
        replace_all_functions();
    }

    void trim()
    {
        for_each_line(&proj4_converter_cpp_bg::remove_empty_preceding_lines);
        for_each_line(&proj4_converter_cpp_bg::remove_empty_trailing_lines);
        for_each_line(&proj4_converter_cpp_bg::remove_redundant_returns);
        for_each_line(&proj4_converter_cpp_bg::trim_right);
    }

    void scan()
    {
        check_unused_parameters();
        for_each_line(&proj4_converter_cpp_bg::scan_includes);
    }

private :

    void for_each_line(void (proj4_converter_cpp_bg::*ptr)(std::vector<std::string>& lines))
    {
        (this->*ptr)(m_prop.proj_parameters);
        (this->*ptr)(m_prop.inlined_functions);
        (this->*ptr)(m_prop.setup_functions);
        BOOST_FOREACH(derived& der, m_prop.derived_projections)
        {
            (this->*ptr)(der.constructor_lines);
        }
        BOOST_FOREACH(projection& proj, m_prop.projections)
        {
            (this->*ptr)(proj.lines);
        }
    }

    void replace_all_functions()
    {
        for_each_line(&proj4_converter_cpp_bg::replace_functions);
        BOOST_FOREACH(macro_or_const& mc, m_prop.defined_macros)
        {
            replace_functions_in_line(mc.value);
        }
        BOOST_FOREACH(macro_or_const& mc, m_prop.defined_consts)
        {
            replace_functions_in_line(mc.value);
        }
    }

    void check_unused_parameters()
    {
        std::vector<std::string>& lines = m_prop.setup_functions;
        if (lines.empty())
        {
            return;
        }

        bool par_used = false, proj_parm_used = false;
        BOOST_FOREACH(std::string& line, lines)
        {
            std::string trimmed = boost::trim_copy(line);
            if (! boost::starts_with(trimmed, "//"))
            {
                if (boost::contains(line, "par."))
                {
                    par_used = true;
                }
                if (boost::contains(line, "proj_parm."))
                {
                    proj_parm_used = true;
                }
            }
        }
        if (! proj_parm_used && ! m_prop.proj_parameters.empty())
        {
            lines.insert(lines.begin(), tab1 + "boost::ignore_unused(proj_parm);");
        }
        if (! par_used)
        {
            lines.insert(lines.begin(), tab1 + "boost::ignore_unused(par);");
        }
    }

    void scan_includes(std::vector<std::string>& lines)
    {
        BOOST_FOREACH(std::string& line, lines)
        {
            if (boost::contains(line, "aasin")
                || boost::contains(line, "aacos")
                || boost::contains(line, "aatan2")
                || boost::contains(line, "asqrt"))
            {
                m_prop.extra_impl_includes.insert("aasincos.hpp");
            }
            if (boost::contains(line, "enfn"))
            {
                m_prop.extra_impl_includes.insert("pj_mlfn.hpp");
            }
            if (boost::contains(line, "pj_auth"))
            {
                m_prop.extra_impl_includes.insert("pj_auth.hpp");
            }
            if (boost::contains(line, "mdist"))
            {
                m_prop.extra_impl_includes.insert("proj_mdist.hpp");
            }
            if (boost::contains(line, "pj_phi2"))
            {
                m_prop.extra_impl_includes.insert("pj_phi2.hpp");
            }
            if (boost::contains(line, "pj_msfn"))
            {
                m_prop.extra_impl_includes.insert("pj_msfn.hpp");
            }
            if (boost::contains(line, "pj_qsfn"))
            {
                m_prop.extra_impl_includes.insert("pj_qsfn.hpp");
            }
            if (boost::contains(line, "pj_tsfn"))
            {
                m_prop.extra_impl_includes.insert("pj_tsfn.hpp");
            }
            if (boost::contains(line, "pj_zpoly"))
            {
                m_prop.extra_impl_includes.insert("pj_zpoly1.hpp");
            }

            // Boost
            if (boost::contains(line, "hypot"))
            {
                m_prop.extra_includes.insert("boost/math/special_functions/hypot.hpp");
            }
            if (boost::contains(line, "ignore_unused"))
            {
                m_prop.extra_includes.insert("boost/core/ignore_unused.hpp");
            }
            if (boost::contains(line, "shared_ptr"))
            {
                m_prop.extra_includes.insert("boost/shared_ptr.hpp");
            }

            // Geometry
            if (boost::contains(line, "::d2r<")
                || boost::contains(line, "::r2d<")
                || boost::contains(line, "::pi<")
                || boost::contains(line, "::two_pi<")
                || boost::contains(line, "::half_pi<")
            )
            {
                m_prop.extra_includes.insert("boost/geometry/util/math.hpp");
            }

            // Std
            if (boost::contains(line, "sprintf"))
            {
                m_prop.extra_includes.insert("cstdio");
            }
        }
    }

    void remove_empty_preceding_lines(std::vector<std::string>& lines)
    {
        while(! lines.empty() && boost::trim_copy(lines.front()).empty())
        {
            lines.erase(lines.begin());
        }
    }

    void remove_empty_trailing_lines(std::vector<std::string>& lines)
    {
        while(! lines.empty() && boost::trim_copy(lines.back()).empty())
        {
            lines.erase(lines.begin() + lines.size());
        }
    }

    void remove_fwd_inv(std::string& line, std::string const& direction)
    {
        std::string::size_type const pos = line.find("P->" + direction);
        if (pos == std::string::npos)
        {
            return;
        }
        std::string::size_type const pos2 = line.find(";", pos);
        if (pos2 != std::string::npos)
        {
            line.erase(pos, pos2 + 1);
        }
    }

    void remove_fwd_inv(std::vector<std::string>& lines)
    {
        // Remove the par.fwd; and par.inv statements

        std::vector<int> to_erase;
        int index = 0;
        BOOST_FOREACH(std::string& line, lines)
        {
            if (boost::contains(line, "P->"))
            {
                remove_fwd_inv(line, "fwd");
                remove_fwd_inv(line, "inv");
                remove_fwd_inv(line, "spc");
                boost::trim_right(line);
                if (boost::trim_copy(line).empty())
                {
                    // remove it
                    to_erase.push_back(index);
                }
            }
            index++;
        }

        // Remove stored empty lines, backwards
        for (std::vector<int>::const_reverse_iterator it =
            to_erase.rbegin(); it != to_erase.rend(); ++it)
        {
            int const index = *it;
            lines.erase(lines.begin() + index);
        }
    }

    void replace_exceptions(std::vector<std::string>& lines)
    {
        BOOST_FOREACH(std::string& line, lines)
        {
            boost::replace_all(line, "F_ERROR", "throw proj_exception();");
            boost::replace_all(line, "I_ERROR", "throw proj_exception();");
            boost::replace_all(line, "E_ERROR_0", "throw proj_exception(0)");
            boost::replace_all(line, "exit(EXIT_FAILURE)", "throw proj_exception()");

            if (boost::contains(line, "E_ERROR"))
            {
                boost::replace_all(line, "E_ERROR", "throw proj_exception");
                if (boost::ends_with(line, ")"))
                {
                    boost::replace_last(line, ")", ");");
                }
            }
            if (boost::contains(line, "pj_ctx_set_errno"))
            {
                boost::replace_all(line, "this->m_par.ctx,", "");
                boost::replace_all(line, "pj_ctx_set_errno", "throw proj_exception");
            }
        }
    }

    void replace_parameter_usage(std::vector<std::string>& lines, std::string const& prefix)
    {
        std::vector<std::string> proj_parameter_names = extract_names(m_prop.proj_parameters);
        BOOST_FOREACH(std::string& line, lines)
        {
            if (boost::contains(line, "->"))
            {
                boost::replace_all(line, "fac->", prefix + "fac.");
                boost::replace_all(line, "P->", prefix + "par.");
                boost::replace_all(line, "P -> ", prefix + "par.");
                // Refer to project-specific parameters
                for (std::vector<std::string>::const_iterator it = proj_parameter_names.begin();
                    it != proj_parameter_names.end();
                    ++it)
                {
                    boost::replace_all(line, "par." + *it, "proj_parm." + *it);
                }
                // If the projection-parameter was a "p", it has replaced par.params/par.phi0 as well... Correct this
                boost::replace_all(line, "proj_parm.params", "par.params");
                boost::replace_all(line, "proj_parm.phi0", "par.phi0");
                // Same for lam0
                boost::replace_all(line, "proj_parm.lam0", "par.lam0");
            }
        }
    }

    void replace_parameter_usage()
    {
        replace_parameter_usage(m_prop.setup_functions, "");
        BOOST_FOREACH(derived& der, m_prop.derived_projections)
        {
            replace_parameter_usage(der.constructor_lines, "");
        }

        replace_parameter_usage(m_prop.inlined_functions, "this->m_");
        BOOST_FOREACH(projection& proj, m_prop.projections)
        {
            replace_parameter_usage(proj.lines, "this->m_");
        }
    }

    inline void replace_return(std::string& line, std::string const& var)
    {
        std::string copy(line);
        boost::replace_all(copy, "return", "");
        boost::trim_left_if(copy, boost::is_any_of(" ("));
        boost::trim_right_if(copy, boost::is_any_of(" );"));
        if (copy == var)
        {
            // Replace with empty return
            // (will be removed later, if it is the last line)
            line = tab3 + "return;";
        }
    }

    void replace_return(std::vector<std::string>& lines, std::string const& var)
    {
        BOOST_FOREACH(std::string& line, lines)
        {
            replace_return(line, var);
        }
    }

    void replace_return()
    {
        replace_return(m_prop.setup_functions, "P");
        BOOST_FOREACH(derived& der, m_prop.derived_projections)
        {
            replace_return(der.constructor_lines, "P");
        }

        BOOST_FOREACH(projection& proj, m_prop.projections)
        {
            replace_return(proj.lines, "xy");
            replace_return(proj.lines, "lp");
        }
    }

    void replace_setup()
    {
        boost::replace_all(m_prop.setup_function_line, "PJ *P", "Parameters& par");
        boost::replace_all(m_prop.setup_function_line, "static PJ *", "void");
        /*libproject*/boost::replace_all(m_prop.setup_function_line, "PROJ *P", "Parameters& par");
        /*libproject*/boost::replace_all(m_prop.setup_function_line, "static PROJ *", "void");

        // add void (in most/all cases 'static PJ *' was on the previous line)
        if (! boost::starts_with(m_prop.setup_function_line, "void"))
        {
            m_prop.setup_function_line = "void " + m_prop.setup_function_line;
        }
    }

    void replace_macros(std::vector<std::string>& lines)
    {
        BOOST_FOREACH(std::string& line, lines)
        {
            // Replace all "defines" containing -> with the defined constant
            for (std::vector<macro_or_const>::const_iterator it = m_prop.defined_parameters.begin();
            it != m_prop.defined_parameters.end();
            ++it)
            {
                boost::replace_all(line, it->name, it->value);
            }
        }
    }

    void trim_right(std::vector<std::string>& lines)
    {
        BOOST_FOREACH(std::string& line, lines)
        {
            boost::trim_right(line);
        }
    }

    void replace_pi(std::string& line)
    {
        // Save
        boost::replace_all(line, "TWO_HALFPI", "@two_halfpi");
        boost::replace_all(line, "PI_HALFPI", "@pi_halfpi");
        boost::replace_all(line, "FORTPI", "@fortpi");
        boost::replace_all(line, "TWORPI", "@tworpi");
        boost::replace_all(line, "HLFPI2", "@hlfpi2");
        boost::replace_all(line, "PI_DIV", "@pi_div");
        boost::replace_all(line, "PISQ", "@pisq");
        boost::replace_all(line, "TWO_D_PI", "@two_d_pi");
        boost::replace_all(line, "PI4_3", "@pi4_3");

        // Replace
        boost::replace_all(line, "M_PI / 2.0", "geometry::math::half_pi<double>()");
        boost::replace_all(line, "M_PI / 180.0", "geometry::math::d2r<double>()");
        boost::replace_all(line, "2 * M_PI", "geometry::math::two_pi<double>()");
        boost::replace_all(line, "2.0 * M_PI", "geometry::math::two_pi<double>()");

        boost::replace_all(line, "HALFPI", "geometry::math::half_pi<double>()");

        boost::replace_all(line, "M_PI_2", "geometry::math::two_pi<double>()");
        boost::replace_all(line, "M_PI", "geometry::math::pi<double>()");

        boost::replace_all(line, "TWOPI", "geometry::math::two_pi<double>()");
        boost::replace_all(line, "PI", "geometry::math::pi<double>()");

        // Restore
        boost::replace_all(line, "@two_halfpi", "TWO_HALFPI");
        boost::replace_all(line, "@pi_halfpi", "PI_HALFPI");
        boost::replace_all(line, "@fortpi", "FORTPI");
        boost::replace_all(line, "@tworpi", "TWORPI");
        boost::replace_all(line, "@hlfpi2", "HLFPI2");
        boost::replace_all(line, "@pi_div", "PI_DIV");
        boost::replace_all(line, "@pisq", "PISQ");
        boost::replace_all(line, "@two_d_pi", "TWO_D_PI");
        boost::replace_all(line, "@pi4_3", "PI4_3");
    }

    void replace_functions_in_line(std::string& line)
    {
        if (boost::starts_with(boost::trim_copy(line), "/*"))
        {
            return;
        }

        replace_pi(line);

        boost::replace_all(line, "DEG_TO_RAD", "geometry::math::d2r<double>()");
        boost::replace_all(line, "RAD_TO_DEG", "geometry::math::r2d<double>()");
        // Isea only:
        boost::replace_all(line, "DEG2RAD", "geometry::math::d2r<double>()");
        boost::replace_all(line, "RAD2DEG", "geometry::math::r2d<double>()");

        boost::replace_all(line, "hypot", "boost::math::hypot");

        // TODO: create something like: replace whole words, case sensitive:
        boost::replace_all(line, " cosl,", " cosl_,");
        boost::replace_all(line, " cosl)", " cosl_)");
        boost::replace_all(line, " cosl ", " cosl_ ");

        // BEGIN libproject:
        boost::replace_all(line, "proj_asin", "std::asin");
        boost::replace_all(line, "proj_acos", "std::acos");
        // END libproject
    }

    void replace_functions(std::vector<std::string>& lines)
    {
        BOOST_FOREACH(std::string& line, lines)
        {
            replace_functions_in_line(line);
        }
    }

    void replace_parameters()
    {
        std::vector<std::string>& lines = m_prop.inlined_functions;

        for (std::vector<std::string>::iterator it = lines.begin();
            it != lines.end(); ++it)
        {
            if (boost::contains(*it, "PJ *P"))
            {
                boost::replace_all(*it, "PJ *P", "Parameters& par");
                std::vector<std::string>::iterator start = it;
                std::string trimmed = boost::trim_copy(*(start - 1));
                if (boost::starts_with(trimmed, "inline ")
                    || boost::starts_with(trimmed, "static "))
                {
                    --start;
                }
                lines.insert(start, "template <typename Parameters>");
                ++it;
                if (it == lines.end())
                {
                    break;
                }
            }
        }
        BOOST_FOREACH(std::string& line, lines)
        {
            boost::replace_all(line, "P->", "par.");
        }
        BOOST_FOREACH(derived& der, m_prop.derived_projections)
        {
            BOOST_FOREACH(std::string& line, der.constructor_lines)
            {
                boost::replace_first(line, "setup(P", "setup(par");
            }
        }
    }

    void replace_xy_ll()
    {
        BOOST_FOREACH(projection& proj, m_prop.projections)
        {
            BOOST_FOREACH(std::string& line, proj.lines)
            {
                boost::replace_all(line, "xy.x", "xy_x");
                boost::replace_all(line, "xy.y", "xy_y");
                boost::replace_all(line, "lp.lam", "lp_lon");
                boost::replace_all(line, "lp.phi", "lp_lat");
            }
        }
    }

    void fix_lastlines()
    {
        // Implementations contain a last line including "}", redundant returns, etc

        BOOST_FOREACH(projection& proj, m_prop.projections)
        {
            // Erase single last closing curly brace (might be located before any comments)
            bool found = false;
            std::size_t index = proj.lines.size() - 1;
            for (std::vector<std::string>::reverse_iterator it = proj.lines.rbegin();
            it != proj.lines.rend(); ++it, index--)
            {
                if (boost::trim_copy(*it) == "}")
                {
                    proj.lines.erase(proj.lines.begin() + index);
                    found = true;
                    break;
                }
            }
            if (! found)
            {
                std::cerr << "Warning: no closing brace found" << std::endl;
            }

            remove_empty_trailing_lines(proj.lines);

            // Move last comments (if any)
            while(! proj.lines.empty()
            && (boost::starts_with(boost::trim_copy(proj.lines.back()), "/*")
            || boost::ends_with(boost::trim_copy(proj.lines.back()), "*/")
            || boost::starts_with(boost::trim_copy(proj.lines.back()), "*"))) // fragile!
            {
                proj.trailing_lines.push_front(proj.lines.back());
                proj.lines.erase(proj.lines.begin() + proj.lines.size());
            }
        }
    }

    void remove_redundant_returns(std::vector<std::string>& lines)
    {
        // Erase last return statement (if any)
        if(! lines.empty() && boost::trim_copy(lines.back()) == "return;")
        {
            lines.erase(lines.begin() + lines.size());
            return;
        }
        // Erase return statement before last curly brace
        std::size_t const n = lines.size();
        if (n > 2
            &&  boost::trim_copy(lines[n - 2]) == "return;"
            &&  boost::trim_copy(lines[n - 1]) == "}")
        {
            lines.erase(lines.begin() + n - 2);
        }
    }

    void remove_pair_of_brackets(std::string& line)
    {
        while (boost::contains(line, "(!(") && boost::contains(line, ")))"))
        {
            // Remove one pair of brackets
            boost::replace_all(line, "(!(", "(!");
            boost::replace_all(line, ")))", "))");
        }
    }

    void pass_parameter_instead_of_return(std::vector<std::string>& lines)
    {
        BOOST_FOREACH(std::string& line, lines)
        {
            if (boost::contains(line, "pj_enfn")
                || boost::contains(line, "proj_mdist_ini"))
            {
                // Pass "en" as parameter instead
                boost::replace_all(line, "proj_parm.en = ", "");
                boost::replace_all(line, "par.es", "par.es, proj_parm.en");
                remove_pair_of_brackets(line);
            }
            if (boost::contains(line, "pj_authset"))
            {
                // Pass "apa" as parameter instead
                boost::replace_all(line, "proj_parm.apa = ", "");
                boost::replace_all(line, "par.es", "par.es, proj_parm.apa");
                remove_pair_of_brackets(line);
            }
        }
    }

    void replace_struct_parameters()
    {
        // For all projections using EN/APA (enfn, auth, mdist)
        BOOST_FOREACH(std::string& line, m_prop.proj_parameters)
        {
            if (boost::contains(line, "double") && boost::contains(line, "*en"))
            {
                boost::replace_all(line, "*en", "en[EN_SIZE]");
            }
            if (boost::contains(line, "double") && boost::contains(line, "*apa"))
            {
                boost::replace_all(line, "*apa", "apa[APA_SIZE]");
            }
        }
        for_each_line(&proj4_converter_cpp_bg::pass_parameter_instead_of_return);
    }

    void determine_const_types()
    {
        BOOST_FOREACH(macro_or_const& con, m_prop.defined_consts)
        {
            // By default:
            con.type = "double";

            // Check if integer:
            {
                int value = atoi(con.value.c_str());
                std::ostringstream out;
                out << value;
                if (out.str() == con.value)
                {
                    con.type = "int";
                }
            }
        }
    }

    void replace_ctx(std::string const& function, std::string& line)
    {
        // remove space after
        boost::replace_all(line, "ctx, ", "ctx,");

        // remove space after opening bracket
        boost::replace_all(line, function + "( ", function + "(");

        std::string const replacement = function + "(";
        boost::replace_all(line, function + "(P->ctx,", replacement);
        boost::replace_all(line, function + "(par.ctx,", replacement);
        boost::replace_all(line, function + "(projCtx ctx,", replacement);
        boost::replace_all(line, function + "(proj_parm.ctx,", replacement);
        boost::replace_all(line, function + "(this->proj_parm.ctx,", replacement);
        boost::replace_all(line, function + "(this->m_proj_parm.ctx,", replacement);
        boost::replace_all(line, function + "(this->m_par.ctx,", replacement);
        boost::replace_all(line, function + "(ctx,", replacement);
    }

    void replace_ctx(std::string& line)
    {
        if (boost::contains(line, "ctx"))
        {
            replace_ctx("aasin", line);
            replace_ctx("aacos", line);
            replace_ctx("vect", line);
            replace_ctx("lc", line);
            replace_ctx("pj_inv_mlfn", line);
            replace_ctx("pj_param", line);
            replace_ctx("pj_phi2", line);
            replace_ctx("proj_inv_mdist", line);
            replace_ctx("proj_exception", line);
        }
    }

    void replace_ctx(std::vector<std::string>& lines)
    {
        // NEW since 4.8 or later, removes ctx from aasin/aacos because we throw an exception there
        BOOST_FOREACH(std::string& line, lines)
        {
            replace_ctx(line);
        }
    }

    void replace_ctx()
    {
        BOOST_FOREACH(derived& der, m_prop.derived_projections)
        {
            functor_nospaces_equals functor("pj.ctx=par.ctx;", "proj_parm.link->ctx=par.ctx;");
            std::vector<std::string>& lines = der.constructor_lines;
            lines.erase
                (
                    std::remove_if(lines.begin(), lines.end(), functor),
                    lines.end()
                );
            BOOST_FOREACH(std::string& line, lines)
            {
                replace_ctx(line);
            }
        }
        for_each_line(&proj4_converter_cpp_bg::replace_ctx);
    }

    void replace_apple_macros()
    {
        // CS/CN are internally used by Apple/XCode. We add an underscore

        for (std::vector<macro_or_const>::iterator it = m_prop.defined_consts.begin();
        it != m_prop.defined_consts.end(); ++it)
        {
            if (it->name == "CS") { it->name = "CS_"; }
            if (it->name == "CN") { it->name = "CN_"; }
        }

        BOOST_FOREACH(projection& proj, m_prop.projections)
        {
            BOOST_FOREACH(std::string& line, proj.lines)
            {
                std::vector<std::string> splitted;
                boost::split(splitted, line, boost::is_any_of(" ,:+-?;"), boost::token_compress_on);

                boost::replace_all(line, " CS ", " CS_ ");
                boost::replace_all(line, " CN ", " CN_ ");
                boost::replace_all(line, " CS)", " CS_)");
                boost::replace_all(line, " CN)", " CN_)");
                boost::replace_all(line, "(CS ", "(CS_ ");
                boost::replace_all(line, "(CN ", "(CN_ ");
            }
        }
    }

    projection_properties& m_prop;
};

}}} // namespace boost::geometry::proj4converter

#endif // TISSOT_CONVERTER_HPP


#include "precompiled.h"
#include "explicit_fields.h"
#include "auto_connecting_body.h"

#ifndef AUTOMATIC_PRECOMPILATION
#include <string>
#include <boost/optional.hpp>
#include <pretty_printer.h>
#endif

#include "logging.h"
#include "grid_fields.h"
#include "black_scholes.h"
#include "global_config.h"
#include "Math.h"
#include "finite_difference_weights_config.h"

namespace fipster { namespace explicit_fields {

	using namespace std;

	// #################################################
	// ##############   PHASE 1:			############
	// ##############   CONFIGURATION       ############
	// #################################################

	node_factory::node_factory(const ptree& pt,shared_const_objs_t objs)
		: pt(pt),objs(objs)
	{
		string stype(pt.get<string>("type"));
		if(stype == "CallPayoff") type = CallPayoff;
		else if(stype == "PutPayoff") type = PutPayoff;
		else if(stype == "BinaryCallPayoff") type = BinaryCallPayoff;
		else if(stype == "Put") type = Put;
		else if(stype == "Call") type = Call;
		else BOOST_THROW_EXCEPTION(runtime_error(stype+" is no valid Explicit Field Type"));
	}

	// #################################################
	// ##############   PHASE 3:			############
	// ##############   Node Bodies         ############
	// #################################################

	/** this class calculates the payoff of a Call/Put on the first
			assert
	 */
	template<type_t type>
	struct VanillaPayoff_t{
		double strike;

		/** constructor that reads in configuration */
		VanillaPayoff_t
		(const ptree& pt,const field_arg_t<>&,shared_const_objs_t)
			:strike(pt.get<double>("strike")){}

		/** core function, computing the value for a given state */
		double getValue(const state_t& state){
			/*if(state[0]>120){
				auto s2=state;
				s2[0]-=120;
				return getValue(s2);
			}
			if(state[0]>75) return 0;*/
			double a=30;
			if(type==BinaryCall) return state[0] >= strike ? 1 : 0;

			return max( (type==Call ? +1 : -1 )*(state[0]-strike-0*a),0.0);
// -
// 			max( (type==Call ? +1 : -1 )*(state[0]-strike-a),0.0)-
// 				max( (type==Call ? +1 : -1 )*(state[0]-strike-2*a),0.0)+
// 				max( (type==Call ? +1 : -1 )*(state[0]-strike-3*a),0.0);
		};
	};

	/** this class calculates the Black Scholes Values for a Call/Put on
	the geometric average of grid.D assets (using equal correlation, dividend and volatility)
	*/
	template<type_t type>
	struct BS_t{
		double strike,interest,volatility,correlation;
		black_scholes bs;

		/** constructor that reads in configuration */
		BS_t(const ptree& pt,const field_arg_t<>& arg,shared_const_objs_t objs){

			auto same_as = pt.get_optional<string>("sameAs");

			if(!same_as)
				bs=black_scholes(0,
					pt.get<double>("strike"),
					global_config::get().years_per_btick*(pt.get<btime>("expiration")-arg.time),
					pt.get<double>("interest"),
					pt.get<double>("volatility"),
					pt.get("dividend",0.0),
					pt.get("correlation",0.0),
					arg.grid->D);
			else{
				FIPSTER_THROW_EXCEPTION(runtime_error("not implemented"));
				/*
				//get the expectation value to take the interval from (if available)
				auto nf=dynamic_cast<const expectation_values::node_factory*>
					(objs->spacetime_fields.at(*same_as).get());
				if(!nf) FIPSTER_THROW_EXCEPTION(runtime_error("sameAs: "+*same_as+" is no expcetation_value"));
				auto fdw=objs->fdweights_configs.at(nf->config->fd_weights_s);
				fdw->
				*/
			}
		}

		/** core function, computing the value for a given state */
		double getValue(const state_t& state);
	};

	template<>
	double BS_t<Call>::getValue(const state_t& state)
	{
		return bs.call(geometric_average(state,bs.n));
	};

	template<>
	double BS_t<Put>::getValue(const state_t& state)
	{
		return bs.put(geometric_average(state,bs.n));
	};

	//#######################  Node body ##############################
	template<int sg, class result_t, class state_it,class core_t>//specify the sub-grid to work on
	struct explicit_field_body :
		auto_connecting_body<explicit_field_body<sg,result_t,state_it,core_t>,//body_t
							result_t>//result
	{
		core_t core;
		const btime time;
		grid_ptr grid;
		string meta_info;
		double factor;

		using result_sptr_t = typename explicit_field_body::result_sptr_t;
		using input_t = typename explicit_field_body::input_t;

		explicit_field_body(field_arg_t<> field_args,const ptree& pt,shared_const_objs_t objs)
			:core(core_t(pt,field_args,objs))
			,time(field_args.time)
			,grid(field_args.grid)
			,meta_info(pt.get<string>("type")
								 + " EXPL FL ("+toS(time)+") "+pt.get<string>("<xmlattr>.id"))
			,factor(pt.get("factor",1.0))
		{};

		//computation function #####################################################
		result_sptr_t operator()(const input_t&){
			// logging
			if(1)thread_logger()<<"EXPL FL ("<<time<<") | START"<<endl;
			auto result = make_shared<result_t>
				(meta_info);

			//allocate new space for the result on the specified Grid
			auto end = result->resize(*grid);

			for(state_it it(*grid); it!=end; ++it)
				result->at(it.index()) = factor*core.getValue(it.state());

	//tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(0.2));

			if(1)thread_logger()<<"EXPL FL| DONE"<<endl;
			return result;
		};

	};

	// #################################################
	// ##############   PHASE 2:			############
	// ##############   Node Factory        ############
	// #################################################

	boost::optional<node_factory::arg_t>
	node_factory::delegation(const arg_t& arg){
		if((type==CallPayoff || type==PutPayoff || type==BinaryCallPayoff) && arg.time != 0 ){
			arg_t a=arg; a.time = 0;
			return a;
		}
		return boost::none;
	}

	template<int sg,class result2_t,class iterator2_t,class sender2_t>
	sender2_t node_factory::selecting_setup( const node_factory::arg_t& arg)
	{
		switch(type){
		case CallPayoff:
			return create_node
				(explicit_field_body<sg,result2_t,iterator2_t,VanillaPayoff_t<Call> >(arg,pt,objs));
		case PutPayoff:
			return create_node
				(explicit_field_body<sg,result2_t,iterator2_t,VanillaPayoff_t<Put> >(arg,pt,objs));
		case BinaryCallPayoff:
			return create_node
				(explicit_field_body<sg,result2_t,iterator2_t,VanillaPayoff_t<BinaryCall> >(arg,pt,objs));
		case Call:
			return create_node
				(explicit_field_body<sg,result2_t,iterator2_t,BS_t<Call> >          (arg,pt,objs));
		case Put:
			return create_node
				(explicit_field_body<sg,result2_t,iterator2_t,BS_t<Put> >           (arg,pt,objs));
		default:
			BOOST_THROW_EXCEPTION(runtime_error(toS(type)+" is no valid Explicit Field Type"));
		}
	}

	// spacetime_field: create_node
	node_factory::sender_ptr node_factory::inner_setup(const arg_t& arg){
		auto a = delegation(arg); if(a) return get_node(*a);
		return selecting_setup<J_SGS,
								discretized_space_field<J_SGS>,
								grid_iterator<J_SGS,true>,
								sender_ptr
							>(arg);
	}

	node_factory::bv_sender_ptr
		node_factory::boundary_setup( const arg_t& arg ){
		auto a = delegation(arg); if(a) return bv_get_node(*a);
		return selecting_setup<J_SGS,
								boundary_field_t<J_SGS>,
								boundary_iterator<J_SGS-1,false,true>,
								bv_sender_ptr
							>(arg);
	}

}}


#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

#include <boost/program_options.hpp>
namespace po = boost::program_options;

bool pieces_well_placed(const std::string& first_row, bool no_adjacent_bishops)
{
    const int size = first_row.size();

    int first_bishop = -1;
    int first_archbishop_or_queen = -1;
    int num_rooks = 0;

    for (auto i = 0; i < size; i++) {
        switch (first_row[i]) {
        case 'B':
            if (first_bishop == -1)
                first_bishop = i;
            else if ((i - first_bishop) % 2 == 0)
                return false;  // same color bishops
            else if (no_adjacent_bishops && i - first_bishop == 1)
                return false;  // adjacent bishops (with --no-adjacent-bishops)
            break;

        case 'A':
        case 'Q':
            if (first_archbishop_or_queen == -1)
                first_archbishop_or_queen = i;
            else if ((i - first_archbishop_or_queen) % 2 == 0)
                return false;  // same color archbishop/queen
            break;

        case 'R':
            num_rooks += 1;
            break;

        case 'K':
            if (num_rooks != 1)
                return false;  // king not between the rooks
            break;
        }
    }

    return true;
}

bool second_row_protected(const std::string& first_row)
{
    const int size = first_row.size();

    std::vector<bool> second_row(size);

    for (auto i = 0; i < size; i++) {
        // knight component
        if (first_row[i] == 'A' || first_row[i] == 'C' || first_row[i] == 'N') {
            if (i-2 >= 0)
                second_row[i-2] = true;
            if (i+2 < size)
                second_row[i+2] = true;
        }

        // ferz component
        if (first_row[i] == 'A' || first_row[i] == 'B' ||
                first_row[i] == 'K' || first_row[i] == 'Q') {
            if (i-1 >= 0)
                second_row[i-1] = true;
            if (i+1 < size)
                second_row[i+1] = true;
        }

        // wazir component
        if (first_row[i] == 'C' || first_row[i] == 'R' ||
                first_row[i] == 'K' || first_row[i] == 'Q') {
            second_row[i] = true;
        }
    }

    return std::all_of(second_row.begin(), second_row.end(),
                       [](bool i) { return i; });
}

int main(int argc, char **argv)
{
    po::options_description desc("Allowed options");
    desc.add_options()
        ("help", "produce help message")
        ("no-adjacent-bishops", "prevent adjacent bishops")
    ;

    po::variables_map vm;
    try {
        po::store(po::parse_command_line(argc, argv, desc), vm);
        po::notify(vm);
    } catch (po::unknown_option& e) {
        std::cerr << desc;
        return 1;
    }

    if (vm.count("help")) {
        std::cerr << desc;
        return 1;
    }

    // piece abbreviations come from
    //  https://en.wikipedia.org/wiki/Capablanca_Random_Chess
    std::string first_row("RNABQKBCNR");
    std::sort(first_row.begin(), first_row.end());

    const bool no_adjacent_bishops = vm.count("no-adjacent-bishops");
    do {
        if (pieces_well_placed(first_row, no_adjacent_bishops) &&
                second_row_protected(first_row))
            std::cout << first_row << '\n';
    } while (std::next_permutation(first_row.begin(), first_row.end()));

    return 0;
}


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

/**
\file

\brief tutorial.cpp

\detailed
Basic tutorial using si units.

Output:
@verbatim

//[tutorial_output
F  = 2 N
dx = 2 m
E  = 4 J

V   = (12.5,0) V
I   = (3,4) A
Z   = (1.5,-2) Ohm
I*Z = (12.5,0) V
I*Z == V? true
//]

@endverbatim
**/

//[tutorial_code
#include <complex>
#include <iostream>

#include <boost/typeof/std/complex.hpp>

#include <boost/units/systems/si/energy.hpp>
#include <boost/units/systems/si/force.hpp>
#include <boost/units/systems/si/length.hpp>
#include <boost/units/systems/si/electric_potential.hpp>
#include <boost/units/systems/si/current.hpp>
#include <boost/units/systems/si/resistance.hpp>
#include <boost/units/systems/si/io.hpp>

using namespace boost::units;
using namespace boost::units::si;

quantity<energy>
work(const quantity<force>& F,const quantity<length>& dx)
{
    return F*dx;
}

int main()
{
    /// test calcuation of work
    quantity<force>     F(2.0*newton);
    quantity<length>    dx(2.0*meter);
    quantity<energy>    E(work(F,dx));

    std::cout << "F  = " << F << std::endl
              << "dx = " << dx << std::endl
              << "E  = " << E << std::endl
              << std::endl;

    /// check complex quantities
    typedef std::complex<double>    complex_type;

    quantity<electric_potential,complex_type> v = complex_type(12.5,0.0)*volts;
    quantity<current,complex_type>            i = complex_type(3.0,4.0)*amperes;
    quantity<resistance,complex_type>         z = complex_type(1.5,-2.0)*ohms;

    std::cout << "V   = " << v << std::endl
              << "I   = " << i << std::endl
              << "Z   = " << z << std::endl
              << "I*Z = " << i*z << std::endl
              << "I*Z == V? " << std::boolalpha << (i*z == v) << std::endl
              << std::endl;

    return 0;
}
//]


///////////////////////////////////////////////////////////////
//  Copyright Christopher Kormanyos 2002 - 2011.
//  Copyright 2011 John Maddock. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
//
// This work is based on an earlier work:
// "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations",
// in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469

#include "setup.hpp"
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>
#include <boost/math/special_functions/math_fwd.hpp>

#include "table_type.hpp"

#include "libs/math/test/powm1_sqrtp1m1_test.hpp"

//
// DESCRIPTION:
// ~~~~~~~~~~~~
//
// This file tests the functions log1p and expm1.  The accuracy tests
// use values generated with NTL::RR at 1000-bit precision
// and our generic versions of these functions.
//
// Note that when this file is first run on a new platform many of
// these tests will fail: the default accuracy is 1 epsilon which
// is too tight for most platforms.  In this situation you will
// need to cast a human eye over the error rates reported and make
// a judgement as to whether they are acceptable.  Either way please
// report the results to the Boost mailing list.  Acceptable rates of
// error are marked up below as a series of regular expressions that
// identify the compiler/stdlib/platform/data-type/test-data/test-function
// along with the maximum expected peek and RMS mean errors for that
// test.
//

void expected_results()
{
   //
   // Define the max and mean errors expected for
   // various compilers and platforms.
   //

   //
   // Catch all cases come last:
   //
   add_expected_result(
       ".*",                         // compiler
       ".*",                         // stdlib
       ".*",                         // platform
       ".*mpfr_float_backend<18>.*", // test type(s)
       ".*",                         // test data group
       ".*",                         // test function
       300,                          // Max Peek error
       50);                          // Max mean error
   add_expected_result(
       ".*", // compiler
       ".*", // stdlib
       ".*", // platform
       ".*", // test type(s)
       ".*", // test data group
       ".*", // test function
       15,   // Max Peek error
       5);   // Max mean error

   //
   // Finish off by printing out the compiler/stdlib/platform names,
   // we do this to make it easier to mark up expected error rates.
   //
   std::cout << "Tests run with " << BOOST_COMPILER << ", "
             << BOOST_STDLIB << ", " << BOOST_PLATFORM << std::endl;
}

template <class T>
void test(T t, const char* p)
{
   test_powm1_sqrtp1m1(t, p);
}

BOOST_AUTO_TEST_CASE(test_main)
{
   using namespace boost::multiprecision;
   expected_results();
   //
   // We test two different precisions:
   // 18 decimal digits triggers Boost.Math's 64-bit long double support.
   // 30 decimal digits triggers Boost.Math's 128-bit long double support.
   // 35 decimal digits triggers true arbitrary precision support.
   //
   ALL_SMALL_TESTS
}


#include <boost/optional.hpp>
#include <numeric>
#include <vector>

// 重み付きUnionFind
template <typename T = int>
class WeightedUnionFind {
	std::vector<size_t> parents;
	std::vector<size_t> rank;
	std::vector<T> diff_weight;
	T identity;

	public:
	WeightedUnionFind(size_t size, T id = static_cast<T>(0))
		: parents(size), rank(size, 0), diff_weight(size, id), identity(id) {
		std::iota(this->parents.begin(), this->parents.end(), 0);
	}

	// 併合
	bool merge(size_t a, size_t b, T w) {
		size_t ar = this->root(a);
		size_t br = this->root(b);
		if(ar == br) {
			return false;
		}
		T dw = w + this->weight(a) - this->weight(b);
		if(this->rank[ar] < this->rank[br]) {
			std::swap(ar, br);
			dw = -dw;
		}
		if(this->rank[ar] == this->rank[br]) {
			this->rank[ar]++;
		}
		this->diff_weight[br] = dw;
		this->parents[br] = ar;
		return true;
	}
	bool unite(size_t a, size_t b, T w) { return this->merge(a, b, w); }

	// 同集合か判定
	bool is_same(size_t a, size_t b) { return this->root(a) == this->root(b); }
	bool is_union(size_t a, size_t b) { return this->is_same(a, b); }

	// 二要素感の距離(同集合に属していなければboost::none)
	optional<T> diff(size_t a, size_t b) {
		if(!this->is_same(a, b)) {
			return nullopt;
		}
		return optional<T>(this->weight(b) - this->weight(a));
	}

	private:
	size_t root(int n) {
		if(this->parents[n] == n) {
			return n;
		}

		size_t r = this->root(this->parents[n]);
		this->diff_weight[n] += this->diff_weight[this->parents[n]];
		this->parents[n] = r;
		return r;
	}

	T weight(size_t n) {
		root(n);
		return this->diff_weight[n];
	}
};


#ifndef QUANTUM_STATE_HPP
#define QUANTUM_STATE_HPP

#include "tools/Utils.hpp"
#include <string>
#include <complex>
#include <Eigen/Dense>
#include <memory>
#include <ostream>
#include "Exceptions.hpp"
#include <cmath>

namespace MiniQbt{
    namespace Core{

        template<int registerSize>
        class QuantumState {
            private:
            bool invalid = false;
            Eigen::Matrix<std::complex<double>, Eigen::Dynamic, 1>  data;
            public:
            QuantumState(){
                this->data = Eigen::Matrix<std::complex<double>, Eigen::Dynamic, 1>::Zero(std::pow(2,registerSize));
                data(0,0) = 1;
                for(int i = 1; i < this->data.rows(); i++){
                    data(i,0) = 0;
                }
            }
            QuantumState(const Eigen::Matrix<std::complex<double>, Eigen::Dynamic, 1>& state){
                this->data = Eigen::Matrix<std::complex<double>, Eigen::Dynamic, 1>(state);
            }
            void setInvalid(){
                this->invalid = true;
            }

            bool usable() const{
                return !this->invalid;
            }
            Eigen::Matrix<std::complex<double>, Eigen::Dynamic, 1> getState() const{
                return this->data;
            }
            int getAmountOfPossibilities() const{
                return this->data.rows();
            }
            QuantumState& operator=(const Eigen::Matrix<std::complex<double>, Eigen::Dynamic, 1>& lh){
                this->data = lh;
                return *this;
            }

            QuantumState& operator*=(const Eigen::Matrix<std::complex<double>, Eigen::Dynamic, 1>& lh){
                this->data *= lh;
                return *this;
            }
            friend std::ostream& operator<<(std::ostream& os, const QuantumState& state){
                os << state.getState();
                return os;
            }

        };
    }
}

#endif


#ifndef PYTHONIC_SCIPY_SPECIAL_HANKEL2_HPP
#define PYTHONIC_SCIPY_SPECIAL_HANKEL2_HPP

#include "pythonic/include/scipy/special/hankel2.hpp"

#include "pythonic/types/ndarray.hpp"
#include "pythonic/types/complex.hpp"
#include "pythonic/utils/functor.hpp"
#include "pythonic/utils/numpy_traits.hpp"

#define BOOST_MATH_THREAD_LOCAL thread_local
#include <boost/math/special_functions/hankel.hpp>

PYTHONIC_NS_BEGIN

namespace scipy
{
  namespace special
  {
    namespace details
    {
      template <class T0, class T1>
      std::complex<double> hankel2(T0 x, T1 y)
      {
        return boost::math::cyl_hankel_2(x, y);
      }
    }

#define NUMPY_NARY_FUNC_NAME hankel2
#define NUMPY_NARY_FUNC_SYM details::hankel2
#include "pythonic/types/numpy_nary_expr.hpp"
  }
}
PYTHONIC_NS_END

#endif


/* Copyright 2017 The sfcpp Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#ifndef SRC_GEO_GEO_HPP_
#define SRC_GEO_GEO_HPP_

#include <Eigen/Dense>

namespace sfcpp {
namespace geo {

Eigen::Matrix4d linearTransform(double u1, double v1, double w1, double u2, double v2, double w2, double u3, double v3, double w3);
Eigen::Matrix4d linearTransform(Eigen::Vector3d u, Eigen::Vector3d v, Eigen::Vector3d w);
Eigen::Matrix4d linearTransform2D(double u1, double v1, double u2, double v2);

Eigen::Matrix4d zRotationTransform(double theta);
Eigen::Matrix4d rotationTransform(double theta, Eigen::Vector3d axis);

Eigen::Matrix4d translationTransform(Eigen::Vector3d v);
Eigen::Matrix4d translationTransform(double tx, double ty, double tz);

Eigen::Matrix4d scaleTransform(double sx, double sy, double sz);

extern Eigen::Matrix4d swapXYTransform;

} /* namespace geo */
} /* namespace utils */

#endif /* SRC_GEO_GEO_HPP_ */


/*******************************************************************************
 * Copyright (C) 2016 Advanced Micro Devices, Inc. All rights reserved.
 ******************************************************************************/

#include <iostream>
#include <sstream>
#include <functional>
#include <cmath>
#include <valarray>

#include <boost/program_options.hpp>
#include "rocfft/rocfft.h"
#include "./rider.h"

#include "./misc.h"

namespace po = boost::program_options;

//	This is used with the program_options class so that the user can type an integer on the command line
//	and we store into an enum varaible
template<class _Elem, class _Traits>
std::basic_istream<_Elem, _Traits> & operator>> (std::basic_istream<_Elem, _Traits> & stream, rocfft_array_type & atype)
{
	unsigned tmp;
	stream >> tmp;
	atype = rocfft_array_type(tmp);
	return stream;
}

// similarly for transform type
template<class _Elem, class _Traits>
std::basic_istream<_Elem, _Traits> & operator>> (std::basic_istream<_Elem, _Traits> & stream, rocfft_transform_type & ttype)
{
	unsigned tmp;
	stream >> tmp;
	ttype = rocfft_transform_type(tmp);
	return stream;
}

template < typename T >
int transform( size_t* lengths, const size_t *inStrides, const size_t *outStrides, size_t batchSize,
		const size_t *inOffset, const size_t *outOffset,
		rocfft_array_type inArrType, rocfft_array_type outArrType,
		rocfft_result_placement place, rocfft_precision precision, rocfft_transform_type transformType,
		double scale, bool packed, int deviceId, int platformId, bool printInfo, unsigned profile_count
		)
{
	//	Our command line does not specify what dimension FFT we wish to transform; we decode
	//	this from the lengths that the user specifies for X, Y, Z.  A length of one means that
	//	The user does not want that dimension.

	const size_t max_dimensions = 3;
	size_t strides[ 4 ];
	size_t o_strides[ 4 ];
	size_t fftVectorSize = 0;
	size_t fftVectorSizePadded = 0;
	size_t fftBatchSize = 0;
	size_t outfftVectorSize = 0;
	size_t outfftVectorSizePadded = 0;
	size_t outfftBatchSize = 0;
	size_t size_of_input_buffers_in_bytes = 0;
	size_t size_of_output_buffers_in_bytes = 0;
	unsigned number_of_output_buffers = 0;
	unsigned dim = 1;
	void *input_device_buffers [2] = { NULL, NULL };
	void *output_device_buffers[2] = { NULL, NULL };
	std::vector< int > device_id;

	if( lengths[ 1 ] > 1 )
	{
		dim	= 2;
	}
	if( lengths[ 2 ] > 1 )
	{
		dim	= 3;
	}

	strides[ 0 ] = inStrides[0];
	strides[ 1 ] = inStrides[1];
	strides[ 2 ] = inStrides[2];
	strides[ 3 ] = inStrides[3];

	o_strides[ 0 ] = outStrides[0];
	o_strides[ 1 ] = outStrides[1];
	o_strides[ 2 ] = outStrides[2];
	o_strides[ 3 ] = outStrides[3];

	fftVectorSize = lengths[0] * lengths[1] * lengths[2];
	fftVectorSizePadded = strides[3];
	fftBatchSize = fftVectorSizePadded * batchSize;

	size_t Nt = 1 + lengths[0]/2;

	if(place == rocfft_placement_inplace)
	{
		outfftVectorSize = fftVectorSize;
		outfftVectorSizePadded = fftVectorSizePadded;
		outfftBatchSize = fftBatchSize;
	}
	else
	{
		outfftVectorSize = lengths[0] * lengths[1] * lengths[2];
		outfftVectorSizePadded = o_strides[3];
		outfftBatchSize = outfftVectorSizePadded * batchSize;
	}

	// Real to complex case
	if( (inArrType == rocfft_array_type_real) || (outArrType == rocfft_array_type_real) )
	{
		fftVectorSizePadded = strides[3];
		fftBatchSize = fftVectorSizePadded * batchSize;

		outfftVectorSizePadded = o_strides[3];
		outfftBatchSize = outfftVectorSizePadded * batchSize;

		fftVectorSize = lengths[0] * lengths[1] * lengths[2];
		outfftVectorSize = fftVectorSize;

	}

	switch( outArrType )
	{
	case rocfft_array_type_complex_interleaved:
		number_of_output_buffers = 1;
		size_of_output_buffers_in_bytes = outfftBatchSize * sizeof( std::complex< T > );
		break;
	case rocfft_array_type_complex_planar:
		number_of_output_buffers = 2;
		size_of_output_buffers_in_bytes = outfftBatchSize * sizeof(T);
		break;
	case rocfft_array_type_hermitian_interleaved:
		number_of_output_buffers = 1;
		size_of_output_buffers_in_bytes = outfftBatchSize * sizeof( std::complex< T > );
		break;
	case rocfft_array_type_hermitian_planar:
		number_of_output_buffers = 2;
		size_of_output_buffers_in_bytes = outfftBatchSize * sizeof(T);
		break;
	case rocfft_array_type_real:
		number_of_output_buffers = 1;
		size_of_output_buffers_in_bytes = outfftBatchSize * sizeof(T);
		break;
	}

	// Fill the input buffers
	switch( inArrType )
	{
	case rocfft_array_type_complex_interleaved:
		{
			size_of_input_buffers_in_bytes = fftBatchSize * sizeof( std::complex< T > );

			setupBuffers(
				device_id,
				size_of_input_buffers_in_bytes, 1, input_device_buffers,
				size_of_output_buffers_in_bytes, number_of_output_buffers, output_device_buffers);

			std::vector< std::complex< T > > input( fftBatchSize );

			// set zero
			for( unsigned i = 0; i < fftBatchSize; ++i )
			{
				input[ i ] = 0;
			}

			// impulse test case
			for(size_t b = 0; b < batchSize; b++)
			{
				size_t p3 = b * strides[3];
				for(size_t k = 0; k < lengths[2]; k++)
				{
					size_t p2 = p3 + k * strides[2];
					for(size_t j = 0; j < lengths[1]; j++)
					{
						size_t p1 = p2 + j * strides[1];
						for(size_t i = 0; i < lengths[0]; i++)
						{
							size_t p0 = p1 + i * strides[0];
							input[p0] = 1;
						}
					}
				}
			}

			HIP_V_THROW( hipMemcpy( input_device_buffers[ 0 ], &input[ 0 ], size_of_input_buffers_in_bytes, hipMemcpyHostToDevice), "hipMemcpy failed" );

		}
		break;
	case rocfft_array_type_complex_planar:
		{
			size_of_input_buffers_in_bytes = fftBatchSize * sizeof( T );

			setupBuffers(
				device_id,
				size_of_input_buffers_in_bytes, 2, input_device_buffers,
				size_of_output_buffers_in_bytes, number_of_output_buffers, output_device_buffers);

			std::vector< T > real( fftBatchSize );
			std::vector< T > imag( fftBatchSize );

			// set zero
			for( unsigned i = 0; i < fftBatchSize; ++i )
			{
				real[ i ] = 0;
				imag[ i ] = 0;
			}

			// impulse test case
			for(size_t b = 0; b < batchSize; b++)
			{
				size_t p3 = b * strides[3];
				for(size_t k = 0; k < lengths[2]; k++)
				{
					size_t p2 = p3 + k * strides[2];
					for(size_t j = 0; j < lengths[1]; j++)
					{
						size_t p1 = p2 + j * strides[1];
						for(size_t i = 0; i < lengths[0]; i++)
						{
							size_t p0 = p1 + i * strides[0];
							real[p0] = 1;
						}
					}
				}
			}

			HIP_V_THROW( hipMemcpy( input_device_buffers[ 0 ], &real[ 0 ], size_of_input_buffers_in_bytes, hipMemcpyHostToDevice), "hipMemcpy failed" );
			HIP_V_THROW( hipMemcpy( input_device_buffers[ 1 ], &imag[ 0 ], size_of_input_buffers_in_bytes, hipMemcpyHostToDevice), "hipMemcpy failed" );
		}
		break;
	case rocfft_array_type_hermitian_interleaved:
		{
			size_of_input_buffers_in_bytes = fftBatchSize * sizeof( std::complex< T > );

			setupBuffers(
				device_id,
				size_of_input_buffers_in_bytes, 1, input_device_buffers,
				size_of_output_buffers_in_bytes, number_of_output_buffers, output_device_buffers);

			std::vector< std::complex< T > > input( fftBatchSize );

			// set zero
			for( unsigned i = 0; i < fftBatchSize; ++i )
			{
				input[ i ] = 0;
			}

			// impulse test case
			for(size_t b = 0; b < batchSize; b++)
			{
				size_t p3 = b * strides[3];
				input[p3] = static_cast<T>(outfftVectorSize);

			}

			HIP_V_THROW( hipMemcpy( input_device_buffers[ 0 ], &input[ 0 ], size_of_input_buffers_in_bytes, hipMemcpyHostToDevice), "hipMemcpy failed" );
		}
		break;
	case rocfft_array_type_hermitian_planar:
		{
			size_of_input_buffers_in_bytes = fftBatchSize * sizeof( T );

			setupBuffers(
				device_id,
				size_of_input_buffers_in_bytes, 2, input_device_buffers,
				size_of_output_buffers_in_bytes, number_of_output_buffers, output_device_buffers);

			std::vector< T > real( fftBatchSize );
			std::vector< T > imag( fftBatchSize );

			// set zero
			for( unsigned i = 0; i < fftBatchSize; ++i )
			{
				real[ i ] = 0;
				imag[ i ] = 0;
			}

			// impulse test case
			for(size_t b = 0; b < batchSize; b++)
			{
				size_t p3 = b * strides[3];
				real[p3] = static_cast<T>(outfftVectorSize);
			}

			HIP_V_THROW( hipMemcpy( input_device_buffers[ 0 ], &real[ 0 ], size_of_input_buffers_in_bytes, hipMemcpyHostToDevice), "hipMemcpy failed" );
			HIP_V_THROW( hipMemcpy( input_device_buffers[ 1 ], &imag[ 0 ], size_of_input_buffers_in_bytes, hipMemcpyHostToDevice), "hipMemcpy failed" );
		}
		break;
	case rocfft_array_type_real:
		{
			size_of_input_buffers_in_bytes = fftBatchSize * sizeof( T );

			setupBuffers(
				device_id,
				size_of_input_buffers_in_bytes, 1, input_device_buffers,
				size_of_output_buffers_in_bytes, number_of_output_buffers, output_device_buffers);

			std::vector< T > real( fftBatchSize );

			// set zero
			for( unsigned i = 0; i < fftBatchSize; ++i )
			{
				real[ i ] = 0;
			}

			// impulse test case
			for(size_t b = 0; b < batchSize; b++)
			{
				size_t p3 = b * strides[3];
				for(size_t k = 0; k < lengths[2]; k++)
				{
					size_t p2 = p3 + k * strides[2];
					for(size_t j = 0; j < lengths[1]; j++)
					{
						size_t p1 = p2 + j * strides[1];
						for(size_t i = 0; i < lengths[0]; i++)
						{
							size_t p0 = p1 + i * strides[0];
							real[p0] = 1;
						}
					}
				}
			}

			HIP_V_THROW( hipMemcpy( input_device_buffers[ 0 ], &real[ 0 ], size_of_input_buffers_in_bytes, hipMemcpyHostToDevice), "hipMemcpy failed" );
		}
		break;
	default:
		{
			throw std::runtime_error( "Input layout format not yet supported" );
		}
		break;
	}

	LIB_V_THROW( rocfft_setup(), " rocfft_setup failed" );

	rocfft_plan plan = NULL;
	rocfft_plan_description desc = NULL;
	rocfft_execution_info info = NULL;

	if( (place == rocfft_placement_inplace) && packed && (scale == 1.0) &&
		(inOffset[0] == 0) && (inOffset[1] == 0) && (outOffset[0] == 0) && (outOffset[1] == 0) )
	{
		LIB_V_THROW( rocfft_plan_create( &plan, place, transformType, precision, dim, lengths, batchSize, NULL  ), "rocfft_plan_create failed" );
	}
	else
	{
		LIB_V_THROW( rocfft_plan_description_create( &desc ), "rocfft_plan_description_create failed" );

		if(!packed)
		{
			LIB_V_THROW( rocfft_plan_description_set_data_layout( desc, inArrType, outArrType, inOffset, outOffset,
						3, strides, strides[3], 3, o_strides, o_strides[3] ), "rocfft_plan_description_data_layout failed" );
		}
		else
		{
			LIB_V_THROW( rocfft_plan_description_set_data_layout( desc, inArrType, outArrType, inOffset, outOffset,
						0, NULL, 0, 0, NULL, 0 ), "rocfft_plan_description_data_layout failed" );
		}

		/*if(scale != 1.0)
		{
			if(precision == rocfft_precision_single)
				LIB_V_THROW( rocfft_plan_description_set_scale_float( desc, (float)scale ), "rocfft_plan_description_set_scale_float failed" );
			else
				LIB_V_THROW( rocfft_plan_description_set_scale_double( desc, scale ), "rocfft_plan_description_set_scale_double failed" );
		}*/

		LIB_V_THROW( rocfft_plan_create( &plan, place, transformType, precision, dim, lengths, batchSize, desc ), "rocfft_plan_create failed" );
	}

	// print
	LIB_V_THROW( rocfft_plan_get_print( plan ), "rocfft_plan_get_print failed" );

	//get the buffersize
	size_t workBufferSize=0;
	LIB_V_THROW( rocfft_plan_get_work_buffer_size( plan, &workBufferSize ), "rocfft_plan_get_work_buffer_size failed" );

	//allocate the intermediate buffer
	void *workBuffer=NULL;

	if (workBufferSize)
	{
		HIP_V_THROW( hipMalloc(&workBuffer, workBufferSize), "Creating intmediate Buffer failed" );
	}

	switch( inArrType )
	{
	case rocfft_array_type_complex_interleaved:
	case rocfft_array_type_complex_planar:
	case rocfft_array_type_hermitian_interleaved:
	case rocfft_array_type_hermitian_planar:
	case rocfft_array_type_real:
		break;
	default:
		//	Don't recognize input layout
		return rocfft_status_invalid_arg_value;
	}

	switch( outArrType )
	{
	case rocfft_array_type_complex_interleaved:
	case rocfft_array_type_complex_planar:
	case rocfft_array_type_hermitian_interleaved:
	case rocfft_array_type_hermitian_planar:
	case rocfft_array_type_real:
		break;
	default:
		//	Don't recognize output layout
		return rocfft_status_invalid_arg_value;
	}

	if (( place == rocfft_placement_inplace )
	&&  ( inArrType != outArrType )) {
		switch( inArrType )
		{
		case rocfft_array_type_complex_interleaved:
			{
				if( (outArrType == rocfft_array_type_complex_planar) || (outArrType == rocfft_array_type_hermitian_planar) )
				{
					throw std::runtime_error( "Cannot use the same buffer for interleaved->planar in-place transforms" );
				}
				break;
			}
		case rocfft_array_type_complex_planar:
			{
				if( (outArrType == rocfft_array_type_complex_interleaved) || (outArrType == rocfft_array_type_hermitian_interleaved) )
				{
					throw std::runtime_error( "Cannot use the same buffer for planar->interleaved in-place transforms" );
				}
				break;
			}
		case rocfft_array_type_hermitian_interleaved:
			{
				if( outArrType != rocfft_array_type_real )
				{
					throw std::runtime_error( "Cannot use the same buffer for interleaved->planar in-place transforms" );
				}
				break;
			}
		case rocfft_array_type_hermitian_planar:
			{
				throw std::runtime_error( "Cannot use the same buffer for planar->interleaved in-place transforms" );
				break;
			}
		case rocfft_array_type_real:
			{
				if( (outArrType == rocfft_array_type_complex_planar) || (outArrType == rocfft_array_type_hermitian_planar) )
				{
					throw std::runtime_error( "Cannot use the same buffer for interleaved->planar in-place transforms" );
				}
				break;
			}
		}
	}

	void **BuffersOut = ( place == rocfft_placement_inplace ) ? NULL : &output_device_buffers[ 0 ];

	LIB_V_THROW( rocfft_execution_info_create(&info), "rocfft_execution_info_create failed" );
	if(workBuffer != NULL)
		LIB_V_THROW( rocfft_execution_info_set_work_buffer(info, workBuffer, workBufferSize), "rocfft_execution_info_set_work_buffer failed" );

	// Execute once for basic functional test
	LIB_V_THROW( rocfft_execute( plan, input_device_buffers, BuffersOut, info ), "rocfft_execute failed" );

	HIP_V_THROW( hipDeviceSynchronize(), "hipDeviceSynchronize failed" );

	if(profile_count > 1)
	{
		Timer tr;
		tr.Start();

		hipEvent_t start, stop;
		HIP_V_THROW( hipEventCreate(&start), "hipEventCreate failed" );
		HIP_V_THROW( hipEventCreate(&stop), "hipEventCreate failed" );

		HIP_V_THROW( hipEventRecord(start), "hipEventRecord failed" );

		for( unsigned i = 0; i < profile_count; ++i )
		{
			LIB_V_THROW( rocfft_execute( plan, input_device_buffers, BuffersOut, info ), "rocfft_execute failed" );
		        //HIP_V_THROW( hipDeviceSynchronize(), "hipDeviceSynchronize failed" );
		}

		HIP_V_THROW( hipEventRecord(stop), "hipEventRecord failed" );
		HIP_V_THROW( hipEventSynchronize(stop), "hipEventSynchronize failed" );

		double wtime = tr.Sample()/((double)profile_count);

		float gpu_time;
		hipEventElapsedTime(&gpu_time, start, stop);
		gpu_time /= (float)profile_count;

		HIP_V_THROW( hipDeviceSynchronize(), "hipDeviceSynchronize failed" );

		size_t totalLen = 1;
		for(int i=0; i<dim; i++) totalLen *= lengths[i];

		double constMultiplier = 1.0;
		if( (inArrType == rocfft_array_type_real ) || (outArrType == rocfft_array_type_real) )
			constMultiplier = 2.5;
		else
			constMultiplier = 5.0;

		double opsconst = (double)batchSize * constMultiplier * (double)totalLen * log((double)totalLen) / log(2.0);
		double bytes = (double)batchSize * 2.0 * (double)totalLen ;// the scalar 2.0 is because read & write

		if( (transformType == rocfft_transform_type_complex_forward) || (transformType == rocfft_transform_type_complex_inverse) ) {
            if(precision == rocfft_precision_single)
                bytes *= sizeof(float) * 2;
            else
                bytes *= sizeof(double) * 2;
		}
		else{
				if(precision == rocfft_precision_single)
						bytes *= sizeof(float);
				else
						bytes *= sizeof(double);
		}

		std::cout << "\nExecution gpu time: " << gpu_time << " ms" << std::endl;
		std::cout << "Execution wall time: " << 1000.0 * wtime << " ms" << std::endl;
		std::cout << "Execution gflops (wall time): " << (opsconst) / (1e9 * wtime) << std::endl;
		std::cout << "Bandwidth GB/s (wall time): " << (bytes) / (1e9 * wtime) << std::endl;
	}

	if(workBuffer)
		HIP_V_THROW( hipFree(workBuffer), "hipFree failed" );

	if(desc != NULL)
		LIB_V_THROW( rocfft_plan_description_destroy(desc), "rocfft_plan_description_destroy failed" );

	if(info != NULL)
		LIB_V_THROW( rocfft_execution_info_destroy(info), "rocfft_execution_info_destroy failed" );

	bool checkflag= false;
    double err_ratio = 1E-6;

	// Read and check output data
	// This check is not valid if the FFT is executed multiple times inplace.
	//
	if (( place == rocfft_placement_notinplace )
	||  ( profile_count == 1))
	{
		switch( outArrType )
		{
		case rocfft_array_type_hermitian_interleaved:
		case rocfft_array_type_complex_interleaved:
			{
				std::vector< std::complex< T > > output( outfftBatchSize );

				if( place == rocfft_placement_inplace )
				{
					HIP_V_THROW( hipMemcpy( &output[ 0 ], input_device_buffers[ 0 ], size_of_input_buffers_in_bytes, hipMemcpyDeviceToHost), "hipMemcpy failed" );
				}
				else
				{
					HIP_V_THROW( hipMemcpy( &output[ 0 ], BuffersOut[ 0 ], size_of_output_buffers_in_bytes, hipMemcpyDeviceToHost), "hipMemcpy failed" );
				}

				//check output data
				for( unsigned i = 0; i < outfftBatchSize; ++i )
				{
					if (0 == (i % outfftVectorSizePadded))
					{
						if (fabs(output[i].real() - outfftVectorSize)/outfftVectorSize > err_ratio)
						{
							checkflag = true;
							break;
						}
					}
					else
					{
						if (fabs(output[ i ].real()) > (err_ratio * outfftVectorSize))
						{
							checkflag = true;
							break;
						}
					}

					if (fabs(output[ i ].imag()) > (err_ratio * outfftVectorSize))
					{
						checkflag = true;
						break;
					}
				}
			}
			break;
		case rocfft_array_type_hermitian_planar:
		case rocfft_array_type_complex_planar:
			{
				std::valarray< T > real( outfftBatchSize );
				std::valarray< T > imag( outfftBatchSize );

				if( place == rocfft_placement_inplace )
				{
					HIP_V_THROW( hipMemcpy( &real[ 0 ], input_device_buffers[ 0 ], size_of_input_buffers_in_bytes, hipMemcpyDeviceToHost), "hipMemcpy failed" );
					HIP_V_THROW( hipMemcpy( &imag[ 0 ], input_device_buffers[ 1 ], size_of_input_buffers_in_bytes, hipMemcpyDeviceToHost), "hipMemcpy failed" );
				}
				else
				{
					HIP_V_THROW( hipMemcpy( &real[ 0 ], BuffersOut[ 0 ], size_of_output_buffers_in_bytes, hipMemcpyDeviceToHost), "hipMemcpy failed" );
					HIP_V_THROW( hipMemcpy( &imag[ 0 ], BuffersOut[ 1 ], size_of_output_buffers_in_bytes, hipMemcpyDeviceToHost), "hipMemcpy failed" );
				}

				//  Check output data
				for( unsigned i = 0; i < outfftBatchSize; ++i )
				{
					if (0 == (i % outfftVectorSizePadded))
					{
						if (real[i] != outfftVectorSize)
						{
							checkflag = true;
							break;
						}
					}
					else
					{
						if (real[i] != 0)
						{
							checkflag = true;
							break;
						}
					}

					if (imag[i] != 0)
					{
						checkflag = true;
						break;
					}
				}
			}
			break;
		case rocfft_array_type_real:
			{
				std::valarray< T > real( outfftBatchSize );

				if( place == rocfft_placement_inplace )
				{
					HIP_V_THROW( hipMemcpy( &real[ 0 ], input_device_buffers[ 0 ], size_of_input_buffers_in_bytes, hipMemcpyDeviceToHost), "hipMemcpy failed" );
				}
				else
				{
					HIP_V_THROW( hipMemcpy( &real[ 0 ], BuffersOut[ 0 ], size_of_output_buffers_in_bytes, hipMemcpyDeviceToHost), "hipMemcpy failed" );
				}

				////check output data

				for(size_t b = 0; b < batchSize; b++)
				{
					size_t p3 = b * o_strides[3];
					for(size_t k = 0; k < lengths[2]; k++)
					{
						size_t p2 = p3 + k * o_strides[2];
						for(size_t j = 0; j < lengths[1]; j++)
						{
							size_t p1 = p2 + j * o_strides[1];
							for(size_t i = 0; i < lengths[0]; i++)
							{
								size_t p0 = p1 + i * o_strides[0];

								if (real[p0] != 1)
								{
									checkflag = true;
									break;
								}

							}
						}
					}
				}
			}
			break;
		default:
			{
				throw std::runtime_error( "Input layout format not yet supported" );
			}
			break;
		}

		if (checkflag)
		{
			std::cout << "\n\n\t\tRider Test *****FAIL*****" << std::endl;
		}
		else
		{
			std::cout << "\n\n\t\tRider Test *****PASS*****" << std::endl;
		}
	}

	LIB_V_THROW( rocfft_plan_destroy( plan ), "rocfft_plan_destroy failed" );
	LIB_V_THROW( rocfft_cleanup( ), "rocfft_cleanup failed" );

	clearBuffers( input_device_buffers, output_device_buffers );

	if(checkflag)
		return -1;

	return 0;
}

int main( int argc, char* argv[] )
{
	//	This helps with mixing output of both wide and narrow characters to the screen
	std::ios::sync_with_stdio( false );

	int				deviceId = 0;
	int				platformId = 0;

	//	FFT state

	rocfft_result_placement place = rocfft_placement_inplace;
	rocfft_transform_type	transformType = rocfft_transform_type_complex_forward;
	rocfft_array_type	inArrType  = rocfft_array_type_complex_interleaved;
	rocfft_array_type	outArrType = rocfft_array_type_complex_interleaved;
	rocfft_precision precision = rocfft_precision_single;

	size_t lengths[ 3 ] = {1,1,1};
	size_t iStrides[ 4 ] = {0,0,0,0};
	size_t oStrides[ 4 ] = {0,0,0,0};
	unsigned profile_count = 0;

	size_t batchSize = 1;
	double scale = 1.0;
	size_t iOffset[2] = {0,0};
	size_t oOffset[2] = {0,0};

	int tret = 0;

	try
	{
		// Declare the supported options.
		po::options_description desc( "rocfft rider command line options" );
		desc.add_options()
			( "help,h",        "produces this help message" )
			( "version,v",     "Print queryable version information from the rocfft library" )
			( "info,i",      "Print queryable information of all the runtimes and devices" )
			( "printChosen",   "Print queryable information of the selected runtime and device" )
			( "platform",      po::value< int >( &platformId )->default_value( 0 ),   "Select a specific platform id as it is reported by info" )
			( "device",        po::value< int >( &deviceId )->default_value( 0 ),   "Select a specific device id as it is reported by info" )
			( "notInPlace,o",    "Not in-place FFT transform (default: in-place)" )
			( "double",		   "Double precision transform (default: single)" )
			( "transformType,t",	po::value< rocfft_transform_type >( &transformType )->default_value( rocfft_transform_type_complex_forward ), "Type of transform:\n0) complex forward\n1) complex inverse\n2) real forward\n3) real inverse" )
			( "lenX,x",        po::value< size_t >( &lengths[ 0 ] )->default_value( 1024 ),   "Specify the length of the 1st dimension of a test array" )
			( "lenY,y",        po::value< size_t >( &lengths[ 1 ] )->default_value( 1 ),      "Specify the length of the 2nd dimension of a test array" )
			( "lenZ,z",        po::value< size_t >( &lengths[ 2 ] )->default_value( 1 ),      "Specify the length of the 3rd dimension of a test array" )
			( "isX",   po::value< size_t >( &iStrides[ 0 ] )->default_value( 1 ),		"Specify the input stride of the 1st dimension of a test array" )
			( "isY",   po::value< size_t >( &iStrides[ 1 ] )->default_value( 0 ),	"Specify the input stride of the 2nd dimension of a test array" )
			( "isZ",   po::value< size_t >( &iStrides[ 2 ] )->default_value( 0 ),	"Specify the input stride of the 3rd dimension of a test array" )
			( "iD", po::value< size_t >( &iStrides[ 3 ] )->default_value( 0 ), "input distance between successive members when batch size > 1" )
			( "osX",   po::value< size_t >( &oStrides[ 0 ] )->default_value( 1 ),		"Specify the output stride of the 1st dimension of a test array" )
			( "osY",   po::value< size_t >( &oStrides[ 1 ] )->default_value( 0 ),	"Specify the output stride of the 2nd dimension of a test array" )
			( "osZ",   po::value< size_t >( &oStrides[ 2 ] )->default_value( 0 ),	"Specify the output stride of the 3rd dimension of a test array" )
			( "oD", po::value< size_t >( &oStrides[ 3 ] )->default_value( 0 ), "output distance between successive members when batch size > 1" )
			( "scale",   po::value< double >( &scale )->default_value( 1.0 ), "Specify the scaling factor " )
			( "iOff0",   po::value< size_t >( &iOffset[ 0 ] )->default_value( 0 ),	"Specify the offset for first/only input buffer" )
			( "iOff1",   po::value< size_t >( &iOffset[ 1 ] )->default_value( 0 ),	"Specify the offset for second input buffer" )
			( "oOff0",   po::value< size_t >( &oOffset[ 0 ] )->default_value( 0 ),	"Specify the offset for first/only output buffer" )
			( "oOff1",   po::value< size_t >( &oOffset[ 1 ] )->default_value( 0 ),	"Specify the offset for second output buffer" )
			( "batchSize,b",   po::value< size_t >( &batchSize )->default_value( 1 ), "If this value is greater than one, arrays will be used " )
			( "profile,p",     po::value< unsigned >( &profile_count )->default_value( 1 ), "Time and report the kernel speed of the FFT (default: profiling off)" )
			( "inArrType",      po::value< rocfft_array_type >( &inArrType )->default_value( rocfft_array_type_complex_interleaved ), "Array type of input data:\n0) interleaved\n1) planar\n2) real\n3) hermitian interleaved\n4) hermitian planar" )
			( "outArrType",     po::value< rocfft_array_type >( &outArrType )->default_value( rocfft_array_type_complex_interleaved ), "Array type of output data:\n0) interleaved\n1) planar\n2) real\n3) hermitian interleaved\n4) hermitian planar" )
			;

		po::variables_map vm;
		po::store( po::parse_command_line( argc, argv, desc ), vm );
		po::notify( vm );

		if( vm.count( "version" ) )
		{
			std::cout << "version" << std::endl;
			return 0;
		}

		if( vm.count( "help" ) )
		{
			//	This needs to be 'cout' as program-options does not support wcout yet
			std::cout << desc << std::endl;
			return 0;
		}

		if( vm.count( "info" ) )
		{
			return 0;
		}

		bool printInfo = false;
		if( vm.count( "printChosen" ) )
		{
			printInfo = true;
		}

		if( vm.count( "notInPlace" ) )
		{
			place = rocfft_placement_notinplace;
		}

		if( vm.count( "double" ) )
		{
			precision = rocfft_precision_double;
		}

		if( profile_count > 1 )
		{
		}

        if(transformType == rocfft_transform_type_real_forward)
            if((inArrType == rocfft_array_type_complex_interleaved) && (outArrType == rocfft_array_type_complex_interleaved))
            {
                inArrType = rocfft_array_type_real;
                outArrType = rocfft_array_type_hermitian_interleaved;
            }

        if(transformType == rocfft_transform_type_real_inverse)
            if((inArrType == rocfft_array_type_complex_interleaved) && (outArrType == rocfft_array_type_complex_interleaved))
            {
                inArrType = rocfft_array_type_hermitian_interleaved;
                outArrType = rocfft_array_type_real;
            }

		int inL = (int)inArrType;
		int otL = (int)outArrType;

		// input output array type support matrix
		int ioArrTypeSupport[5][5] =		{
										{ 1, 1, 0, 0, 0 },
										{ 1, 1, 0, 0, 0 },
										{ 0, 0, 0, 1, 1 },
										{ 0, 0, 1, 0, 0 },
										{ 0, 0, 1, 0, 0 },
										};

		if(inL > 4) throw std::runtime_error( "Invalid Input array type format" );
		if(otL > 4) throw std::runtime_error( "Invalid Output array type format" );

		if(ioArrTypeSupport[inL][otL] == 0) throw std::runtime_error( "Invalid combination of Input/Output array type formats" );

		bool packed = false;
		if( (iStrides[0] == 1) && (iStrides[1] == 0) && (iStrides[2] == 0) && (iStrides[3] == 0)
		&&  (oStrides[0] == 1) && (oStrides[1] == 0) && (oStrides[2] == 0) && (oStrides[3] == 0) )
			packed = true;

		if( (transformType == rocfft_transform_type_complex_forward) || (transformType == rocfft_transform_type_complex_inverse) ) // Complex-Complex cases
		{
			iStrides[1] = iStrides[1] ? iStrides[1] : lengths[0] * iStrides[0];
			iStrides[2] = iStrides[2] ? iStrides[2] : lengths[1] * iStrides[1];
			iStrides[3] = iStrides[3] ? iStrides[3] : lengths[2] * iStrides[2];

			if(place == rocfft_placement_inplace)
			{
				oStrides[0] = iStrides[0];
				oStrides[1] = iStrides[1];
				oStrides[2] = iStrides[2];
				oStrides[3] = iStrides[3];
			}
			else
			{
				oStrides[1] = oStrides[1] ? oStrides[1] : lengths[0] * oStrides[0];
				oStrides[2] = oStrides[2] ? oStrides[2] : lengths[1] * oStrides[1];
				oStrides[3] = oStrides[3] ? oStrides[3] : lengths[2] * oStrides[2];
			}
		}
		else // Real cases
		{
			size_t *rst, *cst;
			size_t N = lengths[0];
			size_t Nt = 1 + lengths[0]/2;
			bool iflag = false;
			bool rcFull = (inL == 0) || (inL == 1) || (otL == 0) || (otL == 1);

			if(inArrType == rocfft_array_type_real ) { iflag = true; rst = iStrides; }
			else { rst = oStrides; } // either in or out should be REAL

			// Set either in or out strides whichever is real
			if(place == rocfft_placement_inplace)
			{
				if(rcFull)	{ rst[1] = rst[1] ? rst[1] :  N * 2 * rst[0]; }
				else		{ rst[1] = rst[1] ? rst[1] : Nt * 2 * rst[0]; }

				rst[2] = rst[2] ? rst[2] : lengths[1] * rst[1];
				rst[3] = rst[3] ? rst[3] : lengths[2] * rst[2];
			}
			else
			{
				rst[1] = rst[1] ? rst[1] : lengths[0] * rst[0];
				rst[2] = rst[2] ? rst[2] : lengths[1] * rst[1];
				rst[3] = rst[3] ? rst[3] : lengths[2] * rst[2];
			}

			// Set the remaining of in or out strides that is not real
			if(iflag) { cst = oStrides; }
			else	  { cst = iStrides; }

			if(rcFull)	{ cst[1] = cst[1] ? cst[1] :  N * cst[0]; }
			else		{ cst[1] = cst[1] ? cst[1] : Nt * cst[0]; }

			cst[2] = cst[2] ? cst[2] : lengths[1] * cst[1];
			cst[3] = cst[3] ? cst[3] : lengths[2] * cst[2];
		}

		if( precision == rocfft_precision_single )
			tret = transform<float>( lengths, iStrides, oStrides, batchSize, iOffset, oOffset, inArrType, outArrType, place, precision, transformType, scale, packed, deviceId, platformId, printInfo, profile_count );
		else
			tret = transform<double>( lengths, iStrides, oStrides, batchSize, iOffset, oOffset, inArrType, outArrType, place, precision, transformType, scale, packed, deviceId, platformId, printInfo, profile_count );
	}
	catch( std::exception& e )
	{
		std::cerr << "rocfft error condition reported:" << std::endl << e.what() << std::endl;
		return 1;
	}

	return tret;
}


#define BZ_DISABLE_RESTRICT

#include <blitz/array.h>
#include <blitz/traversal.h>
#include <blitz/timer.h>

#ifdef BZ_HAVE_STD
  #include <fstream>
#else
  #include <fstream.h>
#endif

BZ_USING_NAMESPACE(blitz)

#if defined(BZ_FORTRAN_SYMBOLS_WITH_TRAILING_UNDERSCORES)
 #define acoustic3d_f90 acoustic3d_f90_
 #define acoustic3d_f77 acoustic3d_f77_
 #define acoustic3d_f90tuned acoustic3d_f90tuned_
 #define acoustic3d_f77tuned acoustic3d_f77tuned_
#elif defined(BZ_FORTRAN_SYMBOLS_WITH_DOUBLE_TRAILING_UNDERSCORES)
 #define acoustic3d_f90 acoustic3d_f90__
 #define acoustic3d_f77 acoustic3d_f77__
 #define acoustic3d_f90tuned acoustic3d_f90tuned__
 #define acoustic3d_f77tuned acoustic3d_f77tuned__
#elif defined(BZ_FORTRAN_SYMBOLS_CAPS)
 #define acoustic3d_f90       ACOUSTIC3D_F90
 #define acoustic3d_f77       ACOUSTIC3D_F77
 #define acoustic3d_f90tuned  ACOUSTIC3D_F90TUNED
 #define acoustic3d_f77tuned  ACOUSTIC3D_F77TUNED
#endif

extern "C" {
void acoustic3d_f90(int& N, int& niters, float& check);
void acoustic3d_f77(int& N, int& niters, float& check);
void acoustic3d_f90tuned(int& N, int& niters, float& check);
void acoustic3d_f77tuned(int& N, int& niters, float& check);
}

float acoustic3D_BlitzRaw(int N, int niters);
float acoustic3D_BlitzInterlacedCycled(int N, int niters);
float acoustic3D_BlitzCycled(int N, int niters);
float acoustic3D_BlitzStencil(int N, int niters);

void output_data(const char* type, const Timer& t, float check, double Gflops)
{
  cout << type << ": " << t.elapsed()
	 << t.indep_var() << "  check = "
         << check << " Gflop/" << t.indep_var() << " = "
	 << (Gflops/t.elapsed())
         << endl << endl;
}

int main()
{
    Timer timer;
    int N = 112;
    int niters = 210;    // Must be divisible by 3 for tuned Fortran versions
    float check;

		cout << "Acoustic 3D Benchmark" << endl << endl;

		double Gflops = (N-2)*(N-2)*(N-2) * 11.0 * niters / 1.0e+9;

    generateFastTraversalOrder(TinyVector<int,2>(N-2,N-2));

    timer.start();
    check = acoustic3D_BlitzRaw(N, niters);
    timer.stop();
    output_data("Blitz++ (raw)", timer, check, Gflops);

    timer.start();
    check = acoustic3D_BlitzStencil(N, niters);
    timer.stop();
    output_data("Blitz++ (stencil)", timer, check, Gflops);

#if 0
    timer.start();
    check = acoustic3D_BlitzInterlaced(N, niters, c);
    timer.stop();
    output_data("Blitz++ (interlaced)", timer, check, Gflops);
#endif

    timer.start();
    check = acoustic3D_BlitzCycled(N, niters);
    timer.stop();
    output_data("Blitz++ (cycled)", timer, check, Gflops);

    timer.start();
    check = acoustic3D_BlitzInterlacedCycled(N, niters);
    timer.stop();
    output_data("Blitz++ (interlaced & cycled)", timer, check, Gflops);

#ifdef FORTRAN_90
    timer.start();
    acoustic3d_f90(N, niters, check);
    timer.stop();
    output_data("Fortran 90", timer, check, Gflops);

    timer.start();
    acoustic3d_f90tuned(N, niters, check);
    timer.stop();
    output_data("Fortran 90 (tuned)", timer, check, Gflops);
#endif

    timer.start();
    acoustic3d_f77(N, niters, check);
    timer.stop();
    output_data("Fortran 77", timer, check, Gflops);

    timer.start();
    acoustic3d_f77tuned(N, niters, check);
    timer.stop();
    output_data("Fortran 77 (tuned)", timer, check, Gflops);

    return 0;
}

void setupInitialConditions(Array<float,3>& P1, Array<float,3>& P2,
    Array<float,3>& P3, Array<float,3>& c, int N);

void snapshot(const Array<float,3>& P, const Array<float,3>& c);

void checkArray(const Array<float,3>& A, int N);

void setupInitialConditions(Array<float,3>& P1, Array<float,3>& P2,
    Array<float,3>& P3, Array<float,3>& c, int N)
{
    // Set the velocity field
    c(Range(0,N/2-1), Range::all(), Range::all()) = 0.05;
    c(Range(N/2,N-1), Range::all(), Range::all()) = 0.3;

    double Nfp = static_cast<double>(N);
    int cavityLeft = static_cast<int>(3*Nfp/7-1);
    int cavityRight = static_cast<int>(4*Nfp/7-1);
    int cavityFront = static_cast<int>(3*Nfp/7-1);
    int cavityBack = static_cast<int>(4*Nfp/7-1);
    int cavityTop = static_cast<int>(5*Nfp/7-1);
    int cavityBottom = static_cast<int>(6*Nfp/7-1);

    c(Range(cavityTop,cavityBottom),Range(cavityLeft,cavityRight),
        Range(cavityFront,cavityBack)) = 0.02;

    int cavityTop2 = static_cast<int>(1*Nfp/7-1);
    int cavityBottom2 = static_cast<int>(2*Nfp/7-1);
    c(Range(cavityTop2,cavityBottom2),Range(cavityLeft,cavityRight),
        Range(cavityFront,cavityBack)) = 0.001;

    // Initial pressure distribution
    BZ_USING_NAMESPACE(blitz::tensor);

    float NN = N;
    float ci = N/2-1;
    float cj = N/2-1;
    float ck = N/2-1;
    // pow2 is an ET-only function, it's not defined for POD types
    float s2 = 64.0 * 9.0 / pow(NN/2.0, 2);
    P1 = 0.0;
    P2 = exp(-(pow2(i-ci)+pow2(j-cj)+pow2(k-ck)) * s2);
    P3 = 0.0;

    checkArray(P2, N);
    checkArray(c, N);
}

void checkArray(const Array<float,3>& A, int N)
{
    double check = 0.0;

    for (int i=0; i < N; ++i)
      for (int j=0; j < N; ++j)
        for (int k=0; k < N; ++k)
          check += A(i,j,k) * ((i+1)+N*(j+1)+N*N*(k+1));

    cout << "Array check: " << check << endl;
}

void snapshot(const Array<float,3>& P, const Array<float,3>& c)
{
    static int count = 0, snapshotNum = 0;
    if (++count < 5)
        return;

    count = 0;
    ++snapshotNum;
    char filename[128];
    sprintf(filename, "snapshot%03d.m", snapshotNum);

    ofstream ofs(filename);
    int N = P.length(firstDim);

    int k = N/2;
    float Pmin = -0.2;
    float PScale = 1.0/0.4;
    float VScale = 0.5;

    ofs << "P" << snapshotNum << " = [ ";
    for (int i=0; i < N; ++i)
    {
        for (int j=0; j < N; ++j)
        {
            float value = (P(i,j,k)-Pmin)*PScale + c(i,j,k)*VScale;
            int r = static_cast<int>(value * 4096);
            ofs << r << " ";
        }
        if (i < N-1)
            ofs << ";" << endl;
    }
    ofs << "];" << endl;
}


#include "ValueWithError.hpp"
#include "precompiled.hpp"
#include "common.hpp"

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma GCC diagnostic ignored "-Wuninitialized"
#endif // __clang__

#if defined __GNUC__ \
            && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) ) \
            && !defined __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wuninitialized"
#endif

#include <Eigen/Eigen>
#include <boost/test/unit_test.hpp>

using namespace error_propagation;

namespace {

  /// Compares two Eigen objects and determines if they are equal or not
  struct eigen_comp
  {
    template<typename Derived1, typename Derived2>
    bool operator()( Derived1 const& v1, Derived2 const& v2) const
    {
      if( v1.size() != v2.size())
        return false;

      bool equal = true;
      for(typename Derived1::Index i = 0; i < v1.size() && equal; i++)
          equal &= ( v1(i) == v2(i) );

      return equal;
    }
  };

#define BOOST_CHECK_EQUAL_PV_EIGEN(A, B, C) \
  BOOST_CHECK_PREDICATE( eigen_comp(), (A.GetValue())(B) ); \
  BOOST_CHECK_PREDICATE( eigen_comp(), (A.GetError())(C) );

  struct ArrayFixture
  {
    typedef Eigen::Array3d     EA;
    typedef ValueWithError<EA> VD;

    VD pvd;
  };

}// anonymous namespace

BOOST_FIXTURE_TEST_SUITE(Test_ValueWithError_Eigen_Array,ArrayFixture)

BOOST_AUTO_TEST_CASE(ctor)
{
  EA val(1,2,3), error(-4,-5,-6);
  VD b(val);
  VD c(val, error);
  BOOST_CHECK_EQUAL_PV_EIGEN(c, val, error.abs());
}

TEST_OP(times,*)
TEST_OP(plus,+)
TEST_OP(minus,-)
TEST_OP(div,/)

// Comparison operators don't return bool so they are not usable for our purpose
// TEST_OP_COMPARE(equal,==)
// TEST_OP_COMPARE(not_equal,!=)
// TEST_OP_COMPARE(greater,>)
// TEST_OP_COMPARE(greater_than,>=)
// TEST_OP_COMPARE(smaller,<)
// TEST_OP_COMPARE(smaller_than,<=)

TEST_MATH_ONE_ARG(abs)
TEST_MATH_ONE_ARG(acos)
TEST_MATH_ONE_ARG(asin)
// TEST_MATH_ONE_ARG(atan)
TEST_MATH_ONE_ARG(cos)
// TEST_MATH_ONE_ARG(cosh)
//   TEST_MATH_ONE_ARG(cbrt)
//   TEST_MATH_ONE_ARG(asinh)
//   TEST_MATH_ONE_ARG(acosh)
//   TEST_MATH_ONE_ARG(atanh)
//   TEST_MATH_ONE_ARG(erf)
//   TEST_MATH_ONE_ARG(erfc)
//   TEST_MATH_ONE_ARG(lgamma)
//   TEST_MATH_ONE_ARG(tgamma)
//   TEST_MATH_TWO_ARGS(hypot)
TEST_MATH_ONE_ARG(exp)
// TEST_MATH_ONE_ARG(fabs)
TEST_MATH_ONE_ARG(log)

// base^array is not defined so we can't use TEST_MATH_TWO_ARGS(pow)

BOOST_AUTO_TEST_CASE(_pow)
{
  VD a;
  a = pow(pvd,pvd);
  a = pow(pvd,1.0);
  BOOST_CHECK(true);
}

TEST_MATH_ONE_ARG(sin)
// TEST_MATH_ONE_ARG(sinh)
TEST_MATH_ONE_ARG(sqrt)
TEST_MATH_ONE_ARG(tan)
// TEST_MATH_ONE_ARG(tanh)

BOOST_AUTO_TEST_SUITE_END() // Test_ValueWithError_Eigen_Array

#ifdef __clang__
#pragma clang diagnostic pop
#endif // __clang__

#if defined __GNUC__ \
            && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) ) \
            && !defined __clang__
#pragma GCC diagnostic pop
#endif


/**
 * @file t_ProductScoreAverage.cpp
 * @brief test ProductScoreAverage to calculate average score.
 * @author Jun Jiang
 * @date Created 2012-11-23
 */

#include "ProductScoreAverageTestFixture.h"
#include <boost/test/unit_test.hpp>

using namespace sf1r;

namespace
{

const float FLOAT_TOLERANCE = 1e-5;

} // namespace

BOOST_FIXTURE_TEST_SUITE(ProductScoreAverageTest,
                         ProductScoreAverageTestFixture)

BOOST_AUTO_TEST_CASE(emptyScorer)
{
    BOOST_TEST_MESSAGE("check empty scorer");

    addScorers("", "");

    docid_t docId = 1;
    score_t gold = 0;
    score_t result = averageScorer_->score(docId);

    BOOST_CHECK_EQUAL(result, gold);
}

BOOST_AUTO_TEST_CASE(oneScorer)
{
    BOOST_TEST_MESSAGE("check one scorer");

    addScorers("0.8", "0.6");

    docid_t docId = 1;
    score_t gold = 0.8 * 0.6;
    score_t result = averageScorer_->score(docId);

    BOOST_CHECK_CLOSE(result, gold, FLOAT_TOLERANCE);
}

BOOST_AUTO_TEST_CASE(multiScorer)
{
    BOOST_TEST_MESSAGE("check multiple scorers");

    addScorers("0.4 0.3 0.7", "0.2 0.6 0.3");

    docid_t docId = 1;
    score_t gold = (0.4*0.2 + 0.3*0.6 + 0.7*0.3) / 3;
    score_t result = averageScorer_->score(docId);

    BOOST_CHECK_CLOSE(result, gold, FLOAT_TOLERANCE);
}

BOOST_AUTO_TEST_SUITE_END()


//  Copyright Christopher Kormanyos 2014.
//  Copyright John Maddock 2014.
//  Copyright Paul A. Bristow 2014.

//  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)

#include <cmath>
#include <complex>
#include <limits>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <boost/math/cstdfloat/cstdfloat_types.hpp>

#ifdef _MSC_VER
#  pragma warning(disable : 4127) // conditional expression is constant.
#  pragma warning(disable : 4512) // assignment operator could not be generated.
#  pragma warning(disable : 4996) // use -D_SCL_SECURE_NO_WARNINGS.
#endif

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp> // Boost.Test
#include <boost/test/floating_point_comparison.hpp>

//
// DESCRIPTION:
// ~~~~~~~~~~~~
//
// This file tests the implementation of floating-point typedefs having
// specified widths, as implemented in <boost/cstdfloat.hpp> and described
// in N3626 (proposed for C++14).

// For more information on <boost/cstdfloat.hpp> and the corresponding
// proposal of "Floating-Point Typedefs Having Specified Widths",
// see: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3626.pdf

// The tests:
//
// Perform sanity checks on boost::float16_t, boost::float32_t,
// boost::float64_t, boost::float80_t, and boost::float128_t when
// these types are available. In the sanity checks, we verify the
// formal behavior of the types and the macros for creating literal
// floating-point constants.
//
// An extended check is included for boost::float128_t. This checks
// the functionality of <cmath>, I/O stream operations, and <complex>
// functions for boost::float128_t.

#define TEST_CSTDFLOAT_SANITY_CHECK(the_digits)                                                      \
void sanity_check_##the_digits##_func()                                                              \
{                                                                                \
  typedef boost::float##the_digits##_t float_type;                                                   \
                                                                                \
  BOOST_CONSTEXPR_OR_CONST int my_digits10 = std::numeric_limits<float_type>::digits10;              \
                                                                                \
  {                                                                                \
    BOOST_CONSTEXPR_OR_CONST float_type x =                                                          \
      BOOST_FLOAT##the_digits##_C(0.33333333333333333333333333333333333333333);                      \
    std::stringstream ss;                                                                            \
    ss << std::setprecision(my_digits10 - 1)                                                         \
       << x;                                                                                \
    std::string str = "0.";                                                                          \
    str += std::string(std::string::size_type(my_digits10 - 1), char('3'));                          \
    BOOST_CHECK_EQUAL( ss.str(), str );                                                              \
  }                                                                                \
  {                                                                                \
    BOOST_CONSTEXPR_OR_CONST float_type x =                                                          \
      BOOST_FLOAT##the_digits##_C(0.66666666666666666666666666666666666666666);                      \
    std::stringstream ss;                                                                            \
    ss << std::setprecision(my_digits10 - 1)                                                         \
       << x;                                                                                \
    std::string str = "0.";                                                                          \
    str += std::string(std::string::size_type(my_digits10 - 2), char('6'));                          \
    str += "7";                                                                                \
    BOOST_CHECK_EQUAL( ss.str(), str );                                                              \
  }                                                                                \
  {                                                                                \
    const float_type x = BOOST_FLOAT##the_digits##_C(1.0) / test_cstdfloat::zero;                    \
    const bool the_inf_test = (   std::numeric_limits<float_type>::has_infinity                      \
                               && (x == std::numeric_limits<float_type>::infinity()));               \
    BOOST_CHECK_EQUAL( the_inf_test, true );                                                         \
  }                                                                                \
  {                                                                                \
    using std::sqrt;                                                                                \
    const float_type x = sqrt(float_type(test_cstdfloat::minus_one));                                \
    const bool the_nan_test = (   std::numeric_limits<float_type>::has_quiet_NaN                     \
                               && (x != x));                                                         \
    BOOST_CHECK_EQUAL( the_nan_test, true );                                                         \
  }                                                                                \
  {                                                                                \
    const bool the_lim_test =                                                                        \
      (std::numeric_limits<boost::floatmax_t>::digits >= std::numeric_limits<float_type>::digits);   \
    BOOST_CHECK_EQUAL( the_lim_test, true );                                                         \
  }                                                                                \
}

namespace test_cstdfloat
{
  int zero;
  int minus_one;

  #if defined(BOOST_FLOATMAX_C)
  BOOST_CONSTEXPR_OR_CONST int has_floatmax_t = 1;
  #else
  BOOST_CONSTEXPR_OR_CONST int has_floatmax_t = 0;
  #endif

  #if defined(BOOST_FLOAT16_C)
  TEST_CSTDFLOAT_SANITY_CHECK(16)
  #endif

  #if defined(BOOST_FLOAT32_C)
  TEST_CSTDFLOAT_SANITY_CHECK(32)
  #endif

  #if defined(BOOST_FLOAT64_C)
  TEST_CSTDFLOAT_SANITY_CHECK(64)
  #endif

  #if defined(BOOST_FLOAT80_C)
  TEST_CSTDFLOAT_SANITY_CHECK(80)
  #endif

  #if defined(BOOST_FLOAT128_C)
  TEST_CSTDFLOAT_SANITY_CHECK(128)

  void extend_check_128_func()
  {
  }
  #endif // defined (BOOST_FLOAT128_C)
}

BOOST_AUTO_TEST_CASE(test_main)
{
  test_cstdfloat::zero      =  0;
  test_cstdfloat::minus_one = -1;

  // Perform basic sanity checks that verify both the existence of the proper
  // floating-point literal macros as well as the correct digit handling
  // for a given floating-point typedef having specified width.

  BOOST_CHECK_EQUAL( test_cstdfloat::has_floatmax_t, 1 );

  #if defined(BOOST_FLOAT16_C)
  test_cstdfloat::sanity_check_16_func();
  #endif

  #if defined(BOOST_FLOAT32_C)
  test_cstdfloat::sanity_check_32_func();
  #endif

  #if defined(BOOST_FLOAT64_C)
  test_cstdfloat::sanity_check_64_func();
  #endif

  #if defined(BOOST_FLOAT80_C)
  test_cstdfloat::sanity_check_80_func();
  #endif

  #if defined(BOOST_FLOAT128_C)
  test_cstdfloat::sanity_check_128_func();

  // Perform an extended check of boost::float128_t including
  // a variety of functions from the C++ standard library.
  test_cstdfloat::extend_check_128_func();
  #endif // defined (BOOST_FLOAT128_C)
}