/*
 * KDataBufferImp.cpp
 *
 *  Created on: 2013-2-4
 *      Author: fasiondog
 */

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>
#include "KDataBufferImp.h"
#include "utilities/util.h"

namespace hku {

KDataBufferImp::KDataBufferImp(): KDataImp() {

}

KDataBufferImp::
KDataBufferImp(const Stock& stock, const KQuery& query)
: KDataImp(stock, query) {
    if (m_stock.isNull() || empty()) {
        return;
    }

    m_buffer = m_stock.getKRecordList(startPos(), endPos(), query.kType());

    //日线以上不支持复权
    if (query.recoverType() == KQuery::NO_RECOVER
            || query.kType() == KQuery::WEEK
            || query.kType() == KQuery::MONTH
            || query.kType() == KQuery::QUARTER
            || query.kType() == KQuery::HALFYEAR
            || query.kType() == KQuery::YEAR) {
        return;
    }

    switch(query.recoverType()) {
    case KQuery::NO_RECOVER:
        //do nothing
        break;

    case KQuery::FORWARD:
        _recoverForward();
        break;

    case KQuery::BACKWARD:
        _recoverBackward();
        break;

    case KQuery::EQUAL_FORWARD:
        _recoverEqualForward();
        break;

    case KQuery::EQUAL_BACKWARD:
        _recoverEqualBackward();
        break;

    default:
        HKU_ERROR("Invalid RecvoerType! [KDataBufferImp::KDataBufferImp]");
        return;
    }
}

KDataBufferImp::~KDataBufferImp() {

}

size_t KDataBufferImp::getPos(const Datetime& datetime) const {
    KRecordList::const_iterator iter;
    KRecord comp_record;
    comp_record.datetime = datetime;
    boost::function<bool(const KRecord&, const KRecord&)> f =
            boost::bind(&KRecord::datetime, _1) <
            boost::bind(&KRecord::datetime, _2);

    iter = lower_bound(m_buffer.begin(), m_buffer.end(), comp_record, f);
    if (iter == m_buffer.end() || iter->datetime != datetime) {
        return Null<size_t>();
    }

    return (iter - m_buffer.begin());
}

/******************************************************************************
 * 前复权公式:复权后价格＝[(复权前价格-现金红利)＋配(新)股价格×流通股份变动比例]÷(1＋流通股份变动比例)
 * 向前复权指以除权后的股价为基准（即除权后的股价不变），将除权前的股价降下来。
 * 复权计算时首先从上市日开始，逐日向后判断，遇到除权日，则将上市日到除权日之间（不包括除权日）的
 * 全部股价通过复权计算降下来；然后再继续向后判断，遇到下一个除权日，则再次将上市日到该除权日之间
 * （不包括除权日）的全部股价通过复权计算降下来。
 *****************************************************************************/
void KDataBufferImp::_recoverForward() {
    size_t total = m_buffer.size();
    if (total == 0) {
        return;
    }

    bd::date start_date = m_buffer.front().datetime.date();
    bd::date end_date = m_buffer.back().datetime.date() + bd::days(1);
    StockWeightList weightList = m_stock.getWeight(start_date, end_date);
    StockWeightList::const_iterator weightIter = weightList.begin();
    StockWeightList::const_iterator pre_weightIter = weightIter;

    size_t pre_pos = 0;
    for (; weightIter != weightList.end(); ++weightIter) {
        size_t i = pre_pos;
        while (i < total && m_buffer[i].datetime < weightIter->datetime()) {
            i++;
        }
        pre_pos = i;  //除权日

        //计算流通股份变动比例
        price_t change = 0.0;
        bool flag = false;
        if (weightIter != weightList.begin()) {
            pre_weightIter = weightIter - 1;
            if (pre_weightIter->freeCount() != 0.0) {
                change = (weightIter->freeCount()
                       - pre_weightIter->freeCount())
                       / pre_weightIter->freeCount();
                flag = true;
            }
        }
        if (!flag){
            change = 0.1 * (weightIter->countAsGift()
                           + weightIter->countForSell()
                           + weightIter->increasement());
        }

        price_t denominator = 1.0 + change; //分母 = (1+流通股份变动比例)
        price_t temp = weightIter->priceForSell() * change
                     - 0.1 * weightIter->bonus();

        for (i = 0; i < pre_pos; ++i) {
            m_buffer[i].openPrice = roundEx((m_buffer[i].openPrice + temp) / denominator, m_stock.precision());
            m_buffer[i].highPrice = roundEx((m_buffer[i].highPrice + temp) / denominator, m_stock.precision());
            m_buffer[i].lowPrice = roundEx((m_buffer[i].lowPrice + temp) / denominator, m_stock.precision());
            m_buffer[i].closePrice = roundEx((m_buffer[i].closePrice + temp) / denominator, m_stock.precision());
        }
    }
}

/******************************************************************************
 * 后复权公式:复权后价格＝复权前价格×(1＋流通股份变动比例)-配(新)股价格×流通股份变动比例＋现金红利
 * 向后复权指以除权前的股价为基准（即除权前的股价不变），将除权后的股价升上去。复权计算时首先从最新日开始，
 * 逐日向前判断，遇到除权日，则将除权日到最新日之间（包括除权日）的全部股价通过复权计算升上去；然后再继续
 * 向前判断，遇到下一个除权日，则再次将除权日到最新日之间（包括除权日）的全部股价通过复权计算升上去。
 *****************************************************************************/
void KDataBufferImp::_recoverBackward() {
    size_t total = m_buffer.size();
    if (0 == total) {
        return;
    }

    bd::date start_date = m_buffer.front().datetime.date();
    bd::date end_date = m_buffer.back().datetime.date() + bd::days(1);
    StockWeightList weightList = m_stock.getWeight(start_date, end_date);
    StockWeightList::const_reverse_iterator weightIter = weightList.rbegin();
    StockWeightList::const_reverse_iterator pre_weightIter;

    size_t pre_pos = total - 1;
    for (; weightIter != weightList.rend(); ++weightIter) {
        size_t i = pre_pos;
        while (i > 0 && m_buffer[i].datetime > weightIter->datetime()) {
            i--;
        }
        pre_pos = i;

        //流通股份变动比例
        price_t change = 0.0;
        bool flag = false;
        pre_weightIter = weightIter + 1;
        if (pre_weightIter != weightList.rend()
                && pre_weightIter->freeCount() != 0.0) {
            change = (weightIter->freeCount() - pre_weightIter->freeCount())
                    / pre_weightIter->freeCount();
            flag = true;
        }
        if (!flag){
            change = 0.1 * (weightIter->countAsGift()
                           + weightIter->countForSell()
                           + weightIter->increasement());
        }

        price_t denominator = 1.0 + change; //(1+流通股份变动比例)
        price_t temp = 0.1 * weightIter->bonus()
                     - weightIter->priceForSell() * change;;

        for (i = pre_pos; i < total; ++i) {
            m_buffer[i].openPrice = roundEx(m_buffer[i].openPrice * denominator + temp, m_stock.precision());
            m_buffer[i].highPrice = roundEx(m_buffer[i].highPrice * denominator + temp, m_stock.precision());
            m_buffer[i].lowPrice = roundEx(m_buffer[i].lowPrice * denominator + temp, m_stock.precision());
            m_buffer[i].closePrice = roundEx(m_buffer[i].closePrice * denominator + temp, m_stock.precision());
        }
    }
}

/******************************************************************************
 * 等比前复权公式:复权后价格＝复权前价格*复权率
 * 复权率＝｛[(股权登记日收盘价-现金红利)＋配(新)股价格×流通股份变动比例]÷(1＋流通股份变动比例)｝÷股权登记日收盘价
 * 向前复权指以除权后的股价为基准（即除权后的股价不变），将除权前的股价降下来。
 * 复权计算时首先从上市日开始，逐日向后判断，遇到除权日，则将上市日到除权日之间（不包括除权日）的
 * 全部股价通过复权计算降下来；然后再继续向后判断，遇到下一个除权日，则再次将上市日到该除权日之间
 * （不包括除权日）的全部股价通过复权计算降下来。
 *****************************************************************************/
void KDataBufferImp::_recoverEqualForward() {
    size_t total = m_buffer.size();
    if (0 == total) {
        return;
    }

    bd::date start_date = m_buffer.front().datetime.date();
    bd::date end_date = m_buffer.back().datetime.date() + bd::days(1);
    StockWeightList weightList = m_stock.getWeight(start_date, end_date);
    if (weightList.empty()) {
        return;
    }

    KRecordList kdata = m_buffer; //防止同一天两条权息记录
    StockWeightList::const_iterator weightIter = weightList.begin();
    StockWeightList::const_iterator pre_weightIter;
    size_t pre_pos = 0;
    for (; weightIter != weightList.end(); ++weightIter) {
        size_t i = pre_pos;
        while (i < total && m_buffer[i].datetime < weightIter->datetime()) {
            i++;
        }
        pre_pos = i; //除权日

        //股权登记日（即除权日的前一天数据）收盘价
        if (pre_pos == 0) {
            continue;
        }
        price_t closePrice = kdata[pre_pos - 1].closePrice;
        if (closePrice == 0.0) {
            continue;  //除零保护
        }

        //流通股份变动比例
        price_t change = 0.0;
        bool flag = false;
        if (weightIter != weightList.begin()) {
            pre_weightIter = weightIter - 1;
            if (pre_weightIter->freeCount() != 0.0) {
                change = (weightIter->freeCount() - pre_weightIter->freeCount())
                        / pre_weightIter->freeCount();
                flag = true;
            }
        }
        if (!flag) {
            change = 0.1 * (weightIter->countAsGift()
                            + weightIter->countForSell()
                            + weightIter->increasement());
        }

        price_t denominator = 1.0 + change; //(1+流通股份变动比例)
        price_t temp = weightIter->priceForSell() * change
                     -  0.1 * weightIter->bonus();

        price_t k = (closePrice + temp) / (denominator * closePrice);

        for (i = 0; i < pre_pos; ++i) {
            m_buffer[i].openPrice = roundEx(k * m_buffer[i].openPrice, m_stock.precision());
            m_buffer[i].highPrice = roundEx(k * m_buffer[i].highPrice, m_stock.precision());
            m_buffer[i].lowPrice = roundEx(k * m_buffer[i].lowPrice, m_stock.precision());
            m_buffer[i].closePrice = roundEx(k * m_buffer[i].closePrice, m_stock.precision());
        }
    }
}

/******************************************************************************
 * 等比后复权公式:复权后价格＝复权前价格÷复权率
 * 复权率＝｛[(股权登记日收盘价-现金红利)＋配(新)股价格×流通股份变动比例]÷(1＋流通股份变动比例)｝÷股权登记日收盘价
 * 向后复权指以除权前的股价为基准（即除权前的股价不变），将除权后的股价升上去。复权计算时首先从最新日开始，
 * 逐日向前判断，遇到除权日，则将除权日到最新日之间（包括除权日）的全部股价通过复权计算升上去；然后再继续
 * 向前判断，遇到下一个除权日，则再次将除权日到最新日之间（包括除权日）的全部股价通过复权计算升上去。
 *****************************************************************************/
void KDataBufferImp::_recoverEqualBackward() {
    size_t total = m_buffer.size();
    if (0 == total) {
        return;
    }

    bd::date start_date = m_buffer.front().datetime.date();
    bd::date end_date = m_buffer.back().datetime.date() + bd::days(1);
    StockWeightList weightList = m_stock.getWeight(start_date, end_date);
    StockWeightList::const_reverse_iterator weightIter = weightList.rbegin();
    StockWeightList::const_reverse_iterator pre_weightIter;

    size_t pre_pos = total - 1;
    for (; weightIter != weightList.rend(); ++weightIter) {
        size_t i = pre_pos;
        while (i > 0 && m_buffer[i].datetime > weightIter->datetime()) {
            i--;
        }
        pre_pos = i; //除权日

        //股权登记日（即除权日的前一天数据）收盘价
        if (pre_pos == 0){
            continue;
        }
        price_t closePrice = m_buffer[pre_pos - 1].closePrice;

        //流通股份变动比例
        price_t change = 0.0;
        bool flag = false;
        pre_weightIter = weightIter + 1;
        if (pre_weightIter != weightList.rend()
                && pre_weightIter->freeCount() != 0.0) {
            change = (weightIter->freeCount() - pre_weightIter->freeCount())
                    / pre_weightIter->freeCount();
            flag = true;
        }
        if (!flag) {
            change = 0.1 * (weightIter->countAsGift()
                           + weightIter->countForSell()
                           + weightIter->increasement());
        }

        price_t denominator = 1.0 + change; //(1+流通股份变动比例)
        price_t temp = closePrice + weightIter->priceForSell() * change
                     - 0.1 * weightIter->bonus();
        if (temp == 0.0) {
            continue;
        }
        price_t k =  (denominator * closePrice) / temp;

        for (i = pre_pos; i < total; ++i) {
            m_buffer[i].openPrice = roundEx(k * m_buffer[i].openPrice, m_stock.precision());
            m_buffer[i].highPrice = roundEx(k * m_buffer[i].highPrice, m_stock.precision());
            m_buffer[i].lowPrice =  roundEx(k * m_buffer[i].lowPrice, m_stock.precision());
            m_buffer[i].closePrice = roundEx(k * m_buffer[i].closePrice, m_stock.precision());
        }
    }
}

} /* namespace hku */


// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2015 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// 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
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#ifndef HISTOGRAM_HH
#define HISTOGRAM_HH

#include <vector>
#include <utility>
#include <algorithm>
#include <array>
#define BOOST_DISABLE_ASSERTS
#include <boost/multi_array.hpp>

#include <boost/type_traits.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/int.hpp>

#include <boost/python/object.hpp>

//
// This is a generic multidimensional histogram type
//

template <class ValueType, class CountType, size_t Dim>
class Histogram
{
public:
    typedef std::array<ValueType,Dim> point_t; // point type to be
                                               // histogrammed
    typedef std::array<size_t,Dim> bin_t;      // bin type

    typedef boost::multi_array<CountType,Dim> count_t; // the histogram itself

    typedef boost::mpl::int_<Dim> dim;
    typedef CountType count_type;
    typedef ValueType value_type;

    // floating point type to calculate the mean
    typedef typename boost::mpl::if_<boost::is_floating_point<ValueType>,
                                     ValueType, double>::type mean_t;

    Histogram(const std::array<std::vector<ValueType>, Dim>& bins):
        _bins(bins)
    {
        bin_t new_shape;
        for (size_t j = 0; j < Dim; ++j)
        {
            if (_bins[j].size() < 1)
                throw std::range_error("invalid bin edge number < 1!");

            _data_range[j] = std::make_pair(0, 0);
            value_type delta = _bins[j][1] - _bins[j][0];

            if (_bins[j].size() == 2)
            {
                _data_range[j] = std::make_pair(_bins[j][0], _bins[j][0]);
                delta = _bins[j][1];
                _const_width[j] = true;
            }
            else
            {
                // detect whether the given bins are of constant width, for faster
                // binning
                _const_width[j] = true;
                for (size_t i = 2; i < _bins[j].size(); ++i)
                {
                    value_type d = _bins[j][i] - _bins[j][i-1];
                    if (delta != d)
                        _const_width[j] = false;
                }

                if (_const_width[j])
                    _data_range[j] = std::make_pair(_bins[j].front(),
                                                    _bins[j].back());
            }
            if (delta == 0)
                throw std::range_error("invalid bin size of zero!");

            new_shape[j] = _bins[j].size() - 1;
        }
        _counts.resize(new_shape);
    }

    void PutValue(const point_t& v, const CountType& weight = 1)
    {
        bin_t bin;
        for (size_t i = 0; i < Dim; ++i)
        {
            if (_const_width[i])
            {
                value_type delta;

                if (_data_range[i].first == _data_range[i].second)
                {
                    delta = _bins[i][1];

                    if (v[i] < _data_range[i].first)
                        return; // out of bounds
                }
                else
                {
                    delta = _bins[i][1] - _bins[i][0];

                    if (v[i] < _data_range[i].first ||
                        v[i] >= _data_range[i].second)
                        return; // out of bounds
                }

                bin[i] = size_t((v[i] - _data_range[i].first) / delta);
                if (bin[i] >= _counts.shape()[i]) // modify shape
                {
                    bin_t new_shape;
                    for (size_t j = 0; j < Dim; ++j)
                        new_shape[j] = _counts.shape()[j];
                    new_shape[i] = bin[i] + 1;
                    _counts.resize(new_shape);
                    while (_bins[i].size() < new_shape[i] + 1)
                        _bins[i].push_back(_bins[i].back() + delta);
                }
            }
            else // arbitrary bins widths. do a binary search
            {
                std::vector<ValueType>& bins = _bins[i];
                typeof(bins.begin()) iter = upper_bound(bins.begin(),
                                                        bins.end(), v[i]);
                if (iter == bins.end())
                {
                    return;  // falls off from last bin, do not count
                }
                else
                {
                    bin[i] = iter - bins.begin();
                    if (bin[i] == 0)
                        return; // falls off from fist bin, do not count
                    else
                       --bin[i];
                }
            }
        }
        _counts(bin) += weight;
    }

    boost::multi_array<CountType,Dim>& GetArray() { return _counts; }

