//==================================================================================================
/*!
  @file

  @copyright 2017 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
**/
//==================================================================================================
#ifndef BOOST_SIMD_ALGORITHM_ALL_OF_HPP_INCLUDED
#define BOOST_SIMD_ALGORITHM_ALL_OF_HPP_INCLUDED

#include <boost/simd/range/segmented_aligned_range.hpp>
#include <boost/simd/detail/identity.hpp>
#include <boost/simd/function/all.hpp>
#include <boost/simd/pack.hpp>
#include <algorithm>

namespace boost { namespace simd
{
  /*!
    @ingroup group-algo

    Returns @c true if @c pred evaluates to @c true for all the elements in the
    ContiguousRange [first,last) or if the range is empty, @c and false otherwise.

    @param first  Beginning of the range of elements to test
    @param last   End of the range of elements to all_of
    @param pred   Predicate function object that will be applied.

    @par Requirement

      - @c first and @c last must be pointers to Vectorizable type.
      - @c pred must be a polymorphic unary function object, i.e callable on
        generic types.

    @par Example;:

      @snippet all_of.cpp all_of

    @par Possible output:

      @snippet all_of.txt all_of
  **/
  template<typename T, typename Pred>
  bool all_of(T const* first, T const* last, Pred const& pred)
  {
    auto pr = segmented_aligned_range(first,last);

    if(!std::all_of(pr.head.begin(), pr.head.end(), pred))
      return false;

    if(!std::all_of(pr.body.begin(), pr.body.end(), [&pred](pack<T> const& x){ return all(pred(x)); }))
      return false;

    if(!std::all_of(pr.tail.begin(), pr.tail.end(), pred))
      return false;

    return true;
  }

  /*!
    @ingroup group-algo

    Returns @c true if all the elements in the ContiguousRange [first,last) are not equal to zero
    or if the range is empty, @c and false otherwise.

    @param first  Beginning of the range of elements to test
    @param last   End of the range of elements to all_of

    @par Requirement

      - @c first and @c last must be pointers to Vectorizable type.
  **/
  template<typename T> bool all_of(T const* first, T const* last)
  {
    return all_of(first, last, detail::identity());
  }
} }

#endif


/*
 Copyright (C) 2017 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <boost/make_shared.hpp>
#include <iostream>
#include <ql/math/interpolations/bilinearinterpolation.hpp>
#include <ql/math/interpolations/linearinterpolation.hpp>
#include <ql/quotes/simplequote.hpp>
#include <ql/termstructures/yield/forwardcurve.hpp>
#include <qle/termstructures/blackvariancesurfacesparse.hpp>

using namespace std;

namespace QuantExt {

struct CloseEnoughComparator {
    explicit CloseEnoughComparator(const Real v) : v_(v) {}
    bool operator()(const Real w) const { return close_enough(v_, w); }
    Real v_;
};

BlackVarianceSurfaceSparse::BlackVarianceSurfaceSparse(const QuantLib::Date& referenceDate, const Calendar& cal,
                                                       const std::vector<Date>& dates, const std::vector<Real>& strikes,
                                                       const std::vector<Volatility>& volatilities,
                                                       const DayCounter& dayCounter)
    : BlackVarianceTermStructure(referenceDate, cal), dayCounter_(dayCounter) {

    QL_REQUIRE((strikes.size() == dates.size()) && (dates.size() == volatilities.size()),
               "dates, strikes and volatilities vectors not of equal size.");

    // first get uniques dates and sort
    set<Date> datesSet(dates.begin(), dates.end());
    datesSet.insert(this->referenceDate());
    expiries_ = vector<Date>(datesSet.begin(), datesSet.end());
    vector<bool> dateDone(expiries_.size(), false);
    times_ = vector<Time>(expiries_.size());
    interpolations_ = vector<Interpolation>(expiries_.size());
    variances_ = vector<vector<Real> >(expiries_.size());
    strikes_ = vector<vector<Real> >(expiries_.size());

    // Populate expiry info. (Except interpolation obj members)
    for (Size i = 0; i < dates.size(); i++) {
        vector<Date>::iterator found = find(expiries_.begin(), expiries_.end(), dates[i]);
        QL_REQUIRE(found != expiries_.end(), "Date should already be loaded" << dates[i]);
        ptrdiff_t ii;
        ii = distance(expiries_.begin(), found); // index of expiry

        if (!dateDone[ii]) {
            // add expiry data if not found
            QL_REQUIRE(dates[i] > referenceDate,
                       "Expiry date:" << dates[i] << " not after asof date: " << referenceDate);
            times_[ii] = timeFromReference(dates[i]);
            strikes_[ii].push_back(strikes[i]);
            variances_[ii].push_back(volatilities[i] * volatilities[i] * times_[ii]);
            dateDone[ii] = true;
        } else {
            // expiry found => add if strike not found
            Real tmpStrike = strikes[i];
            vector<Real>::iterator fnd =
                find_if(strikes_[ii].begin(), strikes_[ii].end(), CloseEnoughComparator(tmpStrike));
            if (fnd == strikes_[ii].end()) {
                // add strike/var pairs if strike not found for this expiry
                strikes_[ii].push_back(strikes[i]);
                variances_[ii].push_back(volatilities[i] * volatilities[i] * times_[ii]);
            }
        }
    }

    for (Size i = 1; i < expiries_.size(); i++) {
        QL_REQUIRE(strikes_[i].size() == variances_[i].size(),
                   "different number of variances and strikes for date: " << expiries_[i]);
    }

    // Short end
    QL_REQUIRE(expiries_[0] == this->referenceDate(), "First date should be reference date. Reference date: "
                                                       << this->referenceDate() << "First date: " << expiries_[0]);

    times_[0] = 0.0;
    vector<Real> tmpStrkVect;
    vector<Real> tmpVarVect;
    tmpStrkVect.push_back(5.0);
    tmpStrkVect.push_back(100.0);
    tmpVarVect.push_back(0.0);
    tmpVarVect.push_back(0.0);
    strikes_[0] = tmpStrkVect;
    variances_[0] = tmpVarVect;

    // set expiries' interpolations.
    for (Size i = 0; i < expiries_.size(); i++) {
        // sort strikes within this expiry
        vector<pair<Real, Real> > tmpPairs(strikes_[i].size());
        vector<Real> sortedStrikes; //(itr->second.expStrikes_.size());
        vector<Real> sortedVars;    // (sortedStrikes)
        for (Size j = 0; j < strikes_[i].size(); j++) {
            tmpPairs[j] = pair<Real, Real>(strikes_[i][j], variances_[i][j]);
        }
        sort(tmpPairs.begin(), tmpPairs.end());         // sorts according to frist. (strikes)
        for (vector<pair<Real, Real> >::iterator it = tmpPairs.begin(); it != tmpPairs.end(); it++) {
            sortedStrikes.push_back(it->first);
            sortedVars.push_back(it->second);
        }
        strikes_[i] = sortedStrikes;
        variances_[i] = sortedVars;

        // set interpolation
        if (strikes_[i].size() == 1) {
            // if only one strike => add different strike with same value for interpolation object.
            strikes_[i].push_back(strikes_[i][0] + strikes_[i][0] * 2);
            variances_[i].push_back(variances_[i][0]);
        }
        LinearInterpolation tmpInterpolation(strikes_[i].begin(), strikes_[i].end(), variances_[i].begin());
        interpolations_[i] = tmpInterpolation;
    }

} // namespace QuantExt

Real BlackVarianceSurfaceSparse::getVarForStrike(Real strike, const vector<Real>& strks, const vector<Real>& vars,
                                                 const Interpolation& intrp) const {

    Real retVar;
    if (strike > strks.back()) {
        retVar = vars.back(); // flat extrapolate far stirke
    } else if (strike < strks.front()) {
        retVar = vars.front(); // flat extrapolate near stirke
    } else {
        retVar = intrp(strike); // interpolate between strikes
    }
    return retVar;
}

Real BlackVarianceSurfaceSparse::blackVarianceImpl(Time t, Real strike) const {

    QL_REQUIRE(t >= 0, "Variance requested for date before reference date: " << this->referenceDate());
    Real varReturn;
    if (t == 0.0) {
        //requested at reference date
        varReturn = variances_[0][0];
    } else if (t <= times_.back()) {
        //requested between existing expiries (interpolate between expiries)
        ptrdiff_t dt;       // index for point after requested
        ptrdiff_t dtPrev;   // index for point before requested
        dt = distance(times_.begin(), lower_bound(times_.begin(), times_.end(), t));
        dtPrev = (dt != 0) ? dt - 1 : 0;
        // interpolate between expiries
        vector<Real> tmpVars(2);
        vector<Time> xAxis;
        xAxis.push_back(times_[dtPrev]);
        xAxis.push_back(times_[dt]);
        tmpVars[1] = getVarForStrike(strike, strikes_[dt], variances_[dt], interpolations_[dt]);
        tmpVars[0] = getVarForStrike(strike, strikes_[dtPrev], variances_[dtPrev], interpolations_[dtPrev]);
        LinearInterpolation tmpInterpolation(xAxis.begin(), xAxis.end(), tmpVars.begin());
        varReturn = tmpInterpolation(t);
    } else {
        // far end of expiries
        varReturn = getVarForStrike(strike, strikes_.back(), variances_.back(), interpolations_.back());
        varReturn = varReturn * t / times_.back(); // scale
    }
    return varReturn;
}
} // namespace QuantExt


/*******************************************************************************
 *
 * MIT License
 *
 * Copyright (c) 2020 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 *******************************************************************************/

#include <limits>
#include <cassert>
#include <miopen/solver.hpp>
#include <miopen/env.hpp>
#include <miopen/gcn_asm_utils.hpp>
#include <miopen/tensor.hpp>
#include <miopen/gemm_v2.hpp>

#include <boost/any.hpp>

#if(MIOPEN_BACKEND_HIP && MIOPEN_USE_ROCBLAS)
#include <miopen/conv/data_invoke_params.hpp>
#define WORKAROUND_SWDEV_203031 1 // See also issues #2075, #2067
#endif

namespace miopen {
namespace solver {
MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_F2X3)
MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_F3X3)
MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_F4X3)
MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_F5X3)
MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_F6X3)
MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_WORKSPACE_MAX)
MIOPEN_DECLARE_ENV_VAR(MIOPEN_CONV_PRECISE_ROCBLAS_TIMING)

// Introduces a number of shader-specific aliases (names) in the current scope at zero cost.
// These names represent shader parameters, e.g. shader C is batch_size etc and useful for
// programming.
#define DEFINE_GETXFORMHWSIZE(params)                                                        \
    const auto                                                                               \
        wino_xform_h =                                                                       \
            solver::ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>:: \
                GetSolverWinoXformHWSize(),                                                  \
        wino_xform_w =                                                                       \
            solver::ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>:: \
                GetSolverWinoXformHWSize();

#define DEFINE_SHADER_ALIASES(params)                      \
    const auto group_cnt = (params).group_counts;          \
    const auto& N        = (params).batch_sz;              \
    const auto& K        = (params).n_outputs / group_cnt; \
    const auto& C        = (params).n_inputs / group_cnt;  \
    const auto& R        = (params).kernel_size_h;         \
    const auto& S        = (params).kernel_size_w;         \
    const auto& H        = (params).in_height;             \
    const auto& W        = (params).in_width;              \
    const auto& out_H    = (params).out_height;            \
    const auto& out_W    = (params).out_width;

#if(MIOPEN_BACKEND_HIP && MIOPEN_USE_ROCBLAS)
#define GENERATE_MAIN_OPTIONS(options)                                                             \
    GenerateClangDefsym((options), "acc_type", 1);                                                 \
    GenerateClangDefsym((options), "buf_type", (params.IsFp32() ? 1 : (params.IsFp16() ? 2 : 3))); \
    GenerateClangDefsym((options), "ROCM_METADATA_VERSION", 5);                                    \
    GenerateClangDefsym((options), "xformx_o_size", WinoDataW);                                    \
    GenerateClangDefsym((options), "xformy_o_size", WinoDataH);                                    \
    GenerateClangDefsym((options), "xformx_d_size", wino_xform_w);                                 \
    GenerateClangDefsym((options), "xformy_d_size", wino_xform_h);                                 \
    GenerateClangDefsym((options), "xformx_f_size", WinoFilterW);                                  \
    GenerateClangDefsym((options), "xformy_f_size", WinoFilterH);                                  \
    GenerateClangDefsym((options), "fdilation_w", params.kernel_stride_w);                         \
    GenerateClangDefsym((options), "fdilation_h", params.kernel_stride_h);

static inline size_t Ceil(const size_t v, const size_t m)
{
    assert(m > 0);
    return (v + m - 1) / m;
}
#endif

template <int WinoDataH, int WinoFilterH, int WinoDataW, int WinoFilterW>
WinogradBufferInfo<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>
GetWinoBuffer(const ConvolutionContext& params, const ConvWinoBuffType buff_type)
{
    DEFINE_GETXFORMHWSIZE(params)
    DEFINE_SHADER_ALIASES(params)

    WinogradBufferInfo<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW> Transform_info(
        N,
        K,
        C,
        group_cnt,
        out_H,
        out_W,
        R,
        S,
        (MemLayout_t::GCNHW),
        (ConvWinoXformType::N_GXhXw_C_Th_Tw),
        GetTypeSize(params.in_data_type),
        buff_type,
        wino_xform_h,
        wino_xform_w);

    (void)H;
    (void)W;
    return Transform_info;
}