    std::array<std::pair<ValueType,ValueType>,Dim>& GetDataRange()
    { return _data_range; }

    std::array<std::vector<ValueType>, Dim>& GetBins() { return _bins; }

protected:
    boost::multi_array<CountType,Dim> _counts;
    std::array<std::vector<ValueType>, Dim> _bins;
    std::array<std::pair<ValueType,ValueType>,Dim> _data_range;
    std::array<bool,Dim> _const_width;
};

// This class will encapsulate a histogram, and atomically sum it to a given
// resulting histogram (which is shared among all copies) after it is
// destructed, or when the Gather() member function is called. This enables, for
// instance, a histogram to be built in parallel.

template <class Histogram>
class SharedHistogram: public Histogram
{
public:

    SharedHistogram(Histogram& hist): Histogram(hist), _sum(&hist) {}
    ~SharedHistogram()
    {
        Gather();
    }

    void Gather()
    {
        if (_sum != 0)
        {
            #pragma omp critical
            {
                typename Histogram::bin_t idx;

                typename Histogram::bin_t shape;
                for (size_t i = 0; i <  this->_counts.num_dimensions(); ++i)
                    shape[i] = std::max(this->_counts.shape()[i],
                                        _sum->GetArray().shape()[i]);
                _sum->GetArray().resize(shape);
                for (size_t i = 0; i < this->_counts.num_elements(); ++i)
                {
                    size_t offset = 1;
                    for (size_t j = 0; j < this->_counts.num_dimensions(); ++j)
                    {
                        size_t L = this->_counts.shape()[j];
                        idx[j] = ((i / offset) % L);
                        offset *= L;
                    }
                    _sum->GetArray()(idx) += this->_counts(idx);
                }
                for (int i = 0; i < Histogram::dim::value; ++i)
                {
                    if (_sum->GetBins()[i].size() < this->_bins[i].size())
                        _sum->GetBins()[i] = this->_bins[i];
                }
            }
            _sum = 0;
        }
    }
private:
    Histogram* _sum;
};

//
// useful functions to get the the mean and standard deviations from simple
// map-based, non-binned histograms. Not to be used with the above type.
//

// gets the mean value of a histogram
template <class Map>
double GetMapMean (const Map &m)
{
    int total = 0;
    double mean = 0;
    for (typeof(m.begin()) iter = m.begin(); iter != m.end(); iter++)
    {
        mean += double(iter->first * iter->second);
        total += iter->second;
    }

    return (total > 0)?mean/total:0.0;
}

// gets the standard deviation of a histogram
template <class Map>
double GetMapDeviation (const Map &m, double avg)
{
    double dev = 0.0;
    int total = 0;
    for (typeof(m.begin()) iter = m.begin(); iter != m.end(); iter++)
    {
        dev += double( (iter->first - avg) *
                       (iter->first - avg) * iter->second);
        total += iter->second;
    }
    return (total > 1)?sqrt(dev/(total-1)):0.0;
}

#endif //HISTOGRAM_HH


// Distributed under the MIT License.
// See LICENSE.txt for details.

#pragma once

#include <boost/functional/hash.hpp>
#include <cstddef>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>

#include "DataStructures/DataBox/DataBox.hpp"
#include "DataStructures/DataBox/PrefixHelpers.hpp"
#include "DataStructures/DataBox/Prefixes.hpp"
#include "DataStructures/DataVector.hpp"
#include "DataStructures/FixedHashMap.hpp"
#include "DataStructures/Index.hpp"
#include "DataStructures/Tensor/Tensor.hpp"
#include "DataStructures/Variables.hpp"
#include "DataStructures/VariablesTag.hpp"
#include "Domain/Structure/Direction.hpp"
#include "Domain/Structure/Element.hpp"
#include "Domain/Structure/ElementId.hpp"
#include "Domain/Structure/MaxNumberOfNeighbors.hpp"
#include "Domain/Tags.hpp"
#include "Domain/TagsTimeDependent.hpp"
#include "Evolution/BoundaryCorrectionTags.hpp"
#include "Evolution/DgSubcell/NeighborData.hpp"
#include "Evolution/DgSubcell/Projection.hpp"
#include "Evolution/DgSubcell/Reconstruction.hpp"
#include "Evolution/DgSubcell/Tags/Mesh.hpp"
#include "Evolution/DgSubcell/Tags/NeighborData.hpp"
#include "Evolution/DgSubcell/Tags/OnSubcellFaces.hpp"
#include "Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp"
#include "Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp"
#include "Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/BoundaryCorrection.hpp"
#include "Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Factory.hpp"
#include "Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp"
#include "Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp"
#include "Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ComputeFluxes.hpp"
#include "Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp"
#include "NumericalAlgorithms/Spectral/Mesh.hpp"
#include "PointwiseFunctions/GeneralRelativity/Tags.hpp"
#include "PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp"
#include "PointwiseFunctions/Hydro/Tags.hpp"
#include "Utilities/FakeVirtual.hpp"
#include "Utilities/Gsl.hpp"
#include "Utilities/TMPL.hpp"

namespace grmhd::ValenciaDivClean::subcell {
/*!
 * \brief On elements using DG, reconstructs the interface data from a
 * neighboring element doing subcell.
 *
 * The neighbor's packaged data needed by the boundary correction is computed
 * and returned so that it can be used for solving the Riemann problem on the
 * interfaces.
 *
 * Note that for strict conservation the Riemann solve should be done on the
 * subcells, with the correction being projected back to the DG interface.
 * However, in practice such strict conservation doesn't seem to be necessary
 * and can be explained by that we only need strict conservation at shocks, and
 * if one element is doing DG, then we aren't at a shock.
 */
struct NeighborPackagedData {
  template <typename DbTagsList>
  static FixedHashMap<
      maximum_number_of_neighbors(3), std::pair<Direction<3>, ElementId<3>>,
      std::vector<double>, boost::hash<std::pair<Direction<3>, ElementId<3>>>>
  apply(const db::DataBox<DbTagsList>& box,
        const std::vector<std::pair<Direction<3>, ElementId<3>>>&
            mortars_to_reconstruct_to) {
    using evolved_vars_tag = typename System::variables_tag;
    using evolved_vars_tags = typename evolved_vars_tag::tags_list;
    using prim_tags = typename System::primitive_variables_tag::tags_list;
    using recons_prim_tags = tmpl::push_back<
        prim_tags,
        hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>;
    using fluxes_tags = db::wrap_tags_in<::Tags::Flux, evolved_vars_tags,
                                         tmpl::size_t<3>, Frame::Inertial>;

    ASSERT(not db::get<domain::Tags::MeshVelocity<3>>(box).has_value(),
           "Haven't yet added support for moving mesh to DG-subcell. This "
           "should be easy to generalize, but we will want to consider "
           "storing the mesh velocity on the faces instead of "
           "re-slicing/projecting.");

    FixedHashMap<maximum_number_of_neighbors(3),
                 std::pair<Direction<3>, ElementId<3>>, std::vector<double>,
                 boost::hash<std::pair<Direction<3>, ElementId<3>>>>
        nhbr_package_data{};

    const auto& nhbr_subcell_data =
        db::get<evolution::dg::subcell::Tags::
                    NeighborDataForReconstructionAndRdmpTci<3>>(box);
    const Mesh<3>& subcell_mesh =
        db::get<evolution::dg::subcell::Tags::Mesh<3>>(box);
    const Mesh<3>& dg_mesh = db::get<domain::Tags::Mesh<3>>(box);

    const auto volume_prims = evolution::dg::subcell::fd::project(
        db::get<typename System::primitive_variables_tag>(box), dg_mesh,
        subcell_mesh.extents());

    const auto& recons =
        db::get<grmhd::ValenciaDivClean::fd::Tags::Reconstructor>(box);
    const auto& boundary_correction =
        db::get<evolution::Tags::BoundaryCorrection<System>>(box);
    using derived_boundary_corrections =
        typename std::decay_t<decltype(boundary_correction)>::creatable_classes;
    tmpl::for_each<
        derived_boundary_corrections>([&box, &boundary_correction, &dg_mesh,
                                       &mortars_to_reconstruct_to,
                                       &nhbr_package_data, &nhbr_subcell_data,
                                       &recons, &subcell_mesh, &volume_prims](
                                          auto derived_correction_v) {
      using DerivedCorrection = tmpl::type_from<decltype(derived_correction_v)>;
      if (typeid(boundary_correction) == typeid(DerivedCorrection)) {
        using dg_package_data_temporary_tags =
            typename DerivedCorrection::dg_package_data_temporary_tags;
        using dg_package_data_argument_tags = tmpl::append<
            evolved_vars_tags, recons_prim_tags, fluxes_tags,
            tmpl::remove_duplicates<tmpl::push_back<
                dg_package_data_temporary_tags, gr::Tags::SpatialMetric<3>,
                gr::Tags::SqrtDetSpatialMetric<DataVector>,
                gr::Tags::InverseSpatialMetric<3, Frame::Inertial, DataVector>,
                evolution::dg::Actions::detail::NormalVector<3>>>>;

        const auto& element = db::get<domain::Tags::Element<3>>(box);
        const auto& eos = get<hydro::Tags::EquationOfStateBase>(box);

        using dg_package_field_tags =
            typename DerivedCorrection::dg_package_field_tags;
        Variables<dg_package_data_argument_tags> vars_on_face{0};
        Variables<dg_package_field_tags> packaged_data{0};
        for (const auto& mortar_id : mortars_to_reconstruct_to) {
          const Direction<3>& direction = mortar_id.first;

          Index<3> extents = subcell_mesh.extents();
          // Switch to face-centered instead of cell-centered points on the FD.
          // There are num_cell_centered+1 face-centered points.
          ++extents[direction.dimension()];

          // Computed prims and cons on face via reconstruction
          const size_t num_face_pts = subcell_mesh.extents()
                                          .slice_away(direction.dimension())
                                          .product();
          vars_on_face.initialize(num_face_pts);
          // Copy spacetime vars over from volume.
          using spacetime_vars_to_copy = tmpl::list<
              gr::Tags::Lapse<DataVector>,
              gr::Tags::Shift<3, Frame::Inertial, DataVector>,
              gr::Tags::SpatialMetric<3>,
              gr::Tags::SqrtDetSpatialMetric<DataVector>,
              gr::Tags::InverseSpatialMetric<3, Frame::Inertial, DataVector>>;
          tmpl::for_each<spacetime_vars_to_copy>(
              [&direction, &extents, &vars_on_face,
               &spacetime_vars_on_faces =
                   db::get<evolution::dg::subcell::Tags::OnSubcellFaces<
                       typename System::flux_spacetime_variables_tag, 3>>(box)](
                  auto tag_v) {
                using tag = tmpl::type_from<decltype(tag_v)>;
                data_on_slice(make_not_null(&get<tag>(vars_on_face)),
                              get<tag>(gsl::at(spacetime_vars_on_faces,
                                               direction.dimension())),
                              extents, direction.dimension(),
                              direction.side() == Side::Lower
                                  ? 0
                                  : extents[direction.dimension()] - 1);
              });

          call_with_dynamic_type<void, typename grmhd::ValenciaDivClean::fd::
                                           Reconstructor::creatable_classes>(
              &recons,
              [&element, &eos, &mortar_id, &nhbr_subcell_data, &subcell_mesh,
               &vars_on_face, &volume_prims](const auto& reconstructor) {
                reconstructor->reconstruct_fd_neighbor(
                    make_not_null(&vars_on_face), volume_prims, eos, element,
                    nhbr_subcell_data, subcell_mesh, mortar_id.first);
              });

          grmhd::ValenciaDivClean::subcell::compute_fluxes(
              make_not_null(&vars_on_face));

          // Note: since the spacetime isn't dynamical we don't need to
          // worry about different normal vectors on the different sides
          // of the element. If the spacetime is dynamical, then the normal
          // covector needs to be sent, or at least the inverse spatial metric
          // from the neighbor so that the normal covector can be computed
          // correctly.
          tnsr::i<DataVector, 3, Frame::Inertial> normal_covector =
              get<evolution::dg::Tags::NormalCovector<3>>(
                  *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<3>>(
                       box)
                       .at(mortar_id.first));
          for (auto& t : normal_covector) {
            t *= -1.0;
          }
          // Note: Only need to do the projection in 2d and 3d, but GRMHD is
          // always 3d currently.
          const auto dg_normal_covector = normal_covector;
          for (size_t i = 0; i < 3; ++i) {
            normal_covector.get(i) = evolution::dg::subcell::fd::project(
                dg_normal_covector.get(i),
                dg_mesh.slice_away(mortar_id.first.dimension()),
                subcell_mesh.extents().slice_away(mortar_id.first.dimension()));
          }

          // Compute the packaged data
          packaged_data.initialize(num_face_pts);
          using dg_package_data_projected_tags = tmpl::append<
              evolved_vars_tags, fluxes_tags, dg_package_data_temporary_tags,
              typename DerivedCorrection::dg_package_data_primitive_tags>;
          evolution::dg::Actions::detail::dg_package_data<System>(
              make_not_null(&packaged_data),
              dynamic_cast<const DerivedCorrection&>(boundary_correction),
              vars_on_face, normal_covector, {std::nullopt}, box,
              typename DerivedCorrection::dg_package_data_volume_tags{},
              dg_package_data_projected_tags{});

          // Reconstruct the DG solution.
          // Really we should be solving the boundary correction and
          // then reconstructing, but away from a shock this doesn't
          // matter.
          auto dg_packaged_data = evolution::dg::subcell::fd::reconstruct(
              packaged_data, dg_mesh.slice_away(mortar_id.first.dimension()),
              subcell_mesh.extents().slice_away(mortar_id.first.dimension()));
          nhbr_package_data[mortar_id] = std::vector<double>{
              dg_packaged_data.data(),
              dg_packaged_data.data() + dg_packaged_data.size()};
        }
      }
    });

    return nhbr_package_data;
  }
};
}  // namespace grmhd::ValenciaDivClean::subcell


/***
第三节 完美转发
（1）完美转发的概念和步骤演绎
a)直接调用：  funcLast();
b)转发： 通过funcMiddle()间接调用funcLast。funcMiddle相当于一个跳板函数。如果有参数，那么参数也需要通过funcMiddle中转传递给funcLast()
c)完美转发：const,左值，右值。实参的属性完全不丢失，原原本本的通过funcMiddle转发给funcLast，这种转发就是完美转发。
万能引用：实参的所有信息都会传递到万能引用当中去从而让编译器推导出来函数模板最终的形参类型 (引用折叠)。
完美转发：就是让程序员可以书写接受任意实参的函数模板（funcMiddle_Temp），并将其转发到目标函数（funcLast2)，目标函数会接收到与
转发函数（funcMiddle_Temp）所接收的完全相同（当然包括类型相同比如保持参数的左值、右值特性）的参数。
要实现完美转发，就要用到std::forward了。
（2）std::forward ：C++11中专门为转发而存在的函数。这个函数要么返回一个左值，要么返回一个右值。
万能引用类型才是forward能够发挥作用的重要条件。
理解：
(a)实参原来是个左值j，到了形参中还是左值t2。forward能够转化回原来该实参的左值或者右值性。所以，forward之后还是个左值。
(b)实参原来是个右值20，到了形参中变成了左值t1。forward能够转化回原来该实参的左值或者右值性。所以，forward之后还是个右值。
forward这个函数有强制把左值转换成右值的能力。所以：forward这个函数只对原来是个右值这种情况有用。
forward的能力：保持原始实参的左值性或者右值性
总结：完美转发：比较好的解决了参数转发的问题。
（3）普通参数的完美转发:auto &&

（4）在构造函数模板中使用完美转发范例
（5）在可变参数模板中使用完美转发范例
（5.1）常规的在可变参模板使用完美转发
（5.2）将目标函数中的返回值通过转发函数返回给调用者函数
用到的技术：auto结合decltype构成返回类型后置语法。
（6）完美转发失败的情形一例
使用NULL或者0作为空指针进行参数传递时导致完美转发失败的情况。
C++11引入nullptr(空指针)
***/

#include <iostream>

//#include <boost/type_index.hpp>
using namespace std;
#pragma warning(disable : 4996)

//函数模板
//template <typename T>
//void myfunc(T  tmprv)
//{
//	cout << "--------------------------------begin----------------" << endl;
//	using boost::typeindex::type_id_with_cvr;
//	cout << "T=" << type_id_with_cvr<T>().pretty_name() << endl; //显示T的类型
//	cout << "tmprv=" << type_id_with_cvr<decltype(tmprv)>().pretty_name() << endl; //显示tmprv的类型
//	cout << "--------------------------------end------------------" << endl;
//}

namespace _nmsp1
{
	class Human
	{
	public:
		////构造函数
		//Human(const string& tmpname) :m_sname(tmpname)
		//{
		//	cout << "Human(const string &tmpname)执行" << endl;
		//}
		////Human(string&& tmpname) :m_sname(tmpname)
		//Human(string&& tmpname) :m_sname(std::move(tmpname)) //move并不具备移动能力，把一个左值转换成一个右值。
		//{
		//	cout << "Human(string&& tmpname)执行" << endl;
		//}

		//构造函数模板
		template<typename T>
		Human(T&& tmpname) : m_sname(std::forward<T>(tmpname))
		{
			cout << "Human(T&& tmpname)执行" << endl;
		}