template <int WinoDataH, int WinoFilterH, int WinoDataW, int WinoFilterW>
bool ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>::IsApplicable(
    const ConvolutionContext& params) const
{
// HIP backend required for sending ptr (buffer + offset)
// ROCBLAS for GEMM step

#if(MIOPEN_BACKEND_HIP && MIOPEN_USE_ROCBLAS)
    static const int wino_data_tile   = std::max(WinoDataH, WinoDataW);
    static const int wino_filter_tile = std::max(WinoFilterH, WinoFilterW);

    const std::string name = params.GetStream().GetDeviceName();

    if(wino_data_tile == 6 && wino_filter_tile == 3)
        if(miopen::IsDisabled(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_F6X3{}))
            return false;
    if(wino_data_tile == 5 && wino_filter_tile == 3)
        if(miopen::IsDisabled(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_F5X3{}))
            return false;
    if(wino_data_tile == 4 && wino_filter_tile == 3)
        if(miopen::IsDisabled(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_F4X3{}))
            return false;
    if(wino_data_tile == 3 && wino_filter_tile == 3)
        if(miopen::IsDisabled(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_F3X3{}))
            return false;
    if(wino_data_tile == 2 && wino_filter_tile == 3)
        if(miopen::IsDisabled(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_F2X3{}))
            return false;
    if(!params.use_asm_kernels)
        return false;
    if(!params.rmv.IsV3())
        return false;
    if(!params.Is2d())
        return false;
    if(params.direction.IsBackwardWrW())
        return false;
    if(!params.IsFp32())
        return false;

    if(!(StartsWith(name, "gfx9")))
        return false;

    {
        std::size_t limit = miopen::Value(MIOPEN_DEBUG_AMD_WINOGRAD_MPASS_WORKSPACE_MAX{});
#if WORKAROUND_SWDEV_203031
        if(limit == 0)
        {
            if(name == "gfx900" ||
               (name == "gfx906" && params.GetStream().GetMaxComputeUnits() <= 60))
                limit = 2000000000ULL; // ~1.862 GiB
            else
                limit = std::numeric_limits<std::size_t>::max();
        }
#else
        if(limit == 0)
            limit = std::numeric_limits<std::size_t>::max();
#endif
        if(limit != std::numeric_limits<std::size_t>::max())
        {
            const auto required = GetWorkspaceSize(params);
            MIOPEN_LOG_I2("Workspace required: " << required << ", limit: " << limit);
            if(required > limit)
                return false;
        }
    }

    // int offset for Workspace buffers.
    if(((GetWinoBuffer<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>(params,
                                                                       ConvWinoBuffType::Input))
                .buff_info.total_byte_size /
            GetTypeSize(params.in_data_type) +
        (GetWinoBuffer<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>(params,
                                                                       ConvWinoBuffType::Output))
                .buff_info.total_byte_size /
            GetTypeSize(params.in_data_type)) >= (1LL << 31))
    {
        return false;
    }

    assert(params.weights_layout.length() == 0); // _weights_layout is not supported yet

    {
        unsigned int const waves_in_group = 512 / wave_size;
        unsigned int const tiles_per_wave = 8;
        auto const tiles_per_group        = waves_in_group * tiles_per_wave / 2;
        auto const n_groups               = params.GetStream().GetMaxComputeUnits();
        auto const tiles_step             = tiles_per_group * n_groups;
        if(tiles_step >= std::pow(2, 16))
            return false;
    }
    {
        unsigned long long const f_tiles_hw =
            Ceil(params.kernel_size_w, WinoFilterW) * Ceil(params.kernel_size_h, WinoFilterH);
        unsigned long long const d_tiles_hw =
            Ceil(params.out_width, WinoDataW) * Ceil(params.out_height, WinoDataH);
        unsigned long long const G_N_C_xTile =
            params.n_inputs * params.group_counts * params.batch_sz * d_tiles_hw;
        unsigned long long const G_K_C_xTile =
            params.n_inputs * params.group_counts * params.n_outputs * f_tiles_hw;
        unsigned long long const G_K_N_xTile =
            params.batch_sz * params.group_counts * params.n_outputs * d_tiles_hw;
        if(G_N_C_xTile >= std::pow(2, 24) || G_K_C_xTile >= std::pow(2, 24) ||
           G_K_N_xTile >= std::pow(2, 24))
            return false;
    }

    // clang-format off
    bool ok = (
           (params.kernel_size_w == WinoFilterW
               && params.kernel_size_h == WinoFilterH)
        && (params.kernel_stride_w == 1)
        && params.kernel_stride_h == params.kernel_stride_w
        && params.kernel_dilation_w == 1
        && params.kernel_dilation_h == 1
        && params.batch_sz < std::pow(2, 16)
        && params.n_inputs < std::pow(2, 16)
        && params.n_outputs < std::pow(2, 16)
        && params.out_height < std::pow(2, 16)
        && params.out_width < std::pow(2, 16)
        && params.bias == 0
        && params.in_layout == "NCHW");
    // clang-format on
    return ok;
#else
    (void)params;
    return false;
#endif
}

template <int WinoDataH, int WinoFilterH, int WinoDataW, int WinoFilterW>
size_t ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>::GetWorkspaceSize(
    const ConvolutionContext& params) const
{
    return (GetWinoBuffer<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>(params,
                                                                          ConvWinoBuffType::Input))
               .buff_info.total_byte_size +
           (GetWinoBuffer<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>(params,
                                                                          ConvWinoBuffType::Output))
               .buff_info.total_byte_size +
           (GetWinoBuffer<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>(params,
                                                                          ConvWinoBuffType::Weight))
               .buff_info.total_byte_size;
}

template <int WinoDataH, int WinoFilterH, int WinoDataW, int WinoFilterW>
ConvSolution ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>::GetSolution(
    const ConvolutionContext& params) const
{

    ConvSolution result;
    result.workspce_sz = GetWorkspaceSize(params);
#if(MIOPEN_BACKEND_HIP && MIOPEN_USE_ROCBLAS)

    const int n_groups = params.GetStream().GetMaxComputeUnits();
    DEFINE_GETXFORMHWSIZE(params)
    const std::vector<size_t> l_wk{512, 1, 1};
    const size_t g_wk_0 = n_groups * l_wk[0];
    const std::vector<size_t> g_wk{g_wk_0, 1, 1};

    std::ostringstream options_in;
    GENERATE_MAIN_OPTIONS(options_in)
    GenerateClangDefsym(options_in, "xform_mirror", 0);

    std::ostringstream options_filter;
    GENERATE_MAIN_OPTIONS(options_filter)
    GenerateClangDefsym(options_filter, "xform_mirror", params.direction.IsBackwardData());

    std::ostringstream options_out;
    GENERATE_MAIN_OPTIONS(options_out)
    GenerateClangDefsym(options_out, "xform_mirror", 0);

    KernelInfo InTransform{
        options_in.str(),
        l_wk,
        g_wk,
        ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>::GetSolverFileNames(
            0),
        ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>::
            GetSolverKernelNames(0),
    };

    KernelInfo FilterTransform{
        options_filter.str(),
        l_wk,
        g_wk,
        ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>::GetSolverFileNames(
            1),
        ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>::
            GetSolverKernelNames(1),
    };

    KernelInfo OutTransform{
        options_out.str(),
        l_wk,
        g_wk,
        ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>::GetSolverFileNames(
            2),
        ConvMPBidirectWinograd<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>::
            GetSolverKernelNames(2),
    };

    result.construction_params.push_back(InTransform);
    result.construction_params.push_back(FilterTransform);
    result.construction_params.push_back(OutTransform);

    const int pad_H = params.direction.IsForward() ? params.pad_h : params.GetBackwardPadH();
    const int pad_W = params.direction.IsForward() ? params.pad_w : params.GetBackwardPadW();
    DEFINE_SHADER_ALIASES(params)
    BuffInfo in_buff(GetGroupConvLayout(GetMemLayout_t(params.in_layout), true),
                     N,
                     C,
                     H,
                     W,
                     group_cnt,
                     GetTypeSize(params.in_data_type)),
        // cppcheck-suppress unreadVariable
        out_buff(GetGroupConvLayout(GetMemLayout_t(params.out_layout), true),
                 N,
                 K,
                 out_H,
                 out_W,
                 group_cnt,
                 GetTypeSize(params.out_data_type)),
        // cppcheck-suppress unreadVariable
        weights_buff(GetGroupConvLayout(params.direction.IsForward()
                                            ? (MemLayout_t::NCHW)
                                            : GetSwappedNCLayout(MemLayout_t::NCHW),
                                        false),
                     K,
                     C,
                     R,
                     S,
                     group_cnt,
                     GetTypeSize(params.weights_data_type));

    auto wino_in = GetWinoBuffer<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>(
        params, ConvWinoBuffType::Input);
    auto wino_out = GetWinoBuffer<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>(
        params, ConvWinoBuffType::Output);
    auto wino_wei = GetWinoBuffer<WinoDataH, WinoFilterH, WinoDataW, WinoFilterW>(
        params, ConvWinoBuffType::Weight);

    int reserved       = 0;
    void* reserved_ptr = nullptr;
    int unused         = 0;

    size_t wino_in_offset = 0, wino_out_offset = wino_in.buff_info.total_byte_size,
           wino_wei_offset = wino_out_offset + wino_out.buff_info.total_byte_size;

    // GEMM
    int m = K, k = C,
        n   = wino_in.buff_info.size.nk * wino_in.buff_info.size.w * wino_in.buff_info.size.h;
    int lda = m, ldb = n, ldc = n;
    int batch_count       = wino_xform_h * wino_xform_w * group_cnt;
    long long int strideA = m * k * 1LL, strideB = k * n * 1LL, strideC = m * n * 1LL;
    float alpha = 1., beta = 0.0;
    const bool isColMajor = false, transA = true, transB = false;
    // clang-format off
    GemmDescriptor wino_gemm_desc{isColMajor,transA,transB,m,n,k,
        lda,ldb,ldc,batch_count,strideA,strideB,
        strideC,alpha,beta,params.in_data_type};

    result.invoker_factory = [=](std::vector<Kernel> kernels) {
        return [=](const Handle& handle, const boost::any& ctx) {
            const auto data_ctx = boost::any_cast<conv::DataInvokeParams>(ctx);
            const auto tensors  = data_ctx.tensors;
            Data_t workSpace = data_ctx.workSpace;
            float total_time    = 0;

            for(int i = 0, cur=0; i < 4; i++)
            {
                std::string kernel_name ;
                if(i == 2) // GEMM
                {
                        CallGemmStridedBatched(handle,
                            wino_gemm_desc,
                            workSpace,
                            static_cast<int>(wino_wei_offset / GetTypeSize(params.in_data_type)),
                            workSpace,
                            static_cast<int>(wino_in_offset / GetTypeSize(params.in_data_type)),
                            workSpace,
                            static_cast<int>(wino_out_offset / GetTypeSize(params.in_data_type)),
                            nullptr,
                            false,
                            GemmBackend_t::rocblas);
                    kernel_name = "WRW_WINO_GEMM: ";
                }
                else
                {
                    const auto kernel        = handle.Run(kernels[cur++]);
                    const BuffInfo* d_buf    = nullptr;
                    const BuffInfo* o_buf    = nullptr;
                    Data_t buff_out_addr     = nullptr;

                    auto const_buff_in_adr  = tensors.in;
                    auto buff_in_adr        = workSpace;
                    bool const_input        = false;
                    size_t buff_in_addr_offset = 0, buff_out_addr_offset = 0;
                    kernel_name = kernel.GetName();

                    if(i==0) // Input
                    {// Transform
                        d_buf               = &in_buff;
                        o_buf               = &(wino_in.buff_info);
                        const_buff_in_adr   = tensors.in;
                        buff_in_addr_offset = 0;
                        buff_out_addr        = workSpace;
                        buff_out_addr_offset = wino_in_offset;
                        const_input         = true;
                    }
                    else if(i==1) // filter
                    {// Transform
                        d_buf                = &weights_buff;
                        o_buf                = &(wino_wei.buff_info);
                        const_buff_in_adr    = tensors.w;
                        buff_in_addr_offset = 0;
                        buff_out_addr         = workSpace;
                        buff_out_addr_offset = wino_wei_offset;
                        const_input          = true;
                    }
                    else if (i==3)
                    { //Output
                        d_buf               = &(wino_out.buff_info);
                        o_buf               = &(out_buff);
                        buff_in_adr         = workSpace;
                        buff_in_addr_offset = wino_out_offset;
                        buff_out_addr        = tensors.out;
                        buff_out_addr_offset = 0;
                        const_input          = false;
                    }

                    const auto input_ptr = static_cast<const void*>(
                        static_cast<const char*>(const_input ? const_buff_in_adr : buff_in_adr) +
                        buff_in_addr_offset);
                    const auto output_ptr =
                        static_cast<void*>(static_cast<char*>(buff_out_addr) + buff_out_addr_offset);
                    // clang-format off
                    MIOPEN_LOG_I2(" N=" << N << " G=" << group_cnt << " C=" << C << " H=" << H << " W=" << W << " K=" << K
                        << " n_groups=" << n_groups << " R=" << R << " S=" << S
                        << " pad_H=" << pad_H << " pad_W=" << pad_W << " out_H=" << out_H << " out_W=" << out_W
                        << " d_buf.byte_stride.nk=" << d_buf->byte_stride.nk << " d_buf->.byte_stride.c=" << d_buf->byte_stride.c
                        << " d_buf->.byte_stride.h=" << d_buf->byte_stride.h << " d_buf->.byte_stride.w=" << d_buf->byte_stride.w
                        << " o_buf->byte_stride.nk=" << o_buf->byte_stride.nk << " o_buf->byte_stride.c=" << o_buf->byte_stride.c
                        << " o_buf.byte_stride.h="  << o_buf->byte_stride.h <<  " o_buf->byte_stride.w=" << o_buf->byte_stride.w
                        << " d_buf->.byte_stride.g=" << d_buf->byte_stride.g  << " o_buf->byte_stride.g="  << o_buf->byte_stride.g);
                    // clang-format on
                    kernel(N,
                           C,
                           H,
                           W,
                           K,
                           n_groups,
                           unused,
                           reserved,
                           input_ptr,
                           reserved_ptr,
                           output_ptr,
                           reserved_ptr, // Unused return_addr.
                           R,
                           S,
                           pad_H, // Like Fwd wino.
                           pad_W,
                           out_H,
                           out_W,
                           reserved_ptr, // Unused bias_addr.
                           reserved,     // Unused relu_alpha.
                           d_buf->byte_stride.nk,
                           d_buf->byte_stride.c,
                           d_buf->byte_stride.h,
                           d_buf->byte_stride.w,
                           unused,
                           unused,
                           unused,
                           unused,
                           o_buf->byte_stride.nk,
                           o_buf->byte_stride.c,
                           o_buf->byte_stride.h,
                           o_buf->byte_stride.w,
                           group_cnt,
                           d_buf->byte_stride.g,
                           unused,
                           o_buf->byte_stride.g);
                }
                if(handle.IsProfilingEnabled())
                {
                    float cur_time = handle.GetKernelTime();
                    MIOPEN_LOG_I2(kernel_name << ": " << cur_time);

                    if(i < 3)
                        total_time += cur_time;
                    else
                        handle.AccumKernelTime(total_time);
                }
            }
        };
    };
    return result;
#else
    (void)params;
    MIOPEN_THROW(miopenStatusBadParm, "ConvMPBidirectWinograd Unsupported ");
#endif
}
template struct ConvMPBidirectWinograd<2, 3>;
template struct ConvMPBidirectWinograd<3, 3>;
template struct ConvMPBidirectWinograd<4, 3>;
template struct ConvMPBidirectWinograd<5, 3>;
template struct ConvMPBidirectWinograd<6, 3>;

} // namespace solver
} // namespace miopen


#pragma once
#ifndef CANNON_RAY_HITTABLE_H
#define CANNON_RAY_HITTABLE_H
/*!
 * \file cannon/ray/hittable.hpp
 * \brief File containing useful classes for geometry hittable by rays.
 */

#include <memory>

#include <Eigen/Dense>

#include <cannon/utils/class_forward.hpp>

using namespace Eigen;

namespace cannon {
  namespace ray {

    CANNON_CLASS_FORWARD(Ray);
    CANNON_CLASS_FORWARD(Material);
    CANNON_CLASS_FORWARD(Aabb);

    /*!
     * \brief Structure recording ray intersection information.
     */
    struct hit_record {
      Vector3d p; //!< Point where ray hit geometry
      Vector3d normal; //!< Normal at hit point
      std::shared_ptr<Material> mat_ptr; //!< Pointer to material for hit surface

      double t; //!< Distance of hit along ray
      double u; //!< Horizontal surface texture coordinate at hit point
      double v; //!< Vertical surface texture coordinate at hit point

      bool front_face; //!< Whether the ray originated from outside the geometry

      /*!
       * Method to store normal with direction always opposite intersecting ray.
       */
      void set_face_normal(const Ray& r, const Vector3d& outward_normal);
    };

    /*!
     * \brief Abstract class representing a hittable geometry.
     */
    class Hittable {

      public:

        /*!
         * Default constructor
         */
        Hittable() :
          object_to_world_(std::make_shared<Affine3d>(Affine3d::Identity())),
          world_to_object_(std::make_shared<Affine3d>(Affine3d::Identity())) {}

        /*!
         * Constructor initializing transforms.
         */
        Hittable(std::shared_ptr<Affine3d> object_to_world) :
          object_to_world_(object_to_world),
          world_to_object_(std::make_shared<Affine3d>(object_to_world->inverse())) {}