		//拷贝构造函数
		Human(const Human& th) : m_sname(th.m_sname)
		{
			cout << "Human(const Human& th)拷贝构造函数执行" << endl;
		}

		//移动构造函数
		Human(Human&& th) : m_sname(std::move(th.m_sname))
		{
			cout << "Human(Human&& th)移动构造函数执行" << endl;
		}

	private:
		string m_sname;
	};
}

namespace _nmsp2
{
	//void funcLast(int v1, int& v2) //目标函数
	//{
	//	++v2; //改变v2的值，让其自增1
	//	cout << v1 + v2 << endl;
	//}

	int funcLast(int v1, int& v2) //目标函数
	{
		++v2; //改变v2的值，让其自增1
		cout << v1 + v2 << endl;
		return v1 + v2;
	}

	//
	//template<typename F, typename T1,typename T2>
	//void funcMiddle_Temp(F f, T1&& t1, T2&& t2)  //转发函数
	//{
	//	f(
	//		std::forward<T1>(t1),
	//		std::forward<T2>(t2)
	//	);
	//}

	//支持任意数量、类型参数的完美转发
	/*template <typename F, typename...T>
	void funcMiddle_Temp(F f, T&&... t)
	{
		f(std::forward<T>(t)...);
	}*/

	template <typename F, typename...T>
	//auto funcMiddle_Temp(F f, T&&... t)->decltype(  f(std::forward<T>(t)...)   )
	decltype(auto) funcMiddle_Temp(F f, T&&... t)
	{
		return f(std::forward<T>(t)...);
	}

}

namespace _nmsp3
{
	//目标函数
	void funcLast4(char* p)
	{
		//if (p != NULL)
		if (p != nullptr)
		{
			strncpy(p, "abc",3);
		}
	}

	//转发函数
	template <typename F, typename...T>
	void funcMiddle_Temp(F f, T&&... t)
	{
		f(std::forward<T>(t)...);
	}

}

int main()
{

	/*
	string sname = "ZhangSan";
	_nmsp1::Human myhuman1(sname);
	_nmsp1::Human myhuman2(string("LiSi")); //"LiSi"是const char[5]类型，而string("LiSi")是string类型。

	//_nmsp1::Human myhuman3(myhuman1); //实际编译器去调用了构造函数模板，而不是调用了拷贝构造函数。 std::enable_if
	_nmsp1::Human myhuman4(std::move(myhuman1));

	const _nmsp1::Human myhuman5(string("WangWu"));
	_nmsp1::Human myhuman6(myhuman5);
	*/

	/*
	int j = 70;
	_nmsp2::funcMiddle_Temp(_nmsp2::funcLast, 20, j);
	cout << "j = " << j << endl;
	*/

	/*
	int j = 70;
	int k = _nmsp2::funcMiddle_Temp(_nmsp2::funcLast, 20, j);
	cout << "j = " << j << endl;
	cout << "k = " << k << endl;
	*/

	char* p = new char[100];
	memset(p, 0, 100);
	//_nmsp3::funcMiddle_Temp(_nmsp3::funcLast4, NULL);
	//_nmsp3::funcLast4(NULL);
	_nmsp3::funcMiddle_Temp(_nmsp3::funcLast4, nullptr);

	return 0;
}


#ifndef ODE_STATE_HPP_INCLUDED
#define ODE_STATE_HPP_INCLUDED

#include <boost/numeric/odeint/util/is_resizeable.hpp>
#include <vector>
#include <cassert>

class GState;

struct vector_proxy
{
public:
	double& operator[](std::size_t idx)
	{
		assert( idx < mSize );
		return mData[idx];
	};
	const double& operator[](std::size_t idx) const
	{
		assert( idx < mSize );
		return mData[idx];
	};

	template<class T>
	vector_proxy& operator=(const T& other)
	{
		assert( other.size() == mSize );
		for(unsigned i = 0; i < other.size(); ++i)
			(*this)[i] = other[i];
		return *this;
	}

	template<class T>
	void assignTo( T& other ) const
	{
		assert( other.size() == mSize );
		for(int i = 0; i < other.size(); ++i)
			other[i] = (*this)[i];
	}

	std::size_t size() const { return mSize;};

private:
	double* mData;
	std::size_t mSize;
	vector_proxy(double* ptr, std::size_t sz) : mData(ptr), mSize(sz) {};

	friend class GState;
};

struct const_vector_proxy
{
public:
	const double& operator[](std::size_t idx) const {
		assert( idx < mSize );
		return mData[idx];
	};
	std::size_t size() const { return mSize;};

	template<class T>
	void assignTo( T& other ) const
	{
		assert( other.size() == mSize );
		for(unsigned i = 0; i < mSize; ++i)
			other[i] = (*this)[i];
	}
private:
	const double* mData;
	std::size_t mSize;

	const_vector_proxy(const double* ptr, std::size_t sz) : mData(ptr), mSize(sz) {};

	friend class GState;
};

class GState
{
public:
	GState();
	//~GState();
	GState( int D, bool m );
	//GState( const GState& cpy );
	//GState( GState&& mov );

	typedef std::vector<double> container_type;
	typedef container_type::iterator iterator;
	typedef container_type::const_iterator const_iterator;
	typedef double value_type;

	std::size_t dimension() const { return mDimension; };
	bool monodromy() const { return mHasMonodromy; };

	void resize( std::size_t size, bool monodromy );
	void init_monodromy();

	// iterator access
	const_iterator begin() const 	{ return mData.begin(); }
	const_iterator end() const		{ return mData.end(); }
	iterator begin() 				{ return mData.begin(); }
	iterator end() 					{ return mData.end(); }

	// accessing values outside of integration
	vector_proxy position() { return vector_proxy(&mData[0], mDimension); };
	const_vector_proxy position() const { return const_vector_proxy(&mData[0], mDimension); };

	vector_proxy velocity() { return vector_proxy(&mData[0] + mDimension, mDimension);};
	const_vector_proxy velocity() const { return const_vector_proxy(&mData[0] + mDimension, mDimension);};

	vector_proxy matrix() { return vector_proxy( &mData[0] + 2*mDimension, 4*mDimension*mDimension ); };
	const_vector_proxy matrix() const { return const_vector_proxy( &mData[0] + 2*mDimension, 4*mDimension*mDimension ); };
private:
	std::vector<double> mData;
	std::size_t mDimension = 0;
	bool mHasMonodromy = false;
};

// mark resizable
namespace boost { namespace numeric { namespace odeint {

template<>
struct is_resizeable< GState >
{
	typedef boost::true_type type;
	static const bool value = type::value;
};

template <class U, class V>
struct same_size_impl;

template <>
struct same_size_impl< GState , GState >
{
    static bool same_size( const GState& x , const GState& y )
    {
        return x.dimension() == y.dimension() && x.monodromy() == y.monodromy();
    }
};

template <class U, class V>
struct resize_impl;

template <>
struct resize_impl< GState , GState >
{
    static void resize( GState& x , const GState& y )
    {
    	x.resize( y.dimension(), y.monodromy() );
    }
};

} } }

#endif // ODE_STATE_HPP_INCLUDED


//
//  Net.cpp
//
//
//  Created by Guanglei Wang on 03/06/2017.
//

#include <gravity/Net.h>
#include <algorithm>
#include <map>
#include <set>
#define _USE_MATH_DEFINES
#include <cmath>
#include <list>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <queue>
#include <time.h>
//#include <armadillo>
#ifdef USE_BOOST
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <deque>
#include <iterator>
#endif

using namespace std;
using namespace gravity;

static int max_line_len;
static char* line = nullptr;

Net::Net() {}

/* returns true if an arc is already present between the given nodes */
bool Net::duplicate(std::string n1, std::string n2, int id1) {
    int id2 = get_arc(n1, n2)->_id;

    if (id2 < id1)
        return true;
    else
        return false;
}

Net* Net::clone() const{
    Net* copy_net = new Net();
    Node* node = NULL;

    for (int i=0; i<nodes.size(); i++) {
        node = this->nodes[i];
        copy_net->add_node(node->clone());
    }

    Arc* arc = NULL;
    for (int i=0; i < arcs.size(); i++) {

        /* ignores if the arc is a paralel line to an already existing arc */
        //if (duplicate(arcs[i]->_src->_name, arcs[i]->_dest->_name, arcs[i]->_id)) {
//            continue;
//        }
        arc = arcs[i]->clone();

        /* Update the source and destination to the new nodes in copy_net */
        arc->_src = copy_net->get_node(arc->_src->_name);
        arc->_dest = copy_net->get_node(arc->_dest->_name);

        /* Add the new arc to the list of arcs */
        copy_net->add_arc(arc);

        /* Connects it to its source and destination */
        arc->connect();
    }
    return copy_net;
}

Net* Net::clone_undirected() {
    Net* copy_net = new Net();
    Node* node = NULL;

    for (int i=0; i<nodes.size(); i++) {
        node = this->nodes[i];
        copy_net->add_node(node->clone());
    }

    Arc* arc = NULL;
    for (int i=0; i < arcs.size(); i++) {
        if (copy_net->get_arc(arcs[i]->_src->_name, arcs[i]->_dest->_name)!=nullptr) {
            continue;
        }
        arc = arcs[i]->clone();

        /* Update the source and destination to the new nodes in copy_net */
        arc->_src = copy_net->get_node(arc->_src->_name);
        arc->_dest = copy_net->get_node(arc->_dest->_name);

        /* Add the undirected arc to the list of arcs */
        copy_net->add_undirected_arc(arc);

        /* Connects it to its source and destination */
        arc->connect();
    }
    return copy_net;
}

const bool bag_compare(const vector<Node*> & a,const vector<Node*>& b) {
    return a.size() > b.size();
}

const bool node_compare(const Node* n1, const Node* n2) {
    return n1->fill_in > n2->fill_in;
}

void Net::add_node(Node* node) {
    node->_id = (int) nodes.size();

    if (!nodeID.insert(pair<string,Node*>(node->_name, node)).second) {
        cerr << "ERROR: adding the same node twice!";
    }

    nodes.push_back(node);
}

Node* Net::get_node(string name) const{
    return nodeID.find(name)->second;
}

/* returns the undirected arc formed by node n1 and n2 */
Arc* Net::get_arc(Node* n1, Node* n2) {
    string src, dest, key, inv_key;
    src = n1->_name;
    dest = n2->_name;
    key.clear();
    inv_key.clear();
    key.append(src);
    inv_key.append(dest);
    key.append(",");
    inv_key.append(",");
    key.append(dest);
    inv_key.append(src);
    map<string, set<Arc*>*>::iterator it= arcID.find(key);
    if (it != arcID.end()) {
        for (auto a: *it->second) {
            //   if (!a->parallel) {
            return a;
            // }
        }
    }
    it = arcID.find(inv_key);
    if (it != arcID.end()) {
        for (auto a: *it->second) {
            //   if (!a->parallel) {
            return a;
            // }
        }
    }
    return nullptr;
}

/* returns the Id of the arc formed by nodes names n1 and n2 */
// this assumes that the graph is undirected.
Arc* Net::get_arc(std::string src, std::string dest) {
    std::string key, inv_key;
    key.clear();
    inv_key.clear();
    key.append(src);
    inv_key.append(dest);
    key.append(",");
    inv_key.append(",");
    key.append(dest);
    inv_key.append(src);
    map<string, set<Arc*>*>::iterator it= arcID.find(key);
    if (it != arcID.end()) {
        for (auto a: *it->second) {
            return a;
        }
    }

    it = arcID.find(inv_key);
    if (it != arcID.end()) {
        for (auto a: *it->second) {
            return a;
        }
    }

    return nullptr;
}

Arc* Net::get_directed_arc(std::string src, std::string dest) {
    std::string key;
    key.clear();
    key.append(src);
    key.append(",");
    key.append(dest);
    map<string, set<Arc*>*>::iterator it= arcID.find(key);
    if (it != arcID.end()) {
        for (auto a: *it->second) {
            return a;
        }
    }

    return nullptr;
}

bool Net::add_arc(Arc* a) {
    bool parallel = false;
    set<Arc*>* s = NULL;
    string src, dest, key;
    src = a->_src->_name;
    dest = a->_dest->_name;
    if (src == dest){
        throw invalid_argument ("It is now allowed to make a node self connected in gravity. \n");

    }

    key.clear();
    key.append(src);
    key.append(",");
    key.append(dest);

    if(arcID.find(key)==arcID.end()) {
        s = new set<Arc*>;
        s->insert(a);
        arcID.insert(pair<string, set<Arc*>*>(key,s));
    }
    else {
        if(arcID.find(key)!=arcID.end())
            s = arcID[key];
        s->insert(a);
        Warning("\nWARNING: adding another Directed line between same nodes! \n Node ID: " << src << " and Node ID: " << dest << endl);
        a->_parallel = true;
        parallel = true;
    }
    arcMap[a->_name] = a;
    arcs.push_back(a);
    return parallel;
}
// undirected
void Net::add_undirected_arc(Arc* a) {
//    bool parallel = false;
    set<Arc*>* s = NULL;
    string src, dest, key, key_inv;
    src = a->_src->_name;
    dest = a->_dest->_name;

    if (src == dest){
        throw invalid_argument ("It is now allowed to make a node self connected in gravity. \n");

    }

    key.clear();
    key.append(src);
    key.append(",");
    key.append(dest);

    key_inv.clear();
    key_inv.append(dest);
    key_inv.append(",");
    key_inv.append(src);

    if(arcID.find(key)==arcID.end()&& arcID.find(key_inv)==arcID.end()) {
        s = new set<Arc*>;
        s->insert(a);
        arcID.insert(pair<string, set<Arc*>*>(key,s));
        arcs.push_back(a);
    }
}

/** remove the arc by
1. removing it from the arcs list, but without changing the arc id.
2. removing it from the map container.
*/

void Net::remove_arc(Arc* a) {
    arcs.erase(arcs.begin()+(a->_id));
    //arcs[a->_id] = nullptr;
    arcID.erase(a->_src->_name+","+a->_dest->_name);
}

// Reading files
char* Net::readline(FILE *input)
{
    size_t len;
    // line, max_line_len have been declared
    if(std::fgets(line,max_line_len,input)==NULL) return NULL;

    while(strrchr(line,'\n') == NULL)
    {
        max_line_len *= 2;
        line = (char *)realloc(line,max_line_len);
        len = strlen(line);
        if(fgets(line+len,max_line_len-len,input) == NULL)
            break;
    }
    return line;
}

void Net::exit_input_error(int line_num) {
    fprintf(stderr,"Wrong input format at line %d\n", line_num);
    exit(1);
}

// Reading graphs with rudy format
void Net::readrudy(const char* fname) {
    int Num_nodes=0;
    int Num_edges=0;

    ifstream infile(fname);

    string sLine;

    if (infile.good())
    {
        getline(infile, sLine);
        istringstream iss(sLine);
        iss >> Num_nodes;
        iss >> Num_edges;
    }
    else{
        fprintf(stderr,"can’t open input file %s\n",fname);
        exit(1);
    }
    // get nodes

    string name;
    Node* node = nullptr;

    for (int i= 1; i< Num_nodes + 1; i++) {
        name = to_string(i);
        node = new Node(name,i-1);
        add_node(node);
    }

    // get arcs
    Arc* arc = NULL;

    // note that src, dest are names of nodes.
    string src, dest;
    double weight;
    while(getline(infile,sLine,'\n'))
    {
        istringstream iss(sLine);
        iss >> src >> dest >> weight;

        name = (int)arcs.size()+1;
        arc = new Arc(name);

        arc->_id = (int)arcs.size();

        arc->_src = get_node(src);
        arc->_dest= get_node(dest);
        arc->_weight=weight;
        add_arc(arc);
        arc->connect();
    }
    infile.close();
}

/** construct a graph by reading an adjacency matrix */
void Net::read_adjacency_matrix(const string& fname) {
    FILE *fp = fopen(fname.c_str(),"r");
    if(fp == NULL)
    {
            cout << "Can’t open input file " << fname;
            exit(1);
    }
    max_line_len = 1024;
    line = new char[max_line_len];

    vector<vector<int>> matrix;
    int temp;
    while(readline(fp)!=NULL)
    {
        vector<int> row;
        stringstream linestream(line);
        while (linestream>>temp)
            row.push_back(temp);
        matrix.push_back(row);
    }

    int n=0;
    n =matrix.size();

    string name;
    int id = 0;

    Node* node = NULL;
    for (int i= 0; i<n; i++) {
        name = to_string(i);
        node = new Node(name,i);
        add_node(node);
    }

    Arc* arc = NULL;
    string src, dest;
    unsigned index = 0;
    for (int i = 0; i <(n); i++)
        for (int j=i+1; j<n; j++) {
            if (matrix[i][j] > 0)
            {
                src = to_string(i);
                dest = to_string(j);
                id = index;
                arc = new Arc(src + "," + dest);
                arc->_id = id;
                arc->_src = get_node(src);
                arc->_dest= get_node(dest);
                add_arc(arc);
                arc->connect();
            }
            index++;
        }
    delete[] line;
    fclose(fp);
}