        /*!
         * Method to test whether this geometry is hit by the input ray in
         * world space between distance t_min and t_max.
         *
         * \param r The ray to check for intersection.
         * \param t_min Minimal distance along the ray to register an intersection.
         * \param t_max Maximum distance along the ray to register an intersection.
         * \param rec Hit record in which to put details about the intersection.
         *
         * \returns Whether the ray intersects this geometry.
         */
        virtual bool hit(const Ray& r, double t_min, double t_max, hit_record& rec) const;

        /*!
         * Method to test whether this geometry is hit by the input ray in
         * object space between distance t_min and t_max.
         *
         * \param r The ray to check for intersection.
         * \param t_min Minimal distance along the ray to register an intersection.
         * \param t_max Maximum distance along the ray to register an intersection.
         * \param rec Hit record in which to put details about the intersection.
         *
         * \returns Whether the ray intersects this geometry.
         */
        virtual bool object_space_hit(const Ray& r, double t_min, double t_max, hit_record& rec) const = 0;

        /*!
         * Method to generate world-space bounding box for this geometry between input times.
         *
         * \param time_0 Start time for bounding box.
         * \param time_1 End time for bounding box.
         * \param output_box Place to put created bounding box.
         *
         * \returns Whether bounding box exists for this geometry, allowing for infinite geometry.
         */
        virtual bool bounding_box(double time_0, double time_1, Aabb& output_box) const;

        /*!
         * Method to generate object-space bounding box for this geometry between input times.
         *
         * \param time_0 Start time for bounding box.
         * \param time_1 End time for bounding box.
         * \param output_box Place to put created bounding box.
         *
         * \returns Whether bounding box exists for this geometry, allowing for infinite geometry.
         */
        virtual bool object_space_bounding_box(double time_0, double time_1, Aabb& output_box) const = 0;

      public:
        std::shared_ptr<Affine3d> object_to_world_;
        std::shared_ptr<Affine3d> world_to_object_;

    };

  } // namespace ray
} // namespace cannon

#endif /* ifndef CANNON_RAY_HITTABLE_H */


#ifndef ELASTY_CLOTH_SIM_OBJECT_HPP
#define ELASTY_CLOTH_SIM_OBJECT_HPP

#include <Eigen/Core>
#include <Eigen/Geometry>
#include <elasty/sim-object.hpp>
#include <string>

namespace elasty
{
    class ClothSimObject : public SimObject
    {
    public:
        enum class InPlaneStrategy
        {
            EdgeDistance,
            ContinuumTriangle,
            Both,
        };

        enum class OutOfPlaneStrategy
        {
            Bending,
            IsometricBending,
            Cross,
        };

        using TriangleList = Eigen::Matrix<std::int32_t, Eigen::Dynamic, 3, Eigen::RowMajor>;
        using UvList       = Eigen::Matrix<float, Eigen::Dynamic, 2 * 3, Eigen::RowMajor>;

        ClothSimObject(const unsigned           resolution,
                       const double             in_plane_stiffness      = 1.000,      ///< PBD
                       const double             in_plane_compliance     = 0.001,      ///< XPBD
                       const double             out_of_plane_stiffness  = 0.100,      ///< PBD
                       const double             out_of_plane_compliance = 0.010,      ///< XPBD
                       const double             dt                      = 1.0 / 60.0, ///< XPBD
                       const Eigen::Affine3d&   transform               = Eigen::Affine3d::Identity(),
                       const InPlaneStrategy    in_plane_strategy       = InPlaneStrategy::Both,
                       const OutOfPlaneStrategy out_of_plane_strategy   = OutOfPlaneStrategy::IsometricBending);

        TriangleList m_triangle_list;
        UvList       m_uv_list;

        bool hasUv() const { return m_uv_list.size() != 0; }

        /// \brief Getter of the areas of the triangles.
        const Eigen::VectorXd& getAreaList() const { return m_area_list; }

        /// \brief Apply aerodynamic forces to the relevant particles.
        ///
        /// \details The aerodynamics model is based on [Wilson+14]. This member function is intended to be called in
        /// the setExternalForces() routine in engines.
        ///
        /// \param drag_coeff The coefficient for drag forces. This value should be larger than lift_coeff.
        ///
        /// \param lift_coeff The coefficient for lift forces. This value should be smaller than drag_coeff.
        void applyAerodynamicForces(const Eigen::Vector3d& global_velocity = Eigen::Vector3d::Zero(),
                                    const double           drag_coeff      = 0.100,
                                    const double           lift_coeff      = 0.060);

    private:
        Eigen::VectorXd m_area_list;

        /// \brief Calculate areas of the triangles.
        ///
        /// \details The result will be stored in m_area_list. This function needs to be called only once.
        void calculateAreas();
    };
} // namespace elasty

#endif // ELASTY_CLOTH_SIM_OBJECT_HPP


#include <NTL/ctools.h>

#include <cstdlib>
#include <immintrin.h>
#include <iostream>

#if (!defined(__GNUC__) || !defined(__x86_64__) || !defined(__AVX2__))
#error "AVX2 with FMA not supported"
#endif

#if (NTL_BITS_PER_LONG != 64 || NTL_BITS_PER_INT != 32 || NTL_DOUBLE_PRECISION != 53)
#error "AVX2 with FMA not supported"
// sanity check -- code that uses this feature also relies on this
#endif

#ifndef NTL_HAVE_ALIGNED_ARRAY
#error "AVX2 with FMA not supported"
#endif

using namespace std;

void fun(double * x, const double *a, const double *b)
{
   __m256d xvec, avec, bvec, cvec;

   avec = _mm256_load_pd(a);
   bvec = _mm256_load_pd(b);
   xvec = _mm256_load_pd(x);

   xvec = _mm256_fmadd_pd(avec, bvec, xvec);

   _mm256_store_pd(x, xvec);
}
int main()
{
   NTL_AVX_LOCAL_ARRAY(vp, double, 12);

   double *a = vp + 0*4;
   double *b = vp + 1*4;
   double *x = vp + 2*4;

   a[0] = atoi("1");
   a[1] = atoi("2");
   a[2] = atoi("3");
   a[3] = atoi("4");

   b[0] = atoi("2");
   b[1] = atoi("3");
   b[2] = atoi("4");
   b[3] = atoi("5");

   x[0] = atoi("3");
   x[1] = atoi("4");
   x[2] = atoi("5");
   x[3] = atoi("6");

   fun(x, a, b);

   if (x[0] == 5 && x[1] == 10 && x[2] == 17 && x[3] == 26)
      return 0;
   else
      return -1;
}


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2007 Ferdinando Ametrano
 Copyright (C) 2007 Marco Bianchetti
 Copyright (C) 2006, 2007 Giorgio Facchinetti

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

#include <ql/termstructures/volatility/swaption/cmsmarket.hpp>
#include <ql/termstructures/yieldtermstructure.hpp>
#include <ql/cashflows/cashflows.hpp>
#include <ql/instruments/makecms.hpp>
#include <ql/quotes/simplequote.hpp>
#include <ql/indexes/swapindex.hpp>
#include <ql/instruments/swap.hpp>

#include <boost/make_shared.hpp>

using std::vector;
using boost::shared_ptr;

namespace QuantLib {

    CmsMarket::CmsMarket(
        const vector<Period>& swapLengths,
        const vector<shared_ptr<SwapIndex> >& swapIndexes,
        const shared_ptr<IborIndex>& iborIndex,
        const vector<vector<Handle<Quote> > >& bidAskSpreads,
        const vector<shared_ptr<CmsCouponPricer> >& pricers,
        const Handle<YieldTermStructure>& discountingTS)
    : swapLengths_(swapLengths),
      swapIndexes_(swapIndexes),
      iborIndex_(iborIndex),
      bidAskSpreads_(bidAskSpreads),
      pricers_(pricers),
      discTS_(discountingTS),

      nExercise_(swapLengths_.size()),
      nSwapIndexes_(swapIndexes_.size()),
      swapTenors_(nSwapIndexes_),