void Net::get_complement(const char* fname) {
    FILE *fp = fopen(fname,"r");
    if(fp == NULL)
    {
        fprintf(stderr,"can’t open input file %s\n",fname);
        exit(1);
    }

    max_line_len = 1024;
    line = new char[max_line_len];

    vector<vector<int>> matrix;
    int temp;
    while(readline(fp)!=NULL)
    {
        vector<int> row;
        stringstream linestream(line);
        while (linestream>>temp)
            row.push_back(temp);
        matrix.push_back(row);
    }
    int n=0;
    n =matrix.size();

    string name;
    int id = 0;

    Node* node = NULL;
    for (int i= 0; i<n; i++) {
        name = to_string(i);
        node = new Node(name,i);
        add_node(node);
    }

    Arc* arc = NULL;
    string src, dest;

    for (int i = 0; i <(n-1); i++)
        for (int j=i+1; j<n; j++) {
            if (matrix[i][j] == 0)
            {
                src = to_string(i);
                dest = to_string(j);
                id = (int)arcs.size();
                arc = new Arc(to_string(id));
                arc->_id = id;
                arc->_src = get_node(src);
                arc->_dest= get_node(dest);
                add_arc(arc);
                arc->connect();
            }
        }
    delete[] line;
    fclose(fp);
}

/*  @brief Remove node and all incident arcs from the network
 @note Does not remove the incident arcs from the list of arcs in the network!
 @return the id of the node removed
 */
string Net::remove_end_node() {
    Node* n = nodes.back();
    Node * nn = nullptr;
    string n_id = n->_name;
    for (auto a: n->branches) {
        nn = a->neighbour(n);
        nn->removeArc(a);
        for (auto aa: nn->branches) {
            if (!aa->neighbour(nn)->is_connected(n)) {
                nn->fill_in--;
                assert(nn->fill_in >=0);
            }
        }
    }
    nodes.pop_back();
    return n_id;
}

// use greedy fill-in algorithm.
void Net::get_tree_decomp_bags(bool print_bags, bool decompose) {
    Node* n = nullptr;
    Node* u = nullptr;
    Node* nn = nullptr;
    Arc* arc = nullptr;
    set<vector<Node*>> unique_bags;
    string name="";
    Net* graph_clone = clone_undirected(); //
    int nb = 0;
    unsigned max_size = 0;

    /** cliques with less than 1 nodes are useless for us.*/
    while (graph_clone->nodes.size()> 2) {
        sort(graph_clone->nodes.begin(), graph_clone->nodes.end(),node_compare);

        // last element has the minimum fill-in.
        n = graph_clone->nodes.back();
        if(!n->_active) {
            graph_clone->remove_end_node();
            continue;
        }
        Debug(n->_name << endl);
        Debug(graph_clone->nodes.size() << endl);
        vector<Node*> bag_copy;
        vector<Node*> bag;
        DebugOff("new bag = { ");
        for (auto nn: n->get_neighbours()) {
            if(!nn->_active) continue;
            bag_copy.push_back(nn);
            bag.push_back(get_node(nn->_name)); // Note it takes original node.
            DebugOff(nn->_name << ", ");
        }
        DebugOff(n->_name << "}\n");
        graph_clone->remove_end_node();
        bag_copy.push_back(n);
        bag.push_back(get_node(n->_name)); // node in this graph
        sort(bag_copy.begin(), bag_copy.end(), [](const Node* a, const Node* b) -> bool{return a->_id < b->_id;});
        sort(bag.begin(), bag.end(), [](const Node* a, const Node* b) -> bool{return a->_id < b->_id;});

        // update clone_graph and construct chordal extension.
        for (int i = 0; i < bag_copy.size(); i++) {
            u = bag_copy.at(i);
            for (int j = i+1; j<bag_copy.size(); j++) {
                nn = bag_copy.at(j);
                if (u->is_connected(nn)) {
                    if(get_arc(u,nn) && !get_arc(u,nn)->_active) {
                        Arc* off_arc = get_arc(u,nn);
                        off_arc->_imaginary = true;
                        off_arc->_free = true;
                    }
                    continue;
                }
                name = to_string((int) graph_clone->arcs.size()+1);
                arc = new Arc(name);

                arc->_id = arcs.size();
                arc->_src = u;
                arc->_dest = nn;
                arc->_imaginary = true;
                arc->_free = true;
                arc->connect();
                graph_clone->add_undirected_arc(arc);
            }
        }

//        if (true) {
            //            DebugOn("bag_copy = {");
            //            for (int i=0; i<bag_copy.size();     i++) {
            //                cout << bag_copy.at(i)->_name << " ";
            //            }
            //            DebugOn("}" << endl);
//            DebugOff("bag = {");
//            for (int i=0; i<bag.size();     i++) {
//                cout << bag.at(i)->_name << " ";
//            }
//            DebugOff("}" << endl);
//        }
        //        _bags_copy.push_back(bag_copy);
        if(unique_bags.insert(bag).second){
            _bags.push_back(bag); // bag original
        }

        if (bag_copy.size()==3) {
            nb++;
        }
        if (bag_copy.size()>max_size) {
            max_size = bag_copy.size();
        }
        else if(decompose && bag_copy.size()>3){
            DebugOff("Decomposing bigger bag into 3d bags\n");

            for (auto i = 0; i<bag_copy.size()-2; i++) {
                for (auto j = i+1; j<bag_copy.size()-1; j++) {
                    for (auto k = j+1; k<bag_copy.size(); k++) {
                        vector<Node*> new_bag;
                        new_bag.push_back(bag[i]);
                        new_bag.push_back(bag[j]);
                        new_bag.push_back(bag[k]);
                        DebugOff("new bag = {");
//                        for (int i=0; i<new_bag.size();     i++) {
//                            cout << new_bag.at(i)->_name << " ";
//                        }
                        DebugOff("}" << endl);
                        if(unique_bags.insert(new_bag).second){
                            _bags.push_back(new_bag);
                        }
                    }
                }
            }
        }
        delete n;
    }
    //    sort(_bags.begin(), _bags.end(), bag_compare);

    Debug("\n Number of 3D bags = " << nb << endl);
    DebugOn("\n Max cliwue size = " << max_size << endl);
//    DebugOn("\n Total number of bags = " << _bags.size() << endl);

    delete graph_clone;

}

/** Return the vector of arcs ignoring parallel lines **/
indices Net::get_bus_pairs(){
    indices bpairs("bus_pairs");
    for (auto a: arcs) {
        if (!a->_parallel) {
            bpairs.add(a->_src->_name+","+a->_dest->_name);
        }
    }
    return bpairs;
}

//Net* Net::get_chordal_extension() {
//    Node* n = nullptr;
//    Node* u = nullptr;
//    Node* nn = nullptr;
//    Arc* arc = nullptr;
//    Arc* arc_chordal = nullptr;
//
//    Node* u_chordal = nullptr;
//    Node* nn_chordal = nullptr;
//
//    string name="";
//    string name_chordal="";
//    Net* chordal_extension = clone();
//    Net* graph_clone = clone_undirected();
//    int nb = 0;
//
//    /** cliques with less than 1 nodes are useless for us.*/
//    while (graph_clone->nodes.size() > 1) {
//        sort(graph_clone->nodes.begin(), graph_clone->nodes.end(),node_compare);
//        // last element has the minimum fill-in.
//        n = graph_clone->nodes.back();
//        Debug(n->_name << endl);
//        Debug(_clone->nodes.size() << endl);
//        vector<Node*> bag_copy;
//        vector<Node*> bag;
//        Debug("new bag_copy = { ");
//
//        for (auto nn: n->get_neighbours()) {
//            bag_copy.push_back(nn);
//            bag.push_back(get_node(nn->_name));
//            Debug(nn->_name << ", ");
//        }
//
//        graph_clone->remove_end_node();
//        bag_copy.push_back(n);
//        bag.push_back(get_node(n->_name)); // node in this graph
//        sort(bag_copy.begin(), bag_copy.end(),[](const Node* a, const Node* b) -> bool{return a->_id < b->_id;});
//        sort(bag.begin(), bag.end(),[](const Node* a, const Node* b) -> bool{return a->_id < b->_id;});
//
//        // update graph_graph and construct chordal extension.
//        for (int i = 0; i < bag_copy.size() - 1; i++) {
//            u = bag_copy.at(i);
//            u_chordal = chordal_extension->get_node(u->_name);
//            for (int j = i+1; j<bag_copy.size(); j++) {
//                nn = bag_copy.at(j);
//                nn_chordal=chordal_extension->get_node(nn->_name);
//                if (u->is_connected(nn)) {
//                    continue;
//                }
//                name = to_string((int) graph_clone->arcs.size()+1);
//                name_chordal = to_string((int)chordal_extension->arcs.size()+1);
//
//                arc = new Arc(name);
//                arc_chordal = new Arc(name_chordal);
//
//                arc->_id = arcs.size();
//                arc->_src = u;
//                arc->_dest = nn;
//                arc->connect();
//                graph_clone->add_undirected_arc(arc);
//
//                arc_chordal->_id = chordal_extension->arcs.size();
//                arc_chordal->_src = u_chordal;
//                arc_chordal->_dest = nn_chordal;
//                arc_chordal->connect();
//                chordal_extension->add_undirected_arc(arc_chordal);
//            }
//        }
//        _bags_copy.push_back(bag_copy);
//        _bags.push_back(bag);
//        if (bag_copy.size()==3) {
//            nb++;
//        }
//        delete n;
//    }
//    // sort the bags by its size (descending order)
//    sort(_bags.begin(), _bags.end(), bag_compare);
//    printf("With greedy fill-in algirithm, the chordal graph added  %lu edges \n", (chordal_extension->arcs.size() - arcs.size()));
//
//    delete graph_clone;
//    return chordal_extension;
//}