      spotFloatLegNPV_(nExercise_, nSwapIndexes_),
      spotFloatLegBPS_(nExercise_, nSwapIndexes_),

      mktBidSpreads_(nExercise_, nSwapIndexes_),
      mktAskSpreads_(nExercise_, nSwapIndexes_),

      mktSpreads_(nExercise_, nSwapIndexes_),
      mdlSpreads_(nExercise_, nSwapIndexes_),
      errSpreads_(nExercise_, nSwapIndexes_),

      mktSpotCmsLegNPV_(nExercise_, nSwapIndexes_),
      mdlSpotCmsLegNPV_(nExercise_, nSwapIndexes_),
      errSpotCmsLegNPV_(nExercise_, nSwapIndexes_),

      mktFwdCmsLegNPV_(nExercise_, nSwapIndexes_),
      mdlFwdCmsLegNPV_(nExercise_, nSwapIndexes_),
      errFwdCmsLegNPV_(nExercise_, nSwapIndexes_),

      spotSwaps_(nExercise_, vector<shared_ptr<Swap> >(nSwapIndexes_)),
      fwdSwaps_(nExercise_, vector<shared_ptr<Swap> >(nSwapIndexes_))
    {
        QL_REQUIRE(2 * nSwapIndexes_ == bidAskSpreads[0].size(),
                   "2*nSwapIndexes_ (" << 2 * nSwapIndexes_
                                       << ") != bidAskSpreads columns() ("
                                       << bidAskSpreads[0].size() << ")");
        QL_REQUIRE(nExercise_ == bidAskSpreads.size(),
                   "nExercise_ (" << nExercise_ << ") != bidAskSpreads rows() ("
                                  << bidAskSpreads.size() << ")");
        QL_REQUIRE(nSwapIndexes_ == pricers.size(),
                   "nSwapIndexes_ (" << nSwapIndexes_ << ") != pricers ("
                                     << pricers_.size() << ")");

        for (Size j=0; j<nSwapIndexes_; ++j) {
            swapTenors_[j] = swapIndexes_[j]->tenor();
            // pricers
            registerWith(pricers_[j]);
            for (Size i=0; i<nExercise_; ++i) {
                // market Spread
                registerWith(bidAskSpreads_[i][j*2]);
                registerWith(bidAskSpreads_[i][j*2+1]);
            }
        }

        Period start(0, Years);
        for (Size i=0; i<nExercise_; ++i) {
            if (i>0) start = swapLengths_[i-1];
            for (Size j=0; j<nSwapIndexes_; ++j) {
                // never evaluate the spot swap, only its ibor floating leg
                spotSwaps_[i][j] = MakeCms(swapLengths_[i],
                                           swapIndexes_[j],
                                           iborIndex_, 0.0,
                                           Period())
                                   .operator shared_ptr<Swap>();
                fwdSwaps_[i][j]  = MakeCms(swapLengths_[i]-start,
                                           swapIndexes_[j],
                                           iborIndex_, 0.0,
                                           start)
                                   .withCmsCouponPricer(pricers_[j])
                                   .withDiscountingTermStructure(discTS_)
                                   .operator shared_ptr<Swap>();
            }
        }
        // probably useless
        performCalculations();
     }

    void CmsMarket::performCalculations() const {
        for (Size j=0; j<nSwapIndexes_; ++j) {
          Real mktPrevPart = 0.0, mdlPrevPart = 0.0;
          for (Size i=0; i<nExercise_; ++i) {

            // **** market

            mktBidSpreads_[i][j] = bidAskSpreads_[i][j*2]->value();
            mktAskSpreads_[i][j] = bidAskSpreads_[i][j*2+1]->value();
            mktSpreads_[i][j] = (mktBidSpreads_[i][j]+mktAskSpreads_[i][j])/2;

            const Leg& spotFloatLeg = spotSwaps_[i][j]->leg(1);
            spotFloatLegNPV_[i][j] = CashFlows::npv(spotFloatLeg,
                                                    **discTS_,
                                                    false, discTS_->referenceDate());
            spotFloatLegBPS_[i][j] = CashFlows::bps(spotFloatLeg,
                                                    **discTS_,
                                                    false, discTS_->referenceDate());

            // imply the spot CMS leg NPV from the spot ibor floating leg NPV
            mktSpotCmsLegNPV_[i][j] = -(spotFloatLegNPV_[i][j] +
                                spotFloatLegBPS_[i][j]*mktSpreads_[i][j]/1e-4);
            // fwd CMS legs can be computed as differences between spot legs
            mktFwdCmsLegNPV_[i][j] = mktSpotCmsLegNPV_[i][j] - mktPrevPart;
            mktPrevPart = mktSpotCmsLegNPV_[i][j];

            // **** model

            // calculate the forward swap (the time consuming part)
            mdlFwdCmsLegNPV_[i][j] = fwdSwaps_[i][j]->legNPV(0);
            errFwdCmsLegNPV_[i][j] = mdlFwdCmsLegNPV_[i][j] -
                                                mktFwdCmsLegNPV_[i][j];

            // spot CMS legs can be computed as incremental sum of forward legs
            mdlSpotCmsLegNPV_[i][j] = mdlPrevPart + mdlFwdCmsLegNPV_[i][j];
            mdlPrevPart = mdlSpotCmsLegNPV_[i][j];
            errSpotCmsLegNPV_[i][j] = mdlSpotCmsLegNPV_[i][j] -
                                                mktSpotCmsLegNPV_[i][j];

            // equilibriums spread over ibor leg
            Real npv = spotFloatLegNPV_[i][j] + mdlSpotCmsLegNPV_[i][j];
            mdlSpreads_[i][j] = - npv/spotFloatLegBPS_[i][j]*1e-4;
            errSpreads_[i][j] = mdlSpreads_[i][j] - mktSpreads_[i][j];
          }
        }
    }

    void CmsMarket::reprice(const Handle<SwaptionVolatilityStructure> &v,
                            Real meanReversion) {
        Handle<Quote> meanReversionQuote(
            shared_ptr<Quote>(boost::make_shared<SimpleQuote>(meanReversion)));
        for (Size j = 0; j < nSwapIndexes_; ++j) {
            // ??
            // set new volatility structure and new mean reversion
            pricers_[j]->setSwaptionVolatility(v);
            if (meanReversion != Null<Real>()) {
                boost::shared_ptr<MeanRevertingPricer> p =
                    boost::dynamic_pointer_cast<MeanRevertingPricer>(
                        pricers_[j]);
                QL_REQUIRE(p != NULL, "mean reverting pricer required at index "
                                          << j);
                p->setMeanReversion(meanReversionQuote);
            }
        }
        performCalculations();
    }

    Real CmsMarket::weightedFwdNpvError(const Matrix& w) {
        performCalculations();
        return weightedMean(errFwdCmsLegNPV_, w);
    }

    Real CmsMarket::weightedSpotNpvError(const Matrix& w) {
        performCalculations();
        return weightedMean(errSpotCmsLegNPV_, w);
    }

    Real CmsMarket::weightedSpreadError(const Matrix& w) {
        performCalculations();
        return weightedMean(errSpreads_, w);
    }

    // array of errors to be used by Levenberg-Marquardt optimization

    Disposable<Array> CmsMarket::weightedFwdNpvErrors(const Matrix& w) {
        performCalculations();
        return weightedMeans(errFwdCmsLegNPV_, w);
    }

    Disposable<Array> CmsMarket::weightedSpotNpvErrors(const Matrix& w) {
        performCalculations();
        return weightedMeans(errSpotCmsLegNPV_, w);
    }

    Disposable<Array> CmsMarket::weightedSpreadErrors(const Matrix& w) {
        performCalculations();
        return weightedMeans(errSpreads_, w);
    }

    Real CmsMarket::weightedMean(const Matrix& var,
                                 const Matrix& w) {
        Real mean = 0.0;
        for (Size i=0; i<nExercise_; ++i) {
            for (Size j=0; j<nSwapIndexes_; ++j) {
                mean += w[i][j]*var[i][j]*var[i][j];
            }
        }
        mean = std::sqrt(mean/(nExercise_*nSwapIndexes_));
        return mean;
    }

    Disposable<Array> CmsMarket::weightedMeans(const Matrix& var,
                                               const Matrix& w) {
        Array weightedVars(nExercise_*nSwapIndexes_);
        for (Size i=0; i<nExercise_; ++i) {
            for (Size j=0; j<nSwapIndexes_; ++j) {
                weightedVars[i*nSwapIndexes_+j] = std::sqrt(w[i][j])*var[i][j];
            }
        }
        return weightedVars;
    }

    Matrix CmsMarket::browse() const {
        calculate();
        //Matrix result(nExercise_*nSwapIndexes_, 15);
        Matrix result(nExercise_*nSwapIndexes_, 14);
            for (Size j=0; j<nSwapIndexes_; ++j) {
                for (Size i=0; i<nExercise_; ++i) {
                result[j*nExercise_+i][0] = swapTenors_[j].length();
                result[j*nExercise_+i][1] = swapLengths_[i].length();

                // Spreads
                result[j*nExercise_+i][2] = mktBidSpreads_[i][j]*10000;
                result[j*nExercise_+i][3] = mktAskSpreads_[i][j]*10000;
                result[j*nExercise_+i][4] = mktSpreads_[i][j]*10000;
                result[j*nExercise_+i][5] = mdlSpreads_[i][j]*10000;
                result[j*nExercise_+i][6] = errSpreads_[i][j]*10000;
                if (mdlSpreads_[i][j]>mktAskSpreads_[i][j])
                    result[j*nExercise_+i][7] = (mdlSpreads_[i][j] -
                                                mktAskSpreads_[i][j])*10000;
                else if (mdlSpreads_[i][j]<mktBidSpreads_[i][j])
                    result[j*nExercise_+i][7] = (mktBidSpreads_[i][j] -
                                                mdlSpreads_[i][j])*10000;
                else
                    result[j*nExercise_+i][7] = 0.0;

                // spot CMS Leg NPVs
                result[j*nExercise_+i][ 8] = mktSpotCmsLegNPV_[i][j];
                result[j*nExercise_+i][ 9] = mdlSpotCmsLegNPV_[i][j];
                result[j*nExercise_+i][10] = errSpotCmsLegNPV_[i][j];

                // forward CMS Leg NPVs
                result[j*nExercise_+i][11] = mktFwdCmsLegNPV_[i][j];
                result[j*nExercise_+i][12] = mdlFwdCmsLegNPV_[i][j];
                result[j*nExercise_+i][13] = errFwdCmsLegNPV_[i][j];
            }
        }
        return result;
    }
}


/*******************************************************************************
*
*  Filename    : MCCompare.cc
*  Description : Simple Comparison graphs for MC sample Comparison
*  Author      : Yi-Mu "Enoch" Chen [ ensc@hep1.phys.ntu.edu.tw ]
*
*******************************************************************************/
#include "TstarAnalysis/SignalMCStudy/interface/MCResMgr.hpp"

#include <boost/program_options.hpp>
#include <iostream>
#include <string>
#include <vector>

using namespace std;
namespace opt = boost::program_options;

/*******************************************************************************
*   Main control flow
*******************************************************************************/
int
main( int argc, char* argv[] )
{
  opt::options_description desc( "options for mc resolution comparison" );
  desc.add_options()
    ( "mass,m", opt::value<string>(), "which mass point to compare" );
  ;
  opt::variables_map vm;
  store( parse_command_line( argc, argv, desc ), vm );
  opt::notify( vm );

  cout << "Running MC resolution plots" << endl;

  MCResMgr* mgr = new MCResMgr( vm["mass"].as<string>() );

  MakeMCResPlot( mgr );

  delete mgr;
  return 0;
}


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file qle/termstructures/swaptionvolatilityconverter.hpp
    \brief Convert swaption volatilities from one type to another
    \ingroup termstructures
*/

#ifndef quantext_swaptionvolatilityconverter_hpp
#define quantext_swaptionvolatilityconverter_hpp

#include <ql/indexes/iborindex.hpp>
#include <ql/indexes/swapindex.hpp>
#include <ql/termstructures/volatility/swaption/swaptionvolmatrix.hpp>

#include <boost/shared_ptr.hpp>

namespace QuantExt {
using namespace QuantLib;
using std::vector;

//! Container for holding swap conventions needed by the SwaptionVolatilityConverter
//! \ingroup termstructures
class SwapConventions {
public:
    //! Constructor
    SwapConventions(Natural settlementDays, const Period& fixedTenor, const Calendar& fixedCalendar,
                    BusinessDayConvention fixedConvention, const DayCounter& fixedDayCounter,
                    const boost::shared_ptr<IborIndex>& floatIndex)
        : settlementDays_(settlementDays), fixedTenor_(fixedTenor), fixedCalendar_(fixedCalendar),
          fixedConvention_(fixedConvention), fixedDayCounter_(fixedDayCounter), floatIndex_(floatIndex) {}

    //! \name Inspectors
    //@{
    Natural settlementDays() const { return settlementDays_; }
    const Period& fixedTenor() const { return fixedTenor_; }
    const Calendar& fixedCalendar() const { return fixedCalendar_; }
    BusinessDayConvention fixedConvention() const { return fixedConvention_; }
    const DayCounter& fixedDayCounter() const { return fixedDayCounter_; }
    const boost::shared_ptr<IborIndex> floatIndex() const { return floatIndex_; }
    //@}

private:
    Natural settlementDays_;
    Period fixedTenor_;
    Calendar fixedCalendar_;
    BusinessDayConvention fixedConvention_;
    DayCounter fixedDayCounter_;
    boost::shared_ptr<IborIndex> floatIndex_;
};

//! Class that converts a supplied SwaptionVolatilityStructure to one of another type with possibly different shifts
/*! \ingroup termstructures

    \warning the converted <tt>SwaptionVolatilityStructure</tt> object has a fixed reference date eqaul to
             <tt>asof</tt> and fixed market data regardless of the type of reference date and market data of the
             original <tt>SwaptionVolatilityStructure</tt> that is passed in
*/
class SwaptionVolatilityConverter {
public:
    //! Construct from SwapConventions
    SwaptionVolatilityConverter(const Date& asof, const boost::shared_ptr<SwaptionVolatilityStructure>& svsIn,
                                const Handle<YieldTermStructure>& discount,
                                const Handle<YieldTermStructure>& shortDiscount,
                                const boost::shared_ptr<SwapConventions>& conventions,
                                const boost::shared_ptr<SwapConventions>& shortConventions,
                                const Period& conventionsTenor, const Period& shortConventionsTenor,
                                const VolatilityType targetType, const Matrix& targetShifts = Matrix());
    //! Construct from SwapIndex
    SwaptionVolatilityConverter(const Date& asof, const boost::shared_ptr<SwaptionVolatilityStructure>& svsIn,
                                const boost::shared_ptr<SwapIndex>& swapIndex,
                                const boost::shared_ptr<SwapIndex>& shortSwapIndex, const VolatilityType targetType,
                                const Matrix& targetShifts = Matrix());

    //! Method that returns the converted <tt>SwaptionVolatilityStructure</tt>
    boost::shared_ptr<SwaptionVolatilityStructure> convert() const;

    // Convert a single vol associated with a given swaption
    Real convert(const Date& expiry, const Period& swapTenor, Real strikeSpread, const DayCounter& volDayCounter,
                 VolatilityType outType, Real outShift = 0.0) const;

    //! Set implied volatility solver accuracy
    Real& accuracy() { return accuracy_; }
    //! Set implied volatility solver max evaluations
    Natural& maxEvaluations() { return maxEvaluations_; }

private:
    // Check inputs
    void checkInputs() const;