// get cliques from the tree decomposition
// Two methods
// first one: check the inclusion relationship
// second one: use the RIP property of the tree decomposition, thus just need to check every leaf..
// One need to execute either get_tree_decomposition or get_chordal_extension first, then run get_clique_tree.

// use _bags instead of bag_copy
//void Net::get_cliquebags (bool print) {
//    for (unsigned i = 0; i < _bags.size(); i++) {
//        for (unsigned j = i+1; j < _bags.size();) {
//            if (std::includes(_bags[i].begin(),_bags[i].end(),
//                              _bags[j].begin(), _bags[j].end()))
//            {
//                _bags.erase(_bags.begin()+j);
//            }
//            else
//                j++;
//        }
//    }
//    cout << "Number of maximal cliques of the chordal extension = " << _bags.size() << endl <<endl;
//}

/* Destructors */
Net::~Net() {
    if (!nodes.empty()) {
        for (vector<Node*>::iterator it = nodes.begin(); it != nodes.end(); it++) {
            delete (*it);
        }
        nodes.clear();
    }
    if(!arcs.empty()) {
        for (vector<Arc*>::iterator it = arcs.begin(); it != arcs.end(); it++) {
            if(*it)
                delete (*it);
        }
        arcs.clear();
    }

    if(!cycle_basis.empty()) {
        for (vector<Path*>::iterator it = cycle_basis.begin(); it != cycle_basis.end(); it++) {
            delete (*it);
        }
        arcs.clear();
    }
    for (pair<string,set<Arc*>*> it:arcID) {
        delete it.second;
    }
}

//Net* Net::get_clique_tree(){
//    Net* cliquetree = new Net();
//    Node* node = nullptr;
//    Arc*  a = nullptr;
//    string name;
//    get_cliquebags(true);
//#ifdef USE_BOOST
//    /** Note that we also need the edge information of the clique tree **/
//    /** boost graph library or implement the expanded version of MCS algorithm by Blair and Peyton */
//    typedef boost::adjacency_list <boost::vecS,
//    boost::vecS,
//    boost::undirectedS,
//    boost::no_property,
//    boost::property < boost::edge_weight_t, int >> Graph;
//    typedef boost::graph_traits <Graph>::edge_descriptor Edge;
//    //typedef boost::graph_traits <Graph>::vertex_descriptor Vertex;
//
//    // BUILD THE INTERSECTION GRAPH OF THE CLIQUES
//    typedef std::pair<int, int> E;
//    std::vector<E> edges;
//    std::vector<int> weights;
//    int nb_cliques = this->_bags.size();
//    for (int i = 0; i < nb_cliques; i++) {
//        DebugOn("bag " << i << " has " << this->_bags[i].size() << " nodes." <<endl);
//        sort(this->_bags[i].begin(), this->_bags[i].end());
//        for (int j = i +1; j < nb_cliques; j++) {
//            vector<Node*> v3;
//            sort(this->_bags[j].begin(), this->_bags[j].end());
//            set_intersection(this->_bags[i].begin(), this->_bags[i].end(), this->_bags[j].begin(), this->_bags[j].end(), back_inserter(v3));
//            if (v3.size() > 0) {
//                edges.push_back(E(i, j));
//                weights.push_back(-v3.size());
//            }
//        }
//    }
//    //size_t num_edges = edges.size();
//
//#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
//    Graph g(num_nodes);
//    boost::property_map<Graph, edge_weight_t>::type weightmap = get(edge_weight, g);
//    for (std::size_t j = 0; j < num_edges; ++j) {
//        Edge e;
//        bool inserted;
//        boost::tie(e, inserted) = boost::add_edge(edges[j].first, edges[j].second, g);
//        boost::weightmap[e] = weights[j];
//    }
//#else
//    Graph g(edges.begin(), edges.end(), weights.begin(), nb_cliques);
//#endif
//    boost::property_map < Graph, boost::edge_weight_t >::type weight = get(boost::edge_weight, g);
//    std::vector < Edge > spanning_tree;
//    boost::kruskal_minimum_spanning_tree(g, std::back_inserter(spanning_tree));
//
//    DebugOn("Print the total " << spanning_tree.size() << " edges in the clique tree:" << endl);
//
//    //////////CLIQUE TREE /////////////////////////////
//    for (int i = 0; i < nb_cliques; i++) {
//        node= new Node(to_string(i), i);
//        cliquetree->add_node(node);
//    }
//
//    for (std::vector < Edge >::iterator ei = spanning_tree.begin();
//         ei != spanning_tree.end(); ++ei) {
//        int u = source(*ei, g);
//        int v = target(*ei, g);
//        DebugOn(u << " <--> " << v
//                << " with weight of " << -weight[*ei]
//                << endl);
//        name = (int) cliquetree->arcs.size();
//        a = new Arc(name);
//        a->_id = cliquetree->arcs.size();
//
//        // intersection
//        vector<Node*> v3;
//        sort(this->_bags[u].begin(), this->_bags[u].end());
//        sort(this->_bags[v].begin(), this->_bags[v].end());
//        set_intersection(this->_bags[u].begin(), this->_bags[u].end(),
//                         this->_bags[v].begin(), this->_bags[v].end(),
//                         back_inserter(v3));
//        a->_src = cliquetree->get_node(to_string(u));
//        a->_dest = cliquetree->get_node(to_string(v));
//        a->_weight = -weight[*ei];
//        a->_intersection = v3;
//        cliquetree->add_arc(a);
//        a->connect();
//
//        for (int i = 0; i < v3.size(); i++){
//                auto  node = v3.at(i);
//            for (int j = i+1; j < v3.size(); j++){
//                auto arc = get_arc(node, v3.at(j));
//                if (arc != nullptr){
//                    a->_intersection_clique.push_back(new index_pair(index_(arc->_src->_name), index_(arc->_dest->_name), arc->_active));
//                }
//             //   else
//               //     a->_intersection_clique.push_back(new index_pair(index_(node->_name), index_(v3.at(j)->_name), true));
//            }
//        }
//    }
//#endif
//    return cliquetree;
//}

//void Net::chol_decompose(bool print){
//    arma::mat adjacency_matrix = arma::zeros(nodes.size(), nodes.size());
//    for (auto &arc: arcs){
//        adjacency_matrix(arc->_src->_id, arc->_dest->_id) = 1;
//        adjacency_matrix(arc->_dest->_id, arc->_src->_id) = 1;
//
//    }
//    arma::mat pertubation = arma::zeros(nodes.size(),nodes.size());
//    // if fails,  make epsilon larger.
//    unsigned epsilon = 2;
//    arma::mat adjacency_matrix_psd = adjacency_matrix + epsilon*pertubation.eye();
//    arma::mat chordal_sparsity = arma::zeros(nodes.size(),nodes.size());
//    chordal_sparsity = arma::chol(adjacency_matrix_psd);
//    if (print){
//        cout << "chordal sparsity matrix: \n " << chordal_sparsity << endl;
//    }
//    unsigned num_nonzeros = 0;
//    for (int i = 0; i < nodes.size(); i++)
//        for (int j = i+1; j< nodes.size(); j++){
//            if (chordal_sparsity(i, j) != 0){
//                num_nonzeros +=1;
//            }
//        }
//    printf("With cholesky decomposition, the chordal graph added  %lu edges \n", (num_nonzeros - arcs.size()));
//}

//std::vector<gravity::index_pair*> Net::get_bus_pairs_all(){
//    vector<gravity::index_pair*> res;
//    string ni, nj;
//    for(int i = 0; i < nodes.size()-1; i++) {
//        for(int j = i+1; j < nodes.size(); j++) {
//            ni = nodes[i]->_name;
//            nj = nodes[j]->_name;
//            res.push_back(new index_pair(index_(ni), index_(nj), 1));
//        }
//    }
//    return res;
//}