    // Method that is called depending on the type of svsIn
    boost::shared_ptr<SwaptionVolatilityStructure>
    convert(const boost::shared_ptr<SwaptionVolatilityMatrix>& svMatrix) const;

    const Date asof_;
    const boost::shared_ptr<SwaptionVolatilityStructure> svsIn_;
    Handle<YieldTermStructure> discount_, shortDiscount_;
    const boost::shared_ptr<SwapConventions> conventions_, shortConventions_;
    const Period conventionsTenor_, shortConventionsTenor_;
    const VolatilityType targetType_;
    const Matrix targetShifts_;

    // Variables for implied volatility solver
    Real accuracy_;
    Natural maxEvaluations_;
    static const Volatility minVol_;
    static const Volatility maxVol_;
    static const Real minVega_;
};
} // namespace QuantExt

#endif


#include <fstream>
#include <iostream>
#include <algorithm>
#include <thread>
#include <numeric>
#include <unordered_map>

#include "succinct/mapper.hpp"

#include "configuration.hpp"
#include "util.hpp"
#include "verify_collection.hpp"
#include "index_build_utils.hpp"

#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/algorithm/string.hpp>

#include "clustered_index_types.hpp"
#include "clustered_binary_freq_collection.hpp"
#include "indexed_sequence.hpp"

using ds2i::logger;

struct cluster_data {
    sequence_t reference;
    std::vector<sequence_t> partitions;
};

cluster_data
reference_selection(ds2i::clustered_binary_freq_collection const& input,
                    uint32_t cluster_size,
                    uint32_t MAX_REF_SIZE)
{
    std::unordered_map<posting_t, uint32_t> occs;
    auto occs_cend = occs.cend();
    ds2i::global_parameters params;
    std::vector<sequence_t> partitions;
    partitions.reserve(cluster_size);

    int elias_fano_type =
        ds2i::indexed_sequence::index_type::elias_fano;
    uint64_t universe = input.num_docs();

    for (const auto& plist : input) {

        auto const& docs = plist.docs;
        auto partition =
            ds2i::partitioned_sequence<>::compute_partition(docs.begin(),
                                                            universe,
                                                            docs.size(),
                                                            params);
        for (uint32_t i = 0; i < partition.size(); ++i) {
            auto const& v = ds2i::get_block_info(i, docs, partition);
            uint32_t lo = v.lo, hi = v.hi, n = v.n, u = v.u;
            int block_best_type =
                ds2i::indexed_sequence::best_type(params, u, n);
            if (block_best_type == elias_fano_type)
            {
                for (uint32_t k = lo; k < hi; ++k)
                {
                    auto doc_id = docs[k];
                    if (occs.find(doc_id) != occs_cend) {
                        occs[doc_id] += 1;
                    } else {
                        occs.emplace(doc_id, 1);
                    }
                }
            }
        }
        partitions.push_back(std::move(partition));
    }

    uint64_t ref_size = std::min(uint64_t(MAX_REF_SIZE), occs.size());

    typedef std::pair<posting_t, uint32_t> pair;
    std::vector<pair> pairs;
    pairs.reserve(occs.size());

    for (auto const& p: occs) {
        pairs.emplace_back(p.first, p.second);
    }

    std::sort(pairs.begin(), pairs.end(),
        [&](pair const& p1,
            pair const& p2) {
            return p1.second > p2.second;
        });

    sequence_t ref;
    ref.reserve(ref_size);
    for (uint32_t i = 0; i < ref_size; ++i) {
        ref.push_back(pairs[i].first);
    }
    std::sort(ref.begin(), ref.end());

    std::cout << "reference length: " << ref.size() << std::endl;

    return {
        std::move(ref),
        std::move(partitions)
    };
}

void create_clustered_collection(ds2i::clustered_binary_freq_collection& input,
                                 ds2i::global_parameters const& params,
                                 const char* cluster_filename,
                                 const char* output_filename,
                                 bool check,
                                 uint32_t MAX_REF_SIZE)
{
    using namespace ds2i;
    uint64_t universe = input.num_docs();
    logger() << "Processing " << universe << " documents" << std::endl;
    double tick = get_time_usecs();
    double user_tick = get_user_time_usecs();
    clustered_opt_index::builder builder(universe, params);
    progress_logger plog;

    std::ifstream file(cluster_filename, std::ios_base::in | std::ios_base::binary);
    boost::iostreams::filtering_streambuf<boost::iostreams::input> inbuf;
    inbuf.push(boost::iostreams::gzip_decompressor());
    inbuf.push(file);
    std::istream instream(&inbuf);

    std::string line;
    uint32_t c = 1;
    std::vector<pos_t> docs_positions;
    std::vector<pos_t> freqs_positions;
    while (std::getline(instream, line))
    {
        std::vector<std::string> data;
        boost::split(data, line, boost::is_any_of(" "));
        uint32_t cluster_size = std::atoi(data.front().data());
        std::cout << "cluster size: " << cluster_size << std::endl;

        docs_positions.reserve(cluster_size);
        freqs_positions.reserve(cluster_size);

        std::for_each(++data.begin(), data.end(),
        [&docs_positions, &freqs_positions](std::string const& s)
        {
            pos_t p = std::stoull(s.data());
            docs_positions.push_back(p);
            freqs_positions.push_back(p - 2);
        });

        input.add_positions(docs_positions, freqs_positions);
        input.set_positions();

        auto const& cluster_data =
            reference_selection(input, cluster_size, MAX_REF_SIZE);

        auto const& reference = cluster_data.reference;
        auto const& partitions = cluster_data.partitions;

        builder.add_reference(reference.size(), reference.begin());

        uint32_t i = 0;
        for (auto const& plist : input)
        {
            uint64_t freqs_sum = std::accumulate(plist.freqs.begin(),
                                                 plist.freqs.end(),
                                                 uint64_t(0));
            builder.add_posting_list(plist.docs,
                                     plist.freqs,
                                     freqs_sum,
                                     partitions[i++]);
            plog.done_sequence(plist.docs.size());
        }

        logger() << "cluster-" << c++ << " encoded:\n";
        builder.print_cluster_stat();

        docs_positions.clear();
        freqs_positions.clear();
    }

    file.close();
    plog.log();
    clustered_opt_index coll;
    builder.build(coll);
    double elapsed_secs = (get_time_usecs() - tick) / 1000000;
    double user_elapsed_secs = (get_user_time_usecs() - user_tick) / 1000000;
    logger() << "clustered_opt collection built in "
             << elapsed_secs << " seconds" << std::endl;

    stats_line()
        ("type", "clustered_opt")
        ("worker_threads", configuration::get().worker_threads)
        ("construction_time", elapsed_secs)
        ("construction_user_time", user_elapsed_secs)
        ;

    dump_stats(coll, "clustered_opt", plog.postings);

    if (output_filename) {
        succinct::mapper::freeze(coll, output_filename);
        if (check) {
            verify_clustered_collection(input, output_filename);
        }
    }
}

int main(int argc, const char** argv)
{
    using namespace ds2i;

    if (argc < 4) {
        std::cerr << "Usage: " << argv[0]
                  << " <collection basename> <cluster filename> <MAX_REF_SIZE> [<output filename>] [--check]"
                  << std::endl;
        return 1;
    }

    const char* input_basename = argv[1];
    const char* cluster_filename = argv[2];
    const uint32_t MAX_REF_SIZE = std::atoi(argv[3]);

    const char* output_filename = nullptr;
    if (argc > 4) {
        output_filename = argv[4];
    }

    bool check = false;
    if (argc > 5 && std::string(argv[5]) == "--check") {
        check = true;
    }

    clustered_binary_freq_collection input(input_basename, check);
    global_parameters params;
    params.log_partition_size = configuration::get().log_partition_size;

    create_clustered_collection(
        input, params, cluster_filename,
        output_filename, check, MAX_REF_SIZE
    );

    return 0;
}